pax_global_header00006660000000000000000000000064120104711760014511gustar00rootroot0000000000000052 comment=32cb5306567558d7d8c0091c10f6df8dbb06f0bf mx-1.4.7/000077500000000000000000000000001201047117600121465ustar00rootroot00000000000000mx-1.4.7/AUTHORS000066400000000000000000000000001201047117600132040ustar00rootroot00000000000000mx-1.4.7/COPYING.LIB000066400000000000000000000635041201047117600136160ustar00rootroot00000000000000 GNU LESSER GENERAL PUBLIC LICENSE Version 2.1, February 1999 Copyright (C) 1991, 1999 Free Software Foundation, Inc. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. [This is the first released version of the Lesser GPL. It also counts as the successor of the GNU Library Public License, version 2, hence the version number 2.1.] Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This license, the Lesser General Public License, applies to some specially designated software packages--typically libraries--of the Free Software Foundation and other authors who decide to use it. You can use it too, but we suggest you first think carefully about whether this license or the ordinary General Public License is the better strategy to use in any particular case, based on the explanations below. When we speak of free software, we are referring to freedom of use, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish); that you receive source code or can get it if you want it; that you can change the software and use pieces of it in new free programs; and that you are informed that you can do these things. To protect your rights, we need to make restrictions that forbid distributors to deny you these rights or to ask you to surrender these rights. These restrictions translate to certain responsibilities for you if you distribute copies of the library or if you modify it. For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights that we gave you. You must make sure that they, too, receive or can get the source code. If you link other code with the library, you must provide complete object files to the recipients, so that they can relink them with the library after making changes to the library and recompiling it. And you must show them these terms so they know their rights. We protect your rights with a two-step method: (1) we copyright the library, and (2) we offer you this license, which gives you legal permission to copy, distribute and/or modify the library. To protect each distributor, we want to make it very clear that there is no warranty for the free library. Also, if the library is modified by someone else and passed on, the recipients should know that what they have is not the original version, so that the original author's reputation will not be affected by problems that might be introduced by others. Finally, software patents pose a constant threat to the existence of any free program. We wish to make sure that a company cannot effectively restrict the users of a free program by obtaining a restrictive license from a patent holder. Therefore, we insist that any patent license obtained for a version of the library must be consistent with the full freedom of use specified in this license. Most GNU software, including some libraries, is covered by the ordinary GNU General Public License. This license, the GNU Lesser General Public License, applies to certain designated libraries, and is quite different from the ordinary General Public License. We use this license for certain libraries in order to permit linking those libraries into non-free programs. When a program is linked with a library, whether statically or using a shared library, the combination of the two is legally speaking a combined work, a derivative of the original library. The ordinary General Public License therefore permits such linking only if the entire combination fits its criteria of freedom. The Lesser General Public License permits more lax criteria for linking other code with the library. We call this license the "Lesser" General Public License because it does Less to protect the user's freedom than the ordinary General Public License. It also provides other free software developers Less of an advantage over competing non-free programs. These disadvantages are the reason we use the ordinary General Public License for many libraries. However, the Lesser license provides advantages in certain special circumstances. For example, on rare occasions, there may be a special need to encourage the widest possible use of a certain library, so that it becomes a de-facto standard. To achieve this, non-free programs must be allowed to use the library. A more frequent case is that a free library does the same job as widely used non-free libraries. In this case, there is little to gain by limiting the free library to free software only, so we use the Lesser General Public License. In other cases, permission to use a particular library in non-free programs enables a greater number of people to use a large body of free software. For example, permission to use the GNU C Library in non-free programs enables many more people to use the whole GNU operating system, as well as its variant, the GNU/Linux operating system. Although the Lesser General Public License is Less protective of the users' freedom, it does ensure that the user of a program that is linked with the Library has the freedom and the wherewithal to run that program using a modified version of the Library. The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The former contains code derived from the library, whereas the latter must be combined with the library in order to run. GNU LESSER GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License Agreement applies to any software library or other program which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Lesser General Public License (also called "this License"). Each licensee is addressed as "you". A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables. The "Library", below, refers to any such software library or work which has been distributed under these terms. A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is included without limitation in the term "modification".) "Source code" for a work means the preferred form of the work for making modifications to it. For a library, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the library. Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does. 1. You may copy and distribute verbatim copies of the Library's complete source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and distribute a copy of this License along with the Library. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Library or any portion of it, thus forming a work based on the Library, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) The modified work must itself be a software library. b) You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change. c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License. d) If a facility in the modified Library refers to a function or a table of data to be supplied by an application program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a good faith effort to ensure that, in the event an application does not supply such function or table, the facility still operates, and performs whatever part of its purpose remains meaningful. (For example, a function in a library to compute square roots has a purpose that is entirely well-defined independent of the application. Therefore, Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it, the square root function must still compute square roots.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Library, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Library. In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices. Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy. This option is useful when you wish to copy part of the code of the Library into a program that is not a library. 4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange. If distribution of object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code. 5. A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License. However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a "work that uses the library". The executable is therefore covered by this License. Section 6 states terms for distribution of such executables. When a "work that uses the Library" uses material from a header file that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true is not precisely defined by law. If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.) Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself. 6. As an exception to the Sections above, you may also combine or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer's own use and reverse engineering for debugging such modifications. You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution displays copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things: a) Accompany the work with the complete corresponding machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an executable linked with the Library, with the complete machine-readable "work that uses the Library", as object code and/or source code, so that the user can modify the Library and then relink to produce a modified executable containing the modified Library. (It is understood that the user who changes the contents of definitions files in the Library will not necessarily be able to recompile the application to use the modified definitions.) b) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (1) uses at run time a copy of the library already present on the user's computer system, rather than copying library functions into the executable, and (2) will operate properly with a modified version of the library, if the user installs one, as long as the modified version is interface-compatible with the version that the work was made with. c) Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution. d) If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place. e) Verify that the user has already received a copy of these materials or that you have already sent this user a copy. For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it. However, as a special exception, the materials to be distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute. 7. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined library, provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities. This must be distributed under the terms of the Sections above. b) Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 9. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it. 10. Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties with this License. 11. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Library at all. For example, if a patent license would not permit royalty-free redistribution of the Library by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Library. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply, and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 13. The Free Software Foundation may publish revised and/or new versions of the Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation. 14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Libraries If you develop a new library, and you want it to be of the greatest possible use to the public, we recommend making it free software that everyone can redistribute and change. You can do so by permitting redistribution under these terms (or, alternatively, under the terms of the ordinary General Public License). To apply these terms, attach the following notices to the library. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Also add information on how to contact you by electronic and paper mail. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the library, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the library `Frob' (a library for tweaking knobs) written by James Random Hacker. , 1 April 1990 Ty Coon, President of Vice That's all there is to it! mx-1.4.7/ChangeLog000066400000000000000000000001671201047117600137240ustar00rootroot00000000000000The ChangeLog is auto-generated when releasing. If you are seeing this, use 'git log' for a detailed list of changes. mx-1.4.7/HACKING000066400000000000000000000037041201047117600131410ustar00rootroot00000000000000Coding Style ============ The coding style of Mx mostly follows the GNU coding standards. See http://www.gnu.org/prep/standards/ for more information. One important exception is that formal parameters to functions are always separated by new lines and the type and name columns are aligned. For example: void foo_bar_set_int (FooBar *type, gint int) { ... } Users of the Vim editor will want to set the following options to help produce properly indented code: :set expandtab shiftwidth=2 softtabstop=2 :set cindent cinoptions=>4,n-2,{2,^-2,\:0,=2,g0,h2,t0,+2,(0,u0,w1,m1 Alternatively, set the following lines in your .vimrc file to enable reading the local vim settings from the sources directory: set exrc " enable per-directory .vimrc files set secure " disable unsafe commands in local .vimrc files Committing ========== Mx is currently managed in a git repository. Git has a special format for commit messages; the first line of the message is a brief (less than 72 characters) explanation of the commit. Where appropriate, the short commit message should be preceded by a "tag", consisting of a word followed by a semicolon. This should either be the lower case class name affected by the commit, or the application binary name. If your patch affects multiple classes or applications, consider whether the patch can be broken into smaller commits. The short log message is followed by a blank line and then a paragraph that details the reasons for the commit and explains the changes. Sometimes it is not necessary to include the long description, but this should be the exception rather than the rule. For example: ------------------------------------------------------------------------------- tag: a brief short description A longer description outlining the changes and why they where introduced. This should be concise, but not too brief. ------------------------------------------------------------------------------- mx-1.4.7/Makefile.am000066400000000000000000000032301201047117600142000ustar00rootroot00000000000000SUBDIRS = mx if ENABLE_GTK_WIDGETS SUBDIRS += mx-gtk endif SUBDIRS += data tests docs po ACLOCAL_AMFLAGS=-I m4 GITIGNOREFILES= $(top_srcdir)/docs/reference/libmx/tmpl \ docs/reference/libmx/mx-overrides.txt \ m4/gtk-doc.m4 \ m4/libtool.m4 \ m4/ltoptions.m4 \ m4/ltsugar.m4 \ m4/ltversion.m4 \ m4/lt~obsolete.m4 \ m4/intltool.m4 \ po/mx-1.0.pot DISTCHECK_CONFIGURE_FLAGS=--enable-gtk-doc pcfiles = mx-$(MX_API_VERSION).pc if ENABLE_GTK_WIDGETS pcfiles += mx-gtk-$(MX_API_VERSION).pc endif mx-$(MX_API_VERSION).pc: mx.pc @cp -f $< $@ if ENABLE_GTK_WIDGETS mx-gtk-$(MX_API_VERSION).pc: mx-gtk.pc @cp -f $< $@ endif pkgconfig_DATA = $(pcfiles) pkgconfigdir = $(libdir)/pkgconfig EXTRA_DIST = $(mxdoc_DATA) mx.pc.in mx-gtk.pc.in CLEANFILES = $(pcfiles) # Extra clean files so that maintainer-clean removes *everything* MAINTAINERCLEANFILES = \ aclocal.m4 \ compile \ config.guess \ config.h.in \ config.sub \ configure \ depcomp \ gtk-doc.make \ install-sh \ ltmain.sh \ Makefile.in \ missing \ intltool-extract.in \ intltool-merge.in \ intltool-update.in \ INSTALL \ mkinstalldirs -include $(top_srcdir)/git.mk dist-hook: @if test -d "$(srcdir)/.git"; \ then \ echo Creating ChangeLog && \ ( cd "$(top_srcdir)" && \ echo '# Generated by Makefile. Do not edit.'; echo; \ $(top_srcdir)/missing --run git log --stat ) > ChangeLog.tmp \ && mv -f ChangeLog.tmp $(top_distdir)/ChangeLog \ || ( rm -f ChangeLog.tmp ; \ echo Failed to generate ChangeLog >&2 ); \ else \ echo A git clone is required to generate a ChangeLog >&2; \ fi mx-1.4.7/NEWS000066400000000000000000000654251201047117600126610ustar00rootroot000000000000001.4.7 ===== Changes since 1.4.6 Fixed Bugs (from bugzilla.clutter-project.org): #2688 - image: add some annotations for mx_image_set_from_buffer/data #2687 - widget: Handle NULL ClutterEventCrossing->related actor in mx_widget_leave #2690 - MxScrollView: Fixed to clip events for clipped child area in ClutterActor->pick() 1.4.6 ===== Changes since 1.4.5 * Fix an issue with picking in MxStack * Fix linking when using ld.gold linker 1.4.5 ===== Changes since 1.4.4 * dialog: ensure that the initial size of the dialog is correct * dialog: disable the blurred background when using Clutter ≥ 1.10 * css: pseudo-class should be identified by ':' in debug output * combo-box: don't use NULL when setting the ClutterText "text" property * test-containers: create a grid of actors in a group for viewport tests * viewport: implement apply_transform and get_paint_volume * scroll-view: clip around the child instead of setting clip-to-allocation * table: fix sorting depth * entry: Don't emit the notify text signal on switching between the hint text * application: ensure the singleton variable is properly assigned * adjustment: emit the "changed" signal if the value property changes 1.4.4 ===== Changes since 1.4.3 * Improved compatibility with Clutter 1.9 * button: avoid setting the ClutterText "text" property to NULL 1.4.3 ===== Changes since 1.4.2 * slider: allow the handle button press and release events to propagate * widget: ensure the "disabled" pseudo class is set when needed * toggle: bind the "disabled" property of the handle and the toggle * entry: use an empty string if the "text" property is set to NULL * tooltip: update position when changing the text * button/widget: don't rely on private variables to track hover state * toggle: change timeline direction even if playing already 1.4.2 ===== Changes since 1.4.1 * create-image-cache: use dynamic memory allocation for paths * entry: conditionally disable code that depends on X11 * kinetic-scroll-view: correctly clamp to the center * offscreen: only restore the cogl state if it had been modified Fixed bugs (from bugzilla.clutter-project.org): #2614 - Document the need for ClutterGesture for MxScrollView gestures #2686 - Crash in xsettings_notify_func() due to NULL setting 1.4.1 ===== Changes since 1.4.0 * Prevent warnings from MxImage by not closing the pixbuf loader before calling g_object_unref. * Honor the disabled property in MxToggle. * Prevent warnings when using MxAction with glib 2.28. 1.4.0 ===== Changes since 1.3.2 * Add new testing applications for widgets and containers * Prevent the cursor from being stuck as an I beam after selecting text in MxEntry * Add "state" and "clamp-to-center" properties for MxKineticScrollView * Add tooltips for MxEntry icons * Allow highlight icons for MxEntry icons * Fix a painting issue with MxScrollBar and clipped redraws * Add "show-tooltip" property to MxLabel to show a tooltip when the text does not fit in the current allocated size. Fixed Bugs (from bugzilla.clutter-project.org): #2685 - stylable/listview: fix some memory leaks #2682 - No tooltip for icons in MxEntry 1.3.2 ===== Changes since 1.3.1 * Implement the get_paint_volume virtual function on MxWidget and MxTextureFrame to enable clipped redraws and automatic culling. * Allow building from git without gtk-doc * MxEntry: Implement Unicode input mode * Implement the GAction interface on MxAction * MxApplication: Add a function to call a remote action with a parameter Fixed Bugs (from bugzilla.clutter-project.org): #2680 - kinetic-scroll-view: Respect MxAdjustment::clamp-values #2683 - MxStack preferred width and height use hidden children #2684 - Mx doesn't work with multi-driver Cogl builds 1.3.1 ===== Changes since 1.3.0 * MxKineticScrollView: lower the minimum value of deceleration to 1.01 * MxKineticScrollView: add "clamp-duration", "clamp-mode" and "acceleration-factor" properties * Optimised the painting of MxImage * MxStack: add a "crop" child property * Implement the MxFocusable interface on MxMenu * MxComboBox: Open the menu when return key is pressed * MxMenu: close the menu when escaped is pressed, or focus moved elsewhere * Use xz compressed distribution tarballs by default * MxSlider: prevent the handle position from being changed while the user is dragging it Fixed Bugs (from bugzilla.clutter-project.org): #2674 - Build fixes for Windows #2675 - Many introspection annotations are missing #2678 - Bump dependency on clutter 1.4 1.3.0 ===== Changes since 1.2.0 * Various performance optimisations and bug fixes * Add a remove_all() method to MxComboBox Fixed Bugs (from bugzilla.clutter-project.org): #2666 - image: Allow a transition duration of 0ms #2663 - Fix compiler warnings #2662 - MxActorManger: unparent container before removing children #2646 - Fix walking of children in MxBoxLayout #2645 - Warnings when destroying an MxEntry while mouse pointer is over it. #2671 - MxImage memory corruption when blitting textures 1.2.0 ===== Changes since 1.1.12 * Update sizes in Gtk light switch * Add scroll-policy support to MxKineticScrollView * Don't swallow all button events in MxWidget * Fix MxKineticScrollView when :use-captured is FALSE * Change the default step-increment in MxAdjustment to 1.0 * Fix many compiler warnings * Allow overriding of the system icon theme * Add tooltip delay property to MxWidget * Fix MxOffscreen incorrect aspect ratio Fixed Bugs (from bugzilla.clutter-project.org): #2653 - test-mx on OSX seems to be broken #2578 - add "scroll-policy" property to MxKineticScrollView #2658 - The background looks wrong when MxDialog has a stage as its parent #2649 - The delay time before showing a tooltip should be settable #2643 - [MxKineticScrollView] Disable per-actor motion events when panning #2642 - MxGrid returns the wrong min_width_p 1.1.12 ====== Changes since 1.1.11 * Apply the paint opacity to both textures correctly in MxImage * Clarify the documentation for mx_bin_allocate_child * Indicate in the documentation which symbols are new in 1.2 * Draw rectangles around actors when using the layout debug flag * MxBin: don't request space for the child when it is not visible * MxBin: hide the label when it is empty or no text has been set * Don't animate MxToggle if it is not mapped * Remove the hover state from MxToggle if the pointer left during a drag * Add a function to MxImage to animate changing the scale mode Fixed Bugs (from bugzilla.clutter-project.org): #2470 - slider: propagate the "disabled" property to internal children #2609 - don't require libmx-gtk when building libmx documentation #2613 - Document mx_set_locale #2618 - Add MxAdjustment::interpolation-completed signal #2635 - Prevent glade support being enabled when --without-glade is used #2636 - Allocate expander child using align/fill properties. #2641 - Remove 'active' state and cancel long press on leave event #2642 - MxGrid: compute min_width and min_height correctly 1.1.11 ====== Changes since 1.1.10 * Allow arbitrary rotation angles in MxImage * Handle loss of focus in MxFocusManager * Handle URIs in MxTextureCache * Allow insertion into MxTextureCache * Allow insertion of metadata in MxTextureCache * Many fixes and refinement of MxImage * Fix calling clutter_actor_queue_relayout during dispose of MxOffscreen * Add MxActorManager, to help spread the load of actor operations * Fix MxTable get_preferred_width/height when used without for_width/height 1.1.10 ====== Changes since 1.1.9 * Fix some concurrency issues with asynchronous loading in MxImage * Fix the fullscreen GObject property setter in MxWindow * Make the toolbar property of MxWindow writable * Add a title property to MxWindow * Fix handling of padding in MxBoxLayout get_preferred_width/height * Add a use-markup property to MxLabel * Ensure the old border-image is correctly allocated in MxWidget * Make sure the corners of MxFadeEffect render consistently * Fix focus issues when showing a still-hiding MxDialog 1.1.9 ===== Changes since 1.1.8 * Fix MxStack not chaining up correctly, breaking background drawing * Add a new spinner graphic that stands out better on white * Add a 'looped' signal to the spinner * Add a 'buffer-value' property to MxSlider * Fix supported rotation values check in MxImage * Ensure that the 'disabled' pseudo-state is correct in MxWidget * Fix various styling issues in MxSlider * Fix incorrect CFLAGS when building documentation * Replace the per-object style cache with a per-style rule cache Fixed bugs (from bugzilla.clutter-project.org): #2577 - MxStack doesn't allow its parent to paint its background #2456 - MxBoxLayout: notify when adjustments change #2579 - Add last-frame / looped / something signal to MxSpinner #2568 - build: Update introspection.m4 #2281 - mx_bin_set_child() does not work with MxScrollView #2590 - Extra "-I" in Makefile.am causes fail to build from source #2510 - Update mx types and sections files for gtk-doc 1.1.8 ===== Changes since 1.1.7: * Documentation updates and fixes * New "image-rotation" property for MxImage Fixed bugs (from bugzilla.clutter-project.org): #2263 - Improve tooltip behavior and presentation #2281 - mx_bin_set_child() doesn't work with MxScrollView #2456 - MxBoxLayout: notify when the adjustments properties change #2568 - build: Update introspection.m4 #2510 - Update mx types and sections files for gtk-doc 1.1.7 ===== Changes since 1.1.6: * New class, MxFadeEffect. This is a sub-class of ClutterOffscreenEffect that lets you fade out the borders of actors * Label supports a 'fade-out' property that instructs it to fade out at the end of the line instead of ellipsizing Fixed bugs (from bugzilla.clutter-project.org): #2560 - MxTable: clutter_container_add does not update row/column count 1.1.6 ===== Changes since 1.1.5: * Label supports a linewrap property that proxies through to the internal ClutterText * MxStack now supports a fit property that attempts to fix the actor into the available space whilst respecting the actor's width-for-height or height-for-width constraints Fixed bugs (from bugzilla.clutter-project.org): #2523 - fix DSO linking issues #2552 - fix behaviour of --disable-gtk-widgets 1.1.5 ===== Changes since 1.1.4: * CSS debugging mode * Interpolation in MxAdjustment now works when the value was exactly at the end * JSON files without valid properties won't cause a segfault * Focus manager now supports a hint when pushing focus Fixed bugs (from bugzilla.clutter-project.org): #1195 - fixes to freeze/thaw behaviour #2533 - setting custom icon causes crash on 64bit #2532 - only requre GTK+ if not disabled #2537 - make mx-create-image-cache only depend on gdk-pixbuf 1.1.4 ===== Changes since 1.1.3: * Bump dependency on GLib to 2.26.0 for GBinding * Small optimisations to CSS matching * Support multiple CSS pseudo-classes * Add CSS selector prioritisation depending on position in stylesheet * Add a 'clear' function to MxImage to blank the current image * Always store the last-focused actor when moving focus * Add asynchronous image-loading to MxImage 1.1.3 ===== Changes since 1.1.2: * Make text and label properties translatable from JSON files * Add a 'drag-threshold' setting to MxSettings * Don't specify #version in 1.10 GLSL shaders * Add an 'overshoot' property to MxKineticScrollView * Compile the blur shader in MxDialog on creation * Fix toggling the toolbar after mapping in MxWindow * Add functions for setting/getting window size in MxWindow Fixed bugs (from bugzilla.clutter-project.org): #2488 - button events inside kinetic scroll view #2507 - add overshoot oeffect to MxKineticScrollView #2208 - Fix the incorrect removal of qdata when disabling MxDroppable #2412 - Don't try to interpolate if we get an invalid target value #2498 - Don't assume that MxScrollables will have both adjustments #2483 - MxIcon warning when "-mx-content-image:none;" is used #2463 - MxWindow with disabled toolbar still shows part of the toolbar ... #2496 - clutter_stage_set_fullscreen() only works after stage is realized #2434 - MxWindow should have a mx_window_set_size method 1.1.2 ===== Changes since 1.1.1: * Focus hints now support direction the request came from * MxSlider: allocate trough on pixel boundaries * MxIcon: now supports a style property for handling the icon suffix * MxEntry: fix shadow drawing when scrolling 1.1.1 ===== Changes since 1.1.0: * Add MxDialog, a modal, single-widget container * MxAdjustment: fix elasticity and add a 'clamp-value' property * MxSettings: add a 'small-screen' property * MxBoxLayout: fix animations and child property variable definitions * MxButton: add icon-name and icon-size properties, add an MxAction property * MxComboBox: fix the position of the popup when transformed * Add MxKinecticScrollView, a kinetic scrolling container widget * MxFocusable: add a focus debug category and other various fixes * Remove transitional mx/mx-gtk.h header * Split out various X11 specific code so that it is possible to compile Mx without X11 * MxSlider: add key focus support * Add MxSpinner, a processing indicator widget * Add MxStack, a container that allows stacking of children over each other * MxStyle: performance improvements and other fixes * MxTable: fix crasher when focusing an empty cell, implement raise/lower/sort container functions. * MxToggle: fix issues when transformed * MxTooltip: fix position when transformed * MxWindow: add orientation property, show/hide functions and various other fixes * Various other small changes and fixes Fixed bugs (from bugzilla.clutter-project.org): #2310 - Use the new CLUTTER_KEY symbols in Mx #2179 - Tooltips should only show after a delay #2312 - Sync style instance with internal children #2430 - Annotate mx_icon_theme_set_search_paths' paths argument properly #2439 - Add exported pacakges to GIRs #2429 - Differences between MxEntry and MxLabel are not documented #2357 - Mx should set the hover state also when the pointer is over a child #2387 - spinner should update at the redraw priority #2451 - [MxStack] position with pixel alignment when aligning in the middle 1.1.0 ===== Changes since 1.0.0: * Updated documentation * Fix compilation with recent versions of GTK+ * Require GTK+ 2.20 * MxWidget: Move padding into the correct struct * Reduce the libtool version age to indicate ABI change * Fix GtkLightSwitch size and remove labels * Focusable: don't accept focus on hidden actors * Allow spacing to be set from CSS in MxTable and MxBoxLayout * MxOffScreen: Add "redirect-enabled" property * MxOffScreen: Add accumulation buffer capability * MxOffScreen: Provide an accessor to the fbo * BoxLayout: add "scroll-to-focused" property * Add MxSettings and support for reading xsettings * Use CoglSubtexture rather than internal subtexture implementation * ScrollView: add shadows to the inside of a scrollview when scrolling * Viewport: respect fill, alignment and padding properties * Window: respect ClutterStage:user-resizable property * Improvements to MxIconTheme * Support pixel (px) and point (pt) font size values in CSS * MxModalFrame: new widget to implement modal dialogs Updated Translations: * Turkish (Ahmet Özgür Erdemli) * Asturian (astur) Many thanks to: Chris Lord Neil Roberts Thomas Wood iain 1.0.0 ===== Changes since 0.99.0: * Updated documentation * Fix build issues with GTK+ 2.20 * Updated visual design * Add "disabled" state support to widgets * Implement up/down/left/right focus support * Improved key focus management support * Improved key focus support in table and box-layout widgets * Prevent unwanted artifacts when resizing a texture frame beyond its smallest dimensions * Fix placement of combobox popup menu when near the bottom edge of the window * Fix issues building with Clutter 1.3 * Remove some unused and undefined API * Remove API marked as deprecated * Optimise the border-image property so that it does not load texture unnecessarily. * Optimise align/fill allocations so that they do not request size information unnecessarily. Fixed bugs (from bugs.meego.com): #909 mx_table_find_actor_at did not use row/col span to work out where an actor was #896 MxTable takes hidden rows/columns into consideration when calculating allocations #848 Build fails as code tries to set CoglColor members (red, green, blue) which don't exist #702 MxWidget paints outside of its allocation if border-image geometry is bigger than the widget size #597 Checkbox style for MX toggle button #345 [MxScrollView]The cross area of h bar and v bar is colored once the scroll view is launched #343 [MxScrollView] the function mx_scroll_view_set_enable_mouse_scrolling() does not take effect #1779 Memory leak in MxWidget when border-image hasn't changed #1570 Setting a label on a mapped MxButton that has had no label previously set results in an incorrect style #1514 Incorrect padding on email update tile #1201 [MxExpander] foreach() callback for expander only works when the expander is expanded #1200 [MxExpander] the constract_complete signal definition can be removed #1198 MxScrollView connects to non-existent property names (due to a rename) #1140 Unnecessary measurement causes performance issues #1139 MxLabel doesn't ask for enough width/height when it has padding #1138 Table: Focus can get trapped when moving right or down from a spanned actor #1053 MxScrollView will show scroll-bars when they aren't necessary #1052 Possible NULL pointer access in MxScrollView New and updated translations: Walloon Mexican Spanish Slovak Danish English (United Kingdom) (en_GB) Korean (ko) Italian (it) Finnish (fi) Polish (pl) Brazilian Portuguese (pt_BR) French (fr) Chinese (China) (zh_CN) Russian (ru) Japanese (ja) Many thanks to: Chris Lord Damien Lespiau Evan Nemerson iain José Dapena Paz Thomas Wood 0.99.0 ====== Changes since 0.9.0: * Deprecated API has been disabled * Rename MxSlider "progress" property to "value" * IconTheme: look in the system and user data directories * Set arbitrary preferred widths for ProgressBar, Slider and Entry, so that they are useful and usable when allocated their preferred size. * Simplify the offscreen shaders API in MxOffscreen * Convert the MxNotebook API to be actor based, rather than index based * Ensure that all enumeration types are namespaced properly * MxWindow icon can now be set from a cogl texture * Add function to retrieve the associated MxWindow from an ClutterStage * Fix type inconsistencies in MxTableChild properties (x/y-align) * Various updates to documentation Updated translations: German (de) translation (Andreas Machoy) Many thanks to: Thomas Wood Chris Lord 0.9.0 ==== Changes since 0.8.0: * Further API Review. Some small changes have been made and properties have been synchronised with their accessors. Please see the ChangeLog or commit log for full details. * MxMenu and MxComboBox now both use MxIcon internally, allowing the icon size to be themed in CSS. * MxMenu now has an -mx-spacing style property to allow custom spacing, similar to MxComboBox. * MxScrollView now has a function to ensure a region is visible. * Fixed bugs: MB#10372 - API for ensuring an actor inside mx-scroll-view is visible MB#9672 - HD usage bar shows incorrect color in Device panel Updated translations: Dutch (Flemish) (nl) translation (auke) Spanish (Castilian) (es) (tomasgalicia) Many thanks to: Hylke Bons Elliot Smith Chris Lord 0.8.0 ===== Changes since 0.7.0: * API Review completed. Lots of API has been renamed, removed or added. Please see the ChangeLog or commit log for full details. * Add deform textures, allowing arbitrary deformations on textures MxDeformTexture, MxDeformPageTurn, MxDeformCloth and MxDeformBowtie * Add MxOffscreen actor, to draw textures offscreen. * Fixed bugs: MB#10194 - Use the right values when setting :y-fill property MB#9527 - table-child: Add accessors for :row and :col MB#10054 - gtk-light-switch: adjust size request MB#6158 - viewport: simplify the viewport allocate MB#9936 - box-layout: Add mx_box_layout_add_actor(_with_position) functions MB#9998 - popup: call ensure style on the children MB#6313 - Add clutter-text properties to MxEntry and MxLabel MB#9978 - [droppable] Don't hide the draggable before picking MB#6159 - Add GObject Introspection annotations MB#6308 - [box-layout] Adjust amount we need to expand for padding MB#6467 - Add emacs mode-lines MB#6504 - [button] Fix property enumeration names MB#9917 - [expander] do not pass NULL to the foreach callback MB#9680 - [label] Implement "pick" in MxLabel MB#9842 - [button] print a warnings when using -mx-content-image with some properties Updated translations: Russian file. (Leila) Chinese (Taiwan) (zh_TW) translation (zerng07) Indonesian (id) translation (andika) Swedish (sv) translation (A Olsson) Many thanks to: Adrien Bustany Bastian Winkler Chris Lord Damien Lespiau Elliot Smith Emmanuele Bassi Hylke Bons Jussi Kukkonen Owen W. Taylor Rob Bradford Ross Burton Thomas Wood 0.7.0 ===== Changes since 0.6.0: * Fixes in resizable windows * Improvements to path-bar * Support allocating children less than their preferred size * Use -mx prefix for custom CSS properties * Fixed size and allocation of toggle switches * Add -mx-border-image-transition-duration to control background cross fade * Add a debug mode to MxTable to indicate column and rows * Make the scrollable interface name more consistent with other interfaces * Fixed bugs: MB#9570 - [widget] Bug in mx_widget_set_tooltip_text MB#9529 - [table] Calculate row_spacing correctly MB#9346 - Memory leak in widgets implementing MxScrollable MB#9055 - Unable to set the popup location of MxComboBox MB#9656 - [MxListView] Missing break statement in mx_list_view_set_property MB#9641 - [scroll-view] Fix setting scrollbar size from CSS Many thanks to: Bastian Winkler Thomas Wood Chris Lord Rob Bradford Evan Nemerson 0.6.0 ===== Changes since 0.5.0: * New MxWindow widget, now used by default in MxApplication * New MxPathBar widget, implementing a bread-crumb UI * New MxIconTheme class, to look up icons in icon themes * Icon themes used in MxIcon and MxButton * MxComboBox now supports icons in items * New MxDeformTexture abstract class for custom deformed textures * New MxDeformCloth, MxDeformBowtie, MxDeformPageTurn widgets, implementing various deformation effects. * Fixes to MxFocusable and MxFocusManager * Focusable support added to MxComboBox * Add -mx-content-image style property to specify an image for the appearance of a button, including sizing. * Add a text-layout-position property to MxEntry * Plenty of other bug fixes * Fixed bugs: MB#9381 - [box-layout] Queue a relayout when removing a child MB#9382 - [button-group] If the active button from a group is removed activate another MB#9288 - [texture-frame] Use the NEAREST filter on the CoglMaterial MB#9146 - [entry] Emit notify signal when text of an entry changes. MB#9287 - [stylable] Use PangoFontDescription to set the style on labels Many thanks to: Bastian Winkler Chris Lord Evan Nemerson Rob Bradford Thomas Wood 0.5.0 ===== Changes since 0.4.0: * Single instance, startup notification and action support added to MxApplication. * Expose mx_allocate_align_fill and mx_stylable_apply_clutter_text_attributes utility functions. * Add gesture support for changing pages in MxNotebook. * Fixes to various aspects of MxNotebook. * Use interpolations in stepper animations. * Add "popup" property to MxWidget. * Add MxFocusable and MxFocusManager to help with focus navigation. * Remove redundant row/column API from scroll view. * Add preferred width and height functions to MxSlider. * Set CSS property priorities based on load order. * Fixed bugs: MB#9054 - [button-group] fix incorrect parameter check in mx_button_group_foreach MB#9046 - [notebook] make sure children are hidden until needed when added MB#9028 - Position MxNotebook children relative to the parent MB#3785 - [grid] Add raise, lower and sort depth implementations MB#9147 - Add password-char property to MxEntry MB#9148 - [table] Skip hidden children when calculating row and column sizes Many thanks to: Chris Lord Evan Nemerson Gustavo Noronha Silva Rob Bradford Thomas Wood 0.4.0 ===== Changes since 0.3.0: * New MxSlider widget (Damien Lespiau) * New MxApplication class to manage windows and application configuration * Experimental gesture support in MxScrollView * Performance improvements to styling * Fixed bugs: MB#8892 - [css] Apply styling from parent classes to subclasses MB#8710 - [button] only emit notify::checked when the property has changed MB#8923 - [box-layout] Implement raise/lower/sort_depth_order 0.3.0 ===== Changes since 0.2.0: * New toolbar widget * Convert MxBin to abstract class * Add MxFrame as a drop-in replacement for plain instances of MxBin * Use the i-beam insertion mouse cursor in MxEntry * Clean up MxGrid API to be more consistent with other widgets * Add support for font-weight property to buttons and labels * Add animation layout support to MxBoxLayout * Add special styling for ComboBoxes inside a toolbar * Implement font styling in ComboBox * Fix expander label visibility * Add missing public headers and single include guards (Bastian Winkler) * Clean up various references to removed functions (Bastian Winkler) * Documentation improvements (Elliot Smith) 0.2.0 ===== Changes since 0.1.0: * Fix animation during drag and drop interaction on MxToggle * Fix scrollbar stepper animation bugs (MB#7803) * Fix insensitive colours in MxGtkLightSwitch (MB#7199) * Integrate many of the tests into a single application * Improved error reporting when loading of images or stylesheets fails * Add MxFloatingWidget, a common base class for "always-on-top" actors, used in tooltips and popup menus. * Add more classes to the documentation generation * Add a "long-press" signal to MxButton, to allow applications to handle long-press (or "tap and hold") actions. * Remove obsolete MxGtkExpander 0.1.2 ===== Changes since 0.1.1: * Prevent CSS debug messages when not requested 0.1.1 ===== Changes since 0.1.0: * Fix issues with hover states introduced to MxEntry by MxWidget * Automatically generate the ChangeLog file for tarball releases * Add ancestor matching to CSS * Improve CSS match scoring in line with w3c specification 0.1.0 ===== Initial release mx-1.4.7/README000066400000000000000000000015611201047117600130310ustar00rootroot00000000000000Mx Toolkit ========== Mx is a widget toolkit using Clutter that provides a set of standard interface elements, including buttons, progress bars, scroll bars and others. It also implements some standard managers. One other interesting feature is the possibility setting style properties from a CSS format file. Documentation ============= API reference is provided by gtk-doc annotations. If Mx is configured with the --enable-gtk-doc option, API reference is built in the docs/reference/libmx directory. Bugs ==== Issues and feature requests should be logged in the bugzilla at: http://bugzilla.clutter-project.org/ Mailing List ============ There is currently no dedicated mailing list for Mx, but general questions regarding Clutter can be directed to the Clutter mailing list. More information about Clutter can be found on the website: http://clutter-project.org/. mx-1.4.7/autogen.sh000077500000000000000000000021661201047117600141540ustar00rootroot00000000000000#! /bin/sh srcdir=`dirname $0` test -z "$srcdir" && srcdir=. olddir=`pwd` cd $srcdir INTLTOOLIZE=`which intltoolize` if test -z $INTLTOOLIZE; then echo "*** No intltoolize found ***" exit 1 else intltoolize --force --copy --automake || exit $? fi GTKDOCIZE=`which gtkdocize` if test -z $GTKDOCIZE; then echo "*** No gtk-doc support ***" echo "EXTRA_DIST =" > gtk-doc.make echo "CLEANFILES =" >> gtk-doc.make else gtkdocize || exit $? sed -e 's#) --mode=compile#) --tag=CC --mode=compile#' gtk-doc.make \ > gtk-doc.temp \ && mv gtk-doc.temp gtk-doc.make sed -e 's#) --mode=link#) --tag=CC --mode=link#' gtk-doc.make \ > gtk-doc.temp \ && mv gtk-doc.temp gtk-doc.make fi AUTORECONF=`which autoreconf` if test -z $AUTORECONF; then echo "*** No autoreconf found ***" exit 1 else ACLOCAL="${ACLOCAL-aclocal} $ACLOCAL_FLAGS" autoreconf -v --install || exit $? fi cd $olddir $srcdir/configure --disable-static --enable-maintainer-flags "$@" && \ echo "Now type 'make' to compile $PROJECT." mx-1.4.7/configure.ac000066400000000000000000000252771201047117600144510ustar00rootroot00000000000000m4_define([mx_major], [1]) m4_define([mx_minor], [4]) m4_define([mx_micro], [7]) m4_define([mx_version], [mx_major.mx_minor.mx_micro]) m4_define([mx_api_version], [mx_major.0]) m4_define([mx_api_version_am], [mx_major\_0]) # increase the interface age of 2 for each release # set to 0 if the API changes m4_define([mx_interface_age], [0]) # binary compatibility changed at interface 2, so the binary age needs to # reflect this m4_define([mx_abi_offset], [2]) m4_define([mx_binary_age], [m4_eval(100 * mx_minor + mx_micro - mx_abi_offset)]) AC_INIT([mx], [mx_version]) AC_CONFIG_MACRO_DIR([m4]) AC_CONFIG_SRCDIR([mx/mx.h]) AM_INIT_AUTOMAKE([1.11 foreign -Wno-portability no-define no-dist-gzip dist-xz]) # enable quiet ("silent") builds, if available m4_ifdef([AM_SILENT_RULES],[AM_SILENT_RULES([yes])]) AC_CONFIG_HEADERS([config.h]) AC_ISC_POSIX AC_HEADER_STDC AC_DISABLE_STATIC AC_PROG_LIBTOOL AM_PROG_CC_C_O AM_PATH_GLIB_2_0([2.28.0]) AC_CHECK_FUNCS([localtime_r]) MX_MAJOR_VERSION=mx_major MX_MINOR_VERSION=mx_minor MX_MICRO_VERSION=mx_micro MX_VERSION=mx_version MX_API_VERSION=mx_api_version MX_API_VERSION_AM=mx_api_version_am AC_SUBST(MX_MAJOR_VERSION) AC_SUBST(MX_MINOR_VERSION) AC_SUBST(MX_MICRO_VERSION) AC_SUBST(MX_VERSION) AC_SUBST(MX_API_VERSION) AC_SUBST(MX_API_VERSION_AM) m4_define([lt_current], [m4_eval(100 * mx_minor + mx_micro - mx_interface_age)]) m4_define([lt_revision], [mx_interface_age]) m4_define([lt_age], [m4_eval(mx_binary_age - mx_interface_age)]) MX_LT_CURRENT=lt_current MX_LT_REV=lt_revision MX_LT_AGE=lt_age MX_LT_VERSION="$MX_LT_CURRENT:$MX_LT_REV:$MX_LT_AGE" MX_LT_LDFLAGS="-no-undefined -version-info $MX_LT_VERSION" AC_SUBST(MX_LT_VERSION) AC_SUBST(MX_LT_LDFLAGS) dnl = Enable debug level =================================================== m4_define([debug_default], [m4_if(m4_eval(mx_minor % 2), [1], [yes], [minimum])]) AC_ARG_ENABLE([debug], [AC_HELP_STRING([--enable-debug=@<:@no/minimum/yes@:>@], [Enables on debugging @<:@default=debug_default@:>@])], [], [enable_debug=debug_default]) AS_CASE([$enable_debug], [yes], [ test "$cflags_set" = set || CFLAGS="$CFLAGS -g" MX_DEBUG_CFLAGS="-DMX_ENABLE_DEBUG" ], [no], [ MX_DEBUG_CFLAGS="-DG_DISABLE_ASSERT -DG_DISABLE_CHECKS -DG_DISABLE_CAST_CHECKS" ], [minimum], [ MX_DEBUG_CFLAGS="-DMX_ENABLE_DEBUG -DG_DISABLE_CAST_CHECKS" ], [AC_MSG_ERROR([Invalid value for --enable-debug])] ) AC_SUBST(MX_DEBUG_CFLAGS) dnl = Enable strict compiler flags ========================================= AC_ARG_ENABLE([maintainer-flags], [AC_HELP_STRING([--enable-maintainer-flags=@<:@no/yes@:>@], [Use strict compiler flags @<:@default=maintainer_flags_default@:>@])], [], [enable_maintainer_flags=no]) AS_IF([test "x$enable_maintainer_flags" = "xyes"], [ AS_COMPILER_FLAGS([MX_MAINTAINER_CFLAGS], ["-Wall -Wshadow -Wcast-align -Wno-uninitialized -Wempty-body -Wformat-security -Winit-self -Wmissing-declarations -Wredundant-decls"]) ], [ AS_COMPILER_FLAGS([MX_MAINTAINER_CFLAGS], ["-Wall"]) ] ) AC_SUBST(MX_MAINTAINER_CFLAGS) dnl = Required Packages ==================================================== MX_REQUIRES="gdk-pixbuf-2.0" CLUTTER_VERSION_REQUIRED="1.7.91" dnl = Winsys =============================================================== MX_WINSYS=x11 AC_ARG_WITH([winsys], [AC_HELP_STRING([--with-winsys=@<:@none/x11@:>@], [Select the window system backend])], [MX_WINSYS=$with_winsys]) AS_CASE([$MX_WINSYS], [x11], [ SUPPORT_X11=yes WINSYS_X11_REQUIRES="clutter-x11-1.0 >= $CLUTTER_VERSION_REQUIRED xrandr >= 1.2.0" MX_REQUIRES="$MX_REQUIRES $WINSYS_X11_REQUIRES" ], [none], [ WINSYS_NONE_REQUIRES="clutter-1.0 >= $CLUTTER_VERSION_REQUIRED" MX_REQUIRES="$MX_REQUIRES $WINSYS_NONE_REQUIRES" ], [AC_MSG_ERROR([Invalid winsys: use 'x11' or 'none'])] ) AS_IF([test "x$SUPPORT_X11" = "xyes"], [AC_DEFINE([HAVE_X11], [1], [Mx supports the X11 window system])]) AM_CONDITIONAL([HAVE_X11], [test "x$SUPPORT_X11" = "xyes"]) AC_SUBST(MX_WINSYS) dnl = Optional Packages ==================================================== AC_ARG_WITH([clutter-imcontext], [AC_HELP_STRING([--without-clutter-imcontext], [disable input method support])], [], [with_clutter_imcontext=auto]) AS_IF([test "x$with_clutter_imcontext" != xno], [PKG_CHECK_EXISTS([clutter-imcontext-0.1], [AC_DEFINE([HAVE_CLUTTER_IMCONTEXT], [1], [Define you have clutter-imcontext]) MX_REQUIRES="$MX_REQUIRES clutter-imcontext-0.1" with_clutter_imcontext=yes], [AS_IF([test "x$with_clutter_imcontext" = xyes],AC_MSG_FAILURE([Could not find clutter-imcontext. Use --without-clutter-imcontext to disable input method support.])) with_clutter_imcontext=no]] )) AC_ARG_WITH([clutter-gesture], [AC_HELP_STRING([--with-clutter-gesture], [enable gesture support])], [], [with_clutter_gesture=auto]) AS_IF([test "x$with_clutter_gesture" != xno], [PKG_CHECK_EXISTS([clutter-gesture], [AC_DEFINE([HAVE_CLUTTER_GESTURE], [1], [Define if clutter-gesture is available]) MX_REQUIRES="$MX_REQUIRES clutter-gesture" with_clutter_gesture=yes], [AS_IF([test "x$with_clutter_gesture" = xyes], AC_MSG_FAILURE([Could not find clutter-gesture. Use --without-clutter-gesture to disable gesture support.])) with_clutter_gesture=no]] )) AC_ARG_WITH([startup-notification], [AC_HELP_STRING([--without-startup-notification], [disable startup notification])], [], [with_startup_notification=auto]) AS_IF([test "x$with_startup_notification" != xno], [PKG_CHECK_EXISTS([libstartup-notification-1.0 >= 0.9], [AC_DEFINE([HAVE_STARTUP_NOTIFICATION], [1], [Define if startup notification is enabled]) MX_REQUIRES="$MX_REQUIRES libstartup-notification-1.0 >= 0.9" with_startup_notification=yes], [AS_IF([test "x$with_startup_notification" = xyes], AC_MSG_FAILURE([Could not find libstartup-notification. Use --without-startup-notification to disable startup notification support.])) with_startup_notification=no]] )) AC_ARG_WITH([dbus], [AC_HELP_STRING([--without-dbus], [disable DBus support])], [], [with_dbus=auto]) AS_IF([test "x$with_dbus" != xno], [PKG_CHECK_EXISTS([dbus-glib-1 >= 0.82], [AC_DEFINE([HAVE_DBUS], [1], [Define if DBus support is enabled]) MX_REQUIRES="$MX_REQUIRES dbus-glib-1 >= 0.82" with_dbus=yes], [AS_IF([test "x$with_debug" = xyes], AC_MSG_FAILURE([Could not find dbus-glib. Use --without-dbus to disable DBus support.])) with_dbus=no]] )) AC_ARG_WITH([glade], [AC_HELP_STRING([--with-glade], [enable glade3 support])], [], [with_glade=no]) AS_IF([test "x$with_glade" = xyes], [PKG_CHECK_EXISTS([gladeui-1.0 >= 3.4.5], [PKG_PROG_PKG_CONFIG() catalogdir=`${PKG_CONFIG} --variable=catalogdir gladeui-1.0`], [AC_MSG_FAILURE([Could not find gladeui-1.0 >= 3.4.5])]] )) AM_CONDITIONAL(WITH_GLADE, test "x$with_glade" = xyes) AC_SUBST([catalogdir]) # # Gtk+ widgets library # # If no parameter given default to yes otherwise use the enable value # --enable-widgets == yes and --disable-widgets == no AC_ARG_ENABLE([gtk-widgets], [AC_HELP_STRING([--disable-gtk-widgets], [disable building the gtk+ widgets library])], [], [enable_gtk_widgets=yes]) AS_IF([test "x$enable_gtk_widgets" = "xyes"], [PKG_CHECK_MODULES(GTK, [gtk+-2.0 >= 2.20]) GTK_CFLAGS="$GTK_CFLAGS -DGSEAL_ENABLE=1"], [] ) AM_CONDITIONAL(ENABLE_GTK_WIDGETS, test "x$enable_gtk_widgets" = xyes) dnl *************************************************************************** dnl Internationalization dnl *************************************************************************** AS_ALL_LINGUAS IT_PROG_INTLTOOL([0.35.0]) GETTEXT_PACKAGE="mx-$MX_API_VERSION" AC_SUBST(GETTEXT_PACKAGE) AC_DEFINE_UNQUOTED([GETTEXT_PACKAGE], ["$GETTEXT_PACKAGE"], [Gettext domain name]) GLIB_DEFINE_LOCALEDIR(LOCALEDIR) AM_GLIB_GNU_GETTEXT AM_PROG_LIBTOOL PKG_CHECK_MODULES(MX, [$MX_REQUIRES]) PKG_CHECK_MODULES(MX_IMAGE_CACHE, [gdk-pixbuf-2.0]) # check for gtk-doc # gtkdocize greps for ^GTK_DOC_CHECK and parses it, so you need to have # it on it's own line. m4_ifdef([GTK_DOC_CHECK], [ GTK_DOC_CHECK([1.14], [--flavour no-tmpl]) ]) GOBJECT_INTROSPECTION_CHECK([0.6.4]) AC_CONFIG_FILES([ Makefile mx.pc mx-gtk.pc data/Makefile data/glade/Makefile data/style/Makefile docs/Makefile docs/reference/Makefile docs/reference/libmx/Makefile docs/reference/libmx/version.xml docs/reference/libmx-gtk/Makefile docs/reference/libmx-gtk/version.xml mx/Makefile mx-gtk/Makefile mx/mx-version.h po/Makefile.in tests/Makefile ]) AC_OUTPUT dnl === Summary === echo "" echo " Mx Toolkit - $VERSION" echo "" echo " Features:" echo " GTK+ widgets: $enable_gtk_widgets" echo " Clutter-Imcontext: $with_clutter_imcontext" echo " Clutter-Gesture: $with_clutter_gesture" echo " Startup Notification: $with_startup_notification" echo " Dbus: $with_dbus" echo " Windowing system: $MX_WINSYS" echo "" echo " Documentation:" echo " Build API Reference: $enable_gtk_doc" echo "" echo " Extra:" echo " Build introspection data: $enable_introspection" echo "" mx-1.4.7/data/000077500000000000000000000000001201047117600130575ustar00rootroot00000000000000mx-1.4.7/data/Makefile.am000066400000000000000000000001241201047117600151100ustar00rootroot00000000000000SUBDIRS=style if WITH_GLADE SUBDIRS += glade endif -include $(top_srcdir)/git.mk mx-1.4.7/data/glade/000077500000000000000000000000001201047117600141335ustar00rootroot00000000000000mx-1.4.7/data/glade/Makefile.am000066400000000000000000000001411201047117600161630ustar00rootroot00000000000000gladedir = $(catalogdir) dist_glade_DATA = \ mx-gtk-catalog.xml -include $(top_srcdir)/git.mk mx-1.4.7/data/glade/mx-gtk-catalog.xml000066400000000000000000000012201201047117600174670ustar00rootroot00000000000000 mx-gtk mx-1.4.7/data/style/000077500000000000000000000000001201047117600142175ustar00rootroot00000000000000mx-1.4.7/data/style/Makefile.am000066400000000000000000000060561201047117600162620ustar00rootroot00000000000000styledir = ${datadir}/mx/style dist_style_DATA = \ button-disabled.png \ button-active.png \ button-hover.png \ button-focus.png \ button.png \ checkbox-background.png \ checkbox-background-active.png \ checkbox-background-disabled.png \ checkbox-background-focus.png \ checkbox-background-hover.png \ checkbox-check.png \ checkbox-check-disabled.png \ combobox.png \ combobox-focus.png \ combobox-hover.png \ combobox-active.png \ combobox-disabled.png \ combobox-marker.png \ combobox-marker-hover.png \ combobox-marker-active.png \ combobox-marker-disabled.png \ close.png \ close-hover.png \ default.css \ entry.png \ entry-focus.png \ entry-active.png \ entry-disabled.png \ entry-hover.png \ expander-arrow-down-hover.png \ expander-arrow-down-disabled.png \ expander-arrow-down.png \ expander-arrow-up-hover.png \ expander-arrow-up-disabled.png \ expander-arrow-up.png \ expander-closed.png \ expander-closed-hover.png \ expander-closed-active.png \ expander-closed-disabled.png \ expander-open.png \ expander-open-hover.png \ expander-open-active.png \ expander-open-disabled.png \ frame-background.png \ resize-grip.png \ scroll-hbackground.png \ scroll-hbackground-disabled.png \ scroll-vbackground.png \ scroll-vbackground-disabled.png \ scroll-button-down-active.png \ scroll-button-down-disabled.png \ scroll-button-down-hover.png \ scroll-button-down.png \ scroll-button-up-active.png \ scroll-button-up-disabled.png \ scroll-button-up-hover.png \ scroll-button-up.png \ scroll-button-left-active.png \ scroll-button-left-disabled.png \ scroll-button-left-hover.png \ scroll-button-left.png \ scroll-button-right-active.png \ scroll-button-right-disabled.png \ scroll-button-right-hover.png \ scroll-button-right.png \ scroll-hhandle.png \ scroll-hhandle-hover.png \ scroll-hhandle-active.png \ scroll-hhandle-disabled.png \ scroll-vhandle.png \ scroll-vhandle-hover.png \ scroll-vhandle-active.png \ scroll-vhandle-disabled.png \ slider-background-active.png \ slider-background-disabled.png \ slider-background-focus.png \ slider-background-hover.png \ slider-background.png \ slider-handle-active.png \ slider-handle-disabled.png \ slider-handle-focus.png \ slider-handle-hover.png \ slider-handle.png \ spinner.png \ menu-item.png \ menu.png \ pathbar-button-active.png \ pathbar-button-disabled.png \ pathbar-button-focus.png \ pathbar-button-hover.png \ pathbar-button.png \ pathbar-button-last-active.png \ pathbar-button-last-disabled.png \ pathbar-button-last-focus.png \ pathbar-button-last-hover.png \ pathbar-button-last.png \ progress-bar-background.png \ progress-bar-bar.png \ toggle-background-active.png \ toggle-background-disabled.png \ toggle-background.png \ toggle-handle.png \ toggle-handle-active.png \ toggle-handle-focus.png \ toggle-handle-hover.png \ toggle-handle-disabled.png \ toolbar-background.png \ tooltip-background.png -include $(top_srcdir)/git.mk mx-1.4.7/data/style/button-active.png000066400000000000000000000010711201047117600175100ustar00rootroot00000000000000PNG  IHDR[kMtEXtSoftwareAdobe ImageReadyqe<IDATxJQ3nhXB."ht .#jAܤ2g )ppg^9f  g V\`Nb![jj#34D"`?2`2pH߹LF*AUUPsTJ8QxG.ٶmy4iu  k>NFaP^8dG$x<.Wd (\tH/) $ $ $(".=!Kls'.]m;!u^FMje;bӛlyja͸e1jDٖx]H$FqL0٣}"R\v:hRPF?iB\Y]V몪vM'XTnfJb^a{pHo^1sB6ݞڈZ# ǧs6IENDB`mx-1.4.7/data/style/button-checked.png000066400000000000000000000010651201047117600176260ustar00rootroot00000000000000PNG  IHDRaltEXtSoftwareAdobe ImageReadyqe<IDATx왿JAg` !6EDш!6y-Mc* X-,|1 "FQf#^XG]wcj cl(&- 7R,[w3\X"I)y} qM8Ny{~҉ 5ͨZg,,g ,;kZB 7(%蜵`I"?$$$$$$Äi?it?%|'HBL ͣ$4R(:!NYC'DJV0/;B%%_$51aSRڀrW`' m9pqz\)8z6'hڞ'l&,j= x{gbNw83azX!NAT:d}Awl,:AX`60+ ,H2!p^ahԔK|&2ʧ<mF@P>5M++l٣4 m4^P6a@^<$.}dʖ[>e?س1٘lF0lg#;ɖwmW`[goRJ/j1!8/}v$YT9h+!usNt]IvX,^~L)5P[2*9IENDB`mx-1.4.7/data/style/button-focus.png000066400000000000000000000012751201047117600173620ustar00rootroot00000000000000PNG  IHDR[kMtEXtSoftwareAdobe ImageReadyqe<_IDATxϊAƫ{z$0;Yě}O >9HQ!ă77]]̟/gv!D RM:וpV%pW|JQ2eӹW^I)!͑v$0N JhۺWv3iP C}9jZ+<}qH0|l6;''g&Fy^~[O;}0t8X0B <>GkM%}[zj<a )V/qD  MD,,_l;ͬ|>B6oٰwakl YI80N~lf(Ssu6K$glk}vvqg'1pfsfߙ͂GBs_Jlf#;4fusdi'l^nj>2&&X,ela(GrtT)%*#fqڸ{sNTXvh<} a16g0?㷃n3oM)0{"kݬ´)f9D6F \8÷`SPuAIENDB`mx-1.4.7/data/style/button-hover.png000066400000000000000000000011351201047117600173610ustar00rootroot00000000000000PNG  IHDR[kMtEXtSoftwareAdobe ImageReadyqe<IDATxKA𷳳Ht":{?CAxXA:?H7A("q41;6fMnA/<cfgbT*WE/o 4d2kH14-JnG\Վ\v/f[:HXx yLӁJri~.Apy8>M&֦ nSɲeuъ)pp80 Nuu[@/Uս\.wH$sZ\m>oZXzA-ivcjOd_6)fuiODOdKv4΃lvIENDB`mx-1.4.7/data/style/checkbox-background-active.png000066400000000000000000000006631201047117600221060ustar00rootroot00000000000000PNG  IHDR]tEXtSoftwareAdobe ImageReadyqe<UIDATx̔j@gu@D^brl^z }> cR !Ѓ$$7M&vFjI%ú;# Gߢ/"Z7/qs8 IR#af,`XTWUu6 Lc,,m2&g|]F#ziZ`ʦi:%vͶjQP$# -Ȳ\yN$_<H*:BX%(K 0jUݝ2!8!I(BBR&":wa<7-)id2Y{M p+)Dw+%ӳvE;IENDB`mx-1.4.7/data/style/checkbox-background-disabled.png000066400000000000000000000006651201047117600224040ustar00rootroot00000000000000PNG  IHDR]tEXtSoftwareAdobe ImageReadyqe<WIDATx̔n@XQlEMwE-M(.%@gHizm/ɒ;f|.|<ڡCl61,.eǁN(%EmE׋ɲcR @GQPS7 -Ѕ$0d?( ;b:+ax~3c BF˽,M _lgP4o))wIENDB`mx-1.4.7/data/style/checkbox-background-hover.png000066400000000000000000000006631201047117600217560ustar00rootroot00000000000000PNG  IHDR]tEXtSoftwareAdobe ImageReadyqe<UIDATx̔j@gu@D^brl^z }> cR !Ѓ$$7M&vFjI%ú;# Gߢ/"Z7/qs8 IR#af,`XTWUu6 Lc,,m2&g|]F#ziZ`ʦi:%vͶjQP$# -Ȳ\yN$_<H*:BX%(K 0jUݝ2!8!I(BBR&":wa<7-)id2Y{M p+)Dw+%ӳvE;IENDB`mx-1.4.7/data/style/checkbox-background.png000066400000000000000000000006631201047117600206350ustar00rootroot00000000000000PNG  IHDR]tEXtSoftwareAdobe ImageReadyqe<UIDATx̔j@gu@D^brl^z }> cR !Ѓ$$7M&vFjI%ú;# Gߢ/"Z7/qs8 IR#af,`XTWUu6 Lc,,m2&g|]F#ziZ`ʦi:%vͶjQP$# -Ȳ\yN$_<H*:BX%(K 0jUݝ2!8!I(BBR&":wa<7-)id2Y{M p+)Dw+%ӳvE;IENDB`mx-1.4.7/data/style/checkbox-check-disabled.png000066400000000000000000000005521201047117600213350ustar00rootroot00000000000000PNG  IHDR]tEXtSoftwareAdobe ImageReadyqe< IDATxb?5cee%EQ"ftj+G 2Y8 JJK .K؀8^b h Eħaf& X BRϢ߿alW FHH@A-kCv> BDő ~YМ y3(_I$E@xM d*|vC51(WBʂC|@|GIЕGT3 ABq**IENDB`mx-1.4.7/data/style/checkbox-check.png000066400000000000000000000005461201047117600175730ustar00rootroot00000000000000PNG  IHDR]tEXtSoftwareAdobe ImageReadyqe<IDATxb?5cee%EQ"6tj'o@29x ;q&/)-a/.@l$vl˟?1j%X.OA-@, )@ sbc$u@< % cwq)R!b dF0 E E2d2l~gAs/ ΁@@, n41! ˀ@P/@| >Xu]yD5 nLHIENDB`mx-1.4.7/data/style/close-hover.png000066400000000000000000000013771201047117600171630ustar00rootroot00000000000000PNG  IHDR937tEXtSoftwareAdobe ImageReadyqe<IDATxM9a3JK;HCH)YZ66>-+JILIq͌5뾞A3EQ&`0|}}Q(ezwiJq8ZRR4 F|>v8Cp(Znt`W,l6q"S/ i  RY8ųT]UV5@ŧiImh6!> '8ZN5!Z`&VYUVUe~/I)B SxA@˥e^qL&bNSz˲b hJ/Pdyv4  NTi<IENDB`mx-1.4.7/data/style/close.png000066400000000000000000000011671201047117600160370ustar00rootroot00000000000000PNG  IHDR937tEXtSoftwareAdobe ImageReadyqe<IDATx9@`E,﫰OmaaJ(Ga!W >曨 IMhxAVd%+YJVZ-K"z/! un7EDfyrXn;~M#D1gh4ʦ\\1fP(\.oӽvFt:F#EGhV[\[dRnٚ^Uz\|&,CTkh^Y|zt:U)J&p].֝t:YEIlVE 4JAR bdpboL&qQ8B&r1"j LK+xvAx hR:d2x  UڥKIs& 4&]YךP~%ɗ_t Mae*Q{=`PV7sBH؋uxVCIaEJt:Ve Fq?\D۶vf0|9N\`fa6C"s޸ah)urk$E39D$?(c ݚZtۉ~ĘcB0'LtBO$ZNDcDcL4&>QQ4Y t֥H+Kɫ.Jh'Gl*"ksu]67a@/پOϱ|`0f)`]t]@e>V樂Ou@:MGI|L&{.%Yu:#|x?!xǴ\.|GQh\am4+ﳊKQk(c\zI=0Ά88 wwYƘ3T*srR5r1#h!% 21tmϊU\/i%nMOkA;z뎆 n IoGH´;%rq42Lg8:=upFsFws]JAά>-:]nh/on:~69hs:}߇x̤6|NO16QwRRJ)qS?*ZzyGzq9e4}pi*l|o Nw']4Զ ^2kN,8]ѫΎ`iMpIENDB`mx-1.4.7/data/style/combobox-hover.png000066400000000000000000000010751201047117600176610ustar00rootroot00000000000000PNG  IHDRZᩨstEXtSoftwareAdobe ImageReadyqe<IDATx1k@%RK Y R:HE:B?@~~q(tv/ ݲ nK1X\zObqpi#3zh4HTR! jJ+~:E1+B]7$U՜D~* z6%DBd2VE,:sV՞\TX,G̦S 1rMfy-.P9' mRGLc` L䤦(&6wDA11ф6b"9&huQ# bFBGsx."[LVоqឮ3`7c1_;{ӻ$ᜣRc<&.w` bGV\.qjRCD1MbirS G,K زf:@`|g !:tV]sbi락<-=OIGIENDB`mx-1.4.7/data/style/combobox-marker-active.png000066400000000000000000000062401201047117600212670ustar00rootroot00000000000000PNG  IHDR i pHYs   OiCCPPhotoshop ICC profilexڝSgTS=BKKoR RB&*! J!QEEȠQ, !{kּ> H3Q5 B.@ $pd!s#~<<+"x M0B\t8K@zB@F&S`cbP-`'{[! eDh;VEX0fK9-0IWfH  0Q){`##xFW<+*x<$9E[-qWW.(I+6aa@.y24x6_-"bbϫp@t~,/;m%h^ uf@Wp~<5j>{-]cK'Xto(hw?G%fIq^D$.Tʳ?D*A, `6B$BB dr`)B(Ͱ*`/@4Qhp.U=pa( Aa!ڈbX#!H$ ɈQ"K5H1RT UH=r9\F;2G1Q= C7F dt1r=6Ыhڏ>C03l0.B8, c˱" VcϱwE 6wB aAHXLXNH $4 7 Q'"K&b21XH,#/{C7$C2'ITFnR#,4H#dk9, +ȅ3![ b@qS(RjJ4e2AURݨT5ZBRQ4u9̓IKhhitݕNWGw Ljg(gwLӋT071oUX**| J&*/Tު UUT^S}FU3S ԖUPSSg;goT?~YYLOCQ_ cx,!k u5&|v*=9C3J3WRf?qtN (~))4L1e\kXHQG6EYAJ'\'GgSSݧ M=:.kDwn^Loy}/TmG X $ <5qo</QC]@Caaᄑ.ȽJtq]zۯ6iܟ4)Y3sCQ? 0k߬~OCOg#/c/Wװwa>>r><72Y_7ȷOo_C#dz%gA[z|!?:eAAA!h쐭!ΑiP~aa~ 'W?pX15wCsDDDޛg1O9-J5*>.j<74?.fYXXIlK9.*6nl {/]py.,:@LN8A*%w% yg"/6шC\*NH*Mz쑼5y$3,幄'L Lݛ:v m2=:1qB!Mggfvˬen/kY- BTZ(*geWf͉9+̳ې7ᒶKW-X潬j9(xoʿܔĹdff-[n ڴ VE/(ۻCɾUUMfeI?m]Nmq#׹=TR+Gw- 6 U#pDy  :v{vg/jBFS[b[O>zG499?rCd&ˮ/~јѡ򗓿m|x31^VwwO| (hSЧc3- cHRMz%u0`:o_FIDATxڬn@Fώ 64QKRǻEj$ Ah"alv(jj53{f4[@z=d( 4%cVq$ EYYkɲj|>g6X,XK,#sev2\2xd:! C H3Q5 B.@ $pd!s#~<<+"x M0B\t8K@zB@F&S`cbP-`'{[! eDh;VEX0fK9-0IWfH  0Q){`##xFW<+*x<$9E[-qWW.(I+6aa@.y24x6_-"bbϫp@t~,/;m%h^ uf@Wp~<5j>{-]cK'Xto(hw?G%fIq^D$.Tʳ?D*A, `6B$BB dr`)B(Ͱ*`/@4Qhp.U=pa( Aa!ڈbX#!H$ ɈQ"K5H1RT UH=r9\F;2G1Q= C7F dt1r=6Ыhڏ>C03l0.B8, c˱" VcϱwE 6wB aAHXLXNH $4 7 Q'"K&b21XH,#/{C7$C2'ITFnR#,4H#dk9, +ȅ3![ b@qS(RjJ4e2AURݨT5ZBRQ4u9̓IKhhitݕNWGw Ljg(gwLӋT071oUX**| J&*/Tު UUT^S}FU3S ԖUPSSg;goT?~YYLOCQ_ cx,!k u5&|v*=9C3J3WRf?qtN (~))4L1e\kXHQG6EYAJ'\'GgSSݧ M=:.kDwn^Loy}/TmG X $ <5qo</QC]@Caaᄑ.ȽJtq]zۯ6iܟ4)Y3sCQ? 0k߬~OCOg#/c/Wװwa>>r><72Y_7ȷOo_C#dz%gA[z|!?:eAAA!h쐭!ΑiP~aa~ 'W?pX15wCsDDDޛg1O9-J5*>.j<74?.fYXXIlK9.*6nl {/]py.,:@LN8A*%w% yg"/6шC\*NH*Mz쑼5y$3,幄'L Lݛ:v m2=:1qB!Mggfvˬen/kY- BTZ(*geWf͉9+̳ې7ᒶKW-X潬j9(xoʿܔĹdff-[n ڴ VE/(ۻCɾUUMfeI?m]Nmq#׹=TR+Gw- 6 U#pDy  :v{vg/jBFS[b[O>zG499?rCd&ˮ/~јѡ򗓿m|x31^VwwO| (hSЧc3- cHRMz%u0`:o_FIDATxڬn@Fώ 64QKRǻEj$ Ah"alv(jj53{f4[@z=d( 4%cVq$ EYYkɲj|>g6X,XK,#sev2\2xd:! C H3Q5 B.@ $pd!s#~<<+"x M0B\t8K@zB@F&S`cbP-`'{[! eDh;VEX0fK9-0IWfH  0Q){`##xFW<+*x<$9E[-qWW.(I+6aa@.y24x6_-"bbϫp@t~,/;m%h^ uf@Wp~<5j>{-]cK'Xto(hw?G%fIq^D$.Tʳ?D*A, `6B$BB dr`)B(Ͱ*`/@4Qhp.U=pa( Aa!ڈbX#!H$ ɈQ"K5H1RT UH=r9\F;2G1Q= C7F dt1r=6Ыhڏ>C03l0.B8, c˱" VcϱwE 6wB aAHXLXNH $4 7 Q'"K&b21XH,#/{C7$C2'ITFnR#,4H#dk9, +ȅ3![ b@qS(RjJ4e2AURݨT5ZBRQ4u9̓IKhhitݕNWGw Ljg(gwLӋT071oUX**| J&*/Tު UUT^S}FU3S ԖUPSSg;goT?~YYLOCQ_ cx,!k u5&|v*=9C3J3WRf?qtN (~))4L1e\kXHQG6EYAJ'\'GgSSݧ M=:.kDwn^Loy}/TmG X $ <5qo</QC]@Caaᄑ.ȽJtq]zۯ6iܟ4)Y3sCQ? 0k߬~OCOg#/c/Wװwa>>r><72Y_7ȷOo_C#dz%gA[z|!?:eAAA!h쐭!ΑiP~aa~ 'W?pX15wCsDDDޛg1O9-J5*>.j<74?.fYXXIlK9.*6nl {/]py.,:@LN8A*%w% yg"/6шC\*NH*Mz쑼5y$3,幄'L Lݛ:v m2=:1qB!Mggfvˬen/kY- BTZ(*geWf͉9+̳ې7ᒶKW-X潬j9(xoʿܔĹdff-[n ڴ VE/(ۻCɾUUMfeI?m]Nmq#׹=TR+Gw- 6 U#pDy  :v{vg/jBFS[b[O>zG499?rCd&ˮ/~јѡ򗓿m|x31^VwwO| (hSЧc3- cHRMz%u0`:o_FIDATxڬn@Fώ 64QKRǻEj$ Ah"alv(jj53{f4[@z=d( 4%cVq$ EYYkɲj|>g6X,XK,#sev2\2xd:! C H3Q5 B.@ $pd!s#~<<+"x M0B\t8K@zB@F&S`cbP-`'{[! eDh;VEX0fK9-0IWfH  0Q){`##xFW<+*x<$9E[-qWW.(I+6aa@.y24x6_-"bbϫp@t~,/;m%h^ uf@Wp~<5j>{-]cK'Xto(hw?G%fIq^D$.Tʳ?D*A, `6B$BB dr`)B(Ͱ*`/@4Qhp.U=pa( Aa!ڈbX#!H$ ɈQ"K5H1RT UH=r9\F;2G1Q= C7F dt1r=6Ыhڏ>C03l0.B8, c˱" VcϱwE 6wB aAHXLXNH $4 7 Q'"K&b21XH,#/{C7$C2'ITFnR#,4H#dk9, +ȅ3![ b@qS(RjJ4e2AURݨT5ZBRQ4u9̓IKhhitݕNWGw Ljg(gwLӋT071oUX**| J&*/Tު UUT^S}FU3S ԖUPSSg;goT?~YYLOCQ_ cx,!k u5&|v*=9C3J3WRf?qtN (~))4L1e\kXHQG6EYAJ'\'GgSSݧ M=:.kDwn^Loy}/TmG X $ <5qo</QC]@Caaᄑ.ȽJtq]zۯ6iܟ4)Y3sCQ? 0k߬~OCOg#/c/Wװwa>>r><72Y_7ȷOo_C#dz%gA[z|!?:eAAA!h쐭!ΑiP~aa~ 'W?pX15wCsDDDޛg1O9-J5*>.j<74?.fYXXIlK9.*6nl {/]py.,:@LN8A*%w% yg"/6шC\*NH*Mz쑼5y$3,幄'L Lݛ:v m2=:1qB!Mggfvˬen/kY- BTZ(*geWf͉9+̳ې7ᒶKW-X潬j9(xoʿܔĹdff-[n ڴ VE/(ۻCɾUUMfeI?m]Nmq#׹=TR+Gw- 6 U#pDy  :v{vg/jBFS[b[O>zG499?rCd&ˮ/~јѡ򗓿m|x31^VwwO| (hSЧc3- cHRMz%u0`:o_FIDATxڬn@Fώ 64QKRǻEj$ Ah"alv(jj53{f4[@z=d( 4%cVq$ EYYkɲj|>g6X,XK,#sev2\2xd:! C@]!$"3o[9%en!K"D @"#D @b"D @X̌6o'"J]j"rvf֫s撶U ઔ\JP8PSJ_?{[s檄rIENDB`mx-1.4.7/data/style/combobox.png000066400000000000000000000011011201047117600165260ustar00rootroot00000000000000PNG  IHDRZᩨstEXtSoftwareAdobe ImageReadyqe<IDATx욱jPϽ*""t(hQBCߠ EAbn}ts/ %55EJLs$:!7L_~{v JEb.oY@eV˦;)eIa^!H{nw0\77lY\*|t]gZ!dYz=vi7}Jnr8q`֗NEFNsKQJf0m St`&'4 MxIcon#mx-expander-arrow-closed { padding: 0; -mx-content-image: url('expander-arrow-up.png'); } MxExpander > MxIcon#mx-expander-arrow-closed:hover { -mx-content-image: url('expander-arrow-up-hover.png'); } MxExpander > MxIcon#mx-expander-arrow-closed:disabled { -mx-content-image: url('expander-arrow-up-disabled.png'); } MxExpander > MxIcon#mx-expander-arrow-open { -mx-content-image: url('expander-arrow-down.png'); } MxExpander > MxIcon#mx-expander-arrow-open:hover { -mx-content-image: url('expander-arrow-down-hover.png'); } MxExpander > MxIcon#mx-expander-arrow-open:disabled { -mx-content-image: url('expander-arrow-down-disabled.png'); } MxTooltip { color: white; padding: 4 10 12 10; border-image: url('tooltip-background.png') 4 11 12 11; font-size: 15; } MxProgressBar { border-image: url('progress-bar-background.png') 4 4 6 4; } MxProgressBar MxProgressBarFill { border-image: url('progress-bar-bar.png') 3; height: 16; } MxComboBox { padding: 5 13 5 13; border-image: url('combobox.png') 4 32 4 4; -mx-marker-image: url('combobox-marker.png'); -mx-spacing: 13; } MxComboBox MxIcon { -mx-icon-size: 16; } MxComboBox:hover { border-image: url('combobox-hover.png') 4 32 4 4; -mx-marker-image: url('combobox-marker-hover.png'); } MxComboBox:focus { border-image: url('combobox-focus.png') 4 32 4 4; -mx-marker-image: url('combobox-marker-hover.png'); } MxComboBox:active { border-image: url('combobox-active.png') 4 32 4 4; -mx-marker-image: url('combobox-marker-active.png'); } MxComboBox:disabled { border-image: url('combobox-disabled.png') 4 32 4 4; -mx-marker-image: url('combobox-marker-disabled.png'); color: #adaead; } MxMenu { padding: 3 3 5 3; border-image: url('menu.png') 5; } MxMenu > MxButton { padding: 3; border-image: none; -mx-icon-size: 16; } MxMenu > MxButton:hover, MxMenu > MxButton:focus, MxMenu > MxButton:active { border-image: url('menu-item.png') 2; padding: 3; } MxToggle { background-image: url("toggle-background.png"); } MxToggle:disabled { background-image: url("toggle-background-disabled.png"); } MxToggle:checked { background-image: url("toggle-background-active.png"); } MxToggleHandle { background-image: url("toggle-handle.png"); } MxToggleHandle:active { background-image: url("toggle-handle-active.png"); } MxToggleHandle:focus { background-image: url("toggle-handle-focus.png"); } MxToggleHandle:hover { background-image: url("toggle-handle-hover.png"); } MxToggleHandle:disabled { background-image: url("toggle-handle-disabled.png"); } MxToolbar { border-image: url("toolbar-background.png") 0; padding: 16; } MxToolbar MxButton#close-button { -mx-icon-name: window-close; -mx-icon-size: 16; padding: 5 7 7 7; } MxToolbar MxButton#close-button:hover { -mx-icon-size: 16; } MxToolbar MxButton#close-button:active { -mx-icon-size: 16; padding: 5 7; } MxSlider { -mx-trough-height: 6; -mx-handle-height: 16; } MxSlider *#trough-background { border-image: url('slider-background.png') 2 3 3 3; } MxSlider *#trough-background:disabled { border-image: url('slider-background-disabled.png') 2 3 3 3; } MxSlider *#fill { } MxSlider > MxButton { border-image: url("slider-handle.png") 4 5 6 5; } MxSlider > MxButton:hover { border-image: url("slider-handle-hover.png") 4 5 6 5; } MxSlider > MxButton:active { border-image: url("slider-handle-active.png") 4 5 6 5; } MxSlider > MxButton:disabled { border-image: url("slider-handle-disabled.png") 4 5 6 5; } MxPathBar { -mx-overlap: 16; } MxPathBar > MxEntry { padding: 4 10 4 24; } MxPathBarButton { border-image: url('pathbar-button.png') 3 14 4 5; padding: 4 24; } MxPathBarButton:hover { border-image: url('pathbar-button-hover.png') 3 14 4 5; padding: 4 24; } MxPathBarButton:active { border-image: url('pathbar-button-active.png') 4 13 4 4; padding: 4 24; } MxPathBarButton:focus { border-image: url('pathbar-button-focus.png') 4 14 6 5; padding: 4 24; } MxPathBarButton:disabled { border-image: url('pathbar-button-disabled.png') 4 14 6 5; padding: 4 24; } MxPathBarButton.End { border-image: url('pathbar-button-last.png') 5; } MxPathBarButton.End:hover { border-image: url('pathbar-button-last-hover.png') 5; } MxPathBarButton.End:active { border-image: url('pathbar-button-last-active.png') 5; } MxPathBarButton.End:focus { border-image: url('pathbar-button-last-focus.png') 5; } MxPathBarButton.End:disabled { border-image: url('pathbar-button-last-disabled.png') 5; } MxButton.check-box { padding: 9; border-image: url('checkbox-background.png') 5; } MxButton.check-box:focus { border-image: url('checkbox-background-focus.png') 5; } MxButton.check-box:hover, MxButton.check-box:active { padding: 9; border-image: url('checkbox-background-hover.png') 5; } MxButton.check-box:disabled { padding: 9; border-image: url('checkbox-background-disabled.png') 5; } MxButton.check-box:checked { padding: 9; background-image: url('checkbox-check.png'); border-image: url('checkbox-background.png') 5; } MxDialog { padding: 20; -mx-spacing: 12; background-color: #000000a0; } MxDialog > MxFrame.MxDialogBackground { padding: 20; border-image: url('frame-background.png') 8 9 10; } MxDialog > MxBoxLayout.MxDialogButtonBox { -mx-spacing: 6; } MxSpinner { -mx-spinner-image: url('spinner.png'); -mx-spinner-frames: 24; -mx-spinner-animation-duration: 800; } mx-1.4.7/data/style/entry-active.png000066400000000000000000000010241201047117600173340ustar00rootroot00000000000000PNG  IHDRZᩨstEXtSoftwareAdobe ImageReadyqe<IDATx욽N`O)a`V0x].@z^$6!ATh0:R'yGZjm;]n z vqNO8wk&e='bhJ%J`1Պ\ץhDUe?iZeQ2$UUIQX y(ϋKs86/z\^ef/ËGq: ΃fz&jrXxC@]D/;XCaP?} @u`&=5@4DhކxD8h0>өdDf@tZ}zi>ҞCqvɲa^oR)X,T O"4MVM*Jï 4ms]{yrM%?K||~ IENDB`mx-1.4.7/data/style/entry-disabled.png000066400000000000000000000010171201047117600176320ustar00rootroot00000000000000PNG  IHDRZᩨstEXtSoftwareAdobe ImageReadyqe<IDATx욱jPOLLL1IASꦅ(A]:)9)tmҥj+&=7`i׫7z# q])ks˹ڭ ÿ_9>BIM%hmS\BVA@Ys2ѪY8J%R$I qLeQV.unVEJ4MK$Ctv"£p:͚iGh4dQPR8exZu!\ʲC_vv &(ՁDL4&P !2$G0 _S6m& dp\.N4Nz=9Y״X,h28a*t]džyfAw@XG}kۣ7иk u#'!d;$|/1kwbK#IENDB`mx-1.4.7/data/style/entry-focus.png000066400000000000000000000010441201047117600172020ustar00rootroot00000000000000PNG  IHDRZᩨstEXtSoftwareAdobe ImageReadyqe<IDATx욽j`O4?H̐EA7Kҥ+*ev)A:t$j ='m }%ӓO8{ݧl\~yxAE<aIS&kZ.y-W .~V"hf bٔfIQXfj%u]/_yT|>Oj"D'=Lh8Fg6rq\.Gq()e-*b rzKqF#"OH蝢Ctz@ufu hlﰽ;0P ڎl`/>$$t(/m$ڲ|n~R@${CqUGxN4͐-.]ץz%l97^2 ,Qha[jvPwDŽDx4GղIENDB`mx-1.4.7/data/style/entry-hover.png000066400000000000000000000010241201047117600172040ustar00rootroot00000000000000PNG  IHDRZᩨstEXtSoftwareAdobe ImageReadyqe<IDATx욽N`O)a`V0x].@z^$6!ATh0:R'yGZjm;]n z vqNO8wk&e='bhJ%J`1Պ\ץhDUe?iZeQ2$UUIQX y(ϋKs86/z\^ef/ËGq: ΃fz&jrXxC@]D/;XCaP?} @u`&=5@4DhކxD8h0>өdDf@tZ}zi>ҞCqvɲa^oR)X,T O"4MVM*Jï 4ms]{yrM%?K||~ IENDB`mx-1.4.7/data/style/entry.png000066400000000000000000000010121201047117600160600ustar00rootroot00000000000000PNG  IHDRZᩨstEXtSoftwareAdobe ImageReadyqe<IDATxڽN`S(-D iUa($Ÿx^G +`6:` qp`5$*MGez䤿ӓ6tQ;EK\7\gzf\\|!fQrlW*fH$fCd2t&/4 8NIu4 j } Eqw_ZR>'4d@CKFsٿ6s\e۠b)lkv*E,dG`\ď0ѣ?@G#ht4Ah@#4C|1ڲ||@X.6O!-K(1~O^@#jO%o5|kIENDB`mx-1.4.7/data/style/expander-arrow-down-active.png000066400000000000000000000003531201047117600221020ustar00rootroot00000000000000PNG  IHDR.tEXtSoftwareAdobe ImageReadyqe<IDATxb?5I31 CYH0k欁~'b K@/ >EeeRcP3璛ҁ,A .$~b ~ ?PhxRqb$ HKX@1). ;+WaBIENDB`mx-1.4.7/data/style/expander-arrow-down-disabled.png000066400000000000000000000003651201047117600224010ustar00rootroot00000000000000PNG  IHDR.tEXtSoftwareAdobe ImageReadyqe<IDATxb?5ĉh,ii$i5sm؅,aPQ CwP_ǁ"5Ij ?C;)Ix?jhxR?J ~ː, 9IPM|ٔl7|K).]RIENDB`mx-1.4.7/data/style/expander-arrow-down-focus.png000066400000000000000000000003551201047117600217500ustar00rootroot00000000000000PNG  IHDR.tEXtSoftwareAdobe ImageReadyqe<IDATxb?5ĉ31 CYH0k欁~'b K@/ >EeeRcP3璛ҁ,A .$~b ~ ?PhxRqb$ HKX@1)1nR@(` \IENDB`mx-1.4.7/data/style/expander-arrow-down-hover.png000066400000000000000000000003551201047117600217540ustar00rootroot00000000000000PNG  IHDR.tEXtSoftwareAdobe ImageReadyqe<IDATxb?5I31 CYH0k欁~'b K@/ >EeeRcP3璛ҁ,A .$~b ~ ?PhxRqb$ HKX@1)1nR?'rIENDB`mx-1.4.7/data/style/expander-arrow-down.png000066400000000000000000000003551201047117600206330ustar00rootroot00000000000000PNG  IHDR.tEXtSoftwareAdobe ImageReadyqe<IDATxb?5ĉ31 CYH0k欁~'b K@/ >EeeRcP3璛ҁ,A .$~b ~ ?PhxRqb$ HKX@1)1nR@(` \IENDB`mx-1.4.7/data/style/expander-arrow-up-active.png000066400000000000000000000003721201047117600215600ustar00rootroot00000000000000PNG  IHDR.tEXtSoftwareAdobe ImageReadyqe<IDATxb?5ĉ31P4Ϛ9$rj .ـ8x#P;L$P;R;x;P;I1q n4= ɉw 5d(4# eS(U8#P@! UTԖIENDB`mx-1.4.7/data/style/expander-arrow-up-disabled.png000066400000000000000000000003701201047117600220520ustar00rootroot00000000000000PNG  IHDR.tEXtSoftwareAdobe ImageReadyqe<IDATxb?5ĉhhb(KZzъg͜EKY8 R( ZP,k5TSX% 5Xڱ @lF$%7p;@=$)d5J) |e@OZ9 Q@! zT}IENDB`mx-1.4.7/data/style/expander-arrow-up-focus.png000066400000000000000000000003721201047117600214240ustar00rootroot00000000000000PNG  IHDR.tEXtSoftwareAdobe ImageReadyqe<IDATxb?5ĉ31P4Ϛ9$rj .ـ8x#P;L$P;R;x;P;I1q n4= ɉw 5d(4# eS(U8#P@! UTԖIENDB`mx-1.4.7/data/style/expander-arrow-up-hover.png000066400000000000000000000003721201047117600214300ustar00rootroot00000000000000PNG  IHDR.tEXtSoftwareAdobe ImageReadyqe<IDATxb?5ĉ31P4Ϛ9$rj .ـ8x#P;L$P;R;x;P;I1q n4= ɉw 5d(4# eS(U8#P@! UTԖIENDB`mx-1.4.7/data/style/expander-arrow-up.png000066400000000000000000000003711201047117600203060ustar00rootroot00000000000000PNG  IHDR.tEXtSoftwareAdobe ImageReadyqe<IDATxb?5I31P4Ϛ9$rj .ـ8x#P;L$P;R;x;P;I1q n4= ɉw 5d(4# eS(U8#P@a2)@\WjIENDB`mx-1.4.7/data/style/expander-closed-active.png000066400000000000000000000011521201047117600212520ustar00rootroot00000000000000PNG  IHDR J1 tEXtSoftwareAdobe ImageReadyqe< IDATxORQsw* 0bZ Ȍw3->/2W7[h2nlhHh5Wܵ>/Y/ tpellMDZjOe/,2~S4vV}i4Jb"s'~d=W._j/\/vqGa~zFωGG& C1;ұ]KϵUR|>ahw;o]#S@ vb vb\%"vVan Ď~C;`_IY.{.ݱ[8^KfٳLJvʷ }k{4 ĘK%˫+3 럭=;fI^y8vfrtG`p.KVdn2:[7.4ob=s ?ejӧ5`= ~s}_ߘIENDB`mx-1.4.7/data/style/expander-closed-disabled.png000066400000000000000000000011211201047117600215420ustar00rootroot00000000000000PNG  IHDR J1 tEXtSoftwareAdobe ImageReadyqe<IDATxܿn@q"lJ+U8FR+ui_'(bY 0 ZuV`hZyRE.wU}~ǟ?h$p#WUQgZ7^SgV(3)Nd2y0`P C9k6"RHl61Vgbo4!T<3-y&:q$I[=k]81SR͹٬un}PJ1%XūTv؟>:l__3Jb6;͞R2 ;@@cOlv͞Llvbb*`;bWJ%ix2X,qwx*ka6rRV_' lxذwQ$mӷ[_:ΟMȼu?tӷ{kl(O,.ÒQE"]:, Տ EŐ!IENDB`mx-1.4.7/data/style/expander-closed-focus.png000066400000000000000000000013211201047117600211140ustar00rootroot00000000000000PNG  IHDR J1 tEXtSoftwareAdobe ImageReadyqe<sIDATx?AgidH҄z+LXYX*0& v&D 8e]9lhg=˄ fvt: &SHS2SVUjM|JtFJ98NOVݾ_ׇW,E!jYo6b\|m2z6jGT#zuZL׬rqύeY0 ^/NONL&cV ߴKڳv-}?ηK2+Ѯ4v~u>{;;@;@(b5@@bCV{Hؗ7=I?Al~vGDq;=?-'Ǭ Ѿs=fo*Eb̽Gy#77fyk[;7J|[ҵ,˅<Nv=N"sW@Fu@Ӎf Cv2u{SfB͎݆uejjJ01$RNOOvp ñ&xCHhsttT~2@\.'l!ث-?vNt%%w vb vb v;@;@;@ vb vb vA;@;@b vb v؁= CT}_2(p/dxXvwwezzI!юuv}xlccѐ Dfӵؾm6`ggSsT qXlfSﭖھKZonn^g&z͸dvu1=޲!54:X1.$wj./|b|w)=8vB) >;!}XIENDB`mx-1.4.7/data/style/expander-open-focus.png000066400000000000000000000020271201047117600206100ustar00rootroot00000000000000PNG  IHDR\6PtEXtSoftwareAdobe ImageReadyqe<IDATx=OSa\} %H`A.N&~6G0GD| 7 0-D^zN%6nFFJf[׵^O-RgYvĹFݡ!8 AOOctO5.hֵxEP? < G;vWǢ_xM.u0lɮ v1H#'{Br'{{5 =do^ש,~C5VG}u'5Ov[c:zdGbG 5Yc4Ae;zU#x]7/Pmω:}dGWLvbGc$lN~x}wɎĿ@/[[M Y6|.'AHerrR\Mܔb}ggR./Còzm]6[aZc0ёA5 -;@;@b vb vb;@;@; vb vb vA;@;?;$" ! Fӱ}dTU))xVI&8ɓB_loow ^+hb}[{}eeURykm[6^ƞvKKKұcS_OVOFOo+޶nkzl_?sQ?$; ~Oork=>/>0nIENDB`mx-1.4.7/data/style/expander-open-hover.png000066400000000000000000000016321201047117600206150ustar00rootroot00000000000000PNG  IHDR\6PtEXtSoftwareAdobe ImageReadyqe<<;;L Vfn>/ڵM&Y*;"sr"] ѹt}KZWveet:]ӭ?͸`3=\ ĞЕuKטd`kfsu˜9=ُs ?ɘK?x<ؽrc[wA``{qՓrIENDB`mx-1.4.7/data/style/expander-open.png000066400000000000000000000016521201047117600174760ustar00rootroot00000000000000PNG  IHDR\6PtEXtSoftwareAdobe ImageReadyqe<LIDATx?K[Q$i %DV&K]:t(u'@)t,'8:tYCH1ޞZҡQ8_U---'Gu]o}Dry] 6}ۭW+++#zdaa! Q`Z&jU׿V*AT?fbq'''֟LJafFRwi3vx>/:P6g|lLJf惮_/ApKh$b{;.>#T'v:z!&{dGĮ_=ɎYc`x Lv`a= v0bXc{`!h4igGNθXM$$H_4z}&LLLrn V2[ёTe<>2;;+)R_X~NcoD~c2u1Xћ9o0دb @;@;@b vb vb;@;@b vb vb v;@;@mAp+$t}?qk`@LNNrSRhwjGa+kkk溮dFFDw-oӹ|(\nzSCwf6+!Ornnx<^ǹ.L.?ѩl1}nЧUqeu3U+jgY]Z7魺 uIENDB`mx-1.4.7/data/style/frame-background.png000066400000000000000000000010641201047117600201350ustar00rootroot00000000000000PNG  IHDR((mtEXtSoftwareAdobe ImageReadyqe<IDATx?N0'AO ʔi{LUh`APhi๸ u(-}JU}}v"ؒ/g=nA\ANxKW.rÍX&R+:p'8H2٩Il[E$G]'GbN[8> EF9guYӉ(j­k&) nm ~ZOkeíF>`g8nr(s w+gZ,AV;WR}-ЏV ZA+hV ZA+ ygUP,J]PaÔv{)3%*!wAtUL30 31r)GXm>kÎ(2G}pSf@ VfR\Jك<,W".7Td+K$6z, ꢦY%.z`wg>IENDB`mx-1.4.7/data/style/menu-item.png000066400000000000000000000001651201047117600166270ustar00rootroot00000000000000PNG  IHDRoxtEXtSoftwareAdobe ImageReadyqe<IDATxbu&*`b0!w.mIENDB`mx-1.4.7/data/style/menu-toolbar.png000066400000000000000000000007231201047117600173330ustar00rootroot00000000000000PNG  IHDR ws|sRGBbKGD pHYs  tIME &$SIDATH=N@G<tH]5 k`1 uI!OX4CKen~:͑b\y0ƌEBZ޶ryfhtZU@U;GE<ϋt:D,*@+EQTm۞''"_Uu;~h#:::=a[XE>wme$5MtO&3 MDXJ4sBcc|>0Ht\Tޮ+k?}SDD[_7ˀ1u6^Ň0Q D`zhF'F8VIENDB`mx-1.4.7/data/style/menu.png000066400000000000000000000004471201047117600156760ustar00rootroot00000000000000PNG  IHDR tEXtSoftwareAdobe ImageReadyqe<IDATx씽0 #X&*%P#.09!y #]~Lw':>˅9"~qp1G(sZn蠰$oU pNb9Bi-XHm,.Ӳ,OƘg3Ҷ`(H$̝i9ov'hsUUU?t`h p"DaHn:\ntl ~fޥ$&*L,Xf`˨x 'zzzbIJ,Dx{&eGFVS82 ]]88 u: I$ݽoc_v@[`pl?E[2 xd#XlDvS#" cp|޺!Ø6xa~~:-$P )V9W {,2%U(ܢ||HB2;#ЭiS1hk}t#RDrŒ.Gy2\.Ύdƞ T]H%ѡvS\U%| d"9Bonn>}91v6Ttm!EJE}ccɫx|4+N)(h|rTZ8T<@bB~HaG=QE.UpkT멩 =]]z333F_,W9NGƤE H8^ԁΙCサ@U^ {ؖl{<']eL^ af_%Ė{\@ /ճIENDB`mx-1.4.7/data/style/pathbar-button-focus.png000066400000000000000000000016171201047117600210010ustar00rootroot00000000000000PNG  IHDRϴtEXtSoftwareAdobe ImageReadyqe<1IDATxڬKOQD.EE>ₔEeCpczV =?AH&@cyyY%oXL3pD"877. AL,fƫ_ &OMM rtz ZTz tʪ5M4 hO`̌+>'e%tq ylu^QH @àb*ɌxC4kyyGqɽ,1==z}mq\bqJi0 ZCQpU'A$]`p{4ssJ]$U¨T@EE; CI!ndAK# -C)SţpKl 0xz.b` ꊢ<{֩:jM">gۃDBCTajb+3Z~]_wEL.7 ߇oZ6}6??Y V3 |tϠ4- r#2ӅgwQ^ kEbH&ָ4f#2$|BfGmDFd ]C'=r;宒ڲC~^g۵E4U/ <^G#zHD+5բrLaPc { HEm٤jV RDEWVRQ 4M5e 麮  /9Ȁ0 ӈ>!!{>=@TyxFAP\< 9Rz7oɑ2~ 2uo@#1#'Q0 fS9X Bi./ U`\2{@IENDB`mx-1.4.7/data/style/pathbar-button-last-disabled.png000066400000000000000000000010031201047117600223570ustar00rootroot00000000000000PNG  IHDRr ߔtEXtSoftwareAdobe ImageReadyqe<IDATx얿jP͍ER'BC[]ڡPu/tU$"uj%I 7J7->B.r~ ^ X 8JG%PƆJ^\f([#{ cqZO0Tb\ 0MS%L&S_UodY,ζ\>_ps%@Xh<.}qd[@eH}ȦӠ¥ⓤi(JKޞm~5Cw#o%"`bUe߲ 1EMF \LY>UzQO=ETټ(Jd2Y4P33ɽuGs&jJ)u ELGSEtpIYIxR"rbq?~øS -+H/IENDB`mx-1.4.7/data/style/pathbar-button-last-focus.png000066400000000000000000000011641201047117600217370ustar00rootroot00000000000000PNG  IHDRr ߔtEXtSoftwareAdobe ImageReadyqe<IDATxVJA3"n4;;JRj!`g/X[((IHf{٨hpF-, ̹.{^.bXÚ1(O0KRf]t'KJٌnT܊iX\@Օ y)F#vxt:;JHI6P(KK/ҹ\c_0JANEVcѳDY&ƹ1!ᴐq]@9=@\OD2]1S w#BB 't-Bc+ O*UDSeq;| ix dϓB‘NC2 l T(҄nq= }߇F6p8$k\$E8!̴72ImXsbq7cb8oGlj3z"j}{"~P9tL$X}:ĝN"UǪ^[:SLJ,Jգ[,IENDB`mx-1.4.7/data/style/pathbar-button-last-hover.png000066400000000000000000000010431201047117600217370ustar00rootroot00000000000000PNG  IHDRr ߔtEXtSoftwareAdobe ImageReadyqe<IDATxJQ|J(BfHDZh.B\BbvC/ f #tM&Cs;13YsgtC@( %!ՑX](|>+bE8#}c8^Z7L&h4J{T*ZXL&۶ahtl6 Jm˹\ZjG |97P$8.AUU^c|YO =FC$I\pQ̲&he6!'i`8Dc >PzKn@q9M-K/ߋо5iځn@8$zhrگ=n6 h9A|eYwеҵZ\KEQJhmZDx1Mn?b XS bN `>.͞^d#wM1L#]qx;wD/heIENDB`mx-1.4.7/data/style/pathbar-button-last.png000066400000000000000000000010521201047117600206160ustar00rootroot00000000000000PNG  IHDRr ߔtEXtSoftwareAdobe ImageReadyqe<IDATx얿JPͽiJPVZܳTp |R:P(n/Pܲ BĦF{K N& J X,!v|fٷgP J%' !*@;dc SXG)Cc\.C:Q/F=`ްRUUrvl5 pZ_''J*zu5*%ONOd2DY 긞_[T 3339I# eZqpxw AOOO^#h-H Ь Jz5An5Q ^y dzL2m^ _%K^+xKgHIENDB`mx-1.4.7/data/style/plus-button-checked-hover.png000066400000000000000000000010231201047117600217220ustar00rootroot00000000000000PNG  IHDR|0tEXtSoftwareAdobe ImageReadyqe<IDATxڴN@wE Q 6 ^ΚxƒB|0p4&^!r2Z3%hh&nvnRl6Ka󛿄="r) +J[$Aaf]Ӵ|> C q*;R"jcY \.ws*~?y~%PtrWX1sĔҠDi۞E91:b2`De٪a'RmtǞg vnZK#w$h$f~EjZ*HoDVn2pvt}T,weYބm~*r_(.`:(2&|{ |A4,ة&F bUrϬUNwua?`7ʲ7IENDB`mx-1.4.7/data/style/plus-button-checked.png000066400000000000000000000007341201047117600206110ustar00rootroot00000000000000PNG  IHDR|0tEXtSoftwareAdobe ImageReadyqe<~IDATxڴJ@g&%V\.]/Ѝ­nq7 Bvݵ"D`MxOzPS!?KfrGZ-j5IoGC u˲ 9[z.촲yWﷃ xOr dbbA:snvE2ȂXJY6M(V`i,,6lE}wbACq\)-|2fhg)⛃ԫJ idDӎDs4W! ipNE /d LUdl^V#jvζvF.C%gz[mކƒR *&EM (~2:LAj+>#y/~o8OC6;GIENDB`mx-1.4.7/data/style/plus-button-hover.png000066400000000000000000000011331201047117600203400ustar00rootroot00000000000000PNG  IHDR|0tEXtSoftwareAdobe ImageReadyqe<IDATxڴKKQKwP%ZUA"ZEHsghmAvA-z=$rf 83D\x.@ [VTCD`P FNsy/,' q(:ҜXlN.vyEc(S9 ?s\wvd3lGj`Z*ȥRV5D-ewTʨ:m]]^} nϞH[9  IRn{Zq6g! L&s֙~{ƺz\S򝩇d ;gDfEQdm~&ɋp8|U 7&ov/P}>c_`uAةZMֺ+ Ql3J&dw$H~GEm>^! lэlȁ\` YEfm1f^׊b0Xp8AEj?c`OSAJ(M"q!`3w:!rN[PBSPIENDB`mx-1.4.7/data/style/progress-bar-background.png000066400000000000000000000007441201047117600214550ustar00rootroot00000000000000PNG  IHDR8DtEXtSoftwareAdobe ImageReadyqe<IDATxMN@LXv$Mixo 8,'T8 ։dfy(0vFkeBrE[s8nZ]4}}N#N̂Ĺn]o/CEkrnIENDB`mx-1.4.7/data/style/progress-bar-bar.png000066400000000000000000000010441201047117600200740ustar00rootroot00000000000000PNG  IHDR8DtEXtSoftwareAdobe ImageReadyqe<IDATx=JA5ÚVJXxRHHE XXyo`gvb!hFHMq&ND0־|;S<|ÊZF|Ѩ=bb88kyu])uh4v+ʝRVT*YN3 Q01"~ϧC@7VErRyN3K]S[uIuH g\fJ 잙 !6) Y; 3{I/H|jzdIYW8NC01F)8y_BG>A)còF0%9FK)o"K5K'KjQUé#&͡)4ǨWv;CDfкb{MTP(la*|iSWzZg斥'q?֞T׎mfzn׽軒™$?4,00;7,IENDB`mx-1.4.7/data/style/resize-grip.png000066400000000000000000000003031201047117600171610ustar00rootroot00000000000000PNG  IHDR;֕JtEXtSoftwareAdobe ImageReadyqe<eIDATxb` 3X`rtuvv5o!fd@z3A|&r5Lj$g|F~&F#V?ϤhD3LFz IENDB`mx-1.4.7/data/style/scroll-button-down-active.png000066400000000000000000000064031201047117600217550ustar00rootroot00000000000000PNG  IHDRw= pHYs   OiCCPPhotoshop ICC profilexڝSgTS=BKKoR RB&*! J!QEEȠQ, !{kּ> H3Q5 B.@ $pd!s#~<<+"x M0B\t8K@zB@F&S`cbP-`'{[! eDh;VEX0fK9-0IWfH  0Q){`##xFW<+*x<$9E[-qWW.(I+6aa@.y24x6_-"bbϫp@t~,/;m%h^ uf@Wp~<5j>{-]cK'Xto(hw?G%fIq^D$.Tʳ?D*A, `6B$BB dr`)B(Ͱ*`/@4Qhp.U=pa( Aa!ڈbX#!H$ ɈQ"K5H1RT UH=r9\F;2G1Q= C7F dt1r=6Ыhڏ>C03l0.B8, c˱" VcϱwE 6wB aAHXLXNH $4 7 Q'"K&b21XH,#/{C7$C2'ITFnR#,4H#dk9, +ȅ3![ b@qS(RjJ4e2AURݨT5ZBRQ4u9̓IKhhitݕNWGw Ljg(gwLӋT071oUX**| J&*/Tު UUT^S}FU3S ԖUPSSg;goT?~YYLOCQ_ cx,!k u5&|v*=9C3J3WRf?qtN (~))4L1e\kXHQG6EYAJ'\'GgSSݧ M=:.kDwn^Loy}/TmG X $ <5qo</QC]@Caaᄑ.ȽJtq]zۯ6iܟ4)Y3sCQ? 0k߬~OCOg#/c/Wװwa>>r><72Y_7ȷOo_C#dz%gA[z|!?:eAAA!h쐭!ΑiP~aa~ 'W?pX15wCsDDDޛg1O9-J5*>.j<74?.fYXXIlK9.*6nl {/]py.,:@LN8A*%w% yg"/6шC\*NH*Mz쑼5y$3,幄'L Lݛ:v m2=:1qB!Mggfvˬen/kY- BTZ(*geWf͉9+̳ې7ᒶKW-X潬j9(xoʿܔĹdff-[n ڴ VE/(ۻCɾUUMfeI?m]Nmq#׹=TR+Gw- 6 U#pDy  :v{vg/jBFS[b[O>zG499?rCd&ˮ/~јѡ򗓿m|x31^VwwO| (hSЧc3- cHRMz%u0`:o_F.IDATxԕka?wI.wRb~Q'A2C:iC[C[]:9E@`D r^4IJxxy>p8:#8zT*;@+j5s44rPTv"[$ dY^Zy88hwi_&it]'(+<#(Je/u7u{tPIVkp/ohZ,( !hɳ!s^owKz%bbBP,(YGia ʖ!e8iˍEqxP:eVk3K$ } ]8?uB~IY`Ԃy''<Ot.8=O<[hx-O:<<׽ҹ@wGVJ~O03X,vnc=|ߧjJgNQ&дmvNOiZn6}7s۵ZZN'׫:H&\S4-+4Okt|^"'IENDB`mx-1.4.7/data/style/scroll-button-down-disabled.png000066400000000000000000000064631201047117600222570ustar00rootroot00000000000000PNG  IHDRw= pHYs   OiCCPPhotoshop ICC profilexڝSgTS=BKKoR RB&*! J!QEEȠQ, !{kּ> H3Q5 B.@ $pd!s#~<<+"x M0B\t8K@zB@F&S`cbP-`'{[! eDh;VEX0fK9-0IWfH  0Q){`##xFW<+*x<$9E[-qWW.(I+6aa@.y24x6_-"bbϫp@t~,/;m%h^ uf@Wp~<5j>{-]cK'Xto(hw?G%fIq^D$.Tʳ?D*A, `6B$BB dr`)B(Ͱ*`/@4Qhp.U=pa( Aa!ڈbX#!H$ ɈQ"K5H1RT UH=r9\F;2G1Q= C7F dt1r=6Ыhڏ>C03l0.B8, c˱" VcϱwE 6wB aAHXLXNH $4 7 Q'"K&b21XH,#/{C7$C2'ITFnR#,4H#dk9, +ȅ3![ b@qS(RjJ4e2AURݨT5ZBRQ4u9̓IKhhitݕNWGw Ljg(gwLӋT071oUX**| J&*/Tު UUT^S}FU3S ԖUPSSg;goT?~YYLOCQ_ cx,!k u5&|v*=9C3J3WRf?qtN (~))4L1e\kXHQG6EYAJ'\'GgSSݧ M=:.kDwn^Loy}/TmG X $ <5qo</QC]@Caaᄑ.ȽJtq]zۯ6iܟ4)Y3sCQ? 0k߬~OCOg#/c/Wװwa>>r><72Y_7ȷOo_C#dz%gA[z|!?:eAAA!h쐭!ΑiP~aa~ 'W?pX15wCsDDDޛg1O9-J5*>.j<74?.fYXXIlK9.*6nl {/]py.,:@LN8A*%w% yg"/6шC\*NH*Mz쑼5y$3,幄'L Lݛ:v m2=:1qB!Mggfvˬen/kY- BTZ(*geWf͉9+̳ې7ᒶKW-X潬j9(xoʿܔĹdff-[n ڴ VE/(ۻCɾUUMfeI?m]Nmq#׹=TR+Gw- 6 U#pDy  :v{vg/jBFS[b[O>zG499?rCd&ˮ/~јѡ򗓿m|x31^VwwO| (hSЧc3- cHRMz%u0`:o_F^IDATxڬVAOAfvBKPm@ֳcP?ѓJ<6 "oj41QnI bwv:cX\Jsg Z}uV*?)PJ["jPױn6r%"m~>99yoii sh`X kPRbYq(c&W7 0Ƭ>Zc05^/TߪF*pop)yxv X}a6!^ZE2<&AGSf&esBt }{lf"w~hPMuv+s3PJ,mmX/AOӗ]~h(q\Ko_4sW# QGJirίD )ex.N_IB:533OHk&R=<Ͻ=oww; 2MVX=??ײ hywZD xL &5u!nC" pkL.K)JSIENDB`mx-1.4.7/data/style/scroll-button-down-hover.png000066400000000000000000000064511201047117600216300ustar00rootroot00000000000000PNG  IHDRw= pHYs   OiCCPPhotoshop ICC profilexڝSgTS=BKKoR RB&*! J!QEEȠQ, !{kּ> H3Q5 B.@ $pd!s#~<<+"x M0B\t8K@zB@F&S`cbP-`'{[! eDh;VEX0fK9-0IWfH  0Q){`##xFW<+*x<$9E[-qWW.(I+6aa@.y24x6_-"bbϫp@t~,/;m%h^ uf@Wp~<5j>{-]cK'Xto(hw?G%fIq^D$.Tʳ?D*A, `6B$BB dr`)B(Ͱ*`/@4Qhp.U=pa( Aa!ڈbX#!H$ ɈQ"K5H1RT UH=r9\F;2G1Q= C7F dt1r=6Ыhڏ>C03l0.B8, c˱" VcϱwE 6wB aAHXLXNH $4 7 Q'"K&b21XH,#/{C7$C2'ITFnR#,4H#dk9, +ȅ3![ b@qS(RjJ4e2AURݨT5ZBRQ4u9̓IKhhitݕNWGw Ljg(gwLӋT071oUX**| J&*/Tު UUT^S}FU3S ԖUPSSg;goT?~YYLOCQ_ cx,!k u5&|v*=9C3J3WRf?qtN (~))4L1e\kXHQG6EYAJ'\'GgSSݧ M=:.kDwn^Loy}/TmG X $ <5qo</QC]@Caaᄑ.ȽJtq]zۯ6iܟ4)Y3sCQ? 0k߬~OCOg#/c/Wװwa>>r><72Y_7ȷOo_C#dz%gA[z|!?:eAAA!h쐭!ΑiP~aa~ 'W?pX15wCsDDDޛg1O9-J5*>.j<74?.fYXXIlK9.*6nl {/]py.,:@LN8A*%w% yg"/6шC\*NH*Mz쑼5y$3,幄'L Lݛ:v m2=:1qB!Mggfvˬen/kY- BTZ(*geWf͉9+̳ې7ᒶKW-X潬j9(xoʿܔĹdff-[n ڴ VE/(ۻCɾUUMfeI?m]Nmq#׹=TR+Gw- 6 U#pDy  :v{vg/jBFS[b[O>zG499?rCd&ˮ/~јѡ򗓿m|x31^VwwO| (hSЧc3- cHRMz%u0`:o_FTIDATxڴkA٬]r(-kJA'i!ڦ MPRS 7"Ҁ5-nv&leg}f/-" бs?[UlD%"(uKe݉,n<ܬFD 766fl\gggQV/˟D$z;Nݞ$Arfnvqק,˚`"LqZRĉRcm3RR*a=`2XY)FQ}EX-˴>R0BL{6˹%^}C "@'rni_`krn3dp[MFEf)ESmm@ Z=?^op.n@\@DFcLk=R0 s@j>wg2#j5|߯믎jML K :G󏦦 0h6+\DΑ \ܸL JЌ$N&l9hĕ0*:IENDB`mx-1.4.7/data/style/scroll-button-down.png000066400000000000000000000065041201047117600205060ustar00rootroot00000000000000PNG  IHDRw= pHYs   OiCCPPhotoshop ICC profilexڝSgTS=BKKoR RB&*! J!QEEȠQ, !{kּ> H3Q5 B.@ $pd!s#~<<+"x M0B\t8K@zB@F&S`cbP-`'{[! eDh;VEX0fK9-0IWfH  0Q){`##xFW<+*x<$9E[-qWW.(I+6aa@.y24x6_-"bbϫp@t~,/;m%h^ uf@Wp~<5j>{-]cK'Xto(hw?G%fIq^D$.Tʳ?D*A, `6B$BB dr`)B(Ͱ*`/@4Qhp.U=pa( Aa!ڈbX#!H$ ɈQ"K5H1RT UH=r9\F;2G1Q= C7F dt1r=6Ыhڏ>C03l0.B8, c˱" VcϱwE 6wB aAHXLXNH $4 7 Q'"K&b21XH,#/{C7$C2'ITFnR#,4H#dk9, +ȅ3![ b@qS(RjJ4e2AURݨT5ZBRQ4u9̓IKhhitݕNWGw Ljg(gwLӋT071oUX**| J&*/Tު UUT^S}FU3S ԖUPSSg;goT?~YYLOCQ_ cx,!k u5&|v*=9C3J3WRf?qtN (~))4L1e\kXHQG6EYAJ'\'GgSSݧ M=:.kDwn^Loy}/TmG X $ <5qo</QC]@Caaᄑ.ȽJtq]zۯ6iܟ4)Y3sCQ? 0k߬~OCOg#/c/Wװwa>>r><72Y_7ȷOo_C#dz%gA[z|!?:eAAA!h쐭!ΑiP~aa~ 'W?pX15wCsDDDޛg1O9-J5*>.j<74?.fYXXIlK9.*6nl {/]py.,:@LN8A*%w% yg"/6шC\*NH*Mz쑼5y$3,幄'L Lݛ:v m2=:1qB!Mggfvˬen/kY- BTZ(*geWf͉9+̳ې7ᒶKW-X潬j9(xoʿܔĹdff-[n ڴ VE/(ۻCɾUUMfeI?m]Nmq#׹=TR+Gw- 6 U#pDy  :v{vg/jBFS[b[O>zG499?rCd&ˮ/~јѡ򗓿m|x31^VwwO| (hSЧc3- cHRMz%u0`:o_FoIDATxڴKkQw&Oi+j`e'\֍4h-ŅS]!ڵP* Ah.MZ}㢓0=Μ;sX^^V!cT!R[.qn=CD~ZkkFIRYH[ 6;;{w122fauuZy\. L&35::ʵ!ww9+\<D ;s+ <\uoq@TJ9Vq]Rv;Z.**ڵNpnV>/_͑L&~:뺝D^WZ3lT*u|O7-ch6f<_hXdaq@75lbHc+0"D@6E75fL '\A>ܿ7~3 DSt=fu. H9sDd/uN mb@k]m +z3g-Y[[ ) S*ƇKDb_Vϗ{";#Hp0o ~ftd@GV7wУm lhT ;F~'IENDB`mx-1.4.7/data/style/scroll-button-left-active.png000066400000000000000000000063761201047117600217510ustar00rootroot00000000000000PNG  IHDRw= pHYs   OiCCPPhotoshop ICC profilexڝSgTS=BKKoR RB&*! J!QEEȠQ, !{kּ> H3Q5 B.@ $pd!s#~<<+"x M0B\t8K@zB@F&S`cbP-`'{[! eDh;VEX0fK9-0IWfH  0Q){`##xFW<+*x<$9E[-qWW.(I+6aa@.y24x6_-"bbϫp@t~,/;m%h^ uf@Wp~<5j>{-]cK'Xto(hw?G%fIq^D$.Tʳ?D*A, `6B$BB dr`)B(Ͱ*`/@4Qhp.U=pa( Aa!ڈbX#!H$ ɈQ"K5H1RT UH=r9\F;2G1Q= C7F dt1r=6Ыhڏ>C03l0.B8, c˱" VcϱwE 6wB aAHXLXNH $4 7 Q'"K&b21XH,#/{C7$C2'ITFnR#,4H#dk9, +ȅ3![ b@qS(RjJ4e2AURݨT5ZBRQ4u9̓IKhhitݕNWGw Ljg(gwLӋT071oUX**| J&*/Tު UUT^S}FU3S ԖUPSSg;goT?~YYLOCQ_ cx,!k u5&|v*=9C3J3WRf?qtN (~))4L1e\kXHQG6EYAJ'\'GgSSݧ M=:.kDwn^Loy}/TmG X $ <5qo</QC]@Caaᄑ.ȽJtq]zۯ6iܟ4)Y3sCQ? 0k߬~OCOg#/c/Wװwa>>r><72Y_7ȷOo_C#dz%gA[z|!?:eAAA!h쐭!ΑiP~aa~ 'W?pX15wCsDDDޛg1O9-J5*>.j<74?.fYXXIlK9.*6nl {/]py.,:@LN8A*%w% yg"/6шC\*NH*Mz쑼5y$3,幄'L Lݛ:v m2=:1qB!Mggfvˬen/kY- BTZ(*geWf͉9+̳ې7ᒶKW-X潬j9(xoʿܔĹdff-[n ڴ VE/(ۻCɾUUMfeI?m]Nmq#׹=TR+Gw- 6 U#pDy  :v{vg/jBFS[b[O>zG499?rCd&ˮ/~јѡ򗓿m|x31^VwwO| (hSЧc3- cHRMz%u0`:o_F)IDATxڴMkQIdhdikil*Y$KA J0.f!]+;э`]4m&6q&NHs9s(Qq ilxܳm{T8spjzښDuE9rExǏ=vMXmKOB) H3Q5 B.@ $pd!s#~<<+"x M0B\t8K@zB@F&S`cbP-`'{[! eDh;VEX0fK9-0IWfH  0Q){`##xFW<+*x<$9E[-qWW.(I+6aa@.y24x6_-"bbϫp@t~,/;m%h^ uf@Wp~<5j>{-]cK'Xto(hw?G%fIq^D$.Tʳ?D*A, `6B$BB dr`)B(Ͱ*`/@4Qhp.U=pa( Aa!ڈbX#!H$ ɈQ"K5H1RT UH=r9\F;2G1Q= C7F dt1r=6Ыhڏ>C03l0.B8, c˱" VcϱwE 6wB aAHXLXNH $4 7 Q'"K&b21XH,#/{C7$C2'ITFnR#,4H#dk9, +ȅ3![ b@qS(RjJ4e2AURݨT5ZBRQ4u9̓IKhhitݕNWGw Ljg(gwLӋT071oUX**| J&*/Tު UUT^S}FU3S ԖUPSSg;goT?~YYLOCQ_ cx,!k u5&|v*=9C3J3WRf?qtN (~))4L1e\kXHQG6EYAJ'\'GgSSݧ M=:.kDwn^Loy}/TmG X $ <5qo</QC]@Caaᄑ.ȽJtq]zۯ6iܟ4)Y3sCQ? 0k߬~OCOg#/c/Wװwa>>r><72Y_7ȷOo_C#dz%gA[z|!?:eAAA!h쐭!ΑiP~aa~ 'W?pX15wCsDDDޛg1O9-J5*>.j<74?.fYXXIlK9.*6nl {/]py.,:@LN8A*%w% yg"/6шC\*NH*Mz쑼5y$3,幄'L Lݛ:v m2=:1qB!Mggfvˬen/kY- BTZ(*geWf͉9+̳ې7ᒶKW-X潬j9(xoʿܔĹdff-[n ڴ VE/(ۻCɾUUMfeI?m]Nmq#׹=TR+Gw- 6 U#pDy  :v{vg/jBFS[b[O>zG499?rCd&ˮ/~јѡ򗓿m|x31^VwwO| (hSЧc3- cHRMz%u0`:o_FcIDATxڬ=oAݽ;˒HXriLB$P">,@(!|V_` '[^P.%4Ziyvvn!N^3 q<؏`$"0"c`ryζ+u DߏTEaX[p8!D$/9YhQk8:ffg/A)u`O|qppUyfR)!bVJ8t:_@Lf1^6Mssnhh4rZZ F-&pjz/p$2\7_ H3Q5 B.@ $pd!s#~<<+"x M0B\t8K@zB@F&S`cbP-`'{[! eDh;VEX0fK9-0IWfH  0Q){`##xFW<+*x<$9E[-qWW.(I+6aa@.y24x6_-"bbϫp@t~,/;m%h^ uf@Wp~<5j>{-]cK'Xto(hw?G%fIq^D$.Tʳ?D*A, `6B$BB dr`)B(Ͱ*`/@4Qhp.U=pa( Aa!ڈbX#!H$ ɈQ"K5H1RT UH=r9\F;2G1Q= C7F dt1r=6Ыhڏ>C03l0.B8, c˱" VcϱwE 6wB aAHXLXNH $4 7 Q'"K&b21XH,#/{C7$C2'ITFnR#,4H#dk9, +ȅ3![ b@qS(RjJ4e2AURݨT5ZBRQ4u9̓IKhhitݕNWGw Ljg(gwLӋT071oUX**| J&*/Tު UUT^S}FU3S ԖUPSSg;goT?~YYLOCQ_ cx,!k u5&|v*=9C3J3WRf?qtN (~))4L1e\kXHQG6EYAJ'\'GgSSݧ M=:.kDwn^Loy}/TmG X $ <5qo</QC]@Caaᄑ.ȽJtq]zۯ6iܟ4)Y3sCQ? 0k߬~OCOg#/c/Wװwa>>r><72Y_7ȷOo_C#dz%gA[z|!?:eAAA!h쐭!ΑiP~aa~ 'W?pX15wCsDDDޛg1O9-J5*>.j<74?.fYXXIlK9.*6nl {/]py.,:@LN8A*%w% yg"/6шC\*NH*Mz쑼5y$3,幄'L Lݛ:v m2=:1qB!Mggfvˬen/kY- BTZ(*geWf͉9+̳ې7ᒶKW-X潬j9(xoʿܔĹdff-[n ڴ VE/(ۻCɾUUMfeI?m]Nmq#׹=TR+Gw- 6 U#pDy  :v{vg/jBFS[b[O>zG499?rCd&ˮ/~јѡ򗓿m|x31^VwwO| (hSЧc3- cHRMz%u0`:o_F[IDATxڬ=oP{(jDI2XYLQUY?GT2@I@""bZ>6>~c-;R' TEq񽇁ʉd7{=Ϲ9!ϣj1)ǴDFD`1˲^QfjF;EDxV[,s\ΕJ%d$1v=tz^BDRh( *JrR Q"@s\Y]E*>p4B\]m()ADHjD%%mkL;2Mk`-?$I\׍)ƣZ VWo^"N#g%xDrcwnEۆyxd g(Nl܇̊UJLA)l^Zyux8;6b"O9O{kp]p!bO|?V_cF?`8BJ@Qt}>/.B(qyp ;aDXV7]4W H4ҔRxk6&"7i}!Qp"# -9qz!| pT%Q:bIENDB`mx-1.4.7/data/style/scroll-button-left.png000066400000000000000000000065251201047117600204740ustar00rootroot00000000000000PNG  IHDRw= pHYs   OiCCPPhotoshop ICC profilexڝSgTS=BKKoR RB&*! J!QEEȠQ, !{kּ> H3Q5 B.@ $pd!s#~<<+"x M0B\t8K@zB@F&S`cbP-`'{[! eDh;VEX0fK9-0IWfH  0Q){`##xFW<+*x<$9E[-qWW.(I+6aa@.y24x6_-"bbϫp@t~,/;m%h^ uf@Wp~<5j>{-]cK'Xto(hw?G%fIq^D$.Tʳ?D*A, `6B$BB dr`)B(Ͱ*`/@4Qhp.U=pa( Aa!ڈbX#!H$ ɈQ"K5H1RT UH=r9\F;2G1Q= C7F dt1r=6Ыhڏ>C03l0.B8, c˱" VcϱwE 6wB aAHXLXNH $4 7 Q'"K&b21XH,#/{C7$C2'ITFnR#,4H#dk9, +ȅ3![ b@qS(RjJ4e2AURݨT5ZBRQ4u9̓IKhhitݕNWGw Ljg(gwLӋT071oUX**| J&*/Tު UUT^S}FU3S ԖUPSSg;goT?~YYLOCQ_ cx,!k u5&|v*=9C3J3WRf?qtN (~))4L1e\kXHQG6EYAJ'\'GgSSݧ M=:.kDwn^Loy}/TmG X $ <5qo</QC]@Caaᄑ.ȽJtq]zۯ6iܟ4)Y3sCQ? 0k߬~OCOg#/c/Wװwa>>r><72Y_7ȷOo_C#dz%gA[z|!?:eAAA!h쐭!ΑiP~aa~ 'W?pX15wCsDDDޛg1O9-J5*>.j<74?.fYXXIlK9.*6nl {/]py.,:@LN8A*%w% yg"/6шC\*NH*Mz쑼5y$3,幄'L Lݛ:v m2=:1qB!Mggfvˬen/kY- BTZ(*geWf͉9+̳ې7ᒶKW-X潬j9(xoʿܔĹdff-[n ڴ VE/(ۻCɾUUMfeI?m]Nmq#׹=TR+Gw- 6 U#pDy  :v{vg/jBFS[b[O>zG499?rCd&ˮ/~јѡ򗓿m|x31^VwwO| (hSЧc3- cHRMz%u0`:o_FIDATxڬOOQowMC.&4A?@/hYI~pPbn#wn$m"rPoǃE[$ݼ{7kì+ j{率 AJ)BL*z`F^) sqqq2J}C\d2O|:JZGV?555ךFTa=`hbe]f\֌gy'5hZ'4QJ OPG l6I$G֚g)ܽ}(HWkT츍m=Cx^/_\YUjuͻ 7n"=l{WI"mKGWK/_all uxX|]m' pm*LuyBY2KшE2738XɾG ַOz+`[]ՊjT2i}Z_Vcwww%"h ``~~~:N_ba5|߯|Vkb=CD!@"xZmC<<#"^kW8`!B+hDµ_o^(ˎIENDB`mx-1.4.7/data/style/scroll-button-right-active.png000066400000000000000000000064031201047117600221230ustar00rootroot00000000000000PNG  IHDRw= pHYs   OiCCPPhotoshop ICC profilexڝSgTS=BKKoR RB&*! J!QEEȠQ, !{kּ> H3Q5 B.@ $pd!s#~<<+"x M0B\t8K@zB@F&S`cbP-`'{[! eDh;VEX0fK9-0IWfH  0Q){`##xFW<+*x<$9E[-qWW.(I+6aa@.y24x6_-"bbϫp@t~,/;m%h^ uf@Wp~<5j>{-]cK'Xto(hw?G%fIq^D$.Tʳ?D*A, `6B$BB dr`)B(Ͱ*`/@4Qhp.U=pa( Aa!ڈbX#!H$ ɈQ"K5H1RT UH=r9\F;2G1Q= C7F dt1r=6Ыhڏ>C03l0.B8, c˱" VcϱwE 6wB aAHXLXNH $4 7 Q'"K&b21XH,#/{C7$C2'ITFnR#,4H#dk9, +ȅ3![ b@qS(RjJ4e2AURݨT5ZBRQ4u9̓IKhhitݕNWGw Ljg(gwLӋT071oUX**| J&*/Tު UUT^S}FU3S ԖUPSSg;goT?~YYLOCQ_ cx,!k u5&|v*=9C3J3WRf?qtN (~))4L1e\kXHQG6EYAJ'\'GgSSݧ M=:.kDwn^Loy}/TmG X $ <5qo</QC]@Caaᄑ.ȽJtq]zۯ6iܟ4)Y3sCQ? 0k߬~OCOg#/c/Wװwa>>r><72Y_7ȷOo_C#dz%gA[z|!?:eAAA!h쐭!ΑiP~aa~ 'W?pX15wCsDDDޛg1O9-J5*>.j<74?.fYXXIlK9.*6nl {/]py.,:@LN8A*%w% yg"/6шC\*NH*Mz쑼5y$3,幄'L Lݛ:v m2=:1qB!Mggfvˬen/kY- BTZ(*geWf͉9+̳ې7ᒶKW-X潬j9(xoʿܔĹdff-[n ڴ VE/(ۻCɾUUMfeI?m]Nmq#׹=TR+Gw- 6 U#pDy  :v{vg/jBFS[b[O>zG499?rCd&ˮ/~јѡ򗓿m|x31^VwwO| (hSЧc3- cHRMz%u0`:o_F.IDATxڴ=oPɵU-P;*~ U|1V~QL6 BjHk&q8g::}yu=J$e҅Sw'{84{ xnrҚGuEH9I|,9!ĄuanRDPu|)%J!jm:Q]all ]QU5 c ,?4裣CrLEiP'!u|tt4tPk>}l Id֥;>j<0 8F1(A!ޢ0A(J0Mn9@\<4{ ]Xb1x={&\)0 sMb(-جVw#q.ׁHN~߀$3e IENDB`mx-1.4.7/data/style/scroll-button-right-disabled.png000066400000000000000000000064771201047117600224320ustar00rootroot00000000000000PNG  IHDRw= pHYs   OiCCPPhotoshop ICC profilexڝSgTS=BKKoR RB&*! J!QEEȠQ, !{kּ> H3Q5 B.@ $pd!s#~<<+"x M0B\t8K@zB@F&S`cbP-`'{[! eDh;VEX0fK9-0IWfH  0Q){`##xFW<+*x<$9E[-qWW.(I+6aa@.y24x6_-"bbϫp@t~,/;m%h^ uf@Wp~<5j>{-]cK'Xto(hw?G%fIq^D$.Tʳ?D*A, `6B$BB dr`)B(Ͱ*`/@4Qhp.U=pa( Aa!ڈbX#!H$ ɈQ"K5H1RT UH=r9\F;2G1Q= C7F dt1r=6Ыhڏ>C03l0.B8, c˱" VcϱwE 6wB aAHXLXNH $4 7 Q'"K&b21XH,#/{C7$C2'ITFnR#,4H#dk9, +ȅ3![ b@qS(RjJ4e2AURݨT5ZBRQ4u9̓IKhhitݕNWGw Ljg(gwLӋT071oUX**| J&*/Tު UUT^S}FU3S ԖUPSSg;goT?~YYLOCQ_ cx,!k u5&|v*=9C3J3WRf?qtN (~))4L1e\kXHQG6EYAJ'\'GgSSݧ M=:.kDwn^Loy}/TmG X $ <5qo</QC]@Caaᄑ.ȽJtq]zۯ6iܟ4)Y3sCQ? 0k߬~OCOg#/c/Wװwa>>r><72Y_7ȷOo_C#dz%gA[z|!?:eAAA!h쐭!ΑiP~aa~ 'W?pX15wCsDDDޛg1O9-J5*>.j<74?.fYXXIlK9.*6nl {/]py.,:@LN8A*%w% yg"/6шC\*NH*Mz쑼5y$3,幄'L Lݛ:v m2=:1qB!Mggfvˬen/kY- BTZ(*geWf͉9+̳ې7ᒶKW-X潬j9(xoʿܔĹdff-[n ڴ VE/(ۻCɾUUMfeI?m]Nmq#׹=TR+Gw- 6 U#pDy  :v{vg/jBFS[b[O>zG499?rCd&ˮ/~јѡ򗓿m|x31^VwwO| (hSЧc3- cHRMz%u0`:o_FjIDATxڬOAǿM Mhm#=#(zMjB ŋp%zG؍nН" ]%/|}{1>> @$nÌ BZrKe#H`Zo4WVVS`j\.w{vv6]Tdćy֖3k;Z\2= M? (қl.EAD(RA pmC3T)>q}궆wk> `"up=w"ڏ !uO?vw/Sb$ $Hul6?h)ɋTj²,:euu+3wz-3q'9B-I#8ϚJ=66EݿIENDB`mx-1.4.7/data/style/scroll-button-right-hover.png000066400000000000000000000064571201047117600220040ustar00rootroot00000000000000PNG  IHDRw= pHYs   OiCCPPhotoshop ICC profilexڝSgTS=BKKoR RB&*! J!QEEȠQ, !{kּ> H3Q5 B.@ $pd!s#~<<+"x M0B\t8K@zB@F&S`cbP-`'{[! eDh;VEX0fK9-0IWfH  0Q){`##xFW<+*x<$9E[-qWW.(I+6aa@.y24x6_-"bbϫp@t~,/;m%h^ uf@Wp~<5j>{-]cK'Xto(hw?G%fIq^D$.Tʳ?D*A, `6B$BB dr`)B(Ͱ*`/@4Qhp.U=pa( Aa!ڈbX#!H$ ɈQ"K5H1RT UH=r9\F;2G1Q= C7F dt1r=6Ыhڏ>C03l0.B8, c˱" VcϱwE 6wB aAHXLXNH $4 7 Q'"K&b21XH,#/{C7$C2'ITFnR#,4H#dk9, +ȅ3![ b@qS(RjJ4e2AURݨT5ZBRQ4u9̓IKhhitݕNWGw Ljg(gwLӋT071oUX**| J&*/Tު UUT^S}FU3S ԖUPSSg;goT?~YYLOCQ_ cx,!k u5&|v*=9C3J3WRf?qtN (~))4L1e\kXHQG6EYAJ'\'GgSSݧ M=:.kDwn^Loy}/TmG X $ <5qo</QC]@Caaᄑ.ȽJtq]zۯ6iܟ4)Y3sCQ? 0k߬~OCOg#/c/Wװwa>>r><72Y_7ȷOo_C#dz%gA[z|!?:eAAA!h쐭!ΑiP~aa~ 'W?pX15wCsDDDޛg1O9-J5*>.j<74?.fYXXIlK9.*6nl {/]py.,:@LN8A*%w% yg"/6шC\*NH*Mz쑼5y$3,幄'L Lݛ:v m2=:1qB!Mggfvˬen/kY- BTZ(*geWf͉9+̳ې7ᒶKW-X潬j9(xoʿܔĹdff-[n ڴ VE/(ۻCɾUUMfeI?m]Nmq#׹=TR+Gw- 6 U#pDy  :v{vg/jBFS[b[O>zG499?rCd&ˮ/~јѡ򗓿m|x31^VwwO| (hSЧc3- cHRMz%u0`:o_FZIDATxڬKkQ;bmX"Xlp𹨮КE>v>~n+PJt $sqᤤęs3T3p?Jq*VD%"(`. O-*+icVvR/ tTbrr2M~:GNlwfff-..N+cR22C :sa0Z#"h͜aE%n :q]U\A)57AZJ {jψ 0RrlF/_$ H3Q5 B.@ $pd!s#~<<+"x M0B\t8K@zB@F&S`cbP-`'{[! eDh;VEX0fK9-0IWfH  0Q){`##xFW<+*x<$9E[-qWW.(I+6aa@.y24x6_-"bbϫp@t~,/;m%h^ uf@Wp~<5j>{-]cK'Xto(hw?G%fIq^D$.Tʳ?D*A, `6B$BB dr`)B(Ͱ*`/@4Qhp.U=pa( Aa!ڈbX#!H$ ɈQ"K5H1RT UH=r9\F;2G1Q= C7F dt1r=6Ыhڏ>C03l0.B8, c˱" VcϱwE 6wB aAHXLXNH $4 7 Q'"K&b21XH,#/{C7$C2'ITFnR#,4H#dk9, +ȅ3![ b@qS(RjJ4e2AURݨT5ZBRQ4u9̓IKhhitݕNWGw Ljg(gwLӋT071oUX**| J&*/Tު UUT^S}FU3S ԖUPSSg;goT?~YYLOCQ_ cx,!k u5&|v*=9C3J3WRf?qtN (~))4L1e\kXHQG6EYAJ'\'GgSSݧ M=:.kDwn^Loy}/TmG X $ <5qo</QC]@Caaᄑ.ȽJtq]zۯ6iܟ4)Y3sCQ? 0k߬~OCOg#/c/Wװwa>>r><72Y_7ȷOo_C#dz%gA[z|!?:eAAA!h쐭!ΑiP~aa~ 'W?pX15wCsDDDޛg1O9-J5*>.j<74?.fYXXIlK9.*6nl {/]py.,:@LN8A*%w% yg"/6шC\*NH*Mz쑼5y$3,幄'L Lݛ:v m2=:1qB!Mggfvˬen/kY- BTZ(*geWf͉9+̳ې7ᒶKW-X潬j9(xoʿܔĹdff-[n ڴ VE/(ۻCɾUUMfeI?m]Nmq#׹=TR+Gw- 6 U#pDy  :v{vg/jBFS[b[O>zG499?rCd&ˮ/~јѡ򗓿m|x31^VwwO| (hSЧc3- cHRMz%u0`:o_FIDATxڬAO@әma+\L{AJ"P1f`BB8r0Ql wn%rPZ[23`^ ,NNNeH!pebmnn.dRWGEx0Z,BJy @n@J3e4T"³p]ØqCCC3s;QƉ㴂bt'n@IJ<}0XS~]e:@Vk8qq{{qx" l@i־ Å\.i 0eM Lf\ӴT-}S\jdf;(lT&8l3{# &ȟB$^ @  3f؟}J\IENDB`mx-1.4.7/data/style/scroll-button-up-active.png000066400000000000000000000063671201047117600214430ustar00rootroot00000000000000PNG  IHDRw= pHYs   OiCCPPhotoshop ICC profilexڝSgTS=BKKoR RB&*! J!QEEȠQ, !{kּ> H3Q5 B.@ $pd!s#~<<+"x M0B\t8K@zB@F&S`cbP-`'{[! eDh;VEX0fK9-0IWfH  0Q){`##xFW<+*x<$9E[-qWW.(I+6aa@.y24x6_-"bbϫp@t~,/;m%h^ uf@Wp~<5j>{-]cK'Xto(hw?G%fIq^D$.Tʳ?D*A, `6B$BB dr`)B(Ͱ*`/@4Qhp.U=pa( Aa!ڈbX#!H$ ɈQ"K5H1RT UH=r9\F;2G1Q= C7F dt1r=6Ыhڏ>C03l0.B8, c˱" VcϱwE 6wB aAHXLXNH $4 7 Q'"K&b21XH,#/{C7$C2'ITFnR#,4H#dk9, +ȅ3![ b@qS(RjJ4e2AURݨT5ZBRQ4u9̓IKhhitݕNWGw Ljg(gwLӋT071oUX**| J&*/Tު UUT^S}FU3S ԖUPSSg;goT?~YYLOCQ_ cx,!k u5&|v*=9C3J3WRf?qtN (~))4L1e\kXHQG6EYAJ'\'GgSSݧ M=:.kDwn^Loy}/TmG X $ <5qo</QC]@Caaᄑ.ȽJtq]zۯ6iܟ4)Y3sCQ? 0k߬~OCOg#/c/Wװwa>>r><72Y_7ȷOo_C#dz%gA[z|!?:eAAA!h쐭!ΑiP~aa~ 'W?pX15wCsDDDޛg1O9-J5*>.j<74?.fYXXIlK9.*6nl {/]py.,:@LN8A*%w% yg"/6шC\*NH*Mz쑼5y$3,幄'L Lݛ:v m2=:1qB!Mggfvˬen/kY- BTZ(*geWf͉9+̳ې7ᒶKW-X潬j9(xoʿܔĹdff-[n ڴ VE/(ۻCɾUUMfeI?m]Nmq#׹=TR+Gw- 6 U#pDy  :v{vg/jBFS[b[O>zG499?rCd&ˮ/~јѡ򗓿m|x31^VwwO| (hSЧc3- cHRMz%u0`:o_F"IDATxԕA?n$b 9+A A+m4ZYRڥ5I#G ܞ٬a v޼~yQ)ZQ.[R | JAVKRG׶rt:e2䄞| E"R ]fBdz<.\M(} 6_:{]qЮ$+d2RD*cpmlxl-(ͻ< "RrP?jl8"7@ܶmt ۶0+oY[VFZEQEVхmFQܥ, - øU}!D>6mxrkZu8sݵ^^bd2Qk1 ɜL0VG( *uzn\~yрv]o47Ajٿ7 G>?T*c21MBg5pvs"dA:IENDB`mx-1.4.7/data/style/scroll-button-up-disabled.png000066400000000000000000000064571201047117600217370ustar00rootroot00000000000000PNG  IHDRw= pHYs   OiCCPPhotoshop ICC profilexڝSgTS=BKKoR RB&*! J!QEEȠQ, !{kּ> H3Q5 B.@ $pd!s#~<<+"x M0B\t8K@zB@F&S`cbP-`'{[! eDh;VEX0fK9-0IWfH  0Q){`##xFW<+*x<$9E[-qWW.(I+6aa@.y24x6_-"bbϫp@t~,/;m%h^ uf@Wp~<5j>{-]cK'Xto(hw?G%fIq^D$.Tʳ?D*A, `6B$BB dr`)B(Ͱ*`/@4Qhp.U=pa( Aa!ڈbX#!H$ ɈQ"K5H1RT UH=r9\F;2G1Q= C7F dt1r=6Ыhڏ>C03l0.B8, c˱" VcϱwE 6wB aAHXLXNH $4 7 Q'"K&b21XH,#/{C7$C2'ITFnR#,4H#dk9, +ȅ3![ b@qS(RjJ4e2AURݨT5ZBRQ4u9̓IKhhitݕNWGw Ljg(gwLӋT071oUX**| J&*/Tު UUT^S}FU3S ԖUPSSg;goT?~YYLOCQ_ cx,!k u5&|v*=9C3J3WRf?qtN (~))4L1e\kXHQG6EYAJ'\'GgSSݧ M=:.kDwn^Loy}/TmG X $ <5qo</QC]@Caaᄑ.ȽJtq]zۯ6iܟ4)Y3sCQ? 0k߬~OCOg#/c/Wװwa>>r><72Y_7ȷOo_C#dz%gA[z|!?:eAAA!h쐭!ΑiP~aa~ 'W?pX15wCsDDDޛg1O9-J5*>.j<74?.fYXXIlK9.*6nl {/]py.,:@LN8A*%w% yg"/6шC\*NH*Mz쑼5y$3,幄'L Lݛ:v m2=:1qB!Mggfvˬen/kY- BTZ(*geWf͉9+̳ې7ᒶKW-X潬j9(xoʿܔĹdff-[n ڴ VE/(ۻCɾUUMfeI?m]Nmq#׹=TR+Gw- 6 U#pDy  :v{vg/jBFS[b[O>zG499?rCd&ˮ/~јѡ򗓿m|x31^VwwO| (hSЧc3- cHRMz%u0`:o_FZIDATxڬn@ .(mI){W)9}W@BHUCoŎ"LDUq,6!FYٙ3ADH$8>>f>DFD`1,?Fo9ib!?ZkBwo(\.¶-۶JcN.f8??k4|>vޤi%}L+cxeBDONN08Ϟ@IYh06Wc,8&< 115-X(5rR G_[0 c ݑ9>}8UjH۝e,"ڝ΃>#J!-p\~vJ>|߇{BLo7 4Mdvvd- ٳ]-99"32:8T]yDԕRF8sTJdHJٸ]Ksꞇv]WIkė,w}_ӪV?h B[iR A6>1#njpdR{A*rXcEojg?IENDB`mx-1.4.7/data/style/scroll-button-up-hover.png000066400000000000000000000064241201047117600213050ustar00rootroot00000000000000PNG  IHDRw= pHYs   OiCCPPhotoshop ICC profilexڝSgTS=BKKoR RB&*! J!QEEȠQ, !{kּ> H3Q5 B.@ $pd!s#~<<+"x M0B\t8K@zB@F&S`cbP-`'{[! eDh;VEX0fK9-0IWfH  0Q){`##xFW<+*x<$9E[-qWW.(I+6aa@.y24x6_-"bbϫp@t~,/;m%h^ uf@Wp~<5j>{-]cK'Xto(hw?G%fIq^D$.Tʳ?D*A, `6B$BB dr`)B(Ͱ*`/@4Qhp.U=pa( Aa!ڈbX#!H$ ɈQ"K5H1RT UH=r9\F;2G1Q= C7F dt1r=6Ыhڏ>C03l0.B8, c˱" VcϱwE 6wB aAHXLXNH $4 7 Q'"K&b21XH,#/{C7$C2'ITFnR#,4H#dk9, +ȅ3![ b@qS(RjJ4e2AURݨT5ZBRQ4u9̓IKhhitݕNWGw Ljg(gwLӋT071oUX**| J&*/Tު UUT^S}FU3S ԖUPSSg;goT?~YYLOCQ_ cx,!k u5&|v*=9C3J3WRf?qtN (~))4L1e\kXHQG6EYAJ'\'GgSSݧ M=:.kDwn^Loy}/TmG X $ <5qo</QC]@Caaᄑ.ȽJtq]zۯ6iܟ4)Y3sCQ? 0k߬~OCOg#/c/Wװwa>>r><72Y_7ȷOo_C#dz%gA[z|!?:eAAA!h쐭!ΑiP~aa~ 'W?pX15wCsDDDޛg1O9-J5*>.j<74?.fYXXIlK9.*6nl {/]py.,:@LN8A*%w% yg"/6шC\*NH*Mz쑼5y$3,幄'L Lݛ:v m2=:1qB!Mggfvˬen/kY- BTZ(*geWf͉9+̳ې7ᒶKW-X潬j9(xoʿܔĹdff-[n ڴ VE/(ۻCɾUUMfeI?m]Nmq#׹=TR+Gw- 6 U#pDy  :v{vg/jBFS[b[O>zG499?rCd&ˮ/~јѡ򗓿m|x31^VwwO| (hSЧc3- cHRMz%u0`:o_F?IDATxڴAk@& {UZ<,떂~'? "ăԦ'U=xV':Wl5_]_cddu}(/00kp]ukrCˋ>ˋ~2 5H0)3E\e"eRuۜ@8g;y<"z,(@ΝkXv^Wt1\ٸ|;8% N XVTǙ`v?NLd,J>M"L1K:w @ `&IsHK*=C|IENDB`mx-1.4.7/data/style/scroll-button-up.png000066400000000000000000000065071201047117600201660ustar00rootroot00000000000000PNG  IHDRw= pHYs   OiCCPPhotoshop ICC profilexڝSgTS=BKKoR RB&*! J!QEEȠQ, !{kּ> H3Q5 B.@ $pd!s#~<<+"x M0B\t8K@zB@F&S`cbP-`'{[! eDh;VEX0fK9-0IWfH  0Q){`##xFW<+*x<$9E[-qWW.(I+6aa@.y24x6_-"bbϫp@t~,/;m%h^ uf@Wp~<5j>{-]cK'Xto(hw?G%fIq^D$.Tʳ?D*A, `6B$BB dr`)B(Ͱ*`/@4Qhp.U=pa( Aa!ڈbX#!H$ ɈQ"K5H1RT UH=r9\F;2G1Q= C7F dt1r=6Ыhڏ>C03l0.B8, c˱" VcϱwE 6wB aAHXLXNH $4 7 Q'"K&b21XH,#/{C7$C2'ITFnR#,4H#dk9, +ȅ3![ b@qS(RjJ4e2AURݨT5ZBRQ4u9̓IKhhitݕNWGw Ljg(gwLӋT071oUX**| J&*/Tު UUT^S}FU3S ԖUPSSg;goT?~YYLOCQ_ cx,!k u5&|v*=9C3J3WRf?qtN (~))4L1e\kXHQG6EYAJ'\'GgSSݧ M=:.kDwn^Loy}/TmG X $ <5qo</QC]@Caaᄑ.ȽJtq]zۯ6iܟ4)Y3sCQ? 0k߬~OCOg#/c/Wװwa>>r><72Y_7ȷOo_C#dz%gA[z|!?:eAAA!h쐭!ΑiP~aa~ 'W?pX15wCsDDDޛg1O9-J5*>.j<74?.fYXXIlK9.*6nl {/]py.,:@LN8A*%w% yg"/6шC\*NH*Mz쑼5y$3,幄'L Lݛ:v m2=:1qB!Mggfvˬen/kY- BTZ(*geWf͉9+̳ې7ᒶKW-X潬j9(xoʿܔĹdff-[n ڴ VE/(ۻCɾUUMfeI?m]Nmq#׹=TR+Gw- 6 U#pDy  :v{vg/jBFS[b[O>zG499?rCd&ˮ/~јѡ򗓿m|x31^VwwO| (hSЧc3- cHRMz%u0`:o_FrIDATxڴkQ?o7KP=@$Ƀ=@Ӵ ă"Ԛ޼^z Aڃ)bM6Mv7-" #%bחɊ" "(A);MR-˺l"SjO}{LC&ajjfvtt6rpN>$":t4]ӯJ1K8eݼ6:Cb19m۷K)TalT}1h*@wZ~هvo]|j]Z]` O= boZZQlZ8Iy 1gV,W|k1=: 2vgKeB^ zȎ㐝#%syAq6ǥ]$.NW bjH# äeY%Z#"E& ?1 jF6Z66Naxh0vpW @gd&t.?11s]w_1GX*^lll|E$LR0o4H@[AZ1+0S]@/1g#ZhbIENDB`mx-1.4.7/data/style/scroll-hbackground-disabled.png000066400000000000000000000007321201047117600222570ustar00rootroot00000000000000PNG  IHDR@JDtEXtSoftwareAdobe ImageReadyqe<|IDATx?N0iҴRHR&b@<@N Ȁ-H Z Ig7* ''}yl.={L-M07)쇣 sf.J*n ̫9h81fm {();-l6Q!&6V]F!DE(JץxkfqAxJNB A E ywuA]ˍ Supp`6k-~ R0H)kd-fӌf~$!,_ce?{Ǔb=M)oi7|>4kp 0_ WwIENDB`mx-1.4.7/data/style/scroll-hbackground.png000066400000000000000000000007321201047117600205120ustar00rootroot00000000000000PNG  IHDR@JDtEXtSoftwareAdobe ImageReadyqe<|IDATx?N0iҴRHR&b@<@N Ȁ-H Z Ig7* ''}yl.={L-M07)쇣 sf.J*n ̫9h81fm {();-l6Q!&6V]F!DE(JץxkfqAxJNB A E ywuA]ˍ Supp`6k-~ R0H)kd-fӌf~$!,_ce?{Ǔb=M)oi7|>4kp 0_ WwIENDB`mx-1.4.7/data/style/scroll-hhandle-active.png000066400000000000000000000010161201047117600210730ustar00rootroot00000000000000PNG  IHDR@JDtEXtSoftwareAdobe ImageReadyqe<IDATxJQό|#٨F pAD=@[IENDB`mx-1.4.7/data/style/scroll-hhandle-hover.png000066400000000000000000000010351201047117600207440ustar00rootroot00000000000000PNG  IHDR@JDtEXtSoftwareAdobe ImageReadyqe<IDATx옿JAI0&)5E04Q,| Z ;v!nS3'y |vogn`(elC9I6KndNgZޙ4 #MY$#n%ou:?7RVB"?AFтs~^(%JrjJ& W+% p, N p4opjٶ}zxp&j1eYgwa:.'Ȝ'vS^54[vzGZ z-e'2R!ĻQ\€bLd{o^+ 1|-9|>'vTn_{H?AE'6d9bHO?;1j5fͩI /XJd?8y3`3hMM6E IENDB`mx-1.4.7/data/style/scroll-hhandle.png000066400000000000000000000010441201047117600176230ustar00rootroot00000000000000PNG  IHDR@JDtEXtSoftwareAdobe ImageReadyqe<IDATxXNAݽN .?M /0R bq  Gȩn;voMyo t]`a"R+#,&l6 +!a؟L&NS!VkZζm,KEy 랶'z.! U UJr^ Sz7 TC񂉫d}- ÀPJ!3݈ `s m4`ֆ/-; LiyX?Y-ϩ ; 뼎Fwv-FE%S{.L[JwFqV,/LӬpΕ\ -x|8XTHt)Q}#~?tKx5UQ#-o[7@`1IENDB`mx-1.4.7/data/style/scroll-vbackground-disabled.png000066400000000000000000000061201201047117600222720ustar00rootroot00000000000000PNG  IHDR?v pHYs   OiCCPPhotoshop ICC profilexڝSgTS=BKKoR RB&*! J!QEEȠQ, !{kּ> H3Q5 B.@ $pd!s#~<<+"x M0B\t8K@zB@F&S`cbP-`'{[! eDh;VEX0fK9-0IWfH  0Q){`##xFW<+*x<$9E[-qWW.(I+6aa@.y24x6_-"bbϫp@t~,/;m%h^ uf@Wp~<5j>{-]cK'Xto(hw?G%fIq^D$.Tʳ?D*A, `6B$BB dr`)B(Ͱ*`/@4Qhp.U=pa( Aa!ڈbX#!H$ ɈQ"K5H1RT UH=r9\F;2G1Q= C7F dt1r=6Ыhڏ>C03l0.B8, c˱" VcϱwE 6wB aAHXLXNH $4 7 Q'"K&b21XH,#/{C7$C2'ITFnR#,4H#dk9, +ȅ3![ b@qS(RjJ4e2AURݨT5ZBRQ4u9̓IKhhitݕNWGw Ljg(gwLӋT071oUX**| J&*/Tު UUT^S}FU3S ԖUPSSg;goT?~YYLOCQ_ cx,!k u5&|v*=9C3J3WRf?qtN (~))4L1e\kXHQG6EYAJ'\'GgSSݧ M=:.kDwn^Loy}/TmG X $ <5qo</QC]@Caaᄑ.ȽJtq]zۯ6iܟ4)Y3sCQ? 0k߬~OCOg#/c/Wװwa>>r><72Y_7ȷOo_C#dz%gA[z|!?:eAAA!h쐭!ΑiP~aa~ 'W?pX15wCsDDDޛg1O9-J5*>.j<74?.fYXXIlK9.*6nl {/]py.,:@LN8A*%w% yg"/6шC\*NH*Mz쑼5y$3,幄'L Lݛ:v m2=:1qB!Mggfvˬen/kY- BTZ(*geWf͉9+̳ې7ᒶKW-X潬j9(xoʿܔĹdff-[n ڴ VE/(ۻCɾUUMfeI?m]Nmq#׹=TR+Gw- 6 U#pDy  :v{vg/jBFS[b[O>zG499?rCd&ˮ/~јѡ򗓿m|x31^VwwO| (hSЧc3- cHRMz%u0`:o_F{IDATxJ0f37<) aH@l|o^r ΓCtvxqb@@ =j> 5kkh) s]11:BO&yY\Xk* R'ah4 RʅBkMR|| \VEQER=Bdiv8{/U-Q@ReMOv H3Q5 B.@ $pd!s#~<<+"x M0B\t8K@zB@F&S`cbP-`'{[! eDh;VEX0fK9-0IWfH  0Q){`##xFW<+*x<$9E[-qWW.(I+6aa@.y24x6_-"bbϫp@t~,/;m%h^ uf@Wp~<5j>{-]cK'Xto(hw?G%fIq^D$.Tʳ?D*A, `6B$BB dr`)B(Ͱ*`/@4Qhp.U=pa( Aa!ڈbX#!H$ ɈQ"K5H1RT UH=r9\F;2G1Q= C7F dt1r=6Ыhڏ>C03l0.B8, c˱" VcϱwE 6wB aAHXLXNH $4 7 Q'"K&b21XH,#/{C7$C2'ITFnR#,4H#dk9, +ȅ3![ b@qS(RjJ4e2AURݨT5ZBRQ4u9̓IKhhitݕNWGw Ljg(gwLӋT071oUX**| J&*/Tު UUT^S}FU3S ԖUPSSg;goT?~YYLOCQ_ cx,!k u5&|v*=9C3J3WRf?qtN (~))4L1e\kXHQG6EYAJ'\'GgSSݧ M=:.kDwn^Loy}/TmG X $ <5qo</QC]@Caaᄑ.ȽJtq]zۯ6iܟ4)Y3sCQ? 0k߬~OCOg#/c/Wװwa>>r><72Y_7ȷOo_C#dz%gA[z|!?:eAAA!h쐭!ΑiP~aa~ 'W?pX15wCsDDDޛg1O9-J5*>.j<74?.fYXXIlK9.*6nl {/]py.,:@LN8A*%w% yg"/6шC\*NH*Mz쑼5y$3,幄'L Lݛ:v m2=:1qB!Mggfvˬen/kY- BTZ(*geWf͉9+̳ې7ᒶKW-X潬j9(xoʿܔĹdff-[n ڴ VE/(ۻCɾUUMfeI?m]Nmq#׹=TR+Gw- 6 U#pDy  :v{vg/jBFS[b[O>zG499?rCd&ˮ/~јѡ򗓿m|x31^VwwO| (hSЧc3- cHRMz%u0`:o_FIDATx?K@Us.݊..-NN~G{BmicR&Ҫ/*w} {V9p{@4cG8>8sPk}mLB9,ͺa"R̔ AJ9s+֔J)|ԍO@DQ4{2Y){hB<(\.sb&] Q&/Ŋ#7 ?G 8_8ERbP@JO1,*''TV I>ej]6zYZJk4wq sk>p@sLR~t) IENDB`mx-1.4.7/data/style/scroll-vhandle-active.png000066400000000000000000000062121201047117600211140ustar00rootroot00000000000000PNG  IHDR?v pHYs   OiCCPPhotoshop ICC profilexڝSgTS=BKKoR RB&*! J!QEEȠQ, !{kּ> H3Q5 B.@ $pd!s#~<<+"x M0B\t8K@zB@F&S`cbP-`'{[! eDh;VEX0fK9-0IWfH  0Q){`##xFW<+*x<$9E[-qWW.(I+6aa@.y24x6_-"bbϫp@t~,/;m%h^ uf@Wp~<5j>{-]cK'Xto(hw?G%fIq^D$.Tʳ?D*A, `6B$BB dr`)B(Ͱ*`/@4Qhp.U=pa( Aa!ڈbX#!H$ ɈQ"K5H1RT UH=r9\F;2G1Q= C7F dt1r=6Ыhڏ>C03l0.B8, c˱" VcϱwE 6wB aAHXLXNH $4 7 Q'"K&b21XH,#/{C7$C2'ITFnR#,4H#dk9, +ȅ3![ b@qS(RjJ4e2AURݨT5ZBRQ4u9̓IKhhitݕNWGw Ljg(gwLӋT071oUX**| J&*/Tު UUT^S}FU3S ԖUPSSg;goT?~YYLOCQ_ cx,!k u5&|v*=9C3J3WRf?qtN (~))4L1e\kXHQG6EYAJ'\'GgSSݧ M=:.kDwn^Loy}/TmG X $ <5qo</QC]@Caaᄑ.ȽJtq]zۯ6iܟ4)Y3sCQ? 0k߬~OCOg#/c/Wװwa>>r><72Y_7ȷOo_C#dz%gA[z|!?:eAAA!h쐭!ΑiP~aa~ 'W?pX15wCsDDDޛg1O9-J5*>.j<74?.fYXXIlK9.*6nl {/]py.,:@LN8A*%w% yg"/6шC\*NH*Mz쑼5y$3,幄'L Lݛ:v m2=:1qB!Mggfvˬen/kY- BTZ(*geWf͉9+̳ې7ᒶKW-X潬j9(xoʿܔĹdff-[n ڴ VE/(ۻCɾUUMfeI?m]Nmq#׹=TR+Gw- 6 U#pDy  :v{vg/jBFS[b[O>zG499?rCd&ˮ/~јѡ򗓿m|x31^VwwO| (hSЧc3- cHRMz%u0`:o_FIDATx?Al6sVHX @,'P &̵O27#R&9D @yLU'ZEꏗr:Y9PU j2qD0> =_\׽F3 }>znϻ. Pp\NM|O9''cd>/JT*U OM˶jZ8)_IH~~p|905? P`v ߄" 07 pIENDB`mx-1.4.7/data/style/scroll-vhandle-hover.png000066400000000000000000000062601201047117600207670ustar00rootroot00000000000000PNG  IHDR?v pHYs   OiCCPPhotoshop ICC profilexڝSgTS=BKKoR RB&*! J!QEEȠQ, !{kּ> H3Q5 B.@ $pd!s#~<<+"x M0B\t8K@zB@F&S`cbP-`'{[! eDh;VEX0fK9-0IWfH  0Q){`##xFW<+*x<$9E[-qWW.(I+6aa@.y24x6_-"bbϫp@t~,/;m%h^ uf@Wp~<5j>{-]cK'Xto(hw?G%fIq^D$.Tʳ?D*A, `6B$BB dr`)B(Ͱ*`/@4Qhp.U=pa( Aa!ڈbX#!H$ ɈQ"K5H1RT UH=r9\F;2G1Q= C7F dt1r=6Ыhڏ>C03l0.B8, c˱" VcϱwE 6wB aAHXLXNH $4 7 Q'"K&b21XH,#/{C7$C2'ITFnR#,4H#dk9, +ȅ3![ b@qS(RjJ4e2AURݨT5ZBRQ4u9̓IKhhitݕNWGw Ljg(gwLӋT071oUX**| J&*/Tު UUT^S}FU3S ԖUPSSg;goT?~YYLOCQ_ cx,!k u5&|v*=9C3J3WRf?qtN (~))4L1e\kXHQG6EYAJ'\'GgSSݧ M=:.kDwn^Loy}/TmG X $ <5qo</QC]@Caaᄑ.ȽJtq]zۯ6iܟ4)Y3sCQ? 0k߬~OCOg#/c/Wװwa>>r><72Y_7ȷOo_C#dz%gA[z|!?:eAAA!h쐭!ΑiP~aa~ 'W?pX15wCsDDDޛg1O9-J5*>.j<74?.fYXXIlK9.*6nl {/]py.,:@LN8A*%w% yg"/6шC\*NH*Mz쑼5y$3,幄'L Lݛ:v m2=:1qB!Mggfvˬen/kY- BTZ(*geWf͉9+̳ې7ᒶKW-X潬j9(xoʿܔĹdff-[n ڴ VE/(ۻCɾUUMfeI?m]Nmq#׹=TR+Gw- 6 U#pDy  :v{vg/jBFS[b[O>zG499?rCd&ˮ/~јѡ򗓿m|x31^VwwO| (hSЧc3- cHRMz%u0`:o_FIDATx?AnքT3`yn87 XXB҈XH`%/.IHg!AdwX8 +&#Yf>DQ ǯ>NӔkGG;_1_..pv}0R`3zR}߯ h.˷nyE$L~+@1/NI@Y]2C-Vl/fS]L16wnIENDB`mx-1.4.7/data/style/scroll-vhandle.png000066400000000000000000000063231201047117600176460ustar00rootroot00000000000000PNG  IHDR?v pHYs   OiCCPPhotoshop ICC profilexڝSgTS=BKKoR RB&*! J!QEEȠQ, !{kּ> H3Q5 B.@ $pd!s#~<<+"x M0B\t8K@zB@F&S`cbP-`'{[! eDh;VEX0fK9-0IWfH  0Q){`##xFW<+*x<$9E[-qWW.(I+6aa@.y24x6_-"bbϫp@t~,/;m%h^ uf@Wp~<5j>{-]cK'Xto(hw?G%fIq^D$.Tʳ?D*A, `6B$BB dr`)B(Ͱ*`/@4Qhp.U=pa( Aa!ڈbX#!H$ ɈQ"K5H1RT UH=r9\F;2G1Q= C7F dt1r=6Ыhڏ>C03l0.B8, c˱" VcϱwE 6wB aAHXLXNH $4 7 Q'"K&b21XH,#/{C7$C2'ITFnR#,4H#dk9, +ȅ3![ b@qS(RjJ4e2AURݨT5ZBRQ4u9̓IKhhitݕNWGw Ljg(gwLӋT071oUX**| J&*/Tު UUT^S}FU3S ԖUPSSg;goT?~YYLOCQ_ cx,!k u5&|v*=9C3J3WRf?qtN (~))4L1e\kXHQG6EYAJ'\'GgSSݧ M=:.kDwn^Loy}/TmG X $ <5qo</QC]@Caaᄑ.ȽJtq]zۯ6iܟ4)Y3sCQ? 0k߬~OCOg#/c/Wװwa>>r><72Y_7ȷOo_C#dz%gA[z|!?:eAAA!h쐭!ΑiP~aa~ 'W?pX15wCsDDDޛg1O9-J5*>.j<74?.fYXXIlK9.*6nl {/]py.,:@LN8A*%w% yg"/6шC\*NH*Mz쑼5y$3,幄'L Lݛ:v m2=:1qB!Mggfvˬen/kY- BTZ(*geWf͉9+̳ې7ᒶKW-X潬j9(xoʿܔĹdff-[n ڴ VE/(ۻCɾUUMfeI?m]Nmq#׹=TR+Gw- 6 U#pDy  :v{vg/jBFS[b[O>zG499?rCd&ˮ/~јѡ򗓿m|x31^VwwO| (hSЧc3- cHRMz%u0`:o_FIDATx1A3 ibqp' J:?]H*BBdOB˺;,nX,ܢ}gߛew#"u4$r37I'RRiZ}~,\;D䛵v<ϟzIn}y{hZ\1׏rd2a</ xG1n2jwxHxyɦQrNmp8| <N'w 1 ZLsq%hTu uQJ)io1TAZ|y*:Md@_)e` nk|;z 0_?}dIENDB`mx-1.4.7/data/style/slider-background-disabled.png000066400000000000000000000004401201047117600220670ustar00rootroot00000000000000PNG  IHDRaAtEXtSoftwareAdobe ImageReadyqe<IDATx? 0_ҴQ"'YxN n@?] 5K)|K/(q'1ǫ4E!B<Z ]ghU7ͻaRȖL0(B(,Iz2Gp;!ZR,˭zu|GROUFC%MZIENDB`mx-1.4.7/data/style/slider-background-focus.png000066400000000000000000000004451201047117600214440ustar00rootroot00000000000000PNG  IHDRaAtEXtSoftwareAdobe ImageReadyqe<IDATx1 0_ڨC"=`WAp nD<;BjX%̔dY63A8lac`X3]AjVzG)]%B1 "X`(Ez9w,EZBE>y*:Md@_)e` nk|;z 0_?}dIENDB`mx-1.4.7/data/style/slider-background-hover.png000066400000000000000000000004451201047117600214500ustar00rootroot00000000000000PNG  IHDRaAtEXtSoftwareAdobe ImageReadyqe<IDATx1 0_ڨC"=`WAp nD<;BjX%̔dY63A8lac`X3]AjVzG)]%B1 "X`(Ez9w,EZBE>y*:Md@_)e` nk|;z 0_?}dIENDB`mx-1.4.7/data/style/slider-background.png000066400000000000000000000004451201047117600203270ustar00rootroot00000000000000PNG  IHDRaAtEXtSoftwareAdobe ImageReadyqe<IDATx1 0_ڨC"=`WAp nD<;BjX%̔dY63A8lac`X3]AjVzG)]%B1 "X`(Ez9w,EZBE>y*:Md@_)e` nk|;z 0_?}dIENDB`mx-1.4.7/data/style/slider-handle-active.png000066400000000000000000000007721201047117600207170ustar00rootroot00000000000000PNG  IHDRxl0tEXtSoftwareAdobe ImageReadyqe<IDATxڼAKQ], $X XtJ "~!SݷnՇ6wZ-`(Q*JCKJ "^V콢(&cl$"!5yV;Fii0 Ja:m`Yd2h4Tdr:9zbJSU T&%8^4|[4XU275|lκ 3iZ <Ҭ u_Ba%!32{M"0|k/&(9Nxu% PM4.JB&:WaB^Bf)1AdS{ ȱ=l v"h)8/JBLbKb-EA,b&NӴ3t-EPU5Sj.Il菽Q^ugx/.smW\" 56lSGRUIE0Q!̸HIENDB`mx-1.4.7/data/style/slider-handle-focus.png000066400000000000000000000011031201047117600205500ustar00rootroot00000000000000PNG  IHDRxl0tEXtSoftwareAdobe ImageReadyqe<IDATxڤT=kA>$ᏸ7I" "u p?"B* >%$Ӟ,{L*=8vͼjZH<`mIx4\"%Z\._9EC$_ԯ~l6cI1@cow7( y ?sFtw? _ ~P,L\%$Jbw76-JRD0x 6Hyby^8)d ,stqmٔDŽx=/ZE@t(Y@7SZ{glnd<\.g%Ḙftm3!ZJL9熇 K)ŴM˂-h, 4 TU2 鼲h6}RǶg>eg* غM)( `V߲bxlECq`;쪐4m˲ "56VxK|A)@|,ؾiϺ_KO&3$IR=8@X5٘mtp30NAZ5ڧ> }ꯀFpr_84̆eǾ&n < B#,9Na˞v j2nF~ L5){oB8Q^˙% J.Ÿ&tQ]1Rap^Mڮ76ѥ!OZD1i6uQNs!Yց+v$6Y l ,B2f2*0 >`aQ> 8^;qFI*).+ u@xYaQ`POn8}y^ɄBm!:qՏtuAnox>'nL|Fk߄Iea < \*:qzt~ˮ/]ڇdh ?~>-ucP_볧D !Uٗ4NꇑE{~Bigr$A7N Zה!63=#eO48n>3Wlw\Bʛ[ϢHt'pj)>]8s=qZZ9xqXp9F{bž)޺cwkHhh,. ! \hKȕY^Bo 7_ԟI kLm#&d->Bj-v8fJkl#G?-.N4ғxB`LܧWI_s+w BGw/]oB}0aBۓ`cD' =.G@r[/ոՃy#FM+f0Be2p0r]ki%L62ܯ4ppr"]nP{{gԉ4TS!CWd1v[bFˎٜ8o2w-$ oa~G^ '#pcDpWh_҈ G;;y=< D#hg~^6h PmPfm.ͰGvjF,o([+!?ۅъ^h=p`f_!U_ᐄʠ:]Q5$DŽϘN+D자y:ɛ9x6u:ypXk?o! RΉ4&k$Їԟ'N3=K=v q="&f;|8]ӣSB<Tjǜe(^=tqExS˵zo} %Z8GJ;:qUԼ~Ziu9UZ,Ni&ۨ^3N6.!}f "p(p)V Һ'RGܼθs0*IuFu͊5q'ċ1Df}L}wG=tcO'@Oz1Lv'>c쭅ㄮ]{ZVߓN\pY#}[Ew3-\;CcFprH]͹?HM"{3v1ќ*:l["#{ۍQ 3'X$~cMmVO3牀 kC4spjuZgDM"+F9N:BU)qk!<걪|BEɠ|M.VF6qk'M0,UN~5C3,P|̭S!Α{̐,wB4f/d|1mi3;i%D4Ɯ'# Wx*4| g&pb&N%=";cM| 8GOX1_՛ 3c@$u4[L#'ɭ5H-?h&&q.r>7{*g8x^[]gOFS;6d;yypn Z2 &1FGhAN :3cďKۉ1'1^'K~-:ρ3c# ):Wkod}LL)ͤ[~w t4W! Ш=h¹w]<*֟KˬYcѸhcB],X^QM٢ IEiwV u$цsF}j'甗g&M 1p387t!c2RC5|6uM83 )8]{H}o]n! [ i )6KyA 8OԴ4ԣkg6iI+"Mn!5dS8ΔC8*,8i<|m3K ˽*6cʖU[I &h{O7֞`)nF9.6'zfq._yԅk,t4g?=n&RU$Aw;LXJKaVis1^Fո8%\"Ip_pNwyT?v8w&QrηGa.1vc-*=yL M_!Mٳܩ|1>BIı9D(XoZ$?C4Q4=iʢ:CpgHBN#:R>aJO]쏩l$Mt;! aA\$/z'XH6֒ݻDZ\Le'Vɣb|BW2-ER_tШF,n <~֓3,J{lڥҜq쏥-)tp2 +hCӊ 8\k;Mb?k]my͹yv͹n+af䏥|&u)]qfphrc^Q:OHL&L-jm0/,JY/R5u'pH3[NLfwNgDnȞSQb !!$mcjİؿ.\O,B qܭÊͬ'MXTsy[RRye16A7n1S~ nų±2bYrP$$9g2Ƹm1ƭltZ}H$pT?r37cA,YVpݓ|lyFΙӾv+ +>QkC5I; iY'nѵBFwSrA5\N^e$ѝԆng416mg4"R`uKE 8.J.h-8}d4+H؜oeܬH&u?PTlFxm Ngo ܳa4"mh+H"'}c)m%?hw6v$a={v4dϋ1a&1 lc|4 6##ҤQ}oFИUv67{׊+݂  /kSףw )f `;xNÜvC8 $.8IDNi,e.Lr&|JLixLZɤ6$9t15&i/cn>\k5W1R9ScO?>ca8D$"eڹW51n="n+3n/Գ)nD>c3I!EUrXGWYn7k'*iX~N޽^ {Nd1_"lvj=fB oU̧5J4RJ#ac,M%ߧdi+n !HZ)n%I[ uYpHn:k#Ι{ZOYD$_&s{D-*IR*v"iiŎVLzC6Y yMIX%vNגnVI9UܰRM˅ѥupDu&=NR*I"w#M93 Z*%sʁLw}$uCw1pvI2\̈1 | Ir})dsZRنӌ{Nw҇3^(c|)7#->\gvd+*ܜa;<4Eӛcv");=hQ^(9'Ϙ l;LF:Yx_ b:]s<.UגD6tt wj{$KL"W!;6.367T?ێzwYh dY/6o]"GC׷ g3 KX[q׷pZI ; fHHpS>T󩣮r'g Z&5U?l>h/=$h EvLUZUp?P$\T*$ɈM+jQ3%kb9pޅU;Rn)? IDAT$iz#zHR̅8,MSt8W׷$΀̦Yw\fe32jO_"2\"Sn ,:8/,b!3M`sHW)K8i~^M>&}Cc>|=emך #Z~I=ґcBcnV4cxON/w&KG&iK=[-?]yZpfXcߑ)(2?2;%qX1$QO GZ׊>?ƸNS٬|?iVih#:UmuIM"U4l#ˠ645Leni4H0Mǝ/}[=0:| <c NQ8rY҄ޏ)v;c!Ҭ 1^ImʙK2Β"*þzvVevl:UȘU~ J/qWgtUμ6ifrᔁh#Y|ƐjqNy&X容i&9UK͹8uF9O1isMcQh62GU8#IrɱaE$=} ~VqL8괈& |XƆpY B\5GĹ[IY^,r&,.>^/Ni גF`PL=saUOL LKZƮNq^?OaR_yֹF;mKN& Ʌ"ea'<-ojNYIjt辙45if;E߫UINT)_6m}[+3nɈ"pN\KE~)9֫$b't[vhaO8e:'M(pUܠzUc`sp9ref4Z51cΌl!ǣ {S$I@(:XC_J%7{uJt8RUFsXBSK^sh\N>3Mfo+/U926&6 4{-3!F]V:I\ٰ@{[Hרȴ72jmJFwo6_G^` ;5δڼ? mC~%ˍ̯D&J8I2 u4'p#ou8Y ̙LZJyl3Nڛ08p*$i4|~IuER@8>CPbiw.w䭐IvC)BY.L\>,3.B.qfygUUél{lXgi¼֑j8QI@Yذ޵&׺:iw\䴓Gf/[L9 gJfKHMu$p#d3IRn< ά4x-RH\jg iiwwxVڞ"ߪlw1=(-tfUq@Soy|O֝ܺI%*XπŦNYtc'RsOqÓ,lgR|#6&\Ja*Nvowv]͓jD];p|'Ml#ߜtTu(٤มp^nN\iYG?[f˶KNE*t>N]$QG'yc?{yv%mr#yYT&Όpy=)g-uK$?O!a.٘Ip"I:qN3v;:XF<\!!Ird_9I#6^δp0>X+Op$uEUpEys^C#Q]ΚB4"ou|M}z4Xe9: 9:'؞4 T7S$S3seޞ40i;g4,P&3Nxޜ׌Sf=_'w Qټ:3V|6 H[-PUikHҠ|.kǙP/$ Z0ߙH \5Z{H)Y%IɸXET@s[ւ]tf=Y_I-"sfN])sO_hFqC?b-[#3 ͍[rISOظ=CqI|K}XyDƭ_f$pd_!Im|h$&f>_+WfHmU3wh. A38 I(G a1ne|omͷ6|omͷ6|o?c2PIENDB`mx-1.4.7/data/style/toggle-background-active.png000066400000000000000000000016331201047117600215770ustar00rootroot00000000000000PNG  IHDRb(tEXtSoftwareAdobe ImageReadyqe<=IDATxZAOA~]tĨ &UVodK?iПS&s5-M/!Dk1E B+}; fMdM7{3$H'7'R誛D}G=\O'">j mfU=T^* lˆzz(2FŞ$IZe"Z5/ DIAժQ]g˲wA8H$ItWD6@P:Mm P"+k'vR@~D(pBqH@Ӫx35M"C8X,83004Fuײn Zm(wJN!hO4e{"p+jrN 8⠆T2 O܁q_ }3? Z +++J}m,).RFݜbNpG v%"FJ>!ZkM&>SS5G]YQ(+£+v0]*l|&O"-t_nO;%Pbwc`& H~L]o{eqq~?4q5/qx(g4FFFݗ9_(^D,й8Iߺ<3;׉Movr(4xQq]*B Rgʍe yD)PU4qw w kKKKуyxgw U@|P[ X9MFxl B޺*E kTJMSJۤ͠IENDB`mx-1.4.7/data/style/toggle-background-disabled.png000066400000000000000000000014741201047117600220760ustar00rootroot00000000000000PNG  IHDRb(tEXtSoftwareAdobe ImageReadyqe<IDATxZKkQ>Ό1K`"*t)H_]W(QDW]E "&g}9qjZ3[˝9y3:%HMq"HnЈH2D#`{.\?^~L&æxuutuj,x^ptta6fMjbMpb^^^:Ptsggz $ -JLb1|ssFId7nm"@ZOO$,ZT 2?SMovKA1H$Vӓui]dL&Sp^513c͕I4Ӭy?t jA! HJrsf&b !srl6jP(pD$akc d2 p{{ l V@L`|!rJhŖ,[eyZ -Af-|zzjtDFlaH0 (D|G AUUMY **Rnu0[($ąP{p::z(GT2ş4Mscf>R6vMMٚ-Oa%`IENDB`mx-1.4.7/data/style/toggle-background.png000066400000000000000000000016301201047117600203230ustar00rootroot00000000000000PNG  IHDRb(tEXtSoftwareAdobe ImageReadyqe<:IDATxYOOAmiحlKB=PkP I"~&~?', x1TP?ifVmFddm;߼vf:$ J}}(Q(+B%tP>]U-Ǭ;3GEml6TKxQP(M^&) 2`q)IV$YUkxaYփd @ΊF`EKϖj@$q$ Κ4A'۴TH]${'H>{m)ɸn!# ` P(M} -,\c:l'+Jp;oUU{ " mJM4wIAIBC&qv;h u6xD- ]auuRl6yKNÜ`MPݥ}')"׼㋰+5IScl\lOM!'vUMcT .*ӫE8fm:'\" !z3pvviBP${k$LLL?p?e|6t00/Ʃ@xfff({FFFFX$;/,_DS,0I_:{kfķ{5BӴ6*ܨ_Z@#"%V0D.VsOـ⯢JBHxupph |S,Tb_~cTJ) }-"J9 KM;wqup&->gXk9IENDB`mx-1.4.7/data/style/toggle-handle-active.png000066400000000000000000000011441201047117600207100ustar00rootroot00000000000000PNG  IHDR2tEXtSoftwareAdobe ImageReadyqe<IDATx1KP%bQ PqR_AJG šإuN*(RU4yw4H%޽{h@dIRнJɤN$0rf/4M+0rd22 Ct:neV;>z&9/m=OALaowLLZ3:f躾6P](Fbä&VLÈmh47{ ƾL ZH"QXld/dY.zpK/'uH}\bGѥ&_x ڎ"ƶM=Ԏ|!5o{ !{NJ5 b'9?<=]QZVWz\ׅwxy}~I 4\.JG\0*E%z8}۽78Z@KV+ j~58|NMIIEJiJ(*g$ 1H`IENDB`mx-1.4.7/data/style/toggle-handle-disabled.png000066400000000000000000000011521201047117600212030ustar00rootroot00000000000000PNG  IHDR2tEXtSoftwareAdobe ImageReadyqe< IDATx̘NA> B!:J`HĨ+ٛBHg#nKP$sݙ5pR,u4Zmֲ#0\8߾r&FK!SLљFz=h6?szQ*}n{S{< 1Ӏѓ< d+$i0Zm'@ &+w6EPl)Qx4 Uǖ 4*fLxZ5mly[xqZf b C :ra /ZonEb:h6W,0BKתHvz%kz^7TjV0_p 7a^e%4vb %8'n4M n߇n{O ao?>NS8`f9` |]l6}Fv'1 \`ۜW"0tlB^>t:z3PjaMڈA.膡|+8fYg4o8oRҪ'Q!5-R_dҘ!ICa.Ug\dȂ` XhU wHA"" (8)N}QUyFs"+"V15 Ija'V$L*M * ".WDeEDAӉ>"[VB-SʆŞ|`+vrU ё*=vTﵰ_O@p1fydኬ\nuA phZHh4l"x^Co5s<!H xJbL%,:ىv*];M.ۓD"<ɪ̕PF~ GUIENDB`mx-1.4.7/data/style/toggle-handle-hover.png000066400000000000000000000011441201047117600205600ustar00rootroot00000000000000PNG  IHDR2tEXtSoftwareAdobe ImageReadyqe<IDATx1KP%bQ PqR_AJG š(n Cj4ywE2IZxJ\r߻wɽFPQuTTT #1` Q.7앦iX;絺yV{! xRCBL&U`vZV߲j@)3eXL"y`J>j68tJkup{k | I'7o;6\3;:Լ-žu")B>?z51ZsT*r30p?q;m^á/&׊Z]1l\TuD!8= N(YT:  TH83tt4^[ oIENDB`mx-1.4.7/data/style/toggle-handle.png000066400000000000000000000012231201047117600174350ustar00rootroot00000000000000PNG  IHDR2tEXtSoftwareAdobe ImageReadyqe<5IDATxԘ@gw%LqcH8]"o'pKa0)S;L:+8RX>K+eFS hV :G^]5F4@zkf3N`0A)rjNSmA$%6` ^_\@PN&klzylBSj ',x0\xm yXTEcsǗKbz_L/ -ILbZ"1֦F:>$4 ЇgI=J!k}4QYとNwXu>ڪBjZ DL  Q)LWUڤ5NWexJvPn@$~{ޗPFn96rhHi8vWF]?h4MkʫJWѮ /%AO'l.`B]Z2asFҙkO}%IENDB`mx-1.4.7/data/style/toolbar-background.png000066400000000000000000000002671201047117600205110ustar00rootroot00000000000000PNG  IHDR?ytEXtSoftwareAdobe ImageReadyqe<YIDATxbz`b@, l$dHu{HUn?X?>,(3݇ѣg|p6IENDB`mx-1.4.7/data/style/toolbar-button-hover.png000066400000000000000000000005301201047117600210170ustar00rootroot00000000000000PNG  IHDR937sRGB pHYs  tIME!29bIDAThA0N0(F4, "S >j[<QiCuoz pgi] c>Y/Ӹ2x gn΄a-LC8u* kar/Tn °2}h`iwo82 ]> Mx-GTK Reference Manual for mx-gtk &version; 2009, 2010 Intel Corporation API Reference GTK widgets Index of all symbols mx-1.4.7/docs/reference/libmx-gtk/mx-gtk-overrides.txt000066400000000000000000000000001201047117600226700ustar00rootroot00000000000000mx-1.4.7/docs/reference/libmx-gtk/version.xml.in000066400000000000000000000000151201047117600215420ustar00rootroot00000000000000@MX_VERSION@ mx-1.4.7/docs/reference/libmx/000077500000000000000000000000001201047117600161475ustar00rootroot00000000000000mx-1.4.7/docs/reference/libmx/Makefile.am000066400000000000000000000075101201047117600202060ustar00rootroot00000000000000## Process this file with automake to produce Makefile.in # We require automake 1.6 at least. AUTOMAKE_OPTIONS = 1.6 # This is a blank Makefile.am for using gtk-doc. # Copy this to your project's API docs directory and modify the variables to # suit your project. See the GTK+ Makefiles in gtk+/docs/reference for examples # of using the various options. # The name of the module, e.g. 'glib'. DOC_MODULE=mx # Uncomment for versioned docs and specify the version of the module, e.g. '2'. #DOC_MODULE_VERSION=2 # The top-level SGML file. You can change this if you want to. DOC_MAIN_SGML_FILE=$(DOC_MODULE)-docs.sgml # The directory containing the source code. Relative to $(srcdir). # gtk-doc will search all .c & .h files beneath here for inline comments # documenting the functions and macros. # e.g. DOC_SOURCE_DIR=../../../gtk DOC_SOURCE_DIR=../../../mx # Extra options to pass to gtkdoc-scangobj. Not normally needed. SCANGOBJ_OPTIONS= # Extra options to supply to gtkdoc-scan. # e.g. SCAN_OPTIONS=--deprecated-guards="GTK_DISABLE_DEPRECATED" SCAN_OPTIONS=--deprecated-guards="MX_DISABLE_DEPRECATED" --rebuild-types # Extra options to supply to gtkdoc-mkdb. # e.g. MKDB_OPTIONS=--sgml-mode --output-format=xml MKDB_OPTIONS=--sgml-mode --output-format=xml --name-space=mx # Extra options to supply to gtkdoc-mktmpl # e.g. MKTMPL_OPTIONS=--only-section-tmpl MKTMPL_OPTIONS= # Extra options to supply to gtkdoc-mkhtml MKHTML_OPTIONS= # Extra options to supply to gtkdoc-fixref. Not normally needed. # e.g. FIXXREF_OPTIONS=--extra-dir=../gdk-pixbuf/html --extra-dir=../gdk/html FIXXREF_OPTIONS= # Used for dependencies. The docs will be rebuilt if any of these change. # e.g. HFILE_GLOB=$(top_srcdir)/gtk/*.h # e.g. CFILE_GLOB=$(top_srcdir)/gtk/*.c HFILE_GLOB=$(top_srcdir)/mx/*.h CFILE_GLOB=$(top_srcdir)/mx/*.c # Header files to ignore when scanning. # e.g. IGNORE_HFILES=gtkdebug.h gtkintl.h IGNORE_HFILES= \ mx.h \ mx-css.h \ mx-gtk.h \ mx-enum-types.h \ mx-marshal.c \ mx-marshal.h \ mx-private.h \ mx-progress-bar-fill.h \ mx-subtexture.h \ mx-path-bar-button.h \ stamp-mx-enum-types.h \ stamp-mx-marshal.h \ xsettings-client.h \ xsettings-common.h \ mx-settings-provider.h \ mx-settings-x11.h \ mx-window-x11.h # Images to copy into HTML directory. # e.g. HTML_IMAGES=$(top_srcdir)/gtk/stock-icons/stock_about_24.png HTML_IMAGES=\ MxBoxLayout-horizontal.png \ MxBoxLayout-vertical.png \ MxExpander-expanded.png \ MxExpander-contracted.png \ MxGrid-3x3.png \ MxGrid-9x1.png \ MxGrid-2cols-column-major.png \ MxGrid-2rows-row-major.png \ MxScrollView.png \ MxTable.png \ MxViewport.png # Extra SGML files that are included by $(DOC_MAIN_SGML_FILE). # e.g. content_files=running.sgml building.sgml changes-2.0.sgml content_files=version.xml # SGML files where gtk-doc abbrevations (#GtkWidget) are expanded # These files must be listed here *and* in content_files # e.g. expand_content_files=running.sgml expand_content_files= # CFLAGS and LDFLAGS for compiling gtkdoc-scangobj with your library. # Only needed if you are using gtkdoc-scangobj to dynamically query widget # signals and properties. # e.g. GTKDOC_CFLAGS=-I$(top_srcdir) -I$(top_builddir) $(GTK_DEBUG_FLAGS) # e.g. GTKDOC_LIBS=$(top_builddir)/gtk/$(gtktargetlib) GTKDOC_CFLAGS=$(MX_CFLAGS) -I$(top_srcdir) -I$(top_builddir) -DMX_COMPILATION GTKDOC_LIBS=$(MX_LIBS) $(top_builddir)/mx/libmx-$(MX_API_VERSION).la # This includes the standard gtk-doc make rules, copied by gtkdocize. include $(top_srcdir)/gtk-doc.make # Other files to distribute EXTRA_DIST += version.xml.in # Files not to distribute # for --rebuild-types in $(SCAN_OPTIONS), e.g. $(DOC_MODULE).types # for --rebuild-sections in $(SCAN_OPTIONS) e.g. $(DOC_MODULE)-sections.txt #DISTCLEANFILES += # Comment this out if you want your docs-status tested during 'make check' #TESTS = $(GTKDOC_CHECK) -include $(top_srcdir)/git.mk mx-1.4.7/docs/reference/libmx/MxBoxLayout-horizontal.png000066400000000000000000000104251201047117600233010ustar00rootroot00000000000000PNG  IHDRHi^sBIT|dtEXtSoftwaregnome-screenshot>IDATx{p\}e߯mْ/H6E\P; @SB@CRg2 ICKHLi&I3$iI  @1X~%V?VZjZ[x컿=+[LamSmz&o ٮ̥~nT""kloq} ;gut{.D䃬봇QNύr:HOvkۮ]C2 &±s`NMigx9=ٚvDHϺt-d+s)G9 O/X uV?>NeHK׺0֖? xFfU\Dd&ں96nNNZœjy_dk1_uZ:xG' M~?s+_a?o/d7q!xϩ.$Gт<3atQ| 5v#'-ngYiTϪ*$ pYu OK#Ͼɑ&QLEq|~w?6Vκ.wx3<GO| k39Yhl1}zlga5m\7T0 Wޞv-Q=Ux(ϡoE䁪tbꕸ\kؘ*/[־g>/,H뷮L 7\ROZjrFgx}L3etԇ )! HJsa|>Kg]s('+WpX3;/@BT&;Q]lj:6mXk|$d ekLU/z;wMκ}=~QAhBmgYXX$gdf|>?Y/ΤEYd8`!FF|gcت\N_{ VKɏuE'oZT,GMmq.)1&r;7@7߼$]xGy=wtk$.6ūutcmG008=44uEykawZ9yvw\ |Br rfuعkwvCm>a?g a9p:[8\S=Bv|t1Mm\nq`[aph=QpGsre^#7/yhx3^b%? 8=0DYq޾QNǜ}Cɂ4zi(/۷_A]}= ;R4E^>@rr ݽ{WOj[Rb<'껂{8Qʼn./r196,rP@0]rg}FkG`i9pX݉k $7=K(_CuSX_^ll1>̊EYX?JceauCgBo᭚N.Xѷ*,➇^Ms3'8gx}ضMVj<Eba&ͧm;XET,aiõdgQS4O5U,k?|!8| %&&U+˴{yC%;w페5ܵ*ôk#45v mME-²M HS=g^}ZrO.6m>GדࢣOye"އ5XrXUCeY. mxX.١?5 )-Jgq~*}Y 0Gl/ݴ HrDŽ+&h{姲>\1Qtz8}qd9ٙfgiߞt;|ε_5 jڋ4B- tT莡3́ \U0߶ z)˛&>.,GLX5Qݾ*V kt8wl+gǶm~w3\ KY$ذv]+&ږ^zdb69Y3{{Got,mJ(I(#O۶qbil?CQN27mZF||>?W([xꇏ#b, #6y;wfU<[P~g\Ego0YXtf )-J?y ɩ#|馵VΗYdtz8T/VQ\\餥ud?7oZUu/M{?w}:O}dy<K9X#==-%%&N;DbccSGItPRFF]x{vl賰 w[MAVRG,zd5SKW_bS >m_p5f_b*ނOyvo9@A~.YwtꊕwJTTJ夫FJK gYQQ$'' >vLmxŹ")10kVW샃^zqR VG5 y=qX7=EN~!1Ӎ`xxuS'dd 3mRSSHMM Zw*}%$&81ۜ>kźbY$dkPWgQaʱFN"^M]fi]^kw5WPsZy/rŪ"|~c,(>{زF1 J?JJ4Vʖm7jk"ʥ+G,x(~+W>NO=J3H(\,׋i|⓷Lx C# guE)"fTib'y@DbVA>q-twupf:/J߁ȬNPt`G"456CDD$RD@)"b1P@( ED "" HRD@)"b1P@( ED "" HRD@)"b1P@( ED "" HRD@)"b1P@( ED "" HRD@)"b1P@( ED "" HRD@)"b1P@( ED "" HRD@)"b1P@( ED "" HRD@)"b1P@( ED "" HRD@)"b1P@( ED "" HRD@)"b1P@( ED "" HRD@)"b1P@( ED "" HRD@)"b1P@( ED "" HRD@)"b5yCWgtwͬjDfkf'(ȹ RD 8ljly߱vyBBDUIp-τUIENDB`mx-1.4.7/docs/reference/libmx/MxBoxLayout-vertical.png000066400000000000000000000116571201047117600227310ustar00rootroot00000000000000PNG  IHDR\0csBIT|dtEXtSoftwaregnome-screenshot>AIDATxytT}m}d@ 6 pBb۱i%v:Ni7[nO'mӤI:Iv6 ر 6#0F.ڥF3 -?B|ё{_ݹܹq4| 0rٳ3v' b >{ެDb@oqgB!#_ {$Imݗ`z}z[v)3;~uIR`$"4OmK׌6XV۹.uxd& "6uHmY#Iً$I>k;ףNU6xMWkOPU%I) :4ڌ޿֩ֆ[418YW 7)wxW[g{F?_~^p\]QT}V'OWno+In[Iϝ Ì'**̟m̶K$f*('#Yc˳5n&mw0>s_֮n? =SstN+JȽzcz׆WzUxNO֓GTQS6[}wxhzX#D IҖMq߉7\(I/9%iúSo&_;_O䩦AGa74׿9)њUM)_T).6VkZ~S{:*?+Ys3_ާs)JJ ^ՙZuvv+ )9ɫ+ mZ۔OXs){z58RJRUJr!z~--ZJy޸&} wuC U\#gx@ ȑ,}y%i~zfRNzd봯Vs?$Z/5Oަ!}{ջV[ֳ="ߍy {匒"Ǒʊ3uS^|uJwռt9*k7uK J6%)}~|ٻ֪0'E1m3ߟ>}*!Σ/%56)0PN>6u /<5Vs/.z%VmN2I敺敒g?f]3nюC?~Cw~F5:K%'q='o:_^zVt;Q7[\뚈nס׫q飷C A~NW?'Y_2G/*6&F-mJ>AKtOg$K޽Ar]3f {{ҹj?ߣrsw_+_UtOqUjҪK %I8V4utke)ɉ:QZޥU:QժDv|աh`8ع[Ft1~Vt9rD3=xr8fefdD =49 W_}W_U.)7J80_wd<5ȘnJocp "fS qz)zm;|JqQJKw$?Mqj}Բ%:vJRzu]eYZxoj'k&L{nK jlh -[|{4 x =#fVsҔ~;pħ̌<y S{w1NMPCc^:'jq r&kIY9vZ7&#oD]ok$V^9Pr>KmPjjyNOPRrR9qgbckgݧ>yKsu]y5D5 (NhPJr3oo?As3٢4~M˙6o)-˶ j:+Ӵu"Z丈YěG5љ5*{Nr4VyYіu*KUYqV6TtNyc]Z Hxт>+bE[6zM6|y.4nIQ2wWc#Gyyyggv5j-WSYqv;OURjQ}5ھ\g"IOTNzCttpE~B%%%rݪoC9;7. *|-TS$N{)).W~tX;rG_m])ܷYYr޽&233B=%9yҹ&+..Nzu%{cUZTv%=}}6 ݯ÷RaNJḫn=ƻkKȑk-rYh+M|q#|1-MIɤxz ;c‚|dgEE5U+Yu xSn}[r?S+(RlLds~UGqq*Si߸K5u =#}Ros]:Y8I q:.]vq%i`p(p] vlц/)hE1++SYYggg); e'|[IIZađu.6GƊ…gWUݰX%irxFCny<}4%oL߽^-rb|z@:gԦ~bnF@;tb:V٤oy]s .ǰw[n׳{Wa;:ONwTMKڼvCa 7k rRm19DWEEr=c+o|6x L~(nM}?l3ej%/ǯOz{M8[wC͸#Wd }we-3ЮEV1֖`'57~>ilG@`@@`@@`@@`@@`@@`@@`@@`@@`@@`@@`@@`@@`@@`@@`@@`@@`@@`@@`@@`@@`@@`@@`@@`@@ K1 y3`@@`@@`I⴪K1pq3`@@`@@`@@`@@`@@`@@`@@`@@`@@`@@`@@`@@`@@`@@`@@`@@`@@`@@`@@`@@`@@`@@`@@`@@`@@ |Ebe%6g  0  0 ]y]E `@@`@@`@@`@@`@@`@@`@@`@@`@@`@@`@@`@@`@@`@@`@@`@@`@@`@@`@@`@@`@@`@@`@@`@@`@@g]]y%@A T[S})\ԓ.WRλcIENDB`mx-1.4.7/docs/reference/libmx/MxExpander-contracted.png000066400000000000000000000135621201047117600230630ustar00rootroot00000000000000PNG  IHDR4b!/sBIT|dtEXtSoftwaregnome-screenshot>IDATx{tSeiӔWJhi)T\d**0qF9sPյFa]osAsA_Q G*{&M?ڤIi)Y+ӝ_' nr!R|s2|ホ~U3{n[íBCPJT/=bOlv>{k_B\IZ:-Q tI޽r>0jKj~0j{Ո·tiq_buIj7*CgB\Z;{in7_ۙ>9bLη1"C/i\aJݏ[~FӘ Cl7.wƷ5vs,,\E|db<[o - ƸTqꚻ814O?~9zW|m1u2SSUryE޿78W:'}wj6(C[wXr:rHh}Mcj>W/|~?y;w~|㕵NNfCϯڲ-;yJnObWǩl8c,S`zZ><#{B,*uΟU%x{LqL<>V]܏(M?`3#.wѨ[زn6 Z+(((w~Zj&Mz'D^F jK%&|4Dwƒ}LUϮI tEG}=B/U{OKH$zf9pO==ήzZy$džgVv}ZCDx(_~u)^BT0sg&a3gQV]K$Jʙ;#?IKȞl!,X[`Iܻb6!:}eıfISoOױzqT7v2)`k8YZ3bmŠ>w.zv|-\4Ϟ` 9&pѼ:.j @Uz`00 UTT֝ZcCfO^f,y׶QV‚{Y9?[F,d{W^c%ssm_ LK "ffeů|C_?V؈ ^Ck{SGM7( Sc8\|W,tU3S6qea¦?܅vRyeMfڇkla cU5D29)AQ~1s>^g`uu ~*`WOQEznXBOx?ΰquls>!HTjU]@Xv=;>| ۈF78GN] >.~ZJSb4ijj^̴XB'iK"BtN+%>. etQ7md$G~kIePF;tR?G1]t,k=9u4I%.~ߖ:l7x5:t:qesӜXV˂tc2PTSOT?𯎉"&:jqTEjsX7i)QX,V*;0 Hu37P5O_wC"[YٚVþYӭOK/;[^ȣߜn<4O`{g04f)1r );?vZmE3hK^*|dOwce(146`SȿuLvU]:z' ~iv~\LzRX8l-ܭ3?;L'3y`S6gkڀ{G')9405t~áh3mKq84tc']۲%9ב ͑(=2EQzg +;؅xiCB  q) ǽ kJv|x8%sީ(pxb{fyLq&DžD|?ECCՍLMgUt@xHS}we!- ug2* @!!Š(ʨp_ glj:z 4.lq.R}Ҫq5,ޙ7p?"::|LL41oLJDD8Nm"ÈIsc `4^mvYLL11Cg5bU㈏sYW+$•'U=i]XRu414quʠU`pj>`/3~Jx~pj ~>s:z M\}fdՖ$&%U[o`NқogGoS3zKocÇ1,YDN#MhF/ܹzV+:%Kef͚ݻ)))opy<;w… h4MBKGL&k׮ĉ!;;???JKKy嗹馛[VN:]wܹsyg='jjj什@mm-۷oW_}(JKKyٿ?999tttCBbw9XG_QAm=5kxc}mX,O>IQQ~!999(BWWFѥC{뮻xضbü Y7<\|hZ^u6ŏ HMM`֭sARRSOa9W(amxc%e>;sXVý^'wx7oKnn.UU #66֩޽{կ~͛yh4իh4h4nfy @UUVXbՊd_#%%UUQUtwws1r/<VwXɉ1.5zڞs^ϬYk׮ԩSNӼ݆і)+›o8p1vAkkeܮ'SUνu$((={?G}^o?3g>mq|az=[laӦM(ӡjߟ͛7vZvE^^ި0|; _/c6#*<5˗f7VQ@f̘1AAA掫( :,;bd8vOFdVݸM(o^!XMXB[fRPU3% M1VВc_,IhBQ|B# M3ȋ!xxmCtCBx{. Ma2.6"W D@@ۄrMGGbB1VDGG}ΥAqq1MMM +S__]]]!(R^^Nkk+===gb"f&MDdd$k6S)Bzz:X?W-V'~8c˱NB'Є>CgHBB IhB! M3$ !|$4!ϐ&Є>CgHBB IhB! M3$ !|$4!ϐ&Є>CgHBB IhB! M3$ !|$4!ϐ&Є>CgHBB IhB! M3$ !|$4!ϐ&Є>CgHBB Z[/z!Eg{h5/z!ES6>zWB!&+18TIENDB`mx-1.4.7/docs/reference/libmx/MxExpander-expanded.png000066400000000000000000000156101201047117600225210ustar00rootroot00000000000000PNG  IHDR4b!/sBIT|dtEXtSoftwaregnome-screenshot>IDATxy|e_Uw,@'D2EDdD#:3^WftF8gqtsDGFAE@od$?B:nY|귞zS[oU5;.׋_VOZskfOP;J!':٬/䫷$6KBskeKTOq=)`4ui^F%8]8{<0ܚԴЖ 꺴!DR^]O:NK'$Od2%[Rgå l3!nv(oz= +fr;A^W4.0eߚ_͝ .Ք_HfV.6y5MV\Qˡ[4nƒ|#-Ӈ%y .ŸRq j8>4g?K ~Wwi]-b@tuv^Y-E^g\ k.Upp#Q.ub4um-4ȏӗb+ךUBs{٩~_ v>.Ur K K޿{1ilj녣-;ۗdBUծWS.&X+gqf/3NqY d+0i0 {_{/2 E<{IQi9駲PU1ɹ|r.[۔/Fo3ץ8Zo?Hz+h4D :2bM?˥A`DQa`L~>rzJ+Ѩ$2"R|A&|=hkBe2-&3?TUY^Lt_(fPH"[[uXQK Gq~7W7s/;Q\kr.lb⑜r)lndK !tx1DhIud4(]oFM[k"_]@ē&;wϿV=z=gv(ww7˴pT\Rwd7ė1 44li>KA\KP|<7|l;#?9h/"ؗ"il2d̳ 7>/-{SX^kyM;ɜs[-{QUgױ<}#c׾C}M=4Ow 6MϷ\ʟ|fsk[ǵnlvh *_UQѨfxl~(`ƭ)P̌[۔;]ɞyiU34߲|rvd}c#kjXSK`B;$設o"ϓgdh/E%e3$*ʚ PU!ap 9945t?%: &|=Y:14җGf$ϓ&1oJGN1;9 /J.=)q,{i'3?l`(e5QPF=NmQi),I Zu2[z3R]6tT -JSEEUZm5>-#"ؗCIJpTWĤhKay{Q80d%~6q=GfNݑ}ۉcy} Lum g蛌fB}X,|HMjY _֮ EQHc <6ks,UL/IC0ad_"&~r:o2j[^Ie+܁Y "*2BQ'vƲ|fV՛zXuB*v)KM=EQPv Y<ܖ~b1]-̖ܢj gq}lw2[eW-idd*0*6Z`f.]85tt-_R5w)V>fgf֤v˨NZs9YSRLQԖ֙ʆyl;rOwϟ>~ved?2 w7 a4-} `K+Ȁ>}PFFf~Gi~| qDŽX6pgK>(L!"F!~iX'eǬGth$V}h|fF% g @U~q.^^TޗǘO_r%Z3w&Ώf#مU,7v0v gЬF:~YܤI#;(s9}:~~~deeax {=CQ]]rL&&ק2ݻ͛73qĖ( QQQp@<=ojf3}8+Yt)IIIhZ4 ۔? x;Lfʂ pssC2{lxgB0gf3477qF-[FLL FO?M]];w: u6e|2gsBeFG2in6( 555]O]]Fr8t~->G;###mb4mΓmzCCN555Y|NN7n'4V{gݏ<=3qsst:>|[CEZSG{KLLݝk2}ts9Kڎ[nz).Y(f-ͪGf9ssvowώ;pJJJ:Epp0e$&&2sL233@ۨ((--e̘1;7nWdB=v@td̘2o4E~XΝKjj*4553KDDaaa{] ћ(˗-5eY}<]>h4Nde1d@/s򈎎dBד^':: @[WL&BUU5e2hll$++&~%D0aj.rO{~`FrN;IrRbYg7Æ V ???VTUˋÇ_qC鑓E)eվ&…!+& MыHBBrO!Dq]Тu9G˄&q)p !čJZhB˧>`0zBBኰ0cvԧJh!UW\\4:lUWWۿ !ĕrzRqtH&4!DWlbuw߹\颢"jjj9s'QB\ZQGyGZh"L&FGsN.kho>~z:UTT멨 ??RKLh0{n:Uǒ멪b׮]_n!DB}hOf޼yĉ'x뭷3gK!󉌌`0PZZʦM[9{,k֬!55D}]kFpp0G^੧BU8k,Ν;tZnuP+C6vɯ~+/s,1<̝;^xUUٶm-o' F#-bŖy/^̱cjmW套^b˖-ݭBr&''$[ZjQQ?&ӎ5ʥO9o߾u:&]ru:]cd6 Zxx8Fua7_G1͖u+uz !:֭qhxo~Ccc#F1Ο@\\1L&^yFAΣ8uѕ:vw6\( 1DEEDDD/]|| 4φ ^x򩧞bӦM1iҤ+Rc+˗-5eQQu< .zcOFb %$$3fȹsPhz|hDW؝B\^^^l&NHuU%YW\NFc9 YG-ccc@tY/!u@g!ĕ>9OrNk8lFnn.FQNB\U&Sv MUUf]F#BQ2k= сJ@uuI MqUWW;=G.%$$PSS#BHMM Xv-4wwwƍǡCpwwTB`c:=Oa`„ ] C8( :NwE*'=Mơ !z IhB^Cא&5$ !z IhB^Cא&5ֶ^[!n4BBZAkY!6erF!D 1-IENDB`mx-1.4.7/docs/reference/libmx/MxGrid-2cols-column-major.png000066400000000000000000000103121201047117600234650ustar00rootroot00000000000000PNG  IHDRO<@VsBIT|dtEXtSoftwaregnome-screenshot>\IDATx{t3BN. R@HERĮ{\m]]k=ݣ-])G[,nt9EE `Q)T!$!1$?L&3}SWxo~0oyg׳-:+x.V/rFImuCO=$|KѢb::=zntKiIcG܇[(i$y}EK=T7pEl֑;yw'uG-II˥;etlZv*MW\η7pcF\gzzj>{%IS71;~:~\^&p,_a(=Yc%({h+I_z|^oCzHnSo r.;[e?/}o̚՗MTTTǎQjJnˑsӦxAIү<՚"*Ȏwjݦau$^NVj:=:NǺgsmnbHt;>Jo)3Uϝ34;?Q/h]acaC 4G'z:(FN ۵8Ǒ#'OSFݶ"ӣOuJS2NN\b.T{G?;՝s,VV=JMKYxRm񨳣]soR\? nyXyI[oHUr'dSZP=K+TD؎>7x}oh-;7Р@.%9#Grrqz3yW'hVm~_{A'UjQ$~*բI꛵Zt$TuZOu?pF[ĩr|Q;9?,KJ^XJ5*IcΖ$7xj6ȝFn 3E7x0oUk7l??8h;7Р$:H>;]_:.gő t^i=-K2T<5K.ن^4}6n9)*y|\zw)׵hD=qZ%INӉSg|YzNYCf=y]QtXWcp^n$gsnֳup hצ;K0s-^+"h!05>JtDsOe>t};p"e'kGY(Ǒ\ 1z32|dUآWjƌ!_SǫX5ن^o@%*)%E&N$WzӣVe%I=LIҧGcxMv<: ~l(s3/@~ J5P`Tw̋Cz76V_\G*tׂɽxjim Im 1Jv9XWE헚zsiͼ@զ{ rYaK`mLbWc;4ΟW,IrupwzMBm ~l(s;ȷps #;sWG8IӎB{Zq}imm[W+KT7*?;Yfֿ[E3 ퟞ֔4 [_yLqX՘1*iSusqIo{ϫ.\ǜA-v>_C@iDE4oάs :@.%n.8^W[@IIs1%q}9q%+JWxZOTiy3NhJc_,I_o~?.^N[VMOx$)91>hH]|m[k pJ]):{77Ѐ}sC9rN[QQQk['0/M/oTbRoݢGW.]VSӝ'kdMv.:p|Ex9.^)5%Eg[jOTK=gp? w,P[u7*::J'k/hպMr\Q8M1]1p)*Qkk<)kqJOs˝g j}^Wy{^[wmUǣ񊏋YpwMQT4cP w=7pvsmh-9\=jz}iJOO?vo[JJRRCINIVrc<^ok۝&ݼLwqE94ص 27аp}jkk}46%' 7/6'Zr;67Р6ղη7r3}.]PK[F@0$ͭ{]ޡK~C4N\UtznɈZW8 s|C$)-EPgy>}o%\|TUVk 0  0  0  0  0  0  0  0  0  0  0  0 :x:}o%0  DHHvGb#϶d䕼>K@7@`@@`@@`@@`@@`@@`@@`@@`@@`@@`@@`@@`@@AtƺH(yu}k g  0s7=quGl"KOGz 0  0  0  0  0  0  0  0  0  0  0  0  0PW~4"}H/@`&BެőXLj+/-mO^JFkЍ3`@@`@@`@@`@@`@@`@@`@@`@@`@@`@@`@@`@@`$"^[Hk g  0saՑXLj;ZZg  0  0  0  0  0  0  0  0  0  0  0  0 :xђ"`T `@@`@@`@@`@@`@@`@@`@@`@@`@@} X0*q gʊHO="9IENDB`mx-1.4.7/docs/reference/libmx/MxGrid-2rows-row-major.png000066400000000000000000000075721201047117600230470ustar00rootroot00000000000000PNG  IHDR!!WsBIT|dtEXtSoftwaregnome-screenshot> IDATx{t3 $B\L@(vD-EZյv=ڲݞrxغgxݢ([*W  ̳$3$3$\Kyy;I\;?TA!J%I흊 {\ixkCgnQTs/ʰH Eoj}$ѻӵom}>=tLfOQ^Sys+&6F{./֢4)!.Fǧ(?Cl#I*0N--mj<{Ns5kT[%IٙV81x\+kUQU.G{Ug_9iO}mm6[=PC{|_k r]{q#GNw-{nO,!.2SU2٧jLʔ/eK;ן_~}gofg[smz۔Ӳʴãz١g`BJw v@Yɚ] r`ueW]*Uwτ.~n36oG;7TgB#9#Gr\9r8 mԲ7:7TTׄ$>ӑu_po:yi[&0USS5a8IRyEu;jimS~v$iסʒ$}rtB_YPs{ gn4 5˱yzcsM(4LjCCd+]35ftlWyw]kDoGũC'^^U}IV7 ѡ:z;r[/(666 9Qr}gvsHb<3<G=Jnnp7v{;p(HsC 嘧'8RW?`_||:u 'x.uoX(I:YӤZO+TFF|rBV} ڼS%G wcj(*ji4X4N~pD47ݓMTb&܀B7ܹYo Ӂ͙5#PQG(9qOK_rX6XIc*%ỳ};}db='2;T2%KKJƫV99YT<)S f{f鯖ޠ4klb kpuL_7kjA%I)I~r^wr&~Cns*\ 5[yߡ~ϑlu|Q}}}_7hc>gNazjZѱZf<MQ|\'2I gjkk#׫c(#'_V΀5]<:֠M7WrXy[{/ݦ5iQ=uHb]?6sC ya5w_A$FefICc>_|= lKMMQjjJ9))J ?H|˖u[̢Lߢ;epVO6_-sCiҲ嫴e5$/ӛ5Z|;WPQ_p[5wޫmomPf,^Щ֐\msCq&\a-m畖ZC9ѩW`y7ik^'i>zuA3]ԥ.1/kki͗?`%^}šg,4XɥWQ6? W;TjZS]U^#IDATxypwϳI6EA JH!MKVTkuUlUx/u#TF%-%;$hN?6y&yrJ+@uDjlOAht_@ϵ~:0,+ͦz4vVFs.@UyL=a=5_v!o:@̜Y3r.0ޤRÏ~#d #5~-;k튀_CX]1SZcv= c3f\h|cv_:y>WO}IgG?M M8lMxnutZ_?FG= 1&HNx鲶>xknGHZڑa΀xĘXmS{5ٱYL }}a맱e{@j|):@(BEӹOt+V-Jýwg>~`UQs6\SMXUol ;vWc| 8$4/)';h}1/4rsnVf6(Z-C˾D`}KrE?N>f|uܲ5+e{an^5+ t:P`R?*:~|7U]I(y(Ią+kv tA~vT@av<  ᝣ5T_DZ.$cQ,e뜳^z.^_C~l}a?T /Tvb:h;a~z줟[y UZz-[Z%R-+PZ&>bDe%\;+ei5P_sVBQ=!ⲏ*0'w~U0E;֠ 6!!@}SKHZ`Fzj2Q H4jې8V27B{M?t=x܉{,&37yjM?n<ıkPmΗ T3Cu:ՌgcSU !?n A@хbшkNFAf8| N0 >f_Gs_V?}--u:̭:~05cjM%^7#` oBCð"6 3z388 KK{omZg^9‚\`߱޸Krpm{# q5ˈƎ:,ps*5/ьy5?=~eo.]\):(cgLIѹ@M9x ?92EzS"~y5BCW%yj=%`mQ*j;=xZ/JrO愇]>UͱxP=ό_057nkEMT8-t},s&!3-nnk} a5O,ej0ID7O۟;|Cב7?$Sۆ;e!+9 Y.T׵ą/8T &:5='qM愇ؙVt^e~.|}^띶+ǿus:-*pZA'<w=.)^ vRSoz *YRGua׶0 (țAWaNt:Ch`g^9!lTF#DF". sBm}"3# U-sx6L90o9wk‚~͞~__jM&i e#e 񳎷"..yXȎmQ:'*: QT|f6lv4 !8|q:z1SgY̔ZSchĊMطHͻQޫ}h9!7ahδZ_70P -SQz>3mϠ#>ى pxu|@xO\!/҂ߩ Qyt3uQ+Bx'{<3򃰒WWm?Y4nh Dc$%(@I4J1Ph Dc$%(@I4J1Ph Dc$%(@I4J1Ph Dc$%(@I4J1Ph Dc$%(@I4J1Ph Dc$%(@I4J1PAU` n`/aZt[\~Dc$%(&)cQI01k*=l J_mkۖXV2 nIDATxyp}cA @ 0+8u'ԮCJ';iW.i8IS7I6R}]HB`HOvՅbg>V|3bDɓ/EDD7?1M3[JDDDHT<l7͍lm '""rgor0,pnhѓƛm4jѽ#:`*n3 Cݺv|zC> 4u]DDƟ[44pǬzQr3u};yW?y¸ }nsSxx/eǁ 8\n7.L!;3{w=MVr rS9~Z2pe%Oz a|ZZIMc\+8u_}klڲxu} yo3l`a􅻷뉌Wfɜ o:?|;=? U,)S `IQ&[w[9GHJEU r;q]˸޹\\Nzoi~cX"n$p4ܴh6<\ @CsEӓZbW|J;t]O uK/bӖ-;(Yht fYAϕ(M]gwzΕ7ӿȲb_5x_kwRw҅~!iV-]t ^ 8YVa+"~k1;^$4 Iwhms6  ~Ak bL@YynֶvRb8z)9{ͯX5֎w\3/;s?6CA}C߻}?H=+*fh́S=?zة!`~v[O ͠c=zT<=c U0ma!!hO,""=Gny{^!"ЧKZLvj,~/y‚\} 93/7ޓɮC1Q3 dFz^Ә6%iShtY=prS  qq^;Ony"9r[kIDATxyp}$H7IQuR-+ˊjFN-Kn餏'He#-+,ʺ )x$~"CJk#`~"kȽ|?-.Iߴ}K=s\ \<#Y7/>9ZBD^lf0ֲqNt̯n6!oJDDn%M.Ο:<嗭A8lO٬|* d@o G]7{hw{Y2/󶖿T_쁇JKӺ0|SCwmW ͤ}M:~-;w2WmEP ;0:iamA\x탿 3iYtToMc5M_7H@'ٙܤزqyd~r?@.\xhJI\Num= .XɇWk͊l_[?`(LQN: ?y|ovR]맽P(Lbb K y8rJ!΂ sq8,mTRS/* +3=kLMIQV%!ΦO^kf'lI)a&SU㧩~\xr}pXt6+'O?edeQ][ϵ뤧@#+:f eԈU#?<7rz&=¶0j.~4kmۑYV$ɇ,";pXOn_(&;3>j/Ɨ& t' ov5y|w7l +%Y\ءAV-gAql|OPGHI9K-l]}qkZd*~܁]wl;hÁrt ?^gj?:`׆صqadw%>y$ Ol[A;;٬^ጋ'"==ćoog<&(I'-eyi6l,z{{L gKGW]},-t vX;e9HHtl^]DCS ˋ,Mޫ<3z'-%+-曤ػ/^CSr=)$YxHI"~b}'*f~,nYw֏"D1:=w[?r˺ӧ4 pDz [5mfϥ<)^Klrƥ -/o҅|iV,fEi6m9r*5V,-$7WM(nr]_MAu8| rѼ1mknopˢOj, .`Ô{|1XMnUaE!2JuQEsޏ;9޶1KRBlkTߠ$/o}6,IGw?n<`i9ϒHO\U۷NAؼ}kvl^ Ab7h_kZe9F7. Mdzu}?6bY..Ŷ,?xdBEY4q:C XR!9EwocyN#9)rBu ˗oߦ®[9Z:uME׍6Iрtm<HrcegxjjR.::Y_:fm!ɉ$'308pNo10Ƕ5|q8VoS:찇~uvv:L%bۑ~eZz:ob.jj3嫖O\яp ĹH@{INú [ع_y(rb9ڷoS|8y( <)-ȉ3a10apGi6Ca) : ].Wƞe~ @7Y!=ymV,[u6ߺB@?3FjHHpq>-差)[:tVFYuK =+oNsZcb۶7+US~>i'ޯ%###H˕ D-X8ƛ!vٕƫⱋ:a\/?W7ޫ+VP(mT4ޭٻ.:{)ENrJygI2ܼU\^}rnaѼ,ˏb 7m_ϑ͇W&onr3jhj"'+DWd=zOVV}-!!m|\oI(&ߛ+S_Off{6HCka=RR_O#dmANJ- h?]|>N'@p<~b]}OJz&O=ZΗUrr|P%kR/MO Wx咞Je?#|cYR$/Nj'6}RSih/8|x "kZrs&9Rj9o頦 ck6,^o<6{,bӪyRQpw/5NI'..Ĥ$.GN|X$ E1W(_zLAFm6?1|T|ՊVL:=V1Ӈ]4 ?􏈥i-'{,yd~hếW_H\\$FMurhl AOo&[JWW7 tuwq\向>in젱^&-u4cոU݆&Hqœnp8LcS3mm ,IKM%77&|NW.trZ57#+W,%>nSeғy'+SȣO;ucmx.=vVyz=x|^|Qm2333-;KvP$w1癬m)),LxycոUݜlrreCnn΄،V-(-'L7!9|[;Fj sU7ڴ EDw Gൗ~Da2o;7;~-v>Ѧ<)"2ݽ]^"4K_ Q1UhAȜ#+=O<370HWO;|RD_qe-_fe-o.}i["͟Fou2?"" HRD@)"b1P@( ED "" HRD@)"b1P@( ED "" HRD@)"b1P@( ED "" HRD@)"b1P@( ED "" HRD@)"b1P@( ED "" HRD@)"b1P@( ED "" HRD@)"b1P@( ED "" HRD@)"b1P@( ED "" HRD@)"b1P@( ED "" HRD@)"b1P@( ED "" HRD@)"b1P@( ED "" HRD@)"b1P@( ED "" HRD@)"b1P@( ED "" HRD@)"b1P@( ED "" HRD@)"b1P@( ED OݍvA}};sfɘ}hLUU^rKp)"b0ӧOъN'>5k֐6g_j TE3dkk+'Odtά hnn7 ywfm=75{$2kTg JlBAAeͨ16o̩Sغu+ezWvDf͝ʣ Ag= #[ZZd=W^phpI*͞={u["2vL/bktv&"b10\Wpu٥kʽNxƀxgIDATxypy߻Xݒmɷeplc0$Mi]2@c2a:cfB 4a0`c9\cF,Flc+vz,Y^AX}W}]9Æ;~u?|Q|g}_\}dr>>m׶k݆T6T;Wbݵ*qJɖ3zg# '}z{B^TKB_7H.tRs-sm|$?}OTr^< sL(5?']5C[u oV^p}x?"Ix@5jVY?e#߿OV\Ds5=]qF L2d᳆skޔ$m\Z.8/:4KҺՋ&4qժ9z*-)TÉSףjn՟><"KhFJ>kбJJLԪK[C,ϕ$5w/tH=> QFZf*SFzxEG5(.Σ" jiKBo.Y*'6\`Nx>c k ^TerYknq"nZ5 r8Ǒ#'X۳gU(y9^5jNY3 -Чo`@=D}"=jRբO M>GڱN ]onX &'?vWYۘ;Y˖oPOўZ8oNL@7U˕.v3Ҳ%dM>|޲-t;熛HȑW9HCLp.5j2]{\=7t?ΣxTuͽZtוHSgO/u_:$hl:NG*HSUE\W=ѡYEYz辫zIuH]=Jx\""9z6_jmd 3t :#gh1x;2ͭ 8/᪨e*Jst')w?dZ˅ 7!H =#!:8[\]:ӯ7?V͎X&iAqf$h`p@q7VeyWNuj`zC3Ɖq>BGR# mQ $1oby$)8XXsg{FIϴ;ClP %D.9-%&&彟彟(1)9j^4Уz$a߲B=ZpszC^) ~j5$[VoX6ʅz%I+n ^QwS)) :.):T?}kwVUE*ՁS***0?%9IMuQQnX;OT)+=)jpe6J򲂟}͏U@&%%v<ڤ%s]_QCi+1!Nت\yx';V/"8s6+Vo٦nlsÍ{ϰYeїY }9*,,U\\[xl͟>s|n{JmX~C~%%n5\^U%,Ȕ_K]l%݇uX.UcǣSY9GG7З؛jOvhᓚ8^1d]9ww66\3ܪCs.F?YO;g!+qR{wAIRiIyQojnQCII%˖Ӧf58x-_8AG W(qTuTWߠ^OII*/+ᏏF-;)ǣ| κ@ S6S8SoH2SO/n4FB$sx:}u$ "?m)aR+WjqXs^^rCz \u,s$UVw$$js=Fqukҗ4 InR `|~mxv>J_s;]jy[6"߀?Ԫ57܄Adt4#9Q믹Y^xJ ?pA8{n/@o`Pݽ}l;|~3~~zy)_|Oo An|Z-%7ӍB$ g G_gC;\׍v$$$$$$$$$>IϧXon&`                                                                                                    XoƾXo$$$$$$$$$$ C.& #f                                                                                                   hkmv6׼MY dcXnL;Nw[IsIENDB`mx-1.4.7/docs/reference/libmx/MxViewport.png000066400000000000000000000273131201047117600210070ustar00rootroot00000000000000PNG  IHDROsBIT|dtEXtSoftwaregnome-screenshot> IDATxytS׹#yA-0\ f7]@&HK4I5I64MV iCo&$$at !ر(ؒlKBYo--[ѻ~p'!\S(̗_g~o?*I3iŽ2NI*$CWqbuŏJ!@[׀Gu=P5vO(8"إv@" :'ƜG+ Q=ݏ…h9nǭA@Q'4@POA(ncXÕHWq}6R0~``x&gS<)ùO1oQ)dѡv٨ֆopޱ*ЦEŗ{@.>J]/Oe:%SMr\of/7/.a(6&|_P:;6$M'kFj[ܙˢC7 C[*9VYlS>>sl=\9&'ƣCcߗ>6oEYSҠY̚1yHׁkRTC`F@`P-mHJ4m3vwXcgG׷ksv= Xߚخ>i4~חfeldhimӷw*ݕk̚ n4HJDe#H1-Y@?a oĆEګX͍ ijs +#BhP(MEGT)ɓ 0u10p\%FFF3 ёhhjōDbf^}U!( Hgҹ=hho@Xh0$% :*=FgorR<AjrMz#7d, ~Gw0 ٱgn?Yˮ>Vph܆|!B6s 29 1 j bB7UF9Rc ZP(`F+0:+ß~6ʞA^߮|0 jΞ^$,3֛XR4Uw-wԷM`ˊ$TaՂLm~R8q07? m7;ޡܼ$0 026 hO@/ªBSmbi\Z \nġc !2T@=0-Y TiƪȚ,EDt>2BޡDjq2'K?adNෛ!"@e-{)1+zA 00@Gw?ꚔkR"<,em vͿu@8γuA,E~1=P@s;W! u$笗>W.F~ yi1ț*uy7uΜĭ!LOY?A#!HD 1384ɱK!3xsvW3 ѡ+z;0HO#㎳ "3Yad$itTڍaClG_vo:ZZokfaF%{\m `l46c-ǹ.̞yUq9.kK+=/!Ypl'ұuA,;۪]FF&1c ј;2a0Xs[W³aC_10ͽOEDjdeebdTPTV7w`amBL;8~h(15]{NJaן$i3+ $1B0045͸''1 ސҠ0}ŕVbcӓH@_J9+r ,Ԅ(0&DDE!iR<6J?8lKW{;N߮>Vo"9`Qa9&շذ$aoqc4``pc?B1#GDtM3Nż$rzr ooںk4ZLQi \:дd BQ׬ĕynh!s1H@p8y1}#V-jvo™1;e;\vu<؜HgӜ]}xq@ͣB}K7 Gp LF~\@C-LW,eItN\l٩Cq=iɝhJݟUR}vn2VC,bficqQ;$B]@XX(4fec+Z'nlAP Fk7%KP=(\&FppkIZC 0 G[{]ȚƯ!a'\\|Ew˜o*M 4ș?Q۩p!:s0}ގPnۋ ̝;  AV (**Bxx8qCܾ}[[[ ~o۰?J7n=9Z_CV\O>Ħ;Õ())Arr2!z#)) Ũ2NӃr\vĀZFUUN<[n^x.Μ9gbhhB1"y``r}h ˡ{Xىܺu ̟z\/sy|x񢉓s~emjkk3(:y&ǞjrNݍ!tuuͯ4:;;L&3ȃ [L!D$JE'!3fGydggyPꫯZM!ʕ+$;;瓒"Jɾ}F!RTTD~m3gȤIٳ̙3IRR9<{}:DNJT*!hȩSH\\elڴٳg}ɓD*"O6oLikk3'[ğ HVVHjj*!rO{|W]]MBCCIkk+:OII!9Baދ/HBCCɔ)SHaa!ٸq4r9^#dxx;v"Y?H>uYfnRȁHdd$ ̘17!###DRO?H$r-BeRT$--/Ivv65О"իWF!g&ӧO'oQ䥗^"W&*3f\P(J"3Y6m" Xt se-OV2::JT*!?яO?m7j H$ȑ#gO<Yf :ONN6pfG:;;MSd֬YdΝdddjixځO~pBr Q\\K.C\\7|cXSSz<P(ēO>Wpu]8qqu<38~8!8v0 oׯGTTB%CSSlزeCԗ.bbbpQO¶m0<</_ZO$?)v @;C ܞjB4^\@?~s~ġC@JŽ;,֧T*Emm-{4leppā ^lv agr$&&"//qqqxx=x#55'Oٳgï@!$$ӧO0())A__,X`0n6W}]6m222aQ>y[>cRRRcaܹ(((}݇e˖Y̫w# S) /_HMMEZZRRR,񩧞BYY0oͥٳ?^yD"X,Fii)Co5kրaŤ-P(D||]9at{60D@Uy2@ @bbͿ;?SNMZaGP(Dii)ՅÇcŊvjlU|MB:S@&a Epp0JKKg|'΂,Y`Xs[D 9P:P:ގ>v2Q(prc/_ؽB`ڵسgކYoG #h2ƚQ* &d .B3'+҇Qџay)ϵ,%ZmֽVANaMfIGe Q!}ƍN544>VANeMfIeMeCPʕ+d׮] 88ڵkw!/_f1v @\*A}]^1j5|AR_)))ePQQa9іDc6mBbb"&MHRTTTXu \yЉ⪪ Dq:U\T߶'O|g ߶ǞΤuhʒ˒hJ?3cوw0N.ܹs;wc:>)K"4k/k-{?uvvb+v&ŤAE*% MųnzQ(@PP+ *W8yT/ty>*(z oHF|T_>[es:oHF|T_>O;#"g)r2FQ JoHF|Z[n8ț#Q*dVfBن+=[#IlWFs7fu lշ' Es#ԍdT^IN8kmPFHq=1P)ԁ(D88yyq *>! ʈbTGE{gpas:B\Q~6*ڣ=>=uPGk#b]FE{TWuGo)B5hԽuO?^)qCE{$,J9sX<FM3q wE=۠=ĤьGPFE{AE{މIkcCE{ Ε=ۡ=C[λ-D xxt6:A[ee%Ξ=kћ2zO;ǖx 6_(3I)W@Bq@p:DsT7qt _..o@Tt1*+yw:sSN{ /l߾ٳgz ?ϑ .r̙3]/cʔ)@3gPXXhvƍqW^ڵk1c vVXt)n݊3gWN:Zaƍx饗svCaΜ9jQIDAT ==VEEE8y$_/Xj{9^i#==rJATO|hkR\X+>!%sWt1.lC7n}YPS,EӡM֦p4Ċi̮DDt1B|Twq.F|Ţc\G+Q1Ũtu .fQ1ق@y_B;w\݌/|<-YnPFo'@D8u <@ΈLF#d r s&4"Qyc=xLf)ch>i=dTGw !-b&B)}'>1.  8W"dTGހYɨsd2*='xLF'$끈":O栂?yZ䓂:op*h :}_M2NSp D8u Q(u-Zy Doʨϻp]ΛQS PʬpǕp纈~u |Ou:1ZΛQ #L>pYY PT޽{1sL0 r^iiiwmP7nDEEq5ÈǡCK/}z]SS"ՙ,zq)Ÿx"^|E<իhyV^}OB/2mۆ/";;gΜ5k@`ҥK'N@*24-շe)OKm0ԍeT[?ԍeTg*$V"eTg,4#QџmLџxXFE1DeTgE x8X7Opz yhxځfFFE[לh2+2D8u >~xQA|0q P0q oHi-m|7>cAG$>*/ ="Q~pΕ Pp:7G$>-Tg*S*RtnP(X,Fnn.btBILL D`ΒM~_@uj 'aaah4O~zv.Ƕq\}Z_|ķ|rrR[[KvE^JC>ߓwy\rMwnoHF|ZϰVVV@ p$֬[#Q8öD&݋M6߮CVc׮]Xr%(PYzmԩ?I9e=su>iD"dggs!h־2ڃP(D||]!حaZ}{"OT*ȄBM5 6_(#p^|A eXLJWQ& t:>@T}8CǗO?3X!388СCv׹8yehhmmm(++qAZ^YSz444 "":;;TWW#33Rj555A^^"##9mߺu ]W QQQDl+++0 h/22]Vގxg:Ƣ=HTj5 ]^ ķ蜻ՈGjjAy,1)ڳT^}}}D}>00n$$$>nkkC\\;dpZ}s/^2tuuᣏ>bŋӁ\QOD:kQ܊[oaѢE`r4Ꞗu/(--EYYJ% ** mk1+#RL*ڳM[kanDT͎;HKK Ť(FE{T,ўD˗/GhhLpMŨh*ڳMgNe@(FE{AE{މIͻ+t|"effU*vaqIV=waҚ(FE{=SPʈbPўPў!:A'|GyuhEPFE{AE{HRzEGG0 -A/[(((cKj5qQ=y,B/|Bxx8 qy>|a' ̙3n8 H8D"jڰ:eBv&Aϰ v%Lqj>PqŢt^R&2g,2=8*aցp9v aj5:::pqu]uR e9b>fCBBS&f ;w.Ο?>Xtv:,s.twPЕb>s R)vI&@@;tE:,1j5֯_ 6૯¹szjlذ}VqTTT&jkP[p d2(>vmjb>]N>m~̙5*Vth<HM/^htuuڊ/-- ~~~6C}1X,@ ہG$9#'Riy @x"6 eb`x^Rb 3;Ѝdt/[̩.˯X,FWW>< ra( vwglNw(..݊BK@@d2v7NtcŊdb]Vºu8ۋD)~}tSʄ]Dh4GUZcxԌg-qW!?b TTT/ݻ裏ìbrƏVJ2'cv'O2iB ]> Mx Toolkit Reference Manual for mx &version; 2009, 2010, 2011 Intel Corporation Clutter Actors and Utilities Widgets Containers Data Views Interfaces Effects Abstract Types and Utilities Index of all symbols Index of new symbols in 1.2 mx-1.4.7/docs/reference/libmx/mx-sections.txt000066400000000000000000000763311201047117600211730ustar00rootroot00000000000000
mx-application MxApplication MxApplication MxApplicationClass MxApplicationFlags mx_application_new mx_application_run mx_application_quit mx_application_create_window mx_application_get_flags mx_application_add_window mx_application_remove_window mx_application_get_windows mx_application_add_action mx_application_remove_action mx_application_get_actions mx_application_invoke_action mx_application_is_running MxApplicationPrivate MX_APPLICATION MX_IS_APPLICATION MX_TYPE_APPLICATION mx_application_get_type MX_APPLICATION_CLASS MX_IS_APPLICATION_CLASS MX_APPLICATION_GET_CLASS
mx-fade-effect MxFadeEffect MxFadeEffect MxFadeEffectClass mx_fade_effect_new mx_fade_effect_set_border mx_fade_effect_get_border mx_fade_effect_set_bounds mx_fade_effect_get_bounds mx_fade_effect_set_color mx_fade_effect_get_color MxFadeEffectPrivate MX_FADE_EFFECT MX_IS_FADE_EFFECT MX_TYPE_FADE_EFFECT mx_fade_effect_get_type MX_FADE_EFFECT_CLASS MX_IS_FADE_EFFECT_CLASS MX_FADE_EFFECT_GET_CLASS
mx-widget MxWidget MxLongPressAction MxWidget MxWidgetClass mx_widget_set_tooltip_text mx_widget_get_tooltip_text mx_widget_show_tooltip mx_widget_hide_tooltip mx_widget_set_menu mx_widget_get_menu mx_widget_get_disabled mx_widget_set_disabled mx_widget_long_press_query mx_widget_long_press_cancel mx_widget_get_background_image mx_widget_get_border_image mx_widget_get_padding mx_widget_paint_background mx_widget_apply_style mx_widget_get_available_area mx_widget_set_tooltip_delay mx_widget_get_tooltip_delay MxWidgetPrivate MX_WIDGET MX_IS_WIDGET MX_TYPE_WIDGET mx_widget_get_type MX_WIDGET_CLASS MX_IS_WIDGET_CLASS MX_WIDGET_GET_CLASS
mx-viewport MxViewport MxViewport MxViewportClass mx_viewport_new mx_viewport_set_origin mx_viewport_get_origin mx_viewport_set_sync_adjustments mx_viewport_get_sync_adjustments MxViewportPrivate MX_VIEWPORT MX_IS_VIEWPORT MX_TYPE_VIEWPORT mx_viewport_get_type MX_VIEWPORT_CLASS MX_IS_VIEWPORT_CLASS MX_VIEWPORT_GET_CLASS
mx-dialog MxDialog MxDialog MxDialogClass mx_dialog_new mx_dialog_set_transient_parent mx_dialog_add_action mx_dialog_remove_action mx_dialog_get_actions MxDialogPrivate MX_DIALOG MX_IS_DIALOG MX_TYPE_DIALOG mx_dialog_get_type MX_DIALOG_CLASS MX_IS_DIALOG_CLASS MX_DIALOG_GET_CLASS
mx-stack MxStack MxStack MxStackClass mx_stack_new MxStackPrivate MX_STACK MX_IS_STACK MX_TYPE_STACK mx_stack_get_type MX_STACK_CLASS MX_IS_STACK_CLASS MX_STACK_GET_CLASS
mx-table MxTable MxTable MxTableClass mx_table_new mx_table_set_column_spacing mx_table_get_column_spacing mx_table_set_row_spacing mx_table_get_row_spacing mx_table_add_actor mx_table_add_actor_with_properties mx_table_get_row_count mx_table_get_column_count MxTablePrivate MX_TABLE MX_IS_TABLE MX_TYPE_TABLE mx_table_get_type MX_TABLE_CLASS MX_IS_TABLE_CLASS MX_TABLE_GET_CLASS
mx-item-view MxItemView MxItemView MxItemViewClass mx_item_view_new mx_item_view_set_model mx_item_view_get_model mx_item_view_set_item_type mx_item_view_get_item_type mx_item_view_add_attribute mx_item_view_freeze mx_item_view_thaw mx_item_view_set_factory mx_item_view_get_factory MxItemViewPrivate MX_ITEM_VIEW MX_IS_ITEM_VIEW MX_TYPE_ITEM_VIEW mx_item_view_get_type MX_ITEM_VIEW_CLASS MX_IS_ITEM_VIEW_CLASS MX_ITEM_VIEW_GET_CLASS
mx-combo-box MxComboBox MxComboBox MxComboBoxClass mx_combo_box_new mx_combo_box_insert_text mx_combo_box_insert_text_with_icon mx_combo_box_append_text mx_combo_box_prepend_text mx_combo_box_remove_text mx_combo_box_remove_all mx_combo_box_set_active_text mx_combo_box_get_active_text mx_combo_box_set_active_icon_name mx_combo_box_get_active_icon_name mx_combo_box_set_index mx_combo_box_get_index MxComboBoxPrivate MX_COMBO_BOX MX_IS_COMBO_BOX MX_TYPE_COMBO_BOX mx_combo_box_get_type MX_COMBO_BOX_CLASS MX_IS_COMBO_BOX_CLASS MX_COMBO_BOX_GET_CLASS
mx-scroll-view MxScrollView MxScrollView MxScrollViewClass mx_scroll_view_new mx_scroll_view_set_enable_mouse_scrolling mx_scroll_view_get_enable_mouse_scrolling mx_scroll_view_set_enable_gestures mx_scroll_view_get_enable_gestures mx_scroll_view_set_scroll_policy mx_scroll_view_get_scroll_policy mx_scroll_view_ensure_visible MxScrollViewPrivate MX_SCROLL_VIEW MX_IS_SCROLL_VIEW MX_TYPE_SCROLL_VIEW mx_scroll_view_get_type MX_SCROLL_VIEW_CLASS MX_IS_SCROLL_VIEW_CLASS MX_SCROLL_VIEW_GET_CLASS
mx-focusable MxFocusable MxFocusDirection MxFocusHint MxFocusable MxFocusableIface mx_focusable_move_focus mx_focusable_accept_focus mx_focus_hint_from_direction MX_FOCUSABLE MX_IS_FOCUSABLE MX_TYPE_FOCUSABLE mx_focusable_get_type MX_FOCUSABLE_GET_INTERFACE
mx-focus-manager MxFocusManager MxFocusManager MxFocusManagerClass mx_focus_manager_get_for_stage mx_focus_manager_get_stage mx_focus_manager_get_focused mx_focus_manager_push_focus mx_focus_manager_push_focus_with_hint mx_focus_manager_move_focus MxFocusManagerPrivate MX_FOCUS_MANAGER MX_IS_FOCUS_MANAGER MX_TYPE_FOCUS_MANAGER mx_focus_manager_get_type MX_FOCUS_MANAGER_CLASS MX_IS_FOCUS_MANAGER_CLASS MX_FOCUS_MANAGER_GET_CLASS
mx-actor-manager MxActorManager MxActorManagerCreateFunc MxActorManagerError MxActorManager MxActorManagerClass mx_actor_manager_new mx_actor_manager_get_for_stage mx_actor_manager_get_stage mx_actor_manager_create_actor mx_actor_manager_add_actor mx_actor_manager_remove_actor mx_actor_manager_remove_container mx_actor_manager_cancel_operation mx_actor_manager_cancel_operations mx_actor_manager_set_time_slice mx_actor_manager_get_time_slice mx_actor_manager_get_n_operations MxActorManagerPrivate MX_ACTOR_MANAGER MX_IS_ACTOR_MANAGER MX_TYPE_ACTOR_MANAGER mx_actor_manager_get_type MX_ACTOR_MANAGER_CLASS MX_IS_ACTOR_MANAGER_CLASS MX_ACTOR_MANAGER_GET_CLASS
mx-spinner MxSpinner MxSpinner MxSpinnerClass mx_spinner_new mx_spinner_set_animating mx_spinner_get_animating MxSpinnerPrivate MX_SPINNER MX_IS_SPINNER MX_TYPE_SPINNER mx_spinner_get_type MX_SPINNER_CLASS MX_IS_SPINNER_CLASS MX_SPINNER_GET_CLASS
mx-tooltip MxTooltip MxTooltip MxTooltipClass mx_tooltip_get_text mx_tooltip_set_text mx_tooltip_show mx_tooltip_hide mx_tooltip_set_tip_area mx_tooltip_get_tip_area mx_tooltip_is_in_browse_mode MxTooltipPrivate MX_TOOLTIP MX_IS_TOOLTIP MX_TYPE_TOOLTIP mx_tooltip_get_type MX_TOOLTIP_CLASS MX_IS_TOOLTIP_CLASS MX_TOOLTIP_GET_CLASS
mx-label MxLabel MxLabel MxLabelClass mx_label_new mx_label_new_with_text mx_label_get_text mx_label_set_text mx_label_get_use_markup mx_label_set_use_markup mx_label_get_clutter_text mx_label_get_x_align mx_label_set_x_align mx_label_get_y_align mx_label_set_y_align mx_label_get_line_wrap mx_label_set_line_wrap mx_label_set_fade_out mx_label_get_fade_out MxLabelPrivate MX_LABEL MX_IS_LABEL MX_TYPE_LABEL mx_label_get_type MX_LABEL_CLASS MX_IS_LABEL_CLASS MX_LABEL_GET_CLASS
mx-slider MxSlider MxSlider MxSliderClass mx_slider_new mx_slider_set_value mx_slider_get_value mx_slider_get_buffer_value mx_slider_set_buffer_value MxSliderPrivate MX_SLIDER MX_IS_SLIDER MX_TYPE_SLIDER mx_slider_get_type MX_SLIDER_CLASS MX_IS_SLIDER_CLASS MX_SLIDER_GET_CLASS
mx-stylable MxStylable MxStyleChangedFlags MxStylable MxStylableIface mx_stylable_iface_install_property mx_stylable_freeze_notify mx_stylable_notify mx_stylable_thaw_notify mx_stylable_list_properties mx_stylable_find_property mx_stylable_set_style mx_stylable_get_style mx_stylable_get mx_stylable_get_property mx_stylable_get_default_value mx_stylable_get_style_class mx_stylable_set_style_class mx_stylable_get_style_pseudo_class mx_stylable_set_style_pseudo_class mx_stylable_style_changed mx_stylable_connect_change_notifiers mx_stylable_apply_clutter_text_attributes mx_stylable_style_pseudo_class_add mx_stylable_style_pseudo_class_remove mx_stylable_style_pseudo_class_contains MX_STYLABLE_IFACE MX_IS_STYLABLE_IFACE MX_STYLABLE MX_IS_STYLABLE MX_TYPE_STYLABLE mx_stylable_get_type MX_STYLABLE_GET_IFACE
mx-path-bar MxPathBar MxPathBar MxPathBarClass mx_path_bar_new mx_path_bar_push mx_path_bar_pop mx_path_bar_get_level mx_path_bar_clear mx_path_bar_get_editable mx_path_bar_set_editable mx_path_bar_get_clear_on_change mx_path_bar_set_clear_on_change mx_path_bar_get_label mx_path_bar_set_label mx_path_bar_get_text mx_path_bar_set_text mx_path_bar_get_entry MxPathBarPrivate MX_PATH_BAR MX_IS_PATH_BAR MX_TYPE_PATH_BAR mx_path_bar_get_type MX_PATH_BAR_CLASS MX_IS_PATH_BAR_CLASS MX_PATH_BAR_GET_CLASS
mx-box-layout-child MxBoxLayoutChild MxBoxLayoutChild MxBoxLayoutChildClass mx_box_layout_child_get_expand mx_box_layout_child_set_expand mx_box_layout_child_get_x_fill mx_box_layout_child_set_x_fill mx_box_layout_child_get_y_fill mx_box_layout_child_set_y_fill mx_box_layout_child_get_x_align mx_box_layout_child_set_x_align mx_box_layout_child_get_y_align mx_box_layout_child_set_y_align MxBoxLayoutChildPrivate MX_BOX_LAYOUT_CHILD MX_IS_BOX_LAYOUT_CHILD MX_TYPE_BOX_LAYOUT_CHILD mx_box_layout_child_get_type MX_BOX_LAYOUT_CHILD_CLASS MX_IS_BOX_LAYOUT_CHILD_CLASS MX_BOX_LAYOUT_CHILD_GET_CLASS
mx-deform-bow-tie MxDeformBowTie MxDeformBowTie MxDeformBowTieClass mx_deform_bow_tie_new mx_deform_bow_tie_get_period mx_deform_bow_tie_set_period mx_deform_bow_tie_get_flip_back mx_deform_bow_tie_set_flip_back MxDeformBowTiePrivate MX_DEFORM_BOW_TIE MX_IS_DEFORM_BOW_TIE MX_TYPE_DEFORM_BOW_TIE mx_deform_bow_tie_get_type MX_DEFORM_BOW_TIE_CLASS MX_IS_DEFORM_BOW_TIE_CLASS MX_DEFORM_BOW_TIE_GET_CLASS
mx-bin MxBin MxBin MxBinClass mx_bin_allocate_child mx_bin_set_child mx_bin_get_child mx_bin_set_alignment mx_bin_get_alignment mx_bin_set_fill mx_bin_get_fill MxBinPrivate MX_BIN MX_IS_BIN MX_TYPE_BIN mx_bin_get_type MX_BIN_CLASS MX_IS_BIN_CLASS MX_BIN_GET_CLASS
mx-kinetic-scroll-view MxKineticScrollView MxKineticScrollView MxKineticScrollViewClass mx_kinetic_scroll_view_new mx_kinetic_scroll_view_stop mx_kinetic_scroll_view_set_deceleration mx_kinetic_scroll_view_get_deceleration mx_kinetic_scroll_view_set_use_captured mx_kinetic_scroll_view_get_use_captured mx_kinetic_scroll_view_set_mouse_button mx_kinetic_scroll_view_get_mouse_button mx_kinetic_scroll_view_set_overshoot mx_kinetic_scroll_view_get_overshoot mx_kinetic_scroll_view_set_scroll_policy mx_kinetic_scroll_view_get_scroll_policy MxKineticScrollViewPrivate MX_KINETIC_SCROLL_VIEW MX_IS_KINETIC_SCROLL_VIEW MX_TYPE_KINETIC_SCROLL_VIEW mx_kinetic_scroll_view_get_type MX_KINETIC_SCROLL_VIEW_CLASS MX_IS_KINETIC_SCROLL_VIEW_CLASS MX_KINETIC_SCROLL_VIEW_GET_CLASS
mx-deform-waves MxDeformWaves MxDeformWaves MxDeformWavesClass mx_deform_waves_new mx_deform_waves_get_period mx_deform_waves_set_period mx_deform_waves_get_angle mx_deform_waves_set_angle mx_deform_waves_get_radius mx_deform_waves_set_radius mx_deform_waves_get_amplitude mx_deform_waves_set_amplitude MxDeformWavesPrivate MX_DEFORM_WAVES MX_IS_DEFORM_WAVES MX_TYPE_DEFORM_WAVES mx_deform_waves_get_type MX_DEFORM_WAVES_CLASS MX_IS_DEFORM_WAVES_CLASS MX_DEFORM_WAVES_GET_CLASS
mx-window MxWindow MxWindow MxWindowClass mx_window_new mx_window_new_with_clutter_stage mx_window_get_for_stage mx_window_get_child mx_window_set_child mx_window_get_toolbar mx_window_set_toolbar mx_window_get_has_toolbar mx_window_set_has_toolbar mx_window_get_small_screen mx_window_set_small_screen mx_window_get_fullscreen mx_window_set_fullscreen mx_window_set_title mx_window_get_title mx_window_set_icon_name mx_window_get_icon_name mx_window_set_icon_from_cogl_texture mx_window_get_clutter_stage mx_window_get_window_position mx_window_set_window_position mx_window_get_window_size mx_window_set_window_size mx_window_present mx_window_set_window_rotation mx_window_get_window_rotation mx_window_show mx_window_hide MxWindowPrivate MX_WINDOW MX_IS_WINDOW MX_TYPE_WINDOW mx_window_get_type MX_WINDOW_CLASS MX_IS_WINDOW_CLASS MX_WINDOW_GET_CLASS
mx-image MxImage MxImageError MX_IMAGE_ERROR mx_image_error_quark MxImage MxImageClass mx_image_new mx_image_set_from_data mx_image_set_from_file mx_image_set_from_file_at_size mx_image_set_from_buffer mx_image_set_from_buffer_at_size mx_image_clear mx_image_set_scale_mode mx_image_get_scale_mode mx_image_animate_scale_mode mx_image_set_image_rotation mx_image_get_image_rotation mx_image_set_load_async mx_image_get_load_async mx_image_set_allow_upscale mx_image_get_allow_upscale mx_image_set_scale_width_threshold mx_image_get_scale_width_threshold mx_image_set_scale_height_threshold mx_image_get_scale_height_threshold mx_image_set_transition_duration mx_image_get_transition_duration mx_image_set_from_cogl_texture MxImagePrivate MX_IMAGE MX_IS_IMAGE MX_TYPE_IMAGE mx_image_get_type MX_IMAGE_CLASS MX_IS_IMAGE_CLASS MX_IMAGE_GET_CLASS
mx-button-group MxButtonGroup MxButtonGroup MxButtonGroupClass mx_button_group_new mx_button_group_add mx_button_group_remove mx_button_group_foreach mx_button_group_set_active_button mx_button_group_get_active_button mx_button_group_set_allow_no_active mx_button_group_get_allow_no_active mx_button_group_get_buttons MxButtonGroupPrivate MX_BUTTON_GROUP MX_IS_BUTTON_GROUP MX_TYPE_BUTTON_GROUP mx_button_group_get_type MX_BUTTON_GROUP_CLASS MX_IS_BUTTON_GROUP_CLASS MX_BUTTON_GROUP_GET_CLASS
mx-button MxButton MxButton MxButtonClass mx_button_new mx_button_new_with_label mx_button_get_label mx_button_set_label mx_button_get_icon_name mx_button_set_icon_name mx_button_get_icon_size mx_button_set_icon_size mx_button_set_is_toggle mx_button_get_is_toggle mx_button_set_toggled mx_button_get_toggled mx_button_set_action mx_button_get_action mx_button_set_icon_position mx_button_get_icon_position mx_button_set_icon_visible mx_button_get_icon_visible mx_button_set_label_visible mx_button_get_label_visible MxButtonPrivate MX_BUTTON MX_IS_BUTTON MX_TYPE_BUTTON mx_button_get_type MX_BUTTON_CLASS MX_IS_BUTTON_CLASS MX_BUTTON_GET_CLASS
mx-progress-bar MxProgressBar MxProgressBar MxProgressBarClass mx_progress_bar_new mx_progress_bar_set_progress mx_progress_bar_get_progress MxProgressBarPrivate MX_PROGRESS_BAR MX_IS_PROGRESS_BAR MX_TYPE_PROGRESS_BAR mx_progress_bar_get_type MX_PROGRESS_BAR_CLASS MX_IS_PROGRESS_BAR_CLASS MX_PROGRESS_BAR_GET_CLASS
mx-toolbar MxToolbar MxToolbar MxToolbarClass mx_toolbar_new mx_toolbar_set_has_close_button mx_toolbar_get_has_close_button MxToolbarPrivate MX_TOOLBAR MX_IS_TOOLBAR MX_TYPE_TOOLBAR mx_toolbar_get_type MX_TOOLBAR_CLASS MX_IS_TOOLBAR_CLASS MX_TOOLBAR_GET_CLASS
mx-table-child MxTableChild MxTableChildClass mx_table_child_get_column mx_table_child_set_column mx_table_child_get_row mx_table_child_set_row mx_table_child_get_column_span mx_table_child_set_column_span mx_table_child_get_row_span mx_table_child_set_row_span mx_table_child_get_x_fill mx_table_child_set_x_fill mx_table_child_get_y_fill mx_table_child_set_y_fill mx_table_child_get_x_expand mx_table_child_set_x_expand mx_table_child_get_y_expand mx_table_child_set_y_expand mx_table_child_get_x_align mx_table_child_set_x_align mx_table_child_get_y_align mx_table_child_set_y_align MX_TABLE_CHILD MX_IS_TABLE_CHILD MX_TYPE_TABLE_CHILD mx_table_child_get_type MX_TABLE_CHILD_CLASS MX_IS_TABLE_CHILD_CLASS MX_TABLE_CHILD_GET_CLASS
mx-stack-child MxStackChild MxStackChild MxStackChildClass mx_stack_child_get_x_fill mx_stack_child_set_x_fill mx_stack_child_get_y_fill mx_stack_child_set_y_fill mx_stack_child_get_x_align mx_stack_child_set_x_align mx_stack_child_get_y_align mx_stack_child_set_y_align mx_stack_child_get_fit mx_stack_child_set_fit MxStackChildPrivate MX_STACK_CHILD MX_IS_STACK_CHILD MX_TYPE_STACK_CHILD mx_stack_child_get_type MX_STACK_CHILD_CLASS MX_IS_STACK_CHILD_CLASS MX_STACK_CHILD_GET_CLASS
mx-deform-texture MxDeformTexture MxDeformTexture MxDeformTextureClass mx_deform_texture_get_resolution mx_deform_texture_set_resolution mx_deform_texture_set_textures mx_deform_texture_get_textures mx_deform_texture_invalidate MxDeformTexturePrivate MX_DEFORM_TEXTURE MX_IS_DEFORM_TEXTURE MX_TYPE_DEFORM_TEXTURE mx_deform_texture_get_type MX_DEFORM_TEXTURE_CLASS MX_IS_DEFORM_TEXTURE_CLASS MX_DEFORM_TEXTURE_GET_CLASS
mx-deform-page-turn MxDeformPageTurn MxDeformPageTurn MxDeformPageTurnClass mx_deform_page_turn_new mx_deform_page_turn_get_period mx_deform_page_turn_set_period mx_deform_page_turn_get_angle mx_deform_page_turn_set_angle mx_deform_page_turn_get_radius mx_deform_page_turn_set_radius MxDeformPageTurnPrivate MX_DEFORM_PAGE_TURN MX_IS_DEFORM_PAGE_TURN MX_TYPE_DEFORM_PAGE_TURN mx_deform_page_turn_get_type MX_DEFORM_PAGE_TURN_CLASS MX_IS_DEFORM_PAGE_TURN_CLASS MX_DEFORM_PAGE_TURN_GET_CLASS
mx-expander MxExpander MxExpander MxExpanderClass mx_expander_new mx_expander_set_label mx_expander_get_expanded mx_expander_set_expanded MxExpanderPrivate MX_EXPANDER MX_IS_EXPANDER MX_TYPE_EXPANDER mx_expander_get_type MX_EXPANDER_CLASS MX_IS_EXPANDER_CLASS MX_EXPANDER_GET_CLASS
mx-settings MxSettings MxSettings MxSettingsClass mx_settings_get_default MxSettingsPrivate MX_SETTINGS MX_IS_SETTINGS MX_TYPE_SETTINGS mx_settings_get_type MX_SETTINGS_CLASS MX_IS_SETTINGS_CLASS MX_SETTINGS_GET_CLASS
mx-item-factory MxItemFactory MxItemFactory MxItemFactoryIface mx_item_factory_create MX_ITEM_FACTORY MX_IS_ITEM_FACTORY MX_TYPE_ITEM_FACTORY mx_item_factory_get_type MX_ITEM_FACTORY_GET_IFACE
mx-scrollable MxScrollable MxScrollable MxScrollableIface mx_scrollable_set_adjustments mx_scrollable_get_adjustments MX_SCROLLABLE MX_IS_SCROLLABLE MX_TYPE_SCROLLABLE mx_scrollable_get_type MX_SCROLLABLE_GET_IFACE
mx-box-layout MxBoxLayout MxBoxLayout MxBoxLayoutClass mx_box_layout_new mx_box_layout_set_orientation mx_box_layout_get_orientation mx_box_layout_set_spacing mx_box_layout_get_spacing mx_box_layout_get_enable_animations mx_box_layout_set_enable_animations mx_box_layout_add_actor mx_box_layout_add_actor_with_properties mx_box_layout_set_scroll_to_focused mx_box_layout_get_scroll_to_focused MxBoxLayoutPrivate MX_BOX_LAYOUT MX_IS_BOX_LAYOUT MX_TYPE_BOX_LAYOUT mx_box_layout_get_type MX_BOX_LAYOUT_CLASS MX_IS_BOX_LAYOUT_CLASS MX_BOX_LAYOUT_GET_CLASS
mx-menu MxMenu MxMenu MxMenuClass mx_menu_new mx_menu_add_action mx_menu_remove_action mx_menu_remove_all mx_menu_show_with_position MxMenuPrivate MX_MENU MX_IS_MENU MX_TYPE_MENU mx_menu_get_type MX_MENU_CLASS MX_IS_MENU_CLASS MX_MENU_GET_CLASS
mx-entry MxEntry MxEntry MxEntryClass mx_entry_new mx_entry_new_with_text mx_entry_get_text mx_entry_set_text mx_entry_get_clutter_text mx_entry_set_hint_text mx_entry_get_hint_text mx_entry_set_password_char mx_entry_get_password_char mx_entry_set_primary_icon_from_file mx_entry_set_secondary_icon_from_file MxEntryPrivate MX_ENTRY MX_IS_ENTRY MX_TYPE_ENTRY mx_entry_get_type MX_ENTRY_CLASS MX_IS_ENTRY_CLASS MX_ENTRY_GET_CLASS
mx-droppable MxDroppable MxDroppable MxDroppableIface mx_droppable_enable mx_droppable_disable mx_droppable_is_enabled mx_droppable_accept_drop MX_DROPPABLE MX_IS_DROPPABLE MX_TYPE_DROPPABLE mx_droppable_get_type MX_DROPPABLE_GET_IFACE
mx-texture-cache MxTextureCache MxTextureCache MxTextureCacheClass mx_texture_cache_get_default mx_texture_cache_get_texture mx_texture_cache_get_actor mx_texture_cache_contains mx_texture_cache_insert mx_texture_cache_get_cogl_texture mx_texture_cache_get_size mx_texture_cache_load_cache mx_texture_cache_contains_meta mx_texture_cache_get_meta_cogl_texture mx_texture_cache_get_meta_texture mx_texture_cache_insert_meta MX_TEXTURE_CACHE MX_IS_TEXTURE_CACHE MX_TYPE_TEXTURE_CACHE mx_texture_cache_get_type MX_TEXTURE_CACHE_CLASS MX_IS_TEXTURE_CACHE_CLASS MX_TEXTURE_CACHE_GET_CLASS
mx-floating-widget MxFloatingWidget MxFloatingWidget MxFloatingWidgetClass MxFloatingWidgetPrivate MX_FLOATING_WIDGET MX_IS_FLOATING_WIDGET MX_TYPE_FLOATING_WIDGET mx_floating_widget_get_type MX_FLOATING_WIDGET_CLASS MX_IS_FLOATING_WIDGET_CLASS MX_FLOATING_WIDGET_GET_CLASS
mx-adjustment MxAdjustment MxAdjustment MxAdjustmentClass mx_adjustment_new mx_adjustment_new_with_values mx_adjustment_get_value mx_adjustment_set_value mx_adjustment_get_lower mx_adjustment_set_lower mx_adjustment_get_upper mx_adjustment_set_upper mx_adjustment_get_step_increment mx_adjustment_set_step_increment mx_adjustment_get_page_increment mx_adjustment_set_page_increment mx_adjustment_get_page_size mx_adjustment_set_page_size mx_adjustment_set_values mx_adjustment_get_values mx_adjustment_interpolate mx_adjustment_interpolate_relative mx_adjustment_get_elastic mx_adjustment_set_elastic mx_adjustment_get_clamp_value mx_adjustment_set_clamp_value MxAdjustmentPrivate MX_ADJUSTMENT MX_IS_ADJUSTMENT MX_TYPE_ADJUSTMENT mx_adjustment_get_type MX_ADJUSTMENT_CLASS MX_IS_ADJUSTMENT_CLASS MX_ADJUSTMENT_GET_CLASS
mx-clipboard MxClipboard MxClipboard MxClipboardClass MxClipboardCallbackFunc mx_clipboard_get_default mx_clipboard_get_text mx_clipboard_set_text MxClipboardPrivate MX_CLIPBOARD MX_IS_CLIPBOARD MX_TYPE_CLIPBOARD mx_clipboard_get_type MX_CLIPBOARD_CLASS MX_IS_CLIPBOARD_CLASS MX_CLIPBOARD_GET_CLASS
mx-offscreen MxOffscreen MxOffscreen MxOffscreenClass mx_offscreen_new mx_offscreen_set_child mx_offscreen_get_child mx_offscreen_set_pick_child mx_offscreen_get_pick_child mx_offscreen_set_auto_update mx_offscreen_get_auto_update mx_offscreen_set_redirect_enabled mx_offscreen_get_redirect_enabled mx_offscreen_get_buffer mx_offscreen_update mx_offscreen_set_accumulation_enabled mx_offscreen_get_accumulation_enabled mx_offscreen_get_accumulation_material MxOffscreenPrivate MX_OFFSCREEN MX_IS_OFFSCREEN MX_TYPE_OFFSCREEN mx_offscreen_get_type MX_OFFSCREEN_CLASS MX_IS_OFFSCREEN_CLASS MX_OFFSCREEN_GET_CLASS
mx-native-window MxNativeWindow MxNativeWindow MxNativeWindowIface MX_NATIVE_WINDOW MX_IS_NATIVE_WINDOW MX_TYPE_NATIVE_WINDOW MX_NATIVE_WINDOW_GET_IFACE
mx-list-view MxListView MxListView MxListViewClass mx_list_view_new mx_list_view_set_model mx_list_view_get_model mx_list_view_set_item_type mx_list_view_get_item_type mx_list_view_add_attribute mx_list_view_freeze mx_list_view_thaw mx_list_view_set_factory mx_list_view_get_factory MxListViewPrivate MX_LIST_VIEW MX_IS_LIST_VIEW MX_TYPE_LIST_VIEW mx_list_view_get_type MX_LIST_VIEW_CLASS MX_IS_LIST_VIEW_CLASS MX_LIST_VIEW_GET_CLASS
mx-grid MxGrid MxGridClass mx_grid_new mx_grid_set_line_alignment mx_grid_get_line_alignment mx_grid_set_homogenous_rows mx_grid_get_homogenous_rows mx_grid_set_homogenous_columns mx_grid_get_homogenous_columns mx_grid_set_orientation mx_grid_get_orientation mx_grid_set_row_spacing mx_grid_get_row_spacing mx_grid_set_column_spacing mx_grid_get_column_spacing mx_grid_set_child_y_align mx_grid_get_child_y_align mx_grid_set_child_x_align mx_grid_get_child_x_align mx_grid_set_max_stride mx_grid_get_max_stride MxGridPrivate MX_GRID MX_IS_GRID MX_TYPE_GRID mx_grid_get_type MX_GRID_CLASS MX_IS_GRID_CLASS MX_GRID_GET_CLASS
mx-draggable MxDraggable MxDragAxis MxDraggable MxDraggableIface mx_draggable_set_axis mx_draggable_get_axis mx_draggable_set_drag_threshold mx_draggable_get_drag_threshold mx_draggable_set_containment_type mx_draggable_get_containment_type mx_draggable_set_containment_area mx_draggable_get_containment_area mx_draggable_set_drag_actor mx_draggable_get_drag_actor mx_draggable_disable mx_draggable_enable mx_draggable_is_enabled MX_DRAGGABLE MX_IS_DRAGGABLE MX_TYPE_DRAGGABLE mx_draggable_get_type MX_DRAGGABLE_GET_IFACE
mx-icon MxIcon MxIcon MxIconClass mx_icon_new mx_icon_get_icon_name mx_icon_set_icon_name mx_icon_get_icon_size mx_icon_set_icon_size MxIconPrivate MX_ICON MX_IS_ICON MX_TYPE_ICON mx_icon_get_type MX_ICON_CLASS MX_IS_ICON_CLASS MX_ICON_GET_CLASS
mx-icon-theme MxIconTheme MxIconTheme MxIconThemeClass mx_icon_theme_new mx_icon_theme_get_default mx_icon_theme_get_theme_name mx_icon_theme_set_theme_name mx_icon_theme_lookup mx_icon_theme_lookup_texture mx_icon_theme_has_icon mx_icon_theme_get_search_paths mx_icon_theme_set_search_paths MxIconThemePrivate MX_ICON_THEME MX_IS_ICON_THEME MX_TYPE_ICON_THEME mx_icon_theme_get_type MX_ICON_THEME_CLASS MX_IS_ICON_THEME_CLASS MX_ICON_THEME_GET_CLASS
mx-texture-frame MxTextureFrame MxTextureFrame MxTextureFrameClass mx_texture_frame_new mx_texture_frame_set_parent_texture mx_texture_frame_get_parent_texture mx_texture_frame_set_border_values mx_texture_frame_get_border_values MxTextureFramePrivate MX_TEXTURE_FRAME MX_IS_TEXTURE_FRAME MX_TYPE_TEXTURE_FRAME mx_texture_frame_get_type MX_TEXTURE_FRAME_CLASS MX_IS_TEXTURE_FRAME_CLASS MX_TEXTURE_FRAME_GET_CLASS
mx-toggle MxToggle MxToggle MxToggleClass mx_toggle_new mx_toggle_set_active mx_toggle_get_active MxTogglePrivate MX_TOGGLE MX_IS_TOGGLE MX_TYPE_TOGGLE mx_toggle_get_type MX_TOGGLE_CLASS MX_IS_TOGGLE_CLASS MX_TOGGLE_GET_CLASS
mx-scroll-bar MxScrollBar MxScrollBar MxScrollBarClass mx_scroll_bar_new mx_scroll_bar_new_with_adjustment mx_scroll_bar_set_adjustment mx_scroll_bar_get_adjustment mx_scroll_bar_set_orientation mx_scroll_bar_get_orientation MxScrollBarPrivate MX_SCROLL_BAR MX_IS_SCROLL_BAR MX_TYPE_SCROLL_BAR mx_scroll_bar_get_type MX_SCROLL_BAR_CLASS MX_IS_SCROLL_BAR_CLASS MX_SCROLL_BAR_GET_CLASS
mx-action MxAction MxAction MxActionClass mx_action_new mx_action_new_with_parameter mx_action_new_stateful mx_action_new_full mx_action_get_name mx_action_set_name mx_action_get_display_name mx_action_set_display_name mx_action_get_icon mx_action_set_icon mx_action_get_active mx_action_set_active MxActionPrivate MX_ACTION MX_IS_ACTION MX_TYPE_ACTION mx_action_get_type MX_ACTION_CLASS MX_IS_ACTION_CLASS MX_ACTION_GET_CLASS
mx-frame MxFrame MxFrame MxFrameClass mx_frame_new MxFramePrivate MX_FRAME MX_IS_FRAME MX_TYPE_FRAME mx_frame_get_type MX_FRAME_CLASS MX_IS_FRAME_CLASS MX_FRAME_GET_CLASS
mx-style MxStyle MxStyleError MxStyle MxStyleClass mx_style_get_default mx_style_new mx_style_load_from_file mx_style_get_property mx_style_get mx_style_get_valist MxStylePrivate MX_STYLE MX_IS_STYLE MX_TYPE_STYLE mx_style_get_type MX_STYLE_CLASS MX_IS_STYLE_CLASS MX_STYLE_GET_CLASS
mx-notebook MxNotebook MxNotebook MxNotebookClass mx_notebook_new mx_notebook_set_current_page mx_notebook_get_current_page mx_notebook_get_enable_gestures mx_notebook_set_enable_gestures MxNotebookPrivate MX_NOTEBOOK MX_IS_NOTEBOOK MX_TYPE_NOTEBOOK mx_notebook_get_type MX_NOTEBOOK_CLASS MX_IS_NOTEBOOK_CLASS MX_NOTEBOOK_GET_CLASS
mx-version MX_MAJOR_VERSION MX_MINOR_VERSION MX_MICRO_VERSION MX_VERSION_S MX_VERSION_HEX MX_CHECK_VERSION
mx-utils mx_set_locale mx_utils_format_time mx_allocate_align_fill mx_actor_box_clamp_to_pixels
mx-types MxBorderImage MxPadding MX_PARAM_TRANSLATEABLE mx_border_image_set_from_string mx_font_weight_set_from_string MxAlign MxFontWeight MxScrollPolicy MxOrientation MxWindowRotation MxPosition MxImageScaleMode MxTooltipAnimation MX_TYPE_BORDER_IMAGE MX_TYPE_PADDING mx_border_image_get_type mx_padding_get_type
mx-1.4.7/docs/reference/libmx/version.xml.in000066400000000000000000000000151201047117600207570ustar00rootroot00000000000000@MX_VERSION@ mx-1.4.7/git.mk000066400000000000000000000125301201047117600132630ustar00rootroot00000000000000# git.mk # # Copyright 2009, Red Hat, Inc. # Written by Behdad Esfahbod # # Copying and distribution of this file, with or without modification, # are permitted in any medium without royalty provided the copyright # notice and this notice are preserved. # # The canonical source for this file is pango/git.mk, or whereever the # header of pango/git.mk suggests in the future. # # To use in your project, import this file in your git repo's toplevel, # then do "make -f git.mk". This modifies all Makefile.am files in # your project to include git.mk. # # This enables automatic .gitignore generation. If you need to ignore # more files, add them to the GITIGNOREFILES variable in your Makefile.am. # But think twice before doing that. If a file has to be in .gitignore, # chances are very high that it's a generated file and should be in one # of MOSTLYCLEANFILES, CLEANFILES, DISTCLEANFILES, or MAINTAINERCLEANFILES. # # The only case that you need to manually add a file to GITIGNOREFILES is # when remove files in one of mostlyclean-local, clean-local, distclean-local, # or maintainer-clean-local. # # Note that for files like editor backup, etc, there are better places to # ignore them. See "man gitignore". # # If "make maintainer-clean" removes the files but they are not recognized # by this script (that is, if "git status" shows untracked files still), send # me the output of "git status" as well as your Makefile.am and Makefile for # the directories involved. # # For a list of toplevel files that should be in MAINTAINERCLEANFILES, see # pango/Makefile.am. # # Don't EXTRA_DIST this file. It is supposed to only live in git clones, # not tarballs. It serves no useful purpose in tarballs and clutters the # build dir. # # This file knows how to handle autoconf, automake, libtool, gtk-doc, # gnome-doc-utils, intltool. # # # KNOWN ISSUES: # # - Recursive configure doesn't work as $(top_srcdir)/git.mk inside the # submodule doesn't find us. If you have configure.{in,ac} files in # subdirs, add a proxy git.mk file in those dirs that simply does: # "include $(top_srcdir)/../git.mk". Add more ..'s to your taste. # And add those files to git. See vte/gnome-pty-helper/git.mk for # example. # git-all: git-mk-install git-mk-install: @echo Installing git makefile @any_failed=; find $(top_srcdir) -name Makefile.am | while read x; do \ if grep 'include .*/git.mk' $$x >/dev/null; then \ echo $$x already includes git.mk; \ else \ failed=; \ echo "Updating $$x"; \ { cat $$x; \ echo ''; \ echo '-include $$(top_srcdir)/git.mk'; \ } > $$x.tmp || failed=1; \ if test x$$failed = x; then \ mv $$x.tmp $$x || failed=1; \ fi; \ if test x$$failed = x; then : else \ echo Failed updating $$x; >&2 \ any_failed=1; \ fi; \ fi; done; test -z "$$any_failed" .PHONY: git-all git-mk-install ### .gitignore generation $(srcdir)/.gitignore: Makefile.am $(top_srcdir)/git.mk $(AM_V_GEN) \ { \ if test "x$(DOC_MODULE)" = x -o "x$(DOC_MAIN_SGML_FILE)" = x; then :; else \ for x in \ $(DOC_MODULE)-decl-list.txt \ $(DOC_MODULE)-decl.txt \ tmpl/$(DOC_MODULE)-unused.sgml \ "tmpl/*.bak" \ xml html \ ; do echo /$$x; done; \ fi; \ if test "x$(DOC_MODULE)" = x -o "x$(DOC_LINGUAS)" = x; then :; else \ for x in \ $(_DOC_C_DOCS) \ $(_DOC_LC_DOCS) \ $(_DOC_OMF_ALL) \ $(_DOC_DSK_ALL) \ $(_DOC_HTML_ALL) \ $(_DOC_POFILES) \ "*/.xml2po.mo" \ "*/*.omf.out" \ ; do echo /$$x; done; \ fi; \ if test -f $(srcdir)/po/Makefile.in.in; then \ for x in \ po/Makefile.in.in \ po/Makefile.in \ po/Makefile \ po/POTFILES \ po/stamp-it \ po/.intltool-merge-cache \ "po/*.gmo" \ "po/*.mo" \ po/$(GETTEXT_PACKAGE).pot \ intltool-extract.in \ intltool-merge.in \ intltool-update.in \ ; do echo /$$x; done; \ fi; \ if test -f $(srcdir)/configure; then \ for x in \ autom4te.cache \ configure \ config.h \ stamp-h1 \ libtool \ config.lt \ ; do echo /$$x; done; \ fi; \ for x in \ .gitignore \ $(GITIGNOREFILES) \ $(CLEANFILES) \ $(PROGRAMS) \ $(check_PROGRAMS) \ $(EXTRA_PROGRAMS) \ $(LTLIBRARIES) \ so_locations \ .libs _libs \ $(MOSTLYCLEANFILES) \ "*.$(OBJEXT)" \ "*.lo" \ $(DISTCLEANFILES) \ $(am__CONFIG_DISTCLEAN_FILES) \ $(CONFIG_CLEAN_FILES) \ TAGS ID GTAGS GRTAGS GSYMS GPATH tags \ "*.tab.c" \ $(MAINTAINERCLEANFILES) \ $(BUILT_SOURCES) \ $(DEPDIR) \ Makefile \ Makefile.in \ "*.orig" \ "*.rej" \ "*.bak" \ "*~" \ ".*.sw[nop]" \ ; do echo /$$x; done; \ } | \ sed "s@^/`echo "$(srcdir)" | sed 's/\(.\)/[\1]/g'`/@/@" | \ sed 's@/[.]/@/@g' | \ LC_ALL=C sort | uniq > $@.tmp && \ mv $@.tmp $@; all: $(srcdir)/.gitignore gitignore-recurse-maybe gitignore-recurse-maybe: @if test "x$(SUBDIRS)" = "x$(DIST_SUBDIRS)"; then :; else \ $(MAKE) $(AM_MAKEFLAGS) gitignore-recurse; \ fi; gitignore-recurse: @for subdir in $(DIST_SUBDIRS); do \ case " $(SUBDIRS) " in \ *" $$subdir "*) :;; \ *) test "$$subdir" = . || (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) .gitignore gitignore-recurse || echo "Skipping $$subdir");; \ esac; \ done gitignore: $(srcdir)/.gitignore gitignore-recurse maintainer-clean: gitignore-clean gitignore-clean: -rm -f $(srcdir)/.gitignore .PHONY: gitignore-clean gitignore gitignore-recurse gitignore-recurse-maybe mx-1.4.7/m4/000077500000000000000000000000001201047117600124665ustar00rootroot00000000000000mx-1.4.7/m4/as-compiler-flag.m4000066400000000000000000000027371201047117600160630ustar00rootroot00000000000000dnl as-compiler-flag.m4 0.1.0 dnl autostars m4 macro for detection of compiler flags dnl David Schleef dnl $Id: as-compiler-flag.m4,v 1.1 2005/12/15 23:35:19 ds Exp $ dnl AS_COMPILER_FLAG(CFLAGS, ACTION-IF-ACCEPTED, [ACTION-IF-NOT-ACCEPTED]) dnl Tries to compile with the given CFLAGS. dnl Runs ACTION-IF-ACCEPTED if the compiler can compile with the flags, dnl and ACTION-IF-NOT-ACCEPTED otherwise. AC_DEFUN([AS_COMPILER_FLAG], [ AC_MSG_CHECKING([to see if compiler understands $1]) save_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS $1" AC_TRY_COMPILE([ ], [], [flag_ok=yes], [flag_ok=no]) CFLAGS="$save_CFLAGS" if test "X$flag_ok" = Xyes ; then m4_ifvaln([$2],[$2]) true else m4_ifvaln([$3],[$3]) true fi AC_MSG_RESULT([$flag_ok]) ]) dnl AS_COMPILER_FLAGS(VAR, FLAGS) dnl Tries to compile with the given CFLAGS. AC_DEFUN([AS_COMPILER_FLAGS], [ list=$2 flags_supported="" flags_unsupported="" AC_MSG_CHECKING([for supported compiler flags]) for each in $list do save_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS $each" AC_TRY_COMPILE([ ], [], [flag_ok=yes], [flag_ok=no]) CFLAGS="$save_CFLAGS" if test "X$flag_ok" = Xyes ; then flags_supported="$flags_supported $each" else flags_unsupported="$flags_unsupported $each" fi done AC_MSG_RESULT([$flags_supported]) if test "X$flags_unsupported" != X ; then AC_MSG_WARN([unsupported compiler flags: $flags_unsupported]) fi $1="$$1 $flags_supported" ]) mx-1.4.7/m4/as-linguas.m4000066400000000000000000000014101201047117600147670ustar00rootroot00000000000000# Set ALL_ALL_LINGUAS based on the .po files present. Optional argument is the # name of the po directory. $podir/LINGUAS.ignore can be used to ignore a # subset of the po files. AC_DEFUN([AS_ALL_LINGUAS], [ AC_MSG_CHECKING([for linguas]) podir="m4_default([$1],[$srcdir/po])" linguas=`cd $podir && ls *.po 2>/dev/null | awk 'BEGIN { FS="."; ORS=" " } { print $[]1 }'` if test -f "$podir/LINGUAS.ignore"; then ALL_LINGUAS=""; ignore_linguas=`sed -n -e 's/^\s\+\|\s\+$//g' -e '/^#/b' -e '/\S/!b' \ -e 's/\s\+/\n/g' -e p "$podir/LINGUAS.ignore"`; for lang in $linguas; do if ! echo "$ignore_linguas" | grep -q "^${lang}$"; then ALL_LINGUAS="$ALL_LINGUAS $lang"; fi; done; else ALL_LINGUAS="$linguas"; fi; AC_SUBST([ALL_LINGUAS]) AC_MSG_RESULT($ALL_LINGUAS) ]) mx-1.4.7/m4/introspection.m4000066400000000000000000000066141201047117600156370ustar00rootroot00000000000000dnl -*- mode: autoconf -*- dnl Copyright 2009 Johan Dahlin dnl dnl This file is free software; the author(s) gives unlimited dnl permission to copy and/or distribute it, with or without dnl modifications, as long as this notice is preserved. dnl # serial 1 m4_define([_GOBJECT_INTROSPECTION_CHECK_INTERNAL], [ AC_BEFORE([AC_PROG_LIBTOOL],[$0])dnl setup libtool first AC_BEFORE([AM_PROG_LIBTOOL],[$0])dnl setup libtool first AC_BEFORE([LT_INIT],[$0])dnl setup libtool first dnl enable/disable introspection m4_if([$2], [require], [dnl enable_introspection=yes ],[dnl AC_ARG_ENABLE(introspection, AS_HELP_STRING([--enable-introspection[=@<:@no/auto/yes@:>@]], [Enable introspection for this build]),, [enable_introspection=auto]) ])dnl AC_MSG_CHECKING([for gobject-introspection]) dnl presence/version checking AS_CASE([$enable_introspection], [no], [dnl found_introspection="no (disabled, use --enable-introspection to enable)" ],dnl [yes],[dnl PKG_CHECK_EXISTS([gobject-introspection-1.0],, AC_MSG_ERROR([gobject-introspection-1.0 is not installed])) PKG_CHECK_EXISTS([gobject-introspection-1.0 >= $1], found_introspection=yes, AC_MSG_ERROR([You need to have gobject-introspection >= $1 installed to build AC_PACKAGE_NAME])) ],dnl [auto],[dnl PKG_CHECK_EXISTS([gobject-introspection-1.0 >= $1], found_introspection=yes, found_introspection=no) ],dnl [dnl AC_MSG_ERROR([invalid argument passed to --enable-introspection, should be one of @<:@no/auto/yes@:>@]) ])dnl AC_MSG_RESULT([$found_introspection]) INTROSPECTION_SCANNER= INTROSPECTION_COMPILER= INTROSPECTION_GENERATE= INTROSPECTION_GIRDIR= INTROSPECTION_TYPELIBDIR= if test "x$found_introspection" = "xyes"; then INTROSPECTION_SCANNER=`$PKG_CONFIG --variable=g_ir_scanner gobject-introspection-1.0` INTROSPECTION_COMPILER=`$PKG_CONFIG --variable=g_ir_compiler gobject-introspection-1.0` INTROSPECTION_GENERATE=`$PKG_CONFIG --variable=g_ir_generate gobject-introspection-1.0` INTROSPECTION_GIRDIR=`$PKG_CONFIG --variable=girdir gobject-introspection-1.0` INTROSPECTION_TYPELIBDIR="$($PKG_CONFIG --variable=typelibdir gobject-introspection-1.0)" INTROSPECTION_CFLAGS=`$PKG_CONFIG --cflags gobject-introspection-1.0` INTROSPECTION_LIBS=`$PKG_CONFIG --libs gobject-introspection-1.0` INTROSPECTION_MAKEFILE=`$PKG_CONFIG --variable=datadir gobject-introspection-1.0`/gobject-introspection-1.0/Makefile.introspection fi AC_SUBST(INTROSPECTION_SCANNER) AC_SUBST(INTROSPECTION_COMPILER) AC_SUBST(INTROSPECTION_GENERATE) AC_SUBST(INTROSPECTION_GIRDIR) AC_SUBST(INTROSPECTION_TYPELIBDIR) AC_SUBST(INTROSPECTION_CFLAGS) AC_SUBST(INTROSPECTION_LIBS) AC_SUBST(INTROSPECTION_MAKEFILE) AM_CONDITIONAL(HAVE_INTROSPECTION, test "x$found_introspection" = "xyes") ]) dnl Usage: dnl GOBJECT_INTROSPECTION_CHECK([minimum-g-i-version]) AC_DEFUN([GOBJECT_INTROSPECTION_CHECK], [ _GOBJECT_INTROSPECTION_CHECK_INTERNAL([$1]) ]) dnl Usage: dnl GOBJECT_INTROSPECTION_REQUIRE([minimum-g-i-version]) AC_DEFUN([GOBJECT_INTROSPECTION_REQUIRE], [ _GOBJECT_INTROSPECTION_CHECK_INTERNAL([$1], [require]) ]) mx-1.4.7/mx-gtk.pc.in000066400000000000000000000004641201047117600143120ustar00rootroot00000000000000prefix=@prefix@ exec_prefix=${prefix} libdir=${exec_prefix}/lib includedir=${exec_prefix}/include Name: MxGtk Description: An experimental toolkit for moblin-netbook implementation Version: @VERSION@ Libs: -L${libdir} -lmx-gtk-@MX_API_VERSION@ Cflags: -I${includedir}/mx-@MX_API_VERSION@ Requires: gtk+-2.0 mx-1.4.7/mx-gtk/000077500000000000000000000000001201047117600133555ustar00rootroot00000000000000mx-1.4.7/mx-gtk/Makefile.am000066400000000000000000000041451201047117600154150ustar00rootroot00000000000000NULL = source_gtk_h = \ $(top_srcdir)/mx-gtk/mx-gtk-frame.h \ $(top_srcdir)/mx-gtk/mx-gtk-light-switch.h \ $(NULL) source_gtk_c = \ $(top_srcdir)/mx-gtk/mx-gtk-frame.c \ $(top_srcdir)/mx-gtk/mx-gtk-light-switch.c \ $(NULL) mxincludedir = $(includedir)/mx-$(MX_API_VERSION)/mx-gtk mxinclude_DATA = \ $(source_gtk_h) \ $(top_srcdir)/mx-gtk/mx-gtk.h \ $(NULL) lib_LTLIBRARIES = libmx-gtk-@MX_API_VERSION@.la # # Mx GTK+ Library # # This is a seperate library with GTK+ widgets. # # libmx-gtk library libmx_gtk_@MX_API_VERSION@_la_CFLAGS = \ $(common_includes) \ -DG_LOG_DOMAIN=\"MxGtk\" \ -DGTK_DISABLE_DEPRECATED \ $(MX_MAINTAINER_CFLAGS) \ $(MX_DEBUG_CFLAGS) \ $(GTK_CFLAGS) \ $(NULL) libmx_gtk_@MX_API_VERSION@_la_LDFLAGS = $(GTK_LT_LDFLAGS) $(common_ldflags) libmx_gtk_@MX_API_VERSION@_la_SOURCES = \ $(source_gtk_h) \ $(source_h_priv) \ $(source_gtk_c) \ mx-gtk.h \ $(NULL) libmx_gtk_@MX_API_VERSION@_la_LIBADD = $(GTK_LIBS) CLEANFILES= # # Introspection Data # if HAVE_INTROSPECTION BUILT_GIRSOURCES = MxGtk-@MX_API_VERSION@.gir: $(INTROSPECTION_SCANNER) libmx-gtk-@MX_API_VERSION@.la $(AM_V_GEN)$(INTROSPECTION_SCANNER) -v \ --namespace MxGtk --nsversion=@MX_API_VERSION@ \ $(common_includes) \ --c-include="mx-gtk/mx-gtk.h" \ --include=GObject-2.0 \ --include=Gtk-2.0 \ --library=libmx-gtk-@MX_API_VERSION@.la \ --libtool="$(top_builddir)/libtool" \ --pkg gobject-2.0 \ --pkg gtk+-2.0 \ --pkg-export mx-gtk-1.0 \ --output $@ \ $(source_gtk_h) \ $(top_srcdir)/mx-gtk/mx-gtk.h \ $(source_gtk_c) BUILT_GIRSOURCES += MxGtk-@MX_API_VERSION@.gir girdir = $(datadir)/gir-1.0 gir_DATA = $(BUILT_GIRSOURCES) typelibsdir = $(libdir)/girepository-1.0/ typelibs_DATA = $(BUILT_GIRSOURCES:.gir=.typelib) %.typelib: %.gir $(INTROSPECTION_COMPILER) $(AM_V_GEN) \ LD_LIBRARY_PATH=.libs${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH} \ $(INTROSPECTION_COMPILER) \ --includedir=$(srcdir) \ --includedir=. \ $(INTROSPECTION_COMPILER_OPTS) $< -o $(@F) CLEANFILES += $(BUILT_GIRSOURCES) $(typelibs_DATA) endif # HAVE_INTROSPECTION -include $(top_srcdir)/git.mk mx-1.4.7/mx-gtk/mx-gtk-frame.c000066400000000000000000000265611201047117600160320ustar00rootroot00000000000000/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright 2009 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * Boston, MA 02111-1307, USA. * */ /** * SECTION:mx-gtk-frame * @short_description: a specially styled frame for GTK+ * * A specially styled frame for use in GTK+ applications. */ #include "mx-gtk-frame.h" #include static GdkColor mx_gtk_frame_default_border_color = { 0, 0xdddd, 0xe2e2, 0xe5e5 }; static void mx_gtk_frame_buildable_init (GtkBuildableIface *iface); static void mx_gtk_frame_buildable_add_child (GtkBuildable *buildable, GtkBuilder *builder, GObject *child, const gchar *type); G_DEFINE_TYPE_WITH_CODE (MxGtkFrame, mx_gtk_frame, GTK_TYPE_FRAME, G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE, mx_gtk_frame_buildable_init)) static void mx_gtk_frame_dispose (GObject *object) { G_OBJECT_CLASS (mx_gtk_frame_parent_class)->dispose (object); } static void mx_gtk_frame_finalize (GObject *object) { G_OBJECT_CLASS (mx_gtk_frame_parent_class)->finalize (object); } static void mx_gtk_frame_update_style (MxGtkFrame *frame) { GdkColor *border_color; char *font = NULL; gtk_widget_style_get (GTK_WIDGET (frame), "border-color", &border_color, "title-font", &font, NULL); if (border_color) { frame->border_color = *border_color; gdk_color_free (border_color); } else { frame->border_color = mx_gtk_frame_default_border_color; } if (font) { GtkWidget *label_widget; label_widget = gtk_frame_get_label_widget (GTK_FRAME (frame)); if (label_widget) { PangoFontDescription *desc; desc = pango_font_description_from_string (font); gtk_widget_modify_font (label_widget, desc); pango_font_description_free (desc); } g_free (font); } } static void label_changed_cb (MxGtkFrame *frame) { char *font = NULL; GtkFrame *gtk_frame = GTK_FRAME (frame); GtkWidget *label_widget; label_widget = gtk_frame_get_label_widget (gtk_frame); if (!label_widget) return; /* ensure font is correct */ gtk_widget_style_get (GTK_WIDGET (frame), "title-font", &font, NULL); if (font) { PangoFontDescription *desc; desc = pango_font_description_from_string (font); gtk_widget_modify_font (label_widget, desc); pango_font_description_free (desc); g_free (font); } gtk_misc_set_alignment (GTK_MISC (label_widget), 0.0, 1.0); } static void rounded_rectangle (cairo_t * cr, double x, double y, double w, double h, guint radius) { if (radius > w / 2) radius = w / 2; if (radius > h / 2) radius = h / 2; cairo_move_to (cr, x + radius, y); cairo_arc (cr, x + w - radius, y + radius, radius, M_PI * 1.5, M_PI * 2); cairo_arc (cr, x + w - radius, y + h - radius, radius, 0, M_PI * 0.5); cairo_arc (cr, x + radius, y + h - radius, radius, M_PI * 0.5, M_PI); cairo_arc (cr, x + radius, y + radius, radius, M_PI, M_PI * 1.5); } static void mx_gtk_frame_paint (GtkWidget *widget, GdkRectangle *area) { MxGtkFrame *frame = MX_GTK_FRAME (widget); cairo_t *cairo; GtkStyle *style; guint width; GtkAllocation allocation; g_return_if_fail (widget != NULL); g_return_if_fail (MX_GTK_IS_FRAME (widget)); g_return_if_fail (area != NULL); style = gtk_widget_get_style (widget); cairo = gdk_cairo_create (gtk_widget_get_window (widget)); width = gtk_container_get_border_width (GTK_CONTAINER (widget)); /* clip to area */ gdk_cairo_rectangle (cairo, area); cairo_clip (cairo); /* initialise the background */ gdk_cairo_set_source_color (cairo, &style->bg[gtk_widget_get_state (widget)]); gtk_widget_get_allocation (widget, &allocation); cairo_rectangle (cairo, allocation.x, allocation.y, allocation.width, allocation.height); cairo_fill (cairo); /* draw border */ if (width != 0) { gdk_cairo_set_source_color (cairo, &frame->border_color); cairo_set_line_width (cairo, width); rounded_rectangle (cairo, allocation.x + (width / 2), allocation.y + (width / 2), allocation.width - (width), allocation.height - (width), width); cairo_stroke (cairo); } cairo_destroy (cairo); } static gboolean mx_gtk_frame_expose (GtkWidget *widget, GdkEventExpose *event) { GtkWidgetClass *grand_parent; if (gtk_widget_is_drawable (widget)) { mx_gtk_frame_paint (widget, &event->area); grand_parent = GTK_WIDGET_CLASS (g_type_class_peek_parent (mx_gtk_frame_parent_class)); grand_parent->expose_event (widget, event); } return FALSE; } static void mx_gtk_frame_size_request (GtkWidget *widget, GtkRequisition *requisition) { GtkFrame *frame = GTK_FRAME (widget); GtkBin *bin = GTK_BIN (widget); GtkRequisition child_req; GtkRequisition title_req; GtkWidget *label_widget; guint border_width; GtkStyle *style; GtkWidget *child; child_req.width = child_req.height = 0; child = gtk_bin_get_child (bin); if (child) gtk_widget_size_request (child, &child_req); title_req.width = title_req.height = 0; label_widget = gtk_frame_get_label_widget (frame); if (label_widget) { gtk_widget_size_request (label_widget, &title_req); } border_width = gtk_container_get_border_width (GTK_CONTAINER (widget)); style = gtk_widget_get_style (widget); requisition->width = MAX (child_req.width, title_req.width) + 2 * (border_width + style->xthickness); requisition->height = title_req.height + child_req.height + 2 * (border_width + style->ythickness); } static void mx_gtk_frame_size_allocate (GtkWidget *widget, GtkAllocation *allocation) { GtkBin *bin = GTK_BIN (widget); GtkFrame *frame = GTK_FRAME (widget); GtkAllocation child_allocation, title_allocation, old_allocation = { 0, }; int xmargin, ymargin; guint border_width; GtkStyle *style; GtkWidget *label_widget, *child; gtk_widget_set_allocation (widget, allocation); border_width = gtk_container_get_border_width (GTK_CONTAINER (widget)); style = gtk_widget_get_style (widget); xmargin = border_width + style->xthickness; ymargin = border_width + style->ythickness; title_allocation.height = title_allocation.width = 0; label_widget = gtk_frame_get_label_widget (frame); if (label_widget) { GtkRequisition title_req; gtk_widget_get_child_requisition (label_widget, &title_req); title_allocation.x = allocation->x + xmargin; title_allocation.y = allocation->y + ymargin; title_allocation.width = MIN (title_req.width, allocation->width - 2 * xmargin); title_allocation.height = title_req.height; gtk_widget_size_allocate (label_widget, &title_allocation); } child = gtk_bin_get_child (bin); if (child) gtk_widget_get_allocation (child, &old_allocation); child_allocation.x = allocation->x + xmargin; child_allocation.y = allocation->y + ymargin + title_allocation.height; child_allocation.width = allocation->width - 2 * xmargin; child_allocation.height = allocation->height - 2 * ymargin - title_allocation.height; if (gtk_widget_get_mapped (widget) && (child_allocation.x != old_allocation.x || child_allocation.y != old_allocation.y || child_allocation.width != old_allocation.width || child_allocation.height != old_allocation.height)) { gdk_window_invalidate_rect (gtk_widget_get_window (widget), allocation, FALSE); } if (child && gtk_widget_get_visible (child)) { gtk_widget_size_allocate (child, &child_allocation); } /* frame->child_allocation is only used inside GtkFrame paint and * size_allocate, so it doesn't strictly need to be set since MxGtkFrame * overrides both functions without chaining up */ /* frame->child_allocation = child_allocation; */ } static void mx_gtk_frame_style_set (GtkWidget *widget, GtkStyle *previous) { MxGtkFrame *frame = MX_GTK_FRAME (widget); mx_gtk_frame_update_style (frame); GTK_WIDGET_CLASS (mx_gtk_frame_parent_class)->style_set (widget, previous); } static void mx_gtk_frame_class_init (MxGtkFrameClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); GParamSpec *pspec; object_class->dispose = mx_gtk_frame_dispose; object_class->finalize = mx_gtk_frame_finalize; widget_class->expose_event = mx_gtk_frame_expose; widget_class->size_request = mx_gtk_frame_size_request; widget_class->size_allocate = mx_gtk_frame_size_allocate; widget_class->style_set = mx_gtk_frame_style_set; pspec = g_param_spec_boxed ("border-color", "Border color", "Color of the outside border", GDK_TYPE_COLOR, G_PARAM_READABLE); gtk_widget_class_install_style_property (widget_class, pspec); pspec = g_param_spec_string ("title-font", "Title font", "Pango font description string for title text", "12", G_PARAM_READWRITE); gtk_widget_class_install_style_property (widget_class, pspec); } static void mx_gtk_frame_buildable_add_child (GtkBuildable *buildable, GtkBuilder *builder, GObject *child, const gchar *type) { if (!type) gtk_container_add (GTK_CONTAINER (buildable), GTK_WIDGET (child)); else GTK_BUILDER_WARN_INVALID_CHILD_TYPE (MX_GTK_FRAME (buildable), type); } static void mx_gtk_frame_buildable_init (GtkBuildableIface *iface) { iface->add_child = mx_gtk_frame_buildable_add_child; } static void mx_gtk_frame_init (MxGtkFrame *self) { g_signal_connect (self, "notify::label-widget", G_CALLBACK (label_changed_cb), NULL); } /** * mx_gtk_frame_new: * * Create a new specially styled frame. * * Returns: a newly allocated #MxGtkFrame */ GtkWidget* mx_gtk_frame_new (void) { return g_object_new (MX_GTK_TYPE_FRAME, "border-width", 4, NULL); } mx-1.4.7/mx-gtk/mx-gtk-frame.h000066400000000000000000000035331201047117600160310ustar00rootroot00000000000000/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright 2009 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * Boston, MA 02111-1307, USA. * */ #ifndef _MX_GTK_FRAME #define _MX_GTK_FRAME #include #include G_BEGIN_DECLS #define MX_GTK_TYPE_FRAME mx_gtk_frame_get_type () #define MX_GTK_FRAME(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), MX_GTK_TYPE_FRAME, MxGtkFrame)) #define MX_GTK_FRAME_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), MX_GTK_TYPE_FRAME, MxGtkFrameClass)) #define MX_GTK_IS_FRAME(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MX_GTK_TYPE_FRAME)) #define MX_GTK_IS_FRAME_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE ((klass), MX_GTK_TYPE_FRAME)) #define MX_GTK_FRAME_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), MX_GTK_TYPE_FRAME, MxGtkFrameClass)) /** * MxGtkFrame: * * The contents of this structure are private and should only be accessed * through the public API. */ typedef struct { /*< private >*/ GtkFrame parent; GdkColor border_color; } MxGtkFrame; typedef struct { GtkFrameClass parent_class; } MxGtkFrameClass; GType mx_gtk_frame_get_type (void); GtkWidget* mx_gtk_frame_new (void); G_END_DECLS #endif mx-1.4.7/mx-gtk/mx-gtk-light-switch.c000066400000000000000000000315211201047117600173360ustar00rootroot00000000000000/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright 2009 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * Boston, MA 02111-1307, USA. * */ /** * SECTION:mx-gtk-light-switch * @short_description: a toggle switch between two states * * A visual representation of a toggle switch that can move between two states. */ #include #include "mx-gtk-light-switch.h" #include /* We use the special gcc constructor attribute so we can avoid * requiring an init function to get translations to work! This * function is also in mx-utils but we also need it here * because that is a separate library */ static void __attribute__ ((constructor)) _start (void) { bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR); bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8"); } G_DEFINE_TYPE (MxGtkLightSwitch, mx_gtk_light_switch, GTK_TYPE_DRAWING_AREA) #define MX_GTK_LIGHT_SWITCH_GET_PRIVATE(o) \ (G_TYPE_INSTANCE_GET_PRIVATE ((o), MX_GTK_TYPE_LIGHT_SWITCH, MxGtkLightSwitchPrivate)) static gboolean mx_gtk_light_switch_configure (GtkWidget *lightswitch, GdkEventConfigure *event); static gboolean mx_gtk_light_switch_expose (GtkWidget *lightswitch, GdkEventExpose *event); static gboolean mx_gtk_light_switch_button_release (GtkWidget *lightswitch, GdkEventButton *event); static gboolean mx_gtk_light_switch_button_press (GtkWidget *lightswitch, GdkEventButton *event); static gboolean mx_gtk_light_switch_motion_notify (GtkWidget *lightswitch, GdkEventMotion *event); static void mx_gtk_light_switch_size_request (GtkWidget *lightswitch, GtkRequisition *req); static void mx_gtk_light_switch_style_set (GtkWidget *lightswitch, GtkStyle *previous_style); enum { SWITCH_FLIPPED, LAST_SIGNAL }; static guint mx_gtk_light_switch_signals[LAST_SIGNAL] = { 0 }; enum { PROP_0, PROP_ACTIVE, }; typedef struct _MxGtkLightSwitchPrivate MxGtkLightSwitchPrivate; struct _MxGtkLightSwitchPrivate { gboolean active; /* boolean state of switch */ gboolean dragging; /* true if dragging switch */ gint x; /* the x position of the switch */ gint drag_start; /* position dragging started at */ gint drag_threshold; gint switch_width; gint switch_height; gint trough_width; gint offset; /* offset of the mouse to slider when dragging */ }; static void mx_gtk_light_switch_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { MxGtkLightSwitch *ls; ls = MX_GTK_LIGHT_SWITCH (object); switch (prop_id) { case PROP_ACTIVE: mx_gtk_light_switch_set_active (ls, g_value_get_boolean (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); } } static void mx_gtk_light_switch_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { MxGtkLightSwitchPrivate *priv; priv = MX_GTK_LIGHT_SWITCH_GET_PRIVATE (object); switch (prop_id) { case PROP_ACTIVE: g_value_set_boolean (value, priv->active); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); } } static void mx_gtk_light_switch_class_init (MxGtkLightSwitchClass *klass) { GObjectClass *object_class; GtkWidgetClass *widget_class; GParamSpec *spec; object_class = G_OBJECT_CLASS (klass); widget_class = GTK_WIDGET_CLASS (klass); object_class->set_property = mx_gtk_light_switch_set_property; object_class->get_property = mx_gtk_light_switch_get_property; widget_class->configure_event = mx_gtk_light_switch_configure; widget_class->expose_event = mx_gtk_light_switch_expose; widget_class->button_release_event = mx_gtk_light_switch_button_release; widget_class->button_press_event = mx_gtk_light_switch_button_press; widget_class->motion_notify_event = mx_gtk_light_switch_motion_notify; widget_class->size_request = mx_gtk_light_switch_size_request; widget_class->style_set = mx_gtk_light_switch_style_set; spec = g_param_spec_boolean ("active", "Active", "Is the light switch on or not", FALSE, G_PARAM_READWRITE); g_object_class_install_property (object_class, PROP_ACTIVE, spec); /* MxGtkLightSwitch signals */ mx_gtk_light_switch_signals[SWITCH_FLIPPED] = g_signal_new ("switch-flipped", G_OBJECT_CLASS_TYPE (object_class), G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (MxGtkLightSwitchClass, switch_flipped), NULL, NULL, g_cclosure_marshal_VOID__BOOLEAN, G_TYPE_NONE, 1, G_TYPE_BOOLEAN); g_type_class_add_private (klass, sizeof (MxGtkLightSwitchPrivate)); } static void mx_gtk_light_switch_init (MxGtkLightSwitch *self) { MxGtkLightSwitchPrivate *priv; priv = MX_GTK_LIGHT_SWITCH_GET_PRIVATE (self); priv->active = FALSE; priv->x = 0; /* add events, do initial draw/update, etc */ gtk_widget_add_events (GTK_WIDGET (self), GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK); } static void mx_gtk_light_switch_size_request (GtkWidget *lightswitch, GtkRequisition *req) { MxGtkLightSwitchPrivate *priv = MX_GTK_LIGHT_SWITCH_GET_PRIVATE (lightswitch); req->height = priv->switch_height; req->width = priv->trough_width; } static void mx_gtk_light_switch_style_set (GtkWidget *lightswitch, GtkStyle *previous_style) { MxGtkLightSwitchPrivate *priv = MX_GTK_LIGHT_SWITCH_GET_PRIVATE (lightswitch); /* TODO: use style properties for these values */ /* MxToggle is 98x24, so make sure light-switch is at least this size */ priv->trough_width = 98; priv->switch_height = 24; priv->switch_width = 50; } static gboolean mx_gtk_light_switch_configure (GtkWidget *lightswitch, GdkEventConfigure *event) { MxGtkLightSwitchPrivate *priv = MX_GTK_LIGHT_SWITCH_GET_PRIVATE (lightswitch); if (priv->active) priv->x = priv->trough_width - priv->switch_width; else priv->x = 0; return FALSE; } static gboolean mx_gtk_light_switch_expose (GtkWidget *lightswitch, GdkEventExpose *event) { MxGtkLightSwitchPrivate *priv; GtkStyle *style; GtkStateType state_type; cairo_t *cr; cr = gdk_cairo_create (gtk_widget_get_window (lightswitch)); cairo_rectangle (cr, event->area.x, event->area.y, event->area.width, event->area.height); cairo_clip (cr); priv = MX_GTK_LIGHT_SWITCH_GET_PRIVATE (lightswitch); style = gtk_widget_get_style (lightswitch); state_type = gtk_widget_get_state (lightswitch); /* draw the trough */ gtk_paint_box (style, gtk_widget_get_window (lightswitch), (state_type != GTK_STATE_INSENSITIVE && priv->active) ? GTK_STATE_SELECTED : state_type, GTK_SHADOW_IN, NULL, NULL, "light-switch-trough", 0, 0, (priv->trough_width), priv->switch_height); /* draw the switch itself */ if (state_type != GTK_STATE_INSENSITIVE) { gtk_paint_box (style, gtk_widget_get_window (lightswitch), gtk_widget_get_state (lightswitch), GTK_SHADOW_OUT, NULL, NULL, "light-switch-handle", priv->x + style->xthickness, style->ythickness, priv->switch_width - style->xthickness * 2, priv->switch_height - style->ythickness * 2); } cairo_destroy (cr); return FALSE; } static gboolean mx_gtk_light_switch_motion_notify (GtkWidget *lightswitch, GdkEventMotion *event) { MxGtkLightSwitchPrivate *priv; priv = MX_GTK_LIGHT_SWITCH_GET_PRIVATE (lightswitch); if (ABS (event->x - priv->drag_start) < priv->drag_threshold) return TRUE; if (event->state & GDK_BUTTON1_MASK) { gint position = event->x - priv->offset; if (position > (priv->trough_width - priv->switch_width)) priv->x = (priv->trough_width - priv->switch_width); else if (position < 0) priv->x = 0; else priv->x = position; priv->dragging = TRUE; gtk_widget_queue_draw ((GtkWidget *) lightswitch); } return TRUE; } /** * mx_gtk_light_switch_get_active: * @lightswitch: A #MxGtkLightSwitch * * Get the value of the "active" property * * Returns: #TRUE if the switch is "on" */ gboolean mx_gtk_light_switch_get_active (MxGtkLightSwitch *lightswitch) { MxGtkLightSwitchPrivate *priv = MX_GTK_LIGHT_SWITCH_GET_PRIVATE (lightswitch); return priv->active; } /** * mx_gtk_light_switch_set_active: * @lightswitch: A #MxGtkLightSwitch * @active: #TRUE to set the switch to its ON state * * Set the value of the "active" property * */ void mx_gtk_light_switch_set_active (MxGtkLightSwitch *lightswitch, gboolean active) { MxGtkLightSwitchPrivate *priv = MX_GTK_LIGHT_SWITCH_GET_PRIVATE (lightswitch); if (priv->active == active) { return; } else { priv->active = active; if (active == TRUE) { priv->x = priv->trough_width - priv->switch_width; } else { priv->x = 0; } gtk_widget_queue_draw ((GtkWidget *) lightswitch); g_object_notify (G_OBJECT (lightswitch), "active"); g_signal_emit (lightswitch, mx_gtk_light_switch_signals[SWITCH_FLIPPED], 0, priv->active); } } static gboolean mx_gtk_light_switch_button_press (GtkWidget *lightswitch, GdkEventButton *event) { MxGtkLightSwitchPrivate *priv = MX_GTK_LIGHT_SWITCH_GET_PRIVATE (lightswitch); if (priv->active) priv->offset = event->x - (priv->trough_width - priv->switch_width); else priv->offset = event->x; priv->drag_start = event->x; g_object_get (gtk_widget_get_settings (lightswitch), "gtk-dnd-drag-threshold", &priv->drag_threshold, NULL); return FALSE; } static gboolean mx_gtk_light_switch_button_release (GtkWidget *lightswitch, GdkEventButton *event) { MxGtkLightSwitchPrivate *priv; priv = MX_GTK_LIGHT_SWITCH_GET_PRIVATE (lightswitch); /* detect whereabouts we are and "drop" into a state */ if (priv->dragging) { priv->dragging = FALSE; if (priv->x + (priv->switch_width / 2) > priv->trough_width / 2) { mx_gtk_light_switch_set_active ((MxGtkLightSwitch *) lightswitch, TRUE); priv->x = priv->trough_width - priv->switch_width; } else { mx_gtk_light_switch_set_active ((MxGtkLightSwitch *) lightswitch, FALSE); priv->x = 0; } /* we always need to queue a redraw after dragging to put the slider back * in the correct place */ gtk_widget_queue_draw ((GtkWidget *) lightswitch); } else { mx_gtk_light_switch_set_active ((MxGtkLightSwitch *) lightswitch, !priv->active); } return FALSE; } /** * mx_gtk_light_switch_new: * * Create a #MxGtkLightSwitch * * Returns: a newly allocated #MxGtkLightSwitch */ GtkWidget* mx_gtk_light_switch_new (void) { return g_object_new (MX_GTK_TYPE_LIGHT_SWITCH, NULL); } mx-1.4.7/mx-gtk/mx-gtk-light-switch.h000066400000000000000000000044051201047117600173440ustar00rootroot00000000000000/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright 2009 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * Boston, MA 02111-1307, USA. * */ #ifndef _MX_GTK_LIGHT_SWITCH #define _MX_GTK_LIGHT_SWITCH #include G_BEGIN_DECLS #define MX_GTK_TYPE_LIGHT_SWITCH mx_gtk_light_switch_get_type () #define MX_GTK_LIGHT_SWITCH(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), MX_GTK_TYPE_LIGHT_SWITCH, MxGtkLightSwitch)) #define MX_GTK_LIGHT_SWITCH_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), MX_GTK_TYPE_LIGHT_SWITCH, MxGtkLightSwitchClass)) #define MX_GTK_IS_LIGHT_SWITCH(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MX_GTK_TYPE_LIGHT_SWITCH)) #define MX_GTK_IS_LIGHT_SWITCH_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE ((klass), MX_GTK_TYPE_LIGHT_SWITCH)) #define MX_GTK_LIGHT_SWITCH_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), MX_GTK_TYPE_LIGHT_SWITCH, MxGtkLightSwitchClass)) /** * MxGtkLightSwitch: * * The contents of this structure are private and should only be accessed * through the public API. */ typedef struct { /*< private >*/ GtkDrawingArea parent; } MxGtkLightSwitch; typedef struct { GtkDrawingAreaClass parent_class; void (*switch_flipped) (MxGtkLightSwitch *lightswitch, gboolean state); } MxGtkLightSwitchClass; GType mx_gtk_light_switch_get_type (void); void mx_gtk_light_switch_set_active (MxGtkLightSwitch *lightswitch, gboolean active); gboolean mx_gtk_light_switch_get_active (MxGtkLightSwitch *lightswitch); GtkWidget* mx_gtk_light_switch_new (void); G_END_DECLS #endif /* _MX_GTK_LIGHT_SWITCH */ mx-1.4.7/mx-gtk/mx-gtk.h000066400000000000000000000021011201047117600147270ustar00rootroot00000000000000/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * mx.h: Collection of high-level actors for Clutter * * Copyright 2007 OpenedHand * Copyright 2009 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * Boston, MA 02111-1307, USA. * */ #ifndef __MX_GTK_H__ #define __MX_GTK_H__ #define MX_H_INSIDE #include "mx-gtk-light-switch.h" #include "mx-gtk-frame.h" #undef MX_H_INSIDE #endif /* __MX_GTK_H__ */ mx-1.4.7/mx.pc.in000066400000000000000000000004601201047117600135230ustar00rootroot00000000000000prefix=@prefix@ exec_prefix=${prefix} libdir=${exec_prefix}/lib includedir=${exec_prefix}/include Name: Mx Description: An experimental toolkit for moblin-netbook implementation Version: @VERSION@ Libs: -L${libdir} -lmx-@MX_API_VERSION@ Cflags: -I${includedir}/mx-@MX_API_VERSION@ Requires: clutter-1.0 mx-1.4.7/mx/000077500000000000000000000000001201047117600125725ustar00rootroot00000000000000mx-1.4.7/mx/.vimrc000066400000000000000000000001571201047117600137160ustar00rootroot00000000000000:set expandtab shiftwidth=2 softtabstop=2 :set cindent cinoptions=>4,n-2,{2,^-2,\:0,=2,g0,h2,t0,+2,(0,u0,w1,m1 mx-1.4.7/mx/Makefile.am000066400000000000000000000221111201047117600146230ustar00rootroot00000000000000NULL = # installed utilities bin_PROGRAMS = mx-create-image-cache mx_create_image_cache_SOURCES = mx-create-image-cache.c mx_create_image_cache_LDADD = $(MX_IMAGE_CACHE_LIBS) mx_create_image_cache_CFLAGS = $(MX_IMAGE_CACHE_CFLAGS) $(MX_MAINTAINER_CFLAGS) BUILT_SOURCES = \ mx-enum-types.h \ mx-enum-types.c \ mx-marshal.h \ mx-marshal.c if HAVE_X11 WINSYS_SRC = \ $(top_srcdir)/mx/x11/mx-clipboard-x11.c \ $(top_srcdir)/mx/x11/mx-settings-x11.c \ $(top_srcdir)/mx/x11/mx-settings-x11.h \ $(top_srcdir)/mx/x11/mx-window-x11.c \ $(top_srcdir)/mx/x11/mx-window-x11.h \ $(top_srcdir)/mx/x11/xsettings-client.c \ $(top_srcdir)/mx/x11/xsettings-client.h \ $(top_srcdir)/mx/x11/xsettings-common.c \ $(top_srcdir)/mx/x11/xsettings-common.h \ $(NULL) else WINSYS_SRC = \ $(top_srcdir)/mx/mx-clipboard-default.c \ $(NULL) endif # keep sorted alphabetically, please source_h = \ $(top_srcdir)/mx/mx-action.h \ $(top_srcdir)/mx/mx-actor-manager.h \ $(top_srcdir)/mx/mx-adjustment.h \ $(top_srcdir)/mx/mx-application.h \ $(top_srcdir)/mx/mx-bin.h \ $(top_srcdir)/mx/mx-box-layout.h \ $(top_srcdir)/mx/mx-box-layout-child.h \ $(top_srcdir)/mx/mx-combo-box.h \ $(top_srcdir)/mx/mx-button.h \ $(top_srcdir)/mx/mx-button-group.h \ $(top_srcdir)/mx/mx-deform-texture.h \ $(top_srcdir)/mx/mx-deform-bow-tie.h \ $(top_srcdir)/mx/mx-deform-page-turn.h \ $(top_srcdir)/mx/mx-deform-waves.h \ $(top_srcdir)/mx/mx-dialog.h \ $(top_srcdir)/mx/mx-draggable.h \ $(top_srcdir)/mx/mx-droppable.h \ $(top_srcdir)/mx/mx-clipboard.h \ $(top_srcdir)/mx/mx-entry.h \ $(top_srcdir)/mx/mx-expander.h \ $(top_srcdir)/mx/mx-fade-effect.h \ $(top_srcdir)/mx/mx-focus-manager.h \ $(top_srcdir)/mx/mx-focusable.h \ $(top_srcdir)/mx/mx-frame.h \ $(top_srcdir)/mx/mx-grid.h \ $(top_srcdir)/mx/mx-item-factory.h \ $(top_srcdir)/mx/mx-item-view.h \ $(top_srcdir)/mx/mx-list-view.h \ $(top_srcdir)/mx/mx-offscreen.h \ $(top_srcdir)/mx/mx-icon.h \ $(top_srcdir)/mx/mx-image.h \ $(top_srcdir)/mx/mx-icon-theme.h \ $(top_srcdir)/mx/mx-label.h \ $(top_srcdir)/mx/mx-notebook.h \ $(top_srcdir)/mx/mx-path-bar.h \ $(top_srcdir)/mx/mx-progress-bar.h \ $(top_srcdir)/mx/mx-menu.h \ $(top_srcdir)/mx/mx-scroll-bar.h \ $(top_srcdir)/mx/mx-scroll-view.h \ $(top_srcdir)/mx/mx-scrollable.h \ $(top_srcdir)/mx/mx-settings.h \ $(top_srcdir)/mx/mx-settings-provider.h \ $(top_srcdir)/mx/mx-slider.h \ $(top_srcdir)/mx/mx-spinner.h \ $(top_srcdir)/mx/mx-stack.h \ $(top_srcdir)/mx/mx-stack-child.h \ $(top_srcdir)/mx/mx-stylable.h \ $(top_srcdir)/mx/mx-style.h \ $(top_srcdir)/mx/mx-table-child.h \ $(top_srcdir)/mx/mx-table.h \ $(top_srcdir)/mx/mx-texture-cache.h \ $(top_srcdir)/mx/mx-texture-frame.h \ $(top_srcdir)/mx/mx-toggle.h \ $(top_srcdir)/mx/mx-toolbar.h \ $(top_srcdir)/mx/mx-tooltip.h \ $(top_srcdir)/mx/mx-types.h \ $(top_srcdir)/mx/mx-utils.h \ $(top_srcdir)/mx/mx-viewport.h \ $(top_srcdir)/mx/mx-widget.h \ $(top_srcdir)/mx/mx-window.h \ $(top_srcdir)/mx/mx-floating-widget.h \ $(top_srcdir)/mx/mx-kinetic-scroll-view.h \ $(NULL) source_h_priv = \ $(top_srcdir)/mx/mx-css.h \ $(top_srcdir)/mx/mx-native-window.h \ $(top_srcdir)/mx/mx-path-bar-button.h \ $(top_srcdir)/mx/mx-progress-bar-fill.h \ $(top_srcdir)/mx/mx-private.h \ $(top_srcdir)/mx/mx-settings-provider.h \ $(NULL) source_c = \ $(top_srcdir)/mx/mx-adjustment.c \ $(top_srcdir)/mx/mx-action.c \ $(top_srcdir)/mx/mx-actor-manager.c \ $(top_srcdir)/mx/mx-application.c \ $(top_srcdir)/mx/mx-bin.c \ $(top_srcdir)/mx/mx-box-layout.c \ $(top_srcdir)/mx/mx-box-layout-child.c \ $(top_srcdir)/mx/mx-css.c \ $(top_srcdir)/mx/mx-combo-box.c \ $(top_srcdir)/mx/mx-button.c \ $(top_srcdir)/mx/mx-button-group.c \ $(top_srcdir)/mx/mx-deform-texture.c \ $(top_srcdir)/mx/mx-deform-bow-tie.c \ $(top_srcdir)/mx/mx-deform-page-turn.c \ $(top_srcdir)/mx/mx-deform-waves.c \ $(top_srcdir)/mx/mx-dialog.c \ $(top_srcdir)/mx/mx-draggable.c \ $(top_srcdir)/mx/mx-droppable.c \ $(top_srcdir)/mx/mx-entry.c \ $(top_srcdir)/mx/mx-expander.c \ $(top_srcdir)/mx/mx-fade-effect.c \ $(top_srcdir)/mx/mx-focus-manager.c \ $(top_srcdir)/mx/mx-focusable.c \ $(top_srcdir)/mx/mx-frame.c \ $(top_srcdir)/mx/mx-grid.c \ $(top_srcdir)/mx/mx-icon-theme.c \ $(top_srcdir)/mx/mx-icon.c \ $(top_srcdir)/mx/mx-image.c \ $(top_srcdir)/mx/mx-item-factory.c \ $(top_srcdir)/mx/mx-item-view.c \ $(top_srcdir)/mx/mx-list-view.c \ $(top_srcdir)/mx/mx-label.c \ $(top_srcdir)/mx/mx-notebook.c \ $(top_srcdir)/mx/mx-offscreen.c \ $(top_srcdir)/mx/mx-path-bar.c \ $(top_srcdir)/mx/mx-path-bar-button.c \ $(top_srcdir)/mx/mx-progress-bar.c \ $(top_srcdir)/mx/mx-progress-bar-fill.c \ $(top_srcdir)/mx/mx-menu.c \ $(top_srcdir)/mx/mx-scroll-bar.c \ $(top_srcdir)/mx/mx-scroll-view.c \ $(top_srcdir)/mx/mx-scrollable.c \ $(top_srcdir)/mx/mx-settings.c \ $(top_srcdir)/mx/mx-slider.c \ $(top_srcdir)/mx/mx-spinner.c \ $(top_srcdir)/mx/mx-stack.c \ $(top_srcdir)/mx/mx-stack-child.c \ $(top_srcdir)/mx/mx-stylable.c \ $(top_srcdir)/mx/mx-style.c \ $(top_srcdir)/mx/mx-table.c \ $(top_srcdir)/mx/mx-table-child.c \ $(top_srcdir)/mx/mx-texture-cache.c \ $(top_srcdir)/mx/mx-texture-frame.c \ $(top_srcdir)/mx/mx-toggle.c \ $(top_srcdir)/mx/mx-toolbar.c \ $(top_srcdir)/mx/mx-tooltip.c \ $(top_srcdir)/mx/mx-types.c \ $(top_srcdir)/mx/mx-utils.c \ $(top_srcdir)/mx/mx-viewport.c \ $(top_srcdir)/mx/mx-widget.c \ $(top_srcdir)/mx/mx-window.c \ $(top_srcdir)/mx/mx-floating-widget.c \ $(top_srcdir)/mx/mx-kinetic-scroll-view.c \ $(NULL) EXTRA_DIST = \ mx-marshal.list \ mx-enum-types.h.in \ mx-enum-types.c.in \ mx-version.h.in STAMP_FILES = stamp-mx-marshal.h stamp-mx-enum-types.h CLEANFILES = $(STAMP_FILES) $(BUILT_SOURCES) mx-marshal.h: stamp-mx-marshal.h @true stamp-mx-marshal.h: Makefile mx-marshal.list $(AM_V_GEN)$(GLIB_GENMARSHAL) \ --prefix=_mx_marshal \ --header \ $(srcdir)/mx-marshal.list > xgen-tmh && \ (cmp -s xgen-tmh mx-marshal.h || cp -f xgen-tmh mx-marshal.h) && \ rm -f xgen-tmh && \ echo timestamp > $(@F) mx-marshal.c: Makefile mx-marshal.list $(AM_V_GEN)(echo "#include \"mx-marshal.h\"" ; \ $(GLIB_GENMARSHAL) \ --prefix=_mx_marshal \ --body \ $(srcdir)/mx-marshal.list ) > xgen-tmc && \ cp -f xgen-tmc mx-marshal.c && \ rm -f xgen-tmc mx-enum-types.h: stamp-mx-enum-types.h Makefile @true stamp-mx-enum-types.h: $(source_h) mx-enum-types.h.in $(AM_V_GEN)( cd $(srcdir) && \ $(GLIB_MKENUMS) \ --template mx-enum-types.h.in \ $(source_h) ) >> xgen-teth && \ (cmp -s xgen-teth mx-enum-types.h || cp xgen-teth mx-enum-types.h) && \ rm -f xgen-teth && \ echo timestamp > $(@F) mx-enum-types.c: stamp-mx-enum-types.h mx-enum-types.c.in $(AM_V_GEN)( cd $(srcdir) && \ $(GLIB_MKENUMS) \ --template mx-enum-types.c.in \ $(source_h) ) >> xgen-tetc && \ cp xgen-tetc mx-enum-types.c && \ rm -f xgen-tetc # libmx library mxincludedir = $(includedir)/mx-$(MX_API_VERSION)/mx mxinclude_DATA = \ $(source_h) \ $(top_srcdir)/mx/mx.h \ $(top_builddir)/mx/mx-version.h \ $(top_builddir)/mx/mx-enum-types.h \ $(NULL) lib_LTLIBRARIES = libmx-@MX_API_VERSION@.la common_includes = \ -I$(top_srcdir) \ -I$(top_builddir) \ -DPACKAGE_LOCALE_DIR=\""$(prefix)/$(DATADIRNAME)/locale"\" \ -DPACKAGE_SRC_DIR=\""$(srcdir)"\" \ -DPACKAGE_DATA_DIR=\""$(datadir)"\" \ -DMX_COMPILATION common_ldflags = \ -export-dynamic \ -export-symbols-regex "^mx.*" \ -rpath $(libdir) libmx_@MX_API_VERSION@_la_CFLAGS = \ $(common_includes) \ -DG_LOG_DOMAIN=\"Mx\" \ $(MX_MAINTAINER_CFLAGS) \ $(MX_DEBUG_CFLAGS) \ $(MX_CFLAGS) \ $(NULL) libmx_@MX_API_VERSION@_la_LDFLAGS = $(MX_LT_LDFLAGS) $(common_ldflags) libmx_@MX_API_VERSION@_la_SOURCES = \ $(BUILT_SOURCES) \ $(WINSYS_SRC) \ $(source_h) \ $(source_h_priv) \ $(source_c) \ $(top_srcdir)/mx/mx-native-window.c \ $(top_srcdir)/mx/mx-private.c \ $(top_srcdir)/mx/mx-settings-provider.c \ $(top_srcdir)/mx/mx.h \ $(NULL) libmx_@MX_API_VERSION@_la_LIBADD = $(MX_LIBS) -lm if HAVE_INTROSPECTION -include $(INTROSPECTION_MAKEFILE) INTROSPECTION_GIRS = Mx-@MX_API_VERSION@.gir Mx-@MX_API_VERSION@.gir: libmx-@MX_API_VERSION@.la Makefile $(INTROSPECTION_SCANNER) Mx_@MX_API_VERSION_AM@_gir_NAMESPACE = Mx Mx_@MX_API_VERSION_AM@_gir_VERSION = @MX_API_VERSION@ Mx_@MX_API_VERSION_AM@_gir_LIBS = libmx-@MX_API_VERSION@.la Mx_@MX_API_VERSION_AM@_gir_FILES = \ $(source_h) \ $(top_srcdir)/mx/mx.h \ $(top_builddir)/mx/mx-version.h \ $(top_builddir)/mx/mx-enum-types.h \ $(source_c) Mx_@MX_API_VERSION_AM@_gir_CFLAGS = $(common_includes) $(MX_CFLAGS) Mx_@MX_API_VERSION_AM@_gir_INCLUDES = Clutter-1.0 Mx_@MX_API_VERSION_AM@_gir_SCANNERFLAGS = \ --warn-all \ --c-include='mx/mx.h' \ --pkg-export=mx-@MX_API_VERSION@ girdir = $(datadir)/gir-1.0/ gir_DATA = $(INTROSPECTION_GIRS) typelibsdir = $(libdir)/girepository-1.0/ typelibs_DATA = $(INTROSPECTION_GIRS:.gir=.typelib) CLEANFILES += $(gir_DATA) $(typelibs_DATA) endif # HAVE_INTROSPECTION -include $(top_srcdir)/git.mk mx-1.4.7/mx/mx-action.c000066400000000000000000000460611201047117600146440ustar00rootroot00000000000000/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * mx-action.c: MxAction object * * Copyright 2009, 2011 Intel Corporation. * Copyright © 2010 Codethink Limited * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * Boston, MA 02111-1307, USA. * */ /** * SECTION: mx-action * @short_description: Represents a user action * * Actions represent operations that the user can perform, such as items in a * menu or toolbar. */ #include "mx-action.h" #include "mx-types.h" static void g_action_iface_init (GActionInterface *iface); G_DEFINE_TYPE_WITH_CODE (MxAction, mx_action, G_TYPE_INITIALLY_UNOWNED, G_IMPLEMENT_INTERFACE (G_TYPE_ACTION, g_action_iface_init)) #define ACTION_PRIVATE(o) \ (G_TYPE_INSTANCE_GET_PRIVATE ((o), MX_TYPE_ACTION, MxActionPrivate)) struct _MxActionPrivate { /* GAction */ gchar *name; GVariantType *parameter_type; guint enabled : 1; guint state_set : 1; GVariant *state; gchar *display_name; gchar *icon; }; enum { PROP_0, PROP_NAME, PROP_DISPLAY_NAME, PROP_ICON, PROP_ACTIVE, /* GAction properties (+ _NAME above) */ PROP_PARAMETER_TYPE, PROP_ENABLED, PROP_STATE_TYPE, PROP_STATE }; enum { ACTIVATED, /* deprecated */ ACTIVATE, /* GAction */ LAST_SIGNAL }; static guint signals[LAST_SIGNAL] = { 0, }; /* * GAction implementation * * Most of the GAction implementation has been derived from GLib's * GSimpleAction. */ static const gchar * mx_g_action_get_name (GAction *action) { MxAction *mx_action = MX_ACTION (action); return mx_action->priv->name; } static const GVariantType * mx_action_get_parameter_type (GAction *action) { MxAction *mx_action = MX_ACTION (action); return mx_action->priv->parameter_type; } static const GVariantType * mx_action_get_state_type (GAction *action) { MxAction *mx_action = MX_ACTION (action); if (mx_action->priv->state != NULL) return g_variant_get_type (mx_action->priv->state); else return NULL; } static GVariant * mx_action_get_state_hint (GAction *action) { return NULL; } static gboolean mx_action_get_enabled (GAction *action) { MxAction *mx_action = MX_ACTION (action); return mx_action->priv->enabled; } static void mx_action_set_state (GAction *action, GVariant *value) { MxAction *mx_action = MX_ACTION (action); g_return_if_fail (value != NULL); { const GVariantType *state_type; state_type = mx_action->priv->state ? g_variant_get_type (mx_action->priv->state) : NULL; g_return_if_fail (state_type != NULL); g_return_if_fail (g_variant_is_of_type (value, state_type)); } g_variant_ref_sink (value); if (!g_variant_equal (mx_action->priv->state, value)) { if (mx_action->priv->state) g_variant_unref (mx_action->priv->state); mx_action->priv->state = g_variant_ref (value); g_object_notify (G_OBJECT (mx_action), "state"); } g_variant_unref (value); } static GVariant * mx_action_get_state (GAction *action) { MxAction *mx_action = MX_ACTION (action); return mx_action->priv->state ? g_variant_ref (mx_action->priv->state) : NULL; } static void mx_action_activate (GAction *action, GVariant *parameter) { MxAction *mx_action = MX_ACTION (action); g_return_if_fail (mx_action->priv->parameter_type == NULL ? parameter == NULL : (parameter != NULL && g_variant_is_of_type (parameter, mx_action->priv->parameter_type))); if (parameter != NULL) g_variant_ref_sink (parameter); if (mx_action->priv->enabled) { g_signal_emit (mx_action, signals[ACTIVATE], 0, parameter); /* This is for the GAction implementation to emulate the old and * deprecated MxAction behaviour */ g_signal_emit (mx_action, signals[ACTIVATED], 0); } if (parameter != NULL) g_variant_unref (parameter); } void g_action_iface_init (GActionInterface *iface) { iface->get_name = mx_g_action_get_name; iface->get_parameter_type = mx_action_get_parameter_type; iface->get_state_type = mx_action_get_state_type; iface->get_state_hint = mx_action_get_state_hint; iface->get_enabled = mx_action_get_enabled; iface->get_state = mx_action_get_state; /* the set_state virtual function was renamed to change_state in glib 2.30 */ #if GLIB_CHECK_VERSION (2, 30, 0) iface->change_state = mx_action_set_state; #else iface->set_state = mx_action_set_state; #endif iface->activate = mx_action_activate; } static void mx_action_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { MxAction *action = MX_ACTION (object); GAction *g_action = G_ACTION (object); switch (property_id) { case PROP_NAME: g_value_set_string (value, mx_action_get_name (action)); break; case PROP_DISPLAY_NAME: g_value_set_string (value, mx_action_get_display_name (action)); break; case PROP_ICON: g_value_set_string (value, mx_action_get_icon (action)); break; case PROP_ACTIVE: g_value_set_boolean (value, mx_action_get_active (action)); break; case PROP_PARAMETER_TYPE: g_value_set_boxed (value, mx_action_get_parameter_type (g_action)); break; case PROP_ENABLED: g_value_set_boolean (value, mx_action_get_enabled (g_action)); break; case PROP_STATE_TYPE: g_value_set_boxed (value, mx_action_get_state_type (g_action)); break; case PROP_STATE: g_value_take_variant (value, mx_action_get_state (g_action)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } } static void mx_action_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { MxAction *action = MX_ACTION (object); switch (property_id) { case PROP_DISPLAY_NAME: mx_action_set_display_name (action, g_value_get_string (value)); break; case PROP_ICON: mx_action_set_icon (action, g_value_get_string (value)); break; case PROP_ACTIVE: mx_action_set_active (action, g_value_get_boolean (value)); break; #if !GLIB_CHECK_VERSION (2, 30, 0) /* these properties were writable construct properties before glib 2.30.0 */ case PROP_NAME: case PROP_PARAMETER_TYPE: case PROP_ENABLED: case PROP_STATE: break; #endif default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } } static void mx_action_finalize (GObject *object) { MxActionPrivate *priv = MX_ACTION (object)->priv; if (priv->name) { g_free (priv->name); priv->name = NULL; } if (priv->display_name) { g_free (priv->display_name); priv->display_name = NULL; } if (priv->icon) { g_free (priv->icon); priv->icon = NULL; } if (priv->parameter_type) g_variant_type_free (priv->parameter_type); if (priv->state) g_variant_unref (priv->state); G_OBJECT_CLASS (mx_action_parent_class)->finalize (object); } static void mx_action_class_init (MxActionClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); g_type_class_add_private (klass, sizeof (MxActionPrivate)); object_class->get_property = mx_action_get_property; object_class->set_property = mx_action_set_property; object_class->finalize = mx_action_finalize; /** * MxAction:name: * * The name of the action. This is mostly meaningful for identifying * the action once it has been added to a #GActionGroup. * * Since: 1.4 */ g_object_class_override_property (object_class, PROP_NAME, "name"); g_object_class_install_property (object_class, PROP_DISPLAY_NAME, g_param_spec_string ("display-name", "Display name", "Localised name to use " "for display.", NULL, G_PARAM_READWRITE | G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB | MX_PARAM_TRANSLATEABLE )); g_object_class_install_property (object_class, PROP_ICON, g_param_spec_string ("icon", "Icon name", "Icon name or path to " "to be used if this " "action is displayed", NULL, G_PARAM_READWRITE | G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB)); /** * MxAction:active * * Deprecated: 1.4: use the #GAction:enabled property instead */ g_object_class_install_property (object_class, PROP_ACTIVE, g_param_spec_boolean ("active", "Active", "Whether the action " "is active.", TRUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_DEPRECATED)); /** * MxAction:parameter-type: * * The type of the parameter that must be given when activating the * action. * * Since: 1.4 */ g_object_class_override_property (object_class, PROP_PARAMETER_TYPE, "parameter-type"); /** * MxAction:enabled: * * If @action is currently enabled. * * If the action is disabled then calls to g_action_activate() and * g_action_change_state() have no effect. * * Since: 1.4 */ g_object_class_override_property (object_class, PROP_ENABLED, "enabled"); /** * MxAction:state-type: * * The #GVariantType of the state that the action has, or %NULL if the * action is stateless. * * Since: 1.4 */ g_object_class_override_property (object_class, PROP_STATE_TYPE, "state-type"); /** * MxAction:state: * * The state of the action, or %NULL if the action is stateless. * * Since: 1.4 */ g_object_class_override_property (object_class, PROP_STATE, "state"); /** * MxAction::activated * @action: the object that received the signal * * Emitted when the MxAction is activated. * * Deprecated: 1.4: Use MxAction::activate instead. */ signals[ACTIVATED] = g_signal_new ("activated", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (MxActionClass, activated), NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); /** * MxAction::activate: * @action: the #MxAction * @parameter: (allow-none): the parameter to the activation * * Indicates that the action was just activated. * * @parameter will always be of the expected type. In the event that * an incorrect type was given, no signal will be emitted. * * Since: 1.4 */ signals[ACTIVATE] = g_signal_new ("activate", MX_TYPE_ACTION, G_SIGNAL_RUN_LAST, 0, NULL, NULL, g_cclosure_marshal_VOID__VARIANT, G_TYPE_NONE, 1, G_TYPE_VARIANT); } static void mx_action_init (MxAction *self) { MxActionPrivate *priv = self->priv = ACTION_PRIVATE (self); priv->enabled = TRUE; } /** * mx_action_new: * * Creates a new, blank, #MxAction * * Returns: (transfer full): a newly allocated #MxAction */ MxAction * mx_action_new (void) { return g_object_new (MX_TYPE_ACTION, NULL); } /** * mx_action_new_with_parameter: * @name: the name of the action * @parameter_type: (allow-none): the type of parameter to the activate * function * * Creates a new action with a parameter. * * The created action is stateless. See mx_action_new_stateful(). * * Returns: a new #MxAction * * Since: 1.4 */ MxAction * mx_action_new_with_parameter (const gchar *name, const GVariantType *parameter_type) { MxAction *action; g_return_val_if_fail (name != NULL, NULL); action = g_object_new (MX_TYPE_ACTION, NULL); mx_action_set_name (action, name); action->priv->parameter_type = g_variant_type_copy (parameter_type); return action; } /** * mx_action_new_stateful: * @name: the name of the action * @parameter_type: (allow-none): the type of the parameter to the activate * function * @state: the initial state of the action * * Creates a new stateful action. * * @state is the initial state of the action. All future state values * must have the same #GVariantType as the initial state. * * Returns: a new #MxAction * * Since: 1.4 */ MxAction * mx_action_new_stateful (const gchar *name, const GVariantType *parameter_type, GVariant *state) { MxAction *action; g_return_val_if_fail (name != NULL, NULL); g_return_val_if_fail (state != NULL, NULL); action = g_object_new (MX_TYPE_ACTION, NULL); mx_action_set_name (action, name); action->priv->parameter_type = g_variant_type_copy (parameter_type); mx_action_set_state (G_ACTION (action), state); return action; } /** * mx_action_new_full: * @name: name of the action * @display_name: name of the action to display to the user * @activated_cb: (type Mx.ActionCallbackFunc) (scope async) (allow-none): callback to connect to the activated signal * @user_data: user data to be passed to the callback * * Creates a new #MxAction with the name and callback set * * Returns: (transfer full): a newly allocated #MxAction */ MxAction * mx_action_new_full (const gchar *name, const gchar *display_name, GCallback activated_cb, gpointer user_data) { MxAction *action = g_object_new (MX_TYPE_ACTION, "display-name", display_name, NULL); mx_action_set_name (action, name); if (activated_cb) g_signal_connect (action, "activated", activated_cb, user_data); return action; } /** * mx_action_get_name: * @action: A #MxAction * * Get the name of the action * * Returns: name of the action, owned by MxAction */ const gchar * mx_action_get_name (MxAction *action) { g_return_val_if_fail (MX_IS_ACTION (action), NULL); return action->priv->name; } /** * mx_action_set_name: * @action: A #MxAction * @name: new name to set * * Set the name of the action * */ void mx_action_set_name (MxAction *action, const gchar *name) { MxActionPrivate *priv; g_return_if_fail (MX_IS_ACTION (action)); priv = action->priv; if (g_strcmp0 (priv->name, name)) { g_free (priv->name); priv->name = g_strdup (name); g_object_notify (G_OBJECT (action), "name"); } } /** * mx_action_get_active: * @action: A #MxAction * * Get the value of the active property * * Returns: #TRUE if the action is active */ gboolean mx_action_get_active (MxAction *action) { g_return_val_if_fail (MX_IS_ACTION (action), FALSE); return action->priv->enabled; } /** * mx_action_set_active: * @action: A #MxAction * @active: the value to set * * Set the value of the active property * */ void mx_action_set_active (MxAction *action, gboolean active) { MxActionPrivate *priv; g_return_if_fail (MX_IS_ACTION (action)); priv = action->priv; if (priv->enabled != active) { priv->enabled = active; g_object_notify (G_OBJECT (action), "active"); } } /** * mx_action_get_display_name: * @action: A #MxAction * * Get the display name of the action * * Returns: display-name of the action, owned by MxAction */ const gchar * mx_action_get_display_name (MxAction *action) { g_return_val_if_fail (MX_IS_ACTION (action), NULL); return action->priv->display_name; } /** * mx_action_get_icon: * @action: A #MxAction * * Get the icon of the action * * Returns: icon of the action, owned by MxAction */ const gchar * mx_action_get_icon (MxAction *action) { g_return_val_if_fail (MX_IS_ACTION (action), NULL); return action->priv->icon; } /** * mx_action_set_display_name: * @action: A #MxAction * @name: new display name to set * * Set the name of the action to display to the user * */ void mx_action_set_display_name (MxAction *action, const gchar *name) { MxActionPrivate *priv; g_return_if_fail (MX_IS_ACTION (action)); priv = action->priv; if (g_strcmp0 (priv->display_name, name)) { g_free (priv->display_name); priv->display_name = g_strdup (name); g_object_notify (G_OBJECT (action), "display-name"); } } /** * mx_action_set_icon: * @action: A #MxAction * @name: new icon to set * * The icon to be used in a visual representation of an action. * */ void mx_action_set_icon (MxAction *action, const gchar *name) { MxActionPrivate *priv; g_return_if_fail (MX_IS_ACTION (action)); priv = action->priv; if (g_strcmp0 (priv->icon, name)) { g_free (priv->icon); priv->icon = g_strdup (name); g_object_notify (G_OBJECT (action), "icon"); } } mx-1.4.7/mx/mx-action.h000066400000000000000000000075401201047117600146500ustar00rootroot00000000000000/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * mx-action.h: MxAction object * * Copyright 2009 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * Boston, MA 02111-1307, USA. * */ #if !defined(MX_H_INSIDE) && !defined(MX_COMPILATION) #error "Only can be included directly.h" #endif #ifndef _MX_ACTION_H #define _MX_ACTION_H #include G_BEGIN_DECLS #define MX_TYPE_ACTION mx_action_get_type() #define MX_ACTION(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ MX_TYPE_ACTION, MxAction)) #define MX_ACTION_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), \ MX_TYPE_ACTION, MxActionClass)) #define MX_IS_ACTION(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \ MX_TYPE_ACTION)) #define MX_IS_ACTION_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE ((klass), \ MX_TYPE_ACTION)) #define MX_ACTION_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), \ MX_TYPE_ACTION, MxActionClass)) typedef struct _MxAction MxAction; typedef struct _MxActionClass MxActionClass; typedef struct _MxActionPrivate MxActionPrivate; /** * MxAction: * * The contents of this structure are private and should only be accessed * through the public API. */ struct _MxAction { /*< private >*/ GInitiallyUnowned parent; MxActionPrivate *priv; }; struct _MxActionClass { GInitiallyUnownedClass parent_class; void (*activated) (MxAction *action); /* padding for future expansion */ void (*_padding_0) (void); void (*_padding_1) (void); void (*_padding_2) (void); void (*_padding_3) (void); void (*_padding_4) (void); }; GType mx_action_get_type (void); /** * MxActionCallbackFunc: * @action: An #MxAction * @user_data: user data * * Callback function called when action is activated. */ typedef void (*MxActionCallbackFunc) (MxAction *action, gpointer user_data); MxAction * mx_action_new (void); MxAction * mx_action_new_with_parameter (const gchar *name, const GVariantType *parameter_type); MxAction * mx_action_new_stateful (const gchar *name, const GVariantType *parameter_type, GVariant *state); MxAction * mx_action_new_full (const gchar *name, const gchar *display_name, GCallback activated_cb, gpointer user_data); const gchar *mx_action_get_name (MxAction *action); void mx_action_set_name (MxAction *action, const gchar *name); const gchar *mx_action_get_display_name (MxAction *action); void mx_action_set_display_name (MxAction *action, const gchar *name); const gchar *mx_action_get_icon (MxAction *action); void mx_action_set_icon (MxAction *action, const gchar *name); gboolean mx_action_get_active (MxAction *action); void mx_action_set_active (MxAction *action, gboolean active); G_END_DECLS #endif /* _MX_ACTION_H */ mx-1.4.7/mx/mx-actor-manager.c000066400000000000000000001011041201047117600160750ustar00rootroot00000000000000/* * mx-actor-manager: Actor life-cycle manager object * * Copyright 2011 Intel Corporation * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * * Written by: Chris Lord * */ /** * SECTION:mx-actor-manager * @short_description: An object that manages ClutterActor lifecycle * * #MxActorManager is an object that helps manage the creation, addition * and removal of actors. It is bound to a particular stage, and spreads * operations over time so as not to interrupt animations or interactivity. * * Operations added to the #MxActorManager will strictly be performed in the * order in which they were added. * * Since: 1.2 */ #include "mx-actor-manager.h" #include "mx-enum-types.h" #include "mx-marshal.h" #include "mx-private.h" G_DEFINE_TYPE (MxActorManager, mx_actor_manager, G_TYPE_OBJECT) #define ACTOR_MANAGER_PRIVATE(o) \ (G_TYPE_INSTANCE_GET_PRIVATE ((o), MX_TYPE_ACTOR_MANAGER, MxActorManagerPrivate)) static GQuark actor_manager_quark = 0; static GQuark actor_manager_error_quark = 0; enum { PROP_0, PROP_STAGE, PROP_TIME_SLICE, PROP_N_OPERATIONS }; enum { ACTOR_CREATED, ACTOR_ADDED, ACTOR_REMOVED, ACTOR_FINISHED, OP_COMPLETED, OP_CANCELLED, OP_FAILED, LAST_SIGNAL }; typedef enum { MX_ACTOR_MANAGER_CREATE, MX_ACTOR_MANAGER_ADD, MX_ACTOR_MANAGER_REMOVE, MX_ACTOR_MANAGER_UNREF } MxActorManagerOperationType; typedef struct { MxActorManager *manager; gulong id; MxActorManagerOperationType type; MxActorManagerCreateFunc create_func; gpointer userdata; ClutterActor *actor; ClutterContainer *container; } MxActorManagerOperation; struct _MxActorManagerPrivate { GQueue *ops; GHashTable *actor_op_links; guint source; gulong post_paint_handler; GTimer *timer; guint time_slice; ClutterStage *stage; guint quark_set : 1; }; static guint signals[LAST_SIGNAL] = { 0, }; static void mx_actor_manager_handle_op (MxActorManager *manager); static guint mx_actor_manager_increment_count (MxActorManager *manager, gpointer actor, GList *op_link); static guint mx_actor_manager_decrement_count (MxActorManager *manager, gpointer actor, GList *op_link); static void mx_actor_manager_ensure_processing (MxActorManager *manager); static void mx_actor_manager_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { MxActorManagerPrivate *priv = MX_ACTOR_MANAGER (object)->priv; switch (property_id) { case PROP_STAGE: g_value_set_object (value, priv->stage); break; case PROP_TIME_SLICE: g_value_set_uint (value, priv->time_slice); break; case PROP_N_OPERATIONS: g_value_set_uint (value, g_queue_get_length (priv->ops)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } } static void mx_actor_manager_stage_destroyed (gpointer data, GObject *old_stage) { MxActorManager *self = MX_ACTOR_MANAGER (data); MxActorManagerPrivate *priv = self->priv; priv->stage = NULL; g_object_unref (self); } static void mx_actor_manager_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { MxActorManager *self = MX_ACTOR_MANAGER (object); switch (property_id) { case PROP_STAGE: self->priv->stage = g_value_get_object (value); g_object_weak_ref (G_OBJECT (self->priv->stage), mx_actor_manager_stage_destroyed, self); break; case PROP_TIME_SLICE: mx_actor_manager_set_time_slice (self, g_value_get_uint (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } } static void mx_actor_manager_dispose (GObject *object) { MxActorManager *self = MX_ACTOR_MANAGER (object); MxActorManagerPrivate *priv = self->priv; if (priv->source) { g_source_remove (priv->source); priv->source = 0; } if (priv->post_paint_handler) { if (priv->stage) g_signal_handler_disconnect (priv->stage, priv->post_paint_handler); priv->post_paint_handler = 0; } while (g_queue_get_length (priv->ops)) { MxActorManagerOperation *op = g_queue_peek_head (priv->ops); mx_actor_manager_cancel_operation (self, op->id); } if (priv->stage) { if (priv->quark_set) g_object_set_qdata (G_OBJECT (priv->stage), actor_manager_quark, NULL); g_object_weak_unref (G_OBJECT (priv->stage), mx_actor_manager_stage_destroyed, self); priv->stage = NULL; } G_OBJECT_CLASS (mx_actor_manager_parent_class)->dispose (object); } static void mx_actor_manager_free_op_links (gpointer key, gpointer value, gpointer userdata) { g_list_free (value); } static void mx_actor_manager_finalize (GObject *object) { MxActorManagerPrivate *priv = MX_ACTOR_MANAGER (object)->priv; g_queue_free (priv->ops); g_hash_table_foreach (priv->actor_op_links, mx_actor_manager_free_op_links, NULL); g_hash_table_unref (priv->actor_op_links); g_timer_destroy (priv->timer); G_OBJECT_CLASS (mx_actor_manager_parent_class)->finalize (object); } static void mx_actor_manager_class_init (MxActorManagerClass *klass) { GParamSpec *pspec; GObjectClass *object_class = G_OBJECT_CLASS (klass); g_type_class_add_private (klass, sizeof (MxActorManagerPrivate)); object_class->get_property = mx_actor_manager_get_property; object_class->set_property = mx_actor_manager_set_property; object_class->dispose = mx_actor_manager_dispose; object_class->finalize = mx_actor_manager_finalize; pspec = g_param_spec_object ("stage", "Stage", "The stage that contains the managed actors.", CLUTTER_TYPE_STAGE, MX_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); g_object_class_install_property (object_class, PROP_STAGE, pspec); pspec = g_param_spec_uint ("time-slice", "Time slice", "The amount of time to spend performing " "operations, per frame, in ms", 0, G_MAXUINT, 5, MX_PARAM_READWRITE); g_object_class_install_property (object_class, PROP_TIME_SLICE, pspec); pspec = g_param_spec_uint ("n-operations", "N operations", "The amount of operations in the queue", 0, G_MAXUINT, 0, MX_PARAM_READABLE); g_object_class_install_property (object_class, PROP_N_OPERATIONS, pspec); /** * MxActorManager::actor-created * @manager: the object that received the signal * @id: The operation ID * @actor: The created #ClutterActor * * Emitted when an actor creation operation has completed. * * Since: 1.2 */ signals[ACTOR_CREATED] = g_signal_new ("actor-created", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (MxActorManagerClass, actor_created), NULL, NULL, _mx_marshal_VOID__ULONG_OBJECT, G_TYPE_NONE, 2, G_TYPE_ULONG, CLUTTER_TYPE_ACTOR); /** * MxActorManager::actor-added * @manager: the object that received the signal * @id: The operation ID * @container: The #ClutterContainer the actor was added to * @actor: The added #ClutterActor * * Emitted when an actor add operation has completed. * * Since: 1.2 */ signals[ACTOR_ADDED] = g_signal_new ("actor-added", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (MxActorManagerClass, actor_added), NULL, NULL, _mx_marshal_VOID__ULONG_OBJECT_OBJECT, G_TYPE_NONE, 3, G_TYPE_ULONG, CLUTTER_TYPE_ACTOR, CLUTTER_TYPE_ACTOR); /** * MxActorManager::actor-removed * @manager: the object that received the signal * @id: The operation ID * @container: The #ClutterContainer the actor was removed from * @actor: The removed #ClutterActor * * Emitted when an actor remove operation has completed. * * Since: 1.2 */ signals[ACTOR_REMOVED] = g_signal_new ("actor-removed", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (MxActorManagerClass, actor_removed), NULL, NULL, _mx_marshal_VOID__ULONG_OBJECT_OBJECT, G_TYPE_NONE, 3, G_TYPE_ULONG, CLUTTER_TYPE_ACTOR, CLUTTER_TYPE_ACTOR); /** * MxActorManager::actor-finished * @manager: the object that received the signal * @actor: The #ClutterActor to which the signal pertains * * Emitted when all queued operations involving @actor have completed. * * Since: 1.2 */ signals[ACTOR_FINISHED] = g_signal_new ("actor-finished", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (MxActorManagerClass, actor_created), NULL, NULL, _mx_marshal_VOID__OBJECT, G_TYPE_NONE, 1, CLUTTER_TYPE_ACTOR); /** * MxActorManager::operation-completed * @manager: the object that received the signal * @id: The operation id * * Emitted when an operation has completed successfully. * * Since: 1.2 */ signals[OP_COMPLETED] = g_signal_new ("operation-completed", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (MxActorManagerClass, operation_completed), NULL, NULL, _mx_marshal_VOID__ULONG, G_TYPE_NONE, 1, G_TYPE_ULONG); /** * MxActorManager::operation-cancelled * @manager: the object that received the signal * @id: The operation id * * Emitted when an operation has been cancelled. * * Since: 1.2 */ signals[OP_CANCELLED] = g_signal_new ("operation-cancelled", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (MxActorManagerClass, operation_cancelled), NULL, NULL, _mx_marshal_VOID__ULONG, G_TYPE_NONE, 1, G_TYPE_ULONG); /** * MxActorManager::operation-failed * @manager: the object that received the signal * @id: The operation id * @error: A #GError describing the reason of the failure * * Emitted when an operation has failed. * * Since: 1.2 */ signals[OP_FAILED] = g_signal_new ("operation-failed", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (MxActorManagerClass, operation_failed), NULL, NULL, _mx_marshal_VOID__ULONG_BOXED, G_TYPE_NONE, 2, G_TYPE_ULONG, G_TYPE_ERROR); actor_manager_quark = g_quark_from_static_string ("mx-actor-manager"); actor_manager_error_quark = g_quark_from_static_string ("mx-actor-manager-error"); } static void mx_actor_manager_init (MxActorManager *self) { MxActorManagerPrivate *priv = self->priv = ACTOR_MANAGER_PRIVATE (self); priv->ops = g_queue_new (); priv->actor_op_links = g_hash_table_new (NULL, NULL); priv->timer = g_timer_new (); priv->time_slice = 5; } /** * mx_actor_manager_new: * @stage: A #ClutterStage * * Creates a new #MxActorManager, associated with the given stage. * * * A reference will not be taken on the stage, and when the stage is destroyed, * the actor manager will lose a reference. The actor manager can be kept * alive by taking a reference, but will no longer divide up events. * * * Returns: (transfer none): An #MxActorManager, tied to the given #ClutterStage * * Since: 1.2 */ MxActorManager * mx_actor_manager_new (ClutterStage *stage) { return g_object_new (MX_TYPE_ACTOR_MANAGER, "stage", stage, NULL); } /** * mx_actor_manager_get_for_stage: * @stage: A #ClutterStage * * Get the MxActorManager associated with a stage, or creates one if this is the * first call to the function with the given #ClutterStage. * * This is a convenience function that allows for easy association of one * #MxActorManager to a #ClutterStage. * * Returns: (transfer none): An #MxActorManager * * Since: 1.2 */ MxActorManager * mx_actor_manager_get_for_stage (ClutterStage *stage) { MxActorManager *manager; g_return_val_if_fail (CLUTTER_IS_STAGE (stage), NULL); manager = g_object_get_qdata (G_OBJECT (stage), actor_manager_quark); if (manager == NULL) { manager = g_object_new (MX_TYPE_ACTOR_MANAGER, "stage", stage, NULL); g_object_set_qdata (G_OBJECT (stage), actor_manager_quark, manager); manager->priv->quark_set = TRUE; } return manager; } /** * mx_actor_manager_get_stage: * @manager: A #MxActorManager * * Gets the #ClutterStage the actor manager is associated with. * * Returns: (transfer none): The #ClutterStage the actor is associated with. * * Since: 1.2 */ ClutterStage * mx_actor_manager_get_stage (MxActorManager *manager) { g_return_val_if_fail (MX_IS_ACTOR_MANAGER (manager), NULL); return manager->priv->stage; } static guint mx_actor_manager_increment_count (MxActorManager *manager, gpointer actor, GList *op_link) { GList *op_links; MxActorManagerPrivate *priv = manager->priv; op_links = g_hash_table_lookup (priv->actor_op_links, actor); op_links = g_list_prepend (op_links, op_link); g_hash_table_insert (priv->actor_op_links, actor, op_links); return g_list_length (op_links); } static guint mx_actor_manager_decrement_count (MxActorManager *manager, gpointer actor, GList *op_link) { guint count; GList *op_links; MxActorManagerPrivate *priv = manager->priv; op_links = g_hash_table_lookup (priv->actor_op_links, actor); op_links = g_list_remove (op_links, op_link); count = g_list_length (op_links); if (count == 0) { g_hash_table_remove (priv->actor_op_links, actor); g_signal_emit (manager, signals[ACTOR_FINISHED], 0, actor); } else g_hash_table_insert (priv->actor_op_links, actor, op_links); return count; } static void mx_actor_manager_actor_destroyed (gpointer data, GObject *old_actor) { MxActorManagerOperation *op = data; MxActorManagerPrivate *priv = op->manager->priv; g_hash_table_remove (priv->actor_op_links, old_actor); op->actor = NULL; } static void mx_actor_manager_container_destroyed (gpointer data, GObject *old_actor) { MxActorManagerOperation *op = data; MxActorManagerPrivate *priv = op->manager->priv; g_hash_table_remove (priv->actor_op_links, old_actor); op->container = NULL; } static MxActorManagerOperation * mx_actor_manager_op_new (MxActorManager *manager, MxActorManagerOperationType type, MxActorManagerCreateFunc create_func, gpointer userdata, ClutterActor *actor, ClutterContainer *container) { GList *op_link; MxActorManagerPrivate *priv = manager->priv; MxActorManagerOperation *op = g_slice_new0 (MxActorManagerOperation); op->manager = manager; if (g_queue_peek_tail (priv->ops)) op->id = ((MxActorManagerOperation *)g_queue_peek_tail (priv->ops))->id + 1; else op->id = 1; op->type = type; op->create_func = create_func; op->userdata = userdata; op->actor = actor; op->container = container; g_queue_push_tail (priv->ops, op); op_link = g_queue_peek_tail_link (priv->ops); if (actor) { g_object_weak_ref (G_OBJECT (actor), mx_actor_manager_actor_destroyed, op); mx_actor_manager_increment_count (manager, actor, op_link); if (type == MX_ACTOR_MANAGER_ADD) g_object_ref_sink (actor); } if (container) { g_object_weak_ref (G_OBJECT (container), mx_actor_manager_container_destroyed, op); mx_actor_manager_increment_count (manager, container, op_link); } return op; } static void mx_actor_manager_op_free (MxActorManager *manager, GList *op_link, gboolean _remove) { MxActorManagerOperation *op = op_link->data; MxActorManagerPrivate *priv = manager->priv; if (op->actor) { mx_actor_manager_decrement_count (manager, op->actor, op_link); g_object_weak_unref (G_OBJECT (op->actor), mx_actor_manager_actor_destroyed, op); if (op->type == MX_ACTOR_MANAGER_ADD) g_object_unref (op->actor); } if (op->container) { mx_actor_manager_decrement_count (manager, op->container, op_link); g_object_weak_unref (G_OBJECT (op->container), mx_actor_manager_container_destroyed, op); } if (_remove) g_queue_delete_link (priv->ops, op_link); g_slice_free (MxActorManagerOperation, op); } static void mx_actor_manager_handle_op (MxActorManager *manager) { ClutterActor *actor; MxActorManagerOperation *op; GError *error = NULL; MxActorManagerPrivate *priv = manager->priv; GList *op_link = g_queue_peek_head_link (priv->ops); if (!op_link) return; op = op_link->data; /* We want the actor and container to remain alive during this function, * for the purposes of signal emission. */ if (op->actor) g_object_ref (op->actor); if (op->container) g_object_ref (op->container); switch (op->type) { case MX_ACTOR_MANAGER_CREATE: actor = op->create_func (manager, op->userdata); if (CLUTTER_IS_ACTOR (actor)) g_signal_emit (manager, signals[ACTOR_CREATED], 0, op->id, actor); else error = g_error_new (actor_manager_error_quark, MX_ACTOR_MANAGER_CREATION_FAILED, "Actor creation function did not " "return a ClutterActor"); break; case MX_ACTOR_MANAGER_ADD: if (op->container) { if (op->actor) { clutter_container_add_actor (op->container, op->actor); g_signal_emit (manager, signals[ACTOR_ADDED], 0, op->id, op->container, op->actor); } else error = g_error_new (actor_manager_error_quark, MX_ACTOR_MANAGER_ACTOR_DESTROYED, "Actor destroyed before addition"); } else error = g_error_new (actor_manager_error_quark, MX_ACTOR_MANAGER_CONTAINER_DESTROYED, "Container destroyed before addition"); break; case MX_ACTOR_MANAGER_REMOVE: if (op->container) { if (op->actor) { clutter_container_remove_actor (op->container, op->actor); g_signal_emit (manager, signals[ACTOR_REMOVED], 0, op->id, op->container, op->actor); } else error = g_error_new (actor_manager_error_quark, MX_ACTOR_MANAGER_ACTOR_DESTROYED, "Actor destroyed before removal"); } else error = g_error_new (actor_manager_error_quark, MX_ACTOR_MANAGER_CONTAINER_DESTROYED, "Container destroyed before removal"); break; case MX_ACTOR_MANAGER_UNREF: if (op->actor) g_object_unref (op->actor); else error = g_error_new (actor_manager_error_quark, MX_ACTOR_MANAGER_ACTOR_DESTROYED, "Actor destroyed before unref"); break; default: g_warning (G_STRLOC ": Unrecognised operation type (%d) " "- Memory corruption?)", op->type); error = g_error_new (actor_manager_error_quark, MX_ACTOR_MANAGER_UNKNOWN_OPERATION, "Unrecognised operation, possibly due to " "memory corruption."); break; } if (error) { g_signal_emit (manager, signals[OP_FAILED], 0, op->id, error); g_error_free (error); } else g_signal_emit (manager, signals[OP_COMPLETED], 0, op->id); if (op->actor) g_object_unref (op->actor); if (op->container) g_object_unref (op->container); mx_actor_manager_op_free (manager, op_link, TRUE); } static void mx_actor_manager_post_paint_cb (ClutterActor *stage, MxActorManager *manager) { MxActorManagerPrivate *priv = manager->priv; g_signal_handler_disconnect (stage, priv->post_paint_handler); priv->post_paint_handler = 0; mx_actor_manager_ensure_processing (manager); } static gboolean mx_actor_manager_process_operations (MxActorManager *manager) { MxActorManagerPrivate *priv = manager->priv; priv->source = 0; g_timer_start (priv->timer); while (!g_queue_is_empty (priv->ops)) { mx_actor_manager_handle_op (manager); if (priv->stage && g_timer_elapsed (priv->timer, NULL) * 1000 >= priv->time_slice) break; } g_timer_stop (priv->timer); if (!g_queue_is_empty (priv->ops)) { if (!priv->post_paint_handler) priv->post_paint_handler = g_signal_connect (priv->stage, "paint", G_CALLBACK (mx_actor_manager_post_paint_cb), manager); return TRUE; } return FALSE; } static void mx_actor_manager_ensure_processing (MxActorManager *manager) { MxActorManagerPrivate *priv = manager->priv; if (!priv->source) priv->source = g_idle_add_full (G_PRIORITY_HIGH, (GSourceFunc)mx_actor_manager_process_operations, manager, NULL); } /** * mx_actor_manager_create_actor: * @manager: A #MxActorManager * @create_func: A #ClutterActor creation function * @userdata: data to be passed to the function, or %NULL * @destroy_func: callback to invoke before the operation is removed * * Creates a #ClutterActor. The actor may not be created immediately, * or at all, if the operation is cancelled. * * On successful completion, the #MxActorManager::actor_created signal will * be fired. * * Returns: The ID for this operation. * * Since: 1.2 */ gulong mx_actor_manager_create_actor (MxActorManager *manager, MxActorManagerCreateFunc create_func, gpointer userdata, GDestroyNotify destroy_func) { MxActorManagerOperation *op; g_return_val_if_fail (MX_IS_ACTOR_MANAGER (manager), 0); g_return_val_if_fail (create_func != NULL, 0); op = mx_actor_manager_op_new (manager, MX_ACTOR_MANAGER_CREATE, create_func, userdata, NULL, NULL); mx_actor_manager_ensure_processing (manager); return op->id; } /** * mx_actor_manager_add_actor: * @manager: A #MxActorManager * @container: A #ClutterContainer * @actor: A #ClutterActor * * Adds @actor to @container. The actor may not be parented immediately, * or at all, if the operation is cancelled. * * On successful completion, the #MxActorManager::actor_added signal will * be fired. * * Returns: The ID for this operation. * * Since: 1.2 */ gulong mx_actor_manager_add_actor (MxActorManager *manager, ClutterContainer *container, ClutterActor *actor) { MxActorManagerOperation *op; g_return_val_if_fail (MX_IS_ACTOR_MANAGER (manager), 0); g_return_val_if_fail (CLUTTER_IS_CONTAINER (container), 0); g_return_val_if_fail (CLUTTER_IS_ACTOR (actor), 0); op = mx_actor_manager_op_new (manager, MX_ACTOR_MANAGER_ADD, NULL, NULL, actor, container); mx_actor_manager_ensure_processing (manager); return op->id; } /** * mx_actor_manager_remove_actor: * @manager: A #MxActorManager * @container: A #ClutterContainer * @actor: A #ClutterActor * * Removes @actor from @container. * * On successful completion, the #MxActorManager::actor_removed signal will * be fired. * * * The actor may not be removed immediately, and thus you may want to set * the actor's opacity to 0 before calling this function. * * * Returns: The ID for this operation. * * Since: 1.2 */ gulong mx_actor_manager_remove_actor (MxActorManager *manager, ClutterContainer *container, ClutterActor *actor) { MxActorManagerOperation *op; g_return_val_if_fail (MX_IS_ACTOR_MANAGER (manager), 0); g_return_val_if_fail (CLUTTER_IS_CONTAINER (container), 0); g_return_val_if_fail (CLUTTER_IS_ACTOR (actor), 0); op = mx_actor_manager_op_new (manager, MX_ACTOR_MANAGER_REMOVE, NULL, NULL, actor, container); mx_actor_manager_ensure_processing (manager); return op->id; } /** * mx_actor_manager_remove_container: * @manager: A #MxActorManager * @container: A #ClutterContainer * * Removes the container. This is a utility function that works by first * removing all the children of the container, then the children itself. This * effectively spreads the load of removing a large container. All prior * operations associated with this container will be cancelled. * * * The container may not be removed immediately, and thus you may want to set * the container's opacity to 0 before calling this function. * * * Since: 1.2 */ void mx_actor_manager_remove_container (MxActorManager *manager, ClutterContainer *container) { GList *children; ClutterActor *parent; g_return_if_fail (MX_IS_ACTOR_MANAGER (manager)); g_return_if_fail (CLUTTER_IS_CONTAINER (container)); /* Cancel all operations on this container */ mx_actor_manager_cancel_operations (manager, CLUTTER_ACTOR (container)); /* Remove all children */ children = clutter_container_get_children (container); while (children) { ClutterActor *child = children->data; mx_actor_manager_op_new (manager, MX_ACTOR_MANAGER_REMOVE, NULL, NULL, child, container); children = g_list_delete_link (children, children); } /* Then remove the container */ parent = clutter_actor_get_parent (CLUTTER_ACTOR (container)); if (parent && CLUTTER_IS_CONTAINER (parent)) { g_object_ref (container); clutter_container_remove_actor (CLUTTER_CONTAINER (parent), CLUTTER_ACTOR (container)); mx_actor_manager_op_new (manager, MX_ACTOR_MANAGER_UNREF, NULL, NULL, (ClutterActor *)container, NULL); } mx_actor_manager_ensure_processing (manager); } static gint mx_actor_manager_find_by_id (gconstpointer a, gconstpointer b) { const MxActorManagerOperation *op = a; const gulong *id = b; return (op->id == *id) ? 0 : -1; } /** * mx_actor_manager_cancel_operation: * @manager: A #MxActorManager * @id: An operation ID * * Cancels the given operation, if it exists. The * #MxActorManager::operation_cancelled signal is fired whenever an operation * is cancelled. * * Since: 1.2 */ void mx_actor_manager_cancel_operation (MxActorManager *manager, gulong id) { GList *op_link; MxActorManagerPrivate *priv; g_return_if_fail (MX_IS_ACTOR_MANAGER (manager)); g_return_if_fail (id > 0); priv = manager->priv; op_link = g_queue_find_custom (priv->ops, &id, mx_actor_manager_find_by_id); if (!op_link) { g_warning (G_STRLOC ": Unknown operation (%lu)", id); return; } g_queue_unlink (priv->ops, op_link); g_signal_emit (manager, signals[OP_CANCELLED], 0, id); mx_actor_manager_op_free (manager, op_link, FALSE); g_list_free (op_link); } /** * mx_actor_manager_cancel_operations: * @manager: A #MxActorManager * @actor: A #ClutterActor * * Cancels all operations associated with the given actor. * * Since: 1.2 */ void mx_actor_manager_cancel_operations (MxActorManager *manager, ClutterActor *actor) { GList *op_links; MxActorManagerPrivate *priv; g_return_if_fail (MX_IS_ACTOR_MANAGER (manager)); g_return_if_fail (CLUTTER_IS_ACTOR (actor)); priv = manager->priv; op_links = g_hash_table_lookup (priv->actor_op_links, actor); while (op_links) { GList *op_link = op_links->data; MxActorManagerOperation *op = op_link->data; op_links = op_links->next; g_queue_unlink (priv->ops, op_link); g_signal_emit (manager, signals[OP_CANCELLED], 0, op->id); mx_actor_manager_op_free (manager, op_link, FALSE); g_list_free (op_link); } } /** * mx_actor_manager_set_time_slice: * @manager: A #MxActorManager * @msecs: A time, in milliseconds * * Sets the amount of time the actor manager will spend performing operations, * before yielding to allow any necessary redrawing to occur. * * Lower times will lead to smoother performance, but will increase the amount * of time it takes for operations to complete. * * Since: 1.2 */ void mx_actor_manager_set_time_slice (MxActorManager *manager, guint msecs) { MxActorManagerPrivate *priv; g_return_if_fail (MX_IS_ACTOR_MANAGER (manager)); priv = manager->priv; if (priv->time_slice != msecs) { priv->time_slice = msecs; g_object_notify (G_OBJECT (manager), "time-slice"); } } /** * mx_actor_manager_get_time_slice: * @manager: A #MxActorManager * * Retrieves the current time slice being used for operations. * * Returns: The time-slice being used, in milliseconds * * Since: 1.2 */ guint mx_actor_manager_get_time_slice (MxActorManager *manager) { g_return_val_if_fail (MX_IS_ACTOR_MANAGER (manager), 0); return manager->priv->time_slice; } /** * mx_actor_manager_get_n_operations: * @manager: A #MxActorManager * * Retrieves the amount of operations left in the queue. * * Returns: Number of operations left to perform * * Since: 1.2 */ guint mx_actor_manager_get_n_operations (MxActorManager *manager) { g_return_val_if_fail (MX_IS_ACTOR_MANAGER (manager), 0); return g_queue_get_length (manager->priv->ops); } mx-1.4.7/mx/mx-actor-manager.h000066400000000000000000000120101201047117600160770ustar00rootroot00000000000000/* * mx-actor-manager: Actor life-cycle manager object * * Copyright 2011 Intel Corporation * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * * Written by: Chris Lord * */ #ifndef _MX_ACTOR_MANAGER_H #define _MX_ACTOR_MANAGER_H #include #include G_BEGIN_DECLS #define MX_TYPE_ACTOR_MANAGER mx_actor_manager_get_type() #define MX_ACTOR_MANAGER(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ MX_TYPE_ACTOR_MANAGER, MxActorManager)) #define MX_ACTOR_MANAGER_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), \ MX_TYPE_ACTOR_MANAGER, MxActorManagerClass)) #define MX_IS_ACTOR_MANAGER(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \ MX_TYPE_ACTOR_MANAGER)) #define MX_IS_ACTOR_MANAGER_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE ((klass), \ MX_TYPE_ACTOR_MANAGER)) #define MX_ACTOR_MANAGER_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), \ MX_TYPE_ACTOR_MANAGER, MxActorManagerClass)) typedef struct _MxActorManager MxActorManager; typedef struct _MxActorManagerClass MxActorManagerClass; typedef struct _MxActorManagerPrivate MxActorManagerPrivate; typedef ClutterActor * (*MxActorManagerCreateFunc) (MxActorManager *manager, gpointer userdata); typedef enum { MX_ACTOR_MANAGER_CONTAINER_DESTROYED, MX_ACTOR_MANAGER_ACTOR_DESTROYED, MX_ACTOR_MANAGER_CREATION_FAILED, MX_ACTOR_MANAGER_UNKNOWN_OPERATION } MxActorManagerError; struct _MxActorManager { GObject parent; MxActorManagerPrivate *priv; }; struct _MxActorManagerClass { GObjectClass parent_class; /* signals */ void (*actor_created) (MxActorManager *manager, gulong id, ClutterActor *actor); void (*actor_added) (MxActorManager *manager, gulong id, ClutterContainer *container, ClutterActor *actor); void (*actor_removed) (MxActorManager *manager, gulong id, ClutterContainer *container, ClutterActor *actor); void (*actor_finished) (MxActorManager *manager, ClutterActor *actor); void (*operation_completed) (MxActorManager *manager, gulong id); void (*operation_cancelled) (MxActorManager *manager, gulong id); void (*operation_failed) (MxActorManager *manager, gulong id, GError *error); /* padding for future expansion */ void (*_padding_0) (void); void (*_padding_1) (void); void (*_padding_2) (void); void (*_padding_3) (void); void (*_padding_4) (void); }; GType mx_actor_manager_get_type (void) G_GNUC_CONST; MxActorManager *mx_actor_manager_new (ClutterStage *stage); MxActorManager *mx_actor_manager_get_for_stage (ClutterStage *stage); ClutterStage *mx_actor_manager_get_stage (MxActorManager *manager); gulong mx_actor_manager_create_actor (MxActorManager *manager, MxActorManagerCreateFunc create_func, gpointer userdata, GDestroyNotify destroy_func); gulong mx_actor_manager_add_actor (MxActorManager *manager, ClutterContainer *container, ClutterActor *actor); gulong mx_actor_manager_remove_actor (MxActorManager *manager, ClutterContainer *container, ClutterActor *actor); void mx_actor_manager_remove_container (MxActorManager *manager, ClutterContainer *container); void mx_actor_manager_cancel_operation (MxActorManager *manager, gulong id); void mx_actor_manager_cancel_operations (MxActorManager *manager, ClutterActor *actor); void mx_actor_manager_set_time_slice (MxActorManager *manager, guint msecs); guint mx_actor_manager_get_time_slice (MxActorManager *manager); guint mx_actor_manager_get_n_operations (MxActorManager *manager); G_END_DECLS #endif /* _MX_ACTOR_MANAGER_H */ mx-1.4.7/mx/mx-adjustment.c000066400000000000000000001062661201047117600155510ustar00rootroot00000000000000/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * mx-adjustment.c: Adjustment object * * Copyright (C) 2008 OpenedHand * Copyright (c) 2009, 2010 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * * Written by: Chris Lord , inspired by GtkAdjustment * Port to Mx by: Robert Staudinger * */ /** * SECTION:mx-adjustment * @short_description: A GObject representing an adjustable bounded value * * The #MxAdjustment object represents a range of values bounded between a * minimum and maximum, together with step and page increments and a page size. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include "mx-adjustment.h" #include "mx-marshal.h" #include "mx-private.h" G_DEFINE_TYPE (MxAdjustment, mx_adjustment, G_TYPE_OBJECT) #define ADJUSTMENT_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), MX_TYPE_ADJUSTMENT, MxAdjustmentPrivate)) struct _MxAdjustmentPrivate { /* Do not sanity-check values while constructing, * not all properties may be set yet. */ guint is_constructing : 1; guint clamp_value : 1; guint elastic : 1; gdouble lower; gdouble upper; gdouble value; gdouble step_increment; gdouble page_increment; gdouble page_size; /* For signal emission/notification */ guint lower_source; guint upper_source; guint value_source; guint step_inc_source; guint page_inc_source; guint page_size_source; guint changed_source; /* For interpolation */ ClutterTimeline *interpolation; gdouble old_position; gdouble new_position; ClutterAlpha *interpolate_alpha; }; enum { PROP_0, PROP_LOWER, PROP_UPPER, PROP_VALUE, PROP_STEP_INC, PROP_PAGE_INC, PROP_PAGE_SIZE, PROP_ELASTIC, PROP_CLAMP_VALUE, }; enum { CHANGED, INTERPOLATION_COMPLETED, LAST_SIGNAL }; static guint signals[LAST_SIGNAL] = { 0, }; static gboolean _mx_adjustment_set_lower (MxAdjustment *adjustment, gdouble lower); static gboolean _mx_adjustment_set_upper (MxAdjustment *adjustment, gdouble upper); static gboolean _mx_adjustment_set_step_increment (MxAdjustment *adjustment, gdouble step); static gboolean _mx_adjustment_set_page_increment (MxAdjustment *adjustment, gdouble page); static gboolean _mx_adjustment_set_page_size (MxAdjustment *adjustment, gdouble size); static void mx_adjustment_clamp_page (MxAdjustment *adjustment, gdouble lower, gdouble upper); static void mx_adjustment_emit_changed (MxAdjustment *adjustment); static void mx_adjustment_constructed (GObject *object) { GObjectClass *g_class; MxAdjustment *self = MX_ADJUSTMENT (object); g_class = G_OBJECT_CLASS (mx_adjustment_parent_class); /* The docs say we're suppose to chain up, but would crash without * some extra care. */ if (g_class && g_class->constructed && g_class->constructed != mx_adjustment_constructed) { g_class->constructed (object); } MX_ADJUSTMENT (self)->priv->is_constructing = FALSE; mx_adjustment_clamp_page (self, self->priv->lower, self->priv->upper); } static void mx_adjustment_get_property (GObject *gobject, guint prop_id, GValue *value, GParamSpec *pspec) { MxAdjustmentPrivate *priv = MX_ADJUSTMENT (gobject)->priv; switch (prop_id) { case PROP_LOWER: g_value_set_double (value, priv->lower); break; case PROP_UPPER: g_value_set_double (value, priv->upper); break; case PROP_VALUE: g_value_set_double (value, priv->value); break; case PROP_STEP_INC: g_value_set_double (value, priv->step_increment); break; case PROP_PAGE_INC: g_value_set_double (value, priv->page_increment); break; case PROP_PAGE_SIZE: g_value_set_double (value, priv->page_size); break; case PROP_ELASTIC: g_value_set_boolean (value, priv->elastic); break; case PROP_CLAMP_VALUE: g_value_set_boolean (value, priv->clamp_value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); break; } } static void mx_adjustment_set_property (GObject *gobject, guint prop_id, const GValue *value, GParamSpec *pspec) { MxAdjustment *adj = MX_ADJUSTMENT (gobject); switch (prop_id) { case PROP_LOWER: mx_adjustment_set_lower (adj, g_value_get_double (value)); break; case PROP_UPPER: mx_adjustment_set_upper (adj, g_value_get_double (value)); break; case PROP_VALUE: mx_adjustment_set_value (adj, g_value_get_double (value)); break; case PROP_STEP_INC: mx_adjustment_set_step_increment (adj, g_value_get_double (value)); break; case PROP_PAGE_INC: mx_adjustment_set_page_increment (adj, g_value_get_double (value)); break; case PROP_PAGE_SIZE: mx_adjustment_set_page_size (adj, g_value_get_double (value)); break; case PROP_ELASTIC: mx_adjustment_set_elastic (adj, g_value_get_boolean (value)); break; case PROP_CLAMP_VALUE: mx_adjustment_set_clamp_value (adj, g_value_get_boolean (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); break; } } static void stop_interpolation (MxAdjustment *adjustment) { MxAdjustmentPrivate *priv = adjustment->priv; if (priv->interpolation) { clutter_timeline_stop (priv->interpolation); g_object_unref (priv->interpolation); priv->interpolation = NULL; } } static void mx_adjustment_remove_idle (guint *source) { if (*source) { g_source_remove (*source); *source = 0; } } static void mx_adjustment_dispose (GObject *object) { MxAdjustmentPrivate *priv = MX_ADJUSTMENT (object)->priv; stop_interpolation (MX_ADJUSTMENT (object)); /* Remove idle handlers */ mx_adjustment_remove_idle (&priv->value_source); mx_adjustment_remove_idle (&priv->lower_source); mx_adjustment_remove_idle (&priv->upper_source); mx_adjustment_remove_idle (&priv->page_inc_source); mx_adjustment_remove_idle (&priv->step_inc_source); mx_adjustment_remove_idle (&priv->page_size_source); mx_adjustment_remove_idle (&priv->changed_source); if (priv->interpolate_alpha) { g_object_unref (priv->interpolate_alpha); priv->interpolate_alpha = NULL; } G_OBJECT_CLASS (mx_adjustment_parent_class)->dispose (object); } static void mx_adjustment_class_init (MxAdjustmentClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); g_type_class_add_private (klass, sizeof (MxAdjustmentPrivate)); object_class->constructed = mx_adjustment_constructed; object_class->get_property = mx_adjustment_get_property; object_class->set_property = mx_adjustment_set_property; object_class->dispose = mx_adjustment_dispose; g_object_class_install_property (object_class, PROP_LOWER, g_param_spec_double ("lower", "Lower", "Lower bound", -G_MAXDOUBLE, G_MAXDOUBLE, 0.0, MX_PARAM_READWRITE | G_PARAM_CONSTRUCT)); g_object_class_install_property (object_class, PROP_UPPER, g_param_spec_double ("upper", "Upper", "Upper bound", -G_MAXDOUBLE, G_MAXDOUBLE, 0.0, MX_PARAM_READWRITE | G_PARAM_CONSTRUCT)); g_object_class_install_property (object_class, PROP_VALUE, g_param_spec_double ("value", "Value", "Current value", -G_MAXDOUBLE, G_MAXDOUBLE, 0.0, MX_PARAM_READWRITE | G_PARAM_CONSTRUCT)); g_object_class_install_property (object_class, PROP_STEP_INC, g_param_spec_double ("step-increment", "Step Increment", "Step increment", 0.0, G_MAXDOUBLE, 1.0, MX_PARAM_READWRITE | G_PARAM_CONSTRUCT)); g_object_class_install_property (object_class, PROP_PAGE_INC, g_param_spec_double ("page-increment", "Page Increment", "Page increment", 0.0, G_MAXDOUBLE, 0.0, MX_PARAM_READWRITE | G_PARAM_CONSTRUCT)); g_object_class_install_property (object_class, PROP_PAGE_SIZE, g_param_spec_double ("page-size", "Page Size", "Page size", 0.0, G_MAXDOUBLE, 0.0, MX_PARAM_READWRITE | G_PARAM_CONSTRUCT)); g_object_class_install_property (object_class, PROP_ELASTIC, g_param_spec_boolean ("elastic", "Elastic", "Make interpolation " "behave in an " "'elastic' way and " "stop clamping value.", FALSE, MX_PARAM_READWRITE)); g_object_class_install_property (object_class, PROP_CLAMP_VALUE, g_param_spec_boolean ("clamp-value", "Clamp value", "Clamp the adjustment " "value between the " "lower and upper " "values, respecting " "the page-size.", TRUE, MX_PARAM_READWRITE)); /** * MxAdjustment::changed: * * Emitted when any of the adjustment values have changed */ signals[CHANGED] = g_signal_new ("changed", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (MxAdjustmentClass, changed), NULL, NULL, _mx_marshal_VOID__VOID, G_TYPE_NONE, 0); /** * MxAdjustment::interpolation-completed: * * Emitted when the animation started by mx_adjustment_interpolate completes */ signals[INTERPOLATION_COMPLETED] = g_signal_new ("interpolation-completed", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (MxAdjustmentClass, interpolation_completed), NULL, NULL, _mx_marshal_VOID__VOID, G_TYPE_NONE, 0); } static void mx_adjustment_init (MxAdjustment *self) { self->priv = ADJUSTMENT_PRIVATE (self); self->priv->is_constructing = TRUE; self->priv->clamp_value = TRUE; } /** * mx_adjustment_new: * * Create a new MxAdjustment * * Returns: a newly allocated MxAdjustment */ MxAdjustment * mx_adjustment_new (void) { return g_object_new (MX_TYPE_ADJUSTMENT, NULL); } /** * mx_adjustment_new_with_values: * @value: A #gdouble * @lower: A #gdouble * @upper: A #gdouble * @step_increment: A #gdouble * @page_increment: A #gdouble * @page_size: A #gdouble * * Create a new MxAdjustment with the properties set to the values specified. * * Returns: a newly allocated MxAdjustment */ MxAdjustment * mx_adjustment_new_with_values (gdouble value, gdouble lower, gdouble upper, gdouble step_increment, gdouble page_increment, gdouble page_size) { return g_object_new (MX_TYPE_ADJUSTMENT, "value", value, "lower", lower, "upper", upper, "step-increment", step_increment, "page-increment", page_increment, "page-size", page_size, NULL); } /** * mx_adjustment_get_value: * @adjustment: An #MxAdjustment * * Get the current value of the #MxAdjustment:value property * * Returns: the current value of the "value" property */ gdouble mx_adjustment_get_value (MxAdjustment *adjustment) { MxAdjustmentPrivate *priv; g_return_val_if_fail (MX_IS_ADJUSTMENT (adjustment), 0); priv = adjustment->priv; return priv->value; } static gboolean mx_adjustment_value_notify_cb (MxAdjustment *adjustment) { MxAdjustmentPrivate *priv = adjustment->priv; priv->value_source = 0; g_object_notify (G_OBJECT (adjustment), "value"); return FALSE; } static gboolean mx_adjustment_lower_notify_cb (MxAdjustment *adjustment) { MxAdjustmentPrivate *priv = adjustment->priv; priv->lower_source = 0; g_object_notify (G_OBJECT (adjustment), "lower"); return FALSE; } static gboolean mx_adjustment_upper_notify_cb (MxAdjustment *adjustment) { MxAdjustmentPrivate *priv = adjustment->priv; priv->upper_source = 0; g_object_notify (G_OBJECT (adjustment), "upper"); return FALSE; } static gboolean mx_adjustment_step_inc_notify_cb (MxAdjustment *adjustment) { MxAdjustmentPrivate *priv = adjustment->priv; priv->step_inc_source = 0; g_object_notify (G_OBJECT (adjustment), "step-increment"); return FALSE; } static gboolean mx_adjustment_page_inc_notify_cb (MxAdjustment *adjustment) { MxAdjustmentPrivate *priv = adjustment->priv; priv->page_inc_source = 0; g_object_notify (G_OBJECT (adjustment), "page-increment"); return FALSE; } static gboolean mx_adjustment_page_size_notify_cb (MxAdjustment *adjustment) { MxAdjustmentPrivate *priv = adjustment->priv; priv->page_size_source = 0; g_object_notify (G_OBJECT (adjustment), "page-size"); return FALSE; } static gboolean mx_adjustment_emit_changed_cb (MxAdjustment *adjustment) { MxAdjustmentPrivate *priv = adjustment->priv; priv->changed_source = 0; g_signal_emit (adjustment, signals[CHANGED], 0); return FALSE; } /** * mx_adjustment_set_value: * @adjustment: An #MxAdjustment * @value: A #gdouble * * Set the value of the #MxAdjustment:value property. * */ void mx_adjustment_set_value (MxAdjustment *adjustment, gdouble value) { MxAdjustmentPrivate *priv; g_return_if_fail (MX_IS_ADJUSTMENT (adjustment)); priv = adjustment->priv; /* Defer clamp until after construction. */ if (!priv->is_constructing) { if (!priv->elastic && priv->clamp_value) value = CLAMP (value, priv->lower, MAX (priv->lower, priv->upper - priv->page_size)); } if (priv->value != value) { stop_interpolation (adjustment); priv->value = value; g_object_notify (G_OBJECT (adjustment), "value"); mx_adjustment_emit_changed (adjustment); } } static void mx_adjustment_clamp_page (MxAdjustment *adjustment, gdouble lower, gdouble upper) { MxAdjustmentPrivate *priv; gboolean changed; g_return_if_fail (MX_IS_ADJUSTMENT (adjustment)); priv = adjustment->priv; lower = CLAMP (lower, priv->lower, priv->upper - priv->page_size); upper = CLAMP (upper, priv->lower + priv->page_size, priv->upper); changed = FALSE; if (priv->value + priv->page_size > upper) { priv->value = upper - priv->page_size; changed = TRUE; } if (priv->value < lower) { priv->value = lower; changed = TRUE; } if (changed && !priv->value_source) priv->value_source = g_idle_add_full (CLUTTER_PRIORITY_REDRAW, (GSourceFunc)mx_adjustment_value_notify_cb, adjustment, NULL); } static void mx_adjustment_emit_changed (MxAdjustment *adjustment) { MxAdjustmentPrivate *priv = adjustment->priv; if (!priv->changed_source) priv->changed_source = g_idle_add_full (CLUTTER_PRIORITY_REDRAW, (GSourceFunc)mx_adjustment_emit_changed_cb, adjustment, NULL); } static gboolean _mx_adjustment_set_lower (MxAdjustment *adjustment, gdouble lower) { MxAdjustmentPrivate *priv = adjustment->priv; if (priv->lower != lower) { priv->lower = lower; mx_adjustment_emit_changed (adjustment); if (!priv->lower_source) priv->lower_source = g_idle_add_full (CLUTTER_PRIORITY_REDRAW, (GSourceFunc)mx_adjustment_lower_notify_cb, adjustment, NULL); /* Defer clamp until after construction. */ if (!priv->is_constructing && priv->clamp_value) mx_adjustment_clamp_page (adjustment, priv->lower, priv->upper); return TRUE; } return FALSE; } /** * mx_adjustment_set_lower: * @adjustment: A #MxAdjustment * @lower: A #gdouble * * Set the value of the #MxAdjustment:lower property. * */ void mx_adjustment_set_lower (MxAdjustment *adjustment, gdouble lower) { _mx_adjustment_set_lower (adjustment, lower); } /** * mx_adjustment_get_lower: * @adjustment: A #MxAdjustment * * Get the value of the #MxAdjustment:lower property. * * Returns: the current value of the "lower" property. */ gdouble mx_adjustment_get_lower (MxAdjustment *adjustment) { g_return_val_if_fail (MX_IS_ADJUSTMENT (adjustment), 0.0); return adjustment->priv->lower; } static gboolean _mx_adjustment_set_upper (MxAdjustment *adjustment, gdouble upper) { MxAdjustmentPrivate *priv = adjustment->priv; if (priv->upper != upper) { priv->upper = upper; mx_adjustment_emit_changed (adjustment); if (!priv->upper_source) priv->upper_source = g_idle_add_full (CLUTTER_PRIORITY_REDRAW, (GSourceFunc)mx_adjustment_upper_notify_cb, adjustment, NULL); /* Defer clamp until after construction. */ if (!priv->is_constructing && priv->clamp_value) mx_adjustment_clamp_page (adjustment, priv->lower, priv->upper); return TRUE; } return FALSE; } /** * mx_adjustment_set_upper: * @adjustment: A #MxAdjustment * @upper: A #gdouble * * Set the value of the #MxAdjustment:upper property. * */ void mx_adjustment_set_upper (MxAdjustment *adjustment, gdouble upper) { _mx_adjustment_set_upper (adjustment, upper); } /** * mx_adjustment_get_upper: * @adjustment: A #MxAdjustment * * Get the value of the #MxAdjustment:upper property. * * Returns: the current value of the "upper" property. */ gdouble mx_adjustment_get_upper (MxAdjustment *adjustment) { g_return_val_if_fail (MX_IS_ADJUSTMENT (adjustment), 0.0); return adjustment->priv->upper; } static gboolean _mx_adjustment_set_step_increment (MxAdjustment *adjustment, gdouble step) { MxAdjustmentPrivate *priv = adjustment->priv; if (priv->step_increment != step) { priv->step_increment = step; mx_adjustment_emit_changed (adjustment); if (!priv->step_inc_source) priv->step_inc_source = g_idle_add_full (CLUTTER_PRIORITY_REDRAW, (GSourceFunc)mx_adjustment_step_inc_notify_cb, adjustment, NULL); return TRUE; } return FALSE; } /** * mx_adjustment_set_step_increment: * @adjustment: A #MxAdjustment * @increment: A #gdouble * * Set the value of the #MxAdjustment:step-increment property. * */ void mx_adjustment_set_step_increment (MxAdjustment *adjustment, gdouble increment) { _mx_adjustment_set_step_increment (adjustment, increment); } /** * mx_adjustment_get_step_increment: * @adjustment: A #MxAdjustment * * Get the value of the MxAdjustment:step-increment property. * * Returns: the current value of the "step-increment" property. */ gdouble mx_adjustment_get_step_increment (MxAdjustment *adjustment) { g_return_val_if_fail (MX_IS_ADJUSTMENT (adjustment), 0.0); return adjustment->priv->step_increment; } static gboolean _mx_adjustment_set_page_increment (MxAdjustment *adjustment, gdouble page) { MxAdjustmentPrivate *priv = adjustment->priv; if (priv->page_increment != page) { priv->page_increment = page; mx_adjustment_emit_changed (adjustment); if (!priv->page_inc_source) priv->page_inc_source = g_idle_add_full (CLUTTER_PRIORITY_REDRAW, (GSourceFunc)mx_adjustment_page_inc_notify_cb, adjustment, NULL); return TRUE; } return FALSE; } /** * mx_adjustment_set_page_increment: * @adjustment: A #MxAdjustment * @increment: A #gdouble * * Set the value of the #MxAdjustment:page-increment property. * */ void mx_adjustment_set_page_increment (MxAdjustment *adjustment, gdouble increment) { _mx_adjustment_set_page_increment (adjustment, increment); } /** * mx_adjustment_get_page_increment: * @adjustment: A #MxAdjustment * * Get the value of the MxAdjustment:page-increment property. * * Returns: the current value of the "page-increment" property. */ gdouble mx_adjustment_get_page_increment (MxAdjustment *adjustment) { g_return_val_if_fail (MX_IS_ADJUSTMENT (adjustment), 0.0); return adjustment->priv->page_increment; } static gboolean _mx_adjustment_set_page_size (MxAdjustment *adjustment, gdouble size) { MxAdjustmentPrivate *priv = adjustment->priv; if (priv->page_size != size) { priv->page_size = size; mx_adjustment_emit_changed (adjustment); if (!priv->page_size_source) priv->page_size_source = g_idle_add_full (CLUTTER_PRIORITY_REDRAW, (GSourceFunc)mx_adjustment_page_size_notify_cb, adjustment, NULL); /* Well explicitely clamp after construction. */ if (!priv->is_constructing && priv->clamp_value) mx_adjustment_clamp_page (adjustment, priv->lower, priv->upper); return TRUE; } return FALSE; } /** * mx_adjustment_set_page_size: * @adjustment: A #MxAdjustment * @page_size: A #gdouble * * Set the #MxAdjustment:page-size property. * */ void mx_adjustment_set_page_size (MxAdjustment *adjustment, gdouble page_size) { _mx_adjustment_set_page_size (adjustment, page_size); } /** * mx_adjustment_get_page_size: * @adjustment: A #MxAdjustment * * Get the value of the #MxAdjustment:page-size property. * * Returns: the current value of the "page-size" property. */ gdouble mx_adjustment_get_page_size (MxAdjustment *adjustment) { g_return_val_if_fail (MX_IS_ADJUSTMENT (adjustment), 0.0); return adjustment->priv->page_size; } /** * mx_adjustment_set_values: * @adjustment: A #MxAdjustment * @value: A #gdouble * @lower: A #gdouble * @upper: A #gdouble * @step_increment: A #gdouble * @page_increment: A #gdouble * @page_size: A #gdouble * * Set the various properties of MxAdjustment. * */ void mx_adjustment_set_values (MxAdjustment *adjustment, gdouble value, gdouble lower, gdouble upper, gdouble step_increment, gdouble page_increment, gdouble page_size) { MxAdjustmentPrivate *priv; gboolean emit_changed = FALSE; g_return_if_fail (MX_IS_ADJUSTMENT (adjustment)); g_return_if_fail (page_size >= 0 && page_size <= G_MAXDOUBLE); g_return_if_fail (step_increment >= 0 && step_increment <= G_MAXDOUBLE); g_return_if_fail (page_increment >= 0 && page_increment <= G_MAXDOUBLE); priv = adjustment->priv; emit_changed = FALSE; g_object_freeze_notify (G_OBJECT (adjustment)); emit_changed |= _mx_adjustment_set_lower (adjustment, lower); emit_changed |= _mx_adjustment_set_upper (adjustment, upper); emit_changed |= _mx_adjustment_set_step_increment (adjustment, step_increment); emit_changed |= _mx_adjustment_set_page_increment (adjustment, page_increment); emit_changed |= _mx_adjustment_set_page_size (adjustment, page_size); if (value != priv->value) { mx_adjustment_set_value (adjustment, value); emit_changed = TRUE; } if (emit_changed) mx_adjustment_emit_changed (adjustment); g_object_thaw_notify (G_OBJECT (adjustment)); } /** * mx_adjustment_get_values: * @adjustment: A #MxAdjustment * @value: A #gdouble * @lower: A #gdouble * @upper: A #gdouble * @step_increment: A #gdouble * @page_increment: A #gdouble * @page_size: A #gdouble * * Get the various properties of MxAdjustment. * */ void mx_adjustment_get_values (MxAdjustment *adjustment, gdouble *value, gdouble *lower, gdouble *upper, gdouble *step_increment, gdouble *page_increment, gdouble *page_size) { MxAdjustmentPrivate *priv; g_return_if_fail (MX_IS_ADJUSTMENT (adjustment)); priv = adjustment->priv; if (lower) *lower = priv->lower; if (upper) *upper = priv->upper; if (value) *value = mx_adjustment_get_value (adjustment); if (step_increment) *step_increment = priv->step_increment; if (page_increment) *page_increment = priv->page_increment; if (page_size) *page_size = priv->page_size; } static void interpolation_new_frame_cb (ClutterTimeline *timeline, guint msecs, MxAdjustment *adjustment) { gdouble new_value; MxAdjustmentPrivate *priv = adjustment->priv; priv->interpolation = NULL; new_value = priv->old_position + (priv->new_position - priv->old_position) * clutter_alpha_get_alpha (priv->interpolate_alpha); mx_adjustment_set_value (adjustment, new_value); priv->interpolation = timeline; /* Stop the interpolation if we've reached the end of the adjustment */ if (!priv->elastic && priv->clamp_value && ((new_value < priv->lower) || (new_value > (priv->upper - priv->page_size)))) stop_interpolation (adjustment); } static void interpolation_completed_cb (ClutterTimeline *timeline, MxAdjustment *adjustment) { MxAdjustmentPrivate *priv = adjustment->priv; if (priv->elastic && priv->clamp_value) { if (clutter_timeline_get_direction (priv->interpolation) == CLUTTER_TIMELINE_FORWARD) { clutter_timeline_set_direction (priv->interpolation, CLUTTER_TIMELINE_BACKWARD); clutter_timeline_set_duration (priv->interpolation, 250); clutter_timeline_rewind (priv->interpolation); if (priv->new_position < priv->lower) { priv->old_position = priv->lower; clutter_timeline_start (priv->interpolation); } else if (priv->new_position > (priv->upper - priv->page_size)) { priv->old_position = priv->upper - priv->page_size; clutter_timeline_start (priv->interpolation); } } else { stop_interpolation (adjustment); mx_adjustment_set_value (adjustment, priv->old_position); } } else { stop_interpolation (adjustment); mx_adjustment_set_value (adjustment, priv->new_position); } g_signal_emit (adjustment, signals[INTERPOLATION_COMPLETED], 0); } /** * mx_adjustment_interpolate: * @adjustment: A #MxAdjustment * @value: A #gdouble * @duration: duration in milliseconds * @mode: A #ClutterAnimationMode * * Interpolate #MxAdjustment:value to the new value specified by @value, using * the mode and duration given. */ void mx_adjustment_interpolate (MxAdjustment *adjustment, gdouble value, guint duration, gulong mode) { MxAdjustmentPrivate *priv = adjustment->priv; g_return_if_fail (isfinite (value)); if (duration <= 1) { stop_interpolation (adjustment); mx_adjustment_set_value (adjustment, value); return; } priv->old_position = priv->value; priv->new_position = value; if (!priv->interpolation) { priv->interpolation = clutter_timeline_new (duration); g_signal_connect (priv->interpolation, "new-frame", G_CALLBACK (interpolation_new_frame_cb), adjustment); g_signal_connect (priv->interpolation, "completed", G_CALLBACK (interpolation_completed_cb), adjustment); } else { /* Extend the animation if it gets interrupted, otherwise frequent calls * to this function will end up with no advancements until the calls * finish (as the animation never gets a chance to start). */ clutter_timeline_set_direction (priv->interpolation, CLUTTER_TIMELINE_FORWARD); clutter_timeline_rewind (priv->interpolation); clutter_timeline_set_duration (priv->interpolation, duration); } if (priv->interpolate_alpha) g_object_unref (priv->interpolate_alpha); priv->interpolate_alpha = clutter_alpha_new_full (priv->interpolation, mode); clutter_timeline_start (priv->interpolation); } /** * mx_adjustment_interpolate_relative: * @adjustment: A #MxAdjustment * @offset: A #gdouble * @duration: duration in milliseconds * @mode: A #ClutterAnimationMode * * Interpolate the value of #MxAdjustment:value to a new value calculated from * @offset. * */ void mx_adjustment_interpolate_relative (MxAdjustment *adjustment, gdouble offset, guint duration, gulong mode) { MxAdjustmentPrivate *priv = adjustment->priv; if (priv->interpolation) offset += priv->new_position; else offset += priv->value; mx_adjustment_interpolate (adjustment, offset, duration, mode); } /** * mx_adjustment_get_elastic: * @adjustment: A #MxAdjustment * * Get the value of the #MxAdjustment:elastic property. * * Returns: the current value of the "elastic" property. */ gboolean mx_adjustment_get_elastic (MxAdjustment *adjustment) { return adjustment->priv->elastic; } /** * mx_adjustment_set_elastic: * @adjustment: A #MxAdjustment * @elastic: A #gboolean * * Set the value of the #MxAdjustment:elastic property. * */ void mx_adjustment_set_elastic (MxAdjustment *adjustment, gboolean elastic) { adjustment->priv->elastic = elastic; } /** * mx_adjustment_get_clamp_value: * @adjustment: A #MxAdjustment * * Get the value of the #MxAdjustment:clamp-value property. * * Returns: the current value of the "clamp-value" property. * * Since: 1.2 */ gboolean mx_adjustment_get_clamp_value (MxAdjustment *adjustment) { return adjustment->priv->clamp_value; } /** * mx_adjustment_set_clamp_value: * @adjustment: A #MxAdjustment * @clamp: a #gboolean * * Set the value of the #MxAdjustment:clamp-value property. * * Since: 1.2 */ void mx_adjustment_set_clamp_value (MxAdjustment *adjustment, gboolean clamp) { adjustment->priv->clamp_value = clamp; } mx-1.4.7/mx/mx-adjustment.h000066400000000000000000000151151201047117600155460ustar00rootroot00000000000000/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * mx-adjustment.h: Adjustment object * * Copyright 2008 OpenedHand * Copyright 2009, 2010 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * * Written by: Chris Lord , inspired by GtkAdjustment * Port to Mx by: Robert Staudinger * */ #if !defined(MX_H_INSIDE) && !defined(MX_COMPILATION) #error "Only can be included directly.h" #endif #ifndef __MX_ADJUSTMENT_H__ #define __MX_ADJUSTMENT_H__ #include #include G_BEGIN_DECLS #define MX_TYPE_ADJUSTMENT (mx_adjustment_get_type()) #define MX_ADJUSTMENT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MX_TYPE_ADJUSTMENT, MxAdjustment)) #define MX_IS_ADJUSTMENT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MX_TYPE_ADJUSTMENT)) #define MX_ADJUSTMENT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MX_TYPE_ADJUSTMENT, MxAdjustmentClass)) #define MX_IS_ADJUSTMENT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MX_TYPE_ADJUSTMENT)) #define MX_ADJUSTMENT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MX_TYPE_ADJUSTMENT, MxAdjustmentClass)) typedef struct _MxAdjustment MxAdjustment; typedef struct _MxAdjustmentPrivate MxAdjustmentPrivate; typedef struct _MxAdjustmentClass MxAdjustmentClass; /** * MxAdjustment: * * Class for handling an interval between to values. The contents of * the #MxAdjustment are private and should be accessed using the * public API. */ struct _MxAdjustment { /*< private >*/ GObject parent_instance; MxAdjustmentPrivate *priv; }; /** * MxAdjustmentClass * @changed: Class handler for the ::changed signal. * * Base class for #MxAdjustment. */ struct _MxAdjustmentClass { /*< private >*/ GObjectClass parent_class; /*< public >*/ void (* changed) (MxAdjustment *adjustment); void (* interpolation_completed) (MxAdjustment *adjustment); /* padding for future expansion */ void (*_padding_0) (void); void (*_padding_1) (void); void (*_padding_2) (void); void (*_padding_3) (void); }; GType mx_adjustment_get_type (void) G_GNUC_CONST; MxAdjustment *mx_adjustment_new (void); MxAdjustment *mx_adjustment_new_with_values (gdouble value, gdouble lower, gdouble upper, gdouble step_increment, gdouble page_increment, gdouble page_size); gdouble mx_adjustment_get_value (MxAdjustment *adjustment); void mx_adjustment_set_value (MxAdjustment *adjustment, gdouble value); gdouble mx_adjustment_get_lower (MxAdjustment *adjustment); void mx_adjustment_set_lower (MxAdjustment *adjustment, gdouble lower); gdouble mx_adjustment_get_upper (MxAdjustment *adjustment); void mx_adjustment_set_upper (MxAdjustment *adjustment, gdouble upper); gdouble mx_adjustment_get_step_increment (MxAdjustment *adjustment); void mx_adjustment_set_step_increment (MxAdjustment *adjustment, gdouble increment); gdouble mx_adjustment_get_page_increment (MxAdjustment *adjustment); void mx_adjustment_set_page_increment (MxAdjustment *adjustment, gdouble increment); gdouble mx_adjustment_get_page_size (MxAdjustment *adjustment); void mx_adjustment_set_page_size (MxAdjustment *adjustment, gdouble page_size); void mx_adjustment_set_values (MxAdjustment *adjustment, gdouble value, gdouble lower, gdouble upper, gdouble step_increment, gdouble page_increment, gdouble page_size); void mx_adjustment_get_values (MxAdjustment *adjustment, gdouble *value, gdouble *lower, gdouble *upper, gdouble *step_increment, gdouble *page_increment, gdouble *page_size); void mx_adjustment_interpolate (MxAdjustment *adjustment, gdouble value, guint duration, gulong mode); void mx_adjustment_interpolate_relative (MxAdjustment *adjustment, gdouble offset, guint duration, gulong mode); gboolean mx_adjustment_get_elastic (MxAdjustment *adjustment); void mx_adjustment_set_elastic (MxAdjustment *adjustment, gboolean elastic); gboolean mx_adjustment_get_clamp_value (MxAdjustment *adjustment); void mx_adjustment_set_clamp_value (MxAdjustment *adjustment, gboolean clamp); G_END_DECLS #endif /* __MX_ADJUSTMENT_H__ */ mx-1.4.7/mx/mx-application.c000066400000000000000000001174101201047117600156670ustar00rootroot00000000000000/* * mx-application: application class * * Copyright 2010 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * * Written by: Thomas Wood , * Chris Lord */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "mx-application.h" #include "mx-private.h" #include "mx-settings.h" #include "mx-window.h" #include #ifdef HAVE_STARTUP_NOTIFICATION # define SN_API_NOT_YET_FROZEN # include # ifdef HAVE_X11 # include # endif #endif #ifdef HAVE_DBUS #include #include #include #endif G_DEFINE_TYPE (MxApplication, mx_application, G_TYPE_OBJECT) #define APPLICATION_PRIVATE(o) \ (G_TYPE_INSTANCE_GET_PRIVATE ((o), MX_TYPE_APPLICATION, MxApplicationPrivate)) struct _MxApplicationPrivate { GList *windows; gchar *name; MxApplicationFlags flags; gboolean is_proxy; gboolean is_running; #ifdef HAVE_STARTUP_NOTIFICATION SnLauncheeContext *sn_context; #endif #ifdef HAVE_DBUS gchar *service_name; DBusGProxy *proxy; DBusGObjectInfo object_info; gboolean actions_valid; #endif GHashTable *actions; }; enum { PROP_0, PROP_APP_NAME, PROP_FLAGS }; enum { ACTIONS_CHANGED, LAST_SIGNAL }; static guint signals[LAST_SIGNAL] = { 0, }; static MxApplication *app_singleton = NULL; static GObject* mx_application_constructor (GType type, guint n_construct_params, GObjectConstructParam *construct_params) { GObject *object; /* ensure MxApplication is a singleton */ if (!app_singleton) { object = G_OBJECT_CLASS (mx_application_parent_class)->constructor (type, n_construct_params, construct_params); app_singleton = MX_APPLICATION (object); } else object = g_object_ref (G_OBJECT (app_singleton)); return object; } static void mx_application_raise_activated_cb (MxAction *action, MxApplication *application) { MX_APPLICATION_GET_CLASS (application)->raise (application); } #ifdef HAVE_DBUS static gchar * mx_application_get_service_path (MxApplication *application) { gint i, length; gchar *path_from_name; MxApplicationPrivate *priv = application->priv; if (!priv->service_name) return NULL; length = strlen (priv->service_name); path_from_name = g_malloc (length + 2); path_from_name[0] = '/'; for (i = 0; i < length; i++) path_from_name[i+1] = (priv->service_name[i] == '.') ? '/' : priv->service_name[i]; path_from_name[i+1] = '\0'; return path_from_name; } static void mx_application_register_dbus (MxApplication *application, gboolean unregister) { DBusGConnection *bus; GError *error = NULL; if (!(bus = dbus_g_bus_get (DBUS_BUS_SESSION, &error))) { g_warning (G_STRLOC "%s", error->message); g_error_free (error); return; } if (unregister) { static gboolean first_run = TRUE; if (first_run) first_run = FALSE; else dbus_g_connection_unregister_g_object (bus, G_OBJECT (application)); } else { gchar *path_from_name = mx_application_get_service_path (application); dbus_g_connection_register_g_object (bus, path_from_name, G_OBJECT (application)); g_free (path_from_name); } } static void mx_application_actions_changed_cb (DBusGProxy *proxy, gpointer data) { MxApplicationPrivate *priv = MX_APPLICATION (data)->priv; /* Mark that our actions table is outdated */ priv->actions_valid = FALSE; g_signal_emit (data, signals[ACTIONS_CHANGED], 0); } #endif static void mx_application_notify_small_screen_cb (MxSettings *settings, GParamSpec *pspec, MxApplication *self) { MxApplicationPrivate *priv = self->priv; /* Reflect small-screen mode in the first added window of the * application. * * FIXME: This should probably be optional. */ if (priv->windows) { gboolean small_screen = FALSE; MxWindow *window = g_list_last (priv->windows)->data; g_object_get (G_OBJECT (settings), "small-screen", &small_screen, NULL); mx_window_set_small_screen (window, small_screen); } } static void mx_application_constructed (GObject *object) { MxSettings *settings; MxApplication *self = MX_APPLICATION (object); MxApplicationPrivate *priv = self->priv; gboolean success = FALSE; #if defined (HAVE_STARTUP_NOTIFICATION) && defined (HAVE_X11) SnDisplay *display; Display *xdisplay; int screen; #endif #ifdef HAVE_DBUS GError *error = NULL; DBusGConnection *bus; guint32 request_status; gboolean unique; if (!priv->service_name) { g_warning ("No service name, not registering DBus service"); return; } if (!(bus = dbus_g_bus_get (DBUS_BUS_SESSION, &error))) { g_warning ("%s", error->message); g_error_free (error); return; } priv->proxy = dbus_g_proxy_new_for_name (bus, DBUS_SERVICE_DBUS, DBUS_PATH_DBUS, DBUS_INTERFACE_DBUS); unique = (priv->flags & MX_APPLICATION_SINGLE_INSTANCE) ? TRUE : FALSE; if (!org_freedesktop_DBus_request_name (priv->proxy, priv->service_name, unique ? DBUS_NAME_FLAG_DO_NOT_QUEUE : 0, &request_status, &error)) { g_warning ("Failed to request name: %s", error->message); g_error_free (error); } else if (request_status == DBUS_REQUEST_NAME_REPLY_EXISTS) { DBusGProxy *proxy; gchar *path_from_name; priv->is_proxy = TRUE; path_from_name = mx_application_get_service_path (self); proxy = dbus_g_proxy_new_for_name (bus, priv->service_name, path_from_name, priv->service_name); g_free (path_from_name); if (proxy) { dbus_g_proxy_add_signal (proxy, "ActionsChanged", G_TYPE_INVALID); dbus_g_proxy_connect_signal (proxy, "ActionsChanged", G_CALLBACK ( mx_application_actions_changed_cb), self, NULL); g_object_unref (proxy); } } else if (request_status == DBUS_REQUEST_NAME_REPLY_IN_QUEUE) { if (!unique) g_warning ("Single-instance application in queue."); else success = TRUE; } else if (request_status != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) { g_warning ("Failed to request name"); } else { success = TRUE; } /* No need to register here, adding an action will cause us to * re-register anyway (and we add raise below). */ /*if (success) mx_application_register_dbus (self, FALSE);*/ dbus_g_connection_unref (bus); #endif /* Add default 'raise' action */ if (!priv->is_proxy && success) { MxAction *raise_action = mx_action_new_full ("Raise", _("Raise application"), G_CALLBACK (mx_application_raise_activated_cb), self); mx_application_add_action (self, raise_action); } settings = mx_settings_get_default (); if (settings) g_signal_connect (settings, "notify::small-screen", G_CALLBACK (mx_application_notify_small_screen_cb), self); #if defined (HAVE_STARTUP_NOTIFICATION) && defined (HAVE_X11) xdisplay = clutter_x11_get_default_display (); screen = clutter_x11_get_default_screen (); if (g_getenv ("LIBSN_SYNC")) XSynchronize (xdisplay, True); display = sn_display_new (xdisplay, NULL, NULL); priv->sn_context = sn_launchee_context_new_from_environment (display, screen); #endif } static void mx_application_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { MxApplicationPrivate *priv = MX_APPLICATION (object)->priv; switch (property_id) { case PROP_APP_NAME: g_value_set_string (value, priv->name); break; case PROP_FLAGS: g_value_set_uint (value, priv->flags); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } } #ifdef HAVE_DBUS static gchar * mx_application_get_safe_name (const gchar *name) { gint i; gchar *camel; gboolean raise_case; const gchar *name_ptr; /* Create an ASCII CamelCase string from arbitrary UTF-8 */ camel = g_malloc (strlen (name) + 1); name_ptr = name; raise_case = TRUE; i = 0; while (*name_ptr) { /* Ignore non-ASCII */ if (*name_ptr < 0x80) { /* Don't let the first character be a number and * only accept alpha/number. */ if (g_ascii_isalpha (*name_ptr) || ((i != 0) && g_ascii_isalnum (*name_ptr))) { if (raise_case) { camel[i] = g_ascii_toupper (*name_ptr); raise_case = FALSE; } else camel[i] = *name_ptr; i++; } else if ((*name_ptr == '-') || (*name_ptr == '_') || (*name_ptr == ' ')) { /* Use upper-case after dashes/underscores/spaces */ raise_case = TRUE; } } name_ptr = g_utf8_find_next_char (name_ptr, NULL); } /* Make sure string is NULL-terminated */ camel[i] = '\0'; return camel; } #endif static void mx_application_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { MxApplicationPrivate *priv = MX_APPLICATION (object)->priv; switch (property_id) { case PROP_APP_NAME: priv->name = g_value_dup_string (value); g_set_application_name (priv->name); #ifdef HAVE_DBUS if (priv->name) { /* Use CamelCase name for service name */ gchar *camel = mx_application_get_safe_name (priv->name); priv->service_name = g_strconcat ("org.moblin.", camel, NULL); g_free (camel); } #endif break; case PROP_FLAGS: priv->flags = g_value_get_uint (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } } static void mx_application_dispose (GObject *object) { MxApplicationPrivate *priv = MX_APPLICATION (object)->priv; #ifdef HAVE_DBUS if (priv->proxy) { g_object_unref (priv->proxy); priv->proxy = NULL; } #endif if (priv->actions) { g_hash_table_destroy (priv->actions); priv->actions = NULL; } G_OBJECT_CLASS (mx_application_parent_class)->dispose (object); } static void mx_application_finalize (GObject *object) { MxApplicationPrivate *priv = MX_APPLICATION (object)->priv; #ifdef HAVE_STARTUP_NOTIFICATION if (priv->sn_context) { sn_launchee_context_complete (priv->sn_context); sn_launchee_context_unref (priv->sn_context); } #endif #ifdef HAVE_DBUS g_free (priv->service_name); g_free ((gpointer)priv->object_info.method_infos); g_free ((gpointer)priv->object_info.data); g_free ((gpointer)priv->object_info.exported_signals); #endif g_free (priv->name); G_OBJECT_CLASS (mx_application_parent_class)->finalize (object); } static MxWindow * mx_application_default_create_window (MxApplication *application) { MxWindow *window; ClutterStage *stage; window = mx_window_new (); stage = mx_window_get_clutter_stage (window); mx_application_add_window (application, window); clutter_stage_set_title (stage, application->priv->name); return window; } static void mx_application_default_raise (MxApplication *application) { MxApplicationPrivate *priv = application->priv; if (!priv->windows) return; mx_window_present (MX_WINDOW (g_list_last (priv->windows)->data)); } static void mx_application_class_init (MxApplicationClass *klass) { GParamSpec *pspec; GObjectClass *object_class = G_OBJECT_CLASS (klass); g_type_class_add_private (klass, sizeof (MxApplicationPrivate)); object_class->constructor = mx_application_constructor; object_class->constructed = mx_application_constructed; object_class->get_property = mx_application_get_property; object_class->set_property = mx_application_set_property; object_class->dispose = mx_application_dispose; object_class->finalize = mx_application_finalize; klass->create_window = mx_application_default_create_window; klass->raise = mx_application_default_raise; pspec = g_param_spec_string ("application-name", "Application Name", "Name of the application", "", G_PARAM_CONSTRUCT_ONLY | MX_PARAM_READWRITE); g_object_class_install_property (object_class, PROP_APP_NAME, pspec); pspec = g_param_spec_uint ("flags", "Flags", "Application Flags", 0, G_MAXINT, 0, G_PARAM_CONSTRUCT_ONLY | MX_PARAM_READWRITE); g_object_class_install_property (object_class, PROP_FLAGS, pspec); /** * MxApplication::actions-changed * * Emitted when an action has been added or removed from the MxApplication. */ signals[ACTIONS_CHANGED] = g_signal_new ("actions-changed", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (MxApplicationClass, actions_changed), NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); } static void mx_application_init (MxApplication *self) { MxApplicationPrivate *priv = self->priv = APPLICATION_PRIVATE (self); priv->actions = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref); } static void mx_application_window_destroy_cb (MxWindow *window, MxApplication *application) { mx_application_remove_window (application, window); if (!(application->priv->flags & MX_APPLICATION_KEEP_ALIVE) && !(application->priv->windows)) { mx_application_quit (application); } } #if defined (HAVE_STARTUP_NOTIFICATION) && defined (HAVE_X11) static void mx_application_window_map_cb (ClutterActor *actor, GParamSpec *pspec, MxApplication *application) { Window xwindow; MxApplicationPrivate *priv = application->priv; if (CLUTTER_ACTOR_IS_MAPPED (actor)) { xwindow = clutter_x11_get_stage_window (CLUTTER_STAGE (actor)); sn_launchee_context_setup_window (priv->sn_context, xwindow); sn_launchee_context_complete (priv->sn_context); sn_launchee_context_unref (priv->sn_context); priv->sn_context = NULL; g_signal_handlers_disconnect_by_func (actor, mx_application_window_map_cb, application); } } #endif /** * mx_application_new: * @argc: (inout): The number of arguments in argv. * @argv: (array length=argc) (inout) (allow-none): A pointer to an array of * arguments * @name: Unique application name. * @flags: Application flags. * * Intialises everything needed to operate Clutter and use #MxApplication. * See clutter_init(). * * Return value: the #MxApplication singleton. */ MxApplication * mx_application_new (gint *argc, gchar ***argv, const gchar *name, MxApplicationFlags flags) { MxApplication *app; ClutterInitError result; GError *error = NULL; /* initialise clutter and the type system */ result = clutter_init_with_args (argc, argv, name, NULL, NULL, &error); if (result != CLUTTER_INIT_SUCCESS) { /* abort the application if Clutter failed to initialise */ g_error ("Failed to initialise Clutter: %s", error->message); } mx_set_locale (); /* create the application singleton */ app = g_object_new (MX_TYPE_APPLICATION, "flags", flags, "application-name", name, NULL); return app; } /** * mx_application_run: * @application: an MxApplication * * Run the main loop of the application and start processing events. This * function will not return until the application is quit. If the application * is single instance and an existing instance is already running, this will * cause the existing instance to be raised and the function will complete * immediately. */ void mx_application_run (MxApplication *application) { MxApplicationPrivate *priv; g_return_if_fail (MX_IS_APPLICATION (application)); priv = application->priv; if (!priv->is_proxy) { priv->is_running = TRUE; clutter_main (); } else { /* Raise the running instance and fall through */ mx_application_invoke_action (application, "Raise"); } priv->is_running = FALSE; } /** * mx_application_quit: * @application: an #MxApplication * * Stop the application from running and quit the main loop. This will cause * the call to mx_application_run() to complete. */ void mx_application_quit (MxApplication *application) { clutter_main_quit (); } /** * mx_application_get_flags: * @application: an #MxApplication * * Get the application flags that where set on @application when created. * * Returns: the application flags */ MxApplicationFlags mx_application_get_flags (MxApplication *application) { g_return_val_if_fail (MX_IS_APPLICATION (application), 0); return application->priv->flags; } /** * mx_application_add_window: * @application: The #MxApplication * @window: (transfer full): The #MxWindow to add to the application * * Adds a window to the list of windows associated with @application. If this * is the first window, it will be treated as the primary window and used for * startup notification. * * This function does not take a reference on @window. */ void mx_application_add_window (MxApplication *application, MxWindow *window) { static gboolean first_window = TRUE; MxApplicationPrivate *priv = application->priv; g_return_if_fail (MX_IS_APPLICATION (application)); g_return_if_fail (MX_IS_WINDOW (window)); priv->windows = g_list_prepend (application->priv->windows, window); g_signal_connect (window, "destroy", G_CALLBACK (mx_application_window_destroy_cb), application); /* Use the first window of the application for startup notification and * mirroring small-screen mode. */ if (first_window) { gboolean small_screen; #if defined (HAVE_X11) && defined (HAVE_STARTUP_NOTIFICATION) ClutterStage *stage; #endif first_window = FALSE; #if defined (HAVE_X11) && defined (HAVE_STARTUP_NOTIFICATION) stage = mx_window_get_clutter_stage (window); if (priv->sn_context) { if (CLUTTER_ACTOR_IS_MAPPED (stage)) mx_application_window_map_cb (CLUTTER_ACTOR (stage), NULL, application); else g_signal_connect (stage, "notify::mapped", G_CALLBACK (mx_application_window_map_cb), application); } #endif g_object_get (G_OBJECT (mx_settings_get_default ()), "small-screen", &small_screen, NULL); mx_window_set_small_screen (window, small_screen); } else { /* FIXME: Other windows in the application should probably be marked * as tool windows, or something special/clever like that. */ } } /** * mx_application_remove_window: * @application: an #MxApplication * @window: an #MxWindow * * Remove the specified window from the application. This will cause the window * to be unreferenced and destroyed unless another reference is held on it. */ void mx_application_remove_window (MxApplication *application, MxWindow *window) { GList *list; g_return_if_fail (MX_IS_APPLICATION (application)); g_return_if_fail (MX_IS_WINDOW (window)); list = g_list_find (application->priv->windows, window); if (!list) { g_warning ("Could not remove window from application, the window was not" " found in the application's window list"); return; } g_object_unref (G_OBJECT (list->data)); application->priv->windows = g_list_delete_link (application->priv->windows, list); } /** * mx_application_get_windows: * @application: an #MxApplication * * Retrieves all windows added to @application. * * Return value: (element-type MxWindow) (transfer none): a list * of #MxWindows. The returned list is owned by * @application and must not be altered. */ const GList* mx_application_get_windows (MxApplication *application) { g_return_val_if_fail (MX_IS_APPLICATION (application), NULL); return application->priv->windows; } /** * mx_application_create_window: * @application: The #MxApplication * * Creates a window and associates it with the application. * * Return value: (transfer none): An #MxWindow. */ MxWindow * mx_application_create_window (MxApplication *application) { g_return_val_if_fail (MX_IS_APPLICATION (application), NULL); return MX_APPLICATION_GET_CLASS (application)->create_window (application); } #ifdef HAVE_DBUS /* The following function/define are derived from generated code from * dbus-binding-tool. */ #define g_marshal_value_peek_pointer(v) (v)->data[0].v_pointer static void dbus_glib_marshal_mx_application_action (GClosure *closure, GValue *return_value, guint n_param_values, const GValue *param_values, gpointer invocation_hint, gpointer marshal_data) { GCClosure *cc = (GCClosure*) closure; MxAction *action; GVariant *variant = NULL; g_return_if_fail (return_value != NULL); action = (MxAction *)(marshal_data ? marshal_data : cc->callback); /* collect the action parameter */ if (n_param_values == 3) { GArray *array; array = g_value_get_boxed (¶m_values[1]); variant = g_variant_new_from_data (g_action_get_parameter_type (G_ACTION (action)), array->data, array->len, FALSE, NULL, NULL); } if (variant) g_action_activate (G_ACTION (action), variant); else g_signal_emit_by_name (action, "activated", NULL); g_value_set_boolean (return_value, TRUE); } /* The following marshaller was generated with dbus-binding-tool */ static void dbus_glib_marshal_mx_application_BOOLEAN__POINTER_POINTER ( GClosure *closure, GValue *return_value, guint n_param_values, const GValue *param_values, gpointer invocation_hint, gpointer marshal_data) { typedef gboolean (*GMarshalFunc_BOOLEAN__POINTER_POINTER)(gpointer data1, gpointer arg_1, gpointer arg_2, gpointer data2); register GMarshalFunc_BOOLEAN__POINTER_POINTER callback; register GCClosure *cc = (GCClosure*) closure; register gpointer data1, data2; gboolean v_return; g_return_if_fail (return_value != NULL); g_return_if_fail (n_param_values == 3); if (G_CCLOSURE_SWAP_DATA (closure)) { data1 = closure->data; data2 = g_value_peek_pointer (param_values + 0); } else { data1 = g_value_peek_pointer (param_values + 0); data2 = closure->data; } callback = (GMarshalFunc_BOOLEAN__POINTER_POINTER) (marshal_data ? marshal_data : cc->callback); v_return = callback (data1, g_marshal_value_peek_pointer (param_values + 1), g_marshal_value_peek_pointer (param_values + 2), data2); g_value_set_boolean (return_value, v_return); } static gboolean mx_application_get_actions_cb (MxApplication *application, GPtrArray **actions, GError **error) { GHashTableIter iter; gpointer key, value; MxApplicationPrivate *priv = application->priv; GValue string_value = { 0 }; GValue bool_value = { 0 }; *actions = g_ptr_array_new (); g_value_init (&string_value, G_TYPE_STRING); g_value_init (&bool_value, G_TYPE_BOOLEAN); g_hash_table_iter_init (&iter, priv->actions); while (g_hash_table_iter_next (&iter, &key, &value)) { const gchar *name, *display_name; MxAction *action = value; GValueArray *value_array = g_value_array_new (3); name = mx_action_get_name (action); display_name = mx_action_get_display_name (action); if (!name) name = ""; if (!display_name) display_name = name; g_value_set_string (&string_value, name); g_value_array_append (value_array, &string_value); g_value_set_string (&string_value, display_name); g_value_array_append (value_array, &string_value); g_value_set_boolean (&bool_value, mx_action_get_active (action)); g_value_array_append (value_array, &bool_value); g_ptr_array_add (*actions, value_array); } g_value_unset (&string_value); g_value_unset (&bool_value); return TRUE; } #endif static void mx_application_register_actions (MxApplication *application) { #ifdef HAVE_DBUS GString *data; GHashTableIter iter; DBusGObjectInfo *info; gint i, n_methods, offset; DBusGMethodInfo *method_info; MxApplicationPrivate *priv = application->priv; const gchar get_actions_data[] = "\0GetActions\0S\0actions\0O\0F\0N\0a(ssb)\0"; /* Unregister the object first as we'll be messing with the * DBusGObjectInfo that backs the object on the bus. */ mx_application_register_dbus (application, TRUE); info = &priv->object_info; n_methods = g_hash_table_size (priv->actions) + 1; /* Fill in the basic information of the object info struct */ info->format_version = 0; info->n_method_infos = n_methods; /* Generate the method info to map actions on the bus object */ g_free ((gpointer)info->method_infos); info->method_infos = g_new (DBusGMethodInfo, n_methods); /* First add the GetActions function */ data = g_string_new (priv->service_name); g_string_append_len (data, get_actions_data, sizeof (get_actions_data)); method_info = (DBusGMethodInfo *)&info->method_infos[0]; method_info->function = (GCallback)mx_application_get_actions_cb; method_info->marshaller = dbus_glib_marshal_mx_application_BOOLEAN__POINTER_POINTER; method_info->data_offset = 0; /* Now the methods for the actions */ g_hash_table_iter_init (&iter, priv->actions); for (i = 1, offset = data->len; i < n_methods; i++) { MxAction *action; gchar *name; method_info = (DBusGMethodInfo *)&info->method_infos[i]; /* It shouldn't be possible for this to fail, unless there's * memory corruption between here and the call to hash_table_size * above. */ if (!g_hash_table_iter_next (&iter, (gpointer *)(&name), (gpointer *)(&action))) g_error ("Action hash-table size mismatch"); /* Get a safe name to put on the bus */ name = mx_application_get_safe_name (name); /* Generate introspection data */ g_string_append (data, priv->service_name); /* Iface */ g_string_append_c (data, '\0'); g_string_append (data, name); /* Name */ g_string_append_c (data, '\0'); g_string_append_c (data, 'S'); /* A/S (Synchronous) */ g_string_append_c (data, '\0'); if (g_action_get_parameter_type (G_ACTION (action))) { g_string_append (data, "action-parameter"); g_string_append_c (data, '\0'); g_string_append_c (data, 'I'); g_string_append_c (data, '\0'); g_string_append (data, "ay"); g_string_append_c (data, '\0'); } else { g_string_append_c (data, '\0'); } g_string_append_c (data, '\0'); g_free (name); /* Fill in method info */ method_info->function = (GCallback)action; method_info->marshaller = dbus_glib_marshal_mx_application_action; method_info->data_offset = offset; /* Update offset to point to the beginning of the next string */ offset = data->len; } g_free ((gpointer)info->data); info->data = data->str; g_string_free (data, FALSE); /* Generate signal info (currently just ActionsChanged) * NOTE: If this string changes location, bad things happen - * i.e. this shouldn't change */ if (!info->exported_signals) { data = g_string_new (priv->service_name); g_string_append_c (data, '\0'); g_string_append (data, "ActionsChanged"); g_string_append_c (data, '\0'); info->exported_signals = data->str; g_string_free (data, FALSE); } /* Generate property info (currently none) */ info->exported_properties = "\0"; /* Install info */ dbus_g_object_type_install_info (MX_TYPE_APPLICATION, info); mx_application_register_dbus (application, FALSE); #endif g_signal_emit (application, signals[ACTIONS_CHANGED], 0); } /** * mx_application_add_action: * @application: an #MxApplication * @action: an #MxAction * * Add an action to the application. */ void mx_application_add_action (MxApplication *application, MxAction *action) { MxApplicationPrivate *priv = application->priv; if (priv->is_proxy) { g_warning ("Can't add actions to remote applications"); return; } g_hash_table_insert (priv->actions, g_strdup (mx_action_get_name (action)), g_object_ref (action)); mx_application_register_actions (application); } /** * mx_application_remove_action: * @application: an #MxApplication * @name: name of the action to remove * * Remove the action with the specified name from the application. */ void mx_application_remove_action (MxApplication *application, const gchar *name) { MxApplicationPrivate *priv = application->priv; if (priv->is_proxy) { g_warning ("Can't remove actions on remote applications"); return; } g_hash_table_remove (priv->actions, name); mx_application_register_actions (application); } #ifdef HAVE_DBUS static DBusGProxy * mx_application_get_dbus_proxy (MxApplication *application) { gchar *path_from_name; DBusGConnection *bus; DBusGProxy *proxy; GError *error = NULL; MxApplicationPrivate *priv = application->priv; if (!(bus = dbus_g_bus_get (DBUS_BUS_SESSION, &error))) { g_warning (G_STRLOC "%s", error->message); g_error_free (error); return NULL; } path_from_name = mx_application_get_service_path (application); proxy = dbus_g_proxy_new_for_name (bus, priv->service_name, path_from_name, priv->service_name); g_free (path_from_name); dbus_g_connection_unref (bus); return proxy; } static void mx_application_proxy_action_cb (MxAction *action, MxApplication *application) { mx_application_invoke_action (application, mx_action_get_name (action)); } #endif /** * mx_application_get_actions: * @application: an #MxApplication * * Retrieves all actions registered on @application. * * Return value: (element-type Mx.Action) (transfer container): a list * of #MxActions. Use g_list_free() on the returned list * when done. */ GList * mx_application_get_actions (MxApplication *application) { MxApplicationPrivate *priv = application->priv; if (priv->is_proxy) { #ifdef HAVE_DBUS /* Refresh our list of actions if necessary */ if (!priv->actions_valid) { DBusGProxy *proxy = mx_application_get_dbus_proxy (application); if (proxy) { GType array_type, struct_type; GPtrArray *actions; GError *error = NULL; /* All this code is just for calling a dbus method with the * signiature 'a(ssb)' (the GetActions method) and creating * corresponding MxAction objects. */ struct_type = dbus_g_type_get_struct ("GValueArray", G_TYPE_STRING, G_TYPE_STRING, G_TYPE_BOOLEAN, G_TYPE_INVALID); array_type = dbus_g_type_get_collection ("GPtrArray", struct_type); if (!dbus_g_proxy_call (proxy, "GetActions", &error, G_TYPE_INVALID, array_type, &actions, G_TYPE_INVALID)) { g_warning (G_STRLOC "%s", error->message); g_error_free (error); } else { guint i; g_hash_table_remove_all (priv->actions); for (i = 0; i < actions->len; i++) { GValue *name, *display_name, *active; MxAction *action; GValueArray *value_array = g_ptr_array_index (actions, i); name = g_value_array_get_nth (value_array, 0); display_name = g_value_array_get_nth (value_array, 1); active = g_value_array_get_nth (value_array, 2); action = mx_action_new_full (g_value_get_string (name), g_value_get_string (display_name), G_CALLBACK ( mx_application_proxy_action_cb), application); mx_action_set_active (action, g_value_get_boolean (active)); g_hash_table_insert (priv->actions, g_value_dup_string (name), action); g_value_array_free (value_array); } g_ptr_array_free (actions, TRUE); } g_object_unref (proxy); } } #endif } return g_hash_table_get_values (priv->actions); } /** * mx_application_invoke_action: * @application: an #MxApplication * @name: name of the action to invoke * * Run the named action for the application. */ void mx_application_invoke_action (MxApplication *application, const gchar *name) { mx_application_invoke_action_with_parameter (application, name, NULL); } /** * mx_application_invoke_action_with_parameter: * @application: an #MxApplication * @name: name of the action to invoke * @variant: parameter for the action * * Run the named action for the application, passing @variant as the parameter * for the action. * * Since: 1.4 */ void mx_application_invoke_action_with_parameter (MxApplication *application, const gchar *name, GVariant *variant) { MxApplicationPrivate *priv = application->priv; if (priv->is_proxy) { #ifdef HAVE_DBUS DBusGProxy *proxy = mx_application_get_dbus_proxy (application); GArray data = { 0, }; if (variant) { data.data = g_new0 (gchar, g_variant_get_size (variant)); g_variant_store (variant, data.data); data.len = g_variant_get_size (variant); } if (proxy) { GError *error = NULL; gchar *safe_name = mx_application_get_safe_name (name); if (!dbus_g_proxy_call (proxy, safe_name, &error, (variant) ? DBUS_TYPE_G_UCHAR_ARRAY : G_TYPE_INVALID, (variant) ? &data : G_TYPE_INVALID, G_TYPE_INVALID, G_TYPE_INVALID)) { g_warning (G_STRLOC "%s", error->message); g_error_free (error); } g_free (safe_name); g_object_unref (proxy); if (variant) { g_free (data.data); } } #endif } else { MxAction *action = g_hash_table_lookup (priv->actions, name); if (action) { if (variant) g_action_activate (G_ACTION (action), variant); else g_signal_emit_by_name (action, "activated", NULL); } } } /** * mx_application_is_running: * @application: an #MxApplication * * Query whether #MxApplication is running. This will also return #TRUE if the * given #MxApplication is single instance and there is an instance already * running. * * Returns: #TRUE if the application is running */ gboolean mx_application_is_running (MxApplication *application) { g_return_val_if_fail (MX_IS_APPLICATION (application), FALSE); return (application->priv->is_proxy || application->priv->is_running); } mx-1.4.7/mx/mx-application.h000066400000000000000000000107601201047117600156740ustar00rootroot00000000000000/* * mx-application: application class * * Copyright 2010 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * * Written by: Thomas Wood */ #if !defined(MX_H_INSIDE) && !defined(MX_COMPILATION) #error "Only can be included directly.h" #endif #ifndef _MX_APPLICATION_H #define _MX_APPLICATION_H #include #include #include G_BEGIN_DECLS #define MX_TYPE_APPLICATION mx_application_get_type() #define MX_APPLICATION(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ MX_TYPE_APPLICATION, MxApplication)) #define MX_APPLICATION_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), \ MX_TYPE_APPLICATION, MxApplicationClass)) #define MX_IS_APPLICATION(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \ MX_TYPE_APPLICATION)) #define MX_IS_APPLICATION_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE ((klass), \ MX_TYPE_APPLICATION)) #define MX_APPLICATION_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), \ MX_TYPE_APPLICATION, MxApplicationClass)) typedef struct _MxApplication MxApplication; typedef struct _MxApplicationClass MxApplicationClass; typedef struct _MxApplicationPrivate MxApplicationPrivate; /** * MxApplication: * * The contents of this structure are private and should only be accessed * through the public API. */ struct _MxApplication { GObject parent; MxApplicationPrivate *priv; }; struct _MxApplicationClass { GObjectClass parent_class; /* vfuncs */ MxWindow* (*create_window) (MxApplication *application); void (*raise) (MxApplication *application); /* signals */ void (*actions_changed) (MxApplication *app); /* padding for future expansion */ void (*_padding_0) (void); void (*_padding_1) (void); void (*_padding_2) (void); void (*_padding_3) (void); void (*_padding_4) (void); }; GType mx_application_get_type (void) G_GNUC_CONST; typedef enum { MX_APPLICATION_SINGLE_INSTANCE = 1, MX_APPLICATION_KEEP_ALIVE = 1 << 2, } MxApplicationFlags; MxApplication* mx_application_new (gint *argc, gchar ***argv, const gchar *name, MxApplicationFlags flags); void mx_application_run (MxApplication *application); void mx_application_quit (MxApplication *application); MxWindow * mx_application_create_window (MxApplication *application); MxApplicationFlags mx_application_get_flags (MxApplication *application); void mx_application_add_window (MxApplication *application, MxWindow *window); void mx_application_remove_window (MxApplication *application, MxWindow *window); const GList * mx_application_get_windows (MxApplication *application); void mx_application_add_action (MxApplication *application, MxAction *action); void mx_application_remove_action (MxApplication *application, const gchar *name); GList * mx_application_get_actions (MxApplication *application); void mx_application_invoke_action (MxApplication *application, const gchar *name); void mx_application_invoke_action_with_parameter (MxApplication *application, const gchar *name, GVariant *variant); gboolean mx_application_is_running (MxApplication *application); G_END_DECLS #endif /* _MX_APPLICATION_H */ mx-1.4.7/mx/mx-bin.c000066400000000000000000000436461201047117600141450ustar00rootroot00000000000000/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * mx-bin.c: Basic container actor * * Copyright (c) 2009 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * * Written by: Emmanuele Bassi * */ /** * SECTION:mx-bin * @short_description: a simple container with one actor. * * #MxBin is a simple abstract container capable of having only one * #ClutterActor as a child. #MxBin does not allocate the child itself, * therefore any subclasses are required to implement the * #ClutterActorClass.allocate function. * #mx_bin_allocate_child() can be used if no special allocation requirements * are needed. * * #MxFrame is a simple implementation of #MxBin that can be used as a single * actor container that implements alignment and padding. * */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include "mx-bin.h" #include "mx-enum-types.h" #include "mx-private.h" #include "mx-focusable.h" #define MX_BIN_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), MX_TYPE_BIN, MxBinPrivate)) struct _MxBinPrivate { ClutterActor *child; gboolean child_has_space; MxAlign x_align; MxAlign y_align; guint x_fill : 1; guint y_fill : 1; }; enum { PROP_0, PROP_CHILD, PROP_X_ALIGN, PROP_Y_ALIGN, PROP_X_FILL, PROP_Y_FILL }; static void clutter_container_iface_init (ClutterContainerIface *iface); static void mx_bin_focusable_iface_init (MxFocusableIface *iface); G_DEFINE_ABSTRACT_TYPE_WITH_CODE (MxBin, mx_bin, MX_TYPE_WIDGET, G_IMPLEMENT_INTERFACE (CLUTTER_TYPE_CONTAINER, clutter_container_iface_init) G_IMPLEMENT_INTERFACE (MX_TYPE_FOCUSABLE, mx_bin_focusable_iface_init)); void _mx_bin_get_align_factors (MxBin *bin, gdouble *x_align, gdouble *y_align) { MxBinPrivate *priv = bin->priv; gdouble factor; switch (priv->x_align) { case MX_ALIGN_START: factor = 0.0; break; case MX_ALIGN_MIDDLE: factor = 0.5; break; case MX_ALIGN_END: factor = 1.0; break; default: factor = 0.0; break; } if (x_align) *x_align = factor; switch (priv->y_align) { case MX_ALIGN_START: factor = 0.0; break; case MX_ALIGN_MIDDLE: factor = 0.5; break; case MX_ALIGN_END: factor = 1.0; break; default: factor = 0.0; break; } if (y_align) *y_align = factor; } static void mx_bin_add (ClutterContainer *container, ClutterActor *actor) { mx_bin_set_child (MX_BIN (container), actor); } static void mx_bin_remove (ClutterContainer *container, ClutterActor *actor) { MxBinPrivate *priv = MX_BIN (container)->priv; if (priv->child == actor) mx_bin_set_child (MX_BIN (container), NULL); } static void mx_bin_foreach (ClutterContainer *container, ClutterCallback callback, gpointer user_data) { MxBinPrivate *priv = MX_BIN (container)->priv; if (priv->child) callback (priv->child, user_data); } static void clutter_container_iface_init (ClutterContainerIface *iface) { iface->add = mx_bin_add; iface->remove = mx_bin_remove; iface->foreach = mx_bin_foreach; } static MxFocusable* mx_bin_accept_focus (MxFocusable *focusable, MxFocusHint hint) { MxBinPrivate *priv = MX_BIN (focusable)->priv; if (MX_IS_FOCUSABLE (priv->child)) return mx_focusable_accept_focus (MX_FOCUSABLE (priv->child), hint); else return NULL; } static void mx_bin_focusable_iface_init (MxFocusableIface *iface) { iface->accept_focus = mx_bin_accept_focus; } static void mx_bin_paint (ClutterActor *self) { MxBinPrivate *priv = MX_BIN (self)->priv; /* allow MxWidget to paint the background */ CLUTTER_ACTOR_CLASS (mx_bin_parent_class)->paint (self); /* then paint our child */ if (priv->child && priv->child_has_space) clutter_actor_paint (priv->child); } static void mx_bin_pick (ClutterActor *self, const ClutterColor *pick_color) { MxBinPrivate *priv = MX_BIN (self)->priv; /* get the default pick implementation */ CLUTTER_ACTOR_CLASS (mx_bin_parent_class)->pick (self, pick_color); if (priv->child) clutter_actor_paint (priv->child); } /** * mx_bin_allocate_child: * @bin: An #MxBin * @box: The allocation box of the parent actor. * @flags: #ClutterAllocationFlags, usually provided by the. * clutter_actor_allocate function. * * Allocates the child of an #MxBin using the width and height from @box. * This function should usually only be called by subclasses of #MxBin. * * This function can be used to allocate the child of an #MxBin if no special * allocation requirements are needed. It is similar to * #mx_allocate_align_fill, except that it reads the alignment, padding and * fill values from the #MxBin, and will call #clutter_actor_allocate on the * child. * */ void mx_bin_allocate_child (MxBin *bin, const ClutterActorBox *box, ClutterAllocationFlags flags) { MxBinPrivate *priv; g_return_if_fail (MX_IS_BIN (bin)); priv = bin->priv; if (priv->child) { MxPadding padding; ClutterActorBox allocation = { 0, }; mx_widget_get_padding (MX_WIDGET (bin), &padding); allocation.x1 = padding.left; allocation.x2 = box->x2 - box->x1 - padding.right; allocation.y1 = padding.top; allocation.y2 = box->y2 - box->y1 - padding.bottom; mx_allocate_align_fill (priv->child, &allocation, priv->x_align, priv->y_align, priv->x_fill, priv->y_fill); clutter_actor_allocate (priv->child, &allocation, flags); } } static void mx_bin_get_preferred_width (ClutterActor *self, gfloat for_height, gfloat *min_width_p, gfloat *natural_width_p) { MxBinPrivate *priv = MX_BIN (self)->priv; gfloat min_width, natural_width; gfloat available_height; MxPadding padding = { 0, }; mx_widget_get_padding (MX_WIDGET (self), &padding); available_height = for_height - padding.top - padding.bottom; min_width = natural_width = padding.left + padding.right; if (priv->child == NULL || !CLUTTER_ACTOR_IS_VISIBLE (priv->child)) { if (min_width_p) *min_width_p = min_width; if (natural_width_p) *natural_width_p = natural_width; } else { clutter_actor_get_preferred_width (priv->child, available_height, min_width_p, natural_width_p); if (min_width_p) *min_width_p += min_width; if (natural_width_p) *natural_width_p += natural_width; } } static void mx_bin_get_preferred_height (ClutterActor *self, gfloat for_width, gfloat *min_height_p, gfloat *natural_height_p) { MxBinPrivate *priv = MX_BIN (self)->priv; gfloat min_height, natural_height; gfloat available_width; MxPadding padding = { 0, }; mx_widget_get_padding (MX_WIDGET (self), &padding); available_width = for_width - padding.left - padding.right; min_height = natural_height = padding.top + padding.bottom; if (priv->child == NULL || !CLUTTER_ACTOR_IS_VISIBLE (priv->child)) { if (min_height_p) *min_height_p = min_height; if (natural_height_p) *natural_height_p = natural_height; } else { clutter_actor_get_preferred_height (priv->child, available_width, min_height_p, natural_height_p); if (min_height_p) *min_height_p += min_height; if (natural_height_p) *natural_height_p += natural_height; } } static void mx_bin_dispose (GObject *gobject) { MxBinPrivate *priv = MX_BIN (gobject)->priv; if (priv->child) { clutter_actor_destroy (priv->child); priv->child = NULL; } G_OBJECT_CLASS (mx_bin_parent_class)->dispose (gobject); } static void mx_bin_set_property (GObject *gobject, guint prop_id, const GValue *value, GParamSpec *pspec) { MxBin *bin = MX_BIN (gobject); switch (prop_id) { case PROP_CHILD: mx_bin_set_child (bin, g_value_get_object (value)); break; case PROP_X_ALIGN: mx_bin_set_alignment (bin, g_value_get_enum (value), bin->priv->y_align); break; case PROP_Y_ALIGN: mx_bin_set_alignment (bin, bin->priv->x_align, g_value_get_enum (value)); break; case PROP_X_FILL: mx_bin_set_fill (bin, g_value_get_boolean (value), bin->priv->y_fill); break; case PROP_Y_FILL: mx_bin_set_fill (bin, bin->priv->x_fill, g_value_get_boolean (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); } } static void mx_bin_get_property (GObject *gobject, guint prop_id, GValue *value, GParamSpec *pspec) { MxBinPrivate *priv = MX_BIN (gobject)->priv; switch (prop_id) { case PROP_CHILD: g_value_set_object (value, priv->child); break; case PROP_X_FILL: g_value_set_boolean (value, priv->x_fill); break; case PROP_Y_FILL: g_value_set_boolean (value, priv->y_fill); break; case PROP_X_ALIGN: g_value_set_enum (value, priv->x_align); break; case PROP_Y_ALIGN: g_value_set_enum (value, priv->y_align); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); } } static void mx_bin_class_init (MxBinClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass); GParamSpec *pspec; g_type_class_add_private (klass, sizeof (MxBinPrivate)); gobject_class->set_property = mx_bin_set_property; gobject_class->get_property = mx_bin_get_property; gobject_class->dispose = mx_bin_dispose; actor_class->get_preferred_width = mx_bin_get_preferred_width; actor_class->get_preferred_height = mx_bin_get_preferred_height; actor_class->paint = mx_bin_paint; actor_class->pick = mx_bin_pick; /** * MxBin:child: * * The child #ClutterActor of the #MxBin container. */ pspec = g_param_spec_object ("child", "Child", "The child of the Bin", CLUTTER_TYPE_ACTOR, MX_PARAM_READWRITE); g_object_class_install_property (gobject_class, PROP_CHILD, pspec); /** * MxBin:x-align: * * The horizontal alignment of the #MxBin child. */ pspec = g_param_spec_enum ("x-align", "X Align", "The horizontal alignment", MX_TYPE_ALIGN, MX_ALIGN_MIDDLE, MX_PARAM_READWRITE); g_object_class_install_property (gobject_class, PROP_X_ALIGN, pspec); /** * MxBin:y-align: * * The vertical alignment of the #MxBin child. */ pspec = g_param_spec_enum ("y-align", "Y Align", "The vertical alignment", MX_TYPE_ALIGN, MX_ALIGN_MIDDLE, MX_PARAM_READWRITE); g_object_class_install_property (gobject_class, PROP_Y_ALIGN, pspec); /** * MxBin:x-fill: * * Whether the child should fill the horizontal allocation */ pspec = g_param_spec_boolean ("x-fill", "X Fill", "Whether the child should fill the " "horizontal allocation", FALSE, MX_PARAM_READWRITE); g_object_class_install_property (gobject_class, PROP_X_FILL, pspec); /** * MxBin:y-fill: * * Whether the child should fill the vertical allocation */ pspec = g_param_spec_boolean ("y-fill", "Y Fill", "Whether the child should fill the " "vertical allocation", FALSE, MX_PARAM_READWRITE); g_object_class_install_property (gobject_class, PROP_Y_FILL, pspec); } static void mx_bin_init (MxBin *bin) { bin->priv = MX_BIN_GET_PRIVATE (bin); bin->priv->x_align = MX_ALIGN_MIDDLE; bin->priv->y_align = MX_ALIGN_MIDDLE; bin->priv->child_has_space = TRUE; } /** * mx_bin_set_child: * @bin: a #MxBin * @child: a #ClutterActor, or %NULL * * Sets @child as the child of @bin. * * If @bin already has a child, the previous child is removed. */ void mx_bin_set_child (MxBin *bin, ClutterActor *child) { MxBinPrivate *priv; g_return_if_fail (MX_IS_BIN (bin)); g_return_if_fail (child == NULL || CLUTTER_IS_ACTOR (child)); priv = bin->priv; if (priv->child == child) return; if (priv->child) { ClutterActor *old_child = priv->child; g_object_ref (old_child); priv->child = NULL; clutter_actor_unparent (old_child); g_signal_emit_by_name (bin, "actor-removed", old_child); g_object_unref (old_child); } if (child) { priv->child = child; clutter_actor_set_parent (child, CLUTTER_ACTOR (bin)); g_signal_emit_by_name (bin, "actor-added", priv->child); } clutter_actor_queue_relayout (CLUTTER_ACTOR (bin)); g_object_notify (G_OBJECT (bin), "child"); } /** * mx_bin_get_child: * @bin: a #MxBin * * Retrieves a pointer to the child of @bin. * * Return value: (transfer none): a #ClutterActor, or %NULL */ ClutterActor * mx_bin_get_child (MxBin *bin) { g_return_val_if_fail (MX_IS_BIN (bin), NULL); return bin->priv->child; } /** * mx_bin_set_alignment: * @bin: a #MxBin * @x_align: horizontal alignment * @y_align: vertical alignment * * Sets the horizontal and vertical alignment of the child * inside a #MxBin. */ void mx_bin_set_alignment (MxBin *bin, MxAlign x_align, MxAlign y_align) { MxBinPrivate *priv; gboolean changed = FALSE; g_return_if_fail (MX_IS_BIN (bin)); priv = bin->priv; g_object_freeze_notify (G_OBJECT (bin)); if (priv->x_align != x_align) { priv->x_align = x_align; g_object_notify (G_OBJECT (bin), "x-align"); changed = TRUE; } if (priv->y_align != y_align) { priv->y_align = y_align; g_object_notify (G_OBJECT (bin), "y-align"); changed = TRUE; } if (changed) clutter_actor_queue_relayout (CLUTTER_ACTOR (bin)); g_object_thaw_notify (G_OBJECT (bin)); } /** * mx_bin_get_alignment: * @bin: a #MxBin * @x_align: return location for the horizontal alignment, or %NULL * @y_align: return location for the vertical alignment, or %NULL * * Retrieves the horizontal and vertical alignment of the child * inside a #MxBin, as set by mx_bin_set_alignment(). */ void mx_bin_get_alignment (MxBin *bin, MxAlign *x_align, MxAlign *y_align) { MxBinPrivate *priv; g_return_if_fail (MX_IS_BIN (bin)); priv = bin->priv; if (x_align) *x_align = priv->x_align; if (y_align) *y_align = priv->y_align; } /** * mx_bin_set_fill: * @bin: a #MxBin * @x_fill: %TRUE if the child should fill horizontally the @bin * @y_fill: %TRUE if the child should fill vertically the @bin * * Sets whether the child of @bin should fill out the horizontal * and/or vertical allocation of the parent */ void mx_bin_set_fill (MxBin *bin, gboolean x_fill, gboolean y_fill) { MxBinPrivate *priv; gboolean changed = FALSE; g_return_if_fail (MX_IS_BIN (bin)); priv = bin->priv; g_object_freeze_notify (G_OBJECT (bin)); if (priv->x_fill != x_fill) { priv->x_fill = x_fill; changed = TRUE; g_object_notify (G_OBJECT (bin), "x-fill"); } if (priv->y_fill != y_fill) { priv->y_fill = y_fill; changed = TRUE; g_object_notify (G_OBJECT (bin), "y-fill"); } if (changed) clutter_actor_queue_relayout (CLUTTER_ACTOR (bin)); g_object_thaw_notify (G_OBJECT (bin)); } /** * mx_bin_get_fill: * @bin: a #MxBin * @x_fill: (out): return location for the horizontal fill, or %NULL * @y_fill: (out): return location for the vertical fill, or %NULL * * Retrieves the horizontal and vertical fill settings */ void mx_bin_get_fill (MxBin *bin, gboolean *x_fill, gboolean *y_fill) { g_return_if_fail (MX_IS_BIN (bin)); if (x_fill) *x_fill = bin->priv->x_fill; if (y_fill) *y_fill = bin->priv->y_fill; } mx-1.4.7/mx/mx-bin.h000066400000000000000000000070671201047117600141470ustar00rootroot00000000000000/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * mx-bin.h: Basic container actor * * Copyright 2009, 2008 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * Boston, MA 02111-1307, USA. * * Written by: Emmanuele Bassi * */ #if !defined(MX_H_INSIDE) && !defined(MX_COMPILATION) #error "Only can be included directly.h" #endif #ifndef __MX_BIN_H__ #define __MX_BIN_H__ #include #include G_BEGIN_DECLS #define MX_TYPE_BIN (mx_bin_get_type ()) #define MX_BIN(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MX_TYPE_BIN, MxBin)) #define MX_IS_BIN(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MX_TYPE_BIN)) #define MX_BIN_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MX_TYPE_BIN, MxBinClass)) #define MX_IS_BIN_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MX_TYPE_BIN)) #define MX_BIN_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MX_TYPE_BIN, MxBinClass)) typedef struct _MxBin MxBin; typedef struct _MxBinPrivate MxBinPrivate; typedef struct _MxBinClass MxBinClass; /** * MxBin: * * The #MxBin struct contains only private data */ struct _MxBin { /*< private >*/ MxWidget parent_instance; MxBinPrivate *priv; }; /** * MxBinClass: * * The #MxBinClass struct contains only private data */ struct _MxBinClass { /*< private >*/ MxWidgetClass parent_class; /* padding for future expansion */ void (*_padding_0) (void); void (*_padding_1) (void); void (*_padding_2) (void); void (*_padding_3) (void); void (*_padding_4) (void); }; GType mx_bin_get_type (void) G_GNUC_CONST; void mx_bin_allocate_child (MxBin *bin, const ClutterActorBox *box, ClutterAllocationFlags flags); void mx_bin_set_child (MxBin *bin, ClutterActor *child); ClutterActor *mx_bin_get_child (MxBin *bin); void mx_bin_set_alignment (MxBin *bin, MxAlign x_align, MxAlign y_align); void mx_bin_get_alignment (MxBin *bin, MxAlign *x_align, MxAlign *y_align); void mx_bin_set_fill (MxBin *bin, gboolean x_fill, gboolean y_fill); void mx_bin_get_fill (MxBin *bin, gboolean *x_fill, gboolean *y_fill); G_END_DECLS #endif /* __MX_BIN_H__ */ mx-1.4.7/mx/mx-box-layout-child.c000066400000000000000000000300161201047117600165440ustar00rootroot00000000000000/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * mx-box-layout-child.c: box layout child actor * * Copyright 2009 Intel Corporation * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * * Written by: Thomas Wood */ /** * SECTION:mx-box-layout-child * @short_description: meta data associated with a #MxBoxLayout child. * * #MxBoxLayoutChild is a #ClutterChildMeta implementation that stores the * child properties for children inside a #MxBoxLayout. */ #include "mx-box-layout-child.h" #include "mx-private.h" G_DEFINE_TYPE (MxBoxLayoutChild, mx_box_layout_child, CLUTTER_TYPE_CHILD_META) #define BOX_LAYOUT_CHILD_PRIVATE(o) \ (G_TYPE_INSTANCE_GET_PRIVATE ((o), MX_TYPE_BOX_LAYOUT_CHILD, MxBoxLayoutChildPrivate)) enum { PROP_0, PROP_EXPAND, PROP_X_FILL, PROP_Y_FILL, PROP_X_ALIGN, PROP_Y_ALIGN }; static void mx_box_layout_child_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { MxBoxLayoutChild *child = MX_BOX_LAYOUT_CHILD (object); switch (property_id) { case PROP_EXPAND: g_value_set_boolean (value, child->expand); break; case PROP_X_FILL: g_value_set_boolean (value, child->x_fill); break; case PROP_Y_FILL: g_value_set_boolean (value, child->y_fill); break; case PROP_X_ALIGN: g_value_set_enum (value, child->x_align); break; case PROP_Y_ALIGN: g_value_set_enum (value, child->y_align); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } } static void mx_box_layout_child_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { MxBoxLayoutChild *child = MX_BOX_LAYOUT_CHILD (object); MxBoxLayout *box = MX_BOX_LAYOUT (CLUTTER_CHILD_META (object)->container); _mx_box_layout_start_animation (box); switch (property_id) { case PROP_EXPAND: child->expand = g_value_get_boolean (value); break; case PROP_X_FILL: child->x_fill = g_value_get_boolean (value); break; case PROP_Y_FILL: child->y_fill = g_value_get_boolean (value); break; case PROP_X_ALIGN: child->x_align = g_value_get_enum (value); break; case PROP_Y_ALIGN: child->y_align = g_value_get_enum (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } clutter_actor_queue_relayout ((ClutterActor*) box); } static void mx_box_layout_child_dispose (GObject *object) { G_OBJECT_CLASS (mx_box_layout_child_parent_class)->dispose (object); } static void mx_box_layout_child_finalize (GObject *object) { G_OBJECT_CLASS (mx_box_layout_child_parent_class)->finalize (object); } static void mx_box_layout_child_class_init (MxBoxLayoutChildClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); GParamSpec *pspec; object_class->get_property = mx_box_layout_child_get_property; object_class->set_property = mx_box_layout_child_set_property; object_class->dispose = mx_box_layout_child_dispose; object_class->finalize = mx_box_layout_child_finalize; pspec = g_param_spec_boolean ("expand", "Expand", "Allocate the child extra space", FALSE, MX_PARAM_READWRITE); g_object_class_install_property (object_class, PROP_EXPAND, pspec); pspec = g_param_spec_boolean ("x-fill", "x-fill", "Whether the child should receive priority " "when the container is allocating spare space " "on the horizontal axis", TRUE, MX_PARAM_READWRITE); g_object_class_install_property (object_class, PROP_X_FILL, pspec); pspec = g_param_spec_boolean ("y-fill", "y-fill", "Whether the child should receive priority " "when the container is allocating spare space " "on the vertical axis", TRUE, MX_PARAM_READWRITE); g_object_class_install_property (object_class, PROP_Y_FILL, pspec); pspec = g_param_spec_enum ("x-align", "X Alignment", "X alignment of the widget within the cell", MX_TYPE_ALIGN, MX_ALIGN_MIDDLE, MX_PARAM_READWRITE); g_object_class_install_property (object_class, PROP_X_ALIGN, pspec); pspec = g_param_spec_enum ("y-align", "Y Alignment", "Y alignment of the widget within the cell", MX_TYPE_ALIGN, MX_ALIGN_MIDDLE, MX_PARAM_READWRITE); g_object_class_install_property (object_class, PROP_Y_ALIGN, pspec); } static void mx_box_layout_child_init (MxBoxLayoutChild *self) { self->expand = FALSE; self->x_fill = TRUE; self->y_fill = TRUE; self->x_align = MX_ALIGN_MIDDLE; self->y_align = MX_ALIGN_MIDDLE; } static MxBoxLayoutChild * _get_child_meta (MxBoxLayout *layout, ClutterActor *child) { MxBoxLayoutChild *meta; meta = (MxBoxLayoutChild*) clutter_container_get_child_meta (CLUTTER_CONTAINER (layout), child); return meta; } /** * mx_box_layout_child_get_expand: * @box_layout: A #MxBoxLayout * @child: A #ClutterActor * * Get the value of the #MxBoxLayoutChild:expand property * * Returns: the current value of the "expand" property */ gboolean mx_box_layout_child_get_expand (MxBoxLayout *box_layout, ClutterActor *child) { MxBoxLayoutChild *meta; g_return_val_if_fail (MX_IS_BOX_LAYOUT (box_layout), FALSE); g_return_val_if_fail (CLUTTER_IS_ACTOR (child), FALSE); meta = _get_child_meta (box_layout, child); return meta->expand; } /** * mx_box_layout_child_set_expand: * @box_layout: A #MxBoxLayout * @child: A #ClutterActor * @expand: A #gboolean * * Set the value of the #MxBoxLayoutChild:expand property. * */ void mx_box_layout_child_set_expand (MxBoxLayout *box_layout, ClutterActor *child, gboolean expand) { MxBoxLayoutChild *meta; g_return_if_fail (MX_IS_BOX_LAYOUT (box_layout)); g_return_if_fail (CLUTTER_IS_ACTOR (child)); meta = _get_child_meta (box_layout, child); meta->expand = expand; clutter_actor_queue_relayout (child); } /** * mx_box_layout_child_get_x_fill: * @box_layout: A #MxBoxLayout * @child: A #ClutterActor * * Get the value of the #MxBoxLayoutChild:x-fill property. * * Returns: the current value of the "x-fill" property. */ gboolean mx_box_layout_child_get_x_fill (MxBoxLayout *box_layout, ClutterActor *child) { MxBoxLayoutChild *meta; g_return_val_if_fail (MX_IS_BOX_LAYOUT (box_layout), FALSE); g_return_val_if_fail (CLUTTER_IS_ACTOR (child), FALSE); meta = _get_child_meta (box_layout, child); return meta->x_fill; } /** * mx_box_layout_child_set_x_fill: * @box_layout: A #MxBoxLayout * @child: A #ClutterActor * @x_fill: A #gboolean * * Set the value of the #MxBoxLayoutChild:x-fill property. * */ void mx_box_layout_child_set_x_fill (MxBoxLayout *box_layout, ClutterActor *child, gboolean x_fill) { MxBoxLayoutChild *meta; g_return_if_fail (MX_IS_BOX_LAYOUT (box_layout)); g_return_if_fail (CLUTTER_IS_ACTOR (child)); meta = _get_child_meta (box_layout, child); meta->x_fill = x_fill; clutter_actor_queue_relayout (child); } /** * mx_box_layout_child_get_y_fill: * @box_layout: An #MxBoxLayout * @child: A #ClutterActor * * Get the value of the #MxBoxLayoutChild:y-fill property * * Returns: the current value of the "y-fill" property */ gboolean mx_box_layout_child_get_y_fill (MxBoxLayout *box_layout, ClutterActor *child) { MxBoxLayoutChild *meta; g_return_val_if_fail (MX_IS_BOX_LAYOUT (box_layout), FALSE); g_return_val_if_fail (CLUTTER_IS_ACTOR (child), FALSE); meta = _get_child_meta (box_layout, child); return meta->y_fill; } /** * mx_box_layout_child_set_y_fill: * @box_layout: An #MxBoxLayout * @child: A #ClutterActor * @y_fill: A #gboolean * * Set the value of the #MxBoxLayoutChild:y-fill property. * */ void mx_box_layout_child_set_y_fill (MxBoxLayout *box_layout, ClutterActor *child, gboolean y_fill) { MxBoxLayoutChild *meta; g_return_if_fail (MX_IS_BOX_LAYOUT (box_layout)); g_return_if_fail (CLUTTER_IS_ACTOR (child)); meta = _get_child_meta (box_layout, child); meta->y_fill = y_fill; clutter_actor_queue_relayout (child); } /** * mx_box_layout_child_get_x_align: * @box_layout: An #MxBoxLayout * @child: A #ClutterActor * * Get the value of the #MxBoxLayoutChild:x-align property * * Returns: the current value of the "x-align" property */ MxAlign mx_box_layout_child_get_x_align (MxBoxLayout *box_layout, ClutterActor *child) { MxBoxLayoutChild *meta; g_return_val_if_fail (MX_IS_BOX_LAYOUT (box_layout), MX_ALIGN_START); g_return_val_if_fail (CLUTTER_IS_ACTOR (child), MX_ALIGN_START); meta = _get_child_meta (box_layout, child); return meta->x_align; } /** * mx_box_layout_child_set_x_align: * @box_layout: A #MxBoxLayout * @child: A #ClutterActor * @x_align: An #MxAlign * * Set the value of the #MxBoxLayoutChild:x-align property. * */ void mx_box_layout_child_set_x_align (MxBoxLayout *box_layout, ClutterActor *child, MxAlign x_align) { MxBoxLayoutChild *meta; g_return_if_fail (MX_IS_BOX_LAYOUT (box_layout)); g_return_if_fail (CLUTTER_IS_ACTOR (child)); meta = _get_child_meta (box_layout, child); meta->x_align = x_align; clutter_actor_queue_relayout (child); } /** * mx_box_layout_child_get_y_align: * @box_layout: An #MxBoxLayout * @child: A #ClutterActor * * Get the value of the #MxBoxLayoutChild:y-align property. * * Returns: the current value of the "y-align" property. */ MxAlign mx_box_layout_child_get_y_align (MxBoxLayout *box_layout, ClutterActor *child) { MxBoxLayoutChild *meta; g_return_val_if_fail (MX_IS_BOX_LAYOUT (box_layout), MX_ALIGN_START); g_return_val_if_fail (CLUTTER_IS_ACTOR (child), MX_ALIGN_START); meta = _get_child_meta (box_layout, child); return meta->y_align; } /** * mx_box_layout_child_set_y_align: * @box_layout: An #MxBoxLayout * @child: A #ClutterActor * @y_align: An #MxAlign * * Set the value of the #MxBoxLayoutChild:y-align property. * */ void mx_box_layout_child_set_y_align (MxBoxLayout *box_layout, ClutterActor *child, MxAlign y_align) { MxBoxLayoutChild *meta; g_return_if_fail (MX_IS_BOX_LAYOUT (box_layout)); g_return_if_fail (CLUTTER_IS_ACTOR (child)); meta = _get_child_meta (box_layout, child); meta->y_align = y_align; clutter_actor_queue_relayout (child); } mx-1.4.7/mx/mx-box-layout-child.h000066400000000000000000000103701201047117600165520ustar00rootroot00000000000000/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * mx-box-layout-child.h: box layout child actor * * Copyright 2009 Intel Corporation * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * * Written by: Thomas Wood */ #if !defined(MX_H_INSIDE) && !defined(MX_COMPILATION) #error "Only can be included directly.h" #endif #ifndef _MX_BOX_LAYOUT_CHILD_H #define _MX_BOX_LAYOUT_CHILD_H #include #include "mx-enum-types.h" #include "mx-box-layout.h" G_BEGIN_DECLS #define MX_TYPE_BOX_LAYOUT_CHILD mx_box_layout_child_get_type() #define MX_BOX_LAYOUT_CHILD(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ MX_TYPE_BOX_LAYOUT_CHILD, MxBoxLayoutChild)) #define MX_BOX_LAYOUT_CHILD_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), \ MX_TYPE_BOX_LAYOUT_CHILD, MxBoxLayoutChildClass)) #define MX_IS_BOX_LAYOUT_CHILD(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \ MX_TYPE_BOX_LAYOUT_CHILD)) #define MX_IS_BOX_LAYOUT_CHILD_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE ((klass), \ MX_TYPE_BOX_LAYOUT_CHILD)) #define MX_BOX_LAYOUT_CHILD_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), \ MX_TYPE_BOX_LAYOUT_CHILD, MxBoxLayoutChildClass)) typedef struct _MxBoxLayoutChild MxBoxLayoutChild; typedef struct _MxBoxLayoutChildClass MxBoxLayoutChildClass; typedef struct _MxBoxLayoutChildPrivate MxBoxLayoutChildPrivate; /** * MxBoxLayoutChild: * * The contents of this structure are private and should only be accessed * through the public API. */ struct _MxBoxLayoutChild { /*< private >*/ ClutterChildMeta parent; gboolean expand; guint x_fill : 1; guint y_fill : 1; MxAlign x_align; MxAlign y_align; }; struct _MxBoxLayoutChildClass { ClutterChildMetaClass parent_class; /* padding for future expansion */ void (*_padding_0) (void); void (*_padding_1) (void); void (*_padding_2) (void); void (*_padding_3) (void); void (*_padding_4) (void); }; GType mx_box_layout_child_get_type (void); gboolean mx_box_layout_child_get_expand (MxBoxLayout *box_layout, ClutterActor *child); void mx_box_layout_child_set_expand (MxBoxLayout *box_layout, ClutterActor *child, gboolean expand); gboolean mx_box_layout_child_get_x_fill (MxBoxLayout *box_layout, ClutterActor *child); void mx_box_layout_child_set_x_fill (MxBoxLayout *box_layout, ClutterActor *child, gboolean x_fill); gboolean mx_box_layout_child_get_y_fill (MxBoxLayout *box_layout, ClutterActor *child); void mx_box_layout_child_set_y_fill (MxBoxLayout *box_layout, ClutterActor *child, gboolean y_fill); MxAlign mx_box_layout_child_get_x_align (MxBoxLayout *box_layout, ClutterActor *child); void mx_box_layout_child_set_x_align (MxBoxLayout *box_layout, ClutterActor *child, MxAlign x_align); MxAlign mx_box_layout_child_get_y_align (MxBoxLayout *box_layout, ClutterActor *child); void mx_box_layout_child_set_y_align (MxBoxLayout *box_layout, ClutterActor *child, MxAlign y_align); G_END_DECLS #endif /* _MX_BOX_LAYOUT_CHILD_H */ mx-1.4.7/mx/mx-box-layout.c000066400000000000000000001505021201047117600154660ustar00rootroot00000000000000/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * mx-box-layout.h: box layout actor * * Copyright 2009, 2010 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * * Written by: Thomas Wood * */ /** * SECTION:mx-box-layout * @short_description: a layout container arranging children in a single line * * The #MxBoxLayout arranges its children along a single line, where each * child can be allocated either its preferred size or larger if the expand * option is set. If the fill option is set, the actor will be allocated more * than its requested size. If the fill option is not set, but the expand option * is enabled, then the position of the actor within the available space can * be determined by the alignment child property. * *
* Box layout with horizontal flow * The image shows an #MxBoxLayout with the * #MxBoxLayout:orientation property set to * #MX_ORIENTATION_HORIZONTAL. * *
* *
* Box layout with vertical flow * The image shows an #MxBoxLayout with the * #MxBoxLayout:orientation property set to * #MX_ORIENTATION_VERTICAL. * *
*/ #include #include #include #include "mx-box-layout.h" #include "mx-private.h" #include "mx-scrollable.h" #include "mx-box-layout-child.h" #include "mx-focusable.h" static void mx_box_container_iface_init (ClutterContainerIface *iface); static void mx_box_scrollable_interface_init (MxScrollableIface *iface); static void mx_box_focusable_iface_init (MxFocusableIface *iface); static void mx_box_stylable_iface_init (MxStylableIface *iface); G_DEFINE_TYPE_WITH_CODE (MxBoxLayout, mx_box_layout, MX_TYPE_WIDGET, G_IMPLEMENT_INTERFACE (CLUTTER_TYPE_CONTAINER, mx_box_container_iface_init) G_IMPLEMENT_INTERFACE (MX_TYPE_SCROLLABLE, mx_box_scrollable_interface_init) G_IMPLEMENT_INTERFACE (MX_TYPE_FOCUSABLE, mx_box_focusable_iface_init) G_IMPLEMENT_INTERFACE (MX_TYPE_STYLABLE, mx_box_stylable_iface_init)); #define BOX_LAYOUT_PRIVATE(o) \ (G_TYPE_INSTANCE_GET_PRIVATE ((o), MX_TYPE_BOX_LAYOUT, MxBoxLayoutPrivate)) enum { PROP_0, PROP_ORIENTATION, PROP_SPACING, PROP_HADJUST, PROP_VADJUST, PROP_ENABLE_ANIMATIONS, PROP_SCROLL_TO_FOCUSED }; struct _MxBoxLayoutPrivate { GList *children; guint ignore_css_spacing : 1; /* Should we ignore spacing from the CSS because the application set it via set_spacing */ guint spacing; MxAdjustment *hadjustment; MxAdjustment *vadjustment; GHashTable *start_allocations; ClutterTimeline *timeline; ClutterAlpha *alpha; guint is_animating : 1; guint enable_animations : 1; guint scroll_to_focused : 1; MxOrientation orientation; MxFocusable *last_focus; }; void _mx_box_layout_finish_animation (MxBoxLayout *box); void _mx_box_layout_start_animation (MxBoxLayout *box) { MxBoxLayoutPrivate *priv = box->priv; if (priv->is_animating || !priv->enable_animations) return; if (!CLUTTER_ACTOR_IS_MAPPED (CLUTTER_ACTOR (box))) return; priv->is_animating = TRUE; priv->timeline = clutter_timeline_new (300); g_signal_connect_swapped (priv->timeline, "new-frame", G_CALLBACK (clutter_actor_queue_relayout), box); g_signal_connect_swapped (priv->timeline, "completed", G_CALLBACK (_mx_box_layout_finish_animation), box); priv->alpha = clutter_alpha_new_full (priv->timeline, CLUTTER_EASE_OUT_CUBIC); clutter_timeline_start (priv->timeline); } void _mx_box_layout_finish_animation (MxBoxLayout *box) { MxBoxLayoutPrivate *priv = box->priv; if (priv->timeline) { g_object_unref (priv->timeline); priv->timeline = NULL; } if (priv->alpha) { g_object_unref (priv->alpha); priv->alpha = NULL; } priv->is_animating = FALSE; } /* * MxScrollable Interface Implementation */ static void adjustment_value_notify_cb (MxAdjustment *adjustment, GParamSpec *pspec, MxBoxLayout *box) { clutter_actor_queue_redraw (CLUTTER_ACTOR (box)); } static void scrollable_set_adjustments (MxScrollable *scrollable, MxAdjustment *hadjustment, MxAdjustment *vadjustment) { MxBoxLayoutPrivate *priv = MX_BOX_LAYOUT (scrollable)->priv; if (hadjustment != priv->hadjustment) { if (priv->hadjustment) { g_signal_handlers_disconnect_by_func (priv->hadjustment, adjustment_value_notify_cb, scrollable); g_object_unref (priv->hadjustment); } if (hadjustment) { g_object_ref (hadjustment); g_signal_connect (hadjustment, "notify::value", G_CALLBACK (adjustment_value_notify_cb), scrollable); } priv->hadjustment = hadjustment; g_object_notify (G_OBJECT (scrollable), "horizontal-adjustment"); } if (vadjustment != priv->vadjustment) { if (priv->vadjustment) { g_signal_handlers_disconnect_by_func (priv->vadjustment, adjustment_value_notify_cb, scrollable); g_object_unref (priv->vadjustment); } if (vadjustment) { g_object_ref (vadjustment); g_signal_connect (vadjustment, "notify::value", G_CALLBACK (adjustment_value_notify_cb), scrollable); } priv->vadjustment = vadjustment; g_object_notify (G_OBJECT (scrollable), "vertical-adjustment"); } } static void scrollable_get_adjustments (MxScrollable *scrollable, MxAdjustment **hadjustment, MxAdjustment **vadjustment) { MxBoxLayoutPrivate *priv; priv = (MX_BOX_LAYOUT (scrollable))->priv; if (hadjustment) { if (priv->hadjustment) *hadjustment = priv->hadjustment; else { MxAdjustment *adjustment; /* create an initial adjustment. this is filled with correct values * as soon as allocate() is called */ adjustment = mx_adjustment_new (); scrollable_set_adjustments (scrollable, adjustment, priv->vadjustment); g_object_unref (adjustment); *hadjustment = adjustment; } } if (vadjustment) { if (priv->vadjustment) *vadjustment = priv->vadjustment; else { MxAdjustment *adjustment; /* create an initial adjustment. this is filled with correct values * as soon as allocate() is called */ adjustment = mx_adjustment_new (); scrollable_set_adjustments (scrollable, priv->hadjustment, adjustment); g_object_unref (adjustment); *vadjustment = adjustment; } } } static void mx_box_scrollable_interface_init (MxScrollableIface *iface) { iface->set_adjustments = scrollable_set_adjustments; iface->get_adjustments = scrollable_get_adjustments; } /* * ClutterContainer Implementation */ static void fade_in_actor (ClutterActor *actor) { clutter_actor_animate (actor, CLUTTER_LINEAR, 300, "opacity", 0xff, NULL); } static void mx_box_container_add_actor (ClutterContainer *container, ClutterActor *actor) { MxBoxLayoutPrivate *priv = MX_BOX_LAYOUT (container)->priv; clutter_actor_set_parent (actor, CLUTTER_ACTOR (container)); priv->children = g_list_append (priv->children, actor); if (priv->enable_animations) { _mx_box_layout_start_animation (MX_BOX_LAYOUT (container)); if (priv->timeline) { /* fade in the new actor when there is room */ clutter_actor_set_opacity (actor, 0); g_signal_connect_swapped (priv->timeline, "completed", G_CALLBACK (fade_in_actor), actor); } } g_signal_emit_by_name (container, "actor-added", actor); } static void mx_box_container_remove_actor (ClutterContainer *container, ClutterActor *actor) { MxBoxLayoutPrivate *priv = MX_BOX_LAYOUT (container)->priv; GList *item = NULL; item = g_list_find (priv->children, actor); if (item == NULL) { g_warning ("Actor of type '%s' is not a child of container of type '%s'", g_type_name (G_OBJECT_TYPE (actor)), g_type_name (G_OBJECT_TYPE (container))); return; } g_object_ref (actor); if ((ClutterActor *)priv->last_focus == actor) priv->last_focus = NULL; priv->children = g_list_delete_link (priv->children, item); clutter_actor_unparent (actor); if (priv->enable_animations) _mx_box_layout_start_animation (MX_BOX_LAYOUT (container)); else clutter_actor_queue_relayout ((ClutterActor *) container); g_signal_emit_by_name (container, "actor-removed", actor); g_object_unref (actor); } static void mx_box_container_foreach (ClutterContainer *container, ClutterCallback callback, gpointer callback_data) { MxBoxLayoutPrivate *priv = MX_BOX_LAYOUT (container)->priv; ClutterActor *child; GList *list; list = priv->children; while (list) { child = list->data; list = list->next; callback (child, callback_data); } } /* * Implementations for raise, lower and sort_by_depth_order are taken from * ClutterBox. */ static void mx_box_container_lower (ClutterContainer *container, ClutterActor *actor, ClutterActor *sibling) { MxBoxLayoutPrivate *priv = MX_BOX_LAYOUT (container)->priv; priv->children = g_list_remove (priv->children, actor); if (sibling == NULL) priv->children = g_list_prepend (priv->children, actor); else { gint index_ = g_list_index (priv->children, sibling); priv->children = g_list_insert (priv->children, actor, index_); } clutter_actor_queue_relayout (CLUTTER_ACTOR (container)); } static void mx_box_container_raise (ClutterContainer *container, ClutterActor *actor, ClutterActor *sibling) { MxBoxLayoutPrivate *priv = MX_BOX_LAYOUT (container)->priv; priv->children = g_list_remove (priv->children, actor); if (sibling == NULL) priv->children = g_list_append (priv->children, actor); else { gint index_ = g_list_index (priv->children, sibling) + 1; priv->children = g_list_insert (priv->children, actor, index_); } clutter_actor_queue_relayout (CLUTTER_ACTOR (container)); } static gint sort_by_depth (gconstpointer a, gconstpointer b) { gfloat depth_a = clutter_actor_get_depth ((ClutterActor *) a); gfloat depth_b = clutter_actor_get_depth ((ClutterActor *) b); if (depth_a < depth_b) return -1; if (depth_a > depth_b) return 1; return 0; } static void mx_box_container_sort_depth_order (ClutterContainer *container) { MxBoxLayoutPrivate *priv = MX_BOX_LAYOUT (container)->priv; priv->children = g_list_sort (priv->children, sort_by_depth); clutter_actor_queue_relayout (CLUTTER_ACTOR (container)); } static void mx_box_container_iface_init (ClutterContainerIface *iface) { iface->add = mx_box_container_add_actor; iface->remove = mx_box_container_remove_actor; iface->foreach = mx_box_container_foreach; iface->lower = mx_box_container_lower; iface->raise = mx_box_container_raise; iface->sort_depth_order = mx_box_container_sort_depth_order; iface->child_meta_type = MX_TYPE_BOX_LAYOUT_CHILD; } /* * focusable implementation */ static void update_adjustments (MxBoxLayout *self, MxFocusable *focusable) { MxBoxLayoutPrivate *priv = self->priv; gdouble value, new_value, page_size; ClutterActorBox box = { 0, }; clutter_actor_get_allocation_box (CLUTTER_ACTOR (focusable), &box); if (!priv->scroll_to_focused) return; if (priv->vadjustment) { mx_adjustment_get_values (priv->vadjustment, &value, NULL, NULL, NULL, NULL, &page_size); if (box.y1 < value) new_value = box.y1; else if (box.y2 > value + page_size) new_value = box.y2 - page_size; else new_value = value; mx_adjustment_interpolate (priv->vadjustment, new_value, 250, CLUTTER_EASE_OUT_CUBIC); } if (priv->hadjustment) { mx_adjustment_get_values (priv->hadjustment, &value, NULL, NULL, NULL, NULL, &page_size); if (box.x1 < value) new_value = box.x1; else if (box.x2 > value + page_size) new_value = box.x2 - page_size; else new_value = value; mx_adjustment_interpolate (priv->hadjustment, new_value, 250, CLUTTER_EASE_OUT_CUBIC); } } static MxFocusable* mx_box_layout_move_focus (MxFocusable *focusable, MxFocusDirection direction, MxFocusable *from) { MxBoxLayoutPrivate *priv = MX_BOX_LAYOUT (focusable)->priv; GList *l, *childlink; MxFocusHint hint; /* find the current focus */ childlink = g_list_find (priv->children, from); if (!childlink) return NULL; priv->last_focus = from; hint = mx_focus_hint_from_direction (direction); /* convert left/right and up/down into next/previous */ if (priv->orientation == MX_ORIENTATION_HORIZONTAL) { if (direction == MX_FOCUS_DIRECTION_LEFT) direction = MX_FOCUS_DIRECTION_PREVIOUS; else if (direction == MX_FOCUS_DIRECTION_RIGHT) direction = MX_FOCUS_DIRECTION_NEXT; } else { if (direction == MX_FOCUS_DIRECTION_UP) direction = MX_FOCUS_DIRECTION_PREVIOUS; else if (direction == MX_FOCUS_DIRECTION_DOWN) direction = MX_FOCUS_DIRECTION_NEXT; } /* find the next widget to focus */ if (direction == MX_FOCUS_DIRECTION_NEXT) { for (l = childlink->next; l; l = g_list_next (l)) { if (MX_IS_FOCUSABLE (l->data)) { MxFocusable *focused, *child; child = MX_FOCUSABLE (l->data); focused = mx_focusable_accept_focus (child, hint); if (focused) { update_adjustments (MX_BOX_LAYOUT (focusable), focused); return focused; } } } } else if (direction == MX_FOCUS_DIRECTION_PREVIOUS) { for (l = g_list_previous (childlink); l; l = g_list_previous (l)) { if (MX_IS_FOCUSABLE (l->data)) { MxFocusable *focused, *child; child = MX_FOCUSABLE (l->data); focused = mx_focusable_accept_focus (child, hint); if (focused) { update_adjustments (MX_BOX_LAYOUT (focusable), focused); return focused; } } } } return NULL; } static MxFocusable* mx_box_layout_accept_focus (MxFocusable *focusable, MxFocusHint hint) { MxBoxLayoutPrivate *priv = MX_BOX_LAYOUT (focusable)->priv; MxFocusable *return_focusable; MxFocusHint modified_hint; GList* list, *l; return_focusable = NULL; /* Transform the hint based on our orientation */ modified_hint = hint; if (priv->orientation == MX_ORIENTATION_HORIZONTAL) { if (hint == MX_FOCUS_HINT_FROM_RIGHT) modified_hint = MX_FOCUS_HINT_LAST; else if (hint == MX_FOCUS_HINT_FROM_LEFT) modified_hint = MX_FOCUS_HINT_FIRST; } else { if (hint == MX_FOCUS_HINT_FROM_BELOW) modified_hint = MX_FOCUS_HINT_LAST; else if (hint == MX_FOCUS_HINT_FROM_ABOVE) modified_hint = MX_FOCUS_HINT_FIRST; } /* find the first/last/prior focusable widget */ switch (modified_hint) { case MX_FOCUS_HINT_LAST: list = g_list_reverse (g_list_copy (priv->children)); break; default: case MX_FOCUS_HINT_PRIOR: if (priv->last_focus) { list = g_list_copy (g_list_find (priv->children, priv->last_focus)); if (list) break; } /* This intentionally runs into the next case */ case MX_FOCUS_HINT_FIRST: list = g_list_copy (priv->children); break; } for (l = list; l; l = g_list_next (l)) { if (MX_IS_FOCUSABLE (l->data)) { return_focusable = mx_focusable_accept_focus (MX_FOCUSABLE (l->data), hint); if (return_focusable) { update_adjustments (MX_BOX_LAYOUT (focusable), return_focusable); break; } } } g_list_free (list); return return_focusable; } static void mx_box_focusable_iface_init (MxFocusableIface *iface) { iface->move_focus = mx_box_layout_move_focus; iface->accept_focus = mx_box_layout_accept_focus; } /* Stylable inplementation */ static void mx_box_stylable_iface_init (MxStylableIface *iface) { static gboolean is_initialized = FALSE; if (G_UNLIKELY (!is_initialized)) { GParamSpec *pspec; is_initialized = TRUE; pspec = g_param_spec_uint ("x-mx-spacing", "Spacing", "The size of the spacing", 0, G_MAXUINT, 0, MX_PARAM_READWRITE); mx_stylable_iface_install_property (iface, MX_TYPE_BOX_LAYOUT, pspec); } } static void mx_box_layout_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { MxBoxLayoutPrivate *priv = MX_BOX_LAYOUT (object)->priv; MxAdjustment *adjustment; switch (property_id) { case PROP_ORIENTATION: g_value_set_enum (value, priv->orientation); break; case PROP_SPACING: g_value_set_uint (value, priv->spacing); break; case PROP_ENABLE_ANIMATIONS: g_value_set_boolean (value, priv->enable_animations); break; case PROP_SCROLL_TO_FOCUSED: g_value_set_boolean (value, priv->scroll_to_focused); break; case PROP_HADJUST: scrollable_get_adjustments (MX_SCROLLABLE (object), &adjustment, NULL); g_value_set_object (value, adjustment); break; case PROP_VADJUST: scrollable_get_adjustments (MX_SCROLLABLE (object), NULL, &adjustment); g_value_set_object (value, adjustment); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } } static void mx_box_layout_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { MxBoxLayout *box = MX_BOX_LAYOUT (object); switch (property_id) { case PROP_ORIENTATION: mx_box_layout_set_orientation (box, g_value_get_enum (value)); break; case PROP_SPACING: mx_box_layout_set_spacing (box, g_value_get_uint (value)); break; case PROP_ENABLE_ANIMATIONS: mx_box_layout_set_enable_animations (box, g_value_get_boolean (value)); break; case PROP_HADJUST: scrollable_set_adjustments (MX_SCROLLABLE (object), g_value_get_object (value), box->priv->vadjustment); break; case PROP_VADJUST: scrollable_set_adjustments (MX_SCROLLABLE (object), box->priv->hadjustment, g_value_get_object (value)); break; case PROP_SCROLL_TO_FOCUSED: mx_box_layout_set_scroll_to_focused (box, g_value_get_boolean (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } } static void mx_box_layout_dispose (GObject *object) { MxBoxLayoutPrivate *priv = MX_BOX_LAYOUT (object)->priv; /* cleanup any running animation */ _mx_box_layout_finish_animation (MX_BOX_LAYOUT (object)); /* destroy the children * clutter_actor_destroy() will call clutter_container_remove() which will * remove the children from the internal list */ while (priv->children) clutter_actor_destroy (CLUTTER_ACTOR (priv->children->data)); if (priv->hadjustment) { g_object_unref (priv->hadjustment); priv->hadjustment = NULL; } if (priv->vadjustment) { g_object_unref (priv->vadjustment); priv->vadjustment = NULL; } G_OBJECT_CLASS (mx_box_layout_parent_class)->dispose (object); } static void mx_box_layout_finalize (GObject *object) { MxBoxLayoutPrivate *priv = MX_BOX_LAYOUT (object)->priv; if (priv->start_allocations) { g_hash_table_destroy (priv->start_allocations); priv->start_allocations = NULL; } G_OBJECT_CLASS (mx_box_layout_parent_class)->finalize (object); } static void mx_box_layout_get_preferred_width (ClutterActor *actor, gfloat for_height, gfloat *min_width_p, gfloat *natural_width_p) { MxBoxLayoutPrivate *priv = MX_BOX_LAYOUT (actor)->priv; MxPadding padding = { 0, }; gint n_children = 0; GList *l; mx_widget_get_padding (MX_WIDGET (actor), &padding); if (min_width_p) *min_width_p = 0; if (natural_width_p) *natural_width_p = 0; if (for_height > 0) for_height = MAX (0, for_height - padding.top - padding.bottom); for (l = priv->children; l; l = g_list_next (l)) { gfloat child_min = 0, child_nat = 0; gfloat child_for_height; if (!CLUTTER_ACTOR_IS_VISIBLE ((ClutterActor*) l->data)) continue; n_children++; if (priv->orientation == MX_ORIENTATION_HORIZONTAL) child_for_height = for_height; else child_for_height = -1; clutter_actor_get_preferred_width ((ClutterActor*) l->data, child_for_height, &child_min, &child_nat); if (priv->orientation == MX_ORIENTATION_VERTICAL) { if (min_width_p) *min_width_p = MAX (child_min, *min_width_p); if (natural_width_p) *natural_width_p = MAX (child_nat, *natural_width_p); } else { if (min_width_p) *min_width_p += child_min; if (natural_width_p) *natural_width_p += child_nat; } } if (priv->orientation == MX_ORIENTATION_HORIZONTAL && n_children > 1) { if (min_width_p) *min_width_p += priv->spacing * (n_children - 1); if (natural_width_p) *natural_width_p += priv->spacing * (n_children - 1); } if (min_width_p) *min_width_p += padding.left + padding.right; if (natural_width_p) *natural_width_p += padding.left + padding.right; } static void mx_box_layout_get_preferred_height (ClutterActor *actor, gfloat for_width, gfloat *min_height_p, gfloat *natural_height_p) { MxBoxLayoutPrivate *priv = MX_BOX_LAYOUT (actor)->priv; MxPadding padding = { 0, }; gint n_children = 0; GList *l; mx_widget_get_padding (MX_WIDGET (actor), &padding); if (min_height_p) *min_height_p = 0; if (natural_height_p) *natural_height_p = 0; if (for_width > 0) for_width = MAX (0, for_width - padding.left - padding.right); for (l = priv->children; l; l = g_list_next (l)) { gfloat child_min = 0, child_nat = 0; gfloat child_for_width; if (!CLUTTER_ACTOR_IS_VISIBLE ((ClutterActor*) l->data)) continue; n_children++; if (priv->orientation == MX_ORIENTATION_VERTICAL) child_for_width = for_width; else child_for_width = -1; clutter_actor_get_preferred_height ((ClutterActor*) l->data, child_for_width, &child_min, &child_nat); if (priv->orientation == MX_ORIENTATION_HORIZONTAL) { if (min_height_p) *min_height_p = MAX (child_min, *min_height_p); if (natural_height_p) *natural_height_p = MAX (child_nat, *natural_height_p); } else { if (min_height_p) *min_height_p += child_min; if (natural_height_p) *natural_height_p += child_nat; } } if (priv->orientation == MX_ORIENTATION_VERTICAL && n_children > 1) { if (min_height_p) *min_height_p += priv->spacing * (n_children - 1); if (natural_height_p) *natural_height_p += priv->spacing * (n_children - 1); } if (min_height_p) *min_height_p += padding.top + padding.bottom; if (natural_height_p) *natural_height_p += padding.top + padding.bottom; } static void mx_box_layout_allocate (ClutterActor *actor, const ClutterActorBox *box, ClutterAllocationFlags flags) { MxBoxLayoutPrivate *priv = MX_BOX_LAYOUT (actor)->priv; gfloat avail_width, avail_height, pref_width, pref_height; MxPadding padding = { 0, }; gboolean allocate_pref; gfloat extra_space = 0; gfloat position = 0; GList *l; gint n_expand_children, n_children; CLUTTER_ACTOR_CLASS (mx_box_layout_parent_class)->allocate (actor, box, flags); if (priv->children == NULL) return; /* count the number of children with expand set to TRUE and the * amount of visible children. */ n_children = n_expand_children = 0; for (l = priv->children; l; l = l->next) { ClutterActor *child; child = (ClutterActor*) l->data; if (CLUTTER_ACTOR_IS_VISIBLE (child)) { MxBoxLayoutChild *meta; meta = (MxBoxLayoutChild*) clutter_container_get_child_meta ((ClutterContainer *) actor, child); n_children++; if (meta->expand) n_expand_children++; } } /* We have no visible children, so bail out */ if (n_children == 0) return; mx_widget_get_padding (MX_WIDGET (actor), &padding); /* do not take off padding just yet, as we are comparing this to the values * from get_preferred_height/width which will include padding */ avail_width = box->x2 - box->x1; avail_height = box->y2 - box->y1; if (priv->orientation == MX_ORIENTATION_VERTICAL) { gfloat min_height; mx_box_layout_get_preferred_height (actor, avail_width, &min_height, &pref_height); pref_width = avail_width; if (!priv->vadjustment && (pref_height > box->y2 - box->y1)) { /* allocated less than the preferred height and not scrolling */ allocate_pref = FALSE; extra_space = avail_height - min_height; } else allocate_pref = TRUE; } else { gfloat min_width; mx_box_layout_get_preferred_width (actor, avail_height, &min_width, &pref_width); pref_height = avail_height; if (!priv->hadjustment && (pref_width > box->x2 - box->x1)) { /* allocated less than the preferred width and not scrolling */ allocate_pref = FALSE; extra_space = avail_width - min_width; } else allocate_pref = TRUE; } /* remove the padding values from the available and preferred sizes so we * can use them for allocating the children */ avail_width -= padding.left + padding.right; avail_height -= padding.top + padding.bottom; pref_width -= padding.left + padding.right; pref_height -= padding.top + padding.bottom; /* update adjustments for scrolling */ if (priv->vadjustment) { gdouble step_inc, page_inc; /* Base the adjustment stepping on the size of the first child. * In the case where all your children are the same size, this * will probably provide the desired behaviour. */ if (priv->children && priv->orientation == MX_ORIENTATION_VERTICAL) { gfloat child_height; ClutterActor *first_child = (ClutterActor *)priv->children->data; clutter_actor_get_preferred_height (first_child, avail_width, NULL, &child_height); step_inc = child_height; page_inc = ((gint)(avail_height / step_inc)) * step_inc; } else { step_inc = avail_height / 6; page_inc = avail_height; } g_object_set (G_OBJECT (priv->vadjustment), "lower", 0.0, "upper", pref_height, "page-size", avail_height, "step-increment", step_inc, "page-increment", page_inc, NULL); } if (priv->hadjustment) { gdouble step_inc, page_inc; if (priv->children && priv->orientation == MX_ORIENTATION_HORIZONTAL) { gfloat child_width; ClutterActor *first_child = (ClutterActor *)priv->children->data; clutter_actor_get_preferred_width (first_child, avail_height, &child_width, NULL); step_inc = child_width; page_inc = ((gint)(avail_width / step_inc)) * step_inc; } else { step_inc = avail_width / 6; page_inc = avail_width; } g_object_set (G_OBJECT (priv->hadjustment), "lower", 0.0, "upper", pref_width, "page-size", avail_width, "step-increment", step_inc, "page-increment", page_inc, NULL); } /* We're allocating our preferred size or higher, so calculate * the extra space to give to expanded children. */ if (allocate_pref) { if (n_expand_children > 0) { if (priv->orientation == MX_ORIENTATION_VERTICAL) extra_space = (avail_height - pref_height) / n_expand_children; else extra_space = (avail_width - pref_width) / n_expand_children; /* don't shrink anything */ if (extra_space < 0) extra_space = 0; } } if (priv->orientation == MX_ORIENTATION_VERTICAL) position = padding.top; else position = padding.left; for (l = priv->children; l; l = l->next) { ClutterActor *child = (ClutterActor*) l->data; ClutterActorBox child_box, old_child_box; gfloat child_nat, child_min; MxBoxLayoutChild *meta; if (!CLUTTER_ACTOR_IS_VISIBLE (child)) continue; meta = (MxBoxLayoutChild*) clutter_container_get_child_meta ((ClutterContainer *) actor, child); if (priv->orientation == MX_ORIENTATION_VERTICAL) { clutter_actor_get_preferred_height (child, avail_width, &child_min, &child_nat); child_box.y1 = position; if (allocate_pref) { if (meta->expand) child_box.y2 = position + child_nat + (int)extra_space; else child_box.y2 = position + child_nat; } else { child_box.y2 = position + MIN (child_nat, child_min + (int)(extra_space / n_children)); n_children --; if (extra_space >= (child_box.y2 - child_box.y1 - child_min)) extra_space -= child_box.y2 - child_box.y1 - child_min; else extra_space = 0; } child_box.x1 = padding.left; child_box.x2 = avail_width + padding.left; } else { clutter_actor_get_preferred_width (child, avail_height, &child_min, &child_nat); child_box.x1 = position; if (allocate_pref) { if (meta->expand) child_box.x2 = position + child_nat + (int)extra_space; else child_box.x2 = position + child_nat; } else { child_box.x2 = position + MIN (child_nat, child_min + (int)(extra_space / n_children)); n_children --; if (extra_space >= (child_box.x2 - child_box.x1 - child_min)) extra_space -= child_box.x2 - child_box.x1 - child_min; else extra_space = 0; } child_box.y1 = padding.top; child_box.y2 = avail_height + padding.top; } /* Adjust the box for alignment/fill */ old_child_box = child_box; mx_allocate_align_fill (child, &child_box, meta->x_align, meta->y_align, meta->x_fill, meta->y_fill); if (priv->is_animating) { ClutterActorBox *start, *end, now; gdouble alpha; ClutterActorBox *copy = g_new (ClutterActorBox, 1); *copy = child_box; start = g_hash_table_lookup (priv->start_allocations, child); end = &child_box; alpha = clutter_alpha_get_alpha (priv->alpha); if (!start) { /* don't know where this actor was from (possibly recently * added), so just allocate the end co-ordinates */ clutter_actor_allocate (child, end, flags); goto next; } now.x1 = (int) (start->x1 + (end->x1 - start->x1) * alpha); now.x2 = (int) (start->x2 + (end->x2 - start->x2) * alpha); now.y1 = (int) (start->y1 + (end->y1 - start->y1) * alpha); now.y2 = (int) (start->y2 + (end->y2 - start->y2) * alpha); clutter_actor_allocate (child, &now, flags); } else { /* store the allocations in case an animation is needed soon */ ClutterActorBox *copy; /* update the value in the hash table */ if (priv->enable_animations) { copy = g_boxed_copy (CLUTTER_TYPE_ACTOR_BOX, &child_box); g_hash_table_insert (priv->start_allocations, child, copy); } else { copy = &child_box; } clutter_actor_allocate (child, copy, flags); } next: if (priv->orientation == MX_ORIENTATION_VERTICAL) position += (old_child_box.y2 - old_child_box.y1) + priv->spacing; else position += (old_child_box.x2 - old_child_box.x1) + priv->spacing; } } static void mx_box_layout_apply_transform (ClutterActor *a, CoglMatrix *m) { MxBoxLayoutPrivate *priv = MX_BOX_LAYOUT (a)->priv; gdouble x, y; CLUTTER_ACTOR_CLASS (mx_box_layout_parent_class)->apply_transform (a, m); if (priv->hadjustment) x = mx_adjustment_get_value (priv->hadjustment); else x = 0; if (priv->vadjustment) y = mx_adjustment_get_value (priv->vadjustment); else y = 0; cogl_matrix_translate (m, (int) -x, (int) -y, 0); } static gboolean mx_box_layout_get_paint_volume (ClutterActor *actor, ClutterPaintVolume *volume) { MxBoxLayoutPrivate *priv = MX_BOX_LAYOUT (actor)->priv; ClutterVertex vertex; if (!clutter_paint_volume_set_from_allocation (volume, actor)) return FALSE; clutter_paint_volume_get_origin (volume, &vertex); if (priv->hadjustment) vertex.x += mx_adjustment_get_value (priv->hadjustment); if (priv->vadjustment) vertex.y += mx_adjustment_get_value (priv->vadjustment); clutter_paint_volume_set_origin (volume, &vertex); return TRUE; } static void mx_box_layout_paint (ClutterActor *actor) { MxBoxLayoutPrivate *priv = MX_BOX_LAYOUT (actor)->priv; GList *l; gdouble x, y; ClutterActorBox child_b; ClutterActorBox box_b; CLUTTER_ACTOR_CLASS (mx_box_layout_parent_class)->paint (actor); if (priv->children == NULL) return; if (priv->hadjustment) x = mx_adjustment_get_value (priv->hadjustment); else x = 0; if (priv->vadjustment) y = mx_adjustment_get_value (priv->vadjustment); else y = 0; clutter_actor_get_allocation_box (actor, &box_b); box_b.x2 = (box_b.x2 - box_b.x1) + x; box_b.x1 = x; box_b.y2 = (box_b.y2 - box_b.y1) + y; box_b.y1 = y; for (l = priv->children; l; l = g_list_next (l)) { ClutterActor *child = (ClutterActor*) l->data; if (!CLUTTER_ACTOR_IS_VISIBLE (child)) continue; clutter_actor_get_allocation_box (child, &child_b); if ((child_b.x1 < box_b.x2) && (child_b.x2 > box_b.x1) && (child_b.y1 < box_b.y2) && (child_b.y2 > box_b.y1)) { clutter_actor_paint (child); } } } static void mx_box_layout_pick (ClutterActor *actor, const ClutterColor *color) { MxBoxLayoutPrivate *priv = MX_BOX_LAYOUT (actor)->priv; GList *l; gdouble x, y; ClutterActorBox child_b; ClutterActorBox box_b; CLUTTER_ACTOR_CLASS (mx_box_layout_parent_class)->pick (actor, color); if (priv->children == NULL) return; if (priv->hadjustment) x = mx_adjustment_get_value (priv->hadjustment); else x = 0; if (priv->vadjustment) y = mx_adjustment_get_value (priv->vadjustment); else y = 0; clutter_actor_get_allocation_box (actor, &box_b); box_b.x2 = (box_b.x2 - box_b.x1) + x; box_b.x1 = x; box_b.y2 = (box_b.y2 - box_b.y1) + y; box_b.y1 = y; for (l = priv->children; l; l = g_list_next (l)) { ClutterActor *child = (ClutterActor*) l->data; if (!CLUTTER_ACTOR_IS_VISIBLE (child)) continue; clutter_actor_get_allocation_box (child, &child_b); if ((child_b.x1 < box_b.x2) && (child_b.x2 > box_b.x1) && (child_b.y1 < box_b.y2) && (child_b.y2 > box_b.y1)) { clutter_actor_paint (child); } } } static void mx_box_layout_class_init (MxBoxLayoutClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass); GParamSpec *pspec; g_type_class_add_private (klass, sizeof (MxBoxLayoutPrivate)); object_class->get_property = mx_box_layout_get_property; object_class->set_property = mx_box_layout_set_property; object_class->dispose = mx_box_layout_dispose; object_class->finalize = mx_box_layout_finalize; actor_class->allocate = mx_box_layout_allocate; actor_class->get_preferred_width = mx_box_layout_get_preferred_width; actor_class->get_preferred_height = mx_box_layout_get_preferred_height; actor_class->apply_transform = mx_box_layout_apply_transform; actor_class->get_paint_volume = mx_box_layout_get_paint_volume; actor_class->paint = mx_box_layout_paint; actor_class->pick = mx_box_layout_pick; pspec = g_param_spec_enum ("orientation", "Orientation", "Orientation of the layout", MX_TYPE_ORIENTATION, MX_ORIENTATION_HORIZONTAL, MX_PARAM_READWRITE); g_object_class_install_property (object_class, PROP_ORIENTATION, pspec); pspec = g_param_spec_uint ("spacing", "Spacing", "Spacing between children", 0, G_MAXUINT, 0, MX_PARAM_READWRITE); g_object_class_install_property (object_class, PROP_SPACING, pspec); pspec = g_param_spec_boolean ("enable-animations", "Enable Animations", "Enable animations between certain property" " and child property changes", FALSE, MX_PARAM_READWRITE); g_object_class_install_property (object_class, PROP_ENABLE_ANIMATIONS, pspec); pspec = g_param_spec_boolean ("scroll-to-focused", "Scroll to focused", "Automatically scroll to the focused actor", TRUE, MX_PARAM_READWRITE); g_object_class_install_property (object_class, PROP_SCROLL_TO_FOCUSED, pspec); /* MxScrollable properties */ g_object_class_override_property (object_class, PROP_HADJUST, "horizontal-adjustment"); g_object_class_override_property (object_class, PROP_VADJUST, "vertical-adjustment"); } static void mx_box_layout_free_allocation (ClutterActorBox *box) { g_boxed_free (CLUTTER_TYPE_ACTOR_BOX, box); } static void mx_box_layout_style_changed (MxWidget *widget, gpointer userdata) { MxBoxLayout *layout = MX_BOX_LAYOUT (widget); MxBoxLayoutPrivate *priv = layout->priv; guint spacing; mx_stylable_get (MX_STYLABLE (widget), "x-mx-spacing", &spacing, NULL); if (!priv->ignore_css_spacing && (priv->spacing != spacing)) { priv->spacing = spacing; clutter_actor_queue_relayout (CLUTTER_ACTOR (widget)); } clutter_actor_queue_redraw (CLUTTER_ACTOR (widget)); } static void mx_box_layout_init (MxBoxLayout *self) { self->priv = BOX_LAYOUT_PRIVATE (self); self->priv->start_allocations = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) mx_box_layout_free_allocation); g_signal_connect (self, "style-changed", G_CALLBACK (mx_box_layout_style_changed), NULL); self->priv->scroll_to_focused = TRUE; } /** * mx_box_layout_new: * * Create a new #MxBoxLayout. * * Returns: a newly allocated #MxBoxLayout */ ClutterActor * mx_box_layout_new (void) { return g_object_new (MX_TYPE_BOX_LAYOUT, NULL); } /** * mx_box_layout_set_orientation: * @box: A #MxBoxLayout * @orientation: orientation value for the layout * * Set the orientation of the box layout. * */ void mx_box_layout_set_orientation (MxBoxLayout *box, MxOrientation orientation) { g_return_if_fail (MX_IS_BOX_LAYOUT (box)); if (box->priv->orientation != orientation) { box->priv->orientation = orientation; _mx_box_layout_start_animation (box); clutter_actor_queue_relayout (CLUTTER_ACTOR (box)); g_object_notify (G_OBJECT (box), "orientation"); } } /** * mx_box_layout_get_orientation: * @box: A #MxBoxLayout * * Get the value of the #MxBoxLayout:orientation property. * * Returns: the orientation of the layout */ MxOrientation mx_box_layout_get_orientation (MxBoxLayout *box) { g_return_val_if_fail (MX_IS_BOX_LAYOUT (box), FALSE); return box->priv->orientation; } /** * mx_box_layout_set_spacing: * @box: A #MxBoxLayout * @spacing: the spacing value * * Set the amount of spacing between children in pixels * */ void mx_box_layout_set_spacing (MxBoxLayout *box, guint spacing) { MxBoxLayoutPrivate *priv; g_return_if_fail (MX_IS_BOX_LAYOUT (box)); priv = box->priv; if (priv->spacing != spacing) { priv->spacing = spacing; priv->ignore_css_spacing = TRUE; clutter_actor_queue_relayout (CLUTTER_ACTOR (box)); g_object_notify (G_OBJECT (box), "spacing"); } } /** * mx_box_layout_get_spacing: * @box: A #MxBoxLayout * * Get the spacing between children in pixels * * Returns: the spacing value */ guint mx_box_layout_get_spacing (MxBoxLayout *box) { g_return_val_if_fail (MX_IS_BOX_LAYOUT (box), 0); return box->priv->spacing; } /** * mx_box_layout_set_enable_animations: * @box: A #MxBoxLayout * @enable_animations: #TRUE to enable animations * * Enable animations when certain properties change. * */ void mx_box_layout_set_enable_animations (MxBoxLayout *box, gboolean enable_animations) { g_return_if_fail (MX_IS_BOX_LAYOUT (box)); if (box->priv->enable_animations != enable_animations) { box->priv->enable_animations = enable_animations; clutter_actor_queue_relayout ((ClutterActor*) box); g_object_notify (G_OBJECT (box), "enable-animations"); } } /** * mx_box_layout_get_enable_animations: * @box: A #MxBoxLayout * * Get the value of the #MxBoxLayout:enable-animations property. * * Returns: #TRUE if animations enabled */ gboolean mx_box_layout_get_enable_animations (MxBoxLayout *box) { g_return_val_if_fail (MX_IS_BOX_LAYOUT (box), FALSE); return box->priv->enable_animations; } static inline void mx_box_layout_set_property_valist (MxBoxLayout *box, ClutterActor *actor, const gchar *first_property, va_list var_args) { ClutterContainer *container = CLUTTER_CONTAINER (box); ClutterChildMeta *meta; GObjectClass *klass; const gchar *pname; meta = clutter_container_get_child_meta (container, actor); g_assert (meta); klass = G_OBJECT_GET_CLASS (meta); pname = first_property; while (pname) { GValue value = { 0, }; GParamSpec *pspec; gchar *error; pspec = g_object_class_find_property (klass, pname); if (pspec == NULL) { g_warning ("%s: the layout property '%s' for MxBoxLayout " "(meta type '%s') does not exist", G_STRLOC, pname, G_OBJECT_TYPE_NAME (meta)); break; } if (!(pspec->flags & G_PARAM_WRITABLE)) { g_warning ("%s: the layout property '%s' for MxBoxLayout " "(meta type '%s') is not writable", G_STRLOC, pspec->name, G_OBJECT_TYPE_NAME (meta)); break; } g_value_init (&value, G_PARAM_SPEC_VALUE_TYPE (pspec)); G_VALUE_COLLECT (&value, var_args, 0, &error); if (error) { g_warning ("%s: %s", G_STRLOC, error); g_free (error); break; } clutter_container_child_set_property (container, actor, pspec->name, &value); g_value_unset (&value); pname = va_arg (var_args, gchar*); } } static void mx_box_layout_create_child_meta (MxBoxLayout *box, ClutterActor *actor) { ClutterContainer *container = CLUTTER_CONTAINER (box); ClutterContainerIface *iface = CLUTTER_CONTAINER_GET_IFACE (container); g_assert (g_type_is_a (iface->child_meta_type, MX_TYPE_BOX_LAYOUT_CHILD)); if (G_LIKELY (iface->create_child_meta)) iface->create_child_meta (container, actor); } /** * mx_box_layout_add_actor: * @box: a #MxBoxLayout * @actor: the #ClutterActor actor to add to the box layout * @position: the position where to insert the actor * * Inserts @actor at @position in @box. */ void mx_box_layout_add_actor (MxBoxLayout *box, ClutterActor *actor, gint position) { MxBoxLayoutPrivate *priv; g_return_if_fail (MX_IS_BOX_LAYOUT (box)); g_return_if_fail (CLUTTER_IS_ACTOR (actor)); priv = box->priv; /* this is really mx_box_container_add_actor() with a different insert() */ priv->children = g_list_insert (priv->children, actor, position); mx_box_layout_create_child_meta (box, actor); clutter_actor_set_parent (actor, (ClutterActor*) box); if (priv->enable_animations) { _mx_box_layout_start_animation (box); if (priv->timeline) { /* fade in the new actor when there is room */ clutter_actor_set_opacity (actor, 0); g_signal_connect_swapped (priv->timeline, "completed", G_CALLBACK (fade_in_actor), actor); } } else { clutter_actor_queue_relayout ((ClutterActor *) box); } g_signal_emit_by_name (box, "actor-added", actor); } /** * mx_box_layout_add_actor_with_properties: * @box: a #MxBoxLayout * @actor: the #ClutterActor actor to add to the box layout * @position: the position where to insert the actor * @first_property: name of the first property to set * @...: value for the first property, followed optionally by more name/value * pairs terminated with NULL. * * Inserts @actor at @position in the layout @box. You can set some layout * properties on the child at the same time. * * If @position is negative, or is larger than the number of actors in the * layout, the new actor is added on to the end of the list. */ void mx_box_layout_add_actor_with_properties (MxBoxLayout *box, ClutterActor *actor, gint position, const char *first_property, ...) { va_list var_args; mx_box_layout_add_actor (box, actor, position); if (first_property == NULL || *first_property == '\0') return; va_start (var_args, first_property); mx_box_layout_set_property_valist (box, actor, first_property, var_args); va_end (var_args); } /** * mx_box_layout_set_scroll_to_focused: * @box: A #MxBoxLayout * @scroll_to_focused: #TRUE to enable automatically scrolling to the * focused actor * * Enables or disables automatic scrolling to the focused actor. * * Since: 1.2 */ void mx_box_layout_set_scroll_to_focused (MxBoxLayout *box, gboolean scroll_to_focused) { MxBoxLayoutPrivate *priv; g_return_if_fail (MX_IS_BOX_LAYOUT (box)); priv = box->priv; if (priv->scroll_to_focused != scroll_to_focused) { priv->scroll_to_focused = scroll_to_focused; g_object_notify (G_OBJECT (box), "scroll-to-focused"); } } /** * mx_box_layout_get_scroll_to_focused: * @box: A #MxBoxLayout * * Get the value of the #MxBoxLayout:scroll-to-focused property. * * Returns: #TRUE if automatically scrolling to the focused actor is enabled * * Since: 1.2 */ gboolean mx_box_layout_get_scroll_to_focused (MxBoxLayout *box) { g_return_val_if_fail (MX_IS_BOX_LAYOUT (box), FALSE); return box->priv->scroll_to_focused; } mx-1.4.7/mx/mx-box-layout.h000066400000000000000000000074121201047117600154740ustar00rootroot00000000000000/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * mx-box-layout.h: box layout actor * * Copyright 2009, 2010 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * * Written by: Thomas Wood * */ #if !defined(MX_H_INSIDE) && !defined(MX_COMPILATION) #error "Only can be included directly.h" #endif #ifndef _MX_BOX_LAYOUT_H #define _MX_BOX_LAYOUT_H #include G_BEGIN_DECLS #define MX_TYPE_BOX_LAYOUT mx_box_layout_get_type() #define MX_BOX_LAYOUT(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ MX_TYPE_BOX_LAYOUT, MxBoxLayout)) #define MX_BOX_LAYOUT_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), \ MX_TYPE_BOX_LAYOUT, MxBoxLayoutClass)) #define MX_IS_BOX_LAYOUT(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \ MX_TYPE_BOX_LAYOUT)) #define MX_IS_BOX_LAYOUT_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE ((klass), \ MX_TYPE_BOX_LAYOUT)) #define MX_BOX_LAYOUT_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), \ MX_TYPE_BOX_LAYOUT, MxBoxLayoutClass)) typedef struct _MxBoxLayout MxBoxLayout; typedef struct _MxBoxLayoutClass MxBoxLayoutClass; typedef struct _MxBoxLayoutPrivate MxBoxLayoutPrivate; /** * MxBoxLayout: * * The contents of this structure are private and should only be accessed * through the public API. */ struct _MxBoxLayout { /*< private >*/ MxWidget parent; MxBoxLayoutPrivate *priv; }; struct _MxBoxLayoutClass { MxWidgetClass parent_class; /* padding for future expansion */ void (*_padding_0) (void); void (*_padding_1) (void); void (*_padding_2) (void); void (*_padding_3) (void); void (*_padding_4) (void); }; GType mx_box_layout_get_type (void); ClutterActor *mx_box_layout_new (void); void mx_box_layout_set_orientation (MxBoxLayout *box, MxOrientation orientation); MxOrientation mx_box_layout_get_orientation (MxBoxLayout *box); void mx_box_layout_set_spacing (MxBoxLayout *box, guint spacing); guint mx_box_layout_get_spacing (MxBoxLayout *box); gboolean mx_box_layout_get_enable_animations (MxBoxLayout *box); void mx_box_layout_set_enable_animations (MxBoxLayout *box, gboolean enable_animations); void mx_box_layout_add_actor (MxBoxLayout *box, ClutterActor *actor, gint position); void mx_box_layout_add_actor_with_properties (MxBoxLayout *box, ClutterActor *actor, gint position, const char *first_property, ...); void mx_box_layout_set_scroll_to_focused (MxBoxLayout *box, gboolean scroll_to_focused); gboolean mx_box_layout_get_scroll_to_focused (MxBoxLayout *box); G_END_DECLS #endif /* _MX_BOX_LAYOUT_H */ mx-1.4.7/mx/mx-button-group.c000066400000000000000000000301061201047117600160250ustar00rootroot00000000000000/* * mx-button-group.c: A group handler for buttons * * Copyright 2009 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * Boston, MA 02111-1307, USA. * * Written by: Thomas Wood * */ #include "mx-button-group.h" #include "mx-private.h" G_DEFINE_TYPE (MxButtonGroup, mx_button_group, G_TYPE_INITIALLY_UNOWNED) #define BUTTON_GROUP_PRIVATE(o) \ (G_TYPE_INSTANCE_GET_PRIVATE ((o), MX_TYPE_BUTTON_GROUP, MxButtonGroupPrivate)) struct _MxButtonGroupPrivate { MxButton *active_button; GSList *children; guint allow_no_active : 1; }; enum { PROP_ACTIVE_BUTTON = 1, PROP_ALLOW_NO_ACTIVE }; static void mx_button_group_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { MxButtonGroup *group = MX_BUTTON_GROUP (object); switch (property_id) { case PROP_ACTIVE_BUTTON: g_value_set_object (value, group->priv->active_button); break; case PROP_ALLOW_NO_ACTIVE: g_value_set_boolean (value, group->priv->allow_no_active); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } } static void mx_button_group_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { MxButtonGroup *group = MX_BUTTON_GROUP (object); switch (property_id) { case PROP_ACTIVE_BUTTON: mx_button_group_set_active_button (group, g_value_get_object (value)); break; case PROP_ALLOW_NO_ACTIVE: mx_button_group_set_allow_no_active (group, g_value_get_boolean (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } } static void mx_button_group_dispose (GObject *object) { MxButtonGroupPrivate *priv = MX_BUTTON_GROUP (object)->priv; if (priv->children) { g_slist_foreach (priv->children, (GFunc) g_object_unref, NULL); g_slist_free (priv->children); priv->children = NULL; } priv->active_button = NULL; G_OBJECT_CLASS (mx_button_group_parent_class)->dispose (object); } static void mx_button_group_finalize (GObject *object) { G_OBJECT_CLASS (mx_button_group_parent_class)->finalize (object); } static void mx_button_group_class_init (MxButtonGroupClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); GParamSpec *pspec; g_type_class_add_private (klass, sizeof (MxButtonGroupPrivate)); object_class->get_property = mx_button_group_get_property; object_class->set_property = mx_button_group_set_property; object_class->dispose = mx_button_group_dispose; object_class->finalize = mx_button_group_finalize; pspec = g_param_spec_object ("active-button", "Active Button", "The currently active (toggled) button", MX_TYPE_BUTTON, MX_PARAM_READWRITE); g_object_class_install_property (object_class, PROP_ACTIVE_BUTTON, pspec); pspec = g_param_spec_boolean ("allow-no-active", "Allow No Active", "Allow no buttons to be active (toggled)", FALSE, MX_PARAM_READWRITE); g_object_class_install_property (object_class, PROP_ALLOW_NO_ACTIVE, pspec); } static void mx_button_group_init (MxButtonGroup *self) { self->priv = BUTTON_GROUP_PRIVATE (self); } /** * mx_button_group_new: * * Create a new #MxButtonGroup. * * Returns: a newly allocated #MxButtonGroup. */ MxButtonGroup * mx_button_group_new (void) { return g_object_new (MX_TYPE_BUTTON_GROUP, NULL); } static void button_toggled_notify_cb (MxButton *button, GParamSpec *pspec, MxButtonGroup *group) { if (mx_button_get_toggled (button)) mx_button_group_set_active_button (group, button); else mx_button_group_set_active_button (group, NULL); } static gboolean button_click_intercept (MxButton *button, ClutterButtonEvent *event, MxButtonGroup *group) { if (button == group->priv->active_button && !group->priv->allow_no_active) return TRUE; else return FALSE; } static void button_weak_notify (MxButtonGroup *group, MxButton *button) { MxButtonGroupPrivate *priv = group->priv; GSList *l, *prev = NULL, *next = NULL; for (l = priv->children; l; l = g_slist_next (l)) { if ((MxButton *) l->data == button) { next = g_slist_next (l); group->priv->children = g_slist_remove (priv->children, button); break; } prev = l; } if (priv->active_button == button) { /* Try and select another button if the one we've removed is active. * But we shouldn't do this in the case where we allow no active button. */ if (priv->allow_no_active) { mx_button_group_set_active_button (group, NULL); } else if (prev) { mx_button_group_set_active_button (group, (MxButton *) prev->data); } else if (next) { mx_button_group_set_active_button (group, (MxButton *) next->data); } else if (priv->children) { mx_button_group_set_active_button (group, (MxButton *) priv->children->data); } else { mx_button_group_set_active_button (group, NULL); } } } /** * mx_button_group_add: * @group: A #MxButtonGroup * @button: A #MxButton * * Add @button to the #MxButtonGroup. * */ void mx_button_group_add (MxButtonGroup *group, MxButton *button) { g_return_if_fail (MX_IS_BUTTON_GROUP (group)); g_return_if_fail (MX_IS_BUTTON (button)); group->priv->children = g_slist_prepend (group->priv->children, button); g_signal_connect (button, "notify::toggled", G_CALLBACK (button_toggled_notify_cb), group); g_signal_connect (button, "button-press-event", G_CALLBACK (button_click_intercept), group); g_signal_connect (button, "button-release-event", G_CALLBACK (button_click_intercept), group); g_object_weak_ref (G_OBJECT (button), (GWeakNotify) button_weak_notify, group); } /** * mx_button_group_remove: * @group: A #MxButtonGroup * @button: A #MxButton * * Remove @button from the #MxButtonGroup * */ void mx_button_group_remove (MxButtonGroup *group, MxButton *button) { GSList *l, *prev = NULL, *next; MxButtonGroupPrivate *priv; gboolean found; g_return_if_fail (MX_IS_BUTTON_GROUP (group)); g_return_if_fail (MX_IS_BUTTON (button)); priv = group->priv; /* check the button exists in this group */ found = FALSE; for (l = priv->children; l; l = g_slist_next (l)) { if ((MxButton*) l->data == button) { found = TRUE; break; } prev = l; } if (!found) return; next = g_slist_next (l); priv->children = g_slist_remove (priv->children, button); g_signal_handlers_disconnect_by_func (button, button_toggled_notify_cb, group); g_signal_handlers_disconnect_by_func (button, button_click_intercept, group); g_object_weak_unref (G_OBJECT (button), (GWeakNotify) button_weak_notify, group); if (priv->active_button == button) { /* Try and select another button if the one we've removed is active. * But we shouldn't do this in the case where we allow no active button. */ if (priv->allow_no_active) { mx_button_group_set_active_button (group, NULL); } else if (prev) { mx_button_group_set_active_button (group, (MxButton *) prev->data); } else if (next) { mx_button_group_set_active_button (group, (MxButton *) next->data); } else if (priv->children) { mx_button_group_set_active_button (group, (MxButton *) priv->children->data); } else { mx_button_group_set_active_button (group, NULL); } } } /** * mx_button_group_foreach: * @group: A #MxButtonGroup * @callback: (scope call): A #ClutterCallback * @userdata: (closure): A #gpointer * * Calls @callback for each button in the group. * */ void mx_button_group_foreach (MxButtonGroup *group, ClutterCallback callback, gpointer userdata) { g_return_if_fail (MX_IS_BUTTON_GROUP (group)); g_return_if_fail (callback != NULL); g_slist_foreach (group->priv->children, (GFunc) callback, userdata); } /** * mx_button_group_set_active_button: * @group: A #MxButtonGroup * @button: (allow-none): A #MxButton * * Set the current active button in the group. The previous active button will * have #MxButton:toggled set to #FALSE. * */ void mx_button_group_set_active_button (MxButtonGroup *group, MxButton *button) { MxButtonGroupPrivate *priv; g_return_if_fail (MX_IS_BUTTON_GROUP (group)); g_return_if_fail (button == NULL || MX_IS_BUTTON (button)); priv = group->priv; if (button == priv->active_button) return; if (priv->active_button) mx_button_set_toggled (priv->active_button, FALSE); if (button) mx_button_set_toggled (button, TRUE); priv->active_button = button; g_object_notify (G_OBJECT (group), "active-button"); } /** * mx_button_group_get_active_button: * @group: A #MxButtonGroup * * Get the current active button * * Returns: (transfer none): the currently active button */ MxButton * mx_button_group_get_active_button (MxButtonGroup *group) { MxButtonGroupPrivate *priv; g_return_val_if_fail (MX_IS_BUTTON_GROUP (group), NULL); priv = group->priv; return priv->active_button; } /** * mx_button_group_set_allow_no_active: * @group: A #MxButtonGroup * @allow_no_active: A #gboolean * * Set the value of the #MxButtonGroup:allow-no-active property. * */ void mx_button_group_set_allow_no_active (MxButtonGroup *group, gboolean allow_no_active) { g_return_if_fail (MX_IS_BUTTON_GROUP (group)); if (group->priv->allow_no_active != allow_no_active) { group->priv->allow_no_active = allow_no_active; g_object_notify (G_OBJECT (group), "allow-no-active"); } } /** * mx_button_group_get_allow_no_active: * @group: A #MxButtonGroup * * Get the value of the #MxButtonGroup:allow-no-active property. * * Returns: the value of the "allow-no-active" property. */ gboolean mx_button_group_get_allow_no_active (MxButtonGroup *group) { g_return_val_if_fail (MX_IS_BUTTON_GROUP (group), FALSE); return group->priv->allow_no_active; } /** * mx_button_group_get_buttons: * @group: A #MxButtonGroup * * Get a list of the buttons in the button group. * * Returns: (element-type Mx.Button): a list of buttons. The list is * owned by the #MxButtonGroup and should not be modified by the * application. */ const GSList * mx_button_group_get_buttons (MxButtonGroup *group) { g_return_val_if_fail (MX_IS_BUTTON_GROUP (group), NULL); return group->priv->children; } mx-1.4.7/mx/mx-button-group.h000066400000000000000000000065231201047117600160400ustar00rootroot00000000000000/* * mx-button-group.h: A group handler for buttons * * Copyright 2009 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * Boston, MA 02111-1307, USA. * * Written by: Thomas Wood * */ #if !defined(MX_H_INSIDE) && !defined(MX_COMPILATION) #error "Only can be included directly.h" #endif #ifndef _MX_BUTTON_GROUP_H #define _MX_BUTTON_GROUP_H #include #include "mx-button.h" G_BEGIN_DECLS #define MX_TYPE_BUTTON_GROUP mx_button_group_get_type() #define MX_BUTTON_GROUP(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ MX_TYPE_BUTTON_GROUP, MxButtonGroup)) #define MX_BUTTON_GROUP_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), \ MX_TYPE_BUTTON_GROUP, MxButtonGroupClass)) #define MX_IS_BUTTON_GROUP(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \ MX_TYPE_BUTTON_GROUP)) #define MX_IS_BUTTON_GROUP_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE ((klass), \ MX_TYPE_BUTTON_GROUP)) #define MX_BUTTON_GROUP_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), \ MX_TYPE_BUTTON_GROUP, MxButtonGroupClass)) typedef struct _MxButtonGroup MxButtonGroup; typedef struct _MxButtonGroupClass MxButtonGroupClass; typedef struct _MxButtonGroupPrivate MxButtonGroupPrivate; /** * MxButtonGroup: * * The contents of this structure are private and should only be accessed * through the public API. */ struct _MxButtonGroup { GInitiallyUnowned parent; MxButtonGroupPrivate *priv; }; struct _MxButtonGroupClass { GInitiallyUnownedClass parent_class; /*< private >*/ /* padding for future expansion */ void (*_padding_0) (void); void (*_padding_1) (void); void (*_padding_2) (void); void (*_padding_3) (void); void (*_padding_4) (void); }; GType mx_button_group_get_type (void) G_GNUC_CONST; MxButtonGroup *mx_button_group_new (void); void mx_button_group_add (MxButtonGroup *group, MxButton *button); void mx_button_group_remove (MxButtonGroup *group, MxButton *button); void mx_button_group_foreach (MxButtonGroup *group, ClutterCallback callback, gpointer userdata); void mx_button_group_set_active_button (MxButtonGroup *group, MxButton *button); MxButton* mx_button_group_get_active_button (MxButtonGroup *group); void mx_button_group_set_allow_no_active (MxButtonGroup *group, gboolean allow_no_active); gboolean mx_button_group_get_allow_no_active (MxButtonGroup *group); const GSList *mx_button_group_get_buttons (MxButtonGroup *group); G_END_DECLS #endif /* _MX_BUTTON_GROUP_H */ mx-1.4.7/mx/mx-button.c000066400000000000000000001136101201047117600146750ustar00rootroot00000000000000/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * mx-button.c: Plain button actor * * Copyright 2007 OpenedHand * Copyright 2008, 2009 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * * Written by: Emmanuele Bassi * Thomas Wood * */ /** * SECTION:mx-button * @short_description: Button widget * * A button widget with support for either a text label or icon, toggle mode * and transitions effects between states. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include "mx-button.h" #include "mx-marshal.h" #include "mx-stylable.h" #include "mx-style.h" #include "mx-texture-frame.h" #include "mx-texture-cache.h" #include "mx-private.h" #include "mx-enum-types.h" #include "mx-focusable.h" enum { PROP_0, PROP_LABEL, PROP_ICON_NAME, PROP_ICON_SIZE, PROP_IS_TOGGLE, PROP_TOGGLED, PROP_ACTION, PROP_ICON_POSITION, PROP_ICON_VISIBLE, PROP_LABEL_VISIBLE }; enum { CLICKED, LAST_SIGNAL }; #define MX_BUTTON_GET_PRIVATE(obj) \ (G_TYPE_INSTANCE_GET_PRIVATE ((obj), MX_TYPE_BUTTON, MxButtonPrivate)) struct _MxButtonPrivate { gchar *text; gchar *icon_name; gchar *style_icon_name; guint icon_size; guint style_icon_size; guint8 old_opacity; guint is_pressed : 1; guint is_toggle : 1; guint is_toggled : 1; ClutterAnimation *animation; ClutterActor *content_image; MxAction *action; MxPosition icon_position; guint icon_visible : 1; guint label_visible : 1; ClutterActor *hbox; ClutterActor *icon; ClutterActor *label; GBinding *action_label_binding; GBinding *action_icon_binding; }; static guint button_signals[LAST_SIGNAL] = { 0, }; static void mx_button_update_contents (MxButton *self); static void mx_stylable_iface_init (MxStylableIface *iface); static void mx_focusable_iface_init (MxFocusableIface *iface); G_DEFINE_TYPE_WITH_CODE (MxButton, mx_button, MX_TYPE_BIN, G_IMPLEMENT_INTERFACE (MX_TYPE_STYLABLE, mx_stylable_iface_init) G_IMPLEMENT_INTERFACE (MX_TYPE_FOCUSABLE, mx_focusable_iface_init)); static void mx_stylable_iface_init (MxStylableIface *iface) { static gboolean is_initialized = FALSE; if (G_UNLIKELY (!is_initialized)) { ClutterColor bg_color = { 0xcc, 0xcc, 0xcc, 0x00 }; GParamSpec *pspec; is_initialized = TRUE; pspec = clutter_param_spec_color ("background-color", "Background Color", "The background color of an actor", &bg_color, G_PARAM_READWRITE); mx_stylable_iface_install_property (iface, MX_TYPE_BUTTON, pspec); pspec = g_param_spec_boxed ("x-mx-content-image", "Content Image", "Image used as the button", MX_TYPE_BORDER_IMAGE, G_PARAM_READWRITE); mx_stylable_iface_install_property (iface, MX_TYPE_BUTTON, pspec); pspec = g_param_spec_string ("x-mx-icon-name", "Icon name", "Named icon to place inside the button", NULL, G_PARAM_READWRITE); mx_stylable_iface_install_property (iface, MX_TYPE_BUTTON, pspec); pspec = g_param_spec_uint ("x-mx-icon-size", "Icon size", "Size to use for icon", 1, G_MAXINT, 48, G_PARAM_READWRITE); mx_stylable_iface_install_property (iface, MX_TYPE_BUTTON, pspec); } } static MxFocusable* mx_button_accept_focus (MxFocusable *focusable, MxFocusHint hint) { mx_stylable_style_pseudo_class_add (MX_STYLABLE (focusable), "focus"); clutter_actor_grab_key_focus (CLUTTER_ACTOR (focusable)); return focusable; } static MxFocusable* mx_button_move_focus (MxFocusable *focusable, MxFocusDirection direction, MxFocusable *from) { /* check if focus is being moved from us */ if (focusable == from) mx_stylable_style_pseudo_class_remove (MX_STYLABLE (focusable), "focus"); return NULL; } static void mx_focusable_iface_init (MxFocusableIface *iface) { iface->accept_focus = mx_button_accept_focus; iface->move_focus = mx_button_move_focus; } static void mx_button_update_label_style (MxButton *button) { MxButtonPrivate *priv = button->priv; mx_stylable_apply_clutter_text_attributes (MX_STYLABLE (button), CLUTTER_TEXT (priv->label)); } static void mx_button_style_changed (MxWidget *widget) { MxButton *button = MX_BUTTON (widget); MxButtonPrivate *priv = button->priv; MxBorderImage *content_image = NULL; /* update the label styling */ mx_button_update_label_style (button); g_free (priv->style_icon_name); mx_stylable_get (MX_STYLABLE (widget), "x-mx-content-image", &content_image, "x-mx-icon-name", &priv->style_icon_name, "x-mx-icon-size", &priv->style_icon_size, NULL); if (content_image && content_image->uri) { GError *err = NULL; if (priv->content_image) { clutter_actor_unparent (priv->content_image); } priv->content_image = clutter_texture_new_from_file (content_image->uri, &err); if (priv->content_image) clutter_actor_set_parent (priv->content_image, CLUTTER_ACTOR (widget)); if (err) { g_warning ("Could not load content image: %s", err->message); g_error_free (err); } g_boxed_free (MX_TYPE_BORDER_IMAGE, content_image); return; } else { /* remove any previous content image */ if (priv->content_image) { clutter_actor_unparent (priv->content_image); priv->content_image = NULL; } if (content_image) g_boxed_free (MX_TYPE_BORDER_IMAGE, content_image); } if (priv->icon_size == 0) mx_icon_set_icon_size (MX_ICON (priv->icon), priv->style_icon_size); if (priv->style_icon_name && !priv->icon_name) { mx_icon_set_icon_name (MX_ICON (priv->icon), priv->style_icon_name); mx_button_update_contents (button); } } static void mx_button_push (MxButton *button, ClutterButtonEvent *event) { MxWidget *widget = MX_WIDGET (button); mx_widget_hide_tooltip (widget); button->priv->is_pressed = TRUE; //clutter_grab_pointer (CLUTTER_ACTOR (button)); mx_stylable_style_pseudo_class_add (MX_STYLABLE (button), "active"); if (event) mx_widget_long_press_query (widget, event); } static void mx_button_pull (MxButton *button) { MxWidget *widget = MX_WIDGET (button); MxButtonPrivate *priv = button->priv; if (!button->priv->is_pressed) return; //clutter_ungrab_pointer (); if (button->priv->is_toggle) { mx_button_set_toggled (button, !button->priv->is_toggled); } button->priv->is_pressed = FALSE; /* activate any associated action */ if (priv->action) g_signal_emit_by_name (priv->action, "activated", 0); g_signal_emit (button, button_signals[CLICKED], 0); mx_widget_long_press_cancel (widget); mx_stylable_style_pseudo_class_remove (MX_STYLABLE (button), "active"); } static gboolean mx_button_button_press (ClutterActor *actor, ClutterButtonEvent *event) { if (mx_widget_get_disabled (MX_WIDGET (actor))) return TRUE; if (event->button == 1) { mx_button_push (MX_BUTTON (actor), event); return TRUE; } return TRUE; } static gboolean mx_button_button_release (ClutterActor *actor, ClutterButtonEvent *event) { if (mx_widget_get_disabled (MX_WIDGET (actor))) return TRUE; if (event->button == 1) { mx_button_pull (MX_BUTTON (actor)); return TRUE; } return TRUE; } static gboolean mx_button_key_press (ClutterActor *actor, ClutterKeyEvent *event) { if (event->keyval == CLUTTER_KEY_Return || event->keyval == CLUTTER_KEY_KP_Enter || event->keyval == CLUTTER_KEY_ISO_Enter || event->keyval == CLUTTER_KEY_space) { mx_button_push (MX_BUTTON (actor), NULL); return TRUE; } return FALSE; } static gboolean mx_button_key_release (ClutterActor *actor, ClutterKeyEvent *event) { if (event->keyval == CLUTTER_KEY_Return || event->keyval == CLUTTER_KEY_KP_Enter || event->keyval == CLUTTER_KEY_ISO_Enter || event->keyval == CLUTTER_KEY_space) { mx_button_pull (MX_BUTTON (actor)); return TRUE; } return FALSE; } static gboolean mx_button_enter (ClutterActor *actor, ClutterCrossingEvent *event) { MxWidget *widget = MX_WIDGET (actor); if (event->source != actor) return FALSE; /* check if the widget is disabled */ if (mx_widget_get_disabled (MX_WIDGET (actor))) return FALSE; mx_stylable_style_pseudo_class_add (MX_STYLABLE (widget), "hover"); return FALSE; } static gboolean mx_button_leave (ClutterActor *actor, ClutterCrossingEvent *event) { MxButton *button = MX_BUTTON (actor); MxWidget *widget = MX_WIDGET (actor); if (event->source != actor) return FALSE; /* hide the tooltip */ if (mx_widget_get_tooltip_text (widget)) mx_widget_hide_tooltip (widget); /* check if the widget is disabled */ if (mx_widget_get_disabled (MX_WIDGET (actor))) return FALSE; if (button->priv->is_pressed) { //clutter_ungrab_pointer (); mx_widget_long_press_cancel (widget); mx_stylable_style_pseudo_class_remove (MX_STYLABLE (widget), "active"); button->priv->is_pressed = FALSE; } mx_stylable_style_pseudo_class_remove (MX_STYLABLE (widget), "hover"); return FALSE; } static void mx_button_set_property (GObject *gobject, guint prop_id, const GValue *value, GParamSpec *pspec) { MxButton *button = MX_BUTTON (gobject); switch (prop_id) { case PROP_LABEL: mx_button_set_label (button, g_value_get_string (value)); break; case PROP_ICON_NAME: mx_button_set_icon_name (button, g_value_get_string (value)); break; case PROP_ICON_SIZE: mx_button_set_icon_size (button, g_value_get_uint (value)); break; case PROP_IS_TOGGLE: mx_button_set_is_toggle (button, g_value_get_boolean (value)); break; case PROP_TOGGLED: mx_button_set_toggled (button, g_value_get_boolean (value)); break; case PROP_ACTION: mx_button_set_action (button, g_value_get_object (value)); break; case PROP_ICON_POSITION: mx_button_set_icon_position (button, g_value_get_enum (value)); break; case PROP_ICON_VISIBLE: mx_button_set_icon_visible (button, g_value_get_boolean (value)); break; case PROP_LABEL_VISIBLE: mx_button_set_label_visible (button, g_value_get_boolean (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); break; } } static void mx_button_get_property (GObject *gobject, guint prop_id, GValue *value, GParamSpec *pspec) { MxButtonPrivate *priv = MX_BUTTON (gobject)->priv; switch (prop_id) { case PROP_LABEL: g_value_set_string (value, priv->text); break; case PROP_ICON_NAME: g_value_set_string (value, priv->icon_name ? priv->icon_name : priv->style_icon_name); break; case PROP_ICON_SIZE: g_value_set_uint (value, priv->icon_size ? priv->icon_size : priv->style_icon_size); break; case PROP_IS_TOGGLE: g_value_set_boolean (value, priv->is_toggle); break; case PROP_TOGGLED: g_value_set_boolean (value, priv->is_toggled); break; case PROP_ACTION: g_value_set_object (value, priv->action); break; case PROP_ICON_POSITION: g_value_set_enum (value, priv->icon_position); break; case PROP_ICON_VISIBLE: g_value_set_boolean (value, priv->icon_visible); break; case PROP_LABEL_VISIBLE: g_value_set_boolean (value, priv->label_visible); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); break; } } static void mx_button_finalize (GObject *gobject) { MxButtonPrivate *priv = MX_BUTTON (gobject)->priv; g_free (priv->text); g_free (priv->icon_name); g_free (priv->style_icon_name); G_OBJECT_CLASS (mx_button_parent_class)->finalize (gobject); } static void mx_button_dispose (GObject *gobject) { MxButtonPrivate *priv = MX_BUTTON (gobject)->priv; if (priv->content_image) { clutter_actor_unparent (priv->content_image); priv->content_image = NULL; } if (priv->action) { g_object_unref (priv->action); priv->action = NULL; } if (priv->hbox) { g_object_unref (priv->hbox); priv->hbox = NULL; } G_OBJECT_CLASS (mx_button_parent_class)->dispose (gobject); } static void mx_button_map (ClutterActor *self) { MxButtonPrivate *priv = MX_BUTTON (self)->priv; CLUTTER_ACTOR_CLASS (mx_button_parent_class)->map (self); if (priv->content_image) clutter_actor_map (priv->content_image); } static void mx_button_unmap (ClutterActor *self) { MxButtonPrivate *priv = MX_BUTTON (self)->priv; CLUTTER_ACTOR_CLASS (mx_button_parent_class)->unmap (self); if (priv->content_image) clutter_actor_unmap (priv->content_image); } static void mx_button_paint_background (MxWidget *widget, ClutterActor *background, const ClutterColor *color) { MxButtonPrivate *priv; priv = MX_BUTTON (widget)->priv; if (priv->content_image) { clutter_actor_paint (priv->content_image); return; } MX_WIDGET_CLASS (mx_button_parent_class)->paint_background (widget, background, color); } static void mx_button_allocate (ClutterActor *actor, const ClutterActorBox *box, ClutterAllocationFlags flags) { MxButtonPrivate *priv = MX_BUTTON (actor)->priv; CLUTTER_ACTOR_CLASS (mx_button_parent_class)->allocate (actor, box, flags); if (priv->content_image) { ClutterActorBox childbox; childbox.x1 = 0; childbox.y1 = 0; childbox.x2 = (box->x2 - box->x1); childbox.y2 = (box->y2 - box->y1); clutter_actor_allocate (priv->content_image, &childbox, flags); } mx_bin_allocate_child (MX_BIN (actor), box, flags); } static void mx_button_get_preferred_width (ClutterActor *actor, gfloat for_height, gfloat *min_width, gfloat *pref_width) { MxButtonPrivate *priv = MX_BUTTON (actor)->priv; if (priv->content_image) { clutter_actor_get_preferred_width (priv->content_image, for_height, min_width, pref_width); return; } CLUTTER_ACTOR_CLASS (mx_button_parent_class)->get_preferred_width (actor, for_height, min_width, pref_width); } static void mx_button_get_preferred_height (ClutterActor *actor, gfloat for_width, gfloat *min_height, gfloat *pref_height) { MxButtonPrivate *priv = MX_BUTTON (actor)->priv; if (priv->content_image) { clutter_actor_get_preferred_height (priv->content_image, for_width, min_height, pref_height); return; } CLUTTER_ACTOR_CLASS (mx_button_parent_class)->get_preferred_height (actor, for_width, min_height, pref_height); } static void mx_button_paint (ClutterActor *actor) { MxButtonPrivate *priv = MX_BUTTON (actor)->priv; if (priv->content_image) clutter_actor_paint (priv->content_image); else CLUTTER_ACTOR_CLASS (mx_button_parent_class)->paint (actor); } static void mx_button_update_contents (MxButton *self) { MxButtonPrivate *priv = self->priv; ClutterActor *child; gboolean icon_visible, label_visible; const gchar *text; /* If the icon doesn't have a name set, treat it as * not-visible. */ if (priv->icon_visible && mx_icon_get_icon_name (MX_ICON (priv->icon))) icon_visible = TRUE; else icon_visible = FALSE; text = clutter_text_get_text (CLUTTER_TEXT (priv->label)); if (priv->label_visible && text && (strcmp (text, "") != 0)) label_visible = TRUE; else label_visible = FALSE; /* replace any custom content */ child = mx_bin_get_child (MX_BIN (self)); if (child != priv->hbox) mx_bin_set_child (MX_BIN (self), priv->hbox); /* Handle the simple cases first */ if (!icon_visible && !label_visible) { clutter_actor_hide (priv->hbox); return; } /* ensure the hbox is visible */ clutter_actor_show (priv->hbox); if (icon_visible && !label_visible) { clutter_actor_show (priv->icon); clutter_actor_hide (priv->label); clutter_actor_lower_bottom (priv->icon); return; } if (!icon_visible && label_visible) { clutter_actor_hide (priv->icon); clutter_actor_show (priv->label); clutter_actor_lower_bottom (priv->label); return; } /* Both the icon and text are visible, handle this case */ clutter_actor_show (priv->icon); clutter_actor_show (priv->label); switch (priv->icon_position) { case MX_POSITION_TOP: mx_box_layout_set_orientation (MX_BOX_LAYOUT (priv->hbox), MX_ORIENTATION_VERTICAL); clutter_actor_lower_bottom (priv->icon); clutter_container_child_set (CLUTTER_CONTAINER (priv->hbox), priv->label, "x-align", MX_ALIGN_MIDDLE, "y-align", MX_ALIGN_END, NULL); clutter_container_child_set (CLUTTER_CONTAINER (priv->hbox), priv->icon, "x-align", MX_ALIGN_MIDDLE, "y-align", MX_ALIGN_START, NULL); break; case MX_POSITION_RIGHT: mx_box_layout_set_orientation (MX_BOX_LAYOUT (priv->hbox), MX_ORIENTATION_HORIZONTAL); clutter_actor_raise_top (priv->icon); clutter_container_child_set (CLUTTER_CONTAINER (priv->hbox), priv->label, "x-align", MX_ALIGN_START, "y-align", MX_ALIGN_MIDDLE, NULL); clutter_container_child_set (CLUTTER_CONTAINER (priv->hbox), priv->icon, "x-align", MX_ALIGN_END, "y-align", MX_ALIGN_MIDDLE, NULL); break; case MX_POSITION_BOTTOM: mx_box_layout_set_orientation (MX_BOX_LAYOUT (priv->hbox), MX_ORIENTATION_VERTICAL); clutter_actor_raise_top (priv->icon); mx_box_layout_child_set_x_align (MX_BOX_LAYOUT (priv->hbox), priv->label, MX_ALIGN_MIDDLE); clutter_container_child_set (CLUTTER_CONTAINER (priv->hbox), priv->label, "x-align", MX_ALIGN_MIDDLE, "y-align", MX_ALIGN_START, NULL); clutter_container_child_set (CLUTTER_CONTAINER (priv->hbox), priv->icon, "x-align", MX_ALIGN_MIDDLE, "y-align", MX_ALIGN_END, NULL); break; case MX_POSITION_LEFT: mx_box_layout_set_orientation (MX_BOX_LAYOUT (priv->hbox), MX_ORIENTATION_HORIZONTAL); clutter_actor_lower_bottom (priv->icon); clutter_container_child_set (CLUTTER_CONTAINER (priv->hbox), priv->label, "x-align", MX_ALIGN_END, "y-align", MX_ALIGN_MIDDLE, NULL); clutter_container_child_set (CLUTTER_CONTAINER (priv->hbox), priv->icon, "x-align", MX_ALIGN_START, "y-align", MX_ALIGN_MIDDLE, NULL); break; } } static void mx_button_class_init (MxButtonClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass); MxWidgetClass *widget_class = MX_WIDGET_CLASS (klass); GParamSpec *pspec; g_type_class_add_private (klass, sizeof (MxButtonPrivate)); gobject_class->set_property = mx_button_set_property; gobject_class->get_property = mx_button_get_property; gobject_class->dispose = mx_button_dispose; gobject_class->finalize = mx_button_finalize; actor_class->button_press_event = mx_button_button_press; actor_class->button_release_event = mx_button_button_release; actor_class->key_press_event = mx_button_key_press; actor_class->key_release_event = mx_button_key_release; actor_class->enter_event = mx_button_enter; actor_class->leave_event = mx_button_leave; actor_class->get_preferred_width = mx_button_get_preferred_width; actor_class->get_preferred_height = mx_button_get_preferred_height; actor_class->paint = mx_button_paint; actor_class->map = mx_button_map; actor_class->unmap = mx_button_unmap; actor_class->allocate = mx_button_allocate; widget_class->paint_background = mx_button_paint_background; pspec = g_param_spec_string ("label", "Label", "Label of the button", NULL, MX_PARAM_READWRITE | MX_PARAM_TRANSLATEABLE); g_object_class_install_property (gobject_class, PROP_LABEL, pspec); pspec = g_param_spec_string ("icon-name", "Icon name", "Icon name of the button", NULL, MX_PARAM_READWRITE); g_object_class_install_property (gobject_class, PROP_ICON_NAME, pspec); pspec = g_param_spec_uint ("icon-size", "Icon size", "The size to use for the button icon (in pixels)", 0, G_MAXUINT, 0, MX_PARAM_READWRITE); g_object_class_install_property (gobject_class, PROP_ICON_SIZE, pspec); pspec = g_param_spec_boolean ("is-toggle", "Is Toggle", "Enable or disable toggling", FALSE, MX_PARAM_READWRITE); g_object_class_install_property (gobject_class, PROP_IS_TOGGLE, pspec); pspec = g_param_spec_boolean ("toggled", "Toggled", "Indicates if a toggle button is \"on\"" " or \"off\"", FALSE, MX_PARAM_READWRITE); g_object_class_install_property (gobject_class, PROP_TOGGLED, pspec); pspec = g_param_spec_object ("action", "Action", "Associated action", MX_TYPE_ACTION, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (gobject_class, PROP_ACTION, pspec); pspec = g_param_spec_enum ("icon-position", "Icon position", "The position of the icon, relative to the text", MX_TYPE_POSITION, MX_POSITION_LEFT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (gobject_class, PROP_ICON_POSITION, pspec); pspec = g_param_spec_boolean ("icon-visible", "Icon visible", "Whether to show the icon", TRUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (gobject_class, PROP_ICON_VISIBLE, pspec); pspec = g_param_spec_boolean ("label-visible", "Label visible", "Whether to show the label", TRUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (gobject_class, PROP_LABEL_VISIBLE, pspec); /** * MxButton::clicked: * @button: the object that received the signal * * Emitted when the user activates the button, either with a mouse press and * release or with the keyboard. */ button_signals[CLICKED] = g_signal_new ("clicked", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (MxButtonClass, clicked), NULL, NULL, _mx_marshal_VOID__VOID, G_TYPE_NONE, 0); } static void mx_button_init (MxButton *button) { MxButtonPrivate *priv; priv = button->priv = MX_BUTTON_GET_PRIVATE (button); clutter_actor_set_reactive ((ClutterActor *) button, TRUE); g_signal_connect (button, "style-changed", G_CALLBACK (mx_button_style_changed), NULL); priv->icon_visible = TRUE; priv->label_visible = TRUE; priv->icon_position = MX_POSITION_LEFT; /* take an extra reference to the hbox */ priv->hbox = g_object_ref (mx_box_layout_new ()); mx_bin_set_child (MX_BIN (button), priv->hbox); priv->icon = mx_icon_new (); priv->label = g_object_new (CLUTTER_TYPE_TEXT, "line-alignment", PANGO_ALIGN_CENTER, "ellipsize", PANGO_ELLIPSIZE_END, NULL); clutter_container_add (CLUTTER_CONTAINER (priv->hbox), priv->icon, priv->label, NULL); mx_box_layout_child_set_expand (MX_BOX_LAYOUT (priv->hbox), priv->label, TRUE); mx_box_layout_child_set_y_fill (MX_BOX_LAYOUT (priv->hbox), priv->label, FALSE); mx_box_layout_child_set_x_fill (MX_BOX_LAYOUT (priv->hbox), priv->label, FALSE); mx_box_layout_child_set_expand (MX_BOX_LAYOUT (priv->hbox), priv->icon, TRUE); mx_box_layout_child_set_y_fill (MX_BOX_LAYOUT (priv->hbox), priv->icon, FALSE); mx_box_layout_child_set_x_fill (MX_BOX_LAYOUT (priv->hbox), priv->icon, FALSE); mx_button_update_contents (button); } /** * mx_button_new: * * Create a new button * * Returns: a new #MxButton */ ClutterActor * mx_button_new (void) { return g_object_new (MX_TYPE_BUTTON, NULL); } /** * mx_button_new_with_label: * @text: text to set the label to * * Create a new #MxButton with the specified label * * Returns: a new #MxButton */ ClutterActor * mx_button_new_with_label (const gchar *text) { return g_object_new (MX_TYPE_BUTTON, "label", text, NULL); } /** * mx_button_get_label: * @button: a #MxButton * * Get the text displayed on the button * * Returns: the text for the button. This must not be freed by the application */ const gchar * mx_button_get_label (MxButton *button) { g_return_val_if_fail (MX_IS_BUTTON (button), NULL); return button->priv->text; } /** * mx_button_set_label: * @button: a #MxButton * @text: text to set the label to * * Sets the text displayed on the button */ void mx_button_set_label (MxButton *button, const gchar *text) { MxButtonPrivate *priv; g_return_if_fail (MX_IS_BUTTON (button)); priv = button->priv; g_free (priv->text); if (text) priv->text = g_strdup (text); else priv->text = g_strdup (""); clutter_text_set_text (CLUTTER_TEXT (priv->label), priv->text); mx_button_update_contents (button); g_object_notify (G_OBJECT (button), "label"); } /** * mx_button_get_is_toggle: * @button: a #MxButton * * Get the toggle mode status of the button. * * Returns: #TRUE if toggle mode is set, otherwise #FALSE */ gboolean mx_button_get_is_toggle (MxButton *button) { g_return_val_if_fail (MX_IS_BUTTON (button), FALSE); return button->priv->is_toggle; } /** * mx_button_set_is_toggle: * @button: a #MxButton * @toggle: #TRUE or #FALSE * * Enables or disables toggle mode for the button. In toggle mode, the active * state will be "toggled" when the user clicks the button. */ void mx_button_set_is_toggle (MxButton *button, gboolean toggle) { g_return_if_fail (MX_IS_BUTTON (button)); button->priv->is_toggle = toggle; g_object_notify (G_OBJECT (button), "is-toggle"); } /** * mx_button_get_toggled: * @button: a #MxButton * * Get the state of the button that is in toggle mode. * * Returns: #TRUE if the button is toggled, or #FALSE if not */ gboolean mx_button_get_toggled (MxButton *button) { g_return_val_if_fail (MX_IS_BUTTON (button), FALSE); return button->priv->is_toggled; } /** * mx_button_set_toggled: * @button: a #MxButton * @toggled: #TRUE or #FALSE * * Sets the toggled state of the button. This is only really useful if the * button has #toggle-mode mode set to #TRUE. */ void mx_button_set_toggled (MxButton *button, gboolean toggled) { g_return_if_fail (MX_IS_BUTTON (button)); if (button->priv->is_toggled != toggled) { button->priv->is_toggled = toggled; if (toggled) mx_stylable_style_pseudo_class_add (MX_STYLABLE (button), "checked"); else mx_stylable_style_pseudo_class_remove (MX_STYLABLE (button), "checked"); g_object_notify (G_OBJECT (button), "toggled"); } } /** * mx_button_set_action: * @button: A #MxButton * @action: A #MxAction * * Sets @action as the action for @button. @Button will take its label and * icon from @action. * * Since: 1.2 */ void mx_button_set_action (MxButton *button, MxAction *action) { MxButtonPrivate *priv; const gchar *display_name; g_return_if_fail (MX_IS_BUTTON (button)); g_return_if_fail (MX_IS_ACTION (action)); priv = button->priv; if (priv->action) g_object_unref (priv->action); if (priv->action_label_binding) g_object_unref (priv->action_label_binding); if (priv->action_icon_binding) g_object_unref (priv->action_icon_binding); priv->action = g_object_ref_sink (action); display_name = mx_action_get_display_name (action); mx_icon_set_icon_name (MX_ICON (priv->icon), mx_action_get_icon (action)); clutter_text_set_text (CLUTTER_TEXT (priv->label), (display_name) ? display_name : ""); /* bind action properties to button properties */ priv->action_label_binding = g_object_bind_property (action, "display-name", priv->label, "text", 0); priv->action_icon_binding = g_object_bind_property (action, "icon", priv->icon, "icon-name", 0); mx_button_update_contents (button); } /** * mx_button_get_action: * @button: A #MxButton * * Retrieves the #MxAction associated with @button. * * Returns: (transfer none): A #MxAction * * Since: 1.2 */ MxAction * mx_button_get_action (MxButton *button) { g_return_val_if_fail (MX_IS_BUTTON (button), NULL); return button->priv->action; } /** * mx_button_set_icon_position: * @button: A #MxButton * @position: A #MxPosition * * Sets the icon position, relative to the text on the button. * * Since: 1.2 */ void mx_button_set_icon_position (MxButton *button, MxPosition position) { MxButtonPrivate *priv; g_return_if_fail (MX_IS_BUTTON (button)); priv = button->priv; if (priv->icon_position != position) { priv->icon_position = position; mx_button_update_contents (button); g_object_notify (G_OBJECT (button), "icon-position"); } } /** * mx_button_get_icon_position: * @button: A #MxButton * * Retrieves the icon's relative position to the text. * * Returns: A #MxPosition * * Since: 1.2 */ MxPosition mx_button_get_icon_position (MxButton *button) { g_return_val_if_fail (MX_IS_BUTTON (button), MX_POSITION_LEFT); return button->priv->icon_position; } /** * mx_button_set_icon_visible: * @button: A #MxButton * @visible: %TRUE if the icon should be visible * * Sets the visibility of the icon associated with the button's action. * * Since: 1.2 */ void mx_button_set_icon_visible (MxButton *button, gboolean visible) { MxButtonPrivate *priv; g_return_if_fail (MX_IS_BUTTON (button)); priv = button->priv; if (priv->icon_visible != visible) { priv->icon_visible = visible; mx_button_update_contents (button); g_object_notify (G_OBJECT (button), "icon-visible"); } } /** * mx_button_get_icon_visible: * @button: A #MxButton * * Retrieves the visibility of the icon associated with the button's action. * * Returns: %TRUE if the icon is visible, %FALSE otherwise * * Since: 1.2 */ gboolean mx_button_get_icon_visible (MxButton *button) { g_return_val_if_fail (MX_IS_BUTTON (button), FALSE); return button->priv->icon_visible; } /** * mx_button_set_label_visible: * @button: A #MxButton * @visible: %TRUE if the text should be visible * * Sets the visibility of the text associated with the button's action. * * Since: 1.2 */ void mx_button_set_label_visible (MxButton *button, gboolean visible) { MxButtonPrivate *priv; g_return_if_fail (MX_IS_BUTTON (button)); priv = button->priv; if (priv->label_visible != visible) { priv->label_visible = visible; mx_button_update_contents (button); g_object_notify (G_OBJECT (button), "label-visible"); } } /** * mx_button_get_label_visible: * @button: A #MxButton * * Retrieves the visibility of the text associated with the button's action. * * Returns: %TRUE if the text is visible, %FALSE otherwise * * Since: 1.2 */ gboolean mx_button_get_label_visible (MxButton *button) { g_return_val_if_fail (MX_IS_BUTTON (button), FALSE); return button->priv->label_visible; } /** * mx_button_get_icon_name: * @button: a #MxButton * * Get the icon-name being used on the button. * * Returns: the icon-name. This must not be freed by the application. %NULL if * no icon has been set * * Since: 1.2 */ const gchar * mx_button_get_icon_name (MxButton *button) { g_return_val_if_fail (MX_IS_BUTTON (button), NULL); return button->priv->icon_name ? button->priv->icon_name : button->priv->style_icon_name; } /** * mx_button_set_icon_name: * @button: a #MxButton * @icon_name: (allow-none): icon-name to use on the button * * Sets the icon-name used to display an icon on the button. Setting %NULL * will remove the icon name, or resort to the icon-name set in the current * style. Setting an icon name overrides any icon set in the style. * * Since: 1.2 */ void mx_button_set_icon_name (MxButton *button, const gchar *icon_name) { MxButtonPrivate *priv; g_return_if_fail (MX_IS_BUTTON (button)); priv = button->priv; g_free (priv->icon_name); priv->icon_name = g_strdup (icon_name); mx_icon_set_icon_name (MX_ICON (priv->icon), icon_name ? icon_name : priv->style_icon_name); mx_button_update_contents (button); g_object_notify (G_OBJECT (button), "icon-name"); } /** * mx_button_get_icon_size: * @button: a #MxButton * * Retrieves the icon-size being used for the displayed icon inside the button. * * Returns: The icon-size being used for the button icon, in pixels * * Since: 1.2 */ guint mx_button_get_icon_size (MxButton *button) { g_return_val_if_fail (MX_IS_BUTTON (button), 0); return button->priv->icon_size ? button->priv->icon_size : button->priv->style_icon_size; } /** * mx_button_set_icon_size: * @button: a #MxButton * * Sets the icon-size to use for the icon displayed inside the button. This will * override the icon-size set in the style. Setting a value of %0 resets to the * size from the style. * * Since: 1.2 */ void mx_button_set_icon_size (MxButton *button, guint icon_size) { MxButtonPrivate *priv; g_return_if_fail (MX_IS_BUTTON (button)); priv = button->priv; if (priv->icon_size != icon_size) { priv->icon_size = icon_size; mx_icon_set_icon_size (MX_ICON (priv->icon), icon_size ? icon_size : priv->style_icon_size); g_object_notify (G_OBJECT (button), "icon-size"); } } mx-1.4.7/mx/mx-button.h000066400000000000000000000105701201047117600147030ustar00rootroot00000000000000/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * mx-button.h: Plain button actor * * Copyright 2007 OpenedHand * Copyright 2008, 2009 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * Boston, MA 02111-1307, USA. * * Written by: Emmanuele Bassi * Thomas Wood * */ #if !defined(MX_H_INSIDE) && !defined(MX_COMPILATION) #error "Only can be included directly.h" #endif #ifndef __MX_BUTTON_H__ #define __MX_BUTTON_H__ G_BEGIN_DECLS #include #include #define MX_TYPE_BUTTON (mx_button_get_type ()) #define MX_BUTTON(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MX_TYPE_BUTTON, MxButton)) #define MX_IS_BUTTON(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MX_TYPE_BUTTON)) #define MX_BUTTON_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MX_TYPE_BUTTON, MxButtonClass)) #define MX_IS_BUTTON_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MX_TYPE_BUTTON)) #define MX_BUTTON_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MX_TYPE_BUTTON, MxButtonClass)) typedef struct _MxButton MxButton; typedef struct _MxButtonPrivate MxButtonPrivate; typedef struct _MxButtonClass MxButtonClass; /** * MxButton: * * The contents of this structure is private and should only be accessed using * the provided API. */ struct _MxButton { /*< private >*/ MxBin parent_instance; MxButtonPrivate *priv; }; struct _MxButtonClass { MxBinClass parent_class; /* signals */ void (* clicked) (MxButton *button); /* padding for future expansion */ void (*_padding_0) (void); void (*_padding_1) (void); void (*_padding_2) (void); void (*_padding_3) (void); void (*_padding_4) (void); }; GType mx_button_get_type (void) G_GNUC_CONST; ClutterActor *mx_button_new (void); ClutterActor *mx_button_new_with_label (const gchar *text); const gchar *mx_button_get_label (MxButton *button); void mx_button_set_label (MxButton *button, const gchar *text); const gchar *mx_button_get_icon_name (MxButton *button); void mx_button_set_icon_name (MxButton *button, const gchar *icon_name); guint mx_button_get_icon_size (MxButton *button); void mx_button_set_icon_size (MxButton *button, guint icon_size); void mx_button_set_is_toggle (MxButton *button, gboolean toggle); gboolean mx_button_get_is_toggle (MxButton *button); void mx_button_set_toggled (MxButton *button, gboolean toggled); gboolean mx_button_get_toggled (MxButton *button); void mx_button_set_action (MxButton *button, MxAction *action); MxAction *mx_button_get_action (MxButton *button); void mx_button_set_icon_position (MxButton *button, MxPosition position); MxPosition mx_button_get_icon_position (MxButton *button); void mx_button_set_icon_visible (MxButton *button, gboolean visible); gboolean mx_button_get_icon_visible (MxButton *button); void mx_button_set_label_visible (MxButton *button, gboolean visible); gboolean mx_button_get_label_visible (MxButton *button); G_END_DECLS #endif /* __MX_BUTTON_H__ */ mx-1.4.7/mx/mx-clipboard-default.c000066400000000000000000000105521201047117600167440ustar00rootroot00000000000000/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * mx-clipboard-default.c: The default clipboard object * * Copyright 2010 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * * Written by: Damien Lespiau * Chris Lord * */ #include "mx-clipboard.h" G_DEFINE_TYPE (MxClipboard, mx_clipboard, G_TYPE_OBJECT) #define CLIPBOARD_PRIVATE(o) \ (G_TYPE_INSTANCE_GET_PRIVATE ((o), MX_TYPE_CLIPBOARD, MxClipboardPrivate)) struct _MxClipboardPrivate { gchar *text; }; static void mx_clipboard_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { switch (property_id) { default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } } static void mx_clipboard_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { switch (property_id) { default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } } static void mx_clipboard_dispose (GObject *object) { G_OBJECT_CLASS (mx_clipboard_parent_class)->dispose (object); } static void mx_clipboard_finalize (GObject *object) { MxClipboardPrivate *priv = MX_CLIPBOARD (object)->priv; g_free (priv->text); G_OBJECT_CLASS (mx_clipboard_parent_class)->finalize (object); } static void mx_clipboard_class_init (MxClipboardClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); g_type_class_add_private (klass, sizeof (MxClipboardPrivate)); object_class->get_property = mx_clipboard_get_property; object_class->set_property = mx_clipboard_set_property; object_class->dispose = mx_clipboard_dispose; object_class->finalize = mx_clipboard_finalize; } static void mx_clipboard_init (MxClipboard *self) { self->priv = CLIPBOARD_PRIVATE (self); } MxClipboard* mx_clipboard_get_default (void) { static MxClipboard *default_clipboard = NULL; if (!default_clipboard) { default_clipboard = g_object_new (MX_TYPE_CLIPBOARD, NULL); } return default_clipboard; } typedef struct { MxClipboard *clipboard; MxClipboardCallbackFunc callback; gpointer user_data; } MxClipboardClosure; static gboolean mx_clipboard_get_text_cb (MxClipboardClosure *closure) { if (closure->clipboard) { MxClipboardPrivate *priv = closure->clipboard->priv; g_object_remove_weak_pointer (G_OBJECT (closure->clipboard), (gpointer *)&closure->clipboard); closure->callback (closure->clipboard, priv->text, closure->user_data); } g_slice_free (MxClipboardClosure, closure); return FALSE; } void mx_clipboard_get_text (MxClipboard *clipboard, MxClipboardCallbackFunc callback, gpointer user_data) { MxClipboardClosure *closure; g_return_if_fail (MX_IS_CLIPBOARD (clipboard)); g_return_if_fail (callback != NULL); closure = g_slice_new (MxClipboardClosure); closure->clipboard = clipboard; closure->callback = callback; closure->user_data = user_data; g_object_add_weak_pointer (G_OBJECT (clipboard), (gpointer *)&closure->clipboard); g_idle_add ((GSourceFunc)mx_clipboard_get_text_cb, closure); } void mx_clipboard_set_text (MxClipboard *clipboard, const gchar *text) { MxClipboardPrivate *priv; g_return_if_fail (MX_IS_CLIPBOARD (clipboard)); g_return_if_fail (text != NULL); priv = clipboard->priv; g_free (priv->text); priv->text = g_strdup (text); } mx-1.4.7/mx/mx-clipboard.h000066400000000000000000000077511201047117600153360ustar00rootroot00000000000000/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * mx-clipboard.h: clipboard object * * Copyright 2009 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * * Written by: Thomas Wood * */ /** * SECTION:mx-clipboard * @short_description: a simple representation clipboard * * #MxClipboard is a very simple object representation of the clipboard * available to applications. Text is always assumed to be UTF-8 and non-text * items are not handled. */ #if !defined(MX_H_INSIDE) && !defined(MX_COMPILATION) #error "Only can be included directly.h" #endif #ifndef _MX_CLIPBOARD_H #define _MX_CLIPBOARD_H #include G_BEGIN_DECLS #define MX_TYPE_CLIPBOARD mx_clipboard_get_type() #define MX_CLIPBOARD(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ MX_TYPE_CLIPBOARD, MxClipboard)) #define MX_CLIPBOARD_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), \ MX_TYPE_CLIPBOARD, MxClipboardClass)) #define MX_IS_CLIPBOARD(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \ MX_TYPE_CLIPBOARD)) #define MX_IS_CLIPBOARD_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE ((klass), \ MX_TYPE_CLIPBOARD)) #define MX_CLIPBOARD_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), \ MX_TYPE_CLIPBOARD, MxClipboardClass)) typedef struct _MxClipboard MxClipboard; typedef struct _MxClipboardClass MxClipboardClass; typedef struct _MxClipboardPrivate MxClipboardPrivate; /** * MxClipboard: * * The contents of this structure is private and should only be accessed using * the provided API. */ struct _MxClipboard { /*< private >*/ GObject parent; MxClipboardPrivate *priv; }; struct _MxClipboardClass { GObjectClass parent_class; /* padding for future expansion */ void (*_padding_0) (void); void (*_padding_1) (void); void (*_padding_2) (void); void (*_padding_3) (void); void (*_padding_4) (void); }; /** * MxClipboardCallbackFunc: * @clipboard: A #MxClipboard * @text: text from the clipboard * @user_data: user data * * Callback function called when text is retrieved from the clipboard. */ typedef void (*MxClipboardCallbackFunc) (MxClipboard *clipboard, const gchar *text, gpointer user_data); GType mx_clipboard_get_type (void); /** * mx_clipboard_get_default: * * Get the global #MxClipboard object that represents the clipboard. * * Returns: (transfer none): a #MxClipboard owned by Mx and must not be * unrefferenced or freed. */ MxClipboard* mx_clipboard_get_default (void); /** * mx_clipboard_get_text: * @clipboard: A #MxClipboard * @callback: (scope async): function to be called when the text is retreived * @user_data: data to be passed to the callback * * Request the data from the clipboard in text form. @callback is executed * when the data is retreived. * */ void mx_clipboard_get_text (MxClipboard *clipboard, MxClipboardCallbackFunc callback, gpointer user_data); /** * mx_clipboard_set_text: * @clipboard: A #MxClipboard * @text: text to copy to the clipboard * * Sets text as the current contents of the clipboard. * */ void mx_clipboard_set_text (MxClipboard *clipboard, const gchar *text); G_END_DECLS #endif /* _MX_CLIPBOARD_H */ mx-1.4.7/mx/mx-combo-box.c000066400000000000000000000673671201047117600152700ustar00rootroot00000000000000/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * mx-combo-box.c: combo box * * Copyright 2009 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * * Written by: Thomas Wood * */ /** * SECTION:mx-combo-box * @short_description: combo box actor * * #MxComboBox combines a button with a popup menu to allow the user to select * an option from a list. */ #include "mx-combo-box.h" #include "mx-menu.h" #include "mx-private.h" #include "mx-stylable.h" static void mx_focusable_iface_init (MxFocusableIface *iface); static void mx_stylable_iface_init (MxStylableIface *iface); G_DEFINE_TYPE_WITH_CODE (MxComboBox, mx_combo_box, MX_TYPE_WIDGET, G_IMPLEMENT_INTERFACE (MX_TYPE_FOCUSABLE, mx_focusable_iface_init) G_IMPLEMENT_INTERFACE (MX_TYPE_STYLABLE, mx_stylable_iface_init)) #define COMBO_BOX_PRIVATE(o) \ (G_TYPE_INSTANCE_GET_PRIVATE ((o), MX_TYPE_COMBO_BOX, MxComboBoxPrivate)) struct _MxComboBoxPrivate { ClutterActor *label; ClutterActor *icon; ClutterActor *marker; GSList *actions; gfloat clip_x; gfloat clip_y; gint index; gint spacing; }; enum { PROP_0, PROP_ACTIVE_TEXT, PROP_ACTIVE_ICON_NAME, PROP_INDEX }; static void mx_stylable_iface_init (MxStylableIface *iface) { static gboolean is_initialised = FALSE; if (!is_initialised) { GParamSpec *pspec; is_initialised = TRUE; pspec = g_param_spec_boxed ("x-mx-marker-image", "Marker image", "Marker image used to denote that a " "combo-box expands on click.", MX_TYPE_BORDER_IMAGE, G_PARAM_READWRITE); mx_stylable_iface_install_property (iface, MX_TYPE_COMBO_BOX, pspec); pspec = g_param_spec_int ("x-mx-spacing", "Spacing", "Spacing to use between elements inside the " "combo-box.", 0, G_MAXINT, 8, G_PARAM_READWRITE); mx_stylable_iface_install_property (iface, MX_TYPE_COMBO_BOX, pspec); } } static void mx_combo_box_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { MxComboBoxPrivate *priv = MX_COMBO_BOX (object)->priv; switch (property_id) { case PROP_ACTIVE_TEXT: g_value_set_string (value, clutter_text_get_text ((ClutterText*) priv->label)); break; case PROP_ACTIVE_ICON_NAME: g_value_set_string (value, mx_combo_box_get_active_icon_name ( MX_COMBO_BOX (object))); break; case PROP_INDEX: g_value_set_int (value, priv->index); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } } static void mx_combo_box_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { MxComboBox *combo = (MxComboBox*) object; switch (property_id) { case PROP_ACTIVE_TEXT: mx_combo_box_set_active_text (combo, g_value_get_string (value)); break; case PROP_ACTIVE_ICON_NAME: mx_combo_box_set_active_icon_name (combo, g_value_get_string (value)); break; case PROP_INDEX: mx_combo_box_set_index (combo, g_value_get_int (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } } static void mx_combo_box_dispose (GObject *object) { MxComboBoxPrivate *priv = MX_COMBO_BOX (object)->priv; if (priv->label) { clutter_actor_destroy (priv->label); priv->label = NULL; } if (priv->icon) { clutter_actor_destroy (priv->icon); priv->icon = NULL; } if (priv->marker) { clutter_actor_destroy (priv->marker); priv->marker = NULL; } G_OBJECT_CLASS (mx_combo_box_parent_class)->dispose (object); } static void mx_combo_box_finalize (GObject *object) { MxComboBoxPrivate *priv = MX_COMBO_BOX (object)->priv; if (priv->actions) { g_slist_foreach (priv->actions, (GFunc) g_object_unref, NULL); g_slist_free (priv->actions); } G_OBJECT_CLASS (mx_combo_box_parent_class)->finalize (object); } static void mx_combo_box_map (ClutterActor *actor) { MxComboBoxPrivate *priv = MX_COMBO_BOX (actor)->priv; CLUTTER_ACTOR_CLASS (mx_combo_box_parent_class)->map (actor); clutter_actor_map (priv->label); if (priv->icon) clutter_actor_map (priv->icon); if (priv->marker) clutter_actor_map (priv->marker); } static void mx_combo_box_unmap (ClutterActor *actor) { MxComboBoxPrivate *priv = MX_COMBO_BOX (actor)->priv; CLUTTER_ACTOR_CLASS (mx_combo_box_parent_class)->unmap (actor); if (priv->label) clutter_actor_unmap (priv->label); if (priv->icon) clutter_actor_unmap (priv->icon); if (priv->marker) clutter_actor_unmap (priv->marker); } static void mx_combo_box_paint (ClutterActor *actor) { MxComboBoxPrivate *priv = MX_COMBO_BOX (actor)->priv; CLUTTER_ACTOR_CLASS (mx_combo_box_parent_class)->paint (actor); clutter_actor_paint (priv->label); if (priv->icon) clutter_actor_paint (priv->icon); if (priv->marker) clutter_actor_paint (priv->marker); } static void mx_combo_box_get_preferred_width (ClutterActor *actor, gfloat for_height, gfloat *min_width_p, gfloat *natural_height_p) { gfloat height; gfloat min_w, nat_w; gfloat min_label_w, nat_label_w; gfloat min_icon_w = 0, nat_icon_w = 0; gfloat min_menu_w = 0, nat_menu_w = 0; gfloat min_marker_w = 0, nat_marker_w = 0; MxComboBoxPrivate *priv = MX_COMBO_BOX (actor)->priv; MxPadding padding; ClutterActor *menu; mx_widget_get_padding (MX_WIDGET (actor), &padding); height = for_height - padding.top - padding.bottom; menu = (ClutterActor *) mx_widget_get_menu (MX_WIDGET (actor)); if (menu) { clutter_actor_get_preferred_width (menu, -1, &min_menu_w, &nat_menu_w); } clutter_actor_get_preferred_width (priv->label, height, &min_label_w, &nat_label_w); min_w = min_label_w; nat_w = nat_label_w; if (priv->icon) { clutter_actor_get_preferred_width (priv->icon, height, &min_icon_w, &nat_icon_w); min_w += min_icon_w + priv->spacing; nat_w += nat_icon_w + priv->spacing; } if (min_menu_w > min_w) min_w = min_menu_w; if (nat_menu_w > nat_w) nat_w = nat_menu_w; if (priv->marker) { clutter_actor_get_preferred_width (priv->marker, height, &min_marker_w, &nat_marker_w); min_w += min_marker_w + priv->spacing; nat_w += nat_marker_w + priv->spacing; } if (min_width_p) *min_width_p = padding.left + padding.right + min_w; if (natural_height_p) *natural_height_p = padding.left + padding.right + nat_w; } static void mx_combo_box_get_preferred_height (ClutterActor *actor, gfloat for_width, gfloat *min_height_p, gfloat *natural_height_p) { MxComboBoxPrivate *priv = MX_COMBO_BOX (actor)->priv; gfloat min_label_h, nat_label_h; gfloat min_icon_h = 0, nat_icon_h = 0; gfloat min_marker_h = 0, nat_marker_h = 0; gfloat min_h, nat_h; MxPadding padding; mx_widget_get_padding (MX_WIDGET (actor), &padding); clutter_actor_get_preferred_height (priv->label, -1, &min_label_h, &nat_label_h); if (priv->icon) { clutter_actor_get_preferred_height (priv->icon, -1, &min_icon_h, &nat_icon_h); } if (priv->marker) { clutter_actor_get_preferred_height (priv->marker, -1, &min_marker_h, &nat_marker_h); } min_h = MAX (MAX (min_icon_h, min_label_h), min_marker_h); nat_h = MAX (MAX (nat_icon_h, nat_label_h), nat_marker_h); if (min_height_p) *min_height_p = padding.top + padding.bottom + min_h; if (natural_height_p) *natural_height_p = padding.top + padding.bottom + nat_h; } static void mx_combo_box_allocate (ClutterActor *actor, const ClutterActorBox *box, ClutterAllocationFlags flags) { MxComboBoxPrivate *priv = MX_COMBO_BOX (actor)->priv; MxPadding padding; gfloat x, y, width, height; gfloat min_menu_h, nat_menu_h; gfloat label_h; gfloat nat_icon_h, icon_h, icon_w; gfloat nat_marker_h, marker_h, marker_w; ClutterActorBox childbox; ClutterActor *menu, *stage; CLUTTER_ACTOR_CLASS (mx_combo_box_parent_class)->allocate (actor, box, flags); mx_widget_get_padding (MX_WIDGET (actor), &padding); x = padding.left; y = padding.top; width = box->x2 - box->x1 - padding.left - padding.right; height = box->y2 - box->y1 - padding.top - padding.bottom; icon_w = marker_w = 0; if (priv->icon) { /* Allocate the icon, if there is one, the space not used by the text */ clutter_actor_get_preferred_height (priv->icon, -1, NULL, &nat_icon_h); if (height >= nat_icon_h) { icon_h = nat_icon_h; clutter_actor_get_preferred_width (priv->icon, -1, NULL, &icon_w); } else { icon_h = height; clutter_actor_get_preferred_width (priv->icon, icon_h, NULL, &icon_w); } childbox.x1 = (int)(x); childbox.y1 = (int)(y + (height - icon_h) / 2); childbox.x2 = (int)(x + icon_w); childbox.y2 = (int)(childbox.y1 + icon_h); clutter_actor_allocate (priv->icon, &childbox, flags); icon_w += priv->spacing; } if (priv->marker) { clutter_actor_get_preferred_height (priv->marker, -1, NULL, &nat_marker_h); if (height >= nat_marker_h) { marker_h = nat_marker_h; clutter_actor_get_preferred_width (priv->marker, -1, NULL, &marker_w); } else { marker_h = height; clutter_actor_get_preferred_width (priv->marker, marker_h, NULL, &marker_w); } childbox.x2 = (int)(x + width); childbox.x1 = (int)(childbox.x2 - marker_w); childbox.y1 = (int)(y + (height - marker_h) / 2); childbox.y2 = (int)(childbox.y1 + marker_h); clutter_actor_allocate (priv->marker, &childbox, flags); marker_w += priv->spacing; } clutter_actor_get_preferred_height (priv->label, -1, NULL, &label_h); childbox.x1 = (int)(x + icon_w); childbox.y1 = (int)(y + (height / 2 - label_h / 2)); childbox.x2 = (int)(x + width - marker_w); childbox.y2 = (int)(childbox.y1 + label_h); clutter_actor_allocate (priv->label, &childbox, flags); menu = (ClutterActor*) mx_widget_get_menu (MX_WIDGET (actor)); clutter_actor_get_preferred_height (menu, (box->x2 - box->x1), &min_menu_h, &nat_menu_h); childbox.x1 = 0; childbox.x2 = (box->x2 - box->x1); childbox.y1 = (box->y2 - box->y1); stage = clutter_actor_get_stage (actor); if (stage != NULL) { ClutterVertex point = { 0, }; gfloat stage_w, stage_h, combo_h = box->y2 - box->y1; clutter_actor_get_size (stage, &stage_w, &stage_h); point.y = combo_h + nat_menu_h; clutter_actor_apply_transform_to_point (actor, &point, &point); /* If the menu would appear off the stage, flip it around. */ if ((point.x < 0) || (point.x >= stage_w) || (point.y < 0) || (point.y >= stage_h)) { childbox.y1 = -nat_menu_h; } } childbox.y2 = childbox.y1 + nat_menu_h; clutter_actor_allocate (menu, &childbox, flags); } static void mx_combo_box_action_activated_cb (ClutterActor *menu, MxAction *action, MxComboBox *box) { gint index; index = GPOINTER_TO_INT (g_object_get_data ((GObject*) action, "index")); mx_combo_box_set_index (box, index); /* reset the combobox style */ mx_stylable_set_style_pseudo_class (MX_STYLABLE (box), NULL); } static void mx_combo_box_update_menu (MxComboBox *box) { MxComboBoxPrivate *priv = box->priv; GSList *l; gint index; MxMenu *menu; menu = mx_widget_get_menu (MX_WIDGET (box)); if (!menu) return; mx_menu_remove_all (menu); for (index = 0, l = priv->actions; l; l = g_slist_next (l), index++) { MxAction *action; action = (MxAction *)l->data; g_object_set_data ((GObject*) action, "index", GINT_TO_POINTER (index)); mx_menu_add_action (menu, action); } /* queue a relayout so the combobox size can match the new menu */ clutter_actor_queue_relayout ((ClutterActor*) box); } static gboolean mx_combo_box_open_menu (MxComboBox *actor) { ClutterActor *menu; menu = (ClutterActor *) mx_widget_get_menu (MX_WIDGET (actor)); if (!menu) return FALSE; clutter_actor_show (menu); return TRUE; } static gboolean mx_combo_box_button_press_event (ClutterActor *actor, ClutterButtonEvent *event) { return mx_combo_box_open_menu (MX_COMBO_BOX (actor)); } static gboolean mx_combo_box_key_press_event (ClutterActor *actor, ClutterKeyEvent *event) { switch (event->keyval) { case CLUTTER_KEY_Return: return mx_combo_box_open_menu (MX_COMBO_BOX (actor)); default: return FALSE; } } static void mx_combo_box_apply_style (MxWidget *widget, MxStyle *style) { MxComboBoxPrivate *priv = MX_COMBO_BOX (widget)->priv; if (priv->icon != NULL) mx_stylable_set_style (MX_STYLABLE (priv->icon), style); } static void mx_combo_box_style_changed (MxComboBox *combo, MxStyleChangedFlags flags) { MxBorderImage *marker_filename; gint spacing; MxComboBoxPrivate *priv = combo->priv; mx_stylable_get (MX_STYLABLE (combo), "x-mx-spacing", &spacing, "x-mx-marker-image", &marker_filename, NULL); if (spacing != priv->spacing) priv->spacing = spacing; if (priv->marker) { clutter_actor_destroy (priv->marker); priv->marker = NULL; } if (marker_filename) { MxTextureCache *cache = mx_texture_cache_get_default (); priv->marker = (ClutterActor *) mx_texture_cache_get_texture (cache, marker_filename->uri); if (priv->marker) clutter_actor_set_parent (priv->marker, CLUTTER_ACTOR (combo)); g_boxed_free (MX_TYPE_BORDER_IMAGE, marker_filename); } mx_stylable_apply_clutter_text_attributes (MX_STYLABLE (combo), CLUTTER_TEXT (combo->priv->label)); /* make sure the popup is also up-to-date */ mx_stylable_style_changed (MX_STYLABLE (mx_widget_get_menu (MX_WIDGET (combo))), flags | MX_STYLE_CHANGED_FORCE); clutter_actor_queue_relayout (CLUTTER_ACTOR (combo)); } static void mx_combo_box_class_init (MxComboBoxClass *klass) { GParamSpec *pspec; GObjectClass *object_class = G_OBJECT_CLASS (klass); ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass); MxWidgetClass *widget_class = MX_WIDGET_CLASS (klass); g_type_class_add_private (klass, sizeof (MxComboBoxPrivate)); object_class->get_property = mx_combo_box_get_property; object_class->set_property = mx_combo_box_set_property; object_class->dispose = mx_combo_box_dispose; object_class->finalize = mx_combo_box_finalize; actor_class->map = mx_combo_box_map; actor_class->unmap = mx_combo_box_unmap; actor_class->paint = mx_combo_box_paint; actor_class->get_preferred_width = mx_combo_box_get_preferred_width; actor_class->get_preferred_height = mx_combo_box_get_preferred_height; actor_class->allocate = mx_combo_box_allocate; actor_class->button_press_event = mx_combo_box_button_press_event; actor_class->key_press_event = mx_combo_box_key_press_event; widget_class->apply_style = mx_combo_box_apply_style; pspec = g_param_spec_string ("active-text", "Active Text", "Text currently displayed in the combo box" " button", "", MX_PARAM_READWRITE); g_object_class_install_property (object_class, PROP_ACTIVE_TEXT, pspec); pspec = g_param_spec_string ("active-icon-name", "Active Icon-name", "Name of the icon currently displayed in the " "combo-box", NULL, MX_PARAM_READWRITE); g_object_class_install_property (object_class, PROP_ACTIVE_ICON_NAME, pspec); pspec = g_param_spec_int ("index", "Index", "Index of the selected item, or -1 if no item is" " selected.", -1, G_MAXINT, -1, MX_PARAM_READWRITE); g_object_class_install_property (object_class, PROP_INDEX, pspec); } static void mx_combo_box_init (MxComboBox *self) { MxComboBoxPrivate *priv; ClutterActor *menu; priv = self->priv = COMBO_BOX_PRIVATE (self); priv->spacing = 8; priv->label = clutter_text_new (); clutter_actor_set_parent (priv->label, (ClutterActor*) self); menu = mx_menu_new (); mx_widget_set_menu (MX_WIDGET (self), MX_MENU (menu)); g_signal_connect (menu, "action-activated", G_CALLBACK (mx_combo_box_action_activated_cb), self); g_signal_connect (self, "style-changed", G_CALLBACK (mx_combo_box_style_changed), NULL); clutter_actor_set_reactive (CLUTTER_ACTOR (self), TRUE); } static MxFocusable * mx_combo_box_accept_focus (MxFocusable *focusable, MxFocusHint hint) { mx_stylable_set_style_pseudo_class (MX_STYLABLE (focusable), "focus"); clutter_actor_grab_key_focus (CLUTTER_ACTOR (focusable)); return focusable; } static MxFocusable * mx_combo_box_move_focus (MxFocusable *focusable, MxFocusDirection direction, MxFocusable *from) { if (focusable == from) { mx_stylable_set_style_pseudo_class (MX_STYLABLE (focusable), ""); } return NULL; } static void mx_focusable_iface_init (MxFocusableIface *iface) { iface->accept_focus = mx_combo_box_accept_focus; iface->move_focus = mx_combo_box_move_focus; } /** * mx_combo_box_new: * * Create a new MxComboBox * * Returns: a newly allocated MxComboBox */ ClutterActor * mx_combo_box_new (void) { return g_object_new (MX_TYPE_COMBO_BOX, NULL); } /** * mx_combo_box_insert_text: * @box: A #MxComboBox * @position: zero indexed position to insert the item at * @text: name of the item * * Insert an item into the combo box list. * */ void mx_combo_box_insert_text (MxComboBox *box, gint position, const gchar *text) { MxAction *action; g_return_if_fail (MX_IS_COMBO_BOX (box)); action = mx_action_new (); mx_action_set_display_name (action, text); box->priv->actions = g_slist_insert (box->priv->actions, g_object_ref_sink (action), position); mx_combo_box_update_menu (box); } /** * mx_combo_box_insert_text_with_icon: * @box: A #MxComboBox * @position: zero indexed position to insert the item at * @text: name of the item * @icon: name of an icon from the icon theme * * Insert an item with text and an icon into the combo box list. * */ void mx_combo_box_insert_text_with_icon (MxComboBox *box, gint position, const gchar *text, const gchar *icon) { MxAction *action; g_return_if_fail (MX_IS_COMBO_BOX (box)); action = mx_action_new (); mx_action_set_display_name (action, text); mx_action_set_icon (action, icon); box->priv->actions = g_slist_insert (box->priv->actions, g_object_ref_sink (action), position); mx_combo_box_update_menu (box); } /** * mx_combo_box_append_text: * @box: A #MxComboBox * @text: name of the item * * Append an item to the combo box list * */ void mx_combo_box_append_text (MxComboBox *box, const gchar *text) { g_return_if_fail (MX_IS_COMBO_BOX (box)); /* -1 pushes it at the end of the list */ mx_combo_box_insert_text (box, -1, text); } /** * mx_combo_box_prepend_text: * @box: A #MxComboBox * @text: name of the item * * Prepend an item to the combo box list * */ void mx_combo_box_prepend_text (MxComboBox *box, const gchar *text) { g_return_if_fail (MX_IS_COMBO_BOX (box)); mx_combo_box_insert_text (box, 0, text); } /** * mx_combo_box_remove_text: * @box: A #MxComboBox * @position: position of the item to remove * * Remove the item at @position * */ void mx_combo_box_remove_text (MxComboBox *box, gint position) { GSList *item; g_return_if_fail (MX_IS_COMBO_BOX (box)); g_return_if_fail (position >= 0); /* find the item, free the string and remove it from the list */ item = g_slist_nth (box->priv->actions, position); if (!item) return; g_object_unref (G_OBJECT (item->data)); box->priv->actions = g_slist_delete_link (box->priv->actions, item); mx_combo_box_update_menu (box); } /** * mx_combo_box_remove_all: * @box: A #MxComboBox * * Remove all the items of @box * * Since: 1.4 */ void mx_combo_box_remove_all (MxComboBox *box) { MxComboBoxPrivate *priv = box->priv; GSList *l; g_return_if_fail (MX_IS_COMBO_BOX (box)); l = priv->actions; while (l) { g_object_unref (l->data); l = g_slist_delete_link (l, l); } priv->actions = NULL; mx_combo_box_update_menu (box); } /** * mx_combo_box_set_active_text: * @box: A #MxComboBox * @text: text to display * * Set the text displayed in the combo box * */ void mx_combo_box_set_active_text (MxComboBox *box, const gchar *text) { g_return_if_fail (MX_IS_COMBO_BOX (box)); box->priv->index = -1; clutter_text_set_text ((ClutterText*) box->priv->label, text); g_object_notify (G_OBJECT (box), "index"); g_object_notify (G_OBJECT (box), "active-text"); } /** * mx_combo_box_get_active_text: * @box: A #MxComboBox * * Get the text displayed in the combo box * * Returns: the text string, owned by the combo box */ const gchar* mx_combo_box_get_active_text (MxComboBox *box) { g_return_val_if_fail (MX_IS_COMBO_BOX (box), NULL); return clutter_text_get_text ((ClutterText*) box->priv->label); } /** * mx_combo_box_set_active_icon_name: * @box: A #MxComboBox * @icon_name: (allow-none): Icon name to use for displayed icon * * Set the icon displayed in the combo box. * */ void mx_combo_box_set_active_icon_name (MxComboBox *box, const gchar *icon_name) { MxComboBoxPrivate *priv; g_return_if_fail (MX_IS_COMBO_BOX (box)); priv = box->priv; if (!priv->icon) { if (icon_name) { MxIconTheme *icon_theme; icon_theme = mx_icon_theme_get_default (); if (mx_icon_theme_has_icon (icon_theme, icon_name)) { priv->icon = mx_icon_new (); mx_icon_set_icon_name (MX_ICON (priv->icon), icon_name); clutter_actor_set_parent (priv->icon, CLUTTER_ACTOR (box)); } } } else { if (icon_name) mx_icon_set_icon_name (MX_ICON (priv->icon), icon_name); else { clutter_actor_destroy (priv->icon); priv->icon = NULL; clutter_actor_queue_relayout (CLUTTER_ACTOR (box)); } } priv->index = -1; g_object_notify (G_OBJECT (box), "index"); g_object_notify (G_OBJECT (box), "active-icon-name"); } /** * mx_combo_box_get_active_icon_name: * @box: A #MxComboBox * * Get the name of the icon displayed in the combo box * * Returns: the text string of the name of the displayed icon, owned by * the combo box, or %NULL if there is no active icon. */ const gchar * mx_combo_box_get_active_icon_name (MxComboBox *box) { MxComboBoxPrivate *priv; g_return_val_if_fail (MX_IS_COMBO_BOX (box), NULL); priv = box->priv; if (priv->icon) return mx_icon_get_icon_name (MX_ICON (priv->icon)); else return NULL; } /** * mx_combo_box_set_index: * @box: A #MxComboBox * @index: the index of the list item to set * * Set the current combo box text from the item at @index in the list. * */ void mx_combo_box_set_index (MxComboBox *box, gint index) { MxComboBoxPrivate *priv; GSList *item; MxAction *action; const gchar *icon_name; g_return_if_fail (MX_IS_COMBO_BOX (box)); priv = box->priv; item = g_slist_nth (box->priv->actions, index); if (!item) { box->priv->index = -1; clutter_text_set_text ((ClutterText*) box->priv->label, ""); return; } box->priv->index = index; action = (MxAction *)item->data; clutter_text_set_text ((ClutterText*) box->priv->label, mx_action_get_display_name (action)); if (priv->icon) { clutter_actor_unparent (priv->icon); priv->icon = NULL; } icon_name = mx_action_get_icon (item->data); if (icon_name) { MxIconTheme *icon_theme; icon_theme = mx_icon_theme_get_default (); if (mx_icon_theme_has_icon (icon_theme, icon_name)) { priv->icon = mx_icon_new (); mx_icon_set_icon_name (MX_ICON (priv->icon), icon_name); clutter_actor_set_parent (priv->icon, CLUTTER_ACTOR (box)); } } g_object_notify (G_OBJECT (box), "index"); g_object_notify (G_OBJECT (box), "active-text"); g_object_notify (G_OBJECT (box), "active-icon-name"); } /** * mx_combo_box_get_index: * @box: A #MxComboBox * * Get the index of the last item selected * * Returns: gint */ gint mx_combo_box_get_index (MxComboBox *box) { g_return_val_if_fail (MX_IS_COMBO_BOX (box), 0); return box->priv->index; } mx-1.4.7/mx/mx-combo-box.h000066400000000000000000000072641201047117600152630ustar00rootroot00000000000000/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * mx-combo-box.h: combo box * * Copyright 2009 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * * Written by: Thomas Wood * */ #if !defined(MX_H_INSIDE) && !defined(MX_COMPILATION) #error "Only can be included directly.h" #endif #ifndef _MX_COMBO_BOX_H #define _MX_COMBO_BOX_H #include #include G_BEGIN_DECLS #define MX_TYPE_COMBO_BOX mx_combo_box_get_type() #define MX_COMBO_BOX(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ MX_TYPE_COMBO_BOX, MxComboBox)) #define MX_COMBO_BOX_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), \ MX_TYPE_COMBO_BOX, MxComboBoxClass)) #define MX_IS_COMBO_BOX(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \ MX_TYPE_COMBO_BOX)) #define MX_IS_COMBO_BOX_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE ((klass), \ MX_TYPE_COMBO_BOX)) #define MX_COMBO_BOX_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), \ MX_TYPE_COMBO_BOX, MxComboBoxClass)) typedef struct _MxComboBox MxComboBox; typedef struct _MxComboBoxClass MxComboBoxClass; typedef struct _MxComboBoxPrivate MxComboBoxPrivate; /** * MxComboBox: * * The contents of this structure are private and should only be accessed * through the public API. */ struct _MxComboBox { /*< private >*/ MxWidget parent; MxComboBoxPrivate *priv; }; struct _MxComboBoxClass { MxWidgetClass parent_class; /* padding for future expansion */ void (*_padding_0) (void); void (*_padding_1) (void); void (*_padding_2) (void); void (*_padding_3) (void); void (*_padding_4) (void); }; GType mx_combo_box_get_type (void); ClutterActor *mx_combo_box_new (void); void mx_combo_box_insert_text (MxComboBox *box, gint position, const gchar *text); void mx_combo_box_insert_text_with_icon (MxComboBox *box, gint position, const gchar *text, const gchar *icon); void mx_combo_box_append_text (MxComboBox *box, const gchar *text); void mx_combo_box_prepend_text (MxComboBox *box, const gchar *text); void mx_combo_box_remove_text (MxComboBox *box, gint position); void mx_combo_box_remove_all (MxComboBox *box); void mx_combo_box_set_active_text (MxComboBox *box, const gchar *text); const gchar* mx_combo_box_get_active_text (MxComboBox *box); void mx_combo_box_set_active_icon_name (MxComboBox *box, const gchar *icon_name); const gchar* mx_combo_box_get_active_icon_name (MxComboBox *box); void mx_combo_box_set_index (MxComboBox *box, gint index); gint mx_combo_box_get_index (MxComboBox *box); G_END_DECLS #endif /* _MX_COMBO_BOX_H */ mx-1.4.7/mx/mx-create-image-cache.c000066400000000000000000000256711201047117600167570ustar00rootroot00000000000000/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * makecache.c: creating a texture cache * * Copyright 2009 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * Boston, MA 02111-1307, USA. * */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #ifndef PATH_MAX #define PATH_MAX 1024 #endif struct imgcache_element { char filename[256]; int width, height; int posX, posY; void *ptr; }; GList *images; int totalarea = 0; int sizes[] = { 0, 256, 384, 512, 640, 768, 896, 1024, 1280, 1536, 1792, 2048, -1}; static gint sort_by_size(gconstpointer a, gconstpointer b) { const struct imgcache_element *A=a, *B=b; if (A->height > B->height) return -1; if (A->height < B->height) return 1; if (A->width > B->width) return -1; if (A->width < B->width) return 1; return 0; } static void do_one_file(char *filename) { GdkPixbuf *image; struct imgcache_element *element; if (strlen(filename) >= 256) return; image = gdk_pixbuf_new_from_file(filename, NULL); if (!image) return; /* we don't want to do files > 256x256 */ if (gdk_pixbuf_get_width(image) > 256) { gdk_pixbuf_unref(image); return; } if (gdk_pixbuf_get_height(image) > 256) { gdk_pixbuf_unref(image); return; } element = malloc(sizeof(struct imgcache_element)); if (!element) { gdk_pixbuf_unref(image); return; } memset(element, 0, sizeof(struct imgcache_element)); element->width = gdk_pixbuf_get_width(image); element->height = gdk_pixbuf_get_height(image); element->posX = -1; element->posY = -1; element->ptr = image; totalarea += element->width * element->height; strncpy(element->filename, filename, 256); if (element->width > sizes[0]) sizes[0] = element->width; images = g_list_append(images, element); } static int get_effective_Y(int X1, int X2) { int Y = 0; GList *item; struct imgcache_element *element; item = g_list_first(images); while (item) { element = item->data; item = g_list_next(item); if (element->posX == -1) continue; if (element->posX >= X2) continue; if (element->posX + element->width <= X1) continue; /* at this point we know the element overlaps our range */ if (element->posY + element->height > Y) Y = element->posY + element->height; } return Y; } static inline uint32_t get_pixel(struct imgcache_element *element, int X, int Y) { GdkPixbuf *buf; unsigned char *p; buf = element->ptr; p = gdk_pixbuf_get_pixels(buf); p += Y * gdk_pixbuf_get_rowstride(buf); p += X * 4; return p[0] | (p[1] << 8) | (p[2] << 16) | (p[3] << 24); } static int can_scooch(struct imgcache_element *one, struct imgcache_element *two) { int i,x,y; if (!one) return 0; if (!two) return 0; if (one->posY != two->posY) return 0; if (one->height != two->height) return 0; if (one->posX + one->width != two->posX) return 0; i = 0; while (i < one->width && i < two->width) { i ++; for (y = 0; y < one->height; y++) { for (x = 0; x < i; x++ ) { int p1, p2; p1 = get_pixel(one, one->width - i + x,y); p2 = get_pixel(two, x, y); if ( p1 != p2) return i-1; } } } return i; } static int do_placement(int maxX) { int maxY; int X, Y, baseY; int tempY, tempX, biggestX = -1; struct imgcache_element *element, *element2, *prev = NULL; GList *item, *item2; int workdone = 1; maxY = 0; X = 0; Y = 0; while (workdone) { workdone = 0; X = 0; item = g_list_first(images); while (item) { int sc; element = item->data; item = g_list_next(item); if (element->posX != -1) /* already placed */ continue; /* first, check if we fit */ if (X + element->width > maxX) continue; tempY = get_effective_Y(X, X + element->width); element->posX = X; element->posY = tempY; workdone ++; sc = can_scooch(prev, element); element->posX -= sc; if (element->posY + element->height > maxY) maxY = element->posY + element->height; prev = element; /* shortcut .. find other icons that fit us without expanding vertically*/ tempX = X; tempY += element->height; repeat: biggestX = -1; item2 = item; baseY = element->posY + element->height; while (item2 && !sc) { element2 = item2->data; item2 = g_list_next(item2); if (element2->posX != -1) continue; if (tempX + element2->width > X + element->width) continue; if (element2->height > maxY - baseY) continue; tempY = get_effective_Y(tempX, tempX+element2->width); if (element2->height > maxY - tempY) continue; element2->posX = tempX; element2->posY = tempY; if (element2->posX + element2->width > biggestX && element2->width != element->width) biggestX = element2->posX + element2->width; if (element2->width == element->width) biggestX = -1; workdone++; prev = NULL; } if (biggestX > 0) { tempX = biggestX; goto repeat; } X = element->posX + element->width; } } X = 0; Y = 0; /* find the bounding box */ item = g_list_first(images); while (item) { element = item->data; item = g_list_next(item); if (element->posX + element->width > X) X = element->posX + element->width; if (element->posY + element->height > Y) Y = element->posY + element->height; } /* penalties for getting too big for a texture */ if (X > 2048) X *= 4; if (Y > 2048) Y *= 4; return X * Y; } static void clear_placement(void) { struct imgcache_element *element; GList *item; /* find the bounding box */ item = g_list_first(images); while (item) { element = item->data; item = g_list_next(item); element->posX = -1; element->posY = -1; } } static void optimal_placement(void) { int bestX = -1; int best_score = INT_MAX; int score; int i; int minX; i = 0; /* skip X values that would lead to a too large Y dimension */ minX = totalarea / 2048; if (minX > 1000) minX = 0; while (sizes[i] > 0) { if (sizes[i] < minX) { i++; continue; } score = do_placement(sizes[i]); if (score < best_score) { bestX = sizes[i]; best_score = score; } clear_placement(); i++; } do_placement(bestX); printf("Best score is %i, %0.1f %% waste\n", best_score, (100.0*best_score / totalarea) -100.0); } static int make_final_image(char *filename) { int maxX = 0, maxY = 0; struct imgcache_element *element; GList *item; GdkPixbuf *final; /* find the bounding box */ item = g_list_first(images); while (item) { element = item->data; item = g_list_next(item); if (element->posX + element->width > maxX) maxX = element->posX + element->width; if (element->posY + element->height > maxY) maxY = element->posY + element->height; } printf("Final image is %ix%i\n", maxX, maxY); if (!maxX) return 0; if (!maxY) return 0; final = gdk_pixbuf_new(GDK_COLORSPACE_RGB, TRUE, 8, maxX, maxY); assert(final != NULL); item = g_list_first(images); while (item) { element = item->data; item = g_list_next(item); if (element->posX == -1) continue; gdk_pixbuf_copy_area(element->ptr, 0, 0, element->width, element->height, final, element->posX, element->posY); } gdk_pixbuf_save(final, filename, "png", NULL, NULL); return 1; } static void makecache(char *directory, int recurse) { GDir *dir; GError *error = NULL; dir = g_dir_open (directory, 0, &error); if (!dir) { printf("Error opening %s: %s\n", directory, error->message); g_clear_error (&error); return; } while (TRUE) { const char *name = g_dir_read_name (dir); char *fullpath = g_build_filename (directory, name, NULL); if (!name) break; if (name[0] == '.') continue; if (recurse && g_file_test (fullpath, G_FILE_TEST_IS_DIR)) { makecache(fullpath, recurse); } if (recurse && g_file_test (fullpath, G_FILE_TEST_IS_REGULAR)) { do_one_file(fullpath); } g_free (fullpath); } g_dir_close (dir); images = g_list_sort(images, sort_by_size); } static void write_cache_file(char *directory, char *pngfile) { FILE *file; char *filename = NULL; struct imgcache_element element; struct imgcache_element *elm; GList *item; memset(&element, 0, sizeof(element)); strcpy(&element.filename[0], pngfile); filename = g_strdup_printf("%s/mx.cache", directory); file = fopen(filename, "w"); if (!file) { fprintf(stderr, "Cannot write cache file: %s\n", filename); g_free (filename); return; } fwrite(&element, 1, sizeof(element), file); item = g_list_first(images); while (item) { elm = item->data; item = g_list_next(item); elm->ptr = NULL; fwrite(elm, 1, sizeof(element), file); } g_free (filename); fclose(file); } int main(int argc, char **argv) { char *image_file = NULL; if (argc <= 1) { printf("Usage:\n\t\tmakecache \n"); return EXIT_FAILURE; } g_type_init(); makecache(argv[1], 1); optimal_placement(); image_file = g_strdup_printf("/var/cache/mx/%08x.png", g_str_hash(argv[1])); if (make_final_image(image_file)) write_cache_file(argv[1], image_file); g_free (image_file); return EXIT_SUCCESS; } mx-1.4.7/mx/mx-css.c000066400000000000000000000603051201047117600141540ustar00rootroot00000000000000/* * Copyright 2009 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * * Author: Thomas Wood * */ #include "mx-css.h" #include #include #include #include #include "mx-private.h" struct _MxStyleSheet { GList *selectors; GList *styles; GList *filenames; }; typedef struct _MxSelector MxSelector; struct _MxSelector { gchar *type; gchar *id; gchar *class; gchar *pseudo_class; MxSelector *parent; MxSelector *ancestor; GHashTable *style; const gchar *filename; /* origin of this selector */ guint line; guint position; gint priority; }; /* MxStyleSheetValue */ static MxStyleSheetValue * mx_style_sheet_value_new () { return g_slice_new0 (MxStyleSheetValue); } static void mx_style_sheet_value_free (MxStyleSheetValue *value) { g_slice_free (MxStyleSheetValue, value); } static gchar* append (gchar *str1, const gchar *str2) { gchar *tmp; if (!str1) return g_strdup (str2); if (!str2) return str1; tmp = g_strconcat (str1, str2, NULL); g_free (str1); return tmp; } static gchar* appendc (gchar *str, gchar c) { gchar *tmp; gint len; if (str == NULL) { tmp = g_malloc (2); len = 0; } else { len = strlen (str); tmp = g_realloc (str, len + 2); } tmp[len] = c; tmp[len + 1] = '\0'; return tmp; } static GTokenType css_parse_key_value (GScanner *scanner, gchar **key, gchar **value) { GTokenType token; gboolean start_with_dash = FALSE; gchar *id_first = scanner->config->cset_identifier_first; gchar *id_nth = scanner->config->cset_identifier_nth; guint scan_identifier_1char = scanner->config->scan_identifier_1char; /* parse property name */ token = g_scanner_get_next_token (scanner); /* allow property names to start with '-' */ if (token == '-') { /* FIXME: this will now ignore whitepsace and comments, but this should * not be allowed in the middle of a property name! */ token = g_scanner_get_next_token (scanner); start_with_dash = TRUE; } if (token != G_TOKEN_IDENTIFIER) return G_TOKEN_IDENTIFIER; if (start_with_dash) *key = g_strconcat ("-", scanner->value.v_identifier, NULL); else *key = g_strdup (scanner->value.v_identifier); token = g_scanner_get_next_token (scanner); if (token != ':') return ':'; /* value parsing options */ scanner->config->cset_identifier_first = G_CSET_a_2_z "#_-0123456789" G_CSET_A_2_Z G_CSET_LATINS G_CSET_LATINC; scanner->config->cset_identifier_nth = scanner->config->cset_identifier_first; scanner->config->scan_identifier_1char = 1; scanner->config->char_2_token = FALSE; scanner->config->cset_skip_characters = "\n"; /* parse value */ while (scanner->next_value.v_char != ';') { token = g_scanner_get_next_token (scanner); switch (token) { case G_TOKEN_IDENTIFIER: *value = append (*value, scanner->value.v_identifier); break; case G_TOKEN_CHAR: *value = appendc (*value, scanner->value.v_char); break; default: return ';'; } g_scanner_peek_next_token (scanner); } /* semi colon */ g_scanner_get_next_token (scanner); if (scanner->value.v_char != ';') return ';'; /* we've come to the end of the value, so reset the options */ scanner->config->cset_identifier_nth = id_nth; scanner->config->cset_identifier_first = id_first; scanner->config->scan_identifier_1char = scan_identifier_1char; scanner->config->char_2_token = TRUE; scanner->config->cset_skip_characters = " \t\n"; /* strip the leading and trailing whitespace */ g_strstrip (*value); return G_TOKEN_NONE; } static GTokenType css_parse_style (GScanner *scanner, GHashTable *table) { GTokenType token; /* { */ token = g_scanner_get_next_token (scanner); if (token != G_TOKEN_LEFT_CURLY) return G_TOKEN_LEFT_CURLY; /* keep going until we find '}' */ token = g_scanner_peek_next_token (scanner); while (token != G_TOKEN_RIGHT_CURLY) { gchar *key = NULL, *value = NULL; token = css_parse_key_value (scanner, &key, &value); if (token != G_TOKEN_NONE) return token; g_hash_table_insert (table, key, value); token = g_scanner_peek_next_token (scanner); } /* } */ token = g_scanner_get_next_token (scanner); if (token != G_TOKEN_RIGHT_CURLY) return G_TOKEN_RIGHT_CURLY; return G_TOKEN_NONE; } static GTokenType css_parse_simple_selector (GScanner *scanner, MxSelector *selector) { guint token; gchar *tmp; /* parse optional type (either '*' or an identifier) */ token = g_scanner_peek_next_token (scanner); switch (token) { case '*': g_scanner_get_next_token (scanner); selector->type = g_strdup ("*"); break; case G_TOKEN_IDENTIFIER: g_scanner_get_next_token (scanner); selector->type = g_strdup (scanner->value.v_identifier); break; default: break; } /* Here we look for '#', '.' or ':' and return if we find anything else */ token = g_scanner_peek_next_token (scanner); while (token != G_TOKEN_NONE) { switch (token) { /* id */ case '#': g_scanner_get_next_token (scanner); token = g_scanner_get_next_token (scanner); if (token != G_TOKEN_IDENTIFIER) return G_TOKEN_IDENTIFIER; selector->id = g_strdup (scanner->value.v_identifier); break; /* class */ case '.': g_scanner_get_next_token (scanner); token = g_scanner_get_next_token (scanner); if (token != G_TOKEN_IDENTIFIER) return G_TOKEN_IDENTIFIER; selector->class = g_strdup (scanner->value.v_identifier); break; /* pseudo-class */ case ':': g_scanner_get_next_token (scanner); token = g_scanner_get_next_token (scanner); if (token != G_TOKEN_IDENTIFIER) return G_TOKEN_IDENTIFIER; tmp = selector->pseudo_class; if (selector->pseudo_class) selector->pseudo_class = g_strconcat (selector->pseudo_class, ":", scanner->value.v_identifier, NULL); else selector->pseudo_class = g_strdup (scanner->value.v_identifier); g_free (tmp); break; /* unhandled */ default: return G_TOKEN_NONE; break; } token = g_scanner_peek_next_token (scanner); } return G_TOKEN_NONE; } static char* selector_to_string (MxSelector *selector) { gchar *ancestor, *string, *tmp, *parent, *ret; if (!selector) return NULL; tmp = selector_to_string (selector->ancestor); if (tmp) ancestor = g_strconcat (tmp, " ", NULL); else ancestor = NULL; g_free (tmp); tmp = selector_to_string (selector->parent); if (tmp) parent = g_strconcat (tmp, " > ", NULL); else parent = NULL; g_free (tmp); string = g_strdup_printf ("%s%s%s%s%s%s%s", (selector->type) ? selector->type : "", (selector->class) ? "." : "", (selector->class) ? selector->class : "", (selector->id) ? "#" : "", (selector->id) ? selector->id : "", (selector->pseudo_class) ? ":" : "", (selector->pseudo_class) ? selector->pseudo_class : ""); ret = g_strconcat ((ancestor) ? ancestor : "", (parent) ? parent : "", string, NULL); g_free (string); g_free (ancestor); g_free (parent); return ret; } static void print_selector (MxSelector *selector, gint score) { gchar *string; string = selector_to_string (selector); MX_NOTE (CSS, "%5d: %s", score, string); g_free (string); } static MxSelector * mx_selector_new (const gchar *filename, gint priority, guint line, guint position) { MxSelector *s; s = g_slice_new0 (MxSelector); s->filename = filename; s->priority = priority; s->position = position; s->line = line; return s; } static void mx_selector_free (MxSelector *selector) { if (!selector) return; g_free (selector->type); g_free (selector->id); g_free (selector->class); g_free (selector->pseudo_class); mx_selector_free (selector->parent); g_slice_free (MxSelector, selector); } static GTokenType css_parse_ruleset (GScanner *scanner, GList **selectors) { guint token; MxSelector *selector, *parent; /* parse the first selector, then keep going until we find left curly */ token = g_scanner_peek_next_token (scanner); parent = NULL; selector = NULL; while (token != G_TOKEN_LEFT_CURLY) { switch (token) { case G_TOKEN_IDENTIFIER: case '*': case '#': case '.': case ':': if (selector) parent = selector; else parent = NULL; /* check if there was a previous selector and if so, the new one * should use the previous selector to match an ancestor */ selector = mx_selector_new (scanner->input_name, GPOINTER_TO_INT (scanner->user_data), scanner->line, scanner->position); *selectors = g_list_prepend (*selectors, selector); if (parent) { *selectors = g_list_remove (*selectors, parent); selector->ancestor = parent; } token = css_parse_simple_selector (scanner, selector); if (token != G_TOKEN_NONE) return token; break; case '>': g_scanner_get_next_token (scanner); if (!selector) { g_warning ("NULL parent when parsing '>'"); } parent = selector; selector = mx_selector_new (scanner->input_name, GPOINTER_TO_INT (scanner->user_data), scanner->line, scanner->position); *selectors = g_list_prepend (*selectors, selector); /* remove parent from list of selectors and link it to the new * selector */ selector->parent = parent; *selectors = g_list_remove (*selectors, parent); token = css_parse_simple_selector (scanner, selector); if (token != G_TOKEN_NONE) return token; break; case ',': g_scanner_get_next_token (scanner); selector = mx_selector_new (scanner->input_name, GPOINTER_TO_INT (scanner->user_data), scanner->line, scanner->position); *selectors = g_list_prepend (*selectors, selector); token = css_parse_simple_selector (scanner, selector); if (token != G_TOKEN_NONE) return token; break; default: g_scanner_get_next_token (scanner); g_scanner_unexp_token (scanner, G_TOKEN_ERROR, NULL, NULL, NULL, "Unhandled selector", 1); return '{'; } token = g_scanner_peek_next_token (scanner); } return G_TOKEN_NONE; } static GTokenType css_parse_block (GScanner *scanner, GList **selectors, GList **styles) { GTokenType token; GHashTable *table; GList *l, *list = NULL; token = css_parse_ruleset (scanner, &list); if (token != G_TOKEN_NONE) return token; /* create a hash table for the properties */ table = g_hash_table_new_full (g_str_hash, g_direct_equal, g_free, (GDestroyNotify) mx_style_sheet_value_free); token = css_parse_style (scanner, table); /* assign all the selectors to this style */ for (l = list; l; l = l->next) { MxSelector* sl; sl = (MxSelector*) l->data; sl->style = table; } *styles = g_list_append (*styles, table); *selectors = g_list_concat (*selectors, list); return token; } static gboolean css_parse_file (MxStyleSheet *sheet, gchar *filename, gint priority) { GScanner *scanner; int fd; GTokenType token; fd = open (filename, O_RDONLY); if (fd == -1) return FALSE; scanner = g_scanner_new (NULL); scanner->input_name = filename; scanner->user_data = GINT_TO_POINTER (priority); /* turn off single line comments, we need to parse '#' */ scanner->config->cpair_comment_single = "\1\n"; scanner->config->cset_identifier_nth = G_CSET_a_2_z "-_0123456789" G_CSET_A_2_Z G_CSET_LATINS G_CSET_LATINC; scanner->config->scan_float = FALSE; /* allows scanning '.' */ scanner->config->scan_hex = FALSE; scanner->config->scan_string_sq = FALSE; scanner->config->scan_string_dq = FALSE; g_scanner_input_file (scanner, fd); token = g_scanner_peek_next_token (scanner); while (token != G_TOKEN_EOF) { token = css_parse_block (scanner, &sheet->selectors, &sheet->styles); if (token != G_TOKEN_NONE) break; token = g_scanner_peek_next_token (scanner); } if (token != G_TOKEN_EOF) g_scanner_unexp_token (scanner, token, NULL, NULL, NULL, "Error", TRUE); close (fd); g_scanner_destroy (scanner); if (token == G_TOKEN_EOF) return TRUE; else return FALSE; } static gboolean list_contains (const gchar *needle, gint needle_len, const gchar *haystack, gchar seperator) { const gchar *start; for (start = haystack; start; start = strchr (start, seperator)) { gint len; gchar *next_seperator; /* move to the character after the separator */ if (start[0] == seperator) start = start + 1; /* find the end of this haystack item */ next_seperator = strchr (start, seperator); if (!next_seperator) len = strlen (start); else len = next_seperator - start; /* if the item in the haystack is not the same length as the separator, * then it is not a match */ if (len != needle_len) continue; if (!strncmp (needle, start, needle_len)) return TRUE; else continue; } /* needle not found */ return FALSE; } static gint css_node_matches_selector (MxSelector *selector, MxStylable *stylable) { gint score; gint a, b, c; const gchar *type = "*"; const gchar *class, *pseudo_class; const gchar *id; ClutterActor *actor; MxStylable *parent; a = 0; b = 0; c = 0; /* get properties for this stylable */ id = clutter_actor_get_name (CLUTTER_ACTOR (stylable)); class = mx_stylable_get_style_class (stylable); pseudo_class = mx_stylable_get_style_pseudo_class (stylable); /* check type */ if (selector->type == NULL || selector->type[0] == '*') { /* NULL or universal selector match, but are ignored for score */ } else { GType type_id; gint matched; gint depth; type_id = G_OBJECT_CLASS_TYPE (G_OBJECT_GET_CLASS (stylable)); type = g_type_name (type_id); matched = FALSE; depth = 10; while (type) { if (!strcmp (selector->type, type)) { matched = depth; break; } else { type_id = g_type_parent (type_id); type = g_type_name (type_id); if (depth > 1) depth--; } } if (!matched) return -1; else c += depth; } /* check id */ if (selector->id) { if (!id || strcmp (selector->id, id)) return -1; else a += 10; } /* check pseudo_class */ if (selector->pseudo_class) { gchar *needle; gint n_matches; /* if no pseudo class is supplied on the node, return instantly */ if (!pseudo_class) return -1; /* check that each pseudo-class from the selector appears in the * pseudo-classes from the node, i.e. the selector pseudo-class list * is a subset of the node's pseudo-class list */ n_matches = 0; for (needle = selector->pseudo_class; needle; needle = strchr (needle, ':')) { gint needle_len; gchar *next; /* move beyond ':' */ if (needle[0] == ':') needle++; /* calculate the length of this needle */ next = strchr (needle, ':'); if (next) needle_len = next - needle; else needle_len = strlen (needle); /* if the pseudo-class from the selector does not appear in the * list of pseudo-classes from the node, then this is not a * match */ if (!list_contains (needle, needle_len, pseudo_class, ':')) return -1; else n_matches++; } /* increase the 'b' score by the number of pseudo-classes in the * selector */ b = b + (10 * n_matches); } /* check class */ if (selector->class) { if (!class || strcmp (selector->class, class)) return -1; else b += 10; } /* check parent */ actor = clutter_actor_get_parent (CLUTTER_ACTOR (stylable)); if (MX_IS_STYLABLE (actor)) parent = MX_STYLABLE (actor); else parent = NULL; if (selector->parent) { gint parent_matches; if (!parent) return -1; parent_matches = css_node_matches_selector (selector->parent, parent); if (parent_matches < 0) return -1; /* increase the 'c' score, since the parent matched */ c += parent_matches; } /* check ancestor */ if (selector->ancestor) { gint ancestor_matches; MxStylable *pparent, *ancestor; ClutterActor *parent_actor; if (!parent) return -1; ancestor = parent; while (ancestor) { parent_actor = clutter_actor_get_parent (CLUTTER_ACTOR (ancestor)); if (MX_IS_STYLABLE (parent_actor)) pparent = MX_STYLABLE (parent_actor); else pparent = NULL; ancestor_matches = css_node_matches_selector (selector->ancestor, ancestor); /* if one of the ancestors match, stop search and increase 'c' score */ if (ancestor_matches >= 0) { c += ancestor_matches; break; } ancestor = pparent; if (!ancestor || !MX_IS_STYLABLE (ancestor)) return -1; } } a = a * 10000; b = b * 100; score = a + b + c; return score; } typedef struct _SelectorMatch { MxSelector *selector; gint score; } SelectorMatch; static gint compare_selector_matches (SelectorMatch *a, SelectorMatch *b) { gint priority; guint line; guint position; gint score; if ((score = a->score - b->score) != 0) return score; else if ((priority = a->selector->priority - b->selector->priority) != 0) return priority; else if ((line = a->selector->line - b->selector->line) != 0) return line; else if ((position = a->selector->position - b->selector->position) != 0) return position; else return 0; } struct _css_table_copy_data { GHashTable *table; const gchar *filename; }; static void css_table_copy (gpointer *key, gpointer *value, struct _css_table_copy_data *data) { MxStyleSheetValue *css_value; css_value = mx_style_sheet_value_new (); css_value->source = data->filename; css_value->string = (gchar*) value; g_hash_table_insert (data->table, key, css_value); } static void free_selector_match (SelectorMatch *data) { g_slice_free (SelectorMatch, data); } GHashTable * mx_style_sheet_get_properties (MxStyleSheet *sheet, MxStylable *node) { GTimer *timer = NULL; GList *l, *matching_selectors = NULL; SelectorMatch *selector_match = NULL; GHashTable *result; if (_mx_debug (MX_DEBUG_CSS)) { const char *id = clutter_actor_get_name (CLUTTER_ACTOR (node)); const char *class = mx_stylable_get_style_class (node); const char *pseudo_class = mx_stylable_get_style_pseudo_class (node); const char *type_name = G_OBJECT_TYPE_NAME (node); timer = g_timer_new (); g_print ("\x1b[1m"); MX_NOTE (CSS, "Matches for: %s%s%s%s%s%s%s", (type_name) ? type_name : "", (class) ? "." : "", (class) ? class : "", (id) ? "#" : "", (id) ? id : "", (pseudo_class) ? ":" : "", (pseudo_class) ? pseudo_class : ""); g_print ("\x1b[22m"); } /* find matching selectors */ for (l = sheet->selectors; l; l = l->next) { gint score; score = css_node_matches_selector (l->data, node); if (score >= 0) { selector_match = g_slice_new (SelectorMatch); selector_match->selector = l->data; selector_match->score = score; matching_selectors = g_list_prepend (matching_selectors, selector_match); } } /* score the selectors by their score */ matching_selectors = g_list_sort (matching_selectors, (GCompareFunc) compare_selector_matches); /* get properties from selector's styles */ result = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, (GDestroyNotify)mx_style_sheet_value_free); for (l = matching_selectors; l; l = l->next) { SelectorMatch *match = l->data; struct _css_table_copy_data copy_data; copy_data.filename = match->selector->filename; copy_data.table = result; g_hash_table_foreach (match->selector->style, (GHFunc) css_table_copy, ©_data); if (_mx_debug (MX_DEBUG_CSS)) print_selector (match->selector, match->score); } g_list_foreach (matching_selectors, (GFunc) free_selector_match, NULL); g_list_free (matching_selectors); if (_mx_debug (MX_DEBUG_CSS)) { g_print ("\x1b[2m"); MX_NOTE (CSS, "%fs", g_timer_elapsed (timer, NULL)); g_print ("\x1b[0m"); g_timer_destroy (timer); } return result; } MxStyleSheet * mx_style_sheet_new () { return g_new0 (MxStyleSheet, 1); } void mx_style_sheet_destroy (MxStyleSheet *sheet) { g_list_foreach (sheet->selectors, (GFunc) mx_selector_free, NULL); g_list_free (sheet->selectors); g_list_foreach (sheet->styles, (GFunc) g_hash_table_destroy, NULL); g_list_free (sheet->styles); g_list_foreach (sheet->filenames, (GFunc) g_free, NULL); g_list_free (sheet->filenames); g_free (sheet); } gboolean mx_style_sheet_add_from_file (MxStyleSheet *sheet, const gchar *filename, GError **error) { gboolean result; gchar *input_name; g_return_val_if_fail (sheet != NULL, FALSE); g_return_val_if_fail (error == NULL || *error != NULL, FALSE); g_return_val_if_fail (filename != NULL, FALSE); input_name = g_strdup (filename); result = css_parse_file (sheet, input_name, g_list_length (sheet->filenames)); sheet->filenames = g_list_prepend (sheet->filenames, input_name); return result; } mx-1.4.7/mx/mx-css.h000066400000000000000000000031371201047117600141610ustar00rootroot00000000000000/* * Copyright 2009 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * * Author: Thomas Wood * */ #ifndef MX_CSS_H #define MX_CSS_H #include #include "mx-stylable.h" typedef struct _MxNode MxNode; typedef struct _MxStyleSheetValue MxStyleSheetValue; typedef struct _MxStyleSheet MxStyleSheet; struct _MxNode { gchar *type; gchar *id; gchar *class; gchar *pseudo_class; MxNode *parent; }; struct _MxStyleSheetValue { const gchar *string; const gchar *source; }; MxStyleSheet* mx_style_sheet_new (); void mx_style_sheet_destroy (); gboolean mx_style_sheet_add_from_file (MxStyleSheet *sheet, const gchar *filename, GError **error); GHashTable* mx_style_sheet_get_properties (MxStyleSheet *sheet, MxStylable *node); #endif /* MX_CSS_H */ mx-1.4.7/mx/mx-deform-bow-tie.c000066400000000000000000000231641201047117600162060ustar00rootroot00000000000000/* * mx-deform-bow-tie.h: A bow-tie deformation actor * * Copyright 2010 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * Boston, MA 02111-1307, USA. * * Written by: Chris Lord * */ #include "mx-deform-bow-tie.h" #include "mx-private.h" #include G_DEFINE_TYPE (MxDeformBowTie, mx_deform_bow_tie, MX_TYPE_DEFORM_TEXTURE) #define DEFORM_BOW_TIE_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), \ MX_TYPE_DEFORM_BOW_TIE, MxDeformBowTiePrivate)) struct _MxDeformBowTiePrivate { gdouble period; ClutterTexture *back; gboolean flip_back; gulong back_id; }; enum { PROP_0, PROP_PERIOD, PROP_FLIP_BACK }; static void mx_deform_bow_tie_texture_vflip (ClutterTexture *texture) { CoglHandle material; material = clutter_texture_get_cogl_material (texture); if (material) { CoglMatrix matrix; cogl_matrix_init_identity (&matrix); /* Vflip */ cogl_matrix_scale (&matrix, 1.f, -1.f, 1.f); cogl_matrix_translate (&matrix, 0.f, 1.f, 0.f); cogl_material_set_layer_matrix (material, 0, &matrix); } } static void mx_deform_bow_tie_texture_reset (ClutterTexture *texture) { CoglHandle material; material = clutter_texture_get_cogl_material (texture); if (material) { CoglMatrix matrix; cogl_matrix_init_identity (&matrix); cogl_material_set_layer_matrix (material, 0, &matrix); } } static void mx_deform_bow_tie_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { MxDeformBowTiePrivate *priv = MX_DEFORM_BOW_TIE (object)->priv; switch (property_id) { case PROP_PERIOD: g_value_set_double (value, priv->period); break; case PROP_FLIP_BACK: g_value_set_boolean (value, priv->flip_back); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } } static void mx_deform_bow_tie_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { switch (property_id) { case PROP_PERIOD: mx_deform_bow_tie_set_period (MX_DEFORM_BOW_TIE (object), g_value_get_double (value)); break; case PROP_FLIP_BACK: mx_deform_bow_tie_set_flip_back (MX_DEFORM_BOW_TIE (object), g_value_get_boolean (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void mx_deform_bow_tie_deform (MxDeformTexture *texture, CoglTextureVertex *vertex, gfloat width, gfloat height) { gfloat cx, cy, rx, ry, turn_angle, height_radius; guint shade; MxDeformBowTiePrivate *priv = ((MxDeformBowTie *)texture)->priv; cx = priv->period * (width + width/2); cy = height/2; rx = ((vertex->x - cx) * cos (0)) - ((vertex->y - cy) * sin (0)); ry = ((vertex->x - cx) * sin (0)) + ((vertex->y - cy) * cos (0)); /* Make angle as a function of distance from the curl ray */ turn_angle = MAX (-G_PI, MIN (0, (rx / (width/4)) * G_PI_2)); /* Add a gradient that makes it look like lighting */ shade = (cos (turn_angle * 2) * 96) + 159; cogl_color_set_from_4ub (&vertex->color, shade, shade, shade, 0xff); /* Calculate the point on a cone (note, a cone, not a right cone) */ height_radius = ry; /*ClutterFixed height_radius = clutter_qmulx (clutter_qdivx (ry, height/2), height/2);*/ ry = height_radius * cos (turn_angle); vertex->x = (rx * cos (0)) - (ry * sin (0)) + cx; vertex->y = (rx * sin (0)) + (ry * cos (0)) + cy; vertex->z = height_radius * sin (turn_angle); } static void mx_deform_bow_tie_back_notify (MxDeformBowTie *self, GParamSpec *pspec) { MxDeformBowTiePrivate *priv = self->priv; if (priv->back_id && priv->back) { g_signal_handler_disconnect (priv->back, priv->back_id); priv->back_id = 0; } if (priv->back) { if (priv->flip_back) mx_deform_bow_tie_texture_reset (priv->back); g_object_remove_weak_pointer (G_OBJECT (priv->back), (gpointer *)&priv->back); priv->back = NULL; } mx_deform_texture_get_textures (MX_DEFORM_TEXTURE (self), NULL, &priv->back); if (priv->back) { g_object_add_weak_pointer (G_OBJECT (priv->back), (gpointer)&priv->back); if (priv->flip_back) { priv->back_id = g_signal_connect (priv->back, "notify::cogl-texture", G_CALLBACK (mx_deform_bow_tie_texture_vflip), self); mx_deform_bow_tie_texture_vflip (priv->back); } } } static void mx_deform_bow_tie_dispose (GObject *object) { MxDeformBowTiePrivate *priv = MX_DEFORM_BOW_TIE (object)->priv; if (priv->back_id && priv->back) { g_signal_handler_disconnect (priv->back, priv->back_id); priv->back_id = 0; } if (priv->back) { CoglHandle material; /* Reset layer matrix */ material = clutter_texture_get_cogl_material (priv->back); if (material) { CoglMatrix matrix; cogl_matrix_init_identity (&matrix); cogl_material_set_layer_matrix (material, 0, &matrix); } g_object_remove_weak_pointer (G_OBJECT (priv->back), (gpointer *)&priv->back); priv->back = NULL; } G_OBJECT_CLASS (mx_deform_bow_tie_parent_class)->dispose (object); } static void mx_deform_bow_tie_class_init (MxDeformBowTieClass *klass) { GParamSpec *pspec; GObjectClass *object_class = G_OBJECT_CLASS (klass); MxDeformTextureClass *deform_class = MX_DEFORM_TEXTURE_CLASS (klass); g_type_class_add_private (klass, sizeof (MxDeformBowTiePrivate)); object_class->get_property = mx_deform_bow_tie_get_property; object_class->set_property = mx_deform_bow_tie_set_property; object_class->dispose = mx_deform_bow_tie_dispose; deform_class->deform = mx_deform_bow_tie_deform; pspec = g_param_spec_double ("period", "Period", "Effect period", 0.0, 1.0, 0.0, MX_PARAM_READWRITE); g_object_class_install_property (object_class, PROP_PERIOD, pspec); pspec = g_param_spec_boolean ("flip-back", "Flip back-face", "Apply a vertical flip transformation to the " "back face.", TRUE, MX_PARAM_READWRITE); g_object_class_install_property (object_class, PROP_PERIOD, pspec); } static void mx_deform_bow_tie_init (MxDeformBowTie *self) { self->priv = DEFORM_BOW_TIE_PRIVATE (self); self->priv->flip_back = TRUE; g_signal_connect (self, "notify::back", G_CALLBACK (mx_deform_bow_tie_back_notify), NULL); } ClutterActor * mx_deform_bow_tie_new (void) { return g_object_new (MX_TYPE_DEFORM_BOW_TIE, NULL); } gdouble mx_deform_bow_tie_get_period (MxDeformBowTie *bow_tie) { g_return_val_if_fail (MX_IS_DEFORM_BOW_TIE (bow_tie), 0.0); return bow_tie->priv->period; } void mx_deform_bow_tie_set_period (MxDeformBowTie *bow_tie, gdouble period) { g_return_if_fail (MX_IS_DEFORM_BOW_TIE (bow_tie)); if (bow_tie->priv->period != period) { bow_tie->priv->period = period; g_object_notify (G_OBJECT (bow_tie), "period"); mx_deform_texture_invalidate (MX_DEFORM_TEXTURE (bow_tie)); } } gboolean mx_deform_bow_tie_get_flip_back (MxDeformBowTie *bow_tie) { g_return_val_if_fail (MX_IS_DEFORM_BOW_TIE (bow_tie), FALSE); return bow_tie->priv->flip_back; } void mx_deform_bow_tie_set_flip_back (MxDeformBowTie *bow_tie, gboolean flip_back) { MxDeformBowTiePrivate *priv; g_return_if_fail (MX_IS_DEFORM_BOW_TIE (bow_tie)); priv = bow_tie->priv; if (priv->flip_back != flip_back) { priv->flip_back = flip_back; if (priv->back) { if (priv->back_id) g_signal_handler_disconnect (priv->back, priv->back_id); if (flip_back) { priv->back_id = g_signal_connect (priv->back, "notify::cogl-texture", G_CALLBACK (mx_deform_bow_tie_texture_vflip), bow_tie); mx_deform_bow_tie_texture_vflip (priv->back); } else { mx_deform_bow_tie_texture_reset (priv->back); priv->back_id = 0; } } g_object_notify (G_OBJECT (bow_tie), "flip-back"); } } mx-1.4.7/mx/mx-deform-bow-tie.h000066400000000000000000000055351201047117600162150ustar00rootroot00000000000000/* * mx-deform-bow-tie.h: A bow-tie deformation actor * * Copyright 2010 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * Boston, MA 02111-1307, USA. * * Written by: Chris Lord * */ #if !defined(MX_H_INSIDE) && !defined(MX_COMPILATION) #error "Only can be included directly.h" #endif #ifndef _MX_DEFORM_BOW_TIE_H #define _MX_DEFORM_BOW_TIE_H #include #include G_BEGIN_DECLS #define MX_TYPE_DEFORM_BOW_TIE mx_deform_bow_tie_get_type() #define MX_DEFORM_BOW_TIE(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ MX_TYPE_DEFORM_BOW_TIE, MxDeformBowTie)) #define MX_DEFORM_BOW_TIE_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), \ MX_TYPE_DEFORM_BOW_TIE, MxDeformBowTieClass)) #define MX_IS_DEFORM_BOW_TIE(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \ MX_TYPE_DEFORM_BOW_TIE)) #define MX_IS_DEFORM_BOW_TIE_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE ((klass), \ MX_TYPE_DEFORM_BOW_TIE)) #define MX_DEFORM_BOW_TIE_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), \ MX_TYPE_DEFORM_BOW_TIE, MxDeformBowTieClass)) typedef struct _MxDeformBowTie MxDeformBowTie; typedef struct _MxDeformBowTieClass MxDeformBowTieClass; typedef struct _MxDeformBowTiePrivate MxDeformBowTiePrivate; /** * MxDeformBowTie: * * The contents of this structure is private and should only be accessed using * the provided API. */ struct _MxDeformBowTie { MxDeformTexture parent; MxDeformBowTiePrivate *priv; }; struct _MxDeformBowTieClass { MxDeformTextureClass parent_class; /* padding for future expansion */ void (*_padding_0) (void); void (*_padding_1) (void); void (*_padding_2) (void); void (*_padding_3) (void); void (*_padding_4) (void); }; GType mx_deform_bow_tie_get_type (void) G_GNUC_CONST; ClutterActor *mx_deform_bow_tie_new (void); gdouble mx_deform_bow_tie_get_period (MxDeformBowTie *bow_tie); void mx_deform_bow_tie_set_period (MxDeformBowTie *bow_tie, gdouble period); gboolean mx_deform_bow_tie_get_flip_back (MxDeformBowTie *bow_tie); void mx_deform_bow_tie_set_flip_back (MxDeformBowTie *bow_tie, gboolean flip_back); G_END_DECLS #endif /* _MX_DEFORM_BOW_TIE_H */ mx-1.4.7/mx/mx-deform-page-turn.c000066400000000000000000000204501201047117600165350ustar00rootroot00000000000000/* * mx-deform-page-turn.c: A page-turning deformation actor * * Copyright 2010 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * Boston, MA 02111-1307, USA. * * Written by: Chris Lord * */ #include "mx-deform-page-turn.h" #include "mx-private.h" #include G_DEFINE_TYPE (MxDeformPageTurn, mx_deform_page_turn, MX_TYPE_DEFORM_TEXTURE) #define DEFORM_PAGE_TURN_PRIVATE(o) \ (G_TYPE_INSTANCE_GET_PRIVATE ((o), \ MX_TYPE_DEFORM_PAGE_TURN, MxDeformPageTurnPrivate)) struct _MxDeformPageTurnPrivate { gdouble period; gdouble angle; gdouble radius; }; enum { PROP_0, PROP_PERIOD, PROP_ANGLE, PROP_RADIUS, }; static void mx_deform_page_turn_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { MxDeformPageTurnPrivate *priv = MX_DEFORM_PAGE_TURN (object)->priv; switch (property_id) { case PROP_PERIOD: g_value_set_double (value, priv->period); break; case PROP_ANGLE: g_value_set_double (value, priv->angle); break; case PROP_RADIUS: g_value_set_double (value, priv->radius); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } } static void mx_deform_page_turn_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { switch (property_id) { case PROP_PERIOD: mx_deform_page_turn_set_period (MX_DEFORM_PAGE_TURN (object), g_value_get_double (value)); break; case PROP_ANGLE: mx_deform_page_turn_set_angle (MX_DEFORM_PAGE_TURN (object), g_value_get_double (value)); break; case PROP_RADIUS: mx_deform_page_turn_set_radius (MX_DEFORM_PAGE_TURN (object), g_value_get_double (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void mx_deform_page_turn_deform (MxDeformTexture *texture, CoglTextureVertex *vertex, gfloat width, gfloat height) { gfloat cx, cy, rx, ry, turn_angle; guint shade; MxDeformPageTurnPrivate *priv = ((MxDeformPageTurn *)texture)->priv; /* Rotate the point around the centre of the page-curl ray to align it with * the y-axis. */ cx = (1.f - priv->period) * width; cy = (1.f - priv->period) * height; rx = ((vertex->x - cx) * cos (-priv->angle)) - ((vertex->y - cy) * sin (-priv->angle)) - priv->radius; ry = ((vertex->x - cx) * sin (-priv->angle)) + ((vertex->y - cy) * cos (-priv->angle)); turn_angle = 0.f; if (rx > -priv->radius * 2) { /* Calculate the curl angle as a function from the distance of the curl * ray (i.e. the page crease) */ turn_angle = (rx / priv->radius * G_PI_2) - G_PI_2; shade = (sin (turn_angle) * 96) + 159; /* Add a gradient that makes it look like lighting and hides the switch * between textures. */ cogl_color_set_from_4ub (&vertex->color, shade, shade, shade, 0xff); } if (rx > 0) { /* Make the curl radius smaller as more circles are formed (stops * z-fighting and looks cool) */ /* Note, 10 is a semi-arbitrary number here - * divide it by two and it's the amount of space between curled * layers of the texture, in pixels. */ gfloat small_radius = priv->radius - MIN (priv->radius, (turn_angle * 10) / G_PI); /* Calculate a point on a cylinder (maybe make this a cone at some point) * and rotate it by the specified angle. */ rx = (small_radius * cos (turn_angle)) + priv->radius; vertex->x = (rx * cos (priv->angle)) - (ry * sin (priv->angle)) + cx; vertex->y = (rx * sin (priv->angle)) + (ry * cos (priv->angle)) + cy; vertex->z = (small_radius * sin (turn_angle)) + priv->radius; } } static void mx_deform_page_turn_class_init (MxDeformPageTurnClass *klass) { GParamSpec *pspec; GObjectClass *object_class = G_OBJECT_CLASS (klass); MxDeformTextureClass *deform_class = MX_DEFORM_TEXTURE_CLASS (klass); g_type_class_add_private (klass, sizeof (MxDeformPageTurnPrivate)); object_class->get_property = mx_deform_page_turn_get_property; object_class->set_property = mx_deform_page_turn_set_property; deform_class->deform = mx_deform_page_turn_deform; pspec = g_param_spec_double ("period", "Period", "Effect period", 0.0, 1.0, 0.0, MX_PARAM_READWRITE); g_object_class_install_property (object_class, PROP_PERIOD, pspec); pspec = g_param_spec_double ("angle", "Angle", "Effect rotation angle", 0.0, G_PI * 2, 0.0, MX_PARAM_READWRITE); g_object_class_install_property (object_class, PROP_ANGLE, pspec); pspec = g_param_spec_double ("radius", "Radius", "Radius of the page curl", G_MINDOUBLE, G_MAXDOUBLE, 24.0, MX_PARAM_READWRITE); g_object_class_install_property (object_class, PROP_RADIUS, pspec); } static void mx_deform_page_turn_init (MxDeformPageTurn *self) { MxDeformPageTurnPrivate *priv = self->priv = DEFORM_PAGE_TURN_PRIVATE (self); priv->radius = 24.0; } ClutterActor * mx_deform_page_turn_new (void) { return g_object_new (MX_TYPE_DEFORM_PAGE_TURN, NULL); } gdouble mx_deform_page_turn_get_period (MxDeformPageTurn *page_turn) { g_return_val_if_fail (MX_IS_DEFORM_PAGE_TURN (page_turn), 0.0); return page_turn->priv->period; } void mx_deform_page_turn_set_period (MxDeformPageTurn *page_turn, gdouble period) { MxDeformPageTurnPrivate *priv; g_return_if_fail (MX_IS_DEFORM_PAGE_TURN (page_turn)); priv = page_turn->priv; if (priv->period != period) { priv->period = period; g_object_notify (G_OBJECT (page_turn), "period"); mx_deform_texture_invalidate (MX_DEFORM_TEXTURE (page_turn)); } } gdouble mx_deform_page_turn_get_angle (MxDeformPageTurn *page_turn) { g_return_val_if_fail (MX_IS_DEFORM_PAGE_TURN (page_turn), 0.0); return page_turn->priv->angle; } void mx_deform_page_turn_set_angle (MxDeformPageTurn *page_turn, gdouble angle) { MxDeformPageTurnPrivate *priv; g_return_if_fail (MX_IS_DEFORM_PAGE_TURN (page_turn)); priv = page_turn->priv; if (priv->angle != angle) { priv->angle = angle; g_object_notify (G_OBJECT (page_turn), "angle"); mx_deform_texture_invalidate (MX_DEFORM_TEXTURE (page_turn)); } } gdouble mx_deform_page_turn_get_radius (MxDeformPageTurn *page_turn) { g_return_val_if_fail (MX_IS_DEFORM_PAGE_TURN (page_turn), 0.0); return page_turn->priv->radius; } void mx_deform_page_turn_set_radius (MxDeformPageTurn *page_turn, gdouble radius) { MxDeformPageTurnPrivate *priv; g_return_if_fail (MX_IS_DEFORM_PAGE_TURN (page_turn)); priv = page_turn->priv; if (priv->radius != radius) { priv->radius = radius; g_object_notify (G_OBJECT (page_turn), "radius"); mx_deform_texture_invalidate (MX_DEFORM_TEXTURE (page_turn)); } } mx-1.4.7/mx/mx-deform-page-turn.h000066400000000000000000000062141201047117600165440ustar00rootroot00000000000000/* * mx-deform-page-turn.h: A page-turning deformation actor * * Copyright 2010 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * Boston, MA 02111-1307, USA. * * Written by: Chris Lord * */ #if !defined(MX_H_INSIDE) && !defined(MX_COMPILATION) #error "Only can be included directly.h" #endif #ifndef _MX_DEFORM_PAGE_TURN_H #define _MX_DEFORM_PAGE_TURN_H #include #include G_BEGIN_DECLS #define MX_TYPE_DEFORM_PAGE_TURN mx_deform_page_turn_get_type() #define MX_DEFORM_PAGE_TURN(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ MX_TYPE_DEFORM_PAGE_TURN, MxDeformPageTurn)) #define MX_DEFORM_PAGE_TURN_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), \ MX_TYPE_DEFORM_PAGE_TURN, MxDeformPageTurnClass)) #define MX_IS_DEFORM_PAGE_TURN(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \ MX_TYPE_DEFORM_PAGE_TURN)) #define MX_IS_DEFORM_PAGE_TURN_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE ((klass), \ MX_TYPE_DEFORM_PAGE_TURN)) #define MX_DEFORM_PAGE_TURN_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), \ MX_TYPE_DEFORM_PAGE_TURN, MxDeformPageTurnClass)) /** * MxDeformPageTurn: * * The contents of this structure is private and should only be accessed using * the provided API. */ typedef struct _MxDeformPageTurn MxDeformPageTurn; typedef struct _MxDeformPageTurnClass MxDeformPageTurnClass; typedef struct _MxDeformPageTurnPrivate MxDeformPageTurnPrivate; struct _MxDeformPageTurn { MxDeformTexture parent; MxDeformPageTurnPrivate *priv; }; struct _MxDeformPageTurnClass { MxDeformTextureClass parent_class; /* padding for future expansion */ void (*_padding_0) (void); void (*_padding_1) (void); void (*_padding_2) (void); void (*_padding_3) (void); void (*_padding_4) (void); }; GType mx_deform_page_turn_get_type (void) G_GNUC_CONST; ClutterActor *mx_deform_page_turn_new (void); gdouble mx_deform_page_turn_get_period (MxDeformPageTurn *page_turn); void mx_deform_page_turn_set_period (MxDeformPageTurn *page_turn, gdouble period); gdouble mx_deform_page_turn_get_angle (MxDeformPageTurn *page_turn); void mx_deform_page_turn_set_angle (MxDeformPageTurn *page_turn, gdouble angle); gdouble mx_deform_page_turn_get_radius (MxDeformPageTurn *page_turn); void mx_deform_page_turn_set_radius (MxDeformPageTurn *page_turn, gdouble radius); G_END_DECLS #endif /* _MX_DEFORM_PAGE_TURN_H */ mx-1.4.7/mx/mx-deform-texture.c000066400000000000000000000542071201047117600163420ustar00rootroot00000000000000/* * mx-deform-texture.c: A texture deformation actor * * Copyright 2010 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * Boston, MA 02111-1307, USA. * * Written by: Chris Lord * */ /** * SECTION:mx-deform-texture * @short_description: Deformable texture abstract-widget * * An abstract widget that provides the interface for producing mesh * deformation effects with a texture. */ #include "mx-deform-texture.h" #include "mx-offscreen.h" #include "mx-private.h" G_DEFINE_ABSTRACT_TYPE (MxDeformTexture, mx_deform_texture, MX_TYPE_WIDGET) #define DEFORM_TEXTURE_PRIVATE(o) \ (G_TYPE_INSTANCE_GET_PRIVATE ((o), MX_TYPE_DEFORM_TEXTURE, MxDeformTexturePrivate)) struct _MxDeformTexturePrivate { gint tiles_x; gint tiles_y; CoglHandle vbo; gint n_indices; CoglHandle indices; CoglHandle bf_indices; CoglTextureVertex *vertices; ClutterActor *front; ClutterActor *back; gboolean dirty; }; enum { PROP_0, PROP_TILES_X, PROP_TILES_Y, PROP_FRONT, PROP_BACK, }; static void mx_deform_texture_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { MxDeformTexturePrivate *priv = MX_DEFORM_TEXTURE (object)->priv; switch (property_id) { case PROP_TILES_X: g_value_set_int (value, priv->tiles_x); break; case PROP_TILES_Y: g_value_set_int (value, priv->tiles_y); break; case PROP_FRONT: g_value_set_object (value, priv->front); break; case PROP_BACK: g_value_set_object (value, priv->back); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } } static void mx_deform_texture_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { MxDeformTexture *texture = MX_DEFORM_TEXTURE (object); MxDeformTexturePrivate *priv = texture->priv; switch (property_id) { case PROP_TILES_X: mx_deform_texture_set_resolution (texture, g_value_get_int (value), priv->tiles_y); break; case PROP_TILES_Y: mx_deform_texture_set_resolution (texture, priv->tiles_x, g_value_get_int (value)); break; case PROP_FRONT: mx_deform_texture_set_textures (texture, (ClutterTexture *) g_value_get_object (value), (ClutterTexture *)priv->back); break; case PROP_BACK: mx_deform_texture_set_textures (texture, (ClutterTexture *)priv->front, (ClutterTexture *) g_value_get_object (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } } static void mx_deform_texture_free_arrays (MxDeformTexture *self) { MxDeformTexturePrivate *priv = self->priv; if (priv->vbo) { cogl_handle_unref (priv->vbo); priv->vbo = NULL; } if (priv->indices) { cogl_handle_unref (priv->indices); priv->indices = NULL; } g_free (priv->vertices); priv->vertices = NULL; } static void mx_deform_texture_dispose (GObject *object) { MxDeformTexture *self = MX_DEFORM_TEXTURE (object); MxDeformTexturePrivate *priv = self->priv; mx_deform_texture_free_arrays (self); if (priv->front) { clutter_actor_unparent (priv->front); priv->front = NULL; } if (priv->back) { clutter_actor_unparent (priv->back); priv->back = NULL; } G_OBJECT_CLASS (mx_deform_texture_parent_class)->dispose (object); } static void mx_deform_texture_finalize (GObject *object) { G_OBJECT_CLASS (mx_deform_texture_parent_class)->finalize (object); } static void mx_deform_texture_paint (ClutterActor *actor) { gint i, j; gboolean depth, cull; CoglHandle front_material, back_material; MxDeformTexture *self = MX_DEFORM_TEXTURE (actor); MxDeformTexturePrivate *priv = self->priv; if (priv->dirty) { guint opacity; gfloat width, height; opacity = clutter_actor_get_paint_opacity (actor); clutter_actor_get_size (actor, &width, &height); for (i = 0; i <= priv->tiles_y; i++) { for (j = 0; j <= priv->tiles_x; j++) { CoglTextureVertex *vertex = &priv->vertices[(i * (priv->tiles_x + 1)) + j]; vertex->tx = j/(gfloat)priv->tiles_x; vertex->ty = i/(gfloat)priv->tiles_y; vertex->x = width * vertex->tx; vertex->y = height * vertex->ty; vertex->z = 0; cogl_color_set_from_4ub (&vertex->color, 0xff, 0xff, 0xff, opacity); MX_DEFORM_TEXTURE_GET_CLASS (self)-> deform (self, vertex, width, height); } } /* We add all three attributes again, although in an ideal case, * we'd add only those that had changed. Because we provide the * ability to change each, unless we had a 'changed' gboolean * in * the function prototype, we have to upload all of it. */ cogl_vertex_buffer_add (priv->vbo, "gl_Vertex", 3, COGL_ATTRIBUTE_TYPE_FLOAT, FALSE, sizeof (CoglTextureVertex), &priv->vertices->x); cogl_vertex_buffer_add (priv->vbo, "gl_MultiTexCoord0", 2, COGL_ATTRIBUTE_TYPE_FLOAT, FALSE, sizeof (CoglTextureVertex), &priv->vertices->tx); cogl_vertex_buffer_add (priv->vbo, "gl_Color", 4, COGL_ATTRIBUTE_TYPE_UNSIGNED_BYTE, FALSE, sizeof (CoglTextureVertex), &priv->vertices->color); cogl_vertex_buffer_submit (priv->vbo); priv->dirty = FALSE; } /* Get materials and update FBOs if necessary */ front_material = back_material = NULL; if (priv->front) { if (MX_IS_OFFSCREEN (priv->front) && mx_offscreen_get_auto_update (MX_OFFSCREEN (priv->front))) mx_offscreen_update (MX_OFFSCREEN (priv->front)); front_material = clutter_texture_get_cogl_material (CLUTTER_TEXTURE (priv->front)); } if (priv->back) { if (MX_IS_OFFSCREEN (priv->back) && mx_offscreen_get_auto_update (MX_OFFSCREEN (priv->back))) mx_offscreen_update (MX_OFFSCREEN (priv->back)); back_material = clutter_texture_get_cogl_material (CLUTTER_TEXTURE (priv->back)); } depth = cogl_get_depth_test_enabled (); if (!depth) cogl_set_depth_test_enabled (TRUE); cull = cogl_get_backface_culling_enabled (); if (back_material && !cull) cogl_set_backface_culling_enabled (TRUE); else if (!back_material && cull) cogl_set_backface_culling_enabled (FALSE); if (front_material) { cogl_set_source (front_material); cogl_vertex_buffer_draw_elements (priv->vbo, COGL_VERTICES_MODE_TRIANGLE_STRIP, priv->indices, 0, (priv->tiles_x + 1) * (priv->tiles_y + 1), 0, priv->n_indices); } if (back_material) { cogl_set_source (back_material); cogl_vertex_buffer_draw_elements (priv->vbo, COGL_VERTICES_MODE_TRIANGLE_STRIP, priv->bf_indices, 0, (priv->tiles_x + 1) * (priv->tiles_y + 1), 0, priv->n_indices); } if (!depth) cogl_set_depth_test_enabled (FALSE); if (back_material && !cull) cogl_set_backface_culling_enabled (FALSE); else if (!back_material && cull) cogl_set_backface_culling_enabled (TRUE); } static void mx_deform_texture_get_preferred_width (ClutterActor *actor, gfloat for_height, gfloat *min_width_p, gfloat *natural_width_p) { MxDeformTexturePrivate *priv = MX_DEFORM_TEXTURE (actor)->priv; if (priv->front || priv->back) { clutter_actor_get_preferred_width (priv->front ? priv->front : priv->back, for_height, min_width_p, natural_width_p); } else { if (min_width_p) *min_width_p = 0; if (natural_width_p) *natural_width_p = 0; return; } } static void mx_deform_texture_get_preferred_height (ClutterActor *actor, gfloat for_width, gfloat *min_height_p, gfloat *natural_height_p) { MxDeformTexturePrivate *priv = MX_DEFORM_TEXTURE (actor)->priv; if (priv->front || priv->back) { clutter_actor_get_preferred_height (priv->front ? priv->front : priv->back, for_width, min_height_p, natural_height_p); } else { if (min_height_p) *min_height_p = 0; if (natural_height_p) *natural_height_p = 0; return; } } static void mx_deform_texture_allocate (ClutterActor *actor, const ClutterActorBox *box, ClutterAllocationFlags flags) { ClutterActorBox child_box; MxDeformTexturePrivate *priv = MX_DEFORM_TEXTURE (actor)->priv; /* The size has changed, so make sure we recalculate values */ priv->dirty = TRUE; /* Chain up */ CLUTTER_ACTOR_CLASS (mx_deform_texture_parent_class)-> allocate (actor, box, flags); child_box.x1 = 0; child_box.y1 = 0; child_box.x2 = box->x2 - box->x1; child_box.y2 = box->y2 - box->y1; if (priv->front) clutter_actor_allocate (priv->front, &child_box, flags); if (priv->back) clutter_actor_allocate (priv->back, &child_box, flags); } static void mx_deform_texture_map (ClutterActor *actor) { MxDeformTexturePrivate *priv = MX_DEFORM_TEXTURE (actor)->priv; CLUTTER_ACTOR_CLASS (mx_deform_texture_parent_class)->map (actor); if (priv->front) clutter_actor_map (priv->front); if (priv->back) clutter_actor_map (priv->back); } static void mx_deform_texture_unmap (ClutterActor *actor) { MxDeformTexturePrivate *priv = MX_DEFORM_TEXTURE (actor)->priv; if (priv->front) clutter_actor_unmap (priv->front); if (priv->back) clutter_actor_unmap (priv->back); CLUTTER_ACTOR_CLASS (mx_deform_texture_parent_class)->unmap (actor); } static void mx_deform_texture_class_init (MxDeformTextureClass *klass) { GParamSpec *pspec; GObjectClass *object_class = G_OBJECT_CLASS (klass); ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass); g_type_class_add_private (klass, sizeof (MxDeformTexturePrivate)); object_class->get_property = mx_deform_texture_get_property; object_class->set_property = mx_deform_texture_set_property; object_class->dispose = mx_deform_texture_dispose; object_class->finalize = mx_deform_texture_finalize; actor_class->get_preferred_width = mx_deform_texture_get_preferred_width; actor_class->get_preferred_height = mx_deform_texture_get_preferred_height; actor_class->allocate = mx_deform_texture_allocate; actor_class->paint = mx_deform_texture_paint; actor_class->map = mx_deform_texture_map; actor_class->unmap = mx_deform_texture_unmap; pspec = g_param_spec_int ("tiles-x", "Horizontal tiles", "Amount of horizontal tiles to split the " "texture into.", 1, G_MAXINT, 32, G_PARAM_READWRITE | G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB); g_object_class_install_property (object_class, PROP_TILES_X, pspec); pspec = g_param_spec_int ("tiles-y", "Vertical tiles", "Amount of vertical tiles to split the " "texture into.", 1, G_MAXINT, 32, G_PARAM_READWRITE | G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB); g_object_class_install_property (object_class, PROP_TILES_Y, pspec); pspec = g_param_spec_object ("front", "Front-face texture", "ClutterTexture to use for the front-face.", CLUTTER_TYPE_TEXTURE, MX_PARAM_READWRITE); g_object_class_install_property (object_class, PROP_FRONT, pspec); pspec = g_param_spec_object ("back", "Back-face texture", "ClutterTexture to use for the back-face.", CLUTTER_TYPE_TEXTURE, MX_PARAM_READWRITE); g_object_class_install_property (object_class, PROP_BACK, pspec); } static void mx_deform_texture_init_arrays (MxDeformTexture *self) { GLushort *idx, *bf_idx; gint x, y, direction; GLushort *static_indices, *static_bf_indices; MxDeformTexturePrivate *priv = self->priv; mx_deform_texture_free_arrays (self); priv->n_indices = (2 + 2 * priv->tiles_x) * priv->tiles_y + (priv->tiles_y - 1); static_indices = g_new (GLushort, priv->n_indices); static_bf_indices = g_new (GLushort, priv->n_indices); #define MESH_INDEX(X, Y) (Y) * (priv->tiles_x + 1) + (X) direction = 1; idx = static_indices; idx[0] = MESH_INDEX (0, 0); idx[1] = MESH_INDEX (0, 1); idx += 2; bf_idx = static_bf_indices; bf_idx[0] = MESH_INDEX (priv->tiles_x, 0); bf_idx[1] = MESH_INDEX (priv->tiles_x, 1); bf_idx += 2; for (y = 0; y < priv->tiles_y; y++) { for (x = 0; x < priv->tiles_x; x++) { /* Add 2 triangles for a quad */ if (direction) { idx[0] = MESH_INDEX (x + 1, y); idx[1] = MESH_INDEX (x + 1, y + 1); bf_idx[0] = MESH_INDEX (priv->tiles_x - (x + 1), y); bf_idx[1] = MESH_INDEX (priv->tiles_x - (x + 1), y + 1); } else { idx[0] = MESH_INDEX (priv->tiles_x - x - 1, y); idx[1] = MESH_INDEX (priv->tiles_x - x - 1, y + 1); bf_idx[0] = MESH_INDEX (x + 1, y); bf_idx[1] = MESH_INDEX (x + 1, y + 1); } idx += 2; bf_idx += 2; } /* Link rows together to draw in one call */ if (y == (priv->tiles_y - 1)) break; if (direction) { idx[0] = MESH_INDEX (priv->tiles_x, y + 1); idx[1] = MESH_INDEX (priv->tiles_x, y + 1); idx[2] = MESH_INDEX (priv->tiles_x, y + 2); bf_idx[0] = MESH_INDEX (0, y + 1); bf_idx[1] = MESH_INDEX (0, y + 1); bf_idx[2] = MESH_INDEX (0, y + 2); } else { idx[0] = MESH_INDEX (0, y + 1); idx[1] = MESH_INDEX (0, y + 1); idx[2] = MESH_INDEX (0, y + 2); bf_idx[0] = MESH_INDEX (priv->tiles_x, y + 1); bf_idx[1] = MESH_INDEX (priv->tiles_x, y + 1); bf_idx[2] = MESH_INDEX (priv->tiles_x, y + 2); } idx += 3; bf_idx += 3; direction = !direction; } priv->indices = cogl_vertex_buffer_indices_new (COGL_INDICES_TYPE_UNSIGNED_SHORT, static_indices, priv->n_indices); priv->bf_indices = cogl_vertex_buffer_indices_new (COGL_INDICES_TYPE_UNSIGNED_SHORT, static_bf_indices, priv->n_indices); g_free (static_indices); g_free (static_bf_indices); priv->vertices = g_new (CoglTextureVertex, (priv->tiles_x + 1) * (priv->tiles_y + 1)); priv->vbo = cogl_vertex_buffer_new ((priv->tiles_x + 1) * (priv->tiles_y + 1)); priv->dirty = TRUE; } static void mx_deform_texture_init (MxDeformTexture *self) { MxDeformTexturePrivate *priv = self->priv = DEFORM_TEXTURE_PRIVATE (self); priv->tiles_x = 32; priv->tiles_y = 32; mx_deform_texture_init_arrays (self); } /** * mx_deform_texture_set_textures: * @texture: an #MxDeformTexture * @front: (allow-none): #ClutterTexture to use for the front-face. * @back: (allow-none): #ClutterTexture to use for the back-face. * * Set textures to use as the sources of a deformation effect. Textures * must not be parented. */ void mx_deform_texture_set_textures (MxDeformTexture *texture, ClutterTexture *front, ClutterTexture *back) { MxDeformTexturePrivate *priv = texture->priv; if (front != (ClutterTexture *)priv->front) { if (priv->front) { clutter_actor_unparent (priv->front); priv->front = NULL; } if (front) { priv->front = (ClutterActor *)front; clutter_actor_set_parent (priv->front, CLUTTER_ACTOR (texture)); } g_object_notify (G_OBJECT (texture), "front"); } if (back != (ClutterTexture *)priv->back) { if (priv->back) { clutter_actor_unparent (priv->back); priv->back = NULL; } if (back) { priv->back = (ClutterActor *)back; clutter_actor_set_parent (priv->back, CLUTTER_ACTOR (texture)); } g_object_notify (G_OBJECT (texture), "back"); } clutter_actor_queue_relayout (CLUTTER_ACTOR (texture)); } /** * mx_deform_texture_get_textures: * @texture: A #MxDeformTexture * @front: (out) (transfer none) (allow-none): The front-facing texture * @back: (out) (transfer none) (allow-none): The back-facing texture * * Retrieves the textures used by @texture. */ void mx_deform_texture_get_textures (MxDeformTexture *texture, ClutterTexture **front, ClutterTexture **back) { MxDeformTexturePrivate *priv = texture->priv; if (front) *front = (ClutterTexture *)priv->front; if (back) *back = (ClutterTexture *)priv->back; } /** * mx_deform_texture_get_resolution: * @texture: A #MxDeformTexture * @tiles_x: (out) (allow-none): The horizontal resolution * @tiles_y: (out) (allow-none): The vertical resolution * * Retrieve the mesh resolution of the texture. * See mx_deform_texture_set_resolution(). */ void mx_deform_texture_get_resolution (MxDeformTexture *texture, gint *tiles_x, gint *tiles_y) { MxDeformTexturePrivate *priv = texture->priv; if (tiles_x) *tiles_x = priv->tiles_x; if (tiles_y) *tiles_y = priv->tiles_y; } /** * mx_deform_texture_set_resolution: * @texture: A #MxDeformTexture * @tiles_x: The horizontal resolution * @tiles_y: The vertical resolution * * Sets the amount of sub-divisions used on each axis when generating * the mesh, where a value of 1 for each axis will produce a single quad. */ void mx_deform_texture_set_resolution (MxDeformTexture *texture, gint tiles_x, gint tiles_y) { MxDeformTexturePrivate *priv = texture->priv; gboolean changed = FALSE; g_return_if_fail ((tiles_x > 0) && (tiles_y > 0)); if (priv->tiles_x != tiles_x) { priv->tiles_x = tiles_x; changed = TRUE; g_object_notify (G_OBJECT (texture), "tiles-x"); } if (priv->tiles_y != tiles_y) { priv->tiles_y = tiles_y; changed = TRUE; g_object_notify (G_OBJECT (texture), "tiles-y"); } if (changed) { mx_deform_texture_init_arrays (texture); mx_deform_texture_invalidate (texture); } } /** * mx_deform_texture_invalidate: * @texture: A #MxDeformTexture * * Make @texture re-calculate its vertices and redraw itself. */ void mx_deform_texture_invalidate (MxDeformTexture *texture) { MxDeformTexturePrivate *priv = texture->priv; priv->dirty = TRUE; clutter_actor_queue_redraw (CLUTTER_ACTOR (texture)); } mx-1.4.7/mx/mx-deform-texture.h000066400000000000000000000067151201047117600163500ustar00rootroot00000000000000/* * mx-deform-texture.h: A texture deformation actor * * Copyright 2010 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * Boston, MA 02111-1307, USA. * * Written by: Chris Lord * */ #if !defined(MX_H_INSIDE) && !defined(MX_COMPILATION) #error "Only can be included directly.h" #endif #ifndef _MX_DEFORM_TEXTURE_H #define _MX_DEFORM_TEXTURE_H #include #include G_BEGIN_DECLS #define MX_TYPE_DEFORM_TEXTURE mx_deform_texture_get_type() #define MX_DEFORM_TEXTURE(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ MX_TYPE_DEFORM_TEXTURE, MxDeformTexture)) #define MX_DEFORM_TEXTURE_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), \ MX_TYPE_DEFORM_TEXTURE, MxDeformTextureClass)) #define MX_IS_DEFORM_TEXTURE(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \ MX_TYPE_DEFORM_TEXTURE)) #define MX_IS_DEFORM_TEXTURE_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE ((klass), \ MX_TYPE_DEFORM_TEXTURE)) #define MX_DEFORM_TEXTURE_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), \ MX_TYPE_DEFORM_TEXTURE, MxDeformTextureClass)) typedef struct _MxDeformTexture MxDeformTexture; typedef struct _MxDeformTextureClass MxDeformTextureClass; typedef struct _MxDeformTexturePrivate MxDeformTexturePrivate; /** * MxDeformTexture: * * The contents of this structure is private and should only be accessed using * the provided API. */ struct _MxDeformTexture { MxWidget parent; MxDeformTexturePrivate *priv; }; struct _MxDeformTextureClass { MxWidgetClass parent_class; /* vfuncs */ void (*deform) (MxDeformTexture *texture, CoglTextureVertex *vertex, gfloat width, gfloat height); /* padding for future expansion */ void (*_padding_0) (void); void (*_padding_1) (void); void (*_padding_2) (void); void (*_padding_3) (void); void (*_padding_4) (void); }; GType mx_deform_texture_get_type (void) G_GNUC_CONST; void mx_deform_texture_get_resolution (MxDeformTexture *texture, gint *tiles_x, gint *tiles_y); void mx_deform_texture_set_resolution (MxDeformTexture *texture, gint tiles_x, gint tiles_y); void mx_deform_texture_set_textures (MxDeformTexture *texture, ClutterTexture *front, ClutterTexture *back); void mx_deform_texture_get_textures (MxDeformTexture *texture, ClutterTexture **front, ClutterTexture **back); void mx_deform_texture_invalidate (MxDeformTexture *texture); G_END_DECLS #endif /* _MX_DEFORM_TEXTURE_H */ mx-1.4.7/mx/mx-deform-waves.c000066400000000000000000000206451201047117600157660ustar00rootroot00000000000000/* * mx-deform-waves.h: A waves deformation actor * * Copyright 2010 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * Boston, MA 02111-1307, USA. * * Written by: Chris Lord * */ #include "mx-deform-waves.h" #include "mx-private.h" #include G_DEFINE_TYPE (MxDeformWaves, mx_deform_waves, MX_TYPE_DEFORM_TEXTURE) #define DEFORM_WAVES_PRIVATE(o) \ (G_TYPE_INSTANCE_GET_PRIVATE ((o), MX_TYPE_DEFORM_WAVES, MxDeformWavesPrivate)) struct _MxDeformWavesPrivate { gdouble period; gdouble angle; gdouble radius; gdouble amplitude; }; enum { PROP_0, PROP_PERIOD, PROP_ANGLE, PROP_RADIUS, PROP_AMPLITUDE }; static void mx_deform_waves_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { MxDeformWavesPrivate *priv = MX_DEFORM_WAVES (object)->priv; switch (property_id) { case PROP_PERIOD: g_value_set_double (value, priv->period); break; case PROP_ANGLE: g_value_set_double (value, priv->angle); break; case PROP_RADIUS: g_value_set_double (value, priv->radius); break; case PROP_AMPLITUDE: g_value_set_double (value, priv->amplitude); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } } static void mx_deform_waves_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { switch (property_id) { case PROP_PERIOD: mx_deform_waves_set_period (MX_DEFORM_WAVES (object), g_value_get_double (value)); break; case PROP_ANGLE: mx_deform_waves_set_angle (MX_DEFORM_WAVES (object), g_value_get_double (value)); break; case PROP_RADIUS: mx_deform_waves_set_radius (MX_DEFORM_WAVES (object), g_value_get_double (value)); break; case PROP_AMPLITUDE: mx_deform_waves_set_amplitude (MX_DEFORM_WAVES (object), g_value_get_double (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void mx_deform_waves_deform (MxDeformTexture *texture, CoglTextureVertex *vertex, gfloat width, gfloat height) { gfloat cx, cy, rx, turn_angle, height_radius; guint shade; MxDeformWavesPrivate *priv = ((MxDeformWaves *)texture)->priv; /* Rotate the point around the centre of the curl ray to align it with * the y-axis. */ cx = (1.f - priv->period) * width; cy = (1.f - priv->period) * height; rx = ((vertex->x - cx) * cos (-priv->angle)) - ((vertex->y - cy) * sin (-priv->angle)) - priv->radius; /* Calculate the angle as a function of the distance from the curl ray */ turn_angle = ((rx / priv->radius) * G_PI_2) - G_PI_2; /* Add a gradient that makes it look like lighting and hides the switch * between textures. */ shade = (255 * (1.f - priv->amplitude)) + (((sin (turn_angle) * 96) + 159) * priv->amplitude); cogl_color_set_from_4ub (&vertex->color, shade, shade, shade, 0xff); /* Make the wave amplitude lower as its distance from the curl ray increases. * Not really necessary, but looks a little nicer I think. */ height_radius = (1 - rx / width) * priv->radius; vertex->z = height_radius * sin (turn_angle) * priv->amplitude; } static void mx_deform_waves_class_init (MxDeformWavesClass *klass) { GParamSpec *pspec; GObjectClass *object_class = G_OBJECT_CLASS (klass); MxDeformTextureClass *deform_class = MX_DEFORM_TEXTURE_CLASS (klass); g_type_class_add_private (klass, sizeof (MxDeformWavesPrivate)); object_class->get_property = mx_deform_waves_get_property; object_class->set_property = mx_deform_waves_set_property; deform_class->deform = mx_deform_waves_deform; pspec = g_param_spec_double ("period", "Period", "Effect period", 0.0, G_MAXDOUBLE, 0.0, MX_PARAM_READWRITE); g_object_class_install_property (object_class, PROP_PERIOD, pspec); pspec = g_param_spec_double ("angle", "Angle", "Effect rotation angle", 0.0, G_PI * 2, 0.0, MX_PARAM_READWRITE); g_object_class_install_property (object_class, PROP_ANGLE, pspec); pspec = g_param_spec_double ("radius", "Radius", "Wave ripple radius", G_MINDOUBLE, G_MAXDOUBLE, 32.0, MX_PARAM_READWRITE); g_object_class_install_property (object_class, PROP_RADIUS, pspec); pspec = g_param_spec_double ("amplitude", "Amplitude", "Effect amplitude", 0.0, G_MAXDOUBLE, 1.0, MX_PARAM_READWRITE); g_object_class_install_property (object_class, PROP_AMPLITUDE, pspec); } static void mx_deform_waves_init (MxDeformWaves *self) { MxDeformWavesPrivate *priv = self->priv = DEFORM_WAVES_PRIVATE (self); priv->radius = 32.0; priv->amplitude = 1.0; } ClutterActor * mx_deform_waves_new (void) { return g_object_new (MX_TYPE_DEFORM_WAVES, NULL); } gdouble mx_deform_waves_get_period (MxDeformWaves *waves) { g_return_val_if_fail (MX_IS_DEFORM_WAVES (waves), 0.0); return waves->priv->period; } void mx_deform_waves_set_period (MxDeformWaves *waves, gdouble period) { MxDeformWavesPrivate *priv; g_return_if_fail (MX_IS_DEFORM_WAVES (waves)); priv = waves->priv; if (priv->period != period) { priv->period = period; g_object_notify (G_OBJECT (waves), "period"); mx_deform_texture_invalidate (MX_DEFORM_TEXTURE (waves)); } } gdouble mx_deform_waves_get_angle (MxDeformWaves *waves) { g_return_val_if_fail (MX_IS_DEFORM_WAVES (waves), 0.0); return waves->priv->angle; } void mx_deform_waves_set_angle (MxDeformWaves *waves, gdouble angle) { MxDeformWavesPrivate *priv; g_return_if_fail (MX_IS_DEFORM_WAVES (waves)); priv = waves->priv; if (priv->angle != angle) { priv->angle = angle; g_object_notify (G_OBJECT (waves), "angle"); mx_deform_texture_invalidate (MX_DEFORM_TEXTURE (waves)); } } gdouble mx_deform_waves_get_radius (MxDeformWaves *waves) { g_return_val_if_fail (MX_IS_DEFORM_WAVES (waves), 0.0); return waves->priv->radius; } void mx_deform_waves_set_radius (MxDeformWaves *waves, gdouble radius) { MxDeformWavesPrivate *priv; g_return_if_fail (MX_IS_DEFORM_WAVES (waves)); priv = waves->priv; if (priv->radius != radius) { priv->radius = radius; g_object_notify (G_OBJECT (waves), "radius"); mx_deform_texture_invalidate (MX_DEFORM_TEXTURE (waves)); } } gdouble mx_deform_waves_get_amplitude (MxDeformWaves *waves) { g_return_val_if_fail (MX_IS_DEFORM_WAVES (waves), 0.0); return waves->priv->amplitude; } void mx_deform_waves_set_amplitude (MxDeformWaves *waves, gdouble amplitude) { MxDeformWavesPrivate *priv; g_return_if_fail (MX_IS_DEFORM_WAVES (waves)); priv = waves->priv; if (priv->amplitude != amplitude) { priv->amplitude = amplitude; g_object_notify (G_OBJECT (waves), "amplitude"); mx_deform_texture_invalidate (MX_DEFORM_TEXTURE (waves)); } } mx-1.4.7/mx/mx-deform-waves.h000066400000000000000000000062331201047117600157700ustar00rootroot00000000000000/* * mx-deform-waves.h: A waves deformation actor * * Copyright 2010 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * Boston, MA 02111-1307, USA. * * Written by: Chris Lord * */ #if !defined(MX_H_INSIDE) && !defined(MX_COMPILATION) #error "Only can be included directly.h" #endif #ifndef _MX_DEFORM_WAVES_H #define _MX_DEFORM_WAVES_H #include #include G_BEGIN_DECLS #define MX_TYPE_DEFORM_WAVES mx_deform_waves_get_type() #define MX_DEFORM_WAVES(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ MX_TYPE_DEFORM_WAVES, MxDeformWaves)) #define MX_DEFORM_WAVES_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), \ MX_TYPE_DEFORM_WAVES, MxDeformWavesClass)) #define MX_IS_DEFORM_WAVES(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \ MX_TYPE_DEFORM_WAVES)) #define MX_IS_DEFORM_WAVES_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE ((klass), \ MX_TYPE_DEFORM_WAVES)) #define MX_DEFORM_WAVES_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), \ MX_TYPE_DEFORM_WAVES, MxDeformWavesClass)) /** * MxDeformWaves: * * The contents of this structure is private and should only be accessed using * the provided API. */ typedef struct _MxDeformWaves MxDeformWaves; typedef struct _MxDeformWavesClass MxDeformWavesClass; typedef struct _MxDeformWavesPrivate MxDeformWavesPrivate; struct _MxDeformWaves { MxDeformTexture parent; MxDeformWavesPrivate *priv; }; struct _MxDeformWavesClass { MxDeformTextureClass parent_class; /* padding for future expansion */ void (*_padding_0) (void); void (*_padding_1) (void); void (*_padding_2) (void); void (*_padding_3) (void); void (*_padding_4) (void); }; GType mx_deform_waves_get_type (void) G_GNUC_CONST; ClutterActor *mx_deform_waves_new (void); gdouble mx_deform_waves_get_period (MxDeformWaves *waves); void mx_deform_waves_set_period (MxDeformWaves *waves, gdouble period); gdouble mx_deform_waves_get_angle (MxDeformWaves *waves); void mx_deform_waves_set_angle (MxDeformWaves *waves, gdouble angle); gdouble mx_deform_waves_get_radius (MxDeformWaves *waves); void mx_deform_waves_set_radius (MxDeformWaves *waves, gdouble radius); gdouble mx_deform_waves_get_amplitude (MxDeformWaves *waves); void mx_deform_waves_set_amplitude (MxDeformWaves *waves, gdouble amplitude); G_END_DECLS #endif /* _MX_DEFORM_WAVES_H */ mx-1.4.7/mx/mx-dialog.c000066400000000000000000001107231201047117600146230ustar00rootroot00000000000000/* * mx-dialog.c * * Copyright 2010 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * Boston, MA 02111-1307, USA. * * Written by: Chris Lord * Iain Holmes * */ /** * SECTION:mx-dialog * @short_description: a modal, single-widget container * * #MxDialog is a single-widget container. When presented, it performs a * suitable transition animation and blocks input to the actors beneath it * until it is hidden again. * * It also allows actions to be added to it, which will be represented as * buttons, using #MxButton. * * Since: 1.2 */ #include "mx-dialog.h" #include "mx-button-group.h" #include "mx-offscreen.h" #include "mx-private.h" #include "mx-stylable.h" #include "mx-utils.h" static void mx_stylable_iface_init (MxStylableIface *iface); static void mx_focusable_iface_init (MxFocusableIface *iface); G_DEFINE_TYPE_WITH_CODE (MxDialog, mx_dialog, MX_TYPE_BIN, G_IMPLEMENT_INTERFACE (MX_TYPE_STYLABLE, mx_stylable_iface_init) G_IMPLEMENT_INTERFACE (MX_TYPE_FOCUSABLE, mx_focusable_iface_init)) #define DIALOG_PRIVATE(o) \ (G_TYPE_INSTANCE_GET_PRIVATE ((o), MX_TYPE_DIALOG, MxDialogPrivate)) typedef struct { MxAction *action; ClutterActor *button; } MxDialogAction; struct _MxDialogPrivate { guint visible : 1; guint needs_allocation : 1; guint do_paint : 1; guint child_has_focus : 1; guint transition_time; gfloat angle; ClutterActor *blur; ClutterShader *shader; ClutterTimeline *timeline; ClutterAlpha *alpha; gfloat zoom; /* Dialog-specific variables */ ClutterActor *background; ClutterActor *button_box; MxButtonGroup *button_group; guint spacing; GList *actions; }; static gchar *blur_shader = "uniform sampler2D tex;\n" "uniform float x_step, y_step;\n" #if 0 "void\n" "main ()\n" " {\n" " float u, v;\n" " int count = 0;\n" " vec4 color = vec4 (0.0, 0.0, 0.0, 0.0);\n" " for (u = -1.0; u <= 1.0; u++)\n" " for (v = -1.0; v <= 1.0; v++)\n" " {\n" " color += texture2D(tex, \n" " vec2(cogl_tex_coord_in[0].s + u * x_step, \n" " cogl_tex_coord_in[0].t + v * y_step));\n" " count ++;\n" " }\n" " color = color / float (count);\n" " cogl_color_out = color * cogl_color_in;\n" " }\n"; #else "vec4 get_rgba_rel(sampler2D tex, float dx, float dy)\n" "{\n" " return texture2D (tex, cogl_tex_coord_in[0].st \n" " + vec2(dx, dy));\n" "}\n" "void\n" "main ()\n" " {\n" " vec4 color;\n" " color = get_rgba_rel (tex, -x_step, -y_step);\n" " color += get_rgba_rel (tex, -x_step, 0.0);\n" " color += get_rgba_rel (tex, -x_step, y_step);\n" " color += get_rgba_rel (tex, 0.0, -y_step);\n" " color += get_rgba_rel (tex, 0.0, 0.0);\n" " color += get_rgba_rel (tex, 0.0, y_step);\n" " color += get_rgba_rel (tex, x_step, -y_step);\n" " color += get_rgba_rel (tex, x_step, 0.0);\n" " color += get_rgba_rel (tex, x_step, y_step);\n" " color = color / 9.0;\n" " cogl_color_out = color * cogl_color_in;\n" " }\n"; #endif static void mx_dialog_show (ClutterActor *self); static void mx_dialog_hide (ClutterActor *self); static int next_p2 (gint a) { /* find the next power of two */ int rval = 1; while (rval < a) rval <<= 1; return rval; } static void mx_dialog_texture_size_change_cb (ClutterActor *texture, gint width, gint height) { clutter_actor_set_shader_param_float (texture, "x_step", (1.0f / next_p2 (width)) * 2); clutter_actor_set_shader_param_float (texture, "y_step", (1.0f / next_p2 (height)) * 2); } static MxFocusable * mx_dialog_move_focus (MxFocusable *focusable, MxFocusDirection direction, MxFocusable *from) { ClutterActor *child; MxFocusHint hint = MX_FOCUS_HINT_PRIOR; MxDialog *self = MX_DIALOG (focusable); MxDialogPrivate *priv = self->priv; child = mx_bin_get_child (MX_BIN (focusable)); if (child && !MX_IS_FOCUSABLE (child)) child = NULL; focusable = NULL; switch (direction) { case MX_FOCUS_DIRECTION_PREVIOUS: hint = MX_FOCUS_HINT_LAST; case MX_FOCUS_DIRECTION_UP: if (!priv->child_has_focus && child) { priv->child_has_focus = TRUE; focusable = mx_focusable_accept_focus (MX_FOCUSABLE (child), hint); } break; case MX_FOCUS_DIRECTION_NEXT: hint = MX_FOCUS_HINT_FIRST; case MX_FOCUS_DIRECTION_DOWN: if (priv->child_has_focus && priv->actions) { priv->child_has_focus = FALSE; focusable = mx_focusable_accept_focus (MX_FOCUSABLE (priv->button_box), hint); } default: break; } if (focusable || MX_FOCUS_DIRECTION_OUT) return focusable; else return mx_focusable_accept_focus (MX_FOCUSABLE (self), hint); } static MxFocusable * mx_dialog_accept_focus (MxFocusable *focusable, MxFocusHint hint) { ClutterActor *child; MxDialog *self = MX_DIALOG (focusable); MxDialogPrivate *priv = self->priv; child = mx_bin_get_child (MX_BIN (focusable)); if (child && !MX_IS_FOCUSABLE (child)) { hint = MX_FOCUS_HINT_PRIOR; child = NULL; } focusable = NULL; switch (hint) { default: case MX_FOCUS_HINT_PRIOR: if (child && priv->child_has_focus) focusable = mx_focusable_accept_focus (MX_FOCUSABLE (child), hint); if (focusable) break; case MX_FOCUS_HINT_LAST: priv->child_has_focus = FALSE; if (priv->actions) focusable = mx_focusable_accept_focus (MX_FOCUSABLE (priv->button_box), hint); if (focusable) break; case MX_FOCUS_HINT_FIRST: priv->child_has_focus = TRUE; if (child) focusable = mx_focusable_accept_focus (MX_FOCUSABLE (child), hint); break; } /* If we don't have a focusable child, we still return ourselves so * that a dialog can't lose focus. */ return focusable ? focusable : MX_FOCUSABLE (self); } static void mx_focusable_iface_init (MxFocusableIface *iface) { iface->move_focus = mx_dialog_move_focus; iface->accept_focus = mx_dialog_accept_focus; } static void mx_stylable_iface_init (MxStylableIface *iface) { static gboolean is_initialized = FALSE; if (G_UNLIKELY (!is_initialized)) { GParamSpec *pspec; is_initialized = TRUE; pspec = g_param_spec_uint ("x-mx-spacing", "Spacing", "The size of the spacing", 0, G_MAXUINT, 12, MX_PARAM_READWRITE); mx_stylable_iface_install_property (iface, MX_TYPE_DIALOG, pspec); } } static void mx_dialog_allocate_cb (ClutterActor *parent, const ClutterActorBox *box, ClutterAllocationFlags flags, MxDialog *self) { ClutterActorBox child_box; child_box.x1 = 0; child_box.y1 = 0; child_box.x2 = box->x2 - box->x1; child_box.y2 = box->y2 - box->y1; clutter_actor_set_width (self, box->x2 - box->x1); clutter_actor_set_height (self, box->y2 - box->y1); } static void mx_dialog_paint_cb (ClutterActor *parent, ClutterActor *self) { MxDialog *dialog = MX_DIALOG (self); MxDialogPrivate *priv = dialog->priv; if (priv->needs_allocation) { ClutterActorBox box; clutter_actor_get_allocation_box (parent, &box); mx_dialog_allocate_cb (parent, &box, CLUTTER_ALLOCATION_NONE, dialog); } priv->do_paint = TRUE; clutter_actor_paint (self); } static void mx_dialog_pick_cb (ClutterActor *parent, ClutterColor *color, ClutterActor *self) { clutter_actor_paint (self); } static void mx_dialog_mapped_cb (ClutterActor *parent, GParamSpec *pspec, ClutterActor *self) { if (CLUTTER_ACTOR_IS_MAPPED (parent)) clutter_actor_map (self); else clutter_actor_unmap (self); } static void mx_dialog_dispose (GObject *object) { ClutterActor *parent = clutter_actor_get_parent (CLUTTER_ACTOR (object)); MxDialog *self = MX_DIALOG (object); MxDialogPrivate *priv = self->priv; if (parent) { g_signal_handlers_disconnect_by_func (parent, mx_dialog_mapped_cb, self); g_signal_handlers_disconnect_by_func (parent, mx_dialog_allocate_cb, self); g_signal_handlers_disconnect_by_func (parent, mx_dialog_paint_cb, self); g_signal_handlers_disconnect_by_func (parent, mx_dialog_pick_cb, self); } if (priv->blur) { clutter_actor_destroy (priv->blur); priv->blur = NULL; } if (priv->background) { clutter_actor_destroy (priv->background); priv->background = NULL; } if (priv->button_box) { clutter_actor_destroy (priv->button_box); priv->button_box = NULL; } if (priv->shader) { g_object_unref (priv->shader); priv->shader = NULL; } G_OBJECT_CLASS (mx_dialog_parent_class)->dispose (object); } static void mx_dialog_finalize (GObject *object) { MxDialog *self = (MxDialog *) object; MxDialogPrivate *priv = self->priv; if (priv->actions) { GList *a; for (a = priv->actions; a; a = a->next) g_slice_free (MxDialogAction, a->data); g_list_free (priv->actions); } G_OBJECT_CLASS (mx_dialog_parent_class)->finalize (object); } static void mx_dialog_get_preferred_width (ClutterActor *actor, gfloat for_height, gfloat *min_width_p, gfloat *nat_width_p) { ClutterActor *child; MxPadding padding, ipadding; gfloat child_width[2], button_width[2]; MxDialog *self = MX_DIALOG (actor); MxDialogPrivate *priv = self->priv; mx_widget_get_padding (MX_WIDGET (actor), &padding); mx_widget_get_padding (MX_WIDGET (priv->background), &ipadding); /* If we're getting the preferred-width with a height, make * sure to take off the padding from the height, and the space * required for the button-box. */ if (for_height >= 0) { gfloat button_height; if (priv->actions) clutter_actor_get_preferred_height (priv->button_box, -1, NULL, &button_height); else button_height = 0; for_height = MAX (0, for_height - padding.top - padding.bottom - ipadding.top - ipadding.bottom - button_height - priv->spacing); } /* Get the minimum/preferred width of the child */ child = mx_bin_get_child (MX_BIN (actor)); if (child) clutter_actor_get_preferred_width (child, for_height, &child_width[0], &child_width[1]); else child_width[0] = child_width[1] = 0; /* Get the minimum/preferred width of the button-box */ if (priv->actions) clutter_actor_get_preferred_width (priv->button_box, -1, &button_width[0], &button_width[1]); else button_width[0] = button_width[1] = 0; /* Our preferred width is the maximum of the button-box width and the * child width, plus space for padding. */ if (min_width_p) *min_width_p = MAX (child_width[0], button_width[0]) + padding.left + padding.right + ipadding.left + ipadding.right; if (nat_width_p) *nat_width_p = MAX (child_width[1], button_width[1]) + padding.left + padding.right + ipadding.left + ipadding.right; } static void mx_dialog_get_preferred_height (ClutterActor *actor, gfloat for_width, gfloat *min_height_p, gfloat *nat_height_p) { ClutterActor *child; MxPadding padding, ipadding; gfloat min_height, nat_height; MxDialog *self = MX_DIALOG (actor); MxDialogPrivate *priv = self->priv; mx_widget_get_padding (MX_WIDGET (actor), &padding); mx_widget_get_padding (MX_WIDGET (priv->background), &ipadding); /* Adjust for-width for padding */ if (for_width >= 0) for_width = MAX (0, for_width - padding.left - padding.right - ipadding.left - ipadding.right); /* Calculate the height of the button-box + padding */ if (priv->actions) clutter_actor_get_preferred_height (priv->button_box, for_width, &min_height, &nat_height); else min_height = nat_height = 0; min_height += padding.top + padding.bottom + ipadding.top + ipadding.bottom; nat_height += padding.top + padding.bottom + ipadding.top + ipadding.bottom; /* If the child exists, add its height + spacing to the result */ child = mx_bin_get_child (MX_BIN (actor)); if (child) { clutter_actor_get_preferred_height (child, for_width, min_height_p, nat_height_p); if (min_height_p) *min_height_p += min_height + priv->spacing; if (nat_height_p) *nat_height_p += nat_height + priv->spacing; } else { if (min_height_p) *min_height_p = min_height; if (nat_height_p) *nat_height_p = nat_height; } } static void mx_dialog_allocate (ClutterActor *actor, const ClutterActorBox *box, ClutterAllocationFlags flags) { MxPadding padding; ClutterActor *child; gfloat button_width, button_height; ClutterActorBox inner_box, child_box; MxDialogPrivate *priv = MX_DIALOG (actor)->priv; CLUTTER_ACTOR_CLASS (mx_dialog_parent_class)-> allocate (actor, box, flags); /* Get the available space */ mx_widget_get_padding (MX_WIDGET (actor), &padding); inner_box.x1 = padding.left; inner_box.y1 = padding.top; inner_box.x2 = box->x2 - box->x1 - padding.right; inner_box.y2 = box->y2 - box->y1 - padding.bottom; /* Get the padding of the background box */ mx_widget_get_padding (MX_WIDGET (priv->background), &padding); /* Get the size of the button-box */ if (priv->actions) { clutter_actor_get_preferred_size (priv->button_box, NULL, NULL, &button_width, &button_height); button_height += priv->spacing; } else button_width = button_height = 0; /* Allocate the child */ child = mx_bin_get_child (MX_BIN (actor)); /* Calculate the available space for the child */ child_box = inner_box; child_box.x1 += padding.left; child_box.y1 += padding.top; child_box.x2 -= padding.right; child_box.y2 -= padding.bottom + button_height; /* Allocate the child and work out the allocation box for the * button box. */ if (child) { gboolean x_fill, y_fill, width; MxAlign x_align, y_align; mx_bin_get_fill (MX_BIN (actor), &x_fill, &y_fill); mx_bin_get_alignment (MX_BIN (actor), &x_align, &y_align); mx_allocate_align_fill (child, &child_box, x_align, y_align, x_fill, y_fill); width = child_box.x2 - child_box.x1; /* Adjust the allocation if the button-box is wider than * the dialog contents. */ if (button_width > width) { child_box.x1 += (gint)((button_width - width)/2.f); child_box.x2 += (gint)((button_width - width)/2.f); } inner_box = child_box; inner_box.y2 += button_height; if (button_width > width) { inner_box.x1 = inner_box.x2 - button_width; child_box.x1 = inner_box.x1; child_box.x2 = child_box.x1 + width; } clutter_actor_allocate (child, &child_box, flags); child_box.y1 = child_box.y2 + priv->spacing; if (button_width > width) child_box.x2 = child_box.x1 + button_width; else child_box.x1 = child_box.x2 - button_width; child_box.y2 = child_box.y1 + button_height - priv->spacing; } else { child_box.x1 += (child_box.x2 - child_box.x1 - button_width) / 2.f; child_box.y1 += (child_box.y2 - child_box.y1 - button_height) / 2.f; child_box.x2 = child_box.x1 + button_width; child_box.y2 = child_box.y2 + button_height; inner_box = child_box; } /* Allocate the button-box if necessary */ if (priv->actions) clutter_actor_allocate (priv->button_box, &child_box, flags); /* Allocate the background */ inner_box.x1 -= padding.left; inner_box.y1 -= padding.top; inner_box.x2 += padding.right; inner_box.y2 += padding.bottom; clutter_actor_allocate (priv->background, &inner_box, flags); /* Allocate the blurred background actor */ if (priv->blur) { child_box.x1 = 0; child_box.y1 = 0; child_box.x2 = box->x2 - box->x1; child_box.y2 = box->y2 - box->y1; clutter_actor_allocate (priv->blur, &child_box, flags); } priv->needs_allocation = FALSE; } static void mx_dialog_paint (ClutterActor *actor) { ClutterActor *child; gfloat width, height; MxDialogPrivate *priv = MX_DIALOG (actor)->priv; if (!priv->do_paint) return; priv->do_paint = FALSE; clutter_actor_get_size (actor, &width, &height); if (priv->blur) { CoglHandle material = cogl_material_new (); CoglHandle texture = clutter_texture_get_cogl_texture (CLUTTER_TEXTURE (priv->blur)); cogl_material_set_color4ub (material, 0xff, 0xff, 0xff, 0xff); cogl_material_set_layer (material, 0, texture); cogl_set_source (material); cogl_rectangle (0, 0, width, height); clutter_actor_paint (priv->blur); } mx_widget_paint_background (MX_WIDGET (actor)); cogl_translate (width/2, height/2, 0); cogl_scale (priv->zoom, priv->zoom, 1.f); cogl_rotate (priv->angle, 0, 0, 1); cogl_translate (-width/2, -height/2, 0); clutter_actor_paint (priv->background); child = mx_bin_get_child (MX_BIN (actor)); if (child) clutter_actor_paint (child); if (priv->actions) clutter_actor_paint (priv->button_box); } static void mx_dialog_pick (ClutterActor *actor, const ClutterColor *color) { gfloat width, height; ClutterGeometry geom; MxDialogPrivate *priv = MX_DIALOG (actor)->priv; /* Paint a rectangle over our allocation to block input to * other actors. */ clutter_actor_get_geometry (actor, &geom); cogl_set_source_color4ub (color->red, color->green, color->blue, color->alpha); cogl_rectangle (0, 0, geom.width, geom.height); clutter_actor_get_size (actor, &width, &height); cogl_translate (width/2, height/2, 0); cogl_scale (priv->zoom, priv->zoom, 1.f); cogl_rotate (priv->angle, 0, 0, 1); cogl_translate (-width/2, -height/2, 0); /* Chain up */ CLUTTER_ACTOR_CLASS (mx_dialog_parent_class)->pick (actor, color); if (priv->actions) clutter_actor_paint (priv->button_box); } static void mx_dialog_angle_cb (GObject *window, GParamSpec *pspec, MxDialog *dialog) { g_object_get (window, "window-rotation-angle", &dialog->priv->angle, NULL); clutter_actor_queue_redraw (CLUTTER_ACTOR (dialog)); } static void mx_dialog_map (ClutterActor *actor) { MxWindow *window; ClutterActor *stage; MxDialog *self = MX_DIALOG (actor); MxDialogPrivate *priv = self->priv; CLUTTER_ACTOR_CLASS (mx_dialog_parent_class)->map (actor); if (priv->blur) clutter_actor_map (priv->blur); clutter_actor_map (priv->background); clutter_actor_map (priv->button_box); stage = clutter_actor_get_parent (actor); if (CLUTTER_IS_STAGE (stage)) { window = mx_window_get_for_stage (CLUTTER_STAGE (stage)); if (window) { g_signal_connect (window, "notify::window-rotation-angle", G_CALLBACK (mx_dialog_angle_cb), actor); mx_dialog_angle_cb (G_OBJECT (window), NULL, self); } } } static void mx_dialog_unmap (ClutterActor *actor) { MxWindow *window; ClutterActor *stage; MxDialogPrivate *priv = MX_DIALOG (actor)->priv; stage = clutter_actor_get_parent (actor); if (CLUTTER_IS_STAGE (stage)) { window = mx_window_get_for_stage (CLUTTER_STAGE (stage)); if (window) g_signal_handlers_disconnect_by_func (window, mx_dialog_angle_cb, actor); } if (priv->button_box) clutter_actor_unmap (priv->button_box); if (priv->background) clutter_actor_unmap (priv->background); if (priv->blur) clutter_actor_unmap (priv->blur); CLUTTER_ACTOR_CLASS (mx_dialog_parent_class)->unmap (actor); } static void mx_dialog_apply_style (MxWidget *widget, MxStyle *style) { MxDialogPrivate *priv = MX_DIALOG (widget)->priv; if (priv->background != NULL) mx_stylable_set_style (MX_STYLABLE (priv->background), style); if (priv->button_box != NULL) mx_stylable_set_style (MX_STYLABLE (priv->button_box), style); } static void mx_dialog_class_init (MxDialogClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass); MxWidgetClass *widget_class = MX_WIDGET_CLASS (klass); g_type_class_add_private (klass, sizeof (MxDialogPrivate)); object_class->dispose = mx_dialog_dispose; object_class->finalize = mx_dialog_finalize; actor_class->get_preferred_width = mx_dialog_get_preferred_width; actor_class->get_preferred_height = mx_dialog_get_preferred_height; actor_class->allocate = mx_dialog_allocate; actor_class->paint = mx_dialog_paint; actor_class->pick = mx_dialog_pick; actor_class->map = mx_dialog_map; actor_class->unmap = mx_dialog_unmap; actor_class->show = mx_dialog_show; actor_class->hide = mx_dialog_hide; widget_class->apply_style = mx_dialog_apply_style; } static void mx_dialog_parent_set_cb (ClutterActor *actor, ClutterActor *old_parent, MxDialog *self) { ClutterActor *parent = clutter_actor_get_parent (actor); if (old_parent) { MxDialogPrivate *priv = self->priv; g_signal_handlers_disconnect_by_func (old_parent, mx_dialog_mapped_cb, self); g_signal_handlers_disconnect_by_func (old_parent, mx_dialog_allocate_cb, self); g_signal_handlers_disconnect_by_func (old_parent, mx_dialog_paint_cb, self); g_signal_handlers_disconnect_by_func (old_parent, mx_dialog_pick_cb, self); priv->visible = FALSE; } if (parent) { g_signal_connect (parent, "notify::mapped", G_CALLBACK (mx_dialog_mapped_cb), self); } } static void mx_dialog_queue_relayout_cb (ClutterActor *actor, MxDialog *self) { self->priv->needs_allocation = TRUE; } static void mx_dialog_completed_cb (ClutterTimeline *timeline, ClutterActor *self) { ClutterTimelineDirection direction; MxDialogPrivate *priv = MX_DIALOG (self)->priv; ClutterActor *parent = clutter_actor_get_parent (self); priv->zoom = 1.f; /* Reverse the direction and rewind the timeline. This means that when * a timeline finishes, its progress stays at 1.0, or 0.0 and it is * ready to start again. */ direction = clutter_timeline_get_direction (timeline); clutter_timeline_set_direction (timeline, (direction == CLUTTER_TIMELINE_FORWARD) ? CLUTTER_TIMELINE_BACKWARD : CLUTTER_TIMELINE_FORWARD); if (direction == CLUTTER_TIMELINE_FORWARD) return; /* Finish hiding */ CLUTTER_ACTOR_SET_FLAGS (self, CLUTTER_ACTOR_VISIBLE); CLUTTER_ACTOR_CLASS (mx_dialog_parent_class)->hide (self); if (priv->blur) { clutter_actor_destroy (priv->blur); priv->blur = NULL; } g_signal_handlers_disconnect_by_func (parent, mx_dialog_paint_cb, self); g_signal_handlers_disconnect_by_func (parent, mx_dialog_pick_cb, self); g_signal_handlers_disconnect_by_func (parent, mx_dialog_allocate_cb, self); } static void mx_dialog_new_frame_cb (ClutterTimeline *timeline, gint msecs, ClutterActor *self) { MxDialog *frame = MX_DIALOG (self); MxDialogPrivate *priv = frame->priv; ClutterActor *parent = clutter_actor_get_parent (self); gfloat opacity = clutter_alpha_get_alpha (priv->alpha); priv->zoom = 1.0f + (1.f - opacity) / 2.f; clutter_actor_set_opacity (self, (guint8)(opacity * 255.f)); /* Queue a redraw on the parent, as having our hidden flag set will * short-circuit the redraw queued on ourselves via set_opacity. */ if (parent) clutter_actor_queue_redraw (parent); } static void mx_dialog_style_changed_cb (MxDialog *self) { guint spacing; MxDialogPrivate *priv = self->priv; mx_stylable_get (MX_STYLABLE (self), "x-mx-spacing", &spacing, NULL); if (priv->spacing != spacing) { priv->spacing = spacing; clutter_actor_queue_relayout (CLUTTER_ACTOR (self)); } } static void mx_dialog_init (MxDialog *self) { ClutterActor *actor = CLUTTER_ACTOR (self); MxDialogPrivate *priv = self->priv = DIALOG_PRIVATE (self); priv->transition_time = 250; priv->timeline = clutter_timeline_new (priv->transition_time); priv->alpha = clutter_alpha_new_full (priv->timeline, CLUTTER_EASE_OUT_QUAD); priv->background = mx_frame_new (); mx_stylable_set_style_class (MX_STYLABLE (priv->background), "MxDialogBackground"); priv->button_box = mx_box_layout_new (); mx_stylable_set_style_class (MX_STYLABLE (priv->button_box), "MxDialogButtonBox"); priv->button_group = mx_button_group_new (); priv->child_has_focus = TRUE; clutter_actor_push_internal (actor); clutter_actor_set_parent (priv->background, actor); clutter_actor_set_parent (priv->button_box, actor); clutter_actor_pop_internal (actor); g_signal_connect (priv->timeline, "completed", G_CALLBACK (mx_dialog_completed_cb), self); g_signal_connect (priv->timeline, "new-frame", G_CALLBACK (mx_dialog_new_frame_cb), self); g_signal_connect (self, "parent-set", G_CALLBACK (mx_dialog_parent_set_cb), self); g_signal_connect (self, "queue-relayout", G_CALLBACK (mx_dialog_queue_relayout_cb), self); g_signal_connect (self, "style-changed", G_CALLBACK (mx_dialog_style_changed_cb), self); g_object_set (G_OBJECT (self), "show-on-set-parent", FALSE, NULL); clutter_actor_set_reactive (CLUTTER_ACTOR (self), TRUE); /* Compile the shader when creating the instance so it's ready when we need * it */ if (!CLUTTER_CHECK_VERSION (1, 10, 0) && clutter_feature_available (CLUTTER_FEATURE_SHADERS_GLSL) && clutter_feature_available (CLUTTER_FEATURE_OFFSCREEN)) { GError *error = NULL; ClutterShader *shader; shader = clutter_shader_new (); clutter_shader_set_fragment_source (shader, blur_shader, -1); if (clutter_shader_compile (shader, &error)) { priv->shader = shader; } else { g_warning (G_STRLOC ": Error compiling shader: %s", error->message); g_error_free (error); g_object_unref (shader); } } } /** * mx_dialog_new: * * Creates a new #MxDialog. * * Returns: A newly allocated #MxDialog * * Since: 1.2 */ ClutterActor * mx_dialog_new (void) { return g_object_new (MX_TYPE_DIALOG, NULL); } /** * mx_dialog_set_transient_parent: * @dialog: A #MxDialog * @actor: A #ClutterActor * * Sets the parent of the #MxDialog. This is the actor over which the * modal frame will appear when clutter_actor_show() is called. * * Since: 1.2 */ void mx_dialog_set_transient_parent (MxDialog *dialog, ClutterActor *actor) { g_return_if_fail (MX_IS_DIALOG (dialog)); g_return_if_fail (CLUTTER_IS_ACTOR (actor)); clutter_actor_push_internal (actor); clutter_actor_set_parent (CLUTTER_ACTOR (dialog), actor); clutter_actor_pop_internal (actor); } static void mx_dialog_steal_focus (MxDialog *self) { ClutterActor *stage = clutter_actor_get_stage (CLUTTER_ACTOR (self)); if (stage) { MxFocusManager *manager = mx_focus_manager_get_for_stage (CLUTTER_STAGE (stage)); mx_focus_manager_push_focus (manager, MX_FOCUSABLE (self)); } } static void mx_dialog_show (ClutterActor *self) { MxDialog *dialog = MX_DIALOG (self); MxDialogPrivate *priv = dialog->priv; if (!priv->visible) { ClutterActor *parent = clutter_actor_get_parent (self); ClutterActorBox box; if (!parent) return; priv->visible = TRUE; if (clutter_timeline_is_playing (priv->timeline)) { ClutterTimelineDirection direction = clutter_timeline_get_direction (priv->timeline); direction = (direction == CLUTTER_TIMELINE_FORWARD) ? CLUTTER_TIMELINE_BACKWARD : CLUTTER_TIMELINE_FORWARD; clutter_timeline_set_direction (priv->timeline, direction); CLUTTER_ACTOR_SET_FLAGS (self, CLUTTER_ACTOR_VISIBLE); mx_dialog_steal_focus (dialog); return; } /* Create the blurred background */ if (priv->shader) { gint width, height; priv->blur = mx_offscreen_new (); clutter_actor_push_internal (self); clutter_actor_set_parent (priv->blur, self); clutter_actor_pop_internal (self); mx_offscreen_set_child (MX_OFFSCREEN (priv->blur), parent); clutter_actor_set_shader (priv->blur, priv->shader); g_signal_connect (priv->blur, "size-change", G_CALLBACK (mx_dialog_texture_size_change_cb), NULL); clutter_texture_get_base_size (CLUTTER_TEXTURE (priv->blur), &width, &height); mx_dialog_texture_size_change_cb (priv->blur, width, height); clutter_actor_set_shader_param_int (priv->blur, "tex", 0); } /* Hook onto signals necessary for drawing */ priv->needs_allocation = TRUE; g_signal_connect (parent, "allocation-changed", G_CALLBACK (mx_dialog_allocate_cb), dialog); g_signal_connect_after (parent, "paint", G_CALLBACK (mx_dialog_paint_cb), dialog); g_signal_connect_after (parent, "pick", G_CALLBACK (mx_dialog_pick_cb), dialog); clutter_actor_set_opacity (self, 0x00); CLUTTER_ACTOR_CLASS (mx_dialog_parent_class)->show (self); clutter_alpha_set_mode (priv->alpha, CLUTTER_EASE_OUT_QUAD); clutter_timeline_start (priv->timeline); mx_dialog_steal_focus (dialog); /* ensure the initial size is correct */ clutter_actor_get_allocation_box (parent, &box); mx_dialog_allocate_cb (parent, &box, CLUTTER_ALLOCATION_NONE, dialog); } } static void mx_dialog_hide (ClutterActor *self) { MxDialog *dialog = MX_DIALOG (self); MxDialogPrivate *priv = dialog->priv; if (priv->visible) { ClutterActor *stage; ClutterActor *parent = clutter_actor_get_parent (self); priv->visible = FALSE; if (!parent) { CLUTTER_ACTOR_CLASS (mx_dialog_parent_class)->hide (self); return; } CLUTTER_ACTOR_UNSET_FLAGS (self, CLUTTER_ACTOR_VISIBLE); stage = clutter_actor_get_stage (self); if (stage) { MxFocusManager *manager = mx_focus_manager_get_for_stage (CLUTTER_STAGE (stage)); mx_focus_manager_move_focus (manager, MX_FOCUS_DIRECTION_OUT); } if (clutter_timeline_is_playing (priv->timeline)) { ClutterTimelineDirection direction = clutter_timeline_get_direction (priv->timeline); direction = (direction == CLUTTER_TIMELINE_FORWARD) ? CLUTTER_TIMELINE_BACKWARD : CLUTTER_TIMELINE_FORWARD; clutter_timeline_set_direction (priv->timeline, direction); return; } /* The timeline is running in reverse, so use ease-in quad */ clutter_alpha_set_mode (priv->alpha, CLUTTER_EASE_IN_QUAD); clutter_timeline_start (priv->timeline); } } /** * mx_dialog_add_action: * @dialog: A #MxDialog * @action: A #MxAction * * Adds an #MxButton that represents @action to the button area of @dialog * * Since: 1.2 */ void mx_dialog_add_action (MxDialog *dialog, MxAction *action) { MxDialogPrivate *priv; ClutterActor *button; MxDialogAction *da; g_return_if_fail (MX_IS_DIALOG (dialog)); g_return_if_fail (MX_IS_ACTION (action)); priv = dialog->priv; button = mx_button_new (); mx_button_set_action (MX_BUTTON (button), action); clutter_container_add_actor (CLUTTER_CONTAINER (priv->button_box), button); mx_button_group_add (priv->button_group, (MxButton *) button); /* So we can maintain the two way relationship between action and button */ da = g_slice_new (MxDialogAction); da->action = action; da->button = button; priv->actions = g_list_append (priv->actions, da); } /** * mx_dialog_remove_action: * @dialog: A #MxDialog * @action: A #MxAction * * Removes the button associated with @action from the button area of @dialog * * Since: 1.2 */ void mx_dialog_remove_action (MxDialog *dialog, MxAction *action) { MxDialogPrivate *priv; MxDialogAction *da; GList *a; g_return_if_fail (MX_IS_DIALOG (dialog)); g_return_if_fail (MX_IS_ACTION (action)); priv = dialog->priv; da = NULL; for (a = priv->actions; a; a = a->next) { MxDialogAction *data = a->data; if (data->action == action) { priv->actions = g_list_delete_link (priv->actions, a); da = data; break; } } if (da == NULL) { g_warning ("Action '%s' was not found in dialog", mx_action_get_name (action)); return; } mx_button_group_remove (priv->button_group, MX_BUTTON (da->button)); clutter_container_remove_actor (CLUTTER_CONTAINER (priv->button_box), da->button); g_slice_free (MxDialogAction, da); } /** * mx_dialog_get_actions: * @dialog: A #MxDialog * * Retrieves a list of actions added to @dialog. * * Returns: (transfer container) (element-type Mx.Action): A newly allocated * #GList of #MxAction objects. The actions in the list are owned by the * dialog. * * Since: 1.2 */ GList * mx_dialog_get_actions (MxDialog *dialog) { GList *a, *list; MxDialogPrivate *priv; g_return_val_if_fail (MX_IS_DIALOG (dialog), NULL); priv = dialog->priv; list = NULL; for (a = priv->actions; a; a = a->next) { MxDialogAction *da = a->data; list = g_list_prepend (list, da->action); } return g_list_reverse (list); } mx-1.4.7/mx/mx-dialog.h000066400000000000000000000046171201047117600146340ustar00rootroot00000000000000/* * mx-dialog.h * * Copyright 2010 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * Boston, MA 02111-1307, USA. * * Written by: Chris Lord * Iain Holmes * */ #ifndef _MX_DIALOG_H #define _MX_DIALOG_H #include #include #include G_BEGIN_DECLS #define MX_TYPE_DIALOG mx_dialog_get_type() #define MX_DIALOG(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ MX_TYPE_DIALOG, MxDialog)) #define MX_DIALOG_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), \ MX_TYPE_DIALOG, MxDialogClass)) #define MX_IS_DIALOG(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \ MX_TYPE_DIALOG)) #define MX_IS_DIALOG_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE ((klass), \ MX_TYPE_DIALOG)) #define MX_DIALOG_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), \ MX_TYPE_DIALOG, MxDialogClass)) /** * MxDialog: * * The contents of this structure is private and should only be accessed using * the provided API. */ typedef struct _MxDialog MxDialog; typedef struct _MxDialogClass MxDialogClass; typedef struct _MxDialogPrivate MxDialogPrivate; struct _MxDialog { /*< private >*/ MxBin parent; MxDialogPrivate *priv; }; struct _MxDialogClass { MxBinClass parent_class; }; GType mx_dialog_get_type (void) G_GNUC_CONST; ClutterActor *mx_dialog_new (void); void mx_dialog_set_transient_parent (MxDialog *dialog, ClutterActor *actor); void mx_dialog_add_action (MxDialog *dialog, MxAction *action); void mx_dialog_remove_action (MxDialog *dialog, MxAction *action); GList *mx_dialog_get_actions (MxDialog *dialog); G_END_DECLS #endif /* _MX_DIALOG_H */ mx-1.4.7/mx/mx-draggable.c000066400000000000000000000465761201047117600153120ustar00rootroot00000000000000/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * mx-draggable.c: draggable interface * * Copyright 2009 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * * Written by: Emmanuele Bassi * */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "mx-draggable.h" #include "mx-enum-types.h" #include "mx-marshal.h" #include "mx-private.h" typedef struct _DragContext DragContext; struct _DragContext { MxDraggable *draggable; ClutterActor *stage; ClutterActor *actor; guint threshold; MxDragAxis axis; #if 0 MxDragContainment containment; ClutterActorBox *containment_area; #endif gfloat press_x; gfloat press_y; guint press_button; ClutterModifierType press_modifiers; gfloat last_x; gfloat last_y; guint emit_delayed_press : 1; guint in_drag : 1; }; enum { DRAG_BEGIN, DRAG_MOTION, DRAG_END, LAST_SIGNAL }; static GQuark quark_draggable_context = 0; static guint draggable_signals[LAST_SIGNAL] = { 0, }; static gboolean on_stage_capture (ClutterActor *stage, ClutterEvent *event, DragContext *context); static gboolean draggable_release (DragContext *context, ClutterButtonEvent *event) { ClutterActor *stage, *actor; gfloat event_x, event_y; gfloat actor_x, actor_y; gboolean res; if (!context->in_drag) return FALSE; event_x = event->x; event_y = event->y; actor_x = 0; actor_y = 0; if (context->actor && !context->emit_delayed_press) actor = context->actor; else actor = CLUTTER_ACTOR (context->draggable); res = clutter_actor_transform_stage_point (actor, event_x, event_y, &actor_x, &actor_y); if (!res) return FALSE; stage = clutter_actor_get_stage (CLUTTER_ACTOR (context->draggable)); context->last_x = actor_x; context->last_y = actor_y; context->in_drag = FALSE; g_signal_handlers_disconnect_by_func (stage, G_CALLBACK (on_stage_capture), context); if (!context->emit_delayed_press) g_signal_emit (context->draggable, draggable_signals[DRAG_END], 0, context->last_x, context->last_y); g_object_set_data (G_OBJECT (stage), "mx-drag-actor", NULL); return FALSE; } static gboolean draggable_motion (DragContext *context, ClutterMotionEvent *event) { gfloat event_x, event_y; gfloat actor_x, actor_y; gfloat delta_x, delta_y; ClutterActor *actor; gboolean res; if (!context->in_drag) return FALSE; event_x = event->x; event_y = event->y; actor_x = 0; actor_y = 0; if (context->actor && !context->emit_delayed_press) actor = context->actor; else actor = CLUTTER_ACTOR (context->draggable); res = clutter_actor_transform_stage_point (actor, event_x, event_y, &actor_x, &actor_y); if (!res) return FALSE; context->last_x = actor_x; context->last_y = actor_y; delta_x = delta_y = 0; if (context->axis == 0) { delta_x = context->last_x - context->press_x; delta_y = context->last_y - context->press_y; } else { if (context->axis == MX_DRAG_AXIS_X) delta_x = context->last_x - context->press_x; else delta_y = context->last_y - context->press_y; } if (context->emit_delayed_press) { if (ABS (delta_x) >= context->threshold || ABS (delta_y) >= context->threshold) { ClutterActor *stage; context->emit_delayed_press = FALSE; g_signal_emit (context->draggable, draggable_signals[DRAG_BEGIN], 0, context->press_x, context->press_y, context->press_button, context->press_modifiers); actor = CLUTTER_ACTOR (context->draggable); stage = clutter_actor_get_stage (actor); g_object_set_data (G_OBJECT (stage), "mx-drag-actor", actor); } else return FALSE; } g_signal_emit (context->draggable, draggable_signals[DRAG_MOTION], 0, delta_x, delta_y); return FALSE; } static gboolean on_stage_capture (ClutterActor *stage, ClutterEvent *event, DragContext *context) { switch (event->type) { case CLUTTER_MOTION: if (context->in_drag) { ClutterMotionEvent *mevent = (ClutterMotionEvent *) event; /* We can miss release events in the case of grabs, so check that * the button is still down here. */ if (!(mevent->modifier_state & CLUTTER_BUTTON1_MASK)) return draggable_release (context, (ClutterButtonEvent *) event); else return draggable_motion (context, (ClutterMotionEvent *) event); } break; case CLUTTER_BUTTON_RELEASE: if (context->in_drag) return draggable_release (context, (ClutterButtonEvent *) event); break; default: break; } return FALSE; } static gboolean on_draggable_press (ClutterActor *actor, ClutterButtonEvent *event, DragContext *context) { MxDraggable *draggable = context->draggable; ClutterActor *stage; gfloat event_x, event_y; gfloat actor_x, actor_y; gboolean res; event_x = event->x; event_y = event->y; actor_x = 0; actor_y = 0; res = clutter_actor_transform_stage_point (actor, event_x, event_y, &actor_x, &actor_y); if (!res) return FALSE; stage = clutter_actor_get_stage (actor); context->press_x = actor_x; context->press_y = actor_y; context->last_x = context->press_x; context->last_y = context->press_y; context->press_button = event->button; context->press_modifiers = event->modifier_state; context->emit_delayed_press = FALSE; g_object_get (G_OBJECT (draggable), "drag-threshold", &context->threshold, "axis", &context->axis, #if 0 "containment-type", &context->containment, "containment-area", &context->containment_area, #endif "drag-actor", &context->actor, NULL); if (context->threshold == 0) { g_signal_emit (draggable, draggable_signals[DRAG_BEGIN], 0, context->press_x, context->press_y, context->press_button, context->press_modifiers); g_object_set_data (G_OBJECT (stage), "mx-drag-actor", actor); } else context->emit_delayed_press = TRUE; context->in_drag = TRUE; context->stage = stage; g_signal_connect_after (stage, "captured-event", G_CALLBACK (on_stage_capture), context); return FALSE; } static void drag_context_free (gpointer data) { if (G_LIKELY (data)) { DragContext *context = data; /* disconnect any signal handlers we may have installed */ g_signal_handlers_disconnect_by_func (context->draggable, G_CALLBACK (on_draggable_press), context); if (context->stage) { g_signal_handlers_disconnect_by_func (context->stage, G_CALLBACK (on_stage_capture), context); context->stage = NULL; } if (context->actor) { g_object_unref (G_OBJECT (context->actor)); context->actor = NULL; } #if 0 if (context->containment_area) g_boxed_free (CLUTTER_TYPE_ACTOR_BOX, context->containment_area); #endif g_slice_free (DragContext, context); } } static DragContext * drag_context_create (MxDraggable *draggable) { DragContext *context; context = g_slice_new (DragContext); context->draggable = draggable; context->threshold = 0; context->axis = 0; #if 0 context->containment = MX_DISABLE_CONTAINMENT; context->containment_area = NULL; #endif context->in_drag = FALSE; context->emit_delayed_press = FALSE; context->stage = NULL; context->actor = NULL; /* attach the context to the draggable */ g_object_set_qdata_full (G_OBJECT (draggable), quark_draggable_context, context, drag_context_free); return context; } static void mx_draggable_real_enable (MxDraggable *draggable) { DragContext *context; ClutterActor *stage; context = g_object_get_qdata (G_OBJECT (draggable), quark_draggable_context); if (G_UNLIKELY (context != NULL)) return; stage = clutter_actor_get_stage (CLUTTER_ACTOR (draggable)); if (G_UNLIKELY (stage == NULL)) { g_warning ("Draggable actors can only be enabled when they " "on the stage"); return; } context = drag_context_create (draggable); g_signal_connect (draggable, "button-press-event", G_CALLBACK (on_draggable_press), context); g_object_notify (G_OBJECT (draggable), "drag-enabled"); } static void mx_draggable_real_disable (MxDraggable *draggable) { DragContext *context; ClutterActor *stage; context = g_object_get_qdata (G_OBJECT (draggable), quark_draggable_context); if (G_UNLIKELY (context == NULL)) return; stage = clutter_actor_get_stage (CLUTTER_ACTOR (draggable)); g_signal_handlers_disconnect_by_func (draggable, G_CALLBACK (on_draggable_press), context); g_signal_handlers_disconnect_by_func (stage, G_CALLBACK (on_stage_capture), context); context->stage = NULL; g_object_set_qdata (G_OBJECT (draggable), quark_draggable_context, NULL); g_object_notify (G_OBJECT (draggable), "drag-enabled"); } static void mx_draggable_base_init (gpointer g_iface) { static gboolean is_initialized = FALSE; if (G_UNLIKELY (!is_initialized)) { MxDraggableIface *iface = g_iface; GType iface_type = G_TYPE_FROM_INTERFACE (g_iface); GParamSpec *pspec; is_initialized = TRUE; quark_draggable_context = g_quark_from_static_string ("mx-draggable-context"); pspec = g_param_spec_boolean ("drag-enabled", "Drag Enabled", "Whether the Draggable is enabled", TRUE, MX_PARAM_READWRITE); g_object_interface_install_property (g_iface, pspec); pspec = g_param_spec_uint ("drag-threshold", "Drag Threshold", "The amount of pixels required to " "start dragging", 0, G_MAXUINT, 0, MX_PARAM_READWRITE); g_object_interface_install_property (g_iface, pspec); #if 0 pspec = g_param_spec_enum ("containment-type", "Containment Type", "The type of containment to be used", MX_TYPE_DRAG_CONTAINMENT, MX_DISABLE_CONTAINMENT, MX_PARAM_READWRITE); g_object_interface_install_property (g_iface, pspec); pspec = g_param_spec_boxed ("containment-area", "Containment Area", "The area to which the draggable is " "contained", CLUTTER_TYPE_ACTOR_BOX, MX_PARAM_READWRITE); g_object_interface_install_property (g_iface, pspec); #endif pspec = g_param_spec_enum ("axis", "Axis", "The axis along which the dragging " "should be performed", MX_TYPE_DRAG_AXIS, MX_DRAG_AXIS_NONE, MX_PARAM_READWRITE); g_object_interface_install_property (g_iface, pspec); pspec = g_param_spec_object ("drag-actor", "Drag Actor", "An actor to use in place of the " "draggable while dragging.", CLUTTER_TYPE_ACTOR, MX_PARAM_READWRITE); g_object_interface_install_property (g_iface, pspec); draggable_signals[DRAG_BEGIN] = g_signal_new (g_intern_static_string ("drag-begin"), iface_type, G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (MxDraggableIface, drag_begin), NULL, NULL, _mx_marshal_VOID__FLOAT_FLOAT_INT_ENUM, G_TYPE_NONE, 4, G_TYPE_FLOAT, G_TYPE_FLOAT, G_TYPE_INT, CLUTTER_TYPE_MODIFIER_TYPE); draggable_signals[DRAG_MOTION] = g_signal_new (g_intern_static_string ("drag-motion"), iface_type, G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (MxDraggableIface, drag_motion), NULL, NULL, _mx_marshal_VOID__FLOAT_FLOAT, G_TYPE_NONE, 2, G_TYPE_FLOAT, G_TYPE_FLOAT); draggable_signals[DRAG_END] = g_signal_new (g_intern_static_string ("drag-end"), iface_type, G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (MxDraggableIface, drag_end), NULL, NULL, _mx_marshal_VOID__FLOAT_FLOAT, G_TYPE_NONE, 2, G_TYPE_FLOAT, G_TYPE_FLOAT); iface->enable = mx_draggable_real_enable; iface->disable = mx_draggable_real_disable; } } GType mx_draggable_get_type (void) { static GType our_type = 0; if (G_UNLIKELY (our_type == 0)) { const GTypeInfo draggable_info = { sizeof (MxDraggableIface), mx_draggable_base_init, NULL, /* base_finalize */ }; our_type = g_type_register_static (G_TYPE_INTERFACE, g_intern_static_string ("MxDraggable"), &draggable_info, 0); g_type_interface_add_prerequisite (our_type, CLUTTER_TYPE_ACTOR); } return our_type; } void mx_draggable_set_axis (MxDraggable *draggable, MxDragAxis axis) { g_return_if_fail (MX_IS_DRAGGABLE (draggable)); g_object_set (G_OBJECT (draggable), "axis", axis, NULL); } MxDragAxis mx_draggable_get_axis (MxDraggable *draggable) { MxDragAxis retval = 0; g_return_val_if_fail (MX_IS_DRAGGABLE (draggable), 0); g_object_get (G_OBJECT (draggable), "axis", &retval, NULL); return retval; } void mx_draggable_set_drag_threshold (MxDraggable *draggable, guint threshold) { g_return_if_fail (MX_IS_DRAGGABLE (draggable)); g_object_set (G_OBJECT (draggable), "drag-threshold", threshold, NULL); } guint mx_draggable_get_drag_threshold (MxDraggable *draggable) { guint retval = 0; g_return_val_if_fail (MX_IS_DRAGGABLE (draggable), 0); g_object_get (G_OBJECT (draggable), "drag-threshold", &retval, NULL); return retval; } #if 0 void mx_draggable_set_containment_type (MxDraggable *draggable, MxDragContainment containment) { g_return_if_fail (MX_IS_DRAGGABLE (draggable)); g_object_set (G_OBJECT (draggable), "containment-type", containment, NULL); } MxDragContainment mx_draggable_get_containment_type (MxDraggable *draggable) { MxDragContainment retval = MX_DISABLE_CONTAINMENT; g_return_val_if_fail (MX_IS_DRAGGABLE (draggable), 0); g_object_get (G_OBJECT (draggable), "containment-type", &retval, NULL); return retval; } void mx_draggable_set_containment_area (MxDraggable *draggable, gfloat x_1, gfloat y_1, gfloat x_2, gfloat y_2) { ClutterActorBox box; g_return_if_fail (MX_IS_DRAGGABLE (draggable)); box.x1 = x_1; box.y1 = y_1; box.x2 = x_2; box.y2 = y_2; g_object_set (G_OBJECT (draggable), "containment-area", &box, NULL); } void mx_draggable_get_containment_area (MxDraggable *draggable, gfloat *x_1, gfloat *y_1, gfloat *x_2, gfloat *y_2) { ClutterActorBox *box = NULL; g_return_if_fail (MX_IS_DRAGGABLE (draggable)); g_object_get (G_OBJECT (draggable), "containment-area", &box, NULL); if (box == NULL) return; if (x_1) *x_1 = box->x1; if (y_1) *y_1 = box->y1; if (x_2) *x_2 = box->x2; if (y_2) *y_2 = box->y2; g_boxed_free (CLUTTER_TYPE_ACTOR_BOX, box); } #endif void mx_draggable_set_drag_actor (MxDraggable *draggable, ClutterActor *actor) { g_return_if_fail (MX_IS_DRAGGABLE (draggable)); g_object_set (G_OBJECT (draggable), "drag-actor", actor, NULL); } /** * mx_draggable_get_drag_actor: * @draggable: a #MxDraggable * * FIXME * * Return value: (transfer none): a #ClutterActor, or %NULL */ ClutterActor * mx_draggable_get_drag_actor (MxDraggable *draggable) { ClutterActor *actor = NULL; g_return_val_if_fail (MX_IS_DRAGGABLE (draggable), NULL); g_object_get (G_OBJECT (draggable), "drag-actor", &actor, NULL); return actor; } void mx_draggable_enable (MxDraggable *draggable) { g_return_if_fail (MX_IS_DRAGGABLE (draggable)); MX_DRAGGABLE_GET_IFACE (draggable)->enable (draggable); } void mx_draggable_disable (MxDraggable *draggable) { g_return_if_fail (MX_IS_DRAGGABLE (draggable)); MX_DRAGGABLE_GET_IFACE (draggable)->disable (draggable); } gboolean mx_draggable_is_enabled (MxDraggable *draggable) { gboolean retval = FALSE; g_return_val_if_fail (MX_IS_DRAGGABLE (draggable), FALSE); g_object_get (G_OBJECT (draggable), "drag-enabled", &retval, NULL); return retval; } mx-1.4.7/mx/mx-draggable.h000066400000000000000000000125711201047117600153030ustar00rootroot00000000000000/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * mx-draggable.h: draggable interface * * Copyright 2009 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * * Written by: Emmanuele Bassi * */ #if !defined(MX_H_INSIDE) && !defined(MX_COMPILATION) #error "Only can be included directly." #endif #ifndef __MX_DRAGGABLE_H__ #define __MX_DRAGGABLE_H__ #include #include G_BEGIN_DECLS #define MX_TYPE_DRAGGABLE (mx_draggable_get_type ()) #define MX_DRAGGABLE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MX_TYPE_DRAGGABLE, MxDraggable)) #define MX_IS_DRAGGABLE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MX_TYPE_DRAGGABLE)) #define MX_DRAGGABLE_GET_IFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), MX_TYPE_DRAGGABLE, MxDraggableIface)) /** * MxDraggable: * * This is an opaque structure whose members cannot be directly accessed. */ typedef struct _MxDraggable MxDraggable; /* dummy typedef */ typedef struct _MxDraggableIface MxDraggableIface; typedef enum { MX_DRAG_AXIS_NONE, MX_DRAG_AXIS_X, MX_DRAG_AXIS_Y } MxDragAxis; #if 0 /* typedef enum { MX_DISABLE_CONTAINMENT, MX_CONTAIN_IN_STAGE, MX_CONTAIN_IN_PARENT, MX_CONTAIN_IN_AREA } MxDragContainment; */ #endif /** * MxDraggableIface: * @enable: virtual function called when enabling a #MxDraggable; MX * already provides a default implementation * @disable: virtual function called when disabling a #MxDraggable; MX * already provides a default implementation * @drag_begin: class handler for the #MxDraggable::drag-begin signal * @drag_motion: class handler for the #MxDraggable::drag-motion signal * @drag_end: class handler for the #MxDraggable::drag-end signal * * Interface for draggable #ClutterActors. */ struct _MxDraggableIface { /*< private >*/ GTypeInterface g_iface; /*< public >*/ /* vfuncs, not signals */ void (* enable) (MxDraggable *draggable); void (* disable) (MxDraggable *draggable); /* signals */ void (* drag_begin) (MxDraggable *draggable, gfloat event_x, gfloat event_y, gint event_button, ClutterModifierType modifiers); void (* drag_motion) (MxDraggable *draggable, gfloat delta_x, gfloat delta_y); void (* drag_end) (MxDraggable *draggable, gfloat event_x, gfloat event_y); }; GType mx_draggable_get_type (void) G_GNUC_CONST; void mx_draggable_set_axis (MxDraggable *draggable, MxDragAxis axis); MxDragAxis mx_draggable_get_axis (MxDraggable *draggable); void mx_draggable_set_drag_threshold (MxDraggable *draggable, guint threshold); guint mx_draggable_get_drag_threshold (MxDraggable *draggable); #if 0 void mx_draggable_set_containment_type (MxDraggable *draggable, MxDragContainment containment); MxDragContainment mx_draggable_get_containment_type (MxDraggable *draggable); void mx_draggable_set_containment_area (MxDraggable *draggable, gfloat x_1, gfloat y_1, gfloat x_2, gfloat y_2); void mx_draggable_get_containment_area (MxDraggable *draggable, gfloat *x_1, gfloat *y_1, gfloat *x_2, gfloat *y_2); #endif void mx_draggable_set_drag_actor (MxDraggable *draggable, ClutterActor *actor); ClutterActor * mx_draggable_get_drag_actor (MxDraggable *draggable); void mx_draggable_disable (MxDraggable *draggable); void mx_draggable_enable (MxDraggable *draggable); gboolean mx_draggable_is_enabled (MxDraggable *draggable); G_END_DECLS #endif /* __MX_DRAGGABLE_H__ */ mx-1.4.7/mx/mx-droppable.c000066400000000000000000000256161201047117600153420ustar00rootroot00000000000000/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * mx-droppable.c: droppable interface * * Copyright 2009 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * * Written by: Emmanuele Bassi * */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "mx-droppable.h" #include "mx-enum-types.h" #include "mx-marshal.h" #include "mx-private.h" typedef struct _DropContext DropContext; enum { OVER_IN, OVER_OUT, DROP, LAST_SIGNAL }; static guint droppable_signals[LAST_SIGNAL] = { 0, }; static GQuark quark_drop_context = 0; struct _DropContext { ClutterActor *stage; GSList *targets; MxDroppable *last_target; guint is_over : 1; }; static gboolean on_stage_capture (ClutterActor *actor, ClutterEvent *event, DropContext *context) { MxDroppable *droppable; MxDraggable *draggable; ClutterActor *target; gfloat event_x, event_y; gboolean draggable_reactive; if (!(event->type == CLUTTER_MOTION || event->type == CLUTTER_BUTTON_RELEASE)) return FALSE; draggable = g_object_get_data (G_OBJECT (actor), "mx-drag-actor"); if (G_UNLIKELY (draggable == NULL)) return FALSE; /* get the actor currently under the cursor; we set the draggable * unreactive so that it does not intefere with get_actor_at_pos(); * the paint that get_actor_at_pos() performs is in the back buffer * so the hide/show cycle will not be visible on screen */ clutter_event_get_coords (event, &event_x, &event_y); draggable_reactive = clutter_actor_get_reactive (CLUTTER_ACTOR (draggable)); clutter_actor_set_reactive (CLUTTER_ACTOR (draggable), FALSE); target = clutter_stage_get_actor_at_pos (CLUTTER_STAGE (actor), CLUTTER_PICK_REACTIVE, event_x, event_y); clutter_actor_set_reactive (CLUTTER_ACTOR (draggable), draggable_reactive); if (G_UNLIKELY (target == NULL)) return FALSE; droppable = NULL; if (!MX_IS_DROPPABLE (target)) { ClutterActor *parent = target; /* check if we're not on a child of a droppable */ while (parent != NULL) { parent = clutter_actor_get_parent (parent); if (parent != NULL && MX_IS_DROPPABLE (parent) && mx_droppable_accept_drop (MX_DROPPABLE (parent), draggable)) { droppable = MX_DROPPABLE (parent); break; } } } else { if (mx_droppable_accept_drop (MX_DROPPABLE (target), draggable)) droppable = MX_DROPPABLE (target); } /* we are on a new target, so emit ::over-out and unset the last target */ if (context->last_target && droppable != context->last_target) { g_signal_emit (context->last_target, droppable_signals[OVER_OUT], 0, draggable); context->last_target = NULL; return FALSE; } if (droppable == NULL) return FALSE; if (event->type == CLUTTER_MOTION) { if (context->last_target == NULL) { context->last_target = droppable; g_signal_emit (context->last_target, droppable_signals[OVER_IN], 0, draggable); } } else if (event->type == CLUTTER_BUTTON_RELEASE && context->last_target) { gfloat drop_x, drop_y; gboolean res; ClutterActor *last_target = CLUTTER_ACTOR (context->last_target); drop_x = drop_y = 0; res = clutter_actor_transform_stage_point (last_target, event_x, event_y, &drop_x, &drop_y); if (!res) return FALSE; g_signal_emit (context->last_target, droppable_signals[DROP], 0, draggable, drop_x, drop_y, event->button.button, event->button.modifier_state); context->last_target = NULL; } return FALSE; } static void drop_context_destroy (gpointer data) { if (G_LIKELY (data != NULL)) { DropContext *context = data; g_slist_free (context->targets); g_object_unref (context->stage); g_slice_free (DropContext, context); } } static void drop_context_update (DropContext *context, MxDroppable *droppable) { context->targets = g_slist_prepend (context->targets, droppable); } static DropContext * drop_context_create (ClutterActor *stage, MxDroppable *droppable) { DropContext *retval; retval = g_slice_new (DropContext); retval->stage = g_object_ref (stage); retval->targets = g_slist_prepend (NULL, droppable); retval->last_target = NULL; retval->is_over = FALSE; g_object_set_qdata_full (G_OBJECT (stage), quark_drop_context, retval, drop_context_destroy); return retval; } static void mx_droppable_real_enable (MxDroppable *droppable) { ClutterActor *stage; DropContext *context; stage = clutter_actor_get_stage (CLUTTER_ACTOR (droppable)); if (G_UNLIKELY (stage == NULL)) { g_warning ("A MxDroppable must be on the stage before " "being enabled."); return; } context = g_object_get_qdata (G_OBJECT (stage), quark_drop_context); if (context == NULL) { context = drop_context_create (stage, droppable); g_signal_connect_after (stage, "captured-event", G_CALLBACK (on_stage_capture), context); } else drop_context_update (context, droppable); } static void mx_droppable_real_disable (MxDroppable *droppable) { ClutterActor *stage; DropContext *context; stage = clutter_actor_get_stage (CLUTTER_ACTOR (droppable)); if (G_UNLIKELY (stage == NULL)) return; context = g_object_get_qdata (G_OBJECT (stage), quark_drop_context); if (G_UNLIKELY (context == NULL)) return; context->targets = g_slist_remove (context->targets, droppable); if (context->targets == NULL) { g_signal_handlers_disconnect_by_func (stage, G_CALLBACK (on_stage_capture), context); g_object_set_qdata (G_OBJECT (stage), quark_drop_context, NULL); } } static gboolean mx_droppable_real_accept_drop (MxDroppable *droppable, MxDraggable *draggable) { /* we always accept by default */ return TRUE; } static void mx_droppable_base_init (gpointer g_iface) { static gboolean is_initialized = FALSE; if (G_UNLIKELY (!is_initialized)) { MxDroppableIface *iface = g_iface; GType iface_type = G_TYPE_FROM_INTERFACE (g_iface); GParamSpec *pspec; is_initialized = TRUE; quark_drop_context = g_quark_from_static_string ("mx-droppable-context"); pspec = g_param_spec_boolean ("drop-enabled", "Drop Enabled", "Whether the Droppable is enabled", FALSE, MX_PARAM_READWRITE); g_object_interface_install_property (g_iface, pspec); droppable_signals[OVER_IN] = g_signal_new (g_intern_static_string ("over-in"), iface_type, G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (MxDroppableIface, over_in), NULL, NULL, _mx_marshal_VOID__OBJECT, G_TYPE_NONE, 1, CLUTTER_TYPE_ACTOR); droppable_signals[OVER_OUT] = g_signal_new (g_intern_static_string ("over-out"), iface_type, G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (MxDroppableIface, over_out), NULL, NULL, _mx_marshal_VOID__OBJECT, G_TYPE_NONE, 1, CLUTTER_TYPE_ACTOR); droppable_signals[DROP] = g_signal_new (g_intern_static_string ("drop"), iface_type, G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (MxDroppableIface, drop), NULL, NULL, _mx_marshal_VOID__OBJECT_FLOAT_FLOAT_INT_ENUM, G_TYPE_NONE, 5, CLUTTER_TYPE_ACTOR, G_TYPE_FLOAT, G_TYPE_FLOAT, G_TYPE_INT, CLUTTER_TYPE_MODIFIER_TYPE); iface->enable = mx_droppable_real_enable; iface->disable = mx_droppable_real_disable; iface->accept_drop = mx_droppable_real_accept_drop; } } GType mx_droppable_get_type (void) { static GType our_type = 0; if (G_UNLIKELY (our_type == 0)) { const GTypeInfo droppable_info = { sizeof (MxDroppableIface), mx_droppable_base_init, NULL, /* base_finalize */ }; our_type = g_type_register_static (G_TYPE_INTERFACE, g_intern_static_string ("MxDroppable"), &droppable_info, 0); g_type_interface_add_prerequisite (our_type, CLUTTER_TYPE_ACTOR); } return our_type; } void mx_droppable_enable (MxDroppable *droppable) { g_return_if_fail (MX_IS_DROPPABLE (droppable)); MX_DROPPABLE_GET_IFACE (droppable)->enable (droppable); } void mx_droppable_disable (MxDroppable *droppable) { g_return_if_fail (MX_IS_DROPPABLE (droppable)); MX_DROPPABLE_GET_IFACE (droppable)->disable (droppable); } gboolean mx_droppable_is_enabled (MxDroppable *droppable) { gboolean retval = FALSE; g_return_val_if_fail (MX_IS_DROPPABLE (droppable), FALSE); g_object_get (G_OBJECT (droppable), "drop-enabled", &retval, NULL); return retval; } gboolean mx_droppable_accept_drop (MxDroppable *droppable, MxDraggable *draggable) { g_return_val_if_fail (MX_IS_DROPPABLE (droppable), FALSE); g_return_val_if_fail (MX_IS_DRAGGABLE (draggable), FALSE); return MX_DROPPABLE_GET_IFACE (droppable)->accept_drop (droppable, draggable); } mx-1.4.7/mx/mx-droppable.h000066400000000000000000000057551201047117600153510ustar00rootroot00000000000000/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * mx-droppable.c: droppable interface * * Copyright 2009 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * * Written by: Emmanuele Bassi * */ #if !defined(MX_H_INSIDE) && !defined(MX_COMPILATION) #error "Only can be included directly." #endif #ifndef __MX_DROPPABLE_H__ #define __MX_DROPPABLE_H__ #include #include #include G_BEGIN_DECLS #define MX_TYPE_DROPPABLE (mx_droppable_get_type ()) #define MX_DROPPABLE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MX_TYPE_DROPPABLE, MxDroppable)) #define MX_IS_DROPPABLE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MX_TYPE_DROPPABLE)) #define MX_DROPPABLE_GET_IFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), MX_TYPE_DROPPABLE, MxDroppableIface)) /** * MxDroppable: * * This is an opaque structure whose members cannot be directly accessed. */ typedef struct _MxDroppable MxDroppable; /* dummy typedef */ typedef struct _MxDroppableIface MxDroppableIface; struct _MxDroppableIface { /*< private >*/ GTypeInterface g_iface; /*< public >*/ /* vfuncs, not signals */ void (* enable) (MxDroppable *droppable); void (* disable) (MxDroppable *droppable); gboolean (* accept_drop) (MxDroppable *droppable, MxDraggable *draggable); /* signals */ void (* over_in) (MxDroppable *droppable, MxDraggable *draggable); void (* over_out) (MxDroppable *droppable, MxDraggable *draggable); void (* drop) (MxDroppable *droppable, MxDraggable *draggable, gfloat event_x, gfloat event_y, gint button, ClutterModifierType modifiers); }; GType mx_droppable_get_type (void) G_GNUC_CONST; void mx_droppable_enable (MxDroppable *droppable); void mx_droppable_disable (MxDroppable *droppable); gboolean mx_droppable_is_enabled (MxDroppable *droppable); gboolean mx_droppable_accept_drop (MxDroppable *droppable, MxDraggable *draggable); G_END_DECLS #endif /* __MX_DROPPABLE_H__ */ mx-1.4.7/mx/mx-entry.c000066400000000000000000001621451201047117600145320ustar00rootroot00000000000000/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * mx-entry.c: Plain entry actor * * Copyright 2008, 2009 Intel Corporation * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * * Written by: Thomas Wood * */ /** * SECTION:mx-entry * @short_description: Widget for displaying text * * #MxEntry is a widget for displaying and editing a single line of text. * It derives from #MxWidget to add extra style and placement functionality over * #ClutterText. The internal #ClutterText is publicly accessibly to allow * applications to set further properties. * * #MxEntry supports the following pseudo style states: * * * focus: the widget has focus * * * indeterminate: the widget is showing the hint text * * */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #ifdef HAVE_CLUTTER_IMCONTEXT #include #endif #include "mx-entry.h" #include "mx-widget.h" #include "mx-stylable.h" #include "mx-texture-cache.h" #include "mx-marshal.h" #include "mx-clipboard.h" #include "mx-focusable.h" #include "mx-private.h" #include "mx-tooltip.h" #ifdef HAVE_X11 /* for pointer cursor support */ #include #include #include #endif #define HAS_FOCUS(actor) (clutter_actor_get_stage (actor) && clutter_stage_get_key_focus ((ClutterStage *) clutter_actor_get_stage (actor)) == actor) #define MX_ENTRY_TOOLTIP_DELAY 500 /* properties */ enum { PROP_0, PROP_CLUTTER_TEXT, PROP_HINT_TEXT, PROP_TEXT, PROP_PASSWORD_CHAR, PROP_ICON_HIGHLIGHT_SUFFIX, PROP_PRIMARY_ICON_TOOLTIP_TEXT, PROP_SECONDARY_ICON_TOOLTIP_TEXT }; /* signals */ enum { PRIMARY_ICON_CLICKED, SECONDARY_ICON_CLICKED, LAST_SIGNAL }; #define MX_ENTRY_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), MX_TYPE_ENTRY, MxEntryPrivate)) #define MX_ENTRY_PRIV(x) ((MxEntry *) x)->priv struct _MxEntryPrivate { ClutterActor *entry; gchar *hint; ClutterActor *primary_icon; ClutterActor *primary_icon_highlight; MxTooltip *primary_icon_tooltip; ClutterActor *secondary_icon; ClutterActor *secondary_icon_highlight; MxTooltip *secondary_icon_tooltip; gchar *primary_icon_filename; gchar *secondary_icon_filename; gchar *icon_highlight_suffix; gfloat spacing; gunichar password_char; GQueue *undo_history; gulong undo_timeout_source; guint hint_visible : 1; guint pause_undo : 1; guint scrolling : 1; guint unicode_input_mode : 1; guint pointer_in_entry : 1; GString *preedit_string; guint tooltip_timeout; }; static guint entry_signals[LAST_SIGNAL] = { 0, }; static void mx_stylable_iface_init (MxStylableIface *iface); static void mx_focusable_iface_init (MxFocusableIface *iface); G_DEFINE_TYPE_WITH_CODE (MxEntry, mx_entry, MX_TYPE_WIDGET, G_IMPLEMENT_INTERFACE (MX_TYPE_STYLABLE, mx_stylable_iface_init) G_IMPLEMENT_INTERFACE (MX_TYPE_FOCUSABLE, mx_focusable_iface_init)); static void mx_entry_set_property (GObject *gobject, guint prop_id, const GValue *value, GParamSpec *pspec) { MxEntry *entry = MX_ENTRY (gobject); switch (prop_id) { case PROP_HINT_TEXT: mx_entry_set_hint_text (entry, g_value_get_string (value)); break; case PROP_TEXT: mx_entry_set_text (entry, g_value_get_string (value)); break; case PROP_PASSWORD_CHAR: mx_entry_set_password_char (entry, g_value_get_uint (value)); break; case PROP_ICON_HIGHLIGHT_SUFFIX: mx_entry_set_icon_highlight_suffix (entry, g_value_get_string (value)); break; case PROP_PRIMARY_ICON_TOOLTIP_TEXT: mx_entry_set_primary_icon_tooltip_text (entry, g_value_get_string (value)); break; case PROP_SECONDARY_ICON_TOOLTIP_TEXT: mx_entry_set_secondary_icon_tooltip_text (entry, g_value_get_string (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); break; } } static void mx_entry_get_property (GObject *gobject, guint prop_id, GValue *value, GParamSpec *pspec) { MxEntryPrivate *priv = MX_ENTRY_PRIV (gobject); switch (prop_id) { case PROP_CLUTTER_TEXT: g_value_set_object (value, priv->entry); break; case PROP_HINT_TEXT: g_value_set_string (value, priv->hint); break; case PROP_TEXT: g_value_set_string (value, clutter_text_get_text (CLUTTER_TEXT (priv->entry))); break; case PROP_PASSWORD_CHAR: g_value_set_uint (value, priv->password_char); break; case PROP_ICON_HIGHLIGHT_SUFFIX: g_value_set_string (value, priv->icon_highlight_suffix); break; case PROP_PRIMARY_ICON_TOOLTIP_TEXT: if (priv->primary_icon_tooltip) g_value_set_string (value, mx_tooltip_get_text (priv->primary_icon_tooltip)); break; case PROP_SECONDARY_ICON_TOOLTIP_TEXT: if (priv->secondary_icon_tooltip) g_value_set_string (value, mx_tooltip_get_text (priv->secondary_icon_tooltip)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); break; } } static void mx_entry_dispose (GObject *object) { MxEntryPrivate *priv = MX_ENTRY_PRIV (object); if (priv->entry) { clutter_actor_destroy (priv->entry); priv->entry = NULL; } if (priv->primary_icon) { clutter_actor_destroy (priv->primary_icon); priv->primary_icon = NULL; } if (priv->secondary_icon) { clutter_actor_destroy (priv->secondary_icon); priv->secondary_icon = NULL; } if (priv->primary_icon_highlight) { clutter_actor_destroy (priv->primary_icon_highlight); priv->primary_icon_highlight = NULL; } if (priv->secondary_icon_highlight) { clutter_actor_destroy (priv->secondary_icon_highlight); priv->secondary_icon_highlight = NULL; } if (priv->primary_icon_tooltip) { clutter_actor_destroy (CLUTTER_ACTOR (priv->primary_icon_tooltip)); priv->primary_icon_tooltip = NULL; } if (priv->secondary_icon_tooltip) { clutter_actor_destroy (CLUTTER_ACTOR (priv->secondary_icon_tooltip)); priv->secondary_icon_tooltip = NULL; } G_OBJECT_CLASS (mx_entry_parent_class)->dispose (object); } static void mx_entry_finalize (GObject *object) { MxEntryPrivate *priv = MX_ENTRY_PRIV (object); g_free (priv->hint); priv->hint = NULL; if (priv->undo_history) { g_queue_foreach (priv->undo_history, (GFunc) g_free, NULL); g_queue_free (priv->undo_history); priv->undo_history = NULL; } if (priv->undo_timeout_source) { g_source_remove (priv->undo_timeout_source); priv->undo_timeout_source = 0; } if (priv->tooltip_timeout) { g_source_remove (priv->tooltip_timeout); priv->tooltip_timeout = 0; } if (priv->preedit_string) { g_string_free (priv->preedit_string, TRUE); priv->preedit_string = NULL; } if (priv->primary_icon_filename) { g_free (priv->primary_icon_filename); priv->primary_icon_filename = NULL; } if (priv->secondary_icon_filename) { g_free (priv->secondary_icon_filename); priv->secondary_icon_filename = NULL; } if (priv->icon_highlight_suffix) { g_free (priv->icon_highlight_suffix); priv->icon_highlight_suffix = NULL; } G_OBJECT_CLASS (mx_entry_parent_class)->finalize (object); } static void mx_stylable_iface_init (MxStylableIface *iface) { static gboolean is_initialized = FALSE; if (!is_initialized) { GParamSpec *pspec; static const ClutterColor default_color = { 0x0, 0x9c, 0xcf, 0xff }; static const ClutterColor white = { 0xff, 0xff, 0xff, 0xff }; is_initialized = TRUE; pspec = clutter_param_spec_color ("caret-color", "Caret Color", "Color of the entry's caret", &default_color, G_PARAM_READWRITE); mx_stylable_iface_install_property (iface, MX_TYPE_ENTRY, pspec); pspec = clutter_param_spec_color ("selection-background-color", "Selection Background Color", "Color of the entry's selection", &default_color, G_PARAM_READWRITE); mx_stylable_iface_install_property (iface, MX_TYPE_ENTRY, pspec); pspec = clutter_param_spec_color ("selected-text-color", "Selected Text Color", "Color of the selected text", &white, G_PARAM_READWRITE); mx_stylable_iface_install_property (iface, MX_TYPE_ENTRY, pspec); } } static MxFocusable* mx_entry_accept_focus (MxFocusable *focusable, MxFocusHint hint) { clutter_actor_grab_key_focus (MX_ENTRY (focusable)->priv->entry); return focusable; } static void mx_focusable_iface_init (MxFocusableIface *iface) { iface->accept_focus = mx_entry_accept_focus; } static void mx_entry_style_changed (MxWidget *self) { MxEntryPrivate *priv = MX_ENTRY_PRIV (self); ClutterColor *caret_color = NULL; ClutterColor *selection_background_color = NULL; ClutterColor *selected_text_color = NULL; mx_stylable_get (MX_STYLABLE (self), "caret-color", &caret_color, "selection-background-color", &selection_background_color, "selected-text-color", &selected_text_color, NULL); if (caret_color) { clutter_text_set_cursor_color (CLUTTER_TEXT (priv->entry), caret_color); clutter_color_free (caret_color); } if (selection_background_color) { clutter_text_set_selection_color (CLUTTER_TEXT (priv->entry), selection_background_color); clutter_color_free (selection_background_color); } if (selected_text_color) { clutter_text_set_selected_text_color (CLUTTER_TEXT (priv->entry), selected_text_color); clutter_color_free (selected_text_color); } mx_stylable_apply_clutter_text_attributes (MX_STYLABLE (self), CLUTTER_TEXT (priv->entry)); } static void mx_entry_get_preferred_width (ClutterActor *actor, gfloat for_height, gfloat *min_width_p, gfloat *natural_width_p) { MxEntryPrivate *priv = MX_ENTRY_PRIV (actor); MxPadding padding; gfloat icon_w; gfloat entry_height; mx_widget_get_padding (MX_WIDGET (actor), &padding); for_height -= padding.top + padding.bottom; clutter_actor_get_preferred_width (priv->entry, for_height, min_width_p, natural_width_p); clutter_actor_get_preferred_height (priv->entry, -1, NULL, &entry_height); /* ensure the preferred width is at least large enough to be useful */ if (natural_width_p) *natural_width_p = MAX ((entry_height * 6), *natural_width_p); if (priv->primary_icon) { clutter_actor_get_preferred_width (priv->primary_icon, -1, NULL, &icon_w); if (min_width_p) *min_width_p += icon_w + priv->spacing; if (natural_width_p) *natural_width_p += icon_w + priv->spacing; } if (priv->secondary_icon) { clutter_actor_get_preferred_width (priv->secondary_icon, -1, NULL, &icon_w); if (min_width_p) *min_width_p += icon_w + priv->spacing; if (natural_width_p) *natural_width_p += icon_w + priv->spacing; } if (min_width_p) *min_width_p += padding.left + padding.right; if (natural_width_p) *natural_width_p += padding.left + padding.right; } static void mx_entry_get_preferred_height (ClutterActor *actor, gfloat for_width, gfloat *min_height_p, gfloat *natural_height_p) { MxEntryPrivate *priv = MX_ENTRY_PRIV (actor); MxPadding padding; gfloat icon_h; mx_widget_get_padding (MX_WIDGET (actor), &padding); for_width -= padding.left + padding.right; clutter_actor_get_preferred_height (priv->entry, for_width, min_height_p, natural_height_p); if (priv->primary_icon) { clutter_actor_get_preferred_height (priv->primary_icon, -1, NULL, &icon_h); if (min_height_p && icon_h > *min_height_p) *min_height_p = icon_h; if (natural_height_p && icon_h > *natural_height_p) *natural_height_p = icon_h; } if (priv->secondary_icon) { clutter_actor_get_preferred_height (priv->secondary_icon, -1, NULL, &icon_h); if (min_height_p && icon_h > *min_height_p) *min_height_p = icon_h; if (natural_height_p && icon_h > *natural_height_p) *natural_height_p = icon_h; } if (min_height_p) *min_height_p += padding.top + padding.bottom; if (natural_height_p) *natural_height_p += padding.top + padding.bottom; } static void mx_entry_allocate (ClutterActor *actor, const ClutterActorBox *box, ClutterAllocationFlags flags) { MxEntryPrivate *priv = MX_ENTRY_PRIV (actor); ClutterActorClass *parent_class; ClutterActorBox child_box, icon_box; MxPadding padding; gfloat icon_w, icon_h; gfloat entry_h, min_h, pref_h, avail_h, pref_w; mx_widget_get_padding (MX_WIDGET (actor), &padding); parent_class = CLUTTER_ACTOR_CLASS (mx_entry_parent_class); parent_class->allocate (actor, box, flags); avail_h = (box->y2 - box->y1) - padding.top - padding.bottom; child_box.x1 = padding.left; child_box.x2 = box->x2 - box->x1 - padding.right; if (priv->primary_icon) { clutter_actor_get_preferred_width (priv->primary_icon, -1, NULL, &icon_w); clutter_actor_get_preferred_height (priv->primary_icon, -1, NULL, &icon_h); icon_box.x1 = padding.left; icon_box.x2 = icon_box.x1 + icon_w; icon_box.y1 = (int)(padding.top + avail_h / 2 - icon_h / 2); icon_box.y2 = icon_box.y1 + icon_h; clutter_actor_allocate (priv->primary_icon, &icon_box, flags); /* reduce the size for the entry */ child_box.x1 += icon_w + priv->spacing; if (priv->primary_icon_tooltip) mx_tooltip_set_tip_area_from_actor (priv->primary_icon_tooltip, priv->primary_icon); if (priv->primary_icon_highlight) { clutter_actor_allocate (priv->primary_icon_highlight, &icon_box, flags); } } if (priv->secondary_icon) { clutter_actor_get_preferred_width (priv->secondary_icon, -1, NULL, &icon_w); clutter_actor_get_preferred_height (priv->secondary_icon, -1, NULL, &icon_h); icon_box.x2 = (box->x2 - box->x1) - padding.right; icon_box.x1 = icon_box.x2 - icon_w; icon_box.y1 = (int)(padding.top + avail_h / 2 - icon_h / 2); icon_box.y2 = icon_box.y1 + icon_h; clutter_actor_allocate (priv->secondary_icon, &icon_box, flags); /* reduce the size for the entry */ child_box.x2 -= icon_w - priv->spacing; if (priv->secondary_icon_tooltip) mx_tooltip_set_tip_area_from_actor (priv->secondary_icon_tooltip, priv->secondary_icon); if (priv->secondary_icon_highlight) { clutter_actor_allocate (priv->secondary_icon_highlight, &icon_box, flags); } } clutter_actor_get_preferred_height (priv->entry, child_box.x2 - child_box.x1, &min_h, &pref_h); entry_h = CLAMP (pref_h, min_h, avail_h); clutter_actor_get_preferred_width (priv->entry, entry_h, NULL, &pref_w); priv->scrolling = (pref_w > (child_box.x2 - child_box.x1)); child_box.y1 = (int)(padding.top + avail_h / 2 - entry_h / 2); child_box.y2 = child_box.y1 + entry_h; clutter_actor_allocate (priv->entry, &child_box, flags); if (priv->secondary_icon_tooltip) clutter_actor_allocate_preferred_size (CLUTTER_ACTOR (priv->secondary_icon_tooltip), flags); if (priv->primary_icon_tooltip) clutter_actor_allocate_preferred_size (CLUTTER_ACTOR (priv->primary_icon_tooltip), flags); } static void clutter_text_changed_cb (ClutterText *text, MxEntry *entry); static void clutter_text_focus_in_cb (ClutterText *text, ClutterActor *actor) { MxEntryPrivate *priv = MX_ENTRY_PRIV (actor); /* remove the hint if visible */ if (priv->hint && priv->hint_visible) { priv->hint_visible = FALSE; /* We don't want to emit the notify text change when we're swapping * between the hint text and blank for entry. */ g_signal_handlers_block_by_func (priv->entry, clutter_text_changed_cb, MX_ENTRY (actor)); clutter_text_set_text (text, ""); g_signal_handlers_unblock_by_func (priv->entry, clutter_text_changed_cb, MX_ENTRY (actor)); if (priv->password_char != 0) { clutter_text_set_password_char (text, priv->password_char); } } mx_stylable_set_style_pseudo_class (MX_STYLABLE (actor), "focus"); clutter_text_set_cursor_visible (text, TRUE); } static void clutter_text_focus_out_cb (ClutterText *text, ClutterActor *actor) { MxEntryPrivate *priv = MX_ENTRY_PRIV (actor); /* move the cursor back to the beginning of the entry */ clutter_text_set_cursor_position (CLUTTER_TEXT (priv->entry), 0); /* add a hint if the entry is empty */ if (priv->hint && !strcmp (clutter_text_get_text (text), "")) { priv->hint_visible = TRUE; /* We don't want to emit the notify text change when we're swapping * between the hint text and blank for entry. */ g_signal_handlers_block_by_func (priv->entry, clutter_text_changed_cb, MX_ENTRY (actor)); clutter_text_set_text (text, priv->hint); g_signal_handlers_unblock_by_func (priv->entry, clutter_text_changed_cb, MX_ENTRY (actor)); mx_stylable_set_style_pseudo_class (MX_STYLABLE (actor), "indeterminate"); if (clutter_text_get_password_char (text) != 0) { clutter_text_set_password_char (text, 0); } } else { mx_stylable_set_style_pseudo_class (MX_STYLABLE (actor), NULL); } clutter_text_set_cursor_visible (text, FALSE); } static void mx_entry_paint (ClutterActor *actor) { MxEntryPrivate *priv = MX_ENTRY_PRIV (actor); ClutterActorClass *parent_class; ClutterStage *stage; parent_class = CLUTTER_ACTOR_CLASS (mx_entry_parent_class); parent_class->paint (actor); clutter_actor_paint (priv->entry); if (priv->primary_icon) clutter_actor_paint (priv->primary_icon); if (priv->primary_icon_highlight) clutter_actor_paint (priv->primary_icon_highlight); if (priv->primary_icon_tooltip) clutter_actor_paint (CLUTTER_ACTOR (priv->primary_icon_tooltip)); if (priv->secondary_icon) clutter_actor_paint (priv->secondary_icon); if (priv->secondary_icon_highlight) clutter_actor_paint (priv->secondary_icon_highlight); if (priv->secondary_icon_tooltip) clutter_actor_paint (CLUTTER_ACTOR (priv->secondary_icon_tooltip)); /* draw a shadow if the entry is not focused and the text is scrolling */ stage = (ClutterStage *) clutter_actor_get_stage (priv->entry); if (clutter_stage_get_key_focus (stage) != priv->entry && priv->scrolling) { ClutterGeometry geo; CoglTextureVertex top[4] = { { 0,}, }; ClutterColor *color; guint8 r, g, b; mx_stylable_get (MX_STYLABLE (actor), "background-color", &color, NULL); r = color->red; g = color->green; b = color->blue; clutter_color_free (color); cogl_set_source_color4ub (0, 0, 0, 0); clutter_actor_get_allocation_geometry (priv->entry, &geo); top[0].x = geo.x + geo.width; top[0].y = geo.y + geo.height; top[1].x = geo.x + geo.width; top[1].y = geo.y; top[2].x = geo.x + geo.width - 30; top[2].y = geo.y; top[3].x = geo.x + geo.width - 30; top[3].y = geo.y + geo.height; cogl_color_set_from_4ub (&top[0].color, r, g, b, 0xff); cogl_color_set_from_4ub (&top[1].color, r, g, b, 0xff); cogl_color_set_from_4ub (&top[2].color, 0, 0, 0, 0); cogl_color_set_from_4ub (&top[3].color, 0, 0, 0, 0); cogl_polygon (top, 4, TRUE); } } static void mx_entry_pick (ClutterActor *actor, const ClutterColor *c) { MxEntryPrivate *priv = MX_ENTRY_PRIV (actor); CLUTTER_ACTOR_CLASS (mx_entry_parent_class)->pick (actor, c); if (!clutter_actor_should_pick_paint (actor)) return; clutter_actor_paint (priv->entry); if (priv->primary_icon) clutter_actor_paint (priv->primary_icon); if (priv->secondary_icon) clutter_actor_paint (priv->secondary_icon); } static void mx_entry_map (ClutterActor *actor) { MxEntryPrivate *priv = MX_ENTRY (actor)->priv; CLUTTER_ACTOR_CLASS (mx_entry_parent_class)->map (actor); clutter_actor_map (priv->entry); if (priv->primary_icon) clutter_actor_map (priv->primary_icon); if (priv->primary_icon_highlight) clutter_actor_map (priv->primary_icon_highlight); if (priv->secondary_icon) clutter_actor_map (priv->secondary_icon); if (priv->secondary_icon_highlight) clutter_actor_map (priv->secondary_icon_highlight); if (priv->secondary_icon_tooltip) clutter_actor_map (CLUTTER_ACTOR (priv->secondary_icon_tooltip)); if (priv->primary_icon_tooltip) clutter_actor_map (CLUTTER_ACTOR (priv->primary_icon_tooltip)); } static void mx_entry_unmap (ClutterActor *actor) { MxEntryPrivate *priv = MX_ENTRY (actor)->priv; CLUTTER_ACTOR_CLASS (mx_entry_parent_class)->unmap (actor); if (priv->entry) clutter_actor_unmap (priv->entry); if (priv->primary_icon) clutter_actor_unmap (priv->primary_icon); if (priv->primary_icon_highlight) clutter_actor_unmap (priv->primary_icon_highlight); if (priv->primary_icon_tooltip) clutter_actor_unmap (CLUTTER_ACTOR (priv->primary_icon_tooltip)); if (priv->secondary_icon) clutter_actor_unmap (priv->secondary_icon); if (priv->secondary_icon_highlight) clutter_actor_unmap (priv->secondary_icon_highlight); if (priv->secondary_icon_tooltip) clutter_actor_unmap (CLUTTER_ACTOR (priv->secondary_icon_tooltip)); } static void mx_entry_clipboard_callback (MxClipboard *clipboard, const gchar *text, gpointer data) { ClutterText *ctext = (ClutterText*)((MxEntry *) data)->priv->entry; gint cursor_pos; if (!text) return; /* delete the current selection before pasting */ clutter_text_delete_selection (ctext); /* "paste" the clipboard text into the entry */ cursor_pos = clutter_text_get_cursor_position (ctext); clutter_text_insert_text (ctext, text, cursor_pos); } static void mx_entry_update_preedit (MxEntry *entry) { PangoAttrList *list; PangoAttribute *attr; attr = pango_attr_underline_new (PANGO_UNDERLINE_SINGLE); list = pango_attr_list_new (); pango_attr_list_insert (list, attr); clutter_text_set_preedit_string (CLUTTER_TEXT (entry->priv->entry), entry->priv->preedit_string->str, list, 0); pango_attr_list_unref (list); } static gboolean mx_entry_captured_event (ClutterActor *actor, ClutterEvent *event) { MxEntryPrivate *priv = MX_ENTRY_PRIV (actor); ClutterKeyEvent *kevent; /* handle key events during unicode input mode */ if (event->type == CLUTTER_KEY_PRESS && priv->unicode_input_mode) { kevent = (ClutterKeyEvent*) event; if (kevent->keyval == CLUTTER_KEY_space || kevent->keyval == CLUTTER_KEY_Return || kevent->keyval == CLUTTER_KEY_Escape) { gunichar unichar; if (sscanf (priv->preedit_string->str + 1, "%x", &unichar) == 1) { clutter_text_set_preedit_string (CLUTTER_TEXT (priv->entry), NULL, NULL, 0); if (kevent->keyval != CLUTTER_KEY_Escape) clutter_text_insert_unichar (CLUTTER_TEXT (priv->entry), unichar); g_string_free (priv->preedit_string, TRUE); priv->preedit_string = NULL; } priv->unicode_input_mode = FALSE; return TRUE; } else if (kevent->keyval == CLUTTER_KEY_BackSpace) { g_string_truncate (priv->preedit_string, MAX (1, priv->preedit_string->len - 1)); mx_entry_update_preedit (MX_ENTRY (actor)); } else { if (g_unichar_isxdigit (kevent->unicode_value)) { g_string_append_c (priv->preedit_string, kevent->unicode_value); mx_entry_update_preedit (MX_ENTRY (actor)); } } return TRUE; } /* button press should cancel unicode input mode */ if (event->type == CLUTTER_BUTTON_PRESS && priv->unicode_input_mode) { clutter_text_set_preedit_string (CLUTTER_TEXT (priv->entry), NULL, NULL, 0); g_string_free (priv->preedit_string, TRUE); priv->preedit_string = NULL; priv->unicode_input_mode = FALSE; } return FALSE; } static gboolean mx_entry_key_press_event (ClutterActor *actor, ClutterKeyEvent *event) { MxEntryPrivate *priv = MX_ENTRY_PRIV (actor); /* This is expected to handle events that were emitted for the inner ClutterText. They only reach this function if the ClutterText didn't handle them */ /* paste */ if ((event->modifier_state & CLUTTER_CONTROL_MASK) && event->keyval == CLUTTER_KEY_v) { MxClipboard *clipboard; clipboard = mx_clipboard_get_default (); mx_clipboard_get_text (clipboard, mx_entry_clipboard_callback, actor); return TRUE; } /* copy */ if ((event->modifier_state & CLUTTER_CONTROL_MASK) && event->keyval == CLUTTER_KEY_c) { MxClipboard *clipboard; gchar *text; clipboard = mx_clipboard_get_default (); text = clutter_text_get_selection ((ClutterText*) priv->entry); if (text && strlen (text)) mx_clipboard_set_text (clipboard, text); return TRUE; } /* cut */ if ((event->modifier_state & CLUTTER_CONTROL_MASK) && event->keyval == CLUTTER_KEY_x) { MxClipboard *clipboard; gchar *text; clipboard = mx_clipboard_get_default (); text = clutter_text_get_selection ((ClutterText*) priv->entry); if (text && strlen (text)) { mx_clipboard_set_text (clipboard, text); /* now delete the text */ clutter_text_delete_selection ((ClutterText *) priv->entry); } return TRUE; } /* undo */ if ((event->modifier_state & CLUTTER_CONTROL_MASK) && event->keyval == CLUTTER_KEY_z) { gchar *str; /* pop the current value off the undo history stack and set the text to * the new value */ if (priv->undo_timeout_source == 0) { str = g_queue_pop_head (priv->undo_history); g_free (str); } /* prevent storing the value just restored */ priv->pause_undo = TRUE; if (priv->undo_history) { clutter_text_set_text (CLUTTER_TEXT (priv->entry), (gchar*) g_queue_peek_head (priv->undo_history)); } return TRUE; } /* unicode input mode */ if (event->modifier_state & CLUTTER_CONTROL_MASK && event->modifier_state & CLUTTER_SHIFT_MASK && event->keyval == CLUTTER_KEY_U) { priv->unicode_input_mode = TRUE; priv->preedit_string = g_string_new ("u"); mx_entry_update_preedit (MX_ENTRY (actor)); } return FALSE; } static void mx_entry_key_focus_in (ClutterActor *actor) { MxEntryPrivate *priv = MX_ENTRY_PRIV (actor); /* We never want key focus. The ClutterText should be given first pass for all key events */ clutter_actor_grab_key_focus (priv->entry); } #ifdef HAVE_X11 static void mx_entry_set_cursor (MxEntry *entry, gboolean use_ibeam) { Display *dpy; ClutterActor *stage, *actor = CLUTTER_ACTOR (entry); Window wid; static Cursor ibeam = None; dpy = clutter_x11_get_default_display (); stage = clutter_actor_get_stage (actor); wid = clutter_x11_get_stage_window (CLUTTER_STAGE (stage)); if (ibeam == None) ibeam = XCreateFontCursor (dpy, XC_xterm); if (use_ibeam) XDefineCursor (dpy, wid, ibeam); else XUndefineCursor (dpy, wid); } #endif static gboolean mx_entry_swallow_crossing_event (ClutterActor *actor, ClutterCrossingEvent *event) { #ifdef HAVE_X11 if (event->source == MX_ENTRY (actor)->priv->entry && event->related != NULL) mx_entry_set_cursor (MX_ENTRY (actor), (event->type == CLUTTER_ENTER)); #endif /* swallow enter and leave events, since the pseudo-class must not be set to * 'hover' because it would loose the 'focus' state. */ return TRUE; } static gboolean mx_entry_swallow_button_event (ClutterActor *actor, ClutterButtonEvent *event) { /* swallow button events, since the pseudo-class must not be set to * 'active' because it would loose the 'focus' state. */ return TRUE; } static void mx_entry_class_init (MxEntryClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass); GParamSpec *pspec; g_type_class_add_private (klass, sizeof (MxEntryPrivate)); gobject_class->set_property = mx_entry_set_property; gobject_class->get_property = mx_entry_get_property; gobject_class->finalize = mx_entry_finalize; gobject_class->dispose = mx_entry_dispose; actor_class->get_preferred_width = mx_entry_get_preferred_width; actor_class->get_preferred_height = mx_entry_get_preferred_height; actor_class->allocate = mx_entry_allocate; actor_class->paint = mx_entry_paint; actor_class->pick = mx_entry_pick; actor_class->map = mx_entry_map; actor_class->unmap = mx_entry_unmap; actor_class->enter_event = mx_entry_swallow_crossing_event; actor_class->leave_event = mx_entry_swallow_crossing_event; actor_class->button_press_event = mx_entry_swallow_button_event; actor_class->button_release_event = mx_entry_swallow_button_event; actor_class->captured_event = mx_entry_captured_event; actor_class->key_press_event = mx_entry_key_press_event; actor_class->key_focus_in = mx_entry_key_focus_in; pspec = g_param_spec_object ("clutter-text", "Clutter Text", "Internal ClutterText actor", CLUTTER_TYPE_TEXT, G_PARAM_READABLE); g_object_class_install_property (gobject_class, PROP_CLUTTER_TEXT, pspec); pspec = g_param_spec_string ("hint-text", "Hint Text", "Text to display when the entry is not focused " "and the text property is empty", NULL, G_PARAM_READWRITE); g_object_class_install_property (gobject_class, PROP_HINT_TEXT, pspec); pspec = g_param_spec_string ("text", "Text", "Text of the entry", NULL, G_PARAM_READWRITE); g_object_class_install_property (gobject_class, PROP_TEXT, pspec); pspec = g_param_spec_unichar ("password-char", "Password Character", "Character to display instead of entered text", 0, G_PARAM_READWRITE); g_object_class_install_property (gobject_class, PROP_PASSWORD_CHAR, pspec); pspec = g_param_spec_string ("icon-highlight-suffix", "Icon highlight suffix", "The filename suffix for the highligh icon", NULL, G_PARAM_READWRITE); g_object_class_install_property (gobject_class, PROP_ICON_HIGHLIGHT_SUFFIX, pspec); pspec = g_param_spec_string ("primary-icon-tooltip-text", "Tooltip for the primary icon", "The tooltip text for the primary icon", NULL, G_PARAM_READWRITE); g_object_class_install_property (gobject_class, PROP_PRIMARY_ICON_TOOLTIP_TEXT, pspec); pspec = g_param_spec_string ("secondary-icon-tooltip-text", "Tooltip for the secondary icon", "The tooltip text for the secondary icon", NULL, G_PARAM_READWRITE); g_object_class_install_property (gobject_class, PROP_SECONDARY_ICON_TOOLTIP_TEXT, pspec); /* signals */ /** * MxEntry::primary-icon-clicked: * * Emitted when the primary icon is clicked */ entry_signals[PRIMARY_ICON_CLICKED] = g_signal_new ("primary-icon-clicked", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (MxEntryClass, primary_icon_clicked), NULL, NULL, _mx_marshal_VOID__VOID, G_TYPE_NONE, 0); /** * MxEntry::secondary-icon-clicked: * * Emitted when the secondary icon is clicked */ entry_signals[SECONDARY_ICON_CLICKED] = g_signal_new ("secondary-icon-clicked", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (MxEntryClass, secondary_icon_clicked), NULL, NULL, _mx_marshal_VOID__VOID, G_TYPE_NONE, 0); } static gboolean mx_entry_store_undo_timeout (MxEntry *entry) { const gchar *str; MxEntryPrivate *priv = entry->priv; priv->undo_timeout_source = 0; str = mx_entry_get_text (entry); if (!priv->undo_history) priv->undo_history = g_queue_new (); /* prevent duplicated */ if (!g_strcmp0 (str, (gchar*) g_queue_peek_head (priv->undo_history))) { return FALSE; } g_queue_push_head (priv->undo_history, g_strdup (str)); /* keep the undo history to only 20 items */ if (g_queue_get_length (priv->undo_history) > 20) { gpointer *tmp; tmp = g_queue_pop_tail (priv->undo_history); g_free (tmp); } return FALSE; } static void mx_entry_store_undo_history (ClutterText *text, MxEntry *entry) { if (entry->priv->undo_timeout_source) { g_source_remove (entry->priv->undo_timeout_source); entry->priv->undo_timeout_source = 0; } if (entry->priv->pause_undo) { entry->priv->pause_undo = FALSE; return; } entry->priv->undo_timeout_source = g_timeout_add (750, (GSourceFunc) mx_entry_store_undo_timeout, entry); } static gboolean mx_entry_store_undo_on_keypress (ClutterText *text, ClutterKeyEvent *event, MxEntry *entry) { if (event->keyval == CLUTTER_KEY_space) { /* store the text immediately */ if (entry->priv->undo_timeout_source) { g_source_remove (entry->priv->undo_timeout_source); entry->priv->undo_timeout_source = 0; } mx_entry_store_undo_timeout (entry); entry->priv->pause_undo = TRUE; } return FALSE; } static void clutter_text_changed_cb (ClutterText *text, MxEntry *entry) { g_return_if_fail (MX_IS_ENTRY (entry)); g_object_notify (G_OBJECT (entry), "text"); mx_entry_store_undo_history (text, entry); } static gboolean entry_event (ClutterActor *actor, ClutterEvent *event, MxEntry *entry) { #ifdef HAVE_X11 /* ensure the cursor is in the correct state when the mouse button is * released */ if (event->type == CLUTTER_BUTTON_RELEASE) mx_entry_set_cursor (entry, entry->priv->pointer_in_entry); #endif /* don't track enter and leave events for actors other than the entry */ if (event->any.source != actor) return FALSE; switch (event->type) { case CLUTTER_LEAVE: entry->priv->pointer_in_entry = FALSE; break; case CLUTTER_ENTER: entry->priv->pointer_in_entry = TRUE; break; default: break; } return FALSE; } static void mx_entry_init (MxEntry *entry) { MxEntryPrivate *priv; priv = entry->priv = MX_ENTRY_GET_PRIVATE (entry); #ifdef HAVE_CLUTTER_IMCONTEXT priv->entry = g_object_new (CLUTTER_TYPE_IMTEXT, #else priv->entry = g_object_new (CLUTTER_TYPE_TEXT, #endif "line-alignment", PANGO_ALIGN_LEFT, "editable", TRUE, "reactive", TRUE, "single-line-mode", TRUE, NULL); g_signal_connect (priv->entry, "key-focus-in", G_CALLBACK (clutter_text_focus_in_cb), entry); g_signal_connect (priv->entry, "key-focus-out", G_CALLBACK (clutter_text_focus_out_cb), entry); g_signal_connect (priv->entry, "text-changed", G_CALLBACK (clutter_text_changed_cb), entry); g_signal_connect (priv->entry, "key-press-event", G_CALLBACK (mx_entry_store_undo_on_keypress), entry); g_signal_connect (priv->entry, "event", G_CALLBACK (entry_event), entry); priv->spacing = 6.0f; clutter_actor_push_internal (CLUTTER_ACTOR (entry)); clutter_actor_set_parent (priv->entry, CLUTTER_ACTOR (entry)); clutter_actor_pop_internal (CLUTTER_ACTOR (entry)); clutter_actor_set_reactive ((ClutterActor *) entry, TRUE); /* set cursor hidden until we receive focus */ clutter_text_set_cursor_visible ((ClutterText *) priv->entry, FALSE); g_signal_connect (entry, "style-changed", G_CALLBACK (mx_entry_style_changed), NULL); } /** * mx_entry_new: * * Create a new #MxEntry * * Returns: a new #MxEntry */ ClutterActor * mx_entry_new (void) { return g_object_new (MX_TYPE_ENTRY, NULL); } /** * mx_entry_new_with_text: * @text: text to set the entry to * * Create a new #MxEntry with the specified entry * * Returns: a new #MxEntry */ ClutterActor * mx_entry_new_with_text (const gchar *text) { MxWidget *entry; entry = g_object_new (MX_TYPE_ENTRY, "text", text, NULL); return (ClutterActor*) entry; } /** * mx_entry_get_text: * @entry: a #MxEntry * * Get the text displayed on the entry * * Returns: the text for the entry. This must not be freed by the application */ const gchar * mx_entry_get_text (MxEntry *entry) { g_return_val_if_fail (MX_IS_ENTRY (entry), NULL); if (entry->priv->hint_visible) return ""; else return clutter_text_get_text (CLUTTER_TEXT (entry->priv->entry)); } /** * mx_entry_set_text: * @entry: a #MxEntry * @text: text to set the entry to * * Sets the text displayed on the entry */ void mx_entry_set_text (MxEntry *entry, const gchar *entry_text) { MxEntryPrivate *priv; gunichar password_char; const gchar *text; g_return_if_fail (MX_IS_ENTRY (entry)); if (entry_text) text = entry_text; else text = ""; priv = entry->priv; /* set a hint if we are blanking the entry */ if (priv->hint && text && !strcmp ("", text) && !HAS_FOCUS (priv->entry)) { text = priv->hint; priv->hint_visible = TRUE; mx_stylable_set_style_pseudo_class (MX_STYLABLE (entry), "indeterminate"); password_char = 0; } else { if (HAS_FOCUS (priv->entry)) mx_stylable_set_style_pseudo_class (MX_STYLABLE (entry), "focus"); else mx_stylable_set_style_pseudo_class (MX_STYLABLE (entry), NULL); priv->hint_visible = FALSE; password_char = priv->password_char; } clutter_text_set_text (CLUTTER_TEXT (priv->entry), text); if (clutter_text_get_password_char (CLUTTER_TEXT (priv->entry)) != password_char) { clutter_text_set_password_char (CLUTTER_TEXT (priv->entry), password_char); } } /** * mx_entry_get_clutter_text: * @entry: a #MxEntry * * Retrieve the internal #ClutterText so that extra parameters can be set * * Returns: (transfer none): the #ClutterText used by #MxEntry. The entry is * owned by the #MxEntry and should not be unref'ed by the application. */ ClutterActor* mx_entry_get_clutter_text (MxEntry *entry) { g_return_val_if_fail (MX_IS_ENTRY (entry), NULL); return entry->priv->entry; } /** * mx_entry_set_hint_text: * @entry: a #MxEntry * @text: text to set as the entry hint * * Sets the text to display when the entry is empty and unfocused. When the * entry is displaying the hint, it has a pseudo class of "indeterminate". * A value of NULL unsets the hint. */ void mx_entry_set_hint_text (MxEntry *entry, const gchar *text) { MxEntryPrivate *priv; g_return_if_fail (MX_IS_ENTRY (entry)); priv = entry->priv; g_free (priv->hint); priv->hint = g_strdup (text); if (!strcmp (clutter_text_get_text (CLUTTER_TEXT (priv->entry)), "") && !HAS_FOCUS (priv->entry)) { priv->hint_visible = TRUE; clutter_text_set_text (CLUTTER_TEXT (priv->entry), priv->hint); mx_stylable_set_style_pseudo_class (MX_STYLABLE (entry), "indeterminate"); if (clutter_text_get_password_char (CLUTTER_TEXT (priv->entry)) != 0) { clutter_text_set_password_char (CLUTTER_TEXT (priv->entry), 0); } } } /** * mx_entry_get_hint_text: * @entry: a #MxEntry * * Gets the text that is displayed when the entry is empty and unfocused * * Returns: the current value of the hint property. This string is owned by the * #MxEntry and should not be freed or modified. */ const gchar * mx_entry_get_hint_text (MxEntry *entry) { g_return_val_if_fail (MX_IS_ENTRY (entry), NULL); return entry->priv->hint; } /** * mx_entry_set_password_char: * @entry: a #MxEntry * @password_char: text to set as the entry hint * * Sets the character to display instead of the text. Use 0 to display * the actual text. */ void mx_entry_set_password_char (MxEntry *entry, gunichar password_char) { MxEntryPrivate *priv; g_return_if_fail (MX_IS_ENTRY (entry)); priv = entry->priv; priv->password_char = password_char; if (!priv->hint_visible) { clutter_text_set_password_char (CLUTTER_TEXT (priv->entry), password_char); } } /** * mx_entry_get_password_char: * @entry: a #MxEntry * * Gets the character to display instead of the text. * * Return value: a character, or 0 if input should not be hidden. */ gunichar mx_entry_get_password_char (MxEntry *entry) { g_return_val_if_fail (MX_IS_ENTRY (entry), 0); return entry->priv->password_char; } static gboolean _mx_entry_icon_press_cb (ClutterActor *actor, ClutterButtonEvent *event, MxEntry *entry) { MxEntryPrivate *priv = entry->priv; if (actor == priv->primary_icon) { g_signal_emit (entry, entry_signals[PRIMARY_ICON_CLICKED], 0); if (priv->primary_icon_tooltip) mx_tooltip_hide (MX_TOOLTIP (priv->primary_icon_tooltip)); } else { g_signal_emit (entry, entry_signals[SECONDARY_ICON_CLICKED], 0); if (priv->secondary_icon_tooltip) mx_tooltip_hide (MX_TOOLTIP (priv->secondary_icon_tooltip)); } return FALSE; } static gboolean _mx_entry_tooltip_timeout_cb (gpointer data) { if (!CLUTTER_ACTOR_IS_VISIBLE (CLUTTER_ACTOR (data))) mx_tooltip_show (MX_TOOLTIP (data)); else mx_tooltip_hide (MX_TOOLTIP (data)); return FALSE; } static guint _mx_entry_which_icon (ClutterActor *actor, MxEntry *entry) { MxEntryPrivate *priv = entry->priv; if (actor == priv->primary_icon) return 1; else if (actor == priv->secondary_icon) return 2; return 0; } static gchar * _mx_entry_make_highlight_filename (MxEntry *entry, const gchar *original_filename) { MxEntryPrivate *priv = entry->priv; gchar *extension, *highlight_icon_path, *filepath; extension = g_strrstr (original_filename, "."); filepath = g_strndup (original_filename, (strlen (original_filename) - strlen (extension))); highlight_icon_path = g_strconcat (filepath, priv->icon_highlight_suffix, extension, NULL); g_free (filepath); if (!g_file_test (highlight_icon_path, G_FILE_TEST_EXISTS)) { g_free (highlight_icon_path); highlight_icon_path = NULL; } return highlight_icon_path; } static void _mx_entry_switch_to_highlight_icon (ClutterActor *icon, ClutterActor *icon_highlight, gboolean highlighted) { /* Switches round the highlight icon to visible or the normal icon * depending on highlighted */ guint icon_opacity; guint highlight_icon_opacity; if (!icon_highlight) return; icon_opacity = !highlighted*255; highlight_icon_opacity = highlighted*255; if (clutter_actor_get_opacity (icon) != icon_opacity) clutter_actor_set_opacity (icon, icon_opacity); if(clutter_actor_get_opacity (icon_highlight) != highlight_icon_opacity) clutter_actor_set_opacity (icon_highlight, highlight_icon_opacity); } static gboolean _mx_entry_icon_motion_event_cb (ClutterActor *actor, ClutterEvent *event, MxEntry *entry) { MxEntryPrivate *priv = entry->priv; MxTooltip *target_tooltip = NULL; guint icon_position; icon_position = _mx_entry_which_icon (actor, entry); if (icon_position == 1) { target_tooltip = priv->primary_icon_tooltip; _mx_entry_switch_to_highlight_icon (priv->primary_icon, priv->primary_icon_highlight, TRUE); } else if (icon_position == 2) { target_tooltip = priv->secondary_icon_tooltip; _mx_entry_switch_to_highlight_icon (priv->secondary_icon, priv->secondary_icon_highlight, TRUE); } /* Show the tooltip if it's not already visible */ if (target_tooltip && !CLUTTER_ACTOR_IS_VISIBLE (target_tooltip)) { if (mx_tooltip_is_in_browse_mode ()) { mx_tooltip_show (target_tooltip); if (priv->tooltip_timeout) { g_source_remove (priv->tooltip_timeout); priv->tooltip_timeout = 0; } } else { /* Not in browse mode so add a small delay before showing tooltip */ if (!priv->tooltip_timeout) priv->tooltip_timeout = clutter_threads_add_timeout (MX_ENTRY_TOOLTIP_DELAY, _mx_entry_tooltip_timeout_cb, target_tooltip); } } return FALSE; } static gboolean _mx_entry_icon_leave_cb (ClutterActor *actor, ClutterEvent *event, MxEntry *entry) { MxEntryPrivate *priv = entry->priv; MxTooltip *target_tooltip = NULL; guint icon_position; icon_position = _mx_entry_which_icon (actor, entry); if (icon_position == 1) { target_tooltip = priv->primary_icon_tooltip; _mx_entry_switch_to_highlight_icon (priv->primary_icon, priv->primary_icon_highlight, FALSE); } else if (icon_position == 2) { target_tooltip = priv->secondary_icon_tooltip; _mx_entry_switch_to_highlight_icon (priv->secondary_icon, priv->secondary_icon_highlight, FALSE); } if (priv->tooltip_timeout) { g_source_remove (priv->tooltip_timeout); priv->tooltip_timeout = 0; } mx_tooltip_hide (target_tooltip); return FALSE; } static void _mx_entry_set_icon_from_file (MxEntry *entry, ClutterActor **icon, const gchar *filename) { if (*icon) { g_signal_handlers_disconnect_by_func (*icon, _mx_entry_icon_press_cb, entry); g_signal_handlers_disconnect_by_func (*icon, _mx_entry_icon_motion_event_cb, entry); g_signal_handlers_disconnect_by_func (*icon, _mx_entry_icon_leave_cb, entry); clutter_actor_unparent (*icon); *icon = NULL; } if (filename) { MxTextureCache *cache; cache = mx_texture_cache_get_default (); *icon = (ClutterActor*) mx_texture_cache_get_texture (cache, filename); if (!*icon) return; clutter_actor_set_reactive (*icon, TRUE); clutter_actor_push_internal (CLUTTER_ACTOR (entry)); clutter_actor_set_parent (*icon, CLUTTER_ACTOR (entry)); clutter_actor_pop_internal (CLUTTER_ACTOR (entry)); g_signal_connect (*icon, "button-release-event", G_CALLBACK (_mx_entry_icon_press_cb), entry); g_signal_connect (*icon, "motion-event", G_CALLBACK (_mx_entry_icon_motion_event_cb), entry); g_signal_connect (*icon, "leave-event", G_CALLBACK (_mx_entry_icon_leave_cb), entry); } clutter_actor_queue_relayout (CLUTTER_ACTOR (entry)); } static void _mx_entry_create_highlight_icon (MxEntry *entry, guint icon_position) { MxEntryPrivate *priv = entry->priv; ClutterActor **target = NULL; const gchar *filename = NULL; gchar *highlight_filename; if (!priv->icon_highlight_suffix) return; if (icon_position == 1) { target = &priv->primary_icon_highlight; filename = priv->primary_icon_filename; } else if (icon_position == 2) { target = &priv->secondary_icon_highlight; filename = priv->secondary_icon_filename; } if (!filename) return; highlight_filename = _mx_entry_make_highlight_filename (entry, filename); if (highlight_filename) { _mx_entry_set_icon_from_file (entry, target, highlight_filename); clutter_actor_set_opacity (*target, 0x0); g_free (highlight_filename); } } /** * mx_entry_set_primary_icon_from_file: * @entry: a #MxEntry * @filename: filename of an icon * * Set the primary icon of the entry to the given filename */ void mx_entry_set_primary_icon_from_file (MxEntry *entry, const gchar *filename) { MxEntryPrivate *priv; g_return_if_fail (MX_IS_ENTRY (entry)); priv = entry->priv; if (priv->primary_icon_filename) g_free (priv->primary_icon_filename); priv->primary_icon_filename = g_strdup (filename); _mx_entry_set_icon_from_file (entry, &priv->primary_icon, filename); _mx_entry_create_highlight_icon (entry, 1); } /** * mx_entry_set_secondary_icon_from_file: * @entry: a #MxEntry * @filename: filename of an icon * * Set the primary icon of the entry to the given filename */ void mx_entry_set_secondary_icon_from_file (MxEntry *entry, const gchar *filename) { MxEntryPrivate *priv; g_return_if_fail (MX_IS_ENTRY (entry)); priv = entry->priv; if (priv->secondary_icon_filename) g_free (priv->secondary_icon_filename); priv->secondary_icon_filename = g_strdup (filename); _mx_entry_set_icon_from_file (entry, &priv->secondary_icon, filename); _mx_entry_create_highlight_icon (entry, 2); } /** * mx_entry_set_secondary_icon_tooltip: * @entry: a #MxEntry * @text: the secondary icon tooltip * * Set the secondary icon tooltip text */ void mx_entry_set_secondary_icon_tooltip_text (MxEntry *entry, const gchar *text) { MxEntryPrivate *priv; g_return_if_fail (MX_IS_ENTRY (entry)); priv = entry->priv; if (!priv->secondary_icon_tooltip) { priv->secondary_icon_tooltip = g_object_new (MX_TYPE_TOOLTIP, "text", text, NULL); mx_tooltip_set_text (priv->secondary_icon_tooltip, text); clutter_actor_set_parent (CLUTTER_ACTOR (priv->secondary_icon_tooltip), CLUTTER_ACTOR (entry)); } else { mx_tooltip_set_text (priv->secondary_icon_tooltip, text); } } /** * mx_entry_set_primary_icon_tooltip: * @entry: a #MxEntry * @text: the primary icon tooltip * * Set the primary icon tooltip text */ void mx_entry_set_primary_icon_tooltip_text (MxEntry *entry, const gchar *text) { MxEntryPrivate *priv; g_return_if_fail (MX_IS_ENTRY (entry)); priv = entry->priv; if (!priv->primary_icon_tooltip) { priv->primary_icon_tooltip = g_object_new (MX_TYPE_TOOLTIP, "text", text, NULL); mx_tooltip_set_text (priv->primary_icon_tooltip, text); clutter_actor_set_parent (CLUTTER_ACTOR (priv->primary_icon_tooltip), CLUTTER_ACTOR (entry)); } else { mx_tooltip_set_text (priv->primary_icon_tooltip, text); } } /** * mx_entry_get_icon_highlight_suffix: * @entry: a #MxEntry * * Get the suffix appended to the filename to use for the highlighted version * of the icon. * * Returns: the highlight filename suffix. This string is owned by the * #MxEntry and should not be freed or modified. */ const gchar *mx_entry_get_icon_highlight_suffix (MxEntry *entry) { MxEntryPrivate *priv; g_return_val_if_fail (MX_IS_ENTRY (entry), NULL); priv = entry->priv; return priv->icon_highlight_suffix; } /** * mx_entry_set_icon_highlight_suffix: * @entry: a #MxEntry * @suffix: the suffix to append to the filename for the highlight version * * Sets the suffix appended to the filename to use for the highlighted version * of the icon. e.g. if you have set your primay icon to "primary-icon.png" * and the suffix to "-highlight" #MxEntry will look for * "primary-icon-highlight.png" */ void mx_entry_set_icon_highlight_suffix (MxEntry *entry, const gchar* suffix) { MxEntryPrivate *priv; g_return_if_fail (MX_IS_ENTRY (entry)); priv = entry->priv; if (g_strcmp0 (priv->icon_highlight_suffix, suffix) == 0) return; if (priv->icon_highlight_suffix) g_free (priv->icon_highlight_suffix); priv->icon_highlight_suffix = g_strdup (suffix); _mx_entry_create_highlight_icon (entry, 1); _mx_entry_create_highlight_icon (entry, 2); } mx-1.4.7/mx/mx-entry.h000066400000000000000000000077061201047117600145400ustar00rootroot00000000000000/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * mx-entry.h: Plain entry actor * * Copyright 2008, 2009 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * Boston, MA 02111-1307, USA. * * Written by: Thomas Wood * */ #if !defined(MX_H_INSIDE) && !defined(MX_COMPILATION) #error "Only can be included directly.h" #endif #ifndef __MX_ENTRY_H__ #define __MX_ENTRY_H__ G_BEGIN_DECLS #include #define MX_TYPE_ENTRY (mx_entry_get_type ()) #define MX_ENTRY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MX_TYPE_ENTRY, MxEntry)) #define MX_IS_ENTRY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MX_TYPE_ENTRY)) #define MX_ENTRY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MX_TYPE_ENTRY, MxEntryClass)) #define MX_IS_ENTRY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MX_TYPE_ENTRY)) #define MX_ENTRY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MX_TYPE_ENTRY, MxEntryClass)) typedef struct _MxEntry MxEntry; typedef struct _MxEntryPrivate MxEntryPrivate; typedef struct _MxEntryClass MxEntryClass; /** * MxEntry: * * The contents of this structure is private and should only be accessed using * the provided API. */ struct _MxEntry { /*< private >*/ MxWidget parent_instance; MxEntryPrivate *priv; }; struct _MxEntryClass { MxWidgetClass parent_class; /* signals */ void (*primary_icon_clicked) (MxEntry *entry); void (*secondary_icon_clicked) (MxEntry *entry); /* padding for future expansion */ void (*_padding_0) (void); void (*_padding_1) (void); void (*_padding_2) (void); void (*_padding_3) (void); void (*_padding_4) (void); }; GType mx_entry_get_type (void) G_GNUC_CONST; ClutterActor *mx_entry_new (void); ClutterActor *mx_entry_new_with_text (const gchar *text); const gchar *mx_entry_get_text (MxEntry *entry); void mx_entry_set_text (MxEntry *entry, const gchar *text); ClutterActor* mx_entry_get_clutter_text (MxEntry *entry); void mx_entry_set_hint_text (MxEntry *entry, const gchar *text); const gchar *mx_entry_get_hint_text (MxEntry *entry); void mx_entry_set_password_char (MxEntry *entry, gunichar password_char); gunichar mx_entry_get_password_char (MxEntry *entry); void mx_entry_set_primary_icon_from_file (MxEntry *entry, const gchar *filename); void mx_entry_set_primary_icon_tooltip_text (MxEntry *entry, const gchar *text); void mx_entry_set_secondary_icon_from_file (MxEntry *entry, const gchar *filename); void mx_entry_set_secondary_icon_tooltip_text (MxEntry *entry, const gchar *text); void mx_entry_set_icon_highlight_suffix (MxEntry *entry, const gchar *suffix); const gchar *mx_entry_get_icon_highlight_suffix (MxEntry *entry); G_END_DECLS #endif /* __MX_ENTRY_H__ */ mx-1.4.7/mx/mx-enum-types.c.in000066400000000000000000000013531201047117600160750ustar00rootroot00000000000000/*** BEGIN file-header ***/ #include "mx-enum-types.h" /*** END file-header ***/ /*** BEGIN file-production ***/ /* enumerations from "@filename@" */ #include "@filename@" /*** END file-production ***/ /*** BEGIN value-header ***/ GType @enum_name@_get_type(void) { static GType enum_type_id = 0; if (G_UNLIKELY (!enum_type_id)) { static const G@Type@Value values[] = { /*** END value-header ***/ /*** BEGIN value-production ***/ { @VALUENAME@, "@VALUENAME@", "@valuenick@" }, /*** END value-production ***/ /*** BEGIN value-tail ***/ { 0, NULL, NULL } }; enum_type_id = g_@type@_register_static (g_intern_static_string ("@EnumName@"), values); } return enum_type_id; } /*** END value-tail ***/ mx-1.4.7/mx/mx-enum-types.h.in000066400000000000000000000011561201047117600161030ustar00rootroot00000000000000/*** BEGIN file-header ***/ #if !defined(MX_H_INSIDE) && !defined(MX_COMPILATION) #error "Only can be included directly.h" #endif #ifndef __MX_ENUM_TYPES_H__ #define __MX_ENUM_TYPES_H__ #include G_BEGIN_DECLS /*** END file-header ***/ /*** BEGIN file-production ***/ /* enumerations from "@filename@" */ /*** END file-production ***/ /*** BEGIN file-tail ***/ G_END_DECLS #endif /* !__MX_ENUM_TYPES_H__ */ /*** END file-tail ***/ /*** BEGIN value-header ***/ GType @enum_name@_get_type (void) G_GNUC_CONST; #define MX_TYPE_@ENUMSHORT@ (@enum_name@_get_type()) /*** END value-header ***/ mx-1.4.7/mx/mx-expander.c000066400000000000000000000510061201047117600151700ustar00rootroot00000000000000/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * mx-expander.c: Expander Widget * * Copyright 2009, 2010 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * * Written by: Thomas Wood * */ /** * SECTION:mx-expander * @short_description: a container which the user can show or hide its child * * #MxExpander is a single child container that allows the user to show or * hide its child. It displays a clickable bar (with a text label), * which (by default) when clicked toggles display of the child. * *
* MxExpander in its expanded state * The image shows an #MxExpander with the * #MxExpander:expanded property set to * #TRUE. * *
* *
* MxExpander in its contracted state * The image shows an #MxExpander with the * #MxExpander:expanded property set to * #FALSE. * *
*/ #include "mx-marshal.h" #include "mx-expander.h" #include "mx-private.h" #include "mx-stylable.h" #include "mx-icon.h" static void clutter_container_iface_init (ClutterContainerIface *iface); G_DEFINE_TYPE_WITH_CODE (MxExpander, mx_expander, MX_TYPE_BIN, G_IMPLEMENT_INTERFACE (CLUTTER_TYPE_CONTAINER, clutter_container_iface_init)) static ClutterContainerIface *container_parent_class = NULL; #define GET_PRIVATE(o) \ (G_TYPE_INSTANCE_GET_PRIVATE ((o), MX_TYPE_EXPANDER, MxExpanderPrivate)) enum { PROP_0, PROP_EXPANDED, PROP_LABEL }; enum { EXPAND_COMPLETE, LAST_SIGNAL }; struct _MxExpanderPrivate { ClutterActor *label; ClutterActor *arrow; gfloat spacing; ClutterTimeline *timeline; ClutterAlpha *alpha; gdouble progress; guint expanded : 1; }; static guint expander_signals[LAST_SIGNAL] = { 0, }; static void mx_expander_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { MxExpanderPrivate *priv = MX_EXPANDER (object)->priv; switch (property_id) { case PROP_EXPANDED: g_value_set_boolean (value, priv->expanded); break; case PROP_LABEL: g_value_set_string (value, clutter_text_get_text (CLUTTER_TEXT (priv->label))); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } } static void mx_expander_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { switch (property_id) { case PROP_EXPANDED: mx_expander_set_expanded ((MxExpander *) object, g_value_get_boolean (value)); break; case PROP_LABEL: mx_expander_set_label ((MxExpander *) object, g_value_get_string (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } } static void mx_expander_dispose (GObject *object) { MxExpanderPrivate *priv = MX_EXPANDER (object)->priv; if (priv->label) { clutter_actor_unparent (priv->label); priv->label = NULL; } if (priv->arrow) { clutter_actor_unparent (priv->arrow); priv->arrow = NULL; } if (priv->timeline) { g_object_unref (priv->timeline); priv->timeline = NULL; } if (priv->alpha) { g_object_unref (priv->alpha); priv->alpha = NULL; } G_OBJECT_CLASS (mx_expander_parent_class)->dispose (object); } static void mx_expander_finalize (GObject *object) { G_OBJECT_CLASS (mx_expander_parent_class)->finalize (object); } static void timeline_complete (ClutterTimeline *timeline, ClutterActor *expander) { guchar opacity; ClutterActor *child; MxExpanderPrivate *priv = MX_EXPANDER (expander)->priv; g_signal_emit (expander, expander_signals[EXPAND_COMPLETE], 0); /* if the expander is now closed, update the style */ if (!priv->expanded) { clutter_actor_set_name (priv->arrow, "mx-expander-arrow-closed"); mx_stylable_set_style_class (MX_STYLABLE (expander), "closed-expander"); clutter_actor_queue_relayout (expander); } child = mx_bin_get_child (MX_BIN (expander)); if (!child) return; /* continue only if we are "opening" */ if (!priv->expanded) return; /* we can't do an animation if there is already one in progress, * because we cannot get the actors original opacity */ if (clutter_actor_get_animation (child)) { clutter_actor_show (child); return; } opacity = clutter_actor_get_opacity (child); clutter_actor_set_opacity (child, 0); clutter_actor_show (child); clutter_actor_animate (child, CLUTTER_EASE_IN_SINE, 100, "opacity", opacity, NULL); } static void new_frame (ClutterTimeline *timeline, gint frame_num, ClutterActor *expander) { MxExpanderPrivate *priv = MX_EXPANDER (expander)->priv; priv->progress = clutter_alpha_get_alpha (priv->alpha); clutter_actor_queue_relayout (expander); } static void mx_expander_update (MxExpander *expander) { MxExpanderPrivate *priv = expander->priv; ClutterActor *child; if (priv->expanded) { clutter_actor_set_name (priv->arrow, "mx-expander-arrow-open"); mx_stylable_set_style_class (MX_STYLABLE (expander), "open-expander"); } /* closed state is set when animation is finished */ child = mx_bin_get_child (MX_BIN (expander)); if (!child) return; /* setup and start the expansion animation */ if (!priv->expanded) { clutter_actor_hide (child); clutter_timeline_set_direction (priv->timeline, CLUTTER_TIMELINE_BACKWARD); } else { clutter_timeline_set_direction (priv->timeline, CLUTTER_TIMELINE_FORWARD); } if (!clutter_timeline_is_playing (priv->timeline)) clutter_timeline_rewind (priv->timeline); clutter_timeline_start (priv->timeline); } static gboolean mx_expander_button_release (ClutterActor *actor, ClutterButtonEvent *event) { MxExpander *expander = MX_EXPANDER (actor); mx_expander_set_expanded (expander, !expander->priv->expanded); return FALSE; } static void mx_expander_get_preferred_width (ClutterActor *actor, gfloat for_height, gfloat *min_width, gfloat *pref_width) { MxExpanderPrivate *priv = MX_EXPANDER (actor)->priv; ClutterActor *child; MxPadding padding; gfloat min_child_w, pref_child_w, min_label_w, pref_label_w, arrow_w; child = mx_bin_get_child (MX_BIN (actor)); mx_widget_get_padding (MX_WIDGET (actor), &padding); if (child) { clutter_actor_get_preferred_width (child, -1, &min_child_w, &pref_child_w); } else { min_child_w = 0; pref_child_w = 0; } clutter_actor_get_preferred_width (priv->label, -1, &min_label_w, &pref_label_w); clutter_actor_get_preferred_width (priv->arrow, -1, NULL, &arrow_w); /* TODO: create a style property for this padding between arrow and label */ if (arrow_w) arrow_w += 6.0f; if (min_width) *min_width = padding.left + MAX (min_child_w, min_label_w + arrow_w) + padding.right; if (pref_width) *pref_width = padding.left + MAX (pref_child_w, pref_label_w + arrow_w) + padding.right; } static void mx_expander_get_preferred_height (ClutterActor *actor, gfloat for_width, gfloat *min_height, gfloat *pref_height) { MxExpanderPrivate *priv = MX_EXPANDER (actor)->priv; ClutterActor *child; MxPadding padding; gfloat min_child_h, pref_child_h, min_label_h, pref_label_h, arrow_h; gfloat available_w; child = mx_bin_get_child (MX_BIN (actor)); mx_widget_get_padding (MX_WIDGET (actor), &padding); available_w = for_width - padding.left - padding.right; if (child) { clutter_actor_get_preferred_height (child, available_w, &min_child_h, &pref_child_h); min_child_h += priv->spacing; pref_child_h += priv->spacing; /* allocate the space multiplied by the progress of the "expansion" * animation */ min_child_h *= priv->progress; pref_child_h *= priv->progress; } else { min_child_h = 0; pref_child_h = 0; } clutter_actor_get_preferred_height (priv->label, available_w, &min_label_h, &pref_label_h); clutter_actor_get_preferred_height (priv->arrow, -1, NULL, &arrow_h); min_label_h = MAX (min_label_h, arrow_h); pref_label_h = MAX (pref_label_h, arrow_h); if (min_height) *min_height = padding.top + min_child_h + min_label_h + padding.bottom; if (pref_height) *pref_height = padding.top + pref_child_h + pref_label_h + padding.bottom; } static void mx_expander_allocate (ClutterActor *actor, const ClutterActorBox *box, ClutterAllocationFlags flags) { ClutterActor *child; MxExpanderPrivate *priv = MX_EXPANDER (actor)->priv; ClutterActorBox child_box; MxPadding padding; gfloat label_w, label_h; gfloat available_w, available_h, min_w, min_h, arrow_h, arrow_w; MxAlign x_align, y_align; gboolean x_fill, y_fill; /* chain up to store allocation */ CLUTTER_ACTOR_CLASS (mx_expander_parent_class)->allocate (actor, box, flags); mx_widget_get_padding (MX_WIDGET (actor), &padding); g_object_get (G_OBJECT (actor), "x-align", &x_align, "y-align", &y_align, "x-fill", &x_fill, "y-fill", &y_fill, NULL); available_w = (box->x2 - box->x1) - padding.left - padding.right; available_h = (box->y2 - box->y1) - padding.top - padding.bottom; /* arrow */ clutter_actor_get_preferred_width (priv->arrow, -1, NULL, &arrow_w); arrow_w = MIN (arrow_w, available_w); clutter_actor_get_preferred_height (priv->arrow, -1, NULL, &arrow_h); arrow_h = MIN (arrow_h, available_h); child_box.x1 = padding.left; child_box.x2 = child_box.x1 + arrow_w; child_box.y1 = padding.top; child_box.y2 = child_box.y1 + arrow_h; clutter_actor_allocate (priv->arrow, &child_box, flags); /* label */ min_h = 0; min_w = 0; clutter_actor_get_preferred_width (priv->label, available_h, &min_w, &label_w); label_w = CLAMP (label_w, min_w, available_w); clutter_actor_get_preferred_height (priv->label, label_w, &min_h, &label_h); label_h = CLAMP (label_h, min_h, available_h); /* TODO: make a style property for padding between arrow and label */ child_box.x1 = padding.left + arrow_w + 6.0f; child_box.x2 = child_box.x1 + label_w; child_box.y1 = padding.top; child_box.y2 = child_box.y1 + MAX (label_h, arrow_h); mx_allocate_align_fill (priv->label, &child_box, MX_ALIGN_START, MX_ALIGN_MIDDLE, FALSE, FALSE); clutter_actor_allocate (priv->label, &child_box, flags); /* remove label height and spacing for child calculations */ available_h -= MAX (label_h, arrow_h) + priv->spacing; /* child */ child = mx_bin_get_child (MX_BIN (actor)); if (child && CLUTTER_ACTOR_IS_VISIBLE (child)) { child_box.x1 = padding.left; child_box.x2 = child_box.x1 + available_w; child_box.y1 = padding.top + priv->spacing + MAX (label_h, arrow_h); child_box.y2 = child_box.y1 + available_h; mx_allocate_align_fill (child, &child_box, x_align, y_align, x_fill, y_fill); clutter_actor_allocate (child, &child_box, flags); } } static void mx_expander_paint (ClutterActor *actor) { CLUTTER_ACTOR_CLASS (mx_expander_parent_class)->paint (actor); clutter_actor_paint (((MxExpander* ) actor)->priv->label); clutter_actor_paint (((MxExpander* ) actor)->priv->arrow); } static void mx_expander_map (ClutterActor *actor) { MxExpanderPrivate *priv = MX_EXPANDER (actor)->priv; ClutterActorClass *parent_parent_class = g_type_class_peek_parent (mx_expander_parent_class); CLUTTER_ACTOR_CLASS (parent_parent_class)->map (actor); clutter_actor_map (priv->label); clutter_actor_map (priv->arrow); } static void mx_expander_unmap (ClutterActor *actor) { MxExpanderPrivate *priv = MX_EXPANDER (actor)->priv; CLUTTER_ACTOR_CLASS (mx_expander_parent_class)->unmap (actor); clutter_actor_unmap (priv->label); clutter_actor_unmap (priv->arrow); } static void mx_expander_apply_style (MxWidget *widget, MxStyle *style) { MxExpanderPrivate *priv = MX_EXPANDER (widget)->priv; if (priv->arrow != NULL) mx_stylable_set_style (MX_STYLABLE (priv->arrow), style); } static void mx_expander_style_changed (MxStylable *stylable) { MxExpander *expander = MX_EXPANDER (stylable); MxExpanderPrivate *priv = expander->priv; const gchar *pseudo_class; pseudo_class = mx_stylable_get_style_pseudo_class (stylable); mx_stylable_set_style_pseudo_class (MX_STYLABLE (expander->priv->arrow), pseudo_class); mx_stylable_apply_clutter_text_attributes (stylable, CLUTTER_TEXT (priv->label)); } static void mx_expander_foreach (ClutterContainer *container, ClutterCallback callback, gpointer user_data) { ClutterActor *child; child = mx_bin_get_child (MX_BIN (container)); if (child) callback (child, user_data); } static void mx_expander_add (ClutterContainer *container, ClutterActor *actor) { MxExpander *expander = MX_EXPANDER (container); MxExpanderPrivate *priv = expander->priv; /* Override the container add method so we can hide the actor if the expander is not expanded */ /* chain up */ container_parent_class->add (container, actor); if (!priv->expanded) { actor = mx_bin_get_child (MX_BIN (container)); if (actor) clutter_actor_hide (actor); } } static void clutter_container_iface_init (ClutterContainerIface *iface) { container_parent_class = g_type_interface_peek_parent (iface); iface->foreach = mx_expander_foreach; iface->add = mx_expander_add; } static void mx_expander_class_init (MxExpanderClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass); MxWidgetClass *widget_class = MX_WIDGET_CLASS (klass); GParamSpec *pspec; g_type_class_add_private (klass, sizeof (MxExpanderPrivate)); object_class->get_property = mx_expander_get_property; object_class->set_property = mx_expander_set_property; object_class->dispose = mx_expander_dispose; object_class->finalize = mx_expander_finalize; actor_class->button_release_event = mx_expander_button_release; actor_class->allocate = mx_expander_allocate; actor_class->get_preferred_width = mx_expander_get_preferred_width; actor_class->get_preferred_height = mx_expander_get_preferred_height; actor_class->paint = mx_expander_paint; actor_class->map = mx_expander_map; actor_class->unmap = mx_expander_unmap; widget_class->apply_style = mx_expander_apply_style; pspec = g_param_spec_boolean ("expanded", "Expanded", "Indicates that the expander is open or closed", FALSE, MX_PARAM_READWRITE); g_object_class_install_property (object_class, PROP_EXPANDED, pspec); pspec = g_param_spec_string ("label", "Label", "Expander title label.", NULL, G_PARAM_READWRITE | MX_PARAM_TRANSLATEABLE); g_object_class_install_property (object_class, PROP_LABEL, pspec); /** * MxExpander::expand-complete: * @expander: the object that received the signal * * Emitted after the expand animation finishes. Check the "expanded" property * of the #MxExpander to determine if the expander is expanded or not. */ expander_signals[EXPAND_COMPLETE] = g_signal_new ("expand-complete", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (MxExpanderClass, expand_complete), NULL, NULL, _mx_marshal_VOID__VOID, G_TYPE_NONE, 0); } static void mx_expander_init (MxExpander *self) { MxExpanderPrivate *priv = self->priv = GET_PRIVATE (self); priv->label = clutter_text_new (); clutter_actor_set_parent (priv->label, (ClutterActor *) self); priv->arrow = (ClutterActor *) mx_icon_new (); clutter_actor_set_parent (priv->arrow, (ClutterActor *) self); clutter_actor_set_name (priv->arrow, "mx-expander-arrow-closed"); /* TODO: make this a style property */ priv->spacing = 10.0f; priv->timeline = clutter_timeline_new (250); g_signal_connect (priv->timeline, "new-frame", G_CALLBACK (new_frame), self); g_signal_connect (priv->timeline, "completed", G_CALLBACK (timeline_complete), self); priv->alpha = clutter_alpha_new_full (priv->timeline, CLUTTER_EASE_IN_SINE); g_object_ref_sink (priv->alpha); clutter_actor_set_reactive ((ClutterActor *) self, TRUE); g_signal_connect (self, "style-changed", G_CALLBACK (mx_expander_style_changed), NULL); } /** * mx_expander_new: * * Creates a new #MxExpander * * Returns: the newly allocated #MxExpander */ ClutterActor * mx_expander_new (void) { return g_object_new (MX_TYPE_EXPANDER, NULL); } /** * mx_expander_set_label: * @expander: A #MxExpander * @label: string to set as the expander label * * Sets the text displayed as the title of the expander */ void mx_expander_set_label (MxExpander *expander, const gchar *label) { g_return_if_fail (MX_IS_EXPANDER (expander)); g_return_if_fail (label != NULL); clutter_text_set_text (CLUTTER_TEXT (expander->priv->label), label); } /** * mx_expander_set_expanded: * @expander: A #MxExpander * @expanded: the state of the expander to set * * Set the state (the #MxExpander:expanded property) of the expander. * This will cause the expander to open or close. */ void mx_expander_set_expanded (MxExpander *expander, gboolean expanded) { g_return_if_fail (MX_IS_EXPANDER (expander)); if (expander->priv->expanded != expanded) { expander->priv->expanded = expanded; mx_expander_update (expander); g_object_notify (G_OBJECT (expander), "expanded"); } } /** * mx_expander_get_expanded: * @expander: a #MxExpander * * Get the current state of the expander (the value of #MxExpander:expanded) * * Returns: #TRUE if the expander is open, #FALSE if it is closed */ gboolean mx_expander_get_expanded (MxExpander *expander) { g_return_val_if_fail (MX_IS_EXPANDER (expander), FALSE); return expander->priv->expanded; } mx-1.4.7/mx/mx-expander.h000066400000000000000000000052111201047117600151720ustar00rootroot00000000000000/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * mx-expander.h: Expander Widget * * Copyright 2009 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * * Written by: Thomas Wood * */ #if !defined(MX_H_INSIDE) && !defined(MX_COMPILATION) #error "Only can be included directly.h" #endif #ifndef _MX_EXPANDER #define _MX_EXPANDER #include #include G_BEGIN_DECLS #define MX_TYPE_EXPANDER mx_expander_get_type() #define MX_EXPANDER(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), MX_TYPE_EXPANDER, MxExpander)) #define MX_EXPANDER_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), MX_TYPE_EXPANDER, MxExpanderClass)) #define MX_IS_EXPANDER(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MX_TYPE_EXPANDER)) #define MX_IS_EXPANDER_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE ((klass), MX_TYPE_EXPANDER)) #define MX_EXPANDER_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), MX_TYPE_EXPANDER, MxExpanderClass)) typedef struct _MxExpanderPrivate MxExpanderPrivate; /** * MxExpander: * * The contents of the this structure are private and should only be accessed * through the public API. */ typedef struct { /*< private >*/ MxBin parent; MxExpanderPrivate *priv; } MxExpander; typedef struct { MxBinClass parent_class; /* signals */ void (* expand_complete) (MxExpander *expander); /* padding for future expansion */ void (*_padding_0) (void); void (*_padding_1) (void); void (*_padding_2) (void); void (*_padding_3) (void); void (*_padding_4) (void); } MxExpanderClass; GType mx_expander_get_type (void); ClutterActor *mx_expander_new (void); void mx_expander_set_label (MxExpander *expander, const gchar *label); gboolean mx_expander_get_expanded (MxExpander *expander); void mx_expander_set_expanded (MxExpander *expander, gboolean expanded); G_END_DECLS #endif /* _MX_EXPANDER */ mx-1.4.7/mx/mx-fade-effect.c000066400000000000000000000725531201047117600155250ustar00rootroot00000000000000/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * mx-fade-effect.c: A border-fading offscreen effect * * Copyright 2011 Intel Corporation * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * * Written by: Chris Lord * */ /** * SECTION:mx-fade-effect * @short_description: An effect used to fade the borders of actors * * #MxFadeEffect is a #ClutterEffect that can be used to fade the borders * of a #ClutterActor. It provides a configurable bounding box, border * size and colour to control the fading effect. * * Since: 1.2 */ #include "mx-fade-effect.h" #include "mx-private.h" G_DEFINE_TYPE (MxFadeEffect, mx_fade_effect, CLUTTER_TYPE_OFFSCREEN_EFFECT) #define FADE_EFFECT_PRIVATE(o) \ (G_TYPE_INSTANCE_GET_PRIVATE ((o), MX_TYPE_FADE_EFFECT, MxFadeEffectPrivate)) enum { PROP_0, PROP_BOUNDS_X, PROP_BOUNDS_Y, PROP_BOUNDS_WIDTH, PROP_BOUNDS_HEIGHT, PROP_BORDER_TOP, PROP_BORDER_RIGHT, PROP_BORDER_BOTTOM, PROP_BORDER_LEFT, PROP_COLOR, PROP_FREEZE_UPDATE }; struct _MxFadeEffectPrivate { gint x; gint y; guint bounds_width; guint bounds_height; guint border[4]; ClutterColor color; gfloat width; gfloat height; CoglHandle vbo; CoglHandle indices; guint n_quads; CoglMaterial *old_material; gulong blocked_id; gfloat x_offset; gfloat y_offset; guint update_vbo : 1; guint freeze_update : 1; }; static void mx_fade_effect_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { MxFadeEffectPrivate *priv = MX_FADE_EFFECT (object)->priv; switch (property_id) { case PROP_BOUNDS_X: g_value_set_int (value, priv->x); break; case PROP_BOUNDS_Y: g_value_set_int (value, priv->y); break; case PROP_BOUNDS_WIDTH: g_value_set_uint (value, priv->bounds_width); break; case PROP_BOUNDS_HEIGHT: g_value_set_uint (value, priv->bounds_height); break; case PROP_BORDER_TOP: g_value_set_uint (value, priv->border[0]); break; case PROP_BORDER_RIGHT: g_value_set_uint (value, priv->border[1]); break; case PROP_BORDER_BOTTOM: g_value_set_uint (value, priv->border[2]); break; case PROP_BORDER_LEFT: g_value_set_uint (value, priv->border[3]); break; case PROP_COLOR: clutter_value_set_color (value, &priv->color); break; case PROP_FREEZE_UPDATE: g_value_set_boolean (value, priv->freeze_update); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } } static void mx_fade_effect_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { MxFadeEffect *effect = MX_FADE_EFFECT (object); MxFadeEffectPrivate *priv = effect->priv; switch (property_id) { case PROP_BOUNDS_X: priv->x = g_value_get_int (value); break; case PROP_BOUNDS_Y: priv->y = g_value_get_int (value); break; case PROP_BOUNDS_WIDTH: priv->bounds_width = g_value_get_uint (value); break; case PROP_BOUNDS_HEIGHT: priv->bounds_height = g_value_get_uint (value); break; case PROP_BORDER_TOP: priv->border[0] = g_value_get_uint (value); break; case PROP_BORDER_RIGHT: priv->border[1] = g_value_get_uint (value); break; case PROP_BORDER_BOTTOM: priv->border[2] = g_value_get_uint (value); break; case PROP_BORDER_LEFT: priv->border[3] = g_value_get_uint (value); break; case PROP_COLOR: priv->color = *(clutter_value_get_color (value)); break; case PROP_FREEZE_UPDATE: priv->freeze_update = g_value_get_boolean (value); return; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); return; } priv->update_vbo = TRUE; } static void mx_fade_effect_dispose (GObject *object) { MxFadeEffectPrivate *priv = MX_FADE_EFFECT (object)->priv; if (priv->vbo) { cogl_handle_unref (priv->vbo); priv->vbo = NULL; } if (priv->blocked_id) { ClutterActor *actor = clutter_actor_meta_get_actor (CLUTTER_ACTOR_META (object)); g_signal_handler_disconnect (actor, priv->blocked_id); priv->blocked_id = 0; } G_OBJECT_CLASS (mx_fade_effect_parent_class)->dispose (object); } static void mx_fade_effect_finalize (GObject *object) { G_OBJECT_CLASS (mx_fade_effect_parent_class)->finalize (object); } static CoglHandle mx_fade_effect_create_texture (ClutterOffscreenEffect *effect, gfloat width, gfloat height) { MxFadeEffectPrivate *priv = MX_FADE_EFFECT (effect)->priv; priv->width = width; priv->height = height; priv->update_vbo = TRUE; return CLUTTER_OFFSCREEN_EFFECT_CLASS (mx_fade_effect_parent_class)-> create_texture (effect, width, height); } static void mx_fade_effect_paint_cb (ClutterActor *actor, MxFadeEffect *self) { MxFadeEffectPrivate *priv = self->priv; g_signal_stop_emission (actor, g_signal_lookup ("paint", CLUTTER_TYPE_ACTOR), 0); g_signal_handler_disconnect (actor, priv->blocked_id); priv->blocked_id = 0; } static gboolean mx_fade_effect_pre_paint (ClutterEffect *effect) { MxFadeEffectPrivate *priv = MX_FADE_EFFECT (effect)->priv; if (!priv->freeze_update) { return CLUTTER_EFFECT_CLASS (mx_fade_effect_parent_class)-> pre_paint (effect); } else { ClutterActorBox box; ClutterActor *actor = clutter_actor_meta_get_actor (CLUTTER_ACTOR_META (effect)); /* Store the stage coordinates of the actor for when we post-paint */ clutter_actor_get_paint_box (actor, &box); clutter_actor_box_get_origin (&box, &priv->x_offset, &priv->y_offset); /* Connect to the paint signal so we can block it */ priv->blocked_id = g_signal_connect (actor, "paint", G_CALLBACK (mx_fade_effect_paint_cb), effect); return TRUE; } } static void mx_fade_effect_post_paint (ClutterEffect *effect) { MxFadeEffectPrivate *priv = MX_FADE_EFFECT (effect)->priv; if (!priv->freeze_update) CLUTTER_EFFECT_CLASS (mx_fade_effect_parent_class)->post_paint (effect); else { CoglMatrix modelview; ClutterActor *actor, *stage; actor = clutter_actor_meta_get_actor (CLUTTER_ACTOR_META (effect)); stage = clutter_actor_get_stage (actor); /* Set up the draw matrix so we draw the offscreen texture at the * absolute coordinates of the actor-box. We need to do this to * avoid transforming by the actor matrix twice, as when it's drawn * into the offscreen surface, it'll already be transformed. */ cogl_push_matrix (); cogl_matrix_init_identity (&modelview); CLUTTER_ACTOR_CLASS (G_OBJECT_GET_CLASS (stage))-> apply_transform (stage, &modelview); cogl_matrix_translate (&modelview, priv->x_offset, priv->y_offset, 0.f); cogl_set_modelview_matrix (&modelview); clutter_offscreen_effect_paint_target (CLUTTER_OFFSCREEN_EFFECT (effect)); cogl_pop_matrix (); } } static void mx_fade_effect_draw_rect (CoglTextureVertex *verts, gfloat x1, gfloat y1, gfloat x2, gfloat y2, gfloat width, gfloat height, CoglColor *color1, CoglColor *color2, CoglColor *color3, CoglColor *color4, gboolean clockwise) { gint i, a; gfloat tx1, ty1, tx2, ty2; /* The Cogl rectangle drawing functions don't allow you to alter * the material colour, so we use cogl_polygon and this convenience * function instead. */ tx1 = x1 / width; ty1 = y1 / height; tx2 = x2 / width; ty2 = y2 / height; if (clockwise) { i = 3; a = -1; } else { i = 0; a = 1; } verts[i].x = x1; verts[i].y = y1; verts[i].z = 0; verts[i].tx = tx1; verts[i].ty = ty1; verts[i].color = *color1; i += a; verts[i].x = x1; verts[i].y = y2; verts[i].z = 0; verts[i].tx = tx1; verts[i].ty = ty2; verts[i].color = *color4; i += a; verts[i].x = x2; verts[i].y = y2; verts[i].z = 0; verts[i].tx = tx2; verts[i].ty = ty2; verts[i].color = *color3; i += a; verts[i].x = x2; verts[i].y = y1; verts[i].z = 0; verts[i].tx = tx2; verts[i].ty = ty1; verts[i].color = *color2; } static void mx_fade_effect_update_vbo (MxFadeEffect *self) { guint n_quads; gint bu, br, bb, bl; gfloat x1, y1, x2, y2; CoglColor opaque, color; CoglTextureVertex verts[9*4]; MxFadeEffectPrivate *priv = self->priv; cogl_color_init_from_4ub (&opaque, 0xff, 0xff, 0xff, 0xff); cogl_color_init_from_4ub (&color, priv->color.red, priv->color.green, priv->color.blue, priv->color.alpha); /* Validate the bounds */ x1 = priv->x; y1 = priv->y; x2 = x1 + (priv->bounds_width ? priv->bounds_width : priv->width); y2 = y1 + (priv->bounds_height ? priv->bounds_height : priv->height); if (x1 < 0) x1 = 0; if (x2 > priv->width) x2 = priv->width; if (y1 < 0) y1 = 0; if (y2 > priv->height) y2 = priv->height; /* Validate the border sizes */ /* Note, * bu = Border-up * br = Border-right * bb = Border-bottom * bl = Border-left */ bu = priv->border[0]; br = priv->border[1]; bb = priv->border[2]; bl = priv->border[3]; if (y1 + bu >= y2) bu = y2 - y1 - 1; if (x1 + bl >= x2) bl = x2 - x1 - 1; if (x2 - br <= x1 + bl) br = x2 - (x1 + bl) - 1; if (y2 - bb <= y1 + bu) bb = y2 - (y1 + bu) - 1; n_quads = 0; /* Generate the top-left square */ if (bl && bu) { mx_fade_effect_draw_rect (&verts[n_quads*4], x1, y1, x1 + bl, y1 + bu, priv->width, priv->height, &color, &color, &opaque, &color, FALSE); n_quads ++; } /* Generate the top-middle square */ if (bu) { mx_fade_effect_draw_rect (&verts[n_quads*4], x1 + bl, y1, x2 - br, y1 + bu, priv->width, priv->height, &color, &color, &opaque, &opaque, FALSE); n_quads ++; } /* Generate the top-right square */ if (br && bu) { mx_fade_effect_draw_rect (&verts[n_quads*4], x2 - br, y1, x2, y1 + bu, priv->width, priv->height, &color, &color, &color, &opaque, TRUE); n_quads ++; } /* Generate the left square */ if (bl) { mx_fade_effect_draw_rect (&verts[n_quads*4], x1, y1 + bu, x1 + bl, y2 - bb, priv->width, priv->height, &color, &opaque, &opaque, &color, TRUE); n_quads ++; } /* Generate the middle square */ mx_fade_effect_draw_rect (&verts[n_quads*4], x1 + bl, y1 + bu, x2 - br, y2 - bb, priv->width, priv->height, &opaque, &opaque, &opaque, &opaque, TRUE); n_quads ++; /* Generate the right square */ if (br) { mx_fade_effect_draw_rect (&verts[n_quads*4], x2 - br, y1 + bu, x2, y2 - bb, priv->width, priv->height, &opaque, &color, &color, &opaque, TRUE); n_quads ++; } /* Generate the bottom-left square */ if (bb && bl) { mx_fade_effect_draw_rect (&verts[n_quads*4], x1, y2 - bb, x1 + bl, y2, priv->width, priv->height, &color, &opaque, &color, &color, TRUE); n_quads ++; } /* Generate the bottom-middle square */ if (bb) { mx_fade_effect_draw_rect (&verts[n_quads*4], x1 + bl, y2 - bb, x2 - br, y2, priv->width, priv->height, &opaque, &opaque, &color, &color, FALSE); n_quads ++; } /* Generate the bottom-right square */ if (bb && br) { mx_fade_effect_draw_rect (&verts[n_quads*4], x2 - br, y2 - bb, x2, y2, priv->width, priv->height, &opaque, &color, &color, &color, FALSE); n_quads ++; } /* Unref the old vbo if it's a different size - otherwise we reuse it */ if (priv->vbo && (n_quads != priv->n_quads)) { cogl_handle_unref (priv->vbo); priv->vbo = NULL; } priv->n_quads = n_quads; if (!priv->vbo) { priv->vbo = cogl_vertex_buffer_new (n_quads * 4); if (!priv->vbo) return; priv->indices = cogl_vertex_buffer_indices_get_for_quads (n_quads * 6); if (!priv->indices) return; } cogl_vertex_buffer_add (priv->vbo, "gl_Vertex", 2, COGL_ATTRIBUTE_TYPE_FLOAT, FALSE, sizeof (CoglTextureVertex), &(verts[0].x)); cogl_vertex_buffer_add (priv->vbo, "gl_MultiTexCoord0", 2, COGL_ATTRIBUTE_TYPE_FLOAT, FALSE, sizeof (CoglTextureVertex), &(verts[0].tx)); cogl_vertex_buffer_add (priv->vbo, "gl_Color", 4, COGL_ATTRIBUTE_TYPE_UNSIGNED_BYTE, FALSE, sizeof (CoglTextureVertex), &(verts[0].color)); cogl_vertex_buffer_submit (priv->vbo); priv->update_vbo = FALSE; } static void mx_fade_effect_paint_target (ClutterOffscreenEffect *effect) { guint8 opacity; CoglColor color; ClutterActor *actor; CoglMaterial *material = clutter_offscreen_effect_get_target (effect); MxFadeEffect *self = MX_FADE_EFFECT (effect); MxFadeEffectPrivate *priv = self->priv; if (priv->update_vbo) mx_fade_effect_update_vbo (self); if (!priv->vbo || !priv->indices || !material) return; /* Set the blend string if the material has changed so we can blend with * the paint opacity. */ if (material != priv->old_material) { GError *error = NULL; priv->old_material = material; if (!cogl_material_set_layer_combine (material, 1, "RGBA = MODULATE(PREVIOUS,CONSTANT)", &error)) { g_warning (G_STRLOC ": Error setting layer combine blend string: %s", error->message); g_error_free (error); } } /* Set the layer-combine constant so the texture is blended with the paint * opacity when painted. */ actor = clutter_actor_meta_get_actor (CLUTTER_ACTOR_META (effect)); opacity = clutter_actor_get_paint_opacity (actor); cogl_color_init_from_4ub (&color, opacity, opacity, opacity, opacity); cogl_material_set_layer_combine_constant (material, 1, &color); /* Draw the texture */ cogl_set_source (material); cogl_vertex_buffer_draw_elements (priv->vbo, COGL_VERTICES_MODE_TRIANGLES, priv->indices, 0, (priv->n_quads * 4) - 1, 0, priv->n_quads * 6); } static void mx_fade_effect_class_init (MxFadeEffectClass *klass) { GParamSpec *pspec; ClutterColor transparent = { 0, }; GObjectClass *object_class = G_OBJECT_CLASS (klass); ClutterEffectClass *effect_class = CLUTTER_EFFECT_CLASS (klass); ClutterOffscreenEffectClass *offscreen_class = CLUTTER_OFFSCREEN_EFFECT_CLASS (klass); g_type_class_add_private (klass, sizeof (MxFadeEffectPrivate)); object_class->get_property = mx_fade_effect_get_property; object_class->set_property = mx_fade_effect_set_property; object_class->dispose = mx_fade_effect_dispose; object_class->finalize = mx_fade_effect_finalize; effect_class->pre_paint = mx_fade_effect_pre_paint; effect_class->post_paint = mx_fade_effect_post_paint; offscreen_class->create_texture = mx_fade_effect_create_texture; offscreen_class->paint_target = mx_fade_effect_paint_target; pspec = g_param_spec_int ("bounds-x", "Bounds X", "X-coordinate of the texture bounding box", -G_MAXINT, G_MAXINT, 0, MX_PARAM_READWRITE | MX_PARAM_TRANSLATEABLE); g_object_class_install_property (object_class, PROP_BOUNDS_X, pspec); pspec = g_param_spec_int ("bounds-y", "Bounds Y", "Y-coordinate of the texture bounding boy", -G_MAXINT, G_MAXINT, 0, MX_PARAM_READWRITE | MX_PARAM_TRANSLATEABLE); g_object_class_install_property (object_class, PROP_BOUNDS_Y, pspec); pspec = g_param_spec_uint ("bounds-width", "Bounds width", "Width of the texture bounding box", 0, G_MAXUINT, 0, MX_PARAM_READWRITE | MX_PARAM_TRANSLATEABLE); g_object_class_install_property (object_class, PROP_BOUNDS_WIDTH, pspec); pspec = g_param_spec_uint ("bounds-height", "Bounds height", "Height of the texture bounding box", 0, G_MAXUINT, 0, MX_PARAM_READWRITE | MX_PARAM_TRANSLATEABLE); g_object_class_install_property (object_class, PROP_BOUNDS_HEIGHT, pspec); pspec = g_param_spec_uint ("border-top", "Border top", "Border at the top of the effect", 0, G_MAXUINT, 0, MX_PARAM_READWRITE | MX_PARAM_TRANSLATEABLE); g_object_class_install_property (object_class, PROP_BORDER_TOP, pspec); pspec = g_param_spec_uint ("border-right", "Border right", "Border at the right of the effect", 0, G_MAXUINT, 0, MX_PARAM_READWRITE | MX_PARAM_TRANSLATEABLE); g_object_class_install_property (object_class, PROP_BORDER_RIGHT, pspec); pspec = g_param_spec_uint ("border-bottom", "Border bottom", "Border at the bottom of the effect", 0, G_MAXUINT, 0, MX_PARAM_READWRITE | MX_PARAM_TRANSLATEABLE); g_object_class_install_property (object_class, PROP_BORDER_BOTTOM, pspec); pspec = g_param_spec_uint ("border-left", "Border left", "Border at the left of the effect", 0, G_MAXUINT, 0, MX_PARAM_READWRITE | MX_PARAM_TRANSLATEABLE); g_object_class_install_property (object_class, PROP_BORDER_LEFT, pspec); pspec = clutter_param_spec_color ("color", "Color", "Color of the faded border", &transparent, MX_PARAM_READWRITE | MX_PARAM_TRANSLATEABLE); g_object_class_install_property (object_class, PROP_COLOR, pspec); pspec = g_param_spec_boolean ("freeze-update", "Freeze update", "Stop updating the offscreen buffer", FALSE, MX_PARAM_READWRITE | MX_PARAM_TRANSLATEABLE); g_object_class_install_property (object_class, PROP_FREEZE_UPDATE, pspec); } static void mx_fade_effect_init (MxFadeEffect *self) { self->priv = FADE_EFFECT_PRIVATE (self); } /** * mx_fade_effect_new: * * Creates a new #MxFadeEffect to be used with clutter_actor_add_effect(). * * Returns: the newly created #MxFadeEffect, or %NULL * * Since: 1.2 */ ClutterEffect * mx_fade_effect_new (void) { return g_object_new (MX_TYPE_FADE_EFFECT, NULL); } /** * mx_fade_effect_set_border: * @effect: A #MxFadeEffect * @top: The upper border, in pixels * @right: The right border, in pixels * @bottom: The lower border, in pixels * @left: The left border, in pixels * * Sets the border to be used for the fading effect. This is the number of * pixels on each side of the effect that should be used to fade. * * Since: 1.2 */ void mx_fade_effect_set_border (MxFadeEffect *effect, guint top, guint right, guint bottom, guint left) { MxFadeEffectPrivate *priv; g_return_if_fail (MX_IS_FADE_EFFECT (effect)); priv = effect->priv; g_object_freeze_notify (G_OBJECT (effect)); if (priv->border[0] != top) { priv->border[0] = top; g_object_notify (G_OBJECT (effect), "border-top"); } if (priv->border[1] != right) { priv->border[1] = right; g_object_notify (G_OBJECT (effect), "border-right"); } if (priv->border[2] != bottom) { priv->border[2] = bottom; g_object_notify (G_OBJECT (effect), "border-bottom"); } if (priv->border[3] != left) { priv->border[3] = left; g_object_notify (G_OBJECT (effect), "border-left"); } priv->update_vbo = TRUE; g_object_thaw_notify (G_OBJECT (effect)); } /** * mx_fade_effect_get_border: * @effect: A #MxFadeEffect * @top: (out): The upper border, in pixels * @right: (out): The right border, in pixels * @bottom: (out): The lower border, in pixels * @left: (out): The left border, in pixels * * Retrieves the border values for @effect. * * Since: 1.2 */ void mx_fade_effect_get_border (MxFadeEffect *effect, guint *top, guint *right, guint *bottom, guint *left) { MxFadeEffectPrivate *priv; g_return_if_fail (MX_IS_FADE_EFFECT (effect)); priv = effect->priv; if (top) *top = priv->border[0]; if (right) *right = priv->border[1]; if (bottom) *bottom = priv->border[2]; if (left) *left = priv->border[3]; } /** * mx_fade_effect_set_color: * @effect: A #MxFadeEffect * @color: A #ClutterColor * * Sets the color of the fade effect. The effect will fade out towards * the set border to this color. * * Since: 1.2 */ void mx_fade_effect_set_color (MxFadeEffect *effect, const ClutterColor *color) { MxFadeEffectPrivate *priv; g_return_if_fail (MX_IS_FADE_EFFECT (effect)); priv = effect->priv; if (!clutter_color_equal (&priv->color, color)) { priv->color = *color; priv->update_vbo = TRUE; g_object_notify (G_OBJECT (effect), "color"); } } /** * mx_fade_effect_get_color: * @effect: A #MxFadeEffect * @color: (out): A #ClutterColor to store the color in * * Retrieves the color used for the fade effect. * * Since: 1.2 */ void mx_fade_effect_get_color (MxFadeEffect *effect, ClutterColor *color) { MxFadeEffectPrivate *priv; g_return_if_fail (MX_IS_FADE_EFFECT (effect)); priv = effect->priv; if (color) *color = priv->color; } /** * mx_fade_effect_set_bounds: * @effect: A #MxFadeEffect * @x: The x value of the effect bounds, in pixels * @y: The y value of the effect bounds, in pixels * @width: The width of the effect bounds, in pixels, or %0 * @height: The height of the effect bounds, in pixels, or %0 * * Sets the bounding box of the effect. The effect will essentially treat * this box as a clipping rectangle. Setting width or height to %0 will * use the width or height of the #ClutterActor the effect is attached to. * * * The effect border will apply to the bounds, and not to the un-altered * rectangle, so an effect with an %x of %5 and a %left-border of %5 will * have a gap of 5 blank pixels to the left, with a fade length of 5 pixels. * * * Since: 1.2 */ void mx_fade_effect_set_bounds (MxFadeEffect *effect, gint x, gint y, guint width, guint height) { MxFadeEffectPrivate *priv; g_return_if_fail (MX_IS_FADE_EFFECT (effect)); priv = effect->priv; g_object_freeze_notify (G_OBJECT (effect)); if (priv->x != x) { priv->x = x; g_object_notify (G_OBJECT (effect), "bounds-x"); } if (priv->y != y) { priv->y = y; g_object_notify (G_OBJECT (effect), "bounds-y"); } if (priv->bounds_width != width) { priv->bounds_width = width; g_object_notify (G_OBJECT (effect), "bounds-width"); } if (priv->bounds_height != height) { priv->bounds_height = height; g_object_notify (G_OBJECT (effect), "bounds-height"); } priv->update_vbo = TRUE; g_object_thaw_notify (G_OBJECT (effect)); } /** * mx_fade_effect_get_bounds: * @effect: A #MxFadeEffect * @x: (out): The x value of the effect bounds, in pixels * @y: (out): The y value of the effect bounds, in pixels * @width: (out): The width of the effect bounds, in pixels, or %0 * @height: (out): The height of the effect bounds, in pixels, or %0 * * Retrieves the bounding box of the effect. * * Since: 1.2 */ void mx_fade_effect_get_bounds (MxFadeEffect *effect, gint *x, gint *y, guint *width, guint *height) { MxFadeEffectPrivate *priv; g_return_if_fail (MX_IS_FADE_EFFECT (effect)); priv = effect->priv; if (x) *x = priv->x; if (y) *y = priv->y; if (width) *width = priv->bounds_width; if (height) *height = priv->bounds_height; } /* * mx_fade_effect_set_freeze_update: * @effect: A #MxFadeEffect * @freeze: %TRUE to freeze updates, %FALSE to unfreeze them * * This will freeze the current image. Any further updates to the * #ClutterActor the effect is applied to will not be reflected until the * effect is unfrozen. * * * Note, that this may conflict with other effects, and in such a situation, * should not be used. * * * Since: 1.2 */ void _mx_fade_effect_set_freeze_update (MxFadeEffect *effect, gboolean freeze) { MxFadeEffectPrivate *priv; g_return_if_fail (MX_IS_FADE_EFFECT (effect)); priv = effect->priv; if (priv->freeze_update != freeze) { priv->freeze_update = freeze; g_object_notify (G_OBJECT (effect), "freeze-update"); } } /* * mx_fade_effect_get_freeze_update: * @effect: A #MxFadeEffect * * Determines if the #MxFadeEffect has had its updates frozen. * * Returns: %TRUE if updates are frozen, %FALSE otherwise * * Since: 1.2 */ gboolean _mx_fade_effect_get_freeze_update (MxFadeEffect *effect) { g_return_val_if_fail (MX_IS_FADE_EFFECT (effect), FALSE); return effect->priv->freeze_update; } mx-1.4.7/mx/mx-fade-effect.h000066400000000000000000000065011201047117600155200ustar00rootroot00000000000000/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * mx-fade-effect.h: A border-fading offscreen effect * * Copyright 2011 Intel Corporation * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * * Written by: Chris Lord * */ #ifndef _MX_FADE_EFFECT_H #define _MX_FADE_EFFECT_H #include #include G_BEGIN_DECLS #define MX_TYPE_FADE_EFFECT mx_fade_effect_get_type() #define MX_FADE_EFFECT(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ MX_TYPE_FADE_EFFECT, MxFadeEffect)) #define MX_FADE_EFFECT_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), \ MX_TYPE_FADE_EFFECT, MxFadeEffectClass)) #define MX_IS_FADE_EFFECT(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \ MX_TYPE_FADE_EFFECT)) #define MX_IS_FADE_EFFECT_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE ((klass), \ MX_TYPE_FADE_EFFECT)) #define MX_FADE_EFFECT_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), \ MX_TYPE_FADE_EFFECT, MxFadeEffectClass)) typedef struct _MxFadeEffect MxFadeEffect; typedef struct _MxFadeEffectClass MxFadeEffectClass; typedef struct _MxFadeEffectPrivate MxFadeEffectPrivate; struct _MxFadeEffect { ClutterOffscreenEffect parent; MxFadeEffectPrivate *priv; }; struct _MxFadeEffectClass { ClutterOffscreenEffectClass parent_class; }; GType mx_fade_effect_get_type (void) G_GNUC_CONST; ClutterEffect *mx_fade_effect_new (void); void mx_fade_effect_set_border (MxFadeEffect *effect, guint top, guint right, guint bottom, guint left); void mx_fade_effect_get_border (MxFadeEffect *effect, guint *top, guint *right, guint *bottom, guint *left); void mx_fade_effect_set_bounds (MxFadeEffect *effect, gint x, gint y, guint width, guint height); void mx_fade_effect_get_bounds (MxFadeEffect *effect, gint *x, gint *y, guint *width, guint *height); void mx_fade_effect_set_color (MxFadeEffect *effect, const ClutterColor *color); void mx_fade_effect_get_color (MxFadeEffect *effect, ClutterColor *color); G_END_DECLS #endif /* _MX_FADE_EFFECT_H */ mx-1.4.7/mx/mx-floating-widget.c000066400000000000000000000165301201047117600164510ustar00rootroot00000000000000/* * mx-floating-widget: always-on-top base actor * * Copyright 2009 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * * Written by: Thomas Wood * */ #include "mx-floating-widget.h" G_DEFINE_ABSTRACT_TYPE (MxFloatingWidget, mx_floating_widget, MX_TYPE_WIDGET) #define FLOATING_WIDGET_PRIVATE(o) \ (G_TYPE_INSTANCE_GET_PRIVATE ((o), MX_TYPE_FLOATING_WIDGET, MxFloatingWidgetPrivate)) struct _MxFloatingWidgetPrivate { ClutterActor *stage; CoglMatrix paint_matrix; CoglMatrix pick_matrix; gulong pick_handler; gulong paint_handler; }; static void mx_floating_widget_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { switch (property_id) { default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } } static void mx_floating_widget_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { switch (property_id) { default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } } static void mx_floating_widget_dispose (GObject *object) { G_OBJECT_CLASS (mx_floating_widget_parent_class)->dispose (object); } static void mx_floating_widget_finalize (GObject *object) { G_OBJECT_CLASS (mx_floating_widget_parent_class)->finalize (object); } static void stage_weak_notify (MxFloatingWidget *widget, ClutterStage *stage) { widget->priv->stage = NULL; } static void mx_floating_widget_pick_from_stage (ClutterActor *stage, const ClutterColor *color, MxFloatingWidget *widget) { MxFloatingWidgetClass *klass; MxFloatingWidgetPrivate *priv = widget->priv; gboolean has_clip; if (!CLUTTER_ACTOR_IS_REACTIVE (widget)) return; klass = MX_FLOATING_WIDGET_CLASS (G_OBJECT_GET_CLASS (widget)); cogl_push_matrix (); cogl_set_modelview_matrix (&(priv->pick_matrix)); has_clip = clutter_actor_has_clip (CLUTTER_ACTOR (widget)); if (has_clip) { gfloat x, y, w, h; clutter_actor_get_clip (CLUTTER_ACTOR (widget), &x, &y, &w, &h); cogl_clip_push_rectangle (x, y, x + w, y + h); } if (klass->floating_pick) klass->floating_pick (CLUTTER_ACTOR (widget), color); if (has_clip) cogl_clip_pop (); cogl_pop_matrix (); } static void mx_floating_widget_paint_from_stage (ClutterActor *stage, MxFloatingWidget *widget) { MxFloatingWidgetClass *klass; MxFloatingWidgetPrivate *priv = widget->priv; gboolean has_clip; klass = MX_FLOATING_WIDGET_CLASS (G_OBJECT_GET_CLASS (widget)); cogl_push_matrix (); cogl_set_modelview_matrix (&(priv->paint_matrix)); has_clip = clutter_actor_has_clip (CLUTTER_ACTOR (widget)); if (has_clip) { gfloat x, y, w, h; clutter_actor_get_clip (CLUTTER_ACTOR (widget), &x, &y, &w, &h); cogl_clip_push_rectangle (x, y, x + w, y + h); } if (klass->floating_paint) klass->floating_paint (CLUTTER_ACTOR (widget)); if (has_clip) cogl_clip_pop (); cogl_pop_matrix (); } static void mx_floating_widget_map (ClutterActor *actor) { MxFloatingWidgetPrivate *priv = MX_FLOATING_WIDGET (actor)->priv; CLUTTER_ACTOR_CLASS (mx_floating_widget_parent_class)->map (actor); /* connect after the paint and pick signals on the stage have run, so that * we can be sure to be painted and picked above all other actors else */ priv->stage = clutter_actor_get_stage (actor); g_object_weak_ref (G_OBJECT (priv->stage), (GWeakNotify) stage_weak_notify, actor); priv->paint_handler = g_signal_connect_after (priv->stage, "paint", G_CALLBACK (mx_floating_widget_paint_from_stage), actor); priv->pick_handler = g_signal_connect_after (priv->stage, "pick", G_CALLBACK (mx_floating_widget_pick_from_stage), actor); } static void mx_floating_widget_unmap (ClutterActor *actor) { MxFloatingWidgetPrivate *priv = MX_FLOATING_WIDGET (actor)->priv; CLUTTER_ACTOR_CLASS (mx_floating_widget_parent_class)->unmap (actor); if (priv->stage) { g_signal_handler_disconnect (priv->stage, priv->paint_handler); priv->paint_handler = 0; g_signal_handler_disconnect (priv->stage, priv->pick_handler); priv->pick_handler = 0; g_object_weak_unref (G_OBJECT (priv->stage), (GWeakNotify) stage_weak_notify, actor); priv->stage = NULL; } } static void mx_floating_widget_paint_null (ClutterActor *actor) { /* store the matrix for later * the widget is painted after the stage has painted, so that it appears above * everything else */ cogl_get_modelview_matrix (&(MX_FLOATING_WIDGET (actor)->priv->paint_matrix)); } static void mx_floating_widget_pick_null (ClutterActor *actor, const ClutterColor *color) { /* store the matrix for later * the widget is picked after the stage has picked, so that it appears above * everything else */ cogl_get_modelview_matrix (&(MX_FLOATING_WIDGET (actor)->priv->pick_matrix)); } static void mx_floating_widget_floating_pick (ClutterActor *widget, const ClutterColor *color) { /* default implementation is to chain up to the original pick */ CLUTTER_ACTOR_CLASS (mx_floating_widget_parent_class)->pick (widget, color); } static void mx_floating_widget_floating_paint (ClutterActor *widget) { /* default implementation is to chain up to the original paint */ CLUTTER_ACTOR_CLASS (mx_floating_widget_parent_class)->paint (widget); } static void mx_floating_widget_class_init (MxFloatingWidgetClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass); MxFloatingWidgetClass *float_class = MX_FLOATING_WIDGET_CLASS (klass); g_type_class_add_private (klass, sizeof (MxFloatingWidgetPrivate)); object_class->get_property = mx_floating_widget_get_property; object_class->set_property = mx_floating_widget_set_property; object_class->dispose = mx_floating_widget_dispose; object_class->finalize = mx_floating_widget_finalize; actor_class->map = mx_floating_widget_map; actor_class->unmap = mx_floating_widget_unmap; actor_class->pick = mx_floating_widget_pick_null; actor_class->paint = mx_floating_widget_paint_null; float_class->floating_pick = mx_floating_widget_floating_pick; float_class->floating_paint = mx_floating_widget_floating_paint; } static void mx_floating_widget_init (MxFloatingWidget *self) { self->priv = FLOATING_WIDGET_PRIVATE (self); } mx-1.4.7/mx/mx-floating-widget.h000066400000000000000000000050441201047117600164540ustar00rootroot00000000000000/* * mx-floating-widget: always-on-top base actor * * Copyright 2009 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * * Written by: Thomas Wood * */ #if !defined(MX_H_INSIDE) && !defined(MX_COMPILATION) #error "Only can be included directly.h" #endif #ifndef _MX_FLOATING_WIDGET_H #define _MX_FLOATING_WIDGET_H #include "mx-widget.h" G_BEGIN_DECLS #define MX_TYPE_FLOATING_WIDGET mx_floating_widget_get_type() #define MX_FLOATING_WIDGET(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ MX_TYPE_FLOATING_WIDGET, MxFloatingWidget)) #define MX_FLOATING_WIDGET_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), \ MX_TYPE_FLOATING_WIDGET, MxFloatingWidgetClass)) #define MX_IS_FLOATING_WIDGET(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \ MX_TYPE_FLOATING_WIDGET)) #define MX_IS_FLOATING_WIDGET_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE ((klass), \ MX_TYPE_FLOATING_WIDGET)) #define MX_FLOATING_WIDGET_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), \ MX_TYPE_FLOATING_WIDGET, MxFloatingWidgetClass)) typedef struct _MxFloatingWidget MxFloatingWidget; typedef struct _MxFloatingWidgetClass MxFloatingWidgetClass; typedef struct _MxFloatingWidgetPrivate MxFloatingWidgetPrivate; /** * MxFloatingWidget: * * The contents of this structure are private and should only be accessed * through the public API. */ struct _MxFloatingWidget { MxWidget parent; MxFloatingWidgetPrivate *priv; }; struct _MxFloatingWidgetClass { MxWidgetClass parent_class; void (*floating_pick) (ClutterActor *actor, const ClutterColor *color); void (*floating_paint) (ClutterActor *actor); /* padding for future expansion */ void (*_padding_0) (void); void (*_padding_1) (void); void (*_padding_2) (void); void (*_padding_3) (void); void (*_padding_4) (void); }; GType mx_floating_widget_get_type (void) G_GNUC_CONST; G_END_DECLS #endif /* _MX_FLOATING_WIDGET_H */ mx-1.4.7/mx/mx-focus-manager.c000066400000000000000000000415351201047117600161170ustar00rootroot00000000000000/* * mx-focus-manager: Keyboard focus manager object * * Copyright 2010 Intel Corporation * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * * Written by: Thomas Wood * */ #include "mx-focus-manager.h" #include "mx-focusable.h" #include "mx-private.h" #include G_DEFINE_TYPE (MxFocusManager, mx_focus_manager, G_TYPE_OBJECT) #define FOCUS_MANAGER_PRIVATE(o) \ (G_TYPE_INSTANCE_GET_PRIVATE ((o), MX_TYPE_FOCUS_MANAGER, MxFocusManagerPrivate)) static GQuark focus_manager_quark = 0; struct _MxFocusManagerPrivate { ClutterActor *stage; MxFocusable *focused; MxFocusable *focused_toplevel; guint refocus_idle; }; enum { PROP_STAGE = 1, PROP_FOCUSED }; static void mx_focus_manager_set_focused (MxFocusManager *manager, MxFocusable *focusable); static void mx_focus_manager_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { MxFocusManagerPrivate *priv = MX_FOCUS_MANAGER (object)->priv; switch (property_id) { case PROP_STAGE: g_value_set_object (value, priv->stage); break; case PROP_FOCUSED: g_value_set_object (value, priv->focused); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } } static void mx_focus_manager_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { switch (property_id) { default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } } static void mx_focus_manager_dispose (GObject *object) { MxFocusManager *self = MX_FOCUS_MANAGER (object); MxFocusManagerPrivate *priv = self->priv; if (priv->refocus_idle) { g_source_remove (priv->refocus_idle); priv->refocus_idle = 0; } if (priv->stage) { g_object_set_qdata (G_OBJECT (priv->stage), focus_manager_quark, NULL); priv->stage = NULL; } if (priv->focused || priv->focused_toplevel) mx_focus_manager_set_focused (self, NULL); G_OBJECT_CLASS (mx_focus_manager_parent_class)->dispose (object); } static void mx_focus_manager_finalize (GObject *object) { G_OBJECT_CLASS (mx_focus_manager_parent_class)->finalize (object); } static void mx_focus_manager_class_init (MxFocusManagerClass *klass) { GParamSpec *pspec; GObjectClass *object_class = G_OBJECT_CLASS (klass); g_type_class_add_private (klass, sizeof (MxFocusManagerPrivate)); object_class->get_property = mx_focus_manager_get_property; object_class->set_property = mx_focus_manager_set_property; object_class->dispose = mx_focus_manager_dispose; object_class->finalize = mx_focus_manager_finalize; pspec = g_param_spec_object ("stage", "Stage", "Top level container for focusables", CLUTTER_TYPE_STAGE, MX_PARAM_READABLE); g_object_class_install_property (object_class, PROP_STAGE, pspec); pspec = g_param_spec_object ("focused", "Focused", "The object that currently has focus", CLUTTER_TYPE_ACTOR, MX_PARAM_READABLE); g_object_class_install_property (object_class, PROP_FOCUSED, pspec); focus_manager_quark = g_quark_from_static_string ("mx-focus-manager"); } static void mx_focus_manager_init (MxFocusManager *self) { self->priv = FOCUS_MANAGER_PRIVATE (self); } static void mx_focus_manager_weak_notify (MxFocusManager *manager, ClutterStage *stage) { manager->priv->stage = NULL; g_object_unref (manager); } static gboolean mx_focus_manager_refocus_cb (MxFocusManager *manager) { MxFocusManagerPrivate *priv = manager->priv; priv->refocus_idle = 0; if (priv->focused_toplevel) mx_focus_manager_push_focus_with_hint (manager, priv->focused_toplevel, MX_FOCUS_HINT_PRIOR); else mx_focus_manager_set_focused (manager, NULL); return FALSE; } static void mx_focus_manager_focused_mapped_cb (ClutterActor *focused, GParamSpec *pspec, MxFocusManager *manager) { MxFocusManagerPrivate *priv = manager->priv; if (CLUTTER_ACTOR_IS_MAPPED (focused)) return; /* We can't refocus straight away, as during the mapped notification, * the actor is still valid (and will just receive focus again). * * This may also trigger a redraw, which would trigger this Clutter bug: * http://bugzilla.clutter-project.org/show_bug.cgi?id=2621 * (patch submitted). */ if (!priv->refocus_idle) priv->refocus_idle = g_idle_add_full (G_PRIORITY_HIGH, (GSourceFunc)mx_focus_manager_refocus_cb, manager, NULL); } static void mx_focus_manager_set_focused (MxFocusManager *manager, MxFocusable *focusable) { MxFocusManagerPrivate *priv = manager->priv; if (priv->focused) { g_object_remove_weak_pointer (G_OBJECT (priv->focused), (gpointer *)&priv->focused); g_signal_handlers_disconnect_by_func (priv->focused, mx_focus_manager_focused_mapped_cb, manager); } if (priv->focused_toplevel) { g_object_remove_weak_pointer (G_OBJECT (priv->focused_toplevel), (gpointer *)&priv->focused_toplevel); priv->focused_toplevel = NULL; } priv->focused = focusable; if (priv->focused) { ClutterActor *parent = clutter_actor_get_parent (CLUTTER_ACTOR (priv->focused)); if (priv->refocus_idle) { g_source_remove (priv->refocus_idle); priv->refocus_idle = 0; } g_signal_connect_after (priv->focused, "notify::mapped", G_CALLBACK (mx_focus_manager_focused_mapped_cb), manager); g_object_add_weak_pointer (G_OBJECT (priv->focused), (gpointer *)&priv->focused); /* Keep track of the highest-level focusable so we can push the focus * back onto it if the focused actor disappears. */ while (parent) { if (MX_IS_FOCUSABLE (parent)) priv->focused_toplevel = MX_FOCUSABLE (parent); parent = clutter_actor_get_parent (parent); } if (priv->focused_toplevel) g_object_add_weak_pointer (G_OBJECT (priv->focused_toplevel), (gpointer *)&priv->focused_toplevel); } } static void mx_focus_manager_start_focus (MxFocusManager *manager, MxFocusHint hint) { MxFocusManagerPrivate *priv = manager->priv; MxFocusable *focusable, *new_focused; GList *children, *l; children = clutter_container_get_children (CLUTTER_CONTAINER (priv->stage)); focusable = NULL; if (hint == MX_FOCUS_HINT_LAST) children = g_list_reverse (children); for (l = children; l; l = g_list_next (l)) { if (MX_IS_FOCUSABLE (l->data)) { focusable = MX_FOCUSABLE (l->data); new_focused = mx_focusable_accept_focus (focusable, hint); mx_focus_manager_set_focused (manager, new_focused); if (new_focused) break; } } g_list_free (children); } static gboolean mx_focus_manager_ensure_focused (MxFocusManager *manager, ClutterStage *stage, MxFocusHint hint) { ClutterActor *actor; MxFocusManagerPrivate *priv = manager->priv; if (!priv->focused) { /* try and guess the current focusable by working up the scene graph from * the actor that currently has key focus until a focusable is found */ actor = clutter_stage_get_key_focus (stage); while (actor) { if (MX_IS_FOCUSABLE (actor)) break; actor = clutter_actor_get_parent (actor); } /* if we didn't find a focusable, try any children of the stage */ if (!actor) { mx_focus_manager_start_focus (manager, hint); } else { MxFocusable *new_focused; new_focused = mx_focusable_accept_focus (MX_FOCUSABLE (actor), MX_FOCUS_HINT_PRIOR); mx_focus_manager_set_focused (manager, new_focused); } if (priv->focused) g_object_notify (G_OBJECT (manager), "focused"); } return (priv->focused) ? TRUE : FALSE; } static gboolean mx_focus_manager_event_cb (ClutterStage *stage, ClutterEvent *event, MxFocusManager *manager) { MxFocusable *old_focus; MxFocusManagerPrivate *priv = manager->priv; if (event->type != CLUTTER_KEY_PRESS) return FALSE; old_focus = priv->focused; switch (event->key.keyval) { case CLUTTER_KEY_Tab: case CLUTTER_KEY_ISO_Left_Tab: if (event->key.modifier_state & CLUTTER_SHIFT_MASK) mx_focus_manager_move_focus (manager, MX_FOCUS_DIRECTION_PREVIOUS); else mx_focus_manager_move_focus (manager, MX_FOCUS_DIRECTION_NEXT); break; case CLUTTER_KEY_Right: mx_focus_manager_move_focus (manager, MX_FOCUS_DIRECTION_RIGHT); break; case CLUTTER_KEY_Left: mx_focus_manager_move_focus (manager, MX_FOCUS_DIRECTION_LEFT); break; case CLUTTER_KEY_Up: mx_focus_manager_move_focus (manager, MX_FOCUS_DIRECTION_UP); break; case CLUTTER_KEY_Down: mx_focus_manager_move_focus (manager, MX_FOCUS_DIRECTION_DOWN); break; default: return FALSE; } if (priv->focused == old_focus) return FALSE; else return TRUE; } /** * mx_focus_manager_get_for_stage: * @stage: A #ClutterStage * * Get the MxFocusManager associated with a stage, or create one if none exist * for the specified stage. * * Returns: (transfer none): An #MxFocusManager */ MxFocusManager * mx_focus_manager_get_for_stage (ClutterStage *stage) { MxFocusManager *manager; g_return_val_if_fail (CLUTTER_IS_STAGE (stage), NULL); manager = g_object_get_qdata (G_OBJECT (stage), focus_manager_quark); if (manager == NULL) { MxFocusManagerPrivate *priv; manager = g_object_new (MX_TYPE_FOCUS_MANAGER, NULL); priv = manager->priv; priv->stage = CLUTTER_ACTOR (stage); g_object_set_qdata (G_OBJECT (stage), focus_manager_quark, manager); g_object_weak_ref (G_OBJECT (stage), (GWeakNotify) mx_focus_manager_weak_notify, manager); g_signal_connect (G_OBJECT (stage), "event", G_CALLBACK (mx_focus_manager_event_cb), manager); g_object_notify (G_OBJECT (manager), "stage"); } return manager; } /** * mx_focus_manager_get_stage: * @manager: A #MxFocusManager * * Get the stage the MxFocusManager is associated with * * Returns: (transfer none): A #ClutterStage */ ClutterStage* mx_focus_manager_get_stage (MxFocusManager *manager) { g_return_val_if_fail (MX_IS_FOCUS_MANAGER (manager), NULL); return CLUTTER_STAGE (manager->priv->stage); } /** * mx_focus_manager_get_focused: * @manager: A #MxFocusManager * * Get the currently focused #MxFocusable * * Returns: (transfer none): MxFocusable */ MxFocusable* mx_focus_manager_get_focused (MxFocusManager *manager) { g_return_val_if_fail (MX_IS_FOCUS_MANAGER (manager), NULL); return manager->priv->focused; } /** * mx_focus_manager_push_focus: * @manager: the focus manager * @focusable: the object to set focus on * * Sets the currently focused actor, with an #MxFocusHint of * %MX_FOCUS_HINT_PRIOR. * * Note: the final focused object may not be the same as @focusable if * @focusable does not accept focus directly. */ void mx_focus_manager_push_focus (MxFocusManager *manager, MxFocusable *focusable) { MxFocusManagerPrivate *priv; g_return_if_fail (MX_IS_FOCUS_MANAGER (manager)); g_return_if_fail (MX_IS_FOCUSABLE (focusable)); priv = manager->priv; if (priv->focused != focusable) { MxFocusable *new_focused; if (priv->focused) { /* notify the current focusable that focus is being moved */ mx_focusable_move_focus (priv->focused, MX_FOCUS_DIRECTION_OUT, priv->focused); } new_focused = mx_focusable_accept_focus (focusable, MX_FOCUS_HINT_PRIOR); mx_focus_manager_set_focused (manager, new_focused); g_object_notify (G_OBJECT (manager), "focused"); } } /** * mx_focus_manager_push_focus_with_hint: * @manager: the focus manager * @focusable: the object to set focus on * @hint: an #MxFocusHint * * Similar to #mx_focus_manager_push_focus, but allows the hint to be specified. * * Note: the final focused object may not be the same as @focusable if * @focusable does not accept focus directly. * * Since: 1.2 */ void mx_focus_manager_push_focus_with_hint (MxFocusManager *manager, MxFocusable *focusable, MxFocusHint hint) { MxFocusManagerPrivate *priv; g_return_if_fail (MX_IS_FOCUS_MANAGER (manager)); g_return_if_fail (MX_IS_FOCUSABLE (focusable)); priv = manager->priv; if (priv->focused != focusable) { MxFocusable *new_focused; if (priv->focused) { /* notify the current focusable that focus is being moved */ mx_focusable_move_focus (priv->focused, MX_FOCUS_DIRECTION_OUT, priv->focused); } new_focused = mx_focusable_accept_focus (focusable, hint); mx_focus_manager_set_focused (manager, new_focused); g_object_notify (G_OBJECT (manager), "focused"); } } /** * mx_focus_manager_move_focus * @manager: the focus manager * @direction: The direction to move focus in * * Moves the current focus in the given direction. */ void mx_focus_manager_move_focus (MxFocusManager *manager, MxFocusDirection direction) { MxFocusable *old_focus, *new_focused; MxFocusManagerPrivate *priv; g_return_if_fail (MX_IS_FOCUS_MANAGER (manager)); priv = manager->priv; old_focus = priv->focused; if (priv->focused) { new_focused = mx_focusable_move_focus (priv->focused, direction, priv->focused); mx_focus_manager_set_focused (manager, new_focused); } if (!priv->focused) { /* If we're going next or previous, we wrap around, otherwise * re-focus the last actor. */ switch (direction) { case MX_FOCUS_DIRECTION_NEXT: if (old_focus) mx_focus_manager_start_focus (manager, MX_FOCUS_HINT_FIRST); else mx_focus_manager_ensure_focused (manager, CLUTTER_STAGE (priv->stage), MX_FOCUS_HINT_FIRST); break; case MX_FOCUS_DIRECTION_PREVIOUS: if (old_focus) mx_focus_manager_start_focus (manager, MX_FOCUS_HINT_LAST); else mx_focus_manager_ensure_focused (manager, CLUTTER_STAGE (priv->stage), MX_FOCUS_HINT_LAST); break; default: /* re-focus the original */ if (old_focus && (direction != MX_FOCUS_DIRECTION_OUT)) { new_focused = mx_focusable_accept_focus (old_focus, 0); mx_focus_manager_set_focused (manager, new_focused); } else mx_focus_manager_ensure_focused (manager, CLUTTER_STAGE (priv->stage), MX_FOCUS_HINT_FIRST); break; } } /* Notify if the focus has changed */ if (priv->focused != old_focus) g_object_notify (G_OBJECT (manager), "focused"); } mx-1.4.7/mx/mx-focus-manager.h000066400000000000000000000062011201047117600161130ustar00rootroot00000000000000/* * mx-focus-manager: Keyboard focus manager object * * Copyright 2010 Intel Corporation * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * * Written by: Thomas Wood * */ #if !defined(MX_H_INSIDE) && !defined(MX_COMPILATION) #error "Only can be included directly.h" #endif #ifndef _MX_FOCUS_MANAGER_H #define _MX_FOCUS_MANAGER_H #include #include #include "mx-focusable.h" G_BEGIN_DECLS #define MX_TYPE_FOCUS_MANAGER mx_focus_manager_get_type() #define MX_FOCUS_MANAGER(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ MX_TYPE_FOCUS_MANAGER, MxFocusManager)) #define MX_FOCUS_MANAGER_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), \ MX_TYPE_FOCUS_MANAGER, MxFocusManagerClass)) #define MX_IS_FOCUS_MANAGER(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \ MX_TYPE_FOCUS_MANAGER)) #define MX_IS_FOCUS_MANAGER_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE ((klass), \ MX_TYPE_FOCUS_MANAGER)) #define MX_FOCUS_MANAGER_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), \ MX_TYPE_FOCUS_MANAGER, MxFocusManagerClass)) typedef struct _MxFocusManager MxFocusManager; typedef struct _MxFocusManagerClass MxFocusManagerClass; typedef struct _MxFocusManagerPrivate MxFocusManagerPrivate; /** * MxFocusManager: * * The contents of this structure are private and should only be accessed * through the public API. */ struct _MxFocusManager { GObject parent; MxFocusManagerPrivate *priv; }; struct _MxFocusManagerClass { GObjectClass parent_class; /* padding for future expansion */ void (*_padding_0) (void); void (*_padding_1) (void); void (*_padding_2) (void); void (*_padding_3) (void); void (*_padding_4) (void); }; GType mx_focus_manager_get_type (void) G_GNUC_CONST; MxFocusManager *mx_focus_manager_get_for_stage (ClutterStage *stage); ClutterStage* mx_focus_manager_get_stage (MxFocusManager *manager); MxFocusable* mx_focus_manager_get_focused (MxFocusManager *manager); void mx_focus_manager_push_focus (MxFocusManager *manager, MxFocusable *focusable); void mx_focus_manager_push_focus_with_hint (MxFocusManager *manager, MxFocusable *focusable, MxFocusHint hint); void mx_focus_manager_move_focus (MxFocusManager *manager, MxFocusDirection direction); G_END_DECLS #endif /* _MX_FOCUS_MANAGER_H */ mx-1.4.7/mx/mx-focusable.c000066400000000000000000000157231201047117600153330ustar00rootroot00000000000000/* * mx-focusable: An interface for actors than can accept keyboard focus * * Copyright 2010 Intel Corporation * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * * Written by: Thomas Wood * */ #include "mx-focusable.h" #include "mx-enum-types.h" #include "mx-private.h" #include "mx-widget.h" #include static void mx_focusable_base_init (gpointer g_iface) { } GType mx_focusable_get_type (void) { static GType type = 0; if (type == 0) { static const GTypeInfo info = { sizeof (MxFocusableIface), mx_focusable_base_init, /* base_init */ NULL, }; type = g_type_register_static (G_TYPE_INTERFACE, "MxFocusable", &info, 0); } return type; } /** * mx_focusable_move_focus: * @focusable: A #MxFocusable * @direction: A #MxFocusDirection * @from: focusable to move the focus from * * Move the focus * * Returns: (transfer none): the newly focused focusable */ MxFocusable* mx_focusable_move_focus (MxFocusable *focusable, MxFocusDirection direction, MxFocusable *from) { MxFocusableIface *iface; ClutterActor *actor, *parent; MxFocusable *moved = NULL; g_return_val_if_fail (MX_IS_FOCUSABLE (focusable), FALSE); iface = MX_FOCUSABLE_GET_INTERFACE (focusable); if (iface->move_focus) moved = iface->move_focus (focusable, direction, from); if (moved) goto found; /* try and pass the focus up to something that can manage it */ actor = CLUTTER_ACTOR (focusable); parent = clutter_actor_get_parent (actor); while (parent && !CLUTTER_IS_STAGE (parent)) { /* the parent will only have knowledge of its direct children * that are focusable. */ if (MX_IS_FOCUSABLE (actor)) from = MX_FOCUSABLE (actor); if (MX_IS_FOCUSABLE (parent)) { moved = mx_focusable_move_focus (MX_FOCUSABLE (parent), direction, from); if (moved) goto found; from = MX_FOCUSABLE (parent); } actor = parent; parent = clutter_actor_get_parent (actor); } /* special case the stage */ if (CLUTTER_IS_STAGE (parent) && ((direction == MX_FOCUS_DIRECTION_NEXT) || (direction == MX_FOCUS_DIRECTION_PREVIOUS))) { GList *children, *l; GList *child_link; children = clutter_container_get_children (CLUTTER_CONTAINER (parent)); /* find the current focused widget */ child_link = g_list_find (children, actor); if (child_link) { if (direction == MX_FOCUS_DIRECTION_NEXT) { /* find the next widget to focus */ for (l = child_link->next; l; l = g_list_next (l)) { if (MX_IS_FOCUSABLE (l->data)) { moved = mx_focusable_accept_focus (l->data, MX_FOCUS_HINT_FIRST); if (moved) break; } } } else if (direction == MX_FOCUS_DIRECTION_PREVIOUS) { /* find the previous widget to focus */ for (l = child_link->prev; l; l = g_list_previous (l)) { if (MX_IS_FOCUSABLE (l->data)) { moved = mx_focusable_accept_focus (l->data, MX_FOCUS_HINT_LAST); if (moved) break; } } } } g_list_free (children); } found: if (moved) { MX_NOTE (FOCUS, "Moving focus from %s (%p) to %s (%p) with direction %s", G_OBJECT_TYPE_NAME (from), from, G_OBJECT_TYPE_NAME (moved), moved, _mx_enum_to_string (MX_TYPE_FOCUS_DIRECTION, direction)); } return moved; } /** * mx_focusable_accept_focus: * @focusable: A #MxFocusable * @hint: A #MxFocusHint * * Accept the focus * * Returns: (transfer none): the focusable */ MxFocusable * mx_focusable_accept_focus (MxFocusable *focusable, MxFocusHint hint) { MxFocusableIface *iface; g_return_val_if_fail (MX_IS_FOCUSABLE (focusable), NULL); /* hidden actors should not accept focus */ if (!CLUTTER_ACTOR_IS_VISIBLE (focusable)) return NULL; /* disabled widgets should also not accept focus */ if (MX_IS_WIDGET (focusable) && mx_widget_get_disabled (MX_WIDGET (focusable))) return NULL; iface = MX_FOCUSABLE_GET_INTERFACE (focusable); if (iface->accept_focus) { MX_NOTE (FOCUS, "Accept focus on %s (%p) with hint %s", G_OBJECT_TYPE_NAME (focusable), focusable, _mx_enum_to_string (MX_TYPE_FOCUS_HINT, hint)); return iface->accept_focus (focusable, hint); } else { return NULL; } } /** * mx_focus_hint_from_direction: * @direction: A #MxFocusDirection * * Transforms a focus direction to a focus hint. This is a convenience * function for actors that implement the #MxFocusable interface, to * pass the correct #MxFocusHint to their children when calling * mx_focusable_accept_focus(). * * %MX_FOCUS_DIRECTION_UP maps to %MX_FOCUS_HINT_FROM_BELOW, * %MX_FOCUS_DIRECTION_DOWN maps to %MX_FOCUS_HINT_FROM_ABOVE, * %MX_FOCUS_DIRECTION_LEFT maps to %MX_FOCUS_HINT_FROM_RIGHT, * %MX_FOCUS_DIRECTION_RIGHT maps to %MX_FOCUS_HINT_FROM_LEFT, * %MX_FOCUS_DIRECTION_NEXT maps to %MX_FOCUS_HINT_FIRST, * %MX_FOCUS_DIRECTION_PREVIOUS maps to %MX_FOCUS_HINT_LAST and * anything else maps to %MX_FOCUS_HINT_PRIOR. * * Returns: A #MxFocusHint * * Since: 1.2 */ MxFocusHint mx_focus_hint_from_direction (MxFocusDirection direction) { switch (direction) { case MX_FOCUS_DIRECTION_UP: return MX_FOCUS_HINT_FROM_BELOW; case MX_FOCUS_DIRECTION_DOWN: return MX_FOCUS_HINT_FROM_ABOVE; case MX_FOCUS_DIRECTION_LEFT: return MX_FOCUS_HINT_FROM_RIGHT; case MX_FOCUS_DIRECTION_RIGHT: return MX_FOCUS_HINT_FROM_LEFT; case MX_FOCUS_DIRECTION_NEXT: return MX_FOCUS_HINT_FIRST; case MX_FOCUS_DIRECTION_PREVIOUS: return MX_FOCUS_HINT_LAST; case MX_FOCUS_DIRECTION_OUT: default: return MX_FOCUS_HINT_PRIOR; } } mx-1.4.7/mx/mx-focusable.h000066400000000000000000000055351201047117600153400ustar00rootroot00000000000000/* * mx-focusable: An interface for actors than can accept keyboard focus * * Copyright 2010 Intel Corporation * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * * Written by: Thomas Wood * */ #if !defined(MX_H_INSIDE) && !defined(MX_COMPILATION) #error "Only can be included directly.h" #endif #ifndef _MX_FOCUSABLE_H #define _MX_FOCUSABLE_H #include G_BEGIN_DECLS #define MX_TYPE_FOCUSABLE mx_focusable_get_type() #define MX_FOCUSABLE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MX_TYPE_FOCUSABLE, MxFocusable)) #define MX_IS_FOCUSABLE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MX_TYPE_FOCUSABLE)) #define MX_FOCUSABLE_GET_INTERFACE(inst) (G_TYPE_INSTANCE_GET_INTERFACE ((inst), MX_TYPE_FOCUSABLE, MxFocusableIface)) /** * MxFocusable: * * This is an opaque structure whose members cannot be directly accessed. */ typedef struct _MxFocusable MxFocusable; /* dummy */ typedef struct _MxFocusableIface MxFocusableIface; typedef enum { MX_FOCUS_DIRECTION_OUT, MX_FOCUS_DIRECTION_UP, MX_FOCUS_DIRECTION_DOWN, MX_FOCUS_DIRECTION_LEFT, MX_FOCUS_DIRECTION_RIGHT, MX_FOCUS_DIRECTION_NEXT, MX_FOCUS_DIRECTION_PREVIOUS } MxFocusDirection; typedef enum { MX_FOCUS_HINT_FIRST, MX_FOCUS_HINT_LAST, MX_FOCUS_HINT_PRIOR, MX_FOCUS_HINT_FROM_ABOVE, MX_FOCUS_HINT_FROM_BELOW, MX_FOCUS_HINT_FROM_LEFT, MX_FOCUS_HINT_FROM_RIGHT } MxFocusHint; struct _MxFocusableIface { GObjectClass parent_class; MxFocusable* (*accept_focus) (MxFocusable *focusable, MxFocusHint hint); MxFocusable* (*move_focus) (MxFocusable *focusable, MxFocusDirection direction, MxFocusable *from); }; GType mx_focusable_get_type (void) G_GNUC_CONST; MxFocusable* mx_focusable_move_focus (MxFocusable *focusable, MxFocusDirection direction, MxFocusable *from); MxFocusable* mx_focusable_accept_focus (MxFocusable *focusable, MxFocusHint hint); MxFocusHint mx_focus_hint_from_direction (MxFocusDirection direction); G_END_DECLS #endif /* _MX_FOCUSABLE_H */ mx-1.4.7/mx/mx-frame.c000066400000000000000000000055541201047117600144630ustar00rootroot00000000000000/* * mx-frame.h: frame actor * * Copyright 2009 Intel Corporation * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * * Written by: Thomas Wood */ #include "mx-frame.h" G_DEFINE_TYPE (MxFrame, mx_frame, MX_TYPE_BIN) #define FRAME_PRIVATE(o) \ (G_TYPE_INSTANCE_GET_PRIVATE ((o), MX_TYPE_FRAME, MxFramePrivate)) struct _MxFramePrivate { gpointer dummy; }; static void mx_frame_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { switch (property_id) { default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } } static void mx_frame_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { switch (property_id) { default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } } static void mx_frame_dispose (GObject *object) { G_OBJECT_CLASS (mx_frame_parent_class)->dispose (object); } static void mx_frame_finalize (GObject *object) { G_OBJECT_CLASS (mx_frame_parent_class)->finalize (object); } static void mx_frame_allocate (ClutterActor *self, const ClutterActorBox *box, ClutterAllocationFlags flags) { CLUTTER_ACTOR_CLASS (mx_frame_parent_class)->allocate (self, box, flags); mx_bin_allocate_child (MX_BIN (self), box, flags); } static void mx_frame_class_init (MxFrameClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass); g_type_class_add_private (klass, sizeof (MxFramePrivate)); object_class->get_property = mx_frame_get_property; object_class->set_property = mx_frame_set_property; object_class->dispose = mx_frame_dispose; object_class->finalize = mx_frame_finalize; actor_class->allocate = mx_frame_allocate; } static void mx_frame_init (MxFrame *self) { self->priv = FRAME_PRIVATE (self); } /** * mx_frame_new: * * Create a new #MxFrame * * Returns: a newly allocated #MxFrame */ ClutterActor * mx_frame_new (void) { return g_object_new (MX_TYPE_FRAME, NULL); } mx-1.4.7/mx/mx-frame.h000066400000000000000000000042251201047117600144620ustar00rootroot00000000000000/* * mx-frame.c: frame actor * * Copyright 2009 Intel Corporation * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * * Written by: Thomas Wood */ #if !defined(MX_H_INSIDE) && !defined(MX_COMPILATION) #error "Only can be included directly.h" #endif #ifndef _MX_FRAME_H #define _MX_FRAME_H #include "mx-bin.h" G_BEGIN_DECLS #define MX_TYPE_FRAME mx_frame_get_type() #define MX_FRAME(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ MX_TYPE_FRAME, MxFrame)) #define MX_FRAME_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), \ MX_TYPE_FRAME, MxFrameClass)) #define MX_IS_FRAME(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \ MX_TYPE_FRAME)) #define MX_IS_FRAME_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE ((klass), \ MX_TYPE_FRAME)) #define MX_FRAME_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), \ MX_TYPE_FRAME, MxFrameClass)) typedef struct _MxFrame MxFrame; typedef struct _MxFrameClass MxFrameClass; typedef struct _MxFramePrivate MxFramePrivate; /** * MxFrame: * * The contents of this structure are private and should only be accessed * through the public API. */ struct _MxFrame { MxBin parent; MxFramePrivate *priv; }; struct _MxFrameClass { MxBinClass parent_class; /* padding for future expansion */ void (*_padding_0) (void); void (*_padding_1) (void); void (*_padding_2) (void); void (*_padding_3) (void); void (*_padding_4) (void); }; GType mx_frame_get_type (void) G_GNUC_CONST; ClutterActor *mx_frame_new (void); G_END_DECLS #endif /* _MX_FRAME_H */ mx-1.4.7/mx/mx-grid.c000066400000000000000000001462071201047117600143170ustar00rootroot00000000000000/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * mx-grid.c: Reflowing grid layout container for mx. * * Copyright 2008, 2009, 2010 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * * Written by: Øyvind Kolås * Ported to mx by: Robert Staudinger * Scrollable support added by: Thomas Wood * */ /* TODO: * * - Better names for properties. * - Caching layouted positions? (perhaps needed for huge collections) * - More comments / overall concept on how the layouting is done. * - Allow more layout directions than just row major / column major. */ /** * SECTION:mx-grid * @short_description: a flow layout container * * #MxGrid is a layout container that arranges its children by placing them * in a single line and wrapping round to a new line when the edge of the * container is reached. * * This layout is particularly flexible, with the following configuration possibilities: * * * Column and row spacing are controllable * (#MxGrid:column_spacing and #MxGrid:row_spacing) * Column and row sizes can be made consistent, regardless of the size * of the contained actors (#MxGrid:homogenous_columns and #MxGrid:homogenous_rows) * * Prefer to pack children vertically first, * rather than horizontally (#MxGrid:orientation) * Specify the maximum number of rows or columns * to allow in the layout, to prevent it from being excessively stretched * (#MxGrid:max_stride). * * * To demonstrate how these settings interact, here are a few images. * *
* MxGrid flowing across multiple rows * An #MxGrid containing 9 child actors; * #MxGrid:orientation is set to the default (MX_ORIENTATION_HORIZONTAL, * i.e. lay out horizontally first); #MxGrid:max_stride has not been set * (so there's no maximum row size); #MxGrid:column_spacing and #MxGrid:row_spacing have * been set so that there is spacing between cells vertically and horizontally. * *
* *
* MxGrid flowing on a single row * The image shows the same #MxGrid with its children flowing into one row. * This is the layout's response to being resized horizontally. * *
* *
* MxGrid flowing onto two rows * The same #MxGrid with 9 children wrapping onto two rows: notice * how the "odd" rectangle is on the end of a row, rather than at the * bottom of a column. This is because preference * is being given to packing onto the end of rows, rather than columns, * because #MxGrid:orientation is set to MX_ORIENTATION_HORIZONTAL. Even though * there is room for the rectangle at the bottom of the column, the * layout prefers to place children onto the end of a row if there is room. * *
* *
* MxGrid flowing into two columns * The same #MxGrid 9 children with #MxGrid:orientation set to * MX_ORIENTATION_VERTICAL. This time, the layout wraps onto two columns rather than two * rows. Even though there is room on the end of the rows for the children, * the preference is for them to be placed on the bottom of columns, or into * new columns, before being added to rows. * *
*/ #include #include "mx-scrollable.h" #include "mx-grid.h" #include "mx-stylable.h" #include "mx-focusable.h" #include "mx-enum-types.h" #include "mx-private.h" typedef struct _MxGridActorData MxGridActorData; static void mx_grid_dispose (GObject *object); static void mx_grid_finalize (GObject *object); static void mx_grid_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec); static void mx_grid_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec); static void clutter_container_iface_init (ClutterContainerIface *iface); static void mx_grid_real_add (ClutterContainer *container, ClutterActor *actor); static void mx_grid_real_remove (ClutterContainer *container, ClutterActor *actor); static void mx_grid_real_foreach (ClutterContainer *container, ClutterCallback callback, gpointer user_data); static void mx_grid_real_raise (ClutterContainer *container, ClutterActor *actor, ClutterActor *sibling); static void mx_grid_real_lower (ClutterContainer *container, ClutterActor *actor, ClutterActor *sibling); static void mx_grid_real_sort_depth_order (ClutterContainer *container); static void mx_grid_free_actor_data (gpointer data); static void mx_grid_paint (ClutterActor *actor); static void mx_grid_pick (ClutterActor *actor, const ClutterColor *color); static void mx_grid_get_preferred_width (ClutterActor *self, gfloat for_height, gfloat *min_width_p, gfloat *natural_width_p); static void mx_grid_get_preferred_height (ClutterActor *self, gfloat for_width, gfloat *min_height_p, gfloat *natural_height_p); static void mx_grid_allocate (ClutterActor *self, const ClutterActorBox *box, ClutterAllocationFlags flags); static void mx_grid_do_allocate (ClutterActor *self, const ClutterActorBox *box, ClutterAllocationFlags flags, gboolean calculate_extents_only, gfloat *actual_width, gfloat *actual_height, gfloat *min_width, gfloat *min_height); static void scrollable_interface_init (MxScrollableIface *iface); static void mx_box_focusable_iface_init (MxFocusableIface *iface); G_DEFINE_TYPE_WITH_CODE (MxGrid, mx_grid, MX_TYPE_WIDGET, G_IMPLEMENT_INTERFACE (CLUTTER_TYPE_CONTAINER, clutter_container_iface_init) G_IMPLEMENT_INTERFACE (MX_TYPE_SCROLLABLE, scrollable_interface_init) G_IMPLEMENT_INTERFACE (MX_TYPE_FOCUSABLE, mx_box_focusable_iface_init)); #define MX_GRID_GET_PRIVATE(obj) \ (G_TYPE_INSTANCE_GET_PRIVATE ((obj), MX_TYPE_GRID, \ MxGridPrivate)) struct _MxGridPrivate { GHashTable *hash_table; GList *list; gboolean homogenous_rows; gboolean homogenous_columns; MxAlign line_alignment; gfloat column_spacing, row_spacing; MxAlign child_x_align; MxAlign child_y_align; MxOrientation orientation; gboolean first_of_batch; gfloat a_current_sum, a_wrap; gfloat max_extent_a; gfloat max_extent_b; gint max_stride; MxAdjustment *hadjustment; MxAdjustment *vadjustment; MxFocusable *last_focus; }; enum { PROP_0, PROP_HOMOGENOUS_ROWS, PROP_HOMOGENOUS_COLUMNS, PROP_ROW_SPACING, PROP_COLUMN_SPACING, PROP_CHILD_X_ALIGN, PROP_CHILD_Y_ALIGN, PROP_LINE_ALIGNMENT, PROP_ORIENTATION, PROP_HADJUST, PROP_VADJUST, PROP_MAX_STRIDE, }; struct _MxGridActorData { gboolean xpos_set, ypos_set; gfloat xpos, ypos; gfloat pref_width, pref_height; }; /* scrollable interface */ static void adjustment_value_notify_cb (MxAdjustment *adjustment, GParamSpec *pspec, MxGrid *grid) { clutter_actor_queue_redraw (CLUTTER_ACTOR (grid)); } static void scrollable_set_adjustments (MxScrollable *scrollable, MxAdjustment *hadjustment, MxAdjustment *vadjustment) { MxGridPrivate *priv = MX_GRID (scrollable)->priv; if (hadjustment != priv->hadjustment) { if (priv->hadjustment) { g_signal_handlers_disconnect_by_func (priv->hadjustment, adjustment_value_notify_cb, scrollable); g_object_unref (priv->hadjustment); } if (hadjustment) { g_object_ref (hadjustment); g_signal_connect (hadjustment, "notify::value", G_CALLBACK (adjustment_value_notify_cb), scrollable); } priv->hadjustment = hadjustment; g_object_notify (G_OBJECT (scrollable), "horizontal-adjustment"); } if (vadjustment != priv->vadjustment) { if (priv->vadjustment) { g_signal_handlers_disconnect_by_func (priv->vadjustment, adjustment_value_notify_cb, scrollable); g_object_unref (priv->vadjustment); } if (vadjustment) { g_object_ref (vadjustment); g_signal_connect (vadjustment, "notify::value", G_CALLBACK (adjustment_value_notify_cb), scrollable); } priv->vadjustment = vadjustment; g_object_notify (G_OBJECT (scrollable), "vertical-adjustment"); } } static void scrollable_get_adjustments (MxScrollable *scrollable, MxAdjustment **hadjustment, MxAdjustment **vadjustment) { MxGridPrivate *priv; g_return_if_fail (MX_IS_GRID (scrollable)); priv = ((MxGrid *) scrollable)->priv; if (hadjustment) { if (priv->hadjustment) *hadjustment = priv->hadjustment; else { MxAdjustment *adjustment; /* create an initial adjustment. this is filled with correct values * as soon as allocate() is called */ adjustment = mx_adjustment_new (); scrollable_set_adjustments (scrollable, adjustment, priv->vadjustment); g_object_unref (adjustment); *hadjustment = adjustment; } } if (vadjustment) { if (priv->vadjustment) *vadjustment = priv->vadjustment; else { MxAdjustment *adjustment; /* create an initial adjustment. this is filled with correct values * as soon as allocate() is called */ adjustment = mx_adjustment_new (); scrollable_set_adjustments (scrollable, priv->hadjustment, adjustment); g_object_unref (adjustment); *vadjustment = adjustment; } } } static void scrollable_interface_init (MxScrollableIface *iface) { iface->set_adjustments = scrollable_set_adjustments; iface->get_adjustments = scrollable_get_adjustments; } static void mx_grid_apply_transform (ClutterActor *a, CoglMatrix *m) { MxGridPrivate *priv = MX_GRID (a)->priv; gdouble x, y; CLUTTER_ACTOR_CLASS (mx_grid_parent_class)->apply_transform (a, m); if (priv->hadjustment) x = mx_adjustment_get_value (priv->hadjustment); else x = 0; if (priv->vadjustment) y = mx_adjustment_get_value (priv->vadjustment); else y = 0; cogl_matrix_translate (m , (int) -x, (int) -y, 0); } static gboolean mx_grid_get_paint_volume (ClutterActor *actor, ClutterPaintVolume *volume) { MxGridPrivate *priv = MX_GRID (actor)->priv; ClutterVertex vertex; if (!clutter_paint_volume_set_from_allocation (volume, actor)) return FALSE; clutter_paint_volume_get_origin (volume, &vertex); if (priv->hadjustment) vertex.x += mx_adjustment_get_value (priv->hadjustment); if (priv->vadjustment) vertex.y += mx_adjustment_get_value (priv->vadjustment); clutter_paint_volume_set_origin (volume, &vertex); return TRUE; } static void mx_grid_class_init (MxGridClass *klass) { GObjectClass *gobject_class = (GObjectClass *) klass; ClutterActorClass *actor_class = (ClutterActorClass *) klass; GParamSpec *pspec; gobject_class->dispose = mx_grid_dispose; gobject_class->finalize = mx_grid_finalize; gobject_class->set_property = mx_grid_set_property; gobject_class->get_property = mx_grid_get_property; actor_class->paint = mx_grid_paint; actor_class->pick = mx_grid_pick; actor_class->get_preferred_width = mx_grid_get_preferred_width; actor_class->get_preferred_height = mx_grid_get_preferred_height; actor_class->allocate = mx_grid_allocate; actor_class->apply_transform = mx_grid_apply_transform; actor_class->get_paint_volume = mx_grid_get_paint_volume; g_type_class_add_private (klass, sizeof (MxGridPrivate)); pspec = g_param_spec_float ("row-spacing", "Row spacing", "spacing between rows in the layout", 0, G_MAXFLOAT, 0, G_PARAM_READWRITE|G_PARAM_CONSTRUCT); g_object_class_install_property (gobject_class, PROP_ROW_SPACING, pspec); pspec = g_param_spec_float ("column-spacing", "Column spacing", "spacing between columns in the layout", 0, G_MAXFLOAT, 0, G_PARAM_READWRITE|G_PARAM_CONSTRUCT); g_object_class_install_property (gobject_class, PROP_COLUMN_SPACING, pspec); pspec = g_param_spec_boolean ("homogenous-rows", "homogenous rows", "Should all rows have the same height?", FALSE, G_PARAM_READWRITE|G_PARAM_CONSTRUCT); g_object_class_install_property (gobject_class, PROP_HOMOGENOUS_ROWS, pspec); pspec = g_param_spec_boolean ("homogenous-columns", "homogenous columns", "Should all columns have the same height?", FALSE, G_PARAM_READWRITE|G_PARAM_CONSTRUCT); g_object_class_install_property (gobject_class, PROP_HOMOGENOUS_COLUMNS, pspec); pspec = g_param_spec_enum ("orientation", "Orientation", "Pack children vertically (in columns), " "instead of horizontally (in rows)", MX_TYPE_ORIENTATION, MX_ORIENTATION_HORIZONTAL, G_PARAM_READWRITE|G_PARAM_CONSTRUCT); g_object_class_install_property (gobject_class, PROP_ORIENTATION, pspec); pspec = g_param_spec_enum ("line-alignment", "Line Alignment", "Alignment of rows/columns", MX_TYPE_ALIGN, MX_ALIGN_START, G_PARAM_READWRITE|G_PARAM_CONSTRUCT); g_object_class_install_property (gobject_class, PROP_LINE_ALIGNMENT, pspec); pspec = g_param_spec_enum ("child-y-align", "Vertical align", "Vertical alignment of items within cells", MX_TYPE_ALIGN, MX_ALIGN_START, G_PARAM_READWRITE|G_PARAM_CONSTRUCT); g_object_class_install_property (gobject_class,PROP_CHILD_Y_ALIGN, pspec); pspec = g_param_spec_enum ("child-x-align", "Horizontal align", "Horizontal alignment of items within cells", MX_TYPE_ALIGN, MX_ALIGN_START, G_PARAM_READWRITE|G_PARAM_CONSTRUCT); g_object_class_install_property (gobject_class, PROP_CHILD_X_ALIGN, pspec); pspec = g_param_spec_int ("max-stride", "Maximum stride", "Maximum number of rows or columns, depending" " on orientation. For example, if max-stride is set" " to 3 with orientation MX_ORIENTATION_HORIZONTAL, there will" " be a maximum of 3 children in a row; if" " orientation is MX_ORIENTATION_VERTICAL, there will be a" " maximum of 3 children in a column", 0, G_MAXINT, 0, G_PARAM_READWRITE|G_PARAM_CONSTRUCT); g_object_class_install_property (gobject_class, PROP_MAX_STRIDE, pspec); g_object_class_override_property (gobject_class, PROP_HADJUST, "horizontal-adjustment"); g_object_class_override_property (gobject_class, PROP_VADJUST, "vertical-adjustment"); } static void clutter_container_iface_init (ClutterContainerIface *iface) { iface->add = mx_grid_real_add; iface->remove = mx_grid_real_remove; iface->foreach = mx_grid_real_foreach; iface->raise = mx_grid_real_raise; iface->lower = mx_grid_real_lower; iface->sort_depth_order = mx_grid_real_sort_depth_order; } /* * focusable implementation */ static void update_adjustments (MxGrid *self, MxFocusable *focusable) { MxGridPrivate *priv = self->priv; gdouble value, new_value, page_size; ClutterActorBox box = { 0, }; clutter_actor_get_allocation_box (CLUTTER_ACTOR (focusable), &box); if (priv->vadjustment) { mx_adjustment_get_values (priv->vadjustment, &value, NULL, NULL, NULL, NULL, &page_size); if (box.y1 < value) new_value = box.y1; else if (box.y2 > value + page_size) new_value = box.y2 - page_size; else new_value = value; mx_adjustment_interpolate (priv->vadjustment, new_value, 250, CLUTTER_EASE_OUT_CUBIC); } if (priv->hadjustment) { mx_adjustment_get_values (priv->hadjustment, &value, NULL, NULL, NULL, NULL, &page_size); if (box.x1 < value) new_value = box.x1; else if (box.x2 > value + page_size) new_value = box.x2 - page_size; else new_value = value; mx_adjustment_interpolate (priv->hadjustment, new_value, 250, CLUTTER_EASE_OUT_CUBIC); } } static MxFocusable* mx_grid_move_focus (MxFocusable *focusable, MxFocusDirection direction, MxFocusable *from) { MxGridPrivate *priv = MX_GRID (focusable)->priv; GList *l, *childlink; /* find the current focus */ childlink = g_list_find (priv->list, from); if (!childlink) return NULL; priv->last_focus = from; /* find the next widget to focus */ if (direction == MX_FOCUS_DIRECTION_NEXT) { for (l = childlink->next; l; l = g_list_next (l)) { if (MX_IS_FOCUSABLE (l->data)) { MxFocusable *focused; focused = mx_focusable_accept_focus (MX_FOCUSABLE (l->data), MX_FOCUS_HINT_FIRST); if (focused) { update_adjustments (MX_GRID (focusable), focused); return focused; } } } } else if (direction == MX_FOCUS_DIRECTION_PREVIOUS) { for (l = g_list_previous (childlink); l; l = g_list_previous (l)) { if (MX_IS_FOCUSABLE (l->data)) { MxFocusable *focused; focused = mx_focusable_accept_focus (MX_FOCUSABLE (l->data), MX_FOCUS_HINT_LAST); if (focused) { update_adjustments (MX_GRID (focusable), focused); return focused; } } } } return NULL; } static MxFocusable* mx_grid_accept_focus (MxFocusable *focusable, MxFocusHint hint) { MxGridPrivate *priv = MX_GRID (focusable)->priv; MxFocusable *return_focusable; GList* list, *l; return_focusable = NULL; /* find the first/last focusable widget */ switch (hint) { case MX_FOCUS_HINT_LAST: list = g_list_reverse (g_list_copy (priv->list)); break; case MX_FOCUS_HINT_PRIOR: if (priv->last_focus) { list = g_list_copy (g_list_find (priv->list, priv->last_focus)); if (list) break; } /* This intentionally runs into the next case */ default: case MX_FOCUS_HINT_FIRST: list = g_list_copy (priv->list); break; } for (l = list; l; l = g_list_next (l)) { if (MX_IS_FOCUSABLE (l->data)) { return_focusable = mx_focusable_accept_focus (MX_FOCUSABLE (l->data), hint); if (return_focusable) { update_adjustments (MX_GRID (focusable), return_focusable); break; } } } g_list_free (list); return return_focusable; } static void mx_box_focusable_iface_init (MxFocusableIface *iface) { iface->move_focus = mx_grid_move_focus; iface->accept_focus = mx_grid_accept_focus; } static void mx_grid_init (MxGrid *self) { MxGridPrivate *priv; self->priv = priv = MX_GRID_GET_PRIVATE (self); /* do not unref in the hashtable, the reference is for now kept by the list * (double bookkeeping sucks) */ priv->hash_table = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, mx_grid_free_actor_data); } static void mx_grid_dispose (GObject *object) { /* Destroy all of the children. This will cause them to be removed from the container and unparented */ clutter_container_foreach (CLUTTER_CONTAINER (object), (ClutterCallback) clutter_actor_destroy, NULL); G_OBJECT_CLASS (mx_grid_parent_class)->dispose (object); } static void mx_grid_finalize (GObject *object) { MxGrid *self = (MxGrid *) object; MxGridPrivate *priv = self->priv; g_hash_table_destroy (priv->hash_table); G_OBJECT_CLASS (mx_grid_parent_class)->finalize (object); } void mx_grid_set_line_alignment (MxGrid *self, MxAlign value) { MxGridPrivate *priv = MX_GRID_GET_PRIVATE (self); priv->line_alignment = value; clutter_actor_queue_relayout (CLUTTER_ACTOR (self)); } gboolean mx_grid_get_line_alignment (MxGrid *self) { MxGridPrivate *priv = MX_GRID_GET_PRIVATE (self); return priv->line_alignment; } void mx_grid_set_homogenous_rows (MxGrid *self, gboolean value) { MxGridPrivate *priv = MX_GRID_GET_PRIVATE (self); priv->homogenous_rows = value; clutter_actor_queue_relayout (CLUTTER_ACTOR (self)); } gboolean mx_grid_get_homogenous_rows (MxGrid *self) { MxGridPrivate *priv = MX_GRID_GET_PRIVATE (self); return priv->homogenous_rows; } void mx_grid_set_homogenous_columns (MxGrid *self, gboolean value) { MxGridPrivate *priv = MX_GRID_GET_PRIVATE (self); priv->homogenous_columns = value; clutter_actor_queue_relayout (CLUTTER_ACTOR (self)); } gboolean mx_grid_get_homogenous_columns (MxGrid *self) { MxGridPrivate *priv = MX_GRID_GET_PRIVATE (self); return priv->homogenous_columns; } void mx_grid_set_orientation (MxGrid *grid, MxOrientation orientation) { MxGridPrivate *priv = grid->priv; g_return_if_fail (MX_IS_GRID (grid)); if (priv->orientation != orientation) { priv->orientation = orientation; clutter_actor_queue_relayout (CLUTTER_ACTOR (grid)); g_object_notify (G_OBJECT (grid), "orientation"); } } MxOrientation mx_grid_get_orientation (MxGrid *grid) { g_return_val_if_fail (MX_IS_GRID (grid), MX_ORIENTATION_HORIZONTAL); return grid->priv->orientation; } void mx_grid_set_column_spacing (MxGrid *self, gfloat value) { MxGridPrivate *priv = MX_GRID_GET_PRIVATE (self); priv->column_spacing = value; clutter_actor_queue_relayout (CLUTTER_ACTOR (self)); } gfloat mx_grid_get_column_spacing (MxGrid *self) { MxGridPrivate *priv = MX_GRID_GET_PRIVATE (self); return priv->column_spacing; } void mx_grid_set_row_spacing (MxGrid *self, gfloat value) { MxGridPrivate *priv = MX_GRID_GET_PRIVATE (self); priv->row_spacing = value; clutter_actor_queue_relayout (CLUTTER_ACTOR (self)); } gfloat mx_grid_get_row_spacing (MxGrid *self) { MxGridPrivate *priv = MX_GRID_GET_PRIVATE (self); return priv->row_spacing; } void mx_grid_set_child_y_align (MxGrid *self, MxAlign value) { MxGridPrivate *priv = MX_GRID_GET_PRIVATE (self); priv->child_y_align = value; clutter_actor_queue_relayout (CLUTTER_ACTOR (self)); } MxAlign mx_grid_get_child_y_align (MxGrid *self) { MxGridPrivate *priv = MX_GRID_GET_PRIVATE (self); return priv->child_y_align; } void mx_grid_set_child_x_align (MxGrid *self, MxAlign value) { MxGridPrivate *priv = MX_GRID_GET_PRIVATE (self); priv->child_x_align = value; clutter_actor_queue_relayout (CLUTTER_ACTOR (self)); } MxAlign mx_grid_get_child_x_align (MxGrid *self) { MxGridPrivate *priv = MX_GRID_GET_PRIVATE (self); return priv->child_x_align; } void mx_grid_set_max_stride (MxGrid *self, gint value) { g_return_if_fail (MX_IS_GRID (self)); self->priv->max_stride = value; clutter_actor_queue_relayout (CLUTTER_ACTOR (self)); } gint mx_grid_get_max_stride (MxGrid *self) { g_return_val_if_fail (MX_IS_GRID (self), 0); return self->priv->max_stride; } static void mx_grid_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { MxGrid *grid = MX_GRID (object); MxGridPrivate *priv; priv = MX_GRID_GET_PRIVATE (object); switch (prop_id) { case PROP_LINE_ALIGNMENT: mx_grid_set_line_alignment (grid, g_value_get_enum (value)); break; case PROP_HOMOGENOUS_ROWS: mx_grid_set_homogenous_rows (grid, g_value_get_boolean (value)); break; case PROP_HOMOGENOUS_COLUMNS: mx_grid_set_homogenous_columns (grid, g_value_get_boolean (value)); break; case PROP_ORIENTATION: mx_grid_set_orientation (grid, g_value_get_enum (value)); break; case PROP_COLUMN_SPACING: mx_grid_set_column_spacing (grid, g_value_get_float (value)); break; case PROP_ROW_SPACING: mx_grid_set_row_spacing (grid, g_value_get_float (value)); break; case PROP_CHILD_X_ALIGN: mx_grid_set_child_x_align (grid, g_value_get_enum (value)); break; case PROP_CHILD_Y_ALIGN: mx_grid_set_child_y_align (grid, g_value_get_enum (value)); break; case PROP_HADJUST: scrollable_set_adjustments (MX_SCROLLABLE (object), g_value_get_object (value), priv->vadjustment); break; case PROP_VADJUST: scrollable_set_adjustments (MX_SCROLLABLE (object), priv->hadjustment, g_value_get_object (value)); break; case PROP_MAX_STRIDE: mx_grid_set_max_stride (grid, g_value_get_int (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void mx_grid_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { MxAdjustment *adjustment; MxGrid *grid = MX_GRID (object); switch (prop_id) { case PROP_HOMOGENOUS_ROWS: g_value_set_boolean (value, mx_grid_get_homogenous_rows (grid)); break; case PROP_HOMOGENOUS_COLUMNS: g_value_set_boolean (value, mx_grid_get_homogenous_columns (grid)); break; case PROP_LINE_ALIGNMENT: g_value_set_enum (value, mx_grid_get_line_alignment (grid)); break; case PROP_ORIENTATION: g_value_set_enum (value, mx_grid_get_orientation (grid)); break; case PROP_COLUMN_SPACING: g_value_set_float (value, mx_grid_get_column_spacing (grid)); break; case PROP_ROW_SPACING: g_value_set_float (value, mx_grid_get_row_spacing (grid)); break; case PROP_CHILD_X_ALIGN: g_value_set_enum (value, mx_grid_get_child_x_align (grid)); break; case PROP_CHILD_Y_ALIGN: g_value_set_enum (value, mx_grid_get_child_y_align (grid)); break; case PROP_HADJUST: scrollable_get_adjustments (MX_SCROLLABLE (grid), &adjustment, NULL); g_value_set_object (value, adjustment); break; case PROP_VADJUST: scrollable_get_adjustments (MX_SCROLLABLE (grid), NULL, &adjustment); g_value_set_object (value, adjustment); break; case PROP_MAX_STRIDE: g_value_set_int (value, mx_grid_get_max_stride (grid)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void mx_grid_free_actor_data (gpointer data) { g_slice_free (MxGridActorData, data); } ClutterActor * mx_grid_new (void) { MxWidget *self = g_object_new (MX_TYPE_GRID, NULL); return (ClutterActor*) self; } static void mx_grid_real_add (ClutterContainer *container, ClutterActor *actor) { MxGridPrivate *priv; MxGridActorData *data; g_return_if_fail (MX_IS_GRID (container)); priv = MX_GRID (container)->priv; g_object_ref (actor); clutter_actor_set_parent (actor, CLUTTER_ACTOR (container)); data = g_slice_alloc0 (sizeof (MxGridActorData)); priv->list = g_list_append (priv->list, actor); g_hash_table_insert (priv->hash_table, actor, data); g_signal_emit_by_name (container, "actor-added", actor); clutter_actor_queue_relayout (CLUTTER_ACTOR (container)); g_object_unref (actor); } static void mx_grid_real_remove (ClutterContainer *container, ClutterActor *actor) { MxGrid *layout = MX_GRID (container); MxGridPrivate *priv = layout->priv; g_object_ref (actor); if (g_hash_table_remove (priv->hash_table, actor)) { clutter_actor_unparent (actor); clutter_actor_queue_relayout (CLUTTER_ACTOR (layout)); g_signal_emit_by_name (container, "actor-removed", actor); } priv->list = g_list_remove (priv->list, actor); g_object_unref (actor); } static void mx_grid_real_foreach (ClutterContainer *container, ClutterCallback callback, gpointer user_data) { MxGrid *layout = MX_GRID (container); MxGridPrivate *priv = layout->priv; g_list_foreach (priv->list, (GFunc) callback, user_data); } /* * Implementations for raise, lower and sort_by_depth_order are taken from * ClutterBox. */ static void mx_grid_real_raise (ClutterContainer *container, ClutterActor *actor, ClutterActor *sibling) { MxGridPrivate *priv = MX_GRID (container)->priv; priv->list = g_list_remove (priv->list, actor); if (sibling == NULL) priv->list = g_list_append (priv->list, actor); else { gint index_ = g_list_index (priv->list, sibling) + 1; priv->list = g_list_insert (priv->list, actor, index_); } clutter_actor_queue_relayout (CLUTTER_ACTOR (container)); } static void mx_grid_real_lower (ClutterContainer *container, ClutterActor *actor, ClutterActor *sibling) { MxGridPrivate *priv = MX_GRID (container)->priv; priv->list = g_list_remove (priv->list, actor); if (sibling == NULL) priv->list = g_list_prepend (priv->list, actor); else { gint index_ = g_list_index (priv->list, sibling); priv->list = g_list_insert (priv->list, actor, index_); } clutter_actor_queue_relayout (CLUTTER_ACTOR (container)); } static gint sort_by_depth (gconstpointer a, gconstpointer b) { gfloat depth_a = clutter_actor_get_depth ((ClutterActor *) a); gfloat depth_b = clutter_actor_get_depth ((ClutterActor *) b); if (depth_a < depth_b) return -1; if (depth_a > depth_b) return 1; return 0; } static void mx_grid_real_sort_depth_order (ClutterContainer *container) { MxGridPrivate *priv = MX_GRID (container)->priv; priv->list = g_list_sort (priv->list, sort_by_depth); clutter_actor_queue_relayout (CLUTTER_ACTOR (container)); } static void mx_grid_paint (ClutterActor *actor) { MxGrid *layout = (MxGrid *) actor; MxGridPrivate *priv = layout->priv; GList *child_item; gfloat x, y; ClutterActorBox grid_b; if (priv->hadjustment) x = mx_adjustment_get_value (priv->hadjustment); else x = 0; if (priv->vadjustment) y = mx_adjustment_get_value (priv->vadjustment); else y = 0; CLUTTER_ACTOR_CLASS (mx_grid_parent_class)->paint (actor); clutter_actor_get_allocation_box (actor, &grid_b); grid_b.x2 = (grid_b.x2 - grid_b.x1) + x; grid_b.x1 = x; grid_b.y2 = (grid_b.y2 - grid_b.y1) + y; grid_b.y1 = y; for (child_item = priv->list; child_item != NULL; child_item = child_item->next) { ClutterActor *child = child_item->data; ClutterActorBox child_b; g_assert (child != NULL); /* ensure the child is "on screen" */ clutter_actor_get_allocation_box (CLUTTER_ACTOR (child), &child_b); if ((child_b.x1 < grid_b.x2) && (child_b.x2 > grid_b.x1) && (child_b.y1 < grid_b.y2) && (child_b.y2 > grid_b.y1) && CLUTTER_ACTOR_IS_VISIBLE (child)) { clutter_actor_paint (child); } } } static void mx_grid_pick (ClutterActor *actor, const ClutterColor *color) { MxGrid *layout = (MxGrid *) actor; MxGridPrivate *priv = layout->priv; GList *child_item; gfloat x, y; ClutterActorBox grid_b; if (priv->hadjustment) x = mx_adjustment_get_value (priv->hadjustment); else x = 0; if (priv->vadjustment) y = mx_adjustment_get_value (priv->vadjustment); else y = 0; /* Chain up so we get a bounding box pained (if we are reactive) */ CLUTTER_ACTOR_CLASS (mx_grid_parent_class)->pick (actor, color); clutter_actor_get_allocation_box (actor, &grid_b); grid_b.x2 = (grid_b.x2 - grid_b.x1) + x; grid_b.x1 = x; grid_b.y2 = (grid_b.y2 - grid_b.y1) + y; grid_b.y1 = y; for (child_item = priv->list; child_item != NULL; child_item = child_item->next) { ClutterActor *child = child_item->data; ClutterActorBox child_b; g_assert (child != NULL); /* ensure the child is "on screen" */ clutter_actor_get_allocation_box (CLUTTER_ACTOR (child), &child_b); if ((child_b.x1 < grid_b.x2) && (child_b.x2 > grid_b.x1) && (child_b.y1 < grid_b.y2) && (child_b.y2 > grid_b.y1) && CLUTTER_ACTOR_IS_VISIBLE (child)) { clutter_actor_paint (child); } } } static void mx_grid_get_preferred_width (ClutterActor *self, gfloat for_height, gfloat *min_width_p, gfloat *natural_width_p) { gfloat actual_width, min_width; ClutterActorBox box; box.x1 = 0; box.y1 = 0; box.x2 = G_MAXFLOAT; box.y2 = for_height; mx_grid_do_allocate (self, &box, FALSE, TRUE, &actual_width, NULL, &min_width, NULL); if (min_width_p) *min_width_p = min_width; if (natural_width_p) *natural_width_p = actual_width; } static void mx_grid_get_preferred_height (ClutterActor *self, gfloat for_width, gfloat *min_height_p, gfloat *natural_height_p) { gfloat actual_height, min_height; ClutterActorBox box; box.x1 = 0; box.y1 = 0; box.x2 = for_width; box.y2 = G_MAXFLOAT; mx_grid_do_allocate (self, &box, FALSE, TRUE, NULL, &actual_height, NULL, &min_height); if (min_height_p) *min_height_p = min_height; if (natural_height_p) *natural_height_p = actual_height; } static gfloat compute_row_height (GList *siblings, gfloat best_yet, gfloat current_a, MxGridPrivate *priv) { GList *l; gboolean homogenous_a; gfloat gap; if (priv->orientation == MX_ORIENTATION_VERTICAL) { homogenous_a = priv->homogenous_rows; gap = priv->row_spacing; } else { homogenous_a = priv->homogenous_columns; gap = priv->column_spacing; } for (l = siblings; l != NULL; l = l->next) { ClutterActor *child = l->data; gfloat natural_width, natural_height; /* each child will get as much space as they require */ clutter_actor_get_preferred_size (CLUTTER_ACTOR (child), NULL, NULL, &natural_width, &natural_height); if (priv->orientation == MX_ORIENTATION_VERTICAL) { gfloat temp = natural_height; natural_height = natural_width; natural_width = temp; } /* if the primary axis is homogenous, each additional item is the same * width */ if (homogenous_a) natural_width = priv->max_extent_a; if (natural_height > best_yet) best_yet = natural_height; /* if the child is overflowing, we wrap to next line */ if (current_a + natural_width + gap > priv->a_wrap) { return best_yet; } current_a += natural_width + gap; } return best_yet; } static gfloat compute_row_start (GList *siblings, gfloat start_x, MxGridPrivate *priv) { gfloat current_a = start_x; GList *l; gboolean homogenous_a; gfloat gap; if (priv->orientation == MX_ORIENTATION_VERTICAL) { homogenous_a = priv->homogenous_rows; gap = priv->row_spacing; } else { homogenous_a = priv->homogenous_columns; gap = priv->column_spacing; } for (l = siblings; l != NULL; l = l->next) { ClutterActor *child = l->data; gfloat natural_width, natural_height; /* each child will get as much space as they require */ clutter_actor_get_preferred_size (CLUTTER_ACTOR (child), NULL, NULL, &natural_width, &natural_height); if (priv->orientation == MX_ORIENTATION_VERTICAL) natural_width = natural_height; /* if the primary axis is homogenous, each additional item is the same width */ if (homogenous_a) natural_width = priv->max_extent_a; /* if the child is overflowing, we wrap to next line */ if (current_a + natural_width + gap > priv->a_wrap) { if (current_a == start_x) return start_x; return (priv->a_wrap - current_a); } current_a += natural_width + gap; } return (priv->a_wrap - current_a); } static void mx_grid_do_allocate (ClutterActor *self, const ClutterActorBox *box, ClutterAllocationFlags flags, gboolean calculate_extents_only, gfloat *actual_width, gfloat *actual_height, gfloat *min_width, gfloat *min_height) { MxGrid *layout = (MxGrid *) self; MxGridPrivate *priv = layout->priv; MxPadding padding; gfloat current_a; gfloat current_b; gfloat next_b; gfloat agap; gfloat bgap; gboolean homogenous_a; gboolean homogenous_b; gdouble aalign; gdouble balign; int current_stride; mx_widget_get_padding (MX_WIDGET (self), &padding); if (actual_width) *actual_width = 0; if (actual_height) *actual_height = 0; if (min_width) *min_width = 0; if (min_height) *min_height = 0; current_a = current_b = next_b = 0; GList *iter; if (priv->orientation == MX_ORIENTATION_VERTICAL) { priv->a_wrap = box->y2 - box->y1 - padding.top - padding.bottom; homogenous_b = priv->homogenous_columns; homogenous_a = priv->homogenous_rows; aalign = MX_ALIGN_TO_FLOAT (priv->child_y_align); balign = MX_ALIGN_TO_FLOAT (priv->child_x_align); agap = priv->row_spacing; bgap = priv->column_spacing; } else { priv->a_wrap = box->x2 - box->x1 - padding.left - padding.right; homogenous_a = priv->homogenous_columns; homogenous_b = priv->homogenous_rows; aalign = MX_ALIGN_TO_FLOAT (priv->child_x_align); balign = MX_ALIGN_TO_FLOAT (priv->child_y_align); agap = priv->column_spacing; bgap = priv->row_spacing; } priv->max_extent_a = 0; priv->max_extent_b = 0; priv->first_of_batch = TRUE; if (homogenous_a || homogenous_b) { for (iter = priv->list; iter; iter = iter->next) { ClutterActor *child = iter->data; gfloat natural_width; gfloat natural_height; if (!CLUTTER_ACTOR_IS_VISIBLE (child)) continue; /* each child will get as much space as they require */ clutter_actor_get_preferred_size (CLUTTER_ACTOR (child), NULL, NULL, &natural_width, &natural_height); if (natural_width > priv->max_extent_a) priv->max_extent_a = natural_width; if (natural_height > priv->max_extent_b) priv->max_extent_b = natural_width; } } if (priv->orientation == MX_ORIENTATION_VERTICAL) { gfloat temp = priv->max_extent_a; priv->max_extent_a = priv->max_extent_b; priv->max_extent_b = temp; } current_stride = 0; for (iter = priv->list; iter; iter=iter->next) { ClutterActor *child = iter->data; gfloat natural_a; gfloat natural_b; gfloat min_a; gfloat min_b; if (!CLUTTER_ACTOR_IS_VISIBLE (child)) continue; /* each child will get as much space as they require */ clutter_actor_get_preferred_size (CLUTTER_ACTOR (child), &min_a, &min_b, &natural_a, &natural_b); /* swap axes around if column is major */ if (priv->orientation == MX_ORIENTATION_VERTICAL) { gfloat temp = natural_a; natural_a = natural_b; natural_b = temp; temp = min_a; min_a = min_b; min_b = temp; } /* if the child is overflowing, or the max-stride has been reached, * we wrap to next line */ current_stride++; if ((priv->max_stride > 0 && current_stride > priv->max_stride) || (current_a + natural_a > priv->a_wrap || (homogenous_a && current_a + priv->max_extent_a > priv->a_wrap))) { current_b = next_b + bgap; current_a = 0; next_b = current_b + bgap; priv->first_of_batch = TRUE; current_stride = 1; } if (priv->line_alignment && priv->first_of_batch) { current_a = compute_row_start (iter, current_a, priv); priv->first_of_batch = FALSE; } if (next_b-current_b < natural_b) next_b = current_b + natural_b; { gfloat row_height; ClutterActorBox child_box; ClutterActorBox min_child_box; if (homogenous_b) { row_height = priv->max_extent_b; } else { row_height = compute_row_height (iter, next_b-current_b, current_a, priv); } if (homogenous_a) { child_box.x1 = current_a + (priv->max_extent_a-natural_a) * aalign; child_box.x2 = child_box.x1 + natural_a; } else { child_box.x1 = current_a; child_box.x2 = child_box.x1 + natural_a; } child_box.y1 = current_b + (row_height-natural_b) * balign; child_box.y2 = child_box.y1 + natural_b; min_child_box.x1 = 0; min_child_box.y1 = 0; min_child_box.x2 = min_a; min_child_box.y2 = min_b; if (priv->orientation == MX_ORIENTATION_VERTICAL) { gfloat temp = child_box.x1; child_box.x1 = child_box.y1; child_box.y1 = temp; temp = child_box.x2; child_box.x2 = child_box.y2; child_box.y2 = temp; temp = min_child_box.x1; min_child_box.x1 = min_child_box.y1; min_child_box.y1 = temp; temp = min_child_box.x2; min_child_box.x2 = min_child_box.y2; min_child_box.y2 = temp; } /* account for padding and pixel-align */ child_box.x1 = (int)(child_box.x1 + padding.left); child_box.y1 = (int)(child_box.y1 + padding.top); child_box.x2 = (int)(child_box.x2 + padding.left); child_box.y2 = (int)(child_box.y2 + padding.top); /* update the allocation */ if (!calculate_extents_only) clutter_actor_allocate (CLUTTER_ACTOR (child), &child_box, flags); /* update extents */ if (actual_width && (child_box.x2 + padding.right) > *actual_width) *actual_width = child_box.x2 + padding.right; if (actual_height && (child_box.y2 + padding.bottom) > *actual_height) *actual_height = child_box.y2 + padding.bottom; if (min_width && padding.left + min_child_box.x2 + padding.right > *min_width) { *min_width = padding.left + min_child_box.x2 + padding.right; } if (min_height && padding.top + min_child_box.y2 + padding.bottom > *min_height) { *min_height = padding.left + min_child_box.y2 + padding.right; } if (homogenous_a) { current_a += priv->max_extent_a + agap; } else { current_a += natural_a + agap; } } } } static void mx_grid_allocate (ClutterActor *self, const ClutterActorBox *box, ClutterAllocationFlags flags) { MxGridPrivate *priv = MX_GRID (self)->priv; ClutterActorBox alloc_box = *box; /* chain up here to preserve the allocated size * * (we ignore the height of the allocation if we have a vadjustment set, * or the width of the allocation if a vertical orientation is set and an * hadjustment is set) */ CLUTTER_ACTOR_CLASS (mx_grid_parent_class) ->allocate (self, box, flags); /* only update vadjustment - we don't really want horizontal scrolling */ if (priv->vadjustment && priv->orientation == MX_ORIENTATION_HORIZONTAL) { gdouble prev_value; gfloat height; /* get preferred height for this width */ mx_grid_do_allocate (self, box, flags, TRUE, NULL, &height, NULL, NULL); /* set our allocated height to be the preferred height, since we will be * scrolling */ alloc_box.y2 = alloc_box.y1 + height; g_object_set (G_OBJECT (priv->vadjustment), "lower", 0.0, "upper", height, "page-size", box->y2 - box->y1, "step-increment", (box->y2 - box->y1) / 6, "page-increment", box->y2 - box->y1, NULL); if (priv->hadjustment) { g_object_set (G_OBJECT (priv->hadjustment), "lower", 0.0, "upper", 0.0, NULL);; } prev_value = mx_adjustment_get_value (priv->vadjustment); mx_adjustment_set_value (priv->vadjustment, prev_value); } if (priv->hadjustment && priv->orientation == MX_ORIENTATION_VERTICAL) { gdouble prev_value; gfloat width; /* get preferred width for this height */ mx_grid_do_allocate (self, box, flags, TRUE, &width, NULL, NULL, NULL); /* set our allocated height to be the preferred height, since we will be * scrolling */ alloc_box.x2 = alloc_box.x1 + width; g_object_set (G_OBJECT (priv->hadjustment), "lower", 0.0, "upper", width, "page-size", box->x2 - box->x1, "step-increment", (box->x2 - box->x1) / 6, "page-increment", box->x2 - box->x1, NULL); if (priv->vadjustment) { g_object_set (G_OBJECT (priv->vadjustment), "lower", 0.0, "upper", 0.0, NULL);; } prev_value = mx_adjustment_get_value (priv->hadjustment); mx_adjustment_set_value (priv->hadjustment, prev_value); } mx_grid_do_allocate (self, &alloc_box, flags, FALSE, NULL, NULL, NULL, NULL); } mx-1.4.7/mx/mx-grid.h000066400000000000000000000111621201047117600143130ustar00rootroot00000000000000/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * mx-grid.h: Reflowing grid layout container for mx. * * Copyright 2008, 2009, 2010 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * Boston, MA 02111-1307, USA. * * Written by: Øyvind Kolås * Ported to mx by: Robert Staudinger * */ #if !defined(MX_H_INSIDE) && !defined(MX_COMPILATION) #error "Only can be included directly.h" #endif #ifndef __MX_GRID_H__ #define __MX_GRID_H__ #include #include G_BEGIN_DECLS #define MX_TYPE_GRID (mx_grid_get_type()) #define MX_GRID(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ MX_TYPE_GRID, \ MxGrid)) #define MX_GRID_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), \ MX_TYPE_GRID, \ MxGridClass)) #define MX_IS_GRID(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \ MX_TYPE_GRID)) #define MX_IS_GRID_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE ((klass), \ MX_TYPE_GRID)) #define MX_GRID_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), \ MX_TYPE_GRID, \ MxGridClass)) typedef struct _MxGrid MxGrid; typedef struct _MxGridClass MxGridClass; typedef struct _MxGridPrivate MxGridPrivate; struct _MxGridClass { MxWidgetClass parent_class; /* padding for future expansion */ void (*_padding_0) (void); void (*_padding_1) (void); void (*_padding_2) (void); void (*_padding_3) (void); void (*_padding_4) (void); }; /** * MxGrid: * * The contents of the this structure are private and should only be accessed * through the public API. */ struct _MxGrid { /*< private >*/ MxWidget parent; MxGridPrivate *priv; }; GType mx_grid_get_type (void) G_GNUC_CONST; ClutterActor *mx_grid_new (void); void mx_grid_set_line_alignment (MxGrid *self, MxAlign value); gboolean mx_grid_get_line_alignment (MxGrid *self); void mx_grid_set_homogenous_rows (MxGrid *self, gboolean value); gboolean mx_grid_get_homogenous_rows (MxGrid *self); void mx_grid_set_homogenous_columns (MxGrid *self, gboolean value); gboolean mx_grid_get_homogenous_columns (MxGrid *self); void mx_grid_set_orientation (MxGrid *grid, MxOrientation orientation); MxOrientation mx_grid_get_orientation (MxGrid *grid); void mx_grid_set_row_spacing (MxGrid *self, gfloat value); gfloat mx_grid_get_row_spacing (MxGrid *self); void mx_grid_set_column_spacing (MxGrid *self, gfloat value); gfloat mx_grid_get_column_spacing (MxGrid *self); void mx_grid_set_child_y_align (MxGrid *self, MxAlign value); MxAlign mx_grid_get_child_y_align (MxGrid *self); void mx_grid_set_child_x_align (MxGrid *self, MxAlign value); MxAlign mx_grid_get_child_x_align (MxGrid *self); void mx_grid_set_max_stride (MxGrid *self, gint value); gint mx_grid_get_max_stride (MxGrid *self); G_END_DECLS #endif /* __MX_GRID_H__ */ mx-1.4.7/mx/mx-icon-theme.c000066400000000000000000000706641201047117600154250ustar00rootroot00000000000000/* * mx-icon-theme.c: A freedesktop icon theme object * * Copyright 2010 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * * Author: Chris Lord * */ #include #include #include #include "mx-icon-theme.h" #include "mx-marshal.h" #include "mx-texture-cache.h" #include "mx-private.h" #include "mx-settings.h" G_DEFINE_TYPE (MxIconTheme, mx_icon_theme, G_TYPE_OBJECT) enum { CHANGED, LAST_SIGNAL }; #define ICON_THEME_PRIVATE(o) \ (G_TYPE_INSTANCE_GET_PRIVATE ((o), MX_TYPE_ICON_THEME, MxIconThemePrivate)) typedef enum { MX_FIXED, MX_SCALABLE, MX_THRESHOLD } MxIconType; typedef struct { gint size; gchar *path; MxIconType type; gint min_size; gint max_size; gint threshold; } MxIconData; struct _MxIconThemePrivate { guint override_theme : 1; GList *search_paths; GHashTable *icon_hash; GHashTable *theme_path_hash; gchar *theme; GKeyFile *theme_file; GList *theme_fallbacks; GKeyFile *hicolor_file; }; enum { PROP_0, PROP_THEME_NAME }; static void mx_icon_theme_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { MxIconTheme *theme = MX_ICON_THEME (object); switch (property_id) { case PROP_THEME_NAME: g_value_set_string (value, mx_icon_theme_get_theme_name (theme)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } } static void mx_icon_theme_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { switch (property_id) { case PROP_THEME_NAME: mx_icon_theme_set_theme_name (MX_ICON_THEME (object), g_value_get_string (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } } static void mx_icon_theme_dispose (GObject *object) { G_OBJECT_CLASS (mx_icon_theme_parent_class)->dispose (object); } static void mx_icon_theme_finalize (GObject *object) { MxIconTheme *self = MX_ICON_THEME (object); MxIconThemePrivate *priv = self->priv; mx_icon_theme_set_search_paths (self, NULL); g_hash_table_unref (priv->icon_hash); g_hash_table_unref (priv->theme_path_hash); g_free (priv->theme); if (priv->theme_file) g_key_file_free (priv->theme_file); while (priv->theme_fallbacks) { g_key_file_free ((GKeyFile *)priv->theme_fallbacks->data); priv->theme_fallbacks = g_list_delete_link (priv->theme_fallbacks, priv->theme_fallbacks); } if (priv->hicolor_file) g_key_file_free (priv->hicolor_file); G_OBJECT_CLASS (mx_icon_theme_parent_class)->finalize (object); } static void mx_icon_theme_class_init (MxIconThemeClass *klass) { GParamSpec *pspec; GObjectClass *object_class = G_OBJECT_CLASS (klass); g_type_class_add_private (klass, sizeof (MxIconThemePrivate)); object_class->get_property = mx_icon_theme_get_property; object_class->set_property = mx_icon_theme_set_property; object_class->dispose = mx_icon_theme_dispose; object_class->finalize = mx_icon_theme_finalize; pspec = g_param_spec_string ("theme-name", "Theme name", "The name of the currently loaded theme.", NULL, MX_PARAM_READWRITE); g_object_class_install_property (object_class, PROP_THEME_NAME, pspec); } static GKeyFile * mx_icon_theme_load_theme (MxIconTheme *self, const gchar *name) { GList *p; GKeyFile *key_file; MxIconThemePrivate *priv = self->priv; key_file = g_key_file_new (); for (p = priv->search_paths; p; p = p->next) { const gchar *path = p->data; gchar *key_path = g_build_filename (path, name, "index.theme", NULL); gboolean success = g_key_file_load_from_file (key_file, key_path, 0, NULL); g_free (key_path); if (success) { g_hash_table_insert (priv->theme_path_hash, key_file, g_strdup (name)); return key_file; } } g_key_file_free (key_file); return NULL; } static void mx_icon_theme_icon_data_free (MxIconData *data) { g_free (data->path); g_free (data); } static void mx_icon_theme_icon_hash_free (gpointer data) { GList *icon_list = (GList *)data; while (icon_list) { mx_icon_theme_icon_data_free ((MxIconData *)icon_list->data); icon_list = g_list_delete_link (icon_list, icon_list); } } static MxIconData * mx_icon_theme_icon_data_new (gint size, const gchar *path, MxIconType type, gint min_size, gint max_size, gint threshold) { MxIconData *data = g_new (MxIconData, 1); data->size = size; data->path = g_strdup (path); data->type = type; data->min_size = min_size; data->max_size = max_size; data->threshold = threshold; return data; } static gboolean mx_icon_theme_equal_func (GThemedIcon *icon, GThemedIcon *search) { gint i; const gchar * const *names1 = g_themed_icon_get_names (icon); const gchar * const *names2 = g_themed_icon_get_names (search); for (i = 0; names1[i] && names2[i]; i++) if (!g_str_equal (names1[i], names2[i])) return FALSE; return TRUE; } static guint mx_icon_theme_hash (GThemedIcon *icon) { const gchar * const *names = g_themed_icon_get_names (icon); return g_str_hash (names[0]); } static gboolean mx_icon_theme_find_name_cb (gpointer key, gpointer value, gpointer user_data) { gchar *name1 = (gchar *)value; gchar *name2 = (gchar *)user_data; return g_str_equal (name1, name2); } static void mx_icon_theme_load_fallbacks (MxIconTheme *theme, GKeyFile *theme_file, gboolean root) { MxIconThemePrivate *priv = theme->priv; gchar *fallbacks = g_key_file_get_string (theme_file, "Icon Theme", "Inherits", NULL); /* If this isn't the root theme, add it to the list of fallbacks */ if (!root) priv->theme_fallbacks = g_list_append (priv->theme_fallbacks, theme_file); /* Check the list of fallbacks in this theme and add any that we haven't * already. */ if (fallbacks) { gint i = 0; gint fallbacks_len = strlen (fallbacks); g_strdelimit (fallbacks, ",", '\0'); while (i < fallbacks_len) { gchar *fallback = fallbacks + i; i += strlen (fallback) + 1; /* Skip hicolor, we keep it loaded all the time */ if (g_str_equal (fallback, "hicolor")) continue; /* Skip if we've already loaded this theme */ if (g_hash_table_find (priv->theme_path_hash, mx_icon_theme_find_name_cb, fallback)) continue; /* Load this theme and store itself and its fallbacks in the * list of fallbacks. */ theme_file = mx_icon_theme_load_theme (theme, fallback); if (theme_file) mx_icon_theme_load_fallbacks (theme, theme_file, FALSE); } g_free (fallbacks); } } static void mx_icon_theme_changed_cb (MxSettings *settings, GParamSpec *pspec, MxIconTheme *self) { gchar *theme; if (self->priv->override_theme) return; g_object_get (settings, "icon-theme", &theme, NULL); mx_icon_theme_set_theme_name (self, theme); g_free (theme); self->priv->override_theme = FALSE; } static void mx_icon_theme_init (MxIconTheme *self) { gint i; gchar *path; const gchar *theme; const gchar *datadir; const gchar * const *datadirs; MxIconThemePrivate *priv = self->priv = ICON_THEME_PRIVATE (self); /* /usr/share/pixmaps, /usr/share/icons and $HOME/.icons are named in the * icon theme spec, but we'll interpret this to look in the system data * dirs, as most other (well, gtk) toolkits do. */ datadirs = g_get_system_data_dirs (); for (i = 0; datadirs[i]; i++) { datadir = datadirs[i]; path = g_build_filename (G_DIR_SEPARATOR_S, datadir, "pixmaps", NULL); priv->search_paths = g_list_prepend (priv->search_paths, path); path = g_build_filename (G_DIR_SEPARATOR_S, datadir, "icons", NULL); priv->search_paths = g_list_prepend (priv->search_paths, path); } datadir = g_get_user_data_dir (); path = g_build_filename (G_DIR_SEPARATOR_S, datadir, "pixmaps", NULL); priv->search_paths = g_list_prepend (priv->search_paths, path); path = g_build_filename (G_DIR_SEPARATOR_S, datadir, "icons", NULL); priv->search_paths = g_list_prepend (priv->search_paths, path); path = g_build_filename (g_get_home_dir (), ".icons", NULL); priv->search_paths = g_list_prepend (priv->search_paths, path); priv->icon_hash = g_hash_table_new_full ((GHashFunc)mx_icon_theme_hash, (GEqualFunc)mx_icon_theme_equal_func, g_object_unref, mx_icon_theme_icon_hash_free); priv->theme_path_hash = g_hash_table_new_full (g_direct_hash, g_str_equal, NULL, g_free); priv->hicolor_file = mx_icon_theme_load_theme (self, "hicolor"); if (!priv->hicolor_file) g_warning ("Error loading fallback icon theme"); theme = g_getenv ("MX_ICON_THEME"); if (theme) mx_icon_theme_set_theme_name (self, theme); else { MxSettings *settings = mx_settings_get_default (); g_signal_connect (settings, "notify::icon-theme", G_CALLBACK (mx_icon_theme_changed_cb), self); mx_icon_theme_changed_cb (settings, NULL, self); } } /** * mx_icon_theme_new: * * Create a new #MxIconTheme. In most cicumstances, it is more useful to use * #mx_icon_theme_get_default to load the default icon theme. * * Returns: a newly allocated #MxIconTheme. */ MxIconTheme * mx_icon_theme_new (void) { return g_object_new (MX_TYPE_ICON_THEME, NULL); } /** * mx_icon_theme_get_default: * * Return the default #MxIconTheme object used by the toolkit. * * Return value: (transfer none): an #MxIconTheme. */ MxIconTheme * mx_icon_theme_get_default (void) { static MxIconTheme *default_icon_theme = NULL; if (!default_icon_theme) default_icon_theme = mx_icon_theme_new (); return default_icon_theme; } /** * mx_icon_theme_get_theme_name: * @theme: A #MxIconTheme * * Get the value of the #MxIconTheme:theme-name property. * * Returns: the current value of the "theme-name" property. */ const gchar * mx_icon_theme_get_theme_name (MxIconTheme *theme) { g_return_val_if_fail (MX_IS_ICON_THEME (theme), NULL); return theme->priv->theme; } /** * mx_icon_theme_set_theme_name: * @theme: A #MxIconTheme * @theme_name: the name of an icon theme to load, or %NULL * * Set the value of the #MxIconTheme:theme-name property. This will cause the * icon theme to be loaded if it differs from the existing theme name. If the * theme could not be loaded, it will fall back to using the default icon theme * (hicolor). * * This will override the system's theme setting. To revert to the system * icon theme, this function can be called with a %NULL @theme_name argument. * */ void mx_icon_theme_set_theme_name (MxIconTheme *theme, const gchar *theme_name) { MxIconThemePrivate *priv; g_return_if_fail (MX_IS_ICON_THEME (theme)); priv = theme->priv; if (!theme_name) { if (priv->override_theme) { gchar *system_theme = NULL; MxSettings *settings = mx_settings_get_default (); g_object_get (settings, "icon-theme", &system_theme, NULL); priv->override_theme = FALSE; mx_icon_theme_set_theme_name (theme, system_theme); priv->override_theme = FALSE; g_free (system_theme); } return; } priv->override_theme = TRUE; if (g_str_equal (theme_name, "hicolor")) return; if (priv->theme && g_str_equal (priv->theme, theme_name)) return; /* Clear old data */ g_hash_table_remove_all (priv->icon_hash); g_free (priv->theme); if (priv->theme_file) { g_hash_table_remove (priv->theme_path_hash, priv->theme_file); g_key_file_free (priv->theme_file); } while (priv->theme_fallbacks) { g_hash_table_remove (priv->theme_path_hash, priv->theme_fallbacks->data); g_key_file_free ((GKeyFile *)priv->theme_fallbacks->data); priv->theme_fallbacks = g_list_delete_link (priv->theme_fallbacks, priv->theme_fallbacks); } /* Load new theme file */ priv->theme = g_strdup (theme_name); priv->theme_file = mx_icon_theme_load_theme (theme, theme_name); if (!priv->theme_file) { g_warning ("Error loading \"%s\" icon theme", priv->theme); return; } /* Load fallbacks */ mx_icon_theme_load_fallbacks (theme, priv->theme_file, TRUE); g_object_notify (G_OBJECT (theme), "theme-name"); } static void mx_icon_theme_collect_dirs (GString *string, const gchar *path, const gchar *root) { GDir *dir; gchar *full_path; const gchar *file; full_path = g_build_filename (root, path, NULL); dir = g_dir_open (full_path, 0, NULL); g_free (full_path); if (!dir) return; while ((file = g_dir_read_name (dir))) { gchar *new_dir = g_build_filename (root, path, file, NULL); gboolean result = g_file_test (new_dir, G_FILE_TEST_IS_DIR); g_free (new_dir); if (result) { gchar *rel_dir = g_build_filename (path, file, NULL); g_string_append (string, rel_dir); g_string_append (string, ","); mx_icon_theme_collect_dirs (string, rel_dir, root); g_free (rel_dir); } } g_dir_close (dir); } static GList * mx_icon_theme_theme_load_icon (MxIconTheme *self, GKeyFile *theme_file, const gchar *icon, GIcon *store_icon, gboolean store_fail) { gchar *dirs; const gchar *theme; GList *data = NULL; MxIconThemePrivate *priv = self->priv; dirs = g_key_file_get_string (theme_file, "Icon Theme", "Directories", NULL); theme = g_hash_table_lookup (priv->theme_path_hash, theme_file); if (!dirs) { GList *p; GString *string; /* Icon theme hasn't specified directories, so recurse and * collect all of them. */ string = g_string_new (""); for (p = priv->search_paths; p; p = p->next) { const gchar *search_path = p->data; gchar *path = g_build_filename (search_path, theme, NULL); mx_icon_theme_collect_dirs (string, "", path); g_free (path); } /* Chop off the trailing comma */ g_string_truncate (string, string->len - 1); dirs = string->str; g_string_free (string, FALSE); } if (dirs) { gint i; gint dirs_len = strlen (dirs); g_strdelimit (dirs, ",", '\0'); i = 0; while (i < dirs_len) { GList *p; MxIconType type; gchar *type_string; gint size, min, max, threshold; const gchar *dir = dirs + i; i += strlen (dir) + 1; size = g_key_file_get_integer (theme_file, dir, "Size", NULL); if (!size) { /* Try to get size from dir name */ size = atoi (dir); if (!size) continue; } type_string = g_key_file_get_string (theme_file, dir, "Type", NULL); type = MX_FIXED; min = max = threshold = 0; if (type_string) { if (g_str_equal (type_string, "Scalable")) { type = MX_SCALABLE; min = g_key_file_get_integer (theme_file, dir, "MinSize", NULL); if (!min) min = size; max = g_key_file_get_integer (theme_file, dir, "MaxSize", NULL); if (!max) max = size; } else if (g_str_equal (type_string, "Threshold")) { type = MX_THRESHOLD; threshold = g_key_file_get_integer (theme_file, dir, "Threshold", NULL); if (!threshold) threshold = 2; min = size - threshold; max = size + threshold; } g_free (type_string); } for (p = priv->search_paths; p; p = p->next) { gchar *file; MxIconData *icon_data = NULL; const gchar *search_path = p->data; gchar *path = g_build_filename (search_path, theme, dir, NULL); /* Try png first, then svg and xpm */ file = g_strconcat (path, G_DIR_SEPARATOR_S, icon, ".png", NULL); if (!g_file_test (file, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_REGULAR)) { g_free (file); file = g_strconcat (path, G_DIR_SEPARATOR_S, icon, ".svg", NULL); if (!g_file_test (file, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_REGULAR)) { g_free (file); file = g_strconcat (path, G_DIR_SEPARATOR_S, icon, ".xpm", NULL); if (!g_file_test (file, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_REGULAR)) { g_free (file); file = NULL; } } } if (file) { icon_data = mx_icon_theme_icon_data_new (size, file, type, min, max, threshold); g_free (file); data = g_list_prepend (data, icon_data); } g_free (path); } } g_free (dirs); } if (data || store_fail) { data = g_list_reverse (data); if (!store_icon) store_icon = g_themed_icon_new_with_default_fallbacks (icon); else store_icon = g_object_ref (store_icon); g_hash_table_insert (priv->icon_hash, store_icon, data); } return data; } static GList * mx_icon_theme_load_icon (MxIconTheme *theme, const gchar *icon_name, GIcon *store_icon) { GList *data, *f; MxIconThemePrivate *priv = theme->priv; /* Try the set theme */ if (priv->theme_file && (data = mx_icon_theme_theme_load_icon (theme, priv->theme_file, icon_name, store_icon, FALSE))) return data; /* Try the inherited themes */ for (f = priv->theme_fallbacks; f; f = f->next) { GKeyFile *theme_file = f->data; if ((data = mx_icon_theme_theme_load_icon (theme, theme_file, icon_name, store_icon, FALSE))) return data; } /* Try the hicolor theme */ if (priv->hicolor_file && (data = mx_icon_theme_theme_load_icon (theme, priv->hicolor_file, icon_name, store_icon, TRUE))) return data; return NULL; } static GList * mx_icon_theme_copy_data_list (GList *data) { GList *d, *new_data = g_list_copy (data); for (d = new_data; d; d = d->next) { MxIconData *icon_data; d->data = g_memdup (d->data, sizeof (MxIconData)); icon_data = d->data; icon_data->path = g_strdup (icon_data->path); } return new_data; } static GList * mx_icon_theme_get_icons (MxIconTheme *theme, const gchar *icon_name) { gint i; GIcon *icon; GList *data; const gchar * const *names = NULL; MxIconThemePrivate *priv = theme->priv; /* Load the icon, or a fallback */ icon = g_themed_icon_new_with_default_fallbacks (icon_name); names = g_themed_icon_get_names (G_THEMED_ICON (icon)); if (!names) { g_object_unref (icon); return NULL; } data = NULL; for (i = 0; names[i]; i++) { /* See if we've loaded this before */ GIcon *single_icon = g_themed_icon_new (names[i]); gboolean success = g_hash_table_lookup_extended (priv->icon_hash, single_icon, NULL, (gpointer *)&data); g_object_unref (single_icon); /* Found in cache on first hit, break */ if (success && (i == 0)) break; /* Found in cache after searching the disk, store again as a new icon */ if (success) { /* If we found this as a fallback, store it so we don't look on * disk again. */ if (data) data = mx_icon_theme_copy_data_list (data); g_hash_table_insert (priv->icon_hash, g_object_ref (icon), data); break; } /* Try to load from disk */ if ((data = mx_icon_theme_load_icon (theme, names[i], icon))) break; } g_object_unref (icon); return data; } static MxIconData * mx_icon_theme_lookup_internal (MxIconTheme *theme, const gchar *icon_name, gint size) { MxIconData *best_match; GList *d, *data; gint distance; data = mx_icon_theme_get_icons (theme, icon_name); if (!data) return NULL; best_match = NULL; distance = G_MAXINT; for (d = data; d; d = d->next) { gint current_distance; MxIconData *current = d->data; switch (current->type) { case MX_FIXED: current_distance = ABS (size - current->size); break; case MX_SCALABLE: case MX_THRESHOLD: if (size < current->min_size) current_distance = current->min_size - size; else if (size > current->max_size) current_distance = size - current->max_size; else current_distance = 0; break; default: g_warning ("Unknown icon type in cache"); current_distance = G_MAXINT - 1; break; } if (current_distance < distance) { distance = current_distance; best_match = current; } } if (!best_match) { g_warning ("No match found, but icon is in cache"); return NULL; } return best_match; } /** * mx_icon_theme_lookup: * @theme: an #MxIconTheme * @icon_name: The name of the icon * @size: The desired size of the icon * * If the icon is available, returns a #CoglHandle of the icon. * * Return value: (transfer none): a #CoglHandle of the icon, or %NULL. */ CoglHandle mx_icon_theme_lookup (MxIconTheme *theme, const gchar *icon_name, gint size) { MxTextureCache *texture_cache; MxIconData *icon_data; g_return_val_if_fail (MX_IS_ICON_THEME (theme), NULL); g_return_val_if_fail (icon_name, NULL); g_return_val_if_fail (size > 0, NULL); if (!(icon_data = mx_icon_theme_lookup_internal (theme, icon_name, size))) return NULL; texture_cache = mx_texture_cache_get_default (); return mx_texture_cache_get_cogl_texture (texture_cache, icon_data->path); } /** * mx_icon_theme_lookup_texture: * @theme: an #MxIconTheme * @icon_name: The name of the icon * @size: The desired size of the icon * * If the icon is available, returns a #ClutterTexture of the icon. * * Return value: (transfer none): a #ClutterTexture of the icon, or %NULL. */ ClutterTexture * mx_icon_theme_lookup_texture (MxIconTheme *theme, const gchar *icon_name, gint size) { MxTextureCache *texture_cache; MxIconData *icon_data; g_return_val_if_fail (MX_IS_ICON_THEME (theme), NULL); g_return_val_if_fail (icon_name, NULL); g_return_val_if_fail (size > 0, NULL); if (!(icon_data = mx_icon_theme_lookup_internal (theme, icon_name, size))) return NULL; texture_cache = mx_texture_cache_get_default (); return mx_texture_cache_get_texture (texture_cache, icon_data->path); } gboolean mx_icon_theme_has_icon (MxIconTheme *theme, const gchar *icon_name) { g_return_val_if_fail (MX_IS_ICON_THEME (theme), FALSE); g_return_val_if_fail (icon_name, FALSE); if (mx_icon_theme_get_icons (theme, icon_name)) return TRUE; else return FALSE; } /** * mx_icon_theme_get_search_paths: * @theme: a #MxIconTheme * * Gets the directories the #MxIconTheme will search in to find icons. * * Return value: (element-type utf8) (transfer none): the search paths */ const GList * mx_icon_theme_get_search_paths (MxIconTheme *theme) { g_return_val_if_fail (MX_IS_ICON_THEME (theme), NULL); return theme->priv->search_paths; } /** * mx_icon_theme_set_search_paths: * @theme: a #MxIconTheme * @paths: (element-type utf8): a list of search paths * * Sets the directories the #MxIconTheme will search in to find icons. * By default, it will look in the default system and local icon * directories. */ void mx_icon_theme_set_search_paths (MxIconTheme *theme, const GList *paths) { GList *p; MxIconThemePrivate *priv; g_return_if_fail (MX_IS_ICON_THEME (theme)); priv = theme->priv; while (priv->search_paths) { g_free (priv->search_paths->data); priv->search_paths = g_list_delete_link (priv->search_paths, priv->search_paths); } priv->search_paths = g_list_copy ((GList *)paths); for (p = priv->search_paths; p; p = p->next) p->data = g_strdup ((const gchar *)p->data); } mx-1.4.7/mx/mx-icon-theme.h000066400000000000000000000065031201047117600154210ustar00rootroot00000000000000/* * mx-icon-theme.h: A freedesktop icon theme object * * Copyright 2010 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * * Author: Chris Lord * */ #if !defined(MX_H_INSIDE) && !defined(MX_COMPILATION) #error "Only can be included directly.h" #endif #ifndef _MX_ICON_THEME_H #define _MX_ICON_THEME_H #include #include #include G_BEGIN_DECLS #define MX_TYPE_ICON_THEME mx_icon_theme_get_type() #define MX_ICON_THEME(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ MX_TYPE_ICON_THEME, MxIconTheme)) #define MX_ICON_THEME_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), \ MX_TYPE_ICON_THEME, MxIconThemeClass)) #define MX_IS_ICON_THEME(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \ MX_TYPE_ICON_THEME)) #define MX_IS_ICON_THEME_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE ((klass), \ MX_TYPE_ICON_THEME)) #define MX_ICON_THEME_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), \ MX_TYPE_ICON_THEME, MxIconThemeClass)) typedef struct _MxIconTheme MxIconTheme; typedef struct _MxIconThemeClass MxIconThemeClass; typedef struct _MxIconThemePrivate MxIconThemePrivate; /** * MxIconTheme: * * The contents of this structure are private and should only be accessed * through the public API. */ struct _MxIconTheme { GObject parent; MxIconThemePrivate *priv; }; struct _MxIconThemeClass { GObjectClass parent_class; /* padding for future expansion */ void (*_padding_0) (void); void (*_padding_1) (void); void (*_padding_2) (void); void (*_padding_3) (void); void (*_padding_4) (void); }; GType mx_icon_theme_get_type (void) G_GNUC_CONST; MxIconTheme *mx_icon_theme_new (void); MxIconTheme *mx_icon_theme_get_default (void); const gchar *mx_icon_theme_get_theme_name (MxIconTheme *theme); void mx_icon_theme_set_theme_name (MxIconTheme *theme, const gchar *theme_name); CoglHandle mx_icon_theme_lookup (MxIconTheme *theme, const gchar *icon_name, gint size); ClutterTexture *mx_icon_theme_lookup_texture (MxIconTheme *theme, const gchar *icon_name, gint size); gboolean mx_icon_theme_has_icon (MxIconTheme *theme, const gchar *icon_name); const GList *mx_icon_theme_get_search_paths (MxIconTheme *theme); void mx_icon_theme_set_search_paths (MxIconTheme *theme, const GList *paths); G_END_DECLS #endif /* _MX_ICON_THEME_H */ mx-1.4.7/mx/mx-icon.c000066400000000000000000000405201201047117600143110ustar00rootroot00000000000000/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * mx-icon.c: icon widget * * Copyright 2009, 2010 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * * Written by: Thomas Wood , * Chris Lord * */ /** * SECTION:mx-icon * @short_description: a simple styled icon actor * * #MxIcon is a simple styled texture actor that displays an image from * a stylesheet. */ #include "mx-icon.h" #include "mx-icon-theme.h" #include "mx-stylable.h" #include "mx-private.h" enum { PROP_0, PROP_ICON_NAME, PROP_ICON_SIZE }; static void mx_stylable_iface_init (MxStylableIface *iface); G_DEFINE_TYPE_WITH_CODE (MxIcon, mx_icon, MX_TYPE_WIDGET, G_IMPLEMENT_INTERFACE (MX_TYPE_STYLABLE, mx_stylable_iface_init)) #define MX_ICON_GET_PRIVATE(obj) \ (G_TYPE_INSTANCE_GET_PRIVATE ((obj), MX_TYPE_ICON, MxIconPrivate)) struct _MxIconPrivate { guint icon_set : 1; guint size_set : 1; guint is_content_image : 1; ClutterActor *icon_texture; gchar *icon_name; gchar *icon_suffix; gint icon_size; }; static void mx_icon_update (MxIcon *icon); static void mx_stylable_iface_init (MxStylableIface *iface) { static gboolean is_initialized = FALSE; if (G_UNLIKELY (!is_initialized)) { GParamSpec *pspec; is_initialized = TRUE; pspec = g_param_spec_boxed ("x-mx-content-image", "Content Image", "Image used as the button", MX_TYPE_BORDER_IMAGE, G_PARAM_READWRITE); mx_stylable_iface_install_property (iface, MX_TYPE_ICON, pspec); pspec = g_param_spec_string ("x-mx-icon-name", "Icon name", "Icon name to load from the theme", NULL, G_PARAM_READWRITE); mx_stylable_iface_install_property (iface, MX_TYPE_ICON, pspec); pspec = g_param_spec_string ("x-mx-icon-suffix", "Icon suffix", "Suffix to append to the icon name", NULL, G_PARAM_READWRITE); mx_stylable_iface_install_property (iface, MX_TYPE_ICON, pspec); pspec = g_param_spec_int ("x-mx-icon-size", "Icon size", "Size to use for icon", -1, G_MAXINT, -1, G_PARAM_READWRITE); mx_stylable_iface_install_property (iface, MX_TYPE_ICON, pspec); } } static void mx_icon_set_property (GObject *gobject, guint prop_id, const GValue *value, GParamSpec *pspec) { MxIcon *icon = MX_ICON (gobject); switch (prop_id) { case PROP_ICON_NAME: mx_icon_set_icon_name (icon, g_value_get_string (value)); break; case PROP_ICON_SIZE: mx_icon_set_icon_size (icon, g_value_get_int (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); break; } } static void mx_icon_get_property (GObject *gobject, guint prop_id, GValue *value, GParamSpec *pspec) { MxIcon *icon = MX_ICON (gobject); switch (prop_id) { case PROP_ICON_NAME: g_value_set_string (value, mx_icon_get_icon_name (icon)); break; case PROP_ICON_SIZE: g_value_set_int (value, mx_icon_get_icon_size (icon)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); break; } } static void mx_icon_notify_theme_name_cb (MxIconTheme *theme, GParamSpec *pspec, MxIcon *self) { mx_icon_update (self); } static void mx_icon_dispose (GObject *gobject) { MxIconPrivate *priv = MX_ICON (gobject)->priv; if (priv->icon_texture) { clutter_actor_destroy (priv->icon_texture); priv->icon_texture = NULL; } if (mx_icon_theme_get_default ()) { g_signal_handlers_disconnect_by_func (mx_icon_theme_get_default (), mx_icon_notify_theme_name_cb, gobject); } G_OBJECT_CLASS (mx_icon_parent_class)->dispose (gobject); } static void mx_icon_finalize (GObject *gobject) { MxIconPrivate *priv = MX_ICON (gobject)->priv; g_free (priv->icon_name); g_free (priv->icon_suffix); G_OBJECT_CLASS (mx_icon_parent_class)->finalize (gobject); } static void mx_icon_get_preferred_height (ClutterActor *actor, gfloat for_width, gfloat *min_height_p, gfloat *nat_height_p) { MxPadding padding; gfloat pref_height; MxIconPrivate *priv = MX_ICON (actor)->priv; if (priv->icon_texture) { gint width, height; clutter_texture_get_base_size (CLUTTER_TEXTURE (priv->icon_texture), &width, &height); if (!priv->is_content_image) { if (width <= height) pref_height = priv->icon_size; else pref_height = height / (gfloat)width * priv->icon_size; } else pref_height = height; } else pref_height = 0; mx_widget_get_padding (MX_WIDGET (actor), &padding); pref_height += padding.top + padding.bottom; if (min_height_p) *min_height_p = pref_height; if (nat_height_p) *nat_height_p = pref_height; } static void mx_icon_get_preferred_width (ClutterActor *actor, gfloat for_height, gfloat *min_width_p, gfloat *nat_width_p) { MxPadding padding; gfloat pref_width; MxIconPrivate *priv = MX_ICON (actor)->priv; if (priv->icon_texture) { gint width, height; clutter_texture_get_base_size (CLUTTER_TEXTURE (priv->icon_texture), &width, &height); if (!priv->is_content_image) { if (height <= width) pref_width = priv->icon_size; else pref_width = width / (gfloat)height * priv->icon_size; } else pref_width = width; } else pref_width = 0; mx_widget_get_padding (MX_WIDGET (actor), &padding); pref_width += padding.left + padding.right; if (min_width_p) *min_width_p = pref_width; if (nat_width_p) *nat_width_p = pref_width; } static void mx_icon_allocate (ClutterActor *actor, const ClutterActorBox *box, ClutterAllocationFlags flags) { MxIconPrivate *priv = MX_ICON (actor)->priv; CLUTTER_ACTOR_CLASS (mx_icon_parent_class)->allocate (actor, box, flags); if (priv->icon_texture) { MxPadding padding; ClutterActorBox child_box; mx_widget_get_padding (MX_WIDGET (actor), &padding); child_box.x1 = padding.left; child_box.y1 = padding.top; child_box.x2 = box->x2 - box->x1 - padding.right; child_box.y2 = box->y2 - box->y1 - padding.bottom; clutter_actor_allocate (priv->icon_texture, &child_box, flags); } } static void mx_icon_paint (ClutterActor *actor) { MxIconPrivate *priv = MX_ICON (actor)->priv; /* Chain up to paint background */ if (!priv->is_content_image) CLUTTER_ACTOR_CLASS (mx_icon_parent_class)->paint (actor); if (priv->icon_texture) clutter_actor_paint (priv->icon_texture); } static void mx_icon_map (ClutterActor *actor) { MxIconPrivate *priv = MX_ICON (actor)->priv; CLUTTER_ACTOR_CLASS (mx_icon_parent_class)->map (actor); if (priv->icon_texture) clutter_actor_map (priv->icon_texture); } static void mx_icon_unmap (ClutterActor *actor) { MxIconPrivate *priv = MX_ICON (actor)->priv; CLUTTER_ACTOR_CLASS (mx_icon_parent_class)->unmap (actor); if (priv->icon_texture) clutter_actor_unmap (priv->icon_texture); } static void mx_icon_class_init (MxIconClass *klass) { GParamSpec *pspec; GObjectClass *object_class = G_OBJECT_CLASS (klass); ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass); g_type_class_add_private (klass, sizeof (MxIconPrivate)); object_class->get_property = mx_icon_get_property; object_class->set_property = mx_icon_set_property; object_class->dispose = mx_icon_dispose; object_class->finalize = mx_icon_finalize; actor_class->get_preferred_height = mx_icon_get_preferred_height; actor_class->get_preferred_width = mx_icon_get_preferred_width; actor_class->allocate = mx_icon_allocate; actor_class->paint = mx_icon_paint; actor_class->map = mx_icon_map; actor_class->unmap = mx_icon_unmap; pspec = g_param_spec_string ("icon-name", "Icon name", "An icon name", NULL, MX_PARAM_READWRITE); g_object_class_install_property (object_class, PROP_ICON_NAME, pspec); pspec = g_param_spec_int ("icon-size", "Icon size", "Size of the icon", 1, G_MAXINT, 48, MX_PARAM_READWRITE); g_object_class_install_property (object_class, PROP_ICON_SIZE, pspec); } static void mx_icon_update (MxIcon *icon) { MxIconPrivate *priv = icon->priv; if (priv->is_content_image) { priv->is_content_image = FALSE; g_signal_connect (mx_icon_theme_get_default (), "notify::theme-name", G_CALLBACK (mx_icon_notify_theme_name_cb), icon); } /* Get rid of the old one */ if (priv->icon_texture) { clutter_actor_destroy (priv->icon_texture); priv->icon_texture = NULL; } /* Try to lookup the new one */ if (priv->icon_name) { gchar *icon_name; MxIconTheme *theme = mx_icon_theme_get_default (); icon_name = g_strconcat (priv->icon_name, priv->icon_suffix, NULL); priv->icon_texture = (ClutterActor *) mx_icon_theme_lookup_texture (theme, icon_name, priv->icon_size); g_free (icon_name); /* If the icon is missing, use the image-missing icon */ if (!priv->icon_texture) priv->icon_texture = (ClutterActor *) mx_icon_theme_lookup_texture (theme, "image-missing", priv->icon_size); if (priv->icon_texture) clutter_actor_set_parent (priv->icon_texture, CLUTTER_ACTOR (icon)); } clutter_actor_queue_relayout (CLUTTER_ACTOR (icon)); } static void mx_icon_style_changed_cb (MxWidget *widget) { MxIcon *self = MX_ICON (widget); MxIconPrivate *priv = self->priv; MxBorderImage *content_image = NULL; gboolean changed = FALSE; gchar *icon_name = NULL; gchar *icon_suffix = NULL; gint icon_size = -1; mx_stylable_get (MX_STYLABLE (widget), "x-mx-content-image", &content_image, "x-mx-icon-name", &icon_name, "x-mx-icon-size", &icon_size, "x-mx-icon-suffix", &icon_suffix, NULL); /* Content-image overrides drawing of the icon, so * don't bother reading those properties if it's set. */ if (content_image) { GError *error = NULL; priv->is_content_image = TRUE; g_signal_handlers_disconnect_by_func (mx_icon_theme_get_default (), mx_icon_notify_theme_name_cb, self); if (priv->icon_texture) { clutter_actor_destroy (priv->icon_texture); priv->icon_texture = NULL; } if (content_image->uri) priv->icon_texture = clutter_texture_new_from_file (content_image->uri, &error); if (priv->icon_texture) clutter_actor_set_parent (priv->icon_texture, CLUTTER_ACTOR (widget)); if (error) { g_warning ("Could not load content image: %s", error->message); g_error_free (error); } g_boxed_free (MX_TYPE_BORDER_IMAGE, content_image); g_free (icon_name); return; } if (icon_name && !priv->icon_set && (!priv->icon_name || !g_str_equal (icon_name, priv->icon_name))) { g_free (priv->icon_name); priv->icon_name = g_strdup (icon_name); changed = TRUE; g_object_notify (G_OBJECT (self), "icon-name"); } else if (!icon_name && !priv->icon_set && priv->icon_name) { /* icon has been unset */ g_free (priv->icon_name); priv->icon_name = NULL; priv->icon_set = FALSE; changed = TRUE; g_object_notify (G_OBJECT (self), "icon-name"); } if ((icon_size > 0) && !priv->size_set && (priv->icon_size != icon_size)) { priv->icon_size = icon_size; changed = TRUE; g_object_notify (G_OBJECT (self), "icon-size"); } if ((icon_suffix != priv->icon_suffix) && (!icon_suffix || !priv->icon_suffix || !g_str_equal (icon_suffix, priv->icon_suffix))) { g_free (priv->icon_suffix); priv->icon_suffix = icon_suffix; changed = TRUE; } else g_free (icon_suffix); if (changed) mx_icon_update (self); } static void mx_icon_init (MxIcon *self) { self->priv = MX_ICON_GET_PRIVATE (self); self->priv->icon_size = 48; g_signal_connect (self, "style-changed", G_CALLBACK (mx_icon_style_changed_cb), NULL); /* make sure we are not reactive */ clutter_actor_set_reactive (CLUTTER_ACTOR (self), FALSE); /* Reload the icon when the theme changes */ g_signal_connect (mx_icon_theme_get_default (), "notify::theme-name", G_CALLBACK (mx_icon_notify_theme_name_cb), self); } /** * mx_icon_new: * * Create a newly allocated #MxIcon * * Returns: A newly allocated #MxIcon */ ClutterActor * mx_icon_new (void) { return g_object_new (MX_TYPE_ICON, NULL); } const gchar * mx_icon_get_icon_name (MxIcon *icon) { g_return_val_if_fail (MX_IS_ICON (icon), NULL); return icon->priv->icon_name; } void mx_icon_set_icon_name (MxIcon *icon, const gchar *icon_name) { MxIconPrivate *priv; g_return_if_fail (MX_IS_ICON (icon)); priv = icon->priv; /* Unset the icon name if necessary */ if (!icon_name) { if (priv->icon_set) { priv->icon_set = FALSE; mx_stylable_style_changed (MX_STYLABLE (icon), MX_STYLE_CHANGED_NONE); } return; } priv->icon_set = TRUE; /* Check if there's no change */ if (priv->icon_name && g_str_equal (priv->icon_name, icon_name)) return; g_free (priv->icon_name); priv->icon_name = g_strdup (icon_name); mx_icon_update (icon); g_object_notify (G_OBJECT (icon), "icon-name"); } gint mx_icon_get_icon_size (MxIcon *icon) { g_return_val_if_fail (MX_IS_ICON (icon), -1); return icon->priv->icon_size; } void mx_icon_set_icon_size (MxIcon *icon, gint size) { MxIconPrivate *priv; g_return_if_fail (MX_IS_ICON (icon)); priv = icon->priv; if (size < 0) { if (priv->size_set) { priv->size_set = FALSE; mx_stylable_style_changed (MX_STYLABLE (icon), MX_STYLE_CHANGED_NONE); } return; } else if (priv->icon_size != size) { priv->icon_size = size; mx_icon_update (icon); g_object_notify (G_OBJECT (icon), "icon-size"); } priv->size_set = TRUE; } mx-1.4.7/mx/mx-icon.h000066400000000000000000000046211201047117600143200ustar00rootroot00000000000000/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * mx-icon.h: icon widget * * Copyright 2009, 2010 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * * Written by: Thomas Wood * */ #if !defined(MX_H_INSIDE) && !defined(MX_COMPILATION) #error "Only can be included directly.h" #endif #ifndef _MX_ICON #define _MX_ICON #include #include "mx-widget.h" G_BEGIN_DECLS #define MX_TYPE_ICON mx_icon_get_type() #define MX_ICON(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), MX_TYPE_ICON, MxIcon)) #define MX_ICON_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), MX_TYPE_ICON, MxIconClass)) #define MX_IS_ICON(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MX_TYPE_ICON)) #define MX_IS_ICON_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE ((klass), MX_TYPE_ICON)) #define MX_ICON_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), MX_TYPE_ICON, MxIconClass)) typedef struct _MxIconPrivate MxIconPrivate; /** * MxIcon: * * The contents of this structure are private and should only be accessed * through the public API. */ typedef struct { /*< private >*/ MxWidget parent; MxIconPrivate *priv; } MxIcon; typedef struct { MxWidgetClass parent_class; /* padding for future expansion */ void (*_padding_0) (void); void (*_padding_1) (void); void (*_padding_2) (void); void (*_padding_3) (void); void (*_padding_4) (void); } MxIconClass; GType mx_icon_get_type (void); ClutterActor* mx_icon_new (void); const gchar *mx_icon_get_icon_name (MxIcon *icon); void mx_icon_set_icon_name (MxIcon *icon, const gchar *icon_name); gint mx_icon_get_icon_size (MxIcon *icon); void mx_icon_set_icon_size (MxIcon *icon, gint size); G_END_DECLS #endif /* _MX_ICON */ mx-1.4.7/mx/mx-image.c000066400000000000000000001777041201047117600144620ustar00rootroot00000000000000/* * Copyright (C) 2008, 2009, 2010, 2011 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * */ /** * SECTION:mx-image * @short_description: A widget to display an image * * The #MxImage widget can load and display images. The image may be centered * or scaled to fit within the allocation. A transition effect occurs when a * new image is loaded. * * * Since: 1.2 */ #include #include #include "mx-image.h" #include "mx-enum-types.h" #include "mx-marshal.h" #include "mx-texture-cache.h" #include G_DEFINE_TYPE (MxImage, mx_image, MX_TYPE_WIDGET) #define MX_IMAGE_GET_PRIVATE(obj) \ (G_TYPE_INSTANCE_GET_PRIVATE ((obj), MX_TYPE_IMAGE, MxImagePrivate)) #define DEFAULT_DURATION 250 /* This stucture holds all that is necessary for cancellable async * image loading using thread pools. * * The idea is that you create this structure (with the pixbuf as NULL) * and add it to the thread-pool. * * The 'complete' member of the struct is protected by the mutex. * The thread handler uses this to indicate that the load was completed. * * The thread will take the mutex while it's loading data - if cancelled * is set when it takes the mutex, it will add the idle handler and let the * main thread free the data. * * The idle handler will check that the cancelled member isn't set and if not, * will try to upload the image using mx_image_set_from_pixbuf(). It will free * the async structure always. It will also reset the pointer to the task in * the MxImage priv struct, but only if the cancelled member *isn't* set. */ typedef struct { MxImage *parent; GMutex *mutex; guint complete : 1; guint cancelled : 1; guint upscale : 1; guint idle_handler; gchar *filename; guchar *buffer; gsize count; GDestroyNotify free_func; gint width; gint height; guint width_threshold; guint height_threshold; GdkPixbuf *pixbuf; GError *error; } MxImageAsyncData; struct _MxImagePrivate { MxImageScaleMode mode; MxImageScaleMode previous_mode; guint load_async : 1; guint upscale : 1; guint width_threshold; guint height_threshold; CoglHandle texture; CoglHandle old_texture; CoglHandle blank_texture; gint rotation; gint old_rotation; MxImageScaleMode old_mode; CoglMaterial *template_material; CoglMaterial *material; ClutterTimeline *timeline; ClutterTimeline *redraw_timeline; ClutterAlpha *redraw_alpha; guint transition_duration; MxImageAsyncData *async_load_data; }; enum { PROP_0, PROP_SCALE_MODE, PROP_LOAD_ASYNC, PROP_ALLOW_UPSCALE, PROP_SCALE_WIDTH_THRESHOLD, PROP_SCALE_HEIGHT_THRESHOLD, PROP_IMAGE_ROTATION, PROP_TRANSITION_DURATION }; enum { IMAGE_LOADED, IMAGE_LOAD_ERROR, LAST_SIGNAL }; static guint signals[LAST_SIGNAL] = { 0, }; static GThreadPool *mx_image_threads = NULL; static GQuark mx_image_cache_quark = 0; static gboolean mx_image_set_from_data_internal (MxImage *image, const guchar *data, const gchar *uri, gboolean use_cache, CoglPixelFormat pixel_format, gint width, gint height, gint rowstride, GError **error); GQuark mx_image_error_quark (void) { return g_quark_from_static_string ("mx-image-error-quark"); } static void mx_image_async_data_free (MxImageAsyncData *data) { g_mutex_free (data->mutex); if (data->free_func) data->free_func (data->buffer); g_free (data->filename); if (data->idle_handler) g_source_remove (data->idle_handler); if (data->pixbuf) g_object_unref (data->pixbuf); if (data->error) g_error_free (data->error); g_free (data); } static MxImageAsyncData * mx_image_async_data_new (MxImage *parent) { MxImageAsyncData *data = g_new0 (MxImageAsyncData, 1); data->parent = parent; data->mutex = g_mutex_new (); data->width = -1; data->height = -1; data->upscale = parent->priv->upscale; data->width_threshold = parent->priv->width_threshold; data->height_threshold = parent->priv->height_threshold; return data; } static void get_center_coords (CoglHandle tex, float rotation, float aw, float ah, float *tex_coords) { float bw, bh; bw = (float) cogl_texture_get_width (tex); /* base texture width */ bh = (float) cogl_texture_get_height (tex); /* base texture height */ tex_coords[0] = 0.5 - (aw / bw) / 2; tex_coords[1] = 0.5 - (ah / bh) / 2; tex_coords[2] = 0.5 + (aw / bw) / 2; tex_coords[3] = 0.5 + (ah / bh) / 2; } static gfloat calculate_scale (CoglHandle texture, float rotation, float aw, float ah, MxImageScaleMode mode) { float bw, bh, tmp, factor; if (mode == MX_IMAGE_SCALE_NONE) return 1.0; bw = cogl_texture_get_width (texture); /* base texture width */ bh = cogl_texture_get_height (texture); /* base texture height */ /* account for the 1px transparent border */ bw -= 2; bh -= 2; /* interpolate between scaling for width and height */ factor = (ABS (rotation) - ((int)(ABS (rotation) / 180) * 180.0)) / 90.0; if (factor > 1.0) factor = 2.0 - factor; tmp = bw + (bh - bw) * factor; bh = bh + (bw - bh) * factor; bw = tmp; if (bw/bh < aw/ah) { if (mode == MX_IMAGE_SCALE_CROP) return bw / aw; else return bh / ah; } else { if (mode == MX_IMAGE_SCALE_CROP) return bh / ah; else return bw / aw; } } static void mx_image_paint (ClutterActor *actor) { MxImagePrivate *priv = MX_IMAGE (actor)->priv; ClutterActorBox box; float aw, ah, bw, bh; guint8 alpha; float tex_coords[8]; MxPadding padding; CoglMatrix matrix; gfloat scale = 1; gfloat ratio; CoglColor color; /* chain up to draw the background */ CLUTTER_ACTOR_CLASS (mx_image_parent_class)->paint (actor); if (!priv->material) return; clutter_actor_get_allocation_box (actor, &box); aw = (float) (box.x2 - box.x1); /* allocation width */ ah = (float) (box.y2 - box.y1); /* allocation height */ /* allow for padding */ mx_widget_get_padding (MX_WIDGET (actor), &padding); aw -= (float) (padding.left + padding.right); ah -= (float) (padding.top + padding.bottom); bw = cogl_texture_get_width (priv->texture); /* base texture width */ bh = cogl_texture_get_height (priv->texture); /* base texture height */ ratio = bw/bh; alpha = clutter_actor_get_paint_opacity (actor); cogl_color_init_from_4ub (&color, alpha, alpha, alpha, alpha); if (priv->old_texture) { /* Paint opacity is applied using a constant on the third layer of the * material. This ensures the paint opacity is applied to both textures. */ cogl_material_set_layer_combine_constant (priv->material, 2, &color); } else { cogl_material_set_color (priv->material, &color); cogl_material_set_layer (priv->material, 0, priv->texture); } /* calculate texture co-ordinates */ get_center_coords (priv->texture, priv->rotation, aw, ah, tex_coords); /* current texture */ scale = calculate_scale (priv->texture, priv->rotation, aw, ah, priv->mode); if (clutter_timeline_is_playing (priv->redraw_timeline)) { gfloat progress, previous_scale; previous_scale = calculate_scale (priv->texture, priv->rotation, aw, ah, priv->previous_mode); progress = clutter_alpha_get_alpha (priv->redraw_alpha); scale = scale + (previous_scale - scale) * (1 - progress); } cogl_matrix_init_identity (&matrix); cogl_matrix_translate (&matrix, 0.5, 0.5, 0); cogl_matrix_scale (&matrix, 1, ratio, 1); cogl_matrix_rotate (&matrix, priv->rotation, 0, 0, -1); cogl_matrix_scale (&matrix, 1, 1 / ratio, 1); cogl_matrix_scale (&matrix, scale, scale, 1); cogl_matrix_translate (&matrix, -0.5, -0.5, 0); cogl_material_set_layer_matrix (priv->material, 0, &matrix); /* old texture */ if (priv->old_texture) { get_center_coords (priv->old_texture, priv->old_rotation, aw, ah, tex_coords + 4); scale = calculate_scale (priv->old_texture, priv->old_rotation, aw, ah, priv->old_mode); bw = cogl_texture_get_width (priv->old_texture); bh = cogl_texture_get_height (priv->old_texture); ratio = bw/bh; cogl_matrix_init_identity (&matrix); cogl_matrix_translate (&matrix, 0.5, 0.5, 0); cogl_matrix_scale (&matrix, 1, ratio, 1); cogl_matrix_rotate (&matrix, priv->old_rotation, 0, 0, -1); cogl_matrix_scale (&matrix, 1, 1 / ratio, 1); cogl_matrix_scale (&matrix, scale, scale, 1); cogl_matrix_translate (&matrix, -0.5, -0.5, 0); cogl_material_set_layer_matrix (priv->material, 1, &matrix); } cogl_set_source (priv->material); cogl_rectangle_with_multitexture_coords (padding.left, padding.top, padding.left + aw, padding.top + ah, tex_coords, 8); } static void mx_image_get_preferred_width (ClutterActor *actor, gfloat for_height, gfloat *min_width, gfloat *pref_width) { MxImagePrivate *priv = MX_IMAGE (actor)->priv; gfloat width; MxPadding padding; mx_widget_get_padding (MX_WIDGET (actor), &padding); width = cogl_texture_get_width (priv->texture); if (min_width) *min_width = 0; if (pref_width) *pref_width = width + padding.left + padding.right; } static void mx_image_get_preferred_height (ClutterActor *actor, gfloat for_width, gfloat *min_height, gfloat *pref_height) { MxImagePrivate *priv = MX_IMAGE (actor)->priv; gfloat height; MxPadding padding; mx_widget_get_padding (MX_WIDGET (actor), &padding); height = cogl_texture_get_height (priv->texture); if (min_height) *min_height = 0; if (pref_height) *pref_height = height + padding.top + padding.bottom; } static void mx_image_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { MxImage *image = MX_IMAGE (object); switch (prop_id) { case PROP_SCALE_MODE: mx_image_set_scale_mode (image, g_value_get_enum (value)); break; case PROP_LOAD_ASYNC: mx_image_set_load_async (image, g_value_get_boolean (value)); break; case PROP_ALLOW_UPSCALE: mx_image_set_allow_upscale (image, g_value_get_boolean (value)); break; case PROP_SCALE_WIDTH_THRESHOLD: mx_image_set_scale_width_threshold (image, g_value_get_uint (value)); break; case PROP_SCALE_HEIGHT_THRESHOLD: mx_image_set_scale_height_threshold (image, g_value_get_uint (value)); break; case PROP_IMAGE_ROTATION: mx_image_set_image_rotation (image, g_value_get_float (value)); break; case PROP_TRANSITION_DURATION: mx_image_set_transition_duration (image, g_value_get_uint (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void mx_image_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { MxImagePrivate *priv = MX_IMAGE (object)->priv; switch (prop_id) { case PROP_SCALE_MODE: g_value_set_enum (value, priv->mode); break; case PROP_LOAD_ASYNC: g_value_set_boolean (value, priv->load_async); break; case PROP_ALLOW_UPSCALE: g_value_set_boolean (value, priv->upscale); break; case PROP_SCALE_WIDTH_THRESHOLD: g_value_set_uint (value, priv->width_threshold); break; case PROP_SCALE_HEIGHT_THRESHOLD: g_value_set_uint (value, priv->height_threshold); break; case PROP_IMAGE_ROTATION: g_value_set_float (value, priv->rotation); break; case PROP_TRANSITION_DURATION: g_value_set_uint (value, priv->transition_duration); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void mx_image_dispose (GObject *object) { MxImagePrivate *priv = MX_IMAGE (object)->priv; if (priv->timeline) { clutter_timeline_stop (priv->timeline); g_object_unref (priv->timeline); priv->timeline = NULL; } if (priv->redraw_timeline) { clutter_timeline_stop (priv->redraw_timeline); g_object_unref (priv->redraw_timeline); priv->redraw_timeline = NULL; } if (priv->redraw_alpha) { g_object_unref (priv->redraw_alpha); priv->redraw_alpha = NULL; } if (priv->material) { cogl_object_unref (priv->material); priv->material = NULL; } if (priv->texture) { cogl_object_unref (priv->texture); priv->texture = NULL; } if (priv->old_texture) { cogl_object_unref (priv->old_texture); priv->old_texture = NULL; } if (priv->blank_texture) { cogl_object_unref (priv->blank_texture); priv->blank_texture = NULL; } if (priv->template_material) { cogl_object_unref (priv->template_material); priv->template_material = NULL; } if (priv->async_load_data) { priv->async_load_data->cancelled = TRUE; priv->async_load_data = NULL; } G_OBJECT_CLASS (mx_image_parent_class)->dispose (object); } static void mx_image_class_init (MxImageClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass); GParamSpec *pspec; g_type_class_add_private (klass, sizeof (MxImagePrivate)); object_class->dispose = mx_image_dispose; object_class->set_property = mx_image_set_property; object_class->get_property = mx_image_get_property; actor_class->paint = mx_image_paint; actor_class->get_preferred_width = mx_image_get_preferred_width; actor_class->get_preferred_height = mx_image_get_preferred_height; pspec = g_param_spec_enum ("scale-mode", "Scale Mode", "The scaling mode for the images", MX_TYPE_IMAGE_SCALE_MODE, MX_IMAGE_SCALE_NONE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_SCALE_MODE, pspec); pspec = g_param_spec_boolean ("load-async", "Load Asynchronously", "Whether to load images asynchronously", FALSE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_LOAD_ASYNC, pspec); pspec = g_param_spec_boolean ("allow-upscale", "Allow Upscale", "Allow images to be up-scaled", FALSE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_ALLOW_UPSCALE, pspec); pspec = g_param_spec_uint ("scale-width-threshold", "Scale Width Threshold", "Amount of pixels difference allowed between " "requested width and image width", 0, G_MAXUINT, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_SCALE_WIDTH_THRESHOLD, pspec); pspec = g_param_spec_uint ("scale-height-threshold", "Scale Height Threshold", "Amount of pixels difference allowed between " "requested height and image height", 0, G_MAXUINT, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_SCALE_HEIGHT_THRESHOLD, pspec); pspec = g_param_spec_float ("image-rotation", "Image Rotation", "Image rotation in degrees", 0, G_MAXINT, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_IMAGE_ROTATION, pspec); pspec = g_param_spec_uint ("transition-duration", "Transition duration", "Transition duration in ms", 0, G_MAXUINT, DEFAULT_DURATION, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_TRANSITION_DURATION, pspec); /** * MxImage::image-loaded: * @image: the #MxImage that emitted the signal * * Emitted when an asynchronous image load has completed successfully * * Since: 1.2 */ signals[IMAGE_LOADED] = g_signal_new ("image-loaded", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (MxImageClass, image_loaded), NULL, NULL, _mx_marshal_VOID__VOID, G_TYPE_NONE, 0); /** * MxImage::image-load-error: * @image: the #MxImage that emitted the signal * * Emitted when an asynchronous image load has encountered an error * and cannot load the requested image. * * Since: 1.2 */ signals[IMAGE_LOAD_ERROR] = g_signal_new ("image-load-error", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (MxImageClass, image_load_error), NULL, NULL, _mx_marshal_VOID__BOXED, G_TYPE_NONE, 1, G_TYPE_ERROR); mx_image_cache_quark = g_quark_from_static_string ("mx-image-cache"); } static void create_new_material (MxImage *image, gdouble progress) { MxImagePrivate *priv = image->priv; CoglHandle copy; CoglColor constant; if (!priv->old_texture) { if (priv->material) cogl_object_unref (priv->material); priv->material = cogl_material_new (); cogl_material_set_layer_wrap_mode (priv->material, 0, COGL_MATERIAL_WRAP_MODE_CLAMP_TO_EDGE); return; } /* You should assume that a material can only be modified once, after * its creation; if you need to modify it later you should use a copy * instead. Cogl makes copying materials reasonably cheap */ copy = cogl_material_copy (priv->material); /* Create the constant color to be used when combining the two * material layers; we use a black color with an alpha component * depending on the current progress of the timeline */ cogl_color_init_from_4ub (&constant, 0x00, 0x00, 0x00, 0xff * progress); /* This sets the value of the constant color we use when combining * the two layers */ cogl_material_set_layer_combine_constant (copy, 1, &constant); if (priv->material) cogl_object_unref (priv->material); priv->material = copy; } static void new_frame_cb (ClutterTimeline *timeline, guint elapsed_msecs, MxImage *image) { MxImagePrivate *priv = image->priv; gdouble progress; if (priv->material == COGL_INVALID_HANDLE) return; progress = clutter_timeline_get_progress (priv->timeline); create_new_material (image, progress); clutter_actor_queue_redraw (CLUTTER_ACTOR (image)); } static void timeline_complete (ClutterTimeline *timeline, MxImage *image) { if (image->priv->old_texture) { cogl_object_unref (image->priv->old_texture); image->priv->old_texture = NULL; } create_new_material (image, 1.0); } static void mx_image_init (MxImage *self) { MxImagePrivate *priv; guchar data[4] = { 0, 0, 0, 0 }; priv = self->priv = MX_IMAGE_GET_PRIVATE (self); priv->transition_duration = DEFAULT_DURATION; priv->timeline = clutter_timeline_new (priv->transition_duration); priv->redraw_timeline = clutter_timeline_new (200); priv->redraw_alpha = clutter_alpha_new_full (priv->redraw_timeline, CLUTTER_EASE_OUT_CUBIC); g_signal_connect (priv->timeline, "new-frame", G_CALLBACK (new_frame_cb), self); g_signal_connect (priv->timeline, "completed", G_CALLBACK (timeline_complete), self); g_signal_connect_swapped (priv->redraw_timeline, "new-frame", G_CALLBACK (clutter_actor_queue_redraw), self); priv->blank_texture = cogl_texture_new_from_data (1, 1, COGL_TEXTURE_NO_ATLAS, COGL_PIXEL_FORMAT_RGBA_8888, COGL_PIXEL_FORMAT_ANY, 1, data); /* set up the initial material */ priv->template_material = cogl_material_new (); cogl_material_set_layer (priv->template_material, 1, priv->blank_texture); cogl_material_set_layer (priv->template_material, 0, priv->blank_texture); cogl_material_set_layer_wrap_mode (priv->template_material, 0, COGL_MATERIAL_WRAP_MODE_CLAMP_TO_EDGE); cogl_material_set_layer_wrap_mode (priv->template_material, 1, COGL_MATERIAL_WRAP_MODE_CLAMP_TO_EDGE); /* override the default combination description in the first layer so that the * paint opacity is not applied to the texture */ cogl_material_set_layer_combine (priv->template_material, 0, "RGBA = REPLACE (TEXTURE)", NULL); /* Set the layer combination description for the second layer; the * default for Cogl is to simply multiply the layer with the * precendent one. In this case we interpolate the color for each * pixel between the pixel value of the previous layer and the * current one, using the alpha component of a constant color as * the interpolation factor. */ cogl_material_set_layer_combine (priv->template_material, 1, "RGBA = INTERPOLATE (PREVIOUS, " "TEXTURE, " "CONSTANT[A])", NULL); /* apply the paint opacity */ cogl_material_set_layer_combine (priv->template_material, 2, "RGBA = MODULATE (PREVIOUS, CONSTANT[A])", NULL); /* set the transparent texture to start from */ mx_image_clear (self); } /** * mx_image_new: * * Creates a new #MxImage object. * * Returns: A newly created #MxImage object * * Since: 1.2 */ ClutterActor* mx_image_new (void) { return g_object_new (MX_TYPE_IMAGE, NULL); } /** * mx_image_set_scale_mode: * @image: An #MxImage * @mode: The #MxImageScaleMode to set * * Set the scale mode on @MxImage * * Since: 1.2 */ void mx_image_set_scale_mode (MxImage *image, MxImageScaleMode mode) { if (image->priv->mode != mode) { image->priv->previous_mode = mode; image->priv->mode = mode; g_object_notify (G_OBJECT (image), "scale-mode"); } clutter_actor_queue_redraw (CLUTTER_ACTOR (image)); } /** * mx_image_animate_scale_mode: * @image: An #MxImage * @mode: a #ClutterAnimationMode * @duration: duration of the animation in milliseconds * @scale_mode: The #MxImageScaleMode to set * * Sets the value of #MxImage:scale-mode to @scale_mode and animates the * scale factor of the image between the previous value and the new value. * * Since: 1.2 */ void mx_image_animate_scale_mode (MxImage *image, gulong mode, guint duration, MxImageScaleMode scale_mode) { MxImagePrivate *priv = image->priv; if (priv->mode != mode) { priv->previous_mode = priv->mode; priv->mode = scale_mode; clutter_timeline_stop (priv->redraw_timeline); clutter_timeline_set_duration (priv->redraw_timeline, duration); clutter_alpha_set_mode (priv->redraw_alpha, mode); clutter_timeline_start (priv->redraw_timeline); g_object_notify (G_OBJECT (image), "scale-mode"); } } /** * mx_image_get_scale_mode: * @image: An #MxImage * * Get the current scale mode of @MxImage. * * Returns: The current MxImageScaleMode * * Since: 1.2 */ MxImageScaleMode mx_image_get_scale_mode (MxImage *image) { return image->priv->mode; } static void mx_image_prepare_texture (MxImage *image) { MxImagePrivate *priv = image->priv; /* Create a new Cogl material holding the two textures inside two * separate layers. */ if (priv->material) cogl_object_unref (priv->material); priv->material = cogl_material_copy (priv->template_material); /* set the new textures on the material */ cogl_material_set_layer (priv->material, 1, priv->old_texture); cogl_material_set_layer (priv->material, 0, priv->texture); /* start the cross fade animation. When not having a transition duration, * we directly jump forward a create the material corresponding to the end * of the transition animation */ clutter_timeline_stop (priv->timeline); if (priv->transition_duration) clutter_timeline_start (priv->timeline); else create_new_material (image, 1.0); /* the image has changed size, so update the preferred width/height */ clutter_actor_queue_relayout (CLUTTER_ACTOR (image)); } static void mx_image_cancel_in_progress (MxImage *image) { MxImagePrivate *priv = image->priv; /* Cancel any asynchronous image load */ if (priv->async_load_data) { priv->async_load_data->cancelled = TRUE; priv->async_load_data = NULL; } } /** * mx_image_clear: * @image: A #MxImage * * Clear the current image and set a blank, transparent image. * * Returns: static void * * Since: 1.2 */ void mx_image_clear (MxImage *image) { MxImagePrivate *priv = image->priv; mx_image_cancel_in_progress (image); if (priv->texture) cogl_object_unref (priv->texture); priv->texture = cogl_object_ref (priv->blank_texture); if (priv->old_texture) cogl_object_unref (priv->old_texture); priv->old_texture = cogl_object_ref (priv->blank_texture); priv->old_rotation = priv->rotation; priv->old_mode = priv->mode; if (priv->material) cogl_object_unref (priv->material); priv->material = cogl_object_ref (priv->template_material); /* the image has changed size, so update the preferred width/height */ clutter_actor_queue_relayout (CLUTTER_ACTOR (image)); } /* * mx_image_set_from_data_internal: * @image: An #MxImage * @data: Image data, or %NULL * @uri: A local file path / URI, or %NULL * @use_cache: Whether the texture cache should be used * @pixel_format: The #CoglPixelFormat of the buffer * @width: Width in pixels of image data. * @height: Height in pixels of image data * @rowstride: Distance in bytes between row starts. * @error: Return location for a #GError, or #NULL * * Set the image data from a buffer. In case of failure, #FALSE is returned * and @error is set. If @data is %NULL, the image will be loaded from the * cache. * * Returns: #TRUE if the image was successfully updated */ static gboolean mx_image_set_from_data_internal (MxImage *image, const guchar *data, const gchar *uri, gboolean use_cache, CoglPixelFormat pixel_format, gint width, gint height, gint rowstride, GError **error) { MxImagePrivate *priv; MxTextureCache *cache; CoglHandle old_texture; if (G_UNLIKELY (!MX_IS_IMAGE (image))) { if (error) g_set_error (error, MX_IMAGE_ERROR, MX_IMAGE_ERROR_INVALID_PARAMETER, "image parameter is not a MxImage"); return FALSE; } priv = image->priv; mx_image_cancel_in_progress (image); /* Store a pointer to the old texture */ old_texture = priv->texture; /* See if the texture's cached, otherwise create it */ cache = mx_texture_cache_get_default (); if (use_cache && uri && !data) { priv->texture = mx_texture_cache_get_meta_cogl_texture ( cache, uri, GINT_TO_POINTER (mx_image_cache_quark)); if (!priv->texture) { priv->texture = old_texture; if (error) g_set_error (error, MX_IMAGE_ERROR, MX_IMAGE_ERROR_INTERNAL, "Image '%s' not found in cache", uri); return FALSE; } } else { gint *blank_area; priv->texture = cogl_texture_new_with_size (width + 2, height + 2, COGL_TEXTURE_NO_ATLAS, COGL_PIXEL_FORMAT_ANY); if (!priv->texture) { priv->texture = old_texture; if (error) g_set_error (error, MX_IMAGE_ERROR, MX_IMAGE_ERROR_BAD_FORMAT, "Failed to create Cogl texture"); return FALSE; } /* Create the new texture */ cogl_texture_set_region (priv->texture, 0, 0, 1, 1, width, height, width, height, pixel_format, rowstride, data); /* Blit a transparent buffer around the texture */ blank_area = g_new0 (gint, MAX (width, height) + 2); cogl_texture_set_region (priv->texture, 0, 0, 0, 0, width, 1, width, 1, COGL_PIXEL_FORMAT_RGBA_8888, (width + 2) * 4, (const guint8 *)blank_area); cogl_texture_set_region (priv->texture, 0, 0, 0, height + 1, width + 2, 1, width + 2, 1, COGL_PIXEL_FORMAT_RGBA_8888, (width + 2) * 4, (const guint8 *)blank_area); cogl_texture_set_region (priv->texture, 0, 0, 0, 0, 1, height + 2, 1, height + 2, COGL_PIXEL_FORMAT_RGBA_8888, 4, (const guint8 *)blank_area); cogl_texture_set_region (priv->texture, 0, 0, width + 1, 0, 1, height + 2, 1, height + 2, COGL_PIXEL_FORMAT_RGBA_8888, 4, (const guint8 *)blank_area); g_free (blank_area); /* Insert the processed image into the cache, if we have a URI */ if (uri) { mx_texture_cache_insert_meta (cache, uri, GINT_TO_POINTER (mx_image_cache_quark), priv->texture, NULL); } } /* Replace the old texture */ if (priv->old_texture) cogl_object_unref (priv->old_texture); priv->old_texture = old_texture; priv->old_rotation = priv->rotation; priv->old_mode = priv->mode; mx_image_prepare_texture (image); return TRUE; } /** * mx_image_set_from_data: * @image: An #MxImage * @data: (array): Image data * @pixel_format: The #CoglPixelFormat of the buffer * @width: Width in pixels of image data. * @height: Height in pixels of image data * @rowstride: Distance in bytes between row starts. * @error: Return location for a #GError, or #NULL * * Set the image data from a buffer. In case of failure, #FALSE is returned * and @error is set. * * Returns: #TRUE if the image was successfully updated * * Since: 1.2 */ gboolean mx_image_set_from_data (MxImage *image, const guchar *data, CoglPixelFormat pixel_format, gint width, gint height, gint rowstride, GError **error) { if (G_UNLIKELY (!MX_IS_IMAGE (image))) { if (error) g_set_error (error, MX_IMAGE_ERROR, MX_IMAGE_ERROR_INVALID_PARAMETER, "image parameter is not a MxImage"); return FALSE; } return mx_image_set_from_data_internal (image, data, NULL, FALSE, pixel_format, width, height, rowstride, error); } /* * mx_image_set_from_pixbuf: * @image: A #MxImage * @pixbuf: A #GdkPixbuf, or %NULL * @filename: A path or URI to an image file, or %NULL * @error: A pointer to a #GError, or %NULL * * Sets the MxImage from a #GdkPixbuf, or from the cache if a filename is * given, no pixbuf is given and the filename has been previously cached. * * Returns: %TRUE on success, %FALSE otherwise. @error is set on failure */ static gboolean mx_image_set_from_pixbuf (MxImage *image, GdkPixbuf *pixbuf, const gchar *filename, GError **error) { gboolean has_alpha; MxTextureCache *cache; gint width, height, rowstride; if (G_UNLIKELY (!MX_IS_IMAGE (image))) { if (error) g_set_error (error, MX_IMAGE_ERROR, MX_IMAGE_ERROR_INVALID_PARAMETER, "image parameter is not a MxImage"); return FALSE; } cache = mx_texture_cache_get_default (); /* Check if we have valid input arguments */ if ((!pixbuf && !filename) || (!pixbuf && filename && !mx_texture_cache_contains_meta (cache, filename, GINT_TO_POINTER (mx_image_cache_quark)))) { if (error) { if (filename) g_set_error (error, MX_IMAGE_ERROR, MX_IMAGE_ERROR_INTERNAL, "Texture '%s' not found in cache", filename); else g_set_error (error, MX_IMAGE_ERROR, MX_IMAGE_ERROR_INTERNAL, "NULL pixbuf and filename"); } return FALSE; } if (pixbuf) { gint bps, channels; GdkColorspace color_space; width = gdk_pixbuf_get_width (pixbuf); height = gdk_pixbuf_get_height (pixbuf); has_alpha = gdk_pixbuf_get_has_alpha (pixbuf); rowstride = gdk_pixbuf_get_rowstride (pixbuf); bps = gdk_pixbuf_get_bits_per_sample (pixbuf); channels = gdk_pixbuf_get_n_channels (pixbuf); color_space = gdk_pixbuf_get_colorspace (pixbuf); if ((bps != 8) || (color_space != GDK_COLORSPACE_RGB) || !((has_alpha && channels == 4) || (!has_alpha && channels == 3))) { if (error) g_set_error (error, MX_IMAGE_ERROR, MX_IMAGE_ERROR_BAD_FORMAT, "Unsupported image formatting"); g_object_unref (pixbuf); return FALSE; } } else { /* Fill these variables with data so the compiler doesn't complain, * but they won't be accessed if the pixbuf is NULL. */ width = height = rowstride = -1; has_alpha = TRUE; } return mx_image_set_from_data_internal (image, pixbuf ? gdk_pixbuf_get_pixels (pixbuf) : NULL, filename, TRUE, has_alpha ? COGL_PIXEL_FORMAT_RGBA_8888 : COGL_PIXEL_FORMAT_RGB_888, width, height, rowstride, error); } static gboolean mx_image_load_complete_cb (gpointer task_data) { MxImageAsyncData *data = task_data; /* Lock/unlock mutex to make sure the thread is finished. This is necessary * as it's possible that this idle handler will run before the thread unlocks * the mutex, and freeing a locked mutex results in undefined behaviour * (well, it crashes on Linux with an assert in pthreads...) */ g_mutex_lock (data->mutex); g_mutex_unlock (data->mutex); /* Reset the idle handler id so we don't try to remove it when we free * the data later on. */ data->idle_handler = 0; /* Don't do anything with the image data if we've been cancelled already */ if (!data->cancelled && data->complete) { /* Reset the current async image load data pointer */ data->parent->priv->async_load_data = NULL; /* If we managed to load the pixbuf, set it now, otherwise forward the * error on to the user via a signal. */ if (data->pixbuf) { GError *error = NULL; gboolean resized = (data->width != -1 || data->height != -1); gboolean success = mx_image_set_from_pixbuf (data->parent, data->pixbuf, resized ? data->filename : NULL, &error); if (success) g_signal_emit (data->parent, signals[IMAGE_LOADED], 0); else { g_signal_emit (data->parent, signals[IMAGE_LOAD_ERROR], 0, error); g_error_free (error); } } else g_signal_emit (data->parent, signals[IMAGE_LOAD_ERROR], 0, data->error); } /* Free the async loading struct */ mx_image_async_data_free (data); return FALSE; } typedef struct { gint width; gint height; guint width_threshold; guint height_threshold; gboolean upscale; gboolean scaled; } MxImageSizeRequest; static void mx_image_size_prepared_cb (GdkPixbufLoader *loader, gint width, gint height, gpointer user_data) { gboolean fit_width; MxImageSizeRequest *constraints = user_data; if (constraints->width >= 0) { if (constraints->height >= 0) { gfloat aspect = constraints->width / (gfloat)constraints->height; gfloat aspect_orig = width / (gfloat)height; fit_width = (aspect_orig < aspect); } else fit_width = TRUE; } else if (constraints->height >= 0) fit_width = FALSE; else return; if (fit_width) { if (!constraints->upscale && (width < constraints->width)) return; if (ABS (width - constraints->width) < constraints->width_threshold) return; gdk_pixbuf_loader_set_size (loader, constraints->width, (constraints->width / (gfloat)width) * (gfloat)height); constraints->scaled = TRUE; } else { if (!constraints->upscale && (height < constraints->height)) return; if (ABS (height - constraints->height) < constraints->height_threshold) return; gdk_pixbuf_loader_set_size (loader, (constraints->height / (gfloat)height) * (gfloat)width, constraints->height); constraints->scaled = TRUE; } } /* * mx_image_pixbuf_new: * @filename: A local file path, or %NULL * @buffer: Encoded image data buffer, or %NULL * @count: The size of @buffer * @width: The scaled width, or -1 * @height: The scaled height, or -1 * @width_threshold: The delta allowed before actually scaling the width * @height_threshold: The delta allowed before actually scaling the height * @upscale: %TRUE if the image should be allowed to scale upwards, * %FALSE otherwise * @error: A pointer to a #GError * * Loads and scales a #GdkPixbuf using the given filename or data. * * Returns: A new #GdkPixbuf, or %NULL on failure (@error will be set) */ static GdkPixbuf * mx_image_pixbuf_new (const gchar *filename, guchar *buffer, gsize count, gint width, gint height, guint width_threshold, guint height_threshold, gboolean upscale, gboolean *scaled, GError **error) { GdkPixbuf *pixbuf; GdkPixbufLoader *loader; MxImageSizeRequest constraints; GError *err = NULL; loader = gdk_pixbuf_loader_new (); constraints.width = width; constraints.height = height; constraints.width_threshold = width_threshold; constraints.height_threshold = height_threshold; constraints.upscale = upscale; g_signal_connect (loader, "size-prepared", G_CALLBACK (mx_image_size_prepared_cb), &constraints); if (filename) { if (!g_file_get_contents (filename, (gchar **)&buffer, &count, &err)) { if (error) g_propagate_error (error, err); gdk_pixbuf_loader_close (loader, NULL); g_object_unref (loader); return NULL; } g_object_weak_ref (G_OBJECT (loader), (GWeakNotify)g_free, buffer); } if (!buffer) { gdk_pixbuf_loader_close (loader, NULL); g_object_unref (loader); return NULL; } if (!gdk_pixbuf_loader_write (loader, buffer, count, &err)) { if (error) g_propagate_error (error, err); gdk_pixbuf_loader_close (loader, NULL); g_object_unref (loader); return NULL; } /* Note, closing the pixbuf loader will make sure that size-prepared * will not be called beyond this point. */ if (!gdk_pixbuf_loader_close (loader, &err)) { if (error) g_propagate_error (error, err); g_object_unref (loader); return NULL; } pixbuf = g_object_ref (gdk_pixbuf_loader_get_pixbuf (loader)); g_object_unref (loader); if (scaled) *scaled = constraints.scaled; return pixbuf; } static void mx_image_async_cb (gpointer task_data, gpointer user_data) { gboolean scaled; MxImageAsyncData *data = task_data; g_mutex_lock (data->mutex); /* Check if the task has been cancelled and bail out - leave to the main * thread to free the data. */ if (data->cancelled) { data->idle_handler = clutter_threads_add_idle_full (G_PRIORITY_HIGH_IDLE, mx_image_load_complete_cb, data, NULL); g_mutex_unlock (data->mutex); return; } /* Try to load the pixbuf */ data->pixbuf = mx_image_pixbuf_new (data->filename, data->buffer, data->count, data->width, data->height, data->width_threshold, data->height_threshold, data->upscale, &scaled, &data->error); /* If scaling was unnecessary, we can cache the result */ if (!scaled) { data->width = -1; data->height = -1; } data->complete = TRUE; data->idle_handler = clutter_threads_add_idle_full (G_PRIORITY_HIGH_IDLE, mx_image_load_complete_cb, data, NULL); g_mutex_unlock (data->mutex); } static gboolean mx_image_set_async (MxImage *image, const gchar *filename, guchar *buffer, gsize count, GDestroyNotify free_func, gint width, gint height, GError **error) { GError *err; MxImagePrivate *priv; MxImageAsyncData *data; if (G_UNLIKELY (!MX_IS_IMAGE (image))) { if (error) g_set_error (error, MX_IMAGE_ERROR, MX_IMAGE_ERROR_INVALID_PARAMETER, "image parameter is not a MxImage"); return FALSE; } priv = image->priv; /* This function should not be called if async loading isn't enabled */ if (!priv->load_async) { g_set_error (error, MX_IMAGE_ERROR, MX_IMAGE_ERROR_NO_ASYNC, "Asynchronous image loading is not enabled"); return FALSE; } err = NULL; data = NULL; /* Load the pixbuf in a thread, then later on upload it to the GPU */ if (!mx_image_threads) { mx_image_threads = g_thread_pool_new (mx_image_async_cb, NULL, #ifdef _SC_NPROCESSORS_ONLN sysconf (_SC_NPROCESSORS_ONLN), #else /* FIXME: add more OSs */ 1, #endif FALSE, &err); if (!mx_image_threads) { g_propagate_error (error, err); return FALSE; } } /* Cancel/free any in-progress load */ if (priv->async_load_data) { MxImageAsyncData *old_data = priv->async_load_data; if (!g_mutex_trylock (old_data->mutex)) { /* The thread is busy, cancel it and start a new one */ old_data->cancelled = TRUE; } else { if (old_data->complete) { /* The load finished, cancel the upload */ old_data->cancelled = TRUE; g_mutex_unlock (old_data->mutex); } else { /* The load hasn't begun, we'll hijack it */ g_free (old_data->filename); old_data->filename = g_strdup (filename); old_data->buffer = buffer; old_data->count = count; old_data->free_func = free_func; old_data->width = width; old_data->height = height; old_data->cancelled = FALSE; g_mutex_unlock (old_data->mutex); data = old_data; } } } if (!data) { /* Create the async load data and add it to the thread-pool */ priv->async_load_data = data = mx_image_async_data_new (image); data->filename = g_strdup (filename); data->buffer = buffer; data->count = count; data->free_func = free_func; data->width = width; data->height = height; g_thread_pool_push (mx_image_threads, data, NULL); } return TRUE; } /** * mx_image_set_from_file: * @image: An #MxImage * @filename: Filename to read the file from * @error: Return location for a #GError, or #NULL * * Set the image data from an image file. In case of failure, #FALSE is returned * and @error is set. * * Returns: #TRUE if the image was successfully updated * * Since: 1.2 */ gboolean mx_image_set_from_file (MxImage *image, const gchar *filename, GError **error) { return mx_image_set_from_file_at_size (image, filename, -1, -1, error); } /** * mx_image_set_from_file_at_size: * @image: An #MxImage * @filename: Filename to read the file from * @width: Width to scale the image to, or -1 * @height: Height to scale the image to, or -1 * @error: Return location for a #GError, or #NULL * * Set the image data from an image file, and scale the image during loading. * In case of failure, #FALSE is returned and @error is set. The aspect ratio * will always be maintained. * * Returns: #TRUE if the image was successfully updated * * Since: 1.2 */ gboolean mx_image_set_from_file_at_size (MxImage *image, const gchar *filename, gint width, gint height, GError **error) { GdkPixbuf *pixbuf; MxImagePrivate *priv; MxTextureCache *cache; gboolean retval, use_cache; if (G_UNLIKELY (!MX_IS_IMAGE (image))) { if (error) g_set_error (error, MX_IMAGE_ERROR, MX_IMAGE_ERROR_INVALID_PARAMETER, "image parameter is not a MxImage"); return FALSE; } priv = image->priv; pixbuf = NULL; /* Check if the processed image is in the cache - we don't use the cache * if we're loading at a particular size. */ cache = mx_texture_cache_get_default (); use_cache = TRUE; if ((width != -1) || (height != -1) || !mx_texture_cache_contains_meta (cache, filename, GINT_TO_POINTER (mx_image_cache_quark))) { /* Check if the unprocessed image is in the cache, and if so, skip * loading it and set it from the Cogl texture handle. */ if ((width == -1) && (height == -1) && mx_texture_cache_contains (cache, filename)) { if (mx_image_set_from_cogl_texture (image, mx_texture_cache_get_cogl_texture (cache, filename))) { /* Add the processed image to the cache */ mx_texture_cache_insert_meta (cache, filename, GINT_TO_POINTER (mx_image_cache_quark), priv->texture, NULL); return TRUE; } else { if (error) g_set_error (error, MX_IMAGE_ERROR, MX_IMAGE_ERROR_INTERNAL, "Setting image '%s' from CoglTexture failed", filename); return FALSE; } } /* Load the pixbuf in a thread, then later on upload it to the GPU */ if (priv->load_async) return mx_image_set_async (image, filename, NULL, 0, NULL, width, height, error); /* Synchronously load the pixbuf and set it */ pixbuf = mx_image_pixbuf_new (filename, NULL, 0, width, height, priv->width_threshold, priv->height_threshold, priv->upscale, &use_cache, error); if (!pixbuf) return FALSE; } retval = mx_image_set_from_pixbuf (image, pixbuf, use_cache ? filename : NULL, error); if (pixbuf) g_object_unref (pixbuf); return retval; } /** * mx_image_set_from_cogl_texture: * @image: A #MxImage * @texture: A #CoglHandle to a texture * * Sets the contents of the image from the given Cogl texture. * * Returns: %TRUE on success, %FALSE on failure * * Since: 1.2 */ gboolean mx_image_set_from_cogl_texture (MxImage *image, CoglHandle texture) { gint width, height; MxImagePrivate *priv; g_return_val_if_fail (MX_IS_IMAGE (image), FALSE); g_return_val_if_fail (cogl_is_texture (texture), FALSE); mx_image_cancel_in_progress (image); priv = image->priv; width = cogl_texture_get_width (texture); height = cogl_texture_get_height (texture); /* If we have offscreen buffers, use those to add the 1-pixel border * around the image on the GPU - if not, fallback to copying the image * data into memory and use set_from_data. */ if (clutter_feature_available (CLUTTER_FEATURE_OFFSCREEN)) { CoglColor transparent; CoglMaterial *clear_material; CoglHandle new_texture = cogl_texture_new_with_size (width + 2, height + 2, COGL_TEXTURE_NO_ATLAS, COGL_PIXEL_FORMAT_RGBA_8888); CoglHandle fbo = cogl_offscreen_new_to_texture (new_texture); CoglMaterial *tex_material = cogl_material_new (); /* Set the blending equation to directly copy the bits of the old * texture without blending the destination pixels. */ cogl_material_set_blend (tex_material, "RGBA=ADD(SRC_COLOR, 0)", NULL); clear_material = cogl_material_copy (tex_material); cogl_color_set_from_4ub (&transparent, 0, 0, 0, 0); cogl_material_set_layer (tex_material, 0, texture); /* Push the off-screen buffer and setup an orthographic projection */ cogl_push_framebuffer (fbo); cogl_ortho (0, width + 2, height +2, 0, -1, 1); /* Draw the texture into the middle */ cogl_push_source (tex_material); cogl_rectangle (1, 1, width +1, height + 1); /* Clear the 1-pixel border around the texture */ cogl_set_source (clear_material); cogl_rectangle (0, 0, width + 2, 1); cogl_rectangle (0, height + 1, width + 2, height + 2); cogl_rectangle (0, 1, 1, height + 1); cogl_rectangle (width + 1, 1, width + 2, height + 1); cogl_pop_source (); cogl_pop_framebuffer (); /* Free unneeded data */ cogl_object_unref (clear_material); cogl_object_unref (tex_material); cogl_handle_unref (fbo); /* Replace the old texture */ if (priv->old_texture) cogl_object_unref (priv->old_texture); priv->old_texture = priv->texture; priv->old_rotation = priv->rotation; priv->old_mode = priv->mode; priv->texture = new_texture; mx_image_prepare_texture (image); return TRUE; } else { guint8 *data; gint rowstride; CoglPixelFormat format; rowstride = cogl_texture_get_rowstride (texture); format = cogl_texture_get_format (texture); data = g_malloc (height * rowstride); cogl_texture_get_data (texture, format, rowstride, data); return mx_image_set_from_data (image, data, format, width, height, rowstride, NULL); } } /** * mx_image_set_from_buffer: * @image: An #MxImage * @buffer: (array length=buffer_size) (transfer full): A buffer * pointing to encoded image data * @buffer_size: The size of @buffer, in bytes * @buffer_free_func: (allow-none): A function to free @buffer, or %NULL * @error: Return location for a #GError, or #NULL * * Set the image data from unencoded image data, stored in memory. In case of * failure, #FALSE is returned and @error is set. It is expected that @buffer * will remain accessible for the duration of the load. Once it is finished * with, @buffer_free_func will be called. * * Returns: #TRUE if the image was successfully updated * * Since: 1.2 */ gboolean mx_image_set_from_buffer (MxImage *image, guchar *buffer, gsize buffer_size, GDestroyNotify buffer_free_func, GError **error) { return mx_image_set_from_buffer_at_size (image, buffer, buffer_size, buffer_free_func, -1, -1, error); } /** * mx_image_set_from_buffer_at_size: * @image: An #MxImage * @buffer: (array length=buffer_size) (transfer full): A buffer * pointing to encoded image data * @buffer_size: The size of @buffer, in bytes * @buffer_free_func: (allow-none): A function to free @buffer, or %NULL * @width: Width to scale the image to, or -1 * @height: Height to scale the image to, or -1 * @error: Return location for a #GError, or #NULL * * Set the image data from unencoded image data, stored in memory, and scales * it while loading. In case of failure, #FALSE is returned and @error is set. * It is expected that @buffer will remain accessible for the duration of the * load. Once it is finished with, @buffer_free_func will be called. The aspect * ratio will always be maintained. * * Returns: #TRUE if the image was successfully updated * * Since: 1.2 */ gboolean mx_image_set_from_buffer_at_size (MxImage *image, guchar *buffer, gsize buffer_size, GDestroyNotify buffer_free_func, gint width, gint height, GError **error) { gboolean retval; GdkPixbuf *pixbuf; MxImagePrivate *priv; if (G_UNLIKELY (!MX_IS_IMAGE (image))) { if (error) g_set_error (error, MX_IMAGE_ERROR, MX_IMAGE_ERROR_INVALID_PARAMETER, "image parameter is not a MxImage"); return FALSE; } priv = image->priv; if (priv->load_async) return mx_image_set_async (image, NULL, buffer, buffer_size, buffer_free_func, width, height, error); pixbuf = mx_image_pixbuf_new (NULL, buffer, buffer_size, width, height, priv->width_threshold, priv->height_threshold, priv->upscale, NULL, error); if (!pixbuf) return FALSE; retval = mx_image_set_from_pixbuf (image, pixbuf, NULL, error); g_object_unref (pixbuf); if (buffer_free_func) buffer_free_func ((gpointer)buffer); return retval; } /** * mx_image_set_load_async: * @image: A #MxImage * @load_async: %TRUE to load images asynchronously * * Sets whether to load images asynchronously. Asynchronous image loading * requires thread support (see g_thread_init()). * * When using asynchronous image loading, all image-loading functions will * return immediately as successful. The #MxImage::image-loaded and * #MxImage::image-load-error signals are used to signal success or failure * of asynchronous image loading. * * Since: 1.2 */ void mx_image_set_load_async (MxImage *image, gboolean load_async) { MxImagePrivate *priv; g_return_if_fail (MX_IS_IMAGE (image)); priv = image->priv; if (priv->load_async != load_async) { priv->load_async = load_async; g_object_notify (G_OBJECT (image), "load-async"); /* Cancel the old transfer if we're turning async off */ if (!load_async && priv->async_load_data) { priv->async_load_data->cancelled = TRUE; priv->async_load_data = NULL; } } } /** * mx_image_get_load_async: * @image: A #MxImage * * Determines whether asynchronous image loading is in use. * * Returns: %TRUE if images are set to load asynchronously, %FALSE otherwise * * Since: 1.2 */ gboolean mx_image_get_load_async (MxImage *image) { g_return_val_if_fail (MX_IS_IMAGE (image), FALSE); return image->priv->load_async; } /** * mx_image_set_allow_upscale: * @image: A #MxImage * @allow: %TRUE to allow upscaling, %FALSE otherwise * * Sets whether up-scaling of images is allowed. If set to %TRUE and a size * larger than the image is requested, the image will be up-scaled in * software. * * The advantage of this is that software up-scaling is potentially higher * quality, but it comes at the expense of video memory. * * Since: 1.2 */ void mx_image_set_allow_upscale (MxImage *image, gboolean allow) { MxImagePrivate *priv; g_return_if_fail (MX_IS_IMAGE (image)); priv = image->priv; if (priv->upscale != allow) { priv->upscale = allow; g_object_notify (G_OBJECT (image), "allow-upscale"); } } /** * mx_image_get_allow_upscale: * @image: A #MxImage * * Determines whether image up-scaling is allowed. * * Returns: %TRUE if upscaling is allowed, %FALSE otherwise * * Since: 1.2 */ gboolean mx_image_get_allow_upscale (MxImage *image) { g_return_val_if_fail (MX_IS_IMAGE (image), FALSE); return image->priv->upscale; } /** * mx_image_set_scale_width_threshold: * @image: A #MxImage * @pixels: Number of pixels * * Sets the threshold used to determine whether to scale the width of the * image. If a specific width is requested, the image width is allowed to * differ by this amount before scaling is employed. * * This can be useful to avoid excessive CPU usage when the image differs * only slightly to the desired size. * * Since: 1.2 */ void mx_image_set_scale_width_threshold (MxImage *image, guint pixels) { MxImagePrivate *priv; g_return_if_fail (MX_IS_IMAGE (image)); priv = image->priv; if (priv->width_threshold != pixels) { priv->width_threshold = pixels; g_object_notify (G_OBJECT (image), "scale-width-threshold"); } } /** * mx_image_get_scale_width_threshold: * @image: A #MxImage * * Retrieves the width scaling threshold. * * Returns: The width scaling threshold, in pixels * * Since: 1.2 */ guint mx_image_get_scale_width_threshold (MxImage *image) { g_return_val_if_fail (MX_IS_IMAGE (image), 0); return image->priv->width_threshold; } /** * mx_image_set_scale_height_threshold: * @image: A #MxImage * @pixels: Number of pixels * * Sets the threshold used to determine whether to scale the height of the * image. If a specific height is requested, the image height is allowed to * differ by this amount before scaling is employed. * * This can be useful to avoid excessive CPU usage when the image differs * only slightly to the desired size. * * Since: 1.2 */ void mx_image_set_scale_height_threshold (MxImage *image, guint pixels) { MxImagePrivate *priv; g_return_if_fail (MX_IS_IMAGE (image)); priv = image->priv; if (priv->height_threshold != pixels) { priv->height_threshold = pixels; g_object_notify (G_OBJECT (image), "scale-height-threshold"); } } /** * mx_image_get_scale_height_threshold: * @image: A #MxImage * * Retrieves the height scaling threshold. * * Returns: The height scaling threshold, in pixels * * Since: 1.2 */ guint mx_image_get_scale_height_threshold (MxImage *image) { g_return_val_if_fail (MX_IS_IMAGE (image), 0); return image->priv->height_threshold; } /** * mx_image_set_image_rotation: * @image: A #MxImage * @rotation: Rotation angle in degrees * * Set the MxImage:image-rotation property. * * Since: 1.2 */ void mx_image_set_image_rotation (MxImage *image, gfloat rotation) { g_return_if_fail (MX_IS_IMAGE (image)); if (image->priv->rotation != rotation) { image->priv->rotation = rotation; clutter_actor_queue_redraw (CLUTTER_ACTOR (image)); g_object_notify (G_OBJECT (image), "image-rotation"); } } /** * mx_image_get_image_rotation: * @image: A #MxImage * * Get the value of the MxImage:image-rotation property. * * Returns: The value of the image-rotation property. * * Since: 1.2 */ gfloat mx_image_get_image_rotation (MxImage *image) { g_return_val_if_fail (MX_IS_IMAGE (image), 0); return image->priv->rotation; } /** * mx_image_set_transition_duration: * @image: A #MxImage * @duration: Transition duration in milliseconds * * Set the MxImage:transition-duration property. * * Since: 1.2 */ void mx_image_set_transition_duration (MxImage *image, guint duration) { g_return_if_fail (MX_IS_IMAGE (image)); if (image->priv->transition_duration != duration) { image->priv->transition_duration = duration; if (duration != 0) clutter_timeline_set_duration (image->priv->timeline, duration); g_object_notify (G_OBJECT (image), "transition-duration"); } } /** * mx_image_get_transition_duration: * @image: A #MxImage * * Get the value of the MxImage:transition-duration property. * * Returns: The value of the transition-duration property. * * Since: 1.2 */ guint mx_image_get_transition_duration (MxImage *image) { g_return_val_if_fail (MX_IS_IMAGE (image), 0); return image->priv->transition_duration; } mx-1.4.7/mx/mx-image.h000066400000000000000000000130051201047117600144460ustar00rootroot00000000000000/* * Copyright (C) 2008, 2009, 2010, 2011 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * */ #ifndef _MX_IMAGE #define _MX_IMAGE #include #include "mx-widget.h" G_BEGIN_DECLS #define MX_TYPE_IMAGE mx_image_get_type() #define MX_IMAGE(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), MX_TYPE_IMAGE, MxImage)) #define MX_IMAGE_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), MX_TYPE_IMAGE, MxImageClass)) #define MX_IS_IMAGE(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MX_TYPE_IMAGE)) #define MX_IS_IMAGE_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE ((klass), MX_TYPE_IMAGE)) #define MX_IMAGE_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), MX_TYPE_IMAGE, MxImageClass)) typedef enum { MX_IMAGE_ERROR_BAD_FORMAT, MX_IMAGE_ERROR_NO_ASYNC, MX_IMAGE_ERROR_INTERNAL, MX_IMAGE_ERROR_INVALID_PARAMETER } MxImageError; #define MX_IMAGE_ERROR (mx_image_error_quark ()) GQuark mx_image_error_quark (void); typedef struct _MxImage MxImage; typedef struct _MxImageClass MxImageClass; typedef struct _MxImagePrivate MxImagePrivate; /** * MxImage: * * The contents of this structure are private and should only be accessed * through the public API. */ struct _MxImage { /*< private >*/ MxWidget parent; MxImagePrivate *priv; }; struct _MxImageClass { /*< private >*/ MxWidgetClass parent_class; /* signals, not vfuncs */ void (* image_loaded) (MxImage *image); void (* image_load_error) (MxImage *image, GError *error); }; GType mx_image_get_type (void); ClutterActor * mx_image_new (void); gboolean mx_image_set_from_data (MxImage *image, const guchar *data, CoglPixelFormat pixel_format, gint width, gint height, gint rowstride, GError **error); gboolean mx_image_set_from_file (MxImage *image, const gchar *filename, GError **error); gboolean mx_image_set_from_file_at_size (MxImage *image, const gchar *filename, gint width, gint height, GError **error); gboolean mx_image_set_from_cogl_texture (MxImage *image, CoglHandle texture); gboolean mx_image_set_from_buffer (MxImage *image, guchar *buffer, gsize buffer_size, GDestroyNotify buffer_free_func, GError **error); gboolean mx_image_set_from_buffer_at_size (MxImage *image, guchar *buffer, gsize buffer_size, GDestroyNotify buffer_free_func, gint width, gint height, GError **error); void mx_image_clear (MxImage *image); void mx_image_set_scale_mode (MxImage *image, MxImageScaleMode mode); MxImageScaleMode mx_image_get_scale_mode (MxImage *image); void mx_image_set_image_rotation (MxImage *image, gfloat rotation); gfloat mx_image_get_image_rotation (MxImage *image); void mx_image_set_load_async (MxImage *image, gboolean load_async); gboolean mx_image_get_load_async (MxImage *image); void mx_image_set_allow_upscale (MxImage *image, gboolean allow); gboolean mx_image_get_allow_upscale (MxImage *image); void mx_image_set_scale_width_threshold (MxImage *image, guint pixels); guint mx_image_get_scale_width_threshold (MxImage *image); void mx_image_set_scale_height_threshold (MxImage *image, guint pixels); guint mx_image_get_scale_height_threshold (MxImage *image); void mx_image_set_transition_duration (MxImage *image, guint duration); guint mx_image_get_transition_duration (MxImage *image); void mx_image_animate_scale_mode (MxImage *image, gulong mode, guint duration, MxImageScaleMode scale_mode); G_END_DECLS #endif /* _MX_IMAGE */ mx-1.4.7/mx/mx-item-factory.c000066400000000000000000000033621201047117600157670ustar00rootroot00000000000000/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * mx-item-factory.c: an item factory interface * * Copyright 2009 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * * Written by: Iain Holmes * */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "mx-item-factory.h" static void mx_item_factory_base_init (gpointer base) { static gboolean initialized = FALSE; if (initialized) { return; } initialized = TRUE; } GType mx_item_factory_get_type (void) { static GType type = 0; if (!type) { const GTypeInfo info = { sizeof (MxItemFactoryIface), mx_item_factory_base_init, NULL, }; type = g_type_register_static (G_TYPE_INTERFACE, "MxItemFactory", &info, 0); } return type; } /** * mx_item_factory_create: * @factory: A #MxItemFactory * * Create an item * * Returns: (transfer full): the new item */ ClutterActor * mx_item_factory_create (MxItemFactory *factory) { return MX_ITEM_FACTORY_GET_IFACE (factory)->create (factory); } mx-1.4.7/mx/mx-item-factory.h000066400000000000000000000045031201047117600157720ustar00rootroot00000000000000/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * mx-item-factory.h: An item factory interface * * Copyright 2009 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * * Written by: Iain Holmes * */ #if !defined(MX_H_INSIDE) && !defined(MX_COMPILATION) #error "Only can be included directly." #endif #ifndef __MX_ITEM_FACTORY_H__ #define __MX_ITEM_FACTORY_H__ #include #include #define MX_TYPE_ITEM_FACTORY (mx_item_factory_get_type ()) #define MX_ITEM_FACTORY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MX_TYPE_ITEM_FACTORY, MxItemFactory)) #define MX_IS_ITEM_FACTORY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MX_TYPE_ITEM_FACTORY)) #define MX_ITEM_FACTORY_GET_IFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), MX_TYPE_ITEM_FACTORY, MxItemFactoryIface)) /** * MxItemFactory: * * This is an opaque structure whose members cannot be directly accessed. */ typedef struct _MxItemFactory MxItemFactory; /* dummy typedef */ typedef struct _MxItemFactoryIface MxItemFactoryIface; /** * MxItemFactoryIface: * @create: virtual function called when creating a new item * * Interface for creating custom items */ struct _MxItemFactoryIface { /*< private >*/ GTypeInterface g_iface; /*< public >*/ /* vfuncs, not signals */ ClutterActor *(* create) (MxItemFactory *factory); /*< private >*/ /* padding for future expansion */ void (*_padding_0) (void); void (*_padding_1) (void); void (*_padding_2) (void); void (*_padding_3) (void); void (*_padding_4) (void); }; GType mx_item_factory_get_type (void) G_GNUC_CONST; ClutterActor *mx_item_factory_create (MxItemFactory *factory); #endif mx-1.4.7/mx/mx-item-view.c000066400000000000000000000373241201047117600152770ustar00rootroot00000000000000/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * mx-item-view.c: MxGrid powered by a model * * Copyright 2008, 2009 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * Boston, MA 02111-1307, USA. * * Written by: Thomas Wood * */ /** * SECTION:mx-item-view * @short_description: a flow layout driven by a model. * * #MxItemView is a flow layout container driven by a #ClutterModel. Children * are created for each row in the model, either by creating actors from the * supplied #ClutterActor derived type, or from a #MxItemFactory. * * Data is set on the children by mapping columns in the model to object * properties on the children. */ #include "mx-item-view.h" #include "mx-private.h" G_DEFINE_TYPE (MxItemView, mx_item_view, MX_TYPE_GRID) #define ITEM_VIEW_PRIVATE(o) \ (G_TYPE_INSTANCE_GET_PRIVATE ((o), MX_TYPE_ITEM_VIEW, MxItemViewPrivate)) typedef struct { gchar *name; gint col; } AttributeData; enum { PROP_0, PROP_MODEL, PROP_ITEM_TYPE, PROP_FACTORY }; struct _MxItemViewPrivate { ClutterModel *model; GSList *attributes; GType item_type; MxItemFactory *factory; gulong filter_changed; gulong row_added; gulong row_changed; gulong row_removed; gulong sort_changed; guint is_frozen : 1; }; /* gobject implementations */ static void mx_item_view_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { MxItemViewPrivate *priv = MX_ITEM_VIEW (object)->priv; switch (property_id) { case PROP_MODEL: g_value_set_object (value, priv->model); break; case PROP_ITEM_TYPE: g_value_set_gtype (value, priv->item_type); break; case PROP_FACTORY: g_value_set_object (value, priv->factory); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } } static void mx_item_view_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { switch (property_id) { case PROP_MODEL: mx_item_view_set_model ((MxItemView*) object, (ClutterModel*) g_value_get_object (value)); case PROP_ITEM_TYPE: mx_item_view_set_item_type ((MxItemView*) object, g_value_get_gtype (value)); break; case PROP_FACTORY: mx_item_view_set_factory ((MxItemView*) object, (MxItemFactory*) g_value_get_object (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } } static void mx_item_view_dispose (GObject *object) { /* This will cause the unref of the model and also disconnect the signals */ mx_item_view_set_model (MX_ITEM_VIEW (object), NULL); G_OBJECT_CLASS (mx_item_view_parent_class)->dispose (object); } static void mx_item_view_finalize (GObject *object) { MxItemViewPrivate *priv = MX_ITEM_VIEW (object)->priv; if (priv->attributes) { g_slist_foreach (priv->attributes, (GFunc) g_free, NULL); g_slist_free (priv->attributes); priv->attributes = NULL; } G_OBJECT_CLASS (mx_item_view_parent_class)->finalize (object); } static void mx_item_view_class_init (MxItemViewClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); GParamSpec *pspec; g_type_class_add_private (klass, sizeof (MxItemViewPrivate)); object_class->get_property = mx_item_view_get_property; object_class->set_property = mx_item_view_set_property; object_class->dispose = mx_item_view_dispose; object_class->finalize = mx_item_view_finalize; pspec = g_param_spec_object ("model", "model", "The model for the item view", CLUTTER_TYPE_MODEL, MX_PARAM_READWRITE); g_object_class_install_property (object_class, PROP_MODEL, pspec); pspec = g_param_spec_gtype ("item-type", "Item Type", "The GType to use as the items in the view. " "Must be a subclass of ClutterActor", CLUTTER_TYPE_ACTOR, MX_PARAM_READWRITE); g_object_class_install_property (object_class, PROP_ITEM_TYPE, pspec); /* Note, interfaces aren't necessarily objects, so you can't use * MX_TYPE_ITEM_FACTORY here. The function mx_item_view_set_factory does * a type check, so this is still safe. */ pspec = g_param_spec_object ("factory", "Factory", "The MxItemFactory used for creating new items.", G_TYPE_OBJECT /*MX_TYPE_ITEM_FACTORY*/, MX_PARAM_READWRITE); g_object_class_install_property (object_class, PROP_FACTORY, pspec); } static void mx_item_view_init (MxItemView *item_view) { item_view->priv = ITEM_VIEW_PRIVATE (item_view); } /* model monitors */ static void model_changed_cb (ClutterModel *model, MxItemView *item_view) { GSList *p; GList *l, *children; MxItemViewPrivate *priv = item_view->priv; ClutterModelIter *iter = NULL; gint model_n = 0, child_n = 0; /* bail out if we don't yet have an item type */ if (!priv->item_type && !priv->factory) return; if (priv->is_frozen) return; if (priv->item_type) { /* check the item-type is an descendant of ClutterActor */ if (!g_type_is_a (priv->item_type, CLUTTER_TYPE_ACTOR)) { g_warning ("%s is not a subclass of ClutterActor and therefore" " cannot be used as items in an MxItemView", g_type_name (priv->item_type)); return; } } children = clutter_container_get_children (CLUTTER_CONTAINER (item_view)); child_n = g_list_length (children); if (model) model_n = clutter_model_get_n_rows (priv->model); else model_n = 0; /* add children as needed */ while (model_n > child_n) { ClutterActor *new_child; if (priv->item_type) { new_child = g_object_new (priv->item_type, NULL); } else { new_child = mx_item_factory_create (priv->factory); } clutter_container_add_actor (CLUTTER_CONTAINER (item_view), new_child); child_n++; } /* remove children as needed */ l = g_list_last (children); while (child_n > model_n) { clutter_container_remove_actor (CLUTTER_CONTAINER (item_view), (ClutterActor*) l->data); l = g_list_previous (l); child_n--; } g_list_free (children); if (!priv->model) return; children = clutter_container_get_children (CLUTTER_CONTAINER (item_view)); /* set the properties on the children */ iter = clutter_model_get_first_iter (priv->model); l = children; while (iter && !clutter_model_iter_is_last (iter)) { GObject *child; child = G_OBJECT (l->data); g_object_freeze_notify (child); for (p = priv->attributes; p; p = p->next) { GValue value = { 0, }; AttributeData *attr = p->data; clutter_model_iter_get_value (iter, attr->col, &value); g_object_set_property (child, attr->name, &value); g_value_unset (&value); } g_object_thaw_notify (child); l = g_list_next (l); clutter_model_iter_next (iter); } g_list_free (children); if (iter) g_object_unref (iter); } static void row_changed_cb (ClutterModel *model, ClutterModelIter *iter, MxItemView *item_view) { model_changed_cb (model, item_view); } static void row_removed_cb (ClutterModel *model, ClutterModelIter *iter, MxItemView *item_view) { GList *children; GList *l; ClutterActor *child; if (item_view->priv->is_frozen) return; children = clutter_container_get_children (CLUTTER_CONTAINER (item_view)); l = g_list_nth (children, clutter_model_iter_get_row (iter)); child = (ClutterActor *) l->data; clutter_container_remove_actor (CLUTTER_CONTAINER (item_view), child); g_list_free (children); } /* public api */ /** * mx_item_view_new: * * Create a new #MxItemView * * Returns: a newly allocated #MxItemView */ ClutterActor * mx_item_view_new (void) { return g_object_new (MX_TYPE_ITEM_VIEW, NULL); } /** * mx_item_view_get_item_type: * @item_view: An #MxItemView * * Get the item type currently being used to create items * * Returns: a #GType */ GType mx_item_view_get_item_type (MxItemView *item_view) { g_return_val_if_fail (MX_IS_ITEM_VIEW (item_view), G_TYPE_INVALID); return item_view->priv->item_type; } /** * mx_item_view_set_item_type: * @item_view: An #MxItemView * @item_type: A #GType * * Set the item type used to create items representing each row in the * model */ void mx_item_view_set_item_type (MxItemView *item_view, GType item_type) { g_return_if_fail (MX_IS_ITEM_VIEW (item_view)); g_return_if_fail (g_type_is_a (item_type, CLUTTER_TYPE_ACTOR)); item_view->priv->item_type = item_type; /* update the view */ model_changed_cb (item_view->priv->model, item_view); } /** * mx_item_view_get_model: * @item_view: An #MxItemView * * Get the model currently used by the #MxItemView * * Returns: (transfer none): the current #ClutterModel */ ClutterModel* mx_item_view_get_model (MxItemView *item_view) { g_return_val_if_fail (MX_IS_ITEM_VIEW (item_view), NULL); return item_view->priv->model; } /** * mx_item_view_set_model: * @item_view: An #MxItemView * @model: A #ClutterModel * * Set the model used by the #MxItemView */ void mx_item_view_set_model (MxItemView *item_view, ClutterModel *model) { MxItemViewPrivate *priv; g_return_if_fail (MX_IS_ITEM_VIEW (item_view)); g_return_if_fail (model == NULL || CLUTTER_IS_MODEL (model)); priv = item_view->priv; if (priv->model) { g_signal_handlers_disconnect_by_func (priv->model, (GCallback) model_changed_cb, item_view); g_signal_handlers_disconnect_by_func (priv->model, (GCallback) row_changed_cb, item_view); g_signal_handlers_disconnect_by_func (priv->model, (GCallback) row_removed_cb, item_view); g_object_unref (priv->model); priv->model = NULL; } if (model) { g_return_if_fail (CLUTTER_IS_MODEL (model)); priv->model = g_object_ref (model); priv->filter_changed = g_signal_connect (priv->model, "filter-changed", G_CALLBACK (model_changed_cb), item_view); priv->row_added = g_signal_connect (priv->model, "row-added", G_CALLBACK (row_changed_cb), item_view); priv->row_changed = g_signal_connect (priv->model, "row-changed", G_CALLBACK (row_changed_cb), item_view); /* * model_changed_cb (called from row_changed_cb) expect the row to already * have been removed, thus we need to use _after */ priv->row_removed = g_signal_connect_after (priv->model, "row-removed", G_CALLBACK (row_removed_cb), item_view); priv->sort_changed = g_signal_connect (priv->model, "sort-changed", G_CALLBACK (model_changed_cb), item_view); /* * Only do this inside this block, setting the model to NULL should have * the effect of preserving the view; just disconnect the handlers */ model_changed_cb (priv->model, item_view); } } /** * mx_item_view_add_attribute: * @item_view: An #MxItemView * @attribute: Name of the attribute * @column: Column number * * Adds an attribute mapping between the current model and the objects from the * cell renderer. * */ void mx_item_view_add_attribute (MxItemView *item_view, const gchar *_attribute, gint column) { MxItemViewPrivate *priv; AttributeData *prop; g_return_if_fail (MX_IS_ITEM_VIEW (item_view)); g_return_if_fail (_attribute != NULL); g_return_if_fail (column >= 0); priv = item_view->priv; prop = g_new (AttributeData, 1); prop->name = g_strdup (_attribute); prop->col = column; priv->attributes = g_slist_prepend (priv->attributes, prop); model_changed_cb (priv->model, item_view); } /** * mx_item_view_freeze * @item_view: An #MxItemView * * Freeze the view. This means that the view will not act on changes to the * model until it is thawed. Call #mx_item_view_thaw to thaw the view */ void mx_item_view_freeze (MxItemView *item_view) { g_return_if_fail (MX_IS_ITEM_VIEW (item_view)); item_view->priv->is_frozen = TRUE; } /** * mx_item_view_thaw * @item_view: An #MxItemView * * Thaw the view. This means that the view will now act on changes to the * model. */ void mx_item_view_thaw (MxItemView *item_view) { MxItemViewPrivate *priv; g_return_if_fail (MX_IS_ITEM_VIEW (item_view)); priv = item_view->priv; priv->is_frozen = FALSE; /* Repopulate */ model_changed_cb (priv->model, item_view); } /** * mx_item_view_set_factory: * @item_view: A #MxItemView * @factory: (allow-none): A #MxItemFactory * * Sets @factory to be the factory used for creating new items */ void mx_item_view_set_factory (MxItemView *item_view, MxItemFactory *factory) { MxItemViewPrivate *priv; g_return_if_fail (MX_IS_ITEM_VIEW (item_view)); g_return_if_fail (!factory || MX_IS_ITEM_FACTORY (factory)); priv = item_view->priv; if (priv->factory == factory) return; if (priv->factory) { g_object_unref (priv->factory); priv->factory = NULL; } if (factory) priv->factory = g_object_ref (factory); g_object_notify (G_OBJECT (item_view), "factory"); } /** * mx_item_view_get_factory: * @item_view: A #MxItemView * * Gets the #MxItemFactory used for creating new items. * * Returns: (transfer none): A #MxItemFactory. */ MxItemFactory * mx_item_view_get_factory (MxItemView *item_view) { g_return_val_if_fail (MX_IS_ITEM_VIEW (item_view), NULL); return item_view->priv->factory; } mx-1.4.7/mx/mx-item-view.h000066400000000000000000000064551201047117600153050ustar00rootroot00000000000000/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * mx-item-view.h: MxGrid powered by a model * * Copyright 2009 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * Boston, MA 02111-1307, USA. * * Written by: Thomas Wood * */ #if !defined(MX_H_INSIDE) && !defined(MX_COMPILATION) #error "Only can be included directly.h" #endif #ifndef _MX_ITEM_VIEW_H #define _MX_ITEM_VIEW_H #include #include "mx-grid.h" #include "mx-item-factory.h" G_BEGIN_DECLS #define MX_TYPE_ITEM_VIEW mx_item_view_get_type() #define MX_ITEM_VIEW(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ MX_TYPE_ITEM_VIEW, MxItemView)) #define MX_ITEM_VIEW_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), \ MX_TYPE_ITEM_VIEW, MxItemViewClass)) #define MX_IS_ITEM_VIEW(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \ MX_TYPE_ITEM_VIEW)) #define MX_IS_ITEM_VIEW_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE ((klass), \ MX_TYPE_ITEM_VIEW)) #define MX_ITEM_VIEW_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), \ MX_TYPE_ITEM_VIEW, MxItemViewClass)) typedef struct _MxItemViewPrivate MxItemViewPrivate; /** * MxItemView: * * The contents of the this structure are private and should only be accessed * through the public API. */ typedef struct { /*< private >*/ MxGrid parent; MxItemViewPrivate *priv; } MxItemView; typedef struct { MxGridClass parent_class; /* padding for future expansion */ void (*_padding_0) (void); void (*_padding_1) (void); void (*_padding_2) (void); void (*_padding_3) (void); void (*_padding_4) (void); } MxItemViewClass; GType mx_item_view_get_type (void); ClutterActor *mx_item_view_new (void); void mx_item_view_set_model (MxItemView *item_view, ClutterModel *model); ClutterModel* mx_item_view_get_model (MxItemView *item_view); void mx_item_view_set_item_type (MxItemView *item_view, GType item_type); GType mx_item_view_get_item_type (MxItemView *item_view); void mx_item_view_add_attribute (MxItemView *item_view, const gchar *attribute, gint column); void mx_item_view_freeze (MxItemView *item_view); void mx_item_view_thaw (MxItemView *item_view); void mx_item_view_set_factory (MxItemView *item_view, MxItemFactory *factory); MxItemFactory* mx_item_view_get_factory (MxItemView *item_view); G_END_DECLS #endif /* _MX_ITEM_VIEW_H */ mx-1.4.7/mx/mx-kinetic-scroll-view.c000066400000000000000000001472651201047117600172710ustar00rootroot00000000000000/* mx-kinetic-scroll-view.c: Kinetic scrolling container actor * * Copyright (C) 2008 OpenedHand * Copyright (C) 2010 Intel Corporation. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * Boston, MA 02111-1307, USA. * * Written by: Chris Lord */ /** * SECTION:mx-kinetic-scroll-view * @short_description: A kinetic scrolling container widget * * #MxKineticScrollView is a single child container for actors that implements * #MxScrollable. It allows the contained child to be dragged to scroll, and * maintains the momentum once the drag is complete. Deceleration after * dragging is configurable, and it will always snap to the * #MxAdjustment:step-increment boundary. * * #MxKineticScrollView also implements #MxScrollable itself, allowing it to * be embedded in an #MxScrollView to provide scroll-bars. * * Since: 1.2 */ #include "mx-kinetic-scroll-view.h" #include "mx-enum-types.h" #include "mx-marshal.h" #include "mx-private.h" #include "mx-scrollable.h" #include static void mx_scrollable_iface_init (MxScrollableIface *iface); G_DEFINE_TYPE_WITH_CODE (MxKineticScrollView, mx_kinetic_scroll_view, MX_TYPE_BIN, G_IMPLEMENT_INTERFACE (MX_TYPE_SCROLLABLE, mx_scrollable_iface_init)) #define KINETIC_SCROLL_VIEW_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), \ MX_TYPE_KINETIC_SCROLL_VIEW, \ MxKineticScrollViewPrivate)) typedef struct { /* Units to store the origin of a click when scrolling */ gfloat x; gfloat y; GTimeVal time; } MxKineticScrollViewMotion; struct _MxKineticScrollViewPrivate { ClutterActor *child; guint use_captured : 1; guint in_drag : 1; guint hmoving : 1; guint vmoving : 1; guint hclamping : 1; guint vclamping : 1; guint32 button; /* Mouse motion event information */ GArray *motion_buffer; guint last_motion; /* Variables for storing acceleration information */ ClutterTimeline *deceleration_timeline; gfloat dx; gfloat dy; gdouble decel_rate; gdouble overshoot; gdouble accumulated_delta; gdouble acceleration_factor; MxScrollPolicy scroll_policy; guint clamp_duration; gulong clamp_mode; gboolean clamp_to_center; MxKineticScrollViewState state; }; enum { PROP_0, PROP_DECELERATION, /* PROP_BUFFER_SIZE,*/ PROP_HADJUST, PROP_VADJUST, PROP_BUTTON, PROP_USE_CAPTURED, PROP_OVERSHOOT, PROP_SCROLL_POLICY, PROP_ACCELERATION_FACTOR, PROP_CLAMP_DURATION, PROP_CLAMP_MODE, PROP_STATE, PROP_CLAMP_TO_CENTER, }; static gboolean button_release (MxKineticScrollView *scroll, gint x, gint y); /* MxScrollableIface implementation */ static void mx_kinetic_scroll_view_set_adjustments (MxScrollable *scrollable, MxAdjustment *hadjustment, MxAdjustment *vadjustment) { MxKineticScrollViewPrivate *priv = MX_KINETIC_SCROLL_VIEW (scrollable)->priv; if (priv->child) mx_scrollable_set_adjustments (MX_SCROLLABLE (priv->child), hadjustment, vadjustment); } static void mx_kinetic_scroll_view_get_adjustments (MxScrollable *scrollable, MxAdjustment **hadjustment, MxAdjustment **vadjustment) { MxKineticScrollViewPrivate *priv = MX_KINETIC_SCROLL_VIEW (scrollable)->priv; if (priv->child) { mx_scrollable_get_adjustments (MX_SCROLLABLE (priv->child), hadjustment, vadjustment); } else { if (hadjustment) *hadjustment = NULL; if (vadjustment) *vadjustment = NULL; } } static void mx_scrollable_iface_init (MxScrollableIface *iface) { iface->set_adjustments = mx_kinetic_scroll_view_set_adjustments; iface->get_adjustments = mx_kinetic_scroll_view_get_adjustments; } /* Object implementation */ static void mx_kinetic_scroll_view_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { MxAdjustment *adjustment; MxKineticScrollViewPrivate *priv = MX_KINETIC_SCROLL_VIEW (object)->priv; switch (property_id) { case PROP_DECELERATION : g_value_set_double (value, priv->decel_rate); break; /* case PROP_BUFFER_SIZE : g_value_set_uint (value, priv->motion_buffer->len); break; */ case PROP_HADJUST: mx_kinetic_scroll_view_get_adjustments (MX_SCROLLABLE (object), &adjustment, NULL); g_value_set_object (value, adjustment); break; case PROP_VADJUST: mx_kinetic_scroll_view_get_adjustments (MX_SCROLLABLE (object), NULL, &adjustment); g_value_set_object (value, adjustment); break; case PROP_BUTTON: g_value_set_uint (value, priv->button); break; case PROP_USE_CAPTURED: g_value_set_boolean (value, priv->use_captured); break; case PROP_OVERSHOOT: g_value_set_double (value, priv->overshoot); break; case PROP_SCROLL_POLICY: g_value_set_enum (value, priv->scroll_policy); break; case PROP_ACCELERATION_FACTOR : g_value_set_double (value, priv->acceleration_factor); break; case PROP_CLAMP_DURATION : g_value_set_uint (value, priv->clamp_duration); break; case PROP_CLAMP_MODE : g_value_set_ulong (value, priv->clamp_mode); break; case PROP_STATE : g_value_set_enum (value, priv->state); break; case PROP_CLAMP_TO_CENTER : g_value_set_boolean (value, priv->clamp_to_center); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } } static void mx_kinetic_scroll_view_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { MxAdjustment *adjustment; MxScrollable *scrollable; MxKineticScrollView *self = MX_KINETIC_SCROLL_VIEW (object); switch (property_id) { case PROP_DECELERATION : mx_kinetic_scroll_view_set_deceleration (self, g_value_get_double (value)); break; /* case PROP_BUFFER_SIZE : mx_kinetic_scroll_view_set_buffer_size (self, g_value_get_uint (value)); break; */ case PROP_HADJUST: scrollable = MX_SCROLLABLE (object); mx_kinetic_scroll_view_get_adjustments (scrollable, NULL, &adjustment); mx_kinetic_scroll_view_set_adjustments (scrollable, g_value_get_object (value), adjustment); break; case PROP_VADJUST: scrollable = MX_SCROLLABLE (object); mx_kinetic_scroll_view_get_adjustments (scrollable, &adjustment, NULL); mx_kinetic_scroll_view_set_adjustments (scrollable, adjustment, g_value_get_object (value)); break; case PROP_BUTTON: mx_kinetic_scroll_view_set_mouse_button (self, g_value_get_uint (value)); break; case PROP_USE_CAPTURED: mx_kinetic_scroll_view_set_use_captured (self, g_value_get_boolean (value)); break; case PROP_OVERSHOOT: mx_kinetic_scroll_view_set_overshoot (self, g_value_get_double (value)); break; case PROP_SCROLL_POLICY: mx_kinetic_scroll_view_set_scroll_policy (self, g_value_get_enum (value)); break; case PROP_ACCELERATION_FACTOR : mx_kinetic_scroll_view_set_acceleration_factor (self, g_value_get_double (value)); break; case PROP_CLAMP_DURATION : mx_kinetic_scroll_view_set_clamp_duration (self, g_value_get_uint (value)); break; case PROP_CLAMP_MODE : mx_kinetic_scroll_view_set_clamp_mode (self, g_value_get_ulong (value)); break; case PROP_CLAMP_TO_CENTER : mx_kinetic_scroll_view_set_clamp_to_center (self, g_value_get_boolean (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } } static void mx_kinetic_scroll_view_dispose (GObject *object) { MxKineticScrollViewPrivate *priv = MX_KINETIC_SCROLL_VIEW (object)->priv; if (priv->deceleration_timeline) { clutter_timeline_stop (priv->deceleration_timeline); g_object_unref (priv->deceleration_timeline); priv->deceleration_timeline = NULL; } G_OBJECT_CLASS (mx_kinetic_scroll_view_parent_class)->dispose (object); } static void mx_kinetic_scroll_view_finalize (GObject *object) { MxKineticScrollViewPrivate *priv = MX_KINETIC_SCROLL_VIEW (object)->priv; g_array_free (priv->motion_buffer, TRUE); G_OBJECT_CLASS (mx_kinetic_scroll_view_parent_class)->finalize (object); } static void mx_kinetic_scroll_view_get_preferred_width (ClutterActor *actor, gfloat for_height, gfloat *min_width_p, gfloat *nat_width_p) { MxKineticScrollViewPrivate *priv = MX_KINETIC_SCROLL_VIEW (actor)->priv; CLUTTER_ACTOR_CLASS (mx_kinetic_scroll_view_parent_class)-> get_preferred_width (actor, for_height, NULL, nat_width_p); if (min_width_p && priv->scroll_policy != MX_SCROLL_POLICY_VERTICAL) { MxPadding padding; mx_widget_get_padding (MX_WIDGET (actor), &padding); *min_width_p = padding.left + padding.right; } } static void mx_kinetic_scroll_view_get_preferred_height (ClutterActor *actor, gfloat for_width, gfloat *min_height_p, gfloat *nat_height_p) { MxKineticScrollViewPrivate *priv = MX_KINETIC_SCROLL_VIEW (actor)->priv; CLUTTER_ACTOR_CLASS (mx_kinetic_scroll_view_parent_class)-> get_preferred_height (actor, for_width, NULL, nat_height_p); if (min_height_p && priv->scroll_policy != MX_SCROLL_POLICY_HORIZONTAL) { MxPadding padding; mx_widget_get_padding (MX_WIDGET (actor), &padding); *min_height_p = padding.top + padding.bottom; } } static void mx_kinetic_scroll_view_allocate (ClutterActor *actor, const ClutterActorBox *box, ClutterAllocationFlags flags) { CLUTTER_ACTOR_CLASS (mx_kinetic_scroll_view_parent_class)-> allocate (actor, box, flags); mx_bin_allocate_child (MX_BIN (actor), box, flags); } static void mx_kinetic_scroll_view_class_init (MxKineticScrollViewClass *klass) { GParamSpec *pspec; GObjectClass *object_class = G_OBJECT_CLASS (klass); ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass); g_type_class_add_private (klass, sizeof (MxKineticScrollViewPrivate)); object_class->get_property = mx_kinetic_scroll_view_get_property; object_class->set_property = mx_kinetic_scroll_view_set_property; object_class->dispose = mx_kinetic_scroll_view_dispose; object_class->finalize = mx_kinetic_scroll_view_finalize; actor_class->get_preferred_width = mx_kinetic_scroll_view_get_preferred_width; actor_class->get_preferred_height = mx_kinetic_scroll_view_get_preferred_height; actor_class->allocate = mx_kinetic_scroll_view_allocate; pspec = g_param_spec_double ("deceleration", "Deceleration", "Rate at which the view will decelerate in.", 1.01, G_MAXDOUBLE, 1.1, MX_PARAM_READWRITE); g_object_class_install_property (object_class, PROP_DECELERATION, pspec); /* pspec = g_param_spec_uint ("buffer-size", "Buffer size", "Amount of motion events to buffer", 1, G_MAXUINT, 3, MX_PARAM_READWRITE); g_object_class_install_property (object_class, PROP_BUFFER_SIZE, pspec); */ pspec = g_param_spec_uint ("mouse-button", "Mouse button", "The mouse button used to control scrolling", 0, G_MAXUINT, 1, MX_PARAM_READWRITE); g_object_class_install_property (object_class, PROP_BUTTON, pspec); pspec = g_param_spec_boolean ("use-captured", "Use captured", "Use captured events to initiate scrolling", FALSE, MX_PARAM_READWRITE); g_object_class_install_property (object_class, PROP_USE_CAPTURED, pspec); pspec = g_param_spec_double ("overshoot", "Overshoot", "The rate at which the view will decelerate " "when scrolled beyond its boundaries.", 0.0, 1.0, 0.0, MX_PARAM_READWRITE); g_object_class_install_property (object_class, PROP_OVERSHOOT, pspec); pspec = g_param_spec_enum ("scroll-policy", "Scroll Policy", "The scroll policy", MX_TYPE_SCROLL_POLICY, MX_SCROLL_POLICY_BOTH, MX_PARAM_READWRITE); g_object_class_install_property (object_class, PROP_SCROLL_POLICY, pspec); pspec = g_param_spec_double ("acceleration-factor", "Initial acceleration factor", "Factor applied to the initial acceleration.", 0.0, G_MAXDOUBLE, 1.0, MX_PARAM_READWRITE); g_object_class_install_property (object_class, PROP_ACCELERATION_FACTOR, pspec); pspec = g_param_spec_uint ("clamp-duration", "Clamp duration", "Duration of the adjustment clamp animation.", 0, G_MAXUINT, 250, MX_PARAM_READWRITE); g_object_class_install_property (object_class, PROP_CLAMP_DURATION, pspec); pspec = g_param_spec_ulong ("clamp-mode", "Clamp mode", "Animation mode to use for the clamp animation.", 0, G_MAXULONG, CLUTTER_EASE_OUT_QUAD, MX_PARAM_READWRITE); g_object_class_install_property (object_class, PROP_CLAMP_MODE, pspec); pspec = g_param_spec_enum ("state", "State", "State of the scrolling", MX_TYPE_KINETIC_SCROLL_VIEW_STATE, MX_KINETIC_SCROLL_VIEW_STATE_IDLE, MX_PARAM_READABLE); g_object_class_install_property (object_class, PROP_STATE, pspec); pspec = g_param_spec_boolean ("clamp-to-center", "Clamp to center", "Whether to clamp to step increments based on the center of the page.", FALSE, MX_PARAM_READWRITE); g_object_class_install_property (object_class, PROP_CLAMP_TO_CENTER, pspec); /* MxScrollable properties */ g_object_class_override_property (object_class, PROP_HADJUST, "horizontal-adjustment"); g_object_class_override_property (object_class, PROP_VADJUST, "vertical-adjustment"); } static void set_state (MxKineticScrollView *scroll, MxKineticScrollViewState state) { MxKineticScrollViewPrivate *priv = scroll->priv; priv->state = state; g_object_notify (G_OBJECT (scroll), "state"); } static gboolean motion_event_cb (ClutterActor *actor, ClutterMotionEvent *event, MxKineticScrollView *scroll) { gfloat x, y; MxKineticScrollViewPrivate *priv = scroll->priv; if (event->type != CLUTTER_MOTION) return FALSE; /* For various reasons, it's possible we may not get the button release * event - in this case, check here if the button is still down and * handle it manually. * * We need to do this, or we can end up just stealing events forever. */ switch (priv->button) { default: case 1: if (!(event->modifier_state & CLUTTER_BUTTON1_MASK)) return button_release (scroll, event->x, event->y); break; case 2: if (!(event->modifier_state & CLUTTER_BUTTON2_MASK)) return button_release (scroll, event->x, event->y); break; case 3: if (!(event->modifier_state & CLUTTER_BUTTON3_MASK)) return button_release (scroll, event->x, event->y); break; case 4: if (!(event->modifier_state & CLUTTER_BUTTON4_MASK)) return button_release (scroll, event->x, event->y); break; case 5: if (!(event->modifier_state & CLUTTER_BUTTON5_MASK)) return button_release (scroll, event->x, event->y); break; } if (clutter_actor_transform_stage_point (CLUTTER_ACTOR (scroll), event->x, event->y, &x, &y)) { MxKineticScrollViewMotion *motion; ClutterActor *child = mx_bin_get_child (MX_BIN (scroll)); /* Check if we've passed the drag threshold */ if (!priv->in_drag) { guint threshold; gboolean threshold_passed; MxSettings *settings = mx_settings_get_default (); g_object_get (G_OBJECT (settings), "drag-threshold", &threshold, NULL); motion = &g_array_index (priv->motion_buffer, MxKineticScrollViewMotion, 0); if ((ABS (motion->y - y) >= threshold) && (priv->scroll_policy == MX_SCROLL_POLICY_VERTICAL || priv->scroll_policy == MX_SCROLL_POLICY_BOTH)) threshold_passed = TRUE; else if ((ABS (motion->x - x) >= threshold) && (priv->scroll_policy == MX_SCROLL_POLICY_HORIZONTAL || priv->scroll_policy == MX_SCROLL_POLICY_BOTH)) threshold_passed = TRUE; else threshold_passed = FALSE; if (threshold_passed) { clutter_set_motion_events_enabled (FALSE); priv->in_drag = TRUE; set_state (scroll, MX_KINETIC_SCROLL_VIEW_STATE_PANNING); if (!priv->use_captured) { g_signal_handlers_disconnect_by_func (actor, motion_event_cb, scroll); g_signal_connect (clutter_actor_get_stage (actor), "captured-event", G_CALLBACK (motion_event_cb), scroll); } } else return FALSE; } if (child) { gdouble dx, dy; MxAdjustment *hadjust, *vadjust; mx_scrollable_get_adjustments (MX_SCROLLABLE (child), &hadjust, &vadjust); motion = &g_array_index (priv->motion_buffer, MxKineticScrollViewMotion, priv->last_motion); if (hadjust) { dx = (motion->x - x) + mx_adjustment_get_value (hadjust); mx_adjustment_set_value (hadjust, dx); } if (vadjust) { dy = (motion->y - y) + mx_adjustment_get_value (vadjust); mx_adjustment_set_value (vadjust, dy); } } priv->last_motion ++; if (priv->last_motion == priv->motion_buffer->len) { priv->motion_buffer = g_array_remove_index (priv->motion_buffer, 0); g_array_set_size (priv->motion_buffer, priv->last_motion); priv->last_motion --; } motion = &g_array_index (priv->motion_buffer, MxKineticScrollViewMotion, priv->last_motion); motion->x = x; motion->y = y; g_get_current_time (&motion->time); } return TRUE; } static void interpolation_completed_cb (MxAdjustment *adj, MxKineticScrollView *scroll) { MxKineticScrollViewPrivate *priv = scroll->priv; MxAdjustment *hadj, *vadj; g_signal_handlers_disconnect_by_func (adj, interpolation_completed_cb, scroll); mx_scrollable_get_adjustments (MX_SCROLLABLE (scroll), &hadj, &vadj); if (adj == hadj) priv->hclamping = FALSE; else priv->vclamping = FALSE; if (!priv->hclamping && !priv->vclamping && \ priv->state == MX_KINETIC_SCROLL_VIEW_STATE_CLAMPING) set_state (scroll, MX_KINETIC_SCROLL_VIEW_STATE_IDLE); } static void clamp_adjustment (MxKineticScrollView *scroll, MxAdjustment *adj, guint duration) { MxKineticScrollViewPrivate *priv = scroll->priv; gdouble d, value, lower, upper, step_increment, page_size; /* Snap to the nearest step increment on hadjustment */ mx_adjustment_get_values (adj, &value, &lower, &upper, &step_increment, NULL, &page_size); d = (rint ((value - lower) / step_increment) * step_increment) + lower; if (priv->clamp_to_center) { gdouble offset = page_size / 2; offset -= step_increment / 2; offset = step_increment - (int) offset % (int) step_increment; if (offset > step_increment / 2) offset = - (step_increment - offset); d += offset; } if (mx_adjustment_get_clamp_value (adj)) d = CLAMP (d, lower, upper - page_size); g_signal_connect (adj, "interpolation-completed", G_CALLBACK (interpolation_completed_cb), scroll); mx_adjustment_interpolate (adj, d, duration, priv->clamp_mode); } static void clamp_adjustments (MxKineticScrollView *scroll, guint duration, gboolean horizontal, gboolean vertical) { MxKineticScrollViewPrivate *priv = scroll->priv; ClutterActor *child = mx_bin_get_child (MX_BIN (scroll)); if (child) { MxAdjustment *hadj, *vadj; mx_scrollable_get_adjustments (MX_SCROLLABLE (child), &hadj, &vadj); if (horizontal && hadj) { priv->hclamping = TRUE; clamp_adjustment (scroll, hadj, duration); } if (vertical && vadj) { priv->vclamping = TRUE; clamp_adjustment (scroll, vadj, duration); }; } if (priv->hclamping && priv->vclamping) set_state (scroll, MX_KINETIC_SCROLL_VIEW_STATE_CLAMPING); } static void deceleration_completed_cb (ClutterTimeline *timeline, MxKineticScrollView *scroll) { MxKineticScrollViewPrivate *priv = scroll->priv; guint duration; duration = (priv->overshoot > 0.0) ? priv->clamp_duration : 10; clamp_adjustments (scroll, duration, priv->hmoving, priv->vmoving); g_object_unref (timeline); priv->deceleration_timeline = NULL; } static void deceleration_new_frame_cb (ClutterTimeline *timeline, gint frame_num, MxKineticScrollView *scroll) { MxKineticScrollViewPrivate *priv = scroll->priv; ClutterActor *child = mx_bin_get_child (MX_BIN (scroll)); if (child) { MxAdjustment *hadjust, *vadjust; gboolean stop = TRUE; mx_scrollable_get_adjustments (MX_SCROLLABLE (child), &hadjust, &vadjust); priv->accumulated_delta += clutter_timeline_get_delta (timeline); if (priv->accumulated_delta <= 1000.0/60.0) stop = FALSE; while (priv->accumulated_delta > 1000.0/60.0) { gdouble hvalue, vvalue; if (hadjust) { if (ABS (priv->dx) > 5) { hvalue = priv->dx + mx_adjustment_get_value (hadjust); mx_adjustment_set_value (hadjust, hvalue); if (priv->overshoot > 0.0) { if ((hvalue > mx_adjustment_get_upper (hadjust) - mx_adjustment_get_page_size (hadjust)) || (hvalue < mx_adjustment_get_lower (hadjust))) priv->dx *= priv->overshoot; } priv->dx = priv->dx / priv->decel_rate; stop = FALSE; } else if (priv->hmoving) { guint duration; priv->hmoving = FALSE; duration = (priv->overshoot > 0.0) ? priv->clamp_duration : 10; clamp_adjustments (scroll, duration, TRUE, FALSE); } } if (vadjust) { if (ABS (priv->dy) > 5) { vvalue = priv->dy + mx_adjustment_get_value (vadjust); mx_adjustment_set_value (vadjust, vvalue); if (priv->overshoot > 0.0) { if ((vvalue > mx_adjustment_get_upper (vadjust) - mx_adjustment_get_page_size (vadjust)) || (vvalue < mx_adjustment_get_lower (vadjust))) priv->dy *= priv->overshoot; } priv->dy = priv->dy / priv->decel_rate; stop = FALSE; } else if (priv->vmoving) { guint duration; priv->vmoving = FALSE; duration = (priv->overshoot > 0.0) ? priv->clamp_duration : 10; clamp_adjustments (scroll, duration, FALSE, TRUE); } } priv->accumulated_delta -= 1000.0/60.0; } if (stop) { clutter_timeline_stop (timeline); deceleration_completed_cb (timeline, scroll); } } } static gboolean button_release_event_cb (ClutterActor *stage, ClutterButtonEvent *event, MxKineticScrollView *scroll) { MxKineticScrollViewPrivate *priv = scroll->priv; if ((event->type != CLUTTER_BUTTON_RELEASE) || (event->button != priv->button)) return FALSE; return button_release (scroll, event->x, event->y); } static gboolean button_release (MxKineticScrollView *scroll, gint x_pos, gint y_pos) { ClutterActor *actor = CLUTTER_ACTOR (scroll); ClutterActor *stage = clutter_actor_get_stage (actor); ClutterActor *child = mx_bin_get_child (MX_BIN (scroll)); MxKineticScrollViewPrivate *priv = scroll->priv; gboolean decelerating = FALSE; g_signal_handlers_disconnect_by_func (scroll, motion_event_cb, scroll); g_signal_handlers_disconnect_by_func (stage, motion_event_cb, scroll); g_signal_handlers_disconnect_by_func (stage, button_release_event_cb, scroll); if (!priv->in_drag) return FALSE; clutter_set_motion_events_enabled (TRUE); if (child) { gfloat event_x, event_y; if (clutter_actor_transform_stage_point (actor, x_pos, y_pos, &event_x, &event_y)) { gdouble value, lower, upper, step_increment, page_size, d, ax, ay, y, nx, ny, n; gfloat frac, x_origin, y_origin; GTimeVal release_time, motion_time; MxAdjustment *hadjust, *vadjust; glong time_diff; guint duration; gint i; /* Get time delta */ g_get_current_time (&release_time); /* Get average position/time of last x mouse events */ priv->last_motion ++; x_origin = y_origin = 0; motion_time = (GTimeVal){ 0, 0 }; for (i = 0; i < priv->last_motion; i++) { MxKineticScrollViewMotion *motion = &g_array_index (priv->motion_buffer, MxKineticScrollViewMotion, i); /* FIXME: This doesn't guard against overflows - Should * either fix that, or calculate the correct maximum * value for the buffer size */ x_origin += motion->x; y_origin += motion->y; motion_time.tv_sec += motion->time.tv_sec; motion_time.tv_usec += motion->time.tv_usec; } x_origin = x_origin / priv->last_motion; y_origin = y_origin / priv->last_motion; motion_time.tv_sec /= priv->last_motion; motion_time.tv_usec /= priv->last_motion; if (motion_time.tv_sec == release_time.tv_sec) time_diff = release_time.tv_usec - motion_time.tv_usec; else time_diff = release_time.tv_usec + (G_USEC_PER_SEC - motion_time.tv_usec); /* Work out the fraction of 1/60th of a second that has elapsed */ frac = (time_diff/1000.0) / (1000.0/60.0); /* See how many units to move in 1/60th of a second */ priv->dx = (x_origin - event_x) / frac * priv->acceleration_factor; priv->dy = (y_origin - event_y) / frac * priv->acceleration_factor; /* If the delta is too low for the equations to work, * bump the values up a bit. */ if (ABS (priv->dx) < 1) priv->dx = (priv->dx > 0) ? 1 : -1; if (ABS (priv->dy) < 1) priv->dy = (priv->dy > 0) ? 1 : -1; /* We want n, where x / y^n < z, * x = Distance to move per frame * y = Deceleration rate * z = maximum distance from target * * Rearrange to n = log (x / z) / log (y) * To simplify, z = 1, so n = log (x) / log (y) */ y = priv->decel_rate; nx = logf (ABS (priv->dx)) / logf (y); ny = logf (ABS (priv->dy)) / logf (y); n = MAX (nx, ny); duration = MAX (1, (gint)(MAX (nx, ny) * (1000/60.0))); if (duration > 250) { /* Now we have n, adjust dx/dy so that we finish on a step * boundary. * * Distance moved, using the above variable names: * * d = x + x/y + x/y^2 + ... + x/y^n * * Using geometric series, * * d = (1 - 1/y^(n+1))/(1 - 1/y)*x * * Let a = (1 - 1/y^(n+1))/(1 - 1/y), * * d = a * x * * Find d and find its nearest page boundary, then solve for x * * x = d / a */ /* Get adjustments, work out y^n */ mx_scrollable_get_adjustments (MX_SCROLLABLE (child), &hadjust, &vadjust); ax = (1.0 - 1.0 / pow (y, n + 1)) / (1.0 - 1.0 / y); ay = (1.0 - 1.0 / pow (y, n + 1)) / (1.0 - 1.0 / y); /* Solving for dx */ if (hadjust) { mx_adjustment_get_values (hadjust, &value, &lower, &upper, &step_increment, NULL, &page_size); /* Make sure we pick the next nearest step increment in the * same direction as the push. */ priv->dx *= n; if (ABS (priv->dx) < step_increment / 2) d = round ((value + priv->dx - lower) / step_increment); else if (priv->dx > 0) d = ceil ((value + priv->dx - lower) / step_increment); else d = floor ((value + priv->dx - lower) / step_increment); if (priv->overshoot <= 0.0) d = CLAMP ((d * step_increment) + lower, lower, upper - page_size) - value; else d = ((d * step_increment) + lower) - value; priv->dx = d / ax; } /* Solving for dy */ if (vadjust) { mx_adjustment_get_values (vadjust, &value, &lower, &upper, &step_increment, NULL, &page_size); priv->dy *= n; if (ABS (priv->dy) < step_increment / 2) d = round ((value + priv->dy - lower) / step_increment); else if (priv->dy > 0) d = ceil ((value + priv->dy - lower) / step_increment); else d = floor ((value + priv->dy - lower) / step_increment); if (priv->overshoot <= 0.0) d = CLAMP ((d * step_increment) + lower, lower, upper - page_size) - value; else d = ((d * step_increment) + lower) - value; priv->dy = d / ay; } priv->deceleration_timeline = clutter_timeline_new (duration); g_signal_connect (priv->deceleration_timeline, "new_frame", G_CALLBACK (deceleration_new_frame_cb), scroll); g_signal_connect (priv->deceleration_timeline, "completed", G_CALLBACK (deceleration_completed_cb), scroll); priv->accumulated_delta = 0; priv->hmoving = priv->vmoving = TRUE; clutter_timeline_start (priv->deceleration_timeline); decelerating = TRUE; set_state (scroll, MX_KINETIC_SCROLL_VIEW_STATE_SCROLLING); } } } /* Reset motion event buffer */ priv->last_motion = 0; if (!decelerating) clamp_adjustments (scroll, priv->clamp_duration, TRUE, TRUE); return TRUE; } static gboolean button_press_event_cb (ClutterActor *actor, ClutterEvent *event, MxKineticScrollView *scroll) { MxKineticScrollViewPrivate *priv = scroll->priv; ClutterButtonEvent *bevent = (ClutterButtonEvent *)event; ClutterActor *stage = clutter_actor_get_stage (actor); if ((event->type == CLUTTER_BUTTON_PRESS) && (bevent->button == priv->button) && stage) { MxKineticScrollViewMotion *motion; /* Reset motion buffer */ priv->last_motion = 0; motion = &g_array_index (priv->motion_buffer, MxKineticScrollViewMotion, 0); if (clutter_actor_transform_stage_point (actor, bevent->x, bevent->y, &motion->x, &motion->y)) { guint threshold; MxSettings *settings = mx_settings_get_default (); g_get_current_time (&motion->time); if (priv->deceleration_timeline) { clutter_timeline_stop (priv->deceleration_timeline); g_object_unref (priv->deceleration_timeline); priv->deceleration_timeline = NULL; clamp_adjustments (scroll, priv->clamp_duration, priv->hmoving, priv->vmoving); } if (priv->use_captured) g_signal_connect (stage, "captured-event", G_CALLBACK (motion_event_cb), scroll); else g_signal_connect (scroll, "motion-event", G_CALLBACK (motion_event_cb), scroll); g_signal_connect (stage, "captured-event", G_CALLBACK (button_release_event_cb), scroll); /* If there's a zero drag threshold, start the drag immediately */ g_object_get (G_OBJECT (settings), "drag-threshold", &threshold, NULL); if (threshold == 0) { priv->in_drag = TRUE; clutter_set_motion_events_enabled (FALSE); /* Swallow the press event */ return TRUE; } else priv->in_drag = FALSE; } } return FALSE; } static void mx_kinetic_scroll_view_actor_added_cb (ClutterContainer *container, ClutterActor *actor) { MxKineticScrollViewPrivate *priv = MX_KINETIC_SCROLL_VIEW (container)->priv; if (MX_IS_SCROLLABLE (actor)) { MxAdjustment *hadjust, *vadjust; priv->child = actor; /* Make sure the adjustments have been created so the child * will initialise them during its allocation (necessary for * MxBoxLayout, for example) */ mx_scrollable_get_adjustments (MX_SCROLLABLE (actor), &hadjust, &vadjust); } else g_warning ("Attempting to add an actor of type %s to " "a MxKineticScrollView, but the actor does " "not implement MxScrollable.", g_type_name (G_OBJECT_TYPE (actor))); } static void mx_kinetic_scroll_view_actor_removed_cb (ClutterContainer *container, ClutterActor *actor) { MxKineticScrollViewPrivate *priv = MX_KINETIC_SCROLL_VIEW (container)->priv; priv->child = NULL; } static void mx_kinetic_scroll_view_init (MxKineticScrollView *self) { MxKineticScrollViewPrivate *priv = self->priv = KINETIC_SCROLL_VIEW_PRIVATE (self); priv->motion_buffer = g_array_sized_new (FALSE, TRUE, sizeof (MxKineticScrollViewMotion), 3); g_array_set_size (priv->motion_buffer, 3); priv->decel_rate = 1.1f; priv->button = 1; priv->scroll_policy = MX_SCROLL_POLICY_BOTH; priv->acceleration_factor = 1.0; priv->clamp_duration = 250; priv->clamp_mode = CLUTTER_EASE_OUT_QUAD; clutter_actor_set_reactive (CLUTTER_ACTOR (self), TRUE); g_signal_connect (self, "button-press-event", G_CALLBACK (button_press_event_cb), self); g_signal_connect (self, "actor-added", G_CALLBACK (mx_kinetic_scroll_view_actor_added_cb), self); g_signal_connect (self, "actor-removed", G_CALLBACK (mx_kinetic_scroll_view_actor_removed_cb), self); mx_bin_set_alignment (MX_BIN (self), MX_ALIGN_START, MX_ALIGN_START); } /** * mx_kinetic_scroll_view_new: * * Creates a new #MxKineticScrollView. * * Returns: a newly allocated #MxKineticScrollView * * Since: 1.2 */ ClutterActor * mx_kinetic_scroll_view_new () { return g_object_new (MX_TYPE_KINETIC_SCROLL_VIEW, NULL); } /** * mx_kinetic_scroll_view_stop: * @scroll: A #MxKineticScrollView * * Stops any current movement due to kinetic scrolling. * * Since: 1.2 */ void mx_kinetic_scroll_view_stop (MxKineticScrollView *scroll) { MxKineticScrollViewPrivate *priv; g_return_if_fail (MX_IS_KINETIC_SCROLL_VIEW (scroll)); priv = scroll->priv; if (priv->deceleration_timeline) { clutter_timeline_stop (priv->deceleration_timeline); g_object_unref (priv->deceleration_timeline); priv->deceleration_timeline = NULL; } } /** * mx_kinetic_scroll_view_set_deceleration: * @scroll: A #MxKineticScrollView * @rate: The deceleration rate * * Sets the deceleration rate when a drag is finished on the kinetic * scroll-view. This is the value that the momentum is divided by * every 60th of a second. * * Since: 1.2 */ void mx_kinetic_scroll_view_set_deceleration (MxKineticScrollView *scroll, gdouble rate) { MxKineticScrollViewPrivate *priv; g_return_if_fail (MX_IS_KINETIC_SCROLL_VIEW (scroll)); g_return_if_fail (rate >= 1.01); priv = scroll->priv; if (priv->decel_rate != rate) { priv->decel_rate = rate; g_object_notify (G_OBJECT (scroll), "deceleration"); } } /** * mx_kinetic_scroll_view_get_deceleration: * @scroll: A #MxKineticScrollView * * Retrieves the deceleration rate of the kinetic scroll-view. * * Returns: The deceleration rate of the kinetic scroll-view * * Since: 1.2 */ gdouble mx_kinetic_scroll_view_get_deceleration (MxKineticScrollView *scroll) { g_return_val_if_fail (MX_IS_KINETIC_SCROLL_VIEW (scroll), 1.01); return scroll->priv->decel_rate; } /* void mx_kinetic_scroll_view_set_buffer_size (MxKineticScrollView *scroll, guint size) { MxKineticScrollViewPrivate *priv; g_return_if_fail (MX_IS_KINETIC_SCROLL_VIEW (scroll)); g_return_if_fail (size > 0); priv = scroll->priv; if (priv->motion_buffer->len != size) { g_array_set_size (priv->motion_buffer, size); g_object_notify (G_OBJECT (scroll), "buffer-size"); } } guint mx_kinetic_scroll_view_get_buffer_size (MxKineticScrollView *scroll) { g_return_val_if_fail (MX_IS_KINETIC_SCROLL_VIEW (scroll), 0); return scroll->priv->motion_buffer->len; } */ /** * mx_kinetic_scroll_view_set_mouse_button: * @scroll: A #MxKineticScrollView * @button: A mouse button number * * Sets the mouse button number used to initiate drag events on the kinetic * scroll-view. * * Since: 1.2 */ void mx_kinetic_scroll_view_set_mouse_button (MxKineticScrollView *scroll, guint32 button) { MxKineticScrollViewPrivate *priv; g_return_if_fail (MX_IS_KINETIC_SCROLL_VIEW (scroll)); priv = scroll->priv; if (priv->button != button) { priv->button = button; g_object_notify (G_OBJECT (scroll), "mouse-button"); } } /** * mx_kinetic_scroll_view_get_mouse_button: * @scroll: A #MxKineticScrollView * * Gets the #MxKineticScrollView:mouse-button property * * Returns: The mouse button number used to initiate drag events on the * kinetic scroll-view * * Since: 1.2 */ guint32 mx_kinetic_scroll_view_get_mouse_button (MxKineticScrollView *scroll) { g_return_val_if_fail (MX_IS_KINETIC_SCROLL_VIEW (scroll), 0); return scroll->priv->button; } /** * mx_kinetic_scroll_view_set_use_captured: * @scroll: A #MxKineticScrollView * @use_captured: %TRUE to use captured events * * Sets whether to use captured events to initiate drag events. This can be * used to block events that would initiate scrolling from reaching the child * actor. * * Since: 1.2 */ void mx_kinetic_scroll_view_set_use_captured (MxKineticScrollView *scroll, gboolean use_captured) { MxKineticScrollViewPrivate *priv; g_return_if_fail (MX_IS_KINETIC_SCROLL_VIEW (scroll)); priv = scroll->priv; if (priv->use_captured != use_captured) { priv->use_captured = use_captured; g_signal_handlers_disconnect_by_func (scroll, button_press_event_cb, scroll); g_signal_connect (scroll, use_captured ? "captured-event" : "button-press-event", G_CALLBACK (button_press_event_cb), scroll); g_object_notify (G_OBJECT (scroll), "use-captured"); } } /** * mx_kinetic_scroll_view_get_use_captured: * @scroll: A #MxKineticScrollView * * Gets the #MxKineticScrollView:use-captured property. * * Returns: %TRUE if captured-events should be used to initiate scrolling * * Since: 1.2 */ gboolean mx_kinetic_scroll_view_get_use_captured (MxKineticScrollView *scroll) { g_return_val_if_fail (MX_IS_KINETIC_SCROLL_VIEW (scroll), FALSE); return scroll->priv->use_captured; } /** * mx_kinetic_scroll_view_set_overshoot: * @scroll: A #MxKineticScrollView * @overshoot: The rate at which the view will decelerate when scrolling beyond * its boundaries. * * Sets the rate at which the view will decelerate when scrolling beyond its * boundaries. The deceleration rate will be multiplied by this value every * 60th of a second when the view is scrolling outside of the range set by its * adjustments. * * See mx_kinetic_scroll_view_set_deceleration() * * Since: 1.2 */ void mx_kinetic_scroll_view_set_overshoot (MxKineticScrollView *scroll, gdouble overshoot) { MxKineticScrollViewPrivate *priv; g_return_if_fail (MX_IS_KINETIC_SCROLL_VIEW (scroll)); priv = scroll->priv; if (priv->overshoot != overshoot) { priv->overshoot = overshoot; g_object_notify (G_OBJECT (scroll), "overshoot"); } } /** * mx_kinetic_scroll_view_get_overshoot: * @scroll: A #MxKineticScrollView * * Retrieves the deceleration rate multiplier used when the scroll-view is * scrolling beyond its boundaries. * * Since: 1.2 */ gdouble mx_kinetic_scroll_view_get_overshoot (MxKineticScrollView *scroll) { g_return_val_if_fail (MX_IS_KINETIC_SCROLL_VIEW (scroll), 0.0); return scroll->priv->overshoot; } /** * mx_kinetic_scroll_view_set_scroll_policy: * @scroll: A #MxKineticScrollView * @policy: A #MxScrollPolicy * * Sets the scrolling policy for the kinetic scroll-view. This controls the * possible axes of movement, and can affect the minimum size of the widget. */ void mx_kinetic_scroll_view_set_scroll_policy (MxKineticScrollView *scroll, MxScrollPolicy policy) { MxKineticScrollViewPrivate *priv; g_return_if_fail (MX_IS_KINETIC_SCROLL_VIEW (scroll)); priv = scroll->priv; if (priv->scroll_policy != policy) { priv->scroll_policy = policy; g_object_notify (G_OBJECT (scroll), "scroll-policy"); } } /** * mx_kinetic_scroll_view_get_scroll_policy: * @scroll: A #MxKineticScrollView * * Retrieves the scrolling policy of the kinetic scroll-view. * * Returns: A #MxScrollPolicy */ MxScrollPolicy mx_kinetic_scroll_view_get_scroll_policy (MxKineticScrollView *scroll) { g_return_val_if_fail (MX_IS_KINETIC_SCROLL_VIEW (scroll), 0); return scroll->priv->scroll_policy; } /** * mx_kinetic_scroll_view_set_acceleration_factor: * @scroll: A #MxKineticScrollView * @acceleration_factor: The acceleration factor * * Factor applied to the initial momentum. * * Since: 1.4 */ void mx_kinetic_scroll_view_set_acceleration_factor (MxKineticScrollView *scroll, gdouble acceleration_factor) { MxKineticScrollViewPrivate *priv; g_return_if_fail (MX_IS_KINETIC_SCROLL_VIEW (scroll)); g_return_if_fail (acceleration_factor >= 0.0); priv = scroll->priv; if (priv->acceleration_factor != acceleration_factor) { priv->acceleration_factor = acceleration_factor; g_object_notify (G_OBJECT (scroll), "acceleration-factor"); } } /** * mx_kinetic_scroll_view_get_acceleration_factor: * @scroll: A #MxKineticScrollView * * Retrieves the initial acceleration factor of the kinetic scroll-view. * * Returns: The initial acceleration factor of the kinetic scroll-view * * Since: 1.4 */ gdouble mx_kinetic_scroll_view_get_acceleration_factor (MxKineticScrollView *scroll) { g_return_val_if_fail (MX_IS_KINETIC_SCROLL_VIEW (scroll), 1.0); return scroll->priv->acceleration_factor; } /** * mx_kinetic_scroll_view_set_clamp_duration: * @scroll: A #MxKineticScrollView * @clamp_duration: Clamp duration * * Duration of the adjustment clamp animation. * * Since: 1.4 */ void mx_kinetic_scroll_view_set_clamp_duration (MxKineticScrollView *scroll, guint clamp_duration) { MxKineticScrollViewPrivate *priv; g_return_if_fail (MX_IS_KINETIC_SCROLL_VIEW (scroll)); priv = scroll->priv; if (priv->clamp_duration != clamp_duration) { priv->clamp_duration = clamp_duration; g_object_notify (G_OBJECT (scroll), "clamp-duration"); } } /** * mx_kinetic_scroll_view_get_clamp_duration: * @scroll: A #MxKineticScrollView * * Retrieves the duration of the adjustment clamp animation. * * Returns: Clamp duration * * Since: 1.4 */ guint mx_kinetic_scroll_view_get_clamp_duration (MxKineticScrollView *scroll) { g_return_val_if_fail (MX_IS_KINETIC_SCROLL_VIEW (scroll), 250); return scroll->priv->clamp_duration; } /** * mx_kinetic_scroll_view_set_clamp_mode: * @scroll: A #MxKineticScrollView * @clamp_mode: Clamp mode * * Animation mode to use for the adjustment clamp animation. * * Since: 1.4 */ void mx_kinetic_scroll_view_set_clamp_mode (MxKineticScrollView *scroll, gulong clamp_mode) { MxKineticScrollViewPrivate *priv; g_return_if_fail (MX_IS_KINETIC_SCROLL_VIEW (scroll)); priv = scroll->priv; if (priv->clamp_mode != clamp_mode) { priv->clamp_mode = clamp_mode; g_object_notify (G_OBJECT (scroll), "clamp-mode"); } } /** * mx_kinetic_scroll_view_get_clamp_mode: * @scroll: A #MxKineticScrollView * * Retrieves the animation mode to use for the adjustment clamp animation. * * Returns: Clamp mode * * Since: 1.4 */ gulong mx_kinetic_scroll_view_get_clamp_mode (MxKineticScrollView *scroll) { g_return_val_if_fail (MX_IS_KINETIC_SCROLL_VIEW (scroll), CLUTTER_EASE_OUT_QUAD); return scroll->priv->clamp_mode; } /** * mx_kinetic_scroll_view_set_clamp_to_center: * @scroll: A #MxKineticScrollView * @clamp_to_center: Clamp to center * * Set whether to clamp to step increments based on the center of the page. * * Since: 1.4 */ void mx_kinetic_scroll_view_set_clamp_to_center (MxKineticScrollView *scroll, gboolean clamp_to_center) { MxKineticScrollViewPrivate *priv; g_return_if_fail (MX_IS_KINETIC_SCROLL_VIEW (scroll)); priv = scroll->priv; if (priv->clamp_to_center != clamp_to_center) { priv->clamp_to_center = clamp_to_center; g_object_notify (G_OBJECT (scroll), "clamp-to-center"); } } /** * mx_kinetic_scroll_view_get_clamp_to_center: * @scroll: A #MxKineticScrollView * * Retrieves whether to clamp to step increments based on the center of the page. * * Returns: Clamp to center * * Since: 1.4 */ gboolean mx_kinetic_scroll_view_get_clamp_to_center (MxKineticScrollView *scroll) { g_return_val_if_fail (MX_IS_KINETIC_SCROLL_VIEW (scroll), FALSE); return scroll->priv->clamp_to_center; } mx-1.4.7/mx/mx-kinetic-scroll-view.h000066400000000000000000000116121201047117600172600ustar00rootroot00000000000000/* mx-kinetic-scroll-view.h: Kinetic scrolling container actor * * Copyright (C) 2008 OpenedHand * Copyright (C) 2010 Intel Corporation. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * Boston, MA 02111-1307, USA. * * Written by: Chris Lord */ #ifndef __MX_KINETIC_SCROLL_VIEW_H__ #define __MX_KINETIC_SCROLL_VIEW_H__ #include #include G_BEGIN_DECLS #define MX_TYPE_KINETIC_SCROLL_VIEW (mx_kinetic_scroll_view_get_type()) #define MX_KINETIC_SCROLL_VIEW(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MX_TYPE_KINETIC_SCROLL_VIEW, MxKineticScrollView)) #define MX_IS_KINETIC_SCROLL_VIEW(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MX_TYPE_KINETIC_SCROLL_VIEW)) #define MX_KINETIC_SCROLL_VIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MX_TYPE_KINETIC_SCROLL_VIEW, MxKineticScrollViewClass)) #define MX_IS_KINETIC_SCROLL_VIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MX_TYPE_KINETIC_SCROLL_VIEW)) #define MX_KINETIC_SCROLL_VIEW_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MX_TYPE_KINETIC_SCROLL_VIEW, MxKineticScrollViewClass)) /** * MxKineticScrollView: * * The contents of this structure is private and should only be accessed using * the provided API. */ typedef struct _MxKineticScrollView MxKineticScrollView; typedef struct _MxKineticScrollViewPrivate MxKineticScrollViewPrivate; typedef struct _MxKineticScrollViewClass MxKineticScrollViewClass; typedef enum { MX_KINETIC_SCROLL_VIEW_STATE_IDLE, MX_KINETIC_SCROLL_VIEW_STATE_PANNING, MX_KINETIC_SCROLL_VIEW_STATE_SCROLLING, MX_KINETIC_SCROLL_VIEW_STATE_CLAMPING, } MxKineticScrollViewState; struct _MxKineticScrollView { /*< private >*/ MxBin parent_instance; MxKineticScrollViewPrivate *priv; }; struct _MxKineticScrollViewClass { MxBinClass parent_class; }; GType mx_kinetic_scroll_view_get_type (void) G_GNUC_CONST; ClutterActor *mx_kinetic_scroll_view_new (void); void mx_kinetic_scroll_view_stop (MxKineticScrollView *scroll); void mx_kinetic_scroll_view_set_deceleration (MxKineticScrollView *scroll, gdouble rate); gdouble mx_kinetic_scroll_view_get_deceleration (MxKineticScrollView *scroll); /* void mx_kinetic_scroll_view_set_buffer_size (MxKineticScrollView *scroll, guint size); guint mx_kinetic_scroll_view_get_buffer_size (MxKineticScrollView *scroll); */ void mx_kinetic_scroll_view_set_use_captured (MxKineticScrollView *scroll, gboolean use_captured); gboolean mx_kinetic_scroll_view_get_use_captured (MxKineticScrollView *scroll); void mx_kinetic_scroll_view_set_mouse_button (MxKineticScrollView *scroll, guint32 button); guint32 mx_kinetic_scroll_view_get_mouse_button (MxKineticScrollView *scroll); void mx_kinetic_scroll_view_set_overshoot (MxKineticScrollView *scroll, gdouble overshoot); gdouble mx_kinetic_scroll_view_get_overshoot (MxKineticScrollView *scroll); void mx_kinetic_scroll_view_set_scroll_policy (MxKineticScrollView *scroll, MxScrollPolicy policy); MxScrollPolicy mx_kinetic_scroll_view_get_scroll_policy ( MxKineticScrollView *scroll); void mx_kinetic_scroll_view_set_acceleration_factor (MxKineticScrollView *scroll, gdouble acceleration_factor); gdouble mx_kinetic_scroll_view_get_acceleration_factor ( MxKineticScrollView *scroll); void mx_kinetic_scroll_view_set_clamp_duration (MxKineticScrollView *scroll, guint clamp_duration); guint mx_kinetic_scroll_view_get_clamp_duration ( MxKineticScrollView *scroll); void mx_kinetic_scroll_view_set_clamp_mode (MxKineticScrollView *scroll, gulong clamp_mode); gulong mx_kinetic_scroll_view_get_clamp_mode ( MxKineticScrollView *scroll); void mx_kinetic_scroll_view_set_clamp_to_center (MxKineticScrollView *scroll, gboolean clamp_to_center); gboolean mx_kinetic_scroll_view_get_clamp_to_center ( MxKineticScrollView *scroll); G_END_DECLS #endif /* __MX_KINETIC_SCROLL_VIEW_H__ */ mx-1.4.7/mx/mx-label.c000066400000000000000000000643741201047117600144550ustar00rootroot00000000000000/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * mx-label.c: Plain label actor * * Copyright 2008,2009 Intel Corporation * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * * Written by: Thomas Wood * */ /** * SECTION:mx-label * @short_description: Widget for displaying text * * #MxLabel is a simple widget for displaying one or more lines of text. * It derives from #MxWidget to add extra style and placement functionality over * #ClutterText. The internal #ClutterText is publicly accessibly to allow * applications to set further properties. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include "mx-label.h" #include "mx-widget.h" #include "mx-stylable.h" #include "mx-private.h" #include "mx-fade-effect.h" enum { PROP_0, PROP_CLUTTER_TEXT, PROP_TEXT, PROP_USE_MARKUP, PROP_X_ALIGN, PROP_Y_ALIGN, PROP_LINE_WRAP, PROP_FADE_OUT, PROP_SHOW_TOOLTIP }; #define MX_LABEL_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), MX_TYPE_LABEL, MxLabelPrivate)) struct _MxLabelPrivate { ClutterActor *label; ClutterEffect *fade_effect; MxAlign x_align; MxAlign y_align; ClutterTimeline *fade_timeline; ClutterAlpha *fade_alpha; gint em_width; guint fade_out : 1; guint label_should_fade : 1; guint show_tooltip : 1; }; G_DEFINE_TYPE (MxLabel, mx_label, MX_TYPE_WIDGET); static void mx_label_set_property (GObject *gobject, guint prop_id, const GValue *value, GParamSpec *pspec) { MxLabel *label = MX_LABEL (gobject); switch (prop_id) { case PROP_TEXT: mx_label_set_text (label, g_value_get_string (value)); break; case PROP_USE_MARKUP: mx_label_set_use_markup (label, g_value_get_boolean (value)); break; case PROP_Y_ALIGN: mx_label_set_y_align (label, g_value_get_enum (value)); break; case PROP_X_ALIGN: mx_label_set_x_align (label, g_value_get_enum (value)); break; case PROP_LINE_WRAP: mx_label_set_line_wrap (label, g_value_get_boolean (value)); break; case PROP_FADE_OUT: mx_label_set_fade_out (label, g_value_get_boolean (value)); break; case PROP_SHOW_TOOLTIP: mx_label_set_show_tooltip (label, g_value_get_boolean (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); break; } } static void mx_label_get_property (GObject *gobject, guint prop_id, GValue *value, GParamSpec *pspec) { MxLabel *self = MX_LABEL (gobject); MxLabelPrivate *priv = self->priv; switch (prop_id) { case PROP_CLUTTER_TEXT: g_value_set_object (value, priv->label); break; case PROP_TEXT: g_value_set_string (value, clutter_text_get_text (CLUTTER_TEXT (priv->label))); break; case PROP_USE_MARKUP: g_value_set_boolean (value, mx_label_get_use_markup (self)); break; case PROP_X_ALIGN: g_value_set_enum (value, priv->x_align); break; case PROP_Y_ALIGN: g_value_set_enum (value, priv->y_align); break; case PROP_LINE_WRAP: g_value_set_boolean (value, mx_label_get_line_wrap (self)); break; case PROP_FADE_OUT: g_value_set_boolean (value, priv->fade_out); break; case PROP_SHOW_TOOLTIP: g_value_set_boolean (value, priv->show_tooltip); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); break; } } static void mx_label_style_changed (MxWidget *self, MxStyleChangedFlags flags) { MxLabelPrivate *priv = MX_LABEL (self)->priv; mx_stylable_apply_clutter_text_attributes (MX_STYLABLE (self), CLUTTER_TEXT (priv->label)); } static void mx_label_get_preferred_width (ClutterActor *actor, gfloat for_height, gfloat *min_width_p, gfloat *natural_width_p) { MxLabelPrivate *priv = MX_LABEL (actor)->priv; MxPadding padding = { 0, }; mx_widget_get_padding (MX_WIDGET (actor), &padding); for_height -= padding.top + padding.bottom; clutter_actor_get_preferred_width (priv->label, for_height, min_width_p, natural_width_p); /* If we're fading out, make sure our minimum width is zero */ if (priv->fade_out && min_width_p) *min_width_p = 0; if (min_width_p) *min_width_p += padding.left + padding.right; if (natural_width_p) *natural_width_p += padding.left + padding.right; } static void mx_label_get_preferred_height (ClutterActor *actor, gfloat for_width, gfloat *min_height_p, gfloat *natural_height_p) { MxLabelPrivate *priv = MX_LABEL (actor)->priv; MxPadding padding = { 0, }; mx_widget_get_padding (MX_WIDGET (actor), &padding); for_width -= padding.left + padding.right; clutter_actor_get_preferred_height (priv->label, for_width, min_height_p, natural_height_p); if (min_height_p) *min_height_p += padding.top + padding.bottom; if (natural_height_p) *natural_height_p += padding.top + padding.bottom; } static void mx_label_allocate (ClutterActor *actor, const ClutterActorBox *box, ClutterAllocationFlags flags) { MxLabelPrivate *priv = MX_LABEL (actor)->priv; gboolean label_did_fade = priv->label_should_fade; ClutterActorClass *parent_class; ClutterActorBox child_box; gboolean x_fill, y_fill; gfloat avail_width; parent_class = CLUTTER_ACTOR_CLASS (mx_label_parent_class); parent_class->allocate (actor, box, flags); mx_widget_get_available_area (MX_WIDGET (actor), box, &child_box); avail_width = child_box.x2 - child_box.x1; /* The default behaviour of ClutterText is to align to the * top-left when it gets more space than is needed. Because * of this behaviour, if we're aligning to the left, we can * assign all our horizontal space to the label without * measuring it (i.e. x-fill), and the same applies for * aligning to the top and vertical space. */ x_fill = (priv->x_align == MX_ALIGN_START) ? TRUE : FALSE; y_fill = (priv->y_align == MX_ALIGN_START) ? TRUE : FALSE; mx_allocate_align_fill (priv->label, &child_box, priv->x_align, priv->y_align, x_fill, y_fill); priv->label_should_fade = FALSE; if (priv->fade_out) { /* If we're fading out, make sure the label has its full width * allocated. This ensures that the offscreen effect has the full * label inside its texture. */ gfloat label_width; clutter_actor_get_preferred_width (priv->label, -1, NULL, &label_width); if (label_width > avail_width) { priv->label_should_fade = TRUE; child_box.x2 = child_box.x1 + label_width; } mx_fade_effect_set_bounds (MX_FADE_EFFECT (priv->fade_effect), 0, 0, MIN (label_width, avail_width), 0); } /* Allocate the label */ clutter_actor_allocate (priv->label, &child_box, flags); if (priv->show_tooltip) { PangoLayout *layout; const gchar *text; layout = clutter_text_get_layout (CLUTTER_TEXT (priv->label)); if (pango_layout_is_ellipsized (layout)) text = clutter_text_get_text (CLUTTER_TEXT (priv->label)); else text = NULL; mx_widget_set_tooltip_text (MX_WIDGET (actor), text); } /* Animate in/out the faded end of the label */ if (label_did_fade != priv->label_should_fade) { /* Begin/reverse the fading timeline when necessary */ if (priv->label_should_fade) clutter_timeline_set_direction (priv->fade_timeline, CLUTTER_TIMELINE_FORWARD); else clutter_timeline_set_direction (priv->fade_timeline, CLUTTER_TIMELINE_BACKWARD); if (!clutter_timeline_is_playing (priv->fade_timeline)) clutter_timeline_rewind (priv->fade_timeline); clutter_timeline_start (priv->fade_timeline); } } static void mx_label_paint (ClutterActor *actor) { MxLabelPrivate *priv = MX_LABEL (actor)->priv; ClutterActorClass *parent_class; parent_class = CLUTTER_ACTOR_CLASS (mx_label_parent_class); parent_class->paint (actor); clutter_actor_paint (priv->label); _mx_fade_effect_set_freeze_update (MX_FADE_EFFECT (priv->fade_effect), TRUE); } static void mx_label_pick (ClutterActor *actor, const ClutterColor *pick_color) { MxLabelPrivate *priv = MX_LABEL (actor)->priv; ClutterActorClass *parent_class; parent_class = CLUTTER_ACTOR_CLASS (mx_label_parent_class); parent_class->pick (actor, pick_color); clutter_actor_paint (priv->label); } static void mx_label_map (ClutterActor *actor) { MxLabelPrivate *priv = MX_LABEL (actor)->priv; CLUTTER_ACTOR_CLASS (mx_label_parent_class)->map (actor); clutter_actor_map (priv->label); } static void mx_label_unmap (ClutterActor *actor) { MxLabelPrivate *priv = MX_LABEL (actor)->priv; if (priv->label) clutter_actor_unmap (priv->label); CLUTTER_ACTOR_CLASS (mx_label_parent_class)->unmap (actor); } static void mx_label_dispose (GObject *actor) { MxLabelPrivate *priv = MX_LABEL (actor)->priv; if (priv->fade_timeline) { clutter_timeline_stop (priv->fade_timeline); g_object_unref (priv->fade_timeline); priv->fade_timeline = NULL; } if (priv->fade_alpha) { g_object_unref (priv->fade_alpha); priv->fade_alpha = NULL; } if (priv->label) { clutter_actor_destroy (priv->label); priv->label = NULL; } G_OBJECT_CLASS (mx_label_parent_class)->dispose (actor); } static void mx_label_class_init (MxLabelClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass); GParamSpec *pspec; g_type_class_add_private (klass, sizeof (MxLabelPrivate)); gobject_class->set_property = mx_label_set_property; gobject_class->get_property = mx_label_get_property; gobject_class->dispose = mx_label_dispose; actor_class->paint = mx_label_paint; actor_class->pick = mx_label_pick; actor_class->allocate = mx_label_allocate; actor_class->get_preferred_width = mx_label_get_preferred_width; actor_class->get_preferred_height = mx_label_get_preferred_height; actor_class->map = mx_label_map; actor_class->unmap = mx_label_unmap; pspec = g_param_spec_object ("clutter-text", "Clutter Text", "Internal ClutterText actor", CLUTTER_TYPE_TEXT, G_PARAM_READABLE); g_object_class_install_property (gobject_class, PROP_CLUTTER_TEXT, pspec); pspec = g_param_spec_string ("text", "Text", "Text of the label", NULL, MX_PARAM_READWRITE | MX_PARAM_TRANSLATEABLE); g_object_class_install_property (gobject_class, PROP_TEXT, pspec); pspec = g_param_spec_boolean ("use-markup", "Use markup", "Whether the text of the label should be " "treated as Pango markup", FALSE, MX_PARAM_READWRITE); g_object_class_install_property (gobject_class, PROP_USE_MARKUP, pspec); pspec = g_param_spec_enum ("x-align", "X Align", "Horizontal position of the text layout", MX_TYPE_ALIGN, MX_ALIGN_START, MX_PARAM_READWRITE); g_object_class_install_property (gobject_class, PROP_X_ALIGN, pspec); pspec = g_param_spec_enum ("y-align", "Y Align", "Vertical position of the text layout", MX_TYPE_ALIGN, MX_ALIGN_START, MX_PARAM_READWRITE); g_object_class_install_property (gobject_class, PROP_Y_ALIGN, pspec); /** * MxLabel:line-wrap: * * Whether to wrap the lines of #MxLabel:text if the contents * exceed the available allocation. * * Since: 1.2 */ pspec = g_param_spec_boolean ("line-wrap", "Line wrap", "If set, wrap the lines if the text becomes too wide", FALSE, MX_PARAM_READWRITE); g_object_class_install_property (gobject_class, PROP_LINE_WRAP, pspec); pspec = g_param_spec_boolean ("fade-out", "Fade out", "Fade out the end of the label, instead " "of ellipsizing", FALSE, MX_PARAM_READWRITE); g_object_class_install_property (gobject_class, PROP_FADE_OUT, pspec); /** * MxLabel:show-tooltip: * * Show a tooltip when there is not enough space to display the text. If set * to %TRUE, this will also cause the #ClutterActor:reactive property to be * enabled. * * Since: 1.4 */ pspec = g_param_spec_boolean ("show-tooltip", "Show Tooltip", "Show a tooltip when there is not enough space" " to display the text.", FALSE, MX_PARAM_READWRITE); g_object_class_install_property (gobject_class, PROP_SHOW_TOOLTIP, pspec); } static void mx_label_single_line_mode_cb (ClutterText *text, GParamSpec *pspec, MxLabel *self) { MxLabelPrivate *priv = self->priv; if (!clutter_text_get_single_line_mode (text) && priv->fade_out) mx_label_set_fade_out (self, FALSE); } static void mx_label_label_changed_cb (MxLabel *label) { MxLabelPrivate *priv = label->priv; /* Enable updating of the off-screen texture */ _mx_fade_effect_set_freeze_update (MX_FADE_EFFECT (priv->fade_effect), FALSE); } static void mx_label_font_description_cb (ClutterText *text, GParamSpec *pspec, MxLabel *self) { PangoFontDescription *font; MxLabelPrivate *priv = self->priv; /* Find out the em-width - code pretty much copied from Clutter, * clutter-backend.c, get_units_per_em () */ font = clutter_text_get_font_description (text); if (font) { gint i_dpi; gdouble dpi; gdouble font_size = 0; gint pango_size = pango_font_description_get_size (font); ClutterSettings *settings = clutter_settings_get_default (); g_object_get (G_OBJECT (settings), "font-dpi", &i_dpi, NULL); dpi = i_dpi / 1024.0; if (pango_font_description_get_size_is_absolute (font)) font_size = pango_size / PANGO_SCALE; else font_size = pango_size / PANGO_SCALE * dpi / 96.f; priv->em_width = (1.2f * font_size) * dpi / 96.f; mx_fade_effect_set_border (MX_FADE_EFFECT (priv->fade_effect), 0, priv->em_width * 5, 0, 0); } } static void mx_label_fade_new_frame_cb (ClutterTimeline *timeline, gint msecs, MxLabel *self) { guint8 a; ClutterColor color; MxLabelPrivate *priv = self->priv; a = (1.0 - clutter_alpha_get_alpha (priv->fade_alpha)) * 255; color.red = a; color.green = a; color.blue = a; color.alpha = a; mx_fade_effect_set_color (MX_FADE_EFFECT (priv->fade_effect), &color); clutter_actor_queue_redraw (CLUTTER_ACTOR (self)); } static void mx_label_fade_started_cb (ClutterTimeline *timeline, MxLabel *label) { clutter_actor_meta_set_enabled (CLUTTER_ACTOR_META (label->priv->fade_effect), TRUE); } static void mx_label_fade_completed_cb (ClutterTimeline *timeline, MxLabel *label) { MxLabelPrivate *priv = label->priv; if (!priv->label_should_fade) clutter_actor_meta_set_enabled (CLUTTER_ACTOR_META (priv->fade_effect), FALSE); } static void mx_label_init (MxLabel *label) { MxLabelPrivate *priv; const ClutterColor opaque = { 0xff, 0xff, 0xff, 0xff }; label->priv = priv = MX_LABEL_GET_PRIVATE (label); priv->label = g_object_new (CLUTTER_TYPE_TEXT, "ellipsize", PANGO_ELLIPSIZE_END, NULL); clutter_actor_set_parent (priv->label, CLUTTER_ACTOR (label)); priv->fade_effect = mx_fade_effect_new (); mx_fade_effect_set_color (MX_FADE_EFFECT (priv->fade_effect), &opaque); clutter_actor_add_effect (priv->label, priv->fade_effect); clutter_actor_meta_set_enabled (CLUTTER_ACTOR_META (priv->fade_effect), FALSE); g_signal_connect (label, "style-changed", G_CALLBACK (mx_label_style_changed), NULL); g_signal_connect (priv->label, "notify::single-line-mode", G_CALLBACK (mx_label_single_line_mode_cb), label); g_signal_connect_swapped (priv->label, "queue-redraw", G_CALLBACK (mx_label_label_changed_cb), label); priv->fade_timeline = clutter_timeline_new (250); priv->fade_alpha = clutter_alpha_new_full (priv->fade_timeline, CLUTTER_EASE_OUT_QUAD); g_signal_connect (priv->fade_timeline, "new-frame", G_CALLBACK (mx_label_fade_new_frame_cb), label); g_signal_connect (priv->fade_timeline, "started", G_CALLBACK (mx_label_fade_started_cb), label); g_signal_connect (priv->fade_timeline, "completed", G_CALLBACK (mx_label_fade_completed_cb), label); } /** * mx_label_new: * * Create a new #MxLabel * * Returns: a new #MxLabel */ ClutterActor * mx_label_new (void) { return g_object_new (MX_TYPE_LABEL, NULL); } /** * mx_label_new_with_text: * @text: text to set the label to * * Create a new #MxLabel with the specified label * * Returns: a new #MxLabel */ ClutterActor * mx_label_new_with_text (const gchar *text) { if (text == NULL || *text == '\0') return g_object_new (MX_TYPE_LABEL, NULL); else return g_object_new (MX_TYPE_LABEL, "text", text, NULL); } /** * mx_label_get_text: * @label: a #MxLabel * * Get the text displayed on the label * * Returns: the text for the label. This must not be freed by the application */ const gchar * mx_label_get_text (MxLabel *label) { g_return_val_if_fail (MX_IS_LABEL (label), NULL); return clutter_text_get_text (CLUTTER_TEXT (label->priv->label)); } /** * mx_label_set_text: * @label: a #MxLabel * @text: text to set the label to * * Sets the text displayed on the label */ void mx_label_set_text (MxLabel *label, const gchar *text) { MxLabelPrivate *priv; g_return_if_fail (MX_IS_LABEL (label)); g_return_if_fail (text != NULL); priv = label->priv; if (clutter_text_get_use_markup (CLUTTER_TEXT (priv->label))) clutter_text_set_markup (CLUTTER_TEXT (priv->label), text); else clutter_text_set_text (CLUTTER_TEXT (priv->label), text); g_object_notify (G_OBJECT (label), "text"); } /** * mx_label_get_use_markup: * @label: a #MxLabel * * Determines whether the text of the label is being treated as Pango markup. * * Returns: %TRUE if the text of the label is treated as Pango markup, * %FALSE otherwise. * * Since: 1.2 */ gboolean mx_label_get_use_markup (MxLabel *label) { g_return_val_if_fail (MX_IS_LABEL (label), FALSE); return clutter_text_get_use_markup (CLUTTER_TEXT (label->priv->label)); } /** * mx_label_set_use_markup: * @label: a #MxLabel * @use_markup: %TRUE to use Pango markup, %FALSE otherwise * * Sets whether the text of the label should be treated as Pango markup. */ void mx_label_set_use_markup (MxLabel *label, gboolean use_markup) { MxLabelPrivate *priv; g_return_if_fail (MX_IS_LABEL (label)); priv = label->priv; clutter_text_set_use_markup (CLUTTER_TEXT (priv->label), use_markup); g_object_notify (G_OBJECT (label), "use-markup"); } /** * mx_label_get_clutter_text: * @label: a #MxLabel * * Retrieve the internal #ClutterText so that extra parameters can be set * * Returns: (transfer none): the #ClutterText used by #MxLabel. The label * is owned by the #MxLabel and should not be unref'ed by the application. */ ClutterActor* mx_label_get_clutter_text (MxLabel *label) { g_return_val_if_fail (MX_IS_LABEL (label), NULL); return label->priv->label; } void mx_label_set_x_align (MxLabel *label, MxAlign align) { g_return_if_fail (MX_IS_LABEL (label)); if (align != label->priv->x_align) { label->priv->x_align = align; clutter_actor_queue_relayout (CLUTTER_ACTOR (label)); g_object_notify (G_OBJECT (label), "x-align"); } } MxAlign mx_label_get_x_align (MxLabel *label) { g_return_val_if_fail (MX_IS_LABEL (label), 0); return label->priv->x_align; } void mx_label_set_y_align (MxLabel *label, MxAlign align) { g_return_if_fail (MX_IS_LABEL (label)); if (align != label->priv->y_align) { label->priv->y_align = align; clutter_actor_queue_relayout (CLUTTER_ACTOR (label)); g_object_notify (G_OBJECT (label), "y-align"); } } MxAlign mx_label_get_y_align (MxLabel *label) { g_return_val_if_fail (MX_IS_LABEL (label), 0); return label->priv->y_align; } /** * mx_label_set_line_wrap: * @label: An #MxLabel * @line_wrap: new value of the line-wrap property. * * Set the value of the #MxLabel:line-wrap property. * * Since: 1.2 */ void mx_label_set_line_wrap (MxLabel *label, gboolean line_wrap) { g_return_if_fail (MX_IS_LABEL (label)); clutter_text_set_line_wrap (CLUTTER_TEXT (label->priv->label), line_wrap); g_object_notify (G_OBJECT (label), "line-wrap"); } /** * mx_label_get_line_wrap: * @label: An #MxLabel * * Get the value of the #MxLabel:line-wrap property. * * Returns: %TRUE if the "line-wrap" property is set. * * Since: 1.2 */ gboolean mx_label_get_line_wrap (MxLabel *label) { g_return_val_if_fail (MX_IS_LABEL (label), FALSE); return clutter_text_get_line_wrap (CLUTTER_TEXT (label->priv->label)); } /** * mx_label_set_fade_out: * @label: A #MxLabel * @fade: %TRUE to fade out, %FALSE otherwise * * Set whether to fade out the end of the label, instead of ellipsizing. * Enabling this mode will also set the #ClutterText:single-line-mode and * #ClutterText:ellipsize properties. * * Since: 1.2 */ void mx_label_set_fade_out (MxLabel *label, gboolean fade) { MxLabelPrivate *priv; g_return_if_fail (MX_IS_LABEL (label)); priv = label->priv; if (priv->fade_out != fade) { priv->fade_out = fade; g_object_notify (G_OBJECT (label), "fade-out"); /* Enable the fade-effect */ if (fade) { priv->label_should_fade = FALSE; clutter_text_set_single_line_mode (CLUTTER_TEXT (priv->label), TRUE); clutter_text_set_ellipsize (CLUTTER_TEXT (priv->label), PANGO_ELLIPSIZE_NONE); } /* If we need to fade, listen for the font-description changing so * we can keep track of the em-width of the label. */ if (fade) { g_signal_connect (priv->label, "notify::font-description", G_CALLBACK (mx_label_font_description_cb), label); mx_label_font_description_cb (CLUTTER_TEXT (priv->label), NULL, label); } else { g_signal_handlers_disconnect_by_func (priv->label, mx_label_font_description_cb, label); } } } /** * mx_label_get_fade_out: * @label: A #MxLabel * * Determines whether the label has been set to fade out when there isn't * enough space allocated to display the entire label. * * Returns: %TRUE if the label is set to fade out, %FALSE otherwise * * Since: 1.2 */ gboolean mx_label_get_fade_out (MxLabel *label) { g_return_val_if_fail (MX_IS_LABEL (label), FALSE); return label->priv->fade_out; } /** * mx_label_set_show_tooltip: * @label: A #MxLabel * @show_tooltip: %TRUE if the tooltip should be shown * * Set the value of the #MxLabel:show-tooltip property * * Since: 1.4 */ void mx_label_set_show_tooltip (MxLabel *label, gboolean show_tooltip) { MxLabelPrivate *priv; g_return_if_fail (MX_IS_LABEL (label)); priv = label->priv; if (priv->show_tooltip != show_tooltip) { priv->show_tooltip = show_tooltip; clutter_actor_queue_relayout (CLUTTER_ACTOR (label)); g_object_notify (G_OBJECT (label), "show-tooltip"); } } /** * mx_label_get_show_tooltip: * @label: A #MxLabel * * Returns the current value of the #MxLabel:show-tooltip property. * * Returns: %TRUE if the #MxLabel:show-tooltip property is enabled * * Since: 1.4 */ gboolean mx_label_get_show_tooltip (MxLabel *label) { g_return_val_if_fail (MX_IS_LABEL (label), FALSE); return label->priv->show_tooltip; } mx-1.4.7/mx/mx-label.h000066400000000000000000000073531201047117600144540ustar00rootroot00000000000000/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * mx-label.h: Plain label actor * * Copyright 2008, 2009 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * Boston, MA 02111-1307, USA. * * Written by: Thomas Wood * */ #if !defined(MX_H_INSIDE) && !defined(MX_COMPILATION) #error "Only can be included directly.h" #endif #ifndef __MX_LABEL_H__ #define __MX_LABEL_H__ G_BEGIN_DECLS #include #define MX_TYPE_LABEL (mx_label_get_type ()) #define MX_LABEL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MX_TYPE_LABEL, MxLabel)) #define MX_IS_LABEL(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MX_TYPE_LABEL)) #define MX_LABEL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MX_TYPE_LABEL, MxLabelClass)) #define MX_IS_LABEL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MX_TYPE_LABEL)) #define MX_LABEL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MX_TYPE_LABEL, MxLabelClass)) typedef struct _MxLabel MxLabel; typedef struct _MxLabelPrivate MxLabelPrivate; typedef struct _MxLabelClass MxLabelClass; /** * MxLabel: * * The contents of this structure is private and should only be accessed using * the provided API. */ struct _MxLabel { /*< private >*/ MxWidget parent_instance; MxLabelPrivate *priv; }; struct _MxLabelClass { MxWidgetClass parent_class; /* padding for future expansion */ void (*_padding_0) (void); void (*_padding_1) (void); void (*_padding_2) (void); void (*_padding_3) (void); void (*_padding_4) (void); }; GType mx_label_get_type (void) G_GNUC_CONST; ClutterActor *mx_label_new (void); ClutterActor *mx_label_new_with_text (const gchar *text); const gchar *mx_label_get_text (MxLabel *label); void mx_label_set_text (MxLabel *label, const gchar *text); gboolean mx_label_get_use_markup (MxLabel *label); void mx_label_set_use_markup (MxLabel *label, gboolean use_markup); ClutterActor * mx_label_get_clutter_text (MxLabel *label); MxAlign mx_label_get_x_align (MxLabel *label); void mx_label_set_x_align (MxLabel *label, MxAlign align); MxAlign mx_label_get_y_align (MxLabel *label); void mx_label_set_y_align (MxLabel *label, MxAlign align); gboolean mx_label_get_line_wrap (MxLabel *label); void mx_label_set_line_wrap (MxLabel *label, gboolean line_wrap); void mx_label_set_fade_out (MxLabel *label, gboolean fade); gboolean mx_label_get_fade_out (MxLabel *label); void mx_label_set_show_tooltip (MxLabel *label, gboolean show_tooltip); gboolean mx_label_get_show_tooltip (MxLabel *label); G_END_DECLS #endif /* __MX_LABEL_H__ */ mx-1.4.7/mx/mx-list-view.c000066400000000000000000000403011201047117600153010ustar00rootroot00000000000000/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * mx-list-view.c: MxBoxLayout powered by a model * * Copyright 2008, 2009 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * Boston, MA 02111-1307, USA. * * Written by: Thomas Wood * */ /** * SECTION:mx-list-view * @short_description: a box layout driven by a model. * * #MxListView is a box layout container driven by a #ClutterModel. Children * are created for each row in the model, either by creating actors from the * supplied #ClutterActor derived type, or from a #MxItemFactory. * * Data is set on the children by mapping columns in the model to object * properties on the children. */ #include "mx-list-view.h" #include "mx-box-layout.h" #include "mx-private.h" #include "mx-item-factory.h" G_DEFINE_TYPE (MxListView, mx_list_view, MX_TYPE_BOX_LAYOUT) #define LIST_VIEW_PRIVATE(o) \ (G_TYPE_INSTANCE_GET_PRIVATE ((o), MX_TYPE_LIST_VIEW, MxListViewPrivate)) typedef struct { gchar *name; gint col; } AttributeData; enum { PROP_0, PROP_MODEL, PROP_ITEM_TYPE, PROP_FACTORY }; struct _MxListViewPrivate { ClutterModel *model; GSList *attributes; GType item_type; MxItemFactory *factory; gulong filter_changed; gulong row_added; gulong row_changed; gulong row_removed; gulong sort_changed; guint is_frozen : 1; }; /* gobject implementations */ static void mx_list_view_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { MxListViewPrivate *priv = MX_LIST_VIEW (object)->priv; switch (property_id) { case PROP_MODEL: g_value_set_object (value, priv->model); break; case PROP_ITEM_TYPE: g_value_set_gtype (value, priv->item_type); break; case PROP_FACTORY: g_value_set_object (value, priv->factory); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } } static void mx_list_view_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { switch (property_id) { case PROP_MODEL: mx_list_view_set_model ((MxListView*) object, (ClutterModel*) g_value_get_object (value)); break; case PROP_ITEM_TYPE: mx_list_view_set_item_type ((MxListView*) object, g_value_get_gtype (value)); break; case PROP_FACTORY: mx_list_view_set_factory ((MxListView*) object, (MxItemFactory*) g_value_get_object (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } } static void mx_list_view_dispose (GObject *object) { MxListViewPrivate *priv = MX_LIST_VIEW (object)->priv; /* This will cause the unref of the model and also disconnect the signals */ mx_list_view_set_model (MX_LIST_VIEW (object), NULL); if (priv->factory) { g_object_unref (priv->factory); priv->factory = NULL; } G_OBJECT_CLASS (mx_list_view_parent_class)->dispose (object); } static void free_attribute (AttributeData *data) { g_free (data->name); g_free (data); } static void mx_list_view_finalize (GObject *object) { MxListViewPrivate *priv = MX_LIST_VIEW (object)->priv; if (priv->attributes) { g_slist_foreach (priv->attributes, (GFunc) free_attribute, NULL); g_slist_free (priv->attributes); priv->attributes = NULL; } G_OBJECT_CLASS (mx_list_view_parent_class)->finalize (object); } static void mx_list_view_class_init (MxListViewClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); GParamSpec *pspec; g_type_class_add_private (klass, sizeof (MxListViewPrivate)); object_class->get_property = mx_list_view_get_property; object_class->set_property = mx_list_view_set_property; object_class->dispose = mx_list_view_dispose; object_class->finalize = mx_list_view_finalize; pspec = g_param_spec_object ("model", "model", "The model for the item view", CLUTTER_TYPE_MODEL, MX_PARAM_READWRITE); g_object_class_install_property (object_class, PROP_MODEL, pspec); pspec = g_param_spec_gtype ("item-type", "Item Type", "The GType to use as the items in the view. " "Must be a subclass of ClutterActor", CLUTTER_TYPE_ACTOR, MX_PARAM_READWRITE); g_object_class_install_property (object_class, PROP_ITEM_TYPE, pspec); /* Note, interfaces aren't necessarily objects, so you can't use * MX_TYPE_ITEM_FACTORY here. The function mx_list_view_set_factory does * a type check, so this is still safe. */ pspec = g_param_spec_object ("factory", "Factory", "The MxItemFactory used for creating new items.", G_TYPE_OBJECT /*MX_TYPE_ITEM_FACTORY*/, MX_PARAM_READWRITE); g_object_class_install_property (object_class, PROP_FACTORY, pspec); } static void mx_list_view_init (MxListView *list_view) { list_view->priv = LIST_VIEW_PRIVATE (list_view); mx_box_layout_set_orientation (MX_BOX_LAYOUT (list_view), MX_ORIENTATION_VERTICAL); } /* model monitors */ static void model_changed_cb (ClutterModel *model, MxListView *list_view) { GSList *p; GList *l, *children; MxListViewPrivate *priv = list_view->priv; ClutterModelIter *iter = NULL; gint model_n = 0, child_n = 0; /* bail out if we don't yet have an item type or a factory */ if (!priv->item_type && !priv->factory) return; if (priv->is_frozen) return; if (priv->item_type) { /* check the item-type is an descendant of ClutterActor */ if (!g_type_is_a (priv->item_type, CLUTTER_TYPE_ACTOR)) { g_warning ("%s is not a subclass of ClutterActor and therefore" " cannot be used as items in an MxListView", g_type_name (priv->item_type)); return; } } children = clutter_container_get_children (CLUTTER_CONTAINER (list_view)); child_n = g_list_length (children); if (model) model_n = clutter_model_get_n_rows (priv->model); else model_n = 0; /* add children as needed */ while (model_n > child_n) { ClutterActor *new_child; if (priv->item_type) { new_child = g_object_new (priv->item_type, NULL); } else { new_child = mx_item_factory_create (priv->factory); } clutter_container_add_actor (CLUTTER_CONTAINER (list_view), new_child); child_n++; } /* remove children as needed */ l = g_list_last (children); while (child_n > model_n) { clutter_container_remove_actor (CLUTTER_CONTAINER (list_view), (ClutterActor*) l->data); l = g_list_previous (l); child_n--; } g_list_free (children); if (!priv->model) return; children = clutter_container_get_children (CLUTTER_CONTAINER (list_view)); /* set the properties on the children */ iter = clutter_model_get_first_iter (priv->model); l = children; while (iter && !clutter_model_iter_is_last (iter)) { GObject *child; child = G_OBJECT (l->data); g_object_freeze_notify (child); for (p = priv->attributes; p; p = p->next) { GValue value = { 0, }; AttributeData *attr = p->data; clutter_model_iter_get_value (iter, attr->col, &value); g_object_set_property (child, attr->name, &value); g_value_unset (&value); } g_object_thaw_notify (child); l = g_list_next (l); clutter_model_iter_next (iter); } g_list_free (children); if (iter) g_object_unref (iter); } static void row_changed_cb (ClutterModel *model, ClutterModelIter *iter, MxListView *list_view) { model_changed_cb (model, list_view); } static void row_removed_cb (ClutterModel *model, ClutterModelIter *iter, MxListView *list_view) { GList *children; GList *l; ClutterActor *child; if (list_view->priv->is_frozen) return; children = clutter_container_get_children (CLUTTER_CONTAINER (list_view)); l = g_list_nth (children, clutter_model_iter_get_row (iter)); child = (ClutterActor *) l->data; clutter_container_remove_actor (CLUTTER_CONTAINER (list_view), child); g_list_free (children); } /* public api */ /** * mx_list_view_new: * * Create a new #MxListView * * Returns: a newly allocated #MxListView */ ClutterActor * mx_list_view_new (void) { return g_object_new (MX_TYPE_LIST_VIEW, NULL); } /** * mx_list_view_get_item_type: * @list_view: An #MxListView * * Get the item type currently being used to create items * * Returns: a #GType */ GType mx_list_view_get_item_type (MxListView *list_view) { g_return_val_if_fail (MX_IS_LIST_VIEW (list_view), G_TYPE_INVALID); return list_view->priv->item_type; } /** * mx_list_view_set_item_type: * @list_view: An #MxListView * @item_type: A #GType * * Set the item type used to create items representing each row in the * model */ void mx_list_view_set_item_type (MxListView *list_view, GType item_type) { g_return_if_fail (MX_IS_LIST_VIEW (list_view)); g_return_if_fail (g_type_is_a (item_type, CLUTTER_TYPE_ACTOR)); list_view->priv->item_type = item_type; /* update the view */ model_changed_cb (list_view->priv->model, list_view); } /** * mx_list_view_get_model: * @list_view: An #MxListView * * Get the model currently used by the #MxListView * * Returns: (transfer none): the current #ClutterModel */ ClutterModel* mx_list_view_get_model (MxListView *list_view) { g_return_val_if_fail (MX_IS_LIST_VIEW (list_view), NULL); return list_view->priv->model; } /** * mx_list_view_set_model: * @list_view: An #MxListView * @model: A #ClutterModel * * Set the model used by the #MxListView */ void mx_list_view_set_model (MxListView *list_view, ClutterModel *model) { MxListViewPrivate *priv; g_return_if_fail (MX_IS_LIST_VIEW (list_view)); g_return_if_fail (model == NULL || CLUTTER_IS_MODEL (model)); priv = list_view->priv; if (priv->model) { g_signal_handlers_disconnect_by_func (priv->model, (GCallback) model_changed_cb, list_view); g_signal_handlers_disconnect_by_func (priv->model, (GCallback) row_changed_cb, list_view); g_signal_handlers_disconnect_by_func (priv->model, (GCallback) row_removed_cb, list_view); g_object_unref (priv->model); priv->model = NULL; } if (model) { g_return_if_fail (CLUTTER_IS_MODEL (model)); priv->model = g_object_ref (model); priv->filter_changed = g_signal_connect (priv->model, "filter-changed", G_CALLBACK (model_changed_cb), list_view); priv->row_added = g_signal_connect (priv->model, "row-added", G_CALLBACK (row_changed_cb), list_view); priv->row_changed = g_signal_connect (priv->model, "row-changed", G_CALLBACK (row_changed_cb), list_view); /* * model_changed_cb (called from row_changed_cb) expect the row to already * have been removed, thus we need to use _after */ priv->row_removed = g_signal_connect_after (priv->model, "row-removed", G_CALLBACK (row_removed_cb), list_view); priv->sort_changed = g_signal_connect (priv->model, "sort-changed", G_CALLBACK (model_changed_cb), list_view); /* * Only do this inside this block, setting the model to NULL should have * the effect of preserving the view; just disconnect the handlers */ model_changed_cb (priv->model, list_view); } } /** * mx_list_view_add_attribute: * @list_view: An #MxListView * @attribute: Name of the attribute * @column: Column number * * Adds an attribute mapping between the current model and the objects from the * cell renderer. * */ void mx_list_view_add_attribute (MxListView *list_view, const gchar *_attribute, gint column) { MxListViewPrivate *priv; AttributeData *prop; g_return_if_fail (MX_IS_LIST_VIEW (list_view)); g_return_if_fail (_attribute != NULL); g_return_if_fail (column >= 0); priv = list_view->priv; prop = g_new (AttributeData, 1); prop->name = g_strdup (_attribute); prop->col = column; priv->attributes = g_slist_prepend (priv->attributes, prop); model_changed_cb (priv->model, list_view); } /** * mx_list_view_freeze * @list_view: An #MxListView * * Freeze the view. This means that the view will not act on changes to the * model until it is thawed. Call #mx_list_view_thaw to thaw the view. */ void mx_list_view_freeze (MxListView *list_view) { MxListViewPrivate *priv; g_return_if_fail (MX_IS_LIST_VIEW (list_view)); priv = list_view->priv; priv->is_frozen = TRUE; } /** * mx_list_view_thaw * @list_view: An #MxListView * * Thaw the view. This means that the view will now act on changes to the * model. */ void mx_list_view_thaw (MxListView *list_view) { MxListViewPrivate *priv; g_return_if_fail (MX_IS_LIST_VIEW (list_view)); priv = list_view->priv; priv->is_frozen = FALSE; /* Repopulate */ model_changed_cb (priv->model, list_view); } /** * mx_list_view_set_factory: * @list_view: A #MxListView * @factory: (allow-none): A #MxItemFactory * * Sets @factory to be the factory used for creating new list items */ void mx_list_view_set_factory (MxListView *list_view, MxItemFactory *factory) { MxListViewPrivate *priv; g_return_if_fail (MX_IS_LIST_VIEW (list_view)); g_return_if_fail (!factory || MX_IS_ITEM_FACTORY (factory)); priv = list_view->priv; if (priv->factory == factory) return; if (priv->factory) { g_object_unref (priv->factory); priv->factory = NULL; } if (factory) priv->factory = g_object_ref (factory); g_object_notify (G_OBJECT (list_view), "factory"); } /** * mx_list_view_get_factory: * @list_view: A #MxListView * * Gets the #MxItemFactory used for creating new list items. * * Returns: (transfer none): A #MxItemFactory. */ MxItemFactory * mx_list_view_get_factory (MxListView *list_view) { g_return_val_if_fail (MX_IS_LIST_VIEW (list_view), NULL); return list_view->priv->factory; } mx-1.4.7/mx/mx-list-view.h000066400000000000000000000065061201047117600153170ustar00rootroot00000000000000/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * mx-item-view.h: MxBoxLayout powered by a model * * Copyright 2009,2010 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * Boston, MA 02111-1307, USA. * * Written by: Thomas Wood * */ #if !defined(MX_H_INSIDE) && !defined(MX_COMPILATION) #error "Only can be included directly.h" #endif #ifndef _MX_LIST_VIEW_H #define _MX_LIST_VIEW_H #include #include "mx-box-layout.h" #include "mx-item-factory.h" G_BEGIN_DECLS #define MX_TYPE_LIST_VIEW mx_list_view_get_type() #define MX_LIST_VIEW(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ MX_TYPE_LIST_VIEW, MxListView)) #define MX_LIST_VIEW_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), \ MX_TYPE_LIST_VIEW, MxListViewClass)) #define MX_IS_LIST_VIEW(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \ MX_TYPE_LIST_VIEW)) #define MX_IS_LIST_VIEW_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE ((klass), \ MX_TYPE_LIST_VIEW)) #define MX_LIST_VIEW_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), \ MX_TYPE_LIST_VIEW, MxListViewClass)) typedef struct _MxListViewPrivate MxListViewPrivate; /** * MxListView: * * The contents of the this structure are private and should only be accessed * through the public API. */ typedef struct { /*< private >*/ MxBoxLayout parent; MxListViewPrivate *priv; } MxListView; typedef struct { MxBoxLayoutClass parent_class; /* padding for future expansion */ void (*_padding_0) (void); void (*_padding_1) (void); void (*_padding_2) (void); void (*_padding_3) (void); void (*_padding_4) (void); } MxListViewClass; GType mx_list_view_get_type (void); ClutterActor *mx_list_view_new (void); void mx_list_view_set_model (MxListView *list_view, ClutterModel *model); ClutterModel* mx_list_view_get_model (MxListView *list_view); void mx_list_view_set_item_type (MxListView *list_view, GType item_type); GType mx_list_view_get_item_type (MxListView *list_view); void mx_list_view_add_attribute (MxListView *list_view, const gchar *attribute, gint column); void mx_list_view_freeze (MxListView *list_view); void mx_list_view_thaw (MxListView *list_view); void mx_list_view_set_factory (MxListView *list_view, MxItemFactory *factory); MxItemFactory *mx_list_view_get_factory (MxListView *list_view); G_END_DECLS #endif /* _MX_LIST_VIEW_H */ mx-1.4.7/mx/mx-marshal.list000066400000000000000000000005421201047117600155410ustar00rootroot00000000000000VOID:OBJECT VOID:VOID VOID:PARAM VOID:POINTER VOID:UINT VOID:ULONG VOID:FLAGS VOID:BOXED VOID:UINT,UINT VOID:UINT,OBJECT VOID:ULONG,BOXED VOID:ULONG,OBJECT VOID:ULONG,OBJECT,OBJECT VOID:OBJECT,OBJECT VOID:STRING,OBJECT VOID:OBJECT,OBJECT,INT,INT VOID:OBJECT,FLOAT,FLOAT,INT,ENUM VOID:FLOAT,FLOAT,INT,ENUM VOID:FLOAT,FLOAT BOOL:FLOAT,FLOAT,ENUM BOOL:VOID mx-1.4.7/mx/mx-menu.c000066400000000000000000000531501201047117600143300ustar00rootroot00000000000000/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * mx-menu.c: menu class * * Copyright (c) 2009, Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. */ /** * SECTION:mx-menu * @short_description: a menu actor representing a list of user actions * * #MxMenu displays a list of user actions, defined by a list of * #MxActions. The menu list will appear above all other actors. */ #include "mx-menu.h" #include "mx-label.h" #include "mx-button.h" #include "mx-stylable.h" #include "mx-focusable.h" #include "mx-focus-manager.h" static void mx_focusable_iface_init (MxFocusableIface *iface); G_DEFINE_TYPE_WITH_CODE (MxMenu, mx_menu, MX_TYPE_FLOATING_WIDGET, G_IMPLEMENT_INTERFACE (MX_TYPE_FOCUSABLE, mx_focusable_iface_init)) #define MENU_PRIVATE(o) \ (G_TYPE_INSTANCE_GET_PRIVATE ((o), MX_TYPE_MENU, MxMenuPrivate)) typedef struct { MxAction *action; MxWidget *box; } MxMenuChild; struct _MxMenuPrivate { GArray *children; gboolean transition_out; ClutterActor *stage; gulong captured_event_handler; gulong internal_focus_push : 1; }; enum { ACTION_ACTIVATED, LAST_SIGNAL }; static guint signals[LAST_SIGNAL] = { 0, }; static gboolean mx_menu_captured_event_handler (ClutterActor *actor, ClutterEvent *event, ClutterActor *menu); /* MxFocusable Interface */ static MxFocusable* mx_menu_move_focus (MxFocusable *focusable, MxFocusDirection direction, MxFocusable *from) { MxMenuPrivate *priv = MX_MENU (focusable)->priv; MxFocusable *result; MxMenuChild *child; gint i, start; /* find the current focused child */ for (i = 0; i < priv->children->len; i++) { child = &g_array_index (priv->children, MxMenuChild, i); if ((MxFocusable*) child->box == from) break; else child = NULL; } if (!child) return NULL; start = i; switch (direction) { case MX_FOCUS_DIRECTION_UP: if (i == 0) i = priv->children->len - 1; else i--; while (i >= 0) { if (i == start) break; child = &g_array_index (priv->children, MxMenuChild, i); result = mx_focusable_accept_focus (MX_FOCUSABLE (child->box), 0); if (result) return result; /* loop */ if (i == 0) i = priv->children->len; i--; } case MX_FOCUS_DIRECTION_DOWN: if (i == priv->children->len - 1) i = 0; else i++; while (i < priv->children->len) { if (i == start) break; child = &g_array_index (priv->children, MxMenuChild, i); result = mx_focusable_accept_focus (MX_FOCUSABLE (child->box), 0); if (result) return result; /* loop */ if (i == priv->children->len - 1) i = -1; i++; } case MX_FOCUS_DIRECTION_OUT: if (priv->internal_focus_push) { /* do nothing if this notification was caused internally */ priv->internal_focus_push = FALSE; return NULL; } default: break; } clutter_actor_hide (CLUTTER_ACTOR (focusable)); return NULL; } static MxFocusable* mx_menu_accept_focus (MxFocusable *focusable, MxFocusHint hint) { MxMenuPrivate *priv = MX_MENU (focusable)->priv; MxMenuChild *child; child = &g_array_index (priv->children, MxMenuChild, 0); return mx_focusable_accept_focus (MX_FOCUSABLE (child->box), 0); } static void mx_focusable_iface_init (MxFocusableIface *iface) { iface->move_focus = mx_menu_move_focus; iface->accept_focus = mx_menu_accept_focus; } static void mx_menu_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { switch (property_id) { default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } } static void mx_menu_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { switch (property_id) { default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } } static void mx_menu_free_action_at (MxMenu *menu, gint index, gboolean remove_action) { MxMenuPrivate *priv = menu->priv; MxMenuChild *child = &g_array_index (priv->children, MxMenuChild, index); clutter_actor_unparent (CLUTTER_ACTOR (child->box)); g_object_unref (child->action); if (remove_action) g_array_remove_index (priv->children, index); } static void mx_menu_dispose (GObject *object) { MxMenu *menu = MX_MENU (object); MxMenuPrivate *priv = menu->priv; if (priv->children) { gint i; for (i = 0; i < priv->children->len; i++) mx_menu_free_action_at (menu, i, FALSE); g_array_free (priv->children, TRUE); priv->children = NULL; } G_OBJECT_CLASS (mx_menu_parent_class)->dispose (object); } static void mx_menu_finalize (GObject *object) { G_OBJECT_CLASS (mx_menu_parent_class)->finalize (object); } static void mx_menu_get_preferred_width (ClutterActor *actor, gfloat for_height, gfloat *min_width_p, gfloat *natural_width_p) { gint i; MxPadding padding; gfloat min_width, nat_width; MxMenuPrivate *priv = MX_MENU (actor)->priv; /* Add padding and the size of the widest child */ mx_widget_get_padding (MX_WIDGET (actor), &padding); min_width = nat_width = 0; for (i = 0; i < priv->children->len; i++) { gfloat child_min_width, child_nat_width; MxMenuChild *child; child = &g_array_index (priv->children, MxMenuChild, i); clutter_actor_get_preferred_width (CLUTTER_ACTOR (child->box), for_height, &child_min_width, &child_nat_width); if (child_min_width > min_width) min_width = child_min_width; if (child_nat_width > nat_width) nat_width = child_nat_width; } if (min_width_p) *min_width_p = min_width + padding.left + padding.right; if (natural_width_p) *natural_width_p = nat_width + padding.left + padding.right; } static void mx_menu_get_preferred_height (ClutterActor *actor, gfloat for_width, gfloat *min_height_p, gfloat *natural_height_p) { gint i; MxPadding padding; gfloat min_height, nat_height; MxMenuPrivate *priv = MX_MENU (actor)->priv; /* Add padding and the cumulative height of the children */ mx_widget_get_padding (MX_WIDGET (actor), &padding); min_height = nat_height = padding.top + padding.bottom; for (i = 0; i < priv->children->len; i++) { gfloat child_min_height, child_nat_height; MxMenuChild *child = &g_array_index (priv->children, MxMenuChild, i); clutter_actor_get_preferred_height (CLUTTER_ACTOR (child->box), for_width, &child_min_height, &child_nat_height); min_height += child_min_height + 1; nat_height += child_nat_height + 1; } if (min_height_p) *min_height_p = min_height; if (natural_height_p) *natural_height_p = nat_height; } static void mx_menu_allocate (ClutterActor *actor, const ClutterActorBox *box, ClutterAllocationFlags flags) { gint i; MxPadding padding; ClutterActorBox child_box; MxMenuPrivate *priv = MX_MENU (actor)->priv; /* Allocate children */ mx_widget_get_padding (MX_WIDGET (actor), &padding); child_box.x1 = padding.left; child_box.y1 = padding.top; child_box.x2 = box->x2 - box->x1 - padding.right; for (i = 0; i < priv->children->len; i++) { gfloat natural_height; MxMenuChild *child = &g_array_index (priv->children, MxMenuChild, i); clutter_actor_get_preferred_height (CLUTTER_ACTOR (child->box), child_box.x2 - child_box.x1, NULL, &natural_height); child_box.y2 = child_box.y1 + natural_height; clutter_actor_allocate (CLUTTER_ACTOR (child->box), &child_box, flags); child_box.y1 = child_box.y2 + 1; } /* Chain up and allocate background */ CLUTTER_ACTOR_CLASS (mx_menu_parent_class)->allocate (actor, box, flags); } static void mx_menu_floating_paint (ClutterActor *menu) { gint i; MxMenuPrivate *priv = MX_MENU (menu)->priv; /* Chain up to get background */ MX_FLOATING_WIDGET_CLASS (mx_menu_parent_class)->floating_paint (menu); /* Paint children */ for (i = 0; i < priv->children->len; i++) { MxMenuChild *child = &g_array_index (priv->children, MxMenuChild, i); clutter_actor_paint (CLUTTER_ACTOR (child->box)); } } static void mx_menu_floating_pick (ClutterActor *menu, const ClutterColor *color) { gint i; MxMenuPrivate *priv = MX_MENU (menu)->priv; /* chain up to get bounding rectangle */ MX_FLOATING_WIDGET_CLASS (mx_menu_parent_class)->floating_pick (menu, color); /* pick children */ for (i = 0; i < priv->children->len; i++) { MxMenuChild *child = &g_array_index (priv->children, MxMenuChild, i); if (clutter_actor_should_pick_paint (CLUTTER_ACTOR (child->box))) { clutter_actor_paint (CLUTTER_ACTOR (child->box)); } } } static void stage_weak_notify (MxMenu *menu, ClutterStage *stage) { menu->priv->stage = NULL; } static void mx_menu_map (ClutterActor *actor) { gint i; MxMenuPrivate *priv = MX_MENU (actor)->priv; CLUTTER_ACTOR_CLASS (mx_menu_parent_class)->map (actor); for (i = 0; i < priv->children->len; i++) { MxMenuChild *child = &g_array_index (priv->children, MxMenuChild, i); clutter_actor_map (CLUTTER_ACTOR (child->box)); } /* set up a capture so we can close the menu if the user clicks outside it */ priv->stage = clutter_actor_get_stage (actor); g_object_weak_ref (G_OBJECT (priv->stage), (GWeakNotify) stage_weak_notify, actor); priv->captured_event_handler = g_signal_connect (priv->stage, "captured-event", G_CALLBACK (mx_menu_captured_event_handler), actor); } static void mx_menu_unmap (ClutterActor *actor) { gint i; MxMenuPrivate *priv = MX_MENU (actor)->priv; CLUTTER_ACTOR_CLASS (mx_menu_parent_class)->unmap (actor); for (i = 0; i < priv->children->len; i++) { MxMenuChild *child = &g_array_index (priv->children, MxMenuChild, i); clutter_actor_unmap (CLUTTER_ACTOR (child->box)); } if (priv->stage) { g_signal_handler_disconnect (priv->stage, priv->captured_event_handler); priv->captured_event_handler = 0; g_object_weak_unref (G_OBJECT (priv->stage), (GWeakNotify) stage_weak_notify, actor); priv->stage = NULL; } } static gboolean mx_menu_event (ClutterActor *actor, ClutterEvent *event) { /* We swallow mouse events so that they don't fall through to whatever's * beneath us. */ switch (event->type) { case CLUTTER_MOTION: case CLUTTER_BUTTON_PRESS: case CLUTTER_BUTTON_RELEASE: case CLUTTER_SCROLL: return TRUE; case CLUTTER_KEY_PRESS: case CLUTTER_KEY_RELEASE: /* hide the menu if the escape key was pressed */ if (((ClutterKeyEvent*) event)->keyval == CLUTTER_KEY_Escape && CLUTTER_ACTOR_IS_VISIBLE (actor)) { clutter_actor_set_reactive (actor, FALSE); clutter_actor_animate (actor, CLUTTER_LINEAR, 250, "opacity", (guchar) 0, "signal-swapped::completed", clutter_actor_hide, actor, NULL); } default: return FALSE; } } static gboolean mx_menu_captured_event_handler (ClutterActor *actor, ClutterEvent *event, ClutterActor *menu) { int i; ClutterActor *source; MxMenuPrivate *priv = MX_MENU (menu)->priv; /* allow the event to continue if it is applied to the menu or any of its * children */ source = clutter_event_get_source (event); if (source == menu) return FALSE; for (i = 0; i < priv->children->len; i++) { MxMenuChild *child; child = &g_array_index (priv->children, MxMenuChild, i); if (source == (ClutterActor*) child->box) return FALSE; } /* hide the menu if the user clicks outside the menu */ if (event->type == CLUTTER_BUTTON_PRESS) { if (clutter_actor_get_animation (menu)) { clutter_animation_completed (clutter_actor_get_animation (menu)); return FALSE; } clutter_actor_set_reactive (menu, FALSE); clutter_actor_animate (menu, CLUTTER_LINEAR, 250, "opacity", (guchar) 0, "signal-swapped::completed", clutter_actor_hide, menu, NULL); } return TRUE; } static void mx_menu_show (ClutterActor *actor) { ClutterAnimation *animation = NULL; ClutterStage *stage; /* set reactive and opacity, since these may have been set by the fade-out * animation (e.g. from captured_event_handler or button_release_cb) */ if ((animation = clutter_actor_get_animation (actor))) { clutter_animation_completed (animation); } clutter_actor_set_reactive (actor, TRUE); clutter_actor_set_opacity (actor, 0xff); /* chain up to run show after re-setting properties above */ CLUTTER_ACTOR_CLASS (mx_menu_parent_class)->show (actor); clutter_actor_grab_key_focus (actor); stage = (ClutterStage*) clutter_actor_get_stage (actor); mx_focus_manager_push_focus (mx_focus_manager_get_for_stage (stage), MX_FOCUSABLE (actor)); } static void mx_menu_hide (ClutterActor *actor) { CLUTTER_ACTOR_CLASS (mx_menu_parent_class)->hide (actor); } static void mx_menu_style_changed (MxMenu *menu, MxStyleChangedFlags flags) { MxMenuPrivate *priv = menu->priv; gint i; for (i = 0; i < priv->children->len; i++) { MxMenuChild *child; child = &g_array_index (priv->children, MxMenuChild, i); mx_stylable_style_changed (MX_STYLABLE (child->box), flags); } } static void mx_menu_class_init (MxMenuClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass); MxFloatingWidgetClass *float_class = MX_FLOATING_WIDGET_CLASS (klass); g_type_class_add_private (klass, sizeof (MxMenuPrivate)); object_class->get_property = mx_menu_get_property; object_class->set_property = mx_menu_set_property; object_class->dispose = mx_menu_dispose; object_class->finalize = mx_menu_finalize; actor_class->show = mx_menu_show; actor_class->hide = mx_menu_hide; actor_class->get_preferred_width = mx_menu_get_preferred_width; actor_class->get_preferred_height = mx_menu_get_preferred_height; actor_class->allocate = mx_menu_allocate; actor_class->map = mx_menu_map; actor_class->unmap = mx_menu_unmap; actor_class->event = mx_menu_event; float_class->floating_paint = mx_menu_floating_paint; float_class->floating_pick = mx_menu_floating_pick; signals[ACTION_ACTIVATED] = g_signal_new ("action-activated", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (MxMenuClass, action_activated), NULL, NULL, g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 1, MX_TYPE_ACTION); } static void mx_menu_init (MxMenu *self) { MxMenuPrivate *priv = self->priv = MENU_PRIVATE (self); priv->children = g_array_new (FALSE, FALSE, sizeof (MxMenuChild)); g_object_set (G_OBJECT (self), "show-on-set-parent", FALSE, NULL); g_signal_connect (self, "style-changed", G_CALLBACK (mx_menu_style_changed), NULL); } /** * mx_menu_new: * * Create a new #MxMenu * * Returns: a newly allocated #MxMenu */ ClutterActor * mx_menu_new (void) { return g_object_new (MX_TYPE_MENU, NULL); } static void mx_menu_button_clicked_cb (ClutterActor *box, MxAction *action) { MxMenu *menu; menu = MX_MENU (clutter_actor_get_parent (box)); /* set the menu unreactive to prevent other items being hilighted */ clutter_actor_set_reactive ((ClutterActor*) menu, FALSE); g_object_ref (menu); g_object_ref (action); g_signal_emit (menu, signals[ACTION_ACTIVATED], 0, action); g_signal_emit_by_name (action, "activated"); clutter_actor_animate (CLUTTER_ACTOR (menu), CLUTTER_LINEAR, 250, "opacity", (guchar) 0, "signal-swapped::completed", clutter_actor_hide, menu, NULL); g_object_unref (action); g_object_unref (menu); } static gboolean mx_menu_button_enter_event_cb (ClutterActor *box, ClutterEvent *event, gpointer user_data) { MxMenuPrivate *priv = MX_MENU (user_data)->priv; ClutterStage *stage; /* each menu item grabs focus when hovered */ stage = (ClutterStage *) clutter_actor_get_stage (box); /* ensure the menu is not closed when focus is pushed to another actor */ priv->internal_focus_push = TRUE; mx_focus_manager_push_focus (mx_focus_manager_get_for_stage (stage), MX_FOCUSABLE (box)); /* prevent the hover pseudo-class from being applied */ return TRUE; } /** * mx_menu_add_action: * @menu: A #MxMenu * @action: A #MxAction * * Append @action to @menu. * */ void mx_menu_add_action (MxMenu *menu, MxAction *action) { MxMenuChild child; g_return_if_fail (MX_IS_MENU (menu)); g_return_if_fail (MX_IS_ACTION (action)); MxMenuPrivate *priv = menu->priv; child.action = g_object_ref_sink (action); /* TODO: Connect to notify signals in case action properties change */ child.box = g_object_new (MX_TYPE_BUTTON, "action", child.action, "x-align", MX_ALIGN_START, NULL); mx_button_set_action (MX_BUTTON (child.box), child.action); g_signal_connect (child.box, "clicked", G_CALLBACK (mx_menu_button_clicked_cb), action); g_signal_connect (child.box, "enter-event", G_CALLBACK (mx_menu_button_enter_event_cb), menu); clutter_actor_set_parent (CLUTTER_ACTOR (child.box), CLUTTER_ACTOR (menu)); g_array_append_val (priv->children, child); clutter_actor_queue_relayout (CLUTTER_ACTOR (menu)); } /** * mx_menu_remove_action: * @menu: A #MxMenu * @action: A #MxAction * * Remove @action from @menu. * */ void mx_menu_remove_action (MxMenu *menu, MxAction *action) { gint i; g_return_if_fail (MX_IS_MENU (menu)); g_return_if_fail (MX_IS_ACTION (action)); MxMenuPrivate *priv = menu->priv; for (i = 0; i < priv->children->len; i++) { MxMenuChild *child = &g_array_index (priv->children, MxMenuChild, i); if (child->action == action) { mx_menu_free_action_at (menu, i, TRUE); break; } } } /** * mx_menu_remove_all: * @menu: A #MxMenu * * Remove all the actions from @menu. * */ void mx_menu_remove_all (MxMenu *menu) { gint i; g_return_if_fail (MX_IS_MENU (menu)); MxMenuPrivate *priv = menu->priv; if (!priv->children->len) return; for (i = 0; i < priv->children->len; i++) mx_menu_free_action_at (menu, i, FALSE); g_array_remove_range (priv->children, 0, priv->children->len); } /** * mx_menu_show_with_position: * @menu: A #MxMenu * @x: X position * @y: Y position * * Moves the menu to the specified position and shows it. * */ void mx_menu_show_with_position (MxMenu *menu, gfloat x, gfloat y) { g_return_if_fail (MX_IS_MENU (menu)); clutter_actor_set_position (CLUTTER_ACTOR (menu), x, y); clutter_actor_show (CLUTTER_ACTOR (menu)); } mx-1.4.7/mx/mx-menu.h000066400000000000000000000054761201047117600143450ustar00rootroot00000000000000/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * mx-menu.c: menu class * * Copyright (c) 2009, Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. */ #if !defined(MX_H_INSIDE) && !defined(MX_COMPILATION) #error "Only can be included directly.h" #endif #ifndef _MX_MENU_H #define _MX_MENU_H #include "mx-floating-widget.h" #include "mx-action.h" G_BEGIN_DECLS #define MX_TYPE_MENU mx_menu_get_type() #define MX_MENU(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ MX_TYPE_MENU, MxMenu)) #define MX_MENU_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), \ MX_TYPE_MENU, MxMenuClass)) #define MX_IS_MENU(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \ MX_TYPE_MENU)) #define MX_IS_MENU_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE ((klass), \ MX_TYPE_MENU)) #define MX_MENU_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), \ MX_TYPE_MENU, MxMenuClass)) /* The following is defined in mx-widget.h to avoid recursion */ /* typedef struct _MxMenu MxMenu; */ typedef struct _MxMenuClass MxMenuClass; typedef struct _MxMenuPrivate MxMenuPrivate; /** * MxMenu: * * The contents of this structure are private and should only be accessed * through the public API. */ struct _MxMenu { /*< private >*/ MxFloatingWidget parent; MxMenuPrivate *priv; }; struct _MxMenuClass { MxFloatingWidgetClass parent_class; void (*action_activated) (MxMenu *menu, MxAction *action); /* padding for future expansion */ void (*_padding_0) (void); void (*_padding_1) (void); void (*_padding_2) (void); void (*_padding_3) (void); void (*_padding_4) (void); }; GType mx_menu_get_type (void); ClutterActor *mx_menu_new (void); void mx_menu_add_action (MxMenu *menu, MxAction *action); void mx_menu_remove_action (MxMenu *menu, MxAction *action); void mx_menu_remove_all (MxMenu *menu); void mx_menu_show_with_position (MxMenu *menu, gfloat x, gfloat y); G_END_DECLS #endif /* _MX_MENU_H */ mx-1.4.7/mx/mx-native-window.c000066400000000000000000000062071201047117600161600ustar00rootroot00000000000000/* * mx-native-window.c: An interface for interacting with native windows * * Copyright 2010 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * Boston, MA 02111-1307, USA. * * Written by: Chris Lord * */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "mx-native-window.h" #include "mx-private.h" static void mx_native_window_base_init (gpointer g_iface) { static gboolean is_initialized = FALSE; if (G_UNLIKELY (!is_initialized)) { GParamSpec *pspec; is_initialized = TRUE; pspec = g_param_spec_object ("window", "Window", "The parent MxWindow", MX_TYPE_WINDOW, MX_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); g_object_interface_install_property (g_iface, pspec); } } GType _mx_native_window_get_type (void) { static GType native_window_type = 0; if (G_UNLIKELY (native_window_type == 0)) { const GTypeInfo native_window_info = { sizeof (MxNativeWindowIface), mx_native_window_base_init, NULL }; native_window_type = g_type_register_static (G_TYPE_INTERFACE, g_intern_static_string ("MxNativeWindow"), &native_window_info, 0); g_type_interface_add_prerequisite (native_window_type, G_TYPE_OBJECT); } return native_window_type; } void _mx_native_window_get_position (MxNativeWindow *window, gint *x, gint *y) { MxNativeWindowIface *iface; g_return_if_fail (MX_IS_NATIVE_WINDOW (window)); iface = MX_NATIVE_WINDOW_GET_IFACE (window); if (iface->get_position) iface->get_position (window, x, y); else { *x = 0; *y = 0; } } void _mx_native_window_set_position (MxNativeWindow *window, gint x, gint y) { MxNativeWindowIface *iface; g_return_if_fail (MX_IS_NATIVE_WINDOW (window)); iface = MX_NATIVE_WINDOW_GET_IFACE (window); if (iface->set_position) iface->set_position (window, x, y); } void _mx_native_window_present (MxNativeWindow *window) { MxNativeWindowIface *iface; g_return_if_fail (MX_IS_NATIVE_WINDOW (window)); iface = MX_NATIVE_WINDOW_GET_IFACE (window); if (iface->present) iface->present (window); } mx-1.4.7/mx/mx-native-window.h000066400000000000000000000041121201047117600161560ustar00rootroot00000000000000/* * mx-native-window.h: An interface for interacting with native windows * * Copyright 2010 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * Boston, MA 02111-1307, USA. * * Written by: Chris Lord * */ #ifndef _MX_NATIVE_WINDOW_H #define _MX_NATIVE_WINDOW_H #include #include G_BEGIN_DECLS #define MX_TYPE_NATIVE_WINDOW _mx_native_window_get_type() #define MX_NATIVE_WINDOW(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ MX_TYPE_NATIVE_WINDOW, MxNativeWindow)) #define MX_IS_NATIVE_WINDOW(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \ MX_TYPE_NATIVE_WINDOW)) #define MX_NATIVE_WINDOW_GET_IFACE(obj) \ (G_TYPE_INSTANCE_GET_INTERFACE ((obj), \ MX_TYPE_NATIVE_WINDOW, MxNativeWindowIface)) typedef struct _MxNativeWindow MxNativeWindow; /* dummy */ typedef struct _MxNativeWindowIface MxNativeWindowIface; struct _MxNativeWindowIface { GTypeInterface parent_iface; void (* get_position) (MxNativeWindow *window, gint *x, gint *y); void (* set_position) (MxNativeWindow *window, gint x, gint y); void (* present) (MxNativeWindow *window); }; GType _mx_native_window_get_type (void) G_GNUC_CONST; void _mx_native_window_get_position (MxNativeWindow *window, gint *x, gint *y); void _mx_native_window_set_position (MxNativeWindow *window, gint x, gint y); void _mx_native_window_present (MxNativeWindow *window); G_END_DECLS #endif /* _MX_NATIVE_WINDOW_H */ mx-1.4.7/mx/mx-notebook.c000066400000000000000000000411541201047117600152050ustar00rootroot00000000000000/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * mx-notebook: notebook actor * * Copyright 2009, 2010 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * * Written by: Thomas Wood * */ #include #include "mx-notebook.h" #include "mx-private.h" #include "mx-focusable.h" #ifdef HAVE_CLUTTER_GESTURE #include #endif static void clutter_container_iface_init (ClutterContainerIface *iface); static void mx_focusable_iface_init (MxFocusableIface *iface); G_DEFINE_TYPE_WITH_CODE (MxNotebook, mx_notebook, MX_TYPE_WIDGET, G_IMPLEMENT_INTERFACE (CLUTTER_TYPE_CONTAINER, clutter_container_iface_init) G_IMPLEMENT_INTERFACE (MX_TYPE_FOCUSABLE, mx_focusable_iface_init)) #define NOTEBOOK_PRIVATE(o) \ (G_TYPE_INSTANCE_GET_PRIVATE ((o), MX_TYPE_NOTEBOOK, MxNotebookPrivate)) struct _MxNotebookPrivate { ClutterActor *current_page; GList *children; gboolean enable_gestures; #if HAVE_CLUTTER_GESTURE ClutterGesture *gesture; #endif }; enum { PROP_CURRENT_PAGE = 1, PROP_ENABLE_GESTURES }; static void mx_notebook_show_complete_cb (MxNotebook *book) { MxNotebookPrivate *priv = book->priv; GList *l; for (l = priv->children; l; l = l->next) { ClutterActor *child = CLUTTER_ACTOR (l->data); if (child != priv->current_page) { clutter_actor_hide (child); clutter_actor_set_opacity (child, 0x00); } } } static void mx_notebook_update_children (MxNotebook *book) { MxNotebookPrivate *priv = book->priv; GList *l; for (l = priv->children; l; l = l->next) { ClutterActor *child = CLUTTER_ACTOR (l->data); ClutterAnimation *anim = clutter_actor_get_animation (child); if (anim) { /* A bit of a hack - we want to just abort the animation, * but there's no way of aborting an animation that was * started with clutter_actor_animate(). */ guint8 opacity = clutter_actor_get_opacity (child); g_signal_handlers_disconnect_by_func (anim, mx_notebook_show_complete_cb, book); clutter_animation_completed (anim); clutter_actor_set_opacity (child, opacity); } if (child == priv->current_page) { clutter_actor_show (child); clutter_actor_animate (child, CLUTTER_LINEAR, 250, "opacity", 255, "signal-swapped::completed", mx_notebook_show_complete_cb, book, NULL); } } } static void mx_notebook_add (ClutterContainer *container, ClutterActor *actor) { MxNotebookPrivate *priv = MX_NOTEBOOK (container)->priv; clutter_actor_set_parent (actor, CLUTTER_ACTOR (container)); priv->children = g_list_append (priv->children, actor); if (!priv->current_page) { priv->current_page = actor; clutter_actor_set_opacity (actor, 0xff); g_object_notify (G_OBJECT (container), "current-page"); } else clutter_actor_hide (actor); g_signal_emit_by_name (container, "actor-added", actor); } static void mx_notebook_remove (ClutterContainer *container, ClutterActor *actor) { MxNotebookPrivate *priv = MX_NOTEBOOK (container)->priv; GList *item = NULL; item = g_list_find (priv->children, actor); if (item == NULL) { g_warning ("Actor of type '%s' is not a child of container of type '%s'", g_type_name (G_OBJECT_TYPE (actor)), g_type_name (G_OBJECT_TYPE (container))); return; } /* If it was the current page, select either the previous or * the next, whichever exists first. */ if (actor == priv->current_page) { priv->current_page = item->prev ? item->prev->data : (item->next ? item->next->data : NULL); g_object_notify (G_OBJECT (container), "current-page"); } g_object_ref (actor); priv->children = g_list_delete_link (priv->children, item); clutter_actor_unparent (actor); g_signal_emit_by_name (container, "actor-removed", actor); g_object_unref (actor); mx_notebook_update_children (MX_NOTEBOOK (container)); } static void mx_notebook_foreach (ClutterContainer *container, ClutterCallback callback, gpointer callback_data) { MxNotebookPrivate *priv = MX_NOTEBOOK (container)->priv; g_list_foreach (priv->children, (GFunc) callback, callback_data); } static void clutter_container_iface_init (ClutterContainerIface *iface) { iface->add = mx_notebook_add; iface->remove = mx_notebook_remove; iface->foreach = mx_notebook_foreach; } static MxFocusable * mx_notebook_accept_focus (MxFocusable *focusable, MxFocusHint hint) { MxNotebookPrivate *priv = MX_NOTEBOOK (focusable)->priv; if (priv->current_page && MX_IS_FOCUSABLE (priv->current_page)) return mx_focusable_accept_focus (MX_FOCUSABLE (priv->current_page), hint); else return NULL; } static MxFocusable * mx_notebook_move_focus (MxFocusable *focusable, MxFocusDirection direction, MxFocusable *from) { return NULL; } static void mx_focusable_iface_init (MxFocusableIface *iface) { iface->accept_focus = mx_notebook_accept_focus; iface->move_focus = mx_notebook_move_focus; } static void mx_notebook_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { MxNotebookPrivate *priv = MX_NOTEBOOK (object)->priv; switch (property_id) { case PROP_CURRENT_PAGE: g_value_set_object (value, priv->current_page); break; case PROP_ENABLE_GESTURES: g_value_set_boolean (value, priv->enable_gestures); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } } static void mx_notebook_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { switch (property_id) { case PROP_CURRENT_PAGE: mx_notebook_set_current_page (MX_NOTEBOOK (object), (ClutterActor *)g_value_get_object (value)); break; case PROP_ENABLE_GESTURES: mx_notebook_set_enable_gestures (MX_NOTEBOOK (object), g_value_get_boolean (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } } static void mx_notebook_dispose (GObject *object) { #ifdef HAVE_CLUTTER_GESTURE MxNotebookPrivate *priv = MX_NOTEBOOK (object)->priv; if (priv->gesture) { g_object_unref (priv->gesture); priv->gesture = NULL; } #endif G_OBJECT_CLASS (mx_notebook_parent_class)->dispose (object); } static void mx_notebook_destroy (ClutterActor *actor) { MxNotebookPrivate *priv = MX_NOTEBOOK (actor)->priv; g_list_foreach (priv->children, (GFunc) clutter_actor_destroy, NULL); if (CLUTTER_ACTOR_CLASS (mx_notebook_parent_class)->destroy) CLUTTER_ACTOR_CLASS (mx_notebook_parent_class)->destroy (actor); } static void mx_notebook_finalize (GObject *object) { G_OBJECT_CLASS (mx_notebook_parent_class)->finalize (object); } static void mx_notebook_get_preferred_width (ClutterActor *actor, gfloat for_height, gfloat *min_width_p, gfloat *natural_width_p) { MxNotebookPrivate *priv = MX_NOTEBOOK (actor)->priv; GList *l; MxPadding padding; mx_widget_get_padding (MX_WIDGET (actor), &padding); if (min_width_p) *min_width_p = 0; if (natural_width_p) *natural_width_p = 0; for (l = priv->children; l; l = l->next) { gfloat child_min, child_nat; clutter_actor_get_preferred_width (CLUTTER_ACTOR (l->data), for_height, &child_min, &child_nat); if (min_width_p) *min_width_p = MAX ((*min_width_p), child_min); if (natural_width_p) *natural_width_p = MAX ((*natural_width_p), child_nat); } if (min_width_p) *min_width_p += padding.left + padding.right; if (natural_width_p) *natural_width_p += padding.left + padding.right; } static void mx_notebook_get_preferred_height (ClutterActor *actor, gfloat for_width, gfloat *min_height_p, gfloat *natural_height_p) { MxNotebookPrivate *priv = MX_NOTEBOOK (actor)->priv; GList *l; MxPadding padding; mx_widget_get_padding (MX_WIDGET (actor), &padding); if (min_height_p) *min_height_p = 0; if (natural_height_p) *natural_height_p = 0; for (l = priv->children; l; l = l->next) { gfloat child_min, child_nat; clutter_actor_get_preferred_height (CLUTTER_ACTOR (l->data), for_width, &child_min, &child_nat); if (min_height_p) *min_height_p = MAX (*min_height_p, child_min); if (natural_height_p) *natural_height_p = MAX (*natural_height_p, child_nat); } if (min_height_p) *min_height_p += padding.top + padding.bottom; if (natural_height_p) *natural_height_p += padding.top + padding.bottom; } static void mx_notebook_paint (ClutterActor *actor) { GList *l; MxNotebookPrivate *priv = MX_NOTEBOOK (actor)->priv; CLUTTER_ACTOR_CLASS (mx_notebook_parent_class)->paint (actor); for (l = priv->children; l; l = l->next) { ClutterActor *child = CLUTTER_ACTOR (l->data); if (child == priv->current_page) continue; if (CLUTTER_ACTOR_IS_VISIBLE (child)) clutter_actor_paint (child); } if (priv->current_page) clutter_actor_paint (priv->current_page); } static void mx_notebook_pick (ClutterActor *actor, const ClutterColor *color) { MxNotebookPrivate *priv = MX_NOTEBOOK (actor)->priv; CLUTTER_ACTOR_CLASS (mx_notebook_parent_class)->pick (actor, color); if (priv->current_page) clutter_actor_paint (priv->current_page); } static void mx_notebook_allocate (ClutterActor *actor, const ClutterActorBox *box, ClutterAllocationFlags flags) { MxNotebookPrivate *priv = MX_NOTEBOOK (actor)->priv; GList *l; MxPadding padding; ClutterActorBox childbox; CLUTTER_ACTOR_CLASS (mx_notebook_parent_class)->allocate (actor, box, flags); mx_widget_get_padding (MX_WIDGET (actor), &padding); childbox.x1 = 0 + padding.left; childbox.x2 = box->x2 - box->x1 - padding.right; childbox.y1 = 0 + padding.top; childbox.y2 = box->y2 - box->y1 - padding.bottom; for (l = priv->children; l; l = l->next) { ClutterActor *child; child = CLUTTER_ACTOR (l->data); if (CLUTTER_ACTOR_IS_VISIBLE (l->data)) clutter_actor_allocate (child, &childbox, flags); } } #ifdef HAVE_CLUTTER_GESTURE static gboolean mx_notebook_gesture_slide_event_cb (ClutterGesture *gesture, ClutterGestureSlideEvent *event, MxNotebook *book) { GList *item; MxNotebookPrivate *priv = book->priv; if (!priv->enable_gestures || !priv->current_page) return FALSE; item = g_list_find (priv->children, priv->current_page); if (!item) { g_warning ("Current page not found in child list"); return FALSE; } if (event->direction % 2) { /* up, left (1, 3) */ if (item->prev) mx_notebook_set_current_page (book, (ClutterActor *)item->prev->data); else mx_notebook_set_current_page (book, (ClutterActor *)g_list_last (item)->data); } else { /* down, right (2, 4) */ if (item->next) mx_notebook_set_current_page (book, (ClutterActor *)item->next->data); else mx_notebook_set_current_page (book, (ClutterActor *)priv->children->data); } return TRUE; } #endif static void mx_notebook_class_init (MxNotebookClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass); GParamSpec *pspec; g_type_class_add_private (klass, sizeof (MxNotebookPrivate)); object_class->get_property = mx_notebook_get_property; object_class->set_property = mx_notebook_set_property; object_class->dispose = mx_notebook_dispose; object_class->finalize = mx_notebook_finalize; actor_class->allocate = mx_notebook_allocate; actor_class->get_preferred_width = mx_notebook_get_preferred_width; actor_class->get_preferred_height = mx_notebook_get_preferred_height; actor_class->paint = mx_notebook_paint; actor_class->pick = mx_notebook_pick; actor_class->destroy = mx_notebook_destroy; pspec = g_param_spec_object ("current-page", "Current page", "The current ClutterActor being displayed", CLUTTER_TYPE_ACTOR, MX_PARAM_READWRITE); g_object_class_install_property (object_class, PROP_CURRENT_PAGE, pspec); pspec = g_param_spec_boolean ("enable-gestures", "Enable Gestures", "Enable use of pointer gestures to switch page", FALSE, G_PARAM_READWRITE); g_object_class_install_property (object_class, PROP_ENABLE_GESTURES, pspec); } static void mx_notebook_init (MxNotebook *self) { self->priv = NOTEBOOK_PRIVATE (self); } ClutterActor * mx_notebook_new (void) { return g_object_new (MX_TYPE_NOTEBOOK, NULL); } void mx_notebook_set_current_page (MxNotebook *book, ClutterActor *page) { MxNotebookPrivate *priv; g_return_if_fail (MX_IS_NOTEBOOK (book)); g_return_if_fail (CLUTTER_IS_ACTOR (page)); priv = book->priv; if (page == priv->current_page) return; priv->current_page = page; /* ensure the correct child is visible */ mx_notebook_update_children (book); g_object_notify (G_OBJECT (book), "current-page"); } /** * mx_notebook_get_current_page: * @notebook: A #MxNotebook * * Get the current page * * Returns: (transfer none): the current page */ ClutterActor * mx_notebook_get_current_page (MxNotebook *notebook) { g_return_val_if_fail (MX_IS_NOTEBOOK (notebook), NULL); return notebook->priv->current_page; } void mx_notebook_set_enable_gestures (MxNotebook *book, gboolean enabled) { MxNotebookPrivate *priv; g_return_if_fail (MX_IS_NOTEBOOK (book)); priv = book->priv; if (priv->enable_gestures != enabled) { priv->enable_gestures = enabled; #ifndef HAVE_CLUTTER_GESTURE g_warning ("Gestures are disabled as Clutter Gesture is not available"); #else if (enabled && !priv->gesture) { priv->gesture = clutter_gesture_new (CLUTTER_ACTOR (book)); clutter_gesture_set_gesture_mask (priv->gesture, CLUTTER_ACTOR (book), GESTURE_MASK_SLIDE); g_signal_connect (priv->gesture, "gesture-slide-event", G_CALLBACK (mx_notebook_gesture_slide_event_cb), book); clutter_actor_set_reactive (CLUTTER_ACTOR (book), TRUE); } #endif g_object_notify (G_OBJECT (book), "enable-gestures"); } } gboolean mx_notebook_get_enable_gestures (MxNotebook *book) { g_return_val_if_fail (MX_IS_NOTEBOOK (book), FALSE); return book->priv->enable_gestures; } mx-1.4.7/mx/mx-notebook.h000066400000000000000000000052751201047117600152160ustar00rootroot00000000000000/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * mx-notebook: notebook actor * * Copyright 2009, 2010 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * * Written by: Thomas Wood * */ #if !defined(MX_H_INSIDE) && !defined(MX_COMPILATION) #error "Only can be included directly.h" #endif #ifndef _MX_NOTEBOOK_H #define _MX_NOTEBOOK_H #include G_BEGIN_DECLS #define MX_TYPE_NOTEBOOK mx_notebook_get_type() #define MX_NOTEBOOK(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ MX_TYPE_NOTEBOOK, MxNotebook)) #define MX_NOTEBOOK_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), \ MX_TYPE_NOTEBOOK, MxNotebookClass)) #define MX_IS_NOTEBOOK(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \ MX_TYPE_NOTEBOOK)) #define MX_IS_NOTEBOOK_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE ((klass), \ MX_TYPE_NOTEBOOK)) #define MX_NOTEBOOK_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), \ MX_TYPE_NOTEBOOK, MxNotebookClass)) /** * MxNotebook: * * The contents of this structure is private and should only be accessed using * the provided API. */ typedef struct _MxNotebook MxNotebook; typedef struct _MxNotebookClass MxNotebookClass; typedef struct _MxNotebookPrivate MxNotebookPrivate; struct _MxNotebook { MxWidget parent; MxNotebookPrivate *priv; }; struct _MxNotebookClass { MxWidgetClass parent_class; /* padding for future expansion */ void (*_padding_0) (void); void (*_padding_1) (void); void (*_padding_2) (void); void (*_padding_3) (void); void (*_padding_4) (void); }; GType mx_notebook_get_type (void) G_GNUC_CONST; ClutterActor *mx_notebook_new (void); void mx_notebook_set_current_page (MxNotebook *notebook, ClutterActor *page); ClutterActor *mx_notebook_get_current_page (MxNotebook *notebook); gboolean mx_notebook_get_enable_gestures (MxNotebook *book); void mx_notebook_set_enable_gestures (MxNotebook *book, gboolean enabled); G_END_DECLS #endif /* _MX_NOTEBOOK_H */ mx-1.4.7/mx/mx-offscreen.c000066400000000000000000001077511201047117600153450ustar00rootroot00000000000000/* * mx-offscreen.h: An offscreen texture container * * Copyright 2010 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * Boston, MA 02111-1307, USA. * * Written by: Chris Lord * */ /** * SECTION:mx-offscreen * @short_description: an offscreen container widget * * #MxOffscreen allows you to redirect the painting of a #ClutterActor to * a texture. It can either contain this actor, or optionally, it can * redirect the painting of an actor that it does not contain. * * This is often useful for applying a #ClutterShader effect to an actor * or group of actors that is not a texture. */ #include "mx-offscreen.h" #include "mx-private.h" static void clutter_container_iface_init (ClutterContainerIface *iface); static void mx_focusable_iface_init (MxFocusableIface *iface); G_DEFINE_TYPE_WITH_CODE (MxOffscreen, mx_offscreen, CLUTTER_TYPE_TEXTURE, G_IMPLEMENT_INTERFACE (CLUTTER_TYPE_CONTAINER, clutter_container_iface_init) G_IMPLEMENT_INTERFACE (MX_TYPE_FOCUSABLE, mx_focusable_iface_init)) #define OFFSCREEN_PRIVATE(o) \ (G_TYPE_INSTANCE_GET_PRIVATE ((o), MX_TYPE_OFFSCREEN, MxOffscreenPrivate)) struct _MxOffscreenPrivate { guint pick_child : 1; guint auto_update : 1; guint redirect_enabled : 1; guint queued_redraw : 1; guint acc_enabled : 1; guint blend_set : 1; guint in_dispose : 1; guint pre_paint_done : 1; ClutterActor *child; CoglHandle fbo; CoglHandle acc_material; CoglHandle acc_fbo; GList *disabled_shaders; }; enum { PROP_0, PROP_CHILD, PROP_PICK_CHILD, PROP_AUTO_UPDATE, PROP_REDIRECT_ENABLED, PROP_BUFFER, PROP_ACC_ENABLED, PROP_ACC_MATERIAL }; static void mx_offscreen_add (ClutterContainer *container, ClutterActor *actor) { /* Warn and return early if the actor already has a parent. * Calling mx_offscreen_set_child with an actor that is * already parented will cause us to mirror that actor without * parenting it, which is not the intention of the ClutterContainer * interface. */ if (clutter_actor_get_parent (actor)) { g_warning (G_STRLOC ": Actor '%s' already has a parent", G_OBJECT_CLASS_NAME (G_OBJECT_GET_CLASS (actor))); return; } mx_offscreen_set_child (MX_OFFSCREEN (container), actor); } static void mx_offscreen_remove (ClutterContainer *container, ClutterActor *actor) { MxOffscreen *self = MX_OFFSCREEN (container); if (clutter_actor_get_parent (actor) != (ClutterActor *)container) { g_warning (G_STRLOC ": Actor '%s' is not parented to this container", G_OBJECT_CLASS_NAME (G_OBJECT_GET_CLASS (actor))); return; } if (self->priv->child == actor) mx_offscreen_set_child (self, NULL); } static void mx_offscreen_foreach (ClutterContainer *container, ClutterCallback callback, gpointer user_data) { MxOffscreenPrivate *priv = MX_OFFSCREEN (container)->priv; if (priv->child && (clutter_actor_get_parent (priv->child) == (ClutterActor *)container)) callback (priv->child, user_data); } static void clutter_container_iface_init (ClutterContainerIface *iface) { iface->add = mx_offscreen_add; iface->remove = mx_offscreen_remove; iface->foreach = mx_offscreen_foreach; } static MxFocusable * mx_offscreen_move_focus (MxFocusable *focusable, MxFocusDirection direction, MxFocusable *from) { return NULL; } static MxFocusable * mx_offscreen_accept_focus (MxFocusable *focusable, MxFocusHint hint) { MxOffscreenPrivate *priv = MX_OFFSCREEN (focusable)->priv; if (priv->child && MX_IS_FOCUSABLE (priv->child)) return mx_focusable_accept_focus (MX_FOCUSABLE (priv->child), hint); else return NULL; } static void mx_focusable_iface_init (MxFocusableIface *iface) { iface->move_focus = mx_offscreen_move_focus; iface->accept_focus = mx_offscreen_accept_focus; } static void mx_offscreen_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { MxOffscreen *self = MX_OFFSCREEN (object); switch (property_id) { case PROP_CHILD: g_value_set_object (value, (GObject *)mx_offscreen_get_child (self)); break; case PROP_PICK_CHILD: g_value_set_boolean (value, mx_offscreen_get_pick_child (self)); break; case PROP_AUTO_UPDATE: g_value_set_boolean (value, mx_offscreen_get_auto_update (self)); break; case PROP_REDIRECT_ENABLED: g_value_set_boolean (value, mx_offscreen_get_redirect_enabled (self)); break; case PROP_BUFFER: g_value_set_pointer (value, mx_offscreen_get_buffer (self)); break; case PROP_ACC_ENABLED: g_value_set_boolean (value, mx_offscreen_get_accumulation_enabled (self)); break; case PROP_ACC_MATERIAL: g_value_set_pointer (value, mx_offscreen_get_accumulation_material (self)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } } static void mx_offscreen_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { MxOffscreen *self = MX_OFFSCREEN (object); switch (property_id) { case PROP_CHILD: mx_offscreen_set_child (self, (ClutterActor *)g_value_get_object (value)); break; case PROP_PICK_CHILD: mx_offscreen_set_pick_child (self, g_value_get_boolean (value)); break; case PROP_AUTO_UPDATE: mx_offscreen_set_auto_update (self, g_value_get_boolean (value)); break; case PROP_REDIRECT_ENABLED: mx_offscreen_set_redirect_enabled (self, g_value_get_boolean (value)); break; case PROP_ACC_ENABLED: mx_offscreen_set_accumulation_enabled (self, g_value_get_boolean (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } } static void mx_offscreen_destroy (ClutterActor *actor) { MxOffscreenPrivate *priv = MX_OFFSCREEN (actor)->priv; if (priv->child && (clutter_actor_get_parent (priv->child) == actor)) { clutter_actor_destroy (priv->child); priv->child = NULL; } if (CLUTTER_ACTOR_CLASS (mx_offscreen_parent_class)->destroy) CLUTTER_ACTOR_CLASS (mx_offscreen_parent_class)->destroy (actor); } static void mx_offscreen_dispose (GObject *object) { MxOffscreen *self = MX_OFFSCREEN (object); MxOffscreenPrivate *priv = self->priv; priv->in_dispose = TRUE; if (priv->child && (clutter_actor_get_parent (priv->child) != (ClutterActor *)self)) mx_offscreen_set_child (self, NULL); if (priv->fbo) { cogl_handle_unref (priv->fbo); priv->fbo = NULL; } if (priv->acc_material) { cogl_handle_unref (priv->acc_material); priv->acc_material = NULL; } if (priv->acc_fbo) { cogl_handle_unref (priv->acc_fbo); priv->acc_fbo = NULL; } G_OBJECT_CLASS (mx_offscreen_parent_class)->dispose (object); } static void mx_offscreen_finalize (GObject *object) { G_OBJECT_CLASS (mx_offscreen_parent_class)->finalize (object); } static void mx_offscreen_get_preferred_width (ClutterActor *actor, gfloat for_height, gfloat *min_width_p, gfloat *nat_width_p) { MxOffscreenPrivate *priv = MX_OFFSCREEN (actor)->priv; if (priv->child) { clutter_actor_get_preferred_width (priv->child, for_height, min_width_p, nat_width_p); } else { if (min_width_p) *min_width_p = 0; if (nat_width_p) *nat_width_p = 0; } } static void mx_offscreen_get_preferred_height (ClutterActor *actor, gfloat for_width, gfloat *min_height_p, gfloat *nat_height_p) { MxOffscreenPrivate *priv = MX_OFFSCREEN (actor)->priv; if (priv->child) { clutter_actor_get_preferred_height (priv->child, for_width, min_height_p, nat_height_p); } else { if (min_height_p) *min_height_p = 0; if (nat_height_p) *nat_height_p = 0; } } static void mx_offscreen_allocate (ClutterActor *actor, const ClutterActorBox *box, ClutterAllocationFlags flags) { MxOffscreenPrivate *priv = MX_OFFSCREEN (actor)->priv; CLUTTER_ACTOR_CLASS (mx_offscreen_parent_class)->allocate (actor, box, flags); if (priv->child && (clutter_actor_get_parent (priv->child) == actor)) { ClutterActorBox child_box; child_box.x1 = 0; child_box.y1 = 0; child_box.x2 = box->x2 - box->x1; child_box.y2 = box->y2 - box->y1; clutter_actor_allocate (priv->child, &child_box, flags); } } static void mx_offscreen_toggle_shaders (MxOffscreen *offscreen, GList **disabled_shaders, gboolean enable) { ClutterActor *actor = (ClutterActor *)offscreen; if (enable) { GList *s; for (s = *disabled_shaders; s; s = s->next) clutter_shader_set_is_enabled ((ClutterShader *)s->data, TRUE); g_list_free (*disabled_shaders); *disabled_shaders = NULL; } else { *disabled_shaders = NULL; do { ClutterShader *shader = clutter_actor_get_shader (actor); if (shader && clutter_shader_get_is_enabled (shader)) { clutter_shader_set_is_enabled (shader, FALSE); *disabled_shaders = g_list_prepend (*disabled_shaders, shader); } } while ((actor = clutter_actor_get_parent (actor))); } } static gboolean mx_offscreen_ensure_buffers (MxOffscreen *offscreen) { CoglHandle texture; gboolean sync_size; gfloat width, height; MxOffscreenPrivate *priv = offscreen->priv; clutter_actor_get_size (priv->child, &width, &height); sync_size = clutter_texture_get_sync_size (CLUTTER_TEXTURE (offscreen)); /* Check our texture exists and is the correct size */ texture = clutter_texture_get_cogl_texture (CLUTTER_TEXTURE (offscreen)); if (!texture || (sync_size && ((cogl_texture_get_width (texture) != (guint)width) || (cogl_texture_get_height (texture) != (guint)height)))) { texture = cogl_texture_new_with_size ((guint)width, (guint)height, COGL_TEXTURE_NO_SLICING, COGL_PIXEL_FORMAT_RGBA_8888_PRE); if (texture) { clutter_texture_set_cogl_texture (CLUTTER_TEXTURE (offscreen), texture); cogl_handle_unref (texture); } } /* The notification of setting a texture will trigger a callback that * creates the fbo. Doing it this ways lets the texture be overriden * externally. */ if (texture && priv->fbo) return TRUE; else return FALSE; } static CoglHandle mx_offscreen_material_get_texture (CoglHandle material, gint layer_n) { const GList *layers = cogl_material_get_layers (material); CoglHandle layer = g_list_nth_data ((GList *)layers, layer_n); return layer ? cogl_material_layer_get_texture (layer) : NULL; } static gboolean mx_offscreen_ensure_accumulation_buffer (MxOffscreen *offscreen) { guint width, height; CoglHandle texture, acc_texture; MxOffscreenPrivate *priv = offscreen->priv; texture = clutter_texture_get_cogl_texture (CLUTTER_TEXTURE (offscreen)); if (!texture) return FALSE; acc_texture = mx_offscreen_material_get_texture (priv->acc_material, 0); width = cogl_texture_get_width (texture); height = cogl_texture_get_height (texture); if (!acc_texture || (cogl_texture_get_width (acc_texture) != width) || (cogl_texture_get_height (acc_texture) != height)) { if (priv->acc_fbo) { cogl_handle_unref (priv->acc_fbo); priv->acc_fbo = NULL; } texture = cogl_texture_new_with_size (width, height, COGL_TEXTURE_NO_SLICING, COGL_PIXEL_FORMAT_RGBA_8888_PRE); cogl_material_set_layer (priv->acc_material, 0, texture); if (texture) { CoglColor color; priv->acc_fbo = cogl_offscreen_new_to_texture (texture); /* Clear the newly created texture */ cogl_color_set_from_4ub (&color, 0, 0, 0, 0); cogl_push_framebuffer (priv->acc_fbo); cogl_clear (&color, COGL_BUFFER_BIT_COLOR); cogl_pop_framebuffer (); cogl_handle_unref (texture); return TRUE; } return FALSE; } return TRUE; } static void mx_offscreen_paint (ClutterActor *actor) { MxOffscreen *self = MX_OFFSCREEN (actor); MxOffscreenPrivate *priv = self->priv; if (!priv->child) return; if (!priv->redirect_enabled) { GList *disabled_shaders; /* Disable our shader when we paint with pass-through. */ mx_offscreen_toggle_shaders (self, &disabled_shaders, FALSE); clutter_actor_paint (priv->child); mx_offscreen_toggle_shaders (self, &disabled_shaders, TRUE); } else { if (priv->auto_update && (clutter_actor_get_parent (priv->child) == actor)) mx_offscreen_update (self); if (priv->acc_enabled && mx_offscreen_ensure_accumulation_buffer (self)) { ClutterActorBox box; CoglColor zero_color; CoglHandle material = clutter_texture_get_cogl_material (CLUTTER_TEXTURE (actor)); /* Blend the texture onto the accumulation buffer */ cogl_push_framebuffer (priv->acc_fbo); cogl_color_set_from_4ub (&zero_color, 0, 0, 0, 0); cogl_clear (&zero_color, COGL_BUFFER_BIT_STENCIL | COGL_BUFFER_BIT_DEPTH); cogl_set_source (material); cogl_rectangle (-1, 1, 1, -1); cogl_pop_framebuffer (); /* Draw the accumulation buffer */ clutter_actor_get_allocation_box (actor, &box); cogl_set_source (priv->acc_material); cogl_rectangle (0, 0, box.x2 - box.x1, box.y2 - box.y1); } else CLUTTER_ACTOR_CLASS (mx_offscreen_parent_class)->paint (actor); } } static void mx_offscreen_pick (ClutterActor *actor, const ClutterColor *color) { MxOffscreenPrivate *priv = MX_OFFSCREEN (actor)->priv; if (priv->pick_child && priv->child) clutter_actor_paint (priv->child); } static void mx_offscreen_map (ClutterActor *actor) { MxOffscreenPrivate *priv = MX_OFFSCREEN (actor)->priv; CLUTTER_ACTOR_CLASS (mx_offscreen_parent_class)->map (actor); if (priv->child && (clutter_actor_get_parent (priv->child) == actor)) clutter_actor_map (priv->child); } static void mx_offscreen_unmap (ClutterActor *actor) { MxOffscreenPrivate *priv = MX_OFFSCREEN (actor)->priv; if (priv->child && (clutter_actor_get_parent (priv->child) == actor)) clutter_actor_unmap (priv->child); CLUTTER_ACTOR_CLASS (mx_offscreen_parent_class)->unmap (actor); } static void mx_offscreen_real_paint_child (MxOffscreen *self) { MxOffscreenPrivate *priv = self->priv; if (priv->child) clutter_actor_paint (priv->child); } static void mx_offscreen_class_init (MxOffscreenClass *klass) { GParamSpec *pspec; GObjectClass *object_class = G_OBJECT_CLASS (klass); ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass); g_type_class_add_private (klass, sizeof (MxOffscreenPrivate)); object_class->get_property = mx_offscreen_get_property; object_class->set_property = mx_offscreen_set_property; object_class->dispose = mx_offscreen_dispose; object_class->finalize = mx_offscreen_finalize; actor_class->get_preferred_width = mx_offscreen_get_preferred_width; actor_class->get_preferred_height = mx_offscreen_get_preferred_height; actor_class->allocate = mx_offscreen_allocate; actor_class->paint = mx_offscreen_paint; actor_class->pick = mx_offscreen_pick; actor_class->map = mx_offscreen_map; actor_class->unmap = mx_offscreen_unmap; actor_class->destroy = mx_offscreen_destroy; klass->paint_child = mx_offscreen_real_paint_child; pspec = g_param_spec_object ("child", "Child", "Child actor of the offscreen texture.", CLUTTER_TYPE_ACTOR, MX_PARAM_READWRITE); g_object_class_install_property (object_class, PROP_CHILD, pspec); pspec = g_param_spec_boolean ("pick-child", "Pick child", "Whether to pick the child.", FALSE, MX_PARAM_READWRITE); g_object_class_install_property (object_class, PROP_PICK_CHILD, pspec); pspec = g_param_spec_boolean ("auto-update", "Auto update", "Update child actor automatically " "when painting.", TRUE, MX_PARAM_READWRITE); g_object_class_install_property (object_class, PROP_AUTO_UPDATE, pspec); pspec = g_param_spec_boolean ("redirect-enabled", "Redirect Enabled", "Enable redirection of the child actor to " "the off-screen surface.", TRUE, MX_PARAM_READWRITE); g_object_class_install_property (object_class, PROP_REDIRECT_ENABLED, pspec); pspec = g_param_spec_pointer ("buffer", "Buffer", "The off-screen buffer used to draw the child.", MX_PARAM_READABLE); g_object_class_install_property (object_class, PROP_BUFFER, pspec); pspec = g_param_spec_boolean ("accumulation-enabled", "Accumulation enabled", "Enable an accumulation buffer via a " "secondary buffer.", FALSE, MX_PARAM_READWRITE); g_object_class_install_property (object_class, PROP_ACC_ENABLED, pspec); pspec = g_param_spec_pointer ("accumulation-material", "Accumulation material", "Material used for the accumulation buffer.", MX_PARAM_READABLE); g_object_class_install_property (object_class, PROP_ACC_MATERIAL, pspec); } static void mx_offscreen_cogl_texture_notify (MxOffscreen *self) { CoglMatrix matrix; ClutterActor *stage; ClutterPerspective perspective; gfloat z_camera, width, height; MxOffscreenPrivate *priv = self->priv; CoglHandle texture = clutter_texture_get_cogl_texture (CLUTTER_TEXTURE (self)); /* Recreated the texture, get rid of the fbo */ if (priv->fbo) { cogl_handle_unref (priv->fbo); priv->fbo = NULL; } if (!texture) return; /* Create fbo */ priv->fbo = cogl_offscreen_new_to_texture (texture); if (!priv->fbo) { g_warning (G_STRLOC ": Unable to create offscreen buffer for actor"); return; } /* Setup the viewport (code derived from Clutter) */ /* FIXME: This code will eventually be a public function in Clutter, * so replace this when it is. */ cogl_push_framebuffer (priv->fbo); stage = clutter_actor_get_stage (priv->child); clutter_stage_get_perspective (CLUTTER_STAGE (stage), &perspective); /* FIXME: The code below to calculate the modelview matrix only works if the aspect ratio is 1.0. Clutter changed to use a different aspect ratio and a different way to calculate the modelview in commit eef9078f892 so the code no longer works. Ideally this would use the new cogl_matrix_view_2d_in_perspective function to share the calculation code with Clutter but that function is marked as experimental. Instead we just always use an aspect ratio of 1.0 */ if (!CLUTTER_IS_STAGE (priv->child)) perspective.aspect = 1.0; width = cogl_texture_get_width (texture); height = cogl_texture_get_height (texture); cogl_set_viewport (0, 0, width, height); cogl_perspective (perspective.fovy, perspective.aspect, perspective.z_near, perspective.z_far); /* If the child is a ClutterStage then we can avoid calculating our own modelview matrix and just directly use its matrix because it should already be indented for the same size buffer. This gives us the best chance of looking the same as what's drawn in the stage */ if (CLUTTER_IS_STAGE (priv->child)) { cogl_matrix_init_identity (&matrix); clutter_actor_get_transformation_matrix (priv->child, &matrix); } else { cogl_get_projection_matrix (&matrix); z_camera = 0.5 * matrix.xx; cogl_matrix_init_identity (&matrix); cogl_matrix_translate (&matrix, -0.5f, -0.5f, -z_camera); cogl_matrix_scale (&matrix, 1.f / width, -1.f / height, 1.f / width); cogl_matrix_translate (&matrix, 0.f, -1.f * height, 0.f); } cogl_set_modelview_matrix (&matrix); cogl_pop_framebuffer (); g_object_notify (G_OBJECT (self), "buffer"); } static void mx_offscreen_init (MxOffscreen *self) { MxOffscreenPrivate *priv = self->priv = OFFSCREEN_PRIVATE (self); priv->auto_update = TRUE; priv->redirect_enabled = TRUE; g_signal_connect (self, "notify::cogl-texture", G_CALLBACK (mx_offscreen_cogl_texture_notify), NULL); } static gboolean mx_offscreen_pre_paint_cb (ClutterActor *actor, MxOffscreen *offscreen) { CoglColor zero_colour; gfloat width, height; MxOffscreenPrivate *priv = offscreen->priv; priv->pre_paint_done = FALSE; clutter_actor_get_size (actor, &width, &height); if (width * height < 1) return FALSE; if (!mx_offscreen_ensure_buffers (offscreen)) { g_warning (G_STRLOC ": Unable to create necessary buffers"); return FALSE; } /* Disable shaders when we paint our off-screen children */ mx_offscreen_toggle_shaders (offscreen, &priv->disabled_shaders, FALSE); /* Start drawing */ cogl_push_framebuffer (priv->fbo); cogl_push_matrix (); /* Clear. If the source actor is a stage then it will clear the buffer itself so we should avoid duplicating that work here */ if (!CLUTTER_IS_STAGE (priv->child)) { cogl_color_set_from_4ub (&zero_colour, 0x00, 0x00, 0x00, 0x00); cogl_clear (&zero_colour, COGL_BUFFER_BIT_COLOR | COGL_BUFFER_BIT_STENCIL | COGL_BUFFER_BIT_DEPTH); } priv->pre_paint_done = TRUE; return TRUE; } static void mx_offscreen_post_paint_cb (ClutterActor *actor, MxOffscreen *offscreen) { MxOffscreenPrivate *priv = offscreen->priv; if (!priv->fbo || !priv->pre_paint_done) return; /* Restore state */ cogl_pop_matrix (); cogl_pop_framebuffer (); /* Re-enable shaders */ mx_offscreen_toggle_shaders (offscreen, &priv->disabled_shaders, TRUE); } static void mx_offscreen_queue_redraw_cb (ClutterActor *source, ClutterActor *origin, ClutterActor *offscreen) { MxOffscreenPrivate *priv = MX_OFFSCREEN (offscreen)->priv; /* This is to stop possible infinite recursion when cloning. */ if (!priv->queued_redraw) { priv->queued_redraw = TRUE; clutter_actor_queue_redraw (offscreen); priv->queued_redraw = FALSE; } } /** * mx_offscreen_new: * * Creates a new #MxOffscreen. * * Returns: a newly allocated #MxOffscreen */ ClutterActor * mx_offscreen_new (void) { return g_object_new (MX_TYPE_OFFSCREEN, NULL); } /** * mx_offscreen_set_child: * @offscreen: A #MxOffscreen * @actor: A #ClutterActor * * Redirects the painting of @actor to the offscreen surface owned by * @offscreen. In the event that @actor is unparented, it will be parented * to @offscreen. Note that when you redirect the painting of @actor, it * will no longer be painted in its original position in the scenegraph. */ void mx_offscreen_set_child (MxOffscreen *offscreen, ClutterActor *actor) { MxOffscreenPrivate *priv; g_return_if_fail (MX_IS_OFFSCREEN (offscreen)); priv = offscreen->priv; if (priv->child == actor) return; if (priv->child) { ClutterActor *old_child = g_object_ref (priv->child); if (clutter_actor_get_parent (priv->child) == (ClutterActor *)offscreen) { clutter_actor_unparent (priv->child); priv->child = NULL; g_signal_emit_by_name (offscreen, "actor-removed", old_child); } else { g_signal_handlers_disconnect_by_func (priv->child, mx_offscreen_pre_paint_cb, offscreen); g_signal_handlers_disconnect_by_func (priv->child, mx_offscreen_post_paint_cb, offscreen); g_signal_handlers_disconnect_by_func (priv->child, mx_offscreen_queue_redraw_cb, offscreen); g_object_unref (priv->child); priv->child = NULL; } g_object_unref (old_child); } if (actor) { priv->child = actor; if (!clutter_actor_get_parent (actor) && !CLUTTER_IS_STAGE (actor)) { clutter_actor_set_parent (actor, CLUTTER_ACTOR (offscreen)); g_signal_emit_by_name (offscreen, "actor-added", actor); } else { g_signal_connect (priv->child, "paint", G_CALLBACK (mx_offscreen_pre_paint_cb), offscreen); g_signal_connect_after (priv->child, "paint", G_CALLBACK (mx_offscreen_post_paint_cb), offscreen); g_signal_connect (priv->child, "queue-redraw", G_CALLBACK (mx_offscreen_queue_redraw_cb), offscreen); g_object_ref (priv->child); /* Update, as there's no guarantee that the child will be * painted before us in the draw stack (and so we could end * up showing a blank frame). */ mx_offscreen_update (offscreen); } } if (!priv->in_dispose) clutter_actor_queue_relayout (CLUTTER_ACTOR (offscreen)); g_object_notify (G_OBJECT (offscreen), "child"); } /** * mx_offscreen_get_child: * @offscreen: A #MxOffscreen * * Gets the value of the #MxOffscreen:child property. * * Returns: (transfer none): The child of the offscreen widget */ ClutterActor * mx_offscreen_get_child (MxOffscreen *offscreen) { g_return_val_if_fail (MX_IS_OFFSCREEN (offscreen), NULL); return offscreen->priv->child; } /** * mx_offscreen_set_pick_child: * @offscreen: A #MxOffscreen * @pick: #TRUE to enable picking of the child actor * * Enable picking of the child actor. */ void mx_offscreen_set_pick_child (MxOffscreen *offscreen, gboolean pick) { g_return_if_fail (MX_IS_OFFSCREEN (offscreen)); if (offscreen->priv->pick_child != pick) { offscreen->priv->pick_child = pick; g_object_notify (G_OBJECT (offscreen), "pick-child"); } } /** * mx_offscreen_get_pick_child: * @offscreen: A #MxOffscreen * * Gets the value of the #MxOffscreen:pick-child property. * * Returns: #TRUE if picking of the child is enabled. */ gboolean mx_offscreen_get_pick_child (MxOffscreen *offscreen) { g_return_val_if_fail (MX_IS_OFFSCREEN (offscreen), FALSE); return offscreen->priv->pick_child; } /** * mx_offscreen_set_auto_update: * @offscreen: A #MxOffscreen * @auto_update: #TRUE if the offscreen surface should be automatically updated * * Enable automatic updating of the offscreen surface when the child is * updated. */ void mx_offscreen_set_auto_update (MxOffscreen *offscreen, gboolean auto_update) { MxOffscreenPrivate *priv; g_return_if_fail (MX_IS_OFFSCREEN (offscreen)); priv = offscreen->priv; if (priv->auto_update != auto_update) { priv->auto_update = auto_update; g_object_notify (G_OBJECT (offscreen), "auto-update"); } } /** * mx_offscreen_get_auto_update: * @offscreen: A #MxOffscreen * * Gets the value of the #MxOffscreen:auto-update property. * * Returns: #TRUE if automatic updating of the offscreen surface is enabled */ gboolean mx_offscreen_get_auto_update (MxOffscreen *offscreen) { g_return_val_if_fail (MX_IS_OFFSCREEN (offscreen), FALSE); return offscreen->priv->auto_update; } /** * mx_offscreen_update: * @offscreen: A #MxOffscreen * * Updates the offscreen surface. This causes the child of @offscreen to be * drawn into the texture of @offscreen. */ void mx_offscreen_update (MxOffscreen *offscreen) { gboolean child_owned; MxOffscreenPrivate *priv = offscreen->priv; if (!priv->child) return; child_owned = (clutter_actor_get_parent (priv->child) == (ClutterActor *)offscreen); if (child_owned) if (!mx_offscreen_pre_paint_cb (priv->child, offscreen)) return; /* Draw actor */ MX_OFFSCREEN_GET_CLASS (offscreen)->paint_child (offscreen); if (child_owned) mx_offscreen_post_paint_cb (priv->child, offscreen); } /** * mx_offscreen_set_redirect_enabled * @offscreen: A #MxOffscreen * @enabled: #TRUE if redirection to the offscreen surface should be enabled * * Sets the value of the #MxOffscreen:redirect-enabled property. When * redirection is enabled, the painting of the child of @offscreen will be * redirected to the texture of @offscreen. * * Since: 1.2 */ void mx_offscreen_set_redirect_enabled (MxOffscreen *offscreen, gboolean enabled) { MxOffscreenPrivate *priv; g_return_if_fail (MX_IS_OFFSCREEN (offscreen)); priv = offscreen->priv; if (priv->redirect_enabled != enabled) { priv->redirect_enabled = enabled; if (enabled && priv->acc_fbo) { CoglColor color; /* Clear the accumulation buffer when the offscreen is * enabled. As the child has been drawn without updating, * the contents of the accumulation buffer is invalid. */ cogl_color_set_from_4ub (&color, 0, 0, 0, 0); cogl_push_framebuffer (priv->fbo); cogl_clear (&color, COGL_BUFFER_BIT_COLOR); cogl_pop_framebuffer (); } g_object_notify (G_OBJECT (offscreen), "redirect-enabled"); clutter_actor_queue_redraw (CLUTTER_ACTOR (offscreen)); } } /** * mx_offscreen_get_redirect_enabled: * @offscreen: A #MxOffscreen * * Gets the value of the #MxOffscreen:redirect-enabled property. * * Returns: #TRUE if offscreen redirection is enabled * * Since: 1.2 */ gboolean mx_offscreen_get_redirect_enabled (MxOffscreen *offscreen) { g_return_val_if_fail (MX_IS_OFFSCREEN (offscreen), FALSE); return offscreen->priv->redirect_enabled; } /** * MxOffscreen:buffer: (type Cogl.Handle) * * The off-screen buffer used to draw the child. */ /** * mx_offscreen_get_buffer: * @offscreen: A #MxOffscreen * * Gets the value of the #MxOffscreen:buffer property. * * Returns: (transfer none): the #CoglHandle for the offscreen buffer object * * Since: 1.2 */ CoglHandle mx_offscreen_get_buffer (MxOffscreen *offscreen) { g_return_val_if_fail (MX_IS_OFFSCREEN (offscreen), NULL); mx_offscreen_ensure_buffers (offscreen); return offscreen->priv->fbo; } /** * mx_offscreen_set_accumulation_enabled: * @offscreen: A #MxOffscreen * @enable: #TRUE to enable an accumulation buffer * * Sets whether the accumulation buffer is enabled. When enabled, an extra * offscreen buffer is allocated, and the contents of the offscreen texture * are blended with this accumulation buffer. By default, the blend function * is set to blend the contents of the offscreen texture with the accumulation * buffer at the opacity specified in the alpha component of the blend * constant. This opacity is 50% by default. * * Since: 1.2 */ void mx_offscreen_set_accumulation_enabled (MxOffscreen *offscreen, gboolean enable) { MxOffscreenPrivate *priv; g_return_if_fail (MX_IS_OFFSCREEN (offscreen)); priv = offscreen->priv; if (priv->acc_enabled != enable) { CoglHandle material = clutter_texture_get_cogl_material (CLUTTER_TEXTURE (offscreen)); priv->acc_enabled = enable; if (enable) { CoglColor blend_color; GError *error = NULL; priv->acc_material = cogl_material_new (); /* Set the default blend string/level for accumulation */ cogl_color_set_from_4ub (&blend_color, 128, 128, 128, 128); cogl_material_set_blend_constant (material, &blend_color); if (!cogl_material_set_blend (material, "RGBA=ADD(SRC_COLOR*(CONSTANT[A])," "DST_COLOR*(1-CONSTANT[A]))", &error)) { g_warning (G_STRLOC ": Error setting blend string: %s", error->message); g_error_free (error); } } else { cogl_handle_unref (priv->acc_material); priv->acc_material = NULL; if (priv->acc_fbo) { cogl_handle_unref (priv->acc_fbo); priv->acc_fbo = NULL; } /* Reset to the default blend string */ cogl_material_set_blend (material, "RGBA=ADD(SRC_COLOR," "DST_COLOR*(1-SRC_COLOR[A]))", NULL); } g_object_notify (G_OBJECT (offscreen), "accumulation-enabled"); } } /** * mx_offscreen_get_accumulation_enabled: * @offscreen: A #MxOffscreen * * Gets the value of the #MxOffscreen:accumulation-enabled property. * * Returns: #TRUE if the accumulation buffer is enabled * * Since: 1.2 */ gboolean mx_offscreen_get_accumulation_enabled (MxOffscreen *offscreen) { g_return_val_if_fail (MX_IS_OFFSCREEN (offscreen), FALSE); return offscreen->priv->acc_enabled; } /** * mx_offscreen_get_accumulation_material: * @offscreen: A #MxOffscreen * * Gets the #MxOffscreen:accumulation-material property. * * Returns: (transfer none): The #CoglHandle for the material used * for the accumulation buffer * * Since: 1.2 */ CoglHandle mx_offscreen_get_accumulation_material (MxOffscreen *offscreen) { g_return_val_if_fail (MX_IS_OFFSCREEN (offscreen), NULL); return offscreen->priv->acc_material; } mx-1.4.7/mx/mx-offscreen.h000066400000000000000000000067331201047117600153500ustar00rootroot00000000000000/* * mx-offscreen.h: An offscreen texture container * * Copyright 2010 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * Boston, MA 02111-1307, USA. * * Written by: Chris Lord * */ #if !defined(MX_H_INSIDE) && !defined(MX_COMPILATION) #error "Only can be included directly.h" #endif #ifndef _MX_OFFSCREEN_H #define _MX_OFFSCREEN_H #include #include G_BEGIN_DECLS #define MX_TYPE_OFFSCREEN mx_offscreen_get_type() #define MX_OFFSCREEN(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ MX_TYPE_OFFSCREEN, MxOffscreen)) #define MX_OFFSCREEN_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), \ MX_TYPE_OFFSCREEN, MxOffscreenClass)) #define MX_IS_OFFSCREEN(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \ MX_TYPE_OFFSCREEN)) #define MX_IS_OFFSCREEN_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE ((klass), \ MX_TYPE_OFFSCREEN)) #define MX_OFFSCREEN_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), \ MX_TYPE_OFFSCREEN, MxOffscreenClass)) /** * MxOffscreen: * * The contents of this structure is private and should only be accessed using * the provided API. */ typedef struct _MxOffscreen MxOffscreen; typedef struct _MxOffscreenClass MxOffscreenClass; typedef struct _MxOffscreenPrivate MxOffscreenPrivate; struct _MxOffscreen { ClutterTexture parent; MxOffscreenPrivate *priv; }; struct _MxOffscreenClass { ClutterTextureClass parent_class; /* vfuncs */ void (* paint_child) (MxOffscreen *self); /* padding for future expansion */ void (*_padding_0) (void); void (*_padding_1) (void); void (*_padding_2) (void); void (*_padding_3) (void); void (*_padding_4) (void); }; GType mx_offscreen_get_type (void) G_GNUC_CONST; ClutterActor *mx_offscreen_new (void); void mx_offscreen_set_child (MxOffscreen *offscreen, ClutterActor *actor); ClutterActor *mx_offscreen_get_child (MxOffscreen *offscreen); void mx_offscreen_set_pick_child (MxOffscreen *offscreen, gboolean pick); gboolean mx_offscreen_get_pick_child (MxOffscreen *offscreen); void mx_offscreen_set_auto_update (MxOffscreen *offscreen, gboolean auto_update); gboolean mx_offscreen_get_auto_update (MxOffscreen *offscreen); void mx_offscreen_set_redirect_enabled (MxOffscreen *offscreen, gboolean enabled); gboolean mx_offscreen_get_redirect_enabled (MxOffscreen *offscreen); CoglHandle mx_offscreen_get_buffer (MxOffscreen *offscreen); void mx_offscreen_update (MxOffscreen *offscreen); void mx_offscreen_set_accumulation_enabled (MxOffscreen *offscreen, gboolean enable); gboolean mx_offscreen_get_accumulation_enabled (MxOffscreen *offscreen); CoglHandle mx_offscreen_get_accumulation_material (MxOffscreen *offscreen); G_END_DECLS #endif /* _MX_OFFSCREEN_H */ mx-1.4.7/mx/mx-path-bar-button.c000066400000000000000000000106121201047117600163670ustar00rootroot00000000000000/* * mx-path-bar-button.c: A button actor for the path bar * * Copyright 2010 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * Boston, MA 02111-1307, USA. * * Written by: Chris Lord * */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "mx-path-bar-button.h" #include G_DEFINE_TYPE (MxPathBarButton, mx_path_bar_button, MX_TYPE_BUTTON) #define PATH_BAR_BUTTON_PRIVATE(o) \ (G_TYPE_INSTANCE_GET_PRIVATE ((o), MX_TYPE_PATH_BAR_BUTTON, MxPathBarButtonPrivate)) struct _MxPathBarButtonPrivate { gdouble transition; }; enum { PROP_0, PROP_TRANSITION }; static void mx_path_bar_button_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { MxPathBarButtonPrivate *priv = MX_PATH_BAR_BUTTON (object)->priv; switch (property_id) { case PROP_TRANSITION: g_value_set_double (value, priv->transition); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } } static void mx_path_bar_button_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { MxPathBarButtonPrivate *priv = MX_PATH_BAR_BUTTON (object)->priv; switch (property_id) { case PROP_TRANSITION: priv->transition = g_value_get_double (value); clutter_actor_queue_relayout (CLUTTER_ACTOR (object)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } } static void mx_path_bar_button_dispose (GObject *object) { G_OBJECT_CLASS (mx_path_bar_button_parent_class)->dispose (object); } static void mx_path_bar_button_finalize (GObject *object) { G_OBJECT_CLASS (mx_path_bar_button_parent_class)->finalize (object); } static void mx_path_bar_button_get_preferred_width (ClutterActor *actor, gfloat for_height, gfloat *min_width_p, gfloat *nat_width_p) { MxPathBarButtonPrivate *priv = MX_PATH_BAR_BUTTON (actor)->priv; CLUTTER_ACTOR_CLASS (mx_path_bar_button_parent_class)-> get_preferred_width (actor, for_height, min_width_p, nat_width_p); if (min_width_p) *min_width_p = ceilf ((*min_width_p) * priv->transition); if (nat_width_p) *nat_width_p = ceilf ((*nat_width_p) * priv->transition); } static void mx_path_bar_button_class_init (MxPathBarButtonClass *klass) { GParamSpec *pspec; GObjectClass *object_class = G_OBJECT_CLASS (klass); ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass); g_type_class_add_private (klass, sizeof (MxPathBarButtonPrivate)); object_class->get_property = mx_path_bar_button_get_property; object_class->set_property = mx_path_bar_button_set_property; object_class->dispose = mx_path_bar_button_dispose; object_class->finalize = mx_path_bar_button_finalize; actor_class->get_preferred_width = mx_path_bar_button_get_preferred_width; pspec = g_param_spec_double ("transition", "Transition", "Transition animation progress.", 0.0, 1.0, 0.0, G_PARAM_READWRITE); g_object_class_install_property (object_class, PROP_TRANSITION, pspec); } static void mx_path_bar_button_init (MxPathBarButton *self) { self->priv = PATH_BAR_BUTTON_PRIVATE (self); g_object_set (G_OBJECT (self), "clip-to-allocation", TRUE, NULL); } ClutterActor * mx_path_bar_button_new (const gchar *label) { return g_object_new (MX_TYPE_PATH_BAR_BUTTON, "label", label, NULL); } mx-1.4.7/mx/mx-path-bar-button.h000066400000000000000000000044451201047117600164030ustar00rootroot00000000000000/* * mx-path-bar-button.h: A button actor for the path bar * * Copyright 2010 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * Boston, MA 02111-1307, USA. * * Written by: Chris Lord * */ #ifndef _MX_PATH_BAR_BUTTON_H #define _MX_PATH_BAR_BUTTON_H #include #include G_BEGIN_DECLS #define MX_TYPE_PATH_BAR_BUTTON mx_path_bar_button_get_type() #define MX_PATH_BAR_BUTTON(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ MX_TYPE_PATH_BAR_BUTTON, MxPathBarButton)) #define MX_PATH_BAR_BUTTON_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), \ MX_TYPE_PATH_BAR_BUTTON, MxPathBarButtonClass)) #define MX_IS_PATH_BAR_BUTTON(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \ MX_TYPE_PATH_BAR_BUTTON)) #define MX_IS_PATH_BAR_BUTTON_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE ((klass), \ MX_TYPE_PATH_BAR_BUTTON)) #define MX_PATH_BAR_BUTTON_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), \ MX_TYPE_PATH_BAR_BUTTON, MxPathBarButtonClass)) typedef struct _MxPathBarButton MxPathBarButton; typedef struct _MxPathBarButtonClass MxPathBarButtonClass; typedef struct _MxPathBarButtonPrivate MxPathBarButtonPrivate; struct _MxPathBarButton { MxButton parent; MxPathBarButtonPrivate *priv; }; struct _MxPathBarButtonClass { MxButtonClass parent_class; /* padding for future expansion */ void (*_padding_0) (void); void (*_padding_1) (void); void (*_padding_2) (void); void (*_padding_3) (void); void (*_padding_4) (void); }; GType mx_path_bar_button_get_type (void) G_GNUC_CONST; ClutterActor *mx_path_bar_button_new (const gchar *label); G_END_DECLS #endif /* _MX_PATH_BAR_BUTTON_H */ mx-1.4.7/mx/mx-path-bar.c000066400000000000000000000652441201047117600150710ustar00rootroot00000000000000/* * mx-path-bar.c: A path bar actor * * Copyright 2010 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * Boston, MA 02111-1307, USA. * * Written by: Chris Lord * */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "mx-path-bar.h" #include "mx-path-bar-button.h" #include "mx-stylable.h" #include "mx-focusable.h" #include "mx-texture-frame.h" #include "mx-private.h" enum { PROP_0, PROP_EDITABLE, PROP_CLEAR_ON_CHANGE, PROP_LEVEL, PROP_ENTRY }; static void mx_stylable_iface_init (MxStylableIface *iface); static void mx_focusable_iface_init (MxFocusableIface *iface); G_DEFINE_TYPE_WITH_CODE (MxPathBar, mx_path_bar, MX_TYPE_WIDGET, G_IMPLEMENT_INTERFACE (MX_TYPE_STYLABLE, mx_stylable_iface_init) G_IMPLEMENT_INTERFACE (MX_TYPE_FOCUSABLE, mx_focusable_iface_init)) #define PATH_BAR_PRIVATE(o) \ (G_TYPE_INSTANCE_GET_PRIVATE ((o), MX_TYPE_PATH_BAR, MxPathBarPrivate)) struct _MxPathBarPrivate { GList *crumbs; gint current_level; gint overlap; gboolean editable; gboolean clear_on_change; ClutterActor *entry; }; static void mx_stylable_iface_init (MxStylableIface *iface) { static gboolean is_initialized = FALSE; if (G_UNLIKELY (!is_initialized)) { GParamSpec *pspec; is_initialized = TRUE; pspec = g_param_spec_int ("x-mx-overlap", "Overlap", "Overlap between buttons.", 0, G_MAXINT, 0, G_PARAM_READWRITE); mx_stylable_iface_install_property (iface, MX_TYPE_PATH_BAR, pspec); } } static MxFocusable * mx_path_bar_accept_focus (MxFocusable *focusable, MxFocusHint hint) { MxFocusable *focus_widget; MxPathBarPrivate *priv = MX_PATH_BAR (focusable)->priv; if (!priv->editable && !priv->current_level) return NULL; if (priv->current_level) { if (hint == MX_FOCUS_HINT_LAST) { if (priv->editable) focus_widget = MX_FOCUSABLE (priv->entry); else focus_widget = MX_FOCUSABLE (g_list_nth_data (priv->crumbs, priv->current_level - 1)); } else focus_widget = MX_FOCUSABLE (priv->crumbs->data); } else focus_widget = MX_FOCUSABLE (priv->entry); return mx_focusable_accept_focus (focus_widget, hint); } static MxFocusable * mx_path_bar_move_focus (MxFocusable *focusable, MxFocusDirection direction, MxFocusable *from) { gint i; GList *c, *last; MxFocusable *focus_widget; MxPathBarPrivate *priv = MX_PATH_BAR (focusable)->priv; if (direction == MX_FOCUS_DIRECTION_UP || direction == MX_FOCUS_DIRECTION_DOWN || direction == MX_FOCUS_DIRECTION_OUT) return NULL; last = NULL; focus_widget = NULL; for (i = 0, c = priv->crumbs; c && (i < priv->current_level); c = c->next, i++) { MxFocusable *crumb = c->data; if (crumb == from) { switch (direction) { case MX_FOCUS_DIRECTION_LEFT: case MX_FOCUS_DIRECTION_PREVIOUS: if (!last) return NULL; focus_widget = (MxFocusable *)last->data; break; case MX_FOCUS_DIRECTION_RIGHT: case MX_FOCUS_DIRECTION_NEXT: if (c->next) focus_widget = (MxFocusable *)c->next->data; else if (priv->editable) focus_widget = (MxFocusable *)priv->entry; else return NULL; break; default: return NULL; } } last = c; } if (from == (MxFocusable *)priv->entry) { switch (direction) { case MX_FOCUS_DIRECTION_LEFT: case MX_FOCUS_DIRECTION_PREVIOUS: if (!last) return NULL; else focus_widget = (MxFocusable *)last->data; break; default: return NULL; } } if (!focus_widget) { if (!priv->editable) return NULL; focus_widget = (MxFocusable *)priv->entry; } return mx_focusable_accept_focus (focus_widget, MX_FOCUS_HINT_FIRST); } static void mx_focusable_iface_init (MxFocusableIface *iface) { iface->accept_focus = mx_path_bar_accept_focus; iface->move_focus = mx_path_bar_move_focus; } static void mx_path_bar_style_changed_cb (MxWidget *self, MxStyleChangedFlags flags) { MxPathBarPrivate *priv = MX_PATH_BAR (self)->priv; gint overlap; GList *c; mx_stylable_get (MX_STYLABLE (self), "x-mx-overlap", &overlap, NULL); if (overlap != priv->overlap) { priv->overlap = overlap; clutter_actor_queue_relayout (CLUTTER_ACTOR (self)); } /* Inform our private children */ for (c = priv->crumbs; c; c = c->next) mx_stylable_style_changed (MX_STYLABLE (c->data), flags); if (priv->entry) mx_stylable_style_changed (MX_STYLABLE (priv->entry), flags); } static void mx_path_bar_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { MxPathBar *self = MX_PATH_BAR (object); switch (property_id) { case PROP_EDITABLE: g_value_set_boolean (value, mx_path_bar_get_editable (self)); break; case PROP_LEVEL: g_value_set_int (value, mx_path_bar_get_level (self)); break; case PROP_CLEAR_ON_CHANGE: g_value_set_boolean (value, mx_path_bar_get_clear_on_change (self)); break; case PROP_ENTRY: g_value_set_object (value, mx_path_bar_get_entry (self)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } } static void mx_path_bar_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { MxPathBar *self = MX_PATH_BAR (object); switch (property_id) { case PROP_EDITABLE: mx_path_bar_set_editable (self, g_value_get_boolean (value)); break; case PROP_CLEAR_ON_CHANGE: mx_path_bar_set_clear_on_change (self, g_value_get_boolean (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } } static void mx_path_bar_dispose (GObject *object) { MxPathBarPrivate *priv = MX_PATH_BAR (object)->priv; while (priv->crumbs) { clutter_actor_unparent ((ClutterActor *)priv->crumbs->data); priv->crumbs = g_list_delete_link (priv->crumbs, priv->crumbs); } if (priv->entry) { clutter_actor_unparent (priv->entry); priv->entry = NULL; priv->editable = FALSE; } G_OBJECT_CLASS (mx_path_bar_parent_class)->dispose (object); } static void mx_path_bar_finalize (GObject *object) { G_OBJECT_CLASS (mx_path_bar_parent_class)->finalize (object); } static void mx_path_bar_get_preferred_width (ClutterActor *actor, gfloat for_height, gfloat *min_width_p, gfloat *nat_width_p) { GList *c; MxPadding padding; gfloat min_width, nat_width; MxPathBarPrivate *priv = MX_PATH_BAR (actor)->priv; min_width = nat_width = 0; for (c = priv->crumbs; c; c = c->next) { gfloat cmin_width, cnat_width; ClutterActor *crumb = c->data; clutter_actor_get_preferred_width (crumb, for_height, &cmin_width, &cnat_width); min_width += cmin_width; nat_width += cnat_width; if (c != priv->crumbs) { min_width -= MIN (priv->overlap, cmin_width); nat_width -= MIN (priv->overlap, cnat_width); } } if (priv->entry) { gfloat emin_width, enat_width; clutter_actor_get_preferred_width (priv->entry, for_height, &emin_width, &enat_width); min_width += emin_width; nat_width += enat_width; if (priv->crumbs) { min_width -= MIN (min_width, priv->overlap); nat_width -= MIN (nat_width, priv->overlap); } } mx_widget_get_padding (MX_WIDGET (actor), &padding); if (min_width_p) *min_width_p = min_width + padding.left + padding.right; if (nat_width_p) *nat_width_p = nat_width + padding.left + padding.right; } static void mx_path_bar_get_preferred_height (ClutterActor *actor, gfloat for_width, gfloat *min_height_p, gfloat *nat_height_p) { GList *c; MxPadding padding; gfloat min_height, nat_height; MxPathBarPrivate *priv = MX_PATH_BAR (actor)->priv; min_height = nat_height = 0; for (c = priv->crumbs; c; c = c->next) { gfloat cmin_height, cnat_height; ClutterActor *crumb = c->data; clutter_actor_get_preferred_height (crumb, -1, &cmin_height, &cnat_height); if (cmin_height > min_height) min_height = cmin_height; if (cnat_height > nat_height) nat_height = cnat_height; } if (priv->entry) { gfloat emin_height, enat_height; clutter_actor_get_preferred_height (priv->entry, -1, &emin_height, &enat_height); if (emin_height > min_height) min_height = emin_height; if (enat_height > nat_height) nat_height = enat_height; } mx_widget_get_padding (MX_WIDGET (actor), &padding); min_height += padding.top + padding.bottom; nat_height += padding.top + padding.bottom; /* Check if the border-image is taller than our height. * If so, say we want this larger size instead - this is to * avoid stretching the border-image vertically. */ if (priv->crumbs) { ClutterActor *border = mx_widget_get_border_image (MX_WIDGET (priv->crumbs->data)); if (border) { ClutterTexture *texture = mx_texture_frame_get_parent_texture (MX_TEXTURE_FRAME (border)); if (texture) { gint border_height; clutter_texture_get_base_size (texture, NULL, &border_height); if (border_height > nat_height) nat_height = border_height; if (border_height > min_height) min_height = border_height; } } } if (min_height_p) *min_height_p = min_height; if (nat_height_p) *nat_height_p = nat_height; } static void mx_path_bar_allocate (ClutterActor *actor, const ClutterActorBox *box, ClutterAllocationFlags flags) { GList *c; gint n_crumbs; MxPadding padding; gboolean allocate_pref; ClutterActorBox child_box; gfloat min_width, nat_width, extra_space; MxPathBarPrivate *priv = MX_PATH_BAR (actor)->priv; CLUTTER_ACTOR_CLASS (mx_path_bar_parent_class)->allocate (actor, box, flags); mx_widget_get_padding (MX_WIDGET (actor), &padding); child_box.x1 = padding.left; child_box.y1 = padding.top; child_box.y2 = box->y2 - box->y1 - padding.bottom; /* Get our minimum/natural width so we can decide how to * squash actors if necessary */ clutter_actor_get_preferred_width (actor, box->y2 - box->y1, &min_width, &nat_width); /* If we have less than our natural width, find out how * much extra space above the minimum width we have to * play with. */ if ((box->x2 - box->x1) < nat_width) { extra_space = box->x2 - box->x1 - min_width; if (extra_space < 0) extra_space = 0; allocate_pref = FALSE; } else { extra_space = 0; allocate_pref = TRUE; } /* Allocate crumbs */ n_crumbs = g_list_length (priv->crumbs); for (c = priv->crumbs; c; c = c->next) { gfloat cmin_width, cnat_width; ClutterActor *crumb = c->data; clutter_actor_get_preferred_width (crumb, child_box.y2 - child_box.y1, &cmin_width, &cnat_width); if (!allocate_pref) { /* Allocate a fair share of extra space, but don't allocate * over the natural width. */ child_box.x2 = child_box.x1 + MIN (cmin_width + extra_space / n_crumbs, cnat_width); n_crumbs --; if (extra_space >= (child_box.x2 - child_box.x1 - cmin_width)) extra_space -= (child_box.x2 - child_box.x1 - cmin_width); else extra_space = 0; } else child_box.x2 = child_box.x1 + cnat_width; /* If this is the last crumb, give it all extra space */ if (!priv->entry && !c->next && (box->x2 - box->x1 - padding.right) > (child_box.x2 - child_box.x1)) child_box.x2 = box->x2 - box->x1 - padding.right; clutter_actor_allocate (crumb, &child_box, flags); child_box.x1 = child_box.x2 - MIN (priv->overlap, (child_box.x2 - child_box.x1)); } /* Allocate the entry the rest of the space */ if (priv->editable) { child_box.x2 = box->x2 - box->x1 - padding.right; clutter_actor_allocate (priv->entry, &child_box, flags); } } static void mx_path_bar_map (ClutterActor *actor) { GList *c; MxPathBarPrivate *priv = MX_PATH_BAR (actor)->priv; CLUTTER_ACTOR_CLASS (mx_path_bar_parent_class)->map (actor); for (c = priv->crumbs; c; c = c->next) clutter_actor_map (CLUTTER_ACTOR (c->data)); if (priv->entry) clutter_actor_map (priv->entry); } static void mx_path_bar_unmap (ClutterActor *actor) { GList *c; MxPathBarPrivate *priv = MX_PATH_BAR (actor)->priv; CLUTTER_ACTOR_CLASS (mx_path_bar_parent_class)->unmap (actor); for (c = priv->crumbs; c; c = c->next) clutter_actor_unmap (CLUTTER_ACTOR (c->data)); if (priv->entry) clutter_actor_unmap (priv->entry); } static void mx_path_bar_paint (ClutterActor *actor) { GList *c; MxPathBarPrivate *priv = MX_PATH_BAR (actor)->priv; CLUTTER_ACTOR_CLASS (mx_path_bar_parent_class)->paint (actor); if (priv->entry) clutter_actor_paint (priv->entry); for (c = g_list_last (priv->crumbs); c; c = c->prev) clutter_actor_paint (CLUTTER_ACTOR (c->data)); } static void mx_path_bar_pick (ClutterActor *actor, const ClutterColor *color) { mx_path_bar_paint (actor); } static void mx_path_bar_apply_style (MxWidget *widget, MxStyle *style) { MxPathBarPrivate *priv = MX_PATH_BAR (widget)->priv; GList *c; for (c = priv->crumbs; c; c = c->next) mx_stylable_set_style (MX_STYLABLE (c->data), style); if (priv->entry != NULL) mx_stylable_set_style (MX_STYLABLE (priv->entry), style); } static void mx_path_bar_class_init (MxPathBarClass *klass) { GParamSpec *pspec; GObjectClass *object_class = G_OBJECT_CLASS (klass); ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass); MxWidgetClass *widget_class = MX_WIDGET_CLASS (klass); g_type_class_add_private (klass, sizeof (MxPathBarPrivate)); object_class->get_property = mx_path_bar_get_property; object_class->set_property = mx_path_bar_set_property; object_class->dispose = mx_path_bar_dispose; object_class->finalize = mx_path_bar_finalize; actor_class->get_preferred_width = mx_path_bar_get_preferred_width; actor_class->get_preferred_height = mx_path_bar_get_preferred_height; actor_class->allocate = mx_path_bar_allocate; actor_class->map = mx_path_bar_map; actor_class->unmap = mx_path_bar_unmap; actor_class->paint = mx_path_bar_paint; actor_class->pick = mx_path_bar_pick; widget_class->apply_style = mx_path_bar_apply_style; pspec = g_param_spec_boolean ("editable", "Editable", "Enable or disable editing", FALSE, G_PARAM_READWRITE); g_object_class_install_property (object_class, PROP_EDITABLE, pspec); pspec = g_param_spec_boolean ("clear-on-change", "Clear on level change", "Whether to clear the entry " "when changing levels", FALSE, G_PARAM_READWRITE); g_object_class_install_property (object_class, PROP_CLEAR_ON_CHANGE, pspec); pspec = g_param_spec_int ("level", "Level", "Depth of the path bar", -1, G_MAXINT, 0, G_PARAM_READABLE); g_object_class_install_property (object_class, PROP_LEVEL, pspec); pspec = g_param_spec_object ("entry", "Entry", "The MxEntry inside the path bar", MX_TYPE_ENTRY, MX_PARAM_READABLE); g_object_class_install_property (object_class, PROP_ENTRY, pspec); } static void mx_path_bar_init (MxPathBar *self) { self->priv = PATH_BAR_PRIVATE (self); g_signal_connect (self, "style-changed", G_CALLBACK (mx_path_bar_style_changed_cb), NULL); } ClutterActor * mx_path_bar_new (void) { return g_object_new (MX_TYPE_PATH_BAR, NULL); } static void mx_path_bar_crumb_clicked_cb (ClutterActor *crumb, MxPathBar *self) { gint i; GList *c; MxPathBarPrivate *priv = self->priv; if (priv->clear_on_change) mx_path_bar_set_text (self, ""); for (c = priv->crumbs, i = 1; c; c = c->next, i++) { if (c->data == crumb) { while (priv->current_level > i) mx_path_bar_pop (self); break; } } } static void mx_path_bar_reset_last_crumb (MxPathBar *bar) { MxPathBarPrivate *priv = bar->priv; ClutterActor *last_crumb = g_list_nth_data (priv->crumbs, priv->current_level -1); if (last_crumb) mx_stylable_set_style_class (MX_STYLABLE (last_crumb), priv->editable ? NULL : "End"); } gint mx_path_bar_push (MxPathBar *bar, const gchar *name) { ClutterActor *crumb; MxPathBarPrivate *priv; g_return_val_if_fail (MX_IS_PATH_BAR (bar), -1); priv = bar->priv; if (priv->clear_on_change) mx_path_bar_set_text (bar, ""); crumb = mx_path_bar_button_new (name); clutter_actor_set_parent (crumb, CLUTTER_ACTOR (bar)); priv->crumbs = g_list_insert (priv->crumbs, crumb, priv->current_level); if (!priv->entry) { if (priv->current_level) { ClutterActor *old_last_crumb = g_list_nth_data (priv->crumbs, priv->current_level - 1); mx_stylable_set_style_class (MX_STYLABLE (old_last_crumb), NULL); } mx_stylable_set_style_class (MX_STYLABLE (crumb), "End"); } priv->current_level ++; g_signal_connect (crumb, "clicked", G_CALLBACK (mx_path_bar_crumb_clicked_cb), bar); clutter_actor_animate (crumb, CLUTTER_EASE_OUT_QUAD, 150, "transition", 1.0, NULL); clutter_actor_queue_relayout (CLUTTER_ACTOR (bar)); g_object_notify (G_OBJECT (bar), "level"); return priv->current_level; } static void mx_path_bar_pop_completed_cb (ClutterAnimation *animation, ClutterActor *crumb) { MxPathBar *self = MX_PATH_BAR (clutter_actor_get_parent (crumb)); MxPathBarPrivate *priv = self->priv; priv->crumbs = g_list_remove (priv->crumbs, crumb); clutter_actor_unparent (crumb); } gint mx_path_bar_pop (MxPathBar *bar) { ClutterActor *crumb; MxPathBarPrivate *priv; g_return_val_if_fail (MX_IS_PATH_BAR (bar), -1); priv = bar->priv; if (priv->clear_on_change) mx_path_bar_set_text (bar, ""); if (priv->current_level == 0) return 0; crumb = g_list_nth_data (priv->crumbs, priv->current_level - 1); clutter_actor_animate (crumb, CLUTTER_EASE_IN_QUAD, 150, "transition", 0.0, "signal-after::completed", mx_path_bar_pop_completed_cb, crumb, NULL); priv->current_level --; mx_path_bar_reset_last_crumb (bar); g_object_notify (G_OBJECT (bar), "level"); return priv->current_level; } gint mx_path_bar_get_level (MxPathBar *bar) { g_return_val_if_fail (MX_IS_PATH_BAR (bar), -1); return bar->priv->current_level; } /** * mx_path_bar_clear: * @bar: An #MxPathBar * * Remove all the current buttons * */ void mx_path_bar_clear (MxPathBar *bar) { g_return_if_fail (MX_IS_PATH_BAR (bar)); while (bar->priv->current_level) mx_path_bar_pop (bar); } /** * mx_path_bar_get_editable: * @bar: A #MxPathBar * * Get the value of the #MxPathBar:editable property. * * Returns: the current value of the "editable" property. */ gboolean mx_path_bar_get_editable (MxPathBar *bar) { g_return_val_if_fail (MX_IS_PATH_BAR (bar), FALSE); return bar->priv->editable; } static void mx_path_bar_entry_faded_cb (ClutterAnimation *animation, MxPathBar *bar) { MxPathBarPrivate *priv = bar->priv; clutter_actor_unparent (priv->entry); priv->entry = NULL; clutter_actor_queue_relayout (CLUTTER_ACTOR (bar)); } /** * mx_path_bar_set_editable: * @bar: A #MxPathBar * @editable: #TRUE if the path bar should be editable * * Set the value of the #MxPathBar:editable property. * */ void mx_path_bar_set_editable (MxPathBar *bar, gboolean editable) { MxPathBarPrivate *priv; g_return_if_fail (MX_IS_PATH_BAR (bar)); priv = bar->priv; if (priv->editable == editable) return; priv->editable = editable; if (!editable) { clutter_actor_animate (priv->entry, CLUTTER_EASE_OUT_QUAD, 150, "opacity", 0x00, "signal-after::completed", mx_path_bar_entry_faded_cb, bar, NULL); } else { if (priv->entry) { ClutterAnimation *anim = clutter_actor_get_animation (priv->entry); g_signal_handlers_disconnect_by_func (anim, mx_path_bar_entry_faded_cb, bar); } else { priv->entry = mx_entry_new (); clutter_actor_set_parent (priv->entry, CLUTTER_ACTOR (bar)); if (CLUTTER_ACTOR_IS_VISIBLE (priv->entry)) clutter_actor_set_opacity (priv->entry, 0x00); } clutter_actor_animate (priv->entry, CLUTTER_EASE_OUT_QUAD, 150, "opacity", 0xff, NULL); } mx_path_bar_reset_last_crumb (bar); g_object_notify (G_OBJECT (bar), "editable"); clutter_actor_queue_relayout (CLUTTER_ACTOR (bar)); } const gchar * mx_path_bar_get_label (MxPathBar *bar, gint level) { ClutterActor *crumb; MxPathBarPrivate *priv; g_return_val_if_fail (MX_IS_PATH_BAR (bar), NULL); g_return_val_if_fail ((level > 0) && (level <= bar->priv->current_level), NULL); priv = bar->priv; crumb = (ClutterActor *)g_list_nth_data (priv->crumbs, level - 1); if (crumb) return mx_button_get_label (MX_BUTTON (crumb)); else return NULL; } /** * mx_path_bar_set_label: * @bar: A #MxPathBar * @level: A #gint * @label: A #gchar * * Set the text on the button specified by @level * */ void mx_path_bar_set_label (MxPathBar *bar, gint level, const gchar *label) { ClutterActor *crumb; MxPathBarPrivate *priv; g_return_if_fail (MX_IS_PATH_BAR (bar)); g_return_if_fail ((level > 0) && (level <= bar->priv->current_level)); priv = bar->priv; crumb = (ClutterActor *)g_list_nth_data (priv->crumbs, level - 1); if (crumb) mx_button_set_label (MX_BUTTON (crumb), label); } const gchar * mx_path_bar_get_text (MxPathBar *bar) { g_return_val_if_fail (MX_IS_PATH_BAR (bar), NULL); if (!bar->priv->editable) return NULL; return mx_entry_get_text (MX_ENTRY (bar->priv->entry)); } /** * mx_path_bar_set_text: * @bar: A #MxPathBar * @text: string to set the editable text to. * * Set the text in the editable area of the #MxPathBar * */ void mx_path_bar_set_text (MxPathBar *bar, const gchar *text) { g_return_if_fail (MX_IS_PATH_BAR (bar)); if (!bar->priv->editable) return; mx_entry_set_text (MX_ENTRY (bar->priv->entry), text); } /** * mx_path_bar_get_entry: * @bar: A #MxPathBar * * Get the MxEntry used as the editable area in the MxPathBar. * * Returns: (transfer none): MxEntry * */ MxEntry * mx_path_bar_get_entry (MxPathBar *bar) { g_return_val_if_fail (MX_IS_PATH_BAR (bar), NULL); return (MxEntry *)bar->priv->entry; } /** * mx_path_bar_get_clear_on_change: * @bar: A #MxPathBar * * Get the value of the #MxPathBar:clear-on-change property * * Returns: the value of the "clear-on-change" property */ gboolean mx_path_bar_get_clear_on_change (MxPathBar *bar) { g_return_val_if_fail (MX_IS_PATH_BAR (bar), FALSE); return bar->priv->clear_on_change; } /** * mx_path_bar_set_clear_on_change: * @bar: A #MxPathBar * @clear_on_change: the new value of the property * * Set theh value of the #MxPathBar:clear-on-change property * */ void mx_path_bar_set_clear_on_change (MxPathBar *bar, gboolean clear_on_change) { g_return_if_fail (MX_IS_PATH_BAR (bar)); if (bar->priv->clear_on_change != clear_on_change) { bar->priv->clear_on_change = clear_on_change; g_object_notify (G_OBJECT (bar), "clear-on-change"); } } mx-1.4.7/mx/mx-path-bar.h000066400000000000000000000064121201047117600150660ustar00rootroot00000000000000/* * mx-path-bar.h: A path bar actor * * Copyright 2010 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * Boston, MA 02111-1307, USA. * * Written by: Chris Lord * */ #if !defined(MX_H_INSIDE) && !defined(MX_COMPILATION) #error "Only can be included directly.h" #endif #ifndef _MX_PATH_BAR_H #define _MX_PATH_BAR_H #include #include #include G_BEGIN_DECLS #define MX_TYPE_PATH_BAR mx_path_bar_get_type() #define MX_PATH_BAR(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ MX_TYPE_PATH_BAR, MxPathBar)) #define MX_PATH_BAR_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), \ MX_TYPE_PATH_BAR, MxPathBarClass)) #define MX_IS_PATH_BAR(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \ MX_TYPE_PATH_BAR)) #define MX_IS_PATH_BAR_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE ((klass), \ MX_TYPE_PATH_BAR)) #define MX_PATH_BAR_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), \ MX_TYPE_PATH_BAR, MxPathBarClass)) /** * MxPathBar: * * The contents of this structure is private and should only be accessed using * the provided API. */ typedef struct _MxPathBar MxPathBar; typedef struct _MxPathBarClass MxPathBarClass; typedef struct _MxPathBarPrivate MxPathBarPrivate; struct _MxPathBar { MxWidget parent; MxPathBarPrivate *priv; }; struct _MxPathBarClass { MxWidgetClass parent_class; /* padding for future expansion */ void (*_padding_0) (void); void (*_padding_1) (void); void (*_padding_2) (void); void (*_padding_3) (void); void (*_padding_4) (void); }; GType mx_path_bar_get_type (void) G_GNUC_CONST; ClutterActor *mx_path_bar_new (void); gint mx_path_bar_push (MxPathBar *bar, const gchar *name); gint mx_path_bar_pop (MxPathBar *bar); gint mx_path_bar_get_level (MxPathBar *bar); void mx_path_bar_clear (MxPathBar *bar); gboolean mx_path_bar_get_editable (MxPathBar *bar); void mx_path_bar_set_editable (MxPathBar *bar, gboolean editable); gboolean mx_path_bar_get_clear_on_change (MxPathBar *bar); void mx_path_bar_set_clear_on_change (MxPathBar *bar, gboolean clear_on_change); const gchar *mx_path_bar_get_label (MxPathBar *bar, gint level); void mx_path_bar_set_label (MxPathBar *bar, gint level, const gchar *label); const gchar *mx_path_bar_get_text (MxPathBar *bar); void mx_path_bar_set_text (MxPathBar *bar, const gchar *text); MxEntry *mx_path_bar_get_entry (MxPathBar *bar); G_END_DECLS #endif /* _MX_PATH_BAR_H */ mx-1.4.7/mx/mx-private.c000066400000000000000000000043441201047117600150370ustar00rootroot00000000000000/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * mx-private.c * * Copyright 2010 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * Boston, MA 02111-1307, USA. * * Written by: Thomas Wood * */ #include "mx-private.h" static GDebugKey debug_keys[] = { {"layout", MX_DEBUG_LAYOUT}, {"inspector", MX_DEBUG_INSPECTOR}, {"focus", MX_DEBUG_FOCUS}, {"css", MX_DEBUG_CSS} }; gboolean _mx_debug (gint check) { static gint debug = -1; if (G_UNLIKELY (debug == -1)) { const char *debug_str; debug_str = g_getenv ("MX_DEBUG"); debug = g_parse_debug_string (debug_str, debug_keys, G_N_ELEMENTS (debug_keys)); } return debug & check; } const gchar * _mx_enum_to_string (GType type, gint value) { GEnumClass *enum_class; GEnumValue *enum_value; const gchar *val; enum_class = g_type_class_ref (type); enum_value = g_enum_get_value (enum_class, value); if (enum_value) val = enum_value->value_nick; else val = ""; g_type_class_unref (enum_class); return val; } gboolean _mx_string_to_enum (GType type, const gchar *nick, gint *value) { GEnumClass *enum_class; GEnumValue *enum_value; gboolean ret = FALSE; enum_class = g_type_class_ref (type); enum_value = g_enum_get_value_by_nick (enum_class, nick); if (enum_value) { if (value) *value = enum_value->value; ret = TRUE; } g_type_class_unref (enum_class); return ret; } mx-1.4.7/mx/mx-private.h000066400000000000000000000101631201047117600150400ustar00rootroot00000000000000/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * mx-private.h: Private declarations * * Copyright 2007 OpenedHand * Copyright 2009 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * Boston, MA 02111-1307, USA. * */ #ifndef __MX_PRIVATE_H__ #define __MX_PRIVATE_H__ #include #include "mx.h" G_BEGIN_DECLS #define MX_PARAM_READABLE \ (G_PARAM_READABLE | \ G_PARAM_STATIC_NICK | G_PARAM_STATIC_NAME | G_PARAM_STATIC_BLURB) #define MX_PARAM_WRITABLE \ (G_PARAM_WRITABLE | \ G_PARAM_STATIC_NICK | G_PARAM_STATIC_NAME | G_PARAM_STATIC_BLURB) #define MX_PARAM_READWRITE \ (G_PARAM_READABLE | G_PARAM_WRITABLE | \ G_PARAM_STATIC_NICK | G_PARAM_STATIC_NAME | G_PARAM_STATIC_BLURB) #define MX_ALIGN_TO_FLOAT(x) ((x == MX_ALIGN_START) ? 0.0 : (x == MX_ALIGN_MIDDLE) ? 0.5 : 1.0) struct _MxTableChild { ClutterChildMeta parent_instance; gint col; gint row; gint col_span; gint row_span; gdouble x_align; gdouble y_align; guint x_expand : 1; guint y_expand : 1; guint x_fill : 1; guint y_fill : 1; }; typedef enum { MX_SETTINGS_ICON_THEME = 1, MX_SETTINGS_FONT_NAME, MX_SETTINGS_LONG_PRESS_TIMEOUT, MX_SETTINGS_SMALL_SCREEN, MX_SETTINGS_DRAG_THRESHOLD } MxSettingsProperty; ClutterActor *_mx_widget_get_dnd_clone (MxWidget *widget); void _mx_box_layout_start_animation (MxBoxLayout *box); void _mx_bin_get_align_factors (MxBin *bin, gdouble *x_align, gdouble *y_align); /* used by MxTableChild to update row/column count */ void _mx_table_update_row_col (MxTable *table, gint row, gint col); CoglHandle _mx_window_get_icon_cogl_texture (MxWindow *window); ClutterActor * _mx_window_get_resize_grip (MxWindow *window); void _mx_style_invalidate_cache (MxStylable *stylable); gchar * _mx_stylable_get_style_string (MxStylable *stylable); const gchar * _mx_enum_to_string (GType type, gint value); gboolean _mx_string_to_enum (GType type, const gchar *nick, gint *value); void _mx_fade_effect_set_freeze_update (MxFadeEffect *effect, gboolean freeze); gboolean _mx_fade_effect_get_freeze_update (MxFadeEffect *effect); typedef enum { MX_DEBUG_LAYOUT = 1 << 0, MX_DEBUG_INSPECTOR = 1 << 1, MX_DEBUG_FOCUS = 1 << 2, MX_DEBUG_CSS = 1 << 3, MX_DEBUG_STYLE_CACHE = 1 << 4 } MxDebugTopic; gboolean _mx_debug (gint debug); #ifdef G_HAVE_ISO_VARARGS #define MX_NOTE(topic,...) G_STMT_START { \ if (G_UNLIKELY (_mx_debug(MX_DEBUG_##topic))) \ g_message ("[" #topic "] " G_STRLOC ": " __VA_ARGS__); \ } G_STMT_END #elif G_HAVE_GNUC_VARARGS #define MX_NOTE(topic, fmt, args...) G_STMT_START { \ if (G_UNLIKELY (_mx_debug(MX_DEBUG_##topic))) \ g_message ("[" #topic "] " G_STRLOC ": " fmt, ##args); \ } G_STMT_END #else /* no variadic macros, your compiler sucks at C */ #warning "Can't use variadic macros, MX_NOTE() disabled." static inline void MX_NOTE (gint topic, const gchar *fmt, ...) { } #endif /* G_HAVE_ISO_VARARGS */ G_END_DECLS #endif /* __MX_PRIVATE_H__ */ mx-1.4.7/mx/mx-progress-bar-fill.c000066400000000000000000000060621201047117600167160ustar00rootroot00000000000000/* * mx-progress-bar-fill.c: Fill used in progress bar/slider widgets * * Copyright 2009 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * */ /* * This class is private to MX */ #include "mx-progress-bar-fill.h" #include "mx-stylable.h" static void mx_stylable_iface_init (MxStylableIface *iface); G_DEFINE_TYPE_WITH_CODE (MxProgressBarFill, _mx_progress_bar_fill, MX_TYPE_WIDGET, G_IMPLEMENT_INTERFACE (MX_TYPE_STYLABLE, mx_stylable_iface_init)) static void mx_stylable_iface_init (MxStylableIface *iface) { static gboolean is_initialized = FALSE; if (!is_initialized) { GParamSpec *pspec; is_initialized = TRUE; pspec = g_param_spec_uint ("height", "Height", "Height of the bar, in px", 0, G_MAXUINT, 16, G_PARAM_READWRITE); mx_stylable_iface_install_property (iface, MX_TYPE_PROGRESS_BAR_FILL, pspec); } } static void mx_progress_bar_fill_get_preferred_height (ClutterActor *actor, gfloat for_width, gfloat *min_height_p, gfloat *nat_height_p) { MxProgressBarFill *self = MX_PROGRESS_BAR_FILL (actor); if (min_height_p) *min_height_p = (gfloat)self->height; if (nat_height_p) *nat_height_p = (gfloat)self->height; } static void _mx_progress_bar_fill_class_init (MxProgressBarFillClass *klass) { ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass); actor_class->get_preferred_height = mx_progress_bar_fill_get_preferred_height; } static void mx_progress_bar_fill_style_changed_cb (MxProgressBarFill *self) { guint height; mx_stylable_get (MX_STYLABLE (self), "height", &height, NULL); if (self->height != height) { self->height = height; clutter_actor_queue_relayout (CLUTTER_ACTOR (self)); } } static void _mx_progress_bar_fill_init (MxProgressBarFill *self) { self->height = 16; g_signal_connect (self, "style-changed", G_CALLBACK (mx_progress_bar_fill_style_changed_cb), NULL); } ClutterActor * _mx_progress_bar_fill_new (void) { return g_object_new (MX_TYPE_PROGRESS_BAR_FILL, NULL); } mx-1.4.7/mx/mx-progress-bar-fill.h000066400000000000000000000041531201047117600167220ustar00rootroot00000000000000/* * mx-progress-bar-fill.c: Fill used in progress bar/slider widgets * * Copyright 2009 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * */ /* * This class is private to MX */ #include #include G_BEGIN_DECLS #ifndef _MX_PROGRESS_BAR_FILL_H #define _MX_PROGRESS_BAR_FILL_H #define MX_TYPE_PROGRESS_BAR_FILL _mx_progress_bar_fill_get_type() #define MX_PROGRESS_BAR_FILL(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ MX_TYPE_PROGRESS_BAR_FILL, MxProgressBarFill)) #define MX_PROGRESS_BAR_FILL_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), \ MX_TYPE_PROGRESS_BAR_FILL, MxProgressBarFillClass)) #define MX_IS_PROGRESS_BAR_FILL(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \ MX_TYPE_PROGRESS_BAR_FILL)) #define MX_IS_PROGRESS_BAR_FILL_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE ((klass), \ MX_TYPE_PROGRESS_BAR_FILL)) #define MX_PROGRESS_BAR_FILL_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), \ MX_TYPE_PROGRESS_BAR_FILL, MxProgressBarFillClass)) typedef struct { MxWidget parent; guint height; } MxProgressBarFill; typedef struct { MxWidgetClass parent_class; } MxProgressBarFillClass; GType _mx_progress_bar_fill_get_type (void) G_GNUC_CONST; ClutterActor * _mx_progress_bar_fill_new (void); G_END_DECLS #endif /* _MX_PROGRESS_BAR_FILL_H */ mx-1.4.7/mx/mx-progress-bar.c000066400000000000000000000227511201047117600157750ustar00rootroot00000000000000/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * mx-progress-bar.c: Progress bar widget * * Copyright 2009 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * * Written by: Chris Lord * */ /** * SECTION:mx-progress-bar * @short_description: visual representation of progress * * #MxProgressBar visually represents the progress of an action or a value in * a range. * */ #include "mx-progress-bar.h" #include "mx-progress-bar-fill.h" #include "mx-texture-frame.h" #include "mx-private.h" G_DEFINE_TYPE (MxProgressBar, mx_progress_bar, MX_TYPE_WIDGET) #define PROGRESS_BAR_PRIVATE(o) \ (G_TYPE_INSTANCE_GET_PRIVATE ((o), MX_TYPE_PROGRESS_BAR, MxProgressBarPrivate)) struct _MxProgressBarPrivate { ClutterActor *fill; gdouble progress; }; enum { PROP_0, PROP_PROGRESS }; static void mx_progress_bar_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { MxProgressBar *self = MX_PROGRESS_BAR (object); switch (property_id) { case PROP_PROGRESS: g_value_set_double (value, mx_progress_bar_get_progress (self)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } } static void mx_progress_bar_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { MxProgressBar *self = MX_PROGRESS_BAR (object); switch (property_id) { case PROP_PROGRESS: mx_progress_bar_set_progress (self, g_value_get_double (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } } static void mx_progress_bar_dispose (GObject *object) { MxProgressBarPrivate *priv = MX_PROGRESS_BAR (object)->priv; if (priv->fill) { clutter_actor_unparent (CLUTTER_ACTOR (priv->fill)); priv->fill = NULL; } G_OBJECT_CLASS (mx_progress_bar_parent_class)->dispose (object); } static void mx_progress_bar_finalize (GObject *object) { G_OBJECT_CLASS (mx_progress_bar_parent_class)->finalize (object); } static void mx_progress_bar_paint (ClutterActor *actor) { MxProgressBarPrivate *priv = MX_PROGRESS_BAR (actor)->priv; CLUTTER_ACTOR_CLASS (mx_progress_bar_parent_class)->paint (actor); if (priv->progress) clutter_actor_paint (priv->fill); } static void mx_progress_bar_allocate_fill (MxProgressBar *self, const ClutterActorBox *box, ClutterAllocationFlags flags) { ClutterActorBox box_data; MxProgressBarPrivate *priv = self->priv; if (!box) { clutter_actor_get_allocation_box (CLUTTER_ACTOR (self), &box_data); box = &box_data; } if (priv->progress) { ClutterActorBox child_box; MxPadding padding; mx_widget_get_padding (MX_WIDGET (self), &padding); child_box.x1 = padding.left; child_box.y1 = padding.top; child_box.y2 = (box->y2 - box->y1) - padding.bottom; child_box.x2 = ((box->x2 - box->x1 - padding.left - padding.right) * priv->progress) + padding.left; clutter_actor_allocate (priv->fill, &child_box, flags); } } static void mx_progress_bar_allocate (ClutterActor *actor, const ClutterActorBox *box, ClutterAllocationFlags flags) { MxProgressBar *self = MX_PROGRESS_BAR (actor); CLUTTER_ACTOR_CLASS (mx_progress_bar_parent_class)-> allocate (actor, box, flags); mx_progress_bar_allocate_fill (self, box, flags); } static void mx_progress_bar_map (ClutterActor *actor) { MxProgressBarPrivate *priv = MX_PROGRESS_BAR (actor)->priv; CLUTTER_ACTOR_CLASS (mx_progress_bar_parent_class)->map (actor); clutter_actor_map (priv->fill); } static void mx_progress_bar_unmap (ClutterActor *actor) { MxProgressBarPrivate *priv = MX_PROGRESS_BAR (actor)->priv; if (priv->fill) clutter_actor_unmap (priv->fill); CLUTTER_ACTOR_CLASS (mx_progress_bar_parent_class)->unmap (actor); } static void mx_progress_bar_apply_style (MxWidget *widget, MxStyle *style) { MxProgressBarPrivate *priv = MX_PROGRESS_BAR (widget)->priv; if (priv->fill != NULL) mx_stylable_set_style (MX_STYLABLE (priv->fill), style); } static void mx_progress_bar_get_preferred_width (ClutterActor *actor, gfloat for_height, gfloat *min_width_p, gfloat *nat_width_p) { MxPadding padding; MxProgressBarPrivate *priv = MX_PROGRESS_BAR (actor)->priv; gfloat height; clutter_actor_get_preferred_width (priv->fill, for_height, min_width_p, nat_width_p); clutter_actor_get_preferred_height (priv->fill, -1, &height, NULL); mx_widget_get_padding (MX_WIDGET (actor), &padding); if (min_width_p) *min_width_p += padding.left + padding.right; /* Add an arbitrary amount to the width for preferred width, so that the * indicator is visible and can display some values */ if (nat_width_p) *nat_width_p += padding.left + padding.right + height * 4; } static void mx_progress_bar_get_preferred_height (ClutterActor *actor, gfloat for_width, gfloat *min_height_p, gfloat *nat_height_p) { MxPadding padding; MxProgressBarPrivate *priv = MX_PROGRESS_BAR (actor)->priv; clutter_actor_get_preferred_height (priv->fill, for_width, min_height_p, nat_height_p); mx_widget_get_padding (MX_WIDGET (actor), &padding); if (min_height_p) *min_height_p += padding.top + padding.bottom; if (nat_height_p) *nat_height_p += padding.top + padding.bottom; } static void mx_progress_bar_class_init (MxProgressBarClass *klass) { GParamSpec *pspec; GObjectClass *object_class = G_OBJECT_CLASS (klass); ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass); MxWidgetClass *widget_class = MX_WIDGET_CLASS (klass); g_type_class_add_private (klass, sizeof (MxProgressBarPrivate)); object_class->get_property = mx_progress_bar_get_property; object_class->set_property = mx_progress_bar_set_property; object_class->dispose = mx_progress_bar_dispose; object_class->finalize = mx_progress_bar_finalize; actor_class->paint = mx_progress_bar_paint; actor_class->get_preferred_width = mx_progress_bar_get_preferred_width; actor_class->get_preferred_height = mx_progress_bar_get_preferred_height; actor_class->allocate = mx_progress_bar_allocate; actor_class->map = mx_progress_bar_map; actor_class->unmap = mx_progress_bar_unmap; widget_class->apply_style = mx_progress_bar_apply_style; pspec = g_param_spec_double ("progress", "Progress", "Progress", 0.0, 1.0, 0.0, MX_PARAM_READWRITE); g_object_class_install_property (object_class, PROP_PROGRESS, pspec); } static void mx_progress_bar_init (MxProgressBar *self) { MxProgressBarPrivate *priv = self->priv = PROGRESS_BAR_PRIVATE (self); priv->fill = g_object_new (MX_TYPE_PROGRESS_BAR_FILL, "clip-to-allocation", TRUE, NULL); clutter_actor_set_parent (priv->fill, CLUTTER_ACTOR (self)); } /** * mx_progress_bar_new: * * Create a new progress bar * * Returns: a new #MxProgressBar */ ClutterActor * mx_progress_bar_new (void) { return g_object_new (MX_TYPE_PROGRESS_BAR, NULL); } /** * mx_progress_bar_set_progress: * @bar: A #MxProgressBar * @progress: A value between 0.0 and 1.0 * * Set the progress of the progress bar * */ void mx_progress_bar_set_progress (MxProgressBar *bar, gdouble progress) { MxProgressBarPrivate *priv; g_return_if_fail (MX_IS_PROGRESS_BAR (bar)); priv = bar->priv; if (priv->progress != progress) { priv->progress = progress; mx_progress_bar_allocate_fill (bar, NULL, 0); clutter_actor_queue_redraw (CLUTTER_ACTOR (bar)); g_object_notify (G_OBJECT (bar), "progress"); } } /** * mx_progress_bar_get_progress: * @bar: A #MxProgressBar * * Get the progress of the progress bar * * Returns: A value between 0.0 and 1.0 */ gdouble mx_progress_bar_get_progress (MxProgressBar *bar) { g_return_val_if_fail (MX_IS_PROGRESS_BAR (bar), 0.0); return bar->priv->progress; } mx-1.4.7/mx/mx-progress-bar.h000066400000000000000000000052411201047117600157750ustar00rootroot00000000000000/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * mx-progress-bar.h: Progress bar widget * * Copyright 2009 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * * Written by: Chris Lord * */ #if !defined(MX_H_INSIDE) && !defined(MX_COMPILATION) #error "Only can be included directly.h" #endif #ifndef _MX_PROGRESS_BAR_H #define _MX_PROGRESS_BAR_H #include #include G_BEGIN_DECLS #define MX_TYPE_PROGRESS_BAR mx_progress_bar_get_type() #define MX_PROGRESS_BAR(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ MX_TYPE_PROGRESS_BAR, MxProgressBar)) #define MX_PROGRESS_BAR_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), \ MX_TYPE_PROGRESS_BAR, MxProgressBarClass)) #define MX_IS_PROGRESS_BAR(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \ MX_TYPE_PROGRESS_BAR)) #define MX_IS_PROGRESS_BAR_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE ((klass), \ MX_TYPE_PROGRESS_BAR)) #define MX_PROGRESS_BAR_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), \ MX_TYPE_PROGRESS_BAR, MxProgressBarClass)) typedef struct _MxProgressBar MxProgressBar; typedef struct _MxProgressBarClass MxProgressBarClass; typedef struct _MxProgressBarPrivate MxProgressBarPrivate; /** * MxProgressBar: * * The contents of this structure are private and should only be * accessed through the public API. */ struct _MxProgressBar { /*< private >*/ MxWidget parent; MxProgressBarPrivate *priv; }; struct _MxProgressBarClass { MxWidgetClass parent_class; /* padding for future expansion */ void (*_padding_0) (void); void (*_padding_1) (void); void (*_padding_2) (void); void (*_padding_3) (void); void (*_padding_4) (void); }; GType mx_progress_bar_get_type (void); ClutterActor *mx_progress_bar_new (void); void mx_progress_bar_set_progress (MxProgressBar *bar, gdouble progress); gdouble mx_progress_bar_get_progress (MxProgressBar *bar); G_END_DECLS #endif /* _MX_PROGRESS_BAR_H */ mx-1.4.7/mx/mx-scroll-bar.c000066400000000000000000001117441201047117600154300ustar00rootroot00000000000000/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * mx-scroll-bar.c: Scroll bar actor * * Copyright 2008 OpenedHand * Copyright 2009, 2010 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * * Written by: Chris Lord * Port to Mx by: Robert Staudinger * */ /** * SECTION:mx-scroll-bar * @short_description: a user interface element to control scrollable areas. * * The #MxScrollBar allows users to scroll scrollable actors, either by * the step or page amount, or by manually dragging the handle. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include "mx-scroll-bar.h" #include "mx-frame.h" #include "mx-marshal.h" #include "mx-stylable.h" #include "mx-enum-types.h" #include "mx-private.h" #include "mx-button.h" static void mx_stylable_iface_init (MxStylableIface *iface); G_DEFINE_TYPE_WITH_CODE (MxScrollBar, mx_scroll_bar, MX_TYPE_BIN, G_IMPLEMENT_INTERFACE (MX_TYPE_STYLABLE, mx_stylable_iface_init)) #define MX_SCROLL_BAR_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), MX_TYPE_SCROLL_BAR, MxScrollBarPrivate)) #define PAGING_INITIAL_REPEAT_TIMEOUT 500 #define PAGING_SUBSEQUENT_REPEAT_TIMEOUT 200 struct _MxScrollBarPrivate { MxAdjustment *adjustment; gulong capture_handler; gfloat x_origin; gfloat y_origin; ClutterActor *bw_stepper; ClutterActor *fw_stepper; ClutterActor *trough; ClutterActor *handle; gfloat move_x; gfloat move_y; guint handle_min_size; /* Trough-click handling. */ enum { NONE, UP, DOWN } paging_direction; guint paging_source_id; guint paging_event_no; gboolean stepper_forward; guint stepper_source_id; MxOrientation orientation; }; enum { PROP_0, PROP_ADJUSTMENT, PROP_ORIENTATION }; enum { SCROLL_START, SCROLL_STOP, LAST_SIGNAL }; static guint signals[LAST_SIGNAL] = { 0, }; static gboolean handle_button_press_event_cb (ClutterActor *actor, ClutterButtonEvent *event, MxScrollBar *bar); static void mx_scroll_bar_get_property (GObject *gobject, guint prop_id, GValue *value, GParamSpec *pspec) { MxScrollBarPrivate *priv = MX_SCROLL_BAR (gobject)->priv; switch (prop_id) { case PROP_ADJUSTMENT: g_value_set_object (value, priv->adjustment); break; case PROP_ORIENTATION: g_value_set_enum (value, priv->orientation); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); break; } } static void mx_scroll_bar_set_property (GObject *gobject, guint prop_id, const GValue *value, GParamSpec *pspec) { MxScrollBar *bar = MX_SCROLL_BAR (gobject); switch (prop_id) { case PROP_ADJUSTMENT: mx_scroll_bar_set_adjustment (bar, g_value_get_object (value)); break; case PROP_ORIENTATION: mx_scroll_bar_set_orientation (bar, g_value_get_enum (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); break; } } static void mx_scroll_bar_dispose (GObject *gobject) { MxScrollBar *bar = MX_SCROLL_BAR (gobject); MxScrollBarPrivate *priv = bar->priv; if (priv->adjustment) mx_scroll_bar_set_adjustment (bar, NULL); if (priv->handle) { g_signal_handlers_disconnect_by_func (priv->handle, G_CALLBACK (handle_button_press_event_cb), bar); clutter_actor_unparent (priv->handle); priv->handle = NULL; } if (priv->bw_stepper) { clutter_actor_unparent (priv->bw_stepper); priv->bw_stepper = NULL; } if (priv->fw_stepper) { clutter_actor_unparent (priv->fw_stepper); priv->fw_stepper = NULL; } if (priv->trough) { clutter_actor_unparent (priv->trough); priv->trough = NULL; } G_OBJECT_CLASS (mx_scroll_bar_parent_class)->dispose (gobject); } static void mx_scroll_bar_paint (ClutterActor *actor) { MxScrollBarPrivate *priv = MX_SCROLL_BAR (actor)->priv; CLUTTER_ACTOR_CLASS (mx_scroll_bar_parent_class)->paint (actor); clutter_actor_paint (priv->bw_stepper); clutter_actor_paint (priv->fw_stepper); clutter_actor_paint (priv->trough); if (priv->handle && CLUTTER_ACTOR_IS_VISIBLE (priv->handle)) clutter_actor_paint (priv->handle); } static void mx_scroll_bar_pick (ClutterActor *actor, const ClutterColor *pick_color) { MxScrollBarPrivate *priv = MX_SCROLL_BAR (actor)->priv; CLUTTER_ACTOR_CLASS (mx_scroll_bar_parent_class)->pick (actor, pick_color); clutter_actor_paint (priv->bw_stepper); clutter_actor_paint (priv->fw_stepper); clutter_actor_paint (priv->trough); if (priv->handle && priv->adjustment) clutter_actor_paint (priv->handle); } static void mx_scroll_bar_map (ClutterActor *actor) { MxScrollBarPrivate *priv = MX_SCROLL_BAR (actor)->priv; CLUTTER_ACTOR_CLASS (mx_scroll_bar_parent_class)->map (actor); clutter_actor_map (priv->bw_stepper); clutter_actor_map (priv->fw_stepper); clutter_actor_map (priv->trough); if (priv->handle) clutter_actor_map (priv->handle); } static void mx_scroll_bar_unmap (ClutterActor *actor) { MxScrollBarPrivate *priv = MX_SCROLL_BAR (actor)->priv; CLUTTER_ACTOR_CLASS (mx_scroll_bar_parent_class)->unmap (actor); if (priv->bw_stepper) clutter_actor_unmap (priv->bw_stepper); if (priv->fw_stepper) clutter_actor_unmap (priv->fw_stepper); if (priv->trough) clutter_actor_unmap (priv->trough); if (priv->handle) clutter_actor_unmap (priv->handle); } static void mx_scroll_bar_apply_style (MxWidget *widget, MxStyle *style) { MxScrollBarPrivate *priv = MX_SCROLL_BAR (widget)->priv; if (priv->bw_stepper != NULL) mx_stylable_set_style (MX_STYLABLE (priv->bw_stepper), style); if (priv->fw_stepper != NULL) mx_stylable_set_style (MX_STYLABLE (priv->fw_stepper), style); if (priv->trough != NULL) mx_stylable_set_style (MX_STYLABLE (priv->trough), style); if (priv->handle != NULL) mx_stylable_set_style (MX_STYLABLE (priv->handle), style); } static void mx_scroll_bar_get_preferred_width (ClutterActor *actor, gfloat for_height, gfloat *min_width, gfloat *pref_width) { MxScrollBarPrivate *priv = MX_SCROLL_BAR (actor)->priv; MxPadding padding; gfloat width; mx_widget_get_padding (MX_WIDGET (actor), &padding); if (priv->orientation == MX_ORIENTATION_VERTICAL) { gfloat bg_w, h_w, bs_w, fs_w; clutter_actor_get_preferred_width (priv->bw_stepper, -1, NULL, &bs_w); clutter_actor_get_preferred_width (priv->fw_stepper, -1, NULL, &fs_w); clutter_actor_get_preferred_width (priv->trough, -1, NULL, &bg_w); clutter_actor_get_preferred_width (priv->handle, -1, NULL, &h_w); h_w += padding.left + padding.right; bs_w += padding.left + padding.right; fs_w += padding.left + padding.right; width = MAX (bg_w, MAX (h_w, MAX (bs_w, fs_w))); } else { gfloat fs_w, bs_w; clutter_actor_get_preferred_width (priv->bw_stepper, -1, NULL, &bs_w); clutter_actor_get_preferred_width (priv->fw_stepper, -1, NULL, &fs_w); width = padding.left + fs_w + priv->handle_min_size + bs_w + padding.right; } if (min_width) *min_width = width; if (pref_width) *pref_width = width; } static void mx_scroll_bar_get_preferred_height (ClutterActor *actor, gfloat for_width, gfloat *min_height, gfloat *pref_height) { MxScrollBarPrivate *priv = MX_SCROLL_BAR (actor)->priv; MxPadding padding; gfloat height; mx_widget_get_padding (MX_WIDGET (actor), &padding); if (priv->orientation == MX_ORIENTATION_HORIZONTAL) { gfloat bg_h, h_h, bs_h, fs_h; clutter_actor_get_preferred_height (priv->bw_stepper, -1, NULL, &bs_h); clutter_actor_get_preferred_height (priv->fw_stepper, -1, NULL, &fs_h); clutter_actor_get_preferred_height (priv->trough, -1, NULL, &bg_h); clutter_actor_get_preferred_height (priv->handle, -1, NULL, &h_h); h_h += padding.top + padding.bottom; bs_h += padding.top + padding.bottom; fs_h += padding.top + padding.bottom; height = MAX (bg_h, MAX (h_h, MAX (bs_h, fs_h))); } else { gfloat fs_h, bs_h; clutter_actor_get_preferred_height (priv->bw_stepper, -1, NULL, &bs_h); clutter_actor_get_preferred_height (priv->fw_stepper, -1, NULL, &fs_h); height = padding.top + fs_h + priv->handle_min_size + bs_h + padding.bottom; } if (min_height) *min_height = height; if (pref_height) *pref_height = height; } static void mx_scroll_bar_allocate (ClutterActor *actor, const ClutterActorBox *box, ClutterAllocationFlags flags) { MxScrollBarPrivate *priv = MX_SCROLL_BAR (actor)->priv; MxPadding padding; ClutterActorBox bw_box, fw_box, trough_box; gfloat x, y, width, height, stepper_size; /* Chain up */ CLUTTER_ACTOR_CLASS (mx_scroll_bar_parent_class)->allocate (actor, box, flags); mx_widget_get_padding (MX_WIDGET (actor), &padding); /* calculate the child area */ x = padding.left; y = padding.top; width = (box->x2 - box->x1) - padding.left - padding.right; height = (box->y2 - box->y1) - padding.top - padding.bottom; if (priv->orientation == MX_ORIENTATION_VERTICAL) { stepper_size = width; /* Backward stepper */ bw_box.x1 = x; bw_box.y1 = y; bw_box.x2 = bw_box.x1 + stepper_size; bw_box.y2 = bw_box.y1 + stepper_size; clutter_actor_allocate (priv->bw_stepper, &bw_box, flags); /* Forward stepper */ fw_box.x1 = x; fw_box.y1 = y + height - stepper_size; fw_box.x2 = fw_box.x1 + stepper_size; fw_box.y2 = fw_box.y1 + stepper_size; clutter_actor_allocate (priv->fw_stepper, &fw_box, flags); /* Trough */ trough_box.x1 = x; trough_box.y1 = y + stepper_size; trough_box.x2 = x + width; trough_box.y2 = y + height - stepper_size; clutter_actor_allocate (priv->trough, &trough_box, flags); } else { stepper_size = height; /* Backward stepper */ bw_box.x1 = x; bw_box.y1 = y; bw_box.x2 = bw_box.x1 + stepper_size; bw_box.y2 = bw_box.y1 + stepper_size; clutter_actor_allocate (priv->bw_stepper, &bw_box, flags); /* Forward stepper */ fw_box.x1 = x + width - stepper_size; fw_box.y1 = y; fw_box.x2 = fw_box.x1 + stepper_size; fw_box.y2 = fw_box.y1 + stepper_size; clutter_actor_allocate (priv->fw_stepper, &fw_box, flags); /* Trough */ trough_box.x1 = x + stepper_size; trough_box.y1 = y; trough_box.x2 = x + width - stepper_size; trough_box.y2 = y + height; clutter_actor_allocate (priv->trough, &trough_box, flags); } if (priv->adjustment) { gfloat handle_size, position, avail_size, handle_pos; gdouble value, lower, upper, page_size, increment; ClutterActorBox handle_box = { 0, }; guint min_size, max_size; mx_adjustment_get_values (priv->adjustment, &value, &lower, &upper, NULL, NULL, &page_size); value = mx_adjustment_get_value (priv->adjustment); if ((upper == lower) || (page_size >= (upper - lower))) increment = 1.0; else increment = page_size / (upper - lower); min_size = priv->handle_min_size; mx_stylable_get (MX_STYLABLE (actor), "mx-max-size", &max_size, NULL); if (upper - lower - page_size <= 0) position = 0; else position = (value - lower) / (upper - lower - page_size); if (priv->orientation == MX_ORIENTATION_VERTICAL) { avail_size = height - stepper_size * 2; handle_size = increment * avail_size; handle_size = CLAMP (handle_size, min_size, max_size); handle_box.x1 = x; handle_pos = bw_box.y2 + position * (avail_size - handle_size); handle_box.y1 = CLAMP (handle_pos, bw_box.y2, fw_box.y1 - min_size); handle_box.x2 = handle_box.x1 + width; handle_box.y2 = CLAMP (handle_pos + handle_size, bw_box.y2 + min_size, fw_box.y1); } else { avail_size = width - stepper_size * 2; handle_size = increment * avail_size; handle_size = CLAMP (handle_size, min_size, max_size); handle_pos = bw_box.x2 + position * (avail_size - handle_size); handle_box.x1 = CLAMP (handle_pos, bw_box.x2, fw_box.x1 - min_size); handle_box.y1 = y; handle_box.x2 = CLAMP (handle_pos + handle_size, bw_box.x2 + min_size, fw_box.x1); handle_box.y2 = handle_box.y1 + height; } /* snap to pixel */ handle_box.x1 = (int) handle_box.x1; handle_box.y1 = (int) handle_box.y1; handle_box.x2 = (int) handle_box.x2; handle_box.y2 = (int) handle_box.y2; clutter_actor_allocate (priv->handle, &handle_box, flags); } } static void mx_scroll_bar_style_changed (MxWidget *widget, MxStyleChangedFlags flags) { MxScrollBarPrivate *priv = MX_SCROLL_BAR (widget)->priv; mx_stylable_get (MX_STYLABLE (widget), "mx-min-size", &priv->handle_min_size, NULL); mx_stylable_style_changed ((MxStylable *) priv->bw_stepper, flags); mx_stylable_style_changed ((MxStylable *) priv->fw_stepper, flags); mx_stylable_style_changed ((MxStylable *) priv->trough, flags); mx_stylable_style_changed ((MxStylable *) priv->handle, flags); } static void bar_reactive_notify_cb (GObject *gobject, GParamSpec *arg1, gpointer user_data) { MxScrollBar *bar = MX_SCROLL_BAR (gobject); clutter_actor_set_reactive (bar->priv->handle, clutter_actor_get_reactive (CLUTTER_ACTOR (bar))); } static GObject* mx_scroll_bar_constructor (GType type, guint n_properties, GObjectConstructParam *properties) { GObjectClass *gobject_class; GObject *obj; MxScrollBar *bar; gobject_class = G_OBJECT_CLASS (mx_scroll_bar_parent_class); obj = gobject_class->constructor (type, n_properties, properties); bar = MX_SCROLL_BAR (obj); g_signal_connect (bar, "notify::reactive", G_CALLBACK (bar_reactive_notify_cb), NULL); return obj; } static gboolean mx_scroll_bar_scroll_event (ClutterActor *actor, ClutterScrollEvent *event) { MxScrollBarPrivate *priv = MX_SCROLL_BAR (actor)->priv; gdouble lower, step, upper, value; if (priv->adjustment) { g_object_get (priv->adjustment, "lower", &lower, "step-increment", &step, "upper", &upper, "value", &value, NULL); } else { return FALSE; } switch (event->direction) { case CLUTTER_SCROLL_UP: case CLUTTER_SCROLL_LEFT: if (value == lower) return FALSE; else mx_adjustment_interpolate_relative (priv->adjustment, -step, 250, CLUTTER_EASE_OUT_CUBIC); break; case CLUTTER_SCROLL_DOWN: case CLUTTER_SCROLL_RIGHT: if (value == upper) return FALSE; else mx_adjustment_interpolate_relative (priv->adjustment, step, 250, CLUTTER_EASE_OUT_CUBIC); break; } return TRUE; } static void mx_scroll_bar_class_init (MxScrollBarClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass); MxWidgetClass *widget_class = MX_WIDGET_CLASS (klass); GParamSpec *pspec; g_type_class_add_private (klass, sizeof (MxScrollBarPrivate)); object_class->get_property = mx_scroll_bar_get_property; object_class->set_property = mx_scroll_bar_set_property; object_class->dispose = mx_scroll_bar_dispose; object_class->constructor = mx_scroll_bar_constructor; actor_class->get_preferred_width = mx_scroll_bar_get_preferred_width; actor_class->get_preferred_height = mx_scroll_bar_get_preferred_height; actor_class->allocate = mx_scroll_bar_allocate; actor_class->paint = mx_scroll_bar_paint; actor_class->pick = mx_scroll_bar_pick; actor_class->scroll_event = mx_scroll_bar_scroll_event; actor_class->map = mx_scroll_bar_map; actor_class->unmap = mx_scroll_bar_unmap; widget_class->apply_style = mx_scroll_bar_apply_style; g_object_class_install_property (object_class, PROP_ADJUSTMENT, g_param_spec_object ("adjustment", "Adjustment", "The adjustment", MX_TYPE_ADJUSTMENT, MX_PARAM_READWRITE)); pspec = g_param_spec_enum ("orientation", "Orientation", "The orientation of the scrollbar", MX_TYPE_ORIENTATION, MX_ORIENTATION_HORIZONTAL, MX_PARAM_READWRITE); g_object_class_install_property (object_class, PROP_ORIENTATION, pspec); signals[SCROLL_START] = g_signal_new ("scroll-start", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (MxScrollBarClass, scroll_start), NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); signals[SCROLL_STOP] = g_signal_new ("scroll-stop", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (MxScrollBarClass, scroll_stop), NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); } static void mx_stylable_iface_init (MxStylableIface *iface) { static gboolean is_initialized = FALSE; if (!is_initialized) { GParamSpec *pspec; is_initialized = TRUE; pspec = g_param_spec_uint ("mx-min-size", "Minimum grabber size", "Minimum size of the scroll grabber, in px", 0, G_MAXUINT, 32, G_PARAM_READWRITE); mx_stylable_iface_install_property (iface, MX_TYPE_SCROLL_BAR, pspec); pspec = g_param_spec_uint ("mx-max-size", "Maximum grabber size", "Maximum size of the scroll grabber, in px", 0, G_MAXINT16, G_MAXINT16, G_PARAM_READWRITE); mx_stylable_iface_install_property (iface, MX_TYPE_SCROLL_BAR, pspec); } } static void move_slider (MxScrollBar *bar, gfloat x, gfloat y) { MxScrollBarPrivate *priv = bar->priv; gdouble position, lower, upper, page_size; gfloat ux, uy, pos, size; if (!priv->adjustment) return; if (!clutter_actor_transform_stage_point (priv->trough, x, y, &ux, &uy)) return; if (priv->orientation == MX_ORIENTATION_VERTICAL) size = clutter_actor_get_height (priv->trough) - clutter_actor_get_height (priv->handle); else size = clutter_actor_get_width (priv->trough) - clutter_actor_get_width (priv->handle); if (size == 0) return; if (priv->orientation == MX_ORIENTATION_VERTICAL) pos = uy - priv->y_origin; else pos = ux - priv->x_origin; pos = CLAMP (pos, 0, size); mx_adjustment_get_values (priv->adjustment, NULL, &lower, &upper, NULL, NULL, &page_size); position = ((pos / size) * (upper - lower - page_size)) + lower; mx_adjustment_set_value (priv->adjustment, position); } static gboolean handle_capture_event_cb (ClutterActor *trough, ClutterEvent *event, MxScrollBar *bar) { if (clutter_event_type (event) == CLUTTER_MOTION) { move_slider (bar, ((ClutterMotionEvent*) event)->x, ((ClutterMotionEvent*) event)->y); } else if (clutter_event_type (event) == CLUTTER_BUTTON_RELEASE && ((ClutterButtonEvent*) event)->button == 1) { ClutterActor *stage, *target; stage = clutter_actor_get_stage(bar->priv->trough); if (bar->priv->capture_handler) { g_signal_handler_disconnect (stage, bar->priv->capture_handler); bar->priv->capture_handler = 0; } clutter_set_motion_events_enabled (TRUE); g_signal_emit (bar, signals[SCROLL_STOP], 0); /* check if the mouse pointer has left the handle during the drag and * remove the hover state if it has */ target = clutter_stage_get_actor_at_pos ((ClutterStage*) stage, CLUTTER_PICK_REACTIVE, ((ClutterButtonEvent*) event)->x, ((ClutterButtonEvent*) event)->y); if (target != bar->priv->handle) { mx_stylable_set_style_pseudo_class (MX_STYLABLE (bar->priv->handle), NULL); } else { /* allow the release event to continue to the handle for processing */ return FALSE; } } return TRUE; } static gboolean handle_button_press_event_cb (ClutterActor *actor, ClutterButtonEvent *event, MxScrollBar *bar) { MxScrollBarPrivate *priv = bar->priv; if (event->button != 1) return FALSE; if (!clutter_actor_transform_stage_point (priv->handle, event->x, event->y, &priv->x_origin, &priv->y_origin)) return FALSE; /* Account for the scrollbar-trough-handle nesting. */ priv->x_origin += clutter_actor_get_x (priv->trough); priv->y_origin += clutter_actor_get_y (priv->trough); /* Turn off picking for motion events */ clutter_set_motion_events_enabled (FALSE); priv->capture_handler = g_signal_connect_after ( clutter_actor_get_stage (priv->trough), "captured-event", G_CALLBACK (handle_capture_event_cb), bar); g_signal_emit (bar, signals[SCROLL_START], 0); return FALSE; } static gboolean trough_paging_cb (MxScrollBar *self) { gfloat handle_pos, event_pos, tx, ty; gdouble value; gdouble page_increment; gboolean ret; gulong mode; if (self->priv->paging_event_no == 0) { /* Scroll on after initial timeout. */ mode = CLUTTER_EASE_OUT_CUBIC; ret = FALSE; self->priv->paging_event_no = 1; self->priv->paging_source_id = g_timeout_add ( PAGING_INITIAL_REPEAT_TIMEOUT, (GSourceFunc) trough_paging_cb, self); } else if (self->priv->paging_event_no == 1) { /* Scroll on after subsequent timeout. */ ret = FALSE; mode = CLUTTER_EASE_IN_CUBIC; self->priv->paging_event_no = 2; self->priv->paging_source_id = g_timeout_add ( PAGING_SUBSEQUENT_REPEAT_TIMEOUT, (GSourceFunc) trough_paging_cb, self); } else { /* Keep scrolling. */ ret = TRUE; mode = CLUTTER_LINEAR; self->priv->paging_event_no++; } /* Do the scrolling */ mx_adjustment_get_values (self->priv->adjustment, &value, NULL, NULL, NULL, &page_increment, NULL); if (self->priv->orientation == MX_ORIENTATION_VERTICAL) handle_pos = clutter_actor_get_y (self->priv->handle); else handle_pos = clutter_actor_get_x (self->priv->handle); clutter_actor_transform_stage_point (CLUTTER_ACTOR (self->priv->trough), self->priv->move_x, self->priv->move_y, &tx, &ty); if (self->priv->orientation == MX_ORIENTATION_VERTICAL) event_pos = ty; else event_pos = tx; if (event_pos > handle_pos) { if (self->priv->paging_direction == NONE) { /* Remember direction. */ self->priv->paging_direction = DOWN; } if (self->priv->paging_direction == UP) { /* Scrolled far enough. */ return FALSE; } value += page_increment; } else { if (self->priv->paging_direction == NONE) { /* Remember direction. */ self->priv->paging_direction = UP; } if (self->priv->paging_direction == DOWN) { /* Scrolled far enough. */ return FALSE; } value -= page_increment; } mx_adjustment_interpolate (self->priv->adjustment, value, 250, mode); return ret; } static gboolean trough_button_press_event_cb (ClutterActor *actor, ClutterButtonEvent *event, MxScrollBar *self) { g_return_val_if_fail (self, FALSE); if (event->button != 1) return FALSE; if (self->priv->adjustment == NULL) return FALSE; self->priv->move_x = event->x; self->priv->move_y = event->y; self->priv->paging_direction = NONE; self->priv->paging_event_no = 0; trough_paging_cb (self); return TRUE; } static gboolean trough_button_release_event_cb (ClutterActor *actor, ClutterButtonEvent *event, MxScrollBar *self) { if (event->button != 1) return FALSE; if (self->priv->paging_source_id) { g_source_remove (self->priv->paging_source_id); self->priv->paging_source_id = 0; } return TRUE; } static gboolean trough_leave_event_cb (ClutterActor *actor, ClutterEvent *event, MxScrollBar *self) { if (self->priv->paging_source_id) { g_source_remove (self->priv->paging_source_id); self->priv->paging_source_id = 0; return TRUE; } return FALSE; } static void stepper_move_on (MxScrollBarPrivate *priv, gint mode) { double value, inc; g_object_get (priv->adjustment, "step-increment", &inc, "value", &value, NULL); if (priv->stepper_forward) value = value + inc; else value = value - inc; mx_adjustment_interpolate (priv->adjustment, value, PAGING_SUBSEQUENT_REPEAT_TIMEOUT, mode); } static gboolean stepper_button_subsequent_timeout (MxScrollBarPrivate *priv) { stepper_move_on (priv, CLUTTER_LINEAR); return TRUE; } static gboolean stepper_button_repeat_timeout (MxScrollBarPrivate *priv) { priv->stepper_source_id = 0; stepper_move_on (priv, CLUTTER_EASE_IN_CUBIC); priv->stepper_source_id = g_timeout_add (PAGING_SUBSEQUENT_REPEAT_TIMEOUT, (GSourceFunc) stepper_button_subsequent_timeout, priv); return FALSE; } static gboolean stepper_button_press_event_cb (ClutterActor *actor, ClutterButtonEvent *event, MxScrollBar *bar) { MxScrollBarPrivate *priv = bar->priv; if (event->button != 1) return FALSE; if (bar->priv->adjustment == NULL) return FALSE; bar->priv->stepper_forward = (actor == priv->fw_stepper); stepper_move_on (priv, CLUTTER_EASE_OUT_CUBIC); priv->stepper_source_id = g_timeout_add (PAGING_INITIAL_REPEAT_TIMEOUT, (GSourceFunc) stepper_button_repeat_timeout, priv); return FALSE; } static gboolean stepper_button_release_cb (ClutterActor *actor, ClutterButtonEvent *event, MxScrollBar *self) { if (self->priv->stepper_source_id) { g_source_remove (self->priv->stepper_source_id); self->priv->stepper_source_id = 0; } return FALSE; } static void mx_scroll_bar_notify_reactive (MxScrollBar *self) { MxScrollBarPrivate *priv = self->priv; gboolean reactive = CLUTTER_ACTOR_IS_REACTIVE (self); clutter_actor_set_reactive (CLUTTER_ACTOR (priv->bw_stepper), reactive); clutter_actor_set_reactive (CLUTTER_ACTOR (priv->fw_stepper), reactive); clutter_actor_set_reactive (CLUTTER_ACTOR (priv->trough), reactive); clutter_actor_set_reactive (CLUTTER_ACTOR (priv->handle), reactive); } static void mx_scroll_bar_init (MxScrollBar *self) { self->priv = MX_SCROLL_BAR_GET_PRIVATE (self); self->priv->bw_stepper = (ClutterActor *) mx_button_new (); mx_stylable_set_style_class (MX_STYLABLE (self->priv->bw_stepper), "backward-stepper"); clutter_actor_set_parent (CLUTTER_ACTOR (self->priv->bw_stepper), CLUTTER_ACTOR (self)); g_signal_connect (self->priv->bw_stepper, "button-press-event", G_CALLBACK (stepper_button_press_event_cb), self); g_signal_connect (self->priv->bw_stepper, "button-release-event", G_CALLBACK (stepper_button_release_cb), self); g_signal_connect (self->priv->bw_stepper, "leave-event", G_CALLBACK (stepper_button_release_cb), self); self->priv->fw_stepper = (ClutterActor *) mx_button_new (); mx_stylable_set_style_class (MX_STYLABLE (self->priv->fw_stepper), "forward-stepper"); clutter_actor_set_parent (CLUTTER_ACTOR (self->priv->fw_stepper), CLUTTER_ACTOR (self)); g_signal_connect (self->priv->fw_stepper, "button-press-event", G_CALLBACK (stepper_button_press_event_cb), self); g_signal_connect (self->priv->fw_stepper, "button-release-event", G_CALLBACK (stepper_button_release_cb), self); g_signal_connect (self->priv->fw_stepper, "leave-event", G_CALLBACK (stepper_button_release_cb), self); self->priv->trough = mx_frame_new (); clutter_actor_set_reactive ((ClutterActor *) self->priv->trough, TRUE); mx_stylable_set_style_class (MX_STYLABLE (self->priv->trough), "htrough"); clutter_actor_set_parent (CLUTTER_ACTOR (self->priv->trough), CLUTTER_ACTOR (self)); g_signal_connect (self->priv->trough, "button-press-event", G_CALLBACK (trough_button_press_event_cb), self); g_signal_connect (self->priv->trough, "button-release-event", G_CALLBACK (trough_button_release_event_cb), self); g_signal_connect (self->priv->trough, "leave-event", G_CALLBACK (trough_leave_event_cb), self); self->priv->handle = (ClutterActor *) mx_button_new (); mx_stylable_set_style_class (MX_STYLABLE (self->priv->handle), "hhandle"); clutter_actor_set_parent (CLUTTER_ACTOR (self->priv->handle), CLUTTER_ACTOR (self)); g_signal_connect (self->priv->handle, "button-press-event", G_CALLBACK (handle_button_press_event_cb), self); clutter_actor_set_reactive (CLUTTER_ACTOR (self), TRUE); g_signal_connect (self, "style-changed", G_CALLBACK (mx_scroll_bar_style_changed), NULL); g_signal_connect (self, "notify::reactive", G_CALLBACK (mx_scroll_bar_notify_reactive), NULL); } /** * mx_scroll_bar_new: * * Create a new #MxScrollBar * * Returns: a new #MxScrollBar */ ClutterActor * mx_scroll_bar_new (void) { return g_object_new (MX_TYPE_SCROLL_BAR, NULL); } /** * mx_scroll_bar_new_with_adjustment: * @adjustment: an #MxAdjustment * * Create a new #MxScrollBar with the given adjustment set * * Returns: a new #MxScrollBar */ ClutterActor * mx_scroll_bar_new_with_adjustment (MxAdjustment *adjustment) { return g_object_new (MX_TYPE_SCROLL_BAR, "adjustment", adjustment, NULL); } void mx_scroll_bar_set_adjustment (MxScrollBar *bar, MxAdjustment *adjustment) { MxScrollBarPrivate *priv; g_return_if_fail (MX_IS_SCROLL_BAR (bar)); priv = bar->priv; if (priv->adjustment) { g_signal_handlers_disconnect_by_func (priv->adjustment, clutter_actor_queue_relayout, bar); g_signal_handlers_disconnect_by_func (priv->adjustment, clutter_actor_queue_relayout, bar); g_object_unref (priv->adjustment); priv->adjustment = NULL; } if (adjustment) { priv->adjustment = g_object_ref (adjustment); g_signal_connect_swapped (priv->adjustment, "notify::value", G_CALLBACK (clutter_actor_queue_relayout), bar); g_signal_connect_swapped (priv->adjustment, "changed", G_CALLBACK (clutter_actor_queue_relayout), bar); clutter_actor_queue_relayout (CLUTTER_ACTOR (bar)); } } /** * mx_scroll_bar_get_adjustment: * @bar: a #MxScrollBar * * Gets the adjustment object that stores the current position * of the scrollbar. * * Return value: (transfer none): the adjustment */ MxAdjustment * mx_scroll_bar_get_adjustment (MxScrollBar *bar) { g_return_val_if_fail (MX_IS_SCROLL_BAR (bar), NULL); return bar->priv->adjustment; } MxOrientation mx_scroll_bar_get_orientation (MxScrollBar *bar) { g_return_val_if_fail (MX_IS_SCROLL_BAR (bar), MX_ORIENTATION_HORIZONTAL); return bar->priv->orientation; } void mx_scroll_bar_set_orientation (MxScrollBar *bar, MxOrientation orientation) { MxScrollBarPrivate *priv; g_return_if_fail (MX_IS_SCROLL_BAR (bar)); priv = bar->priv; if (orientation != priv->orientation) { priv->orientation = orientation; if (bar->priv->orientation) { mx_stylable_set_style_class (MX_STYLABLE (priv->bw_stepper), "up-stepper"); mx_stylable_set_style_class (MX_STYLABLE (priv->fw_stepper), "down-stepper"); mx_stylable_set_style_class (MX_STYLABLE (priv->handle), "vhandle"); mx_stylable_set_style_class (MX_STYLABLE (priv->trough), "vtrough"); } else { mx_stylable_set_style_class (MX_STYLABLE (priv->fw_stepper), "forward-stepper"); mx_stylable_set_style_class (MX_STYLABLE (priv->bw_stepper), "backward-stepper"); mx_stylable_set_style_class (MX_STYLABLE (priv->handle), "hhandle"); mx_stylable_set_style_class (MX_STYLABLE (priv->trough), "htrough"); } clutter_actor_queue_relayout (CLUTTER_ACTOR (bar)); g_object_notify (G_OBJECT (bar), "orientation"); } } mx-1.4.7/mx/mx-scroll-bar.h000066400000000000000000000061761201047117600154370ustar00rootroot00000000000000/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * mx-scroll-bar.h: Scroll bar actor * * Copyright 2008 OpenedHand * Copyright 2009, 2010 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * Boston, MA 02111-1307, USA. * * Written by: Chris Lord * Port to Mx by: Robert Staudinger * */ #if !defined(MX_H_INSIDE) && !defined(MX_COMPILATION) #error "Only can be included directly.h" #endif #ifndef __MX_SCROLL_BAR_H__ #define __MX_SCROLL_BAR_H__ #include #include G_BEGIN_DECLS #define MX_TYPE_SCROLL_BAR (mx_scroll_bar_get_type()) #define MX_SCROLL_BAR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MX_TYPE_SCROLL_BAR, MxScrollBar)) #define MX_IS_SCROLL_BAR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MX_TYPE_SCROLL_BAR)) #define MX_SCROLL_BAR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MX_TYPE_SCROLL_BAR, MxScrollBarClass)) #define MX_IS_SCROLL_BAR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MX_TYPE_SCROLL_BAR)) #define MX_SCROLL_BAR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MX_TYPE_SCROLL_BAR, MxScrollBarClass)) typedef struct _MxScrollBar MxScrollBar; typedef struct _MxScrollBarPrivate MxScrollBarPrivate; typedef struct _MxScrollBarClass MxScrollBarClass; /** * MxScrollBar: * * The contents of this structure are private and should only be accessed * through the public API. */ struct _MxScrollBar { /*< private >*/ MxBin parent_instance; MxScrollBarPrivate *priv; }; struct _MxScrollBarClass { MxBinClass parent_class; /* signals */ void (*scroll_start) (MxScrollBar *bar); void (*scroll_stop) (MxScrollBar *bar); /* padding for future expansion */ void (*_padding_0) (void); void (*_padding_1) (void); void (*_padding_2) (void); void (*_padding_3) (void); void (*_padding_4) (void); }; GType mx_scroll_bar_get_type (void) G_GNUC_CONST; ClutterActor *mx_scroll_bar_new (void); ClutterActor *mx_scroll_bar_new_with_adjustment (MxAdjustment *adjustment); void mx_scroll_bar_set_adjustment (MxScrollBar *bar, MxAdjustment *adjustment); MxAdjustment *mx_scroll_bar_get_adjustment (MxScrollBar *bar); void mx_scroll_bar_set_orientation (MxScrollBar *bar, MxOrientation orientation); MxOrientation mx_scroll_bar_get_orientation (MxScrollBar *bar); G_END_DECLS #endif /* __MX_SCROLL_BAR_H__ */ mx-1.4.7/mx/mx-scroll-view.c000066400000000000000000001051111201047117600156250ustar00rootroot00000000000000/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * mx-scroll-view.h: Container with scroll-bars * * Copyright 2008 OpenedHand * Copyright 2009, 2010 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * * Written by: Chris Lord * Port to Mx by: Robert Staudinger * */ /** * SECTION:mx-scroll-view * @short_description: a container for scrollable children * * #MxScrollView is a single child container for actors that implement * #MxScrollable. It provides scrollbars around the edge of the child to * allow the user to move around the scrollable area. * *
* #MxScrollView around an #MxBoxLayout * An example of an #MxScrollView wrapped around an #MxBoxLayout * actor (which implements #MxScrollable). The #MxBoxLayout contains * nine #ClutterRectangle instances, but the stage is too small for * all of them to be visible. The #MxScrollView adds the appropriate * horizontal scroll, which makes it possible to scroll to the end * of the row of rectangles. * *
*/ #include "mx-scroll-view.h" #include "mx-marshal.h" #include "mx-scroll-bar.h" #include "mx-scrollable.h" #include "mx-stylable.h" #include "mx-enum-types.h" #include "mx-private.h" #include #include "config.h" #ifdef HAVE_CLUTTER_GESTURE #include #endif static void clutter_container_iface_init (ClutterContainerIface *iface); static void mx_stylable_iface_init (MxStylableIface *iface); G_DEFINE_TYPE_WITH_CODE (MxScrollView, mx_scroll_view, MX_TYPE_BIN, G_IMPLEMENT_INTERFACE (CLUTTER_TYPE_CONTAINER, clutter_container_iface_init) G_IMPLEMENT_INTERFACE (MX_TYPE_STYLABLE, mx_stylable_iface_init)) #define SCROLL_VIEW_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), \ MX_TYPE_SCROLL_VIEW, \ MxScrollViewPrivate)) struct _MxScrollViewPrivate { /* a pointer to the child; this is actually stored * inside MxBin:child, but we keep it to avoid * calling mx_bin_get_child() every time we need it */ ClutterActor *child; ClutterActor *hscroll; ClutterActor *vscroll; guint mouse_scroll : 1; guint enable_gestures : 1; guint scrollbar_width; guint scrollbar_height; MxScrollPolicy scroll_policy; #ifdef HAVE_CLUTTER_GESTURE ClutterGesture *gesture; ClutterAnimation *animation; #endif }; enum { PROP_0, PROP_MOUSE_SCROLL, PROP_ENABLE_GESTURES, PROP_SCROLL_POLICY }; static void mx_scroll_view_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { MxScrollViewPrivate *priv = ((MxScrollView *) object)->priv; switch (property_id) { case PROP_MOUSE_SCROLL: g_value_set_boolean (value, priv->mouse_scroll); break; case PROP_ENABLE_GESTURES: g_value_set_boolean (value, priv->enable_gestures); break; case PROP_SCROLL_POLICY: g_value_set_enum (value, priv->scroll_policy); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } } static void mx_scroll_view_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { MxScrollView *view = MX_SCROLL_VIEW (object); switch (property_id) { case PROP_MOUSE_SCROLL: mx_scroll_view_set_enable_mouse_scrolling (view, g_value_get_boolean (value)); break; case PROP_ENABLE_GESTURES: mx_scroll_view_set_enable_gestures (view, g_value_get_boolean (value)); break; case PROP_SCROLL_POLICY: mx_scroll_view_set_scroll_policy (view, g_value_get_enum (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } } static void mx_scroll_view_dispose (GObject *object) { MxScrollViewPrivate *priv = MX_SCROLL_VIEW (object)->priv; if (priv->vscroll) { clutter_actor_unparent (priv->vscroll); priv->vscroll = NULL; } if (priv->hscroll) { clutter_actor_unparent (priv->hscroll); priv->hscroll = NULL; } #ifdef HAVE_CLUTTER_GESTURE if (priv->gesture) { g_object_unref (priv->gesture); priv->gesture = NULL; } if (priv->animation) { g_object_unref (priv->animation); priv->animation = NULL; } #endif /* Chaining up will remove the child actor */ G_OBJECT_CLASS (mx_scroll_view_parent_class)->dispose (object); } static void mx_scroll_view_finalize (GObject *object) { G_OBJECT_CLASS (mx_scroll_view_parent_class)->finalize (object); } static void mx_scroll_view_paint (ClutterActor *actor) { ClutterActorBox box; gfloat w, h; MxAdjustment *vadjustment = NULL, *hadjustment = NULL; MxScrollViewPrivate *priv = MX_SCROLL_VIEW (actor)->priv; ClutterColor *color; guint8 r, g, b; const gint shadow = 15; mx_stylable_get (MX_STYLABLE (actor), "background-color", &color, NULL); r = color->red; g = color->green; b = color->blue; clutter_color_free (color); /* If there is a child to paint, clip it */ if (priv->child) { clutter_actor_get_allocation_box (priv->child, &box); cogl_clip_push_rectangle (0, 0, (box.x2 - box.x1), (box.y2 - box.y1)); } CLUTTER_ACTOR_CLASS (mx_scroll_view_parent_class)->paint (actor); if (priv->child) cogl_clip_pop (); clutter_actor_get_allocation_box (actor, &box); w = box.x2 - box.x1; h = box.y2 - box.y1; /* paint our custom children */ if (CLUTTER_ACTOR_IS_VISIBLE (priv->hscroll)) { clutter_actor_paint (priv->hscroll); clutter_actor_get_allocation_box (priv->hscroll, &box); h -= (box.y2 - box.y1); hadjustment = mx_scroll_bar_get_adjustment (MX_SCROLL_BAR(priv->hscroll)); } if (CLUTTER_ACTOR_IS_VISIBLE (priv->vscroll)) { clutter_actor_paint (priv->vscroll); clutter_actor_get_allocation_box (priv->vscroll, &box); w -= (box.x2 - box.x1); vadjustment = mx_scroll_bar_get_adjustment (MX_SCROLL_BAR(priv->vscroll)); } /* set up the matrial using dummy set source call */ cogl_set_source_color4ub (0, 0, 0, 0); if (vadjustment) { gdouble len; if ((len = mx_adjustment_get_value (vadjustment)) > 0) { CoglTextureVertex top[4] = { { 0,}, }; if (len > shadow) len = shadow; top[1].x = w; top[2].x = w; top[2].y = len; top[3].y = len; cogl_color_set_from_4ub (&top[0].color, r, g, b, 0xff); cogl_color_set_from_4ub (&top[1].color, r, g, b, 0xff); cogl_color_set_from_4ub (&top[2].color, 0, 0, 0, 0); cogl_color_set_from_4ub (&top[3].color, 0, 0, 0, 0); cogl_polygon (top, 4, TRUE); } if ((len = (mx_adjustment_get_upper (vadjustment) - mx_adjustment_get_page_size (vadjustment)) - mx_adjustment_get_value (vadjustment)) > 0) { CoglTextureVertex bottom[4] = { {0, }, }; if (len > shadow) len = shadow; bottom[0].x = w; bottom[0].y = h; bottom[1].y = h; bottom[2].y = h - len; bottom[3].x = w; bottom[3].y = h - len; cogl_color_set_from_4ub (&bottom[0].color, r, g, b, 0xff); cogl_color_set_from_4ub (&bottom[1].color, r, g, b, 0xff); cogl_color_set_from_4ub (&bottom[2].color, 0, 0, 0, 0); cogl_color_set_from_4ub (&bottom[3].color, 0, 0, 0, 0); cogl_polygon (bottom, 4, TRUE); } } if (hadjustment) { gdouble len; if ((len = mx_adjustment_get_value (hadjustment)) > 0) { CoglTextureVertex left[4] = { { 0, }, }; if (len > shadow) len = shadow; left[0].y = h; left[2].x = len; left[3].x = len; left[3].y = h; cogl_color_set_from_4ub (&left[0].color, r, g, b, 0xff); cogl_color_set_from_4ub (&left[1].color, r, g, b, 0xff); cogl_color_set_from_4ub (&left[2].color, 0, 0, 0, 0); cogl_color_set_from_4ub (&left[3].color, 0, 0, 0, 0); cogl_polygon (left, 4, TRUE); } if ((len = (mx_adjustment_get_upper (hadjustment) - mx_adjustment_get_page_size (hadjustment)) - mx_adjustment_get_value (hadjustment)) > 0) { CoglTextureVertex right[4] = { { 0, }, }; if (len > shadow) len = shadow; right[0].x = w; right[1].x = w; right[1].y = h; right[2].x = w - len; right[2].y = h; right[3].x = w - len; cogl_color_set_from_4ub (&right[0].color, r, g, b, 0xff); cogl_color_set_from_4ub (&right[1].color, r, g, b, 0xff); cogl_color_set_from_4ub (&right[2].color, 0, 0, 0, 0); cogl_color_set_from_4ub (&right[3].color, 0, 0, 0, 0); cogl_polygon (right, 4, TRUE); } } } static void mx_scroll_view_pick (ClutterActor *actor, const ClutterColor *color) { MxScrollViewPrivate *priv = MX_SCROLL_VIEW (actor)->priv; ClutterActorBox box; /* Chain up so we get a bounding box pained (if we are reactive) */ if (priv->child) { clutter_actor_get_allocation_box (priv->child, &box); cogl_clip_push_rectangle (0, 0, (box.x2 - box.x1), (box.y2 - box.y1)); } CLUTTER_ACTOR_CLASS (mx_scroll_view_parent_class)->pick (actor, color); if (priv->child) cogl_clip_pop (); /* paint our custom children */ if (CLUTTER_ACTOR_IS_VISIBLE (priv->hscroll)) clutter_actor_paint (priv->hscroll); if (CLUTTER_ACTOR_IS_VISIBLE (priv->vscroll)) clutter_actor_paint (priv->vscroll); } static void mx_scroll_view_get_preferred_width (ClutterActor *actor, gfloat for_height, gfloat *min_width_p, gfloat *natural_width_p) { MxPadding padding; gfloat child_min_w, child_nat_w; gfloat vscroll_w; MxScrollViewPrivate *priv = MX_SCROLL_VIEW (actor)->priv; if (!priv->child) return; mx_widget_get_padding (MX_WIDGET (actor), &padding); /* Our natural width is the natural width of the child */ clutter_actor_get_preferred_width (priv->child, for_height, &child_min_w, &child_nat_w); /* Add space for the scroll-bar if we can determine it will be necessary */ vscroll_w = 0; if (for_height >= 0) { gfloat natural_height; clutter_actor_get_preferred_height (priv->child, -1.0, NULL, &natural_height); if (for_height < natural_height) vscroll_w = priv->scrollbar_width; } if (min_width_p) { *min_width_p = padding.left + padding.right + vscroll_w; /* if the scroll policy is not set to always or horizontal, then the * minimum size of the scroll view is the minimum size of the child */ if (!(priv->scroll_policy == MX_SCROLL_POLICY_BOTH || priv->scroll_policy == MX_SCROLL_POLICY_HORIZONTAL)) { *min_width_p += child_min_w; } } /* Add space for padding */ if (natural_width_p) *natural_width_p = padding.left + padding.right + child_nat_w + vscroll_w; } static void mx_scroll_view_get_preferred_height (ClutterActor *actor, gfloat for_width, gfloat *min_height_p, gfloat *natural_height_p) { MxPadding padding; gfloat min_child_h, nat_child_h; gfloat scroll_h; MxScrollViewPrivate *priv = MX_SCROLL_VIEW (actor)->priv; if (!priv->child) return; mx_widget_get_padding (MX_WIDGET (actor), &padding); /* Our natural height is the natural height of the child */ clutter_actor_get_preferred_height (priv->child, for_width, &min_child_h, &nat_child_h); /* Add space for the scroll-bar if we can determine it will be necessary */ scroll_h = 0; if (for_width >= 0) { gfloat natural_width; clutter_actor_get_preferred_width (priv->child, -1.0, NULL, &natural_width); if (for_width < natural_width) scroll_h = priv->scrollbar_height; } /* Add space for padding */ if (min_height_p) { *min_height_p = padding.top + padding.bottom + scroll_h; if (!(priv->scroll_policy == MX_SCROLL_POLICY_BOTH || priv->scroll_policy == MX_SCROLL_POLICY_VERTICAL)) { *min_height_p += min_child_h; } } if (natural_height_p) *natural_height_p = padding.top + nat_child_h + padding.bottom + scroll_h; } static void mx_scroll_view_allocate (ClutterActor *actor, const ClutterActorBox *box, ClutterAllocationFlags flags) { MxPadding padding; ClutterActorBox child_box; gfloat avail_width, avail_height, sb_width, sb_height; MxScrollViewPrivate *priv = MX_SCROLL_VIEW (actor)->priv; CLUTTER_ACTOR_CLASS (mx_scroll_view_parent_class)-> allocate (actor, box, flags); mx_widget_get_padding (MX_WIDGET (actor), &padding); avail_width = (box->x2 - box->x1) - padding.left - padding.right; avail_height = (box->y2 - box->y1) - padding.top - padding.bottom; sb_width = priv->scrollbar_width; sb_height = priv->scrollbar_height; if (!CLUTTER_ACTOR_IS_VISIBLE (priv->vscroll)) sb_width = 0; if (!CLUTTER_ACTOR_IS_VISIBLE (priv->hscroll)) sb_height = 0; /* Vertical scrollbar */ if (CLUTTER_ACTOR_IS_VISIBLE (priv->vscroll)) { child_box.x1 = avail_width - sb_width; child_box.y1 = padding.top; child_box.x2 = avail_width; child_box.y2 = child_box.y1 + avail_height - sb_height; clutter_actor_allocate (priv->vscroll, &child_box, flags); } /* Horizontal scrollbar */ if (CLUTTER_ACTOR_IS_VISIBLE (priv->hscroll)) { child_box.x1 = padding.left; child_box.x2 = child_box.x1 + avail_width - sb_width; child_box.y1 = avail_height - sb_height; child_box.y2 = avail_height; clutter_actor_allocate (priv->hscroll, &child_box, flags); } /* Child */ child_box.x1 = padding.left; child_box.x2 = avail_width - sb_width; child_box.y1 = padding.top; child_box.y2 = avail_height - sb_height; if (priv->child) clutter_actor_allocate (priv->child, &child_box, flags); } static void mx_scroll_view_style_changed (MxWidget *widget, MxStyleChangedFlags flags) { MxScrollViewPrivate *priv = MX_SCROLL_VIEW (widget)->priv; mx_stylable_style_changed (MX_STYLABLE (priv->hscroll), flags); mx_stylable_style_changed (MX_STYLABLE (priv->vscroll), flags); mx_stylable_get (MX_STYLABLE (widget), "x-mx-scrollbar-width", &priv->scrollbar_width, "x-mx-scrollbar-height", &priv->scrollbar_height, NULL); } static gboolean mx_scroll_view_scroll_event (ClutterActor *self, ClutterScrollEvent *event) { MxScrollViewPrivate *priv = MX_SCROLL_VIEW (self)->priv; gdouble lower, value, upper, step; MxAdjustment *vadjustment, *hadjustment; /* don't handle scroll events if requested not to */ if (!priv->mouse_scroll) return FALSE; hadjustment = mx_scroll_bar_get_adjustment (MX_SCROLL_BAR(priv->hscroll)); vadjustment = mx_scroll_bar_get_adjustment (MX_SCROLL_BAR(priv->vscroll)); switch (event->direction) { case CLUTTER_SCROLL_UP: case CLUTTER_SCROLL_DOWN: if (vadjustment) g_object_get (vadjustment, "lower", &lower, "step-increment", &step, "value", &value, "upper", &upper, NULL); else return FALSE; break; case CLUTTER_SCROLL_LEFT: case CLUTTER_SCROLL_RIGHT: if (hadjustment) g_object_get (hadjustment, "lower", &lower, "step-increment", &step, "value", &value, "upper", &upper, NULL); else return FALSE; break; } switch (event->direction) { case CLUTTER_SCROLL_UP: if (value == lower) return FALSE; else mx_adjustment_interpolate_relative (vadjustment, -step, 250, CLUTTER_EASE_OUT_CUBIC); break; case CLUTTER_SCROLL_DOWN: if (value == upper) return FALSE; else mx_adjustment_interpolate_relative (vadjustment, step, 250, CLUTTER_EASE_OUT_CUBIC); break; case CLUTTER_SCROLL_LEFT: if (value == lower) return FALSE; else mx_adjustment_interpolate_relative (hadjustment, -step, 250, CLUTTER_EASE_OUT_CUBIC); break; case CLUTTER_SCROLL_RIGHT: if (value == upper) return FALSE; else mx_adjustment_interpolate_relative (hadjustment, step, 250, CLUTTER_EASE_OUT_CUBIC); break; } return TRUE; } static void mx_scroll_view_apply_style (MxWidget *widget, MxStyle *style) { MxScrollViewPrivate *priv = MX_SCROLL_VIEW (widget)->priv; if (priv->hscroll != NULL) mx_stylable_set_style (MX_STYLABLE (priv->hscroll), style); if (priv->vscroll != NULL) mx_stylable_set_style (MX_STYLABLE (priv->vscroll), style); } static void mx_scroll_view_class_init (MxScrollViewClass *klass) { GParamSpec *pspec; GObjectClass *object_class = G_OBJECT_CLASS (klass); ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass); MxWidgetClass *widget_class = MX_WIDGET_CLASS (klass); g_type_class_add_private (klass, sizeof (MxScrollViewPrivate)); object_class->get_property = mx_scroll_view_get_property; object_class->set_property = mx_scroll_view_set_property; object_class->dispose= mx_scroll_view_dispose; object_class->finalize = mx_scroll_view_finalize; actor_class->paint = mx_scroll_view_paint; actor_class->pick = mx_scroll_view_pick; actor_class->get_preferred_width = mx_scroll_view_get_preferred_width; actor_class->get_preferred_height = mx_scroll_view_get_preferred_height; actor_class->allocate = mx_scroll_view_allocate; actor_class->scroll_event = mx_scroll_view_scroll_event; widget_class->apply_style = mx_scroll_view_apply_style; pspec = g_param_spec_boolean ("enable-mouse-scrolling", "Enable Mouse Scrolling", "Enable automatic mouse wheel scrolling", TRUE, MX_PARAM_READWRITE); g_object_class_install_property (object_class, PROP_MOUSE_SCROLL, pspec); pspec = g_param_spec_boolean ("enable-gestures", "Enable Gestures", "Enable use of pointer gestures for scrolling " "if Mx was built with ClutterGesture support", FALSE, MX_PARAM_READWRITE); g_object_class_install_property (object_class, PROP_ENABLE_GESTURES, pspec); pspec = g_param_spec_enum ("scroll-policy", "Scroll Policy", "The scroll policy", MX_TYPE_SCROLL_POLICY, MX_SCROLL_POLICY_BOTH, MX_PARAM_READWRITE); g_object_class_install_property (object_class, PROP_SCROLL_POLICY, pspec); } static void mx_stylable_iface_init (MxStylableIface *iface) { static gboolean is_initialized = FALSE; if (!is_initialized) { GParamSpec *pspec; is_initialized = TRUE; pspec = g_param_spec_uint ("x-mx-scrollbar-width", "Vertical scroll-bar thickness", "Thickness of vertical scrollbar, in px", 0, G_MAXUINT, 24, G_PARAM_READWRITE); mx_stylable_iface_install_property (iface, MX_TYPE_SCROLL_VIEW, pspec); pspec = g_param_spec_uint ("x-mx-scrollbar-height", "Horizontal scroll-bar thickness", "Thickness of horizontal scrollbar, in px", 0, G_MAXUINT, 24, G_PARAM_READWRITE); mx_stylable_iface_install_property (iface, MX_TYPE_SCROLL_VIEW, pspec); } } static void child_adjustment_changed_cb (MxAdjustment *adjustment, ClutterActor *bar) { MxScrollView *scroll; gdouble lower, upper, page_size; scroll = MX_SCROLL_VIEW (clutter_actor_get_parent (bar)); /* Determine if this scroll-bar should be visible */ mx_adjustment_get_values (adjustment, NULL, &lower, &upper, NULL, NULL, &page_size); if ((upper - lower) > page_size) clutter_actor_show (bar); else clutter_actor_hide (bar); /* Request a resize */ clutter_actor_queue_relayout (CLUTTER_ACTOR (scroll)); } static void child_hadjustment_notify_cb (GObject *gobject, GParamSpec *arg1, gpointer user_data) { MxAdjustment *hadjust; ClutterActor *actor = CLUTTER_ACTOR (gobject); MxScrollViewPrivate *priv = MX_SCROLL_VIEW (user_data)->priv; hadjust = mx_scroll_bar_get_adjustment (MX_SCROLL_BAR(priv->hscroll)); if (hadjust) g_signal_handlers_disconnect_by_func (hadjust, child_adjustment_changed_cb, priv->hscroll); mx_scrollable_get_adjustments (MX_SCROLLABLE (actor), &hadjust, NULL); if (hadjust) { mx_scroll_bar_set_adjustment (MX_SCROLL_BAR(priv->hscroll), hadjust); g_signal_connect (hadjust, "changed", G_CALLBACK ( child_adjustment_changed_cb), priv->hscroll); child_adjustment_changed_cb (hadjust, priv->hscroll); } } static void child_vadjustment_notify_cb (GObject *gobject, GParamSpec *arg1, gpointer user_data) { MxAdjustment *vadjust; ClutterActor *actor = CLUTTER_ACTOR (gobject); MxScrollViewPrivate *priv = MX_SCROLL_VIEW (user_data)->priv; vadjust = mx_scroll_bar_get_adjustment (MX_SCROLL_BAR(priv->vscroll)); if (vadjust) g_signal_handlers_disconnect_by_func (vadjust, child_adjustment_changed_cb, priv->vscroll); mx_scrollable_get_adjustments (MX_SCROLLABLE(actor), NULL, &vadjust); if (vadjust) { mx_scroll_bar_set_adjustment (MX_SCROLL_BAR(priv->vscroll), vadjust); g_signal_connect (vadjust, "changed", G_CALLBACK ( child_adjustment_changed_cb), priv->vscroll); child_adjustment_changed_cb (vadjust, priv->vscroll); } } #ifdef HAVE_CLUTTER_GESTURE static gboolean mx_scroll_view_gesture_slide_event_cb (ClutterGesture *gesture, ClutterGestureSlideEvent *event, MxScrollView *scrollview) { MxScrollViewPrivate *priv = scrollview->priv; gdouble step, value, final; MxAdjustment *adjustment; ClutterInterval *interval; ClutterTimeline *timeline; if (!priv->enable_gestures) return FALSE; if (!priv->animation) { priv->animation = clutter_animation_new (); clutter_animation_set_duration (priv->animation, 1000); clutter_animation_set_mode (priv->animation, CLUTTER_EASE_OUT_CUBIC); } if (event->direction > 2) adjustment = mx_scroll_bar_get_adjustment (MX_SCROLL_BAR(priv->hscroll)); else adjustment = mx_scroll_bar_get_adjustment (MX_SCROLL_BAR(priv->vscroll)); g_object_get (adjustment, "page-increment", &step, "value", &value, NULL); clutter_animation_set_object (priv->animation, G_OBJECT (adjustment)); if (event->direction % 2) /* up, left (1, 3) */ final = value + step; else /* down, right (2, 4) */ final = value - step; timeline = clutter_animation_get_timeline (priv->animation); interval = clutter_animation_get_interval (priv->animation, "value"); if (!interval) { interval = clutter_interval_new (G_TYPE_DOUBLE, value, final); clutter_animation_bind_interval (priv->animation, "value", interval); clutter_timeline_start (timeline); } else { GValue *end, *start; end = clutter_interval_peek_final_value (interval); start = clutter_interval_peek_initial_value (interval); g_value_set_double (end, final); g_value_set_double (start, value); clutter_timeline_rewind (timeline); clutter_timeline_start (timeline); } return TRUE; } #endif static void mx_scroll_view_init (MxScrollView *self) { MxScrollViewPrivate *priv = self->priv = SCROLL_VIEW_PRIVATE (self); priv->hscroll = mx_scroll_bar_new (); priv->vscroll = g_object_new (MX_TYPE_SCROLL_BAR, "orientation", MX_ORIENTATION_VERTICAL, NULL); priv->scroll_policy = MX_SCROLL_POLICY_BOTH; clutter_actor_set_parent (priv->hscroll, CLUTTER_ACTOR (self)); clutter_actor_set_parent (priv->vscroll, CLUTTER_ACTOR (self)); clutter_actor_hide (priv->hscroll); clutter_actor_hide (priv->vscroll); /* mouse scroll is enabled by default, so we also need to be reactive */ priv->mouse_scroll = TRUE; g_object_set (G_OBJECT (self), "reactive", TRUE, NULL); g_signal_connect (self, "style-changed", G_CALLBACK (mx_scroll_view_style_changed), NULL); } static void mx_scroll_view_actor_added (ClutterContainer *container, ClutterActor *actor) { MxScrollView *self = MX_SCROLL_VIEW (container); MxScrollViewPrivate *priv = self->priv; if (MX_IS_SCROLLABLE (actor)) { priv->child = actor; /* Get adjustments for scroll-bars */ g_signal_connect (actor, "notify::horizontal-adjustment", G_CALLBACK (child_hadjustment_notify_cb), container); g_signal_connect (actor, "notify::vertical-adjustment", G_CALLBACK (child_vadjustment_notify_cb), container); child_hadjustment_notify_cb (G_OBJECT (actor), NULL, container); child_vadjustment_notify_cb (G_OBJECT (actor), NULL, container); } else { g_warning ("Attempting to add an actor of type %s to " "a MxScrollView, but the actor does " "not implement MxScrollable.", g_type_name (G_OBJECT_TYPE (actor))); } } static void mx_scroll_view_actor_removed (ClutterContainer *container, ClutterActor *actor) { MxScrollViewPrivate *priv = MX_SCROLL_VIEW (container)->priv; if (actor == priv->child) { g_object_ref (priv->child); g_signal_handlers_disconnect_by_func (priv->child, child_hadjustment_notify_cb, container); g_signal_handlers_disconnect_by_func (priv->child, child_vadjustment_notify_cb, container); mx_scrollable_set_adjustments ((MxScrollable*) priv->child, NULL, NULL); g_object_unref (priv->child); priv->child = NULL; } } static void mx_scroll_view_foreach_with_internals (ClutterContainer *container, ClutterCallback callback, gpointer user_data) { MxScrollViewPrivate *priv = MX_SCROLL_VIEW (container)->priv; if (priv->child != NULL) callback (priv->child, user_data); if (priv->hscroll != NULL) callback (priv->hscroll, user_data); if (priv->vscroll != NULL) callback (priv->vscroll, user_data); } static void clutter_container_iface_init (ClutterContainerIface *iface) { iface->foreach_with_internals = mx_scroll_view_foreach_with_internals; iface->actor_added = mx_scroll_view_actor_added; iface->actor_removed = mx_scroll_view_actor_removed; } /** * mx_scroll_view_get_hscroll_bar: * @scroll: a #MxScrollView * * Gets the horizontal scrollbar of the scrollbiew * * Return value: (transfer none): the horizontal #MxScrollbar */ ClutterActor * mx_scroll_view_new (void) { return g_object_new (MX_TYPE_SCROLL_VIEW, NULL); } void mx_scroll_view_set_enable_mouse_scrolling (MxScrollView *scroll, gboolean enabled) { MxScrollViewPrivate *priv; g_return_if_fail (MX_IS_SCROLL_VIEW (scroll)); priv = scroll->priv; if (priv->mouse_scroll != enabled) { priv->mouse_scroll = enabled; /* make sure we can receive mouse wheel events */ if (enabled) clutter_actor_set_reactive ((ClutterActor *) scroll, TRUE); g_object_notify (G_OBJECT (scroll), "enable-mouse-scrolling"); } } gboolean mx_scroll_view_get_enable_mouse_scrolling (MxScrollView *scroll) { MxScrollViewPrivate *priv; g_return_val_if_fail (MX_IS_SCROLL_VIEW (scroll), FALSE); priv = scroll->priv; return priv->mouse_scroll; } void mx_scroll_view_set_enable_gestures (MxScrollView *scroll, gboolean enabled) { MxScrollViewPrivate *priv; g_return_if_fail (MX_IS_SCROLL_VIEW (scroll)); priv = scroll->priv; if (priv->enable_gestures != enabled) { priv->enable_gestures = enabled; #ifndef HAVE_CLUTTER_GESTURE g_warning ("Gestures are disabled as Clutter Gesture is not available"); #else if (enabled && !priv->gesture) { priv->gesture = clutter_gesture_new (CLUTTER_ACTOR (scroll)); clutter_gesture_set_gesture_mask (priv->gesture, CLUTTER_ACTOR (scroll), GESTURE_MASK_SLIDE); g_signal_connect (priv->gesture, "gesture-slide-event", G_CALLBACK (mx_scroll_view_gesture_slide_event_cb), scroll); clutter_actor_set_reactive (CLUTTER_ACTOR (scroll), TRUE); } #endif g_object_notify (G_OBJECT (scroll), "enable-gestures"); } } gboolean mx_scroll_view_get_enable_gestures (MxScrollView *scroll) { g_return_val_if_fail (MX_IS_SCROLL_VIEW (scroll), FALSE); return scroll->priv->enable_gestures; } void mx_scroll_view_set_scroll_policy (MxScrollView *scroll, MxScrollPolicy policy) { MxScrollViewPrivate *priv; g_return_if_fail (MX_IS_SCROLL_VIEW (scroll)); priv = scroll->priv; if (priv->scroll_policy != policy) { priv->scroll_policy = policy; g_object_notify (G_OBJECT (scroll), "scroll-policy"); clutter_actor_queue_relayout (CLUTTER_ACTOR (scroll)); } } MxScrollPolicy mx_scroll_view_get_scroll_policy (MxScrollView *scroll) { g_return_val_if_fail (MX_IS_SCROLL_VIEW (scroll), 0); return scroll->priv->scroll_policy; } static void _mx_scroll_view_ensure_visible_axis (MxScrollBar *bar, gdouble lower, gdouble upper) { gdouble new_value, adjust_lower, adjust_upper, adjust_page_size; gboolean changed = FALSE; MxAdjustment *adjust = mx_scroll_bar_get_adjustment (bar); mx_adjustment_get_values (adjust, &new_value, &adjust_lower, &adjust_upper, NULL, NULL, &adjust_page_size); /* Sanitise input values */ lower = CLAMP (lower, adjust_lower, adjust_upper - adjust_page_size); upper = CLAMP (upper, adjust_lower + adjust_page_size, adjust_upper); /* Ensure the bottom is visible */ if (new_value + adjust_page_size < upper) { new_value = upper - adjust_page_size; changed = TRUE; } /* Ensure the top is visible */ if (lower < new_value) { new_value = lower; changed = TRUE; } if (changed) mx_adjustment_interpolate (adjust, new_value, 250, CLUTTER_EASE_OUT_CUBIC); } /** * mx_scroll_view_ensure_visible: * @scroll: A #MxScrollView * @geometry: The region to make visible * * Ensures that a given region is visible in the ScrollView, with the top-left * taking precedence. * */ void mx_scroll_view_ensure_visible (MxScrollView *scroll, const ClutterGeometry *geometry) { MxScrollViewPrivate *priv; g_return_if_fail (MX_IS_SCROLL_VIEW (scroll)); priv = scroll->priv; _mx_scroll_view_ensure_visible_axis (MX_SCROLL_BAR (priv->hscroll), geometry->x, geometry->x + geometry->width); _mx_scroll_view_ensure_visible_axis (MX_SCROLL_BAR (priv->vscroll), geometry->y, geometry->y + geometry->height); } mx-1.4.7/mx/mx-scroll-view.h000066400000000000000000000066221201047117600156410ustar00rootroot00000000000000/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * mx-scroll-view.h: Container with scroll-bars * * Copyright 2008 OpenedHand * Copyright 2009 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * Boston, MA 02111-1307, USA. * * Written by: Chris Lord * Port to Mx by: Robert Staudinger * */ #if !defined(MX_H_INSIDE) && !defined(MX_COMPILATION) #error "Only can be included directly.h" #endif #ifndef __MX_SCROLL_VIEW_H__ #define __MX_SCROLL_VIEW_H__ #include G_BEGIN_DECLS #define MX_TYPE_SCROLL_VIEW (mx_scroll_view_get_type()) #define MX_SCROLL_VIEW(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MX_TYPE_SCROLL_VIEW, MxScrollView)) #define MX_IS_SCROLL_VIEW(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MX_TYPE_SCROLL_VIEW)) #define MX_SCROLL_VIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MX_TYPE_SCROLL_VIEW, MxScrollViewClass)) #define MX_IS_SCROLL_VIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MX_TYPE_SCROLL_VIEW)) #define MX_SCROLL_VIEW_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MX_TYPE_SCROLL_VIEW, MxScrollViewClass)) typedef struct _MxScrollView MxScrollView; typedef struct _MxScrollViewPrivate MxScrollViewPrivate; typedef struct _MxScrollViewClass MxScrollViewClass; /** * MxScrollView: * * The contents of this structure are private and should only be accessed * through the public API. */ struct _MxScrollView { /*< private >*/ MxBin parent_instance; MxScrollViewPrivate *priv; }; struct _MxScrollViewClass { MxBinClass parent_class; /* padding for future expansion */ void (*_padding_0) (void); void (*_padding_1) (void); void (*_padding_2) (void); void (*_padding_3) (void); void (*_padding_4) (void); }; GType mx_scroll_view_get_type (void) G_GNUC_CONST; ClutterActor *mx_scroll_view_new (void); void mx_scroll_view_set_enable_mouse_scrolling (MxScrollView *scroll, gboolean enabled); gboolean mx_scroll_view_get_enable_mouse_scrolling (MxScrollView *scroll); void mx_scroll_view_set_enable_gestures (MxScrollView *scroll, gboolean enabled); gboolean mx_scroll_view_get_enable_gestures (MxScrollView *scroll); void mx_scroll_view_set_scroll_policy (MxScrollView *scroll, MxScrollPolicy policy); MxScrollPolicy mx_scroll_view_get_scroll_policy (MxScrollView *scroll); void mx_scroll_view_ensure_visible (MxScrollView *scroll, const ClutterGeometry *geometry); G_END_DECLS #endif /* __MX_SCROLL_VIEW_H__ */ mx-1.4.7/mx/mx-scrollable.c000066400000000000000000000066711201047117600155140ustar00rootroot00000000000000/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * mx-scrollable.c: Scrollable interface * * Copyright 2008 OpenedHand * Copyright 2009 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * * Written by: Chris Lord * Port to Mx by: Robert Staudinger * */ #include "mx-scrollable.h" #include "mx-private.h" static void mx_scrollable_base_init (gpointer g_iface) { static gboolean initialized = FALSE; if (!initialized) { GParamSpec *pspec; pspec = g_param_spec_object ("horizontal-adjustment", "Horizontal adjustment", "The MxAdjustment for horizontal scrolling.", MX_TYPE_ADJUSTMENT, MX_PARAM_READWRITE); g_object_interface_install_property (g_iface, pspec); pspec = g_param_spec_object ("vertical-adjustment", "Vertical adjustment", "The MxAdjustment for vertical scrolling.", MX_TYPE_ADJUSTMENT, MX_PARAM_READWRITE); g_object_interface_install_property (g_iface, pspec); initialized = TRUE; } } GType mx_scrollable_get_type (void) { static GType type = 0; if (type == 0) { static const GTypeInfo info = { sizeof (MxScrollableIface), mx_scrollable_base_init, /* base_init */ NULL, }; type = g_type_register_static (G_TYPE_INTERFACE, "MxScrollable", &info, 0); } return type; } void mx_scrollable_set_adjustments (MxScrollable *scrollable, MxAdjustment *hadjustment, MxAdjustment *vadjustment) { MX_SCROLLABLE_GET_IFACE (scrollable)->set_adjustments (scrollable, hadjustment, vadjustment); } /** * mx_scrollable_get_adjustments: * @hadjustment: (transfer none) (out) (allow-none): location to store the horizontal adjustment, or %NULL * @vadjustment: (transfer none) (out) (allow-none): location to store the vertical adjustment, or %NULL * * Gets the adjustment objects that store the offsets of the scrollable widget * into its possible scrolling area. */ void mx_scrollable_get_adjustments (MxScrollable *scrollable, MxAdjustment **hadjustment, MxAdjustment **vadjustment) { MX_SCROLLABLE_GET_IFACE (scrollable)->get_adjustments (scrollable, hadjustment, vadjustment); } mx-1.4.7/mx/mx-scrollable.h000066400000000000000000000053311201047117600155110ustar00rootroot00000000000000/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * mx-scrollable.h: Scrollable interface * * Copyright 2008 OpenedHand * Copyright 2009 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * Boston, MA 02111-1307, USA. * * Written by: Chris Lord * Port to Mx by: Robert Staudinger * */ #if !defined(MX_H_INSIDE) && !defined(MX_COMPILATION) #error "Only can be included directly.h" #endif #ifndef __MX_SCROLLABLE_H__ #define __MX_SCROLLABLE_H__ #include #include G_BEGIN_DECLS #define MX_TYPE_SCROLLABLE (mx_scrollable_get_type ()) #define MX_SCROLLABLE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MX_TYPE_SCROLLABLE, MxScrollable)) #define MX_IS_SCROLLABLE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MX_TYPE_SCROLLABLE)) #define MX_SCROLLABLE_GET_IFACE(inst) (G_TYPE_INSTANCE_GET_INTERFACE ((inst), MX_TYPE_SCROLLABLE, MxScrollableIface)) /** * MxScrollable: * * This is an opaque structure whose members cannot be directly accessed. */ typedef struct _MxScrollable MxScrollable; /* Dummy object */ typedef struct _MxScrollableIface MxScrollableIface; struct _MxScrollableIface { /*< private >*/ GTypeInterface parent; /*< public >*/ void (* set_adjustments) (MxScrollable *scrollable, MxAdjustment *hadjustment, MxAdjustment *vadjustment); void (* get_adjustments) (MxScrollable *scrollable, MxAdjustment **hadjustment, MxAdjustment **vadjustment); }; GType mx_scrollable_get_type (void) G_GNUC_CONST; void mx_scrollable_set_adjustments (MxScrollable *scrollable, MxAdjustment *hadjustment, MxAdjustment *vadjustment); void mx_scrollable_get_adjustments (MxScrollable *scrollable, MxAdjustment **hadjustment, MxAdjustment **vadjustment); G_END_DECLS #endif /* __MX_SCROLLABLE_H__ */ mx-1.4.7/mx/mx-settings-provider.c000066400000000000000000000074771201047117600170670ustar00rootroot00000000000000/* * mx-settings-provider.c: An object that provides settings * * Copyright 2010 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * Boston, MA 02111-1307, USA. * */ #include "mx-settings-provider.h" #include "mx-enum-types.h" #include "mx-marshal.h" enum { SETTING_CHANGED, LAST_SIGNAL }; static guint signals[LAST_SIGNAL] = { 0, }; static void mx_settings_provider_base_init (gpointer g_iface) { static gboolean is_initialized = FALSE; if (G_UNLIKELY (!is_initialized)) { GParamSpec *pspec; GType iface_type = G_TYPE_FROM_INTERFACE (g_iface); is_initialized = TRUE; pspec = g_param_spec_object ("settings", "Settings", "The parent MxSettings", MX_TYPE_SETTINGS, MX_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); g_object_interface_install_property (g_iface, pspec); signals[SETTING_CHANGED] = g_signal_new (g_intern_static_string ("setting-changed"), iface_type, G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (MxSettingsProviderIface, setting_changed), NULL, NULL, _mx_marshal_VOID__UINT, G_TYPE_NONE, 1, G_TYPE_UINT); } } GType _mx_settings_provider_get_type (void) { static GType settings_provider_type = 0; if (G_UNLIKELY (settings_provider_type == 0)) { const GTypeInfo settings_provider_info = { sizeof (MxSettingsProviderIface), mx_settings_provider_base_init, NULL }; settings_provider_type = g_type_register_static (G_TYPE_INTERFACE, g_intern_static_string ("MxSettingsProvider"), &settings_provider_info, 0); g_type_interface_add_prerequisite (settings_provider_type, G_TYPE_OBJECT); } return settings_provider_type; } gboolean _mx_settings_provider_get_setting (MxSettingsProvider *provider, MxSettingsProperty id, gpointer value) { MxSettingsProviderIface *iface; g_return_val_if_fail (MX_IS_SETTINGS_PROVIDER (provider), FALSE); iface = MX_SETTINGS_PROVIDER_GET_IFACE (provider); if (iface->get_setting) return iface->get_setting (provider, id, value); return FALSE; } gboolean _mx_settings_provider_set_setting (MxSettingsProvider *provider, MxSettingsProperty id, gpointer value) { MxSettingsProviderIface *iface; g_return_val_if_fail (MX_IS_SETTINGS_PROVIDER (provider), FALSE); iface = MX_SETTINGS_PROVIDER_GET_IFACE (provider); if (iface->set_setting) return iface->set_setting (provider, id, value); return FALSE; } void _mx_settings_provider_setting_changed (MxSettingsProvider *provider, MxSettingsProperty id) { g_return_if_fail (MX_IS_SETTINGS_PROVIDER (provider)); g_signal_emit (provider, signals[SETTING_CHANGED], 0, id); } mx-1.4.7/mx/mx-settings-provider.h000066400000000000000000000054441201047117600170640ustar00rootroot00000000000000/* * mx-settings-provider.h: An object that provides settings * * Copyright 2010 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * Boston, MA 02111-1307, USA. * */ #ifndef _MX_SETTINGS_PROVIDER_H #define _MX_SETTINGS_PROVIDER_H #include #include #include "mx-private.h" G_BEGIN_DECLS #define MX_TYPE_SETTINGS_PROVIDER _mx_settings_provider_get_type() #define MX_SETTINGS_PROVIDER(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ MX_TYPE_SETTINGS_PROVIDER, MxSettingsProvider)) #define MX_IS_SETTINGS_PROVIDER(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \ MX_TYPE_SETTINGS_PROVIDER)) #define MX_SETTINGS_PROVIDER_GET_IFACE(obj) \ (G_TYPE_INSTANCE_GET_INTERFACE ((obj), \ MX_TYPE_SETTINGS_PROVIDER, MxSettingsProviderIface)) typedef struct _MxSettingsProvider MxSettingsProvider; /* dummy */ typedef struct _MxSettingsProviderIface MxSettingsProviderIface; struct _MxSettingsProviderIface { /*< private >*/ GTypeInterface parent_iface; /*< public >*/ /* signals, not vfuncs */ void (* setting_changed) (MxSettingsProvider *provider, MxSettingsProperty id); /* vfuncs */ gboolean (* get_setting) (MxSettingsProvider *provider, MxSettingsProperty id, gpointer value); gboolean (* set_setting) (MxSettingsProvider *provider, MxSettingsProperty id, gpointer value); }; GType _mx_settings_provider_get_type (void) G_GNUC_CONST; gboolean _mx_settings_provider_get_setting (MxSettingsProvider *provider, MxSettingsProperty id, gpointer value); gboolean _mx_settings_provider_set_setting (MxSettingsProvider *provider, MxSettingsProperty id, gpointer value); void _mx_settings_provider_setting_changed (MxSettingsProvider *provider, MxSettingsProperty id); G_END_DECLS #endif /* _MX_SETTINGS_PROVIDER_H */ mx-1.4.7/mx/mx-settings.c000066400000000000000000000260301201047117600152210ustar00rootroot00000000000000/* * mx-settings.c: Global settings * * Copyright 2010 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * Boston, MA 02111-1307, USA. * */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "mx-settings.h" #include "mx-private.h" #include "mx-settings-provider.h" #ifdef HAVE_X11 #include "x11/mx-settings-x11.h" #endif G_DEFINE_TYPE (MxSettings, mx_settings, G_TYPE_OBJECT) #define SETTINGS_PRIVATE(o) \ (G_TYPE_INSTANCE_GET_PRIVATE ((o), MX_TYPE_SETTINGS, MxSettingsPrivate)) struct _MxSettingsPrivate { MxSettingsProvider *provider; gchar *icon_theme; gchar *font_name; guint long_press_timeout; guint drag_threshold; guint small_screen : 1; }; static void mx_settings_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { guint uint_value; gchar *string_value; gboolean boolean_value; MxSettingsPrivate *priv = MX_SETTINGS (object)->priv; /* Check if the settings provider has these settings first */ if (priv->provider) { switch (property_id) { case MX_SETTINGS_ICON_THEME: case MX_SETTINGS_FONT_NAME: if (_mx_settings_provider_get_setting (priv->provider, property_id, &string_value)) { g_value_set_string (value, string_value); return; } break; case MX_SETTINGS_LONG_PRESS_TIMEOUT: if (_mx_settings_provider_get_setting (priv->provider, property_id, &uint_value)) { g_value_set_uint (value, uint_value); return; } break; case MX_SETTINGS_SMALL_SCREEN: if (_mx_settings_provider_get_setting (priv->provider, property_id, &boolean_value)) { g_value_set_boolean (value, boolean_value); return; } break; case MX_SETTINGS_DRAG_THRESHOLD: if (_mx_settings_provider_get_setting (priv->provider, property_id, &uint_value)) { g_value_set_uint (value, uint_value); return; } } } /* Check the internal settings */ switch (property_id) { case MX_SETTINGS_ICON_THEME: g_value_set_string (value, priv->icon_theme); break; case MX_SETTINGS_FONT_NAME: g_value_set_string (value, priv->font_name); break; case MX_SETTINGS_LONG_PRESS_TIMEOUT: g_value_set_uint (value, priv->long_press_timeout); break; case MX_SETTINGS_SMALL_SCREEN: g_value_set_boolean (value, priv->small_screen); break; case MX_SETTINGS_DRAG_THRESHOLD: g_value_set_uint (value, priv->drag_threshold); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } } static void mx_settings_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { guint uint_value; gboolean boolean_value; const gchar *string_value; MxSettingsPrivate *priv = MX_SETTINGS (object)->priv; /* Check if the settings provider can set these settings first */ if (priv->provider) { switch (property_id) { case MX_SETTINGS_ICON_THEME: case MX_SETTINGS_FONT_NAME: string_value = g_value_get_string (value); if (_mx_settings_provider_set_setting (priv->provider, property_id, &string_value)) return; break; case MX_SETTINGS_LONG_PRESS_TIMEOUT: uint_value = g_value_get_uint (value); if (_mx_settings_provider_set_setting (priv->provider, property_id, &uint_value)) return; break; case MX_SETTINGS_SMALL_SCREEN: boolean_value = g_value_get_boolean (value); if (_mx_settings_provider_set_setting (priv->provider, property_id, &boolean_value)) return; break; case MX_SETTINGS_DRAG_THRESHOLD: uint_value = g_value_get_uint (value); if (_mx_settings_provider_set_setting (priv->provider, property_id, &uint_value)) return; break; } } switch (property_id) { case MX_SETTINGS_ICON_THEME: g_free (priv->icon_theme); priv->icon_theme = g_value_dup_string (value); break; case MX_SETTINGS_FONT_NAME: g_free (priv->font_name); priv->font_name = g_value_dup_string (value); break; case MX_SETTINGS_LONG_PRESS_TIMEOUT: priv->long_press_timeout = g_value_get_uint (value); break; case MX_SETTINGS_SMALL_SCREEN: priv->small_screen = g_value_get_boolean (value); break; case MX_SETTINGS_DRAG_THRESHOLD: priv->drag_threshold = g_value_get_uint (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } } static void mx_settings_dispose (GObject *object) { MxSettingsPrivate *priv = MX_SETTINGS (object)->priv; if (priv->provider) { g_object_unref (priv->provider); priv->provider = NULL; } G_OBJECT_CLASS (mx_settings_parent_class)->dispose (object); } static void mx_settings_finalize (GObject *object) { MxSettingsPrivate *priv = MX_SETTINGS (object)->priv; g_free (priv->icon_theme); g_free (priv->font_name); G_OBJECT_CLASS (mx_settings_parent_class)->finalize (object); } static GObject * mx_settings_constructor (GType type, guint n_construct_params, GObjectConstructParam *construct_params) { static MxSettings *the_singleton = NULL; GObject *object; if (!the_singleton) { object = G_OBJECT_CLASS (mx_settings_parent_class)->constructor (type, n_construct_params, construct_params); the_singleton = MX_SETTINGS (object); } else object = (G_OBJECT (the_singleton)); return object; } #if defined(HAVE_X11) static void mx_settings_changed_cb (MxSettingsProvider *provider, MxSettingsProperty id, MxSettings *self) { switch (id) { case MX_SETTINGS_ICON_THEME: g_object_notify (G_OBJECT (self), "icon-theme"); return; case MX_SETTINGS_FONT_NAME: g_object_notify (G_OBJECT (self), "font-name"); return; case MX_SETTINGS_LONG_PRESS_TIMEOUT: g_object_notify (G_OBJECT (self), "long-press-timeout"); return; case MX_SETTINGS_SMALL_SCREEN: g_object_notify (G_OBJECT (self), "small-screen"); return; case MX_SETTINGS_DRAG_THRESHOLD: g_object_notify (G_OBJECT (self), "drag-threshold"); return; } } #endif static void mx_settings_constructed (GObject *self) { #ifdef HAVE_X11 MxSettings *settings = MX_SETTINGS (self); MxSettingsPrivate *priv = settings->priv; priv->provider = _mx_settings_x11_new (settings); g_signal_connect (priv->provider, "setting-changed", G_CALLBACK (mx_settings_changed_cb), settings); #endif } static void mx_settings_class_init (MxSettingsClass *klass) { GParamSpec *pspec; GObjectClass *object_class = G_OBJECT_CLASS (klass); g_type_class_add_private (klass, sizeof (MxSettingsPrivate)); object_class->get_property = mx_settings_get_property; object_class->set_property = mx_settings_set_property; object_class->dispose = mx_settings_dispose; object_class->finalize = mx_settings_finalize; object_class->constructor = mx_settings_constructor; object_class->constructed = mx_settings_constructed; pspec = g_param_spec_string ("icon-theme", "Icon Theme", "Name of the icon theme to use when loading" " icons", "hicolor", MX_PARAM_READWRITE); g_object_class_install_property (object_class, MX_SETTINGS_ICON_THEME, pspec); pspec = g_param_spec_string ("font-name", "Font Name", "Default font name", "Sans 10", MX_PARAM_READWRITE); g_object_class_install_property (object_class, MX_SETTINGS_FONT_NAME, pspec); pspec = g_param_spec_uint ("long-press-timeout", "Long Press Timeout", "Long press timeout", 0, G_MAXUINT, 500, MX_PARAM_READWRITE); g_object_class_install_property (object_class, MX_SETTINGS_LONG_PRESS_TIMEOUT, pspec); pspec = g_param_spec_boolean ("small-screen", "Small screen", "MeeGo small-screen mode is active", FALSE, MX_PARAM_READWRITE); g_object_class_install_property (object_class, MX_SETTINGS_SMALL_SCREEN, pspec); pspec = g_param_spec_uint ("drag-threshold", "Drag threshold", "Pixel threshold to exceed before initiating " "a drag event", 0, G_MAXUINT, 8, MX_PARAM_READWRITE); g_object_class_install_property (object_class, MX_SETTINGS_DRAG_THRESHOLD, pspec); } static void mx_settings_init (MxSettings *self) { MxSettingsPrivate *priv = self->priv = SETTINGS_PRIVATE (self); /* Setup defaults */ priv->icon_theme = g_strdup ("hicolor"); priv->font_name = g_strdup ("Sans 10"); priv->long_press_timeout = 500; priv->small_screen = FALSE; priv->drag_threshold = 8; } /** * mx_settings_get_default: * * Get the default #MxSettings object. * * Returns: the #MxSettings singleton. * * Since: 1.2 */ MxSettings * mx_settings_get_default (void) { return g_object_new (MX_TYPE_SETTINGS, NULL); } mx-1.4.7/mx/mx-settings.h000066400000000000000000000042271201047117600152320ustar00rootroot00000000000000/* * mx-settings.h: Global settings * * Copyright 2010 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * Boston, MA 02111-1307, USA. * */ #ifndef _MX_SETTINGS_H #define _MX_SETTINGS_H #include G_BEGIN_DECLS #define MX_TYPE_SETTINGS mx_settings_get_type() #define MX_SETTINGS(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ MX_TYPE_SETTINGS, MxSettings)) #define MX_SETTINGS_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), \ MX_TYPE_SETTINGS, MxSettingsClass)) #define MX_IS_SETTINGS(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \ MX_TYPE_SETTINGS)) #define MX_IS_SETTINGS_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE ((klass), \ MX_TYPE_SETTINGS)) #define MX_SETTINGS_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), \ MX_TYPE_SETTINGS, MxSettingsClass)) typedef struct _MxSettings MxSettings; typedef struct _MxSettingsClass MxSettingsClass; typedef struct _MxSettingsPrivate MxSettingsPrivate; struct _MxSettings { GObject parent; MxSettingsPrivate *priv; }; struct _MxSettingsClass { GObjectClass parent_class; /*< private >*/ /* padding for future expansion */ void (*_padding_0) (void); void (*_padding_1) (void); void (*_padding_2) (void); void (*_padding_3) (void); void (*_padding_4) (void); }; GType mx_settings_get_type (void) G_GNUC_CONST; /** * mx_settings_get_default: * * Get the global MxSettings object. * * Returns: (transfer none): an #MxSettings object */ MxSettings *mx_settings_get_default (void); G_END_DECLS #endif /* _MX_SETTINGS_H */ mx-1.4.7/mx/mx-slider.c000066400000000000000000000700501201047117600146440ustar00rootroot00000000000000/* * mx-slider.c: Slider widget * * Copyright 2009 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * * Written by: Damien Lespiau * */ /** * SECTION:mx-slider * @short_description: A widget to visualize and control a range * * FIXME * */ #include "mx-slider.h" #include "mx-stylable.h" #include "mx-progress-bar-fill.h" #include "mx-button.h" #include "mx-frame.h" #include "mx-focusable.h" static void mx_stylable_iface_init (MxStylableIface *iface); static void mx_focusable_iface_init (MxFocusableIface *iface); G_DEFINE_TYPE_WITH_CODE (MxSlider, mx_slider, MX_TYPE_WIDGET, G_IMPLEMENT_INTERFACE (MX_TYPE_STYLABLE, mx_stylable_iface_init) G_IMPLEMENT_INTERFACE (MX_TYPE_FOCUSABLE, mx_focusable_iface_init)) #define SLIDER_PRIVATE(o) \ (G_TYPE_INSTANCE_GET_PRIVATE ((o), \ MX_TYPE_SLIDER, \ MxSliderPrivate)) #define DEFAULT_HANDLE_WIDTH 25 #define DEFAULT_HANDLE_HEIGHT 16 struct _MxSliderPrivate { ClutterActor *trough_bg; ClutterActor *fill; ClutterActor *trough; ClutterActor *handle; ClutterActor *buffer; gulong capture_handler; gfloat x_origin; /* the middle of the handle can wander on the axis between start and end */ gfloat handle_middle_start; gfloat handle_middle_end; /* keep those around for ::alocate_fill() */ gfloat trough_box_y1; gfloat trough_box_y2; gint trough_height; guint handle_width; guint handle_height; gdouble value; gdouble buffer_value; }; enum { PROP_0, PROP_VALUE, PROP_BUFFER_VALUE, }; static void mx_slider_allocate_fill_handle (MxSlider *self, const ClutterActorBox *box, ClutterAllocationFlags flags); /* MxFocusable interface */ static MxFocusable* mx_slider_move_focus (MxFocusable *focusable, MxFocusDirection direction, MxFocusable *old_focus) { mx_stylable_style_pseudo_class_remove (MX_STYLABLE (focusable), "focus"); return NULL; } static MxFocusable* mx_slider_accept_focus (MxFocusable *focusable, MxFocusHint hint) { clutter_actor_grab_key_focus (CLUTTER_ACTOR (focusable)); mx_stylable_style_pseudo_class_add (MX_STYLABLE (focusable), "focus"); return focusable; } static void mx_focusable_iface_init (MxFocusableIface *iface) { iface->move_focus = mx_slider_move_focus; iface->accept_focus = mx_slider_accept_focus; } static void drag_handle (MxSlider *bar, gfloat x, gfloat y) { MxSliderPrivate *priv = bar->priv; gdouble value; gfloat ux, pos, handle_width_2, fill_size, offset; if (!clutter_actor_transform_stage_point (CLUTTER_ACTOR (bar), x, y, &ux, NULL)) { return; } fill_size = priv->handle_middle_end - priv->handle_middle_start; /* offset is the difference between the middle of the handle and where one * clicked on it */ handle_width_2 = clutter_actor_get_width (priv->handle) / 2; offset = handle_width_2 - priv->x_origin; pos = ux - priv->handle_middle_start + offset; pos = CLAMP (pos, 0, fill_size); value = pos / fill_size; mx_slider_set_value (bar, value); /* update the handle position */ mx_slider_allocate_fill_handle (bar, NULL, 0); clutter_actor_queue_redraw (CLUTTER_ACTOR (bar)); } static gboolean on_handle_capture_event (ClutterActor *trough, ClutterEvent *event, MxSlider *bar) { MxSliderPrivate *priv = bar->priv; if (clutter_event_type (event) == CLUTTER_MOTION) { drag_handle (bar, ((ClutterMotionEvent*)event)->x, ((ClutterMotionEvent*)event)->y); } else if (clutter_event_type (event) == CLUTTER_BUTTON_RELEASE && ((ClutterButtonEvent*)event)->button == 1) { ClutterActor *stage, *target; stage = clutter_actor_get_stage(priv->trough); if (priv->capture_handler) { g_signal_handler_disconnect (stage, priv->capture_handler); priv->capture_handler = 0; /* update the handle position */ mx_slider_allocate_fill_handle (bar, NULL, 0); clutter_actor_queue_redraw (CLUTTER_ACTOR (bar)); } clutter_set_motion_events_enabled (TRUE); /* check if the mouse pointer has left the handle during the drag and * remove the hover state if it has */ target = clutter_stage_get_actor_at_pos ((ClutterStage*) stage, CLUTTER_PICK_REACTIVE, ((ClutterButtonEvent*) event)->x, ((ClutterButtonEvent*) event)->y); if (target != priv->handle) { mx_stylable_set_style_pseudo_class (MX_STYLABLE (priv->handle), NULL); } } return FALSE; } static void move_handle (MxSlider *bar, gfloat x, gfloat y) { MxSliderPrivate *priv = bar->priv; gdouble value; gfloat ux, pos, fill_size; if (!clutter_actor_transform_stage_point (CLUTTER_ACTOR (bar), x, y, &ux, NULL)) { return; } priv->x_origin = priv->handle_width / 2 + clutter_actor_get_x (priv->trough); fill_size = priv->handle_middle_end - priv->handle_middle_start; pos = ux - priv->handle_middle_start; pos = CLAMP (pos, 0, fill_size); value = pos / fill_size; mx_slider_set_value (bar, value); } static gboolean on_trough_bg_button_press_event (ClutterActor *actor, ClutterButtonEvent *event, MxSlider *self) { MxSliderPrivate *priv = self->priv; if (event->button != 1) return FALSE; if (mx_widget_get_disabled (MX_WIDGET (actor))) return FALSE; move_handle (self, event->x, event->y); /* Turn off picking for motion events */ clutter_set_motion_events_enabled (FALSE); priv->capture_handler = g_signal_connect_after (clutter_actor_get_stage (priv->handle), "captured-event", G_CALLBACK (on_handle_capture_event), self); return TRUE; } static gboolean on_trough_bg_button_release_event (ClutterActor *actor, ClutterButtonEvent *event, MxSlider *self) { if (event->button != 1) return FALSE; return TRUE; } static gboolean on_trough_bg_leave_event (ClutterActor *actor, ClutterEvent *event, MxSlider *self) { return TRUE; } static gboolean on_handle_button_press_event (ClutterActor *actor, ClutterButtonEvent *event, MxSlider *bar) { MxSliderPrivate *priv = bar->priv; if (mx_widget_get_disabled (MX_WIDGET (actor))) return FALSE; if (event->button != 1) return FALSE; if (!clutter_actor_transform_stage_point (priv->handle, event->x, event->y, &priv->x_origin, NULL)) return FALSE; /* Account for the scrollbar-trough-handle nesting. */ priv->x_origin += clutter_actor_get_x (priv->trough); /* Turn off picking for motion events */ clutter_set_motion_events_enabled (FALSE); priv->capture_handler = g_signal_connect_after (clutter_actor_get_stage (priv->trough), "captured-event", G_CALLBACK (on_handle_capture_event), bar); return FALSE; } /* * MxStylable */ static void mx_stylable_iface_init (MxStylableIface *iface) { static gboolean is_initialized = FALSE; if (!is_initialized) { GParamSpec *pspec; is_initialized = TRUE; /* if specified, this will be the allocated height of the trough. * By default, the height of the trough is the same as its parent. */ pspec = g_param_spec_int ("x-mx-trough-height", "Height of the trough", "Height of the trough, in px", -1, G_MAXINT, -1, G_PARAM_READWRITE); mx_stylable_iface_install_property (iface, MX_TYPE_SLIDER, pspec); /* FIXME: have a trough-width property too? */ pspec = g_param_spec_uint ("x-mx-handle-width", "Handle width", "Width of the handle, in px", 0, G_MAXUINT, DEFAULT_HANDLE_WIDTH, G_PARAM_READWRITE); mx_stylable_iface_install_property (iface, MX_TYPE_SLIDER, pspec); pspec = g_param_spec_uint ("x-mx-handle-height", "Handle height", "Height of the handle, in px", 0, G_MAXUINT, DEFAULT_HANDLE_HEIGHT, G_PARAM_READWRITE); mx_stylable_iface_install_property (iface, MX_TYPE_SLIDER, pspec); } } /* * ClutterActor overloading */ static void mx_slider_paint (ClutterActor *actor) { MxSlider *self = MX_SLIDER (actor); MxSliderPrivate *priv = self->priv; CLUTTER_ACTOR_CLASS (mx_slider_parent_class)->paint (actor); clutter_actor_paint (priv->trough_bg); clutter_actor_paint (priv->trough); if (priv->buffer_value) clutter_actor_paint (priv->buffer); if (priv->value) clutter_actor_paint (priv->fill); clutter_actor_paint (priv->handle); } static void mx_slider_pick (ClutterActor *actor, const ClutterColor *pick_color) { MxSlider *self = MX_SLIDER (actor); MxSliderPrivate *priv = self->priv; /* Chaining up won't draw the media bar outline as it's not set reactive * by default */ CLUTTER_ACTOR_CLASS (mx_slider_parent_class)->pick (actor, pick_color); clutter_actor_paint (priv->trough_bg); clutter_actor_paint (priv->handle); } static void mx_slider_get_preferred_width (ClutterActor *actor, gfloat for_height, gfloat *min_width_p, gfloat *nat_width_p) { MxPadding padding; MxSliderPrivate *priv = MX_SLIDER (actor)->priv; mx_widget_get_padding (MX_WIDGET (actor), &padding); /* Set the size of the handle + padding */ if (min_width_p) *min_width_p = priv->handle_width + padding.left + padding.right; /* Set the preferred size as some arbitrary value larger than the handle * width. This ensures the slider can actually move! */ if (nat_width_p) *nat_width_p = (priv->handle_width * 4) + padding.left + padding.right; } static void mx_slider_get_preferred_height (ClutterActor *actor, gfloat for_width, gfloat *min_height_p, gfloat *nat_height_p) { MxPadding padding; MxSliderPrivate *priv = MX_SLIDER (actor)->priv; mx_widget_get_padding (MX_WIDGET (actor), &padding); /* Add on the size of the trough/handle + padding */ if (min_height_p) *min_height_p = MAX (priv->handle_height, priv->trough_height) + padding.top + padding.bottom; if (nat_height_p) *nat_height_p = MAX (priv->handle_height, priv->trough_height) + padding.top + padding.bottom; } static void mx_slider_allocate_fill_handle (MxSlider *self, const ClutterActorBox *box, ClutterAllocationFlags flags) { MxSliderPrivate *priv = self->priv; MxPadding padding; ClutterActorBox bar_box; ClutterActorBox fill_box; ClutterActorBox buffer_box; ClutterActorBox handle_box; guint handle_width_2; if (box == NULL) { clutter_actor_get_allocation_box (CLUTTER_ACTOR (self), &bar_box); box = &bar_box; } mx_widget_get_padding (MX_WIDGET (self), &padding); handle_width_2 = priv->handle_width >> 1; /* fill */ fill_box.x1 = padding.left; fill_box.y1 = priv->trough_box_y1; fill_box.x2 = ((box->x2 - box->x1 - padding.left - padding.right - priv->handle_width) * priv->value) + padding.left + handle_width_2; fill_box.x2 = CLAMP (fill_box.x2, priv->handle_middle_start, priv->handle_middle_end); fill_box.y2 = priv->trough_box_y2; clutter_actor_allocate (priv->fill, &fill_box, flags); /* buffer */ buffer_box.x1 = padding.left; buffer_box.y1 = priv->trough_box_y1; buffer_box.x2 = buffer_box.x1 + ((box->x2 - box->x1 - padding.left - padding.right) * priv->buffer_value); buffer_box.y2 = priv->trough_box_y2; clutter_actor_allocate (priv->buffer, &buffer_box, flags); /* handle */ handle_box.x1 = fill_box.x2 - handle_width_2; handle_box.x2 = handle_box.x1 + priv->handle_width; /* If the handle height is unset, occupy all available space. * Otherwise we want it to be centred in the trough. */ if (priv->handle_height == 0) { handle_box.y1 = padding.top; handle_box.y2 = (box->y2 - box->y1) - padding.bottom; } else { handle_box.y1 = (box->y2 - box->y1 - priv->handle_height) / 2.f; handle_box.y2 = handle_box.y1 + priv->handle_height; } /* snap to pixel */ handle_box.x1 = (int) handle_box.x1; handle_box.y1 = (int) handle_box.y1; handle_box.x2 = (int) handle_box.x2; handle_box.y2 = (int) handle_box.y2; clutter_actor_allocate (priv->handle, &handle_box, flags); } static void mx_slider_allocate (ClutterActor *actor, const ClutterActorBox *box, ClutterAllocationFlags flags) { MxSlider *self = MX_SLIDER (actor); MxSliderPrivate *priv = self->priv; MxPadding padding; ClutterActorClass *actor_class; ClutterActorBox bar_box; ClutterActorBox trough_box; guint handle_width_2; actor_class = CLUTTER_ACTOR_CLASS (mx_slider_parent_class); actor_class->allocate (actor, box, flags); if (box == NULL) { clutter_actor_get_allocation_box (CLUTTER_ACTOR (self), &bar_box); box = &bar_box; } handle_width_2 = priv->handle_width >> 1; mx_widget_get_padding (MX_WIDGET (self), &padding); /* save the min/max position of the middle of the handle */ priv->handle_middle_start = padding.left + handle_width_2 + 1; priv->handle_middle_end = box->x2 - box->x1 - padding.right - handle_width_2 - 1; if (priv->trough_height < 0) { /* trough-height has not been specified, take the whole height */ trough_box.x1 = padding.left; trough_box.y1 = padding.top; trough_box.x2 = (box->x2 - box->x1) - padding.right; trough_box.y2 = (box->y2 - box->y1) - padding.bottom; } else { trough_box.x1 = padding.left; trough_box.y1 = (int) ((box->y2 - box->y1 - padding.bottom - padding.top - priv->trough_height) / 2); trough_box.x2 = (box->x2 - box->x1) - padding.right; trough_box.y2 = trough_box.y1 + priv->trough_height; } clutter_actor_allocate (priv->trough_bg, &trough_box, flags); /* save trough_box.y1 and trough_box.y2 so we don't have the duplicate * the logic above in ::allocate_fill() */ priv->trough_box_y1 = trough_box.y1; priv->trough_box_y2 = trough_box.y2; mx_slider_allocate_fill_handle (self, box, flags); clutter_actor_allocate (priv->trough, &trough_box, flags); } static void mx_slider_map (ClutterActor *actor) { MxSlider *self = MX_SLIDER (actor); MxSliderPrivate *priv = self->priv; CLUTTER_ACTOR_CLASS (mx_slider_parent_class)->map (actor); clutter_actor_map (priv->trough_bg); clutter_actor_map (priv->fill); clutter_actor_map (priv->trough); clutter_actor_map (priv->handle); clutter_actor_map (priv->buffer); } static void mx_slider_unmap (ClutterActor *actor) { MxSlider *self = MX_SLIDER (actor); MxSliderPrivate *priv = self->priv; if (priv->trough_bg) clutter_actor_unmap (priv->trough_bg); if (priv->fill) clutter_actor_unmap (priv->fill); if (priv->trough) clutter_actor_unmap (priv->trough); if (priv->handle) clutter_actor_unmap (priv->handle); if (priv->buffer) clutter_actor_unmap (priv->buffer); CLUTTER_ACTOR_CLASS (mx_slider_parent_class)->unmap (actor); } static void mx_slider_apply_style (MxWidget *widget, MxStyle *style) { MxSliderPrivate *priv = MX_SLIDER (widget)->priv; if (priv->trough_bg != NULL) mx_stylable_set_style (MX_STYLABLE (priv->trough_bg), style); if (priv->fill != NULL) mx_stylable_set_style (MX_STYLABLE (priv->fill), style); if (priv->trough != NULL) mx_stylable_set_style (MX_STYLABLE (priv->trough), style); if (priv->handle != NULL) mx_stylable_set_style (MX_STYLABLE (priv->handle), style); } /* * GObject overloading */ static void mx_slider_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { MxSlider *self = MX_SLIDER (object); switch (property_id) { case PROP_VALUE: g_value_set_double (value, mx_slider_get_value (self)); break; case PROP_BUFFER_VALUE: g_value_set_double (value, mx_slider_get_buffer_value (self)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } } static void mx_slider_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { MxSlider *self = MX_SLIDER (object); switch (property_id) { case PROP_VALUE: mx_slider_set_value (self, g_value_get_double (value)); break; case PROP_BUFFER_VALUE: mx_slider_set_buffer_value (self, g_value_get_double (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } } static void mx_slider_dispose (GObject *object) { MxSlider *self = MX_SLIDER (object); MxSliderPrivate *priv = self->priv; if (priv->capture_handler && priv->trough) { ClutterActor *stage; stage = clutter_actor_get_stage (priv->trough); g_signal_handler_disconnect (stage, priv->capture_handler); priv->capture_handler = 0; } if (priv->trough_bg) { clutter_actor_unparent (priv->trough_bg); priv->trough_bg = NULL; } if (priv->fill) { clutter_actor_unparent (priv->fill); priv->fill = NULL; } /* unparent the handle before the trough, as the handle is parented on the * trough */ if (priv->handle) { clutter_actor_unparent (priv->handle); priv->handle = NULL; } if (priv->trough) { clutter_actor_unparent (priv->trough); priv->trough = NULL; } if (priv->buffer) { clutter_actor_destroy (priv->buffer); priv->buffer = NULL; } G_OBJECT_CLASS (mx_slider_parent_class)->dispose (object); } static gboolean mx_slider_key_press_event (ClutterActor *actor, ClutterKeyEvent *event) { gdouble value; value = mx_slider_get_value (MX_SLIDER (actor)); switch (event->keyval) { case CLUTTER_Left : mx_slider_set_value (MX_SLIDER (actor), MAX (value - 0.1, 0)); return TRUE; case CLUTTER_Right : mx_slider_set_value (MX_SLIDER (actor), MIN (value + 0.1, 1)); return TRUE; default: break; } return FALSE; } static void mx_slider_class_init (MxSliderClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass); MxWidgetClass *widget_class = MX_WIDGET_CLASS (klass); GParamSpec *pspec; g_type_class_add_private (klass, sizeof (MxSliderPrivate)); object_class->get_property = mx_slider_get_property; object_class->set_property = mx_slider_set_property; object_class->dispose = mx_slider_dispose; actor_class->paint = mx_slider_paint; actor_class->pick = mx_slider_pick; actor_class->get_preferred_width = mx_slider_get_preferred_width; actor_class->get_preferred_height = mx_slider_get_preferred_height; actor_class->allocate = mx_slider_allocate; actor_class->map = mx_slider_map; actor_class->unmap = mx_slider_unmap; actor_class->key_press_event = mx_slider_key_press_event; widget_class->apply_style = mx_slider_apply_style; pspec = g_param_spec_double ("value", "Value", "Value", 0.0, 1.0, 0.0, G_PARAM_READWRITE); g_object_class_install_property (object_class, PROP_VALUE, pspec); pspec = g_param_spec_double ("buffer-value", "Buffer Value", "Buffer Value", 0.0, 1.0, 0.0, G_PARAM_READWRITE); g_object_class_install_property (object_class, PROP_BUFFER_VALUE, pspec); } static void mx_slider_style_changed_cb (MxSlider *self) { gboolean relayout; gint trough_height; guint handle_width, handle_height; MxSliderPrivate *priv = self->priv; mx_stylable_get (MX_STYLABLE (self), "x-mx-trough-height", &trough_height, "x-mx-handle-width", &handle_width, "x-mx-handle-height", &handle_height, NULL); relayout = FALSE; if (priv->trough_height != trough_height) { priv->trough_height = trough_height; relayout = TRUE; } if (priv->handle_width != handle_width) { priv->handle_width = handle_width; relayout = TRUE; } if (priv->handle_height != handle_height) { priv->handle_height = handle_height; relayout = TRUE; } /* invalidate the style cache of the internal children */ mx_stylable_style_changed (MX_STYLABLE (priv->trough_bg), MX_STYLE_CHANGED_INVALIDATE_CACHE); mx_stylable_style_changed (MX_STYLABLE (priv->fill), MX_STYLE_CHANGED_INVALIDATE_CACHE); mx_stylable_style_changed (MX_STYLABLE (priv->trough), MX_STYLE_CHANGED_INVALIDATE_CACHE); mx_stylable_style_changed (MX_STYLABLE (priv->handle), MX_STYLE_CHANGED_INVALIDATE_CACHE); mx_stylable_style_changed (MX_STYLABLE (priv->buffer), MX_STYLE_CHANGED_INVALIDATE_CACHE); if (relayout) clutter_actor_queue_relayout (CLUTTER_ACTOR (self)); } static void mx_slider_disable_notify_cb (MxSlider *slider) { MxSliderPrivate *priv = slider->priv; gboolean disabled; disabled = mx_widget_get_disabled (MX_WIDGET (slider)); /* update the disabled state of internal children */ mx_widget_set_disabled (MX_WIDGET (priv->trough_bg), disabled); mx_widget_set_disabled (MX_WIDGET (priv->fill), disabled); mx_widget_set_disabled (MX_WIDGET (priv->trough), disabled); mx_widget_set_disabled (MX_WIDGET (priv->handle), disabled); mx_widget_set_disabled (MX_WIDGET (priv->buffer), disabled); } static void mx_slider_init (MxSlider *self) { MxSliderPrivate *priv; self->priv = priv = SLIDER_PRIVATE (self); g_signal_connect (self, "style-changed", G_CALLBACK (mx_slider_style_changed_cb), NULL); priv->trough_bg = CLUTTER_ACTOR (_mx_progress_bar_fill_new ()); clutter_actor_set_name (priv->trough_bg, "trough-background"); clutter_actor_set_reactive (priv->trough_bg, TRUE); clutter_actor_set_parent (priv->trough_bg, CLUTTER_ACTOR (self)); g_signal_connect (priv->trough_bg, "button-press-event", G_CALLBACK (on_trough_bg_button_press_event), self); g_signal_connect (priv->trough_bg, "button-release-event", G_CALLBACK (on_trough_bg_button_release_event), self); g_signal_connect (priv->trough_bg, "leave-event", G_CALLBACK (on_trough_bg_leave_event), self); priv->fill = CLUTTER_ACTOR (_mx_progress_bar_fill_new ()); clutter_actor_set_name (priv->fill, "fill"); clutter_actor_set_parent (priv->fill, CLUTTER_ACTOR (self)); priv->trough = CLUTTER_ACTOR (mx_frame_new ()); clutter_actor_set_name (priv->trough, "trough"); clutter_actor_set_parent (priv->trough, CLUTTER_ACTOR (self)); self->priv->handle = CLUTTER_ACTOR (mx_button_new ()); clutter_actor_set_name (priv->handle, "handle"); clutter_actor_set_parent (priv->handle, CLUTTER_ACTOR (self)); g_signal_connect (priv->handle, "button-press-event", G_CALLBACK (on_handle_button_press_event), self); priv->buffer = _mx_progress_bar_fill_new (); clutter_actor_set_name (priv->buffer, "buffer"); clutter_actor_set_parent (priv->buffer, CLUTTER_ACTOR (self)); g_signal_connect (self, "notify::disabled", G_CALLBACK (mx_slider_disable_notify_cb), NULL); } /** * mx_slider_new: * * Create a new slider * * Returns: a new #MxSlider */ ClutterActor * mx_slider_new (void) { return g_object_new (MX_TYPE_SLIDER, NULL); } /** * mx_slider_set_value: * @bar: A #MxSlider * @value: A value between 0.0 and 1.0 * * Set the value of the slider */ void mx_slider_set_value (MxSlider *bar, gdouble value) { MxSliderPrivate *priv = bar->priv; g_return_if_fail (MX_IS_SLIDER (bar)); if (priv->value == value) return; if (G_UNLIKELY ((value < 0.0) || (value > 1.0))) { g_warning ("MxSlider:value must be a number between 0.0 and 1.0"); return; } priv->value = value; if (!priv->capture_handler) { mx_slider_allocate_fill_handle (bar, NULL, 0); clutter_actor_queue_redraw (CLUTTER_ACTOR (bar)); } g_object_notify (G_OBJECT (bar), "value"); } /** * mx_slider_get_value: * @bar: A #MxSlider * * Retrieve the current value of the media bar * * Returns: gdouble */ gdouble mx_slider_get_value (MxSlider *bar) { g_return_val_if_fail (MX_IS_SLIDER (bar), 0.0); return bar->priv->value; } /** * mx_slider_set_buffer_value: * @slider: A #MxSlider * @value: the new buffer value of the slider * * Set the value of the #MxSlider:buffer-value property. * * Since: 1.2 */ void mx_slider_set_buffer_value (MxSlider *slider, gdouble value) { MxSliderPrivate *priv; g_return_if_fail (MX_IS_SLIDER (slider)); g_return_if_fail (value >= 0.0 && value <= 1.0); priv = slider->priv; if (priv->buffer_value == value) return; priv->buffer_value = value; clutter_actor_queue_relayout (CLUTTER_ACTOR (slider)); g_object_notify (G_OBJECT (slider), "buffer-value"); } /** * mx_slider_get_buffer_value: * @slider: A #MxSlider * * Get the value of the #MxSlider:buffer-value property. * * Returns: The current value of the "buffer-value" property. * * Since: 1.2 */ gdouble mx_slider_get_buffer_value (MxSlider *slider) { g_return_val_if_fail (MX_IS_SLIDER (slider), 0.0); return slider->priv->buffer_value; } mx-1.4.7/mx/mx-slider.h000066400000000000000000000051521201047117600146520ustar00rootroot00000000000000/* * mx-slider.c: Slider widget * * Copyright 2009 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * * Written by: Damien Lespiau * */ #if !defined(MX_H_INSIDE) && !defined(MX_COMPILATION) #error "Only can be included directly.h" #endif #ifndef __MX_SLIDER_H__ #define __MX_SLIDER_H__ #include #include G_BEGIN_DECLS #define MX_TYPE_SLIDER mx_slider_get_type() #define MX_SLIDER(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), MX_TYPE_SLIDER, MxSlider)) #define MX_SLIDER_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), MX_TYPE_SLIDER, MxSliderClass)) #define MX_IS_SLIDER(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MX_TYPE_SLIDER)) #define MX_IS_SLIDER_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE ((klass), MX_TYPE_SLIDER)) #define MX_SLIDER_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), MX_TYPE_SLIDER, MxSliderClass)) typedef struct _MxSlider MxSlider; typedef struct _MxSliderClass MxSliderClass; typedef struct _MxSliderPrivate MxSliderPrivate; /** * MxSlider: * * The contents of this structure are private and should only be accessed * through the public API. */ struct _MxSlider { /*< private >*/ MxWidget parent; MxSliderPrivate *priv; }; struct _MxSliderClass { MxWidgetClass parent_class; /* padding for future expansion */ void (*_padding_0) (void); void (*_padding_1) (void); void (*_padding_2) (void); void (*_padding_3) (void); void (*_padding_4) (void); }; GType mx_slider_get_type (void) G_GNUC_CONST; ClutterActor * mx_slider_new (void); void mx_slider_set_value (MxSlider *bar, gdouble value); gdouble mx_slider_get_value (MxSlider *bar); void mx_slider_set_buffer_value (MxSlider *slider, gdouble value); gdouble mx_slider_get_buffer_value (MxSlider *slider); G_END_DECLS #endif /* __MX_SLIDER_H__ */ mx-1.4.7/mx/mx-spinner.c000066400000000000000000000323321201047117600150410ustar00rootroot00000000000000/* * mx-spinner.c: a loading indicator widget * * Copyright 2010 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * * Written by: Chris Lord * */ /** * SECTION:mx-spinner * @short_description: a processing indicator widget * * The #MxSpinner is a widget to use to indicate that something is being * processed, usually a task of indeterminate length. */ #include "mx-spinner.h" #include "mx-marshal.h" #include "mx-private.h" #include "mx-stylable.h" static void mx_stylable_iface_init (MxStylableIface *iface); G_DEFINE_TYPE_WITH_CODE (MxSpinner, mx_spinner, MX_TYPE_WIDGET, G_IMPLEMENT_INTERFACE (MX_TYPE_STYLABLE, mx_stylable_iface_init)) #define SPINNER_PRIVATE(o) \ (G_TYPE_INSTANCE_GET_PRIVATE ((o), MX_TYPE_SPINNER, MxSpinnerPrivate)) enum { LOOPED, LAST_SIGNAL }; enum { PROP_0, PROP_ANIMATING }; struct _MxSpinnerPrivate { CoglHandle texture; CoglHandle material; guint frames; guint anim_duration; guint current_frame; guint update_id; guint animating : 1; }; static guint signals[LAST_SIGNAL] = { 0, }; static void mx_spinner_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { MxSpinner *spinner = MX_SPINNER (object); switch (property_id) { case PROP_ANIMATING: g_value_set_boolean (value, mx_spinner_get_animating (spinner)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } } static void mx_spinner_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { MxSpinner *spinner = MX_SPINNER (object); switch (property_id) { case PROP_ANIMATING: mx_spinner_set_animating (spinner, g_value_get_boolean (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } } static void mx_spinner_dispose (GObject *object) { MxSpinnerPrivate *priv = MX_SPINNER (object)->priv; if (priv->update_id) { g_source_remove (priv->update_id); priv->update_id = 0; } if (priv->material) { cogl_handle_unref (priv->material); priv->material = COGL_INVALID_HANDLE; } G_OBJECT_CLASS (mx_spinner_parent_class)->dispose (object); } static void mx_spinner_finalize (GObject *object) { G_OBJECT_CLASS (mx_spinner_parent_class)->finalize (object); } static void mx_spinner_get_preferred_width (ClutterActor *actor, gfloat for_height, gfloat *min_width_p, gfloat *nat_width_p) { guint min_width, width, height; MxPadding padding; MxSpinnerPrivate *priv = MX_SPINNER (actor)->priv; mx_widget_get_padding (MX_WIDGET (actor), &padding); if (priv->material != COGL_INVALID_HANDLE) { width = cogl_texture_get_width (priv->texture) / priv->frames; height = cogl_texture_get_height (priv->texture); min_width = width; if (for_height >= 0 && for_height < height) { for_height = MAX (0, for_height - padding.top - padding.bottom); width = (guint)((gfloat)width * (for_height / (gfloat)height)); } } else min_width = width = 0; width += padding.left + padding.right; if (min_width_p) *min_width_p = MIN (min_width, width); if (nat_width_p) *nat_width_p = width; } static void mx_spinner_get_preferred_height (ClutterActor *actor, gfloat for_width, gfloat *min_height_p, gfloat *nat_height_p) { guint min_height, height, width; MxPadding padding; MxSpinnerPrivate *priv = MX_SPINNER (actor)->priv; mx_widget_get_padding (MX_WIDGET (actor), &padding); if (priv->material != COGL_INVALID_HANDLE) { height = cogl_texture_get_height (priv->texture); width = cogl_texture_get_width (priv->texture); min_height = height; if (for_width >= 0 && for_width < width) { width = cogl_texture_get_width (priv->texture) / priv->frames; for_width = MAX (0, for_width - padding.left - padding.right); height = (guint)((gfloat)height * (for_width / (gfloat)width)); } } else min_height = height = 0; height += padding.top + padding.bottom; if (min_height_p) *min_height_p = MIN (min_height, height); if (nat_height_p) *nat_height_p = height; } static void mx_spinner_paint (ClutterActor *actor) { guint8 opacity; MxPadding padding; gfloat width, height; MxSpinnerPrivate *priv = MX_SPINNER (actor)->priv; /* Chain up for background */ CLUTTER_ACTOR_CLASS (mx_spinner_parent_class)->paint (actor); if (priv->material == COGL_INVALID_HANDLE) return; mx_widget_get_padding (MX_WIDGET (actor), &padding); clutter_actor_get_size (actor, &width, &height); opacity = clutter_actor_get_paint_opacity (actor); cogl_material_set_color4ub (priv->material, opacity, opacity, opacity, opacity); cogl_set_source (priv->material); cogl_rectangle_with_texture_coords (padding.left, padding.top, width - padding.right, height - padding.bottom, priv->current_frame / (gfloat)priv->frames, 0, (priv->current_frame + 1) / (gfloat)priv->frames, 1); } static void mx_stylable_iface_init (MxStylableIface *iface) { static gboolean is_initialized = FALSE; if (G_UNLIKELY (!is_initialized)) { GParamSpec *pspec; is_initialized = TRUE; pspec = g_param_spec_boxed ("x-mx-spinner-image", "Spinner image", "Image containing the frames to use for " "the spinner animation", MX_TYPE_BORDER_IMAGE, MX_PARAM_READWRITE); mx_stylable_iface_install_property (iface, MX_TYPE_SPINNER, pspec); pspec = g_param_spec_uint ("x-mx-spinner-frames", "Spinner frames", "Number of frames contained in the spinner " "image, horizontally.", 1, G_MAXUINT, 1, MX_PARAM_READWRITE); mx_stylable_iface_install_property (iface, MX_TYPE_SPINNER, pspec); pspec = g_param_spec_uint ("x-mx-spinner-animation-duration", "Spinner animation duration", "Duration of the entire spinner animation, " "in milliseconds.", 1, G_MAXUINT, 500, MX_PARAM_READWRITE); mx_stylable_iface_install_property (iface, MX_TYPE_SPINNER, pspec); } } static void mx_spinner_class_init (MxSpinnerClass *klass) { GParamSpec *pspec; GObjectClass *object_class = G_OBJECT_CLASS (klass); ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass); g_type_class_add_private (klass, sizeof (MxSpinnerPrivate)); object_class->get_property = mx_spinner_get_property; object_class->set_property = mx_spinner_set_property; object_class->dispose = mx_spinner_dispose; object_class->finalize = mx_spinner_finalize; actor_class->get_preferred_width = mx_spinner_get_preferred_width; actor_class->get_preferred_height = mx_spinner_get_preferred_height; actor_class->paint = mx_spinner_paint; pspec = g_param_spec_boolean ("animating", "Animating", "Whether the spinner is animating.", TRUE, MX_PARAM_READWRITE); g_object_class_install_property (object_class, PROP_ANIMATING, pspec); /** * MxSpinner::looped: * @spinner: the #MxSpinner that received the signal * * Emitted after the animation has displayed the final frame. * * Since: 1.2 */ signals[LOOPED] = g_signal_new ("looped", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (MxSpinnerClass, looped), NULL, NULL, _mx_marshal_VOID__VOID, G_TYPE_NONE, 0); } static gboolean mx_spinner_timeout_cb (MxSpinner *spinner) { MxSpinnerPrivate *priv = spinner->priv; /* We may be destroyed during the signal emission, so * queue the redraw here instead of below. */ clutter_actor_queue_redraw (CLUTTER_ACTOR (spinner)); if (++priv->current_frame == priv->frames) { priv->current_frame = 0; g_signal_emit (spinner, signals[LOOPED], 0); } return TRUE; } static void mx_spinner_update_timeout (MxSpinner *spinner) { MxSpinnerPrivate *priv = spinner->priv; if (priv->update_id) { g_source_remove (priv->update_id); priv->update_id = 0; } if (priv->animating && priv->frames && priv->material) priv->update_id = clutter_threads_add_timeout_full (CLUTTER_PRIORITY_REDRAW, MAX (1, priv->anim_duration / priv->frames), (GSourceFunc) mx_spinner_timeout_cb, spinner, NULL); else priv->current_frame = 0; } static void mx_spinner_style_changed_cb (MxStylable *stylable, MxStyleChangedFlags flags) { MxBorderImage *image; guint frames, anim_duration; MxSpinner *spinner = MX_SPINNER (stylable); MxSpinnerPrivate *priv = spinner->priv; mx_stylable_get (stylable, "x-mx-spinner-image", &image, "x-mx-spinner-frames", &frames, "x-mx-spinner-animation-duration", &anim_duration, NULL); if (priv->material) { cogl_handle_unref (priv->material); priv->material = NULL; } priv->anim_duration = anim_duration; priv->frames = frames; priv->current_frame = 0; if (image) { MxTextureCache *cache = mx_texture_cache_get_default (); priv->texture = mx_texture_cache_get_cogl_texture (cache, image->uri); g_boxed_free (MX_TYPE_BORDER_IMAGE, image); priv->material = cogl_material_new (); cogl_material_set_layer (priv->material, 0, priv->texture); cogl_handle_unref (priv->texture); } mx_spinner_update_timeout (spinner); clutter_actor_queue_relayout (CLUTTER_ACTOR (stylable)); } static void mx_spinner_init (MxSpinner *self) { MxSpinnerPrivate *priv = self->priv = SPINNER_PRIVATE (self); priv->anim_duration = 500; priv->frames = 1; priv->animating = TRUE; g_signal_connect (self, "style-changed", G_CALLBACK (mx_spinner_style_changed_cb), NULL); } /** * mx_spinner_new: * * Create a new #MxSpinner widget. * * Returns: a newly allocated #MxSpinner * * Since: 1.2 */ ClutterActor * mx_spinner_new (void) { return g_object_new (MX_TYPE_SPINNER, NULL); } /** * mx_spinner_get_animating: * @spinner: A #MxSpinner widget * * Determines whether the spinner is animating. * * Returns: %TRUE if the spinner is animating, %FALSE otherwise * * Since: 1.2 */ gboolean mx_spinner_get_animating (MxSpinner *spinner) { g_return_val_if_fail (MX_IS_SPINNER (spinner), FALSE); return spinner->priv->animating; } /** * mx_spinner_set_animating: * @spinner: A #MxSpinner widget * @animating: %TRUE to enable animation, %FALSE to disable * * Sets whether the spinner is animating. A spinner can be stopped if * the task it represents has finished, or to save energy. * * Since: 1.2 */ void mx_spinner_set_animating (MxSpinner *spinner, gboolean animating) { MxSpinnerPrivate *priv; g_return_if_fail (MX_IS_SPINNER (spinner)); priv = spinner->priv; if (priv->animating != animating) { priv->animating = animating; mx_spinner_update_timeout (spinner); g_object_notify (G_OBJECT (spinner), "animating"); } } mx-1.4.7/mx/mx-spinner.h000066400000000000000000000046711201047117600150530ustar00rootroot00000000000000/* * mx-spinner.h: a loading indicator widget * * Copyright 2010 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * * Written by: Chris Lord * */ #if !defined(MX_H_INSIDE) && !defined(MX_COMPILATION) #error "Only can be included directly.h" #endif #ifndef _MX_SPINNER_H #define _MX_SPINNER_H #include #include G_BEGIN_DECLS #define MX_TYPE_SPINNER mx_spinner_get_type() #define MX_SPINNER(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ MX_TYPE_SPINNER, MxSpinner)) #define MX_SPINNER_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), \ MX_TYPE_SPINNER, MxSpinnerClass)) #define MX_IS_SPINNER(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \ MX_TYPE_SPINNER)) #define MX_IS_SPINNER_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE ((klass), \ MX_TYPE_SPINNER)) #define MX_SPINNER_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), \ MX_TYPE_SPINNER, MxSpinnerClass)) typedef struct _MxSpinner MxSpinner; typedef struct _MxSpinnerClass MxSpinnerClass; typedef struct _MxSpinnerPrivate MxSpinnerPrivate; /** * MxSpinner: * * The contents of this structure are private and should only be accessed * through the public API. */ struct _MxSpinner { /*< private >*/ MxWidget parent; MxSpinnerPrivate *priv; }; struct _MxSpinnerClass { MxWidgetClass parent_class; /* signals */ void (* looped) (MxSpinner *spinner); /* padding for future expansion */ void (*_padding_0) (void); void (*_padding_1) (void); void (*_padding_2) (void); void (*_padding_3) (void); }; GType mx_spinner_get_type (void) G_GNUC_CONST; ClutterActor *mx_spinner_new (void); void mx_spinner_set_animating (MxSpinner *spinner, gboolean animating); gboolean mx_spinner_get_animating (MxSpinner *spinner); G_END_DECLS #endif /* _MX_SPINNER_H */ mx-1.4.7/mx/mx-stack-child.c000066400000000000000000000321061201047117600155500ustar00rootroot00000000000000/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * mx-stack-child.c: stack child actor * * Copyright 2010 Intel Corporation * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * * Written by: Thomas Wood * Chris Lord */ /** * SECTION:mx-stack-child * @short_description: meta data associated with a #MxStack child. * * #MxStackChild is a #ClutterChildMeta implementation that stores the * child properties for children inside a #MxStack. * * Since: 1.2 */ #include "mx-stack-child.h" #include "mx-private.h" G_DEFINE_TYPE (MxStackChild, mx_stack_child, CLUTTER_TYPE_CHILD_META) #define STACK_CHILD_PRIVATE(o) \ (G_TYPE_INSTANCE_GET_PRIVATE ((o), MX_TYPE_STACK_CHILD, MxStackChildPrivate)) enum { PROP_0, PROP_X_FILL, PROP_Y_FILL, PROP_X_ALIGN, PROP_Y_ALIGN, PROP_FIT, PROP_CROP }; static void mx_stack_child_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { MxStackChild *child = MX_STACK_CHILD (object); switch (property_id) { case PROP_X_FILL: g_value_set_boolean (value, child->x_fill); break; case PROP_Y_FILL: g_value_set_boolean (value, child->y_fill); break; case PROP_X_ALIGN: g_value_set_enum (value, child->x_align); break; case PROP_Y_ALIGN: g_value_set_enum (value, child->y_align); break; case PROP_FIT: g_value_set_boolean (value, child->fit); break; case PROP_CROP: g_value_set_boolean (value, child->crop); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } } static void mx_stack_child_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { MxStackChild *child = MX_STACK_CHILD (object); ClutterActor *stack = CLUTTER_ACTOR (CLUTTER_CHILD_META (object)->container); switch (property_id) { case PROP_X_FILL: child->x_fill = g_value_get_boolean (value); break; case PROP_Y_FILL: child->y_fill = g_value_get_boolean (value); break; case PROP_X_ALIGN: child->x_align = g_value_get_enum (value); break; case PROP_Y_ALIGN: child->y_align = g_value_get_enum (value); break; case PROP_FIT: child->fit = g_value_get_boolean (value); break; case PROP_CROP: child->crop = g_value_get_boolean (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } clutter_actor_queue_relayout (stack); } static void mx_stack_child_class_init (MxStackChildClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); GParamSpec *pspec; object_class->get_property = mx_stack_child_get_property; object_class->set_property = mx_stack_child_set_property; pspec = g_param_spec_boolean ("x-fill", "x-fill", "Whether the child should receive priority " "when the container is allocating spare space " "on the horizontal axis", TRUE, MX_PARAM_READWRITE); g_object_class_install_property (object_class, PROP_X_FILL, pspec); pspec = g_param_spec_boolean ("y-fill", "y-fill", "Whether the child should receive priority " "when the container is allocating spare space " "on the vertical axis", TRUE, MX_PARAM_READWRITE); g_object_class_install_property (object_class, PROP_Y_FILL, pspec); pspec = g_param_spec_enum ("x-align", "X Alignment", "X alignment of the widget within the cell", MX_TYPE_ALIGN, MX_ALIGN_MIDDLE, MX_PARAM_READWRITE); g_object_class_install_property (object_class, PROP_X_ALIGN, pspec); pspec = g_param_spec_enum ("y-align", "Y Alignment", "Y alignment of the widget within the cell", MX_TYPE_ALIGN, MX_ALIGN_MIDDLE, MX_PARAM_READWRITE); g_object_class_install_property (object_class, PROP_Y_ALIGN, pspec); pspec = g_param_spec_boolean ("fit", "Fit", "Attempt to fit the actor into the available" " space while respecting the actor's" " width-for-height or height-for-width" " constraints. The fill properties are ignored" " when this property is enabled.", FALSE, MX_PARAM_READWRITE); g_object_class_install_property (object_class, PROP_FIT, pspec); pspec = g_param_spec_boolean ("crop", "Fill space", "Attempt to fill the parent's space by scaling" " the child and keeping its aspect ratio as" " well. The fit and fill properties are" " ignored when this property is enabled.", FALSE, MX_PARAM_READWRITE); g_object_class_install_property (object_class, PROP_CROP, pspec); } static void mx_stack_child_init (MxStackChild *self) { self->x_fill = TRUE; self->y_fill = TRUE; self->x_align = MX_ALIGN_MIDDLE; self->y_align = MX_ALIGN_MIDDLE; } static MxStackChild * _get_child_meta (MxStack *stack, ClutterActor *child) { MxStackChild *meta; meta = (MxStackChild*) clutter_container_get_child_meta (CLUTTER_CONTAINER (stack), child); return meta; } /** * mx_stack_child_get_x_fill: * @stack: A #MxStack * @child: A #ClutterActor * * Get the value of the #MxStackChild:x-fill property. * * Returns: the current value of the "x-fill" property. * * Since: 1.2 */ gboolean mx_stack_child_get_x_fill (MxStack *stack, ClutterActor *child) { MxStackChild *meta; g_return_val_if_fail (MX_IS_STACK (stack), FALSE); g_return_val_if_fail (CLUTTER_IS_ACTOR (child), FALSE); meta = _get_child_meta (stack, child); return meta->x_fill; } /** * mx_stack_child_set_x_fill: * @stack: A #MxStack * @child: A #ClutterActor * @x_fill: A #gboolean * * Set the value of the #MxStackChild:x-fill property. * * Since: 1.2 */ void mx_stack_child_set_x_fill (MxStack *stack, ClutterActor *child, gboolean x_fill) { MxStackChild *meta; g_return_if_fail (MX_IS_STACK (stack)); g_return_if_fail (CLUTTER_IS_ACTOR (child)); meta = _get_child_meta (stack, child); meta->x_fill = x_fill; clutter_actor_queue_relayout (child); } /** * mx_stack_child_get_y_fill: * @stack: An #MxStack * @child: A #ClutterActor * * Get the value of the #MxStackChild:y-fill property * * Returns: the current value of the "y-fill" property * * Since: 1.2 */ gboolean mx_stack_child_get_y_fill (MxStack *stack, ClutterActor *child) { MxStackChild *meta; g_return_val_if_fail (MX_IS_STACK (stack), FALSE); g_return_val_if_fail (CLUTTER_IS_ACTOR (child), FALSE); meta = _get_child_meta (stack, child); return meta->y_fill; } /** * mx_stack_child_set_y_fill: * @stack: An #MxStack * @child: A #ClutterActor * @y_fill: A #gboolean * * Set the value of the #MxStackChild:y-fill property. * * Since: 1.2 */ void mx_stack_child_set_y_fill (MxStack *stack, ClutterActor *child, gboolean y_fill) { MxStackChild *meta; g_return_if_fail (MX_IS_STACK (stack)); g_return_if_fail (CLUTTER_IS_ACTOR (child)); meta = _get_child_meta (stack, child); meta->y_fill = y_fill; clutter_actor_queue_relayout (child); } /** * mx_stack_child_get_x_align: * @stack: An #MxStack * @child: A #ClutterActor * * Get the value of the #MxStackChild:x-align property * * Returns: the current value of the "x-align" property * * Since: 1.2 */ MxAlign mx_stack_child_get_x_align (MxStack *stack, ClutterActor *child) { MxStackChild *meta; g_return_val_if_fail (MX_IS_STACK (stack), MX_ALIGN_START); g_return_val_if_fail (CLUTTER_IS_ACTOR (child), MX_ALIGN_START); meta = _get_child_meta (stack, child); return meta->x_align; } /** * mx_stack_child_set_x_align: * @stack: A #MxStack * @child: A #ClutterActor * @x_align: An #MxAlign * * Set the value of the #MxStackChild:x-align property. * * Since: 1.2 */ void mx_stack_child_set_x_align (MxStack *stack, ClutterActor *child, MxAlign x_align) { MxStackChild *meta; g_return_if_fail (MX_IS_STACK (stack)); g_return_if_fail (CLUTTER_IS_ACTOR (child)); meta = _get_child_meta (stack, child); meta->x_align = x_align; clutter_actor_queue_relayout (child); } /** * mx_stack_child_get_y_align: * @stack: An #MxStack * @child: A #ClutterActor * * Get the value of the #MxStackChild:y-align property. * * Returns: the current value of the "y-align" property. * * Since: 1.2 */ MxAlign mx_stack_child_get_y_align (MxStack *stack, ClutterActor *child) { MxStackChild *meta; g_return_val_if_fail (MX_IS_STACK (stack), MX_ALIGN_START); g_return_val_if_fail (CLUTTER_IS_ACTOR (child), MX_ALIGN_START); meta = _get_child_meta (stack, child); return meta->y_align; } /** * mx_stack_child_set_y_align: * @stack: An #MxStack * @child: A #ClutterActor * @y_align: An #MxAlign * * Set the value of the #MxStackChild:y-align property. * * Since: 1.2 */ void mx_stack_child_set_y_align (MxStack *stack, ClutterActor *child, MxAlign y_align) { MxStackChild *meta; g_return_if_fail (MX_IS_STACK (stack)); g_return_if_fail (CLUTTER_IS_ACTOR (child)); meta = _get_child_meta (stack, child); meta->y_align = y_align; clutter_actor_queue_relayout (child); } /** * mx_stack_child_get_fit: * @stack: An #MxStack * @child: A #ClutterActor * * Get the value of the #MxStackChild:fit property. * * Returns: the current value of the #MxStackChild:fit property * * Since: 1.2 */ gboolean mx_stack_child_get_fit (MxStack *stack, ClutterActor *child) { MxStackChild *meta; g_return_val_if_fail (MX_IS_STACK (stack), FALSE); g_return_val_if_fail (CLUTTER_IS_ACTOR (child), FALSE); meta = _get_child_meta (stack, child); return meta->fit; } /** * mx_stack_child_set_fit: * @stack: An #MxStack * @child: A #ClutterActor * @fit: A #gboolean * * Set the value of the #MxStackChild:fit property. * * Since: 1.2 */ void mx_stack_child_set_fit (MxStack *stack, ClutterActor *child, gboolean fit) { MxStackChild *meta; g_return_if_fail (MX_IS_STACK (stack)); g_return_if_fail (CLUTTER_IS_ACTOR (child)); meta = _get_child_meta (stack, child); meta->fit = fit; clutter_actor_queue_relayout (child); } /** * mx_stack_child_get_crop: * @stack: An #MxStack * @child: A #ClutterActor * * Get the value of the #MxStackChild:fit property. * * Returns: the current value of the #MxStackChild:crop property * * Since: 1.4 */ gboolean mx_stack_child_get_crop (MxStack *stack, ClutterActor *child) { MxStackChild *meta; g_return_val_if_fail (MX_IS_STACK (stack), FALSE); g_return_val_if_fail (CLUTTER_IS_ACTOR (child), FALSE); meta = _get_child_meta (stack, child); return meta->crop; } /** * mx_stack_child_set_crop: * @stack: An #MxStack * @child: A #ClutterActor * @crop: A #gboolean * * Set the value of the #MxStackChild:crop property. * * Since: 1.4 */ void mx_stack_child_set_crop (MxStack *stack, ClutterActor *child, gboolean crop) { MxStackChild *meta; g_return_if_fail (MX_IS_STACK (stack)); g_return_if_fail (CLUTTER_IS_ACTOR (child)); meta = _get_child_meta (stack, child); meta->crop = crop; clutter_actor_queue_relayout (child); } mx-1.4.7/mx/mx-stack-child.h000066400000000000000000000104441201047117600155560ustar00rootroot00000000000000/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * mx-stack-child.h: stack child actor * * Copyright 2010 Intel Corporation * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * * Written by: Thomas Wood * Chris Lord */ #if !defined(MX_H_INSIDE) && !defined(MX_COMPILATION) #error "Only can be included directly.h" #endif #ifndef _MX_STACK_CHILD_H #define _MX_STACK_CHILD_H #include #include "mx-enum-types.h" #include "mx-stack.h" G_BEGIN_DECLS #define MX_TYPE_STACK_CHILD mx_stack_child_get_type() #define MX_STACK_CHILD(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ MX_TYPE_STACK_CHILD, MxStackChild)) #define MX_STACK_CHILD_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), \ MX_TYPE_STACK_CHILD, MxStackChildClass)) #define MX_IS_STACK_CHILD(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \ MX_TYPE_STACK_CHILD)) #define MX_IS_STACK_CHILD_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE ((klass), \ MX_TYPE_STACK_CHILD)) #define MX_STACK_CHILD_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), \ MX_TYPE_STACK_CHILD, MxStackChildClass)) typedef struct _MxStackChild MxStackChild; typedef struct _MxStackChildClass MxStackChildClass; typedef struct _MxStackChildPrivate MxStackChildPrivate; /** * MxStackChild: * * The contents of this structure are private and should only be accessed * through the public API. */ struct _MxStackChild { /*< private >*/ ClutterChildMeta parent; guint x_fill : 1; guint y_fill : 1; guint fit : 1; guint crop : 1; MxAlign x_align; MxAlign y_align; }; struct _MxStackChildClass { ClutterChildMetaClass parent_class; /* padding for future expansion */ void (*_padding_0) (void); void (*_padding_1) (void); void (*_padding_2) (void); void (*_padding_3) (void); void (*_padding_4) (void); }; GType mx_stack_child_get_type (void); gboolean mx_stack_child_get_x_fill (MxStack *stack, ClutterActor *child); void mx_stack_child_set_x_fill (MxStack *stack, ClutterActor *child, gboolean x_fill); gboolean mx_stack_child_get_y_fill (MxStack *stack, ClutterActor *child); void mx_stack_child_set_y_fill (MxStack *stack, ClutterActor *child, gboolean y_fill); MxAlign mx_stack_child_get_x_align (MxStack *stack, ClutterActor *child); void mx_stack_child_set_x_align (MxStack *stack, ClutterActor *child, MxAlign x_align); MxAlign mx_stack_child_get_y_align (MxStack *stack, ClutterActor *child); void mx_stack_child_set_y_align (MxStack *stack, ClutterActor *child, MxAlign y_align); gboolean mx_stack_child_get_fit (MxStack *stack, ClutterActor *child); void mx_stack_child_set_fit (MxStack *stack, ClutterActor *child, gboolean fit); gboolean mx_stack_child_get_crop (MxStack *stack, ClutterActor *child); void mx_stack_child_set_crop (MxStack *stack, ClutterActor *child, gboolean crop); G_END_DECLS #endif /* _MX_STACK_CHILD_H */ mx-1.4.7/mx/mx-stack.c000066400000000000000000000515021201047117600144700ustar00rootroot00000000000000/* * mx-stack.c: A container that allows the stacking of multiple widgets * * Copyright 2010 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * Boston, MA 02111-1307, USA. * * Written by: Chris Lord * */ /** * SECTION:mx-stack * @short_description: A container allow stacking of children over each other * * The #MxStack arranges its children in a stack, where each child can be * allocated its preferred size or larger, if the fill option is set. If * the fill option isn't set, a child's position will be determined by its * alignment properties. * * Since: 1.2 */ #include "mx-stack.h" #include "mx-stack-child.h" #include "mx-focusable.h" #include "mx-utils.h" #include static void clutter_container_iface_init (ClutterContainerIface *iface); static void mx_focusable_iface_init (MxFocusableIface *iface); G_DEFINE_TYPE_WITH_CODE (MxStack, mx_stack, MX_TYPE_WIDGET, G_IMPLEMENT_INTERFACE (CLUTTER_TYPE_CONTAINER, clutter_container_iface_init) G_IMPLEMENT_INTERFACE (MX_TYPE_FOCUSABLE, mx_focusable_iface_init)) #define STACK_PRIVATE(o) \ (G_TYPE_INSTANCE_GET_PRIVATE ((o), MX_TYPE_STACK, MxStackPrivate)) struct _MxStackPrivate { GList *children; ClutterActor *current_focus; ClutterActorBox allocation; }; /* ClutterContainerIface */ static void mx_stack_add_actor (ClutterContainer *container, ClutterActor *actor) { MxStackPrivate *priv = MX_STACK (container)->priv; clutter_actor_set_parent (actor, CLUTTER_ACTOR (container)); priv->children = g_list_append (priv->children, actor); g_signal_emit_by_name (container, "actor-added", actor); } static void mx_stack_remove_actor (ClutterContainer *container, ClutterActor *actor) { GList *actor_link; MxStackPrivate *priv = MX_STACK (container)->priv; actor_link = g_list_find (priv->children, actor); if (!actor_link) { g_warning (G_STRLOC ": Actor of type '%s' is not a child of container " "of type '%s'", g_type_name (G_OBJECT_TYPE (actor)), g_type_name (G_OBJECT_TYPE (container))); return; } g_object_ref (actor); if (priv->current_focus == actor) priv->current_focus = NULL; priv->children = g_list_delete_link (priv->children, actor_link); clutter_actor_unparent (actor); g_signal_emit_by_name (container, "actor-removed", actor); g_object_unref (actor); } static void mx_stack_foreach (ClutterContainer *container, ClutterCallback callback, gpointer callback_data) { MxStackPrivate *priv = MX_STACK (container)->priv; g_list_foreach (priv->children, (GFunc)callback, callback_data); } static void mx_stack_lower (ClutterContainer *container, ClutterActor *actor, ClutterActor *sibling) { gint i; GList *c, *position, *actor_link = NULL; MxStackPrivate *priv = MX_STACK (container)->priv; if (priv->children && (priv->children->data == actor)) return; position = priv->children; for (c = priv->children, i = 0; c; c = c->next, i++) { if (c->data == actor) actor_link = c; if (c->data == sibling) position = c; } if (!actor_link) { g_warning (G_STRLOC ": Actor of type '%s' is not a child of container " "of type '%s'", g_type_name (G_OBJECT_TYPE (actor)), g_type_name (G_OBJECT_TYPE (container))); return; } priv->children = g_list_delete_link (priv->children, actor_link); priv->children = g_list_insert_before (priv->children, position, actor); clutter_actor_queue_redraw (CLUTTER_ACTOR (container)); } static void mx_stack_raise (ClutterContainer *container, ClutterActor *actor, ClutterActor *sibling) { gint i; GList *c, *actor_link = NULL; gint position = -1; MxStackPrivate *priv = MX_STACK (container)->priv; for (c = priv->children, i = 0; c; c = c->next, i++) { if (c->data == actor) actor_link = c; if (c->data == sibling) position = i; } if (!actor_link) { g_warning (G_STRLOC ": Actor of type '%s' is not a child of container " "of type '%s'", g_type_name (G_OBJECT_TYPE (actor)), g_type_name (G_OBJECT_TYPE (container))); return; } if (!actor_link->next) return; priv->children = g_list_delete_link (priv->children, actor_link); priv->children = g_list_insert (priv->children, actor, position); clutter_actor_queue_redraw (CLUTTER_ACTOR (container)); } static gint mx_stack_depth_sort_cb (gconstpointer a, gconstpointer b) { gfloat depth_a = clutter_actor_get_depth ((ClutterActor *)a); gfloat depth_b = clutter_actor_get_depth ((ClutterActor *)a); if (depth_a < depth_b) return -1; else if (depth_a > depth_b) return 1; else return 0; } static void mx_stack_sort_depth_order (ClutterContainer *container) { MxStackPrivate *priv = MX_STACK (container)->priv; priv->children = g_list_sort (priv->children, mx_stack_depth_sort_cb); clutter_actor_queue_redraw (CLUTTER_ACTOR (container)); } static void clutter_container_iface_init (ClutterContainerIface *iface) { iface->add = mx_stack_add_actor; iface->remove = mx_stack_remove_actor; iface->foreach = mx_stack_foreach; iface->lower = mx_stack_lower; iface->raise = mx_stack_raise; iface->sort_depth_order = mx_stack_sort_depth_order; iface->child_meta_type = MX_TYPE_STACK_CHILD; } /* MxFocusableIface */ static MxFocusable * mx_stack_move_focus (MxFocusable *focusable, MxFocusDirection direction, MxFocusable *from) { GList *c; MxStackPrivate *priv = MX_STACK (focusable)->priv; if (direction == MX_FOCUS_DIRECTION_OUT) return NULL; focusable = NULL; c = g_list_find (priv->children, from); while (c && !focusable) { ClutterActor *child; switch (direction) { case MX_FOCUS_DIRECTION_PREVIOUS : case MX_FOCUS_DIRECTION_LEFT : case MX_FOCUS_DIRECTION_UP : c = c->prev; break; default: c = c->next; break; } if (!c) continue; child = c->data; if (!MX_IS_FOCUSABLE (child)) continue; focusable = mx_focusable_accept_focus (MX_FOCUSABLE (child), MX_FOCUS_HINT_PRIOR); if (focusable) priv->current_focus = child; } return focusable; } static MxFocusable * mx_stack_accept_focus (MxFocusable *focusable, MxFocusHint hint) { GList *c, *children; MxStackPrivate *priv = MX_STACK (focusable)->priv; ClutterContainer *container = CLUTTER_CONTAINER (focusable); focusable = NULL; switch (hint) { default: case MX_FOCUS_HINT_PRIOR: if (priv->current_focus && (!MX_IS_WIDGET (priv->current_focus) || !mx_widget_get_disabled ((MxWidget *)priv->current_focus))) { focusable = mx_focusable_accept_focus (MX_FOCUSABLE (priv->current_focus), hint); if (focusable) break; } /* This purposefully runs into the next case statement */ case MX_FOCUS_HINT_FIRST: case MX_FOCUS_HINT_LAST: children = clutter_container_get_children (container); if (hint == MX_FOCUS_HINT_LAST) children = g_list_reverse (children); if (children) { c = children; while (c && !focusable) { ClutterActor *child = c->data; c = c->next; if (!MX_IS_FOCUSABLE (child)) continue; if (MX_IS_WIDGET (child) && mx_widget_get_disabled ((MxWidget *)child)) continue; priv->current_focus = child; focusable = mx_focusable_accept_focus (MX_FOCUSABLE (child), hint); } g_list_free (children); } break; } return focusable; } static void mx_focusable_iface_init (MxFocusableIface *iface) { iface->move_focus = mx_stack_move_focus; iface->accept_focus = mx_stack_accept_focus; } /* Actor implementation */ static void mx_stack_dispose (GObject *object) { MxStackPrivate *priv = MX_STACK (object)->priv; while (priv->children) clutter_actor_destroy (CLUTTER_ACTOR (priv->children->data)); G_OBJECT_CLASS (mx_stack_parent_class)->dispose (object); } static void mx_stack_get_preferred_width (ClutterActor *actor, gfloat for_height, gfloat *min_width_p, gfloat *nat_width_p) { GList *c; MxPadding padding; gfloat min_width, nat_width, child_min_width, child_nat_width; MxStackPrivate *priv = MX_STACK (actor)->priv; mx_widget_get_padding (MX_WIDGET (actor), &padding); if (for_height >= 0) for_height = MAX (0, for_height - padding.top - padding.bottom); min_width = nat_width = 0; for (c = priv->children; c; c = c->next) { ClutterActor *child = c->data; if (!CLUTTER_ACTOR_IS_VISIBLE (child)) continue; clutter_actor_get_preferred_width (child, for_height, &child_min_width, &child_nat_width); if (child_min_width > min_width) min_width = child_min_width; if (child_nat_width > nat_width) nat_width = child_nat_width; } min_width += padding.left + padding.right; nat_width += padding.left + padding.right; if (min_width_p) *min_width_p = min_width; if (nat_width_p) *nat_width_p = nat_width; } static void mx_stack_get_preferred_height (ClutterActor *actor, gfloat for_width, gfloat *min_height_p, gfloat *nat_height_p) { GList *c; MxPadding padding; gfloat min_height, nat_height, child_min_height, child_nat_height; MxStackPrivate *priv = MX_STACK (actor)->priv; mx_widget_get_padding (MX_WIDGET (actor), &padding); if (for_width >= 0) for_width = MAX (0, for_width - padding.left - padding.right); min_height = nat_height = 0; for (c = priv->children; c; c = c->next) { ClutterActor *child = c->data; if (!CLUTTER_ACTOR_IS_VISIBLE (child)) continue; clutter_actor_get_preferred_height (child, for_width, &child_min_height, &child_nat_height); if (child_min_height > min_height) min_height = child_min_height; if (child_nat_height > nat_height) nat_height = child_nat_height; } min_height += padding.top + padding.bottom; nat_height += padding.top + padding.bottom; if (min_height_p) *min_height_p = min_height; if (nat_height_p) *nat_height_p = nat_height; } static void mx_stack_allocate (ClutterActor *actor, const ClutterActorBox *box, ClutterAllocationFlags flags) { GList *c; ClutterActorBox avail_space; MxStackPrivate *priv = MX_STACK (actor)->priv; CLUTTER_ACTOR_CLASS (mx_stack_parent_class)->allocate (actor, box, flags); mx_widget_get_available_area (MX_WIDGET (actor), box, &avail_space); memcpy (&priv->allocation, box, sizeof (priv->allocation)); for (c = priv->children; c; c = c->next) { gboolean x_fill, y_fill, fit, crop; MxAlign x_align, y_align; ClutterActor *child = c->data; ClutterActorBox child_box = avail_space; if (!CLUTTER_ACTOR_IS_VISIBLE (child)) continue; clutter_container_child_get (CLUTTER_CONTAINER (actor), child, "x-fill", &x_fill, "y-fill", &y_fill, "x-align", &x_align, "y-align", &y_align, "fit", &fit, "crop", &crop, NULL); /* when "crop" is set, fit and fill properties are ignored */ if (crop) { gfloat available_height, available_width; gfloat natural_width, natural_height; gfloat ratio_width, ratio_height, ratio_child; available_width = avail_space.x2 - avail_space.x1; available_height = avail_space.y2 - avail_space.y1; clutter_actor_get_preferred_size (child, NULL, NULL, &natural_width, &natural_height); ratio_child = natural_width / natural_height; ratio_width = available_width / natural_width; ratio_height = available_height / natural_height; if (ratio_width > ratio_height) { natural_width = available_width; natural_height = natural_width / ratio_child; } else { natural_height = available_height; natural_width = ratio_child * natural_height; } child_box.x1 = (available_width - natural_width) / 2; child_box.y1 = (available_height - natural_height) / 2; child_box.x2 = natural_width; child_box.y2 = natural_height; clutter_actor_allocate (child, &child_box, flags); continue; } /* when "fit" is set, fill properties are ignored */ if (fit) { gfloat available_height, available_width, width, height; gfloat min_width, natural_width, min_height, natural_height; ClutterRequestMode request_mode; available_height = avail_space.y2 - avail_space.y1; available_width = avail_space.x2 - avail_space.x1; request_mode = clutter_actor_get_request_mode (child); if (request_mode == CLUTTER_REQUEST_HEIGHT_FOR_WIDTH) { clutter_actor_get_preferred_width (child, available_height, &min_width, &natural_width); width = CLAMP (natural_width, min_width, available_width); clutter_actor_get_preferred_height (child, width, &min_height, &natural_height); height = CLAMP (natural_height, min_height, available_height); } else { clutter_actor_get_preferred_height (child, available_width, &min_height, &natural_height); height = CLAMP (natural_height, min_height, available_height); clutter_actor_get_preferred_width (child, height, &min_width, &natural_width); width = CLAMP (natural_width, min_width, available_width); } child_box.x1 = 0; child_box.y1 = 0; switch (x_align) { case MX_ALIGN_START: break; case MX_ALIGN_MIDDLE: child_box.x1 += (gint)(available_width / 2 - width / 2); break; case MX_ALIGN_END: child_box.x1 = avail_space.x2 - width; break; } switch (y_align) { case MX_ALIGN_START: break; case MX_ALIGN_MIDDLE: child_box.y1 += (gint)(available_height / 2 - height / 2); break; case MX_ALIGN_END: child_box.y1 = avail_space.y2 - height; break; } child_box.x2 = child_box.x1 + width; child_box.y2 = child_box.y1 + height; clutter_actor_allocate (child, &child_box, flags); continue; } /* Adjust the available space when not filling, otherwise * actors that support width-for-height or height-for-width * allocation won't shrink correctly. */ if (!x_fill) { gfloat width; clutter_actor_get_preferred_width (child, -1, NULL, &width); switch (x_align) { case MX_ALIGN_START: break; case MX_ALIGN_MIDDLE: child_box.x1 += (gint)((avail_space.x2 - avail_space.x1) / 2 - width / 2); break; case MX_ALIGN_END: child_box.x1 = avail_space.x2 - width; break; } child_box.x2 = child_box.x1 + width; if (child_box.x2 > avail_space.x2) child_box.x2 = avail_space.x2; if (child_box.x1 < avail_space.x1) child_box.x1 = avail_space.x1; } if (!y_fill) { gfloat height; clutter_actor_get_preferred_height (child, -1, NULL, &height); switch (y_align) { case MX_ALIGN_START: break; case MX_ALIGN_MIDDLE: child_box.y1 += (gint)((avail_space.y2 - avail_space.y1) / 2 - height / 2); break; case MX_ALIGN_END: child_box.y1 = avail_space.y2 - height; break; } child_box.y2 = child_box.y1 + height; if (child_box.y2 > avail_space.y2) child_box.y2 = avail_space.y2; if (child_box.y1 < avail_space.y1) child_box.y1 = avail_space.y1; } mx_allocate_align_fill (child, &child_box, x_align, y_align, x_fill, y_fill); clutter_actor_allocate (child, &child_box, flags); } } static void mx_stack_paint_children (ClutterActor *actor) { MxStackPrivate *priv = MX_STACK (actor)->priv; GList *c; for (c = priv->children; c; c = c->next) { ClutterActor *child = c->data; gboolean crop; if (!CLUTTER_ACTOR_IS_VISIBLE (child)) continue; clutter_container_child_get (CLUTTER_CONTAINER (actor), child, "crop", &crop, NULL); if (crop) { /* clip */ cogl_clip_push_rectangle (priv->allocation.x1, priv->allocation.y1, priv->allocation.x2, priv->allocation.y2); clutter_actor_paint (c->data); cogl_clip_pop (); } else clutter_actor_paint (c->data); } } static void mx_stack_paint (ClutterActor *actor) { /* allow MxWidget to paint the background */ CLUTTER_ACTOR_CLASS (mx_stack_parent_class)->paint (actor); mx_stack_paint_children (actor); } static void mx_stack_pick (ClutterActor *actor, const ClutterColor *color) { CLUTTER_ACTOR_CLASS (mx_stack_parent_class)->pick (actor, color); mx_stack_paint_children (actor); } static void mx_stack_class_init (MxStackClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass); g_type_class_add_private (klass, sizeof (MxStackPrivate)); object_class->dispose = mx_stack_dispose; actor_class->get_preferred_width = mx_stack_get_preferred_width; actor_class->get_preferred_height = mx_stack_get_preferred_height; actor_class->allocate = mx_stack_allocate; actor_class->paint = mx_stack_paint; actor_class->pick = mx_stack_pick; } static void mx_stack_init (MxStack *self) { self->priv = STACK_PRIVATE (self); } /** * mx_stack_new: * * Create a new #MxStack. * * Returns: a newly allocated #MxStack * * Since: 1.2 */ ClutterActor * mx_stack_new () { return g_object_new (MX_TYPE_STACK, NULL); } mx-1.4.7/mx/mx-stack.h000066400000000000000000000044271201047117600145010ustar00rootroot00000000000000/* * mx-stack.h: A container that allows the stacking of multiple widgets * * Copyright 2010 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * Boston, MA 02111-1307, USA. * * Written by: Chris Lord * */ #if !defined(MX_H_INSIDE) && !defined(MX_COMPILATION) #error "Only can be included directly.h" #endif #ifndef _MX_STACK_H #define _MX_STACK_H #include #include G_BEGIN_DECLS #define MX_TYPE_STACK mx_stack_get_type() #define MX_STACK(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ MX_TYPE_STACK, MxStack)) #define MX_STACK_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), \ MX_TYPE_STACK, MxStackClass)) #define MX_IS_STACK(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \ MX_TYPE_STACK)) #define MX_IS_STACK_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE ((klass), \ MX_TYPE_STACK)) #define MX_STACK_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), \ MX_TYPE_STACK, MxStackClass)) typedef struct _MxStack MxStack; typedef struct _MxStackClass MxStackClass; typedef struct _MxStackPrivate MxStackPrivate; /** * MxStack: * * The contents of this structure are private and should only be accessed * through the public API. */ struct _MxStack { /*< private >*/ MxWidget parent; MxStackPrivate *priv; }; struct _MxStackClass { MxWidgetClass parent_class; /* padding for future expansion */ void (*_padding_0) (void); void (*_padding_1) (void); void (*_padding_2) (void); void (*_padding_3) (void); void (*_padding_4) (void); }; GType mx_stack_get_type (void) G_GNUC_CONST; ClutterActor *mx_stack_new (); G_END_DECLS #endif /* _MX_STACK_H */ mx-1.4.7/mx/mx-stylable.c000066400000000000000000001031341201047117600152010ustar00rootroot00000000000000/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * mx-stylable.c: Interface for stylable objects * * Copyright 2008 Intel Corporation * Copyright 2009 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * * Written by: Emmanuele Bassi * Thomas Wood * */ /** * SECTION:mx-stylable * @short_description: Interface for stylable objects * * Stylable objects are classes that can have "style properties", that is * properties that can be changed by attaching a #MxStyle to them. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include "mx-marshal.h" #include "mx-private.h" #include "mx-stylable.h" #include "mx-settings.h" enum { STYLE_CHANGED, #if 0 STYLE_NOTIFY, #endif CHANGED, LAST_SIGNAL }; static GObjectNotifyContext property_notify_context = { 0, }; static GParamSpecPool *style_property_spec_pool = NULL; static GQuark quark_real_owner = 0; static GQuark quark_style = 0; static guint stylable_signals[LAST_SIGNAL] = { 0, }; static void mx_stylable_property_changed_notify (MxStylable *stylable); static void mx_stylable_notify_dispatcher (GObject *gobject, guint n_pspecs, GParamSpec **pspecs) { #if 0 guint i; for (i = 0; i < n_pspecs; i++) g_signal_emit (gobject, stylable_signals[STYLE_NOTIFY], g_quark_from_string (pspecs[i]->name), pspecs[i]); #endif } static void mx_stylable_base_finalize (gpointer g_iface) { GList *list, *node; list = g_param_spec_pool_list_owned (style_property_spec_pool, G_TYPE_FROM_INTERFACE (g_iface)); for (node = list; node; node = node->next) { GParamSpec *pspec = node->data; g_param_spec_pool_remove (style_property_spec_pool, pspec); g_param_spec_unref (pspec); } g_list_free (list); } static void mx_stylable_base_init (gpointer g_iface) { static gboolean initialised = FALSE; GParamSpec *pspec; GType iface_type = G_TYPE_FROM_INTERFACE (g_iface); if (G_LIKELY (initialised)) return; initialised = TRUE; quark_real_owner = g_quark_from_static_string ("mx-stylable-real-owner-quark"); quark_style = g_quark_from_static_string ("mx-stylable-style-quark"); style_property_spec_pool = g_param_spec_pool_new (FALSE); property_notify_context.quark_notify_queue = g_quark_from_static_string ("MxStylable-style-property-notify-queue"); property_notify_context.dispatcher = mx_stylable_notify_dispatcher; pspec = g_param_spec_object ("style", "Style", "A style object", MX_TYPE_STYLE, MX_PARAM_READWRITE); g_object_interface_install_property (g_iface, pspec); pspec = g_param_spec_string ("style-class", "Style Class", "String representation of the item's class", "", MX_PARAM_READWRITE); g_object_interface_install_property (g_iface, pspec); pspec = g_param_spec_string ("style-pseudo-class", "Style Pseudo Class", "List of pseudo class, such as current state," "separated by ':'.", "", MX_PARAM_READWRITE); g_object_interface_install_property (g_iface, pspec); /** * MxStylable::style-changed: * @stylable: the #MxStylable that received the signal * @flags: the #MxStyleChangedFlags associated with the signal * * The ::style-changed signal is emitted each time one of the style * properties have changed. */ stylable_signals[STYLE_CHANGED] = g_signal_new (g_intern_static_string ("style-changed"), iface_type, G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (MxStylableIface, style_changed), NULL, NULL, _mx_marshal_VOID__FLAGS, G_TYPE_NONE, 1, MX_TYPE_STYLE_CHANGED_FLAGS); #if 0 stylable_signals[STYLE_NOTIFY] = g_signal_new (g_intern_static_string ("style-notify"), iface_type, G_SIGNAL_RUN_FIRST | G_SIGNAL_NO_RECURSE | G_SIGNAL_DETAILED | G_SIGNAL_NO_HOOKS | G_SIGNAL_ACTION, G_STRUCT_OFFSET (MxStylableIface, style_notify), NULL, NULL, _mx_marshal_VOID__PARAM, G_TYPE_NONE, 1, G_TYPE_PARAM); #endif } GType mx_stylable_get_type (void) { static GType our_type = 0; if (G_UNLIKELY (our_type == 0)) { GTypeInfo stylable_info = { sizeof (MxStylableIface), mx_stylable_base_init, mx_stylable_base_finalize }; our_type = g_type_register_static (G_TYPE_INTERFACE, g_intern_static_string ("MxStylable"), &stylable_info, 0); } return our_type; } static void _mx_stylable_prepend_style_string (GString *string, MxStylable *stylable) { GType type_id; gchar *style_string; const gchar *type, *id, *class, *pseudo_class; type_id = G_OBJECT_CLASS_TYPE (G_OBJECT_GET_CLASS (stylable)); type = g_type_name (type_id); id = clutter_actor_get_name ((ClutterActor*) stylable); class = mx_stylable_get_style_class (stylable); pseudo_class = mx_stylable_get_style_pseudo_class (stylable); style_string = g_strconcat (type, "#", id ? id : "", ".", class ? class : "", ":", pseudo_class ? pseudo_class : "", ">", NULL); g_string_prepend (string, style_string); g_free (style_string); } gchar * _mx_stylable_get_style_string (MxStylable *stylable) { GType type_id; gchar *cstring; GString *string; const gchar *type, *id, *class, *pseudo_class; ClutterActor *actor; ClutterActor *parent; /* Create a string that contains all the properties of a * Stylable that can be matched against in the CSS. */ type_id = G_OBJECT_CLASS_TYPE (G_OBJECT_GET_CLASS (stylable)); type = g_type_name (type_id); actor = CLUTTER_ACTOR (stylable); id = clutter_actor_get_name (actor); class = mx_stylable_get_style_class (stylable); pseudo_class = mx_stylable_get_style_pseudo_class (stylable); string = g_string_sized_new (512); /* Add the actor hierarchy */ parent = clutter_actor_get_parent (actor); while (parent) { if (MX_IS_STYLABLE (parent)) _mx_stylable_prepend_style_string (string, (MxStylable*) parent); parent = clutter_actor_get_parent (parent); } g_string_append_printf (string, "%s#%s.%s:%s", type, id ? id : "", class ? class : "", pseudo_class ? pseudo_class : ""); cstring = string->str; g_string_free (string, FALSE); return cstring; } #if 0 void mx_stylable_freeze_notify (MxStylable *stylable) { g_return_if_fail (MX_IS_STYLABLE (stylable)); g_object_ref (stylable); g_object_notify_queue_freeze (G_OBJECT (stylable), &property_notify_context); g_object_unref (stylable); } void mx_stylable_thaw_notify (MxStylable *stylable) { GObjectNotifyQueue *nqueue; g_return_if_fail (MX_IS_STYLABLE (stylable)); g_object_ref (stylable); nqueue = g_object_notify_queue_from_object (G_OBJECT (stylable), &property_notify_context); if (!nqueue || !nqueue->freeze_count) g_warning ("%s: property-changed notification for %s(%p) is not frozen", G_STRFUNC, G_OBJECT_TYPE_NAME (stylable), stylable); else g_object_notify_queue_thaw (G_OBJECT (stylable), nqueue); g_object_unref (stylable); } void mx_stylable_notify (MxStylable *stylable, const gchar *property_name) { GParamSpec *pspec; g_return_if_fail (MX_IS_STYLABLE (stylable)); g_return_if_fail (property_name != NULL); g_object_ref (stylable); pspec = g_param_spec_pool_lookup (style_property_spec_pool, property_name, G_OBJECT_TYPE (stylable), TRUE); if (!pspec) g_warning ("%s: object class `%s' has no style property named `%s'", G_STRFUNC, G_OBJECT_TYPE_NAME (stylable), property_name); else { GObjectNotifyQueue *nqueue; nqueue = g_object_notify_queue_freeze (G_OBJECT (stylable), &property_notify_context); g_object_notify_queue_add (G_OBJECT (stylable), nqueue, pspec); g_object_notify_queue_thaw (G_OBJECT (stylable), nqueue); } g_object_unref (stylable); } #endif /** * mx_stylable_iface_install_property: * @iface: a #MxStylableIface * @owner_type: #GType of the style property owner * @pspec: a #GParamSpec * * Installs a property for @owner_type using @pspec as the property * description. * * This function should be used inside the #MxStylableIface initialization * function of a class, for instance: * * * G_DEFINE_TYPE_WITH_CODE (FooActor, foo_actor, CLUTTER_TYPE_ACTOR, * G_IMPLEMENT_INTERFACE (MX_TYPE_STYLABLE, * mx_stylable_init)); * ... * static void * mx_stylable_init (MxStylableIface *iface) * { * static gboolean is_initialized = FALSE; * * if (!is_initialized) * { * ... * mx_stylable_iface_install_property (stylable, * FOO_TYPE_ACTOR, * g_param_spec_int ("x-spacing", * "X Spacing", * "Horizontal spacing", * -1, G_MAXINT, * 2, * G_PARAM_READWRITE)); * ... * } * } * */ void mx_stylable_iface_install_property (MxStylableIface *iface, GType owner_type, GParamSpec *pspec) { g_return_if_fail (MX_IS_STYLABLE_IFACE (iface)); g_return_if_fail (owner_type != G_TYPE_INVALID); g_return_if_fail (G_IS_PARAM_SPEC (pspec)); g_return_if_fail (pspec->flags & G_PARAM_READABLE); g_return_if_fail (!(pspec->flags & (G_PARAM_CONSTRUCT_ONLY | G_PARAM_CONSTRUCT ))); if (g_param_spec_pool_lookup (style_property_spec_pool, pspec->name, owner_type, FALSE)) { g_warning ("%s: class `%s' already contains a style property named `%s'", G_STRLOC, g_type_name (owner_type), pspec->name); return; } g_param_spec_ref_sink (pspec); g_param_spec_set_qdata_full (pspec, quark_real_owner, g_strdup (g_type_name (owner_type)), g_free); g_param_spec_pool_insert (style_property_spec_pool, pspec, owner_type); } /** * mx_stylable_list_properties: * @stylable: a #MxStylable * @n_props: (out): return location for the number of properties, or %NULL * * Retrieves all the #GParamSpecs installed by @stylable. * * Return value: (transfer container) (array length=n_props): an array * of #GParamSpecs. Free it with g_free() when done. */ GParamSpec ** mx_stylable_list_properties (MxStylable *stylable, guint *n_props) { GParamSpec **pspecs = NULL; guint n; g_return_val_if_fail (MX_IS_STYLABLE (stylable), NULL); pspecs = g_param_spec_pool_list (style_property_spec_pool, G_OBJECT_TYPE (stylable), &n); if (n_props) *n_props = n; return pspecs; } /** * mx_stylable_find_property: * @stylable: a #MxStylable * @property_name: the name of the property to find * * Finds the #GParamSpec installed by @stylable for the property * with @property_name. * * Return value: (transfer none): a #GParamSpec for the given property, * or %NULL if no property with that name was found */ GParamSpec * mx_stylable_find_property (MxStylable *stylable, const gchar *property_name) { g_return_val_if_fail (MX_IS_STYLABLE (stylable), NULL); g_return_val_if_fail (property_name != NULL, NULL); return g_param_spec_pool_lookup (style_property_spec_pool, property_name, G_OBJECT_TYPE (stylable), TRUE); } static inline void mx_stylable_get_property_internal (MxStylable *stylable, GParamSpec *pspec, GValue *value) { MxStyle *style; GValue real_value = { 0, }; style = mx_stylable_get_style (stylable); if (!style) { g_value_reset (value); return; } mx_style_get_property (style, stylable, pspec, &real_value); g_value_copy (&real_value, value); g_value_unset (&real_value); } /** * mx_stylable_get_property: * @stylable: a #MxStylable * @property_name: the name of the property * @value: (out): return location for an empty #GValue * * Retrieves the value of @property_name for @stylable, and puts it * into @value. */ void mx_stylable_get_property (MxStylable *stylable, const gchar *property_name, GValue *value) { GParamSpec *pspec; g_return_if_fail (MX_IS_STYLABLE (stylable)); g_return_if_fail (property_name != NULL); g_return_if_fail (value != NULL); pspec = mx_stylable_find_property (stylable, property_name); if (!pspec) { g_warning ("Stylable class `%s' doesn't have a property named `%s'", g_type_name (G_OBJECT_TYPE (stylable)), property_name); return; } if (!(pspec->flags & G_PARAM_READABLE)) { g_warning ("Style property `%s' of class `%s' is not readable", pspec->name, g_type_name (G_OBJECT_TYPE (stylable))); return; } if (G_VALUE_TYPE (value) != G_PARAM_SPEC_VALUE_TYPE (pspec)) { g_warning ("Passed value is not of the requested type `%s' for " "the style property `%s' of class `%s'", g_type_name (G_PARAM_SPEC_VALUE_TYPE (pspec)), pspec->name, g_type_name (G_OBJECT_TYPE (stylable))); return; } mx_stylable_get_property_internal (stylable, pspec, value); } /** * mx_stylable_get: * @stylable: a #MxStylable * @first_property_name: name of the first property to get * @Varargs: return location for the first property, followed optionally * by more name/return location pairs, followed by %NULL * * Gets the style properties for @stylable. * * In general, a copy is made of the property contents and the called * is responsible for freeing the memory in the appropriate manner for * the property type. * * * Using mx_stylable_get(<!-- -->) * An example of using mx_stylable_get() to get the contents of * two style properties - one of type #G_TYPE_INT and one of type * #CLUTTER_TYPE_COLOR: * * gint x_spacing; * ClutterColor *bg_color; * * mx_stylable_get (stylable, * "x-spacing", &x_spacing, * "bg-color", &bg_color, * NULL); * * /* do something with x_spacing and bg_color */ * * clutter_color_free (bg_color); * * */ void mx_stylable_get (MxStylable *stylable, const gchar *first_property_name, ...) { MxStyle *style; va_list args; g_return_if_fail (MX_IS_STYLABLE (stylable)); g_return_if_fail (first_property_name != NULL); style = mx_stylable_get_style (stylable); va_start (args, first_property_name); mx_style_get_valist (style, stylable, first_property_name, args); va_end (args); } static gboolean _set_from_xsettings (GParamSpec *pspec, GValue *value) { gchar *font_string; gint font_size; PangoFontDescription *descr; MxSettings *settings = mx_settings_get_default (); gboolean result = FALSE; if (!strcmp (pspec->name, "font-family") || !strcmp (pspec->name, "font-size")) { const gchar *fn; PangoFontMask set_fields; g_object_get (settings, "font-name", &font_string, NULL); if (!font_string) return FALSE; descr = pango_font_description_from_string (font_string); g_free (font_string); set_fields = pango_font_description_get_set_fields (descr); /* font name */ if ((set_fields & PANGO_FONT_MASK_FAMILY) && !strcmp (pspec->name, "font-family") && G_VALUE_HOLDS_STRING (value)) { fn = pango_font_description_get_family (descr); g_value_set_string (value, fn); result = TRUE; } /* font size */ else if ((set_fields & PANGO_FONT_MASK_SIZE) && !strcmp (pspec->name, "font-size") && G_VALUE_HOLDS_INT (value)) { font_size = pango_font_description_get_size (descr) / PANGO_SCALE; if (!pango_font_description_get_size_is_absolute (descr)) { ClutterBackend *backend = clutter_get_default_backend (); gdouble res = clutter_backend_get_resolution (backend); font_size = font_size * res / 72.0; } g_value_set_int (value, font_size); result = TRUE; } pango_font_description_free (descr); descr = NULL; } return result; } /** * mx_stylable_get_default_value: * @stylable: a #MxStylable * @property_name: name of the property to query * @value_out: (out): return location for the default value * * Query @stylable for the default value of property @property_name and * fill @value_out with the result. * * Returns: %TRUE if property @property_name exists and the default value has * been returned. */ gboolean mx_stylable_get_default_value (MxStylable *stylable, const gchar *property_name, GValue *value_out) { GParamSpec *pspec; pspec = mx_stylable_find_property (stylable, property_name); if (!pspec) { g_warning ("%s: no style property named `%s' found for class `%s'", G_STRLOC, property_name, g_type_name (G_OBJECT_TYPE (stylable))); return FALSE; } if (!(pspec->flags & G_PARAM_READABLE)) { g_warning ("Style property `%s' of class `%s' is not readable", pspec->name, g_type_name (G_OBJECT_TYPE (stylable))); return FALSE; } g_value_init (value_out, G_PARAM_SPEC_VALUE_TYPE (pspec)); /* default font values come from xsettings if possible */ if (!_set_from_xsettings (pspec, value_out)) g_param_value_set_default (pspec, value_out); return TRUE; } /** * mx_stylable_get_style: * @stylable: a #MxStylable * * Retrieves the #MxStyle used by @stylable. This function does not * alter the reference count of the returned object. * * Return value: (transfer none): a #MxStyle */ MxStyle * mx_stylable_get_style (MxStylable *stylable) { MxStylableIface *iface; g_return_val_if_fail (MX_IS_STYLABLE (stylable), NULL); iface = MX_STYLABLE_GET_IFACE (stylable); if (G_LIKELY (iface->get_style)) return iface->get_style (stylable); return g_object_get_data (G_OBJECT (stylable), "mx-stylable-style"); } typedef struct { GObject *instance; gulong handler_id; } SignalData; static void disconnect_style_changed_signal (SignalData *data) { g_signal_handler_disconnect (data->instance, data->handler_id); g_object_unref (data->instance); g_slice_free (SignalData, data); } /** * mx_stylable_set_style: * @stylable: a #MxStylable * @style: a #MxStyle * * Sets @style as the new #MxStyle to be used by @stylable. * * The #MxStylable will take ownership of the passed #MxStyle. * * After the #MxStyle has been set, the MxStylable::style-set signal * will be emitted. */ void mx_stylable_set_style (MxStylable *stylable, MxStyle *style) { MxStylableIface *iface; SignalData *data; g_return_if_fail (MX_IS_STYLABLE (stylable)); g_return_if_fail (MX_IS_STYLE (style)); iface = MX_STYLABLE_GET_IFACE (stylable); if (G_LIKELY (iface->set_style)) iface->set_style (stylable, style); /* connect to the "changed" signal of the MxStyle and take a reference to * the style */ data = g_slice_new (SignalData); data->instance = g_object_ref_sink (style); data->handler_id = g_signal_connect_swapped (style, "changed", G_CALLBACK (mx_stylable_property_changed_notify), stylable); g_object_set_qdata_full (G_OBJECT (stylable), quark_style, data, (GDestroyNotify) disconnect_style_changed_signal); mx_stylable_style_changed (stylable, MX_STYLE_CHANGED_INVALIDATE_CACHE); g_object_notify (G_OBJECT (stylable), "style"); } /** * mx_stylable_get_style_pseudo_class: * @stylable: a #MxStylable * * Get the current style pseudo class. This can contain multiple pseudo class * names, separated by ':'. * * Returns: the pseudo class string. The string is owned by the #MxWidget and * should not be modified or freed. */ const gchar* mx_stylable_get_style_pseudo_class (MxStylable *stylable) { MxStylableIface *iface; g_return_val_if_fail (MX_IS_STYLABLE (stylable), NULL); iface = MX_STYLABLE_GET_IFACE (stylable); if (G_LIKELY (iface->get_style_pseudo_class)) return iface->get_style_pseudo_class (stylable); g_warning ("MxStylable of type '%s' does not implement" " get_style_pseudo_class()", g_type_name (G_OBJECT_TYPE (stylable))); return NULL; } /** * mx_stylable_set_style_pseudo_class: * @stylable: a #MxStylable * @pseudo_class: a new pseudo class string * * Set the style pseudo class. The string can contain multiple pseudo class * names, separated by ':'. */ void mx_stylable_set_style_pseudo_class (MxStylable *stylable, const gchar *pseudo_class) { MxStylableIface *iface; g_return_if_fail (MX_IS_STYLABLE (stylable)); iface = MX_STYLABLE_GET_IFACE (stylable); if (G_LIKELY (iface->set_style_pseudo_class)) iface->set_style_pseudo_class (stylable, pseudo_class); else g_warning ("MxStylable of type '%s' does not implement" " set_style_pseudo_class()", g_type_name (G_OBJECT_TYPE (stylable))); } /** * mx_stylable_style_pseudo_class_contains: * @stylable: A #MxStylable * @pseudo_class: A pseudo-class name * * Check if the given pseudo-class name is contained in the list of * set pseudo classes on this #MxStylable object. * * Returns: %TRUE if the given pseudo-class is set, %FALSE otherwise * * Since: 1.2 */ gboolean mx_stylable_style_pseudo_class_contains (MxStylable *stylable, const gchar *pseudo_class) { const gchar *old_class, *match; g_return_val_if_fail (MX_IS_STYLABLE (stylable), FALSE); g_return_val_if_fail (pseudo_class != NULL, FALSE); old_class = mx_stylable_get_style_pseudo_class (stylable); if (old_class && pseudo_class && (match = strstr (old_class, pseudo_class))) { if ((match == old_class) || (match[-1] == ':')) { size_t length = strlen (match); if ((match[length] == ':') || (match[length] == '\0')) return TRUE; } } return FALSE; } /** * mx_stylable_style_pseudo_class_add: * @stylable: A #MxStylable * @new_class: A pseudo-class name to add * * Add a pseudo-class name to the list of pseudo classes, contained in the * #MxStylable:style-pseudo-class property. * * Since: 1.2 */ void mx_stylable_style_pseudo_class_add (MxStylable *stylable, const gchar *new_class) { const gchar *old_class; gchar *tmp; g_return_if_fail (MX_IS_STYLABLE (stylable)); g_return_if_fail (new_class != NULL); /* check if the pseudo class already contains new_class */ if (mx_stylable_style_pseudo_class_contains (stylable, new_class)) return; old_class = mx_stylable_get_style_pseudo_class (stylable); /* add the new pseudo class */ if (old_class) tmp = g_strconcat (old_class, ":", new_class, NULL); else tmp = g_strdup (new_class); mx_stylable_set_style_pseudo_class (stylable, tmp); g_free (tmp); } /** * mx_stylable_style_pseudo_class_remove: * @stylable: An #MxStylable * @remove_class: A pseudo class name to remove * * Remove the specified pseudo class name from the list of pseudo classes * contained in the #MxStylable:style-pseudo-class property. * * Since: 1.2 */ void mx_stylable_style_pseudo_class_remove (MxStylable *stylable, const gchar *remove_class) { const gchar *old_class; gchar *tmp; gchar **list; gint i, len; g_return_if_fail (MX_IS_STYLABLE (stylable)); g_return_if_fail (remove_class != NULL); /* check if the pseudo class does not container remove_class */ if (!mx_stylable_style_pseudo_class_contains (stylable, remove_class)) return; old_class = mx_stylable_get_style_pseudo_class (stylable); /* remove the old pseudo class */ list = g_strsplit (old_class, ":", -1); len = g_strv_length (list); tmp = NULL; for (i = 0; i < len; i++) { /* skip over any instances of remove_class */ if (!strcmp (list[i], remove_class)) continue; if (tmp) tmp = g_strconcat (list[i], ":", tmp, NULL); else tmp = g_strdup (list[i]); } mx_stylable_set_style_pseudo_class (stylable, tmp); g_strfreev (list); g_free (tmp); } /** * mx_stylable_get_style_class: * @stylable: a #MxStylable * * Get the current style class name * * Returns: the class name string. The string is owned by the #MxWidget and * should not be modified or freed. */ const gchar* mx_stylable_get_style_class (MxStylable *stylable) { MxStylableIface *iface; g_return_val_if_fail (MX_IS_STYLABLE (stylable), NULL); iface = MX_STYLABLE_GET_IFACE (stylable); if (G_LIKELY (iface->get_style_class)) return iface->get_style_class (stylable); g_warning ("MxStylable of type '%s' does not implement get_style_class()", g_type_name (G_OBJECT_TYPE (stylable))); return NULL; } /** * mx_stylable_set_style_class: * @stylable: a #MxStylable * @style_class: a new style class string * * Set the style class name */ void mx_stylable_set_style_class (MxStylable *stylable, const gchar *style_class) { MxStylableIface *iface; g_return_if_fail (MX_IS_STYLABLE (stylable)); iface = MX_STYLABLE_GET_IFACE (stylable); if (G_LIKELY (iface->set_style_class)) iface->set_style_class (stylable, style_class); else g_warning ("MxStylable of type '%s' does not implement" " set_style_class()", g_type_name (G_OBJECT_TYPE (stylable))); } static void mx_stylable_property_changed_notify (MxStylable *stylable) { mx_stylable_style_changed (stylable, MX_STYLE_CHANGED_INVALIDATE_CACHE); } static void mx_stylable_parent_set_notify (ClutterActor *actor, ClutterActor *old_parent) { ClutterActor *new_parent = clutter_actor_get_parent (actor); /* check the actor has a new parent */ if (new_parent) { mx_stylable_style_changed (MX_STYLABLE (actor), MX_STYLE_CHANGED_INVALIDATE_CACHE); } } static void mx_stylable_style_changed_internal (MxStylable *stylable, MxStyleChangedFlags flags); static void mx_stylable_child_notify (ClutterActor *actor, gpointer flags) { if (MX_IS_STYLABLE (actor)) mx_stylable_style_changed_internal (MX_STYLABLE (actor), GPOINTER_TO_INT (flags)); } static void mx_stylable_style_changed_internal (MxStylable *stylable, MxStyleChangedFlags flags) { /* don't update stylables until they are mapped (unless ensure is set) */ if (G_LIKELY (CLUTTER_IS_ACTOR (stylable)) && !CLUTTER_ACTOR_IS_MAPPED (CLUTTER_ACTOR (stylable)) && !(flags & MX_STYLE_CHANGED_FORCE)) return; if (flags & MX_STYLE_CHANGED_INVALIDATE_CACHE) _mx_style_invalidate_cache (stylable); /* If the parent style has changed, child cache needs to be * invalidated. This needs to happen for internal children as * well, which is why it's here and not in the container block * lower down. */ flags |= MX_STYLE_CHANGED_INVALIDATE_CACHE; g_signal_emit (stylable, stylable_signals[STYLE_CHANGED], 0, flags); /* propagate the style-changed signal to children, since their style may * depend on one or more properties of the parent */ if (CLUTTER_IS_CONTAINER (stylable)) { /* notify our children that their parent stylable has changed */ clutter_container_foreach ((ClutterContainer *) stylable, mx_stylable_child_notify, GINT_TO_POINTER (flags)); } } /** * mx_stylable_style_changed: * @stylable: an MxStylable * @flags: flags that control the style changing * * Emit the "style-changed" signal on @stylable to notify it that one or more * of the style properties has changed. * * If @stylable is a #ClutterContainer then the "style-changed" notification is * propagated to it's children, since their style may depend on one or more * properties of the parent. * */ void mx_stylable_style_changed (MxStylable *stylable, MxStyleChangedFlags flags) { mx_stylable_style_changed_internal (stylable, flags); } void mx_stylable_connect_change_notifiers (MxStylable *stylable) { g_return_if_fail (CLUTTER_IS_ACTOR (stylable)); g_return_if_fail (MX_IS_STYLABLE (stylable)); /* ClutterActor signals */ g_signal_connect (stylable, "notify::name", G_CALLBACK (mx_stylable_property_changed_notify), NULL); g_signal_connect (stylable, "parent-set", G_CALLBACK (mx_stylable_parent_set_notify), NULL); /* style-changed is blocked until the actor is mapped, so style-changed * needs to be sent as soon as the actor is mapped */ g_signal_connect (stylable, "notify::mapped", G_CALLBACK (mx_stylable_property_changed_notify), NULL); /* MxStylable notifiers */ g_signal_connect (stylable, "notify::style-class", G_CALLBACK (mx_stylable_property_changed_notify), NULL); g_signal_connect (stylable, "notify::style-pseudo-class", G_CALLBACK (mx_stylable_property_changed_notify), NULL); } void mx_stylable_apply_clutter_text_attributes (MxStylable *stylable, ClutterText *text) { ClutterColor *real_color = NULL; gchar *font_name = NULL; gint font_size = 0; MxFontWeight font_weight; PangoWeight weight; PangoFontDescription *descr; gchar *descr_string; mx_stylable_get (stylable, "color", &real_color, "font-family", &font_name, "font-size", &font_size, "font-weight", &font_weight, NULL); /* Create a description, we will convert to a string and set on the * ClutterText. When Clutter gets API to set the description directly this * won't be necessary. */ descr = pango_font_description_new (); /* font name */ pango_font_description_set_family (descr, font_name); g_free (font_name); /* font size */ pango_font_description_set_absolute_size (descr, font_size * PANGO_SCALE); /* font weight */ switch (font_weight) { case MX_FONT_WEIGHT_BOLD: weight = PANGO_WEIGHT_BOLD; break; case MX_FONT_WEIGHT_LIGHTER: weight = PANGO_WEIGHT_LIGHT; break; case MX_FONT_WEIGHT_BOLDER: weight = PANGO_WEIGHT_HEAVY; break; default: weight = PANGO_WEIGHT_NORMAL; break; } pango_font_description_set_weight (descr, weight); descr_string = pango_font_description_to_string (descr); clutter_text_set_font_name (text, descr_string); g_free (descr_string); pango_font_description_free (descr); /* font color */ if (real_color) { clutter_text_set_color (text, real_color); clutter_color_free (real_color); } } mx-1.4.7/mx/mx-stylable.h000066400000000000000000000142551201047117600152130ustar00rootroot00000000000000/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * mx-stylable.h: Interface for stylable objects * * Copyright 2008, 2009 Intel Corporation * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * Boston, MA 02111-1307, USA. * * Written by: Emmanuele Bassi * Thomas Wood * */ #if !defined(MX_H_INSIDE) && !defined(MX_COMPILATION) #error "Only can be included directly.h" #endif #ifndef __MX_STYLABLE_H__ #define __MX_STYLABLE_H__ #include #include #include G_BEGIN_DECLS #define MX_TYPE_STYLABLE (mx_stylable_get_type ()) #define MX_STYLABLE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MX_TYPE_STYLABLE, MxStylable)) #define MX_IS_STYLABLE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MX_TYPE_STYLABLE)) #define MX_STYLABLE_IFACE(iface) (G_TYPE_CHECK_CLASS_CAST ((iface), MX_TYPE_STYLABLE, MxStylableIface)) #define MX_IS_STYLABLE_IFACE(iface) (G_TYPE_CHECK_CLASS_TYPE ((iface), MX_TYPE_STYLABLE)) #define MX_STYLABLE_GET_IFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), MX_TYPE_STYLABLE, MxStylableIface)) /* MxStylableIface is defined in mx-style.h */ /** * MxStyleChangedFlags: * @MX_STYLE_CHANGED_NONE: No flag set * @MX_STYLE_CHANGED_FORCE: Whether to force propogation of the style-changed * signal, regardless of the state of the stylable object. * @MX_STYLE_CHANGED_INVALIDATE_CACHE: Internal flag used to track style * caching state. * */ typedef enum { MX_STYLE_CHANGED_NONE = 0, MX_STYLE_CHANGED_FORCE = 1 << 0, MX_STYLE_CHANGED_INVALIDATE_CACHE = 1 << 1 } MxStyleChangedFlags; struct _MxStylableIface { /*< private >*/ GTypeInterface g_iface; /*< public >*/ /* virtual functions */ MxStyle * (* get_style) (MxStylable *stylable); void (* set_style) (MxStylable *stylable, MxStyle *style); const gchar* (* get_style_class) (MxStylable *stylable); void (* set_style_class) (MxStylable *stylable, const gchar *style_class); const gchar* (* get_style_pseudo_class) (MxStylable *stylable); void (* set_style_pseudo_class) (MxStylable *stylable, const gchar *pseudo_class); /* context virtual functions */ /* signals, not vfuncs */ #if 0 void (* style_notify) (MxStylable *stylable, GParamSpec *pspec); #endif void (* style_changed) (MxStylable *stylable, MxStyleChangedFlags flags); }; GType mx_stylable_get_type (void) G_GNUC_CONST; void mx_stylable_iface_install_property (MxStylableIface *iface, GType owner_type, GParamSpec *pspec); #if 0 void mx_stylable_freeze_notify (MxStylable *stylable); void mx_stylable_notify (MxStylable *stylable, const gchar *property_name); void mx_stylable_thaw_notify (MxStylable *stylable); #endif GParamSpec **mx_stylable_list_properties (MxStylable *stylable, guint *n_props); GParamSpec * mx_stylable_find_property (MxStylable *stylable, const gchar *property_name); void mx_stylable_set_style (MxStylable *stylable, MxStyle *style); MxStyle * mx_stylable_get_style (MxStylable *stylable); void mx_stylable_get (MxStylable *stylable, const gchar *first_property_name, ...) G_GNUC_NULL_TERMINATED; void mx_stylable_get_property (MxStylable *stylable, const gchar *property_name, GValue *value); gboolean mx_stylable_get_default_value (MxStylable *stylable, const gchar *property_name, GValue *value_out); const gchar* mx_stylable_get_style_class (MxStylable *stylable); void mx_stylable_set_style_class (MxStylable *stylable, const gchar *style_class); const gchar* mx_stylable_get_style_pseudo_class (MxStylable *stylable); void mx_stylable_set_style_pseudo_class (MxStylable *stylable, const gchar *pseudo_class); void mx_stylable_style_changed (MxStylable *stylable, MxStyleChangedFlags flags); void mx_stylable_connect_change_notifiers (MxStylable *stylable); /* utilities */ void mx_stylable_apply_clutter_text_attributes (MxStylable *stylable, ClutterText *text); void mx_stylable_style_pseudo_class_add (MxStylable *stylable, const gchar *new_class); void mx_stylable_style_pseudo_class_remove (MxStylable *stylable, const gchar *remove_class); gboolean mx_stylable_style_pseudo_class_contains (MxStylable *stylable, const gchar *pseudo_class); G_END_DECLS #endif /* __MX_STYLABLE_H__ */ mx-1.4.7/mx/mx-style.c000066400000000000000000000550241201047117600145260ustar00rootroot00000000000000/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright 2009 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * */ /** * SECTION:mx-style * @short_description: a data store for style properties * * #MxStyle is a property data store that can read properties from a style * sheet. It is queried with objects that implement the MxStylable * interface. */ #include #include #include #include #include #include "mx-stylable.h" #include "mx-css.h" #include "mx-marshal.h" #include "mx-style.h" #include "mx-enum-types.h" #include "mx-types.h" #include "mx-private.h" enum { CHANGED, LAST_SIGNAL }; #define MX_STYLE_GET_PRIVATE(obj) \ (G_TYPE_INSTANCE_GET_PRIVATE ((obj), MX_TYPE_STYLE, MxStylePrivate)) #define MX_STYLE_ERROR g_style_error_quark () #define MX_STYLE_CACHE g_style_cache_quark () /* This is the amount of entries that will be allowed per * stylable object. * * In the usual case, objects share rules and * so we don't really need to allocate more than one space in * the cache per object, but in case of the pathological case * of every stylable object having a unique style rule that * applies only to itself, we allow 6 per object. * * e.g. normal, hover, active, checked, focus + 1 extra */ #define MX_STYLE_CACHE_SIZE 6 /* A style cache entry is the unique string representing all the properties * that can be matched against in CSS, and the matched properties themselves. */ typedef struct { gchar *style_string; gint age; GHashTable *properties; } MxStyleCacheEntry; /* This is the per-stylable cache store. We need a reference back to the * parent style so that we can maintain the count of alive stylables. */ typedef struct { GList *styles; gchar *string; } MxStylableCache; typedef struct { GType value_type; gchar *value_name; GValue value; } StyleProperty; struct _MxStylePrivate { MxStyleSheet *stylesheet; GHashTable *style_hash; GHashTable *node_hash; gint alive_stylables; GQueue *cached_matches; GHashTable *cache_hash; gint age; }; static guint style_signals[LAST_SIGNAL] = { 0, }; static MxStyle *default_style = NULL; G_DEFINE_TYPE (MxStyle, mx_style, G_TYPE_OBJECT); static GQuark g_style_error_quark (void) { return g_quark_from_static_string ("mx-style-error-quark"); } static GQuark g_style_cache_quark (void) { return g_quark_from_static_string ("mx-style-cache-quark"); } static gboolean mx_style_real_load_from_file (MxStyle *style, const gchar *filename, GError **error, gint priority) { MxStylePrivate *priv; GError *internal_error; g_return_val_if_fail (MX_IS_STYLE (style), FALSE); g_return_val_if_fail (filename != NULL, FALSE); priv = MX_STYLE (style)->priv; if (!g_file_test (filename, G_FILE_TEST_IS_REGULAR)) { internal_error = g_error_new (MX_STYLE_ERROR, MX_STYLE_ERROR_INVALID_FILE, "Invalid theme file '%s'", filename); g_propagate_error (error, internal_error); return FALSE; } if (!priv->stylesheet) priv->stylesheet = mx_style_sheet_new (); mx_style_sheet_add_from_file (priv->stylesheet, filename, NULL); /* Increment the age so we know if a style cache entry is valid */ priv->age ++; g_signal_emit (style, style_signals[CHANGED], 0, NULL); return TRUE; } /** * mx_style_load_from_file: * @style: a #MxStyle * @filename: filename of the style sheet to load * @error: a #GError or #NULL * * Load style information from the specified file. * * returns: TRUE if the style information was loaded successfully. Returns * FALSE on error. */ gboolean mx_style_load_from_file (MxStyle *style, const gchar *filename, GError **error) { return mx_style_real_load_from_file (style, filename, error, 0); } static void mx_style_load (MxStyle *style) { const gchar *env_var; gchar *rc_file = NULL; GError *error; env_var = g_getenv ("MX_RC_FILE"); if (env_var && *env_var) rc_file = g_strdup (env_var); if (!rc_file) rc_file = g_build_filename (PACKAGE_DATA_DIR, "mx", "style", "default.css", NULL); error = NULL; if (g_file_test (rc_file, G_FILE_TEST_EXISTS)) { /* load the default theme with lowest priority */ if (!mx_style_real_load_from_file (style, rc_file, &error, 0)) { g_critical ("Unable to load resource file '%s': %s", rc_file, error->message); g_error_free (error); } } g_free (rc_file); } static MxStyleCacheEntry * mx_style_cache_entry_new (const gchar *style_string, GHashTable *properties, gint age) { MxStyleCacheEntry *entry = g_slice_new (MxStyleCacheEntry); entry->style_string = g_strdup (style_string); entry->properties = properties; entry->age = age; return entry; } static void mx_style_cache_entry_free (MxStyleCacheEntry *entry, gboolean free_struct) { g_free (entry->style_string); g_hash_table_unref (entry->properties); if (free_struct) g_slice_free (MxStyleCacheEntry, entry); } static void mx_style_finalize (GObject *gobject) { MxStylePrivate *priv = MX_STYLE (gobject)->priv; g_hash_table_unref (priv->cache_hash); while (g_queue_get_length (priv->cached_matches)) mx_style_cache_entry_free (g_queue_pop_head (priv->cached_matches), TRUE); g_queue_free (priv->cached_matches); G_OBJECT_CLASS (mx_style_parent_class)->finalize (gobject); } static void mx_style_class_init (MxStyleClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); g_type_class_add_private (klass, sizeof (MxStylePrivate)); gobject_class->finalize = mx_style_finalize; /** * MxStyle::changed: * * Indicates that the style data has changed in some way. For example, a new * stylesheet may have been loaded. */ style_signals[CHANGED] = g_signal_new ("changed", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (MxStyleClass, changed), NULL, NULL, _mx_marshal_VOID__VOID, G_TYPE_NONE, 0); } static void mx_style_init (MxStyle *style) { MxStylePrivate *priv; style->priv = priv = MX_STYLE_GET_PRIVATE (style); priv->cached_matches = g_queue_new (); priv->cache_hash = g_hash_table_new (g_str_hash, g_str_equal); mx_style_load (style); } /** * mx_style_new: * * Creates a new #MxStyle object. This must be freed using #g_object_unref * when no longer required. * * Returns: a newly allocated #MxStyle */ MxStyle * mx_style_new (void) { return g_object_new (MX_TYPE_STYLE, NULL); } /** * mx_style_get_default: * * Return the default MxStyle object. This includes the current theme (if * any). * * Returns: (transfer none): a #MxStyle object. This must not be freed or * unref'd by applications */ MxStyle * mx_style_get_default (void) { if (G_LIKELY (default_style)) return default_style; default_style = g_object_new (MX_TYPE_STYLE, NULL); return default_style; } static void mx_style_transform_css_value (MxStyleSheetValue *css_value, MxStylable *stylable, GParamSpec *pspec, GValue *value) { if (pspec->value_type == G_TYPE_INT) { g_value_init (value, pspec->value_type); if (css_value->string) { gint number = atoi (css_value->string); if (g_str_equal (g_param_spec_get_name (pspec), "font-size") && g_str_has_suffix (css_value->string, "pt")) { ClutterBackend *backend = clutter_get_default_backend (); gdouble res = clutter_backend_get_resolution (backend); number = number * res / 72.0; } g_value_set_int (value, number); } else g_value_set_int (value, 0); } else if (pspec->value_type == G_TYPE_UINT) { g_value_init (value, pspec->value_type); if (css_value->string) g_value_set_uint (value, atoi (css_value->string)); else g_value_set_uint (value, 0); } else if (pspec->value_type == MX_TYPE_BORDER_IMAGE) { g_value_init (value, pspec->value_type); mx_border_image_set_from_string (value, css_value->string, css_value->source); } else if (pspec->value_type == MX_TYPE_FONT_WEIGHT) { g_value_init (value, pspec->value_type); mx_font_weight_set_from_string (value, css_value->string); } else if (pspec->value_type == G_TYPE_STRING) { gchar *stripped, *original; gint len; g_value_init (value, pspec->value_type); if (!g_strcmp0 (css_value->string, "none")) { g_value_set_string (value, NULL); return; } original = g_strdup (css_value->string); len = strlen (original); if ((original[0] == '\'' && original[len -1] == '\'') || (original[0] == '\"' && original[len -1] == '\"')) { stripped = original + 1; original[len -1] = '\0'; } else stripped = original; g_value_set_string (value, stripped); g_free (original); } else if (g_type_is_a (pspec->value_type, G_TYPE_ENUM)) { GEnumValue *enum_value; GEnumClass *class; g_value_init (value, pspec->value_type); class = g_type_class_ref (pspec->value_type); enum_value = g_enum_get_value_by_nick (class, css_value->string); if (!enum_value) { g_warning ("Error setting property \"%s\" on \"%s\", could" " not transform \"%s\" from string to type %s", pspec->name, G_OBJECT_CLASS_NAME(G_OBJECT_GET_CLASS (stylable)), css_value->string, g_type_name (pspec->value_type)); } else { g_value_set_enum (value, enum_value->value); } g_type_class_unref (class); } else { GValue strval = { 0, }; g_value_init (value, pspec->value_type); g_value_init (&strval, G_TYPE_STRING); g_value_set_string (&strval, css_value->string); if (!g_value_transform (&strval, value)) { g_warning ("Error setting property \"%s\" on \"%s\", could" " not transform \"%s\" from string to type %s", pspec->name, G_OBJECT_CLASS_NAME(G_OBJECT_GET_CLASS (stylable)), css_value->string, g_type_name (pspec->value_type)); } g_value_unset (&strval); } } static const gchar* mx_style_normalize_property_name (const gchar *name) { /* gobject properties cannot start with a '-', but custom CSS properties * must be prefixed with '-' + vendor identifier. Therefore, the custom * style properties in mx are installed with "x-" */ if (!name) return NULL; if (strncmp (name, "x-mx", 4) == 0) return &name[1]; else return name; } static void mx_style_cache_weak_ref_cb (gpointer data, GObject *old_object) { MxStylableCache *cache = data; GList *style_link = g_list_find (cache->styles, old_object); if (style_link) cache->styles = g_list_delete_link (cache->styles, style_link); else g_warning (G_STRLOC ": Weak unref on a stylable with no style reference"); } static void mx_style_stylable_cache_free (MxStylableCache *cache) { /* If there are still styles referencing this stylable, decrement their * count of alive stylables and remove the weak reference. */ while (cache->styles) { MxStyle *style = cache->styles->data; MxStylePrivate *priv = style->priv; g_object_weak_unref (G_OBJECT (style), mx_style_cache_weak_ref_cb, cache); priv->alive_stylables --; MX_NOTE (STYLE_CACHE, "(%p) Alive stylables: %d", style, priv->alive_stylables); cache->styles = g_list_delete_link (cache->styles, cache->styles); } g_free (cache->string); g_slice_free (MxStylableCache, cache); } void _mx_style_invalidate_cache (MxStylable *stylable) { GObject *object = G_OBJECT (stylable); MxStylableCache *cache = g_object_get_qdata (object, MX_STYLE_CACHE); /* Reset the cache string */ if (cache) { g_free (cache->string); cache->string = NULL; } } static GHashTable * mx_style_get_style_sheet_properties (MxStyle *style, MxStylable *stylable) { GList *entry_link; MxStylableCache *cache; MxStyleCacheEntry *entry = NULL; MxStylePrivate *priv = style->priv; /* see if we have a cached style and return that if possible */ cache = g_object_get_qdata (G_OBJECT (stylable), MX_STYLE_CACHE); if (cache) { /* Make sure that the style string is up-to-date. We set this * to NULL when invalidating the stylable's cache. */ if (!cache->string) cache->string = _mx_stylable_get_style_string (stylable); /* Check that the stylable has a reference to us. If the stylable * cache struct was created by another style, we need to add ourselves * to the list. */ if (!g_list_find (cache->styles, style)) { cache->styles = g_list_prepend (cache->styles, style); g_object_weak_ref (G_OBJECT (style), mx_style_cache_weak_ref_cb, cache); priv->alive_stylables ++; } } else { /* This is the first time this stylable has tried to get style * properties, initialise a cache. */ cache = g_slice_new0 (MxStylableCache); cache->string = _mx_stylable_get_style_string (stylable); cache->styles = g_list_prepend (NULL, style); /* Increase the alive-stylables count and add a weak reference so we * can remove it. */ priv->alive_stylables ++; g_object_weak_ref (G_OBJECT (style), mx_style_cache_weak_ref_cb, cache); MX_NOTE (STYLE_CACHE, "(%p) Alive stylables: %d", style, priv->alive_stylables); /* Use qdata to associate the cache entry with the stylable object */ g_object_set_qdata_full (G_OBJECT (stylable), MX_STYLE_CACHE, cache, (GDestroyNotify)mx_style_stylable_cache_free); } if ((entry_link = g_hash_table_lookup (priv->cache_hash, cache->string))) { entry = entry_link->data; /* If the entry is old, remove it from the cache */ if (entry->age != priv->age) { g_hash_table_remove (priv->cache_hash, entry->style_string); g_queue_delete_link (priv->cached_matches, entry_link); mx_style_cache_entry_free (entry, TRUE); entry = NULL; } /* As the cache is rarely emptied, FIFO is good enough for the * eviction strategy (preferring speedy retrieval to efficient * eviction) * * If we did want LRU though, for example, here would be the place * to do it. You could remove the link from the queue using * g_queue_unlink() and reinsert it with g_queue_push_head_link() */ } /* No cached style properties were found, or the entry found is out of date, * so look them up from the style-sheet and (re-)add them to the cache. */ if (!entry || (entry->age != priv->age)) { /* Look up style properties */ GHashTable *properties = mx_style_sheet_get_properties (priv->stylesheet, stylable); /* Append this to the style cache */ entry = mx_style_cache_entry_new (cache->string, properties, priv->age); g_queue_push_head (priv->cached_matches, entry); g_hash_table_insert (priv->cache_hash, entry->style_string, priv->cached_matches->head); /* Shrink the cache if its grown too large */ while (g_queue_get_length (priv->cached_matches) > (priv->alive_stylables * MX_STYLE_CACHE_SIZE)) { MxStyleCacheEntry *old_entry = g_queue_pop_tail (priv->cached_matches); g_hash_table_remove (priv->cache_hash, old_entry->style_string); mx_style_cache_entry_free (old_entry, TRUE); } MX_NOTE (STYLE_CACHE, "(%p) Cache size: %d, (Max-size: %d)", style, g_queue_get_length (priv->cached_matches), priv->alive_stylables * MX_STYLE_CACHE_SIZE); } return entry->properties ? g_hash_table_ref (entry->properties) : NULL; } /** * mx_style_get_property: * @style: the style data store object * @stylable: a stylable to retreive the data for * @pspec: a #GParamSpec describing the property required * @value: (out): a #GValue to place the return value in * * Requests the property described in @pspec for the specified stylable */ void mx_style_get_property (MxStyle *style, MxStylable *stylable, GParamSpec *pspec, GValue *value) { MxStylePrivate *priv; g_return_if_fail (MX_IS_STYLE (style)); g_return_if_fail (MX_IS_STYLABLE (stylable)); g_return_if_fail (pspec != NULL); g_return_if_fail (value != NULL); priv = style->priv; /* look up the property in the css */ if (priv->stylesheet) { MxStyleSheetValue *css_value; GHashTable *properties; properties = mx_style_get_style_sheet_properties (style, stylable); css_value = g_hash_table_lookup (properties, mx_style_normalize_property_name (pspec->name)); if (!css_value) { mx_stylable_get_default_value (stylable, pspec->name, value); } else mx_style_transform_css_value (css_value, stylable, pspec, value); g_hash_table_unref (properties); } } /** * mx_style_get_valist: * @style: a #MxStyle * @stylable: a #MxStylable * @first_property_name: name of the first property to get * @va_args: return location for the first property, followed optionally * by more name/return location pairs, followed by %NULL * * Gets the style properties for @stylable from @style. * * Please refer to mx_style_get() for further information. */ void mx_style_get_valist (MxStyle *style, MxStylable *stylable, const gchar *first_property_name, va_list va_args) { MxStylePrivate *priv; const gchar *name = first_property_name; gboolean values_set = FALSE; g_return_if_fail (MX_IS_STYLE (style)); g_return_if_fail (MX_IS_STYLABLE (stylable)); g_return_if_fail (style->priv != NULL); priv = style->priv; /* look up the property in the css */ if (priv->stylesheet) { GHashTable *properties; properties = mx_style_get_style_sheet_properties (style, stylable); while (name) { GValue value = { 0, }; GParamSpec *pspec = mx_stylable_find_property (stylable, name); gchar *error; MxStyleSheetValue *css_value; if (!pspec) { g_critical ("No style property \"%s\" installed on object of" " type \"%s\".", name, G_OBJECT_CLASS_NAME (G_OBJECT_GET_CLASS (stylable))); break; } css_value = g_hash_table_lookup (properties, mx_style_normalize_property_name (name)); if (!css_value) { mx_stylable_get_default_value (stylable, pspec->name, &value); } else mx_style_transform_css_value (css_value, stylable, pspec, &value); G_VALUE_LCOPY (&value, va_args, 0, &error); if (error) { g_warning ("%s: %s", G_STRLOC, error); g_free (error); g_value_unset (&value); break; } g_value_unset (&value); name = va_arg (va_args, gchar*); } values_set = TRUE; g_hash_table_unref (properties); } if (!values_set) { /* Set the remaining properties to their default values * even if broken out of the above loop. */ while (name) { GValue value = { 0, }; gchar *error = NULL; mx_stylable_get_default_value (stylable, name, &value); G_VALUE_LCOPY (&value, va_args, 0, &error); if (error) { g_warning ("%s: %s", G_STRLOC, error); g_free (error); g_value_unset (&value); break; } g_value_unset (&value); name = va_arg (va_args, gchar*); } } } /** * mx_style_get: * @style: a #MxStyle * @stylable: a #MxStylable * @first_property_name: name of the first property to get * @Varargs: return location for the first property, followed optionally * by more name/return location pairs, followed by %NULL * * Gets the style properties for @stylable from @style. * * In general, a copy is made of the property contents and the caller * is responsible for freeing the memory in the appropriate manner for * the property type. */ void mx_style_get (MxStyle *style, MxStylable *stylable, const gchar *first_property_name, ...) { va_list va_args; g_return_if_fail (MX_IS_STYLE (style)); g_return_if_fail (first_property_name != NULL); va_start (va_args, first_property_name); mx_style_get_valist (style, stylable, first_property_name, va_args); va_end (va_args); } mx-1.4.7/mx/mx-style.h000066400000000000000000000067761201047117600145450ustar00rootroot00000000000000/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright 2009 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * */ #if !defined(MX_H_INSIDE) && !defined(MX_COMPILATION) #error "Only can be included directly.h" #endif #ifndef __MX_STYLE_H__ #define __MX_STYLE_H__ #include G_BEGIN_DECLS #define MX_TYPE_STYLE (mx_style_get_type ()) #define MX_STYLE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MX_TYPE_STYLE, MxStyle)) #define MX_IS_STYLE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MX_TYPE_STYLE)) #define MX_STYLE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MX_TYPE_STYLE, MxStyleClass)) #define MX_IS_STYLE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MX_TYPE_STYLE)) #define MX_STYLE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MX_TYPE_STYLE, MxStyleClass)) typedef struct _MxStyle MxStyle; typedef struct _MxStylePrivate MxStylePrivate; typedef struct _MxStyleClass MxStyleClass; /** * MxStylable: * * This is an opaque structure whose members cannot be directly accessed. */ /* forward declaration */ typedef struct _MxStylable MxStylable; /* dummy typedef */ typedef struct _MxStylableIface MxStylableIface; typedef enum { /*< prefix=MX_STYLE_ERROR >*/ MX_STYLE_ERROR_INVALID_FILE } MxStyleError; /** * MxStyle: * * The contents of this structure is private and should only be accessed using * the provided API. */ struct _MxStyle { /*< private >*/ GObject parent_instance; MxStylePrivate *priv; }; struct _MxStyleClass { GObjectClass parent_class; void (* changed) (MxStyle *style); /* padding for future expansion */ void (*_padding_0) (void); void (*_padding_1) (void); void (*_padding_2) (void); void (*_padding_3) (void); void (*_padding_4) (void); }; GType mx_style_get_type (void) G_GNUC_CONST; MxStyle *mx_style_get_default (void); MxStyle *mx_style_new (void); gboolean mx_style_load_from_file (MxStyle *style, const gchar *filename, GError **error); void mx_style_get_property (MxStyle *style, MxStylable *stylable, GParamSpec *pspec, GValue *value); void mx_style_get (MxStyle *style, MxStylable *stylable, const gchar *first_property_name, ...) G_GNUC_NULL_TERMINATED; void mx_style_get_valist (MxStyle *style, MxStylable *stylable, const gchar *first_property_name, va_list va_args); G_END_DECLS #endif /* __MX_STYLE_H__ */ mx-1.4.7/mx/mx-table-child.c000066400000000000000000000535121201047117600155360ustar00rootroot00000000000000/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * mx-table-child.h: Table child implementation * * Copyright 2008, 2009 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * Boston, MA 02111-1307, USA. * * Written by: Thomas Wood * */ #include "mx-table-child.h" #include "mx-private.h" #include #include /* * ClutterChildMeta Implementation */ /** * SECTION:mx-table-child * @short_description: The child property store for #MxTable * * The #ClutterChildMeta implementation for the #MxTable container widget. * */ enum { CHILD_PROP_0, CHILD_PROP_COLUMN, CHILD_PROP_ROW, CHILD_PROP_COLUMN_SPAN, CHILD_PROP_ROW_SPAN, CHILD_PROP_X_EXPAND, CHILD_PROP_Y_EXPAND, CHILD_PROP_X_ALIGN, CHILD_PROP_Y_ALIGN, CHILD_PROP_X_FILL, CHILD_PROP_Y_FILL, }; G_DEFINE_TYPE (MxTableChild, mx_table_child, CLUTTER_TYPE_CHILD_META); static void table_child_set_property (GObject *gobject, guint prop_id, const GValue *value, GParamSpec *pspec) { MxTableChild *child = MX_TABLE_CHILD (gobject); MxTable *table = MX_TABLE (CLUTTER_CHILD_META(gobject)->container); switch (prop_id) { case CHILD_PROP_COLUMN: child->col = g_value_get_int (value); _mx_table_update_row_col (table, -1, child->col); clutter_actor_queue_relayout (CLUTTER_ACTOR (table)); break; case CHILD_PROP_ROW: child->row = g_value_get_int (value); _mx_table_update_row_col (table, child->row, -1); clutter_actor_queue_relayout (CLUTTER_ACTOR (table)); break; case CHILD_PROP_COLUMN_SPAN: child->col_span = g_value_get_int (value); clutter_actor_queue_relayout (CLUTTER_ACTOR (table)); break; case CHILD_PROP_ROW_SPAN: child->row_span = g_value_get_int (value); clutter_actor_queue_relayout (CLUTTER_ACTOR (table)); break; case CHILD_PROP_X_EXPAND: child->x_expand = g_value_get_boolean (value); clutter_actor_queue_relayout (CLUTTER_ACTOR (table)); break; case CHILD_PROP_Y_EXPAND: child->y_expand = g_value_get_boolean (value); clutter_actor_queue_relayout (CLUTTER_ACTOR (table)); break; case CHILD_PROP_X_ALIGN: switch (g_value_get_enum (value)) { case MX_ALIGN_START: child->x_align = 0.0; break; case MX_ALIGN_MIDDLE: child->x_align = 0.5; break; case MX_ALIGN_END: child->x_align = 1.0; break; } clutter_actor_queue_relayout (CLUTTER_ACTOR (table)); break; case CHILD_PROP_Y_ALIGN: switch (g_value_get_enum (value)) { case MX_ALIGN_START: child->y_align = 0.0; break; case MX_ALIGN_MIDDLE: child->y_align = 0.5; break; case MX_ALIGN_END: child->y_align = 1.0; break; } clutter_actor_queue_relayout (CLUTTER_ACTOR (table)); break; case CHILD_PROP_X_FILL: child->x_fill = g_value_get_boolean (value); clutter_actor_queue_relayout (CLUTTER_ACTOR (table)); break; case CHILD_PROP_Y_FILL: child->y_fill = g_value_get_boolean (value); clutter_actor_queue_relayout (CLUTTER_ACTOR (table)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); break; } } static void table_child_get_property (GObject *gobject, guint prop_id, GValue *value, GParamSpec *pspec) { MxTableChild *child = MX_TABLE_CHILD (gobject); switch (prop_id) { case CHILD_PROP_COLUMN: g_value_set_int (value, child->col); break; case CHILD_PROP_ROW: g_value_set_int (value, child->row); break; case CHILD_PROP_COLUMN_SPAN: g_value_set_int (value, child->col_span); break; case CHILD_PROP_ROW_SPAN: g_value_set_int (value, child->row_span); break; case CHILD_PROP_X_EXPAND: g_value_set_boolean (value, child->x_expand); break; case CHILD_PROP_Y_EXPAND: g_value_set_boolean (value, child->y_expand); break; case CHILD_PROP_X_ALIGN: if (child->x_align < 1.0/3.0) g_value_set_enum (value, MX_ALIGN_START); else if (child->x_align > 2.0/3.0) g_value_set_enum (value, MX_ALIGN_END); else g_value_set_enum (value, MX_ALIGN_MIDDLE); break; case CHILD_PROP_Y_ALIGN: if (child->y_align < 1.0/3.0) g_value_set_enum (value, MX_ALIGN_START); else if (child->y_align > 2.0/3.0) g_value_set_enum (value, MX_ALIGN_END); else g_value_set_enum (value, MX_ALIGN_MIDDLE); break; case CHILD_PROP_X_FILL: g_value_set_boolean (value, child->x_fill); break; case CHILD_PROP_Y_FILL: g_value_set_boolean (value, child->y_fill); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); break; } } static void mx_table_child_class_init (MxTableChildClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); GParamSpec *pspec; gobject_class->set_property = table_child_set_property; gobject_class->get_property = table_child_get_property; pspec = g_param_spec_int ("column", "Column Number", "The column the widget resides in", 0, G_MAXINT, 0, MX_PARAM_READWRITE); g_object_class_install_property (gobject_class, CHILD_PROP_COLUMN, pspec); pspec = g_param_spec_int ("row", "Row Number", "The row the widget resides in", 0, G_MAXINT, 0, MX_PARAM_READWRITE); g_object_class_install_property (gobject_class, CHILD_PROP_ROW, pspec); pspec = g_param_spec_int ("row-span", "Row Span", "The number of rows the widget should span", 1, G_MAXINT, 1, MX_PARAM_READWRITE); g_object_class_install_property (gobject_class, CHILD_PROP_ROW_SPAN, pspec); pspec = g_param_spec_int ("column-span", "Column Span", "The number of columns the widget should span", 1, G_MAXINT, 1, MX_PARAM_READWRITE); g_object_class_install_property (gobject_class, CHILD_PROP_COLUMN_SPAN, pspec); pspec = g_param_spec_boolean ("x-expand", "X Expand", "Whether the child should receive priority " "when the container is allocating spare space " "on the horizontal axis", TRUE, MX_PARAM_READWRITE); g_object_class_install_property (gobject_class, CHILD_PROP_X_EXPAND, pspec); pspec = g_param_spec_boolean ("y-expand", "Y Expand", "Whether the child should receive priority " "when the container is allocating spare space " "on the vertical axis", TRUE, MX_PARAM_READWRITE); g_object_class_install_property (gobject_class, CHILD_PROP_Y_EXPAND, pspec); pspec = g_param_spec_enum ("x-align", "X Alignment", "X alignment of the widget within the cell", MX_TYPE_ALIGN, MX_ALIGN_MIDDLE, MX_PARAM_READWRITE); g_object_class_install_property (gobject_class, CHILD_PROP_X_ALIGN, pspec); pspec = g_param_spec_enum ("y-align", "Y Alignment", "Y alignment of the widget within the cell", MX_TYPE_ALIGN, MX_ALIGN_MIDDLE, MX_PARAM_READWRITE); g_object_class_install_property (gobject_class, CHILD_PROP_Y_ALIGN, pspec); pspec = g_param_spec_boolean ("x-fill", "X Fill", "Whether the child should be allocated its " "entire available space, or whether it should " "be squashed and aligned.", TRUE, MX_PARAM_READWRITE); g_object_class_install_property (gobject_class, CHILD_PROP_X_FILL, pspec); pspec = g_param_spec_boolean ("y-fill", "Y Fill", "Whether the child should be allocated its " "entire available space, or whether it should " "be squashed and aligned.", TRUE, MX_PARAM_READWRITE); g_object_class_install_property (gobject_class, CHILD_PROP_Y_FILL, pspec); } static void mx_table_child_init (MxTableChild *self) { self->col_span = 1; self->row_span = 1; self->x_align = 0.5; self->y_align = 0.5; self->x_expand = TRUE; self->y_expand = TRUE; self->x_fill = TRUE; self->y_fill = TRUE; } static MxTableChild* get_child_meta (MxTable *table, ClutterActor *child) { MxTableChild *meta; meta = (MxTableChild*) clutter_container_get_child_meta (CLUTTER_CONTAINER (table), child); return meta; } /** * mx_table_child_get_column_span: * @table: an #MxTable * @child: a #ClutterActor * * Get the column span of the child. Defaults to 1. * * Returns: the column span of the child */ gint mx_table_child_get_column_span (MxTable *table, ClutterActor *child) { MxTableChild *meta; g_return_val_if_fail (MX_IS_TABLE (table), 0); g_return_val_if_fail (CLUTTER_IS_ACTOR (child), 0); meta = get_child_meta (table, child); return meta->col_span; } /** * mx_table_child_set_column_span: * @table: An #MxTable * @child: An #ClutterActor * @span: The number of columns to span * * Set the column span of the child. * */ void mx_table_child_set_column_span (MxTable *table, ClutterActor *child, gint span) { MxTableChild *meta; g_return_if_fail (MX_IS_TABLE (table)); g_return_if_fail (CLUTTER_IS_ACTOR (child)); g_return_if_fail (span >= 1); meta = get_child_meta (table, child); meta->col_span = span; clutter_actor_queue_relayout (child); } /** * mx_table_child_get_row_span: * @table: A #MxTable * @child: A #ClutterActor * * Get the row span of the child. Defaults to 1. * * Returns: the row span of the child */ gint mx_table_child_get_row_span (MxTable *table, ClutterActor *child) { MxTableChild *meta; g_return_val_if_fail (MX_IS_TABLE (table), 0); g_return_val_if_fail (CLUTTER_IS_ACTOR (child), 0); meta = get_child_meta (table, child); return meta->row_span; } /** * mx_table_child_set_row_span: * @table: A #MxTable * @child: A #ClutterActor * @span: the number of rows to span * * Set the row span of the child. * */ void mx_table_child_set_row_span (MxTable *table, ClutterActor *child, gint span) { MxTableChild *meta; g_return_if_fail (MX_IS_TABLE (table)); g_return_if_fail (CLUTTER_IS_ACTOR (child)); g_return_if_fail (span >= 1); meta = get_child_meta (table, child); meta->row_span = span; clutter_actor_queue_relayout (child); } /** * mx_table_child_get_x_fill: * @table: A #MxTable * @child: A #ClutterActor * * Get the x-fill state of the child * * Returns: #TRUE if the child is set to x-fill */ gboolean mx_table_child_get_x_fill (MxTable *table, ClutterActor *child) { MxTableChild *meta; g_return_val_if_fail (MX_IS_TABLE (table), 0); g_return_val_if_fail (CLUTTER_IS_ACTOR (child), 0); meta = get_child_meta (table, child); return meta->x_fill; } /** * mx_table_child_set_x_fill: * @table: A #MxTable * @child: A #ClutterActor * @fill: the fill state * * Set the fill state of the child on the x-axis. This will cause the child to * be allocated the maximum available space. * */ void mx_table_child_set_x_fill (MxTable *table, ClutterActor *child, gboolean fill) { MxTableChild *meta; g_return_if_fail (MX_IS_TABLE (table)); g_return_if_fail (CLUTTER_IS_ACTOR (child)); meta = get_child_meta (table, child); meta->x_fill = fill; clutter_actor_queue_relayout (child); } /** * mx_table_child_get_y_fill: * @table: A #MxTable * @child: A #ClutterActor * * Get the y-fill state of the child * * Returns: #TRUE if the child is set to y-fill */ gboolean mx_table_child_get_y_fill (MxTable *table, ClutterActor *child) { MxTableChild *meta; g_return_val_if_fail (MX_IS_TABLE (table), 0); g_return_val_if_fail (CLUTTER_IS_ACTOR (child), 0); meta = get_child_meta (table, child); return meta->y_fill; } /** * mx_table_child_set_y_fill: * @table: A #MxTable * @child: A #ClutterActor * @fill: the fill state * * Set the fill state of the child on the y-axis. This will cause the child to * be allocated the maximum available space. * */ void mx_table_child_set_y_fill (MxTable *table, ClutterActor *child, gboolean fill) { MxTableChild *meta; g_return_if_fail (MX_IS_TABLE (table)); g_return_if_fail (CLUTTER_IS_ACTOR (child)); meta = get_child_meta (table, child); meta->y_fill = fill; clutter_actor_queue_relayout (child); } /** * mx_table_child_get_x_expand: * @table: A #MxTable * @child: A #ClutterActor * * Get the x-expand property of the child * * Returns: #TRUE if the child is set to x-expand */ gboolean mx_table_child_get_x_expand (MxTable *table, ClutterActor *child) { MxTableChild *meta; g_return_val_if_fail (MX_IS_TABLE (table), 0); g_return_val_if_fail (CLUTTER_IS_ACTOR (child), 0); meta = get_child_meta (table, child); return meta->x_expand; } /** * mx_table_child_set_x_expand: * @table: A #MxTable * @child: A #ClutterActor * @expand: the new value of the x expand child property * * Set x-expand on the child. This causes the column which the child * resides in to be allocated any extra space if the allocation of the table is * larger than the preferred size. * */ void mx_table_child_set_x_expand (MxTable *table, ClutterActor *child, gboolean expand) { MxTableChild *meta; g_return_if_fail (MX_IS_TABLE (table)); g_return_if_fail (CLUTTER_IS_ACTOR (child)); meta = get_child_meta (table, child); meta->x_expand = expand; clutter_actor_queue_relayout (child); } /** * mx_table_child_set_y_expand: * @table: A #MxTable * @child: A #ClutterActor * @expand: the new value of the y-expand child property * * Set y-expand on the child. This causes the row which the child * resides in to be allocated any extra space if the allocation of the table is * larger than the preferred size. * */ void mx_table_child_set_y_expand (MxTable *table, ClutterActor *child, gboolean expand) { MxTableChild *meta; g_return_if_fail (MX_IS_TABLE (table)); g_return_if_fail (CLUTTER_IS_ACTOR (child)); meta = get_child_meta (table, child); meta->y_expand = expand; clutter_actor_queue_relayout (child); } /** * mx_table_child_get_y_expand: * @table: A #MxTable * @child: A #ClutterActor * * Get the y-expand property of the child. * * Returns: #TRUE if the child is set to y-expand */ gboolean mx_table_child_get_y_expand (MxTable *table, ClutterActor *child) { MxTableChild *meta; g_return_val_if_fail (MX_IS_TABLE (table), 0); g_return_val_if_fail (CLUTTER_IS_ACTOR (child), 0); meta = get_child_meta (table, child); return meta->y_expand; } /** * mx_table_child_get_x_align: * @table: A #MxTable * @child: A #ClutterActor * * Get the x-align value of the child * * Returns: An #MxAlign value */ MxAlign mx_table_child_get_x_align (MxTable *table, ClutterActor *child) { MxTableChild *meta; g_return_val_if_fail (MX_IS_TABLE (table), 0); g_return_val_if_fail (CLUTTER_IS_ACTOR (child), 0); meta = get_child_meta (table, child); if (meta->x_align == 0.0) return MX_ALIGN_START; else if (meta->x_align == 1.0) return MX_ALIGN_END; else return MX_ALIGN_MIDDLE; } /** * mx_table_child_set_x_align: * @table: A #MxTable * @child: A #ClutterActor * @align: A #MxAlign value * * Set the alignment of the child within its cell. This will only have an effect * if the the x-fill property is FALSE. * */ void mx_table_child_set_x_align (MxTable *table, ClutterActor *child, MxAlign align) { MxTableChild *meta; g_return_if_fail (MX_IS_TABLE (table)); g_return_if_fail (CLUTTER_IS_ACTOR (child)); meta = get_child_meta (table, child); switch (align) { case MX_ALIGN_START: meta->x_align = 0.0; break; case MX_ALIGN_MIDDLE: meta->x_align = 0.5; break; case MX_ALIGN_END: meta->x_align = 1.0; break; } clutter_actor_queue_relayout (child); } /** * mx_table_child_get_y_align: * @table: A #MxTable * @child: A #ClutterActor * * Get the y-align value of the child * * Returns: An #MxAlign value */ MxAlign mx_table_child_get_y_align (MxTable *table, ClutterActor *child) { MxTableChild *meta; g_return_val_if_fail (MX_IS_TABLE (table), 0); g_return_val_if_fail (CLUTTER_IS_ACTOR (child), 0); meta = get_child_meta (table, child); if (meta->y_align == 0.0) return MX_ALIGN_START; else if (meta->y_align == 1.0) return MX_ALIGN_END; else return MX_ALIGN_MIDDLE; } /** * mx_table_child_set_y_align: * @table: A #MxTable * @child: A #ClutterActor * @align: A #MxAlign value * * Set the value of the y-align property. This will only have an effect if * y-fill value is set to FALSE. * */ void mx_table_child_set_y_align (MxTable *table, ClutterActor *child, MxAlign align) { MxTableChild *meta; g_return_if_fail (MX_IS_TABLE (table)); g_return_if_fail (CLUTTER_IS_ACTOR (child)); meta = get_child_meta (table, child); switch (align) { case MX_ALIGN_START: meta->y_align = 0.0; break; case MX_ALIGN_MIDDLE: meta->y_align = 0.5; break; case MX_ALIGN_END: meta->y_align = 1.0; break; } clutter_actor_queue_relayout (child); } /** * mx_table_child_get_column: * @table: an #MxTable * @child: a #ClutterActor * * Get the column of the child. * * Returns: the column of the child */ gint mx_table_child_get_column (MxTable *table, ClutterActor *child) { MxTableChild *meta; g_return_val_if_fail (MX_IS_TABLE (table), -1); g_return_val_if_fail (CLUTTER_IS_ACTOR (child), -1); meta = get_child_meta (table, child); return meta->col; } /** * mx_table_child_set_column: * @table: a #MxTable * @child: a #ClutterActor * @col: the column of the child * * Set the column of the child */ void mx_table_child_set_column (MxTable *table, ClutterActor *child, gint col) { MxTableChild *meta; g_return_if_fail (MX_IS_TABLE (table)); g_return_if_fail (CLUTTER_IS_ACTOR (child)); meta = get_child_meta (table, child); if (meta->col == col) return; meta->col = col; _mx_table_update_row_col (table, -1, meta->col); clutter_actor_queue_relayout (CLUTTER_ACTOR (table)); } /** * mx_table_child_get_row: * @table: an #MxTable * @child: a #ClutterActor * * Get the row of the child. * * Returns: the row of the child */ gint mx_table_child_get_row (MxTable *table, ClutterActor *child) { MxTableChild *meta; g_return_val_if_fail (MX_IS_TABLE (table), -1); g_return_val_if_fail (CLUTTER_IS_ACTOR (child), -1); meta = get_child_meta (table, child); return meta->row; } /** * mx_table_child_set_row: * @table: a #MxTable * @child: a #ClutterActor * @row: the row of the child * * Set the row of the child */ void mx_table_child_set_row (MxTable *table, ClutterActor *child, gint row) { MxTableChild *meta; g_return_if_fail (MX_IS_TABLE (table)); g_return_if_fail (CLUTTER_IS_ACTOR (child)); meta = get_child_meta (table, child); if (meta->row == row) return; meta->row = row; _mx_table_update_row_col (table, meta->row, -1); clutter_actor_queue_relayout (CLUTTER_ACTOR (table)); } mx-1.4.7/mx/mx-table-child.h000066400000000000000000000131651201047117600155430ustar00rootroot00000000000000/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * mx-table-child.h: Table child implementation * * Copyright 2008, 2009 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * Boston, MA 02111-1307, USA. * * Written by: Thomas Wood * */ #if !defined(MX_H_INSIDE) && !defined(MX_COMPILATION) #error "Only can be included directly.h" #endif #ifndef __MX_TABLE_CHILD_H__ #define __MX_TABLE_CHILD_H__ #include #include #include #include G_BEGIN_DECLS #define MX_TYPE_TABLE_CHILD (mx_table_child_get_type ()) #define MX_TABLE_CHILD(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MX_TYPE_TABLE_CHILD, MxTableChild)) #define MX_IS_TABLE_CHILD(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MX_TYPE_TABLE_CHILD)) #define MX_TABLE_CHILD_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MX_TYPE_TABLE_CHILD, MxTableChildClass)) #define MX_IS_TABLE_CHILD_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MX_TYPE_TABLE_CHILD)) #define MX_TABLE_CHILD_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MX_TYPE_TABLE_CHILD, MxTableChildClass)) /** * MxTableChild: * * The contents of this structure is private and should only be accessed using * the provided API. */ typedef struct _MxTableChild MxTableChild; typedef struct _MxTableChildClass MxTableChildClass; struct _MxTableChildClass { ClutterChildMetaClass parent_class; /* padding for future expansion */ void (*_padding_0) (void); void (*_padding_1) (void); void (*_padding_2) (void); void (*_padding_3) (void); void (*_padding_4) (void); }; GType mx_table_child_get_type (void) G_GNUC_CONST; gint mx_table_child_get_column (MxTable *table, ClutterActor *child); void mx_table_child_set_column (MxTable *table, ClutterActor *child, gint col); gint mx_table_child_get_row (MxTable *table, ClutterActor *child); void mx_table_child_set_row (MxTable *table, ClutterActor *child, gint row); gint mx_table_child_get_column_span (MxTable *table, ClutterActor *child); void mx_table_child_set_column_span (MxTable *table, ClutterActor *child, gint span); gint mx_table_child_get_row_span (MxTable *table, ClutterActor *child); void mx_table_child_set_row_span (MxTable *table, ClutterActor *child, gint span); gboolean mx_table_child_get_x_fill (MxTable *table, ClutterActor *child); void mx_table_child_set_x_fill (MxTable *table, ClutterActor *child, gboolean fill); gboolean mx_table_child_get_y_fill (MxTable *table, ClutterActor *child); void mx_table_child_set_y_fill (MxTable *table, ClutterActor *child, gboolean fill); gboolean mx_table_child_get_x_expand (MxTable *table, ClutterActor *child); void mx_table_child_set_x_expand (MxTable *table, ClutterActor *child, gboolean expand); gboolean mx_table_child_get_y_expand (MxTable *table, ClutterActor *child); void mx_table_child_set_y_expand (MxTable *table, ClutterActor *child, gboolean expand); MxAlign mx_table_child_get_x_align (MxTable *table, ClutterActor *child); void mx_table_child_set_x_align (MxTable *table, ClutterActor *child, MxAlign align); MxAlign mx_table_child_get_y_align (MxTable *table, ClutterActor *child); void mx_table_child_set_y_align (MxTable *table, ClutterActor *child, MxAlign align); G_END_DECLS #endif /* __MX_TABLE_H__ */ mx-1.4.7/mx/mx-table.c000066400000000000000000001512721201047117600144570ustar00rootroot00000000000000/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * mx-table.c: Table layout widget * * Copyright 2008, 2009 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * Boston, MA 02111-1307, USA. * * Written by: Thomas Wood * */ /** * SECTION:mx-table * @short_description: A multi-child layout container based on rows * and columns * * #MxTable is a mult-child layout container based on a table arrangement * with rows and columns. #MxTable adds several child properties to its * children which control their position and size in the table. * * While other layouts (like #MxGrid) allow you to achieve * table-like effects, #MxTable is the only layout which allows * you to precisely (and easily) place elements at particular grid coordinates, * via mx_table_add_actor(). * *
* #MxTable, 3 rows by 3 columns * Notice how rectangles have only been placed in a few of * the cells inside the table: this would be very difficult to do * with any other layout, without having layouts within layouts within * layouts... * *
*/ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "mx-table.h" #include #include #include #include #include "mx-enum-types.h" #include "mx-marshal.h" #include "mx-private.h" #include "mx-table-child.h" #include "mx-stylable.h" #include "mx-focusable.h" enum { PROP_0, PROP_PADDING, PROP_COLUMN_SPACING, PROP_ROW_SPACING, PROP_ROW_COUNT, PROP_COL_COUNT, }; #define MX_TABLE_GET_PRIVATE(obj) \ (G_TYPE_INSTANCE_GET_PRIVATE ((obj), MX_TYPE_TABLE, MxTablePrivate)) typedef struct { guint expand : 1; guint shrink : 1; guint is_visible : 1; gfloat min_size; gfloat pref_size; gfloat final_size; } DimensionData; struct _MxTablePrivate { GList *children; guint ignore_css_col_spacing : 1; guint ignore_css_row_spacing : 1; gint col_spacing; gint row_spacing; gint visible_rows; gint visible_cols; gint n_rows; gint n_cols; gint active_row; gint active_col; GArray *columns; GArray *rows; MxFocusable *last_focus; }; static void mx_container_iface_init (ClutterContainerIface *iface); static void mx_focusable_iface_init (MxFocusableIface *iface); static void mx_stylable_iface_init (MxStylableIface *iface); G_DEFINE_TYPE_WITH_CODE (MxTable, mx_table, MX_TYPE_WIDGET, G_IMPLEMENT_INTERFACE (CLUTTER_TYPE_CONTAINER, mx_container_iface_init) G_IMPLEMENT_INTERFACE (MX_TYPE_STYLABLE, mx_stylable_iface_init) G_IMPLEMENT_INTERFACE (MX_TYPE_FOCUSABLE, mx_focusable_iface_init)); static ClutterActor* mx_table_find_actor_at (MxTable *table, int row, int column) { MxTablePrivate *priv; GList *l; priv = table->priv; for (l = priv->children; l; l = g_list_next (l)) { MxTableChild *child; ClutterActor *actor_child = CLUTTER_ACTOR (l->data); child = (MxTableChild *) clutter_container_get_child_meta (CLUTTER_CONTAINER (table), actor_child); if ((row >= child->row && row <= child->row + (child->row_span - 1)) && (column >= child->col && column <= child->col + (child->col_span - 1))) return actor_child; } return NULL; } static MxFocusable* mx_table_move_focus (MxFocusable *focusable, MxFocusDirection direction, MxFocusable *from) { MxTablePrivate *priv = MX_TABLE (focusable)->priv; MxTable *table = MX_TABLE (focusable); GList *l, *childlink; MxTableChild *child_meta; ClutterActor *child_actor; MxFocusable *focused; gint row, column; ClutterActor *found; /* find the current focus */ childlink = g_list_find (priv->children, from); if (!childlink) return NULL; priv->last_focus = from; child_actor = CLUTTER_ACTOR (childlink->data); child_meta = (MxTableChild *) clutter_container_get_child_meta (CLUTTER_CONTAINER (focusable), child_actor); /* find the next widget to focus */ switch (direction) { case MX_FOCUS_DIRECTION_NEXT: for (l = childlink->next; l; l = g_list_next (l)) { if (MX_IS_FOCUSABLE (l->data)) { focused = mx_focusable_accept_focus (MX_FOCUSABLE (l->data), MX_FOCUS_HINT_FIRST); if (focused) return focused; } } /* no next widgets to focus */ return NULL; case MX_FOCUS_DIRECTION_PREVIOUS: for (l = g_list_previous (childlink); l; l = g_list_previous (l)) { if (MX_IS_FOCUSABLE (l->data)) { focused = mx_focusable_accept_focus (MX_FOCUSABLE (l->data), MX_FOCUS_HINT_LAST); if (focused) return focused; } } /* no widget found in the previous position */ return NULL; case MX_FOCUS_DIRECTION_UP: /* move focus up */ row = child_meta->row - 1; column = child_meta->col; focused = NULL; while (!focused && row >= 0) { found = mx_table_find_actor_at (table, row, column); if (found) { if (MX_IS_FOCUSABLE (found)) { focused = mx_focusable_accept_focus (MX_FOCUSABLE (found), MX_FOCUS_HINT_FIRST); if (focused) break; } child_meta = (MxTableChild *) clutter_container_get_child_meta (CLUTTER_CONTAINER (focusable), found); /* row might not be the top row if @found is a spanned actor */ row = child_meta->row - 1; } else row --; } return focused; case MX_FOCUS_DIRECTION_DOWN: /* move focus down */ row = child_meta->row + child_meta->row_span; column = child_meta->col; focused = NULL; while (!focused && row < priv->n_rows) { found = mx_table_find_actor_at (table, row, column); if (found) { if (MX_IS_FOCUSABLE (found)) { focused = mx_focusable_accept_focus (MX_FOCUSABLE (found), MX_FOCUS_HINT_FIRST); if (focused) break; } child_meta = (MxTableChild *) clutter_container_get_child_meta (CLUTTER_CONTAINER (focusable), found); row = child_meta->row + child_meta->row_span; } else row ++; } return focused; case MX_FOCUS_DIRECTION_LEFT: /* move focus left */ row = child_meta->row; column = child_meta->col - 1; focused = NULL; while (!focused && column >= 0) { found = mx_table_find_actor_at (table, row, column); if (found) { if (MX_IS_FOCUSABLE (found)) { focused = mx_focusable_accept_focus (MX_FOCUSABLE (found), MX_FOCUS_HINT_FIRST); if (focused) break; } child_meta = (MxTableChild *) clutter_container_get_child_meta (CLUTTER_CONTAINER (focusable), found); /* col might not be the first column if @found is a spanned actor */ column = child_meta->col - 1; } else column --; } return focused; case MX_FOCUS_DIRECTION_RIGHT: /* move focus right */ row = child_meta->row; column = child_meta->col + child_meta->col_span; focused = NULL; while (!focused && column < priv->n_cols) { found = mx_table_find_actor_at (table, row, column); if (found) { if (MX_IS_FOCUSABLE (found)) { focused = mx_focusable_accept_focus (MX_FOCUSABLE (found), MX_FOCUS_HINT_FIRST); if (focused) break; } child_meta = (MxTableChild *) clutter_container_get_child_meta (CLUTTER_CONTAINER (focusable), found); column = child_meta->col + child_meta->col_span; } else column ++; } return focused; default: break; } return NULL; } static MxFocusable* mx_table_accept_focus (MxFocusable *focusable, MxFocusHint hint) { MxTablePrivate *priv = MX_TABLE (focusable)->priv; MxFocusable *return_focusable; GList* list, *l; return_focusable = NULL; /* find the first/last focusable widget */ switch (hint) { case MX_FOCUS_HINT_LAST: list = g_list_reverse (g_list_copy (priv->children)); break; case MX_FOCUS_HINT_PRIOR: if (priv->last_focus) { list = g_list_copy (g_list_find (priv->children, priv->last_focus)); if (list) break; } /* This intentionally runs into the next switch case */ default: case MX_FOCUS_HINT_FIRST: list = g_list_copy (priv->children); break; } for (l = list; l; l = g_list_next (l)) { if (MX_IS_FOCUSABLE (l->data)) { return_focusable = mx_focusable_accept_focus (MX_FOCUSABLE (l->data), hint); if (return_focusable) break; } } g_list_free (list); return return_focusable; } static void mx_focusable_iface_init (MxFocusableIface *iface) { iface->move_focus = mx_table_move_focus; iface->accept_focus = mx_table_accept_focus; } /* * ClutterContainer Implementation */ static void mx_container_add_actor (ClutterContainer *container, ClutterActor *actor) { MxTablePrivate *priv = MX_TABLE (container)->priv; clutter_actor_set_parent (actor, CLUTTER_ACTOR (container)); priv->children = g_list_append (priv->children, actor); /* default position of the actor is 0, 0 */ _mx_table_update_row_col (MX_TABLE (container), 0, 0); clutter_actor_queue_relayout (CLUTTER_ACTOR (container)); g_signal_emit_by_name (container, "actor-added", actor); } static void mx_container_remove_actor (ClutterContainer *container, ClutterActor *actor) { MxTablePrivate *priv = MX_TABLE (container)->priv; gint rows, cols; GList *item = NULL; MxTableChild *meta; GList *l; item = g_list_find (priv->children, actor); if (item == NULL) { g_warning ("Widget of type '%s' is not a child of container of type '%s'", g_type_name (G_OBJECT_TYPE (actor)), g_type_name (G_OBJECT_TYPE (container))); return; } g_object_ref (actor); if ((ClutterActor *)priv->last_focus == actor) priv->last_focus = NULL; priv->children = g_list_delete_link (priv->children, item); clutter_actor_unparent (actor); /* update row/column count */ rows = 0; cols = 0; for (l = priv->children; l; l = l->next) { ClutterActor *child = CLUTTER_ACTOR (l->data); meta = (MxTableChild *) clutter_container_get_child_meta (container, child); rows = MAX (rows, meta->row + meta->row_span); cols = MAX (cols, meta->col + meta->col_span); } priv->n_rows = rows; priv->n_cols = cols; clutter_actor_queue_relayout (CLUTTER_ACTOR (container)); g_signal_emit_by_name (container, "actor-removed", actor); g_object_unref (actor); } static void mx_container_foreach (ClutterContainer *container, ClutterCallback callback, gpointer callback_data) { MxTablePrivate *priv = MX_TABLE (container)->priv; g_list_foreach (priv->children, (GFunc) callback, callback_data); } static void mx_container_lower (ClutterContainer *container, ClutterActor *actor, ClutterActor *sibling) { gint i; GList *c, *position, *actor_link = NULL; MxTablePrivate *priv = MX_TABLE (container)->priv; if (priv->children && (priv->children->data == actor)) return; position = priv->children; for (c = priv->children, i = 0; c; c = c->next, i++) { if (c->data == actor) actor_link = c; if (c->data == sibling) position = c; } if (!actor_link) { g_warning (G_STRLOC ": Actor of type '%s' is not a child of container " "of type '%s'", g_type_name (G_OBJECT_TYPE (actor)), g_type_name (G_OBJECT_TYPE (container))); return; } priv->children = g_list_delete_link (priv->children, actor_link); priv->children = g_list_insert_before (priv->children, position, actor); clutter_actor_queue_redraw (CLUTTER_ACTOR (container)); } static void mx_container_raise (ClutterContainer *container, ClutterActor *actor, ClutterActor *sibling) { gint i; GList *c, *actor_link = NULL; gint position = -1; MxTablePrivate *priv = MX_TABLE (container)->priv; for (c = priv->children, i = 0; c; c = c->next, i++) { if (c->data == actor) actor_link = c; if (c->data == sibling) position = i; } if (!actor_link) { g_warning (G_STRLOC ": Actor of type '%s' is not a child of container " "of type '%s'", g_type_name (G_OBJECT_TYPE (actor)), g_type_name (G_OBJECT_TYPE (container))); return; } if (!actor_link->next) return; priv->children = g_list_delete_link (priv->children, actor_link); priv->children = g_list_insert (priv->children, actor, position); clutter_actor_queue_redraw (CLUTTER_ACTOR (container)); } static gint mx_table_depth_sort_cb (gconstpointer a, gconstpointer b) { gfloat depth_a = clutter_actor_get_depth ((ClutterActor *)a); gfloat depth_b = clutter_actor_get_depth ((ClutterActor *)b); if (depth_a < depth_b) return -1; else if (depth_a > depth_b) return 1; else return 0; } static void mx_container_sort_depth_order (ClutterContainer *container) { MxTablePrivate *priv = MX_TABLE (container)->priv; priv->children = g_list_sort (priv->children, mx_table_depth_sort_cb); clutter_actor_queue_redraw (CLUTTER_ACTOR (container)); } static void mx_container_iface_init (ClutterContainerIface *iface) { iface->add = mx_container_add_actor; iface->remove = mx_container_remove_actor; iface->foreach = mx_container_foreach; iface->lower = mx_container_lower; iface->raise = mx_container_raise; iface->sort_depth_order = mx_container_sort_depth_order; iface->child_meta_type = MX_TYPE_TABLE_CHILD; } /* MxStylable implementation */ static void mx_stylable_iface_init (MxStylableIface *iface) { static gboolean is_initialized = FALSE; if (G_UNLIKELY (is_initialized == FALSE)) { GParamSpec *pspec; is_initialized = TRUE; pspec = g_param_spec_uint ("x-mx-column-spacing", "Column Spacing", "The size of the column spacing", 0, G_MAXUINT, 0, MX_PARAM_READWRITE); mx_stylable_iface_install_property (iface, MX_TYPE_TABLE, pspec); pspec = g_param_spec_uint ("x-mx-row-spacing", "Row Spacing", "The size of the row spacing", 0, G_MAXUINT, 0, MX_PARAM_READWRITE); mx_stylable_iface_install_property (iface, MX_TYPE_TABLE, pspec); } } /* MxTable Class Implementation */ static void mx_table_set_property (GObject *gobject, guint prop_id, const GValue *value, GParamSpec *pspec) { MxTable *table = MX_TABLE (gobject); switch (prop_id) { case PROP_COLUMN_SPACING: mx_table_set_column_spacing (table, g_value_get_int (value)); break; case PROP_ROW_SPACING: mx_table_set_row_spacing (table, g_value_get_int (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); break; } } static void mx_table_get_property (GObject *gobject, guint prop_id, GValue *value, GParamSpec *pspec) { MxTablePrivate *priv = MX_TABLE (gobject)->priv; switch (prop_id) { case PROP_COLUMN_SPACING: g_value_set_int (value, priv->col_spacing); break; case PROP_ROW_SPACING: g_value_set_int (value, priv->row_spacing); break; case PROP_COL_COUNT: g_value_set_int (value, priv->n_cols); break; case PROP_ROW_COUNT: g_value_set_int (value, priv->n_rows); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); break; } } static void mx_table_finalize (GObject *gobject) { MxTablePrivate *priv = MX_TABLE (gobject)->priv; g_array_free (priv->columns, TRUE); g_array_free (priv->rows, TRUE); G_OBJECT_CLASS (mx_table_parent_class)->finalize (gobject); } static void mx_table_dispose (GObject *gobject) { MxTablePrivate *priv = MX_TABLE (gobject)->priv; /* destroy the children * clutter_actor_destroy() will call clutter_container_remove() which will * remove the children from the internal list */ while (priv->children) clutter_actor_destroy (CLUTTER_ACTOR (priv->children->data)); G_OBJECT_CLASS (mx_table_parent_class)->dispose (gobject); } #define CLAMP_TO_PIXEL(x) ((float)((int)(x))) static void mx_table_calculate_col_widths (MxTable *table, gint for_width) { gint i; MxTablePrivate *priv = table->priv; DimensionData *columns; GList *l; MxPadding padding; g_array_set_size (priv->columns, 0); g_array_set_size (priv->columns, priv->n_cols); columns = &g_array_index (priv->columns, DimensionData, 0); /* take off the padding values to calculate the allocatable width */ mx_widget_get_padding (MX_WIDGET (table), &padding); for_width -= (int)(padding.left + padding.right); /* Reset all the visible attributes for the columns */ priv->visible_cols = 0; for (i = 0; i < priv->n_cols; i++) columns[i].is_visible = FALSE; /* STAGE ONE: calculate column widths for non-spanned children */ for (l = priv->children; l; l = g_list_next (l)) { MxTableChild *meta; ClutterActor *child; DimensionData *col; gfloat c_min, c_pref; child = CLUTTER_ACTOR (l->data); if (!CLUTTER_ACTOR_IS_VISIBLE (child)) continue; meta = (MxTableChild *) clutter_container_get_child_meta (CLUTTER_CONTAINER (table), child); if (meta->col_span > 1) continue; col = &columns[meta->col]; /* If this child is visible, then its column is visible */ if (!col->is_visible) { col->is_visible = TRUE; priv->visible_cols++; } clutter_actor_get_preferred_width (child, -1, &c_min, &c_pref); col->min_size = MAX (col->min_size, c_min); col->final_size = col->pref_size = MAX (col->pref_size, c_pref); col->expand = MAX (col->expand, meta->x_expand); } /* STAGE TWO: take spanning children into account */ for (l = priv->children; l; l = g_list_next (l)) { MxTableChild *meta; ClutterActor *child; DimensionData *col; gfloat c_min, c_pref; gfloat min_width, pref_width; gint start_col, end_col; gint n_expand; child = CLUTTER_ACTOR (l->data); if (!CLUTTER_ACTOR_IS_VISIBLE (child)) continue; meta = (MxTableChild *) clutter_container_get_child_meta (CLUTTER_CONTAINER (table), child); if (meta->col_span < 2) continue; col = &columns[meta->col]; start_col = meta->col; end_col = meta->col + meta->col_span - 1; clutter_actor_get_preferred_width (child, -1, &c_min, &c_pref); /* check there is enough room for this actor */ min_width = 0; pref_width = 0; n_expand = 0; for (i = start_col; i <= end_col; i++) { min_width += columns[i].min_size; pref_width += columns[i].pref_size; if (columns[i].expand) { n_expand++; } /* If this child is visible, then the columns it spans are also visible */ if (!col->is_visible) { col->is_visible = TRUE; priv->visible_cols++; } } min_width += priv->col_spacing * (meta->col_span - 1); pref_width += priv->col_spacing * (meta->col_span - 1); /* see mx_table_calculate_row_heights() for comments */ /* (1) */ if (c_min > min_width) { /* (2) */ /* we can start from preferred width and decrease */ if (pref_width > c_min) { for (i = start_col; i <= end_col; i++) { columns[i].final_size = columns[i].pref_size; } while (pref_width > c_min) { for (i = start_col; i <= end_col; i++) { if (columns[i].final_size > columns[i].min_size) { columns[i].final_size--; pref_width--; } } } for (i = start_col; i <= end_col; i++) { columns[i].min_size = columns[i].final_size; } } else { /* (3) */ /* we can expand from preferred size */ gfloat expand_by; expand_by = c_pref - pref_width; for (i = start_col; i <= end_col; i++) { if (n_expand) { if (columns[i].expand) columns[i].min_size = columns[i].pref_size + expand_by / n_expand; } else { columns[i].min_size = columns[i].pref_size + expand_by / meta->col_span; } } } } } /* calculate final widths */ if (for_width >= 0) { gfloat min_width, pref_width; gint n_expand; min_width = 0; pref_width = 0; n_expand = 0; for (i = 0; i < priv->n_cols; i++) { pref_width += columns[i].pref_size; min_width += columns[i].min_size; if (columns[i].expand) n_expand++; } pref_width += priv->col_spacing * (priv->n_cols - 1); min_width += priv->col_spacing * (priv->n_cols - 1); if (for_width <= min_width) { /* erk, we can't shrink this! */ for (i = 0; i < priv->n_cols; i++) { columns[i].final_size = columns[i].min_size; } return; } if (for_width == pref_width) { /* perfect! */ for (i = 0; i < priv->n_cols; i++) { columns[i].final_size = columns[i].pref_size; } return; } /* for_width is between min_width and pref_width */ if (for_width < pref_width && for_width > min_width) { gfloat width; /* shrink columns until they reach min_width */ /* start with all columns at preferred size */ for (i = 0; i < priv->n_cols; i++) { columns[i].final_size = columns[i].pref_size; } width = pref_width; while (width > for_width) { for (i = 0; i < priv->n_cols; i++) { if (columns[i].final_size > columns[i].min_size) { columns[i].final_size--; width--; } } } return; } /* expand columns */ if (for_width > pref_width) { gfloat extra_width = for_width - pref_width; gint remaining; if (n_expand) remaining = (gint) extra_width % n_expand; else remaining = (gint) extra_width % priv->n_cols; for (i = 0; i < priv->n_cols; i++) { if (columns[i].expand) { if (n_expand) { columns[i].final_size = columns[i].pref_size + (extra_width / n_expand); } else { columns[i].final_size = columns[i].pref_size + (extra_width / priv->n_cols); } } else columns[i].final_size = columns[i].pref_size; } /* distribute the remainder among children */ i = 0; while (remaining) { columns[i].final_size++; i++; remaining--; } } } } static void mx_table_calculate_row_heights (MxTable *table, gint for_height) { MxTablePrivate *priv = MX_TABLE (table)->priv; GList *l; gint i; DimensionData *rows, *columns; MxPadding padding; mx_widget_get_padding (MX_WIDGET (table), &padding); /* take padding off available height */ for_height -= (int)(padding.top + padding.bottom); g_array_set_size (priv->rows, 0); g_array_set_size (priv->rows, priv->n_rows); rows = &g_array_index (priv->rows, DimensionData, 0); columns = &g_array_index (priv->columns, DimensionData, 0); /* Reset the visible rows */ priv->visible_rows = 0; for (i = 0; i < priv->n_rows; i++) rows[i].is_visible = FALSE; /* STAGE ONE: calculate row heights for non-spanned children */ for (l = priv->children; l; l = g_list_next (l)) { MxTableChild *meta; ClutterActor *child; DimensionData *row; gfloat c_min, c_pref; child = CLUTTER_ACTOR (l->data); if (!CLUTTER_ACTOR_IS_VISIBLE (child)) continue; meta = (MxTableChild *) clutter_container_get_child_meta (CLUTTER_CONTAINER (table), child); if (meta->row_span > 1) continue; row = &rows[meta->row]; /* If this child is visible, then its row is visible */ if (!row->is_visible) { row->is_visible = TRUE; priv->visible_rows++; } clutter_actor_get_preferred_height (child, columns[meta->col].final_size, &c_min, &c_pref); row->min_size = MAX (row->min_size, c_min); row->final_size = row->pref_size = MAX (row->pref_size, c_pref); row->expand = MAX (row->expand, meta->y_expand); } /* STAGE TWO: take spanning children into account */ for (l = priv->children; l; l = g_list_next (l)) { MxTableChild *meta; ClutterActor *child; gfloat c_min, c_pref; gfloat min_height, pref_height; gint start_row, end_row; gint n_expand; child = CLUTTER_ACTOR (l->data); if (!CLUTTER_ACTOR_IS_VISIBLE (child)) continue; meta = (MxTableChild *) clutter_container_get_child_meta (CLUTTER_CONTAINER (table), child); if (meta->row_span < 2) continue; start_row = meta->row; end_row = meta->row + meta->row_span - 1; clutter_actor_get_preferred_height (child, columns[meta->col].final_size, &c_min, &c_pref); /* check there is enough room for this actor */ min_height = 0; pref_height = 0; n_expand = 0; for (i = start_row; i <= end_row; i++) { min_height += rows[i].min_size; pref_height += rows[i].pref_size; if (rows[i].expand) { n_expand++; } /* If this actor is visible, then all the rows is spans are visible */ if (!rows[i].is_visible) { rows[i].is_visible = TRUE; priv->visible_rows++; } } min_height += priv->row_spacing * (meta->row_span - 1); pref_height += priv->row_spacing * (meta->row_span - 1); /* 1) If the minimum height of the rows spanned is less than the minimum * height of the child that is spanning them, then we must increase the * minimum height of the rows spanned. * * 2) If the preferred height of the spanned rows is more that the minimum * height of the spanning child, then we can start at this size and * decrease each row evenly. * * 3) If the preferred height of the rows is more than the minimum height * of the spanned child, then we can start at the preferred height and * expand. */ /* (1) */ if (c_min > min_height) { /* (2) */ /* we can start from preferred height and decrease */ if (pref_height > c_min) { for (i = start_row; i <= end_row; i++) { rows[i].final_size = rows[i].pref_size; } while (pref_height > c_min) { for (i = start_row; i <= end_row; i++) { if (rows[i].final_size > rows[i].min_size) { rows[i].final_size--; pref_height--; } } } for (i = start_row; i <= end_row; i++) { rows[i].min_size = rows[i].final_size; } } else { /* (3) */ /* we can expand from preferred size */ gfloat expand_by; expand_by = c_pref - pref_height; for (i = start_row; i <= end_row; i++) { if (n_expand) { if (rows[i].expand) rows[i].min_size = rows[i].pref_size + expand_by / n_expand; } else { rows[i].min_size = rows[i].pref_size + expand_by / meta->row_span; } } } } } /* calculate final heights */ if (for_height >= 0) { gfloat min_height, pref_height; gint n_expand; min_height = 0; pref_height = 0; n_expand = 0; for (i = 0; i < priv->n_rows; i++) { pref_height += rows[i].pref_size; min_height += rows[i].min_size; if (rows[i].expand) n_expand++; } pref_height += priv->row_spacing * (priv->n_rows - 1); min_height += priv->row_spacing * (priv->n_rows - 1); if (for_height <= min_height) { /* erk, we can't shrink this! */ for (i = 0; i < priv->n_rows; i++) { rows[i].final_size = rows[i].min_size; } return; } if (for_height == pref_height) { /* perfect! */ for (i = 0; i < priv->n_rows; i++) { rows[i].final_size = rows[i].pref_size; } return; } /* for_height is between min_height and pref_height */ if (for_height < pref_height && for_height > min_height) { gfloat height; /* shrink rows until they reach min_height */ /* start with all rows at preferred size */ for (i = 0; i < priv->n_rows; i++) { rows[i].final_size = rows[i].pref_size; } height = pref_height; while (height > for_height) { for (i = 0; i < priv->n_rows; i++) { if (rows[i].final_size > rows[i].min_size) { rows[i].final_size--; height--; } } } return; } /* expand rows */ if (for_height > pref_height) { gfloat extra_height = for_height - pref_height; gint remaining; if (n_expand) remaining = (gint) extra_height % n_expand; else remaining = (gint) extra_height % priv->n_rows; for (i = 0; i < priv->n_rows; i++) { if (rows[i].expand) { if (n_expand) { rows[i].final_size = rows[i].pref_size + (extra_height / n_expand); } else { rows[i].final_size = rows[i].pref_size + (extra_height / priv->n_rows); } } else rows[i].final_size = rows[i].pref_size; } /* distribute the remainder among children */ i = 0; while (remaining) { rows[i].final_size++; i++; remaining--; } } } } static void mx_table_calculate_dimensions (MxTable *table, gfloat for_width, gfloat for_height) { mx_table_calculate_col_widths (table, for_width); mx_table_calculate_row_heights (table, for_height); } static void mx_table_preferred_allocate (ClutterActor *self, const ClutterActorBox *box, gboolean flags) { GList *list; gint row_spacing, col_spacing; gint i; MxTable *table; MxTablePrivate *priv; MxPadding padding; DimensionData *rows, *columns; table = MX_TABLE (self); priv = MX_TABLE (self)->priv; mx_widget_get_padding (MX_WIDGET (self), &padding); col_spacing = (priv->col_spacing); row_spacing = (priv->row_spacing); mx_table_calculate_dimensions (table, box->x2 - box->x1, box->y2 - box->y1); rows = &g_array_index (priv->rows, DimensionData, 0); columns = &g_array_index (priv->columns, DimensionData, 0); for (list = priv->children; list; list = g_list_next (list)) { gint row, col, row_span, col_span; gint col_width, row_height; MxTableChild *meta; ClutterActor *child; ClutterActorBox childbox; gint child_x, child_y; gdouble x_align_d, y_align_d; gboolean x_fill, y_fill; MxAlign x_align, y_align; child = CLUTTER_ACTOR (list->data); meta = (MxTableChild *) clutter_container_get_child_meta (CLUTTER_CONTAINER (self), child); if (!CLUTTER_ACTOR_IS_VISIBLE (child)) continue; /* get child properties */ col = meta->col; row = meta->row; row_span = meta->row_span; col_span = meta->col_span; x_align_d = meta->x_align; y_align_d = meta->y_align; x_fill = meta->x_fill; y_fill = meta->y_fill; /* Convert to MxAlign */ if (x_align_d < 1.0 / 3.0) x_align = MX_ALIGN_START; else if (x_align_d > 2.0 / 3.0) x_align = MX_ALIGN_END; else x_align = MX_ALIGN_MIDDLE; if (y_align_d < 1.0 / 3.0) y_align = MX_ALIGN_START; else if (y_align_d > 2.0 / 3.0) y_align = MX_ALIGN_END; else y_align = MX_ALIGN_MIDDLE; /* initialise the width and height */ col_width = columns[col].final_size; row_height = rows[row].final_size; /* Add the widths of the spanned columns: * * First check that we have a non-zero span. Then we loop over each of * the columns that we're spanning but we stop short if we go past the * number of columns in the table. This is necessary to avoid accessing * uninitialised memory. We add the spacing in here too since we only * want to add as much spacing as times we successfully span. */ if (col + col_span > priv->n_cols) g_warning ("MxTable: col-span exceeds number of columns"); if (row + row_span > priv->n_rows) g_warning ("MxTable: row-span exceeds number of rows"); if (col_span > 1) { for (i = col + 1; i < col + col_span && i < priv->n_cols; i++) { col_width += columns[i].final_size; col_width += col_spacing; } } /* add the height of the spanned rows */ if (row_span > 1) { for (i = row + 1; i < row + row_span && i < priv->n_rows; i++) { row_height += rows[i].final_size; row_height += row_spacing; } } /* calculate child x */ child_x = (int) padding.left; for (i = 0; i < col; i++) { if (columns[i].is_visible) { child_x += columns[i].final_size; child_x += col_spacing; } } /* calculate child y */ child_y = (int) padding.top; for (i = 0; i < row; i++) { if (rows[i].is_visible) { child_y += rows[i].final_size; child_y += row_spacing; } } /* set up childbox */ childbox.x1 = (float) child_x; childbox.x2 = (float) MAX (0, child_x + col_width); childbox.y1 = (float) child_y; childbox.y2 = (float) MAX (0, child_y + row_height); mx_allocate_align_fill (child, &childbox, x_align, y_align, x_fill, y_fill); clutter_actor_allocate (child, &childbox, flags); } } static void mx_table_allocate (ClutterActor *self, const ClutterActorBox *box, ClutterAllocationFlags flags) { MxTablePrivate *priv = MX_TABLE (self)->priv; CLUTTER_ACTOR_CLASS (mx_table_parent_class)->allocate (self, box, flags); if (priv->n_cols < 1 || priv->n_rows < 1) { return; }; mx_table_preferred_allocate (self, box, flags); } static void mx_table_get_preferred_width (ClutterActor *self, gfloat for_height, gfloat *min_width_p, gfloat *natural_width_p) { gfloat total_min_width, total_pref_width; MxTablePrivate *priv = MX_TABLE (self)->priv; gint i; MxPadding padding; DimensionData *columns; mx_widget_get_padding (MX_WIDGET (self), &padding); if (priv->n_cols < 1) { *min_width_p = 0; *natural_width_p = 0; return; } mx_table_calculate_dimensions (MX_TABLE (self), -1, for_height); columns = &g_array_index (priv->columns, DimensionData, 0); total_min_width = padding.left + padding.right + (priv->visible_cols - 1) * (float) priv->col_spacing; total_pref_width = total_min_width; for (i = 0; i < priv->n_cols; i++) { total_min_width += columns[i].min_size; total_pref_width += columns[i].pref_size; } if (min_width_p) *min_width_p = total_min_width; if (natural_width_p) *natural_width_p = total_pref_width; } static void mx_table_get_preferred_height (ClutterActor *self, gfloat for_width, gfloat *min_height_p, gfloat *natural_height_p) { MxTablePrivate *priv = MX_TABLE (self)->priv; gint i, total_min_height, total_pref_height; MxPadding padding; DimensionData *rows; if (priv->n_rows < 1) { *min_height_p = 0; *natural_height_p = 0; return; } /* use min_widths to help allocation of height-for-width widgets */ mx_table_calculate_dimensions (MX_TABLE (self), for_width, -1); rows = &g_array_index (priv->rows, DimensionData, 0); mx_widget_get_padding (MX_WIDGET (self), &padding); /* start off with padding plus row spacing */ total_min_height = padding.top + padding.bottom + (priv->visible_rows - 1) * (float)(priv->row_spacing); total_pref_height = total_min_height; for (i = 0; i < priv->n_rows; i++) { total_min_height += rows[i].min_size; total_pref_height += rows[i].pref_size; } if (min_height_p) *min_height_p = total_min_height; if (natural_height_p) *natural_height_p = total_pref_height; } static void mx_table_paint (ClutterActor *self) { MxTablePrivate *priv = MX_TABLE (self)->priv; GList *list; /* make sure the background gets painted first */ CLUTTER_ACTOR_CLASS (mx_table_parent_class)->paint (self); for (list = priv->children; list; list = g_list_next (list)) { ClutterActor *child = CLUTTER_ACTOR (list->data); if (CLUTTER_ACTOR_IS_VISIBLE (child)) clutter_actor_paint (child); } if (_mx_debug (MX_DEBUG_LAYOUT)) { int i; float width, height; gfloat pos = 0; DimensionData *rows, *cols; rows = &g_array_index (priv->rows, DimensionData, 0); cols = &g_array_index (priv->columns, DimensionData, 0); clutter_actor_get_size (self, &width, &height); cogl_set_source_color4f (0.0, 0.0, 1.0, 0.7); for (i = 0; i < priv->n_rows; i++) { cogl_rectangle (0, pos, 10, pos + rows[i].final_size); pos += rows[i].final_size + priv->row_spacing; } cogl_set_source_color4f (1.0, 0.0, 0.0, 0.7); pos = 0; for (i = 0; i < priv->n_rows; i++) { cogl_rectangle (pos, 0, pos + cols[i].final_size, 10); pos += cols[i].final_size + priv->col_spacing; } } } static void mx_table_pick (ClutterActor *self, const ClutterColor *color) { MxTablePrivate *priv = MX_TABLE (self)->priv; GList *list; /* Chain up so we get a bounding box painted (if we are reactive) */ CLUTTER_ACTOR_CLASS (mx_table_parent_class)->pick (self, color); for (list = priv->children; list; list = g_list_next (list)) { if (CLUTTER_ACTOR_IS_VISIBLE (list->data)) clutter_actor_paint (CLUTTER_ACTOR (list->data)); } } static void mx_table_show_all (ClutterActor *table) { MxTablePrivate *priv = MX_TABLE (table)->priv; GList *l; for (l = priv->children; l; l = l->next) clutter_actor_show_all (CLUTTER_ACTOR (l->data)); clutter_actor_show (table); } static void mx_table_hide_all (ClutterActor *table) { MxTablePrivate *priv = MX_TABLE (table)->priv; GList *l; clutter_actor_hide (table); for (l = priv->children; l; l = l->next) clutter_actor_hide_all (CLUTTER_ACTOR (l->data)); } static void mx_table_class_init (MxTableClass *klass) { GParamSpec *pspec; GObjectClass *gobject_class = G_OBJECT_CLASS (klass); ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass); /* MxWidgetClass *mx_widget_class = MX_WIDGET_CLASS (klass); */ g_type_class_add_private (klass, sizeof (MxTablePrivate)); gobject_class->set_property = mx_table_set_property; gobject_class->get_property = mx_table_get_property; gobject_class->dispose = mx_table_dispose; gobject_class->finalize = mx_table_finalize; actor_class->paint = mx_table_paint; actor_class->pick = mx_table_pick; actor_class->allocate = mx_table_allocate; actor_class->get_preferred_width = mx_table_get_preferred_width; actor_class->get_preferred_height = mx_table_get_preferred_height; actor_class->show_all = mx_table_show_all; actor_class->hide_all = mx_table_hide_all; pspec = g_param_spec_int ("column-spacing", "Column Spacing", "Spacing between columns", 0, G_MAXINT, 0, MX_PARAM_READWRITE); g_object_class_install_property (gobject_class, PROP_COLUMN_SPACING, pspec); pspec = g_param_spec_int ("row-spacing", "Row Spacing", "Spacing between row", 0, G_MAXINT, 0, MX_PARAM_READWRITE); g_object_class_install_property (gobject_class, PROP_ROW_SPACING, pspec); pspec = g_param_spec_int ("row-count", "Row Count", "The number of rows in the table", 0, G_MAXINT, 0, MX_PARAM_READABLE); g_object_class_install_property (gobject_class, PROP_ROW_COUNT, pspec); pspec = g_param_spec_int ("column-count", "Column Count", "The number of columns in the table", 0, G_MAXINT, 0, MX_PARAM_READABLE); g_object_class_install_property (gobject_class, PROP_COL_COUNT, pspec); } static void mx_table_style_changed (MxWidget *widget, gpointer userdata) { MxTable *table = MX_TABLE (widget); MxTablePrivate *priv = table->priv; guint row_spacing, col_spacing; mx_stylable_get (MX_STYLABLE (widget), "x-mx-column-spacing", &col_spacing, "x-mx-row-spacing", &row_spacing, NULL); if (!priv->ignore_css_col_spacing) priv->col_spacing = col_spacing; if (!priv->ignore_css_row_spacing) priv->row_spacing = row_spacing; } static void mx_table_init (MxTable *table) { table->priv = MX_TABLE_GET_PRIVATE (table); table->priv->n_cols = 0; table->priv->n_rows = 0; table->priv->columns = g_array_new (FALSE, TRUE, sizeof (DimensionData)); table->priv->rows = g_array_new (FALSE, TRUE, sizeof (DimensionData)); g_signal_connect (table, "style-changed", G_CALLBACK (mx_table_style_changed), NULL); } /* used by MxTableChild to update row/column count */ void _mx_table_update_row_col (MxTable *table, gint row, gint col) { if (col > -1) table->priv->n_cols = MAX (table->priv->n_cols, col + 1); if (row > -1) table->priv->n_rows = MAX (table->priv->n_rows, row + 1); } /*** Public Functions ***/ /** * mx_table_new: * * Create a new #MxTable * * Returns: a new #MxTable */ ClutterActor * mx_table_new (void) { return g_object_new (MX_TYPE_TABLE, NULL); } /** * mx_table_set_column_spacing * @table: a #MxTable * @spacing: spacing in pixels * * Sets the amount of spacing between columns. */ void mx_table_set_column_spacing (MxTable *table, gint spacing) { MxTablePrivate *priv; g_return_if_fail (MX_IS_TABLE (table)); g_return_if_fail (spacing >= 0); priv = MX_TABLE (table)->priv; if (priv->col_spacing != spacing) { priv->col_spacing = spacing; priv->ignore_css_col_spacing = TRUE; clutter_actor_queue_relayout (CLUTTER_ACTOR (table)); g_object_notify (G_OBJECT (table), "column-spacing"); } } /** * mx_table_set_row_spacing * @table: a #MxTable * @spacing: spacing in pixels * * Sets the amount of spacing between rows. */ void mx_table_set_row_spacing (MxTable *table, gint spacing) { MxTablePrivate *priv; g_return_if_fail (MX_IS_TABLE (table)); g_return_if_fail (spacing >= 0); priv = MX_TABLE (table)->priv; if (priv->row_spacing != spacing) { priv->row_spacing = spacing; priv->ignore_css_row_spacing = TRUE; clutter_actor_queue_relayout (CLUTTER_ACTOR (table)); g_object_notify (G_OBJECT (table), "row-spacing"); } } /** * mx_table_get_row_spacing * @table: a #MxTable * * Gets the amount of spacing between rows. * * Returns: the spacing between rows in device units */ gint mx_table_get_row_spacing (MxTable *table) { MxTablePrivate *priv; g_return_val_if_fail (MX_IS_TABLE (table), -1); priv = MX_TABLE (table)->priv; return priv->row_spacing; } /** * mx_table_get_column_spacing * @table: a #MxTable * * Gets the amount of spacing between columns. * * Returns: the spacing between columns in device units */ gint mx_table_get_column_spacing (MxTable *table) { MxTablePrivate *priv; g_return_val_if_fail (MX_IS_TABLE (table), -1); priv = MX_TABLE (table)->priv; return priv->col_spacing; } /** * mx_table_add_actor: * @table: a #MxTable * @actor: the child to insert * @row: the row to place the child into * @column: the column to place the child into * * Add an actor at the specified row and column * * Note, column and rows numbers start from zero */ void mx_table_add_actor (MxTable *table, ClutterActor *actor, gint row, gint column) { MxTableChild *meta; ClutterContainer *container; g_return_if_fail (MX_IS_TABLE (table)); g_return_if_fail (CLUTTER_IS_ACTOR (actor)); g_return_if_fail (row >= -1); g_return_if_fail (column >= -1); if (row < 0) row = table->priv->n_rows + 1; if (column < 0) column = table->priv->n_cols + 1; container = CLUTTER_CONTAINER (table); clutter_container_add_actor (container, actor); meta = (MxTableChild *) clutter_container_get_child_meta (container, actor); meta->row = row; meta->col = column; _mx_table_update_row_col (table, row, column); clutter_actor_queue_relayout (CLUTTER_ACTOR (table)); } /** * mx_table_add_actor_with_properties * @table: a #MxTable * @actor: the child #ClutterActor * @row: the row to place the child into * @column: the column to place the child into * @first_property_name: name of the first property to set * @...: value for the first property, followed optionally by more name/value pairs terminated with NULL. * * Add an actor into at the specified row and column, with additional child * properties to set. */ void mx_table_add_actor_with_properties (MxTable *table, ClutterActor *actor, gint row, gint column, const gchar *first_property_name, ...) { va_list args; MxTableChild *meta; ClutterContainer *container; g_return_if_fail (MX_IS_TABLE (table)); g_return_if_fail (CLUTTER_IS_ACTOR (actor)); g_return_if_fail (row >= -1); g_return_if_fail (column >= -1); g_return_if_fail (first_property_name != NULL); if (row < 0) row = table->priv->n_rows + 1; if (column < 0) column = table->priv->n_cols + 1; container = (ClutterContainer *) table; clutter_container_add_actor (container, actor); meta = (MxTableChild *) clutter_container_get_child_meta (container, actor); meta->row = row; meta->col = column; _mx_table_update_row_col (table, row, column); va_start (args, first_property_name); g_object_set_valist ((GObject*) meta, first_property_name, args); va_end (args); clutter_actor_queue_relayout (CLUTTER_ACTOR (table)); } /** * mx_table_get_row_count: * @table: A #MxTable * * Retrieve the current number rows in the @table * * Returns: the number of rows */ gint mx_table_get_row_count (MxTable *table) { g_return_val_if_fail (MX_IS_TABLE (table), -1); return MX_TABLE (table)->priv->n_rows; } /** * mx_table_get_column_count: * @table: A #MxTable * * Retrieve the current number of columns in @table * * Returns: the number of columns */ gint mx_table_get_column_count (MxTable *table) { g_return_val_if_fail (MX_IS_TABLE (table), -1); return MX_TABLE (table)->priv->n_cols; } mx-1.4.7/mx/mx-table.h000066400000000000000000000066061201047117600144640ustar00rootroot00000000000000/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * mx-table.h: Table layout widget * * Copyright 2008, 2009 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * Boston, MA 02111-1307, USA. * * Written by: Thomas Wood * */ #if !defined(MX_H_INSIDE) && !defined(MX_COMPILATION) #error "Only can be included directly.h" #endif #ifndef __MX_TABLE_H__ #define __MX_TABLE_H__ #include #include G_BEGIN_DECLS #define MX_TYPE_TABLE (mx_table_get_type ()) #define MX_TABLE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MX_TYPE_TABLE, MxTable)) #define MX_IS_TABLE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MX_TYPE_TABLE)) #define MX_TABLE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MX_TYPE_TABLE, MxTableClass)) #define MX_IS_TABLE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MX_TYPE_TABLE)) #define MX_TABLE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MX_TYPE_TABLE, MxTableClass)) typedef struct _MxTable MxTable; typedef struct _MxTablePrivate MxTablePrivate; typedef struct _MxTableClass MxTableClass; /** * MxTable: * * The contents of this structure is private and should only be accessed using * the provided API. */ struct _MxTable { /*< private >*/ MxWidget parent_instance; MxTablePrivate *priv; }; struct _MxTableClass { MxWidgetClass parent_class; /* padding for future expansion */ void (*_padding_0) (void); void (*_padding_1) (void); void (*_padding_2) (void); void (*_padding_3) (void); void (*_padding_4) (void); }; GType mx_table_get_type (void) G_GNUC_CONST; ClutterActor *mx_table_new (void); void mx_table_set_column_spacing (MxTable *table, gint spacing); gint mx_table_get_column_spacing (MxTable *table); void mx_table_set_row_spacing (MxTable *table, gint spacing); gint mx_table_get_row_spacing (MxTable *table); void mx_table_add_actor (MxTable *table, ClutterActor *actor, gint row, gint column); void mx_table_add_actor_with_properties (MxTable *table, ClutterActor *actor, gint row, gint column, const gchar *first_property_name, ...); gint mx_table_get_row_count (MxTable *table); gint mx_table_get_column_count (MxTable *table); G_END_DECLS #endif /* __MX_TABLE_H__ */ mx-1.4.7/mx/mx-texture-cache.c000066400000000000000000000505501201047117600161260ustar00rootroot00000000000000/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * mx-widget.h: Base class for Mx actors * * Copyright 2007 OpenedHand * Copyright 2009 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * Boston, MA 02111-1307, USA. * */ /** * SECTION:mx-texture-cache * @short_description: A per-process store to cache textures * * #MxTextureCache allows an application to re-use an previously loaded * textures. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include "mx-texture-cache.h" #include "mx-marshal.h" #include "mx-private.h" G_DEFINE_TYPE (MxTextureCache, mx_texture_cache, G_TYPE_OBJECT) #define TEXTURE_CACHE_PRIVATE(o) \ (G_TYPE_INSTANCE_GET_PRIVATE ((o), MX_TYPE_TEXTURE_CACHE, MxTextureCachePrivate)) typedef struct _MxTextureCachePrivate MxTextureCachePrivate; struct _MxTextureCachePrivate { GHashTable *cache; GRegex *is_uri; }; typedef struct FinalizedClosure { gchar *uri; MxTextureCache *cache; } FinalizedClosure; enum { PROP_0, }; static MxTextureCache* __cache_singleton = NULL; /* * Convention: posX with a value of -1 indicates whole texture */ typedef struct MxTextureCacheItem { char filename[256]; int width, height; int posX, posY; CoglHandle ptr; GHashTable *meta; } MxTextureCacheItem; typedef struct { gpointer ident; CoglHandle *texture; GDestroyNotify destroy_func; } MxTextureCacheMetaEntry; static MxTextureCacheItem * mx_texture_cache_item_new (void) { return g_slice_new0 (MxTextureCacheItem); } static void mx_texture_cache_item_free (MxTextureCacheItem *item) { if (item->ptr) cogl_handle_unref (item->ptr); if (item->meta) g_hash_table_unref (item->meta); g_slice_free (MxTextureCacheItem, item); } static void mx_texture_cache_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { switch (prop_id) { default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void mx_texture_cache_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { switch (prop_id) { default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void mx_texture_cache_dispose (GObject *object) { if (G_OBJECT_CLASS (mx_texture_cache_parent_class)->dispose) G_OBJECT_CLASS (mx_texture_cache_parent_class)->dispose (object); } static void mx_texture_cache_finalize (GObject *object) { MxTextureCachePrivate *priv = TEXTURE_CACHE_PRIVATE(object); if (priv->cache) g_hash_table_unref (priv->cache); if (priv->is_uri) g_regex_unref (priv->is_uri); G_OBJECT_CLASS (mx_texture_cache_parent_class)->finalize (object); } static void mx_texture_cache_class_init (MxTextureCacheClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); g_type_class_add_private (klass, sizeof (MxTextureCachePrivate)); object_class->get_property = mx_texture_cache_get_property; object_class->set_property = mx_texture_cache_set_property; object_class->dispose = mx_texture_cache_dispose; object_class->finalize = mx_texture_cache_finalize; } static void mx_texture_cache_init (MxTextureCache *self) { GError *error = NULL; MxTextureCachePrivate *priv = TEXTURE_CACHE_PRIVATE(self); priv->cache = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify)mx_texture_cache_item_free); priv->is_uri = g_regex_new ("^([a-zA-Z0-9+.-]+)://.*", G_REGEX_OPTIMIZE, 0, &error); if (!priv->is_uri) g_error (G_STRLOC ": Unable to compile regex: %s", error->message); } /** * mx_texture_cache_get_default: * * Returns the default texture cache. This is owned by Mx and should not be * unreferenced or freed. * * Returns: (transfer none): a MxTextureCache */ MxTextureCache* mx_texture_cache_get_default (void) { if (G_UNLIKELY (__cache_singleton == NULL)) __cache_singleton = g_object_new (MX_TYPE_TEXTURE_CACHE, NULL); return __cache_singleton; } #if 0 static void on_texure_finalized (gpointer data, GObject *where_the_object_was) { FinalizedClosure *closure = (FinalizedClosure *) data; MxTextureCachePrivate *priv = TEXTURE_CACHE_PRIVATE(closure->cache); g_hash_table_remove (priv->cache, closure->uri); g_free(closure->uri); g_free(closure); } #endif /** * mx_texture_cache_get_size: * @self: A #MxTextureCache * * Returns the number of items in the texture cache * * Returns: the current size of the cache */ gint mx_texture_cache_get_size (MxTextureCache *self) { MxTextureCachePrivate *priv = TEXTURE_CACHE_PRIVATE(self); return g_hash_table_size (priv->cache); } static void add_texture_to_cache (MxTextureCache *self, const gchar *uri, MxTextureCacheItem *item) { /* FinalizedClosure *closure; */ MxTextureCachePrivate *priv = TEXTURE_CACHE_PRIVATE(self); g_hash_table_insert (priv->cache, g_strdup (uri), item); #if 0 /* Make sure we can remove from hash */ closure = g_new0 (FinalizedClosure, 1); closure->uri = g_strdup (uri); closure->cache = self; g_object_weak_ref (G_OBJECT (res), on_texure_finalized, closure); #endif } /* NOTE: you should unref the returned texture when not needed */ static gchar * mx_texture_cache_resolve_relative_path (const gchar *path) { gchar *cwd, *new_path; if (g_path_is_absolute (path)) return NULL; cwd = g_get_current_dir (); new_path = g_build_filename (cwd, path, NULL); g_free (cwd); return new_path; } static gchar * mx_texture_cache_filename_to_uri (const gchar *file) { gchar *uri; gchar *new_file; GError *error = NULL; new_file = mx_texture_cache_resolve_relative_path (file); if (new_file) { uri = g_filename_to_uri (new_file, NULL, &error); g_free (new_file); } else uri = g_filename_to_uri (file, NULL, &error); if (!uri) { g_warning ("Unable to transform filename to URI: %s", error->message); g_error_free (error); return NULL; } return uri; } static gchar * mx_texture_cache_uri_to_filename (const gchar *uri) { GError *error = NULL; gchar *file = g_filename_from_uri (uri, NULL, &error); if (!file) g_warning (G_STRLOC ": Unable to transform URI to filename: %s", error->message); return file; } static MxTextureCacheItem * mx_texture_cache_get_item (MxTextureCache *self, const gchar *uri, gboolean create_if_not_exists) { MxTextureCachePrivate *priv; MxTextureCacheItem *item; gchar *new_file, *new_uri; const gchar *file = NULL; priv = TEXTURE_CACHE_PRIVATE (self); /* Make sure we have the URI (and the path if we're loading) */ new_file = new_uri = NULL; if (g_regex_match (priv->is_uri, uri, 0, NULL)) { if (create_if_not_exists) { file = new_file = mx_texture_cache_uri_to_filename (uri); if (!new_file) return NULL; } } else { file = uri; uri = new_uri = mx_texture_cache_filename_to_uri (file); if (!new_uri) return NULL; } item = g_hash_table_lookup (priv->cache, uri); if ((!item || !item->ptr) && create_if_not_exists) { gboolean created; GError *err = NULL; if (!item) { item = mx_texture_cache_item_new (); created = TRUE; } else created = FALSE; item->ptr = cogl_texture_new_from_file (file, COGL_TEXTURE_NONE, COGL_PIXEL_FORMAT_ANY, &err); if (!item->ptr) { if (err) { g_warning ("Error loading image: %s", err->message); g_error_free (err); } if (created) mx_texture_cache_item_free (item); g_free (new_file); g_free (new_uri); return NULL; } if (created) add_texture_to_cache (self, uri, item); } g_free (new_file); g_free (new_uri); return item; } /** * mx_texture_cache_get_cogl_texture: * @self: A #MxTextureCache * @uri: A URI or path to an image file * * Create a #CoglHandle representing a texture of the specified image. Adds * the image to the cache if the image had not been previously loaded. * Subsequent calls with the same image URI/path will return the #CoglHandle of * the previously loaded image with an increased reference count. * * Returns: (transfer none): a #CoglHandle to the cached texture */ CoglHandle mx_texture_cache_get_cogl_texture (MxTextureCache *self, const gchar *uri) { MxTextureCacheItem *item; g_return_val_if_fail (MX_IS_TEXTURE_CACHE (self), NULL); g_return_val_if_fail (uri != NULL, NULL); item = mx_texture_cache_get_item (self, uri, TRUE); if (item) return cogl_handle_ref (item->ptr); else return NULL; } /** * mx_texture_cache_get_texture: * @self: A #MxTextureCache * @uri: A URI or path to a image file * * Create a new ClutterTexture with the specified image. Adds the image to the * cache if the image had not been previously loaded. Subsequent calls with * the same image URI/path will return a new ClutterTexture with the previously * loaded image. * * Returns: (transfer none): a newly created ClutterTexture */ ClutterTexture* mx_texture_cache_get_texture (MxTextureCache *self, const gchar *uri) { MxTextureCacheItem *item; g_return_val_if_fail (MX_IS_TEXTURE_CACHE (self), NULL); g_return_val_if_fail (uri != NULL, NULL); item = mx_texture_cache_get_item (self, uri, TRUE); if (item) { ClutterActor *texture = clutter_texture_new (); clutter_texture_set_cogl_texture ((ClutterTexture*) texture, item->ptr); return (ClutterTexture *)texture; } else return NULL; } /** * mx_texture_cache_get_actor: * @self: A #MxTextureCache * @uri: A URI or path to a image file * * This is a wrapper around mx_texture_cache_get_texture() which returns * a ClutterActor. * * Returns: (transfer none): a newly created ClutterTexture */ ClutterActor* mx_texture_cache_get_actor (MxTextureCache *self, const gchar *uri) { ClutterTexture *tex; g_return_val_if_fail (MX_IS_TEXTURE_CACHE (self), NULL); g_return_val_if_fail (uri != NULL, NULL); if ((tex = mx_texture_cache_get_texture (self, uri))) return CLUTTER_ACTOR (tex); else return NULL; } /** * mx_texture_cache_get_meta_texture: * @self: A #MxTextureCache * @uri: A URI or path to an image file * @ident: A unique identifier * * Create a new ClutterTexture using the previously added image associated * with the given unique identifier. * * See mx_texture_cache_insert_meta() * * Returns: (transfer full): A newly allocated #ClutterTexture, or * %NULL if no image was found * * Since: 1.2 */ ClutterTexture * mx_texture_cache_get_meta_texture (MxTextureCache *self, const gchar *uri, gpointer ident) { MxTextureCacheItem *item; g_return_val_if_fail (MX_IS_TEXTURE_CACHE (self), NULL); g_return_val_if_fail (uri != NULL, NULL); item = mx_texture_cache_get_item (self, uri, TRUE); if (item && item->meta) { MxTextureCacheMetaEntry *entry = g_hash_table_lookup (item->meta, ident); if (entry->texture) { ClutterActor *texture = clutter_texture_new (); clutter_texture_set_cogl_texture ((ClutterTexture*) texture, entry->texture); return (ClutterTexture *)texture; } } return NULL; } /** * mx_texture_cache_get_meta_cogl_texture: * @self: A #MxTextureCache * @uri: A URI or path to an image file * @ident: A unique identifier * * Retrieves the #CoglHandle of the previously added image associated * with the given unique identifier. * * See mx_texture_cache_insert_meta() * * Returns: (transfer full): A #CoglHandle to a texture, with an added * reference. %NULL if no image was found. * * Since: 1.2 */ CoglHandle mx_texture_cache_get_meta_cogl_texture (MxTextureCache *self, const gchar *uri, gpointer ident) { MxTextureCacheItem *item; g_return_val_if_fail (MX_IS_TEXTURE_CACHE (self), NULL); g_return_val_if_fail (uri != NULL, NULL); item = mx_texture_cache_get_item (self, uri, TRUE); if (item && item->meta) { MxTextureCacheMetaEntry *entry = g_hash_table_lookup (item->meta, ident); if (entry->texture) return cogl_handle_ref (entry->texture); } return NULL; } /** * mx_texture_cache_contains: * @self: A #MxTextureCache * @uri: A URI or path to an image file * * Checks whether the given URI/path is contained within the texture * cache. * * Returns: %TRUE if the image exists, %FALSE otherwise * * Since: 1.2 */ gboolean mx_texture_cache_contains (MxTextureCache *self, const gchar *uri) { g_return_val_if_fail (MX_IS_TEXTURE_CACHE (self), FALSE); g_return_val_if_fail (uri != NULL, FALSE); return mx_texture_cache_get_item (self, uri, FALSE) ? TRUE : FALSE; } /** * mx_texture_cache_contains_meta: * @self: A #MxTextureCache * @uri: A URI or path to an image file * @ident: A unique identifier * * Checks whether there are any textures associated with the given URI by * the given identifier. * * Returns: %TRUE if the data exists, %FALSE otherwise * * Since: 1.2 */ gboolean mx_texture_cache_contains_meta (MxTextureCache *self, const gchar *uri, gpointer ident) { MxTextureCacheItem *item; g_return_val_if_fail (MX_IS_TEXTURE_CACHE (self), FALSE); g_return_val_if_fail (uri != NULL, FALSE); item = mx_texture_cache_get_item (self, uri, FALSE); if (item && item->meta && g_hash_table_lookup (item->meta, ident)) return TRUE; else return FALSE; } /** * mx_texture_cache_insert: * @self: A #MxTextureCache * @uri: A URI or local file path * @texture: A #CoglHandle to a texture * * Inserts a texture into the texture cache. This can be useful if you * want to cache a texture from a custom or unhandled URI type, or you * want to override a particular texture. * * If the image is already in the cache, this texture will replace it. A * reference will be taken on the given texture. * * Since: 1.2 */ void mx_texture_cache_insert (MxTextureCache *self, const gchar *uri, CoglHandle *texture) { gchar *new_uri = NULL; MxTextureCacheItem *item; MxTextureCachePrivate *priv; g_return_if_fail (MX_IS_TEXTURE_CACHE (self)); g_return_if_fail (uri != NULL); g_return_if_fail (cogl_is_texture (texture)); priv = TEXTURE_CACHE_PRIVATE (self); /* Transform path to URI, if necessary */ if (!g_regex_match (priv->is_uri, uri, 0, NULL)) { uri = new_uri = mx_texture_cache_filename_to_uri (uri); if (!new_uri) return; } item = mx_texture_cache_item_new (); item->ptr = cogl_handle_ref (texture); add_texture_to_cache (self, uri, item); g_free (new_uri); } static void mx_texture_cache_destroy_meta_entry (gpointer data) { MxTextureCacheMetaEntry *entry = data; if (entry->destroy_func) entry->destroy_func (entry->ident); if (entry->texture) cogl_handle_unref (entry->texture); g_slice_free (MxTextureCacheMetaEntry, entry); } /** * mx_texture_cache_insert_meta: * @self: A #MxTextureCache * @uri: A URI or local file path * @ident: A unique identifier * @texture: A #CoglHandle to a texture * @destroy_func: An optional destruction function for @ident * * Inserts a texture that's associated with a URI into the cache. * If the metadata already exists for this URI, it will be replaced. * * This is useful if you have a widely used modification of an image, * for example, an image with a border composited around it. * * Since: 1.2 */ void mx_texture_cache_insert_meta (MxTextureCache *self, const gchar *uri, gpointer ident, CoglHandle *texture, GDestroyNotify destroy_func) { gchar *new_uri = NULL; MxTextureCacheItem *item; MxTextureCachePrivate *priv; MxTextureCacheMetaEntry *entry; g_return_if_fail (MX_IS_TEXTURE_CACHE (self)); g_return_if_fail (uri != NULL); g_return_if_fail (cogl_is_texture (texture)); priv = TEXTURE_CACHE_PRIVATE (self); /* Transform path to URI, if necessary */ if (!g_regex_match (priv->is_uri, uri, 0, NULL)) { uri = new_uri = mx_texture_cache_filename_to_uri (uri); if (!new_uri) return; } item = mx_texture_cache_get_item (self, uri, FALSE); if (!item) { item = mx_texture_cache_item_new (); add_texture_to_cache (self, uri, item); } g_free (new_uri); if (!item->meta) item->meta = g_hash_table_new_full (NULL, NULL, NULL, mx_texture_cache_destroy_meta_entry); entry = g_slice_new0 (MxTextureCacheMetaEntry); entry->ident = ident; entry->texture = cogl_handle_ref (texture); entry->destroy_func = destroy_func; g_hash_table_insert (item->meta, ident, entry); } void mx_texture_cache_load_cache (MxTextureCache *self, const gchar *filename) { FILE *file; MxTextureCacheItem *element, head; int ret; CoglHandle full_texture; MxTextureCachePrivate *priv; g_return_if_fail (MX_IS_TEXTURE_CACHE (self)); g_return_if_fail (filename != NULL); priv = TEXTURE_CACHE_PRIVATE (self); file = fopen(filename, "rm"); if (!file) return; ret = fread (&head, sizeof(MxTextureCacheItem), 1, file); if (ret < 0) { fclose (file); return; } /* check if we already if this texture in the cache */ if (g_hash_table_lookup (priv->cache, head.filename)) { /* skip it, we're done */ fclose (file); return; } full_texture = mx_texture_cache_get_cogl_texture (self, head.filename); if (full_texture == COGL_INVALID_HANDLE) { g_critical (G_STRLOC ": Error opening cache image file"); fclose (file); return; } while (!feof (file)) { gchar *uri; element = mx_texture_cache_item_new (); ret = fread (element, sizeof (MxTextureCacheItem), 1, file); if (ret < 1) { /* end of file */ mx_texture_cache_item_free (element); break; } uri = mx_texture_cache_filename_to_uri (element->filename); if (!uri) { /* Couldn't resolve path */ mx_texture_cache_item_free (element); continue; } if (g_hash_table_lookup (priv->cache, uri)) { /* URI is already in the cache.... */ mx_texture_cache_item_free (element); g_free (uri); } else { element->ptr = cogl_texture_new_from_sub_texture (full_texture, element->posX, element->posY, element->width, element->height); g_hash_table_insert (priv->cache, uri, element); } } fclose (file); } mx-1.4.7/mx/mx-texture-cache.h000066400000000000000000000110061201047117600161240ustar00rootroot00000000000000/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * mx-texture-cache.h: Cached textures object * * Copyright 2007 OpenedHand * Copyright 2009 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * Boston, MA 02111-1307, USA. * */ #if !defined(MX_H_INSIDE) && !defined(MX_COMPILATION) #error "Only can be included directly.h" #endif #ifndef _MX_TEXTURE_CACHE #define _MX_TEXTURE_CACHE #include #include G_BEGIN_DECLS #define MX_TYPE_TEXTURE_CACHE mx_texture_cache_get_type() #define MX_TEXTURE_CACHE(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ MX_TYPE_TEXTURE_CACHE, MxTextureCache)) #define MX_TEXTURE_CACHE_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), \ MX_TYPE_TEXTURE_CACHE, MxTextureCacheClass)) #define MX_IS_TEXTURE_CACHE(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \ MX_TYPE_TEXTURE_CACHE)) #define MX_IS_TEXTURE_CACHE_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE ((klass), \ MX_TYPE_TEXTURE_CACHE)) #define MX_TEXTURE_CACHE_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), \ MX_TYPE_TEXTURE_CACHE, MxTextureCacheClass)) /** * MxTextureCache: * * The contents of this structure are private and should only be accessed * through the public API. */ typedef struct { /*< private >*/ GObject parent; } MxTextureCache; typedef struct { GObjectClass parent_class; void (* loaded) (MxTextureCache *self, const gchar *uri, ClutterTexture *texture); void (* error_loading) (MxTextureCache *self, GError *error); /* padding for future expansion */ void (*_padding_0) (void); void (*_padding_1) (void); void (*_padding_2) (void); void (*_padding_3) (void); void (*_padding_4) (void); } MxTextureCacheClass; GType mx_texture_cache_get_type (void); MxTextureCache* mx_texture_cache_get_default (void); ClutterTexture* mx_texture_cache_get_texture (MxTextureCache *self, const gchar *uri); ClutterActor* mx_texture_cache_get_actor (MxTextureCache *self, const gchar *uri); CoglHandle mx_texture_cache_get_cogl_texture (MxTextureCache *self, const gchar *uri); ClutterTexture *mx_texture_cache_get_meta_texture (MxTextureCache *self, const gchar *uri, gpointer ident); CoglHandle mx_texture_cache_get_meta_cogl_texture (MxTextureCache *self, const gchar *uri, gpointer ident); gint mx_texture_cache_get_size (MxTextureCache *self); gboolean mx_texture_cache_contains (MxTextureCache *self, const gchar *uri); gboolean mx_texture_cache_contains_meta (MxTextureCache *self, const gchar *uri, gpointer ident); void mx_texture_cache_insert (MxTextureCache *self, const gchar *uri, CoglHandle *texture); void mx_texture_cache_insert_meta (MxTextureCache *self, const gchar *uri, gpointer ident, CoglHandle *texture, GDestroyNotify destroy_func); void mx_texture_cache_load_cache (MxTextureCache *self, const char *filename); G_END_DECLS #endif /* _MX_TEXTURE_CACHE */ mx-1.4.7/mx/mx-texture-frame.c000066400000000000000000000454231201047117600161600ustar00rootroot00000000000000/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * mx-texture-frame.h: Expandible texture actor * * Copyright 2007 OpenedHand * Copyright 2009 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * Boston, MA 02111-1307, USA. * */ /** * SECTION:mx-texture-frame * @short_description: Stretch a texture to fit the entire allocation * * #MxTextureFrame * */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include "mx-texture-frame.h" #include "mx-private.h" enum { PROP_0, PROP_PARENT_TEXTURE, PROP_TOP, PROP_RIGHT, PROP_BOTTOM, PROP_LEFT }; G_DEFINE_TYPE (MxTextureFrame, mx_texture_frame, CLUTTER_TYPE_ACTOR); #define MX_TEXTURE_FRAME_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), MX_TYPE_TEXTURE_FRAME, MxTextureFramePrivate)) struct _MxTextureFramePrivate { ClutterTexture *parent_texture; gfloat top; gfloat right; gfloat bottom; gfloat left; }; static void mx_texture_frame_get_preferred_width (ClutterActor *self, gfloat for_height, gfloat *min_width_p, gfloat *natural_width_p) { MxTextureFramePrivate *priv = MX_TEXTURE_FRAME (self)->priv; if (G_UNLIKELY (priv->parent_texture == NULL)) { if (min_width_p) *min_width_p = 0; if (natural_width_p) *natural_width_p = 0; } else { ClutterActorClass *klass; /* by directly querying the parent texture's class implementation * we are going around any override mechanism the parent texture * might have in place, and we ask directly for the original * preferred width */ klass = CLUTTER_ACTOR_GET_CLASS (priv->parent_texture); klass->get_preferred_width (CLUTTER_ACTOR (priv->parent_texture), for_height, min_width_p, natural_width_p); } } static void mx_texture_frame_get_preferred_height (ClutterActor *self, gfloat for_width, gfloat *min_height_p, gfloat *natural_height_p) { MxTextureFramePrivate *priv = MX_TEXTURE_FRAME (self)->priv; if (G_UNLIKELY (priv->parent_texture == NULL)) { if (min_height_p) *min_height_p = 0; if (natural_height_p) *natural_height_p = 0; } else { ClutterActorClass *klass; /* by directly querying the parent texture's class implementation * we are going around any override mechanism the parent texture * might have in place, and we ask directly for the original * preferred height */ klass = CLUTTER_ACTOR_GET_CLASS (priv->parent_texture); klass->get_preferred_height (CLUTTER_ACTOR (priv->parent_texture), for_width, min_height_p, natural_height_p); } } static void mx_texture_frame_paint (ClutterActor *self) { MxTextureFramePrivate *priv = MX_TEXTURE_FRAME (self)->priv; CoglHandle cogl_texture = COGL_INVALID_HANDLE; CoglHandle cogl_material = COGL_INVALID_HANDLE; ClutterActorBox box = { 0, }; gfloat width, height; gfloat tex_width, tex_height; gfloat ex, ey; gfloat tx1, ty1, tx2, ty2; guint8 opacity; /* no need to paint stuff if we don't have a texture */ if (G_UNLIKELY (priv->parent_texture == NULL)) return; /* parent texture may have been hidden, so need to make sure it gets * realized */ if (!CLUTTER_ACTOR_IS_REALIZED (priv->parent_texture)) clutter_actor_realize (CLUTTER_ACTOR (priv->parent_texture)); cogl_texture = clutter_texture_get_cogl_texture (priv->parent_texture); if (cogl_texture == COGL_INVALID_HANDLE) return; cogl_material = clutter_texture_get_cogl_material (priv->parent_texture); if (cogl_material == COGL_INVALID_HANDLE) return; tex_width = cogl_texture_get_width (cogl_texture); tex_height = cogl_texture_get_height (cogl_texture); clutter_actor_get_allocation_box (self, &box); width = box.x2 - box.x1; height = box.y2 - box.y1; opacity = clutter_actor_get_paint_opacity (self); /* Paint using the parent texture's material. It should already have the cogl texture set as the first layer */ /* NB: for correct blending we need set a preumultiplied color here: */ cogl_material_set_color4ub (cogl_material, opacity, opacity, opacity, opacity); cogl_set_source (cogl_material); /* simple stretch */ if (priv->left == 0 && priv->right == 0 && priv->top == 0 && priv->bottom == 0) { cogl_rectangle (0, 0, width, height); return; } tx1 = priv->left / tex_width; tx2 = (tex_width - priv->right) / tex_width; ty1 = priv->top / tex_height; ty2 = (tex_height - priv->bottom) / tex_height; ex = width - priv->right; if (ex < priv->left) ex = priv->left; ey = height - priv->bottom; if (ey < priv->top) ey = priv->top; { GLfloat rectangles[] = { /* top left corner */ 0, 0, priv->left, priv->top, 0.0, 0.0, tx1, ty1, /* top middle */ priv->left, 0, MAX (priv->left, ex), priv->top, tx1, 0.0, tx2, ty1, /* top right */ ex, 0, MAX (ex + priv->right, width), priv->top, tx2, 0.0, 1.0, ty1, /* mid left */ 0, priv->top, priv->left, ey, 0.0, ty1, tx1, ty2, /* center */ priv->left, priv->top, ex, ey, tx1, ty1, tx2, ty2, /* mid right */ ex, priv->top, MAX (ex + priv->right, width), ey, tx2, ty1, 1.0, ty2, /* bottom left */ 0, ey, priv->left, MAX (ey + priv->bottom, height), 0.0, ty2, tx1, 1.0, /* bottom center */ priv->left, ey, ex, MAX (ey + priv->bottom, height), tx1, ty2, tx2, 1.0, /* bottom right */ ex, ey, MAX (ex + priv->right, width), MAX (ey + priv->bottom, height), tx2, ty2, 1.0, 1.0 }; cogl_rectangles_with_texture_coords (rectangles, 9); } } static inline void mx_texture_frame_set_frame_internal (MxTextureFrame *frame, gfloat top, gfloat right, gfloat bottom, gfloat left) { MxTextureFramePrivate *priv = frame->priv; GObject *gobject = G_OBJECT (frame); gboolean changed = FALSE; g_object_freeze_notify (gobject); if (priv->top != top) { priv->top = top; g_object_notify (gobject, "top"); changed = TRUE; } if (priv->right != right) { priv->right = right; g_object_notify (gobject, "right"); changed = TRUE; } if (priv->bottom != bottom) { priv->bottom = bottom; g_object_notify (gobject, "bottom"); changed = TRUE; } if (priv->left != left) { priv->left = left; g_object_notify (gobject, "left"); changed = TRUE; } if (changed && CLUTTER_ACTOR_IS_VISIBLE (frame)) clutter_actor_queue_redraw (CLUTTER_ACTOR (frame)); g_object_thaw_notify (gobject); } static void mx_texture_frame_set_property (GObject *gobject, guint prop_id, const GValue *value, GParamSpec *pspec) { MxTextureFrame *frame = MX_TEXTURE_FRAME (gobject); MxTextureFramePrivate *priv = frame->priv; switch (prop_id) { case PROP_PARENT_TEXTURE: mx_texture_frame_set_parent_texture (frame, g_value_get_object (value)); break; case PROP_TOP: mx_texture_frame_set_frame_internal (frame, g_value_get_float (value), priv->right, priv->bottom, priv->left); break; case PROP_RIGHT: mx_texture_frame_set_frame_internal (frame, priv->top, g_value_get_float (value), priv->bottom, priv->left); break; case PROP_BOTTOM: mx_texture_frame_set_frame_internal (frame, priv->top, priv->right, g_value_get_float (value), priv->left); break; case PROP_LEFT: mx_texture_frame_set_frame_internal (frame, priv->top, priv->right, priv->bottom, g_value_get_float (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); break; } } static void mx_texture_frame_get_property (GObject *gobject, guint prop_id, GValue *value, GParamSpec *pspec) { MxTextureFramePrivate *priv = MX_TEXTURE_FRAME (gobject)->priv; switch (prop_id) { case PROP_PARENT_TEXTURE: g_value_set_object (value, priv->parent_texture); break; case PROP_LEFT: g_value_set_float (value, priv->left); break; case PROP_TOP: g_value_set_float (value, priv->top); break; case PROP_RIGHT: g_value_set_float (value, priv->right); break; case PROP_BOTTOM: g_value_set_float (value, priv->bottom); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); break; } } static void mx_texture_frame_dispose (GObject *gobject) { MxTextureFramePrivate *priv = MX_TEXTURE_FRAME (gobject)->priv; if (priv->parent_texture) { g_object_unref (priv->parent_texture); priv->parent_texture = NULL; } G_OBJECT_CLASS (mx_texture_frame_parent_class)->dispose (gobject); } static gboolean mx_texture_frame_get_paint_volume (ClutterActor *actor, ClutterPaintVolume *volume) { if (G_OBJECT_TYPE (actor) != mx_texture_frame_get_type ()) return FALSE; return clutter_paint_volume_set_from_allocation (volume, actor); } static void mx_texture_frame_class_init (MxTextureFrameClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass); GParamSpec *pspec; g_type_class_add_private (gobject_class, sizeof (MxTextureFramePrivate)); actor_class->get_preferred_width = mx_texture_frame_get_preferred_width; actor_class->get_preferred_height = mx_texture_frame_get_preferred_height; actor_class->paint = mx_texture_frame_paint; actor_class->get_paint_volume = mx_texture_frame_get_paint_volume; gobject_class->set_property = mx_texture_frame_set_property; gobject_class->get_property = mx_texture_frame_get_property; gobject_class->dispose = mx_texture_frame_dispose; pspec = g_param_spec_object ("parent-texture", "Parent Texture", "The parent ClutterTexture", CLUTTER_TYPE_TEXTURE, MX_PARAM_READWRITE | G_PARAM_CONSTRUCT); g_object_class_install_property (gobject_class, PROP_PARENT_TEXTURE, pspec); pspec = g_param_spec_float ("left", "Left", "Left offset", 0, G_MAXFLOAT, 0, MX_PARAM_READWRITE | G_PARAM_CONSTRUCT); g_object_class_install_property (gobject_class, PROP_LEFT, pspec); pspec = g_param_spec_float ("top", "Top", "Top offset", 0, G_MAXFLOAT, 0, MX_PARAM_READWRITE | G_PARAM_CONSTRUCT); g_object_class_install_property (gobject_class, PROP_TOP, pspec); pspec = g_param_spec_float ("bottom", "Bottom", "Bottom offset", 0, G_MAXFLOAT, 0, MX_PARAM_READWRITE | G_PARAM_CONSTRUCT); g_object_class_install_property (gobject_class, PROP_BOTTOM, pspec); pspec = g_param_spec_float ("right", "Right", "Right offset", 0, G_MAXFLOAT, 0, MX_PARAM_READWRITE | G_PARAM_CONSTRUCT); g_object_class_install_property (gobject_class, PROP_RIGHT, pspec); } static void mx_texture_frame_init (MxTextureFrame *self) { self->priv = MX_TEXTURE_FRAME_GET_PRIVATE (self); } /** * mx_texture_frame_new: * @texture: a #ClutterTexture or %NULL * @left: left margin preserving its content * @top: top margin preserving its content * @right: right margin preserving its content * @bottom: bottom margin preserving its content * * A #MxTextureFrame is a specialized texture that efficiently clones * an area of the given @texture while keeping preserving portions of the * same texture. * * A #MxTextureFrame can be used to make a rectangular texture fit a * given size without stretching its borders. * * Return value: the newly created #MxTextureFrame */ ClutterActor* mx_texture_frame_new (ClutterTexture *texture, gfloat top, gfloat right, gfloat bottom, gfloat left) { g_return_val_if_fail (texture == NULL || CLUTTER_IS_TEXTURE (texture), NULL); return g_object_new (MX_TYPE_TEXTURE_FRAME, "parent-texture", texture, "top", top, "right", right, "bottom", bottom, "left", left, NULL); } /** * mx_texture_frame_get_parent_texture: * @frame: A #MxTextureFrame * * Return the texture used by the #MxTextureFrame * * Returns: (transfer none): a #ClutterTexture owned by the #MxTextureFrame */ ClutterTexture * mx_texture_frame_get_parent_texture (MxTextureFrame *frame) { g_return_val_if_fail (MX_IS_TEXTURE_FRAME (frame), NULL); return frame->priv->parent_texture; } /** * mx_texture_frame_set_parent_texture: * @frame: A #MxTextureFrame * @texture: A #ClutterTexture * * Set the #ClutterTexture used by this #MxTextureFrame * */ void mx_texture_frame_set_parent_texture (MxTextureFrame *frame, ClutterTexture *texture) { MxTextureFramePrivate *priv; gboolean was_visible; g_return_if_fail (MX_IS_TEXTURE_FRAME (frame)); g_return_if_fail (texture == NULL || CLUTTER_IS_TEXTURE (texture)); priv = frame->priv; was_visible = CLUTTER_ACTOR_IS_VISIBLE (frame); if (priv->parent_texture == texture) return; if (priv->parent_texture) { g_object_unref (priv->parent_texture); priv->parent_texture = NULL; if (was_visible) clutter_actor_hide (CLUTTER_ACTOR (frame)); } if (texture) { CoglHandle cogl_material = COGL_INVALID_HANDLE; priv->parent_texture = g_object_ref_sink (texture); if (was_visible && CLUTTER_ACTOR_IS_VISIBLE (priv->parent_texture)) clutter_actor_show (CLUTTER_ACTOR (frame)); /* set the wrap mode */ cogl_material = clutter_texture_get_cogl_material (priv->parent_texture); cogl_material_set_layer_wrap_mode (cogl_material, 0, COGL_MATERIAL_WRAP_MODE_REPEAT); /* The default filter can pull from adjacent pixels which is not what we * want. */ cogl_material_set_layer_filters (cogl_material, 0, COGL_MATERIAL_FILTER_NEAREST, COGL_MATERIAL_FILTER_NEAREST); } clutter_actor_queue_relayout (CLUTTER_ACTOR (frame)); g_object_notify (G_OBJECT (frame), "parent-texture"); } /** * mx_texture_frame_set_border_values: * @frame: A #MxTextureFrame * @top: width of the top slice * @right: width of the right slice * @bottom: width of the bottom slice * @left: width of the left slice * * Set the slice lines of the specified frame. The slices are calculated as * widths from the edge of the frame. * */ void mx_texture_frame_set_border_values (MxTextureFrame *frame, gfloat top, gfloat right, gfloat bottom, gfloat left) { g_return_if_fail (MX_IS_TEXTURE_FRAME (frame)); mx_texture_frame_set_frame_internal (frame, top, right, bottom, left); } /** * mx_texture_frame_get_border_values: * @frame: A #MxTextureFrame * @top: width of the top slice * @right: width of the right slice * @bottom: width of the bottom slice * @left: width of the left slice * * Retrieve the current slice lines from the specified frame. * */ void mx_texture_frame_get_border_values (MxTextureFrame *frame, gfloat *top, gfloat *right, gfloat *bottom, gfloat *left) { MxTextureFramePrivate *priv; g_return_if_fail (MX_IS_TEXTURE_FRAME (frame)); priv = frame->priv; if (top) *top = priv->top; if (right) *right = priv->right; if (bottom) *bottom = priv->bottom; if (left) *left = priv->left; } mx-1.4.7/mx/mx-texture-frame.h000066400000000000000000000076131201047117600161640ustar00rootroot00000000000000/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * mx-texture-frame.h: Expandible texture actor * * Copyright 2007, 2008 OpenedHand Ltd * Copyright 2009 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * Boston, MA 02111-1307, USA. * */ #if !defined(MX_H_INSIDE) && !defined(MX_COMPILATION) #error "Only can be included directly.h" #endif #ifndef __MX_TEXTURE_FRAME_H__ #define __MX_TEXTURE_FRAME_H__ #include G_BEGIN_DECLS #define MX_TYPE_TEXTURE_FRAME (mx_texture_frame_get_type ()) #define MX_TEXTURE_FRAME(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MX_TYPE_TEXTURE_FRAME, MxTextureFrame)) #define MX_TEXTURE_FRAME_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MX_TYPE_TEXTURE_FRAME, MxTextureFrameClass)) #define MX_IS_TEXTURE_FRAME(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MX_TYPE_TEXTURE_FRAME)) #define MX_IS_TEXTURE_FRAME_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MX_TYPE_TEXTURE_FRAME)) #define MX_TEXTURE_FRAME_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MX_TYPE_TEXTURE_FRAME, MxTextureFrameClass)) typedef struct _MxTextureFrame MxTextureFrame; typedef struct _MxTextureFramePrivate MxTextureFramePrivate; typedef struct _MxTextureFrameClass MxTextureFrameClass; /** * MxTextureFrame: * * The contents of this structure are private and should only be accessed * through the public API. */ struct _MxTextureFrame { /*< private >*/ ClutterActor parent_instance; MxTextureFramePrivate *priv; }; struct _MxTextureFrameClass { ClutterActorClass parent_class; /* padding for future expansion */ void (*_padding_0) (void); void (*_padding_1) (void); void (*_padding_2) (void); void (*_padding_3) (void); void (*_padding_4) (void); }; GType mx_texture_frame_get_type (void) G_GNUC_CONST; ClutterActor *mx_texture_frame_new (ClutterTexture *texture, gfloat top, gfloat right, gfloat bottom, gfloat left); void mx_texture_frame_set_parent_texture (MxTextureFrame *frame, ClutterTexture *texture); ClutterTexture *mx_texture_frame_get_parent_texture (MxTextureFrame *frame); void mx_texture_frame_set_border_values (MxTextureFrame *frame, gfloat top, gfloat right, gfloat bottom, gfloat left); void mx_texture_frame_get_border_values (MxTextureFrame *frame, gfloat *top, gfloat *right, gfloat *bottom, gfloat *left); G_END_DECLS #endif /* __MX_TEXTURE_FRAME_H__ */ mx-1.4.7/mx/mx-toggle.c000066400000000000000000000401101201047117600146350ustar00rootroot00000000000000/* * mx-toggle: toggle switch actor * * Copyright 2009 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * * Written by: Thomas Wood * */ #include "mx-toggle.h" #include "mx-private.h" #include "mx-stylable.h" /* mx-toggle-handle */ #define MX_TYPE_TOGGLE_HANDLE mx_toggle_handle_get_type() #define MX_TOGGLE_HANDLE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MX_TYPE_TOGGLE_HANDLE, MxToggleHandle)) #define MX_IS_TOGGLE_HANDLE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MX_TYPE_TOGGLE_HANDLE)) typedef struct { MxWidget parent; } MxToggleHandle; typedef struct { MxWidgetClass parent_class; } MxToggleHandleClass; GType mx_toggle_handle_get_type (void) G_GNUC_CONST; G_DEFINE_TYPE (MxToggleHandle, mx_toggle_handle, MX_TYPE_WIDGET) static void mx_toggle_get_preferred_width (ClutterActor *actor, gfloat for_height, gfloat *min_width_p, gfloat *pref_width_p) { ClutterActor *background; gfloat pref_w; background = mx_widget_get_border_image (MX_WIDGET (actor)); if (!background) background = mx_widget_get_background_image (MX_WIDGET (actor)); if (!background) { if (min_width_p) *min_width_p = 0; if (pref_width_p) *pref_width_p = 0; return; } clutter_actor_get_preferred_width (background, -1, NULL, &pref_w); if (min_width_p) *min_width_p = pref_w; if (pref_width_p) *pref_width_p = pref_w; } static void mx_toggle_get_preferred_height (ClutterActor *actor, gfloat for_width, gfloat *min_height_p, gfloat *pref_height_p) { ClutterActor *background; gfloat pref_h; background = mx_widget_get_border_image (MX_WIDGET (actor)); if (!background) background = mx_widget_get_background_image (MX_WIDGET (actor)); if (!background) { if (min_height_p) *min_height_p = 0; if (pref_height_p) *pref_height_p = 0; return; } clutter_actor_get_preferred_height (background, -1, NULL, &pref_h); if (min_height_p) *min_height_p = pref_h; if (pref_height_p) *pref_height_p = pref_h; } static void mx_toggle_handle_class_init (MxToggleHandleClass *klass) { } static void mx_toggle_handle_init (MxToggleHandle *self) { } /* mx-toggle */ static void mx_toggle_stylable_iface_init (MxStylableIface *iface); G_DEFINE_TYPE_WITH_CODE (MxToggle, mx_toggle, MX_TYPE_WIDGET, G_IMPLEMENT_INTERFACE (MX_TYPE_STYLABLE, mx_toggle_stylable_iface_init)) #define TOGGLE_PRIVATE(o) \ (G_TYPE_INSTANCE_GET_PRIVATE ((o), MX_TYPE_TOGGLE, MxTogglePrivate)) struct _MxTogglePrivate { gboolean active; ClutterActor *handle; gchar *handle_filename; ClutterAlpha *alpha; gfloat position; gfloat drag_offset; gfloat slide_length; gfloat last_move; }; enum { PROP_ACTIVE = 1 }; static void mx_toggle_stylable_iface_init (MxStylableIface *iface) { GParamSpec *pspec; pspec = g_param_spec_string ("handle-image", "Handle Image", "Image used for the handle of the toggle", "", MX_PARAM_READWRITE); mx_stylable_iface_install_property (iface, MX_TYPE_TOGGLE, pspec); } static void mx_toggle_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { MxTogglePrivate *priv = MX_TOGGLE (object)->priv; switch (property_id) { case PROP_ACTIVE: g_value_set_boolean (value, priv->active); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } } static void mx_toggle_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { MxToggle *toggle = MX_TOGGLE (object); switch (property_id) { case PROP_ACTIVE: mx_toggle_set_active (toggle, g_value_get_boolean (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } } static void mx_toggle_dispose (GObject *object) { MxTogglePrivate *priv = MX_TOGGLE (object)->priv; if (priv->handle) { clutter_actor_destroy (priv->handle); priv->handle = NULL; } G_OBJECT_CLASS (mx_toggle_parent_class)->dispose (object); } static void mx_toggle_finalize (GObject *object) { G_OBJECT_CLASS (mx_toggle_parent_class)->finalize (object); } static void mx_toggle_pick (ClutterActor *actor, const ClutterColor *color) { CLUTTER_ACTOR_CLASS (mx_toggle_parent_class)->pick (actor, color); clutter_actor_paint (MX_TOGGLE (actor)->priv->handle); } static void mx_toggle_paint (ClutterActor *actor) { CLUTTER_ACTOR_CLASS (mx_toggle_parent_class)->paint (actor); clutter_actor_paint (MX_TOGGLE (actor)->priv->handle); } static void mx_toggle_map (ClutterActor *actor) { MxTogglePrivate *priv = MX_TOGGLE (actor)->priv; CLUTTER_ACTOR_CLASS (mx_toggle_parent_class)->map (actor); clutter_actor_map (priv->handle); } static void mx_toggle_unmap (ClutterActor *actor) { MxTogglePrivate *priv = MX_TOGGLE (actor)->priv; if (priv->handle) clutter_actor_unmap (priv->handle); CLUTTER_ACTOR_CLASS (mx_toggle_parent_class)->unmap (actor); } static void mx_toggle_allocate (ClutterActor *actor, const ClutterActorBox *box, ClutterAllocationFlags flags) { MxTogglePrivate *priv = MX_TOGGLE (actor)->priv; ClutterActorBox handle_box, child_box; ClutterActor *background; gfloat handle_w; gfloat toggle_pos; CLUTTER_ACTOR_CLASS (mx_toggle_parent_class)->allocate (actor, box, flags); mx_widget_get_available_area (MX_WIDGET (actor), box, &child_box); /* background-image don't get stretched, so adjust the child box so that the * handle appears in the correct place. */ background = mx_widget_get_background_image (MX_WIDGET (actor)); if (background) { gfloat width; MxPadding padding; mx_widget_get_padding (MX_WIDGET (actor), &padding); clutter_actor_get_preferred_width (background, -1, NULL, &width); width -= padding.left + padding.right; child_box.x1 += (child_box.x2 - child_box.x1) / 2.f; child_box.x1 -= width / 2.f; child_box.x2 = child_box.x1 + width; } handle_w = (int)((child_box.x2 - child_box.x1) / 2.f); toggle_pos = child_box.x2 - handle_w - child_box.x1; priv->slide_length = toggle_pos; toggle_pos = toggle_pos * priv->position; handle_box.x1 = (gint) (child_box.x1 + toggle_pos); handle_box.y1 = child_box.y1; handle_box.x2 = handle_box.x1 + handle_w; handle_box.y2 = child_box.y2; clutter_actor_allocate (priv->handle, &handle_box, flags); } static gboolean mx_toggle_button_release_event (ClutterActor *actor, ClutterButtonEvent *event) { MxToggle *toggle = MX_TOGGLE (actor); if (mx_widget_get_disabled (MX_WIDGET (actor))) return FALSE; mx_toggle_set_active (toggle, !toggle->priv->active); return FALSE; } static gboolean mx_toggle_handle_button_press_event (ClutterActor *actor, ClutterButtonEvent *event, MxToggle *toggle) { if (mx_widget_get_disabled (MX_WIDGET (toggle))) return FALSE; clutter_grab_pointer (actor); clutter_actor_transform_stage_point (CLUTTER_ACTOR (toggle), event->x, event->y, &toggle->priv->drag_offset, NULL); return FALSE; } static gboolean mx_toggle_handle_button_release_event (ClutterActor *actor, ClutterButtonEvent *event, MxToggle *toggle) { ClutterActorBox box; if (mx_widget_get_disabled (MX_WIDGET (toggle))) return FALSE; if (toggle->priv->last_move == 0) mx_toggle_set_active (toggle, !toggle->priv->active); else mx_toggle_set_active (toggle, (toggle->priv->last_move > 0.0)); toggle->priv->drag_offset = -1; toggle->priv->last_move = 0; clutter_ungrab_pointer (); /* ensure the hover state is removed if the pointer left the handle * during the grab */ clutter_actor_get_allocation_box (actor, &box); if (!clutter_actor_box_contains (&box, event->x, event->y)) mx_stylable_style_pseudo_class_remove (MX_STYLABLE (actor), "hover"); return TRUE; } static gboolean mx_toggle_handle_motion_event (ClutterActor *actor, ClutterMotionEvent *event, MxToggle *toggle) { MxTogglePrivate *priv = toggle->priv; if (mx_widget_get_disabled (MX_WIDGET (toggle))) return FALSE; if (priv->drag_offset > -1) { if (priv->slide_length) { gfloat pos, x; clutter_actor_transform_stage_point (CLUTTER_ACTOR (toggle), event->x, event->y, &x, NULL); if (priv->active) pos = 1 - ((priv->drag_offset - x) / priv->slide_length); else pos = (x - priv->drag_offset) / priv->slide_length; if (pos - priv->position) priv->last_move = (pos - priv->position); priv->position = CLAMP (pos, 0, 1); } else priv->position = 0; clutter_actor_queue_relayout (actor); } return TRUE; } static gboolean mx_toggle_button_press_event (ClutterActor *actor, ClutterButtonEvent *event) { return TRUE; } static gboolean mx_toggle_leave_event (ClutterActor *actor, ClutterCrossingEvent *event) { return FALSE; } static gboolean mx_toggle_enter_event (ClutterActor *actor, ClutterCrossingEvent *event) { return FALSE; } static void mx_toggle_apply_style (MxWidget *widget, MxStyle *style) { MxTogglePrivate *priv = MX_TOGGLE (widget)->priv; if (priv->handle != NULL) mx_stylable_set_style (MX_STYLABLE (priv->handle), style); } static void mx_toggle_class_init (MxToggleClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass); MxWidgetClass *widget_class = MX_WIDGET_CLASS (klass); GParamSpec *pspec; g_type_class_add_private (klass, sizeof (MxTogglePrivate)); object_class->get_property = mx_toggle_get_property; object_class->set_property = mx_toggle_set_property; object_class->dispose = mx_toggle_dispose; object_class->finalize = mx_toggle_finalize; actor_class->pick = mx_toggle_pick; actor_class->paint = mx_toggle_paint; actor_class->map = mx_toggle_map; actor_class->unmap = mx_toggle_unmap; actor_class->get_preferred_width = mx_toggle_get_preferred_width; actor_class->get_preferred_height = mx_toggle_get_preferred_height; actor_class->allocate = mx_toggle_allocate; actor_class->button_release_event = mx_toggle_button_release_event; actor_class->button_press_event = mx_toggle_button_press_event; actor_class->leave_event = mx_toggle_leave_event; actor_class->enter_event = mx_toggle_enter_event; widget_class->apply_style = mx_toggle_apply_style; pspec = g_param_spec_boolean ("active", "Active", "Whether the toggle switch is activated", FALSE, MX_PARAM_READWRITE); g_object_class_install_property (object_class, PROP_ACTIVE, pspec); } static void mx_toggle_update_position (ClutterTimeline *timeline, gint msecs, MxToggle *toggle) { MxTogglePrivate *priv = toggle->priv; priv->position = clutter_alpha_get_alpha (priv->alpha); clutter_actor_queue_relayout (CLUTTER_ACTOR (toggle)); } static void mx_toggle_style_changed (MxToggle *toggle) { mx_stylable_style_changed (MX_STYLABLE (toggle->priv->handle), 0); } static void mx_toggle_init (MxToggle *self) { ClutterTimeline *timeline; self->priv = TOGGLE_PRIVATE (self); self->priv->handle = g_object_new (MX_TYPE_TOGGLE_HANDLE, "reactive", TRUE, NULL); clutter_actor_set_parent (self->priv->handle, CLUTTER_ACTOR (self)); g_object_bind_property (self, "disabled", self->priv->handle, "disabled", G_BINDING_SYNC_CREATE); timeline = clutter_timeline_new (300); g_signal_connect (timeline, "new-frame", G_CALLBACK (mx_toggle_update_position), self); self->priv->alpha = clutter_alpha_new_full (timeline, CLUTTER_EASE_IN_OUT_CUBIC); clutter_actor_set_reactive (CLUTTER_ACTOR (self), TRUE); clutter_actor_set_reactive (CLUTTER_ACTOR (self->priv->handle), TRUE); self->priv->drag_offset = -1; g_signal_connect (self->priv->handle, "button-press-event", G_CALLBACK (mx_toggle_handle_button_press_event), self); g_signal_connect (self->priv->handle, "button-release-event", G_CALLBACK (mx_toggle_handle_button_release_event), self); g_signal_connect (self->priv->handle, "motion-event", G_CALLBACK (mx_toggle_handle_motion_event), self); g_signal_connect (self, "style-changed", G_CALLBACK (mx_toggle_style_changed), NULL); } ClutterActor * mx_toggle_new (void) { return g_object_new (MX_TYPE_TOGGLE, NULL); } void mx_toggle_set_active (MxToggle *toggle, gboolean active) { MxTogglePrivate *priv; g_return_if_fail (MX_IS_TOGGLE (toggle)); priv = toggle->priv; if (priv->active != active || (priv->position > 0 && priv->position < 1)) { ClutterTimeline *timeline; priv->active = active; if (active) mx_stylable_set_style_pseudo_class (MX_STYLABLE (toggle), "checked"); else mx_stylable_set_style_pseudo_class (MX_STYLABLE (toggle), NULL); g_object_notify (G_OBJECT (toggle), "active"); /* don't run an animation if the actor is not mapped */ if (!CLUTTER_ACTOR_IS_MAPPED (CLUTTER_ACTOR (toggle))) { priv->position = (active) ? 1 : 0; return; } timeline = clutter_alpha_get_timeline (priv->alpha); if (active) clutter_timeline_set_direction (timeline, CLUTTER_TIMELINE_FORWARD); else clutter_timeline_set_direction (timeline, CLUTTER_TIMELINE_BACKWARD); if (clutter_timeline_is_playing (timeline)) return; clutter_timeline_rewind (timeline); if (priv->drag_offset > -1) { clutter_alpha_set_mode (priv->alpha, CLUTTER_LINEAR); clutter_timeline_advance (timeline, priv->position * 300); } else { clutter_alpha_set_mode (priv->alpha, CLUTTER_EASE_IN_OUT_CUBIC); } clutter_timeline_start (timeline); } } gboolean mx_toggle_get_active (MxToggle *toggle) { g_return_val_if_fail (MX_IS_TOGGLE (toggle), FALSE); return toggle->priv->active; } mx-1.4.7/mx/mx-toggle.h000066400000000000000000000044761201047117600146610ustar00rootroot00000000000000/* * mx-toggle: toggle switch actor * * Copyright 2009 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * * Written by: Thomas Wood * */ #if !defined(MX_H_INSIDE) && !defined(MX_COMPILATION) #error "Only can be included directly.h" #endif #ifndef _MX_TOGGLE_H #define _MX_TOGGLE_H #include "mx-widget.h" G_BEGIN_DECLS #define MX_TYPE_TOGGLE mx_toggle_get_type() #define MX_TOGGLE(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ MX_TYPE_TOGGLE, MxToggle)) #define MX_TOGGLE_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), \ MX_TYPE_TOGGLE, MxToggleClass)) #define MX_IS_TOGGLE(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \ MX_TYPE_TOGGLE)) #define MX_IS_TOGGLE_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE ((klass), \ MX_TYPE_TOGGLE)) #define MX_TOGGLE_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), \ MX_TYPE_TOGGLE, MxToggleClass)) typedef struct _MxToggle MxToggle; typedef struct _MxToggleClass MxToggleClass; typedef struct _MxTogglePrivate MxTogglePrivate; /** * MxToggle: * * The contents of this structure is private and should only be accessed using * the provided API. */ struct _MxToggle { MxWidget parent; MxTogglePrivate *priv; }; struct _MxToggleClass { MxWidgetClass parent_class; /* padding for future expansion */ void (*_padding_0) (void); void (*_padding_1) (void); void (*_padding_2) (void); void (*_padding_3) (void); void (*_padding_4) (void); }; GType mx_toggle_get_type (void) G_GNUC_CONST; ClutterActor *mx_toggle_new (void); void mx_toggle_set_active (MxToggle *toggle, gboolean active); gboolean mx_toggle_get_active (MxToggle *toggle); G_END_DECLS #endif /* _MX_TOGGLE_H */ mx-1.4.7/mx/mx-toolbar.c000066400000000000000000000370341201047117600150310ustar00rootroot00000000000000/* * mx-toolbar.c: toolbar actor * * Copyright 2009 Intel Corporation * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * * Written by: Thomas Wood */ /** * SECTION:mx-toolbar * @short_description: A toolbar widget * * An #MxToolbar is an area that contains items at the top of an #MxWindow. * It can optionally include a close button that will close the window. */ #include "mx-toolbar.h" #include "mx-private.h" #include "mx-marshal.h" #include static void mx_focusable_iface_init (MxFocusableIface *iface); G_DEFINE_TYPE_WITH_CODE (MxToolbar, mx_toolbar, MX_TYPE_BIN, G_IMPLEMENT_INTERFACE (MX_TYPE_FOCUSABLE, mx_focusable_iface_init)) #define TOOLBAR_PRIVATE(o) \ (G_TYPE_INSTANCE_GET_PRIVATE ((o), MX_TYPE_TOOLBAR, MxToolbarPrivate)) #define PADDING 10 enum { PROP_CLOSE_BUTTON = 1 }; enum { CLOSE_BUTTON_CLICKED, LAST_SIGNAL }; static guint toolbar_signals[LAST_SIGNAL] = { 0, }; struct _MxToolbarPrivate { guint has_close_button : 1; guint child_has_focus : 1; ClutterActor *close_button; }; static MxFocusable * mx_toolbar_move_focus (MxFocusable *self, MxFocusDirection direction, MxFocusable *from) { ClutterActor *child; MxFocusable *focusable; MxToolbarPrivate *priv = MX_TOOLBAR (self)->priv; child = mx_bin_get_child (MX_BIN (self)); if (child && !MX_IS_FOCUSABLE (child)) child = NULL; focusable = NULL; switch (direction) { case MX_FOCUS_DIRECTION_LEFT: case MX_FOCUS_DIRECTION_PREVIOUS: if (!priv->child_has_focus && child) { priv->child_has_focus = TRUE; focusable = mx_focusable_accept_focus (MX_FOCUSABLE (child), MX_FOCUS_HINT_LAST); } break; case MX_FOCUS_DIRECTION_RIGHT: case MX_FOCUS_DIRECTION_NEXT: if (priv->child_has_focus && priv->has_close_button) { priv->child_has_focus = FALSE; focusable = mx_focusable_accept_focus (MX_FOCUSABLE (priv->close_button), MX_FOCUS_HINT_FIRST); } default: break; } return focusable; } static MxFocusable * mx_toolbar_accept_focus (MxFocusable *self, MxFocusHint hint) { ClutterActor *child; MxFocusable *focusable; MxToolbarPrivate *priv = MX_TOOLBAR (self)->priv; child = mx_bin_get_child (MX_BIN (self)); if (child && !MX_IS_FOCUSABLE (child)) child = NULL; focusable = NULL; switch (hint) { default: case MX_FOCUS_HINT_PRIOR: if (child && priv->child_has_focus) focusable = mx_focusable_accept_focus (MX_FOCUSABLE (child), hint); if (focusable) break; case MX_FOCUS_HINT_LAST: priv->child_has_focus = FALSE; if (priv->has_close_button) focusable = mx_focusable_accept_focus (MX_FOCUSABLE (priv->close_button), hint); if (focusable) break; case MX_FOCUS_HINT_FIRST: priv->child_has_focus = TRUE; if (child) focusable = mx_focusable_accept_focus (MX_FOCUSABLE (child), hint); break; } return focusable; } static void mx_focusable_iface_init (MxFocusableIface *iface) { iface->move_focus = mx_toolbar_move_focus; iface->accept_focus = mx_toolbar_accept_focus; } static void mx_toolbar_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { MxToolbarPrivate *priv = MX_TOOLBAR (object)->priv; switch (property_id) { case PROP_CLOSE_BUTTON: g_value_set_boolean (value, priv->has_close_button); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } } static void mx_toolbar_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { switch (property_id) { case PROP_CLOSE_BUTTON: mx_toolbar_set_has_close_button (MX_TOOLBAR (object), g_value_get_boolean (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } } static void mx_toolbar_dispose (GObject *object) { MxToolbarPrivate *priv = MX_TOOLBAR (object)->priv; if (priv->close_button) { clutter_actor_unparent (priv->close_button); priv->close_button = NULL; } G_OBJECT_CLASS (mx_toolbar_parent_class)->dispose (object); } static void mx_toolbar_finalize (GObject *object) { G_OBJECT_CLASS (mx_toolbar_parent_class)->finalize (object); } static void mx_toolbar_map (ClutterActor *actor) { MxToolbarPrivate *priv = MX_TOOLBAR (actor)->priv; CLUTTER_ACTOR_CLASS (mx_toolbar_parent_class)->map (actor); if (priv->close_button) clutter_actor_map (priv->close_button); } static void mx_toolbar_unmap (ClutterActor *actor) { MxToolbarPrivate *priv = MX_TOOLBAR (actor)->priv; if (priv->close_button) clutter_actor_unmap (priv->close_button); CLUTTER_ACTOR_CLASS (mx_toolbar_parent_class)->unmap (actor); } static void mx_toolbar_get_preferred_width (ClutterActor *actor, gfloat for_height, gfloat *min_width, gfloat *pref_width) { MxToolbarPrivate *priv = MX_TOOLBAR (actor)->priv; MxPadding padding; gfloat min_child, pref_child, min_close, pref_close; ClutterActor *child; /* if there is no close button, then use the default preferred width * function from MxBin */ if (!priv->has_close_button) { CLUTTER_ACTOR_CLASS (mx_toolbar_parent_class)-> get_preferred_width (actor, for_height, min_width, pref_width); return; } mx_widget_get_padding (MX_WIDGET (actor), &padding); for_height = for_height - padding.top - padding.bottom; if (priv->close_button) { clutter_actor_get_preferred_width (priv->close_button, for_height, &min_close, &pref_close); } else { min_close = 0; pref_close = 0; } child = mx_bin_get_child (MX_BIN (actor)); if (child) { clutter_actor_get_preferred_width (child, for_height, &min_child, &pref_child); } else { min_child = 0; pref_child = 0; } if (min_width) *min_width = padding.left + padding.right + min_close + min_child + PADDING; if (pref_width) *pref_width = padding.left + padding.right + pref_close + pref_child + PADDING; } static void mx_toolbar_get_preferred_height (ClutterActor *actor, gfloat for_width, gfloat *min_height, gfloat *pref_height) { MxToolbarPrivate *priv = MX_TOOLBAR (actor)->priv; MxPadding padding; gfloat min_child, pref_child, min_close, pref_close; ClutterActor *child; /* if there is no close button, then use the default preferred width * function from MxBin */ if (!priv->has_close_button) { CLUTTER_ACTOR_CLASS (mx_toolbar_parent_class)-> get_preferred_height (actor, for_width, min_height, pref_height); return; } mx_widget_get_padding (MX_WIDGET (actor), &padding); if (priv->close_button) { clutter_actor_get_preferred_height (priv->close_button, -1, &min_close, &pref_close); } else { min_close = 0; pref_close = 0; } child = mx_bin_get_child (MX_BIN (actor)); if (child) { clutter_actor_get_preferred_height (child, -1, &min_child, &pref_child); } else { min_child = 0; pref_child = 0; } if (min_height) *min_height = padding.top + padding.bottom + MAX (min_close, min_child); if (pref_height) *pref_height = padding.top + padding.bottom + MAX (pref_close, pref_child); } static void mx_toolbar_allocate (ClutterActor *actor, const ClutterActorBox *box, ClutterAllocationFlags flags) { MxToolbarPrivate *priv = MX_TOOLBAR (actor)->priv; ClutterActorBox childbox, avail; gfloat close_w; CLUTTER_ACTOR_CLASS (mx_toolbar_parent_class)->allocate (actor, box, flags); mx_widget_get_available_area (MX_WIDGET (actor), box, &avail); if (priv->close_button) { gfloat pref_h; clutter_actor_get_preferred_size (priv->close_button, NULL, NULL, &close_w, &pref_h); childbox.x1 = avail.x2 - close_w; childbox.y1 = avail.y1; childbox.x2 = avail.x2; childbox.y2 = avail.y2; clutter_actor_allocate (priv->close_button, &childbox, flags); } else { close_w = 0; } childbox.x1 = box->x1; childbox.y1 = box->y1; childbox.x2 = box->x2 - close_w - PADDING; childbox.y2 = box->y2; mx_bin_allocate_child (MX_BIN (actor), &childbox, flags); } static void mx_toolbar_pick (ClutterActor *actor, const ClutterColor *color) { MxToolbarPrivate *priv = MX_TOOLBAR (actor)->priv; CLUTTER_ACTOR_CLASS (mx_toolbar_parent_class)->pick (actor, color); if (priv->close_button && clutter_actor_should_pick_paint (priv->close_button)) clutter_actor_paint (priv->close_button); } static void mx_toolbar_paint (ClutterActor *actor) { MxToolbarPrivate *priv = MX_TOOLBAR (actor)->priv; CLUTTER_ACTOR_CLASS (mx_toolbar_parent_class)->paint (actor); if (priv->close_button) clutter_actor_paint (priv->close_button); } static void mx_toolbar_apply_style (MxWidget *widget, MxStyle *style) { MxToolbarPrivate *priv = MX_TOOLBAR (widget)->priv; if (priv->close_button != NULL) mx_stylable_set_style (MX_STYLABLE (priv->close_button), style); } static void mx_toolbar_class_init (MxToolbarClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass); MxWidgetClass *widget_class = MX_WIDGET_CLASS (klass); GParamSpec *pspec; g_type_class_add_private (klass, sizeof (MxToolbarPrivate)); object_class->get_property = mx_toolbar_get_property; object_class->set_property = mx_toolbar_set_property; object_class->dispose = mx_toolbar_dispose; object_class->finalize = mx_toolbar_finalize; actor_class->map = mx_toolbar_map; actor_class->unmap = mx_toolbar_unmap; actor_class->get_preferred_width = mx_toolbar_get_preferred_width; actor_class->get_preferred_height = mx_toolbar_get_preferred_height; actor_class->allocate = mx_toolbar_allocate; actor_class->pick = mx_toolbar_pick; actor_class->paint = mx_toolbar_paint; widget_class->apply_style = mx_toolbar_apply_style; /** * MxToolbar::close-button-clicked: * * Emitted when the close button of the toolbar is clicked. * * Normally, the parent stage will be closed when the close button is * clicked. Return #TRUE from this handler to prevent the stage from being * destroyed. * * Returns: #TRUE to prevent the parent stage being destroyed. */ pspec = g_param_spec_boolean ("has-close-button", "Has Close Button", "Whether to show a close button on the toolbar", TRUE, MX_PARAM_READWRITE); g_object_class_install_property (object_class, PROP_CLOSE_BUTTON, pspec); toolbar_signals[CLOSE_BUTTON_CLICKED] = g_signal_new ("close-button-clicked", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (MxToolbarClass, close_button_clicked), NULL, NULL, _mx_marshal_BOOL__VOID, G_TYPE_BOOLEAN, 0); } static void mx_toolbar_init (MxToolbar *self) { self->priv = TOOLBAR_PRIVATE (self); mx_toolbar_set_has_close_button (self, TRUE); mx_bin_set_alignment (MX_BIN (self), MX_ALIGN_START, MX_ALIGN_MIDDLE); } static void close_button_click_cb (MxButton *button, MxToolbar *toolbar) { gboolean handled; g_signal_emit (toolbar, toolbar_signals[CLOSE_BUTTON_CLICKED], 0, &handled); if (!handled) clutter_actor_destroy (clutter_actor_get_stage (CLUTTER_ACTOR (toolbar))); } /** * mx_toolbar_new: * * Create a new #MxToolbar. This is not normally necessary if using #MxWindow, * where #mx_window_get_toolbar should be used to retrieve the toolbar instead. * * Returns: A newly allocated #MxToolbar */ ClutterActor * mx_toolbar_new (void) { return g_object_new (MX_TYPE_TOOLBAR, NULL); } /** * mx_toolbar_set_has_close_button: * @toolbar: A #MxToolbar * @has_close_button: #TRUE if a close button should be displayed * * Set the #MxToolbar:has-close-button property * */ void mx_toolbar_set_has_close_button (MxToolbar *toolbar, gboolean has_close_button) { MxToolbarPrivate *priv; g_return_if_fail (MX_IS_TOOLBAR (toolbar)); priv = toolbar->priv; if (priv->has_close_button != has_close_button) { priv->has_close_button = has_close_button; if (!has_close_button) { if (priv->close_button) { clutter_actor_unparent (priv->close_button); priv->close_button = NULL; } } else { priv->close_button = mx_button_new (); clutter_actor_set_name (priv->close_button, "close-button"); clutter_actor_set_parent (priv->close_button, CLUTTER_ACTOR (toolbar)); g_signal_connect (priv->close_button, "clicked", G_CALLBACK (close_button_click_cb), toolbar); mx_stylable_style_changed (MX_STYLABLE (priv->close_button), MX_STYLE_CHANGED_FORCE); } clutter_actor_queue_relayout (CLUTTER_ACTOR (toolbar)); g_object_notify (G_OBJECT (toolbar), "has-close-button"); } } /** * mx_toolbar_get_has_close_button: * @toolbar: A #MxToolbar * * Get the value of the #MxToolbar:has-close-button property. * * Returns: the current value of the "hast-close-button" property. */ gboolean mx_toolbar_get_has_close_button (MxToolbar *toolbar) { g_return_val_if_fail (MX_IS_TOOLBAR (toolbar), FALSE); return toolbar->priv->has_close_button; } mx-1.4.7/mx/mx-toolbar.h000066400000000000000000000047221201047117600150340ustar00rootroot00000000000000/* * mx-toolbar.h: toolbar actor * * Copyright 2009 Intel Corporation * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * * Written by: Thomas Wood */ #if !defined(MX_H_INSIDE) && !defined(MX_COMPILATION) #error "Only can be included directly.h" #endif #ifndef _MX_TOOLBAR_H #define _MX_TOOLBAR_H #include "mx-widget.h" #include "mx-button.h" G_BEGIN_DECLS #define MX_TYPE_TOOLBAR mx_toolbar_get_type() #define MX_TOOLBAR(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ MX_TYPE_TOOLBAR, MxToolbar)) #define MX_TOOLBAR_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), \ MX_TYPE_TOOLBAR, MxToolbarClass)) #define MX_IS_TOOLBAR(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \ MX_TYPE_TOOLBAR)) #define MX_IS_TOOLBAR_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE ((klass), \ MX_TYPE_TOOLBAR)) #define MX_TOOLBAR_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), \ MX_TYPE_TOOLBAR, MxToolbarClass)) /** * MxToolbar: * *The contents of this structure are private and should only be accessed * through the public API. */ typedef struct _MxToolbar MxToolbar; typedef struct _MxToolbarClass MxToolbarClass; typedef struct _MxToolbarPrivate MxToolbarPrivate; struct _MxToolbar { MxBin parent; MxToolbarPrivate *priv; }; struct _MxToolbarClass { MxBinClass parent_class; /* signals */ gboolean (*close_button_clicked) (MxToolbar *toolbar); /* padding for future expansion */ void (*_padding_0) (void); void (*_padding_1) (void); void (*_padding_2) (void); void (*_padding_3) (void); void (*_padding_4) (void); }; GType mx_toolbar_get_type (void) G_GNUC_CONST; ClutterActor *mx_toolbar_new (void); void mx_toolbar_set_has_close_button (MxToolbar *toolbar, gboolean has_close_button); gboolean mx_toolbar_get_has_close_button (MxToolbar *toolbar); G_END_DECLS #endif /* _MX_TOOLBAR_H */ mx-1.4.7/mx/mx-tooltip.c000066400000000000000000000632441201047117600150630ustar00rootroot00000000000000/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * mx-tooltip.c: Plain tooltip actor * * Copyright 2008, 2009 Intel Corporation * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * Boston, MA 02111-1307, USA. * * Written by: Thomas Wood * */ /** * SECTION:mx-tooltip * @short_description: A tooltip widget * * #MxTooltip implements a single tooltip. It should not normally be created * by the application but by the widget implementing tooltip capabilities, for * example, #mx_widget_set_tooltip_text. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include "mx-tooltip.h" #include "mx-widget.h" #include "mx-stylable.h" #include "mx-private.h" enum { PROP_0, PROP_TEXT, PROP_TIP_AREA }; #define MX_TOOLTIP_GET_PRIVATE(obj) \ (G_TYPE_INSTANCE_GET_PRIVATE ((obj), MX_TYPE_TOOLTIP, MxTooltipPrivate)) struct _MxTooltipPrivate { ClutterActor *label; gfloat arrow_offset; gboolean actor_below; ClutterGeometry *tip_area; CoglMatrix stage_matrix; MxTooltipAnimation animation_mode; }; /* Time in milliseconds after a tooltip is hidden before disabling browse mode */ #define MX_TOOLTIP_BROWSE_MODE_TIMEOUT 500 static gboolean mx_tooltip_in_browse_mode = FALSE; static guint mx_tooltip_browse_mode_timeout = 0; static void mx_stylable_iface_init (MxStylableIface *iface); G_DEFINE_TYPE_WITH_CODE (MxTooltip, mx_tooltip, MX_TYPE_FLOATING_WIDGET, G_IMPLEMENT_INTERFACE (MX_TYPE_STYLABLE, mx_stylable_iface_init)); /* Stylable Implementation */ static void mx_stylable_iface_init (MxStylableIface *iface) { static gboolean is_initialized = FALSE; if (G_UNLIKELY (!is_initialized)) { GParamSpec *pspec; is_initialized = TRUE; pspec = g_param_spec_enum ("x-mx-tooltip-animation", "Tooltip animation", "The hide and show animation of the tooltip", MX_TYPE_TOOLTIP_ANIMATION, MX_TOOLTIP_ANIMATION_FADE, G_PARAM_READWRITE); mx_stylable_iface_install_property (iface, MX_TYPE_TOOLTIP, pspec); } } static void mx_tooltip_set_property (GObject *gobject, guint prop_id, const GValue *value, GParamSpec *pspec) { MxTooltip *tooltip = MX_TOOLTIP (gobject); switch (prop_id) { case PROP_TEXT: mx_tooltip_set_text (tooltip, g_value_get_string (value)); break; case PROP_TIP_AREA: mx_tooltip_set_tip_area (tooltip, g_value_get_boxed (value)); default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); break; } } static void mx_tooltip_get_property (GObject *gobject, guint prop_id, GValue *value, GParamSpec *pspec) { MxTooltipPrivate *priv = MX_TOOLTIP (gobject)->priv; switch (prop_id) { case PROP_TEXT: g_value_set_string (value, clutter_text_get_text (CLUTTER_TEXT (priv->label))); break; case PROP_TIP_AREA: g_value_set_boxed (value, priv->tip_area); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); break; } } static void mx_tooltip_style_changed (MxWidget *self) { ClutterColor *color = NULL; MxTooltipPrivate *priv; gchar *font_name; gchar *font_string; gint font_size; priv = MX_TOOLTIP (self)->priv; mx_stylable_get (MX_STYLABLE (self), "color", &color, "font-family", &font_name, "font-size", &font_size, "x-mx-tooltip-animation", &priv->animation_mode, NULL); if (color) { clutter_text_set_color (CLUTTER_TEXT (priv->label), color); clutter_color_free (color); } if (font_name || font_size) { if (font_name && font_size) { font_string = g_strdup_printf ("%s %dpx", font_name, font_size); g_free (font_name); } else if (font_size) font_string = g_strdup_printf ("%dpx", font_size); else font_string = font_name; clutter_text_set_font_name (CLUTTER_TEXT (priv->label), font_string); g_free (font_string); } } static void mx_tooltip_get_preferred_width (ClutterActor *self, gfloat for_height, gfloat *min_width_p, gfloat *natural_width_p) { MxTooltipPrivate *priv = MX_TOOLTIP (self)->priv; gfloat min_label_w, natural_label_w; gfloat label_height, arrow_height; ClutterActor *arrow_image; MxPadding padding; mx_widget_get_padding (MX_WIDGET (self), &padding); arrow_image = mx_widget_get_background_image (MX_WIDGET (self)); if (arrow_image) { clutter_actor_get_preferred_height (arrow_image, -1, NULL, &arrow_height); } else { arrow_height = 0; } if (for_height > -1) { label_height = for_height - arrow_height - padding.top - padding.bottom; } else { label_height = -1; } if (priv->label) { clutter_actor_get_preferred_width (priv->label, label_height, &min_label_w, &natural_label_w); } else { min_label_w = 0; natural_label_w = 0; } if (min_width_p) { *min_width_p = padding.left + padding.right + min_label_w; } if (natural_width_p) { *natural_width_p = padding.left + padding.right + natural_label_w; } } static void mx_tooltip_get_preferred_height (ClutterActor *self, gfloat for_width, gfloat *min_height_p, gfloat *natural_height_p) { MxTooltipPrivate *priv = MX_TOOLTIP (self)->priv; gfloat arrow_height; gfloat min_label_h, natural_label_h; gfloat label_width; ClutterActor *arrow_image; MxPadding padding; arrow_image = mx_widget_get_background_image (MX_WIDGET (self)); if (arrow_image && !priv->actor_below) { clutter_actor_get_preferred_height (arrow_image, -1, NULL, &arrow_height); } else { arrow_height = 0; } mx_widget_get_padding (MX_WIDGET (self), &padding); if (for_width > -1) { label_width = for_width - padding.left - padding.right; } else { label_width = -1; } if (priv->label) { clutter_actor_get_preferred_height (priv->label, label_width, &min_label_h, &natural_label_h); } else { min_label_h = 0; natural_label_h = 0; } if (min_height_p) { *min_height_p = padding.top + padding.bottom + arrow_height + min_label_h; } if (natural_height_p) { *natural_height_p = padding.top + padding.bottom + arrow_height + natural_label_h; } } static void mx_tooltip_allocate (ClutterActor *self, const ClutterActorBox *box, ClutterAllocationFlags flags) { MxTooltipPrivate *priv = MX_TOOLTIP (self)->priv; ClutterActorBox child_box, arrow_box; gfloat arrow_height, arrow_width; ClutterActor *border_image, *arrow_image; MxPadding padding; CLUTTER_ACTOR_CLASS (mx_tooltip_parent_class)->allocate (self, box, flags); mx_widget_get_padding (MX_WIDGET (self), &padding); arrow_image = mx_widget_get_background_image (MX_WIDGET (self)); if (arrow_image && !priv->actor_below) { clutter_actor_get_preferred_height (arrow_image, -1, NULL, &arrow_height); clutter_actor_get_preferred_width (arrow_image, -1, NULL, &arrow_width); arrow_box.x1 = (float)(priv->arrow_offset) - (int)(arrow_width / 2); arrow_box.y1 = 0; arrow_box.x2 = arrow_box.x1 + arrow_width; arrow_box.y2 = arrow_box.y1 + arrow_height; clutter_actor_allocate (arrow_image, &arrow_box, flags); } else { arrow_height = 0; arrow_width = 0; } child_box.x1 = child_box.y1 = 0; child_box.x2 = (box->x2 - box->x1); child_box.y2 = (box->y2 - box->y1); /* remove the space that is used by the arrow */ child_box.y1 += arrow_height; border_image = mx_widget_get_border_image (MX_WIDGET (self)); if (border_image) clutter_actor_allocate (border_image, &child_box, flags); if (priv->label) { /* now remove the padding */ child_box.y1 += padding.top; child_box.x1 += padding.left; child_box.x2 -= padding.right; child_box.y2 -= padding.bottom; clutter_actor_allocate (priv->label, &child_box, flags); } } static void mx_tooltip_paint (ClutterActor *self) { gfloat width, height; ClutterActor *border_image, *arrow_image; MxTooltipPrivate *priv = MX_TOOLTIP (self)->priv; clutter_actor_get_size (self, &width, &height); width = (gint)(width / 2.f); height = (gint)(height / 2.f); border_image = mx_widget_get_border_image (MX_WIDGET (self)); if (border_image) clutter_actor_paint (border_image); arrow_image = mx_widget_get_background_image (MX_WIDGET (self)); if (arrow_image && !priv->actor_below) clutter_actor_paint (arrow_image); clutter_actor_paint (priv->label); } static void mx_tooltip_map (ClutterActor *self) { MxTooltipPrivate *priv = MX_TOOLTIP (self)->priv; ClutterActor *border_image, *arrow_image; CLUTTER_ACTOR_CLASS (mx_tooltip_parent_class)->map (self); border_image = mx_widget_get_border_image (MX_WIDGET (self)); if (border_image) clutter_actor_map (border_image); arrow_image = mx_widget_get_background_image (MX_WIDGET (self)); if (arrow_image) clutter_actor_map (arrow_image); clutter_actor_map (priv->label); } static void mx_tooltip_unmap (ClutterActor *self) { MxTooltipPrivate *priv = MX_TOOLTIP (self)->priv; ClutterActor *border_image, *arrow_image; CLUTTER_ACTOR_CLASS (mx_tooltip_parent_class)->unmap (self); border_image = mx_widget_get_border_image (MX_WIDGET (self)); if (border_image) clutter_actor_unmap (border_image); arrow_image = mx_widget_get_background_image (MX_WIDGET (self)); if (arrow_image) clutter_actor_unmap (arrow_image); clutter_actor_unmap (priv->label); } static void mx_tooltip_dispose (GObject *object) { MxTooltip *tooltip = MX_TOOLTIP (object); if (tooltip->priv->label) { clutter_actor_destroy (tooltip->priv->label); tooltip->priv->label = NULL; } G_OBJECT_CLASS (mx_tooltip_parent_class)->dispose (object); } static void mx_tooltip_finalize (GObject *object) { MxTooltip *tooltip = MX_TOOLTIP (object); if (tooltip->priv->tip_area) { g_boxed_free (CLUTTER_TYPE_GEOMETRY, tooltip->priv->tip_area); tooltip->priv->tip_area = NULL; } G_OBJECT_CLASS (mx_tooltip_parent_class)->finalize (object); } static void mx_tooltip_class_init (MxTooltipClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass); MxFloatingWidgetClass *floating_class = MX_FLOATING_WIDGET_CLASS (klass); GParamSpec *pspec; g_type_class_add_private (klass, sizeof (MxTooltipPrivate)); gobject_class->set_property = mx_tooltip_set_property; gobject_class->get_property = mx_tooltip_get_property; gobject_class->dispose = mx_tooltip_dispose; gobject_class->finalize = mx_tooltip_finalize; actor_class->get_preferred_width = mx_tooltip_get_preferred_width; actor_class->get_preferred_height = mx_tooltip_get_preferred_height; actor_class->allocate = mx_tooltip_allocate; actor_class->map = mx_tooltip_map; actor_class->unmap = mx_tooltip_unmap; floating_class->floating_paint = mx_tooltip_paint; pspec = g_param_spec_string ("text", "Text", "Text of the tooltip", NULL, G_PARAM_READWRITE | MX_PARAM_TRANSLATEABLE); g_object_class_install_property (gobject_class, PROP_TEXT, pspec); pspec = g_param_spec_boxed ("tip-area", "Tip Area", "Area on the stage the tooltip applies to", CLUTTER_TYPE_GEOMETRY, MX_PARAM_READWRITE); g_object_class_install_property (gobject_class, PROP_TIP_AREA, pspec); } static void mx_tooltip_init (MxTooltip *tooltip) { tooltip->priv = MX_TOOLTIP_GET_PRIVATE (tooltip); tooltip->priv->label = g_object_new (CLUTTER_TYPE_TEXT, "line-alignment", PANGO_ALIGN_CENTER, "ellipsize", PANGO_ELLIPSIZE_END, "use-markup", TRUE, NULL); tooltip->priv->tip_area = NULL; clutter_actor_set_parent (CLUTTER_ACTOR (tooltip->priv->label), CLUTTER_ACTOR (tooltip)); g_object_set (tooltip, "show-on-set-parent", FALSE, NULL); clutter_actor_set_reactive (CLUTTER_ACTOR (tooltip), FALSE); g_signal_connect (tooltip, "style-changed", G_CALLBACK (mx_tooltip_style_changed), NULL); } static void mx_tooltip_update_position (MxTooltip *tooltip) { MxTooltipPrivate *priv = tooltip->priv; ClutterGeometry tip_area = *tooltip->priv->tip_area; gfloat tooltip_w, tooltip_h, tooltip_x, tooltip_y, abs_x, abs_y; ClutterActor *stage, *parent; gfloat stage_w, stage_h, parent_w, parent_h; MxWindow *window; /* If there's no stage, bail out - there's nothing we can do */ stage = clutter_actor_get_stage ((ClutterActor *) tooltip); if (!stage) return; /* find out the stage's size to keep the tooltip on-screen */ clutter_actor_get_size (stage, &stage_w, &stage_h); parent = clutter_actor_get_parent ((ClutterActor *) tooltip); clutter_actor_get_transformed_position (parent, &abs_x, &abs_y); clutter_actor_get_size (parent, &parent_w, &parent_h); /* ensure the tooltip with is not fixed size */ clutter_actor_set_size ((ClutterActor*) tooltip, -1, -1); /* if no area set, just position ourselves top left */ if (!priv->tip_area) { clutter_actor_set_position ((ClutterActor*) tooltip, abs_x, abs_y); return; } /* check if we're in a window and if there's rotation */ window = mx_window_get_for_stage (CLUTTER_STAGE (stage)); if (window) { MxWindowRotation rotation; gfloat old_x; g_object_get (G_OBJECT (window), "window-rotation", &rotation, NULL); if (rotation == MX_WINDOW_ROTATION_90 || rotation == MX_WINDOW_ROTATION_270) { /* swap stage width and height */ old_x = stage_w; stage_w = stage_h; stage_h = old_x; /* swap tip area width and height */ old_x = tip_area.width; tip_area.width = tip_area.height; tip_area.height = old_x; } switch (rotation) { case MX_WINDOW_ROTATION_90: /* absolute position */ old_x = abs_x; abs_x = abs_y; abs_y = stage_h - old_x; /* tip area */ old_x = tip_area.x; tip_area.x = tip_area.y; tip_area.y = stage_h - old_x - tip_area.height; break; case MX_WINDOW_ROTATION_180: tip_area.x = stage_w - tip_area.x - tip_area.width; tip_area.y = stage_h - tip_area.y - tip_area.height; abs_x = stage_w - abs_x; abs_y = stage_h - abs_y; break; case MX_WINDOW_ROTATION_270: /* absolute position */ old_x = abs_x; abs_x = stage_w - abs_y; abs_y = old_x; /* tip area */ old_x = tip_area.x; tip_area.x = stage_w - tip_area.y - tip_area.width; tip_area.y = old_x; break; default: break; } } /* we need to have a style in case there are padding values to take into * account when calculating width/height */ mx_stylable_style_changed (MX_STYLABLE (tooltip), MX_STYLE_CHANGED_FORCE); /* find out the tooltip's size */ clutter_actor_get_size ((ClutterActor*) tooltip, &tooltip_w, &tooltip_h); /* attempt to place the tooltip */ /* This special-cases the 4 window rotations, as doing this with * arbitrary rotations would massively complicate the code for * little benefit. */ priv->actor_below = FALSE; tooltip_x = (int)(tip_area.x + (tip_area.width / 2) - (tooltip_w / 2)); tooltip_y = (int)(tip_area.y + tip_area.height); /* Keep on the screen vertically */ if (tooltip_y + tooltip_h > stage_h) { priv->actor_below = TRUE; /* re-query size as may have changed */ clutter_actor_get_preferred_height ((ClutterActor*) tooltip, -1, NULL, &tooltip_h); tooltip_y = tip_area.y - tooltip_h; } /* Keep on the screen horizontally */ if (tooltip_w > stage_w) { tooltip_x = 0; clutter_actor_set_width ((ClutterActor*) tooltip, stage_w); } else if (tooltip_x < 0) tooltip_x = 0; else if (tooltip_x + tooltip_w > stage_w) tooltip_x = (int)(stage_w) - tooltip_w; gfloat pos_x, pos_y; pos_x = tooltip_x - abs_x; pos_y = tooltip_y - abs_y; /* calculate the arrow offset */ priv->arrow_offset = tip_area.x + tip_area.width / 2 - tooltip_x; clutter_actor_set_position ((ClutterActor*) tooltip, pos_x, pos_y); } /** * mx_tooltip_get_text: * @tooltip: a #MxTooltip * * Get the text displayed on the tooltip * * Returns: the text for the tooltip. This must not be freed by the application */ const gchar * mx_tooltip_get_text (MxTooltip *tooltip) { g_return_val_if_fail (MX_IS_TOOLTIP (tooltip), NULL); return clutter_text_get_text (CLUTTER_TEXT (tooltip->priv->label)); } /** * mx_tooltip_set_text: * @tooltip: a #MxTooltip * @text: text to set the label to * * Sets the text displayed on the tooltip */ void mx_tooltip_set_text (MxTooltip *tooltip, const gchar *text) { MxTooltipPrivate *priv; g_return_if_fail (MX_IS_TOOLTIP (tooltip)); priv = tooltip->priv; clutter_text_set_text (CLUTTER_TEXT (priv->label), text); if (CLUTTER_ACTOR_IS_VISIBLE (tooltip)) mx_tooltip_update_position (tooltip); g_object_notify (G_OBJECT (tooltip), "text"); } /** * mx_tooltip_show: * @tooltip: a #MxTooltip * * Show the tooltip relative to the associated widget. */ void mx_tooltip_show (MxTooltip *tooltip) { MxTooltipPrivate *priv; ClutterActor *self = CLUTTER_ACTOR (tooltip); ClutterAnimation *animation; /* make sure we're not currently already animating (e.g. hiding) */ animation = clutter_actor_get_animation (CLUTTER_ACTOR (tooltip)); if (animation) clutter_animation_completed (animation); priv = tooltip->priv; mx_tooltip_update_position (tooltip); /* finally show the tooltip... */ CLUTTER_ACTOR_CLASS (mx_tooltip_parent_class)->show (self); if (priv->animation_mode == MX_TOOLTIP_ANIMATION_BOUNCE) { g_object_set (G_OBJECT (self), "scale-center-x", priv->arrow_offset, "scale-center-y", (priv->actor_below) ? clutter_actor_get_height (self) : 0, "opacity", 255, NULL); clutter_actor_set_scale (self, 0.0, 0.0); clutter_actor_animate (self, CLUTTER_EASE_OUT_ELASTIC, 500, "scale-x", 1.0, "scale-y", 1.0, NULL); } else { clutter_actor_set_scale (self, 1.0, 1.0); clutter_actor_set_opacity (self, 0); clutter_actor_animate (self, CLUTTER_EASE_OUT_QUAD, 150, "opacity", 255, NULL); } /* Enter browse mode */ mx_tooltip_in_browse_mode = TRUE; /* Disable any previous queued attempts to leave browse mode */ if (mx_tooltip_browse_mode_timeout) { g_source_remove (mx_tooltip_browse_mode_timeout); mx_tooltip_browse_mode_timeout = 0; } } static gboolean mx_tooltip_browse_mode_timeout_cb (gpointer data) { mx_tooltip_in_browse_mode = FALSE; mx_tooltip_browse_mode_timeout = 0; return FALSE; } static void mx_tooltip_hide_complete (ClutterAnimation *animation, ClutterActor *actor) { CLUTTER_ACTOR_CLASS (mx_tooltip_parent_class)->hide (actor); g_signal_handlers_disconnect_by_func (actor, mx_tooltip_hide_complete, actor); } /** * mx_tooltip_hide: * @tooltip: a #MxTooltip * * Hide the tooltip */ void mx_tooltip_hide (MxTooltip *tooltip) { ClutterAnimation *animation; MxTooltipPrivate *priv; g_return_if_fail (MX_IS_TOOLTIP (tooltip)); priv = tooltip->priv; /* make sure we're not currently already animating (e.g. hiding) */ animation = clutter_actor_get_animation (CLUTTER_ACTOR (tooltip)); if (animation) clutter_animation_completed (animation); if (priv->animation_mode == MX_TOOLTIP_ANIMATION_BOUNCE) { g_object_set (G_OBJECT (tooltip), "scale-center-x", tooltip->priv->arrow_offset, NULL); animation = clutter_actor_animate (CLUTTER_ACTOR (tooltip), CLUTTER_EASE_IN_SINE, 150, "scale-x", 0.0, "scale-y", 0.0, NULL); } else { animation = clutter_actor_animate (CLUTTER_ACTOR (tooltip), CLUTTER_EASE_OUT_QUAD, 150, "opacity", 0, NULL);; } g_signal_connect (animation, "completed", G_CALLBACK (mx_tooltip_hide_complete), tooltip); /* Leave browse mode after a short delay */ if (mx_tooltip_browse_mode_timeout) g_source_remove (mx_tooltip_browse_mode_timeout); mx_tooltip_browse_mode_timeout = g_timeout_add (MX_TOOLTIP_BROWSE_MODE_TIMEOUT, mx_tooltip_browse_mode_timeout_cb, NULL); } /** * mx_tooltip_set_tip_area: * @tooltip: A #MxTooltip * @area: A #ClutterGeometry * * Set the area on the stage that the tooltip applies to. */ void mx_tooltip_set_tip_area (MxTooltip *tooltip, const ClutterGeometry *area) { g_return_if_fail (MX_IS_TOOLTIP (tooltip)); if (tooltip->priv->tip_area) g_boxed_free (CLUTTER_TYPE_GEOMETRY, tooltip->priv->tip_area); tooltip->priv->tip_area = g_boxed_copy (CLUTTER_TYPE_GEOMETRY, area); } /** * mx_tooltip_get_tip_area: * @tooltip: A #MxTooltip * * Retrieve the area on the stage that the tooltip currently applies to * * Returns: the #ClutterGeometry, owned by the tooltip which must not be freed * by the application. */ const ClutterGeometry* mx_tooltip_get_tip_area (MxTooltip *tooltip) { g_return_val_if_fail (MX_IS_TOOLTIP (tooltip), NULL); return tooltip->priv->tip_area; } /** * mx_tooltip_is_in_browse_mode: * * Browse mode is entered whenever a tooltip is displayed and it is * left after a short delay when a tooltip is hidden. This is used to * make tooltips display quicker when a previous tooltip is already * displayed. * * Returns: %TRUE if the app is in tooltip browse mode or %FALSE * otherwise. * * Since: 1.2 */ gboolean mx_tooltip_is_in_browse_mode (void) { return mx_tooltip_in_browse_mode; } /** * mx_tooltip_set_tip_area_from_actor: * @tooltip: A #MxTooltip * @actor: A #ClutterActor * * Utility function to set the geometry of the tooltip area * from an existing actor. * See also mx_tooltip_set_tip_area * */ void mx_tooltip_set_tip_area_from_actor (MxTooltip *tooltip, ClutterActor *actor) { ClutterVertex verts[4]; ClutterGeometry area; gfloat x, y, x2, y2; gint i; /* Work out the bounding box */ clutter_actor_get_abs_allocation_vertices (actor, verts); x = y = G_MAXFLOAT; x2 = y2 = -G_MAXFLOAT; for (i = 0; i < G_N_ELEMENTS (verts); i++) { if (verts[i].x < x) x = verts[i].x; if (verts[i].x > x2) x2 = verts[i].x; if (verts[i].y < y) y = verts[i].y; if (verts[i].y > y2) y2 = verts[i].y; } area.x = x; area.y = y; area.width = x2 - x; area.height = y2 - y; mx_tooltip_set_tip_area (tooltip, &area); } mx-1.4.7/mx/mx-tooltip.h000066400000000000000000000063471201047117600150710ustar00rootroot00000000000000/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * mx-tooltip.h: Plain tooltip actor * * Copyright 2008, 2009 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. * * Written by: Thomas Wood * */ #if !defined(MX_H_INSIDE) && !defined(MX_COMPILATION) #error "Only can be included directly.h" #endif #ifndef __MX_TOOLTIP_H__ #define __MX_TOOLTIP_H__ G_BEGIN_DECLS #include "mx-floating-widget.h" #define MX_TYPE_TOOLTIP (mx_tooltip_get_type ()) #define MX_TOOLTIP(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MX_TYPE_TOOLTIP, MxTooltip)) #define MX_IS_TOOLTIP(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MX_TYPE_TOOLTIP)) #define MX_TOOLTIP_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MX_TYPE_TOOLTIP, MxTooltipClass)) #define MX_IS_TOOLTIP_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MX_TYPE_TOOLTIP)) #define MX_TOOLTIP_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MX_TYPE_TOOLTIP, MxTooltipClass)) typedef struct _MxTooltip MxTooltip; typedef struct _MxTooltipPrivate MxTooltipPrivate; typedef struct _MxTooltipClass MxTooltipClass; /** * MxTooltip: * * The contents of this structure is private and should only be accessed using * the provided API. */ struct _MxTooltip { /*< private >*/ MxFloatingWidget parent_instance; MxTooltipPrivate *priv; }; struct _MxTooltipClass { MxFloatingWidgetClass parent_class; /* padding for future expansion */ void (*_padding_0) (void); void (*_padding_1) (void); void (*_padding_2) (void); void (*_padding_3) (void); void (*_padding_4) (void); }; GType mx_tooltip_get_type (void) G_GNUC_CONST; const gchar *mx_tooltip_get_text (MxTooltip *tooltip); void mx_tooltip_set_text (MxTooltip *tooltip, const gchar *text); void mx_tooltip_show (MxTooltip *tooltip); void mx_tooltip_hide (MxTooltip *tooltip); void mx_tooltip_set_tip_area (MxTooltip *tooltip, const ClutterGeometry *area); const ClutterGeometry* mx_tooltip_get_tip_area (MxTooltip *tooltip); gboolean mx_tooltip_is_in_browse_mode (void); void mx_tooltip_set_tip_area_from_actor (MxTooltip *tooltip, ClutterActor *actor); G_END_DECLS #endif /* __MX_TOOLTIP_H__ */ mx-1.4.7/mx/mx-types.c000066400000000000000000000136721201047117600145350ustar00rootroot00000000000000/* * mx-types.c * * Copyright 2010 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * Boston, MA 02111-1307, USA. * * Written by: Thomas Wood * */ #include #include #include #include "mx-private.h" void mx_font_weight_set_from_string (GValue *dest, const gchar *src) { if (!src) { g_value_set_enum (dest, MX_FONT_WEIGHT_NORMAL); return; } if (!strcmp (src, "bold")) g_value_set_enum (dest, MX_FONT_WEIGHT_BOLD); else if (!strcmp (src, "normal")) g_value_set_enum (dest, MX_FONT_WEIGHT_NORMAL); else if (!strcmp (src, "lighter")) g_value_set_enum (dest, MX_FONT_WEIGHT_LIGHTER); else if (!strcmp (src, "bolder")) g_value_set_enum (dest, MX_FONT_WEIGHT_BOLDER); else g_value_set_enum (dest, MX_FONT_WEIGHT_NORMAL); } static gpointer mx_padding_copy (gpointer data) { return g_slice_dup (MxPadding, data); } static void mx_padding_free (gpointer data) { if (G_LIKELY (data)) g_slice_free (MxPadding, data); } static void mx_padding_from_string (const GValue *src, GValue *dest) { const gchar *str; gchar **strv; MxPadding padding = { 0, }; str = g_value_get_string (src); if (!str) { g_value_set_boxed (dest, &padding); return; } strv = g_strsplit (str, " ", 0); if (!strv) { g_value_set_boxed (dest, &padding); return; } switch (g_strv_length (strv)) { case 1: padding.top = padding.right = padding.bottom = padding.left = atoi (strv[0]); break; case 2: padding.top = padding.bottom = atoi (strv[0]); padding.left = padding.right = atoi (strv[1]); break; case 3: padding.top = atoi (strv[0]); padding.right = padding.left = atoi (strv[1]); padding.bottom = atoi (strv[2]); break; case 4: padding.top = atoi (strv[0]); padding.right = atoi (strv[1]); padding.bottom = atoi (strv[2]); padding.left = atoi (strv[3]); break; } g_strfreev (strv); g_value_set_boxed (dest, &padding); } GType mx_padding_get_type (void) { static GType our_type = 0; if (G_UNLIKELY (our_type == 0)) our_type = g_boxed_type_register_static (g_intern_static_string ("MxPadding"), mx_padding_copy, mx_padding_free); g_value_register_transform_func (G_TYPE_STRING, our_type, mx_padding_from_string); return our_type; } static MxBorderImage * mx_border_image_copy (const MxBorderImage *border_image) { MxBorderImage *copy; g_return_val_if_fail (border_image != NULL, NULL); copy = g_slice_new0 (MxBorderImage); *copy = *border_image; copy->uri = g_strdup (border_image->uri); return copy; } static void mx_border_image_free (MxBorderImage *border_image) { if (G_LIKELY (border_image)) { g_free (border_image->uri); border_image->uri = NULL; g_slice_free (MxBorderImage, border_image); } } void mx_border_image_set_from_string (GValue *dest, const gchar *str, const gchar *filename) { MxBorderImage border_image = { 0, }; gchar **strv; gint n_tokens; gchar *base; if (!g_strcmp0 (str, "none")) { g_value_set_boxed (dest, &border_image); return; } strv = g_strsplit_set (str, " (\"\')", 0); n_tokens = g_strv_length (strv); if (n_tokens < 2) { g_strfreev (strv); g_value_set_boxed (dest, &border_image); g_warning ("Could not parse border image from \"%s\"", str); return; } if (g_strcmp0 (strv[0], "url")) { g_strfreev (strv); g_value_set_boxed (dest, &border_image); g_warning ("Could not parse border image from \"%s\"", str); return; } /* check for relative path */ if (strv[2][0] == '/') border_image.uri = g_strdup (strv[2]); else { base = g_path_get_dirname (filename); border_image.uri = g_build_filename (base, strv[2], NULL); g_free (base); } if (n_tokens == 6) /* one */ { border_image.top = border_image.right = border_image.bottom = border_image.left = atoi (strv[5]); } else if (n_tokens == 7) /* two */ { border_image.top = border_image.bottom = atoi (strv[5]); border_image.right = border_image.left = atoi (strv[6]); } else if (n_tokens == 8) /* three */ { border_image.top = atoi (strv[5]); border_image.right = border_image.left = atoi (strv[6]); border_image.bottom = atoi (strv[7]); } else if (n_tokens == 9) /* four */ { border_image.top = atoi (strv[5]); border_image.right = atoi (strv[6]); border_image.bottom = atoi (strv[7]); border_image.left = atoi (strv[8]); } g_strfreev (strv); g_value_set_boxed (dest, &border_image); g_free (border_image.uri); } GType mx_border_image_get_type (void) { static GType our_type = 0; if (G_UNLIKELY (our_type == 0)) our_type = g_boxed_type_register_static (g_intern_static_string ("MxBorderImage"), (GBoxedCopyFunc) mx_border_image_copy, (GBoxedFreeFunc) mx_border_image_free); return our_type; } mx-1.4.7/mx/mx-types.h000066400000000000000000000131741201047117600145370ustar00rootroot00000000000000/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright 2009, 2010 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * */ /** * SECTION:mx-types * @short_description: type definitions used throughout Mx * * Common types for MxWidgets. */ #if !defined(MX_H_INSIDE) && !defined(MX_COMPILATION) #error "Only can be included directly.h" #endif #ifndef __MX_TYPES_H__ #define __MX_TYPES_H__ #include #include G_BEGIN_DECLS #define MX_TYPE_BORDER_IMAGE (mx_border_image_get_type ()) #define MX_TYPE_PADDING (mx_padding_get_type ()) #define MX_PARAM_TRANSLATEABLE 1 << 8 typedef struct _MxBorderImage MxBorderImage; typedef struct _MxPadding MxPadding; GType mx_border_image_get_type (void) G_GNUC_CONST; /** * MxBorderImage: * @uri: uri of a supported image file * @top: top border slice width * @right: right border slice width * @bottom: bottom border slice width * @left: bottom border slice width */ struct _MxBorderImage { gchar *uri; gint top; gint right; gint bottom; gint left; }; void mx_border_image_set_from_string (GValue *value, const gchar *str, const gchar *filename); void mx_font_weight_set_from_string (GValue *value, const gchar *str); /** * MxPadding: * @top: padding from the top * @right: padding from the right * @bottom: padding from the bottom * @left: padding from the left * * The padding from the internal border of the parent container. */ struct _MxPadding { gfloat top; gfloat right; gfloat bottom; gfloat left; }; GType mx_padding_get_type (void) G_GNUC_CONST; /** * MxAlign: * @MX_ALIGN_START: Align at the beginning of the axis * @MX_ALIGN_MIDDLE: Align in the middle of the axis * @MX_ALIGN_END: Align at the end of the axis * * Set the alignment of the item */ typedef enum { /*< prefix=MX_ALIGN >*/ MX_ALIGN_START, MX_ALIGN_MIDDLE, MX_ALIGN_END } MxAlign; /** * MxFontWeight: * @MX_FONT_WEIGHT_NORMAL: Normal font weight * @MX_FONT_WEIGHT_BOLD: Bold font weight * @MX_FONT_WEIGHT_BOLDER: Bolder font weight * @MX_FONT_WEIGHT_LIGHTER: Lighter font weight * * Support values of font weight */ typedef enum /*< prefix=MX_FONT_WEIGHT >*/ { MX_FONT_WEIGHT_NORMAL, MX_FONT_WEIGHT_BOLD, MX_FONT_WEIGHT_BOLDER, MX_FONT_WEIGHT_LIGHTER } MxFontWeight; /** * MxScrollPolicy: * @MX_SCROLL_POLICY_NONE: Never scroll * @MX_SCROLL_POLICY_HORIZONTAL: Only allow horizontal scrolling * @MX_SCROLL_POLICY_VERTICAL: Only allow vertical scrolling * @MX_SCROLL_POLICY_BOTH: Allow scrolling both horizontally and vertically * * Defines the scrolling policy of scrollable widgets. */ typedef enum /*< prefix=MX_SCROLL_POLICY >*/ { MX_SCROLL_POLICY_NONE, MX_SCROLL_POLICY_HORIZONTAL, MX_SCROLL_POLICY_VERTICAL, MX_SCROLL_POLICY_BOTH } MxScrollPolicy; /** * MxOrientation: * @MX_ORIENTATION_HORIZONTAL: horizontal orientation * @MX_ORIENTATION_VERTICAL: vertical orientation * * Defines the orientation of various layout widgets. */ typedef enum /*< prefix=MX_ORIENTATION >*/ { MX_ORIENTATION_HORIZONTAL, MX_ORIENTATION_VERTICAL } MxOrientation; /** * MxWindowRotation: * @MX_WINDOW_ROTATION_0: Zero degrees of rotation * @MX_WINDOW_ROTATION_90: 90 degrees of rotation * @MX_WINDOW_ROTATION_180: 180 degrees of rotation * @MX_WINDOW_ROTATION_270: 270 degrees of rotation * * Defines the clock-wise rotation angle of a window. * * Since: 1.2 */ typedef enum /*< prefix=MX_WINDOW_ROTATION >*/ { MX_WINDOW_ROTATION_0, MX_WINDOW_ROTATION_90, MX_WINDOW_ROTATION_180, MX_WINDOW_ROTATION_270 } MxWindowRotation; /** * MxPosition: * @MX_POSITION_TOP: The top position * @MX_POSITION_RIGHT: The right position * @MX_POSITION_BOTTOM: The bottom position * @MX_POSITION_LEFT: The left position * * Defines the position of an interface element. * * Since: 1.2 */ typedef enum /*< prefix=MX_POSITION >*/ { MX_POSITION_TOP, MX_POSITION_RIGHT, MX_POSITION_BOTTOM, MX_POSITION_LEFT } MxPosition; /** * MxImageScaleMode: * @MX_IMAGE_SCALE_NONE: Do not apply any scaling and center the image within * the allocation * @MX_IMAGE_SCALE_FIT: Scale the image, but maintain the aspect ratio so that * it fits exactly within the allocation * @MX_IMAGE_SCALE_CROP: Scale and crop the image so that it covers the entire * allocation while retaining the correct aspect ratio * * Defines the scaling mode of an image. * */ typedef enum /*< prefix=MX_IMAGE_SCALE >*/ { MX_IMAGE_SCALE_NONE, MX_IMAGE_SCALE_FIT, MX_IMAGE_SCALE_CROP } MxImageScaleMode; /** * MxTooltipAnimation: * @MX_TOOLTIP_ANIMATION_BOUNCE: Bounce the tooltips when they appear * @MX_TOOLTIP_ANIMATION_FADE: Fade the tooltips on show and hide * * Defines the animation when tooltips are shown and hidden. * * Since: 1.2 */ typedef enum /*< prefix=MX_TOOLTIP_ANIMATION >*/ { MX_TOOLTIP_ANIMATION_BOUNCE, MX_TOOLTIP_ANIMATION_FADE } MxTooltipAnimation; G_END_DECLS #endif /* __MX_TYPES_H__ */ mx-1.4.7/mx/mx-utils.c000066400000000000000000000174451201047117600145330ustar00rootroot00000000000000/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * mx-utils.c: General utility functions used in Moblin * * Copyright 2009, 2010 Intel Corporation * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * * Authors: Emmanuele Bassi * Rob Bradford * Neil Roberts */ /** * SECTION:mx-utils * @short_description: General utility functions useful for GUIs * * Utilities useful for creating user interfaces. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include "mx-utils.h" #include "mx-private.h" /** * mx_set_locale: * * Initializes internationalization support for Mx. If MxApplication is * used, this is called automatically. Otherwise it has to be called * together with clutter_init() before using Mx. */ void mx_set_locale () { /* initialise i81n language bindings for mx */ bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR); bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8"); } /* This code is based on a similar function in Tweet */ /** * mx_utils_format_time: * @time_: a time value * * Generates a string describing the time given in @time_ using * colloquial language suitable for display to the user. Examples of * what might be returned are "A few minutes ago" or "Yesterday". * * Returns: a string. Free with g_free(). */ gchar * mx_utils_format_time (GTimeVal *time_) { GTimeVal now; struct tm tm_mtime; gchar *retval = NULL; GDate d1, d2; gint secs_diff, mins_diff, hours_diff, days_diff, months_diff, years_diff; g_return_val_if_fail (time_->tv_usec >= 0 && time_->tv_usec < G_USEC_PER_SEC, NULL); g_get_current_time (&now); #ifdef HAVE_LOCALTIME_R localtime_r ((time_t *) &(time_->tv_sec), &tm_mtime); #else { struct tm *ptm = localtime ((time_t *) &(time_->tv_sec)); if (!ptm) { g_warning ("ptm != NULL failed"); return NULL; } else memcpy ((void *) &tm_mtime, (void *) ptm, sizeof (struct tm)); } #endif /* HAVE_LOCALTIME_R */ secs_diff = now.tv_sec - time_->tv_sec; if (secs_diff < 60) return g_strdup (_("Less than a minute ago")); mins_diff = secs_diff / 60; if (mins_diff < 60) return g_strdup (_("A few minutes ago")); hours_diff = mins_diff / 60; if (hours_diff < 3) return g_strdup (_("A couple of hours ago")); g_date_set_time_t (&d1, now.tv_sec); g_date_set_time_t (&d2, time_->tv_sec); days_diff = g_date_get_julian (&d1) - g_date_get_julian (&d2); if (days_diff == 0) return g_strdup (_("Earlier today")); if (days_diff == 1) return g_strdup (_("Yesterday")); if (days_diff < 7) { const gchar *format = NULL; gchar *locale_format = NULL; gchar buf[256]; format = _("On %A"); /* day of the week */ locale_format = g_locale_from_utf8 (format, -1, NULL, NULL, NULL); if (strftime (buf, sizeof (buf), locale_format, &tm_mtime) != 0) retval = g_locale_to_utf8 (buf, -1, NULL, NULL, NULL); else retval = g_strdup (_("Unknown")); g_free (locale_format); return retval; } if (days_diff < 14) return g_strdup (_("Last week")); if (days_diff < 21) return g_strdup (_("A couple of weeks ago")); months_diff = g_date_get_month (&d1) - g_date_get_month (&d2); years_diff = g_date_get_year (&d1) - g_date_get_year (&d2); if (years_diff == 0 && months_diff == 0) return g_strdup (_("This month")); if ((years_diff == 0 && months_diff == 1) || (years_diff == 1 && months_diff == -11)) /* Now Jan., last used in Dec. */ return g_strdup (_("Last month")); if (years_diff == 0) return g_strdup (_("This year")); if (years_diff == 1) return g_strdup (_("Last year")); return g_strdup (_("Ages ago")); } /* Utility function to modify a child allocation box with respect to the * x/y-fill child properties. Expects childbox to contain the available * allocation space. */ void mx_allocate_align_fill (ClutterActor *child, ClutterActorBox *childbox, MxAlign x_alignment, MxAlign y_alignment, gboolean x_fill, gboolean y_fill) { gfloat natural_width, natural_height; gfloat min_width, min_height; gfloat child_width, child_height; gfloat available_width, available_height; ClutterRequestMode request; ClutterActorBox allocation = { 0, }; gdouble x_align, y_align; if (x_alignment == MX_ALIGN_START) x_align = 0.0; else if (x_alignment == MX_ALIGN_MIDDLE) x_align = 0.5; else x_align = 1.0; if (y_alignment == MX_ALIGN_START) y_align = 0.0; else if (y_alignment == MX_ALIGN_MIDDLE) y_align = 0.5; else y_align = 1.0; available_width = childbox->x2 - childbox->x1; available_height = childbox->y2 - childbox->y1; if (available_width < 0) available_width = 0; if (available_height < 0) available_height = 0; if (x_fill) { allocation.x1 = childbox->x1; allocation.x2 = (int)(allocation.x1 + available_width); } if (y_fill) { allocation.y1 = childbox->y1; allocation.y2 = (int)(allocation.y1 + available_height); } /* if we are filling horizontally and vertically then we're done */ if (x_fill && y_fill) { *childbox = allocation; return; } request = clutter_actor_get_request_mode (child); child_width = child_height = 0.0f; if (request == CLUTTER_REQUEST_HEIGHT_FOR_WIDTH) { clutter_actor_get_preferred_width (child, available_height, &min_width, &natural_width); child_width = CLAMP (natural_width, min_width, available_width); if (!y_fill) { clutter_actor_get_preferred_height (child, child_width, &min_height, &natural_height); child_height = CLAMP (natural_height, min_height, available_height); } } else { clutter_actor_get_preferred_height (child, available_width, &min_height, &natural_height); child_height = CLAMP (natural_height, min_height, available_height); if (!x_fill) { clutter_actor_get_preferred_width (child, child_height, &min_width, &natural_width); child_width = CLAMP (natural_width, min_width, available_width); } } if (!x_fill) { allocation.x1 = childbox->x1 + (int)((available_width - child_width) * x_align); allocation.x2 = allocation.x1 + (int) child_width; } if (!y_fill) { allocation.y1 = childbox->y1 + (int)((available_height - child_height) * y_align); allocation.y2 = allocation.y1 + (int) child_height; } *childbox = allocation; } void mx_actor_box_clamp_to_pixels (ClutterActorBox *box) { box->x1 = (int) box->x1; box->y1 = (int) box->y1; box->x2 = (int) box->x2; box->y2 = (int) box->y2; } mx-1.4.7/mx/mx-utils.h000066400000000000000000000033511201047117600145270ustar00rootroot00000000000000/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * mx-utils.h: General utility functions used in Moblin * * Copyright 2009 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * Boston, MA 02111-1307, USA. * * Authors: Emmanuele Bassi * Rob Bradford * Neil Roberts * */ #if !defined(MX_H_INSIDE) && !defined(MX_COMPILATION) #error "Only can be included directly.h" #endif #ifndef __MX_UTILS_H__ #define __MX_UTILS_H__ #include #include G_BEGIN_DECLS void mx_set_locale (); gchar *mx_utils_format_time (GTimeVal *time_); void mx_allocate_align_fill (ClutterActor *child, ClutterActorBox *childbox, MxAlign x_alignment, MxAlign y_alignment, gboolean x_fill, gboolean y_fill); void mx_actor_box_clamp_to_pixels (ClutterActorBox *box); G_END_DECLS #endif /* __MX_UTILS_H__ */ mx-1.4.7/mx/mx-version.h.in000066400000000000000000000050751201047117600154660ustar00rootroot00000000000000/* * mx-version * * Copyright 2010 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * Boston, MA 02111-1307, USA. * */ #if !defined(MX_H_INSIDE) && !defined(MX_COMPILATION) #error "Only can be included directly.h" #endif /** * SECTION:mx-version * @short_description: Versioning utility macros * * Mx offers a set of macros for checking the version of the library * an application was linked to. */ #ifndef __MX_VERSION_H__ #define __MX_VERSION_H__ /** * MX_MAJOR_VERSION: * * The major version of the Mx clibrary (1 if version is 1.2.3) */ #define MX_MAJOR_VERSION (@MX_MAJOR_VERSION@) /** * MX_MINOR_VERSION: * * The minor version of the Mx library (2, if version is 1.2.3) */ #define MX_MINOR_VERSION (@MX_MINOR_VERSION@) /** * MX_MICRO_VERSION: * * The micro version of the Mx library (3, if version is 1.2.3) */ #define MX_MICRO_VERSION (@MX_MICRO_VERSION@) /** * MX_VERSION_S: * * The full version of the Mx library, in string form (suited for * string concatenation) */ #define MX_VERSION_S "@MX_VERSION@" /** * MX_VERSION_HEX: * * Numerically encoded version of the Mx library, like 0x010203 */ #define MX_VERSION_HEX (MX_MAJOR_VERSION << 24 | \ MX_MINOR_VERSION << 16 | \ MX_MICRO_VERSION << 8) /** * MX_CHECK_VERSION: * @major: major version, like 1 in 1.2.3 * @minor: minor version, like 2 in 1.2.3 * @micro: micro version, like 3 in 1.2.3 * * Evaluates to %TRUE if the version of the Mx library is greater * than @major, @minor and @micro */ #define MX_CHECK_VERSION(major,minor,micro) \ (MX_MAJOR_VERSION > (major) || \ (MX_MAJOR_VERSION == (major) && MX_MINOR_VERSION > (minor)) || \ (MX_MAJOR_VERSION == (major) && MX_MINOR_VERSION == (minor) && MX_MICRO_VERSION >= (micro))) #endif /* __MX_VERSION_H__ */ mx-1.4.7/mx/mx-viewport.c000066400000000000000000000466541201047117600152560ustar00rootroot00000000000000/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * mx-viewport.c: Viewport actor * * Copyright 2008 OpenedHand * Copyright 2009, 2010 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * Boston, MA 02111-1307, USA. * * Written by: Chris Lord * Port to Mx by: Robert Staudinger * */ /** * SECTION:mx-viewport * @short_description: single child scrollable container * * #MxViewport allows non-scrollable children (like images or text) * to be scrollable by implementing the #MxScrollable and #ClutterContainer * interface. * * To use it, add the non-scrollable child to an #MxViewport; then sit the * viewport inside an #MxScrollView to get the scrollbars. * *
* #MxViewport around an #MxLabel * An example of a large label (which isn't normally scrollable), * placed inside an #MxViewport, which is in turn inside an #MxScrollView. * * *
* * Do not use #MxViewport if you need good performance as it does can not * be selective about the area of its child that is painted/picked. Therefore * if the child is very large or contains a lot of children, you will experience * poor performance. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include "mx-viewport.h" #include "mx-adjustment.h" #include "mx-scrollable.h" #include "mx-private.h" #include "mx-bin.h" static void scrollable_interface_init (MxScrollableIface *iface); static void scrollable_set_adjustments (MxScrollable *scrollable, MxAdjustment *hadjustment, MxAdjustment *vadjustment); static void scrollable_get_adjustments (MxScrollable *scrollable, MxAdjustment **hadjustment, MxAdjustment **vadjustment); G_DEFINE_TYPE_WITH_CODE (MxViewport, mx_viewport, MX_TYPE_BIN, G_IMPLEMENT_INTERFACE (MX_TYPE_SCROLLABLE, scrollable_interface_init)) #define VIEWPORT_PRIVATE(o) \ (G_TYPE_INSTANCE_GET_PRIVATE ((o), MX_TYPE_VIEWPORT, \ MxViewportPrivate)) struct _MxViewportPrivate { gfloat x; gfloat y; gfloat z; MxAdjustment *hadjustment; MxAdjustment *vadjustment; gboolean sync_adjustments; }; enum { PROP_0, PROP_X_ORIGIN, PROP_Y_ORIGIN, PROP_Z_ORIGIN, PROP_HADJUST, PROP_VADJUST, PROP_SYNC_ADJUST }; static void mx_viewport_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { MxAdjustment *adjustment; MxViewportPrivate *priv = MX_VIEWPORT (object)->priv; switch (prop_id) { case PROP_X_ORIGIN: g_value_set_float (value, priv->x); break; case PROP_Y_ORIGIN: g_value_set_float (value, priv->y); break; case PROP_Z_ORIGIN: g_value_set_float (value, priv->z); break; case PROP_HADJUST: scrollable_get_adjustments (MX_SCROLLABLE (object), &adjustment, NULL); g_value_set_object (value, adjustment); break; case PROP_VADJUST: scrollable_get_adjustments (MX_SCROLLABLE (object), NULL, &adjustment); g_value_set_object (value, adjustment); break; case PROP_SYNC_ADJUST: g_value_set_boolean (value, priv->sync_adjustments); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void mx_viewport_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { MxViewport *viewport = MX_VIEWPORT (object); MxViewportPrivate *priv = viewport->priv; switch (prop_id) { case PROP_X_ORIGIN: mx_viewport_set_origin (viewport, g_value_get_float (value), priv->y, priv->z); break; case PROP_Y_ORIGIN: mx_viewport_set_origin (viewport, priv->x, g_value_get_float (value), priv->z); break; case PROP_Z_ORIGIN: mx_viewport_set_origin (viewport, priv->x, priv->y, g_value_get_float (value)); break; case PROP_HADJUST: scrollable_set_adjustments (MX_SCROLLABLE (object), g_value_get_object (value), priv->vadjustment); break; case PROP_VADJUST: scrollable_set_adjustments (MX_SCROLLABLE (object), priv->hadjustment, g_value_get_object (value)); break; case PROP_SYNC_ADJUST: mx_viewport_set_sync_adjustments (viewport, g_value_get_boolean (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void mx_viewport_dispose (GObject *gobject) { MxViewportPrivate *priv = MX_VIEWPORT (gobject)->priv; if (priv->hadjustment) { g_object_unref (priv->hadjustment); priv->hadjustment = NULL; } if (priv->vadjustment) { g_object_unref (priv->vadjustment); priv->vadjustment = NULL; } G_OBJECT_CLASS (mx_viewport_parent_class)->dispose (gobject); } static void mx_viewport_allocate (ClutterActor *self, const ClutterActorBox *box, ClutterAllocationFlags flags) { MxViewportPrivate *priv = MX_VIEWPORT (self)->priv; MxPadding padding; ClutterActor *child; gfloat width, height; gfloat available_width, available_height; /* Chain up. */ CLUTTER_ACTOR_CLASS (mx_viewport_parent_class)-> allocate (self, box, flags); mx_widget_get_padding (MX_WIDGET (self), &padding); available_width = box->x2 - box->x1 - padding.left - padding.right; available_height = box->y2 - box->y1 - padding.top - padding.bottom; child = mx_bin_get_child (MX_BIN (self)); if (child) { gfloat natural_width, natural_height; ClutterActorBox child_box; MxAlign x_align, y_align; gboolean x_fill, y_fill; clutter_actor_get_preferred_size (child, NULL, NULL, &natural_width, &natural_height); mx_bin_get_fill (MX_BIN (self), &x_fill, &y_fill); if (x_fill && (available_width > natural_width)) width = available_width; else width = natural_width; if (y_fill && (available_height > natural_height)) height = available_height; else height = natural_height; mx_bin_get_alignment (MX_BIN (self), &x_align, &y_align); if (!x_fill && width < available_width) { switch (x_align) { case MX_ALIGN_START: child_box.x1 = padding.left; break; case MX_ALIGN_MIDDLE: child_box.x1 = padding.left + (available_width - width) / 2.f; break; case MX_ALIGN_END: child_box.x1 = box->x2 - box->x1 - padding.right - width; break; } } else child_box.x1 = padding.left; if (!y_fill && height < available_height) { switch (y_align) { case MX_ALIGN_START: child_box.y1 = padding.top; break; case MX_ALIGN_MIDDLE: child_box.y1 = padding.top + (available_height - height) / 2.f; break; case MX_ALIGN_END: child_box.y1 = box->y2 - box->y1 - padding.bottom - height; break; } } else child_box.y1 = padding.top; child_box.x2 = child_box.x1 + width; child_box.y2 = child_box.y1 + height; clutter_actor_allocate (child, &child_box, flags); } else { width = 0; height = 0; } /* Refresh adjustments */ if (priv->sync_adjustments) { if (priv->hadjustment) { g_object_set (G_OBJECT (priv->hadjustment), "lower", 0.0, "page-size", available_width, "upper", width, "page-increment", available_width / 3, "step-increment", available_width / 12, NULL); } if (priv->vadjustment) { g_object_set (G_OBJECT (priv->vadjustment), "lower", 0.0, "page-size", available_height, "upper", height, "page-increment", available_height / 3, "step-increment", available_height / 12, NULL); } } } static gboolean mx_viewport_get_paint_volume (ClutterActor *actor, ClutterPaintVolume *volume) { MxViewportPrivate *priv = MX_VIEWPORT (actor)->priv; ClutterVertex vertex; if (!clutter_paint_volume_set_from_allocation (volume, actor)) return FALSE; clutter_paint_volume_get_origin (volume, &vertex); if (priv->hadjustment) vertex.x += mx_adjustment_get_value (priv->hadjustment); if (priv->vadjustment) vertex.y += mx_adjustment_get_value (priv->vadjustment); clutter_paint_volume_set_origin (volume, &vertex); return TRUE; } static void mx_viewport_apply_transform (ClutterActor *actor, CoglMatrix *matrix) { MxViewportPrivate *priv = MX_VIEWPORT (actor)->priv; gdouble x, y; CLUTTER_ACTOR_CLASS (mx_viewport_parent_class)->apply_transform (actor, matrix); if (priv->hadjustment) x = mx_adjustment_get_value (priv->hadjustment); else x = 0; if (priv->vadjustment) y = mx_adjustment_get_value (priv->vadjustment); else y = 0; cogl_matrix_translate (matrix, (int) -x, (int) -y, 0); } static void mx_viewport_class_init (MxViewportClass *klass) { GParamSpec *pspec; GObjectClass *gobject_class = G_OBJECT_CLASS (klass); ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass); g_type_class_add_private (klass, sizeof (MxViewportPrivate)); gobject_class->get_property = mx_viewport_get_property; gobject_class->set_property = mx_viewport_set_property; gobject_class->dispose = mx_viewport_dispose; actor_class->allocate = mx_viewport_allocate; actor_class->get_paint_volume = mx_viewport_get_paint_volume; actor_class->apply_transform = mx_viewport_apply_transform; pspec = g_param_spec_float ("x-origin", "X Origin", "Origin's X coordinate in pixels", -G_MAXFLOAT, G_MAXFLOAT, 0, MX_PARAM_READWRITE); g_object_class_install_property (gobject_class, PROP_X_ORIGIN, pspec); pspec = g_param_spec_float ("y-origin", "Y Origin", "Origin's Y coordinate in pixels", -G_MAXFLOAT, G_MAXFLOAT, 0, MX_PARAM_READWRITE); g_object_class_install_property (gobject_class, PROP_Y_ORIGIN, pspec); pspec = g_param_spec_float ("z-origin", "Z Origin", "Origin's Z coordinate in pixels", -G_MAXFLOAT, G_MAXFLOAT, 0, MX_PARAM_READWRITE); g_object_class_install_property (gobject_class, PROP_Z_ORIGIN, pspec); pspec = g_param_spec_boolean ("sync-adjustments", "Synchronise adjustments", "Whether to synchronise adjustments with " "viewport size", TRUE, MX_PARAM_READWRITE); g_object_class_install_property (gobject_class, PROP_SYNC_ADJUST, pspec); g_object_class_override_property (gobject_class, PROP_HADJUST, "horizontal-adjustment"); g_object_class_override_property (gobject_class, PROP_VADJUST, "vertical-adjustment"); } static void hadjustment_value_notify_cb (MxAdjustment *adjustment, GParamSpec *pspec, MxViewport *viewport) { MxViewportPrivate *priv = viewport->priv; gdouble value; value = mx_adjustment_get_value (adjustment); mx_viewport_set_origin (viewport, (float)(value), priv->y, priv->z); } static void vadjustment_value_notify_cb (MxAdjustment *adjustment, GParamSpec *arg1, MxViewport *viewport) { MxViewportPrivate *priv = viewport->priv; gdouble value; value = mx_adjustment_get_value (adjustment); mx_viewport_set_origin (viewport, priv->x, (float)(value), priv->z); } static void scrollable_set_adjustments (MxScrollable *scrollable, MxAdjustment *hadjustment, MxAdjustment *vadjustment) { MxViewportPrivate *priv = MX_VIEWPORT (scrollable)->priv; if (hadjustment != priv->hadjustment) { if (priv->hadjustment) { g_signal_handlers_disconnect_by_func (priv->hadjustment, hadjustment_value_notify_cb, scrollable); g_object_unref (priv->hadjustment); } if (hadjustment) { g_object_ref (hadjustment); g_signal_connect (hadjustment, "notify::value", G_CALLBACK (hadjustment_value_notify_cb), scrollable); } priv->hadjustment = hadjustment; g_object_notify (G_OBJECT (scrollable), "horizontal-adjustment"); } if (vadjustment != priv->vadjustment) { if (priv->vadjustment) { g_signal_handlers_disconnect_by_func (priv->vadjustment, vadjustment_value_notify_cb, scrollable); g_object_unref (priv->vadjustment); } if (vadjustment) { g_object_ref (vadjustment); g_signal_connect (vadjustment, "notify::value", G_CALLBACK (vadjustment_value_notify_cb), scrollable); } priv->vadjustment = vadjustment; g_object_notify (G_OBJECT (scrollable), "vertical-adjustment"); } } static void scrollable_get_adjustments (MxScrollable *scrollable, MxAdjustment **hadjustment, MxAdjustment **vadjustment) { MxViewportPrivate *priv; g_return_if_fail (MX_IS_VIEWPORT (scrollable)); priv = ((MxViewport *) scrollable)->priv; if (hadjustment) { if (priv->hadjustment) *hadjustment = priv->hadjustment; else { MxAdjustment *adjustment; /* create an initial adjustment. this is filled with correct values * as soon as allocate() is called */ adjustment = mx_adjustment_new (); scrollable_set_adjustments (scrollable, adjustment, priv->vadjustment); g_object_unref (adjustment); *hadjustment = adjustment; } } if (vadjustment) { if (priv->vadjustment) *vadjustment = priv->vadjustment; else { MxAdjustment *adjustment; /* create an initial adjustment. this is filled with correct values * as soon as allocate() is called */ adjustment = mx_adjustment_new (); scrollable_set_adjustments (scrollable, priv->hadjustment, adjustment); g_object_unref (adjustment); *vadjustment = adjustment; } } } static void scrollable_interface_init (MxScrollableIface *iface) { iface->set_adjustments = scrollable_set_adjustments; iface->get_adjustments = scrollable_get_adjustments; } static void mx_viewport_init (MxViewport *self) { self->priv = VIEWPORT_PRIVATE (self); self->priv->sync_adjustments = TRUE; g_object_set (G_OBJECT (self), "reactive", FALSE, "x-align", MX_ALIGN_START, "y-align", MX_ALIGN_START, NULL); } ClutterActor * mx_viewport_new (void) { return g_object_new (MX_TYPE_VIEWPORT, NULL); } void mx_viewport_set_origin (MxViewport *viewport, gfloat x, gfloat y, gfloat z) { MxViewportPrivate *priv; g_return_if_fail (MX_IS_VIEWPORT (viewport)); priv = viewport->priv; g_object_freeze_notify (G_OBJECT (viewport)); if (x != priv->x) { priv->x = x; g_object_notify (G_OBJECT (viewport), "x-origin"); if (priv->hadjustment) mx_adjustment_set_value (priv->hadjustment, (float)(x)); } if (y != priv->y) { priv->y = y; g_object_notify (G_OBJECT (viewport), "y-origin"); if (priv->vadjustment) mx_adjustment_set_value (priv->vadjustment, (float)(y)); } if (z != priv->z) { priv->z = z; g_object_notify (G_OBJECT (viewport), "z-origin"); } g_object_thaw_notify (G_OBJECT (viewport)); clutter_actor_queue_redraw (CLUTTER_ACTOR (viewport)); } void mx_viewport_get_origin (MxViewport *viewport, gfloat *x, gfloat *y, gfloat *z) { MxViewportPrivate *priv; g_return_if_fail (MX_IS_VIEWPORT (viewport)); priv = viewport->priv; if (x) *x = priv->x; if (y) *y = priv->y; if (z) *z = priv->z; } void mx_viewport_set_sync_adjustments (MxViewport *viewport, gboolean sync_adjustments) { MxViewportPrivate *priv; g_return_if_fail (MX_IS_VIEWPORT (viewport)); priv = viewport->priv; if (priv->sync_adjustments != sync_adjustments) { priv->sync_adjustments = sync_adjustments; g_object_notify (G_OBJECT (viewport), "sync-adjustments"); } } gboolean mx_viewport_get_sync_adjustments (MxViewport *viewport) { g_return_val_if_fail (MX_IS_VIEWPORT (viewport), FALSE); return viewport->priv->sync_adjustments; } mx-1.4.7/mx/mx-viewport.h000066400000000000000000000060361201047117600152510ustar00rootroot00000000000000/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * mx-viewport.h: Viewport actor * * Copyright 2008 OpenedHand * Copyright 2009 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * Boston, MA 02111-1307, USA. * * Written by: Chris Lord * Port to Mx by: Robert Staudinger * */ #if !defined(MX_H_INSIDE) && !defined(MX_COMPILATION) #error "Only can be included directly.h" #endif #ifndef __MX_VIEWPORT_H__ #define __MX_VIEWPORT_H__ #include #include G_BEGIN_DECLS #define MX_TYPE_VIEWPORT (mx_viewport_get_type()) #define MX_VIEWPORT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MX_TYPE_VIEWPORT, MxViewport)) #define MX_IS_VIEWPORT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MX_TYPE_VIEWPORT)) #define MX_VIEWPORT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MX_TYPE_VIEWPORT, MxViewportClass)) #define MX_IS_VIEWPORT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MX_TYPE_VIEWPORT)) #define MX_VIEWPORT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MX_TYPE_VIEWPORT, MxViewportClass)) typedef struct _MxViewport MxViewport; typedef struct _MxViewportPrivate MxViewportPrivate; typedef struct _MxViewportClass MxViewportClass; /** * MxViewport: * * The contents of this structure are private and should only be accessed * through the public API. */ struct _MxViewport { /*< private >*/ MxBin parent; MxViewportPrivate *priv; }; struct _MxViewportClass { MxBinClass parent_class; /* padding for future expansion */ void (*_padding_0) (void); void (*_padding_1) (void); void (*_padding_2) (void); void (*_padding_3) (void); void (*_padding_4) (void); }; GType mx_viewport_get_type (void) G_GNUC_CONST; ClutterActor *mx_viewport_new (void); void mx_viewport_set_origin (MxViewport *viewport, gfloat x, gfloat y, gfloat z); void mx_viewport_get_origin (MxViewport *viewport, gfloat *x, gfloat *y, gfloat *z); void mx_viewport_set_sync_adjustments (MxViewport *viewport, gboolean sync); gboolean mx_viewport_get_sync_adjustments (MxViewport *viewport); G_END_DECLS #endif /* __MX_VIEWPORT_H__ */ mx-1.4.7/mx/mx-widget.c000066400000000000000000001445701201047117600146560ustar00rootroot00000000000000/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * mx-widget.c: Base class for Mx actors * * Copyright 2007 OpenedHand * Copyright 2008, 2009, 2010 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * Boston, MA 02111-1307, USA. * * Written by: Emmanuele Bassi * Thomas Wood * */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include "mx-widget.h" #include "mx-marshal.h" #include "mx-private.h" #include "mx-stylable.h" #include "mx-texture-cache.h" #include "mx-texture-frame.h" #include "mx-tooltip.h" #include "mx-enum-types.h" #include "mx-settings.h" /* * Forward declaration for sake of MxWidgetChild */ struct _MxWidgetPrivate { MxPadding border; MxPadding padding; MxStyle *style; gchar *pseudo_class; gchar *style_class; MxBorderImage *mx_border_image; ClutterActor *border_image; ClutterActor *old_border_image; ClutterActor *background_image; ClutterColor *bg_color; guint is_disabled : 1; guint parent_disabled : 1; MxTooltip *tooltip; MxMenu *menu; guint long_press_source; guint tooltip_timeout; guint tooltip_delay; }; /** * SECTION:mx-widget * @short_description: Base class for stylable actors * * #MxWidget is a simple abstract class on top of #ClutterActor. It * provides basic themeing properties. * * Actors in the Mx library should subclass #MxWidget if they plan * to obey to a certain #MxStyle. */ enum { PROP_0, PROP_STYLE, PROP_STYLE_CLASS, PROP_STYLE_PSEUDO_CLASS, PROP_TOOLTIP_TEXT, PROP_MENU, PROP_DISABLED, PROP_TOOLTIP_DELAY, LAST_PROP }; static GParamSpec *widget_properties[LAST_PROP]; enum { LONG_PRESS, LAST_SIGNAL }; static guint widget_signals[LAST_SIGNAL] = { 0, }; static void mx_stylable_iface_init (MxStylableIface *iface); static ClutterScriptableIface *parent_scriptable_iface = NULL; static void scriptable_iface_init (ClutterScriptableIface *iface); /* Length of time in milliseconds that the cursor must be held steady over a widget before the tooltip is displayed */ #define MX_WIDGET_TOOLTIP_TIMEOUT 500 G_DEFINE_ABSTRACT_TYPE_WITH_CODE (MxWidget, mx_widget, CLUTTER_TYPE_ACTOR, G_IMPLEMENT_INTERFACE (MX_TYPE_STYLABLE, mx_stylable_iface_init) G_IMPLEMENT_INTERFACE (CLUTTER_TYPE_SCRIPTABLE, scriptable_iface_init)); #define MX_WIDGET_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), MX_TYPE_WIDGET, MxWidgetPrivate)) static void mx_widget_set_property (GObject *gobject, guint prop_id, const GValue *value, GParamSpec *pspec) { MxWidget *actor = MX_WIDGET (gobject); switch (prop_id) { case PROP_STYLE: mx_stylable_set_style (MX_STYLABLE (actor), g_value_get_object (value)); break; case PROP_STYLE_PSEUDO_CLASS: mx_stylable_set_style_pseudo_class (MX_STYLABLE (actor), g_value_get_string (value)); break; case PROP_STYLE_CLASS: mx_stylable_set_style_class (MX_STYLABLE (actor), g_value_get_string (value)); break; case PROP_TOOLTIP_TEXT: mx_widget_set_tooltip_text (actor, g_value_get_string (value)); break; case PROP_MENU: mx_widget_set_menu (actor, (MxMenu *)g_value_get_object (value)); break; case PROP_DISABLED: mx_widget_set_disabled (actor, g_value_get_boolean (value)); break; case PROP_TOOLTIP_DELAY: mx_widget_set_tooltip_delay (actor, g_value_get_int (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); break; } } static void mx_widget_get_property (GObject *gobject, guint prop_id, GValue *value, GParamSpec *pspec) { MxWidget *actor = MX_WIDGET (gobject); MxWidgetPrivate *priv = actor->priv; switch (prop_id) { case PROP_STYLE: g_value_set_object (value, priv->style); break; case PROP_STYLE_PSEUDO_CLASS: g_value_set_string (value, priv->pseudo_class); break; case PROP_STYLE_CLASS: g_value_set_string (value, priv->style_class); break; case PROP_TOOLTIP_TEXT: g_value_set_string (value, mx_widget_get_tooltip_text (actor)); break; case PROP_MENU: g_value_set_object (value, mx_widget_get_menu (actor)); break; case PROP_DISABLED: g_value_set_boolean (value, mx_widget_get_disabled (actor)); break; case PROP_TOOLTIP_DELAY: g_value_set_int (value, mx_widget_get_tooltip_delay (actor)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); break; } } static gboolean mx_widget_tooltip_timeout_cb (gpointer data) { MxWidget *self = MX_WIDGET (data); MxWidgetPrivate *priv = self->priv; mx_widget_show_tooltip (self); priv->tooltip_timeout = 0; return FALSE; } static void mx_widget_remove_tooltip_timeout (MxWidget *widget) { MxWidgetPrivate *priv = widget->priv; if (priv->tooltip_timeout) { g_source_remove (priv->tooltip_timeout); priv->tooltip_timeout = 0; } } static void mx_widget_set_tooltip_timeout (MxWidget *widget) { MxWidgetPrivate *priv = widget->priv; /* Remove any existing tooltip timeout so that we can start again */ mx_widget_remove_tooltip_timeout (widget); priv->tooltip_timeout = clutter_threads_add_timeout (mx_widget_get_tooltip_delay (widget), mx_widget_tooltip_timeout_cb, widget); } static void mx_widget_dispose (GObject *gobject) { MxWidget *actor = MX_WIDGET (gobject); MxWidgetPrivate *priv = MX_WIDGET (actor)->priv; if (priv->style) { g_object_unref (priv->style); priv->style = NULL; } if (priv->border_image) { clutter_actor_unparent (priv->border_image); priv->border_image = NULL; } if (priv->old_border_image) { g_object_remove_weak_pointer (G_OBJECT (priv->old_border_image), (gpointer)&priv->old_border_image); clutter_actor_unparent (priv->old_border_image); priv->old_border_image = NULL; } if (priv->background_image) { clutter_actor_unparent (priv->background_image); priv->background_image = NULL; } if (priv->tooltip) { clutter_actor_unparent (CLUTTER_ACTOR (priv->tooltip)); priv->tooltip = NULL; } if (priv->menu) { clutter_actor_unparent (CLUTTER_ACTOR (priv->menu)); priv->menu = NULL; } G_OBJECT_CLASS (mx_widget_parent_class)->dispose (gobject); } static void mx_widget_finalize (GObject *gobject) { MxWidgetPrivate *priv = MX_WIDGET (gobject)->priv; mx_widget_remove_tooltip_timeout (MX_WIDGET (gobject)); g_free (priv->style_class); g_free (priv->pseudo_class); if (priv->mx_border_image) { g_boxed_free (MX_TYPE_BORDER_IMAGE, priv->mx_border_image); priv->mx_border_image = NULL; } clutter_color_free (priv->bg_color); G_OBJECT_CLASS (mx_widget_parent_class)->finalize (gobject); } static void mx_widget_allocate (ClutterActor *actor, const ClutterActorBox *box, ClutterAllocationFlags flags) { MxWidgetPrivate *priv = MX_WIDGET (actor)->priv; ClutterActorClass *klass; ClutterActorBox frame_box = { 0, 0, box->x2 - box->x1, box->y2 - box->y1 }; klass = CLUTTER_ACTOR_CLASS (mx_widget_parent_class); klass->allocate (actor, box, flags); /* update tooltip position */ if (priv->tooltip) { ClutterVertex verts[4]; ClutterGeometry area; gfloat x, y, x2, y2; gint i; clutter_actor_get_abs_allocation_vertices (actor, verts); x = y = G_MAXFLOAT; x2 = y2 = -G_MAXFLOAT; for (i = 0; i < G_N_ELEMENTS (verts); i++) { if (verts[i].x < x) x = verts[i].x; if (verts[i].x > x2) x2 = verts[i].x; if (verts[i].y < y) y = verts[i].y; if (verts[i].y > y2) y2 = verts[i].y; } area.x = x; area.y = y; area.width = x2 - x; area.height = y2 - y; mx_tooltip_set_tip_area (priv->tooltip, &area); } /* allocate border images */ if (priv->border_image) clutter_actor_allocate (priv->border_image, &frame_box, flags); if (priv->old_border_image) clutter_actor_allocate (priv->old_border_image, &frame_box, flags); if (priv->background_image) { gfloat w, h; clutter_actor_get_preferred_size (CLUTTER_ACTOR (priv->background_image), NULL, NULL, &w, &h); /* scale the background into the allocated bounds */ if (w > frame_box.x2 || h > frame_box.y2) { gint new_h, new_w, offset; gint box_w, box_h; box_w = (int) frame_box.x2; box_h = (int) frame_box.y2; /* scale to fit */ new_h = (int)((h / w) * ((gfloat) box_w)); new_w = (int)((w / h) * ((gfloat) box_h)); if (new_h > box_h) { /* center for new width */ offset = ((box_w) - new_w) * 0.5; frame_box.x1 = offset; frame_box.x2 = offset + new_w; frame_box.y2 = box_h; } else { /* center for new height */ offset = ((box_h) - new_h) * 0.5; frame_box.y1 = offset; frame_box.y2 = offset + new_h; frame_box.x2 = box_w; } } else { /* center the background on the widget */ frame_box.x1 = (int)(((box->x2 - box->x1) / 2) - (w / 2)); frame_box.y1 = (int)(((box->y2 - box->y1) / 2) - (h / 2)); frame_box.x2 = frame_box.x1 + w; frame_box.y2 = frame_box.y1 + h; } clutter_actor_allocate (CLUTTER_ACTOR (priv->background_image), &frame_box, flags); } if (priv->tooltip) clutter_actor_allocate_preferred_size (CLUTTER_ACTOR (priv->tooltip), flags); if (priv->menu) clutter_actor_allocate_preferred_size (CLUTTER_ACTOR (priv->menu), flags); } static void mx_widget_real_paint_background (MxWidget *self, ClutterActor *background, const ClutterColor *color) { /* Default implementation just draws the background * colour and the image on top */ if (color && color->alpha != 0) { ClutterActor *actor = CLUTTER_ACTOR (self); ClutterActorBox allocation = { 0, }; ClutterColor bg_color = *color; gfloat w, h; bg_color.alpha = clutter_actor_get_paint_opacity (actor) * bg_color.alpha / 255; clutter_actor_get_allocation_box (actor, &allocation); w = allocation.x2 - allocation.x1; h = allocation.y2 - allocation.y1; cogl_set_source_color4ub (bg_color.red, bg_color.green, bg_color.blue, bg_color.alpha); cogl_rectangle (0, 0, w, h); } if (background) clutter_actor_paint (background); if (self->priv->old_border_image) clutter_actor_paint (self->priv->old_border_image); } static void mx_widget_paint (ClutterActor *self) { MxWidgetPrivate *priv = MX_WIDGET (self)->priv; MxWidgetClass *klass = MX_WIDGET_GET_CLASS (self); klass->paint_background (MX_WIDGET (self), priv->border_image, priv->bg_color); if (priv->background_image != NULL) clutter_actor_paint (priv->background_image); if (priv->tooltip) clutter_actor_paint (CLUTTER_ACTOR (priv->tooltip)); if (priv->menu) clutter_actor_paint (CLUTTER_ACTOR (priv->menu)); } static void mx_widget_pick (ClutterActor *self, const ClutterColor *color) { MxWidgetPrivate *priv = MX_WIDGET (self)->priv; CLUTTER_ACTOR_CLASS (mx_widget_parent_class)->pick (self, color); if (priv->menu) clutter_actor_paint (CLUTTER_ACTOR (priv->menu)); } static void mx_widget_map (ClutterActor *actor) { MxWidgetPrivate *priv = MX_WIDGET (actor)->priv; CLUTTER_ACTOR_CLASS (mx_widget_parent_class)->map (actor); if (priv->border_image) clutter_actor_map (priv->border_image); if (priv->old_border_image) clutter_actor_map (priv->old_border_image); if (priv->background_image) clutter_actor_map (priv->background_image); if (priv->tooltip) clutter_actor_map (CLUTTER_ACTOR (priv->tooltip)); if (priv->menu) clutter_actor_map (CLUTTER_ACTOR (priv->menu)); } static void mx_widget_unmap (ClutterActor *actor) { MxWidgetPrivate *priv = MX_WIDGET (actor)->priv; CLUTTER_ACTOR_CLASS (mx_widget_parent_class)->unmap (actor); if (priv->border_image) clutter_actor_unmap (priv->border_image); if (priv->old_border_image) clutter_actor_unmap (CLUTTER_ACTOR (priv->old_border_image)); if (priv->background_image) clutter_actor_unmap (priv->background_image); if (priv->tooltip) clutter_actor_unmap (CLUTTER_ACTOR (priv->tooltip)); if (priv->menu) clutter_actor_unmap (CLUTTER_ACTOR (priv->menu)); } static void old_background_faded_cb (ClutterAnimation *animation, ClutterActor *self) { clutter_actor_unparent (self); } /* TODO: move to mx-types.c */ static gboolean mx_border_image_equal (MxBorderImage *v1, MxBorderImage *v2) { if (v1 == v2) return FALSE; if (!v1 && v2) return TRUE; if (!v2 && v1) return TRUE; if (g_strcmp0 (v1->uri, v2->uri)) return TRUE; if (v1->top != v2->top) return TRUE; if (v1->right != v2->right) return TRUE; if (v1->bottom != v2->bottom) return TRUE; if (v1->left != v2->left) return TRUE; return FALSE; } static void mx_widget_style_changed (MxStylable *self, MxStyleChangedFlags flags) { MxWidgetPrivate *priv = MX_WIDGET (self)->priv; MxBorderImage *border_image = NULL, *background_image = NULL; MxTextureCache *texture_cache; ClutterTexture *texture; gchar *bg_file; MxPadding *padding = NULL; gboolean relayout_needed = FALSE; gboolean has_changed = FALSE; ClutterColor *color; guint duration; gboolean border_image_changed = FALSE; /* cache these values for use in the paint function */ mx_stylable_get (self, "background-color", &color, "background-image", &background_image, "border-image", &border_image, "padding", &padding, "x-mx-border-image-transition-duration", &duration, NULL); if (color) { if (priv->bg_color && clutter_color_equal (color, priv->bg_color)) { /* color is the same ... */ clutter_color_free (color); } else { clutter_color_free (priv->bg_color); priv->bg_color = color; has_changed = TRUE; } } else if (priv->bg_color) { clutter_color_free (priv->bg_color); priv->bg_color = NULL; has_changed = TRUE; } if (padding) { if (priv->padding.top != padding->top || priv->padding.left != padding->left || priv->padding.right != padding->right || priv->padding.bottom != padding->bottom) { /* Padding changed. Need to relayout. */ has_changed = TRUE; relayout_needed = TRUE; } priv->padding = *padding; g_boxed_free (MX_TYPE_PADDING, padding); } /* border-image property */ /* check whether the border-image has changed */ border_image_changed = mx_border_image_equal (priv->mx_border_image, border_image); /* remove the old border-image if it has changed */ if (border_image_changed && priv->border_image) { if (duration == 0) { clutter_actor_unparent (priv->border_image); } else { if (priv->old_border_image) { g_object_remove_weak_pointer (G_OBJECT (priv->old_border_image), (gpointer)&priv->old_border_image); clutter_actor_unparent (priv->old_border_image); } priv->old_border_image = priv->border_image; g_object_add_weak_pointer (G_OBJECT (priv->old_border_image), (gpointer)&priv->old_border_image); clutter_actor_animate (priv->old_border_image, CLUTTER_LINEAR, duration, "opacity", 0x0, "signal-after::completed", old_background_faded_cb, priv->old_border_image, NULL); } priv->border_image = NULL; } texture_cache = mx_texture_cache_get_default (); /* apply the new border-image, as long as there is a valid URI */ if (border_image_changed && border_image && border_image->uri) { gint border_left, border_right, border_top, border_bottom; gint width, height; texture = mx_texture_cache_get_texture (texture_cache, border_image->uri); clutter_texture_get_base_size (CLUTTER_TEXTURE (texture), &width, &height); border_left = border_image->left; border_top = border_image->top; border_right = border_image->right; border_bottom = border_image->bottom; priv->border_image = mx_texture_frame_new (texture, border_top, border_right, border_bottom, border_left); clutter_actor_set_parent (priv->border_image, CLUTTER_ACTOR (self)); has_changed = TRUE; relayout_needed = TRUE; } /* if the border-image has changed, free the old one and store the new one */ if (border_image_changed) { if (priv->mx_border_image) g_boxed_free (MX_TYPE_BORDER_IMAGE, priv->mx_border_image); priv->mx_border_image = border_image; } else { /* If it's not changed just free the one we've requested */ if (border_image) g_boxed_free (MX_TYPE_BORDER_IMAGE, border_image); } /* background-image property */ if (priv->background_image) { clutter_actor_unparent (priv->background_image); priv->background_image = NULL; } if (background_image) { bg_file = background_image->uri; if (bg_file != NULL && strcmp (bg_file, "none")) { texture = mx_texture_cache_get_texture (texture_cache, bg_file); priv->background_image = (ClutterActor*) texture; if (priv->background_image != NULL) { clutter_actor_set_parent (priv->background_image, CLUTTER_ACTOR (self)); } else g_warning ("Could not load %s", bg_file); has_changed = TRUE; relayout_needed = TRUE; } g_boxed_free (MX_TYPE_BORDER_IMAGE, background_image); } /* If there are any properties above that need to cause a relayout thay * should set this flag. */ if (has_changed) { if (relayout_needed) clutter_actor_queue_relayout ((ClutterActor *) self); else clutter_actor_queue_redraw ((ClutterActor *) self); } } static gboolean mx_widget_enter (ClutterActor *actor, ClutterCrossingEvent *event) { MxWidget *widget = MX_WIDGET (actor); /* This is also expected to handle enter events from child actors because they will bubble up */ mx_stylable_style_pseudo_class_add (MX_STYLABLE (widget), "hover"); return FALSE; } static gboolean mx_widget_leave (ClutterActor *actor, ClutterCrossingEvent *event) { MxWidget *widget = MX_WIDGET (actor); /* If the leave event is simply moving to a child actor then we'll ignore it so that the actor will retain its hover state until the pointer moves to an unrelated actor. For this to work the actual leave event from the child needs to bubble up to this actor */ if (event->related && clutter_actor_contains (actor, event->related)) return FALSE; mx_widget_hide_tooltip (widget); mx_widget_long_press_cancel (widget); mx_stylable_style_pseudo_class_remove (MX_STYLABLE (widget), "active"); mx_stylable_style_pseudo_class_remove (MX_STYLABLE (widget), "hover"); return FALSE; } static gboolean mx_widget_motion (ClutterActor *actor, ClutterMotionEvent *event) { MxWidget *widget = MX_WIDGET (actor); MxWidgetPrivate *priv = widget->priv; if (priv->tooltip && !CLUTTER_ACTOR_IS_VISIBLE (priv->tooltip)) { /* If tooltips are in browse mode then display the tooltip immediately */ if (mx_tooltip_is_in_browse_mode ()) mx_widget_show_tooltip (widget); else mx_widget_set_tooltip_timeout (widget); } return FALSE; } static gboolean mx_widget_emit_long_press (MxWidget *widget) { gboolean result; g_signal_emit (widget, widget_signals[LONG_PRESS], 0, 0.0, 0.0, MX_LONG_PRESS_ACTION, &result); widget->priv->long_press_source = 0; return FALSE; } /** * mx_widget_long_press_query: * @widget: An MxWidget * @event: the event used to determine whether to run a long-press * * Emit the long-press query signal and start a long-press timeout if required. */ void mx_widget_long_press_query (MxWidget *widget, ClutterButtonEvent *event) { MxWidgetPrivate *priv = widget->priv; gboolean query_result = FALSE; MxSettings *settings = mx_settings_get_default (); guint timeout; g_object_get (settings, "long-press-timeout", &timeout, NULL); g_signal_emit (widget, widget_signals[LONG_PRESS], 0, event->x, event->y, MX_LONG_PRESS_QUERY, &query_result); if (query_result) priv->long_press_source = g_timeout_add (timeout, (GSourceFunc) mx_widget_emit_long_press, widget); } /** * mx_widget_long_press_cancel: * @widget: An MxWidget * * Cancel a long-press timeout if one is running and emit the signal to notify * that the long-press has been cancelled. */ void mx_widget_long_press_cancel (MxWidget *widget) { MxWidgetPrivate *priv = widget->priv; if (priv->long_press_source) { gboolean result; g_source_remove (priv->long_press_source); priv->long_press_source = 0; g_signal_emit (widget, widget_signals[LONG_PRESS], 0, 0.0, 0.0, MX_LONG_PRESS_CANCEL, &result); } } /** * mx_widget_apply_style: * @widget: A #MxWidget * @style: A #MxStyle * * Used to implement how a new style instance should be applied in the widget. * For instance, setting style instance on stylable internal children. * * Since: 1.2 */ void mx_widget_apply_style (MxWidget *widget, MxStyle *style) { MxWidgetClass *klass; g_return_if_fail (MX_IS_WIDGET (widget)); g_return_if_fail (style != NULL); klass = MX_WIDGET_GET_CLASS (widget); if (klass->apply_style != NULL) klass->apply_style (widget, style); } static gboolean mx_widget_button_press (ClutterActor *actor, ClutterButtonEvent *event) { MxWidget *widget = MX_WIDGET (actor); if (mx_widget_get_disabled (MX_WIDGET (actor))) return TRUE; if (event->button == 1) mx_stylable_style_pseudo_class_add (MX_STYLABLE (widget), "active"); mx_widget_long_press_query (widget, event); return FALSE; } static gboolean mx_widget_button_release (ClutterActor *actor, ClutterButtonEvent *event) { MxWidget *widget = MX_WIDGET (actor); if (mx_widget_get_disabled (MX_WIDGET (actor))) return TRUE; if (event->button == 1) mx_stylable_style_pseudo_class_remove (MX_STYLABLE (widget), "active"); mx_widget_long_press_cancel (widget); return FALSE; } static void mx_widget_hide (ClutterActor *actor) { MxWidget *widget = (MxWidget *) actor; /* hide the tooltip, if there is one */ mx_widget_hide_tooltip (widget); CLUTTER_ACTOR_CLASS (mx_widget_parent_class)->hide (actor); } static void _mx_widget_propagate_disabled (ClutterContainer *container, gboolean disabled) { GList *c, *children; children = clutter_container_get_children (container); /* Recurse through the children and set the 'parent_disabled' flag. */ for (c = children; c; c = c->next) { MxWidget *child = c->data; if (MX_IS_WIDGET (child)) { MxWidgetPrivate *child_priv = child->priv; child_priv->parent_disabled = disabled; /* emit the "notify" signal for the "disabled" property */ g_object_notify_by_pspec (G_OBJECT (container), widget_properties[PROP_DISABLED]); if (disabled) mx_stylable_style_pseudo_class_add (MX_STYLABLE (child), "disabled"); else mx_stylable_style_pseudo_class_remove (MX_STYLABLE (child), "disabled"); /* If this child has already been disabled explicitly, * we don't need to recurse through its children to set * the 'parent_disabled' flag, as it'll already be set. */ if (child_priv->is_disabled) continue; } if (CLUTTER_IS_CONTAINER (child)) _mx_widget_propagate_disabled ((ClutterContainer *) child, disabled); } g_list_free (children); } static void mx_widget_parent_set (ClutterActor *actor, ClutterActor *old_parent) { ClutterActor *parent; MxWidget *widget = (MxWidget *) actor; MxWidgetPrivate *priv = widget->priv; /* Make sure the disabled state cache remains valid */ if (!priv->is_disabled) { gboolean disabled; parent = clutter_actor_get_parent (actor); while (parent && !MX_IS_WIDGET (parent)) parent = clutter_actor_get_parent (parent); if (parent) { MxWidgetPrivate *parent_priv = ((MxWidget *) parent)->priv; disabled = parent_priv->is_disabled || parent_priv->parent_disabled; } else disabled = FALSE; if (disabled != priv->parent_disabled) { priv->parent_disabled = disabled; if (CLUTTER_IS_CONTAINER (widget)) _mx_widget_propagate_disabled ((ClutterContainer *) widget, disabled); } } } static gboolean mx_widget_get_paint_volume (ClutterActor *actor, ClutterPaintVolume *volume) { /* the allocation of scrollable widgets cannot be used as the paint volume * because it does not account for any transformations applied during * scrolling */ if (MX_IS_SCROLLABLE (actor)) return FALSE; return clutter_paint_volume_set_from_allocation (volume, actor); } static void mx_widget_class_init (MxWidgetClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass); g_type_class_add_private (klass, sizeof (MxWidgetPrivate)); gobject_class->set_property = mx_widget_set_property; gobject_class->get_property = mx_widget_get_property; gobject_class->dispose = mx_widget_dispose; gobject_class->finalize = mx_widget_finalize; actor_class->allocate = mx_widget_allocate; actor_class->paint = mx_widget_paint; actor_class->pick = mx_widget_pick; actor_class->map = mx_widget_map; actor_class->unmap = mx_widget_unmap; actor_class->enter_event = mx_widget_enter; actor_class->leave_event = mx_widget_leave; actor_class->motion_event = mx_widget_motion; actor_class->button_press_event = mx_widget_button_press; actor_class->button_release_event = mx_widget_button_release; actor_class->hide = mx_widget_hide; actor_class->parent_set = mx_widget_parent_set; actor_class->get_paint_volume = mx_widget_get_paint_volume; klass->paint_background = mx_widget_real_paint_background; /* stylable interface properties */ g_object_class_override_property (gobject_class, PROP_STYLE, "style"); widget_properties[PROP_STYLE] = g_object_class_find_property (gobject_class, "style"); g_object_class_override_property (gobject_class, PROP_STYLE_CLASS, "style-class"); widget_properties[PROP_STYLE_CLASS] = g_object_class_find_property (gobject_class, "style-class"); g_object_class_override_property (gobject_class, PROP_STYLE_PSEUDO_CLASS, "style-pseudo-class"); widget_properties[PROP_STYLE_PSEUDO_CLASS] = g_object_class_find_property (gobject_class, "style-pseudo-class"); /** * MxWidget:tooltip-text: * * text displayed on the tooltip */ widget_properties[PROP_TOOLTIP_TEXT] = g_param_spec_string ("tooltip-text", "Tooltip Text", "Text displayed on the tooltip", "", MX_PARAM_READWRITE | MX_PARAM_TRANSLATEABLE); g_object_class_install_property (gobject_class, PROP_TOOLTIP_TEXT, widget_properties[PROP_TOOLTIP_TEXT]); /** * MxWidget:menu: * * #MxMenu associated with the widget. */ widget_properties[PROP_MENU] = g_param_spec_object ("menu", "Menu", "The MxMenu associated with the widget", MX_TYPE_MENU, MX_PARAM_READWRITE); g_object_class_install_property (gobject_class, PROP_MENU, widget_properties[PROP_MENU]); widget_properties[PROP_DISABLED] = g_param_spec_boolean ("disabled", "Disabled", "Whether disabled" " styling should be" " applied and the" " widget made" " unreactive.", FALSE, MX_PARAM_READWRITE); g_object_class_install_property (gobject_class, PROP_DISABLED, widget_properties[PROP_DISABLED]); widget_properties[PROP_TOOLTIP_DELAY] = g_param_spec_int ("tooltip-delay", "Tooltip delay", "Delay time before showing the tooltip", 0, G_MAXINT, MX_WIDGET_TOOLTIP_TIMEOUT, MX_PARAM_READWRITE | G_PARAM_CONSTRUCT); g_object_class_install_property (gobject_class, PROP_TOOLTIP_DELAY, widget_properties[PROP_TOOLTIP_DELAY]); /** * MxWidget::long-press: * @widget: the object that received the signal * * Emitted when the user holds a mouse button down for a longer period. */ widget_signals[LONG_PRESS] = g_signal_new ("long-press", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (MxWidgetClass, long_press), NULL, NULL, _mx_marshal_BOOL__FLOAT_FLOAT_ENUM, G_TYPE_BOOLEAN, 3, G_TYPE_FLOAT, G_TYPE_FLOAT, MX_TYPE_LONG_PRESS_ACTION); } static MxStyle * mx_widget_get_style (MxStylable *stylable) { MxWidgetPrivate *priv = MX_WIDGET (stylable)->priv; return priv->style; } static void mx_widget_set_style (MxStylable *stylable, MxStyle *style) { MxWidgetPrivate *priv = MX_WIDGET (stylable)->priv; if (priv->style) g_object_unref (priv->style); priv->style = g_object_ref_sink (style); mx_widget_apply_style (MX_WIDGET (stylable), style); } static void _mx_widget_set_style_class (MxStylable *actor, const gchar *style_class) { MxWidgetPrivate *priv; g_return_if_fail (MX_IS_WIDGET (actor)); priv = MX_WIDGET (actor)->priv; if (g_strcmp0 (style_class, priv->style_class)) { g_free (priv->style_class); priv->style_class = g_strdup (style_class); g_object_notify_by_pspec (G_OBJECT (actor), widget_properties[PROP_STYLE_CLASS]); } } static void _mx_stylable_set_style_pseudo_class (MxStylable *actor, const gchar *pseudo_class) { MxWidgetPrivate *priv; g_return_if_fail (MX_IS_WIDGET (actor)); priv = MX_WIDGET (actor)->priv; if (g_strcmp0 (pseudo_class, priv->pseudo_class)) { g_free (priv->pseudo_class); priv->pseudo_class = g_strdup (pseudo_class); g_object_notify_by_pspec (G_OBJECT (actor), widget_properties[PROP_STYLE_PSEUDO_CLASS]); } } static const gchar* _mx_stylable_get_style_pseudo_class (MxStylable *actor) { return ((MxWidget *) actor)->priv->pseudo_class; } static const gchar* _mx_widget_get_style_class (MxStylable *actor) { return ((MxWidget *) actor)->priv->style_class; } static void mx_stylable_iface_init (MxStylableIface *iface) { static gboolean is_initialized = FALSE; if (!is_initialized) { GParamSpec *pspec; ClutterColor color = { 0x00, 0x00, 0x00, 0xff }; ClutterColor bg_color = { 0xff, 0xff, 0xff, 0x00 }; is_initialized = TRUE; pspec = clutter_param_spec_color ("background-color", "Background Color", "The background color of an actor", &bg_color, G_PARAM_READWRITE); mx_stylable_iface_install_property (iface, MX_TYPE_WIDGET, pspec); pspec = clutter_param_spec_color ("color", "Text Color", "The color of the text of an actor", &color, G_PARAM_READWRITE); mx_stylable_iface_install_property (iface, MX_TYPE_WIDGET, pspec); pspec = g_param_spec_boxed ("background-image", "Background Image", "Background image filename", MX_TYPE_BORDER_IMAGE, G_PARAM_READWRITE); mx_stylable_iface_install_property (iface, MX_TYPE_WIDGET, pspec); pspec = g_param_spec_string ("font-family", "Font Family", "Name of the font to use", "Sans", G_PARAM_READWRITE); mx_stylable_iface_install_property (iface, MX_TYPE_WIDGET, pspec); pspec = g_param_spec_int ("font-size", "Font Size", "Size of the font to use in pixels", 0, G_MAXINT, 12, G_PARAM_READWRITE); mx_stylable_iface_install_property (iface, MX_TYPE_WIDGET, pspec); pspec = g_param_spec_enum ("font-weight", "Font Weight", "Font Weight", MX_TYPE_FONT_WEIGHT, MX_FONT_WEIGHT_NORMAL, G_PARAM_READWRITE); mx_stylable_iface_install_property (iface, MX_TYPE_WIDGET, pspec); pspec = g_param_spec_boxed ("border-image", "Border image", "9-slice image to use for drawing borders and background", MX_TYPE_BORDER_IMAGE, G_PARAM_READWRITE); mx_stylable_iface_install_property (iface, MX_TYPE_WIDGET, pspec); pspec = g_param_spec_boxed ("padding", "Padding", "Padding between the widget's borders " "and its content", MX_TYPE_PADDING, G_PARAM_READWRITE); mx_stylable_iface_install_property (iface, MX_TYPE_WIDGET, pspec); pspec = g_param_spec_uint ("x-mx-border-image-transition-duration", "background transition duration", "Length of the cross fade when changing images" " in milliseconds", 0, G_MAXUINT, 0, G_PARAM_READWRITE); mx_stylable_iface_install_property (iface, MX_TYPE_WIDGET, pspec); /* pspec = g_param_spec_uint ("x-mx-transition-duration", "transition duration", "Length of transition in milliseconds", 0, G_MAXUINT, 0, G_PARAM_READWRITE); mx_stylable_iface_install_property (iface, MX_TYPE_WIDGET, pspec); pspec = g_param_spec_string ("x-mx-transition-property", "transition property", "Property to apply the transition too", "", G_PARAM_READWRITE); mx_stylable_iface_install_property (iface, MX_TYPE_WIDGET, pspec); */ iface->style_changed = mx_widget_style_changed; iface->get_style = mx_widget_get_style; iface->set_style = mx_widget_set_style; iface->get_style_pseudo_class = _mx_stylable_get_style_pseudo_class; iface->set_style_pseudo_class = _mx_stylable_set_style_pseudo_class; iface->get_style_class = _mx_widget_get_style_class; iface->set_style_class = _mx_widget_set_style_class; } } static void mx_widget_init (MxWidget *actor) { actor->priv = MX_WIDGET_GET_PRIVATE (actor); /* set the default style */ mx_stylable_set_style (MX_STYLABLE (actor), mx_style_get_default ()); /* connect the notifiers for the stylable */ mx_stylable_connect_change_notifiers (MX_STYLABLE (actor)); } /** * mx_widget_get_border_image: * @actor: A #MxWidget * * Get the texture used as the border image. This is set using the * "border-image" CSS property. This function should normally only be used * by subclasses. * * Returns: (transfer none): #ClutterActor */ ClutterActor * mx_widget_get_border_image (MxWidget *actor) { MxWidgetPrivate *priv = MX_WIDGET (actor)->priv; return priv->border_image; } /** * mx_widget_get_background_image: * @actor: A #MxWidget * * Get the texture used as the background image. This is set using the * "background-image" CSS property. This function should normally only be used * by subclasses. * * Returns: (transfer none): a #ClutterActor */ ClutterActor * mx_widget_get_background_image (MxWidget *actor) { MxWidgetPrivate *priv = MX_WIDGET (actor)->priv; return priv->background_image; } /** * mx_widget_get_padding: * @widget: A #MxWidget * @padding: A pointer to an #MxPadding to fill * * Gets the padding of the widget, set using the "padding" CSS property. This * function should normally only be used by subclasses. * */ void mx_widget_get_padding (MxWidget *widget, MxPadding *padding) { g_return_if_fail (MX_IS_WIDGET (widget)); g_return_if_fail (padding != NULL); *padding = widget->priv->padding; } static void mx_widget_set_has_tooltip (MxWidget *widget, gboolean has_tooltip) { MxWidgetPrivate *priv; ClutterActor *actor; g_return_if_fail (MX_IS_WIDGET (widget)); actor = CLUTTER_ACTOR (widget); priv = widget->priv; if (has_tooltip) { clutter_actor_set_reactive (actor, TRUE); if (!priv->tooltip) { priv->tooltip = g_object_new (MX_TYPE_TOOLTIP, NULL); clutter_actor_set_parent (CLUTTER_ACTOR (priv->tooltip), actor); if (mx_stylable_style_pseudo_class_contains (MX_STYLABLE (widget), "hover")) mx_widget_show_tooltip (widget); } } else { if (priv->tooltip) { clutter_actor_unparent (CLUTTER_ACTOR (priv->tooltip)); priv->tooltip = NULL; } mx_widget_remove_tooltip_timeout (widget); } } /** * mx_widget_set_tooltip_text: * @widget: A #MxWidget * @text: text to set as the tooltip * * Set the tooltip text of the widget. Note that setting tooltip text will cause * the widget to be set reactive. If you no longer need tooltips and you do not * need the widget to be reactive, you must set ClutterActor::reactive to * %FALSE. * */ void mx_widget_set_tooltip_text (MxWidget *widget, const gchar *text) { MxWidgetPrivate *priv; const gchar *old_text; g_return_if_fail (MX_IS_WIDGET (widget)); priv = widget->priv; if (priv->tooltip) old_text = mx_tooltip_get_text (priv->tooltip); else old_text = NULL; /* Don't do anything if the text hasn't changed */ if ((text == old_text) || (text && old_text && g_str_equal (text, old_text))) return; if (text == NULL) mx_widget_set_has_tooltip (widget, FALSE); else mx_widget_set_has_tooltip (widget, TRUE); if (priv->tooltip) mx_tooltip_set_text (priv->tooltip, text); g_object_notify_by_pspec (G_OBJECT (widget), widget_properties[PROP_TOOLTIP_TEXT]); } /** * mx_widget_get_tooltip_text: * @widget: A #MxWidget * * Get the current tooltip string * * Returns: The current tooltip string, owned by the #MxWidget */ const gchar* mx_widget_get_tooltip_text (MxWidget *widget) { MxWidgetPrivate *priv; g_return_val_if_fail (MX_IS_WIDGET (widget), NULL); priv = widget->priv; if (!priv->tooltip) return NULL; return mx_tooltip_get_text (widget->priv->tooltip); } /** * mx_widget_show_tooltip: * @widget: A #MxWidget * * Show the tooltip for @widget * */ void mx_widget_show_tooltip (MxWidget *widget) { gint i; gfloat x, y, x2, y2; ClutterGeometry area; ClutterVertex verts[4]; g_return_if_fail (MX_IS_WIDGET (widget)); /* Remove any timeout so we don't show the tooltip again */ mx_widget_remove_tooltip_timeout (widget); /* XXX not necceary, but first allocate transform is wrong */ /* Work out the bounding box */ clutter_actor_get_abs_allocation_vertices ((ClutterActor*) widget, verts); x = y = G_MAXFLOAT; x2 = y2 = -G_MAXFLOAT; for (i = 0; i < G_N_ELEMENTS (verts); i++) { if (verts[i].x < x) x = verts[i].x; if (verts[i].x > x2) x2 = verts[i].x; if (verts[i].y < y) y = verts[i].y; if (verts[i].y > y2) y2 = verts[i].y; } area.x = x; area.y = y; area.width = x2 - x; area.height = y2 - y; if (widget->priv->tooltip) { mx_tooltip_set_tip_area (widget->priv->tooltip, &area); mx_tooltip_show (widget->priv->tooltip); } } /** * mx_widget_hide_tooltip: * @widget: A #MxWidget * * Hide the tooltip for @widget * */ void mx_widget_hide_tooltip (MxWidget *widget) { g_return_if_fail (MX_IS_WIDGET (widget)); mx_widget_remove_tooltip_timeout (widget); if (widget->priv->tooltip) mx_tooltip_hide (widget->priv->tooltip); } /** * mx_widget_paint_background: * @widget: a #MxWidget * * Invokes #MxWidget::paint_background() using the default background * image and/or color from the @widget style * * This function should be used by subclasses of #MxWidget that override * the paint() virtual function and cannot chain up */ void mx_widget_paint_background (MxWidget *self) { MxWidgetPrivate *priv; MxWidgetClass *klass; g_return_if_fail (MX_IS_WIDGET (self)); priv = self->priv; klass = MX_WIDGET_GET_CLASS (self); klass->paint_background (MX_WIDGET (self), priv->border_image, priv->bg_color); } /** * mx_widget_get_available_area: * @widget: A #MxWidget * @allocation: A #ClutterActorBox * @area: A #ClutterActorBox * * Copies @allocation into @area and accounts for the padding values. This * gives the area that is available in which to allocate children with respect * to padding. * */ void mx_widget_get_available_area (MxWidget *widget, const ClutterActorBox *allocation, ClutterActorBox *area) { MxWidgetPrivate *priv = widget->priv; area->x1 = priv->padding.left; area->y1 = priv->padding.top; area->x2 = allocation->x2 - allocation->x1 - priv->padding.right; area->y2 = allocation->y2 - allocation->y1 - priv->padding.bottom; } /** * mx_widget_set_menu: * @widget: A #MxWidget * @menu: A #MxMenu * * Set the value of the #MxWidget:menu property. * */ void mx_widget_set_menu (MxWidget *widget, MxMenu *menu) { MxWidgetPrivate *priv = widget->priv; if (priv->menu) { clutter_actor_unparent (CLUTTER_ACTOR (priv->menu)); priv->menu = NULL; } if (menu) { priv->menu = menu; clutter_actor_set_parent (CLUTTER_ACTOR (menu), CLUTTER_ACTOR (widget)); } clutter_actor_queue_relayout (CLUTTER_ACTOR (widget)); } /** * mx_widget_get_menu: * @widget: A #MxWidget * * Get the object in the #MxWidget:menu property. * * Returns: (transfer none): The current object in the "menu" property. */ MxMenu * mx_widget_get_menu (MxWidget *widget) { return widget->priv->menu; } /** * mx_widget_set_disabled: * @widget: an #MxWidget * @disabled: value to set * * Set the disabled property. Disabled widgets have a "disabled" pseudo-class * until disabled is set to #FALSE. */ void mx_widget_set_disabled (MxWidget *widget, gboolean disabled) { MxWidgetPrivate *priv; g_return_if_fail (MX_IS_WIDGET (widget)); priv = widget->priv; if (priv->is_disabled != disabled) { priv->is_disabled = disabled; if (disabled) mx_stylable_style_pseudo_class_add (MX_STYLABLE (widget), "disabled"); else mx_stylable_style_pseudo_class_remove (MX_STYLABLE (widget), "disabled"); /* Propagate the disabled state to our children, if necessary */ if (!priv->parent_disabled && CLUTTER_IS_CONTAINER (widget)) _mx_widget_propagate_disabled ((ClutterContainer *) widget, disabled); /* when a widget is disabled, get_style_pseudo_class will always return * "disabled" */ clutter_actor_queue_relayout (CLUTTER_ACTOR (widget)); mx_stylable_style_changed (MX_STYLABLE (widget), 0); g_object_notify_by_pspec (G_OBJECT (widget), widget_properties[PROP_DISABLED]); } } /** * mx_widget_get_disabled: * @widget: an #MxWidget * * Get the value of the "disabled" property. */ gboolean mx_widget_get_disabled (MxWidget *widget) { g_return_val_if_fail (MX_IS_WIDGET (widget), FALSE); return widget->priv->is_disabled || widget->priv->parent_disabled; } /** * mx_widget_set_tooltip_delay: * @widget: an #MxWidget * * Set the value, in milliseconds, of the "tooltip-delay" property. * This is initially set to MX_WIDGET_TOOLTIP_TIMEOUT. */ void mx_widget_set_tooltip_delay (MxWidget *widget, guint delay) { g_return_if_fail (MX_IS_WIDGET (widget)); if (widget->priv->tooltip_delay != delay) { widget->priv->tooltip_delay = delay; g_object_notify_by_pspec (G_OBJECT (widget), widget_properties[PROP_TOOLTIP_DELAY]); } } /** * mx_widget_get_tooltip_delay: * @widget: an #MxWidget * * Get the value of the "tooltip-delay" property. * * Returns: the current delay value in milliseconds */ guint mx_widget_get_tooltip_delay (MxWidget *widget) { g_return_val_if_fail (MX_IS_WIDGET (widget), 0); return widget->priv->tooltip_delay; } /* Support translateable strings from JSON */ static void widget_scriptable_set_custom_property (ClutterScriptable *scriptable, ClutterScript *script, const gchar *name, const GValue *value) { GParamSpec *pspec; pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (scriptable), name); if (pspec && pspec->flags & MX_PARAM_TRANSLATEABLE && pspec->value_type == G_TYPE_STRING) { g_object_set (scriptable, name, gettext (g_value_get_string (value)), NULL); } else { /* chain up */ if (parent_scriptable_iface->set_custom_property) parent_scriptable_iface->set_custom_property (scriptable, script, name, value); } } static void scriptable_iface_init (ClutterScriptableIface *iface) { parent_scriptable_iface = g_type_interface_peek_parent (iface); if (!parent_scriptable_iface) parent_scriptable_iface = g_type_default_interface_peek (CLUTTER_TYPE_SCRIPTABLE); iface->set_custom_property = widget_scriptable_set_custom_property; } mx-1.4.7/mx/mx-widget.h000066400000000000000000000116031201047117600146510ustar00rootroot00000000000000/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * mx-widget.h: Base class for Mx actors * * Copyright 2007 OpenedHand * Copyright 2008, 2009 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * Boston, MA 02111-1307, USA. * */ #if !defined(MX_H_INSIDE) && !defined(MX_COMPILATION) #error "Only can be included directly.h" #endif #ifndef __MX_WIDGET_H__ #define __MX_WIDGET_H__ #include #include #include G_BEGIN_DECLS #define MX_TYPE_WIDGET (mx_widget_get_type ()) #define MX_WIDGET(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MX_TYPE_WIDGET, MxWidget)) #define MX_IS_WIDGET(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MX_TYPE_WIDGET)) #define MX_WIDGET_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MX_TYPE_WIDGET, MxWidgetClass)) #define MX_IS_WIDGET_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MX_TYPE_WIDGET)) #define MX_WIDGET_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MX_TYPE_WIDGET, MxWidgetClass)) typedef struct _MxWidget MxWidget; typedef struct _MxWidgetPrivate MxWidgetPrivate; typedef struct _MxWidgetClass MxWidgetClass; typedef struct _MxMenu MxMenu; typedef enum { MX_LONG_PRESS_QUERY, MX_LONG_PRESS_ACTION, MX_LONG_PRESS_CANCEL } MxLongPressAction; /** * MxWidget: * * Base class for stylable actors. The contents of the #MxWidget * structure are private and should only be accessed through the * public API. */ struct _MxWidget { /*< private >*/ ClutterActor parent_instance; MxWidgetPrivate *priv; }; /** * MxWidgetClass: * * Base class for stylable actors. */ struct _MxWidgetClass { /*< private >*/ ClutterActorClass parent_class; /* vfuncs */ void (* paint_background) (MxWidget *self, ClutterActor *background, const ClutterColor *color); gboolean (* long_press) (MxWidget *widget, MxLongPressAction action, gfloat x, gfloat y); void (* apply_style) (MxWidget *widget, MxStyle *style); /*< private >*/ /* padding for future expansion */ void (*_padding_0) (void); void (*_padding_1) (void); void (*_padding_2) (void); void (*_padding_3) (void); void (*_padding_4) (void); void (*_padding_5) (void); void (*_padding_6) (void); void (*_padding_7) (void); void (*_padding_8) (void); }; GType mx_widget_get_type (void) G_GNUC_CONST; void mx_widget_set_tooltip_text (MxWidget *widget, const gchar *text); const gchar* mx_widget_get_tooltip_text (MxWidget *widget); void mx_widget_show_tooltip (MxWidget *widget); void mx_widget_hide_tooltip (MxWidget *widget); void mx_widget_set_menu (MxWidget *widget, MxMenu *menu); MxMenu * mx_widget_get_menu (MxWidget *widget); gboolean mx_widget_get_disabled (MxWidget *widget); void mx_widget_set_disabled (MxWidget *widget, gboolean disabled); void mx_widget_long_press_query (MxWidget *widget, ClutterButtonEvent *event); void mx_widget_long_press_cancel (MxWidget *widget); void mx_widget_set_tooltip_delay (MxWidget *widget, guint delay); guint mx_widget_get_tooltip_delay (MxWidget *widget); /* Only to be used by sub-classes of MxWidget */ ClutterActor *mx_widget_get_background_image (MxWidget *actor); ClutterActor *mx_widget_get_border_image (MxWidget *actor); void mx_widget_get_padding (MxWidget *widget, MxPadding *padding); void mx_widget_paint_background (MxWidget *widget); void mx_widget_apply_style (MxWidget *widget, MxStyle *style); void mx_widget_get_available_area (MxWidget *widget, const ClutterActorBox *allocation, ClutterActorBox *area); G_END_DECLS #endif /* __MX_WIDGET_H__ */ mx-1.4.7/mx/mx-window.c000066400000000000000000001330041201047117600146700ustar00rootroot00000000000000/* * mx-window.c: A top-level window class * * Copyright 2010 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * Boston, MA 02111-1307, USA. * * Written by: Thomas Wood * Chris Lord * */ /** * SECTION:mx-window * @short_description: an object that represents a platform-specific window * * #MxWindow is a platform-specific window, providing functions for moving, * resizing, icons and rotation. Every window has an associated #ClutterStage * in which its children are kept. This #ClutterStage can be used in the * normal way, but to take advantage of automatic resizing and rotation, the * #MxWindow functions should be used. * * When the #MxWindow loses its last reference, its contents are destroyed. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "mx-window.h" #include "mx-native-window.h" #include "mx-toolbar.h" #include "mx-focus-manager.h" #include "mx-private.h" #include "mx-marshal.h" #ifdef HAVE_X11 #include "x11/mx-window-x11.h" #endif G_DEFINE_TYPE (MxWindow, mx_window, G_TYPE_OBJECT) static GQuark window_quark = 0; struct _MxWindowPrivate { MxNativeWindow *native_window; guint has_toolbar : 1; guint small_screen : 1; guint fullscreen : 1; guint rotate_size : 1; gchar *icon_name; CoglHandle icon_texture; ClutterActor *stage; ClutterActor *toolbar; ClutterActor *child; ClutterActor *resize_grip; ClutterActor *debug_actor; MxWindowRotation rotation; ClutterTimeline *rotation_timeline; ClutterAlpha *rotation_alpha; gfloat start_angle; gfloat end_angle; gfloat angle; }; #define WINDOW_PRIVATE(o) \ (G_TYPE_INSTANCE_GET_PRIVATE ((o), MX_TYPE_WINDOW, MxWindowPrivate)) enum { PROP_STYLE = 1, PROP_STYLE_CLASS, PROP_STYLE_PSEUDO_CLASS, PROP_HAS_TOOLBAR, PROP_TOOLBAR, PROP_SMALL_SCREEN, PROP_FULLSCREEN, PROP_TITLE, PROP_ICON_NAME, PROP_ICON_COGL_TEXTURE, PROP_CLUTTER_STAGE, PROP_CHILD, PROP_WINDOW_ROTATION, PROP_WINDOW_ROTATION_TIMELINE, PROP_WINDOW_ROTATION_ANGLE }; enum { DESTROY, LAST_SIGNAL }; static guint signals[LAST_SIGNAL] = { 0, }; static void mx_window_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { MxWindow *self = MX_WINDOW (object); MxWindowPrivate *priv = self->priv; switch (property_id) { case PROP_HAS_TOOLBAR: g_value_set_boolean (value, priv->has_toolbar); break; case PROP_TOOLBAR: g_value_set_object (value, priv->toolbar); break; case PROP_SMALL_SCREEN: g_value_set_boolean (value, priv->small_screen); break; case PROP_FULLSCREEN: g_value_set_boolean (value, priv->fullscreen); break; case PROP_TITLE: g_value_set_string (value, mx_window_get_title (self)); break; case PROP_ICON_NAME: g_value_set_string (value, priv->icon_name); break; case PROP_ICON_COGL_TEXTURE: g_value_set_pointer (value, priv->icon_texture); break; case PROP_CLUTTER_STAGE: g_value_set_object (value, priv->stage); break; case PROP_CHILD: g_value_set_object (value, priv->child); break; case PROP_WINDOW_ROTATION: g_value_set_enum (value, priv->rotation); break; case PROP_WINDOW_ROTATION_TIMELINE: g_value_set_object (value, priv->rotation_timeline); break; case PROP_WINDOW_ROTATION_ANGLE: g_value_set_float (value, priv->angle); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } } static void mx_window_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { MxWindow *window = MX_WINDOW (object); switch (property_id) { case PROP_HAS_TOOLBAR: mx_window_set_has_toolbar (window, g_value_get_boolean (value)); break; case PROP_TOOLBAR: mx_window_set_toolbar (window, g_value_get_object (value)); break; case PROP_SMALL_SCREEN: mx_window_set_small_screen (window, g_value_get_boolean (value)); break; case PROP_FULLSCREEN: mx_window_set_fullscreen (window, g_value_get_boolean (value)); break; case PROP_TITLE: mx_window_set_title (window, g_value_get_string (value)); break; case PROP_ICON_NAME: mx_window_set_icon_name (window, g_value_get_string (value)); break; case PROP_ICON_COGL_TEXTURE: mx_window_set_icon_from_cogl_texture (MX_WINDOW (object), g_value_get_pointer (value)); break; case PROP_CLUTTER_STAGE: window->priv->stage = (ClutterActor *)g_value_get_object (value); break; case PROP_CHILD: mx_window_set_child (window, (ClutterActor *)g_value_get_object (value)); break; case PROP_WINDOW_ROTATION: mx_window_set_window_rotation (window, g_value_get_enum (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } } static void mx_window_dispose (GObject *object) { MxWindow *self = MX_WINDOW (object); MxWindowPrivate *priv = self->priv; if (priv->icon_texture) { cogl_handle_unref (priv->icon_texture); priv->icon_texture = NULL; } if (priv->toolbar) mx_window_set_toolbar (self, NULL); if (priv->resize_grip) { g_object_remove_weak_pointer (G_OBJECT (priv->resize_grip), (gpointer *)&priv->resize_grip); priv->resize_grip = NULL; } if (priv->stage) { g_object_set_qdata (G_OBJECT (priv->stage), window_quark, NULL); /* Destroying the stage will destroy all the actors inside it */ g_object_remove_weak_pointer (G_OBJECT (priv->stage), (gpointer *)&priv->stage); ClutterActor *stage = priv->stage; priv->stage = NULL; clutter_actor_destroy (stage); } if (priv->native_window) { g_object_unref (priv->native_window); priv->native_window = NULL; } if (priv->rotation_alpha) { g_object_unref (priv->rotation_alpha); priv->rotation_alpha = NULL; } if (priv->rotation_timeline) { g_object_unref (priv->rotation_timeline); priv->rotation_timeline = NULL; } G_OBJECT_CLASS (mx_window_parent_class)->dispose (object); } static void mx_window_finalize (GObject *object) { MxWindowPrivate *priv = MX_WINDOW (object)->priv; g_free (priv->icon_name); G_OBJECT_CLASS (mx_window_parent_class)->finalize (object); } static void mx_window_get_size (MxWindow *window, gfloat *width, gfloat *height) { gfloat stage_width, stage_height, scale, angle; MxWindowPrivate *priv = window->priv; clutter_actor_get_size (priv->stage, &stage_width, &stage_height); /* Normalise the angle */ angle = priv->angle; while (angle < 0) angle += 360.f; while (angle >= 360.f) angle -= 360.f; /* Interpolate between width and height depending on rotation */ if (!clutter_timeline_is_playing (priv->rotation_timeline) || priv->rotate_size) { if (angle <= 90.f) scale = angle / 90.f; else if (angle <= 180.f) scale = 1.f - (angle - 90.f) / 90.f; else if (angle <= 270.f) scale = (angle - 180.f) / 90.f; else scale = 1.f - (angle - 270.f) / 90.f; } else if ((priv->rotation == MX_WINDOW_ROTATION_0) || (priv->rotation == MX_WINDOW_ROTATION_180)) scale = 0; else scale = 1; if (width) *width = (scale * stage_height) + ((1.f - scale) * stage_width); if (height) *height = (scale * stage_width) + ((1.f - scale) * stage_height); } static void debug_paint (ClutterActor *stage, MxWindow *window) { MxPadding padding = { 0, }; static gpointer current; ClutterActor *actor; gfloat width, height, x, y; CoglMatrix matrix; actor = window->priv->debug_actor; if (!actor) return; if (MX_IS_WIDGET (actor)) mx_widget_get_padding (MX_WIDGET (actor), &padding); clutter_actor_get_size (actor, &width, &height); clutter_actor_get_transformed_position (actor, &x, &y); { ClutterActor *cur_actor; GSList *parents = NULL, *l; cogl_get_modelview_matrix (&matrix); /* Get the complete modelview matrix for light by applying all of its parent transformations as well as its own in reverse */ for (cur_actor = actor; cur_actor != stage; cur_actor = clutter_actor_get_parent (cur_actor)) parents = g_slist_prepend (parents, cur_actor); for (l = parents; l; l = l->next) { CoglMatrix actor_matrix; cogl_matrix_init_identity (&actor_matrix); clutter_actor_get_transformation_matrix (CLUTTER_ACTOR (l->data), &actor_matrix); cogl_matrix_multiply (&matrix, &matrix, &actor_matrix); } g_slist_free (parents); } cogl_push_matrix (); cogl_set_modelview_matrix (&matrix); if (current != actor) { current = actor; const char *id = clutter_actor_get_name (current); const char *class = NULL; const char *type_name = G_OBJECT_TYPE_NAME (current); if (MX_IS_STYLABLE (current)) class = mx_stylable_get_style_class (current); g_debug ("%s%s%s%s%s (%.1f, %.1f) (%.1f x %.1f)", (type_name) ? type_name : "", (class) ? "." : "", (class) ? class : "", (id) ? "#" : "", (id) ? id : "", x, y, width, height); } cogl_set_source_color4f (0, 0, 1, 0.5); cogl_rectangle (0, 0, width, height); cogl_set_source_color4f (0, 1, 0, 0.5); cogl_rectangle (padding.left, padding.top, width - padding.right, height - padding.bottom); cogl_set_source_color4f (0, 0, 0, 1); cogl_path_rectangle (0, 0, width, height); cogl_path_stroke (); cogl_set_source_color4f (1, 1, 1, 1); cogl_path_rectangle (1, 1, width - 1, height - 1); cogl_path_stroke (); cogl_pop_matrix (); } static void debug_actor_notify (gpointer debug_actor_p, GObject *actor) { *((gpointer *)debug_actor_p) = NULL; } static gboolean debug_captured_event (ClutterActor *actor, ClutterEvent *event, gpointer data) { MxWindowPrivate *priv = MX_WINDOW (data)->priv; if (event->type == CLUTTER_MOTION) { ClutterMotionEvent *m_event = (ClutterMotionEvent *) event; if (priv->debug_actor) g_object_weak_unref (G_OBJECT (priv->debug_actor), debug_actor_notify, &priv->debug_actor); priv->debug_actor = clutter_stage_get_actor_at_pos (CLUTTER_STAGE (actor), CLUTTER_PICK_ALL, m_event->x, m_event->y); g_object_weak_ref (G_OBJECT (priv->debug_actor), debug_actor_notify, &priv->debug_actor); } return FALSE; } static void mx_window_post_paint_cb (ClutterActor *actor, MxWindow *window) { gfloat width, height, stage_width, stage_height; MxWindowPrivate *priv = window->priv; /* If we're in small-screen or fullscreen mode, or we don't have the toolbar, * we don't want a frame or a resize handle. */ if (!priv->has_toolbar || priv->small_screen || priv->fullscreen) return; mx_window_get_size (window, &width, &height); clutter_actor_get_size (actor, &stage_width, &stage_height); cogl_push_matrix (); /* Adjust for rotation */ cogl_translate ((stage_width - width) / 2.f, (stage_height - height) / 2.f, 0); cogl_translate (width / 2.f, height / 2.f, 0); cogl_rotate (priv->angle, 0, 0, 1); cogl_translate (-width / 2.f, -height / 2.f, 0); /* paint frame */ cogl_set_source_color4f (0.2, 0.2, 0.2, 1); cogl_rectangle (0, 0, width, 1); cogl_rectangle (0, height - 1, width, height); cogl_rectangle (0, 1, 1, height - 1); cogl_rectangle (width - 1, 1, width, height - 1); cogl_pop_matrix (); if (_mx_debug (MX_DEBUG_LAYOUT)) debug_paint (actor, window); } static void mx_window_allocation_changed_cb (ClutterActor *actor, ClutterActorBox *box, ClutterAllocationFlags flags, MxWindow *window) { MxPadding padding; gboolean from_toolbar; MxWindowPrivate *priv; gfloat x, y, width, height, toolbar_height, stage_width, stage_height; /* Note, ideally this would happen just before allocate, but there's * no signal we can connect to for that without overriding an actor. * * Instead, we do this each time the allocation is changed on the stage * or the toolbar. This shouldn't be a frequent occurence, but * unfortunately can happen multiple times before the actual relayout * happens. */ priv = window->priv; from_toolbar = (actor == priv->toolbar); actor = priv->stage; mx_window_get_size (window, &width, &height); clutter_actor_get_size (actor, &stage_width, &stage_height); x = (stage_width - width) / 2.f; y = (stage_height - height) / 2.f; if (!priv->has_toolbar || priv->small_screen || priv->fullscreen) padding.top = padding.right = padding.bottom = padding.left = 0; else padding.top = padding.right = padding.bottom = padding.left = 1; if (priv->has_toolbar && priv->toolbar) { clutter_actor_get_preferred_height (priv->toolbar, width - padding.left - padding.right, NULL, &toolbar_height); if (!from_toolbar) { clutter_actor_set_position (priv->toolbar, padding.left + x, padding.top + y); clutter_actor_set_rotation (priv->toolbar, CLUTTER_Z_AXIS, priv->angle, width / 2.f - padding.left, height / 2.f - padding.top, 0); g_object_set (G_OBJECT (priv->toolbar), "natural-width", width - padding.left - padding.right, NULL); } } else { toolbar_height = 0; } if (priv->child) { g_object_set (G_OBJECT (priv->child), "natural-width", width - padding.left - padding.right, "natural-height", height - toolbar_height - padding.top - padding.bottom, "x", padding.left + x, "y", toolbar_height + padding.top + y, NULL); clutter_actor_set_rotation (priv->child, CLUTTER_Z_AXIS, priv->angle, width / 2.f - padding.left, height / 2.f - padding.top - toolbar_height, 0); } if (priv->resize_grip) { clutter_actor_get_preferred_size (priv->resize_grip, NULL, NULL, &width, &height); clutter_actor_set_position (priv->resize_grip, stage_width - width - padding.right, stage_height - height - padding.bottom); } } static void mx_window_reallocate (MxWindow *self) { ClutterActorBox box; MxWindowPrivate *priv = self->priv; clutter_actor_get_allocation_box (priv->stage, &box); mx_window_allocation_changed_cb (priv->stage, &box, 0, self); } static void mx_window_fullscreen_set_cb (ClutterStage *stage, GParamSpec *pspec, MxWindow *self) { MxWindowPrivate *priv = self->priv; /* Synchronise our local fullscreen-set property */ if (priv->fullscreen != clutter_stage_get_fullscreen (stage)) { priv->fullscreen = !priv->fullscreen; g_object_notify (G_OBJECT (self), "fullscreen"); } /* If we're in small-screen mode, make sure the size gets reset * correctly. */ if (!priv->fullscreen) { if (!priv->small_screen && priv->resize_grip && priv->has_toolbar && clutter_stage_get_user_resizable (stage)) { clutter_actor_show (priv->resize_grip); if (priv->child) clutter_actor_raise (priv->resize_grip, priv->child); } } else if (priv->resize_grip) clutter_actor_hide (priv->resize_grip); mx_window_reallocate (self); clutter_actor_queue_relayout (CLUTTER_ACTOR (stage)); } static void mx_window_title_cb (ClutterStage *stage, GParamSpec *pspec, MxWindow *self) { g_object_notify (G_OBJECT (self), "title"); } static void mx_window_destroy_cb (ClutterStage *stage, MxWindow *self) { self->priv->stage = NULL; g_signal_emit (self, signals[DESTROY], 0); } static void mx_window_actor_added_cb (ClutterContainer *container, ClutterActor *actor, MxWindow *self) { MxWindowPrivate *priv = self->priv; if (priv->resize_grip && priv->child) clutter_actor_raise (priv->resize_grip, priv->child); } static void mx_window_actor_removed_cb (ClutterContainer *container, ClutterActor *actor, MxWindow *self) { MxWindowPrivate *priv = self->priv; if (actor == priv->child) { g_object_set (G_OBJECT (priv->child), "natural-width-set", FALSE, "natural-height-set", FALSE, NULL); priv->child = NULL; g_object_notify (G_OBJECT (self), "child"); } } static void mx_window_user_resizable_cb (ClutterStage *stage, GParamSpec *pspec, MxWindow *self) { MxWindowPrivate *priv = self->priv; if (!clutter_stage_get_user_resizable (stage)) clutter_actor_hide (priv->resize_grip); else { if (!priv->fullscreen && !priv->small_screen && priv->has_toolbar) { clutter_actor_show (priv->resize_grip); if (priv->child) clutter_actor_raise (priv->resize_grip, priv->child); } } } static void mx_window_constructed (GObject *object) { MxWindow *self = MX_WINDOW (object); MxWindowPrivate *priv = self->priv; if (!priv->stage) { priv->stage = g_object_new (CLUTTER_TYPE_STAGE, NULL); clutter_stage_set_user_resizable ((ClutterStage *)priv->stage, TRUE); } mx_focus_manager_get_for_stage ((ClutterStage *)priv->stage); g_object_add_weak_pointer (G_OBJECT (priv->stage), (gpointer *)&priv->stage); g_object_set_qdata (G_OBJECT (priv->stage), window_quark, object); if (!priv->toolbar && priv->has_toolbar) mx_window_set_toolbar (self, MX_TOOLBAR (mx_toolbar_new ())); priv->resize_grip = mx_icon_new (); mx_stylable_set_style_class (MX_STYLABLE (priv->resize_grip), "ResizeGrip"); clutter_container_add_actor (CLUTTER_CONTAINER (priv->stage), priv->resize_grip); if (priv->fullscreen || !clutter_stage_get_user_resizable (CLUTTER_STAGE (priv->stage)) || !priv->has_toolbar) clutter_actor_hide (priv->resize_grip); g_object_add_weak_pointer (G_OBJECT (priv->resize_grip), (gpointer *)&priv->resize_grip); g_signal_connect_after (priv->stage, "paint", G_CALLBACK (mx_window_post_paint_cb), object); g_signal_connect (priv->stage, "allocation-changed", G_CALLBACK (mx_window_allocation_changed_cb), object); g_signal_connect (priv->stage, "notify::fullscreen-set", G_CALLBACK (mx_window_fullscreen_set_cb), object); g_signal_connect (priv->stage, "notify::title", G_CALLBACK (mx_window_title_cb), object); g_signal_connect (priv->stage, "destroy", G_CALLBACK (mx_window_destroy_cb), object); g_signal_connect (priv->stage, "actor-added", G_CALLBACK (mx_window_actor_added_cb), object); g_signal_connect (priv->stage, "actor-removed", G_CALLBACK (mx_window_actor_removed_cb), object); g_signal_connect (priv->stage, "notify::user-resizable", G_CALLBACK (mx_window_user_resizable_cb), object); if (_mx_debug (MX_DEBUG_LAYOUT)) g_signal_connect (priv->stage, "captured-event", G_CALLBACK (debug_captured_event), object); g_object_set (G_OBJECT (priv->stage), "use-alpha", TRUE, NULL); #ifdef HAVE_X11 priv->native_window = _mx_window_x11_new (self); #endif } static void mx_window_class_init (MxWindowClass *klass) { GParamSpec *pspec; GObjectClass *object_class = G_OBJECT_CLASS (klass); g_type_class_add_private (klass, sizeof (MxWindowPrivate)); object_class->get_property = mx_window_get_property; object_class->set_property = mx_window_set_property; object_class->dispose = mx_window_dispose; object_class->finalize = mx_window_finalize; object_class->constructed = mx_window_constructed; pspec = g_param_spec_boolean ("has-toolbar", "Has toolbar", "Window should have a toolbar.", TRUE, MX_PARAM_READWRITE); g_object_class_install_property (object_class, PROP_HAS_TOOLBAR, pspec); pspec = g_param_spec_object ("toolbar", "Toolbar", "The MxToolbar associated with the window.", MX_TYPE_TOOLBAR, MX_PARAM_READWRITE); g_object_class_install_property (object_class, PROP_TOOLBAR, pspec); pspec = g_param_spec_boolean ("small-screen", "Small screen", "Window should occupy the entire screen " "contents, without explicitly setting " "itself fullscreen.", FALSE, MX_PARAM_READWRITE); g_object_class_install_property (object_class, PROP_SMALL_SCREEN, pspec); pspec = g_param_spec_boolean ("fullscreen", "Fullscreen", "Window should be set to full-screen mode.", FALSE, MX_PARAM_READWRITE); g_object_class_install_property (object_class, PROP_FULLSCREEN, pspec); pspec = g_param_spec_string ("title", "Title", "Title to use for the window.", NULL, MX_PARAM_READWRITE); g_object_class_install_property (object_class, PROP_TITLE, pspec); pspec = g_param_spec_string ("icon-name", "Icon name", "Icon name to use for the window icon.", NULL, MX_PARAM_READWRITE); g_object_class_install_property (object_class, PROP_ICON_NAME, pspec); pspec = g_param_spec_string ("icon-cogl-texture", "Icon CoglTexture", "CoglTexture to use for the window icon.", NULL, MX_PARAM_READWRITE); g_object_class_install_property (object_class, PROP_ICON_COGL_TEXTURE, pspec); pspec = g_param_spec_object ("clutter-stage", "Clutter stage", "ClutterStage to use as the window.", CLUTTER_TYPE_STAGE, MX_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); g_object_class_install_property (object_class, PROP_CLUTTER_STAGE, pspec); pspec = g_param_spec_object ("child", "Child", "ClutterActor used as the window child.", CLUTTER_TYPE_ACTOR, MX_PARAM_READWRITE); g_object_class_install_property (object_class, PROP_CHILD, pspec); pspec = g_param_spec_enum ("window-rotation", "Window rotation", "The window's rotation.", MX_TYPE_WINDOW_ROTATION, MX_WINDOW_ROTATION_0, MX_PARAM_READWRITE); g_object_class_install_property (object_class, PROP_WINDOW_ROTATION, pspec); pspec = g_param_spec_object ("window-rotation-timeline", "Window rotation timeline", "The timeline used for the window rotation " "transition animation.", CLUTTER_TYPE_TIMELINE, MX_PARAM_READABLE); g_object_class_install_property (object_class, PROP_WINDOW_ROTATION_TIMELINE, pspec); pspec = g_param_spec_float ("window-rotation-angle", "Window rotation angle", "The current angle of rotation about the z-axis " "for the window.", 0.f, 360.f, 0.f, MX_PARAM_READABLE); g_object_class_install_property (object_class, PROP_WINDOW_ROTATION_ANGLE, pspec); /** * MxWindow::destroy: * @window: the object that received the signal * * Emitted when the stage managed by the window is destroyed. */ signals[DESTROY] = g_signal_new ("destroy", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (MxWindowClass, destroy), NULL, NULL, _mx_marshal_VOID__VOID, G_TYPE_NONE, 0); window_quark = g_quark_from_static_string ("mx-window"); } static void mx_window_rotation_new_frame_cb (ClutterTimeline *timeline, gint msecs, MxWindow *self) { MxWindowPrivate *priv = self->priv; gfloat alpha = clutter_alpha_get_alpha (priv->rotation_alpha); priv->angle = (alpha * priv->end_angle) + ((1.f - alpha) * priv->start_angle); mx_window_reallocate (self); g_object_notify (G_OBJECT (self), "window-rotation-angle"); } static void mx_window_rotation_completed_cb (ClutterTimeline *timeline, MxWindow *self) { MxWindowPrivate *priv = self->priv; priv->angle = priv->end_angle; while (priv->angle >= 360.f) priv->angle -= 360.f; while (priv->angle < 0.f) priv->angle += 360.f; priv->rotate_size = FALSE; mx_window_reallocate (self); g_object_notify (G_OBJECT (self), "window-rotation-angle"); } static void mx_window_init (MxWindow *self) { MxWindowPrivate *priv = self->priv = WINDOW_PRIVATE (self); priv->rotation_timeline = clutter_timeline_new (400); priv->rotation_alpha = clutter_alpha_new_full (priv->rotation_timeline, CLUTTER_EASE_IN_OUT_QUAD); priv->has_toolbar = TRUE; g_signal_connect (priv->rotation_timeline, "new-frame", G_CALLBACK (mx_window_rotation_new_frame_cb), self); g_signal_connect (priv->rotation_timeline, "completed", G_CALLBACK (mx_window_rotation_completed_cb), self); } CoglHandle _mx_window_get_icon_cogl_texture (MxWindow *window) { CoglHandle texture; MxWindowPrivate *priv; g_return_val_if_fail (MX_IS_WINDOW (window), COGL_INVALID_HANDLE); priv = window->priv; texture = priv->icon_texture; priv->icon_texture = NULL; return texture; } ClutterActor * _mx_window_get_resize_grip (MxWindow *window) { g_return_val_if_fail (MX_IS_WINDOW (window), NULL); return window->priv->resize_grip; } /** * mx_window_new: * * Creates a new #MxWindow. * * Returns: A #MxWindow */ MxWindow * mx_window_new (void) { return g_object_new (MX_TYPE_WINDOW, NULL); } /** * mx_window_new_with_clutter_stage: * @stage: A #ClutterStage * * Creates a new #MxWindow, using @stage as the backing #ClutterStage. This * function is meant for use primarily for embedding a #MxWindow into * a foreign stage when using a Clutter toolkit integration library. * * Returns: A #MxWindow */ MxWindow * mx_window_new_with_clutter_stage (ClutterStage *stage) { return g_object_new (MX_TYPE_WINDOW, "clutter-stage", stage, NULL); } /** * mx_window_get_for_stage: * @stage: A #ClutterStage * * Gets the #MxWindow parent of the #ClutterStage, if it exists. * * Returns: (transfer none): A #MxWindow, or %NULL */ MxWindow * mx_window_get_for_stage (ClutterStage *stage) { g_return_val_if_fail (CLUTTER_IS_STAGE (stage), NULL); return (MxWindow *)g_object_get_qdata (G_OBJECT (stage), window_quark); } /** * mx_window_set_child: * @window: A #MxWindow * @actor: A #ClutterActor * * Adds @actor to the window and sets it as the primary child. When the * stage managed in the window changes size, the child will be resized * to match it. */ void mx_window_set_child (MxWindow *window, ClutterActor *actor) { MxWindowPrivate *priv; g_return_if_fail (MX_IS_WINDOW (window)); g_return_if_fail (actor == NULL || CLUTTER_IS_ACTOR (actor)); priv = window->priv; if (!priv->stage) return; if (priv->child == actor) return; if (priv->child) clutter_container_remove_actor (CLUTTER_CONTAINER (priv->stage), priv->child); if (actor) { priv->child = actor; clutter_container_add_actor (CLUTTER_CONTAINER (priv->stage), priv->child); } mx_window_reallocate (window); g_object_notify (G_OBJECT (window), "child"); } /** * mx_window_get_child: * @window: A #MxWindow * * Get the primary child of the window. See mx_window_set_child(). * * Returns: (transfer none): A #ClutterActor, or %NULL */ ClutterActor* mx_window_get_child (MxWindow *window) { g_return_val_if_fail (MX_IS_WINDOW (window), NULL); return window->priv->child; } /** * mx_window_set_has_toolbar: * @window: A #MxWindow * @toolbar: %TRUE if the toolbar should be displayed * * Sets whether the window has a toolbar or not. If the window has a toolbar, * client-side window decorations will be enabled. */ void mx_window_set_has_toolbar (MxWindow *window, gboolean toolbar) { MxWindowPrivate *priv; g_return_if_fail (MX_IS_WINDOW (window)); priv = window->priv; if (priv->has_toolbar != toolbar) { priv->has_toolbar = toolbar; if (!toolbar) { clutter_actor_hide (priv->toolbar); clutter_actor_hide (priv->resize_grip); } else { clutter_actor_show (priv->toolbar); if (clutter_stage_get_user_resizable ((ClutterStage *)priv->stage)) clutter_actor_show (priv->resize_grip); } g_object_notify (G_OBJECT (window), "has-toolbar"); mx_window_reallocate (window); } } /** * mx_window_get_has_toolbar: * @window: A #MxWindow * * Determines whether the window has a toolbar or not. * See mx_window_set_has_toolbar(). * * Returns: %TRUE if the window has a toolbar, otherwise %FALSE */ gboolean mx_window_get_has_toolbar (MxWindow *window) { g_return_val_if_fail (MX_IS_WINDOW (window), FALSE); return window->priv->has_toolbar; } /** * mx_window_get_toolbar: * @window: A #MxWindow * * Retrieves the toolbar associated with the window. * * Returns: (transfer none): A #MxToolbar */ MxToolbar * mx_window_get_toolbar (MxWindow *window) { g_return_val_if_fail (MX_IS_WINDOW (window), NULL); return MX_TOOLBAR (window->priv->toolbar); } /** * mx_window_set_toolbar: * @window: (allow-none): A #MxWindow * * Sets the toolbar associated with the window. * * Since: 1.2 */ void mx_window_set_toolbar (MxWindow *window, MxToolbar *toolbar) { MxWindowPrivate *priv; g_return_if_fail (MX_IS_WINDOW (window)); g_return_if_fail (!toolbar || MX_IS_TOOLBAR (toolbar)); priv = window->priv; if (priv->toolbar == (ClutterActor *)toolbar) return; /* Remove old toolbar */ if (priv->toolbar) { g_signal_handlers_disconnect_by_func (priv->toolbar, mx_window_allocation_changed_cb, window); g_object_remove_weak_pointer (G_OBJECT (priv->toolbar), (gpointer *)&priv->toolbar); clutter_container_remove_actor (CLUTTER_CONTAINER (priv->stage), priv->toolbar); } priv->toolbar = (ClutterActor *)toolbar; /* Add new toolbar */ if (toolbar) { clutter_container_add_actor (CLUTTER_CONTAINER (priv->stage), priv->toolbar); g_object_add_weak_pointer (G_OBJECT (priv->toolbar), (gpointer *)&priv->toolbar); g_signal_connect (priv->toolbar, "allocation-changed", G_CALLBACK (mx_window_allocation_changed_cb), window); } priv->has_toolbar = priv->toolbar ? TRUE : FALSE; } /** * mx_window_get_small_screen: * @window: A #MxWindow * * Determines if the window is in small-screen mode. * See mx_window_set_small_screen(). * * Returns: %TRUE if the window is in small-screen mode, otherwise %FALSE */ gboolean mx_window_get_small_screen (MxWindow *window) { g_return_val_if_fail (MX_IS_WINDOW (window), FALSE); return window->priv->small_screen; } /** * mx_window_set_small_screen: * @window: A #MxWindow * @small_screen: %TRUE if small-screen mode should be enabled * * Enables or disables small-screen mode. This mode is meant primarily * for platforms with limited screen-space, such as netbooks. When enabled, * the window will take up all available room and will disable moving and * resizing. */ void mx_window_set_small_screen (MxWindow *window, gboolean small_screen) { MxWindowPrivate *priv; g_return_if_fail (MX_IS_WINDOW (window)); priv = window->priv; if (priv->small_screen != small_screen) { priv->small_screen = small_screen; g_object_notify (G_OBJECT (window), "small-screen"); } } /** * mx_window_get_fullscreen: * @window: A #MxWindow * * Determines if the window has been set to be in fullscreen mode. * * Returns: %TRUE if the window has been set to be in fullscreen mode, * otherwise %FALSE * * Since: 1.2 */ gboolean mx_window_get_fullscreen (MxWindow *window) { g_return_val_if_fail (MX_IS_WINDOW (window), FALSE); return window->priv->fullscreen; } /** * mx_window_set_fullscreen: * @window: A #MxWindow * @fullscreen: %TRUE to request fullscreen mode, %FALSE to disable * * Set the window to be in fullscreen mode or windowed mode. * * * Setting fullscreen mode doesn't necessarily mean the window is actually * fullscreen. Setting this property is only a request to the underlying * window system. * * * Since: 1.2 */ void mx_window_set_fullscreen (MxWindow *window, gboolean fullscreen) { MxWindowPrivate *priv; g_return_if_fail (MX_IS_WINDOW (window)); priv = window->priv; if (priv->fullscreen != fullscreen) { priv->fullscreen = fullscreen; clutter_stage_set_fullscreen (CLUTTER_STAGE (priv->stage), fullscreen); g_object_notify (G_OBJECT (window), "fullscreen"); } } /** * mx_window_set_title: * @window: A #MxWindow * @title: A string to use for the window title name * * Sets the title used for the window, the results of which are * window-system specific. * * Since: 1.2 */ void mx_window_set_title (MxWindow *window, const gchar *title) { g_return_if_fail (MX_IS_WINDOW (window)); g_return_if_fail (title != NULL); clutter_stage_set_title (CLUTTER_STAGE (window->priv->stage), title); } /** * mx_window_get_title: * @window: A #MxWindow * * Retrieves the title used for the window. * * Returns: The title used for the window * * Since: 1.2 */ const gchar * mx_window_get_title (MxWindow *window) { g_return_val_if_fail (MX_IS_WINDOW (window), NULL); return clutter_stage_get_title (CLUTTER_STAGE (window->priv->stage)); } /** * mx_window_set_icon_name: * @window: A #MxWindow * @icon_name: (allow-none): An icon name, or %NULL * * Set an icon-name to use for the window icon. The icon will be looked up * from the default theme. */ void mx_window_set_icon_name (MxWindow *window, const gchar *icon_name) { MxWindowPrivate *priv; g_return_if_fail (MX_IS_WINDOW (window)); priv = window->priv; if (priv->icon_name && icon_name && g_str_equal (priv->icon_name, icon_name)) return; if (!priv->icon_name && !icon_name) return; g_free (priv->icon_name); priv->icon_name = g_strdup (icon_name); g_object_notify (G_OBJECT (window), "icon-name"); } /** * mx_window_get_icon_name: * @window: A #MxWindow * * Gets the currently set window icon name. This will be %NULL if there is none * set, or the icon was set with mx_window_set_icon_from_cogl_texture(). * * Returns: The window icon name, or %NULL */ const gchar * mx_window_get_icon_name (MxWindow *window) { g_return_val_if_fail (MX_IS_WINDOW (window), NULL); return window->priv->icon_name; } /** * mx_window_set_icon_from_cogl_texture: * @window: A #MxWindow * @texture: A #CoglHandle for a texture * * Sets the window icon from a texture. This will take precedence over * any currently set icon-name. */ void mx_window_set_icon_from_cogl_texture (MxWindow *window, CoglHandle texture) { MxWindowPrivate *priv; g_return_if_fail (MX_IS_WINDOW (window)); g_return_if_fail (texture != NULL); priv = window->priv; if (priv->icon_name) { g_free (priv->icon_name); priv->icon_name = NULL; g_object_notify (G_OBJECT (window), "icon-name"); } if (priv->icon_texture) { cogl_handle_unref (priv->icon_texture); priv->icon_texture = NULL; } if (texture) priv->icon_texture = cogl_handle_ref (texture); g_object_notify (G_OBJECT (window), "icon-cogl-texture"); } /** * mx_window_get_clutter_stage: * @window: A #MxWindow * * Gets the #ClutterStage managed by the window. * * Returns: (transfer none): A #ClutterStage */ ClutterStage * mx_window_get_clutter_stage (MxWindow *window) { g_return_val_if_fail (MX_IS_WINDOW (window), NULL); return CLUTTER_STAGE (window->priv->stage); } /** * mx_window_get_window_position: * @window: an #MxWindow * @x: (out): A pointer for the x-coordinate * @y: (out): A pointer for the y-coordinate * * Retrieves the absolute position of the window on the screen. */ void mx_window_get_window_position (MxWindow *window, gint *x, gint *y) { MxWindowPrivate *priv; g_return_if_fail (MX_IS_WINDOW (window)); priv = window->priv; if (priv->native_window) _mx_native_window_get_position (priv->native_window, x, y); else { if (x) *x = 0; if (y) *y = 0; } } /** * mx_window_set_window_position: * @window: A #MxWindow * @x: An x-coordinate * @y: A y-coordinate * * Sets the absolute position of the window on the screen. */ void mx_window_set_window_position (MxWindow *window, gint x, gint y) { MxWindowPrivate *priv; g_return_if_fail (MX_IS_WINDOW (window)); priv = window->priv; if (priv->native_window) _mx_native_window_set_position (priv->native_window, x, y); } /** * mx_window_get_window_size: * @window: A #MxWindow * @width: (out): A #gint pointer for the window's width * @height: (out): A #gint pointer for the window's height * * Retrieves the size of the display area of the window, taking into * account any window border. This includes the area occupied by the * window's toolbar, if it's enabled. * * Since: 1.2 */ void mx_window_get_window_size (MxWindow *window, gint *width, gint *height) { MxWindowPrivate *priv; gfloat fwidth, fheight; g_return_if_fail (MX_IS_WINDOW (window)); priv = window->priv; mx_window_get_size (window, &fwidth, &fheight); if (width) *width = (gint)fwidth; if (height) *height = (gint)fheight; if (priv->has_toolbar && !priv->small_screen && !priv->fullscreen) { if (width) *width = *width + 1; if (height) *height = *height + 1; } } /** * mx_window_set_window_size: * @window: A #MxWindow * @width: A width, in pixels * @height: A height, in pixels * * Sets the size of the window, taking into account any window border. This * corresponds to the window's available area for its child, minus the area * occupied by the window's toolbar, if it's enabled. * * * Setting the window size may involve a request to the underlying windowing * system, and may not immediately be reflected. * * * Since: 1.2 */ void mx_window_set_window_size (MxWindow *window, gint width, gint height) { MxWindowPrivate *priv; g_return_if_fail (MX_IS_WINDOW (window)); priv = window->priv; if (priv->has_toolbar && !priv->small_screen && !priv->fullscreen) { width ++; height ++; } clutter_actor_set_size (priv->stage, width, height); } /** * mx_window_present: * @window: A #MxWindow * * Present the window. The actual behaviour is specific to the window system. * * Since: 1.2 */ void mx_window_present (MxWindow *window) { MxWindowPrivate *priv; g_return_if_fail (MX_IS_WINDOW (window)); priv = window->priv; if (priv->native_window) _mx_native_window_present (priv->native_window); } /** * mx_window_set_window_rotation: * @window: A #MxWindow * @rotation: The #MxWindowRotation * * Set the rotation of the window. * * Since: 1.2 */ void mx_window_set_window_rotation (MxWindow *window, MxWindowRotation rotation) { guint msecs; MxWindowPrivate *priv; g_return_if_fail (MX_IS_WINDOW (window)); priv = window->priv; if (priv->rotation == rotation) return; if (((priv->rotation == MX_WINDOW_ROTATION_0) || (priv->rotation == MX_WINDOW_ROTATION_180)) && ((rotation == MX_WINDOW_ROTATION_90) || (rotation == MX_WINDOW_ROTATION_270))) priv->rotate_size = TRUE; else if (((priv->rotation == MX_WINDOW_ROTATION_90) || (priv->rotation == MX_WINDOW_ROTATION_270)) && ((rotation == MX_WINDOW_ROTATION_0) || (rotation == MX_WINDOW_ROTATION_180))) priv->rotate_size = TRUE; priv->rotation = rotation; priv->start_angle = priv->angle; switch (rotation) { case MX_WINDOW_ROTATION_0 : priv->end_angle = 0.f; break; case MX_WINDOW_ROTATION_90 : priv->end_angle = 90.f; break; case MX_WINDOW_ROTATION_180 : priv->end_angle = 180.f; break; case MX_WINDOW_ROTATION_270 : priv->end_angle = 270.f; break; } if (priv->end_angle - priv->start_angle > 180.f) priv->end_angle -= 360.f; else if (priv->end_angle - priv->start_angle < -180.f) priv->end_angle += 360.f; msecs = (guint)((ABS (priv->end_angle - priv->start_angle) / 90.f) * 400.f); clutter_timeline_rewind (priv->rotation_timeline); clutter_timeline_set_duration (priv->rotation_timeline, msecs); clutter_timeline_start (priv->rotation_timeline); g_object_notify (G_OBJECT (window), "window-rotation"); } /** * mx_window_get_window_rotation: * @window: A #MxWindow * * Retrieve the rotation of the window. * * Returns: An #MxWindowRotation * * Since: 1.2 */ MxWindowRotation mx_window_get_window_rotation (MxWindow *window) { g_return_val_if_fail (MX_IS_WINDOW (window), MX_WINDOW_ROTATION_0); return window->priv->rotation; } /** * mx_window_show: * @window: A #MxWindow * * Show the window * * Since: 1.2 */ void mx_window_show (MxWindow *window) { g_return_if_fail (MX_IS_WINDOW (window)); clutter_actor_show (window->priv->stage); } /** * mx_window_hide: * @window: A #MxWindow * * Hide the window * * Since: 1.2 */ void mx_window_hide (MxWindow *window) { g_return_if_fail (MX_IS_WINDOW (window)); clutter_actor_hide (window->priv->stage); } mx-1.4.7/mx/mx-window.h000066400000000000000000000105321201047117600146750ustar00rootroot00000000000000/* * mx-window.h * * Copyright 2010 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * Boston, MA 02111-1307, USA. * * Written by: Thomas Wood * Chris Lord * */ #if !defined(MX_H_INSIDE) && !defined(MX_COMPILATION) #error "Only can be included directly.h" #endif #ifndef _MX_WINDOW_H #define _MX_WINDOW_H #include #include G_BEGIN_DECLS #define MX_TYPE_WINDOW mx_window_get_type() #define MX_WINDOW(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ MX_TYPE_WINDOW, MxWindow)) #define MX_WINDOW_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), \ MX_TYPE_WINDOW, MxWindowClass)) #define MX_IS_WINDOW(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \ MX_TYPE_WINDOW)) #define MX_IS_WINDOW_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE ((klass), \ MX_TYPE_WINDOW)) #define MX_WINDOW_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), \ MX_TYPE_WINDOW, MxWindowClass)) /** * MxWindow: * * The contents of this structure is private and should only be accessed using * the provided API. */ typedef struct _MxWindow MxWindow; typedef struct _MxWindowClass MxWindowClass; typedef struct _MxWindowPrivate MxWindowPrivate; struct _MxWindow { GObject parent; MxWindowPrivate *priv; }; struct _MxWindowClass { GObjectClass parent_class; /* signals, not vfuncs */ void (*destroy) (MxWindow *window); /* padding for future expansion */ void (*_padding_0) (void); void (*_padding_1) (void); void (*_padding_2) (void); void (*_padding_3) (void); void (*_padding_4) (void); }; GType mx_window_get_type (void) G_GNUC_CONST; MxWindow *mx_window_new (void); MxWindow *mx_window_new_with_clutter_stage (ClutterStage *stage); MxWindow *mx_window_get_for_stage (ClutterStage *stage); ClutterActor* mx_window_get_child (MxWindow *window); void mx_window_set_child (MxWindow *window, ClutterActor *actor); MxToolbar* mx_window_get_toolbar (MxWindow *window); void mx_window_set_toolbar (MxWindow *window, MxToolbar *toolbar); gboolean mx_window_get_has_toolbar (MxWindow *window); void mx_window_set_has_toolbar (MxWindow *window, gboolean toolbar); gboolean mx_window_get_small_screen (MxWindow *window); void mx_window_set_small_screen (MxWindow *window, gboolean small_screen); gboolean mx_window_get_fullscreen (MxWindow *window); void mx_window_set_fullscreen (MxWindow *window, gboolean fullscreen); void mx_window_set_title (MxWindow *window, const gchar *title); const gchar *mx_window_get_title (MxWindow *window); void mx_window_set_icon_name (MxWindow *window, const gchar *icon_name); const gchar *mx_window_get_icon_name (MxWindow *window); void mx_window_set_icon_from_cogl_texture (MxWindow *window, CoglHandle texture); ClutterStage *mx_window_get_clutter_stage (MxWindow *window); void mx_window_get_window_position (MxWindow *window, gint *x, gint *y); void mx_window_set_window_position (MxWindow *window, gint x, gint y); void mx_window_get_window_size (MxWindow *window, gint *width, gint *height); void mx_window_set_window_size (MxWindow *window, gint width, gint height); void mx_window_present (MxWindow *window); void mx_window_set_window_rotation (MxWindow *window, MxWindowRotation rotation); MxWindowRotation mx_window_get_window_rotation (MxWindow *window); void mx_window_show (MxWindow *window); void mx_window_hide (MxWindow *window); G_END_DECLS #endif /* _MX_WINDOW_H */ mx-1.4.7/mx/mx.h000066400000000000000000000053751201047117600134010ustar00rootroot00000000000000/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * mx.h: Collection of high-level actors for Clutter * * Copyright 2009, 2010 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * Boston, MA 02111-1307, USA. * */ #ifndef __MX_H__ #define __MX_H__ #define MX_H_INSIDE #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 #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 #include #include #include #include #include #include #include #include #include #undef MX_H_INSIDE #endif /* __MX_H__ */ mx-1.4.7/mx/x11/000077500000000000000000000000001201047117600132035ustar00rootroot00000000000000mx-1.4.7/mx/x11/mx-clipboard-x11.c000066400000000000000000000250771201047117600163520ustar00rootroot00000000000000/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * mx-clipboard.c: clipboard object * * Copyright 2009 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * * Written by: Thomas Wood * */ /** * SECTION:mx-clipboard * @short_description: a simple representation of the X clipboard * * #MxClipboard is a very simple object representation of the clipboard * available to applications. Text is always assumed to be UTF-8 and non-text * items are not handled. */ #include "mx-clipboard.h" #include #include #include #include G_DEFINE_TYPE (MxClipboard, mx_clipboard, G_TYPE_OBJECT) #define CLIPBOARD_PRIVATE(o) \ (G_TYPE_INSTANCE_GET_PRIVATE ((o), MX_TYPE_CLIPBOARD, MxClipboardPrivate)) struct _MxClipboardPrivate { Window clipboard_window; gchar *clipboard_text; Atom *supported_targets; gint n_targets; }; typedef struct _EventFilterData EventFilterData; struct _EventFilterData { MxClipboard *clipboard; MxClipboardCallbackFunc callback; gpointer user_data; }; static Atom __atom_clip = None; static Atom __utf8_string = None; static Atom __atom_targets = None; static void mx_clipboard_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { switch (property_id) { default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } } static void mx_clipboard_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { switch (property_id) { default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } } static void mx_clipboard_dispose (GObject *object) { G_OBJECT_CLASS (mx_clipboard_parent_class)->dispose (object); } static void mx_clipboard_finalize (GObject *object) { MxClipboardPrivate *priv = ((MxClipboard *) object)->priv; g_free (priv->clipboard_text); priv->clipboard_text = NULL; g_free (priv->supported_targets); priv->supported_targets = NULL; priv->n_targets = 0; G_OBJECT_CLASS (mx_clipboard_parent_class)->finalize (object); } static ClutterX11FilterReturn mx_clipboard_provider (XEvent *xev, ClutterEvent *cev, MxClipboard *clipboard) { XSelectionEvent notify_event; XSelectionRequestEvent *req_event; if (xev->type != SelectionRequest) return CLUTTER_X11_FILTER_CONTINUE; req_event = &xev->xselectionrequest; clutter_x11_trap_x_errors (); if (req_event->target == __atom_targets) { XChangeProperty (req_event->display, req_event->requestor, req_event->property, XA_ATOM, 32, PropModeReplace, (guchar*) clipboard->priv->supported_targets, clipboard->priv->n_targets); } else { if (!clipboard->priv->clipboard_text) { g_warning ("Clipboard request received, but no text available"); return CLUTTER_X11_FILTER_REMOVE; } XChangeProperty (req_event->display, req_event->requestor, req_event->property, req_event->target, 8, PropModeReplace, (guchar*) clipboard->priv->clipboard_text, strlen (clipboard->priv->clipboard_text)); } notify_event.type = SelectionNotify; notify_event.display = req_event->display; notify_event.requestor = req_event->requestor; notify_event.selection = req_event->selection; notify_event.target = req_event->target; notify_event.time = req_event->time; if (req_event->property == None) notify_event.property = req_event->target; else notify_event.property = req_event->property; /* notify the requestor that they have a copy of the selection */ XSendEvent (req_event->display, req_event->requestor, False, 0, (XEvent *) ¬ify_event); /* Make it happen non async */ XSync (clutter_x11_get_default_display(), FALSE); clutter_x11_untrap_x_errors (); /* FIXME: Warn here on fail ? */ return CLUTTER_X11_FILTER_REMOVE; } static void mx_clipboard_class_init (MxClipboardClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); g_type_class_add_private (klass, sizeof (MxClipboardPrivate)); object_class->get_property = mx_clipboard_get_property; object_class->set_property = mx_clipboard_set_property; object_class->dispose = mx_clipboard_dispose; object_class->finalize = mx_clipboard_finalize; } static void mx_clipboard_init (MxClipboard *self) { Display *dpy; MxClipboardPrivate *priv; priv = self->priv = CLIPBOARD_PRIVATE (self); priv->clipboard_window = XCreateSimpleWindow (clutter_x11_get_default_display (), clutter_x11_get_root_window (), -1, -1, 1, 1, 0, 0, 0); dpy = clutter_x11_get_default_display (); /* Only create once */ if (__atom_clip == None) __atom_clip = XInternAtom (dpy, "CLIPBOARD", 0); if (__utf8_string == None) __utf8_string = XInternAtom (dpy, "UTF8_STRING", 0); if (__atom_targets == None) __atom_targets = XInternAtom (dpy, "TARGETS", 0); priv->n_targets = 2; priv->supported_targets = g_new (Atom, priv->n_targets); priv->supported_targets[0] = __utf8_string; priv->supported_targets[1] = __atom_targets; clutter_x11_add_filter ((ClutterX11FilterFunc) mx_clipboard_provider, self); } static ClutterX11FilterReturn mx_clipboard_x11_event_filter (XEvent *xev, ClutterEvent *cev, EventFilterData *filter_data) { Atom actual_type; int actual_format, result; unsigned long nitems, bytes_after; unsigned char *data = NULL; if(xev->type != SelectionNotify) return CLUTTER_X11_FILTER_CONTINUE; if (xev->xselection.property == None) { /* clipboard empty */ filter_data->callback (filter_data->clipboard, NULL, filter_data->user_data); clutter_x11_remove_filter ((ClutterX11FilterFunc) mx_clipboard_x11_event_filter, filter_data); g_free (filter_data); return CLUTTER_X11_FILTER_REMOVE; } clutter_x11_trap_x_errors (); result = XGetWindowProperty (xev->xselection.display, xev->xselection.requestor, xev->xselection.property, 0L, G_MAXINT, True, AnyPropertyType, &actual_type, &actual_format, &nitems, &bytes_after, &data); if (clutter_x11_untrap_x_errors () || result != Success) { /* FIXME: handle failure better */ g_warning ("Clipboard: prop retrival failed"); } filter_data->callback (filter_data->clipboard, (char*) data, filter_data->user_data); clutter_x11_remove_filter ((ClutterX11FilterFunc) mx_clipboard_x11_event_filter, filter_data); g_free (filter_data); if (data) XFree (data); return CLUTTER_X11_FILTER_REMOVE; } /** * mx_clipboard_get_default: * * Get the global #MxClipboard object that represents the clipboard. * * Returns: (transfer none): a #MxClipboard owned by Mx and must not be * unrefferenced or freed. */ MxClipboard* mx_clipboard_get_default (void) { static MxClipboard *default_clipboard = NULL; if (!default_clipboard) { default_clipboard = g_object_new (MX_TYPE_CLIPBOARD, NULL); } return default_clipboard; } /** * mx_clipboard_get_text: * @clipboard: A #MxClipboard * @callback: function to be called when the text is retreived * @user_data: data to be passed to the callback * * Request the data from the clipboard in text form. @callback is executed * when the data is retreived. * */ void mx_clipboard_get_text (MxClipboard *clipboard, MxClipboardCallbackFunc callback, gpointer user_data) { EventFilterData *data; Display *dpy; g_return_if_fail (MX_IS_CLIPBOARD (clipboard)); g_return_if_fail (callback != NULL); data = g_new0 (EventFilterData, 1); data->clipboard = clipboard; data->callback = callback; data->user_data = user_data; clutter_x11_add_filter ((ClutterX11FilterFunc) mx_clipboard_x11_event_filter, data); dpy = clutter_x11_get_default_display (); clutter_x11_trap_x_errors (); /* safety on */ XConvertSelection (dpy, __atom_clip, __utf8_string, __utf8_string, clipboard->priv->clipboard_window, CurrentTime); clutter_x11_untrap_x_errors (); } /** * mx_clipboard_set_text: * @clipboard: A #MxClipboard * @text: text to copy to the clipboard * * Sets text as the current contents of the clipboard. * */ void mx_clipboard_set_text (MxClipboard *clipboard, const gchar *text) { MxClipboardPrivate *priv; Display *dpy; g_return_if_fail (MX_IS_CLIPBOARD (clipboard)); g_return_if_fail (text != NULL); priv = clipboard->priv; /* make a copy of the text */ g_free (priv->clipboard_text); priv->clipboard_text = g_strdup (text); /* tell X we own the clipboard selection */ dpy = clutter_x11_get_default_display (); clutter_x11_trap_x_errors (); XSetSelectionOwner (dpy, __atom_clip, priv->clipboard_window, CurrentTime); XSync (dpy, FALSE); clutter_x11_untrap_x_errors (); } mx-1.4.7/mx/x11/mx-settings-x11.c000066400000000000000000000331541201047117600162460ustar00rootroot00000000000000/* * mx-settings-x11.c: X11 settings provider * * Copyright 2010 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * Boston, MA 02111-1307, USA. * * Written by: Thomas Wood * */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "mx-settings-x11.h" #include "mx-private.h" #include "xsettings-client.h" #include #include static void mx_settings_provider_iface_init (MxSettingsProviderIface *iface); G_DEFINE_TYPE_WITH_CODE (MxSettingsX11, _mx_settings_x11, G_TYPE_OBJECT, G_IMPLEMENT_INTERFACE (MX_TYPE_SETTINGS_PROVIDER, mx_settings_provider_iface_init)) #define SETTINGS_X11_PRIVATE(o) \ (G_TYPE_INSTANCE_GET_PRIVATE ((o), MX_TYPE_SETTINGS_X11, MxSettingsX11Private)) enum { MX_ATOM_NET_SUPPORTING_WM_CHECK, MX_ATOM_MOBLIN }; static gchar *mx_settings_atoms[] = { "_NET_SUPPORTING_WM_CHECK", "_MOBLIN" }; enum { PROP_0, PROP_SETTINGS }; struct _MxSettingsX11Private { MxSettings *settings; gchar *icon_theme; gchar *font_name; guint long_press_timeout; guint small_screen : 1; guint atoms_init : 1; XSettingsClient *client; Atom atoms[G_N_ELEMENTS(mx_settings_atoms)]; Window *wm_window; }; static void xsettings_notify_func (const char *name, XSettingsAction action, XSettingsSetting *setting, void *cb_data); static ClutterX11FilterReturn mx_settings_x11_event_filter (XEvent *xev, ClutterEvent *cev, void *data); static gboolean mx_settings_x11_get_setting (MxSettingsProvider *self, MxSettingsProperty id, gpointer value) { MxSettingsX11Private *priv = MX_SETTINGS_X11 (self)->priv; switch (id) { case MX_SETTINGS_ICON_THEME: *((gchar **)value) = priv->icon_theme; return TRUE; case MX_SETTINGS_FONT_NAME: *((gchar **)value) = priv->font_name; return TRUE; case MX_SETTINGS_LONG_PRESS_TIMEOUT: *((guint *)value) = priv->long_press_timeout; return TRUE; case MX_SETTINGS_SMALL_SCREEN: *((gboolean *)value) = priv->small_screen; return TRUE; default: return FALSE; } } static gboolean mx_settings_x11_set_setting (MxSettingsProvider *self, MxSettingsProperty id, gpointer value) { MxSettingsX11Private *priv = MX_SETTINGS_X11 (self)->priv; switch (id) { case MX_SETTINGS_ICON_THEME: g_free (priv->icon_theme); priv->icon_theme = g_strdup (*((gchar **)value)); break; case MX_SETTINGS_FONT_NAME: g_free (priv->font_name); priv->font_name = g_strdup (*((gchar **)value)); break; case MX_SETTINGS_LONG_PRESS_TIMEOUT: priv->long_press_timeout = *((guint *)value); break; case MX_SETTINGS_SMALL_SCREEN: priv->small_screen = *((gboolean *)value); break; default: return FALSE; } _mx_settings_provider_setting_changed (self, id); return TRUE; } static void mx_settings_x11_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { MxSettingsX11Private *priv = MX_SETTINGS_X11 (object)->priv; switch (property_id) { case PROP_SETTINGS: g_value_set_object (value, priv->settings); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } } static void mx_settings_x11_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { MxSettingsX11Private *priv = MX_SETTINGS_X11 (object)->priv; switch (property_id) { case PROP_SETTINGS: priv->settings = g_value_get_object (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } } static void mx_settings_x11_finalize (GObject *object) { MxSettingsX11Private *priv = MX_SETTINGS_X11 (object)->priv; clutter_x11_remove_filter (mx_settings_x11_event_filter, object); g_free (priv->icon_theme); g_free (priv->font_name); if (priv->client) xsettings_client_destroy (priv->client); if (priv->wm_window) XFree (priv->wm_window); G_OBJECT_CLASS (_mx_settings_x11_parent_class)->finalize (object); } static void mx_settings_x11_constructed (GObject *object) { Display *dpy; Window root_win; XWindowAttributes attr; MxSettingsX11 *self = MX_SETTINGS_X11 (object); /* setup xsettings client */ /* This needs to be done after the construction of the object * to prevent recursion because creating a new xsettings client will * cause the notify function to be called, which in turn may cause the * style-changed signal to be emitted on MxStyle. Handlers of the * style-changed signal may need an MxSettings object. */ dpy = clutter_x11_get_default_display (); self->priv->client = xsettings_client_new (dpy, clutter_x11_get_default_screen (), xsettings_notify_func, NULL, self); /* Add X property change notifications to the event mask */ root_win = clutter_x11_get_root_window (); if (XGetWindowAttributes (dpy, root_win, &attr)) XSelectInput (dpy, root_win, attr.your_event_mask | PropertyChangeMask); clutter_x11_add_filter (mx_settings_x11_event_filter, self); } static void mx_settings_provider_iface_init (MxSettingsProviderIface *iface) { static gboolean is_initialised = FALSE; if (!is_initialised) { is_initialised = TRUE; iface->get_setting = mx_settings_x11_get_setting; iface->set_setting = mx_settings_x11_set_setting; } } static void _mx_settings_x11_class_init (MxSettingsX11Class *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); g_type_class_add_private (klass, sizeof (MxSettingsX11Private)); object_class->get_property = mx_settings_x11_get_property; object_class->set_property = mx_settings_x11_set_property; object_class->finalize = mx_settings_x11_finalize; object_class->constructed = mx_settings_x11_constructed; g_object_class_override_property (object_class, PROP_SETTINGS, "settings"); } static void xsettings_notify_func (const char *name, XSettingsAction action, XSettingsSetting *setting, void *cb_data) { MxSettingsX11Private *priv = MX_SETTINGS_X11 (cb_data)->priv; if (!name || !setting) return; if (!strcmp (name, "Net/IconThemeName")) { /* check icon theme name has changed */ if (g_strcmp0 (priv->icon_theme, setting->data.v_string)) { g_free (priv->icon_theme); priv->icon_theme = g_strdup (setting->data.v_string); _mx_settings_provider_setting_changed (MX_SETTINGS_PROVIDER (cb_data), MX_SETTINGS_ICON_THEME); } } else if (!strcmp (name, "Gtk/FontName")) { /* check if the font name has changed */ if (g_strcmp0 (priv->font_name, setting->data.v_string)) { g_free (priv->font_name); priv->font_name = g_strdup (setting->data.v_string); _mx_settings_provider_setting_changed (MX_SETTINGS_PROVIDER (cb_data), MX_SETTINGS_FONT_NAME); g_signal_emit_by_name (mx_style_get_default (), "changed", 0, NULL); } } } static void mx_settings_x11_refresh_wm_props (MxSettingsX11 *self) { unsigned long n_items, bytes_left; unsigned char *return_string; int return_format; Atom return_type; Display *dpy; MxSettingsX11Private *priv = self->priv; if (!priv->atoms[MX_ATOM_MOBLIN] || !priv->wm_window) return; dpy = clutter_x11_get_default_display (); /* Get the Moblin WM properties string */ clutter_x11_trap_x_errors (); return_string = NULL; XGetWindowProperty (dpy, *priv->wm_window, priv->atoms[MX_ATOM_MOBLIN], 0, 8192, False, XA_STRING, &return_type, &return_format, &n_items, &bytes_left, &return_string); clutter_x11_untrap_x_errors (); /* The _MOBLIN properties string is a list of 'key=value' pairs, * delimited by colons. */ if (return_string) { gboolean small_screen = priv->small_screen; gchar *prop = g_strdelimit ((gchar *)return_string, ":", '\0'); priv->small_screen = FALSE; while (*prop) { gchar *key = g_strdelimit (prop, "=", '\0'); gchar *value = key + strlen (key) + 1; /* Check for session-type=small-screen - the only property * we support, currently. */ if (g_str_equal (key, "session-type") && g_str_equal (value, "small-screen")) priv->small_screen = TRUE; prop = value + strlen (value) + 1; } XFree (return_string); if (priv->small_screen != small_screen) _mx_settings_provider_setting_changed (MX_SETTINGS_PROVIDER (self), MX_SETTINGS_SMALL_SCREEN); } } static void mx_settings_x11_refresh_wm_window (MxSettingsX11 *self) { unsigned long n_items, bytes_left; int return_format; Atom return_type; Window root_win; Display *dpy; MxSettingsX11Private *priv = self->priv; if (!priv->atoms[MX_ATOM_NET_SUPPORTING_WM_CHECK]) return; root_win = clutter_x11_get_root_window (); dpy = clutter_x11_get_default_display (); if (priv->wm_window) { XFree (priv->wm_window); priv->wm_window = NULL; } /* Get the WM window */ clutter_x11_trap_x_errors (); XGetWindowProperty (dpy, root_win, priv->atoms[MX_ATOM_NET_SUPPORTING_WM_CHECK], 0, 1, False, XA_WINDOW, &return_type, &return_format, &n_items, &bytes_left, (unsigned char **)(&priv->wm_window)); clutter_x11_untrap_x_errors (); if (priv->wm_window && (*priv->wm_window == None)) { XFree (priv->wm_window); priv->wm_window = NULL; } if (priv->wm_window) { XWindowAttributes attr; /* Add property-change notification */ if (XGetWindowAttributes (dpy, *priv->wm_window, &attr)) XSelectInput (dpy, *priv->wm_window, attr.your_event_mask | PropertyChangeMask); /* Refresh the WM properties */ mx_settings_x11_refresh_wm_props (self); } } static void mx_settings_x11_init_wm (MxSettingsX11 *self) { Display *dpy; MxSettingsX11Private *priv = self->priv; priv->atoms_init = TRUE; dpy = clutter_x11_get_default_display (); /* Create the relevant Atoms for reading WM properties */ XInternAtoms (dpy, mx_settings_atoms, G_N_ELEMENTS (mx_settings_atoms), False, priv->atoms); /* Read the current properties */ mx_settings_x11_refresh_wm_window (self); } static ClutterX11FilterReturn mx_settings_x11_event_filter (XEvent *xev, ClutterEvent *cev, void *data) { Window root_win; MxSettingsX11 *self = MX_SETTINGS_X11 (data); MxSettingsX11Private *priv = self->priv; if (!priv->atoms_init) mx_settings_x11_init_wm (self); root_win = clutter_x11_get_root_window (); switch (xev->type) { case PropertyNotify: if (xev->xproperty.window == root_win) { if (xev->xproperty.atom == priv->atoms[MX_ATOM_NET_SUPPORTING_WM_CHECK]) mx_settings_x11_refresh_wm_window (self); } else if (priv->wm_window && (xev->xproperty.window == *priv->wm_window)) { if (xev->xproperty.atom == priv->atoms[MX_ATOM_MOBLIN]) mx_settings_x11_refresh_wm_props (self); } break; case DestroyNotify: if (priv->wm_window && (xev->xdestroywindow.window == *priv->wm_window)) { XFree (priv->wm_window); priv->wm_window = NULL; } break; } if (xsettings_client_process_event (priv->client, xev)) return CLUTTER_X11_FILTER_REMOVE; else return CLUTTER_X11_FILTER_CONTINUE; } static void _mx_settings_x11_init (MxSettingsX11 *self) { self->priv = SETTINGS_X11_PRIVATE (self); /* setup defaults */ self->priv->long_press_timeout = 500; self->priv->icon_theme = g_strdup ("hicolor"); self->priv->font_name = g_strdup ("Sans 10"); } MxSettingsProvider * _mx_settings_x11_new (MxSettings *settings) { return g_object_new (MX_TYPE_SETTINGS_X11, "settings", settings, NULL); } mx-1.4.7/mx/x11/mx-settings-x11.h000066400000000000000000000040501201047117600162440ustar00rootroot00000000000000/* * mx-settings-x11.h: X11 settings provider * * Copyright 2010 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * Boston, MA 02111-1307, USA. * * Written by: Thomas Wood * */ #ifndef _MX_SETTINGS_X11_H #define _MX_SETTINGS_X11_H #include #include "mx-settings-provider.h" G_BEGIN_DECLS #define MX_TYPE_SETTINGS_X11 _mx_settings_x11_get_type() #define MX_SETTINGS_X11(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ MX_TYPE_SETTINGS_X11, MxSettingsX11)) #define MX_SETTINGS_X11_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), \ MX_TYPE_SETTINGS_X11, MxSettingsX11Class)) #define MX_IS_SETTINGS_X11(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \ MX_TYPE_SETTINGS_X11)) #define MX_IS_SETTINGS_X11_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE ((klass), \ MX_TYPE_SETTINGS_X11)) #define MX_SETTINGS_X11_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), \ MX_TYPE_SETTINGS_X11, MxSettingsX11Class)) typedef struct _MxSettingsX11 MxSettingsX11; typedef struct _MxSettingsX11Class MxSettingsX11Class; typedef struct _MxSettingsX11Private MxSettingsX11Private; struct _MxSettingsX11 { GObject parent; MxSettingsX11Private *priv; }; struct _MxSettingsX11Class { GObjectClass parent_class; }; GType _mx_settings_x11_get_type (void) G_GNUC_CONST; MxSettingsProvider *_mx_settings_x11_new (MxSettings *settings); G_END_DECLS #endif /* _MX_SETTINGS_X11_H */ mx-1.4.7/mx/x11/mx-window-x11.c000066400000000000000000000730011201047117600157100ustar00rootroot00000000000000/* * mx-window-x11.c: A native, top-level window (X11 backend) * * Copyright 2010 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * Boston, MA 02111-1307, USA. * * Written by: Thomas Wood * Chris Lord * */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "mx-window-x11.h" #include "mx-toolbar.h" #include "mx-private.h" #include "mx-marshal.h" #include #include #include #include #include static void mx_native_window_iface_init (MxNativeWindowIface *iface); G_DEFINE_TYPE_WITH_CODE (MxWindowX11, _mx_window_x11, G_TYPE_OBJECT, G_IMPLEMENT_INTERFACE (MX_TYPE_NATIVE_WINDOW, mx_native_window_iface_init)) #define WINDOW_X11_PRIVATE(o) \ (G_TYPE_INSTANCE_GET_PRIVATE ((o), MX_TYPE_WINDOW_X11, MxWindowX11Private)) struct _MxWindowX11Private { MxWindow *window; guint is_resizing : 1; guint has_mapped : 1; guint width_set : 1; guint height_set : 1; guint icon_changed : 1; gint is_moving; gfloat last_width; gfloat last_height; gfloat natural_width; gfloat natural_height; gint drag_x_start; gint drag_y_start; gint drag_win_x_start; gint drag_win_y_start; guint drag_width_start; guint drag_height_start; }; enum { PROP_0, PROP_WINDOW }; /* The data format expected by the _NET_WM_ICON property is defined so that the alpha component is in the most significant byte and the blue component is in the least significant byte which means that on a big endian architecture the data format retrieved from Cogl would need to be in the reverse order. */ #if G_BYTE_ORDER == G_LITTLE_ENDIAN #define MX_WINDOW_X11_GET_TEXTURE_DATA_FORMAT COGL_PIXEL_FORMAT_BGRA_8888 #else #define MX_WINDOW_X11_GET_TEXTURE_DATA_FORMAT COGL_PIXEL_FORMAT_ARGB_8888 #endif static void mx_window_x11_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { switch (property_id) { case PROP_WINDOW: g_value_set_object (value, MX_WINDOW_X11 (object)->priv->window); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } } static void mx_window_x11_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { switch (property_id) { case PROP_WINDOW: MX_WINDOW_X11 (object)->priv->window = g_value_get_object (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } } static gboolean mx_window_x11_get_has_border (MxWindowX11 *self) { MxWindow *win = self->priv->window; return (mx_window_get_has_toolbar (win) && !(mx_window_get_small_screen (win) || mx_window_get_fullscreen (win))); } static void mx_window_x11_get_size (MxWindowX11 *self, gfloat *width_p, gfloat *height_p, gfloat *pref_width_p, gfloat *pref_height_p) { gboolean has_border; gfloat width, pref_width; ClutterActor *child, *toolbar; MxWindow *win = self->priv->window; ClutterStage *stage = mx_window_get_clutter_stage (win); pref_width = width = 0; if (!stage) return; has_border = mx_window_x11_get_has_border (self); child = mx_window_get_child (win); toolbar = (ClutterActor *)mx_window_get_toolbar (win); if (toolbar) clutter_actor_get_preferred_width (toolbar, -1, &width, &pref_width); if (child) { gfloat child_min_width, child_pref_width; clutter_actor_get_preferred_width (child, -1, &child_min_width, &child_pref_width); if (child_min_width > width) width = child_min_width; if (child_pref_width > pref_width) pref_width = child_pref_width; } if (width_p) *width_p = width + (has_border ? 2 : 0); if (pref_width_p) *pref_width_p = pref_width + (has_border ? 2 : 0); if (!height_p && !pref_height_p) return; if (height_p) *height_p = 0; if (pref_height_p) *pref_height_p = 0; if (toolbar) clutter_actor_get_preferred_height (toolbar, width, height_p, pref_height_p); if (child) { gfloat child_min_height, child_pref_height; clutter_actor_get_preferred_height (child, width, &child_min_height, &child_pref_height); if (height_p) *height_p += child_min_height; if (pref_height_p) *pref_height_p += child_pref_height; } if (has_border) { if (height_p) *height_p += 2; if (pref_height_p) *pref_height_p += 2; } } typedef struct { unsigned long flags; unsigned long functions; unsigned long decorations; long inputMode; unsigned long status; } PropMotifWmHints; static void mx_window_x11_set_wm_hints (MxWindowX11 *self) { const gchar *icon_name; CoglHandle texture; Display *dpy; Window win; static Atom motif_wm_hints_atom = None; static Atom net_wm_icon = None; MxWindowX11Private *priv = self->priv; MxWindow *window = priv->window; ClutterStage *stage = mx_window_get_clutter_stage (window); if (!stage) return; if (!CLUTTER_ACTOR_IS_MAPPED (stage)) return; dpy = clutter_x11_get_default_display (); win = clutter_x11_get_stage_window (stage); if (win == None) return; if (!motif_wm_hints_atom) motif_wm_hints_atom = XInternAtom (dpy, "_MOTIF_WM_HINTS", False); /* Remove/add the window decorations */ if (motif_wm_hints_atom) { PropMotifWmHints new_hints = {0,}; PropMotifWmHints *hints; hints = &new_hints; hints->flags = 0x2; hints->functions = 0x0; hints->decorations = mx_window_get_has_toolbar (window) ? 0x0 : 0x1; XChangeProperty (dpy, win, motif_wm_hints_atom, motif_wm_hints_atom, 32, PropModeReplace, (guchar*) hints, sizeof(PropMotifWmHints)/ sizeof (long)); } if (!net_wm_icon) net_wm_icon = XInternAtom (dpy, "_NET_WM_ICON", False); /* Set the window icon */ if (!priv->icon_changed) return; priv->icon_changed = FALSE; icon_name = mx_window_get_icon_name (window); if (!icon_name) icon_name = g_get_prgname (); texture = _mx_window_get_icon_cogl_texture (window); if ((icon_name || texture) && net_wm_icon) { guint width, height; gulong *data; gint size; /* Lookup icon for program name if there's no texture set */ if (!texture) { texture = mx_icon_theme_lookup (mx_icon_theme_get_default (), icon_name, 32); if (!texture) { /* Remove the window icon */ clutter_x11_trap_x_errors (); XDeleteProperty (dpy, win, net_wm_icon); clutter_x11_untrap_x_errors (); return; } } /* Get window icon size */ width = cogl_texture_get_width (texture); height = cogl_texture_get_height (texture); size = cogl_texture_get_data (texture, MX_WINDOW_X11_GET_TEXTURE_DATA_FORMAT, width * 4, NULL); if (size != width * height * 4) { g_warning ("Unable to get texture data in " "correct format for window icon"); cogl_handle_unref (texture); return; } data = g_new (gulong, width * height + 2); data[0] = width; data[1] = height; /* Get the window icon */ if (cogl_texture_get_data (texture, MX_WINDOW_X11_GET_TEXTURE_DATA_FORMAT, width * 4, (guint8 *) (data + 2)) == size) { /* For some inexplicable reason XChangeProperty always takes * an array of longs when the format == 32 even on 64-bit * architectures where sizeof(long) != 32. Therefore we need * to pointlessly pad each 32-bit value with an extra 4 * bytes so that libX11 can remove them again to send the * request. We can do this in-place if we start from the * end */ if (sizeof (gulong) != 4) { const guint32 *src = (guint32 *) (data + 2) + width * height; gulong *dst = data + 2 + width * height; while (dst > data + 2) *(--dst) = *(--src); } /* Set the property */ XChangeProperty (dpy, win, net_wm_icon, XA_CARDINAL, 32, PropModeReplace, (unsigned char *) data, (width * height) + 2); } else g_warning ("Size mismatch when retrieving texture data " "for window icon"); cogl_handle_unref (texture); g_free (data); } } static void mx_window_x11_mapped_notify_cb (ClutterActor *actor, GParamSpec *pspec, MxWindowX11 *self) { if (CLUTTER_ACTOR_IS_MAPPED (actor)) mx_window_x11_set_wm_hints (self); } static void mx_window_x11_has_toolbar_notify_cb (MxWindow *window, GParamSpec *pspec, MxWindowX11 *self) { if (CLUTTER_ACTOR_IS_MAPPED (mx_window_get_clutter_stage (window))) mx_window_x11_set_wm_hints (self); } static void mx_window_x11_allocation_changed_cb (ClutterActor *actor, ClutterActorBox *box, ClutterAllocationFlags flags, MxWindowX11 *self) { gfloat width, height, stage_width, stage_height; MxWindowX11Private *priv = self->priv; MxWindow *window = priv->window; actor = clutter_actor_get_stage (actor); clutter_actor_get_size (actor, &stage_width, &stage_height); /* Don't mess with the window size when we're full-screen, or you * get odd race conditions (and you never want to do it anyway) */ if (mx_window_get_fullscreen (window)) return; if (!priv->has_mapped) { Window win; Display *dpy; dpy = clutter_x11_get_default_display (); win = clutter_x11_get_stage_window (CLUTTER_STAGE (actor)); if (!win) return; priv->has_mapped = TRUE; if (mx_window_get_small_screen (window)) { XRRScreenResources *res; res = XRRGetScreenResourcesCurrent (dpy, win); XMoveResizeWindow (dpy, win, 0, 0, res->modes[res->nmode].width, res->modes[res->nmode].height); XRRFreeScreenResources (res); } else { /* Set the initial size of the window - if the user has set * a dimension, it will be used, otherwise the preferred size * will be used. */ mx_window_x11_get_size (self, NULL, NULL, &width, &height); if (priv->width_set) width = priv->natural_width + 2; if (priv->height_set) height = priv->natural_height + 2; XResizeWindow (dpy, win, width, height); stage_width = width; stage_height = height; } } else { /* Update minimum size */ mx_window_x11_get_size (self, &width, &height, NULL, NULL); if (width < 1.0) width = 1.0; if (height < 1.0) height = 1.0; clutter_stage_set_minimum_size (CLUTTER_STAGE (actor), (guint)width, (guint)height); } } static gboolean mx_window_x11_button_press_event_cb (ClutterActor *actor, ClutterEvent *event, MxWindowX11 *self) { unsigned int width, height, border_width, depth, mask; Window win, root, child; int x, y, win_x, win_y; MxWindowX11Private *priv; Display *dpy; priv = self->priv; /* Bail out early in no-toolbar, small-screen or fullscreen mode */ if (!mx_window_x11_get_has_border (self)) return FALSE; /* We're already moving/resizing */ if (priv->is_moving != -1) return FALSE; /* We only care about the first mouse button */ if (clutter_event_get_button (event) != 1) return FALSE; priv->is_moving = clutter_event_get_device_id (event); win = clutter_x11_get_stage_window (CLUTTER_STAGE (actor)); dpy = clutter_x11_get_default_display (); if (win == None) return FALSE; /* Get the initial width/height */ XGetGeometry (dpy, win, &root, &x, &y, &width, &height, &border_width, &depth); priv->drag_win_x_start = x; priv->drag_win_y_start = y; priv->drag_width_start = width; priv->drag_height_start = height; /* Get the initial cursor position */ XQueryPointer (dpy, root, &root, &child, &x, &y, &win_x, &win_y, &mask); priv->drag_x_start = x; priv->drag_y_start = y; /* Disable motion events on other actors */ clutter_set_motion_events_enabled (FALSE); /* Grab the mouse so that we receive the release if the cursor * goes off-stage. */ clutter_grab_pointer_for_device (actor, priv->is_moving); return TRUE; } static void mx_window_x11_button_release (MxWindowX11 *self) { MxWindowX11Private *priv = self->priv; if (priv->is_moving != -1) { clutter_ungrab_pointer_for_device (priv->is_moving); clutter_set_motion_events_enabled (TRUE); priv->is_moving = -1; } } static gboolean mx_window_x11_button_release_event_cb (ClutterActor *actor, ClutterEvent *event, MxWindowX11 *self) { MxWindowX11Private *priv = self->priv; if (clutter_event_get_device_id (event) == priv->is_moving && clutter_event_get_button (event) == 1) { mx_window_x11_button_release (self); return TRUE; } return FALSE; } static gboolean mx_window_x11_captured_event_cb (ClutterActor *actor, ClutterEvent *event, MxWindowX11 *self) { MxWindowX11Private *priv = self->priv; ClutterActor *resize_grip = _mx_window_get_resize_grip (priv->window); switch (clutter_event_type (event)) { case CLUTTER_MOTION: /* Check if we're over the resize handle */ if ((priv->is_moving == -1) && mx_window_x11_get_has_border (self) && clutter_stage_get_user_resizable (CLUTTER_STAGE (actor)) && resize_grip) { gfloat x, y; Window win; Display *dpy; gfloat height, width, rwidth, rheight; static Cursor csoutheast = 0; win = clutter_x11_get_stage_window (CLUTTER_STAGE (actor)); dpy = clutter_x11_get_default_display (); if (win == None) return FALSE; clutter_actor_get_size (actor, &width, &height); clutter_event_get_coords (event, &x, &y); /* Create the resize cursor */ if (!csoutheast) csoutheast = XCreateFontCursor (dpy, XC_bottom_right_corner); clutter_actor_get_size (resize_grip, &rwidth, &rheight); /* Set the cursor if necessary */ if (x > width - rwidth && y > height - rheight) { if (!priv->is_resizing) { XDefineCursor (dpy, win, csoutheast); priv->is_resizing = TRUE; } return TRUE; } else { if (priv->is_resizing) { XUndefineCursor (dpy, win); priv->is_resizing = FALSE; } return FALSE; } } return FALSE; case CLUTTER_BUTTON_PRESS: /* We want resizing to happen even if there are active widgets * underneath the resize-handle. */ if (priv->is_resizing) return mx_window_x11_button_press_event_cb (actor, event, self); else return FALSE; default: return FALSE; } } static gboolean mx_window_x11_motion_event_cb (ClutterActor *actor, ClutterEvent *event, MxWindowX11 *self) { gint offsetx, offsety; gint x, y, winx, winy; guint mask; MxWindowX11Private *priv; Window win, root_win, root, child; Display *dpy; gfloat event_x, event_y; gfloat height, width; priv = self->priv; /* Ignore motion events when we don't have a border, or if they're not from * our grabbed device. */ if (!mx_window_x11_get_has_border (self) || (clutter_event_get_device_id (event) != priv->is_moving)) return FALSE; /* Check if the mouse button is still down - if the user releases the * mouse button while outside of the stage (which can happen), we don't * get the release event. */ if (!(clutter_event_get_state (event) & CLUTTER_BUTTON1_MASK)) { mx_window_x11_button_release (self); return TRUE; } win = clutter_x11_get_stage_window (CLUTTER_STAGE (actor)); dpy = clutter_x11_get_default_display (); if (win == None) return FALSE; clutter_actor_get_size (actor, &width, &height); clutter_event_get_coords (event, &event_x, &event_y); x = (int) event_x; y = (int) event_y; /* Move/resize the window if we're dragging */ offsetx = priv->drag_x_start; offsety = priv->drag_y_start; root_win = clutter_x11_get_root_window (); XQueryPointer (dpy, root_win, &root, &child, &x, &y, &winx, &winy, &mask); if (priv->is_resizing) { XRRScreenResources *res; gfloat min_width, min_height; mx_window_x11_get_size (self, &min_width, &min_height, NULL, NULL); x = MAX (priv->drag_width_start + (x - priv->drag_x_start), min_width); y = MAX (priv->drag_height_start + (y - priv->drag_y_start), min_height); res = XRRGetScreenResourcesCurrent (dpy, win); width = res->modes[res->nmode].width; height = res->modes[res->nmode].height; XRRFreeScreenResources (res); width = MIN (x, width - priv->drag_win_x_start); height = MIN (y, height - priv->drag_win_y_start); clutter_actor_set_size (actor, width, height); } else XMoveWindow (dpy, win, MAX (0, priv->drag_win_x_start + x - offsetx), MAX (0, priv->drag_win_y_start + y - offsety)); return TRUE; } static void mx_window_x11_realize_cb (ClutterActor *actor, MxWindowX11 *self) { gboolean width_set, height_set; MxWindowX11Private *priv = self->priv; /* See if the user has set a size on the window to use on initial map */ g_object_get (G_OBJECT (actor), "natural-width", &priv->natural_width, "natural-width-set", &width_set, "natural-height", &priv->natural_height, "natural-height-set", &height_set, NULL); priv->width_set = width_set; priv->height_set = height_set; } static void mx_window_x11_fullscreen_set_cb (ClutterStage *stage, GParamSpec *pspec, MxWindowX11 *self) { MxWindowX11Private *priv = self->priv; /* If we're in small-screen mode, make sure the size gets reset * correctly. */ if (!clutter_stage_get_fullscreen (stage) && mx_window_get_small_screen (priv->window)) priv->has_mapped = FALSE; } static void mx_window_x11_notify_small_screen_cb (MxWindow *window, GParamSpec *pspec, MxWindowX11 *self) { ClutterActor *resize_grip; gboolean small_screen; ClutterStage *stage; Display *dpy; Window win; MxWindowX11Private *priv = self->priv; stage = mx_window_get_clutter_stage (window); if (!stage) return; win = clutter_x11_get_stage_window (stage); dpy = clutter_x11_get_default_display (); /* If there's no window, we're not mapped yet - we'll resize * on map. */ if (win == None) return; small_screen = mx_window_get_small_screen (window); resize_grip = _mx_window_get_resize_grip (window); /* In case we were in the middle of a move/resize */ if (priv->is_moving != -1) { clutter_ungrab_pointer_for_device (priv->is_moving); clutter_set_motion_events_enabled (TRUE); priv->is_moving = -1; if (priv->is_resizing) { XUndefineCursor (dpy, win); priv->is_resizing = FALSE; } } if (small_screen) { if (!mx_window_get_fullscreen (priv->window)) { int width, height; XRRScreenResources *res; clutter_actor_get_size (CLUTTER_ACTOR (stage), &priv->last_width, &priv->last_height); /* Move/size ourselves to the size of the screen. We could * also set ourselves as not resizable, but a WM that respects * our small-screen mode won't give the user controls to * modify the window, and if it does, just let them. */ res = XRRGetScreenResourcesCurrent (dpy, win); width = res->modes[res->nmode].width; height = res->modes[res->nmode].height; XRRFreeScreenResources (res); XMoveResizeWindow (dpy, win, 0, 0, width, height); } if (resize_grip) clutter_actor_hide (resize_grip); } else { /* If we started off in small-screen mode, our last size won't * be known, so use the preferred size. */ if (!priv->last_width && !priv->last_height) mx_window_x11_get_size (self, NULL, NULL, &priv->last_width, &priv->last_height); clutter_actor_set_size (CLUTTER_ACTOR (stage), priv->last_width, priv->last_height); if (resize_grip && mx_window_get_has_toolbar (window) && clutter_stage_get_user_resizable (stage)) { ClutterActor *child = mx_window_get_child (window); clutter_actor_show (resize_grip); if (child) clutter_actor_raise (resize_grip, child); } } } static void mx_window_x11_notify_icon_changed_cb (MxWindowX11 *self) { self->priv->icon_changed = TRUE; mx_window_x11_set_wm_hints (self); } static void mx_window_x11_constructed (GObject *object) { ClutterStage *stage; MxWindowX11 *self = MX_WINDOW_X11 (object); MxWindowX11Private *priv = self->priv; stage = mx_window_get_clutter_stage (priv->window); g_signal_connect (stage, "notify::mapped", G_CALLBACK (mx_window_x11_mapped_notify_cb), self); g_signal_connect (stage, "allocation-changed", G_CALLBACK (mx_window_x11_allocation_changed_cb), self); g_signal_connect (mx_window_get_toolbar (priv->window), "allocation-changed", G_CALLBACK (mx_window_x11_allocation_changed_cb), self); g_signal_connect (stage, "notify::fullscreen-set", G_CALLBACK (mx_window_x11_fullscreen_set_cb), self); g_signal_connect (stage, "realize", G_CALLBACK (mx_window_x11_realize_cb), self); g_signal_connect (stage, "button-press-event", G_CALLBACK (mx_window_x11_button_press_event_cb), self); g_signal_connect (stage, "button-release-event", G_CALLBACK (mx_window_x11_button_release_event_cb), self); g_signal_connect (stage, "captured-event", G_CALLBACK (mx_window_x11_captured_event_cb), self); g_signal_connect (stage, "motion-event", G_CALLBACK (mx_window_x11_motion_event_cb), self); g_signal_connect (priv->window, "notify::small-screen", G_CALLBACK (mx_window_x11_notify_small_screen_cb), self); g_signal_connect_swapped (priv->window, "notify::icon-name", G_CALLBACK (mx_window_x11_notify_icon_changed_cb), self); g_signal_connect_swapped (priv->window, "notify::icon-cogl-texture", G_CALLBACK (mx_window_x11_notify_icon_changed_cb), self); g_signal_connect (priv->window, "notify::has-toolbar", G_CALLBACK (mx_window_x11_has_toolbar_notify_cb), self); } static void mx_window_x11_get_position (MxNativeWindow *self, gint *x, gint *y) { unsigned int width, height, border_width, depth; MxWindowX11Private *priv; Window win, root_win; ClutterStage *stage; int win_x, win_y; Display *dpy; g_return_if_fail (MX_IS_WINDOW_X11 (self)); priv = MX_WINDOW_X11 (self)->priv; stage = mx_window_get_clutter_stage (priv->window); if (!stage) return; if (mx_window_get_fullscreen (priv->window) || mx_window_get_small_screen (priv->window)) { if (x) *x = 0; if (y) *y = 0; return; } win = clutter_x11_get_stage_window (stage); dpy = clutter_x11_get_default_display (); XGetGeometry (dpy, win, &root_win, &win_x, &win_y, &width, &height, &border_width, &depth); if (x) *x = win_x; if (y) *y = win_y; } static void mx_window_x11_set_position (MxNativeWindow *self, gint x, gint y) { Window win; Display *dpy; ClutterStage *stage; MxWindowX11Private *priv; g_return_if_fail (MX_IS_WINDOW_X11 (self)); priv = MX_WINDOW_X11 (self)->priv; stage = mx_window_get_clutter_stage (priv->window); if (!stage) return; /* Don't try to move a full-screen/small-screen window */ if (mx_window_get_fullscreen (priv->window) || mx_window_get_small_screen (priv->window)) return; win = clutter_x11_get_stage_window (stage); dpy = clutter_x11_get_default_display (); XMoveWindow (dpy, win, x, y); } static void mx_window_x11_present (MxNativeWindow *self) { Window xwindow; Display *display; guint32 timestamp; ClutterStage *stage; XClientMessageEvent xclient; g_return_if_fail (MX_IS_WINDOW_X11 (self)); stage = mx_window_get_clutter_stage (MX_WINDOW_X11 (self)->priv->window); /* As with all these arcane, poorly documented X11 things, learnt * how to do this from reading GTK/GDK code. */ display = clutter_x11_get_default_display (); xwindow = clutter_x11_get_stage_window (stage); XRaiseWindow (display, xwindow); /* These two calls may not be necessary */ timestamp = 0x7FFFFFFF; XChangeProperty (display, xwindow, XInternAtom (display, "_NET_WM_USER_TIME", False), XA_CARDINAL, 32, PropModeReplace, (guchar *)×tamp, 1); XMapWindow (display, xwindow); memset (&xclient, 0, sizeof (xclient)); xclient.type = ClientMessage; xclient.window = xwindow; xclient.message_type = XInternAtom (display, "_NET_ACTIVE_WINDOW", False); xclient.format = 32; xclient.data.l[0] = 1; xclient.data.l[1] = timestamp; xclient.data.l[2] = None; xclient.data.l[3] = 0; xclient.data.l[4] = 0; XSendEvent (display, clutter_x11_get_root_window (), False, SubstructureRedirectMask | SubstructureNotifyMask, (XEvent *)&xclient); } static void _mx_window_x11_class_init (MxWindowX11Class *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); g_type_class_add_private (klass, sizeof (MxWindowX11Private)); object_class->get_property = mx_window_x11_get_property; object_class->set_property = mx_window_x11_set_property; object_class->constructed = mx_window_x11_constructed; g_object_class_override_property (object_class, PROP_WINDOW, "window"); } static void mx_native_window_iface_init (MxNativeWindowIface *iface) { static gboolean is_initialised = FALSE; if (!is_initialised) { is_initialised = TRUE; iface->get_position = mx_window_x11_get_position; iface->set_position = mx_window_x11_set_position; iface->present = mx_window_x11_present; } } static void _mx_window_x11_init (MxWindowX11 *self) { MxWindowX11Private *priv = self->priv = WINDOW_X11_PRIVATE (self); priv->is_moving = -1; priv->icon_changed = TRUE; } MxNativeWindow * _mx_window_x11_new (MxWindow *window) { return g_object_new (MX_TYPE_WINDOW_X11, "window", window, NULL); } mx-1.4.7/mx/x11/mx-window-x11.h000066400000000000000000000040741201047117600157210ustar00rootroot00000000000000/* * mx-window-x11.h: A native, top-level window (X11 backend) * * Copyright 2010 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * Boston, MA 02111-1307, USA. * * Written by: Thomas Wood * Chris Lord * */ #ifndef _MX_WINDOW_X11_H #define _MX_WINDOW_X11_H #include #include "mx-window.h" #include "mx-native-window.h" G_BEGIN_DECLS #define MX_TYPE_WINDOW_X11 _mx_window_x11_get_type() #define MX_WINDOW_X11(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ MX_TYPE_WINDOW_X11, MxWindowX11)) #define MX_WINDOW_X11_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), \ MX_TYPE_WINDOW_X11, MxWindowX11Class)) #define MX_IS_WINDOW_X11(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \ MX_TYPE_WINDOW_X11)) #define MX_IS_WINDOW_X11_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE ((klass), \ MX_TYPE_WINDOW_X11)) #define MX_WINDOW_X11_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), \ MX_TYPE_WINDOW_X11, MxWindowX11Class)) typedef struct _MxWindowX11 MxWindowX11; typedef struct _MxWindowX11Class MxWindowX11Class; typedef struct _MxWindowX11Private MxWindowX11Private; struct _MxWindowX11 { GObject parent; MxWindowX11Private *priv; }; struct _MxWindowX11Class { GObjectClass parent_class; }; GType _mx_window_x11_get_type (void) G_GNUC_CONST; MxNativeWindow *_mx_window_x11_new (MxWindow *window); G_END_DECLS #endif /* _MX_WINDOW_X11_H */ mx-1.4.7/mx/x11/xsettings-client.c000066400000000000000000000344521201047117600166630ustar00rootroot00000000000000/* * Copyright © 2001, 2007 Red Hat, Inc. * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation, and that the name of Red Hat not be used in advertising or * publicity pertaining to distribution of the software without specific, * written prior permission. Red Hat makes no representations about the * suitability of this software for any purpose. It is provided "as is" * without express or implied warranty. * * RED HAT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL RED HAT * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Author: Owen Taylor, Red Hat, Inc. */ #include "config.h" #include #include #include #include #include #include /* For CARD16 */ #include "xsettings-client.h" struct _XSettingsClient { Display *display; int screen; XSettingsNotifyFunc notify; XSettingsWatchFunc watch; void *cb_data; XSettingsGrabFunc grab; XSettingsGrabFunc ungrab; Window manager_window; Atom manager_atom; Atom selection_atom; Atom xsettings_atom; XSettingsList *settings; }; static void notify_changes (XSettingsClient *client, XSettingsList *old_list) { XSettingsList *old_iter = old_list; XSettingsList *new_iter = client->settings; if (!client->notify) return; while (old_iter || new_iter) { int cmp; if (old_iter && new_iter) cmp = strcmp (old_iter->setting->name, new_iter->setting->name); else if (old_iter) cmp = -1; else cmp = 1; if (cmp < 0) { client->notify (old_iter->setting->name, XSETTINGS_ACTION_DELETED, NULL, client->cb_data); } else if (cmp == 0) { if (!xsettings_setting_equal (old_iter->setting, new_iter->setting)) client->notify (old_iter->setting->name, XSETTINGS_ACTION_CHANGED, new_iter->setting, client->cb_data); } else { client->notify (new_iter->setting->name, XSETTINGS_ACTION_NEW, new_iter->setting, client->cb_data); } if (old_iter) old_iter = old_iter->next; if (new_iter) new_iter = new_iter->next; } } static int ignore_errors (Display *display, XErrorEvent *event) { return True; } static char local_byte_order = '\0'; #define BYTES_LEFT(buffer) ((buffer)->data + (buffer)->len - (buffer)->pos) static XSettingsResult fetch_card16 (XSettingsBuffer *buffer, CARD16 *result) { CARD16 x; if (BYTES_LEFT (buffer) < 2) return XSETTINGS_ACCESS; x = *(CARD16 *)buffer->pos; buffer->pos += 2; if (buffer->byte_order == local_byte_order) *result = x; else *result = (x << 8) | (x >> 8); return XSETTINGS_SUCCESS; } static XSettingsResult fetch_ushort (XSettingsBuffer *buffer, unsigned short *result) { CARD16 x; XSettingsResult r; r = fetch_card16 (buffer, &x); if (r == XSETTINGS_SUCCESS) *result = x; return r; } static XSettingsResult fetch_card32 (XSettingsBuffer *buffer, CARD32 *result) { CARD32 x; if (BYTES_LEFT (buffer) < 4) return XSETTINGS_ACCESS; x = *(CARD32 *)buffer->pos; buffer->pos += 4; if (buffer->byte_order == local_byte_order) *result = x; else *result = (x << 24) | ((x & 0xff00) << 8) | ((x & 0xff0000) >> 8) | (x >> 24); return XSETTINGS_SUCCESS; } static XSettingsResult fetch_card8 (XSettingsBuffer *buffer, CARD8 *result) { if (BYTES_LEFT (buffer) < 1) return XSETTINGS_ACCESS; *result = *(CARD8 *)buffer->pos; buffer->pos += 1; return XSETTINGS_SUCCESS; } #define XSETTINGS_PAD(n,m) ((n + m - 1) & (~(m-1))) static XSettingsList * parse_settings (unsigned char *data, size_t len) { XSettingsBuffer buffer; XSettingsResult result = XSETTINGS_SUCCESS; XSettingsList *settings = NULL; CARD32 serial; CARD32 n_entries; CARD32 i; XSettingsSetting *setting = NULL; local_byte_order = xsettings_byte_order (); buffer.pos = buffer.data = data; buffer.len = len; result = fetch_card8 (&buffer, (unsigned char *)&buffer.byte_order); if (buffer.byte_order != MSBFirst && buffer.byte_order != LSBFirst) { fprintf (stderr, "Invalid byte order in XSETTINGS property\n"); result = XSETTINGS_FAILED; goto out; } buffer.pos += 3; result = fetch_card32 (&buffer, &serial); if (result != XSETTINGS_SUCCESS) goto out; result = fetch_card32 (&buffer, &n_entries); if (result != XSETTINGS_SUCCESS) goto out; for (i = 0; i < n_entries; i++) { CARD8 type; CARD16 name_len; CARD32 v_int; size_t pad_len; result = fetch_card8 (&buffer, &type); if (result != XSETTINGS_SUCCESS) goto out; buffer.pos += 1; result = fetch_card16 (&buffer, &name_len); if (result != XSETTINGS_SUCCESS) goto out; pad_len = XSETTINGS_PAD(name_len, 4); if (BYTES_LEFT (&buffer) < pad_len) { result = XSETTINGS_ACCESS; goto out; } setting = malloc (sizeof *setting); if (!setting) { result = XSETTINGS_NO_MEM; goto out; } setting->type = XSETTINGS_TYPE_INT; /* No allocated memory */ setting->name = malloc (name_len + 1); if (!setting->name) { result = XSETTINGS_NO_MEM; goto out; } memcpy (setting->name, buffer.pos, name_len); setting->name[name_len] = '\0'; buffer.pos += pad_len; result = fetch_card32 (&buffer, &v_int); if (result != XSETTINGS_SUCCESS) goto out; setting->last_change_serial = v_int; switch (type) { case XSETTINGS_TYPE_INT: result = fetch_card32 (&buffer, &v_int); if (result != XSETTINGS_SUCCESS) goto out; setting->data.v_int = (INT32)v_int; break; case XSETTINGS_TYPE_STRING: result = fetch_card32 (&buffer, &v_int); if (result != XSETTINGS_SUCCESS) goto out; pad_len = XSETTINGS_PAD (v_int, 4); if (v_int + 1 == 0 || /* Guard against wrap-around */ BYTES_LEFT (&buffer) < pad_len) { result = XSETTINGS_ACCESS; goto out; } setting->data.v_string = malloc (v_int + 1); if (!setting->data.v_string) { result = XSETTINGS_NO_MEM; goto out; } memcpy (setting->data.v_string, buffer.pos, v_int); setting->data.v_string[v_int] = '\0'; buffer.pos += pad_len; break; case XSETTINGS_TYPE_COLOR: result = fetch_ushort (&buffer, &setting->data.v_color.red); if (result != XSETTINGS_SUCCESS) goto out; result = fetch_ushort (&buffer, &setting->data.v_color.green); if (result != XSETTINGS_SUCCESS) goto out; result = fetch_ushort (&buffer, &setting->data.v_color.blue); if (result != XSETTINGS_SUCCESS) goto out; result = fetch_ushort (&buffer, &setting->data.v_color.alpha); if (result != XSETTINGS_SUCCESS) goto out; break; default: /* Quietly ignore unknown types */ break; } setting->type = type; result = xsettings_list_insert (&settings, setting); if (result != XSETTINGS_SUCCESS) goto out; setting = NULL; } out: if (result != XSETTINGS_SUCCESS) { switch (result) { case XSETTINGS_NO_MEM: fprintf(stderr, "Out of memory reading XSETTINGS property\n"); break; case XSETTINGS_ACCESS: fprintf(stderr, "Invalid XSETTINGS property (read off end)\n"); break; case XSETTINGS_DUPLICATE_ENTRY: fprintf (stderr, "Duplicate XSETTINGS entry for '%s'\n", setting->name); case XSETTINGS_FAILED: case XSETTINGS_SUCCESS: case XSETTINGS_NO_ENTRY: break; } if (setting) xsettings_setting_free (setting); xsettings_list_free (settings); settings = NULL; } return settings; } static void read_settings (XSettingsClient *client) { Atom type; int format; unsigned long n_items; unsigned long bytes_after; unsigned char *data; int result; int (*old_handler) (Display *, XErrorEvent *); XSettingsList *old_list = client->settings; client->settings = NULL; if (client->manager_window) { old_handler = XSetErrorHandler (ignore_errors); result = XGetWindowProperty (client->display, client->manager_window, client->xsettings_atom, 0, LONG_MAX, False, client->xsettings_atom, &type, &format, &n_items, &bytes_after, &data); XSetErrorHandler (old_handler); if (result == Success && type != None) { if (type != client->xsettings_atom) { fprintf (stderr, "Invalid type for XSETTINGS property"); } else if (format != 8) { fprintf (stderr, "Invalid format for XSETTINGS property %d", format); } else client->settings = parse_settings (data, n_items); XFree (data); } } notify_changes (client, old_list); xsettings_list_free (old_list); } static void add_events (Display *display, Window window, long mask) { XWindowAttributes attr; XGetWindowAttributes (display, window, &attr); XSelectInput (display, window, attr.your_event_mask | mask); } static void check_manager_window (XSettingsClient *client) { if (client->manager_window && client->watch) client->watch (client->manager_window, False, 0, client->cb_data); if (client->grab) client->grab (client->display); else XGrabServer (client->display); client->manager_window = XGetSelectionOwner (client->display, client->selection_atom); if (client->manager_window) XSelectInput (client->display, client->manager_window, PropertyChangeMask | StructureNotifyMask); if (client->ungrab) client->ungrab (client->display); else XUngrabServer (client->display); XFlush (client->display); if (client->manager_window && client->watch) { if (!client->watch (client->manager_window, True, PropertyChangeMask | StructureNotifyMask, client->cb_data)) { /* Inability to watch the window probably means that it was destroyed * after we ungrabbed */ client->manager_window = None; return; } } read_settings (client); } XSettingsClient * xsettings_client_new (Display *display, int screen, XSettingsNotifyFunc notify, XSettingsWatchFunc watch, void *cb_data) { return xsettings_client_new_with_grab_funcs (display, screen, notify, watch, cb_data, NULL, NULL); } XSettingsClient * xsettings_client_new_with_grab_funcs (Display *display, int screen, XSettingsNotifyFunc notify, XSettingsWatchFunc watch, void *cb_data, XSettingsGrabFunc grab, XSettingsGrabFunc ungrab) { XSettingsClient *client; char buffer[256]; char *atom_names[3]; Atom atoms[3]; client = malloc (sizeof *client); if (!client) return NULL; client->display = display; client->screen = screen; client->notify = notify; client->watch = watch; client->cb_data = cb_data; client->grab = grab; client->ungrab = ungrab; client->manager_window = None; client->settings = NULL; sprintf(buffer, "_XSETTINGS_S%d", screen); atom_names[0] = buffer; atom_names[1] = "_XSETTINGS_SETTINGS"; atom_names[2] = "MANAGER"; #ifdef HAVE_XINTERNATOMS XInternAtoms (display, atom_names, 3, False, atoms); #else atoms[0] = XInternAtom (display, atom_names[0], False); atoms[1] = XInternAtom (display, atom_names[1], False); atoms[2] = XInternAtom (display, atom_names[2], False); #endif client->selection_atom = atoms[0]; client->xsettings_atom = atoms[1]; client->manager_atom = atoms[2]; /* Select on StructureNotify so we get MANAGER events */ add_events (display, RootWindow (display, screen), StructureNotifyMask); if (client->watch) client->watch (RootWindow (display, screen), True, StructureNotifyMask, client->cb_data); check_manager_window (client); return client; } void xsettings_client_set_grab_func (XSettingsClient *client, XSettingsGrabFunc grab) { client->grab = grab; } void xsettings_client_set_ungrab_func (XSettingsClient *client, XSettingsGrabFunc ungrab) { client->ungrab = ungrab; } void xsettings_client_destroy (XSettingsClient *client) { if (client->watch) client->watch (RootWindow (client->display, client->screen), False, 0, client->cb_data); if (client->manager_window && client->watch) client->watch (client->manager_window, False, 0, client->cb_data); xsettings_list_free (client->settings); free (client); } XSettingsResult xsettings_client_get_setting (XSettingsClient *client, const char *name, XSettingsSetting **setting) { XSettingsSetting *search = xsettings_list_lookup (client->settings, name); if (search) { *setting = xsettings_setting_copy (search); return *setting ? XSETTINGS_SUCCESS : XSETTINGS_NO_MEM; } else return XSETTINGS_NO_ENTRY; } Bool xsettings_client_process_event (XSettingsClient *client, XEvent *xev) { /* The checks here will not unlikely cause us to reread * the properties from the manager window a number of * times when the manager changes from A->B. But manager changes * are going to be pretty rare. */ if (xev->xany.window == RootWindow (client->display, client->screen)) { if (xev->xany.type == ClientMessage && xev->xclient.message_type == client->manager_atom && xev->xclient.data.l[1] == client->selection_atom) { check_manager_window (client); return True; } } else if (xev->xany.window == client->manager_window) { if (xev->xany.type == DestroyNotify) { check_manager_window (client); /* let GDK do its cleanup */ return False; } else if (xev->xany.type == PropertyNotify) { read_settings (client); return True; } } return False; } mx-1.4.7/mx/x11/xsettings-client.h000066400000000000000000000062511201047117600166640ustar00rootroot00000000000000/* * Copyright © 2001, 2007 Red Hat, Inc. * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation, and that the name of Red Hat not be used in advertising or * publicity pertaining to distribution of the software without specific, * written prior permission. Red Hat makes no representations about the * suitability of this software for any purpose. It is provided "as is" * without express or implied warranty. * * RED HAT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL RED HAT * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Author: Owen Taylor, Red Hat, Inc. */ #ifndef XSETTINGS_CLIENT_H #define XSETTINGS_CLIENT_H #include #include "xsettings-common.h" #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ typedef struct _XSettingsClient XSettingsClient; typedef enum { XSETTINGS_ACTION_NEW, XSETTINGS_ACTION_CHANGED, XSETTINGS_ACTION_DELETED } XSettingsAction; typedef void (*XSettingsNotifyFunc) (const char *name, XSettingsAction action, XSettingsSetting *setting, void *cb_data); typedef Bool (*XSettingsWatchFunc) (Window window, Bool is_start, long mask, void *cb_data); typedef void (*XSettingsGrabFunc) (Display *display); XSettingsClient *xsettings_client_new (Display *display, int screen, XSettingsNotifyFunc notify, XSettingsWatchFunc watch, void *cb_data); XSettingsClient *xsettings_client_new_with_grab_funcs (Display *display, int screen, XSettingsNotifyFunc notify, XSettingsWatchFunc watch, void *cb_data, XSettingsGrabFunc grab, XSettingsGrabFunc ungrab); void xsettings_client_set_grab_func (XSettingsClient *client, XSettingsGrabFunc grab); void xsettings_client_set_ungrab_func (XSettingsClient *client, XSettingsGrabFunc ungrab); void xsettings_client_destroy (XSettingsClient *client); Bool xsettings_client_process_event (XSettingsClient *client, XEvent *xev); XSettingsResult xsettings_client_get_setting (XSettingsClient *client, const char *name, XSettingsSetting **setting); #ifdef __cplusplus } #endif /* __cplusplus */ #endif /* XSETTINGS_CLIENT_H */ mx-1.4.7/mx/x11/xsettings-common.c000066400000000000000000000132551201047117600166730ustar00rootroot00000000000000/* * Copyright © 2001 Red Hat, Inc. * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation, and that the name of Red Hat not be used in advertising or * publicity pertaining to distribution of the software without specific, * written prior permission. Red Hat makes no representations about the * suitability of this software for any purpose. It is provided "as is" * without express or implied warranty. * * RED HAT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL RED HAT * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Author: Owen Taylor, Red Hat, Inc. */ #include "config.h" #include "string.h" #include "stdlib.h" #include #include /* For CARD32 */ #include "xsettings-common.h" XSettingsSetting * xsettings_setting_copy (XSettingsSetting *setting) { XSettingsSetting *result; size_t str_len; result = malloc (sizeof *result); if (!result) return NULL; str_len = strlen (setting->name); result->name = malloc (str_len + 1); if (!result->name) goto err; memcpy (result->name, setting->name, str_len + 1); result->type = setting->type; switch (setting->type) { case XSETTINGS_TYPE_INT: result->data.v_int = setting->data.v_int; break; case XSETTINGS_TYPE_COLOR: result->data.v_color = setting->data.v_color; break; case XSETTINGS_TYPE_STRING: str_len = strlen (setting->data.v_string); result->data.v_string = malloc (str_len + 1); if (!result->data.v_string) goto err; memcpy (result->data.v_string, setting->data.v_string, str_len + 1); break; } result->last_change_serial = setting->last_change_serial; return result; err: if (result->name) free (result->name); free (result); return NULL; } XSettingsList * xsettings_list_copy (XSettingsList *list) { XSettingsList *new = NULL; XSettingsList *old_iter = list; XSettingsList *new_iter = NULL; while (old_iter) { XSettingsList *new_node; new_node = malloc (sizeof *new_node); if (!new_node) goto error; new_node->setting = xsettings_setting_copy (old_iter->setting); if (!new_node->setting) { free (new_node); goto error; } if (new_iter) new_iter->next = new_node; else new = new_node; new_iter = new_node; old_iter = old_iter->next; } return new; error: xsettings_list_free (new); return NULL; } int xsettings_setting_equal (XSettingsSetting *setting_a, XSettingsSetting *setting_b) { if (setting_a->type != setting_b->type) return 0; if (strcmp (setting_a->name, setting_b->name) != 0) return 0; switch (setting_a->type) { case XSETTINGS_TYPE_INT: return setting_a->data.v_int == setting_b->data.v_int; case XSETTINGS_TYPE_COLOR: return (setting_a->data.v_color.red == setting_b->data.v_color.red && setting_a->data.v_color.green == setting_b->data.v_color.green && setting_a->data.v_color.blue == setting_b->data.v_color.blue && setting_a->data.v_color.alpha == setting_b->data.v_color.alpha); case XSETTINGS_TYPE_STRING: return strcmp (setting_a->data.v_string, setting_b->data.v_string) == 0; } return 0; } void xsettings_setting_free (XSettingsSetting *setting) { if (setting->type == XSETTINGS_TYPE_STRING) free (setting->data.v_string); if (setting->name) free (setting->name); free (setting); } void xsettings_list_free (XSettingsList *list) { while (list) { XSettingsList *next = list->next; xsettings_setting_free (list->setting); free (list); list = next; } } XSettingsResult xsettings_list_insert (XSettingsList **list, XSettingsSetting *setting) { XSettingsList *node; XSettingsList *iter; XSettingsList *last = NULL; node = malloc (sizeof *node); if (!node) return XSETTINGS_NO_MEM; node->setting = setting; iter = *list; while (iter) { int cmp = strcmp (setting->name, iter->setting->name); if (cmp < 0) break; else if (cmp == 0) { free (node); return XSETTINGS_DUPLICATE_ENTRY; } last = iter; iter = iter->next; } if (last) last->next = node; else *list = node; node->next = iter; return XSETTINGS_SUCCESS; } XSettingsResult xsettings_list_delete (XSettingsList **list, const char *name) { XSettingsList *iter; XSettingsList *last = NULL; iter = *list; while (iter) { if (strcmp (name, iter->setting->name) == 0) { if (last) last->next = iter->next; else *list = iter->next; xsettings_setting_free (iter->setting); free (iter); return XSETTINGS_SUCCESS; } last = iter; iter = iter->next; } return XSETTINGS_FAILED; } XSettingsSetting * xsettings_list_lookup (XSettingsList *list, const char *name) { XSettingsList *iter; iter = list; while (iter) { if (strcmp (name, iter->setting->name) == 0) return iter->setting; iter = iter->next; } return NULL; } char xsettings_byte_order (void) { CARD32 myint = 0x01020304; return (*(char *)&myint == 1) ? MSBFirst : LSBFirst; } mx-1.4.7/mx/x11/xsettings-common.h000066400000000000000000000061271201047117600167000ustar00rootroot00000000000000/* * Copyright © 2001 Red Hat, Inc. * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation, and that the name of Red Hat not be used in advertising or * publicity pertaining to distribution of the software without specific, * written prior permission. Red Hat makes no representations about the * suitability of this software for any purpose. It is provided "as is" * without express or implied warranty. * * RED HAT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL RED HAT * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Author: Owen Taylor, Red Hat, Inc. */ #ifndef XSETTINGS_COMMON_H #define XSETTINGS_COMMON_H #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ typedef struct _XSettingsBuffer XSettingsBuffer; typedef struct _XSettingsColor XSettingsColor; typedef struct _XSettingsList XSettingsList; typedef struct _XSettingsSetting XSettingsSetting; /* Types of settings possible. Enum values correspond to * protocol values. */ typedef enum { XSETTINGS_TYPE_INT = 0, XSETTINGS_TYPE_STRING = 1, XSETTINGS_TYPE_COLOR = 2 } XSettingsType; typedef enum { XSETTINGS_SUCCESS, XSETTINGS_NO_MEM, XSETTINGS_ACCESS, XSETTINGS_FAILED, XSETTINGS_NO_ENTRY, XSETTINGS_DUPLICATE_ENTRY } XSettingsResult; struct _XSettingsBuffer { char byte_order; size_t len; unsigned char *data; unsigned char *pos; }; struct _XSettingsColor { unsigned short red, green, blue, alpha; }; struct _XSettingsList { XSettingsSetting *setting; XSettingsList *next; }; struct _XSettingsSetting { char *name; XSettingsType type; union { int v_int; char *v_string; XSettingsColor v_color; } data; unsigned long last_change_serial; }; XSettingsSetting *xsettings_setting_copy (XSettingsSetting *setting); void xsettings_setting_free (XSettingsSetting *setting); int xsettings_setting_equal (XSettingsSetting *setting_a, XSettingsSetting *setting_b); void xsettings_list_free (XSettingsList *list); XSettingsList *xsettings_list_copy (XSettingsList *list); XSettingsResult xsettings_list_insert (XSettingsList **list, XSettingsSetting *setting); XSettingsSetting *xsettings_list_lookup (XSettingsList *list, const char *name); XSettingsResult xsettings_list_delete (XSettingsList **list, const char *name); char xsettings_byte_order (void); #define XSETTINGS_PAD(n,m) ((n + m - 1) & (~(m-1))) #ifdef __cplusplus } #endif /* __cplusplus */ #endif /* XSETTINGS_COMMON_H */ mx-1.4.7/po/000077500000000000000000000000001201047117600125645ustar00rootroot00000000000000mx-1.4.7/po/.cvsignore000066400000000000000000000003111201047117600145570ustar00rootroot00000000000000.libs .deps .*swp .nautilus-metafile.xml *.autosave *.gmo *.mo *.pot *~ #*# *.bak *.o *.lo *.la cat-id-tbl.c stamp-cat-id messages missing POTFILES Makefile Makefile.in Makefile.in.in translations.xml mx-1.4.7/po/ChangeLog000066400000000000000000000000001201047117600143240ustar00rootroot00000000000000mx-1.4.7/po/POTFILES.in000066400000000000000000000001671201047117600143450ustar00rootroot00000000000000# List of source files containing translatable strings. mx/mx-application.c mx-gtk/mx-gtk-light-switch.c mx/mx-utils.c mx-1.4.7/po/POTFILES.skip000066400000000000000000000000011201047117600146700ustar00rootroot00000000000000 mx-1.4.7/po/ast.po000066400000000000000000000037701201047117600137220ustar00rootroot00000000000000# msgid "" msgstr "" "Project-Id-Version: mx.master\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2010-05-12 09:42+0000\n" "PO-Revision-Date: \n" "Last-Translator: astur \n" "Language-Team: asturian \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Poedit-Language: asturian\n" "X-Poedit-SourceCharset: utf-8\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" # This string is used by the action which brings the application to the # foreground. For example, if the application was minimised, or obscured by other windows, it would raise the application window to the foreground. This is the same as "Restaure application". #: ../mx/mx-application.c:311 msgid "Raise application" msgstr "Restaurar l'aplicación" #: ../mx-gtk/mx-gtk-light-switch.c:96 msgid "Unavailable" msgstr "Non disponible" #: ../mx/mx-utils.c:94 msgid "Less than a minute ago" msgstr "Fai menos d'un minutu" #: ../mx/mx-utils.c:98 msgid "A few minutes ago" msgstr "Fai unos minutos" #: ../mx/mx-utils.c:102 msgid "A couple of hours ago" msgstr "Fai un par d'hores" #: ../mx/mx-utils.c:109 msgid "Earlier today" msgstr "Güei, ceo" #: ../mx/mx-utils.c:112 msgid "Yesterday" msgstr "Ayeri" # #: ../mx/mx-utils.c:120 #, c-format msgid "On %A" msgstr "El %A" #: ../mx/mx-utils.c:126 msgid "Unknown" msgstr "Desconocíu" #: ../mx/mx-utils.c:133 msgid "Last week" msgstr "La selmana pasada" #: ../mx/mx-utils.c:136 msgid "A couple of weeks ago" msgstr "Fai un par de selmanes" #: ../mx/mx-utils.c:142 msgid "This month" msgstr "Esti mes" #. Now Jan., last used in Dec. #: ../mx/mx-utils.c:146 msgid "Last month" msgstr "El mes pasáu" #: ../mx/mx-utils.c:149 msgid "This year" msgstr "Esti añu" #: ../mx/mx-utils.c:152 msgid "Last year" msgstr "L'añu pasáu" # Long time ago #: ../mx/mx-utils.c:154 msgid "Ages ago" msgstr "Fai años" #~ msgid "On" #~ msgstr "Activáu" #~ msgid "Off" #~ msgstr "Desactiváu" mx-1.4.7/po/ca.po000066400000000000000000000041251201047117600135110ustar00rootroot00000000000000# mx Catalan translation. # Copyright (C) 2009 Free Software Foundation, Inc. # This file is distributed under the same license as the mx package. # Gil Forcada , 2009. # msgid "" msgstr "" "Project-Id-Version: mx 2.x\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2010-02-26 12:45+0000\n" "PO-Revision-Date: 2009-08-16 18:28+0200\n" "Last-Translator: Gil Forcada \n" "Language-Team: Catalan \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" #: mx/mx-gtk-light-switch.c:232 msgid "Unavailable" msgstr "No disponible" #. TRANSLATORS: If this string takes more than four or five characters in #. * your language, please use the MEDIUM VERTICAL BAR unicode character #. * instead #: mx/mx-gtk-light-switch.c:252 mx/mx-gtk-light-switch.c:326 msgid "On" msgstr "Actiu" #. Draw the second label; "Off" #. TRANSLATORS: If this string takes more than four or five characters in #. * your language, please use the WHITE CIRCLE U+25CB unicode character #. * instead #: mx/mx-gtk-light-switch.c:271 mx/mx-gtk-light-switch.c:324 msgid "Off" msgstr "Apagat" #: mx/mx-utils.c:93 msgid "Less than a minute ago" msgstr "Fa menys d'un minut" #: mx/mx-utils.c:97 msgid "A few minutes ago" msgstr "Fa uns minuts" #: mx/mx-utils.c:101 msgid "A couple of hours ago" msgstr "Fa unes hores" #: mx/mx-utils.c:108 msgid "Earlier today" msgstr "Avui" #: mx/mx-utils.c:111 msgid "Yesterday" msgstr "Ahir" #: mx/mx-utils.c:119 #, c-format msgid "On %A" msgstr "El %A" #: mx/mx-utils.c:125 msgid "Unknown" msgstr "Desconegut" #: mx/mx-utils.c:132 msgid "Last week" msgstr "La setmana passada" #: mx/mx-utils.c:135 msgid "A couple of weeks ago" msgstr "Fa un parell de setmanes" #: mx/mx-utils.c:141 msgid "This month" msgstr "Aquest mes" #. Now Jan., last used in Dec. #: mx/mx-utils.c:145 msgid "Last month" msgstr "El mes passat" #: mx/mx-utils.c:148 msgid "This year" msgstr "Aquest any" #: mx/mx-utils.c:151 msgid "Last year" msgstr "L'any passat" #: mx/mx-utils.c:153 msgid "Ages ago" msgstr "Fa milers d'anys" mx-1.4.7/po/da.po000066400000000000000000000034771201047117600135230ustar00rootroot00000000000000# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: mx\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2010-04-14 09:54+0000\n" "PO-Revision-Date: 2010-04-15 09:02+0100\n" "Last-Translator: Jon Hedemann \n" "Language-Team: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n!=1;\n" "X-Poedit-Language: Danish\n" "X-Poedit-Country: DENMARK\n" "X-Poedit-SourceCharset: utf-8\n" #: ../mx/mx-application.c:311 msgid "Raise application" msgstr "Bring program i forgrunden" #: ../mx-gtk/mx-gtk-light-switch.c:96 msgid "Unavailable" msgstr "Utilgængelig" #: ../mx/mx-utils.c:94 msgid "Less than a minute ago" msgstr "Mindre end ét minut siden" #: ../mx/mx-utils.c:98 msgid "A few minutes ago" msgstr "Nogle få minutter siden" #: ../mx/mx-utils.c:102 msgid "A couple of hours ago" msgstr "Et par timer tiden" #: ../mx/mx-utils.c:109 msgid "Earlier today" msgstr "Tidligere i dag" #: ../mx/mx-utils.c:112 msgid "Yesterday" msgstr "I går" #: ../mx/mx-utils.c:120 #, c-format msgid "On %A" msgstr "%A" #: ../mx/mx-utils.c:126 msgid "Unknown" msgstr "Ukendt" #: ../mx/mx-utils.c:133 msgid "Last week" msgstr "Sidste uge" #: ../mx/mx-utils.c:136 msgid "A couple of weeks ago" msgstr "Et par uger siden" #: ../mx/mx-utils.c:142 msgid "This month" msgstr "Denne måned" #. Now Jan., last used in Dec. #: ../mx/mx-utils.c:146 msgid "Last month" msgstr "Sidste måned" #: ../mx/mx-utils.c:149 msgid "This year" msgstr "I år" #: ../mx/mx-utils.c:152 msgid "Last year" msgstr "Sidste år" #: ../mx/mx-utils.c:154 msgid "Ages ago" msgstr "Lang tid siden" mx-1.4.7/po/de.po000066400000000000000000000031631201047117600135170ustar00rootroot00000000000000msgid "" msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2010-03-27 09:42+0000\n" "PO-Revision-Date: \n" "Last-Translator: Andreas Machoy \n" "Language-Team: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Poedit-Language: German\n" "X-Poedit-SourceCharset: utf-8\n" #: ../mx/mx-application.c:311 msgid "Raise application" msgstr "Anwendung nach vorn bringen" #: ../mx/mx-gtk-light-switch.c:96 msgid "Unavailable" msgstr "Nicht verfügbar" #: ../mx/mx-utils.c:94 msgid "Less than a minute ago" msgstr "Vor weniger als einer Minute" #: ../mx/mx-utils.c:98 msgid "A few minutes ago" msgstr "Vor einigen Minuten" #: ../mx/mx-utils.c:102 msgid "A couple of hours ago" msgstr "Vor ein paar Stunden" #: ../mx/mx-utils.c:109 msgid "Earlier today" msgstr "Heute schon früher" #: ../mx/mx-utils.c:112 msgid "Yesterday" msgstr "Gestern" #: ../mx/mx-utils.c:120 #, c-format msgid "On %A" msgstr "Am %A" #: ../mx/mx-utils.c:126 msgid "Unknown" msgstr "Unbekannt" #: ../mx/mx-utils.c:133 msgid "Last week" msgstr "Letzte Woche" #: ../mx/mx-utils.c:136 msgid "A couple of weeks ago" msgstr "Vor einigen Wochen" #: ../mx/mx-utils.c:142 msgid "This month" msgstr "Diesen Monat" #. Now Jan., last used in Dec. #: ../mx/mx-utils.c:146 msgid "Last month" msgstr "Letzten Monat" #: ../mx/mx-utils.c:149 msgid "This year" msgstr "Dieses Jahr" #: ../mx/mx-utils.c:152 msgid "Last year" msgstr "Letztes Jahr" #: ../mx/mx-utils.c:154 msgid "Ages ago" msgstr "Vor einer Ewigkeit" #~ msgid "On" #~ msgstr "Ein" #~ msgid "Off" #~ msgstr "Aus" mx-1.4.7/po/en_GB.po000066400000000000000000000036311201047117600141010ustar00rootroot00000000000000# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: MX\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2010-04-14 09:54+0000\n" "PO-Revision-Date: 2010-02-24 16:45-0800\n" "Last-Translator: Margie Foster \n" "Language-Team: British English \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n!=1;\n" "X-Poedit-Language: English\n" "X-Poedit-SourceCharset: utf-8\n" "X-Poedit-Country: UNITED KINGDOM\n" #: ../mx-gtk/mx-gtk-light-switch.c:96 msgid "Unavailable" msgstr "Unavailable" #: ../mx/mx-utils.c:94 msgid "Less than a minute ago" msgstr "Less than a minute ago" #: ../mx/mx-utils.c:98 msgid "A few minutes ago" msgstr "A few minutes ago" #: ../mx/mx-utils.c:102 msgid "A couple of hours ago" msgstr "A couple of hours ago" #: ../mx/mx-utils.c:109 msgid "Earlier today" msgstr "Earlier today" #: ../mx/mx-utils.c:112 msgid "Yesterday" msgstr "Yesterday" #: ../mx/mx-utils.c:120 #, c-format msgid "On %A" msgstr "On %A" #: ../mx/mx-utils.c:126 msgid "Unknown" msgstr "Unknown" #: ../mx/mx-utils.c:133 msgid "Last week" msgstr "Last week" #: ../mx/mx-utils.c:136 msgid "A couple of weeks ago" msgstr "A couple of weeks ago" #: ../mx/mx-utils.c:142 msgid "This month" msgstr "This month" #. Now Jan., last used in Dec. #: ../mx/mx-utils.c:146 msgid "Last month" msgstr "Last month" #: ../mx/mx-utils.c:149 msgid "This year" msgstr "This year" #: ../mx/mx-utils.c:152 msgid "Last year" msgstr "Last year" #: ../mx/mx-utils.c:154 msgid "Ages ago" msgstr "Ages ago" #: ../mx/mx-application.c:311 msgid "Raise application" msgstr "Raise application" #~ msgid "On" #~ msgstr "On" #~ msgid "Off" #~ msgstr "Off" mx-1.4.7/po/eo.po000066400000000000000000000043031201047117600135270ustar00rootroot00000000000000# Esperanto translations for mx package. # Copyright (C) 2009 THE mx'S COPYRIGHT HOLDER # This file is distributed under the same license as the mx package. # Neil Roberts , 2009. # msgid "" msgstr "" "Project-Id-Version: mx 1.1.2\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2010-02-26 12:45+0000\n" "PO-Revision-Date: 2009-09-10 10:11+0100\n" "Last-Translator: Neil Roberts \n" "Language-Team: Esperanto \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" #: mx/mx-gtk-light-switch.c:232 msgid "Unavailable" msgstr "Nedisponebla" #. TRANSLATORS: If this string takes more than four or five characters in #. * your language, please use the MEDIUM VERTICAL BAR unicode character #. * instead #: mx/mx-gtk-light-switch.c:252 mx/mx-gtk-light-switch.c:326 msgid "On" msgstr "Ŝalta" #. Draw the second label; "Off" #. TRANSLATORS: If this string takes more than four or five characters in #. * your language, please use the WHITE CIRCLE U+25CB unicode character #. * instead #: mx/mx-gtk-light-switch.c:271 mx/mx-gtk-light-switch.c:324 msgid "Off" msgstr "Mal" #: mx/mx-utils.c:93 msgid "Less than a minute ago" msgstr "Antaŭ malpli ol minuto" #: mx/mx-utils.c:97 msgid "A few minutes ago" msgstr "Antaŭ kelkaj minutoj" #: mx/mx-utils.c:101 msgid "A couple of hours ago" msgstr "Antaŭ kelkaj horoj" #: mx/mx-utils.c:108 msgid "Earlier today" msgstr "Hodiaŭ antaŭe" #: mx/mx-utils.c:111 msgid "Yesterday" msgstr "Hieraŭ" #: mx/mx-utils.c:119 #, c-format msgid "On %A" msgstr "Je %A" #: mx/mx-utils.c:125 msgid "Unknown" msgstr "Nesciata" #: mx/mx-utils.c:132 msgid "Last week" msgstr "La pasintan semajnon" #: mx/mx-utils.c:135 msgid "A couple of weeks ago" msgstr "Antaŭ kelkaj semajnoj" #: mx/mx-utils.c:141 msgid "This month" msgstr "Ĉi-monaton" #. Now Jan., last used in Dec. #: mx/mx-utils.c:145 msgid "Last month" msgstr "La pasintan monaton" #: mx/mx-utils.c:148 msgid "This year" msgstr "Ĉi-jaron" #: mx/mx-utils.c:151 msgid "Last year" msgstr "La pasintan jaron" #: mx/mx-utils.c:153 msgid "Ages ago" msgstr "Pratempe" mx-1.4.7/po/es.po000066400000000000000000000036611201047117600135410ustar00rootroot00000000000000# msgid "" msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2010-03-22 09:34+0000\n" "PO-Revision-Date: \n" "Last-Translator: Tomás Galicia \n" "Language-Team: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Poedit-Language: Spanish\n" "X-Poedit-SourceCharset: utf-8\n" #: ../mx/mx-gtk-light-switch.c:96 msgid "Unavailable" msgstr "No disponible" #: ../mx/mx-utils.c:94 msgid "Less than a minute ago" msgstr "Hace menos de un minuto" #: ../mx/mx-utils.c:98 msgid "A few minutes ago" msgstr "Hace unos minutos" #: ../mx/mx-utils.c:102 msgid "A couple of hours ago" msgstr "Hace un par de horas" #: ../mx/mx-utils.c:109 msgid "Earlier today" msgstr "Hoy, pero antes" #: ../mx/mx-utils.c:112 msgid "Yesterday" msgstr "Ayer" # #: ../mx/mx-utils.c:120 #, c-format msgid "On %A" msgstr "El %A" #: ../mx/mx-utils.c:126 msgid "Unknown" msgstr "Desconocido" #: ../mx/mx-utils.c:133 msgid "Last week" msgstr "La semana pasada" #: ../mx/mx-utils.c:136 msgid "A couple of weeks ago" msgstr "Hace un par de semanas" #: ../mx/mx-utils.c:142 msgid "This month" msgstr "Este mes" #. Now Jan., last used in Dec. #: ../mx/mx-utils.c:146 msgid "Last month" msgstr "El mes pasado" #: ../mx/mx-utils.c:149 msgid "This year" msgstr "Este año" #: ../mx/mx-utils.c:152 msgid "Last year" msgstr "El año pasado" # Long time ago #: ../mx/mx-utils.c:154 msgid "Ages ago" msgstr "Hace mucho tiempo" # This string is used by the action which brings the application to the # foreground. For example, if the application was minimised, or obscured by other windows, it would raise the application window to the foreground. This is the same as "Restaure application". #: ../mx/mx-application.c:311 msgid "Raise application" msgstr "Restaurar la aplicación" #~ msgid "On" #~ msgstr "Activado" #~ msgid "Off" #~ msgstr "Desactivado" mx-1.4.7/po/es_MX.po000066400000000000000000000032111201047117600141340ustar00rootroot00000000000000msgid "" msgstr "" "Project-Id-Version: mx\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2010-04-26 09:42+0000\n" "PO-Revision-Date: \n" "Last-Translator: Salvador Cabrera Lozano \n" "Language-Team: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n!=1; \n" "X-Poedit-Language: Spanish\n" "X-Poedit-Country: MEXICO\n" "X-Poedit-SourceCharset: utf-8\n" #: ../mx/mx-application.c:311 msgid "Raise application" msgstr "Restaurar aplicación" #: ../mx-gtk/mx-gtk-light-switch.c:96 msgid "Unavailable" msgstr "No disponible" #: ../mx/mx-utils.c:94 msgid "Less than a minute ago" msgstr "Hace menos de un minuto" #: ../mx/mx-utils.c:98 msgid "A few minutes ago" msgstr "Hace unos pocos minutos" #: ../mx/mx-utils.c:102 msgid "A couple of hours ago" msgstr "Hace un par de horas" #: ../mx/mx-utils.c:109 msgid "Earlier today" msgstr "Hoy mas temprano" #: ../mx/mx-utils.c:112 msgid "Yesterday" msgstr "Ayer" #: ../mx/mx-utils.c:120 #, c-format msgid "On %A" msgstr "El %A" #: ../mx/mx-utils.c:126 msgid "Unknown" msgstr "Desconocido" #: ../mx/mx-utils.c:133 msgid "Last week" msgstr "La semana pasada" #: ../mx/mx-utils.c:136 msgid "A couple of weeks ago" msgstr "Hace un par de semanas" #: ../mx/mx-utils.c:142 msgid "This month" msgstr "Este mes" #. Now Jan., last used in Dec. #: ../mx/mx-utils.c:146 msgid "Last month" msgstr "El mes pasado" #: ../mx/mx-utils.c:149 msgid "This year" msgstr "Este año" #: ../mx/mx-utils.c:152 msgid "Last year" msgstr "El año pasado" #: ../mx/mx-utils.c:154 msgid "Ages ago" msgstr "Hace mucho tiempo" mx-1.4.7/po/fi.po000066400000000000000000000032761201047117600135320ustar00rootroot00000000000000# msgid "" msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2010-04-07 09:46+0000\n" "PO-Revision-Date: \n" "Last-Translator: Omar Antila \n" "Language-Team: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n!=1;\n" "X-Poedit-Language: Finnish\n" "X-Poedit-SourceCharset: utf-8\n" #: ../mx-gtk/mx-gtk-light-switch.c:96 msgid "Unavailable" msgstr "Ei käytettävissä" #: ../mx/mx-utils.c:94 msgid "Less than a minute ago" msgstr "Alle minuutti sitten" #: ../mx/mx-utils.c:98 msgid "A few minutes ago" msgstr "Muutama minuutti sitten" #: ../mx/mx-utils.c:102 msgid "A couple of hours ago" msgstr "Muutama tunti sitten" #: ../mx/mx-utils.c:109 msgid "Earlier today" msgstr "Tänään aikaisemmin" #: ../mx/mx-utils.c:112 msgid "Yesterday" msgstr "Eilen" #: ../mx/mx-utils.c:120 #, c-format msgid "On %A" msgstr "Päivämääränä %A" #: ../mx/mx-utils.c:126 msgid "Unknown" msgstr "Tuntematon" #: ../mx/mx-utils.c:133 msgid "Last week" msgstr "Viime viikolla" #: ../mx/mx-utils.c:136 msgid "A couple of weeks ago" msgstr "Pari viikkoa sitten" #: ../mx/mx-utils.c:142 msgid "This month" msgstr "Tässä kuussa" #. Now Jan., last used in Dec. #: ../mx/mx-utils.c:146 msgid "Last month" msgstr "Viime kuussa" #: ../mx/mx-utils.c:149 msgid "This year" msgstr "Tänä vuonna" #: ../mx/mx-utils.c:152 msgid "Last year" msgstr "Viime vuonna" #: ../mx/mx-utils.c:154 msgid "Ages ago" msgstr "Aataminaikoina" #: ../mx/mx-application.c:311 msgid "Raise application" msgstr "Nosta sovellus" #~ msgid "On" #~ msgstr "Päällä" #~ msgid "Off" #~ msgstr "Pois päältä" mx-1.4.7/po/fr.po000066400000000000000000000034011201047117600135310ustar00rootroot00000000000000# msgid "" msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2010-04-05 09:39+0000\n" "PO-Revision-Date: \n" "Last-Translator: Loïc Dufresne de Virel \n" "Language-Team: lpdufres \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Poedit-Language: French\n" "X-Poedit-SourceCharset: utf-8\n" #: ../mx-gtk/mx-gtk-light-switch.c:96 msgid "Unavailable" msgstr "Indisponible" #: ../mx/mx-utils.c:94 msgid "Less than a minute ago" msgstr "Il y a moins d'une minute" #: ../mx/mx-utils.c:98 msgid "A few minutes ago" msgstr "Il y a quelques minutes" #: ../mx/mx-utils.c:102 msgid "A couple of hours ago" msgstr "Il y a quelques heures" #: ../mx/mx-utils.c:109 msgid "Earlier today" msgstr "Un peu plus tôt aujourd'hui" #: ../mx/mx-utils.c:112 msgid "Yesterday" msgstr "Hier" # "On Tuesday" would be "Mardi" only in French. #: ../mx/mx-utils.c:120 #, c-format msgid "On %A" msgstr "%A" #: ../mx/mx-utils.c:126 msgid "Unknown" msgstr "Inconnu" #: ../mx/mx-utils.c:133 msgid "Last week" msgstr "La semaine dernière" #: ../mx/mx-utils.c:136 msgid "A couple of weeks ago" msgstr "Il y a quelques semaines" #: ../mx/mx-utils.c:142 msgid "This month" msgstr "Ce mois" #. Now Jan., last used in Dec. #: ../mx/mx-utils.c:146 msgid "Last month" msgstr "Le mois dernier" #: ../mx/mx-utils.c:149 msgid "This year" msgstr "Cette année" #: ../mx/mx-utils.c:152 msgid "Last year" msgstr "L'année dernière" #: ../mx/mx-utils.c:154 msgid "Ages ago" msgstr "Ça fait un bail" #: ../mx/mx-application.c:311 msgid "Raise application" msgstr "Relever l'application" #~ msgid "On" #~ msgstr "Activé" #~ msgid "Off" #~ msgstr "Désactivé" mx-1.4.7/po/hu.po000066400000000000000000000036441201047117600135470ustar00rootroot00000000000000# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2010-02-26 12:45+0000\n" "PO-Revision-Date: 2009-07-31 22:53+0100\n" "Last-Translator: Kökéndy Ákos \n" "Language-Team: LANGUAGE \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" #: mx/mx-gtk-light-switch.c:232 msgid "Unavailable" msgstr "Nem elérhető" #. TRANSLATORS: If this string takes more than four or five characters in #. * your language, please use the MEDIUM VERTICAL BAR unicode character #. * instead #: mx/mx-gtk-light-switch.c:252 mx/mx-gtk-light-switch.c:326 msgid "On" msgstr "Be" #. Draw the second label; "Off" #. TRANSLATORS: If this string takes more than four or five characters in #. * your language, please use the WHITE CIRCLE U+25CB unicode character #. * instead #: mx/mx-gtk-light-switch.c:271 mx/mx-gtk-light-switch.c:324 msgid "Off" msgstr "Ki" #: mx/mx-utils.c:93 msgid "Less than a minute ago" msgstr "" #: mx/mx-utils.c:97 msgid "A few minutes ago" msgstr "" #: mx/mx-utils.c:101 msgid "A couple of hours ago" msgstr "" #: mx/mx-utils.c:108 msgid "Earlier today" msgstr "" #: mx/mx-utils.c:111 msgid "Yesterday" msgstr "" #: mx/mx-utils.c:119 #, c-format msgid "On %A" msgstr "" #: mx/mx-utils.c:125 msgid "Unknown" msgstr "" #: mx/mx-utils.c:132 msgid "Last week" msgstr "" #: mx/mx-utils.c:135 msgid "A couple of weeks ago" msgstr "" #: mx/mx-utils.c:141 msgid "This month" msgstr "" #. Now Jan., last used in Dec. #: mx/mx-utils.c:145 msgid "Last month" msgstr "" #: mx/mx-utils.c:148 msgid "This year" msgstr "" #: mx/mx-utils.c:151 msgid "Last year" msgstr "" #: mx/mx-utils.c:153 msgid "Ages ago" msgstr "" mx-1.4.7/po/id.po000066400000000000000000000034031201047117600135200ustar00rootroot00000000000000# SOME DESCRIPTIVE TITLE. # Copyright (C) 2009 Intel Corp # This file is distributed under the same license as the PACKAGE package. # Andika Triwidada , 2009. msgid "" msgstr "" "Project-Id-Version: mx\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2010-03-17 09:40+0000\n" "PO-Revision-Date: 2009-08-31 04:19+0700\n" "Last-Translator: Andika Triwidada \n" "Language-Team: Indonesian \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" #: ../mx/mx-gtk-light-switch.c:96 msgid "Unavailable" msgstr "Tak tersedia" #: ../mx/mx-utils.c:94 msgid "Less than a minute ago" msgstr "Kurang dari semenit yang lalu" #: ../mx/mx-utils.c:98 msgid "A few minutes ago" msgstr "Beberapa menit yang lalu" #: ../mx/mx-utils.c:102 msgid "A couple of hours ago" msgstr "Beberapa jam yang lalu" #: ../mx/mx-utils.c:109 msgid "Earlier today" msgstr "Hari ini" #: ../mx/mx-utils.c:112 msgid "Yesterday" msgstr "Kemarin" #: ../mx/mx-utils.c:120 #, c-format msgid "On %A" msgstr "Pada %A" #: ../mx/mx-utils.c:126 msgid "Unknown" msgstr "Tak diketahui" #: ../mx/mx-utils.c:133 msgid "Last week" msgstr "Minggu lalu" #: ../mx/mx-utils.c:136 msgid "A couple of weeks ago" msgstr "Beberapa minggu yang lalu" #: ../mx/mx-utils.c:142 msgid "This month" msgstr "Bulan ini" #. Now Jan., last used in Dec. #: ../mx/mx-utils.c:146 msgid "Last month" msgstr "Bulan lalu" #: ../mx/mx-utils.c:149 msgid "This year" msgstr "Tahun ini" #: ../mx/mx-utils.c:152 msgid "Last year" msgstr "Tahun lalu" #: ../mx/mx-utils.c:154 msgid "Ages ago" msgstr "Telah lama berlalu" #: ../mx/mx-application.c:311 msgid "Raise application" msgstr "Naikkan aplikasi" #~ msgid "On" #~ msgstr "Nyala" #~ msgid "Off" #~ msgstr "Mati" mx-1.4.7/po/it.po000066400000000000000000000034301201047117600135400ustar00rootroot00000000000000# Italian translation of mx. # Copyright (C) 2009 the mx copyright holder # This file is distributed under the same license as the mx package. # Milo Casagrande , 2009. # msgid "" msgstr "" "Project-Id-Version: mx\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2010-04-07 09:46+0000\n" "PO-Revision-Date: 2009-07-22 22:05+0200\n" "Last-Translator: Milo Casagrande \n" "Language-Team: Italian \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" #: ../mx-gtk/mx-gtk-light-switch.c:96 msgid "Unavailable" msgstr "Non disponibile" #: ../mx/mx-utils.c:94 msgid "Less than a minute ago" msgstr "Meno di un minuto fa" #: ../mx/mx-utils.c:98 msgid "A few minutes ago" msgstr "Alcuni minuti fa" #: ../mx/mx-utils.c:102 msgid "A couple of hours ago" msgstr "Un paio d'ore fa" #: ../mx/mx-utils.c:109 msgid "Earlier today" msgstr "Oggi" #: ../mx/mx-utils.c:112 msgid "Yesterday" msgstr "Ieri" # (ndt) giorno della settimana #: ../mx/mx-utils.c:120 #, c-format msgid "On %A" msgstr "%A" #: ../mx/mx-utils.c:126 msgid "Unknown" msgstr "Sconosciuto" #: ../mx/mx-utils.c:133 msgid "Last week" msgstr "La scorsa settimana" #: ../mx/mx-utils.c:136 msgid "A couple of weeks ago" msgstr "Un paio di settimane fa" #: ../mx/mx-utils.c:142 msgid "This month" msgstr "Questo mese" #. Now Jan., last used in Dec. #: ../mx/mx-utils.c:146 msgid "Last month" msgstr "Lo scorso mese" #: ../mx/mx-utils.c:149 msgid "This year" msgstr "Quest'anno" #: ../mx/mx-utils.c:152 msgid "Last year" msgstr "Lo scorso anno" #: ../mx/mx-utils.c:154 msgid "Ages ago" msgstr "Anni fa" #: ../mx/mx-application.c:311 msgid "Raise application" msgstr "Sposta applicazione" #~ msgid "On" #~ msgstr "On" #~ msgid "Off" #~ msgstr "Off" mx-1.4.7/po/ja.po000066400000000000000000000030351201047117600135170ustar00rootroot00000000000000msgid "" msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2010-04-05 09:39+0000\n" "PO-Revision-Date: \n" "Last-Translator: Takashi Kazami \n" "Language-Team: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Poedit-Language: Japanese\n" "X-Poedit-SourceCharset: utf-8\n" # Not sure the meaning of "raise" in this case... #: ../mx/mx-application.c:311 msgid "Raise application" msgstr "アプリケーションを上げる" #: ../mx-gtk/mx-gtk-light-switch.c:96 msgid "Unavailable" msgstr "使用できません" #: ../mx/mx-utils.c:94 msgid "Less than a minute ago" msgstr "1分以内" #: ../mx/mx-utils.c:98 msgid "A few minutes ago" msgstr "数分前" #: ../mx/mx-utils.c:102 msgid "A couple of hours ago" msgstr "数時間前" #: ../mx/mx-utils.c:109 msgid "Earlier today" msgstr "今日" #: ../mx/mx-utils.c:112 msgid "Yesterday" msgstr "昨日" #: ../mx/mx-utils.c:120 #, c-format msgid "On %A" msgstr "%A" #: ../mx/mx-utils.c:126 msgid "Unknown" msgstr "不明" #: ../mx/mx-utils.c:133 msgid "Last week" msgstr "先週" #: ../mx/mx-utils.c:136 msgid "A couple of weeks ago" msgstr "数週間前" #: ../mx/mx-utils.c:142 msgid "This month" msgstr "今月" #. Now Jan., last used in Dec. #: ../mx/mx-utils.c:146 msgid "Last month" msgstr "先月" #: ../mx/mx-utils.c:149 msgid "This year" msgstr "今年" #: ../mx/mx-utils.c:152 msgid "Last year" msgstr "去年" #: ../mx/mx-utils.c:154 msgid "Ages ago" msgstr "ずいぶんと前" mx-1.4.7/po/ko.po000066400000000000000000000032131201047117600135340ustar00rootroot00000000000000# msgid "" msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2010-04-07 09:46+0000\n" "PO-Revision-Date: \n" "Last-Translator: GLSKOR_Sook \n" "Language-Team: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n!=1;\n" "X-Poedit-Language: Korean\n" "X-Poedit-SourceCharset: utf-8\n" #: ../mx-gtk/mx-gtk-light-switch.c:96 msgid "Unavailable" msgstr "사용불가" #: ../mx/mx-utils.c:94 msgid "Less than a minute ago" msgstr "일분도 채 되기전에" #: ../mx/mx-utils.c:98 msgid "A few minutes ago" msgstr "몇분 전에" #: ../mx/mx-utils.c:102 msgid "A couple of hours ago" msgstr "한두시간 전에" #: ../mx/mx-utils.c:109 msgid "Earlier today" msgstr "조금전 오늘" #: ../mx/mx-utils.c:112 msgid "Yesterday" msgstr "어제" #: ../mx/mx-utils.c:120 #, c-format msgid "On %A" msgstr " %A에" #: ../mx/mx-utils.c:126 msgid "Unknown" msgstr "알수 없음" #: ../mx/mx-utils.c:133 msgid "Last week" msgstr "지난 주" #: ../mx/mx-utils.c:136 msgid "A couple of weeks ago" msgstr "한두 주 전" #: ../mx/mx-utils.c:142 msgid "This month" msgstr "이번 달" #. Now Jan., last used in Dec. #: ../mx/mx-utils.c:146 msgid "Last month" msgstr "지난 달" #: ../mx/mx-utils.c:149 msgid "This year" msgstr "올 해" #: ../mx/mx-utils.c:152 msgid "Last year" msgstr "지난 해" #: ../mx/mx-utils.c:154 msgid "Ages ago" msgstr "오래전" #: ../mx/mx-application.c:311 msgid "Raise application" msgstr "응용 프로그램 올리기" #~ msgid "On" #~ msgstr "껴기" #~ msgid "Off" #~ msgstr "끄기" mx-1.4.7/po/lo.po000066400000000000000000000041241201047117600135370ustar00rootroot00000000000000# Laotian translations for mx package. # Copyright (C) 2009 THE mx'S COPYRIGHT HOLDER # This file is distributed under the same license as the mx package. # Neil Roberts , 2009. # msgid "" msgstr "" "Project-Id-Version: mx 1.1.2\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2010-02-26 12:45+0000\n" "PO-Revision-Date: 2009-09-09 15:10+0100\n" "Last-Translator: Anousak Souphavanh \n" "Language-Team: Lao \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" #: mx/mx-gtk-light-switch.c:232 msgid "Unavailable" msgstr "" #. TRANSLATORS: If this string takes more than four or five characters in #. * your language, please use the MEDIUM VERTICAL BAR unicode character #. * instead #: mx/mx-gtk-light-switch.c:252 mx/mx-gtk-light-switch.c:326 msgid "On" msgstr "" #. Draw the second label; "Off" #. TRANSLATORS: If this string takes more than four or five characters in #. * your language, please use the WHITE CIRCLE U+25CB unicode character #. * instead #: mx/mx-gtk-light-switch.c:271 mx/mx-gtk-light-switch.c:324 msgid "Off" msgstr "" #: mx/mx-utils.c:93 msgid "Less than a minute ago" msgstr "" #: mx/mx-utils.c:97 msgid "A few minutes ago" msgstr "" #: mx/mx-utils.c:101 msgid "A couple of hours ago" msgstr "" #: mx/mx-utils.c:108 msgid "Earlier today" msgstr "ເຊົ້າມື່ນີ້" #: mx/mx-utils.c:111 msgid "Yesterday" msgstr "ມື້ວານນີ້" #: mx/mx-utils.c:119 #, c-format msgid "On %A" msgstr "ໃນ %A" #: mx/mx-utils.c:125 msgid "Unknown" msgstr "ບໍ່ຮູ້" #: mx/mx-utils.c:132 msgid "Last week" msgstr "ອາທິດພານມາ" #: mx/mx-utils.c:135 msgid "A couple of weeks ago" msgstr "" #: mx/mx-utils.c:141 msgid "This month" msgstr "ເດືອນນີ້" #. Now Jan., last used in Dec. #: mx/mx-utils.c:145 msgid "Last month" msgstr "ເດືອນກ່ອນ" #: mx/mx-utils.c:148 msgid "This year" msgstr "ປີນີ້" #: mx/mx-utils.c:151 msgid "Last year" msgstr "ປີກອນ" #: mx/mx-utils.c:153 msgid "Ages ago" msgstr "" mx-1.4.7/po/nl.po000066400000000000000000000034571201047117600135460ustar00rootroot00000000000000# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2010-03-22 09:34+0000\n" "PO-Revision-Date: 2009-08-13 19:37+0100\n" "Last-Translator: Sibo Attema \n" "Language-Team: LANGUAGE \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" #: ../mx/mx-gtk-light-switch.c:96 msgid "Unavailable" msgstr "Niet beschikbaar" #: ../mx/mx-utils.c:94 msgid "Less than a minute ago" msgstr "Minder dan een minuut geleden" #: ../mx/mx-utils.c:98 msgid "A few minutes ago" msgstr "Een paar minuten geleden" #: ../mx/mx-utils.c:102 msgid "A couple of hours ago" msgstr "Een paar uur geleden" #: ../mx/mx-utils.c:109 msgid "Earlier today" msgstr "Eerder vandaag" #: ../mx/mx-utils.c:112 msgid "Yesterday" msgstr "Gisteren" #: ../mx/mx-utils.c:120 #, c-format msgid "On %A" msgstr "Op %A" #: ../mx/mx-utils.c:126 msgid "Unknown" msgstr "Onbekend" #: ../mx/mx-utils.c:133 msgid "Last week" msgstr "Vorige week" #: ../mx/mx-utils.c:136 msgid "A couple of weeks ago" msgstr "Een paar weken geleden" #: ../mx/mx-utils.c:142 msgid "This month" msgstr "Deze maand" #. Now Jan., last used in Dec. #: ../mx/mx-utils.c:146 msgid "Last month" msgstr "Vorige maand" #: ../mx/mx-utils.c:149 msgid "This year" msgstr "Dit jaar" #: ../mx/mx-utils.c:152 msgid "Last year" msgstr "Vorig jaar" #: ../mx/mx-utils.c:154 msgid "Ages ago" msgstr "Lang lang geleden" #: ../mx/mx-application.c:311 msgid "Raise application" msgstr "Applicatie naar de voorgrond brengen" #~ msgid "On" #~ msgstr "Aan" #~ msgid "Off" #~ msgstr "Uit" mx-1.4.7/po/pa.po000066400000000000000000000041731201047117600135310ustar00rootroot00000000000000# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # # A S Alam , 2009, 2010. msgid "" msgstr "" "Project-Id-Version: MX.Moblin2-UI.mx\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2010-05-05 09:45+0000\n" "PO-Revision-Date: 2010-05-06 07:40+0530\n" "Last-Translator: A S Alam \n" "Language-Team: Punjabi/Panjabi \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Generator: Lokalize 1.0\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" #: ../mx/mx-application.c:311 msgid "Raise application" msgstr "ਐਪਲੀਕੇਸ਼ਨ ਉਭਾਰੋ" #: ../mx-gtk/mx-gtk-light-switch.c:96 msgid "Unavailable" msgstr "ਨਾ-ਉਪਲੱਬਧ" #: ../mx/mx-utils.c:94 msgid "Less than a minute ago" msgstr "ਇੱਕ ਮਿੰਟ ਤੋਂ ਘੱਟ ਪਹਿਲਾਂ" #: ../mx/mx-utils.c:98 msgid "A few minutes ago" msgstr "ਕੁਝ ਕੁ ਮਿੰਟ ਪਹਿਲਾਂ" #: ../mx/mx-utils.c:102 msgid "A couple of hours ago" msgstr "ਕੁਝ ਘੰਟੇ ਪਹਿਲਾਂ" #: ../mx/mx-utils.c:109 msgid "Earlier today" msgstr "ਅੱਜ ਤੜਕੇ" #: ../mx/mx-utils.c:112 msgid "Yesterday" msgstr "ਕੱਲ੍ਹ" #: ../mx/mx-utils.c:120 #, c-format msgid "On %A" msgstr "%A ਉੱਤੇ" #: ../mx/mx-utils.c:126 msgid "Unknown" msgstr "ਅਣਜਾਣ" #: ../mx/mx-utils.c:133 msgid "Last week" msgstr "ਪਿਛਲੇ ਹਫ਼ਤੇ" #: ../mx/mx-utils.c:136 msgid "A couple of weeks ago" msgstr "ਕੁਝ ਹਫ਼ਤੇ ਪਹਿਲਾਂ" #: ../mx/mx-utils.c:142 msgid "This month" msgstr "ਇਹ ਮਹੀਨੇ" #. Now Jan., last used in Dec. #: ../mx/mx-utils.c:146 msgid "Last month" msgstr "ਪਿਛਲੇ ਮਹੀਨੇ" #: ../mx/mx-utils.c:149 msgid "This year" msgstr "ਇਹ ਸਾਲ" #: ../mx/mx-utils.c:152 msgid "Last year" msgstr "ਪਿਛਲੇ ਸਾਲ" #: ../mx/mx-utils.c:154 msgid "Ages ago" msgstr "ਕਈ ਸਾਲ ਪਹਿਲਾਂ" #~ msgid "On" #~ msgstr "ਚਾਲੂ" #~ msgid "Off" #~ msgstr "ਬੰਦ" mx-1.4.7/po/pl.po000066400000000000000000000037151201047117600135450ustar00rootroot00000000000000# pl locale # Copyright (C) 2009 Intel Corporation # This file is distributed under the same license as the MX package. # Andrzej Zaborowski , 2009. # msgid "" msgstr "" "Project-Id-Version: Moblin\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2010-04-07 09:46+0000\n" "PO-Revision-Date: \n" "Last-Translator: Romuald Pawlikowski \n" "Language-Team: Moblin\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n" "X-Poedit-Language: Polish\n" "X-Poedit-SourceCharset: utf-8\n" #: ../mx/mx-application.c:311 msgid "Raise application" msgstr "Podnieś aplikację" #: ../mx-gtk/mx-gtk-light-switch.c:96 msgid "Unavailable" msgstr "Niedostępne" #: ../mx/mx-utils.c:94 msgid "Less than a minute ago" msgstr "Mniej niż minutę temu" #: ../mx/mx-utils.c:98 msgid "A few minutes ago" msgstr "Parę minut temu" #: ../mx/mx-utils.c:102 msgid "A couple of hours ago" msgstr "Kilka godzin temu" # No good way to say "earlier today" #: ../mx/mx-utils.c:109 msgid "Earlier today" msgstr "Kilkanaście godzin temu" #: ../mx/mx-utils.c:112 msgid "Yesterday" msgstr "Wczoraj" #: ../mx/mx-utils.c:120 #, c-format msgid "On %A" msgstr "Dnia %A" #: ../mx/mx-utils.c:126 msgid "Unknown" msgstr "Nie sprecyzowano" #: ../mx/mx-utils.c:133 msgid "Last week" msgstr "Zeszły tydzień" #: ../mx/mx-utils.c:136 msgid "A couple of weeks ago" msgstr "Parę tygodni temu" #: ../mx/mx-utils.c:142 msgid "This month" msgstr "W tym miesiącu" #. Now Jan., last used in Dec. #: ../mx/mx-utils.c:146 msgid "Last month" msgstr "W zeszłym miesiącu" #: ../mx/mx-utils.c:149 msgid "This year" msgstr "W tym roku" #: ../mx/mx-utils.c:152 msgid "Last year" msgstr "W zeszłym roku" #: ../mx/mx-utils.c:154 msgid "Ages ago" msgstr "Wieki temu" #~ msgid "On" #~ msgstr "Tak" #~ msgid "Off" #~ msgstr "Nie" mx-1.4.7/po/pt_BR.po000066400000000000000000000032211201047117600141300ustar00rootroot00000000000000msgid "" msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2010-04-07 09:46+0000\n" "PO-Revision-Date: \n" "Last-Translator: GLSPTB_Gabor \n" "Language-Team: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Poedit-Language: Portuguese\n" "X-Poedit-SourceCharset: utf-8\n" "Plural-Forms: nplurals=2; plural=n!=1;\n" #: ../mx/mx-application.c:311 msgid "Raise application" msgstr "Elevar o aplicativo" #: ../mx-gtk/mx-gtk-light-switch.c:96 msgid "Unavailable" msgstr "Não disponível" #: ../mx/mx-utils.c:94 msgid "Less than a minute ago" msgstr "Há menos de um minuto atrás" #: ../mx/mx-utils.c:98 msgid "A few minutes ago" msgstr "Alguns minutos atrás" #: ../mx/mx-utils.c:102 msgid "A couple of hours ago" msgstr "Algumas horas atrás" #: ../mx/mx-utils.c:109 msgid "Earlier today" msgstr "Hoje cedo" #: ../mx/mx-utils.c:112 msgid "Yesterday" msgstr "Ontem" #: ../mx/mx-utils.c:120 #, c-format msgid "On %A" msgstr "Em %A" #: ../mx/mx-utils.c:126 msgid "Unknown" msgstr "Desconhecido" #: ../mx/mx-utils.c:133 msgid "Last week" msgstr "Semana passada" #: ../mx/mx-utils.c:136 msgid "A couple of weeks ago" msgstr "Algumas semanas atrás" #: ../mx/mx-utils.c:142 msgid "This month" msgstr "Este mês" #. Now Jan., last used in Dec. #: ../mx/mx-utils.c:146 msgid "Last month" msgstr "Mês passado" #: ../mx/mx-utils.c:149 msgid "This year" msgstr "Este ano" #: ../mx/mx-utils.c:152 msgid "Last year" msgstr "Ano passado" #: ../mx/mx-utils.c:154 msgid "Ages ago" msgstr "Há muito tempo" #~ msgid "On" #~ msgstr "Lig." #~ msgid "Off" #~ msgstr "Desl." mx-1.4.7/po/ro.po000066400000000000000000000043221201047117600135450ustar00rootroot00000000000000# Romanian translations for mx package. # Copyright (C) 2009 The mx copyright holder # This file is distributed under the same license as the mx package. # Cosmin Bordeianu , 2009. # msgid "" msgstr "" "Project-Id-Version: mx\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2010-02-26 12:45+0000\n" "PO-Revision-Date: 2009-09-15 15:05+0200\n" "Last-Translator: Cosmin Bordeianu \n" "Language-Team: Moblin Romania \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Poedit-Language: Romanian\n" "X-Poedit-Country: ROMANIA\n" "X-Poedit-SourceCharset: utf-8\n" #: mx/mx-gtk-light-switch.c:232 msgid "Unavailable" msgstr "Indisponibil" #. TRANSLATORS: If this string takes more than four or five characters in #. * your language, please use the MEDIUM VERTICAL BAR unicode character #. * instead #: mx/mx-gtk-light-switch.c:252 mx/mx-gtk-light-switch.c:326 msgid "On" msgstr "On" #. Draw the second label; "Off" #. TRANSLATORS: If this string takes more than four or five characters in #. * your language, please use the WHITE CIRCLE U+25CB unicode character #. * instead #: mx/mx-gtk-light-switch.c:271 mx/mx-gtk-light-switch.c:324 msgid "Off" msgstr "Off" #: mx/mx-utils.c:93 msgid "Less than a minute ago" msgstr "Mai puțin de un minut în urmă" #: mx/mx-utils.c:97 msgid "A few minutes ago" msgstr "Acum câteva minute" #: mx/mx-utils.c:101 msgid "A couple of hours ago" msgstr "Acum câteva ore" #: mx/mx-utils.c:108 msgid "Earlier today" msgstr "Mai devreme" #: mx/mx-utils.c:111 msgid "Yesterday" msgstr "Ieri" #: mx/mx-utils.c:119 #, c-format msgid "On %A" msgstr "Pe %A" #: mx/mx-utils.c:125 msgid "Unknown" msgstr "Necunoscut" #: mx/mx-utils.c:132 msgid "Last week" msgstr "Săptămâna trecută" #: mx/mx-utils.c:135 msgid "A couple of weeks ago" msgstr "Acum câteva săptămâni" #: mx/mx-utils.c:141 msgid "This month" msgstr "Luna aceasta" #. Now Jan., last used in Dec. #: mx/mx-utils.c:145 msgid "Last month" msgstr "Luna trecută" #: mx/mx-utils.c:148 msgid "This year" msgstr "Anul acesta" #: mx/mx-utils.c:151 msgid "Last year" msgstr "Anul trecut" #: mx/mx-utils.c:153 msgid "Ages ago" msgstr "Foarte demult" mx-1.4.7/po/ru.po000066400000000000000000000037371201047117600135640ustar00rootroot00000000000000# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2010-04-05 09:39+0000\n" "PO-Revision-Date: 2010-04-06 16:16+0200\n" "Last-Translator: Yuri Nazarenko \n" "Language-Team: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n!=1;\n" "X-Poedit-Language: Russian\n" "X-Poedit-SourceCharset: utf-8\n" #: ../mx/mx-application.c:311 msgid "Raise application" msgstr "Вызвать приложение" #: ../mx-gtk/mx-gtk-light-switch.c:96 msgid "Unavailable" msgstr "Недоступно" #: ../mx/mx-utils.c:94 msgid "Less than a minute ago" msgstr "Меньше минуты назад" #: ../mx/mx-utils.c:98 msgid "A few minutes ago" msgstr "Несколько минут назад" #: ../mx/mx-utils.c:102 msgid "A couple of hours ago" msgstr "Пару часов назад" #: ../mx/mx-utils.c:109 msgid "Earlier today" msgstr "Ранее сегодня" #: ../mx/mx-utils.c:112 msgid "Yesterday" msgstr "Вчера" #: ../mx/mx-utils.c:120 #, c-format msgid "On %A" msgstr "В %A" #: ../mx/mx-utils.c:126 msgid "Unknown" msgstr "Неизвестно" #: ../mx/mx-utils.c:133 msgid "Last week" msgstr "На прошлой неделе" #: ../mx/mx-utils.c:136 msgid "A couple of weeks ago" msgstr "Пару недель назад" #: ../mx/mx-utils.c:142 msgid "This month" msgstr "В этом месяце" #. Now Jan., last used in Dec. #: ../mx/mx-utils.c:146 msgid "Last month" msgstr "В прошлом месяце" #: ../mx/mx-utils.c:149 msgid "This year" msgstr "В этом году" #: ../mx/mx-utils.c:152 msgid "Last year" msgstr "В прошлом году" #: ../mx/mx-utils.c:154 msgid "Ages ago" msgstr "Очень давно" mx-1.4.7/po/sk.po000066400000000000000000000032601201047117600135420ustar00rootroot00000000000000# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # Tomáš Virgl , 2010. # msgid "" msgstr "" "Project-Id-Version: mx.master\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2010-04-21 09:42+0000\n" "PO-Revision-Date: 2010-04-21 05:24+0200\n" "Last-Translator: Tomáš Virgl \n" "Language-Team: slovak \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" #: ../mx/mx-application.c:311 msgid "Raise application" msgstr "" #: ../mx-gtk/mx-gtk-light-switch.c:96 msgid "Unavailable" msgstr "Nedostupný" #: ../mx/mx-utils.c:94 msgid "Less than a minute ago" msgstr "Pred menej ako minútou" #: ../mx/mx-utils.c:98 msgid "A few minutes ago" msgstr "Pred pár minútami" #: ../mx/mx-utils.c:102 msgid "A couple of hours ago" msgstr "Pred pár hodinami" #: ../mx/mx-utils.c:109 msgid "Earlier today" msgstr "" #: ../mx/mx-utils.c:112 msgid "Yesterday" msgstr "Včera" #: ../mx/mx-utils.c:120 #, c-format msgid "On %A" msgstr "" #: ../mx/mx-utils.c:126 msgid "Unknown" msgstr "Neznámy" #: ../mx/mx-utils.c:133 msgid "Last week" msgstr "Minulý týždeň" #: ../mx/mx-utils.c:136 msgid "A couple of weeks ago" msgstr "Pred pár týždňami" #: ../mx/mx-utils.c:142 msgid "This month" msgstr "Tento mesiac" #. Now Jan., last used in Dec. #: ../mx/mx-utils.c:146 msgid "Last month" msgstr "Minulý mesiac" #: ../mx/mx-utils.c:149 msgid "This year" msgstr "Tento rok" #: ../mx/mx-utils.c:152 msgid "Last year" msgstr "Minulý rok" #: ../mx/mx-utils.c:154 msgid "Ages ago" msgstr "Pred rokmi" mx-1.4.7/po/sv.po000066400000000000000000000032231201047117600135540ustar00rootroot00000000000000msgid "" msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2010-03-17 09:40+0000\n" "PO-Revision-Date: \n" "Last-Translator: Annika \n" "Language-Team: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Poedit-Language: Swedish\n" "X-Poedit-SourceCharset: utf-8\n" "Plural-Forms: nplurals=2; plural=n!=1;\n" #: ../mx/mx-application.c:311 msgid "Raise application" msgstr "Öppna program" #: ../mx/mx-gtk-light-switch.c:96 msgid "Unavailable" msgstr "Otillgänglig" #: ../mx/mx-utils.c:94 msgid "Less than a minute ago" msgstr "Mindre än för en minut sedan" #: ../mx/mx-utils.c:98 msgid "A few minutes ago" msgstr "Ett par minuter sedan" #: ../mx/mx-utils.c:102 msgid "A couple of hours ago" msgstr "Ett par timmar sedan" #: ../mx/mx-utils.c:109 msgid "Earlier today" msgstr "Tidigare idag" #: ../mx/mx-utils.c:112 msgid "Yesterday" msgstr "Igår" #: ../mx/mx-utils.c:120 #, c-format msgid "On %A" msgstr "På %A" #: ../mx/mx-utils.c:126 msgid "Unknown" msgstr "Okänd enhet" #: ../mx/mx-utils.c:133 msgid "Last week" msgstr "Förra veckan" #: ../mx/mx-utils.c:136 msgid "A couple of weeks ago" msgstr "Ett par veckor sedan" #: ../mx/mx-utils.c:142 msgid "This month" msgstr "Denna månad" #. Now Jan., last used in Dec. #: ../mx/mx-utils.c:146 msgid "Last month" msgstr "Förra månaden" #: ../mx/mx-utils.c:149 msgid "This year" msgstr "Innevarande år" #: ../mx/mx-utils.c:152 msgid "Last year" msgstr "Föregående år" #: ../mx/mx-utils.c:154 msgid "Ages ago" msgstr "För länge sedan" #~ msgid "On" #~ msgstr "På" #~ msgid "Off" #~ msgstr "Av" mx-1.4.7/po/th.po000066400000000000000000000040511201047117600135370ustar00rootroot00000000000000# Thai locale mx-mx_api_version # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # Anuchit Chalothorn , 2009. # msgid "" msgstr "" "Project-Id-Version: mx-mx_api_version 2.x\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2010-02-26 12:45+0000\n" "PO-Revision-Date: 2009-08-14 16:21+0700\n" "Last-Translator: Anuchit Chalothorn \n" "Language-Team: Thai \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Poedit-Language: Thai\n" "X-Poedit-Country: THAILAND\n" #: mx/mx-gtk-light-switch.c:232 msgid "Unavailable" msgstr "ใช้การไม่ได้" #. TRANSLATORS: If this string takes more than four or five characters in #. * your language, please use the MEDIUM VERTICAL BAR unicode character #. * instead #: mx/mx-gtk-light-switch.c:252 mx/mx-gtk-light-switch.c:326 msgid "On" msgstr "เปิด" #. Draw the second label; "Off" #. TRANSLATORS: If this string takes more than four or five characters in #. * your language, please use the WHITE CIRCLE U+25CB unicode character #. * instead #: mx/mx-gtk-light-switch.c:271 mx/mx-gtk-light-switch.c:324 msgid "Off" msgstr "ปิด" #: mx/mx-utils.c:93 msgid "Less than a minute ago" msgstr "" #: mx/mx-utils.c:97 msgid "A few minutes ago" msgstr "" #: mx/mx-utils.c:101 msgid "A couple of hours ago" msgstr "" #: mx/mx-utils.c:108 msgid "Earlier today" msgstr "" #: mx/mx-utils.c:111 msgid "Yesterday" msgstr "" #: mx/mx-utils.c:119 #, c-format msgid "On %A" msgstr "" #: mx/mx-utils.c:125 msgid "Unknown" msgstr "" #: mx/mx-utils.c:132 msgid "Last week" msgstr "" #: mx/mx-utils.c:135 msgid "A couple of weeks ago" msgstr "" #: mx/mx-utils.c:141 msgid "This month" msgstr "" #. Now Jan., last used in Dec. #: mx/mx-utils.c:145 msgid "Last month" msgstr "" #: mx/mx-utils.c:148 msgid "This year" msgstr "" #: mx/mx-utils.c:151 msgid "Last year" msgstr "" #: mx/mx-utils.c:153 msgid "Ages ago" msgstr "" mx-1.4.7/po/tr.po000066400000000000000000000043271201047117600135570ustar00rootroot00000000000000# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2010-02-26 12:45+0000\n" "PO-Revision-Date: 2010-05-21 10:03-0500\n" "Last-Translator: Ahmet Özgür Erdemli \n" "Language-Team: MeeGo Türkçe\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=1; plural=0;\n" "X-Poedit-Language: Turkish\n" "X-Poedit-Country: TURKEY\n" "X-Poedit-SourceCharset: utf-8\n" #: mx/mx-gtk-light-switch.c:232 msgid "Unavailable" msgstr "Kullanılamaz" #. TRANSLATORS: If this string takes more than four or five characters in #. * your language, please use the MEDIUM VERTICAL BAR unicode character #. * instead #: mx/mx-gtk-light-switch.c:252 #: mx/mx-gtk-light-switch.c:326 msgid "On" msgstr "Açık" #. Draw the second label; "Off" #. TRANSLATORS: If this string takes more than four or five characters in #. * your language, please use the WHITE CIRCLE U+25CB unicode character #. * instead #: mx/mx-gtk-light-switch.c:271 #: mx/mx-gtk-light-switch.c:324 msgid "Off" msgstr "Kapalı" #: mx/mx-utils.c:93 msgid "Less than a minute ago" msgstr "Bir dakikadan önce" #: mx/mx-utils.c:97 msgid "A few minutes ago" msgstr "Bir kaç dakika önce" #: mx/mx-utils.c:101 msgid "A couple of hours ago" msgstr "Bir kaç saat önce" #: mx/mx-utils.c:108 msgid "Earlier today" msgstr "Bugün erken saatlerde" #: mx/mx-utils.c:111 msgid "Yesterday" msgstr "Dün" #: mx/mx-utils.c:119 #, c-format msgid "On %A" msgstr "%A da" #: mx/mx-utils.c:125 msgid "Unknown" msgstr "Bilinmiyor" #: mx/mx-utils.c:132 msgid "Last week" msgstr "Geçen hafta" #: mx/mx-utils.c:135 msgid "A couple of weeks ago" msgstr "Bir kaç hafta önce" #: mx/mx-utils.c:141 msgid "This month" msgstr "Bu ay" #. Now Jan., last used in Dec. #: mx/mx-utils.c:145 msgid "Last month" msgstr "Geçen ay" #: mx/mx-utils.c:148 msgid "This year" msgstr "Bu yıl" #: mx/mx-utils.c:151 msgid "Last year" msgstr "Geçen yıl" #: mx/mx-utils.c:153 msgid "Ages ago" msgstr "Asırlar önce" mx-1.4.7/po/uk.po000066400000000000000000000033211201047117600135420ustar00rootroot00000000000000msgid "" msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2010-02-26 12:45+0000\n" "PO-Revision-Date: \n" "Last-Translator: Oleksii Baranov \n" "Language-Team: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" #: mx/mx-gtk-light-switch.c:232 msgid "Unavailable" msgstr "Недоступно" #. TRANSLATORS: If this string takes more than four or five characters in #. * your language, please use the MEDIUM VERTICAL BAR unicode character #. * instead #: mx/mx-gtk-light-switch.c:252 mx/mx-gtk-light-switch.c:326 msgid "On" msgstr "Включено" #. Draw the second label; "Off" #. TRANSLATORS: If this string takes more than four or five characters in #. * your language, please use the WHITE CIRCLE U+25CB unicode character #. * instead #: mx/mx-gtk-light-switch.c:271 mx/mx-gtk-light-switch.c:324 msgid "Off" msgstr "Вимкнено" #: mx/mx-utils.c:93 msgid "Less than a minute ago" msgstr "" #: mx/mx-utils.c:97 msgid "A few minutes ago" msgstr "" #: mx/mx-utils.c:101 msgid "A couple of hours ago" msgstr "" #: mx/mx-utils.c:108 msgid "Earlier today" msgstr "" #: mx/mx-utils.c:111 msgid "Yesterday" msgstr "" #: mx/mx-utils.c:119 #, c-format msgid "On %A" msgstr "" #: mx/mx-utils.c:125 msgid "Unknown" msgstr "" #: mx/mx-utils.c:132 msgid "Last week" msgstr "" #: mx/mx-utils.c:135 msgid "A couple of weeks ago" msgstr "" #: mx/mx-utils.c:141 msgid "This month" msgstr "" #. Now Jan., last used in Dec. #: mx/mx-utils.c:145 msgid "Last month" msgstr "" #: mx/mx-utils.c:148 msgid "This year" msgstr "" #: mx/mx-utils.c:151 msgid "Last year" msgstr "" #: mx/mx-utils.c:153 msgid "Ages ago" msgstr "" mx-1.4.7/po/wa.po000066400000000000000000000035401201047117600135350ustar00rootroot00000000000000# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: MeeGo 1.0\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2010-05-03 09:40+0000\n" "PO-Revision-Date: 2010-05-04 15:27+0100\n" "Last-Translator: Jean Cayron \n" "Language-Team: Walloon \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n!=1;\n" "X-Poedit-Language: Walloon\n" "X-Poedit-SourceCharset: utf-8\n" #: ../mx/mx-application.c:311 msgid "Raise application" msgstr "Monter programe" #: ../mx-gtk/mx-gtk-light-switch.c:96 msgid "Unavailable" msgstr "Nén disponibe" #: ../mx/mx-utils.c:94 msgid "Less than a minute ago" msgstr "I gn a moens d' ene munute" #: ../mx/mx-utils.c:98 msgid "A few minutes ago" msgstr "I gn a kékès munutes" #: ../mx/mx-utils.c:102 msgid "A couple of hours ago" msgstr "I gn a deus eures" #: ../mx/mx-utils.c:109 msgid "Earlier today" msgstr "Pus timpe ouy" #: ../mx/mx-utils.c:112 msgid "Yesterday" msgstr "Ayir" #: ../mx/mx-utils.c:120 #, c-format msgid "On %A" msgstr "%A" #: ../mx/mx-utils.c:126 msgid "Unknown" msgstr "Nén cnoxhou" #: ../mx/mx-utils.c:133 msgid "Last week" msgstr "Li samwinne passêye" #: ../mx/mx-utils.c:136 msgid "A couple of weeks ago" msgstr "I gn a deus samwinnes" #: ../mx/mx-utils.c:142 msgid "This month" msgstr "Ci moes ci" #. Now Jan., last used in Dec. #: ../mx/mx-utils.c:146 msgid "Last month" msgstr "Li moes passé" #: ../mx/mx-utils.c:149 msgid "This year" msgstr "Ciste anêye" #: ../mx/mx-utils.c:152 msgid "Last year" msgstr "L' anêye passêye" #: ../mx/mx-utils.c:154 msgid "Ages ago" msgstr "I gn a des ans et des razans" mx-1.4.7/po/zh_CN.po000066400000000000000000000032041201047117600141240ustar00rootroot00000000000000# MX # Copyright Intel(C) 2009 # This file is distributed under the same license as the PACKAGE package. # Zhu Yanhai , 2009. # msgid "" msgstr "" "Project-Id-Version: MX\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2010-04-05 09:39+0000\n" "PO-Revision-Date: 2009-07-25 10:33+0800\n" "Last-Translator: Zhu Yanhai \n" "Language-Team: zh_CN\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" #: ../mx-gtk/mx-gtk-light-switch.c:96 msgid "Unavailable" msgstr "不可用" #: ../mx/mx-utils.c:94 msgid "Less than a minute ago" msgstr "一分钟内" #: ../mx/mx-utils.c:98 msgid "A few minutes ago" msgstr "几分钟前" #: ../mx/mx-utils.c:102 msgid "A couple of hours ago" msgstr "两小时前" #: ../mx/mx-utils.c:109 msgid "Earlier today" msgstr "更早时候" #: ../mx/mx-utils.c:112 msgid "Yesterday" msgstr "昨天" #: ../mx/mx-utils.c:120 #, c-format msgid "On %A" msgstr "在 %A" #: ../mx/mx-utils.c:126 msgid "Unknown" msgstr "未知" #: ../mx/mx-utils.c:133 msgid "Last week" msgstr "上周" #: ../mx/mx-utils.c:142 msgid "This month" msgstr "本月" #. Now Jan., last used in Dec. #: ../mx/mx-utils.c:146 msgid "Last month" msgstr "上月" #: ../mx/mx-utils.c:149 msgid "This year" msgstr "今年" #: ../mx/mx-utils.c:152 msgid "Last year" msgstr "去年" #: ../mx/mx-utils.c:154 msgid "Ages ago" msgstr "几年前" #: ../mx/mx-application.c:311 msgid "Raise application" msgstr "提出申请" #: ../mx/mx-utils.c:136 msgid "A couple of weeks ago" msgstr "几周前" #~ msgid "On" #~ msgstr "开" #~ msgid "Off" #~ msgstr "关" mx-1.4.7/po/zh_TW.po000066400000000000000000000030561201047117600141630ustar00rootroot00000000000000msgid "" msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2010-03-17 09:40+0000\n" "PO-Revision-Date: \n" "Last-Translator: Cheng-Chia Tseng \n" "Language-Team: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Poedit-Language: Chinese\n" "X-Poedit-SourceCharset: utf-8\n" #: ../mx/mx-application.c:311 msgid "Raise application" msgstr "抬升應用程式" #: ../mx/mx-gtk-light-switch.c:96 msgid "Unavailable" msgstr "不可用" #: ../mx/mx-utils.c:94 msgid "Less than a minute ago" msgstr "少於一分鐘時" #: ../mx/mx-utils.c:98 msgid "A few minutes ago" msgstr "幾分鐘前" #: ../mx/mx-utils.c:102 msgid "A couple of hours ago" msgstr "幾小時前" #: ../mx/mx-utils.c:109 msgid "Earlier today" msgstr "今日早些時候" #: ../mx/mx-utils.c:112 msgid "Yesterday" msgstr "昨天" #: ../mx/mx-utils.c:120 #, c-format msgid "On %A" msgstr "在 %A" #: ../mx/mx-utils.c:126 msgid "Unknown" msgstr "不明" #: ../mx/mx-utils.c:133 msgid "Last week" msgstr "上禮拜" #: ../mx/mx-utils.c:136 msgid "A couple of weeks ago" msgstr "幾週以前" #: ../mx/mx-utils.c:142 msgid "This month" msgstr "這個月" #. Now Jan., last used in Dec. #: ../mx/mx-utils.c:146 msgid "Last month" msgstr "上個月" #: ../mx/mx-utils.c:149 msgid "This year" msgstr "今年" #: ../mx/mx-utils.c:152 msgid "Last year" msgstr "去年" #: ../mx/mx-utils.c:154 msgid "Ages ago" msgstr "好幾年前" #~ msgid "On" #~ msgstr "啟動" #~ msgid "Off" #~ msgstr "關閉" mx-1.4.7/tests/000077500000000000000000000000001201047117600133105ustar00rootroot00000000000000mx-1.4.7/tests/Makefile.am000066400000000000000000000015361201047117600153510ustar00rootroot00000000000000NULL = AM_CFLAGS = $(MX_CFLAGS) $(MX_MAINTAINER_CFLAGS) LDADD = $(top_builddir)/mx/libmx-$(MX_API_VERSION).la $(MX_LIBS) if ENABLE_GTK_WIDGETS AM_CFLAGS += $(GTK_CFLAGS) LDADD += $(top_builddir)/mx-gtk/libmx-gtk-$(MX_API_VERSION).la $(GTK_LIBS) endif INCLUDES = \ -I$(top_srcdir) \ -I$(top_builddir) noinst_PROGRAMS = \ test-deform-texture \ test-draggable \ test-droppable \ test-window \ test-widgets \ test-containers \ $(NULL) if ENABLE_GTK_WIDGETS noinst_PROGRAMS += test-gtk test_gtk_SOURCES = test-gtk.c endif test_widgets_SOURCES = test-widgets.c test_containers_SOURCES = test-containers.c test_deform_texture_SOURCES = test-deform-texture.c test_draggable_SOURCES = test-draggable.c test_droppable_SOURCES = test-droppable.c test_window_SOURCES = test-window.c EXTRA_DIST = redhand.png -include $(top_srcdir)/git.mk mx-1.4.7/tests/edit-clear-highlight.png000066400000000000000000000015311201047117600177740ustar00rootroot00000000000000PNG  IHDRw=sRGBbKGD pHYs B(xtIME  &{IDATHǽkkpiWm6XUQ苁_D>EAQt32acI4c*Á$<Ϲ>GnWG] hDE XQ,K@0Hn`gP_7> ˀe p 6p|̓(-`8/$5&K $z|XS|bR*-i LD}l25efv'z&S\kVqckxr<{=kfUoj*Au:-i@J>gy<3sn޼1w~**)Νu+C *W0V3eZZb΋G.$mպwOW=Ol[67uö}I.K2-L\N[z+ TZ\.IŷٙHmi!T@k5SF++wv0z=f")I~i JAӖaF@9Nx6FGxz˗X~2/*5-y@_pqcC3 M'Op?g`ݿ*?E0L*ϩrƖi?DKKCcw$JN*Rd,˜,"E^=NENWO9+_^|weuMsIENDB`mx-1.4.7/tests/edit-clear.png000066400000000000000000000006771201047117600160410ustar00rootroot00000000000000PNG  IHDRw=sBIT|d pHYs B(xtEXtSoftwarewww.inkscape.org<еbju IENDB`mx-1.4.7/tests/edit-find.png000066400000000000000000000022501201047117600156600ustar00rootroot00000000000000PNG  IHDRbKGDC pHYsHHFk> vpAgxLKIDATX՗KHQǿ;q$B{8."L)=( =)EI*EE1hAHK3 Br=af$ʴ9$8Hg|{?MMDY`--#6Wӹk]$a)/!H7,mm ơ+( "D}[}Q>֩SS缩ĪEľ>bGB1SB`j1f1p'ڕOlGy1g .~Y>OL71D˼#_]%nrA%/֪~!'f X7!gDnpl~%6]/rU$V\@I'/wW 2qe:؜f.sF.M=^,ز\{H[^X~A M`Ed@@3dkOBpf}~#mDj2/xE^ށ};BNVVž_ۉ kX:ޭ)B2NNM<%Ēy=L$DLqKq!~,Ac'ѲdDcs{{@ s98 ͅƣ| 54?#F9zTțUh7o R.zLH9L=8=M>B 3Y?)={K9>&f5Ē1NΊzeÕpB=N]E4܉KU>;QF HWi^.zTXtcreate-datex3205052 1426256020B ..zTXtmodify-datex3205052 1426256020B ={hzTXtsvg:base-urix10 t :2`c#%+-q8AQ[[OrS?qI"uQ{IENDB`mx-1.4.7/tests/javascript/000077500000000000000000000000001201047117600154565ustar00rootroot00000000000000mx-1.4.7/tests/javascript/buttons.js000077500000000000000000000027221201047117600175200ustar00rootroot00000000000000#!/usr/bin/env gjs /* * Copyright 2010 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * Boston, MA 02111-1307, USA. * */ const Clutter = imports.gi.Clutter; const Mx = imports.gi.Mx; Clutter.init (null); Mx.Style.get_default ().load_from_file ("tests.css"); let script = new Clutter.Script (); script.load_from_file ("buttons.json", -1); let stage = script.get_object ("stage"); stage.show (); stage.connect ("destroy", Clutter.main_quit); let button = script.get_object ("action-button"); let action = button.action; let i = 0; action.connect ("activated", function (a) {a.display_name = "Click " + ++i; a.icon = "dialog-information";}) button = script.get_object ("custom-content"); button.connect ("clicked", function (b) { b.label = "Clicked"; }); Clutter.main (); mx-1.4.7/tests/javascript/buttons.json000066400000000000000000000152701201047117600200540ustar00rootroot00000000000000[ { "id" : "action-1", "type" : "MxAction", "display-name" : "Action Test", "icon" : "go-home" }, { "id" : "action-2", "type" : "MxAction", "display-name" : "Icon Only", "icon" : "help-about" }, { "id" : "action-3", "type" : "MxAction", "display-name" : "Text Only", "icon" : "dialog-warning" }, { "id" : "stage", "type" : "ClutterStage", "user-resizable" : true, "title" : "Button Test", "children" : [ { "type" : "MxTable", "constraints" : [ { "type" : "ClutterBindConstraint", "source" : "stage", "coordinate" : "width" }, { "type" : "ClutterBindConstraint", "source" : "stage", "coordinate" : "height" } ], "children" : [ { "type" : "MxLabel", "text" : "Plain Button", "child::column" : 0, "child::row" : 0, "child::x-expand" : false, "child::y-fill" : false, "child::y-align" : "middle" }, { "type" : "MxLabel", "text" : "Toggle Button", "child::column" : 0, "child::row" : 1, "child::x-expand" : false, "child::y-fill" : false, "child::y-align" : "middle" }, { "type" : "MxLabel", "text" : "Action Test (click to test)", "child::column" : 0, "child::row" : 2, "child::x-expand" : false, "child::y-fill" : false, "child::y-align" : "middle" }, { "type" : "MxLabel", "text" : "Left icon position", "child::column" : 0, "child::row" : 3, "child::x-expand" : false, "child::y-fill" : false, "child::y-align" : "middle" }, { "type" : "MxLabel", "text" : "Top icon position", "child::column" : 0, "child::row" : 4, "child::x-expand" : false, "child::y-fill" : false, "child::y-align" : "middle" }, { "type" : "MxLabel", "text" : "Bottom icon position", "child::column" : 0, "child::row" : 5, "child::x-expand" : false, "child::y-fill" : false, "child::y-align" : "middle" }, { "type" : "MxLabel", "text" : "Right icon position", "child::column" : 0, "child::row" : 6, "child::x-expand" : false, "child::y-fill" : false, "child::y-align" : "middle" }, { "type" : "MxLabel", "text" : "Icon only", "child::column" : 0, "child::row" : 7, "child::x-expand" : false, "child::y-fill" : false, "child::y-align" : "middle" }, { "type" : "MxLabel", "text" : "Text only", "child::column" : 0, "child::row" : 8, "child::x-expand" : false, "child::y-fill" : false, "child::y-align" : "middle" }, { "type" : "MxLabel", "text" : "Custom content", "child::column" : 0, "child::row" : 9, "child::x-expand" : false, "child::y-fill" : false, "child::y-align" : "middle" }, { "type" : "MxLabel", "text" : "Custom icon and size", "child::column" : 0, "child::row" : 10, "child::x-expand" : false, "child::y-fill" : false, "child::y-align" : "middle" }, { "type" : "MxButton", "label" : "Plain", "child::column" : 1, "child::row" : 0, "child::x-expand" : false }, { "type" : "MxButton", "label" : "Toggle", "is-toggle" : true, "toggled" : true, "child::column" : 1, "child::row" : 1, "child::x-expand" : false }, { "type" : "MxButton", "action" : "action-1", "id" : "action-button", "child::column" : 1, "child::row" : 2, "child::x-expand" : false }, { "type" : "MxButton", "name" : "left", "label" : "Left", "icon-position" : "left", "child::column" : 1, "child::row" : 3, "child::x-expand" : false }, { "type" : "MxButton", "name" : "top", "label" : "Top", "icon-position" : "top", "child::column" : 1, "child::row" : 4, "child::x-expand" : false }, { "type" : "MxButton", "name" : "bottom", "label" : "Bottom", "icon-position" : "bottom", "child::column" : 1, "child::row" : 5, "child::x-expand" : false }, { "type" : "MxButton", "name" : "right", "label" : "Right", "icon-position" : "right", "child::column" : 1, "child::row" : 6, "child::x-expand" : false }, { "type" : "MxButton", "action" : "action-2", "label-visible" : false, "child::column" : 1, "child::row" : 7, "child::x-expand" : false }, { "type" : "MxButton", "action" : "action-3", "icon-visible" : false, "child::column" : 1, "child::row" : 8, "child::x-expand" : false }, { "id" : "custom-content", "type" : "MxButton", "children" : [ { "type" : "ClutterRectangle", "color" : "red", "width" : 16, "height" : 16 } ], "child::column" : 1, "child::row" : 9, "child::x-expand" : false }, { "id" : "custom-icon", "type" : "MxButton", "name" : "left", "icon-name" : "go-home", "icon-size" : 32, "label" : "Large home icon", "child::column" : 1, "child::row" : 10, "child::x-expand" : false } ] } ] } ] mx-1.4.7/tests/javascript/slider.js000077500000000000000000000025331201047117600173040ustar00rootroot00000000000000#!/usr/bin/env gjs /* * Copyright 2009 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * Boston, MA 02111-1307, USA. * */ const Clutter = imports.gi.Clutter; const Mx = imports.gi.Mx; Clutter.init (null); Mx.Style.get_default ().load_from_file ("tests.css"); let script = new Clutter.Script (); script.load_from_file ("slider.json"); let stage = script.get_object ("stage"); stage.connect ("destroy", Clutter.main_quit); let slider = script.get_object ("slider"); let manager = Mx.FocusManager.get_for_stage (stage); manager.push_focus (slider); script.get_object ("reset").connect ("clicked", function (b) { slider.value = 0 }) stage.show (); Clutter.main (); mx-1.4.7/tests/javascript/slider.json000066400000000000000000000004331201047117600176330ustar00rootroot00000000000000[ { "type": "ClutterStage", "id": "stage", "children": [ { "type": "MxBoxLayout", "children": [ { "type": "MxSlider", "id": "slider", "width": 200 }, { "type": "MxButton", "label": "Reset", "id":"reset" } ] } ] } ] mx-1.4.7/tests/javascript/test-actions.js000077500000000000000000000037121201047117600204370ustar00rootroot00000000000000#!/usr/bin/env gjs /* * Copyright 2009 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * Boston, MA 02111-1307, USA. * */ const Clutter = imports.gi.Clutter; const Mx = imports.gi.Mx; function add_button (action, container, group) { let button = new Mx.Button ({label: action.display_name }); button.toggle_mode = true; container.add_actor (button); group.add (button); button.connect ("clicked", function (b) { action.emit ("activated"); }); return button; } function connect_cb (text) { let app = new Mx.Application ({flags: 1, "application-name": text.text}); if (!app.is_running ()) return; let stage = new Clutter.Stage (); stage.title = text.text; let group = new Mx.ButtonGroup ({allow_no_active: true}); let grid = new Mx.Grid (); let actions = app.get_actions (); for (let a = 0; a < actions.length; a++) add_button (actions[a], grid, group); grid.set_size (stage.width, stage.height); stage.add_actor (grid); stage.show (); } Clutter.init (null); let main_stage = Clutter.Stage.get_default (); main_stage.title = "Test Mx Actions"; let entry = new Mx.Entry ({text: "TestMx"}); entry.set_width (main_stage.width); main_stage.add_actor (entry); entry.get_clutter_text ().connect ("activate", connect_cb); entry.grab_key_focus (); main_stage.show (); Clutter.main (); mx-1.4.7/tests/javascript/test-button-group.js000077500000000000000000000037061201047117600214470ustar00rootroot00000000000000#!/usr/bin/env gjs /* * Copyright 2009 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * Boston, MA 02111-1307, USA. * */ const Clutter = imports.gi.Clutter; const Mx = imports.gi.Mx; function add_button (name, box, group) { let button = new Mx.Button ({label: name }) button.toggle_mode = true; box.add_actor (button); group.add (button) return button; } Clutter.init (null); let stage = Clutter.Stage.get_default (); stage.title = "Test Toolkit" let group = new Mx.ButtonGroup ({allow_no_active: true}) let box = new Mx.BoxLayout () stage.add_actor (box) add_button ("Entry", box, group) add_button ("Table", box, group) add_button ("Scroll View", box, group) add_button ("Progress Bar", box, group) add_button ("Drag and Drop", box, group) let button1 = add_button ("Expander", box, group) let button2 = add_button ("Combo Box", box, group) group.connect ("notify::active-button", function (g, p) { if (g.active_button) stage.title = g.active_button.label else stage.title = "" } ) stage.connect ("button-press-event", function (s, e) { group.remove (button1)}); stage.connect ("key-press-event", function (s, e) { button2.destroy() }); stage.show (); Clutter.main (); stage.destroy (); mx-1.4.7/tests/javascript/test-combo-box.js000077500000000000000000000024651201047117600206700ustar00rootroot00000000000000#!/usr/bin/env gjs /* * Copyright 2009 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * Boston, MA 02111-1307, USA. * */ const Clutter = imports.gi.Clutter; const Mx = imports.gi.Mx; Clutter.init (null); let stage = Clutter.Stage.get_default (); stage.title = "Test Combo Box"; let combo = new Mx.ComboBox (); combo.set_position (10, 480 - 20); stage.add_actor (combo); combo.title = "London"; combo.append_text ("Strand"); combo.append_text ("Fleet Street"); combo.append_text ("Trafalgar Square"); combo.append_text ("Leicester Square"); combo.append_text ("Coventry Street"); combo.append_text ("Piccadilly"); stage.show (); Clutter.main (); stage.destroy (); mx-1.4.7/tests/javascript/test-deformations.js000077500000000000000000000030251201047117600214660ustar00rootroot00000000000000#!/usr/bin/env gjs /* * Copyright 2010 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * Boston, MA 02111-1307, USA. * */ const Clutter = imports.gi.Clutter; const Mx = imports.gi.Mx; const grey = new Clutter.Color(); grey.from_string ("grey"); Clutter.init (null); let app = new Mx.Application ({"application-name": "Test deformations"}); let win = app.create_window (); let stage = win.get_clutter_stage (); stage.set_color (grey); stage.set_size (400, 500); let deform = new Mx.DeformPageTurn (); deform.set_actors (new Mx.Button ({label: "Front"}), new Mx.Button ({label: "Back"})); //deform.set_from_files ("redhand.png", "redhand.png"); deform.angle = Math.PI/5; deform.radius = 32; deform.animatev (Clutter.AnimationMode.LINEAR, 10000, 1, ["period"], [1.0]); win.set_child (deform); stage.show (); app.run (); mx-1.4.7/tests/javascript/test-icon-theme.js000077500000000000000000000032341201047117600210260ustar00rootroot00000000000000#!/usr/bin/env gjs /* * Copyright 2010 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * Boston, MA 02111-1307, USA. * */ const Clutter = imports.gi.Clutter; const Mx = imports.gi.Mx; Clutter.init (null); let stage = Clutter.Stage.get_default (); stage.title = "Test Icon Theme" let vbox = new Mx.BoxLayout ( {orientation: Mx.Orientation.VERTICAL} ); stage.add_actor (vbox); vbox.set_size (stage.width, stage.height); let entry = new Mx.Entry (); vbox.add_actor (entry, -1); entry.grab_key_focus (); let theme = Mx.IconTheme.get_default (); let texture = null; entry.get_clutter_text ().connect ("activate", function (e) { if (texture) vbox.remove_actor (texture); texture = theme.lookup_texture (entry.text, 48); if (texture) { //texture.keep_aspect_ratio = true; vbox.add_actor (texture, -1); vbox.child_set_property (texture, "x-fill", false); vbox.child_set_property (texture, "y-fill", false); } }); stage.show (); Clutter.main (); stage.destroy (); mx-1.4.7/tests/javascript/test-notebook.js000077500000000000000000000030751201047117600206210ustar00rootroot00000000000000#!/usr/bin/env gjs /* * Copyright 2009 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * Boston, MA 02111-1307, USA. * */ const Clutter = imports.gi.Clutter; const Mx = imports.gi.Mx; Clutter.init (null); let stage = Clutter.Stage.get_default (); stage.title = "Test Notebook"; let notebook = new Mx.Notebook (); stage.add_actor (notebook); let buttons = new Array (); buttons[0] = new Mx.Button ({label: "Paddington"}); notebook.add_actor (buttons[0]); buttons[1] = new Mx.Button ({label: "Aldgate"}); notebook.add_actor (buttons[1]); buttons[2] = new Mx.Button ({label: "Baker Street"}); notebook.add_actor (buttons[2]); let page_no = 0; stage.connect ("button-release-event", function (o, e) { if (++page_no >= buttons.length) page_no = 0; notebook.current_page = buttons[page_no]; }); stage.show (); Clutter.main (); stage.destroy (); mx-1.4.7/tests/javascript/test-path-bar.js000077500000000000000000000050741201047117600205000ustar00rootroot00000000000000#!/usr/bin/env gjs /* * Copyright 2010 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * Boston, MA 02111-1307, USA. * */ const Clutter = imports.gi.Clutter; const Mx = imports.gi.Mx; Clutter.init (null); let stage = Clutter.Stage.get_default (); stage.title = "Test Path Bar" let focus_man = Mx.FocusManager.get_for_stage (stage); let vbox = new Mx.BoxLayout ({orientation: Mx.Orientation.VERTICAL}); stage.add_actor (vbox); vbox.set_width (stage.width); let bar = new Mx.PathBar ({"clear-on-change": true}); vbox.add_actor (bar, -1); vbox.child_set_property (bar, "x-align", 0); vbox.child_set_property (bar, "x-fill", false); let hbox = new Mx.BoxLayout (); vbox.add_actor (hbox, -1); let push_button = new Mx.Button ( {label: "Add crumb"} ) hbox.add_actor (push_button, -1); let pop_button = new Mx.Button ( {label: "Remove crumb"} ); hbox.add_actor (pop_button, -1); let editable_button = new Mx.Button ( {label: "Toggle editable"} ); hbox.add_actor (editable_button, -1); let rename_button = new Mx.Button ( {label: "Re-label 1st button"} ); hbox.add_actor (rename_button, -1); push_button.connect ("clicked", function (b) { bar.push ("Crumb" + (bar.level + 1)); }); pop_button.connect ("clicked", function (b) { bar.pop (); }); editable_button.connect ("clicked", function (b) { bar.editable = !bar.editable; vbox.child_set_property (bar, "expand", bar.editable); vbox.child_set_property (bar, "x-fill", bar.editable); }); rename_button.connect ("clicked", function (b) { if (bar.editable) bar.set_label (1, bar.entry.text); }); stage.show (); Clutter.main (); stage.destroy (); mx-1.4.7/tests/javascript/test-popup.js000077500000000000000000000025571201047117600201500ustar00rootroot00000000000000#!/usr/bin/env gjs /* * Copyright 2009 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * Boston, MA 02111-1307, USA. * */ const Clutter = imports.gi.Clutter; const Mx = imports.gi.Mx; Clutter.init (null); let stage = Clutter.Stage.get_default (); stage.title = "Test Widget Popup" let button = new Mx.Button ( {label: "Click me!"} ) stage.add_actor (button); button.set_position (100, 50); let popup = new Mx.Popup (); let action = new Mx.Action ({name: "action1", "display-name": "A pop-up!"}); popup.add_action (action); button.set_popup (popup); button.connect ("clicked", function (b) { button.show_popup (0, 0); }); stage.show (); Clutter.main (); stage.destroy (); mx-1.4.7/tests/javascript/test-small-screen.js000077500000000000000000000032771201047117600213720ustar00rootroot00000000000000#!/usr/bin/env gjs /* * Copyright 2010 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * Boston, MA 02111-1307, USA. * */ const Clutter = imports.gi.Clutter; const Mx = imports.gi.Mx; let [app] = Mx.Application.new (0, null, "Test Small Screen", 0); let win = app.create_window (); let stage = win.get_clutter_stage (); let hbox = new Mx.BoxLayout ({spacing: 8}); let toggle = new Mx.Toggle (); let label = new Mx.Label ({text: "Small-screen mode"}); toggle.active = stage.small_screen; toggle.connect ("notify::active", function (t) { stage.small_screen = toggle.active; }); hbox.add_actor (toggle); hbox.add_actor (label); win.set_child (hbox); hbox.child_set_property (toggle, "expand", true); hbox.child_set_property (toggle, "x-fill", false); // Mx.Align.end == 2; // FIXME: Need to add the correct introspection data for this? hbox.child_set_property (toggle, "x-align", 2); hbox.child_set_property (label, "y-fill", false); hbox.child_set_property (label, "expand", true); stage.show (); app.run (); mx-1.4.7/tests/javascript/test-toggle-switch.js000077500000000000000000000022201201047117600215500ustar00rootroot00000000000000#!/usr/bin/env gjs /* * Copyright 2009 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * Boston, MA 02111-1307, USA. * */ const Clutter = imports.gi.Clutter; const Mx = imports.gi.Mx; Clutter.init (null); let stage = Clutter.Stage.get_default (); stage.title = "Test Toggle"; let toggle = new Mx.Toggle (); toggle.set_position (10, 10); stage.add_actor (toggle); stage.connect ("key-press-event", function (o,e) {toggle.active = !toggle.active} ); stage.show (); Clutter.main (); stage.destroy (); mx-1.4.7/tests/javascript/tests.css000066400000000000000000000005571201047117600173410ustar00rootroot00000000000000MxSlider *#fill { background-color: yellow; } MxSlider *#trough { background-color: black; } MxButton { -mx-icon-size: 16; } MxButton#left { -mx-icon-name: "go-previous"; } MxButton#top { -mx-icon-name: "go-top"; } MxButton#bottom { -mx-icon-name: "go-bottom"; } MxButton#right { -mx-icon-name: "go-next"; } MxImage { background-color: blue; } mx-1.4.7/tests/redhand.png000066400000000000000000000200721201047117600154240ustar00rootroot00000000000000PNG  IHDRbKGD pHYs  tIME 3x<,IDATx]y|M~>( "HQV> Q)UU:hPzRJu]\PBiBEDD99Z9kwg=Z888888888888888888888888D),rU0H#ۜ#Tىm!8Ad' ` (@9cɂ 4$ p.w Y) $m"!Eچ.RT 3 49m0L)c&f=(cl+TRJRJ/e-JcPJ#VboL)}RYp'l0t0;]X Lfse(4VuC]e J)SDؐmC)N)-۸6?! [R:ֆ^l*xE%#wo&&+b}i)JFuZ ` aa+<\0N<5uWT@m:t@P>M芋Qv~ 0Cpu٪EeB:8:A.BK-۸W`v2ϖ-vDN M^KKݠz }=s&&u156v- l0Dھ4^^6O x1J~ζqwGOHر$$t !dBJSRpaLzγuktZÆ:3CMw?J:l:/^ m|^f=H3y6z…h9tlu1{Fm i"KXn*b)ANJwe:\0AZ|ot7n|c.Ńpv_ԀE;wZɢEۆR\ۺ6דE-ÑҔW2QmӺPq٦637lZCEΉ 0AWµz%"%lnb9/GDRKca+.bi9R4ټ.TuiY^@-s +W \ApuvApipU],L],s +Ws +W \Ap \Ap +W . TNJ^Z v]ލX[ S}=P @􄛏. .AX}yC+A8x cp +W vBJhPrWSXUgQaKvѣ;wX ׯa(;reRW֮qMouuq3V(ؼ`= @WRf<3gx RG>\s^O0Jqu2Ǐ󞼛ۥ!sB{ _c O2EA4h||  D3F6264^GqӮ5d ҿ!t Ovh֩uxm Z-A[v)5@u:XX}֬m3A68!!d ̞ ]QdT?d1h4>C؜w#gX"ƍCsϡ>?׶l-[k(6rUwfwDz _}E?eP061Bj:bOfqqDj6jBG=^8䷻K qHX:ur=VawNkxic 4.Wx8]Ĥ$:>:|88)S xxpK8E˸1iՙH??͝>w,gXz}=̓Sw_.u+:&;w!߿]j!fL$&%eK>]ٳiɁ v'VD7ް+7C-͛ǹ pGϤ$9/D<qehw40憸3kVNcQp&-ZmyNt>OO\7O3)<Q~~HܴBD2O 1{wދ3- Ɛ vI'ӵZ$Xx l ܯ|rⷄիp#$Vpr?>13XDA_3,l1nZ],Gw<,B|h'A@ŋ pTKۼ9by9s t,:k>z4lq ~I`hSviT>J{o%5/t%GV._v0 u99RJDj rĀb }yWjxAkkjԔD< R)8NEco 6*)E4NՁJJ# TA:Aj)j1$_1iR[gh2y<+lljF4XM6?||NΜ.K$HCa" VԢԫbWCHgEgQgUEa[#X vv_)KRPX[ =GZ HQTCcQ'(u[tŋ5O8|wX>OV?XFwTCvZ/*eF~qDRJ*RSq7we/n&)VRE/ A0Jk|ˡC[ 5xy)VdiJU]aQ/OwMQtY#0ŗK~ c|Ʈ]3, ݖ-y"Ed%Q/j%o鸾cw< :#-[YG]vR]s7DԔ)h+vG$~*+n a lwg^ $UmD9zmx6VWO?aH}oL ۷\bfB>jƢϏ??!A oWD b@jU'h}m3֒O]i^3W.:.:i5rZScj LLD={9q7bBeˬZ\߱Ckޜ d/o.]$ULw 6oV=@hݹ-~&hat?xP_L ":H1c$W.grȦ߭w.?dq|cb{Yz[j)Q:<.Q1 hva,/[CUJCiJ ʏEcQ݉L4Cp<ݻJf4S92(1#!DA6j7mTÆ)zm 5 R_G}~>srTܚ5gVn׮FY_@ƍ>cJ! Qjpxß~/)t8t}0TVި_D܂bjA?(ꋘy+v^۲ nwEd̟/;9BH-dzE7Ғ%uT**pbh4+~N~ [˟+9I,Nhd;SݔҖo}fƐ1o2,*SUׯctD3IVb5A*2Gʳ//$\)֜q~tlvFȑ]T$c$qalYܤC a*V *OƩ瞃X:40i'gh:B2%^Ba Bڴie#G1~<í}wъĬ"kL򬩡#GYYA &0VW㷷FmN>"'L@W_l c Rڌr _ǯh((NO=N qV[Y2uJݳf[="}t(!Drݚt CUlulqs"tpTcu5.})~K-R qfJM1\2E b.x3g>_~(N-CFG;MQ8Î\Ih}w$,Q fW$WrN=쇤4h;q"^z -Z8(3*Ϝ%K\z}w풲30!d1/!jQ̤I6I 6>^gǎ!{R8w܄n˗OJvA0)N3I^$Xò4%ƏYXZ?$"'NOt](TGiJ rWDّ# pG T f~+YSSqz8+*lVO"?8iUWnߎk?<΂ q&)7pIL5=Xڬ,3W//48-~!<VQףEߏw*-Q"1nO|72O5I҆r Ut%%8=a*S迧 zkWxGFB4h<}Ǐ)4.5XG}>62WUIYE ͮooxGF«MxiPh' ``24>/ h((3Ȗ 13!$@ﺶe .̞ cM M C e%K6 \`M`9O>~{"gO>28Ɨ qQ|MScAv'93f~e An"9H"h1E`^|09D" 4}w Uh>pqڍb  D^p'縺aO0*A$;~IW}_!_ 1ßŀ#G1nbpr&H!kQڅQ}^| / ;!qj*o:M1ďMm}E򓒐Դeopxݿ<"O!3 7d IrɄ_~Aߢx^氹9s.gnsE2"/ cM Cю(=|bAVs"rD8EY]p"t8~%QԤDtvht^Y},!$ɡ r𪛱UgϢ*- 騹pOs;_Fѣل(#ljףh((iny9Luuz}jh<= ot4~]u."p(ܤ&X+κs4B,8b )] !mP!_v,j ' AӽٗxsX`r8,A?n#$x.x1TSD99k !;n&\yXG$KqfPJ#<+r]RRBsUq dx|8AĹ_]0@;8BT3 !ܟ3)420&hJsXsWե?nf$ cs 6M Ncx3a"54+ rR SpքpRDsVIENDB`mx-1.4.7/tests/rounded-corner.png000066400000000000000000000047001201047117600167450ustar00rootroot00000000000000PNG  IHDRp`6sBIT|d pHYs B(xtEXtSoftwarewww.inkscape.org< =IDATxmLT;PV0H"m4jm\Vjkbv+n٤$jK\hh6itJ4i5eh. dqQ`{Quw|fVlN8if~̞}GْwdNϳ~CO<(3;bf4j] ' 9s8;;;|?b7(bfO(- PU\__X>ΞVz&>`f0A HDo8(W 5<H^ٳg{Icc{) P]~MĒ7%%Fnﮩ뫌N}9LlnnƊ+fNzLDSܹ % ǩ1|Yu`SSS4=,IPlݺ5@-`#me۷#1XAFdee.3;a`) Q2^W>1sa`|X@"Ϙ1c2lQJFeڵ߯>ແͰr^~}}cAzzǽ0tCD6,Zǣ%g%gΜ1 QQQ?vp(Ǐ;tP0zpVTT;8I48~Ypa\>"} Һm۶)P!o[%̬;"IH޸qZ]]u ve1sxЮ1s$3P".^xZf$&&zu]1)pXVE%T0@[pޢ wPQEgMMM7srr;(I(O "zN@y"#-SR@!$P!!bCn?1e@!! `n#֘ KiB)...@At]ՆN^ADPٍn͛sUJJJO#pkhh8l{.ז'l!!2YYY2PBjbnn6nܸK R@!! `I#*6$kRWW_TTT.kѣ2e!b2XJ;vXƚ5k"m6/IBbP .;wpG$ #?d-T0RSS{d f3+n3ˎ}j2䵠v-6%THykؐ, Jkkk D޿@qVWWO,$nWkjj}9~xU[{{{˗'Z`WآESSSd[[[3 ;e˖H8p`|KK9߿nOt8]*RWWgmnnu}o`lQUٳ?9sfГ]$555:C /@D5Ο??rŊ?2?0@DT^'''7Ĕ_"Z_oMMM'5|֭4AfDӴ 33~X>5Ǽg1\Bѣ@Ę;K^ ]r,W\Qu]F?(ȑ#p8%ܼySu\E|m,Ç+*\t"%}(.K{%|z gHUU-u} zg͚'&&6Q_\\>Я~v OQv|zHKKViAAA3% $D~%%%w$جPZZ#SVnٲed;uF'ٳvҥیl111_|ᇿ1544$TU}|#ڵ1"kֱnݺN;wvՐ4fIENDB`mx-1.4.7/tests/style/000077500000000000000000000000001201047117600144505ustar00rootroot00000000000000mx-1.4.7/tests/style/border-image-dark.png000066400000000000000000000024661201047117600204420ustar00rootroot00000000000000PNG  IHDR.} sRGBbKGD pHYs B(xtIME0 +,6IDATXOhgƟlL6f6YYcb/"/S@@Kc#VȞs BA@NJ]V*u'q|}fj͡>|3ygxy^|1`0z4R_EЌR2JB̔KI^MUcvf ml@a*X2Rj\*o Os:urgKE ' 40ca i旤ʥBE+:9yy+N'+mdz z $ |Xs-uq }-kJBT΋cő앉s֚jgkׄJLƐ'k %_=3{*|lcJIqD 69ΞH1)j+mL˜Oww,_49_gcN/حx]ץgRR![mzȡt:ELart:9r1vH/Srެ{Aҩ'wV?j?o^t~ax1~:uD& 6G'9eomID0hӏ>0AHx$ `X~{ι[Fd94=U'(es֣qbU4shzZ)545c_}y0QE:{)?x3ֽoל@xGDi({W1Ɯkafѯ<{ hdQvgQTB3O衞&9#JFDaGo4=TkB:d>DjKA"/;/Kf@cDo?bynB-yU؇x}}A@; P#TvAeZ(0M(if˞ O,OkRj`h=Nr` rch, 0 q zj2zVjӍ}XFQtpjMVnJ<|NWZFY,MV|ęj)`݄Yl4_`mї(#?kk|l5s0~UK*|}ӒfVoYV>Ai8sP8̡4E10}ܾ:LNk_Po<=7;{85QEB)xfsr 2ҜYF;DF_|nɼJ-#wnۥ5j_mqP~S`C}IENDB`mx-1.4.7/tests/style/border-image.png000066400000000000000000000025131201047117600175140ustar00rootroot00000000000000PNG  IHDR.} sBIT|d pHYs B(xtEXtSoftwarewww.inkscape.org<IDATXOlUǿoޛl]%۝5-,L 4&Ƌ$\J9p-+˝IuG Jޡ-20Vlᇨ|̸Q*M)*Bt yb3<]v"I^(F#4 v\(t ;`tp_~h[_Z 2aیN&Md{bH' & $.G1S 1g-cO6 Br yHw8uhpx^S\ =vr+Zϥ 8C0qljqꛛ?}}_Pgxt{>grl*_ V WoJi~( ~F0 xW {X xƿ(rn!5`ٹc[IJ#FK%:} BuLC՞ 999t-ZT[J%"v T3.r T) <_HabRᇶi4ց+mCPJ}F)Vjm -ّ+RŽ1Tj>(UF6Z+ߩÍ@87^0oUq6C4p'4ST :δu(tPNkn!_R:s.ܔPmSQ 6Zؔp̹PJ5 /\2ΖcքN5\ ';[V.^Bb-yE{?4Bp <Օ(ApBC 8qٟ60WU*wUɿH]O8qj牕>Mg0 B&-L#6eW:-P Z:~z!{!\! DIENDB`mx-1.4.7/tests/style/cyan-button.png000066400000000000000000001105321201047117600174230ustar00rootroot00000000000000PNG  IHDR,,y}ubKGDtIME-` IDATx{fGY'}~{'NH'!tDrpt@8Ǚqq3|FFgH: AQQBc4Qt[uXooZ޻/ɮ{}U[VZki3mʹ6fLi}lK0yp_AoDQDpn|}7_3Nws GQO EL}onx3]&ic|Y@yۇph| pKeJȶm6ɞ|:\8-߇˷O8 ,aL?6l3ml\v3YБ+"V8 @!\j0 k?}~z~Ѐʹi.tx![\&yƁ1P^:_;(ze0XTp>XV5cpKkwnB7KuCm|9W,$06Q8zu7y=,,ˋ(<>^x-śi&`]^ _pGxN$0>Q]5x-/K #p}7ʹ>i.tMp pSs(MN1],9Wx8X:_M3x;p!̎^uҨflKu) P fi`buHw vj =`{㵀m5.;`6aΕx`'VS8 GWVGW._[ h+9pn[(P܉?柛i&`]t,#>0=[ldG I`8okƁsƠǣY^^|a?͆VKk|qx=x#Yj6ӆMXБf`fe$ڱz7L77M݂1=vM= 4%I;6n7ӎ|$xlq_'V??Yg+[Q=5`!)8|kxL6B=t;<~V?6H*t޾q[@j:LX3~I+96N ų-_s#/Lyi62}xi9`zVK%z$Mf{K^i u+npSgS$ʬ '3'Z6s)i XyP$^nҌW 55 t|x6k[rtZ>vR,']Be8+8FJБYؤf=[۶UԅQMp^v%㧀z1UqԳZǁ/mz| J P˝fV@5n/;T{kH]hhrF,ue"p wNU &p8s8DKOpvZ{CGjWƴ/tu޼z]hs%36Sv7W=/jL= \6׸FO5J=(c淙;͙ e'z (skFjBcE^Ow<<' Cqq`3iCGƁ'h={=`*2^l?vWGMk. ZOXQwUO2| pj˄I_z8ގ6S6+7:ZqxΟN|z$(Np=/k9'5&`;(Q+*ڲ|i_^ բzYۉ\,QR:ɴhWW<)`sgkY} 18$^767&`YaOLfWVӕP=XΫ*# (z)NEdgV9^l nS[͡z]^TmΟX^?VPܧ>U&`t[oC6`:US{~upF:xr"Sf.@"s-SZKVeWcAo;= vU8ތ X]? _@+}lu:s^;uʾPu cVٹ.n-vl4L !+`P-ʟ9!K&𩔞ڀuǠmW۶+ڦ/]S_Gɡ'%1l ygGvi>|'Fu,pڣ>  YTJO]/gSUTʡzԿxaFFQ~%:t4'.<-kdurx1O+8~zF1#8,x*`}y-o`]՟sQ;_1Mk@ x\BvW]1'J.(ǜi_xnGվ(_pہOɂ'{zj֡{cZՍ] dnh :L;(@<3vAVu+br |x5Uv̗e5E\8OᶛSiS|p8*lv:T[+L2(!h@5\ABkz7ͤy4,i%\NUwϔ}o?,x2'?`{o8;켪zHƁ9<LVG;v2 ,I.feգ=t /W!uxÁOCG;,?\yM{ˋz 6IrA m(%Gv:)ܷ<B5;ʫl"xތOC&Qeӳ)VTF ͉[h^A)m On& mHfc3}8s.>pd]zr]_ >ΪmYo~:_{$Qe] j@wGXZWBEZKR44Sg*ZݖWSUqM^uj9=){7賀EQEUs4TGwv/0XX(mnN\0[{4Y)]3ewUEVBfDd傘EMA ׁO V?Z}?O?k=y띾MG~?c>pl*Tz~iPkrACiql7.ùmGq>|n>,E{u ;~hWu>>)xY<]&7_c;dhs(k{9[iS ȢeJ׎X^[>~pj zY,gD:M”ty08YQFձ\Dd;L&zM=mS?)z ض7+nq$c;TSUc7TI3mͮ,cpbj)=ލs~˵1`ۦY <3Su = )<|o~Y;k]*L`Xn s)1&+ c=j sG.`oCHɻ}Loc-͞T$Z: h`#c(n^\FC #is4dbLH8'1SvʺX2@svNGFMF pIJEQ O2ϝ{E?n~?.tyVu7S`rWWo8DHkz)aLfȦPi jKJ{3޵5]G] X296[H4x|82)Wv~= ҥXxxK-,fHg_Lۯ{Ez1>>6HDz+yMқ#ʱ;WWޢ)%})Y]| b E? 6U_kZkqM%.mq~c1' ^2W^-oL:T]]@*9(V6k?Zm&# R,ʿS?zD2>8] GUZj~󽸄ӥ X> {X+ VUh`o nd]ԗi_6",vK9 !==)2BoHk$+p</[M0`=om_Vϟ>t}5 pRc;ջJTO}]0pZxC=G2*|*ť*wyZKEog?K0]u=,o~{5p >p LC!:S~g8}.eF7 yZthFKEn@ѤI: ɩoz}%;W/.f굓\th'=``u8p V.+דFuY\:69; eV{[h~r] :j2ZIB.-r!oX5f";H ]=.tDX<|>W7y1}G~h.߅ L[;s%WG 4Gk@C+uD G:UMI ?Z:pZ@\yw^"4ʗn`&|"Xw>dyw[w1ruF`iS1(. #HTVݒ@xB꺎G@߀&~m)q0 <'e/]o~2_/]Ǿ/})q]tQ _>=*vw3AM!LҮ6ɭ[]<RӵLd!UAY[)R#znP* > 9?|XEn|X8Mj9?EL/ªM8zy@ӵk o.)Qy-JњdU'Y-2SU =;&ֶP//wp(pRNAi|?++՞k=T #w_G4_Wz>H ^#(uҫf7QF`GYWKh5hK֩:?if&$m٣gԧMZɟUwZWcp[p_EJ'º7,op[XM*ej؁@H~B1XkZ(}Rv3M_ $Qk]3r<am4)YҦ79x]<֜Ti4ZgzD?8]}QEvObl*kXp]WpS3gNRGݸ= .̔{8<ɛZ ܾ]7[k $ڠ;%ٕ jג!ۂhOmהTc.F{S [:a+2Ĺ5{9;1 <^fӡZ<5ґ䴖Ee=gဲ,-6g6DjT&-ڭϭ.:͏}&?[Hַ9*,gmDe)> =||u nf}R;އCH?%+ucu*odi>`yTf ?Pf.B-"+d׃$#c/mΝCsoxs1X$,D*$y ?H22D[eE4^u ]R(w6îHi}[yl IDATtСi%yRv^NS:K=!x\h\<GY: L7%<绀h+ 9V;ƀ*д6&m/0ezQ,dh.#)KeMCet4"/)+_d]C8AZ2zc^Lu(*>9-=8;swOAi",735p[vے$ȕ"y+ZFԊlZ#x!@ eCLGvz[˜JY@ 35=viXHdgM4=ߙ2mE Rm6vvԿ9W۾,9*X٘1uC͹W9N|\S9V; B& S<ͱd(MCRwϬA;f-, <H jt$ey=2`tVk'KTY> Afl7j_rHOq]49e>Oa1ztg 6(* 7 f{ۮP@ǝ=i.F`QBlH|v  @Tް@+eOsNBS~GYYgm@P֕۴[dud]TCzI[}OJ oaP)lϜfG!;x x؀ dw>r^pwu+8v&Np'GhYjI:&Leҋmbg+{Ot@ɔ4);-6O#"&Y49m'_RuQu&ѡIT/T`ut܎T5] pϜz(K^uNcO`aA7 _b-Q~:Y@qcPsBf Y0sC@b凁pGi}g 06bˎշEߺ~!5&J}OlLv+W \`Y[{nSeІmkM+y7LaGЫS,+Q*6^mײ7K[|GzdG[#eM] D>IEKԶ5>(ɵvx}cXǴ~ч;WVodp9y3b`{!rІ5qj'H^KV y9iq64Y ٛHI;wiK #2H m%gC]i{OMϼA yZDەbǐJ_M v1z0]S?uLX˫oЄSnj&s5kXaqrCژ,z| Ze)&yGΧ9T 4i]SRX[$}r>IRvkQ Vaw vi[Gۧ;6g"?ְ].ͣ,E  `k9/ńOcO`Gb+QϢ^lI6ևrJ\mp+xv`'"᧜&FI^ ;}ڀʝ.61|\&#Fs6[vYRS rd)3[E?Y!y4 B3ԅ;d dnu%2, c}L?r<_ꫴ>'h;aUUD|=^6֮~th6pWW,Q:]8gkDB[ h2D>o`:98ڟ6QPhM`ȍSӧ-vHw7NL"SD?}C`lSj/Da,r~80AwO4KwXcZ`-`>-pTr y1xDM4'KDM@2#0a6Pkv3tXCl<N60#NIY{2䀒GO7mEia1&lNnT*Rl~5&jj4UR= Yzlǒf΅,n.:pց 6YTS;ش۲42i{"ߥ~]Ljgt_;"Q]~h{tأ}A,MNM(:6NoV]m wP:P7hɼmJM}&א}@oI:DZ4:`y ֭okW9`bDʃ,5 .S͌VqP0ZQՁh]$mQ];@ =j/m:huL١9=j$_)vFQ1bMñ3LQ8c4`:2I3l*8WT> =O܈v@Ǡau֩y+A6x3ΦȎة5ch6;Sw&Sk(H)۔E@E[_^9,Y')9¤`_;z*/y*#GY#FX),WmxP2y+(E65&REG /rj HdD M:؄,k0ZmEQ'+_m kG“k4-,i! )+Z(7hc凲"7mckV('0d7mg>F뫵, ,@|z֡GAPMaV!_noYsDz=dhTv'N8;=uec(LsLHYN.K;mwdY$SYH[e2 bu<-HK}^4Ru$ۅj.N(2ֳ>v0 :lIV[0BX h^}ҷVS<tސ(R:%#KC] 2`EdPi]S+ZvTdZG>'[ْ'FGPZ{Xu3hzŏ MۖK w`)uG 8 _0jP 8\֜(h^؞./!;[eCq2M&Cs6D~Hs锵ܠ=h#ҖTAיTJk";Y@7C(2h~\Bt/kk?Rf#Utqyt蘺!^bzgwn6R]6!4;elVVRED)5!o?zMH%aΐ8Aiiu+:3dhA*LHr4..&XaXHK$h]Q@ u{2ECԅOR_J.³C6 ;R;b~+fUm+i%hO6 i~PأDeMQ]Hf.NAʴhalA+!<;]E~d$ګ#>jt$BYI[C{șGoy)lm/^=357^Jspc(fTp^0;GGK m[tW@j$Gm"mW \ԣʦQGN~(ZȐK k_d"4c\" Hh%AаS[hx_2 ֋8iRHݞ;zɗ r|~7֠;mc8/ cXiyTZk@ ٕf`j Ψ./=lTtXq(\oH)^ G@FG dC<*WKs87][Wb@gW}@SB&5Ѽbn+_|:|ቫ7gߋHD:XшAKB#{nhP5cйiڠ]%GSE{`w&V!RdDMyZG:FGEce06Qۦ\T6UJ L%,!ds )uIL)z}3s sݫW(AcOP&eu.j@4D,1b ۛΡ7;۝uA5@ Wm4@JM њI(&@d!Ri dX2 ejGQp87U7[^(z; F۱:lQdZZ22?irhRRVO%m'n=Tv=>-w-_TDuvk`7Xc,%"h[@L2_1EQa&#k V~{](%H0ruGT ˾VCfQ]w%X*s939jq-<V,DdtbeReg#5Z#?HP]l y͑,D4JDeK MB97KrՅ7;G 5h Jӛ;橸Aݵq>B5+ĔLsK6!|ʣ8g lS.h#"pw[Y dk:2«- !S"m,(} y.c:j]g: buwSJߦ M̀m"N4`y*zڛ?+憇gO; qHE sZn5lRȠI4jkn&%Rw.T="G9$E29=atZ"]K{5<˧n@&~v{h?r=.|#u~ m{E7㯭eh 0\}ߌC d;}WS7676"kƁ=h%8ߌ'<qF\f?Y.*ьp\)Jv1ʣ]ᅡZACixim{Et2D4)Yv}1&,ã3.gGQ 2EeҾIAU*W lo|vAg hH6Q8N z,gZ׋?5(|)(/mKӦg)$~]^MgR"̚&ۘLuh>jPcMVk+]|XŔ=.sj꡴SC#=تCē{H$J|#FXH#6(uz-bv)4R *1>#Zju9okYήvZ>՞D_}L)\=A+۠siS-ʯMLsڤLZ2֟6= v54쓻Yے j4t'96.*''jv]v sʩ;ViΑt.%О߅tʚsN$8{dPY4NArj}фLeAz4ʼnS V/П* -; %vFw)AK"}!7[և rN3t0/@Ўr8zsX=uAI:}Ӏ{^C41 ]=}"FgzM !+`WtKx"{֎QʺN״vHѤN¹Ȧyv0mlQLɠla]F RuIh0m 3T6 I;'}L;I=NZnri#nrF`P CvjƢ~:4Xrxֹot7 B\$DS]HAKߵk]cng? |!dY !ѺFs~*C8>/:WRڝVJC e->sk_I RW2ZLmvɻqDʩ_Ðr5=/Ӧ~o .OrrHf߆}6rd%djQIT Yt#emXDجjARma|D(\U{0ђKA͊)?$44>) IDAT#P#ATPLL|Y@I$SR_iܶ~EC-:MO5^zEϴ;Z]cmhpDc=R -h+@8k1luՑ-$ <("QVG9I1"@W>Yya=:1LCm@'Rv> wp('i7He qp<@#{^q |cA.t#lFwZPI.)xEErxi]#D =j ~4}ڹ 𳨯4Sx);\|P{Oee&U`5Z]VL0*0I 9Nrذb+Ӽ"Q@@-8dyBL=SA-̉}@ 4$G($>,D<-P^ZN 5- `n = Nah&31 &K(aAQNB͘2IѴ1oZzt<.H9}IF_:8"| м>h ":`XN[0Pt?JSYZ=\tIr75I*mvRCg83 l[IqfM4ojЬJ߫(Ks )@ŒEG^oWO p`?dռtj%-:].Y@HZe=c(W R Xī5Crta3LT&\'l0顋./+ lc'95`1dW]!C!^f!Q[cesziNo.ޑ~*1Tcb,D/ñaH0&FT>cMEJfTf{нJ!+jjb|N^Ư XPׯ)sn8?1E4n8pHtXLb1@z5Oө~XY2%R6%3:`%A]Fo0hb lyXK;9Ql+% N4b2'Rm͝F{UTJ |}W`~7v :Li'E[_q(9Z#5UnEˊ BػX~_86Il9WF l0Ndc碂X BM$"1SD^'r~bWC(hd((RzDF+r.eXpwQ9*@YIF[VDF/aJ*Lm9bDxh'Dg s74 viftKQV? |X!쏱ֳ1'Z$s)c"m:07tAmĥ+y)mJM(q+;{kZќYQxRe Њ@%(GbcRt퉂h ֘ɈK1j>2c`ӂbl<$Z`"^8f(+/Wr![F s@;{&KMK}PAL<ީA+|&Xs:uq%2*2*6~h`ºp-7`WrPk@64RVh/ ZI~8,>z:,_1a˺2̚&Υy-xGP0 ]:<^LcѤdE=DҲcr*SI4;#FӋB "8W'W6Iw.ؼXc$EkiP屻2 lʊ,YtҭYH]("}tXGehVKr~-)щtpgz]pߔN!?@n@$eԬzmm7rNEYv^@v8>σ<@l R$ h- @7SG ѻՅOhwI=M1x@Ȣ<5!27U^Er!c=r>: dB$tkr|!*: Gn"; fAeZIk'n9=aRbPc픒 >V=ptxleIG9T4}|Cʂ(&G6ا4yNG)H+@]8Xm `E*T"1kWg9W EYWБfHk("4!*T-x}LDt[ :@}$XzW'쑆8ݶPo8@jO"Kz~'G|D׋SO & 6kG#]J落9}/jv{7}_=G.ĿYx4(o9-f7+OicW@op5/|[Dt1!A`9)(.;ȷƹ xStHKrmlt<"?:vT^q gMi[`W'VS͑vVʤPAXi(vHpS>) ⫿ ߺ.Q㳪MmҔ^tTEOZ)v@퍀8B!ɗ.I>;vD#CaTXXRLktXw\S(j<)"u0 j%*\#He p ZP<gL]W4OgeR66T.%nGIɌPX<4gd(݀5]Jd0Fj3)KFaPJAiT1P5Q'?Evr-gV;u}BfYX; YOOi2327ĀI;8;{8:^P9唋#hq[3ȌvWZ9 h~+/ʛsCL @LiɢB b68OP)8R>Mdi}H.k jlXhISReepTVRকG4〩 NlNK]GXh0ΛNs!@ir%F94٢ܮF{9Κn(x-o y6F+lj 0LhM6{6! $kkQ4\.(1BYPdZ8aWS?:eѧ&`V7G#dPq[4{&ґ`Azʖ̑CrMjCG%[ J]*IY UWQՒҷVmKko1d۱$XȒfɴA_dJ L^ i3dyM[a`Xb !wӳhCg(WMTXP<(uW59ndQd:ؖΚ"7v o&h>c7_?+瓲ّЦw-`ֺ=OKpzAƐI~gY^)AZU:0{:2!ApH>Χ2]}GjPyNJNOT[RgnDF/њ{Tj8Emw;:7Z@ 0ʕ\0[k,sivҩQѮ(*GvC;~> FM ,H+[]J+W9]ΐ顾[*`R%礍,sZ;F/=GaK0[ΑXV5RjsB--GDVh:1E+SɅ3'6SFɷNQ E{͢VXdߑAttph8v `AUMlP)@i!hBCH/_H@[/R@Ʃ'iǙ6횚) L# Q.ZPZ(r#,YSQ.o5' QV;UˆaBTD];8b$YˠLOnKi"~ d~-]dP\ow^0u{U+9]WR$/^}7LL8}U.hCMYy QYKkJR,:#!\po?gE>]NI Y?O#i;#Na\PLX~(OA>L:2Z9'իKA}pM!m1%ˠ)?E+HMfkeREcҥi>6ο^/]#zr E qHALsV] hD{ 2Yi4.VQY rx!Ӂ쁬 *y!{ҿ^hk}2: >EVH9#"grA }1ʧꕝVO+D?p,2?zOy|Zh]x$I҃4|1Hj+3^uOhvaeC2Լ᭒{<ߜ:Vٞme~I /u .ULH3tUO ɡz=]=/1wTI0 `MѴw)3<淕rgиXN Ȥ\w])#+ˤ%iqNap2D2_x hl}LW-:Q(=U֩i/X=Ӯ €UGIOt4R~X{ޫEF^T e4_$bڳꀖ2Fj2Mvȫ ,|2ӜH:lp:3HvhH㉎4IYTȊU<[ NJr~EESB90V=:ϝ(.l:V\4a`;Xʎkΐ`)YQ2VI9uWrG B9]A}3ESKZ1. 8zQ ]a'FtѹCv4%-MWT)4Lv#Ïu&;5 KOϘ"<c 1rEP\R{}iH'*aY;*J&x)v@xW ,IE^CQ)sĔyj$N^iju[u 簀UV`m!N+_\Q{+vB7ѝ~ SX`)K MѺ(ѭhI`K~G9C2KvȌ,E]>Ԗv? lQ'ָ*ʨLZN::GN":R6fc`w:[4 `^Ps+mN5`i~geIYu-d1Cv%KYUstSj 7s¯StUH% s5c1@K8xWo `5 ;HtN)6'Mqrݍ^% ;Cv"%_17V-D(EE-1ʀ OD'd* 4ih$eGYJ'0%蛍AR?~+SRckFǒpֲNF*kr& (RuUN(KhU50,hVh 5%42j[>~YC_)R{~a>-M#vPj(D=B{H2_wxr\ 4ٗܵ<%m~GyF:*o0aUFED%d>+Zb4AB%?q{f9g+#=cUU20n-hE>w{# 4YehM 5UDmR{]DTUM ƌ9<4;qhkkBDgv JBmk^GheXêD@ _4Tof) IV j`eh=Tʕ-@S1`U74ZТiZ@Qe%m@>ЀZer=F]hOɡUIs$-Wְ*ǚ_WZT6^,/,Uw /Q=X ao~F#x"$@K;ysCQFW[єZK%y94;T:\d i)R\&2]+hUVd94RFeiވITf \`<`bTI3>pe8IН M0dEAIVǠ~! CȐh<,=V PI$hS.pvUr^\g.E[ `h!,| )Sōb:TJF4&Aa>~ #dyccV 4 J% ĎV5= iGdPtQ4PF׺hC]Ս3epRx}$Hu[,~6I^%ֶjZ7w-t(NQe,E(dڷ¶4X]gqG ,>XaLIa@?R=޳lN(M$t F t< (Qt\8_BjUb{B)ZAK&,.>~,W"[OQ2Z@z-`UK-SS&_&R{"e=Г!`j`u%Iuc_-_m}o& ԑGqkޓsk'l!6`L޶RPɺ[Vbȏ:5Zm5Ft-31\xNɗFeit>-ɶLUh{k0ʲЬ%A &Cx8QVG|!:C{AOuAJǏ*8O+*moiP<3raMgER@KQ:#w=7_Ch[-C9=#XXj9b4k |;+sy/ $qH(JV a9/= l09޷TTY Z MH],uW*2Vfd2 d.KpyQEKSnj2(9` ǽ%_iW܉ IDATU 4JbGTqjC$KTlzrvp?P:S3U?xbh4|}WУl[N:odQrdr6]s9=_EƘI淕rBegxB*Ez*_%WխEK8~ ,6 k8_V{-^ ) Gu9;"VK@CTSbz@MyFТ$_ tH*X朖h}$ͧrDY] C`%)bE Q8[^^TAkA\xtY}c\u,hcFN44aOAD3k Cx 2+cJzĀ<6ZӋLO{_ԋc=7_4J^N%rT" )MS@ŲzBTt@ N@. Bpoygap'f^GMVkKF2RK 8 (E4 yt-fp%KdӼ=Y+K?TrjTPS24Z`8 &\u+++s Y9ʾw#CP;cj T9^ESe9 Le$hqXh9uYhrHuB"Z#YmL9VQ%lu ߊs@QVk]N?`xB=JңW=3jw$AّL@acv$<`81 I;v|bv0 6HdI聄ҮZ3~OOO?[_}V=3;Nwݺ꯿ƙOۆQMY%Ch.],#c$r.@, v3eivY<%q-;BW ef NDYg,KY?jemR;%bd^>FQͲfEf&8`SUʜ/[5WiuNHT=d1Ǐ+7UFo uZω2:ńS9WtaQѨQ' 3p>xeopQ @Iy-H&I #GHdʘ F{xlO&u ɘ]K`NݖJմuò˜V_)ݖ>볋{q>qE4CW _F8C6iH׮iTf *NTiȊ"5HH-!˂8s搃v<ȃv#%f'C9q6Œ+"yf֡NOŜ,-ay[iɁlJ ˹ʝr1mNV!D")jBI6΋!>GL΃vN& !{: 9oY sKhV5oU?JU$QogimMb$AAG%buzf!$&qdEOӳzdE[P$l yN9I/9!‘gt8ƫ'nZ^*gk @z 9|DU"7hHkIV\_c:.eXj[?Cjk뛥(c2^rkLr丹 X=Pqaa)pcdGCT(=Aq?|,  T27`rY‡SN+o,D1e;wLL9}к 8 Ǝsp)f[5H!P5LGVtE{^ZR1ohO` Qq<R)ؕ5*LlBRj++3gR ʴI[ei.DSf6AsNn2U=EYRoMg!em춊oYp:my>re :U@^֫P>Ƕ7QN ''LA8PP>.=/ce "ҥέ$cj/3wQִ^j5^>1kM9"@Ғ8zZ("e k7W"dD$eOv88IZ^n.}x[Gn]2lGKh :Y>B#ʋ]9]of[Bt(\$/M]DE)?`H%>C&ػ唜N+o@"e@>ЖgH0KV9{su'lZbFNq !keCr !cj )X\uc~-ˀwGpJ$ ?Xl^Trߘ/JoW說5ή9Xҳd2]k 4svPEk#ůlx|uEHj F4"O}pIめ*ZDКv,XzR/MN=G$_x劷!oJ׼w-[/ʹtX5MAKMXZrY pvP|qe1"dF!G2kgKH;41mq [zQčo-UNʈvR\V^!j˾VT7x a'L5s ig}=[& [N-)xqH@ _-kR:>QcӎS6qg',]fvnvΡ\4qvB]W9%wZKsɕ?}$ }9duM,u=D+BfفPrk5C~)i{Vk g{҆ ]>5&!H?\v_EK;d|p7ơrxdv]eRS(N5|>YWj5t@\lbә4*P@:1}uʘ1h噱%>'[b2q'ו詤V9>8Rcb@iq}u蚲1Tk5˱MMkjl1ޖD&(`m9]n22"0]H$a@9Kq7'i%Ƣ<6T߸qqGBA`EDR$"djhmj;TW "}pr>TVJγZzDh7h OTl+ceʝlD9NLHS֊jPljf:VցaEo޷( 7%ͮe>_tEno1̫RO&m1-ȴ2X7p6hfѨJHPA]] 6I/3ίҷ1vRk~2ɺHCN{eiJb/'U֮=NgABbqpeAmꓒ//TDi7~}%58It |p!$A|\@6naPqMnۇ@<8J-$mv[Ɍ-$1"DZ7'sfEػҴ0º!@W\By1чZ e.P$_$3~CѲ,טc1y" '@Klca~P.pKfu-v똘mp4_ #ڿ`&2hmpi ;/e R/Dy%`H;n\s#2F_кX-Ɉ"*7]s4O8a]4]c1#9UQ,q02-GGTS&-C\6Rcd%IX*\cɇɸ@!xw!{B53eS-X_* N%YNٻRtNٹ"6L_Y#Kj|]cE <!syydNA'nOպ~bfF[ m"ԶJ xp~L;.9]Mun,ݫ=Ҵvwdr"u7HGX_nf(Ԕ3N7tu”6Eb.LB8\d_#̍P709)N?O/aVcSк9[''KŐ;E.{Af_l{!D3rط`"ѩIVEi7J־fvŽ, vq6JlRW?rb`&A|!d, NyEdzFXGɱqDòu!I.rD9 0m,_czTKl>f쾺I^$:;OH-&&ݕv =T3[067!H-M{'cGK2}4AE 69-$T~]B/]|` !cLB쟴J[%| j&V-<f_,E  uB'IJ4d ojgK_)cuDQDhq䤵1QdF OX]5 0:ŨoA[0d8@AQmru+ lWA2yk ~ Pǚ٥j'ƍs"{A0 YԬ}ƀ9l}iz 1HF6~GVHI98gj'; ˵F,CAqeȆb++P=S48:>8 ](DW—i +j  $'4g&Gld3Dh6Lj#X<Ɔ!Mԟ.L""58F& * IbA# %AQ%OϘ %4y= Q4IN/bx*ta ]zoQ&' EsvJ.pֹΦNRE@僩!,X<#6(C!H<" (Y6悅Id+GLTB`/9 1율Cl`axz٥lR @f% ps%(*`ORlǁmO٤$*YDHgG?eɲ2T&F*ΞMߒp ˜EN9#!@`΀v?2[KvpeAWvlmwMXxn$\ 05fC{ 44+1`3 سUۨ,-C_v}:+Tyhu|2: %~RJZQ]q1'lVpT(.}쎧YFqeqv!s$%WFRCHk;OXpSP7L<^gM "HJt-XAbK![0؁o/<+&ӿuȾpa>(4C 4uT⧃CV7TFש:z OG1>{X3TO~r@مZy) HCZin?[Nə9H#˺vt*^R#CۡYQa . [7iժ=SFߦ%YRBKC9v ""[+E0!d#7g1eɌQut* ]OR*=}nr޽utG 5EFO=a=BT$|f!4ؚ\{B.B'F"V%L_Lcȸ::&}‰/Q'2VKXpc@uREC!Ï+E' d'dr?SWosvfY9+ ac|~ۏ; }&oaqX}wL,}z&!h0,O悌:uq2åXqI\gJNKJٮ /5+x DCbs3T+a8eNXbs:}'L%\(41:g$:yX)]׍(rae+. "*PP?_Əh}c4)KZ : z:Cώlr"cHaec(>Pk-jOTټi]5un}2C3 ",n]_ckv.v-~ɮ"큼K$2rX$(!Z>Y/NӅ?=1yڃGXpӵC(a<~VÇ, MPC#dFTR ?_K_ ]f5߮JΞpdbʒ?CgPMÝKIDATUlOW\/v5RRP{ç"9.k%yvE <)ܷ] %,+Hg*+OH"vUy!sDw[z$RhGLF(A8yAe/S)<7a!lvkkB y4yn,dE\Ndw_OnVYF1>G{84IJj3 A|xm-ݾa>`x U`Y{AE#}֥Eڹk4ocpzO &~AP'k냰S~8i*j|\)D=+W3*cNڏyv>=+a@GqS4}yFcUv9TuBV$Bb;-ßҶY(%?d_ z]w[X_d&KH`;d#e %Ίŵ)*tVc%Ae?5gV;pӵCvZ#,C/h\_@FZ,2rխ%/[ʖO3'>?@˞Iư> {JƷK˸c[h'! nÑg9MLs'$<'O5<1B-ڻHKX0n._/ PSV[$i{"Ɉն(rV4Q IDo?YX߄} V wxǦ͗Ƞ^ДSD& ksBۑK:whsV.6/zuOXp˞@]ZH#CjaXKW3خs`vi~y\4.n ,eCzсaDX?X3}E0r>(| :_ppl]QBdљ~-$g-Wqc8},2dn<,$7}!2:>Ejh,’sO`C.Ӎ̅]))EHy󗅴8hI <##IΧ(B˃N,,1aoFV {_ _!PK(&nx5͗DR+AoK$_9HV"D /VOsPWc_ÞaBk/">zW]7_JR7i]K惄7ay К2V_,B᣸pgcۉaeŝ!]v᭗ bB*\DZed $0 vW>i-y& ,7Z :gf1R^fzjpcA^Vq㛡ο:M@]l5lu;C?¼I V9,L[Cɉ]YxlËG/#P O|!]Ͻ @+Ttaέسk%Н`GZ@(B& 2rF -xI*}}|aA Fmq[qͮ<+]ea╹D qqp1\Á\E,9ϤZ8{K :(Zk󁍿 ~|Hԛ8 ߾o8 ްc ^}6JGZ@:x.թrg˭4<2q wB;qs RڃaeVR7c͛$f]%+mowl[7-G<bA{!o?- V._b ݺٺ Wݺ Wl݄-=`&|W;w!%> UPoyR 6ta[wtaۆ.l.@J%fzD}иtNL]<f 4Ǎ}:w;V+]5w hu..ԅ.Xy_cN5 I& "=P* `1ۯ.:Ձ~z 6^PR@%rEw@YG V~QED=؀Ixd[AtAްQIENDB`mx-1.4.7/tests/style/default.css000066400000000000000000000024201201047117600166040ustar00rootroot00000000000000MxBoxLayout { background-color: black; padding: 10; } MxLabel { color: red; background-color: yellow; font-size: 30; font-family: "Times"; } MxLabel#padded-label { padding-left: 50; padding: 10; } MxButton#test-border-image { color: black; border-image: url('border-image.png') 12; } MxButton MxBin#button-icon { background-color: green; } MxButton MxBin#button-icon:hover { background-color: yellow; } MxLabel#test-border-image { color: black; border-image: url('border-image.png') 12; border-width: 12; } *#red-button { border-image: url('red-button.png') 0; font-weight: bold; } *#blue-button { border-image: url('cyan-button.png') 0; font-weight: bolder; } *#green-button { border-image: url('green-button.png') 0; font-weight: lighter; } *#style-button { border-image: url('green-button.png') 0; } *#style-button:hover { border-image: url('red-button.png') 0; } MxTable { color: #aee; background-color: #ddd; border-image: url('rounded-corner.png') 30; padding: 10; } MxTable > MxButton { color: blue; padding: 12; border-image: url('border-image.png') 12; } MxTable > MxButton:hover { border-image: url('border-image-light.png') 12; } MxTable > MxButton:active { border-image: url('border-image-dark.png') 12; } mx-1.4.7/tests/style/green-button.png000066400000000000000000001210571201047117600175750ustar00rootroot00000000000000PNG  IHDR,,y}ubKGDtIME.;1 IDATx{Gu&U9$Yoٲl [F$cLILX EVk3LKʐ̰q @;dؒy ,cWے]骳k׮s,ɾu]v]^_kw`)-RZJKi)-nR[t& m5$Iy k$kk5D'5t@ `EQr xЁ\QUZՉ>y?|ƃKJK,M[غb,TA=GAmZoQP[40vZpP+A4O{OlK4%z.onՉ]w*v؂wVNs4۳"iXJDWNsWn:9.`A[/$U~2z ;cc8>Gۏx|qtےK7n<.'[|]_-X(i Nsszf;^'FYsEi5@5T[s..ƦMoF4VI{MSC+{s ߟ>}>'b?[ Iko USPg> ]RZԴX)M6;o,F9zz9|leضb؆K_d*8(yxc=S fVKSiLn޶6ަ~md#E.W?WջqɲKvl*H-H+5dI'C?B@D_]>:V(i mI&Ի DMg\` z1.ע" V R%%ޫ@''ǿ=o/'C_ju*DpOkEp .hZSvܶcJ@+DomuoV& h͋pkg\6HQXWEg_|_~?2]J`-b}T+ }tǺ7ʵ~yA*vdSo!Xo,ږ q=ݓE,\mi$@ǺcsuKKiۯLxH^X7׬o/EJZSvTg"h`!Z.>7 >$&ZVUǠ['Y:ki o~Rxt_+Xݱni y-XZDsj`F}|ȗWtKQ}Xw,uS%i FHey_qѪXV/ TWo|5|qګ=`1u>mXE^:~<hRO1.~Ck7|R %n9!S+xV\Z\ny+nx ҦNÜ[H:Xi]&6# ڇPjh[ARPLbqێk{u=zԅ8S#qJti`#tƪڔԾ'3|a t X;h*|}RdX%.n?FkTَ{Spf>:Vç4E/ߓno#DV߶xZ@x=5`0?1leK<)ZE-U*7l]^UOs~N|Qi14MA6z-k>t]ʲJ٤XH[=R:ZH:`A -kU`$^!0Ӟ'I=d hDHmWִXEqˎ!CC{ShMn Ϲh'jgW*WM9\(ؽ#)5z LOH*kK =k{:k Jϒ vk޺xOHk.QzM_ӥRyIO܋'hvޗubO? |6g5`eE: iFO5^eK_^((f40Yբra ~ \,#nVUe:@2կ߳)YX~#;1D%xwm7a<gN|@HTvU&d9W*T<4n<&S?o^g`z֞4>tw4;7ОhےO8||5WBCCAEMEYUD-*V1-JN%Ie6jx(>+LMJ%~ ~t}xg4`M:}R^F$ԜXWPxov˛K`t j-#B'.ğ6"ЄN[O < 5/v-߽GGCMēZ}gRzΏܭp>7Mx_ v`5&/"xjvX־g_ X;n:h|̪xqyWC6NlPI`ʢU2C)w!r@X,Bu+ɧGz#f_:%|Os7"?>޾M~g`Mlgԇ@hOk*[~JZe@c% ֮[\[͕!Gw^U9tQW?~xl1`8>}gZ3އdz(։ԬZ\xVhEhYLౘm1*њB^( kU/Lkt>'_ibNk}ݰ;ٚu՝WM?q4~giy V -z5Ox'RetѶ9 .[x;%GCd4!Z45m=>Yl0~eHw}_7tֶ-oς}My-TJ^{a1-@JZrxЎUlV!y(eJVTkaU`yAǿ{ma|;ornٖj';W<^dۛJ)Ė{wp^UɂT"BRW0*b4U.gD{2QЈ"cӎX_L 3[p}F dUJ] gq:kk:kh\N]iI^e[*9EP)٩B9MU0乀~[xB"U#Drh|6;ށo4K19 5߰8KS_D8CҮ;vmʳ퉶²2|hU}h:LyU %.ڮ5UeWI\*O[<<{O4>&մu׮὇ga:kg=`/0'莹(~|=n\ 92L`ƤS [KyhBkRXk'yp:6Ġ\d ?x#كC~FAߴ3,UUhVwcљx*p[ejKp(є.prXBtvҫbJYe`Q`sҏy47*іJ&놠yxbL6&qa[FChZ 6\᱙3_Y+e:\jrBRd%ʛa|8ȡEK'3ًlge^ ,JW\Б7),]a^V(_$ii{Pϐ6J]Ӿu.M0_?a`# k׽i݃ΒtV?ٹ*6yVV J)\yGқD]a@$Uc`*#6xmU|b*;4];M?0YV@큀JAaݛ?YqX[غkvd+h͋|)2i# /{0[@,]XBtUh oJ̤( )Y |O_Sk6Unx[M3d i/(7mxk3vdRPx oOfR5k_KѾeMi%;t^id ^U.D; /FѶ0Zy}Sg2{|}9r< hR<-4V)]w/{ ta]wu7 4=ڽz712M)\6VC#x?0IT@hlSz+S].3U-y?,RB,e8:y%yTV/: Ig^wмxq._u9``< pE хhCw r 8Hm"h'{)2!UͲ|®qUBx#?Ysk"y]^++pOK+~FAg ?y X;>?(ߤyVEVLv&(KL,;1p,뀃@1uvꂼU&WXT)&w) Ses1@:Pp\\s7cxOidl0 ^s>GNs:kcoPPjG{jKW\XXVĢ( 򈀔#|is!mtxI.Of?<@XrAgh%S5:oCrlڱ։oSȓy|Z6|5oS|Os:kmۯH%{FFfΩ 6Mn,GK> Q*lY*!2#<#SqTJ+q8 g衾}@ucqN8qPh9s##f8X;h1y1|xխ >u܁+Nz>v,`2?QĘҼbzׄN"@؎"^7i~NL UfPZ!Hx7;!=wi̝,&O+e XMJLOE8I..OQV"s<p(_GFyHej }ECdI矚_>wpiwl]fpRڹj'~coγ 'F s--ro\P%ur )[ TG1EWJ-txGB)$P?%!GFh3k\U406V8pp2këֿigiJ 6qAsM֍Dc)_nA{]>/?⸮u4CXTˀ_A{euH@ !'G LkS@|O55a,s ( o~7~{![i^{~U+mw\0yAt/b^d9\T*d/l)x1Kx9q*- NUӐGPm, _ e l:Nh ?׼OGPX}I[DV+``k^-$b+RT-K%ϯnyFԮ*e6QWdxFAUucckV.ȋ]6Y_02D~GhyئM84wso-*GzG3{gOszZ;Ko(\j$C{ `۞0)k, eC<%NNTma *XX}Zk67K#.$d7\2hgl폮#x Xk_OӼeR<~j ^DGՅ*1/x@hT/}pcEPk?GB!ЬU]㚌oiO ^]m+iayc9VO?{[ 0=m]JAwsO m%-忉uc⋲zT)I++h*jT]Ҫdi+$W=;yb΋Nu!gxIrhp{a IDAT}4UO;xX{Ә;>7yY#Cg?.g/Yx틇,\羰x+BHW$" <_mw-*E Y!Crؾ 8 <﫪GC,lݚϋ s 4<;?G&áChMiif׆M]ҧ|{4Ok4A'2`uګ3-trIʗ& i!ubmG)NFWEoQ2PkZ4et]20+ye4=>ȩCBCGv7;'3Ms$:/I+zG})?5[ *必V2؍R.]@4='1y+pCmf|I< 핋+TW^zOAw2VGm֌c0*7 R/^{{1tJkw*l=JAEg·N>;2F.:DxIN~e5὇SN`vx=ݾ~޽غlkhui2Up_\HT>`JUL;*mF).:EOAA>UƾyY @GQE=CY-6RtYqw|6?l p-NA:%֝;@~Hb~j:λ/YyI&-1|2&Gxs).e/P6?mFy*Gcb:@T|<:O*~u$'36R͕xh!K9ҾcW5YtJk?"s7sj1_U{ qrKoe#љwۑ]=Gn2,JN@Eȯ̏ XHtALXpRO[ 1j m:וܦQ50tvǓqtuOT7Y vÙzX/]#*8[.D;^4䃻?ELamy4I? `' ot-y'o1 MXY)?D3m,^YjslYZTY: r$LM  oV v:+Әm|=(8iZ8'[w'1ʖ/U&/hUA~EA VQb@ո}a1=Pކ2#_ 4C7s` Zt^W4W!;4Nnf+]3_"Eae*+u^nJ@C~?O,ׅ4[a8 Dt־W|POr0dpe!?RܧɸXҘ<مCrjh(KBrD|p!oS(Q;7kϋOqj%-l2(5<_/_uUȌa]zۥk$0f::^迻aJKĢ3!܏_#E3uzpRQuVZpB}Vl:֖_E^^8/ׅB)sAT#SbȂ] &9+fAhee)Ⲙ>9w柰` +Xn>Ff;`JW<FwiI;e :>Ī 9gy]䡚PڏΪeCt9ж'z''I;T;bj!miXwV"D惤m۱P^ϹUB$pB,^3>y݉xgpע։3"kF|P0_v~*cԄs{qWWIYﺆqR^7b⟁L24wKSPiY/DKk9ծ"GƐ.3?'1&͚Ҟ5֩Yu#?҂4 {#Oscgݞ Z3VehrƦ'u4=E'@ M`ZJP"Cڠ1m0%e|~ŏ‘_)X<6Ƅ+%LfZ,$֣rt,~UJaĦα" l޻ܽgAJ>=wi=5'1yemф2\wuhENY:4~|^66ybeCu.6]|%~4PQ]Yã +JOZE u }(WlNDR)P9>&hR~,5:x[xYiMSxذTW=5!?u?2~[ԅ:kfuA ~ڗVhh )[Zs 84ة+G^T:^FZ}US-~X3eCs,J+#sʘ7'ڶI Np@Xd<)X:J)l߈s7ѣf!1l&H~^wǻ+8?r |W=TBM?1yYjYs8M:aL)k|:\7o鄉rL"1I*W0Bme!3|IeӴWgRp/VNA.jNQI=R1ᙂDKu508r0a{d yw޾FҰo~\i5hIS)YQ-_A<$utHiyE˲D/d4O iIu&OҼZM ,oCYbey{:=-g|ee?4Jo?)9`&h/io"zJmAxEsIodP2t@*x%*ualF=!w{FҰTM{=rs /x:ap5#ӄx:7:x0Vr yNpxҢxYGnJ{9:%_b"ŰZY/\:dž H hQPOR;o:+]~~Wkhs[i ?\{Y 70A&hYv`>LRК) GlhEASN hwbh"/fꕙN VN; JfXJ)KI7X=G:GlQo֜=~&щ~'_FT[A΂Q/X,k,G^9Ռm@4 $pzd|`4!:OSC^ҫ69$}h9LC`tOKjc%tix]1%gN?JCCi{ogCp{&;U@Z$L:N5Ř4:qiݦMnmԾ6r2ӻ& [098x "&o_3~k>[G$Tjg @Xk"4 @畍VѸXH'0N`??BG0uoV|ɴV>ΛiQ\ K(އx29I%)l<-%3!6W:bjKt,G\Ss"!@&C_R&T%/U rL]m1 tpvR :*z۶ TI*b^b;,'ǹ:G%4hҨx "Jq]QIؙEyŴ(7 }V^Ľ''=#(msʄ8:gqP:f'M2kh@:&!Mˬ9h^TR~{%8q97|v**J)6f({n!@; P3vO:(cAX9f(~|Of*hIԠSAxz0$C(`L`m)Rk9@"2ZVy9&DkԌ7| 7@  "yVc9#V TvVx/*J&RLJ )W=3n+2L9i@K|1)(v')ߑ쬵cr-@-ݳd&uW4"ym Ex# M49fvNk48\NdgSq;tyKJs-:}*ݩs0O~kZθE2Yc6RȒ3rBM:BTHel3+utc˶bEc4+5. ^S:tsٜUZ i;5}yc/؏Q-+ Xnߵ>묑Y1 .ۊd7&!"jdcA l{kpm/vS?5M^R JcA͇b~O4e,HzQҹ~c>4ۖo',aLxR|Q &z㿪 PL4ƞ}2 t|`[]H)Ǖ/ǃ ;#1GFYG8%$ A0l7%5-J+Ȣ/H5dl_ܩkUg<_ou&y#~%U}vgA N5#G;N tviA%~cdt1@^ -Mp>QZgY! H$. ߆&dr /Ke 6uXپWƗ+ŮP`?v9=V)O;w; :A x`>J)Җ}@$c u>$JWXfѮ6o9sZ%i[!Ĥ#g@<:i;lmD.:t%G>"/r@7$"R>qIm'SJO7Zm -;" 3I8㽺OiQm$+׸6|B/<ƒ1t]V`"/Dv6nhWۖos4FA)C=p2SUZY-|,h^AI_nƉmO$@N_kk #@ JI%tN9&Ӓz,{琮 r@QprႇFzOKkl>KPP|A_41(3Ny[l=%kI+u.[qY䦘 (+JRY' ek?hQ'LL?)az& "5қOJ(-ݗ(oW? YWKC;aЙN4#Cxv5"BoCکWr;d -pϬ3 e# UJqϙ~I4 wdd[(oCh+yrxckjd't|Sp4p3C;aa ?!NR$`^wy&,ڪlej`P'.QJ'/#)n2[v/6Q8UtX&NO+? rYkK;-4s*sehcz1ϿQuSL0e;yOItP&x,xz+kXC0԰tLnͺmC"N?L& O#ޡ!>Mxh|ѼPN-7MVvn>*K'3G)SDɑ3p!yEMjYkNe&Ng*W) 5.g1$,} LEhQB>mF㴙>hÐX:M!=9qWԅrS;7$;59vZvL^ .fh8@f}[Rs 72{x\ZҖրF#*ȍqRKJ~V Rߝ }G 2'0_ -ph1g_^ڔPK@wWb&u4miYYeւMB7݄7frߺ\X5W7r'AA B̄Z3MDD#U@pJ (QF(ӣi/\e0-z$Zo3`ʅh>dh .Hl9\w2Jnl1X `ec4;-tr+|8oqPbip|"-۞v6'N65,Uy)hoK/+e6&Ëx5F!2usw"Sř̩jf{m#KpiDmJi &YQG154KT9Ҟ yH2Mc5aSw2 ^!N= a/vR UpڇV@/8Of rQXk2y_=Hx&"8WDΊ 8mhEY-mP̀^zMnx<3^ dJQeT;iY("!CfJM Zi1x"dXn;jWP^/lӿRx!`3rٻaFM. \fTWnv)o2?b̯hSpAYZ#C6 \@&aѐ,+ omm4;@0]#ORq2~)t8Zv7߀%i@R R^Ic{b ONcyry29wS.p;әuu+dK_SZ!Uׇit̒U8N=9Oer: ;63+{CڕsPDV;l ;$XZ/ X<):[R` a G#oP'?F'ᗉo# W(FPc5җOO1p.[1ߛF}tnE_?.m_aORTM*EC5&i19xvm_i9=|WѺ Lث7aPպ %h']kphfx˚P X\ 6$_ayf"/L0$dm|< MG f#J6oσMsCz0mIr0:ynE[NBO5z5_zȑ9:2kh4T @S5IS!`⫤rF&qmX6 9q1D1fU3we p]~GLXzP*J;\u VB#iZH:N}q͇Yt]U& m[,sØtb83F'2٘kCVJ8ƠGTodfyuNRg6 rո;Q%ZKךl~ό\6'OcwTfxA3 t\F-ssE*SH 4HtC[ GPRXK,L鸖iSAװ0tS4gT:!ZcWr:Hx SOn]L|1xR?%Nxzm:yO@'vHeM;h]1TYB\oBV[;.hY1[SؘI9}[MC 9(!<'PUhȾ*IR 0'sMHɧ ahƃyT2x4EZdmTNBG'K g`\FWf*PL :A)^>t](6?r`AG_6<%, |;޽@=n|6l]3VZaJvKo;߶+~wN.8ء}S$5Bގ"1 &ZlђOz=OE¸AGzM x<TE9IdyOk&vH#I=@1(GI.GUm̯寫iAZQ)4ps$V"@ew iQ޷'e 34H%Ir2ເZ 15)k>vYi&Z55 ,"@&D寒E-M(JCy9Q2c`Vb& TDYd:2&T淒@oS3J./?:htfH}KTpv)x9|yx-3$Wp- O JcEDw|O=sK>Jac %xD h*e%pf (OPK( iNVi> 5uƆjD-&Ŧ{j  iR-fǁGuϟ\t~ o<0SDeMHF 9ى{{jß$1&6W{+idpw'z"A҄(82j3ZJA xHZ-,Aa jY|q0a3ka) ۀ@{!7%F eL \%28ȵ {>R~$ Y{B@, O`7BXm7}~4TqwlWUT?l!MʂWKAIKV@x^z#E Apbmyyb,P‖Ju0;$P*M.m9 $~eiE F3S$V'5Jn/*oN6D`fSWw+k;բbZ$O}DZ1d: Qd%1,^L W\ۊiS޸T- (/9$}LY[l-K4_aZko@sĖ|!d@ъKǮw^QZ|i7 -h8E~`K5'?Sޠjxs2Re {2jht.=܉-Kp]d0%ZP(zj->1TII>@BFu, _COEc 0j:RpTӟ.!I6De@2sDv. JOkm mN8?x CuK24xxwg}Hsq+\E A/* h`^.!U&&IőmmT|kE `MMBHs$␡4g̘,X "4L8,O'zL;(9ځd:w#Cm4IjwY +Md(I{S0[SEfN'>8 xU.5G_IV{ ZUYU5*V(PERe-+dTQO80 ]o6$w 7Bh;HRyL 4"r A,`!&8  OB{(p@ }GN'A Ti `O 5/SIt$ ;祾r ղ"@MJ W{@{$Sy=\bhl_-|\6EuWeamT-ٺ(TF;M7 =4I Xb}^:JAJvBp<;vvT#~<y5aY1#擲< %$嫰ܴB\ӾYT,K7GV3'ya|g;8 WͤiwB^4ujmxûTcc陶%bh1qtbv="L.؟VmtǍ!˧fdvۗoGS55L/[p Dt> ? ;Hp'$O?){3g׈ =<2NM)-pyi?V/ q @8c%0$g=-ӞX|p&dB?zT.'Dg|,$kR$BeJ)t'{\FIV09=]w%q~|_7]C^GM0O*f K&׏43 R#̄JoIlچ8C>Z+/iM`O87;Þ,=1l>|Q2ԣ$h\eh[zliOp k$,B&קFV\3+q~+#_iٺw)b;mcQA SGH]oo*tO;q IDATs-wtb+HNf'qԥX\ IB@Hdq+P)И<Jklzhp-;K]+PqƟ $`ЅO Eޕ@ \)@@CvZ61O(g֖366ėw#l$Lۮ%T@24N܇CC1{tmɖ:aZD;8lʃSMyx} Y:$D*SA߾cf6Q>4SW6fp^|i7qBeM^QBJ#B|`1?tEJq{['b2ƏEX]irm:YȂ} 1eMHډ)Om0t(h6 :yOCJq.4]st s4Co3̜tmɹв<uwa-g B:*e0b߉}894|qlLEA畟%^ШBXHrIem>Dz|T؁Sn<\10 ;䝠CǍ~vɣg-仕 ʫb(.@N], 뼯g&ASHQ%>֊UIXYVٕ؎f"K"e:qEqbVRe,bӲX+H,)cˢ(`w@XX¾wy;?vt3p>=ݷ֨_b,ɝN 켧܍eh=^FWJz,#LdDV'8>"!㴃ax%a MP -`6ªĶ2qƿN^˘PC.ߜY (`fv0C坯 q3Fx:: oiTe# j&^:4(`8~F \f[8Iu՛iygi3Tɋ䡾)9B*canɺA3nOPfd}Er+qcߍs4]f=܄OA&K3Juá@ژ|yQf=RT>S~6So2D<9IcpA|rQ^s,=q./[*d7FNOD]< O,Izγ!d<Ă( Tqnn{K{]Y] 52^M60G$Y›(MIm8w&32]nQM4˯u 2ɤ4}%&Fbن]4/F: @G-1TGeJI)&&M&/Bt[x+p-P1.pJ G3´ɍgbܝR)NZ=LUgA#--76:2JTbEXhnalK< ʍ>6.[5A4pS+fKC5+aY%\a.zGx~-[Z":h$ gδxPi;0|O?7V%ݜ|$hvPh<$'ɷG*c-j=P2UiqS&FxgHב(|R x1e-0ۘBs@'L ?73N۞6;.c Ug[h# M%UZf]n'94ڒLX{0*AQ]Yy9,g 9nAӱcLg:ƽ .rVoWk7݀oD08S}ߘMP2ɹ b32!7 :1L_focP ˛NeMt(O"?3Pfh^pl&:6^ Ҋ`+2pr\(\$!VvpLd%Mאlp{&5J/cʘ\.W/c6dv-+lKVM>m~ |u6`XwJm\>/ODHNWv2Pnˇ\SgV#ϡPn%n>ZN^LŔOqLet˞ rl[9'%k֫W߷]Pw6`[STWʓKmA40ܼ)Vnpi~Ct9 {5q9>E[<+aQ>:=)#Zc""&IƴŹYNoYҕKA9˨ؓ?ʊB](t+;˵{K{%Url$6AA``E<eQO_ 9j{橡9/ <̚a/ KzL%5=;#4\7FdtYͤe,cP bojr:#EE\z騹wV8]u,rg8.nG◀2ǃ80*t\`Eu[eF;8,-p6`Akޱ7UĀ-m,Y|QF&YS7j5PB`iU?Jľօ=8Qx5E232&h/%<Y~(W4)K'u1zmZ rA ŀeMSRti%}ѕrW|z{^JI={ciD|B<*,֟%kXn-c"VTwF?&.MJukCc[hh5X>by's^(8:ؾsdu̅0JKEY8;}d|R>O<=w.V)tv06P`9K:( (XjKFJCNF֓ߪ[\IPBi}jTPr4; cl7zM}MӟRo0K5,qV]1^=Y5T4Z2o~4js#C7o^YW Q?UX6m輍_POGTh<7*\ TJey> ew%or:(?/g ŀT,TF0@~"Hr6MilN>fxR04p_ P Y}}eIϲ0[ѭF]XNmj/ \LLC o?$CJN쟱O+*ZEt,xl <yڔz9" 1y")CgC2Q!O$S~Sl5p|ȋ2fD6dmH,o'p>yQ&foݴgd8rǨ`2%ڀOA@N9q@d}; #.#،y;qʸv灂pym heF }5pm5\[KgB7y9,+'C;,.4Z@+캩S1˻KwHNc8,2P6:3e3r~7hL3m-=@~KJvpQy)m7|O츐.dL[$YSǓr ɊXI__%}"`A/dk1> Fܣ_ay#6A,:͉)NXϚܞjt;1GsVe~f[tMS>}sisF`4fzPB+OxWjW'Jo_]"`m4&FJF4t?FXvԑ$1~~^s-/XzB@L[Ob ?+ @6>c#Jo=<:}|R;9'_d*)KM* ʎ$h<18-}xH[+:S/]YCi|my|D%_iwsO$[PV:=Xdڿb&+†#StZHtQ]D4o 6GγO(ɿkZ Sn'MdʹTgl՟RH4,S>{{xec 7i8cʹO;٧?FoN PM㛀Vڈ,2_{[ b_sm[ב`eb.iS[̧~K&/zL&;~(Ώ}]Cr\ tRSc:ղө>{:X.vI r[)<.Z^(c Z\;鿘3 Ex8~o!cu@SF `WtFE4] ($+x'{"sҤ-4*a@$lre&>?=ٵoIH,%Ȫ[T\y3cyr2y,yK^+jWWA% \z J}!'>gƒ(:[:1nAQecngJ@y"uNZ/O{HvzmIzUU9oKmVy4cih:/<1_XwY ,;NT9GPS>&/[>'>_(/kO8[~1)I̬Xy`WC_g`'G,u\,? Z&ϻLHaY9frm4A%[ z%i#+fJӎJxy,d(r3hevŎzs0Hx6M-ͭL4) .+D 响8e$!VIZjmGFYT8*2;1'9ZWPJ2k]Rҙ `4\vPGL\X{tDNt!$2@Lҝæk˗V^Fbr&ʱI N?KSKHk~ݣťo餕`qrel7ը-؎9|"%N &r^bΙ`M쇶ٱŰi,EMݨ5*QZu,:aZݫk+h_3:F&38$D^_Z א @ bۚ8Y>4m[ٶGק*p#FP@8fָQT꿙yL#Dvu|Ac:Љf]S6Y1v6i Zf9m+Kz_~!ᡶE;⁹W`Y[ZCu:ܧ>0u? U.z$6nvtx32I\ʽ6u8@-Ix|{Y-FZ님)ysݱCn,i70пqȉUN9} f-\]~1G܀g]vCmN@ङiNf3d3ZIeM>5ڑD͗H]1v,.Q|ߕ_+jCH,XN? |nX|[X("*Gr@D#mL&_GASRۄ'LJ . -vo(zn3BKsYw8;ubg&.|dw*~[^@ސDu438pg[D< |8ŵ*}ў2=1;?&\rldk+F-MDcl6cMīɸr݌ H˭tCH4EM[7ٗ3fnf?mTG'CAvyaKtCڰ2H] YYU,.)کEH'02 ͕LH0LqYi -qڶ19 ͇vƪp#TVN{*'[O]w5̯0K7;WT) ^!zIJY ]+:|>N?:NR^n$S?BcO1ubLS2kc }fY/c JSSt8nos0;`B`'飓?gyk (x?R}ic\9 WL!'DѡhZ[^ܒK([&uzd?IyaN]ĩ6}6iyQiaiqvPݎ=HuuQ>7Cccu3٤`ph菏6jTF*ǵֹ솗r:ޙ0EMH3&IlMHҩ^f&},{sWD}*'Z/wð3e_F07~塩KS~uyomG[X*i:i%zj+pwt(#Цl Wε-tNI+I8NI][y60kDLyhYHmKDM[DEJain KRP+Ǐ-Hz:M[X#U(@:*f2GeN(YMĜ?k DOT'm T|`;(XۦdR%gG_,nA~2RB,V$0u ܂XneOxhBo>q|:c銼8utD˖ en6,-D[ X0G|̻r tK 9:=tJ|VVxKgc,k0`79`|d]S2V?|< Jm@ K˸2qП.-6eѶl ϛyqAbvNe/(V*q{1#_ S`s㵺Vqu {k{n Ҏ,(-7=9ٙ/6y'5INyM0e #ܕgP=Q-)fϝ?~<ݧ~~Ef h`+nfM\Rgv]uw\u/Zv;](c[(~Ɠäٳh6 KP߯>࿈$i#.YgM]Qԭ1p'J_|!G [F;`R0h4p4{+w# Q:)E .^+4&efEaN-NasrP7XW,]e#lN pʕ2_"m R0ha r \+/?t&hTV٣5pZYA1cf3D&pL:6vEW}hrz&s G0hHR2N=M;M MEm&6m1'ihgrR~ʶaʤt%[T4ZI$cGƞPJ3g 6Cw\wN8'&.6f>5S7S(%'x̵8pb>O>;I`؇ 8u}⮠_Ax\e 66D˜gqY@kl;ۥ]X0vtPM3oqi'ϜNZ _NgU6PPS]X-wyLiW̼eTNUhFlN?n[KVD&"5¹+>_wT6 {>է~`; q GPZ;3oyu˨kT8iAǨ0qi}'J?q+iW:4jucưVݢw_E|z^ąhɳ_=2ب{]v-`@y_Z'?I de')G?xC]$IpI\$?[16㶆v5`@h]0^h60vz v~bwC{2tII'( ި'03G~En9]XP9ZcdwNsmli&[m๓aqeпUwš. *G*Љ~/㧡>^VThGmZ~{vٷUtj4Wodɤ) LT>G TWL|HeVu$4禞-+Pl]k),,M_Ke)0Sa,/̓SP^`hũxs}ﭣGG2m'"HAGʿ7d697:W^.,qbKSa8*VVzcaQ4BSC݇٭_`n^^071#쮛ƒ~{t>Y-[q6,i[]*DyTÿhPviPoԉJ;.S`ROjp3ïƝ75]uxb ɔR08Լ p-ݻVj+8 LMrC՟;γ\˅^}P“eО\VO2 VM\J&pdHT_v" 箜C ]g+#l6}4ũ1t7HV+Go8.{L_!v-ʗ[$*% V0}6:;EwzϑϯۧT٤>Aмԋ rޤAZvЍ~0n?x;l5Ė!ċY`73g=-ODFIݎc\WU8״rd`q>?W܁7FܰVA]@dtCe4% `=G+F=1!40F+i38y)Jj7,=nE?>JJ:<1?q)6*29o(XE+ "J Xm_v }X///;V=ߦ-в={p]C_$t(ʳLSneNd  T0:>6hCcz5R"'/܇zuiKbά"5\ 84-QǁBn dl^(9oUHsO=X9qBg! j+@MR* zu_z-`JpY6F-% ,N?Kğ#CG6 =,Qoݏz5L. z,bOᨮXBr~Ke\dנIo~Ϳ=x+Xm^?+7s{y?:CLtFʁbZ!@V)گ~PT`F)!>6"1(L= ?9R^sk][NRo?r=N`uz_0WCnn{Pm<)xrGL@ʮ='SҐʼDRƹ9| /\yՆ+5&-):>r|^bQ~VhD" |nwST7jgsFQVt80ʑmŀUv-T6 &^z''Nbr~jI 2~/{Tݡ`u?5< o0((~v%OV\oeE=a ~ x+zwt'|P?JzRn wNu.v6w?4YZ@iG 6Z.^ r(?sZVF*hkX~`z3|-[ou"@\ $̮CH,&'),.8Q\V|_GՈmk҃kKImZ~3⦡pM88t0={CCr5xfeTͤ*V02y̭_BuIDATU-(V__^9>2.SΥ`]+( =`RJެz ՝P]R%  p?`o}}; :Z$IV_z_m5:vrE)D'߄3{{9N)QSzo|e7D}5g-(CcThIsC'zo>X/#zTUUFo«;'yY1otldܛ Giu0CulQJǕR wu2V' iCZi$ɾJ{ ArSIZT )謔P6{ȩR=Qzԣ&/a IENDB`mx-1.4.7/tests/style/lime-button.png000066400000000000000000001076421201047117600174270ustar00rootroot00000000000000PNG  IHDR,,y}ubKGDtIME/6z7| IDATxyeQ{۪^ջz lQWxl/0o`0AH*,ˀ5BRw5ƨA{/U][s3"22ϹェjϫsOfdddf/#da7ݰvn agCx Ͻ#- 1c`  #q8&2"0$ ,Y83F"SD x*Sa“1`ēVȷ[p-]4 oz뼄 mm1rpp;  p<6$Y<,˶ Xp$C>/D^/k &>@ࡍ%/g[peõN_4SpZe FWV>#='yϱ+_U`7\ XxxwEB c^ XX`8:FSA7a2d &ޏ7`s656]^7S?n&.`]c{9rU^:gO6Et.o,Mr6Fɀȯ~t7xg9<ž>~8=ڳq ܍XEp˼EncXVͽGSMM`2ƣ\p C]˗x_t7?8Mdg).߸osg:Mp^<wÎ] &kF YRM-rx*q/#l|MEy~.^lic ? /t@]?_ aO vC XW0</=] B=zK;I}|JX%;(@b~+n+18or9ǯ%dg66p鎍b 7 nF+NK|yfaՃ][.|-8~b'=Ig]C]]"\8e2~4'aav0<?HK4{VX-0dqʫRWan藴]ey#\Qn]\YS܄sճO+k@;vk C}Z:!*w6x4} ]֜{&xs_dzL'p }U$. ~ml"?|>_ytsowCgS#Gvk p$18#8x<8ȫ9k+=Vsm=}+3ϒtu[^<)\O? *w"n(]#<"9EU~J`Amo-ېh;3H1;WTwʚi5N.>G&\?\ ]i3 S.]? rf~?ϙ~7T.`u1+mZ4 uHL_`>7 [~]εU+| wsC+1@jJ,-؃v>^-ċ|:KXs]eo+ :I~{Ot|?|kniixC?Cp互ywqv~vxX<>ϭ[)[cukA[[Oxq?|O[z? K+U忏*087+i +8V0? u9O>q~rmr'IW3YN?ќQtq.Ng)(xC8x Y pKF_ KG`I08l]|uc%'~keE~黹t]O|GC~=KyO+:AaہZOʗkwK#z%V[+keN3aӼ3c~2iϢC` |}w?6~[@p `\ C\Azي<}dK5hyNL,btV:EUn^VMq~5>llO){_3Y0<=~e|p损y,n߲?J }w+ayknɑ*:X50W˫\%_Dy~45Cȫ~/Ky5`*?듧]ͻ["O8>eܚj}]}%hʼr{FƟy"Y,7SoY]⛿0#~qp0h=+;ʀ^ +[n,~t;#'Wr|9SxMn>Tx>&&|ۯEw_[ƏFp_~oiVo y1/H]Ykn+/YdDt n9/̓4M-C'>W>:\yX' yj ׿2/v~NG=+ǛBz_yOʼ<]j}emϔ3?yyزj#t3w7@<8<6<9_'\w~յ ~> \b߭ޞJng)3^. 8[]_,R5+d-yʯmm'iZK<~z19whjC_4`rhaϗ񫇚͠vc|7Ϧ*Hӿ>P3(jr{~yrk͢cE啯78֓h/]hfz.5Yz89Wǚ1 ?CU)YzAh#VACwAaZr§%[Np' .ʻ~>;6#\C~x?r]sv~U|_Sy[nИV+P j[rڲ<]K?/gLx]ӖN-xdzg}73۹s( d20>,FƕgG5g|OP R[`Sq>O}O?^]/ =82$8;R,?̧|w.9 X^\Z]DXk{U9񩭾mepr-< U,>u\+<֧l?zޖխ b9ز.ҵ?+ل5>$td{ 65h]5̓;x3lpj[#}ߩ>oYe^@yPy;N*h˛[Gfu]ZkO4&~}-Dzͥy2>s0<'C|'˸`{)Xk2S2ѧ\= _{ur`ߗT*/O'H24oD~bRA~{2_/"ϱ ;ڳڼҸ6 p'?/yK4%P(ͶuU?_T._j4[ٗv^ߪSAO|T5Qz [KhwlsŸ{rxq"*ok% 7x+~`aj[+T2tx0&,o@ZA*Q:/(ytY\ekBd'Y:|~`7=ɗMW9?­1K0;Cp+wU6()M/;#yK-O;ە0/ Κ5Hyucj|Y*) 2yN=%g%y DuaaXޓ}em?*ja<ɐ5bh>n+WeQj Jymm XmS[<f2~(g$8u%&_')d3 OfpSR}9|kⷹ>`9%^ =ʾ s2O^K}M OGS@YGL>)*N??T̓4_r~Z(rk/gO嗧-,d{%Z*x' ^fL?Xً IM'Oڒ-$<|]]n]V nuZq<2ugxɲ}U>Ztvem5U%2N;-ZK[ 73Kn93 AY[p< \-tZZ*y~>U+ .^9Дgyey2tKyZۉi&O.<G>_vTzU{`5rZK9 eZ`R{'돫ikZmזv"ec=P^vye54BlI6q]6-5|i{Z7VFT&_Yl^do %O2nhף|a( '~{X*+$xHn]MWݚ2r^Mr'6O4Q X"0dM{Dҿa^ +jafIbBߕ=+>*5w$֭6@ȷY5[!%sFogҾuiR=kC)K.kl-A;G=,﯋Ԗ&.o'Mnx.,~c/0Z>8 WN~{"|[{7AV2Vw>HeV&UH7jfe@>EkS]FKO4<4ytKmե9RRWlY[&jcʬux|'Hn,Yop•7f :|unQKngr$o2@M)\\ HuMg^ܢ ZQc4H,`R]Kx=Ij92I+#7 $ x[e\>_bSQf5FAv y6ך*ǣ{gd+<5y6y+nW*KZyK0V|y6H픗[uxe,äП3bVF +:̘K@w(pfa ;]t~G;ڸ*mϢ{x|)}x/;yJr]RއL~Y2AW7 r󶞘>Jy>-HK 5?_6$P.YǛ<:f'Ks[ZXGRajehPF:$zߓzҬNE'{H^XS&A+@^I/]re:2mIOAV dY}WY'4|鮧]-&ԗ]<9',r3#,(p蘁iNw~>wAs0[%ȆY؁#k~$۩_J)N/=A]>}7J?Oz_xe n%@[*= Km/yi>κ5O^ϖ櫏/{ )2all?2`=҄;`1Z什#|U`cu7TZG6̣_u^6+P}'W>g 4e]k2}LHi+)ٶ]7>k6g -_6Ǜ`ez~nYY~y~2,@[ 2 LkӐ#Gӗܷv~w9{eܡ봓x5#ZauY)(^ۥ]OE% L_R:+g <}R=^=yj@)zX<暬Ήgu <}4י^NK+5~u ;X |/0;ýg5ҰnVr)-P|23SK rtr`,Ye9s;KaRcY^L֗V˶C.Wʜ2d%moɫWyr9E-3Z"KvC;6m;~#8t<=l|=Cq k=J<~@0iC/'WW݊zg쪇<$_In D2N__xt/ ,SlsEnyJ3 ,QVqmxfڻ?18A9#٧~[!9 5\V+GΣ Hxy=pcQ|W^|peD%ե{ ŽLS{|NvI K>kYݝxkΙP0{W|u#yaAK[M ]$ӊYs~ \/նf!ap,uk _u?[D9)76Y:-8P1'O=}sy`f7F&,r+A֗;ȱ6ök}kv Cg,tWn5yɷ յfrp& [?k˖Ou-X4yͶl|`ZǶWkP3r(j e0g2`= [2o纚Òjki)u\CT9o+7mR4(tyL>0{߲&%|JO^vDKWx -k-Bˣ-v߳3yjOigmcV$K;z,r̗kY[Y?~Wez߳/,FfWY3snjE(ovV3JIr+wÿU" 9 yrЪd+wn oW새':v@Q+t+o|_W}y"yG% A L}'ʺ"En!# =l#l"|=-$@vb 4,܂2'Z_qAvrL~]rY^%y;8Xp+G_浇+,Tg)q^ )/]oo&ݳX\VVO`6aKu_ r`>dp*xupSީ,m'$:6|MZ}*ghPZ6km샇PAᷗk}r֞Lkxi- ?=/yiLiv[_F+/`Y`z$0%1}c nemŠGRj͒>X\Yl×cd&/nU8#ف`+T_c:*. xyK@! K(gϱZ,Ղ\wo:oQ5)_4, - ky܀u`߁ֺj,r?/*%pZN/]Cqpk3;əbUeVoꑧi|w y*UeCukVqK<SC)Ov9(0>H)}4u-e#ɗ_뒮>ҵ~D2A>IɮԖ^?)_zmdM%v̤IgiM?o.2/k-Z]i]r5!I-g\~k\EY;(yJ-3ЃW)pM^'s1ֹ0˷АgSka. +ld] ݲun|m寈 fegTZ]+YYg˖ ~PƵJYO%_SJɞvt/{F3yxT ,J2Y뵁-Wʅ3)-P%R2ȧ:~dOr)C(Գ^'y ]Sޒ-Y}KՃ|'MzV7 z0{q|ƴ-2k*-'s'O$ټL ޲e^в3TygW>Ӄ+8E-󔮶oJ_-~!DV9L wzYXa}vپ{IHkQߺr&M_oN 'ZC|SRr2/GZ:m!ݳuû y_m(޴:iA!:}DO :z˂n\Voۑ 9@Rt`eߐ>}``!zg{`ک\k!k)2f898)Jҡ<|57K3ZU7g@Ji۵yuWБ~2h^؆<<߾ޮwO<>Z΍!2{g x|=Bpx+{ i)e>!Lt*NnJN-/+I/KK6_Cyb蟱ó WVZ'tu?_2<59Q=V+75 zy]5$_f4yK& Ye&;0ܙLa51smhʐ($ =T#e͜J+-o ,&<^4 ROv7*:rzȼ 7y]r $K-?5Ֆk RW{ߧ ~2GӲ3O r_ݐ*tVv%S] B` -Vvr74 x)4 Je + zje%@S h4vXɑﵒ?9r 2wL-wzYV 5[.Gzˣ$ \}^;z<$f, XaŦ.p陕u[Or:*` |~{<[/a2>Hk$HLs&r@jRv6y *TW60%%p>12/#`Їu Ln|1_I%`f>lXnlO 'aFGz)$ؽQRll⃉/e哢^=jۜK11uotBW{9vd>9S`0,Uy'1*X |E{XXU`[rp4xthIg_ӚζY#WzV+7=NzG{r%ۃT Y1R2/N>]lڼ8t ɣ"_ޥ{} G!團^kK}9lvX̛diGɭA0`)au#|1t9( r9(.VP)oshۈwVR&Kk]#|WQZFXx-+ۣ ]~Z?s^)4k’dvɷ6[ВmEa*ճf|BDy*X*yg_[?QV\=G\`JrC1}Ŏk/ׯ+8IH+\tu-/#;ე&~͒l$^u~YT$ix: I|-omOׁIFOZfq]Z_8,. XXP寋e\*?%Z- VRXar%hnEW˛lAת`[`'dިJO]?GҵjyH^-.9el>:ɋ:DQ{y[wJ)+2\ODW^Ye$ՊJoyVIʼ0`mu eKRpVFݼ%[٧ZO6Syo}r޸Mqs1牬"2VxNu{02N*`i˂H* DfMr4oziZ3yZ8S@dOcC~j;I)dyx̫)WyhY@< [:S=+4vV/S L5mud<8?ay KKd2WϕK0zɗeWk8t^9?k)\b*i%ϗS61<xuAĶ_'=lK$Mu./=II;ȝ2tl=OSL-DXXɐedN4 ʇu#D Bk] OL|38xm|kcx~ ~B~ײ-I`(Y0AR^) $[/w G<|0ie怣5@+GW"`vRPǩM qBN󒯪f,,t^S!=cA/mP0=pKUKS&VůMr$=ik*ǷԁTd3],U&k-H4LVol\ ^fK D-|N\P._'!B3 +vր{$/ka5By(*I)v[\2x@KKJgڥf>vt|$k̄5"D։lNoN]Dր׈HE'K ,h-쇬X}ocix} ғ&opmYRd> V%ӼeWiĺ2f2r,C<_pe"kZw#7H3v+Z# X 9 ,޲2L/JT}߭5zihOnnZeP!'M'>#3[^!¢}`3PI { t}WieU5ٓ"U`iPlcۿ2x&Ω`eAZoA:w·D;(Jym5omQ+iMPj^[9/פ56i^Xd0XZnUA)}JOi1!|8\Ֆ-*'{Dť !/`Enk6`َ8${dw_A48%x\p ^)_-`-1Zȿg[^<5ԥ//':h-5\`G[-0J}B@$MvhȈS{ҋI]4%<>ik.1QE(_%+k`0H~9FzB8ZUcaI*=PSܒ([% (D9-4jpRS}*)|]rtQ?nu^^ّ <3 n[0ؚ\y:p2hYߤ^%k/.NAlA'}O,- v,>|g-.o'y|ֺc+RZ>6w F!0q~{TJrxi ]*m-]y!굁"FJ^ЖYyyy[Je4{)#&p1givGz\^y6F~(/=ktK/ɺ{i iR K>ϲey#>zxǁE XP72 UzSL.喀UhTEϺc]?tMtwya|Y<9mou ::0aE69C9[~ROe{b2"댧HfM䞨jz9g*Y\%^9u6@٩/VBna&g5&3[  YEw'jo4,nKG˗MRlpqns /SʃK/}ʛNWvcQ>k[.+ %*RhIY:6-Wsmmn )G%̃i(rz 7g2&g鵰(m!vY46}"dC{ym%xdIwFˡV~ nVJ׾k'i;)ԛXM)^`llI' ?*iG|^-]~y2]o[n IqɎzKM߂]>GstcexMzJERi +0`Q[,cr9c;I].$S'Gr"$)@S5MsY> a-SUt\~= \"Qs1gk''rOyu>GW~[nնqNm-@`H'z={Hs%tYMWzoB簃5/+r@r-&.nVZJ>T\gvnヮUp4Tl錙%x.[s rIU$_6 nyZgʼAd@/_g$>rjEyuL[֗5ip?@OڐXC!+@l R'rF 5-|.ҹsJmsH3{RϛVy 7V9l?*/d=uNZ$mޕ(84HRXQPG2 [(طNJ5kVY~ykܭϳd*fDO.'Zy@.f05VZMk[AJ'])K4c0Ͱi͒ІeVvN `yGJKꓞ`X`jE?<0Qڱ0fl[v xޑ+mO7p<-lkP4]xh]dSK6 FCk4`5Nvbiv)Y\`C%=w]m?ieI6Stz} Q׾4@,ߖ/y4 yȄMNCmֲ)ߺ7!d~YR]z$s J!|0+wnV `4@Z%ozJ*ɗ%PV2$\m)klrm9P}^ʟ^ho@pIMvmޗ& 6ݵVk  %P7,=>154BVYJk@ %6o[<̓myg\ێq@e]r߯ .pk*`tMZ?h/ _⏠=oR_7a` (^P"9:a.mlsx,ri'/;Ql}*A4#uY`[vn/.9>!fn Ⱦ=U]4t?.Z+ִJ?U_bto<7"I(QE[)"-7]w=ٶO`auc%~6-oY˒u1/&mzZ.rмx[FNJ*9&.UJ1xMB]免`RZʁn0l;[PLme> 5T|gUepY(nA 9Ccd._DYy{J̶ߕ&i2KPTLG8a"7W im*mv_vx{Tm|)?coH AYF\-JJujeDCgj94Em1,G2,z4Oj.Vhl[zg%ҭ\]OhImQ__&gfƒ.F[E@dE˄s,iQIrK>-I:As(}`g1TǼvHZ.%а2%XZ#? ~)^ #^q:m?ym ]dg.<(qyb e*MmyNS"-^:{:xCy1Ye4 a\`~x QKBBM6lZ^]PRZ`^>ɲs+iRDY6y"/OɊ*L߳Ԁ)ϧe-n~G{ T4XXM_:8iM!iLƳ> >ɘg2(N^SY%yd[zZ:}/4&z4<)Oc\?@p |ܖqJwh}JJۛ=His̗5%vk6s@WW ڴ|S;i5 Woھe`*YS^`6M<__ KQLvI!Qӈp?`3p'9?>0fMB 9^4}R*YE6>jo޶^J"hmkJ& 8f}8u)E.wN& |J\DJ[9&6Z;À k{>&Opaqɟ%;H,8GOzJ9=-߼n)wݲ)Vħ7 -X^Rz%,o\gkv?~4xosryfF, g7 O"?2p.PD*YHeŧҴB/=?.<@o.Mu~huKP]c2s ,+oZݡo(o b4'RBxͧ/MjIFT]2Mrւ1XIr =8ˊ%;զkI;|`JEb♷O|(뫩r Kk|\,X崥y ҹJʁ+!ざԝy@ sfMBnӻwy%lmh(WpѼqt㲜x\1X\gZXKor犚uwkzj.nz{"^NҠ!mdHJeiyJ>R`2׵z A? y%駃|׷dMyq6;-oVq&r? aM=Il1Ʉxo4fӳ,r C @~n7zViKZrzyry*<]f#4@u@A<P@ʫb4 }On(r K4PzvYY%^5l '>'ڛXn r=lٰ g38ʐJ'/l^@2ŗ:_UwukUE_ڧժ&)dTy?] вlhX+ F}_eI u;~-xʛK)o73Db4(YPPCd >&rA~S[*{O~!W|_6.2a]yV-oboqP>em Z}x.G{^]p~dVWʱ9GsB˜͊Ȳ bu 9=6o&'4CM-͢@rהao\9sߨ`tMjV%D6Mz;MB 4[OaҎvJ ^_iyzp\ņX6:@,2f CLU-Rh_,:G.IkҵX@URO@e TTR/ ,$"\iGԊ,-m%keՀ,'hs]&f_|X// 籤|}>4ӘQ6c.~A'Y1di.)4ZYDps+X5m U]o{W*wkȉaכv2(7+ͦw9t7l/ڕ]G޾q|Ubs GU}@,2.1`CҼZI{9C+^;%7 Hۜzl%[נkUsX}{=dTF?뒦8 IDAT ɵka>G{xG4ї&\=T6gN+0/e^[gye@A۾ 3&c &Z/b!HmU?\%0rgd,ʔ2wGS+#nB^[5! R-M՝si2<%:q}BެmH1g}kC˛,xQ>3dymc!ȐHO VLm{'`ȟlMH~ 5P}'{/*rYZ`Ndz'~~> zJe\7"K2]ր$ϵ 6Oul,X"8IͬrfK>k]y }bRus:Ԙ>٠% Y]%S<$Q[# ֑Uَ'{q/Z~ڿ:etPIX48w ~ج6B\VVt%tK$*zNV[W'{%aԔCPZQ'iOM0dK:[F&>:2o풭ɺgeɖZ:A2.\KfNM}Oy5*yV j|eyݙfPfrl\MY 2P$9rI[e%+dCji Wu2Ky*l=I* GJ" 9TP4^"CWo}RvDG,Ӓ,;HҔn B:Wmt%,BFR >{W*4zWI $[4e" c&̃8ޏ:Jꖆ͝ atH읥򒬢)Lb#Mnv_0 $lL$>ϞQ6.|Z./tD[FMNKOZ~?ЦmAWX*/Gtz%YpULBfJ'; !1l!Ш+>ML"ɦcӗG۽*k}[ߛr\Y,gP=\U4}.u ai+GQ{_|ll7ISuU߹kMNd7وylʔl}&%}lcE?<] 6qo))NOOXP򤼾, -|Z \އYnV"4LELnj~eO m5vd?WA*+}ؓ3Cgw8Y^JVᵟT_!*H)2V]%6}jUO?L~Li!KAݤu©6W stF?^{\¡OuUM;YִwB>:ՏP$&;418v9x|rٹOI.y:Mplide?£)סIt|.}+jjem4lfî1(sۛ/c)T${ #,biVkk9>!ݍv|;UnSml&~tz&~V^S_E:r4)קo|y!Q Ө}&]#a n|a~1 i2Hcku~|ui$O>uRʺH:6ݦ9>BKc*gii]&z0q(K_/NU+G1a=q5͟a$:wN`a!|an 3]yuro!}"^4DC6\6br)gQTv!cf?tr9cHX_1?meHn|,zeK|n+Inl:4yPw O>;;QzMDmGWu}1|tٗ˯ Y*K>M,JP$d!V}Ŕ(4*î/Op}^n٢.SXOO2NP]>~Y'-[{7S6j?珲tzf]>1 Q~`ӷV,^y fjp25eKmEnsJg!\IL>vBLt\n1ޛٷl6RP4,cg+a4SS ۊ|ĖLztɠxtMP%*? '97yI&m0ǮY9ى YS>2OoJh%~1|q^TX^^TlG8V񖬓˷OZdDDnjf$ $ !&:St6:=Y.LìoiDi܂2a;SΆ ʫrm.6]Iͤ$IkcxuTC+-  a.Fߏc4|\l%j*KuL위08Mb[W kd.˳G8ۙuEU^7ițMFN.r#$/$177O>%sV/JE '{f{D ^RPԐ~o^"B0BCg/2ơ4Lc6^XuF;ϰhE^RRkKpܑ<%ݱ e'wkhmۆ+dm83.y›BT7DlaGZ.U~WAe̗4 ֣ZP^J~oo=kj{lKۈ$HR$mt8nB"=RiljŅy<ڂe6IGrĦ2 G8i=?Cm'hEE. s%&)>>?+cf&LT[N.x֚fjSlfZ0+ҭaw J4s"$}]~Q<nĠl]mm,#t,{L"iY['7'L1VVV\&g>hZRF  I`& I0Wg{=q7vr["96SC 냂(Qˡ][P'Qt^N>C~Yb*dؚMWN1U^gL$}Z}QיO_\<ڼ1KBLO7®2gc[bMx~d>o=iJ/BllrYG t6]/s̲?o6fWihU1?xZHقk$n>fByQ~a޸ÿsLO[ 6,lRȪ˯VE_>˼RYaa>up,EN!|Icz|733A{V~iV:_4d],2>~do|zVOYkw9fr#o_kQF&zW"拍(>Z0H$G?\ 2YaIsN$$ӗ'"TS5WL(amӿ@^cf6u7AßfbH q߇-[tMO 6]QUh/@y8+B,4Ld3G?sz&CWw(311h^ P|l4đ+Bk:QdGq_HrY4Wx?yai33i/yYa֐E6KjJp9܄uq^Vn}Y/e)O C~4=f -E tyjLp^cbr8-W{&,taO>0$S9lp-mݗ:z{{ĕ,O=MGwmCrOSOGW*"|xq^U ګc!d2ɒ&9Jjr~ Lu-7IIObY8kxիLN⢢+(DIO ON9K!y`˪gq?O2FV,{2n,핷/mDtX2;E=J|%7\Cauzѱkϕ8&^?i'#HBHHoն5B>27JUD\\t@#,,zi!s`ZKSDd* L&>=Ҿ#,6*Cowh$ÌWs$`JX7GϺ 9 kIo0_BOWr?)@¶&A|lu t;P<5e)Q2Pu`1 7րw´$毓HMe3DG^:߾,ɏC2^9P<#/ɹ|9˱-W2AR64rN"*5L6k'4Avh'#YW\Lڳ.2;ϡɩA*DO,ǂ O X_ V*KOZO/a.QȠ矐INJF&Ru$##Y65b"*$fgZ ,*FN @K+X`e؎q{(~~6}JӧG^vT}dNAm$Y\Q}?GCyr'168bpYE6 ,J>7y&DV(v^g"-S5Hd[n$:c~>pj$xoL<2වj|?K1~y~j5H 1m!,pqE5NN}hDj9]J&r]MĤK9+!FjZHfEܰyӚbf6XļZfm#ۮ`s KOcwZq^خº\mf'1ߥ$>:A62,}/9R9=|4->(haaD cX,OLF-JXR8 6dzcO?"ĢGx r5Ƈz*hRLf|aͲLy,Zc|8S3˜鿚GsfJX_7)g99WdgvG' D &R#)a&.0!AXt>&dK_Pi/sz)Y}IH [ ',yIʦg]Q aݤq ٟrW䆼@{d&tij!Vn7yn~ye~H~'ۗMAXk ILN~ujҫ6vB+vVH&l>0Cbfy6B|l|6~~BS*;hz]0^w\Kȣ[MCXܗ>r >,yUGu7sa"b6|Wie_yl:xV*-\ʼnLEi6aRa_c~"$ͿNhfr x.MU%soNM֋b }|TҼ{y,G!ZMEX2UW>9|c-ѾAо*otT"-E*MEr_ץ ʳ~k6ap %$_.V,&u:LE,/N鞺!,moBu41<S@E'a [MIX7[ߐRY+ZYlQQ,ۆ|MTr>ܟ-IvEij w4&GF-Ǧ%,^"6`s֥Y^vr+Pe~PO A#(}~%z|( g鱟pz Ydwވ2IXħK+8SǾI i9W,E8v>>u7qF8 Ri Q^.r]M+8CQK`BR::b 9σV*s=5&fO' xMxxcJzG>|8?)hϕ겷%q(JR/yI*_!klU<~WdO4x$/ {Y,YOX_9̕QW&mضu/aN֊s]4ьhXj aq14DPS8+2ngv󷁯&9v;cGD ^WKmhkR.<˕ :scĝO>Ijxutц{h;/2"kŹ( aW#$f",]Ƨb`# Q3;YqNDX AGKOȋ€8??΄(*9trPr<W&9xcV_]U>-iϫ.}+]H6m_wJ~DOrtGI3 O"C}<"'+8g ;P`ORݽ+.=ȅ\ S=ʿٞL*mᕗy=Bta46iݚ໱n"k*Y'rvBh>חyqyCc~Ie%x 49aIa^]/Y[q;#,R'e!JZH FV!DяAp2WeNaQ:b7 $7n9}Kb/qɞ7 DO0քh^MP +E`nX\b`^ǿ4 8""{ցaeW7tzv}ooR'6EJ憛fj݆|vB[=/08 &¨gcۉaG%߁һs_䲽obKͧ*ǑeHr4i>HRITѕ|ZA`󈈩|IS'bJ?װXh!pQu~ѥ Ż_/| mޚPľWuq2DxNVEFg~,W&1`'vɳ'6:U0DZ4NXEYf^V^b|nj>AyePO|#98t͸ >MV|j\j.ZR[EZW2M2~KJNz(c36#5OMHcD1 @$ _t{vg~<ޝҽǃxBI Enjꘑ|7V\iEgEq٥,,ZM\ڃam}1o- w^ޝWs]NW1QI ͫ^aan;YƔWx 7wBtk]:-]>@m^սf4JFhyd PQQͭ!cCeG%hDRS^No8.b#.[Nts#/kD+ˉ90^%t8|};w;V|qzmn^]<\.Uqĥ"؇{!B0I$0 L@sBĜ(8RĭW-to|qv,l® 0"Qn! @$VIGXz_aRtAtpQ*eIENDB`mx-1.4.7/tests/style/red-button.png000066400000000000000000000710001201047117600172370ustar00rootroot00000000000000PNG  IHDR,,y}ubKGDtIME ͟U IDATxy%Gq/ݳ/EH 30 `-`lql ~6o`6fKiF3gțyFDFfU28瞪UdV*`iiiibXG%к88] ` %3,] (tI'-p *C-w<\;SWWEZ(XP8k)py..[2wYZHs@uM9;VWص*tw 6l{02׎-RXg \0\^U1:՞g w7K}S"5I/pWC`7@~ԲNdm84idH|7 QW]EZ`t'vZ.L1r-[ lSw4n_xGihmh?^WW9&Z`98yLQs);єiWKǀ,>Xv%xk \f)iE Ȗ D)zd+@tKX: % :,\yF5~m%1yP1P  ..8|`Æ3ǵk!vVt 3>:ɓpp8p(02~O? ~dpunA`pp tEczX3{J%1!09I54\z)e ],#ǘ̡C=<~=~Ǐ> 4 ^'Hm||3=.HM"`=t V8i5p2MP  `V`x&vUv`vvv}f \1*|| J-,"`"=Y?YQ"-TX UWx5Nw ~sr!ԁcp W>]O]ifjJrK2B)x.ZjSQ{o} vkh6zKj0jAϗ &(z )H\ 5~]~y5G;e 5>'4 Y `[ |>H-V /OYPZx끷Yqӱ:xA/~-S;apQD{ r-RX5kZoMpfY) @v-?@kC526;!)G?忒~hjm_ER M 58y~7U"jUl5 /?3-ԏt wWE@ۀz|??`} 9~ǁwm̡Z'J-irlq55|g?~p!x~>ƕOAwc`ppkT}!H-YhkfcKz9 T?p:]pG g)+n~~CAʕ֭s ?vlEju3߁,ݾn>஻tū 燁dEXFGoj#T}.›E<|S ,p.a8O'_]|[+Bnׂ[H߈PE 7?[LHlխ,ݟ"IOC\?X|"` XR+`p.ݠ!P]y%o|c1MExڴr Xg:{qt%>Cf>{wDo9 iV~ xӛtj,b97͖?`cQ_ݚi6p!(tn7>~ZV 8nAB@R75=8ՙͅ-:L=p'WfpDWwLZ.m^YSмp?qj:=[B>7`Q@=bX7gV(Υ6ͳ

S׵HgfBzˀ[Ϝ\+Z_HzUlQɭ[U4'٢uf\=ypSD)g?~Ig`m>RAT .p{ i!&gQXuix;ZAh6ݻ~x~RoR߷x?kaXw+W=/wV`ueieR46(*TXk`\,mOoo՟h#q OW= ~BV^T+%*k˪KSt|?MڊɤX.Gi!l|c\E`ӂ2綦iܣ*ɟtU}UR]VOϛ kT8~kc2Z}io=ֵ&x۰{r`i61=  V{Ar4r)ƀ'>޵}{O')| $`\8|. @/X^ _}m:X58U@#Ts_YM9knMڨ9@JBif#eJ<{tZ5m =` ]~Wȿi`VEUp+Wʃ#3hm>˫ V[*3Zu;壿_Z~.Hfj$zW'ʳd99~jqy6m><0PQ``q sK v/+J<aӋ(X]|1o]Ў3Hee7}J$ݪ`&/9?R^G憪h9kδ{KN.;8:&yCv zl6ntۀ'w|Rlp1`eaFZĤ=R9Lsesb:"K p' ͋5Op1eVpkV/zQxdceB:4وђ_Ub#b#WU3+} ]wMv%N&:w_܆y@>Z6.ঁ(X ޺t.j@KʟmkKO $lL)[Vyi^β^r{Ҝ4 `#~77xKYX˰j\`MX6B[/_A7Zms6GGݓ&<^=/,^{M.٥9]\ ׮p>Vx3ݏ7s(ePƞy1?WTת~XchHurs*b;\ u>}g~,º8k-=(0@ꪫ_nl`s s`i * 5[6$>lD`9FS3bAu,mu_S3R:iJ$1]hG_ŞI1R%m_jueѺѺpmpЍɁ1(f>(? 0oϬ><|0?`ɒu6˫P‹*ٲ-6\Rk{ HuS ^Ĝ_ծd#L_GYEEcw,×ms/0h@^y+'E|#=PeFؠ1~_U6b) f)(HiC X#u'EΔ7;?ӡSéx0p/F `V^ DҴ s酡68r&+u K)?+WGcSۃ8?,`3V7o&j9l_Ke'^J7E5V= O\1紀!۞SXM׮6l>~ =!^Ĝ(vX4ݜ2bW/*"櫵NZݨ65?Xĝp>i i~pet,?5܆Ҳᦍ!Z^`C+| .M l?(p>E./)bV8cѭr*˭ In ,,si 4N-%peE! d_P߿Q#5a^XCwYxv!:|kתXEu|+(IePR\]_@R|̵[ YK:ª_a>(p,.V5.Fw7k|z]Q` zZm E֓u07j; ȵ?g}bH쉡E'u1^i`&c}E?Zx<(p/yQd}JxKV ׭wwݴǰ4\xQ[\JnV@:)*eܣuxb6=I 9ZI[p'Jl(p+[-jZ#%pjͬ]>Ig)| X%xYlXɭ[m+iktu-Ǻnj[2̤)f3,;fC0{7pm驲#vY[m5RigCZ`=?nluslZd2464g3&G[@-:1AoicIG+i:@qJ` }5, ja: |*K\ըE;?Eoҵknt%KMKRuU|sALɶ6ՑbVhqZ@N珫Wau:{BMT[YA]E\M ?S*٪@R:<Nl#RtR5뱊.ԣ6Viyhj~%oq_]`@Q8@~S㨁jv 6^80л(rP-ζ4mIs(6Օ6C\t5VgN Z5Kѕʌ9HIO#}VRtYgO<1giQdY[_WӋjkN2$?V6>?cՐOӡ-IFҡ!N$a~(Oe5[I"Nӱ|C:ƮV'nLOXmlʗڔm(Z̽p3X> +%KKEUr 50Aw}^>ͣ}4 IDATes&Mt2gե:ͬ1ۚ*|*{(R%Pi(_+\ݭj 9] ֬qO 2Kt2u5|rq ڢ2 7AFzѻE/ eh.*M,\g|:5yRԶŖ|ήE1ki'NVg.?>)f(Z;I).tgTL{;+Q.y/w`>3n(鹴5rkjYmRh,NUڀ餀6୶50姂䯥.9KM&kX^.ʺ`NWjT;Jꂜ,o~F`h7hi,~ZK֠\ xz-dM,bijZTt% LNR8^P5u=K+EGr io4fihﯿJ`'):* %KށE▁ZEi:1fC'vb#L[JUf9R;ŦR:|* Ҧ1֜'CX$FCC}79e]Rv|DZjT(vSB{Dqr6<_ˋiYg %[$ msk V[tQ[ }H"/gXݭmbKܵ!u65sqn ~`l nJPwkq?/˴?[LBkj+LNuqay+bLzR CJjSli~pu)Z~'O c3ZfJT"^jg<9Hui\+ @YAL{4lqT6e=˗b`$ lI6bb#6C)ٗbFCWteq27NMˬ 6X%0lk_xi´uS db$ t-@dPlir1,zI)ߒT ++hI و֭sʚ; &f{}W{`$xO/) VxES$+5Phâ5&7y\[֯5/ZoV'ΆcrvliǵuZtXB}ν^ll4 x ֯.$J}:";k^3a \4TOukʨځid9В`lp[hzmщ2gCrTW9\Չw}/[欁t#H)[Bf2^SE10u)`?Ɇů.zj3Ǻ/R>7֯:ruBU@֖8˲D оuKz&K\Y=L-/$`խw}.˶WnL^+/v#XJs )S7/!f[j_4mX:VױiySv.@=ע*ϵڵ4ixer/{Y?)m1iEktژ4^mjУdYu5[atx:iim@I mlC%OIS?.)L"V%= 4 cg?I#eK`ǵ> ÒV Gg/A$Rkp.|zU5J#.spwu,5xɲ~.J0ِHid#9{و ޒ1pe[ʡ|c f+n[=0 V;8NE`V!hܻLy/}0zΫiZRXB7^x!pY<`ŞizlKr(8T&FaY1 V6W7OX=,d V\ǁj rʔ} -zd4R"0mʧ"]|:(KqHˁާW[bUX!1uJctu+ lh6R@#̗Zvyξ89+XIWY+ pRq| dBgqeh\B%Kf &ϭia:` + \nV(<|?AEKJ~傖d["júPp1 pIΒE1cLg<\:9XTU C+PE %Z* LvKh; ְן4p*6mQ=Dp\t88 )sɦ 8R,S|JUKxAnM~p~r@}ebE<Xi i#|CCӟO LM }~*D[-…vU47(JzҦ8`\!/6cHѓ4EL2 bI| 8?id |&b~q`AeRGCH$/XxGYbE#aS ?? >4"7(qOk*.vw> B EmR\! QIٔڒzBB%P%@_I}ˋͺ^E\;i[ڊ޸@ҔabwJ= #pk=g36m ) xZG14[2,1]fzvǢGu4@b|@t\s| ɞPQcOH,`Z$[}'NOd3ܪ HPlZ0 UplYVҵU.|Z%V.Xu[xZW~@_rͬcx0(@ᦄOyWHh$KO4 $0bFbT[ӊ՗lj|)Rꤵ͗#]?g/IG% 2%x-HK)8T$*9N}5gbwlhp,dְ6[J`OEWE.J+gYl ָRTlp  :m{NV"s_/P) xVi:sCmHe4>(S]\4unu,>:4[ _g6)zj2q8eAJ ;>\=AB{JKe-阜W(`4` ӱ~/Hm*'#Mb6.5IŃouޥݝYvZKBs+iYǢd -Ά8a'@+f BJ,k++I~jD"*˧< xbppl1Ct(q7he Hi6) W' L8bd]w%tPJ>r!K#ӡa3 nr3i _,?` Uҥ}zI. HI6ŶeR-Cj%utFȷc8)ppupkj70 m_QY.hlOLD XKl=SX§PaŹU_:R{JڦtDI{ʦ+'֎)6bV+jC< $"3ؔ0&˕EbOHcC,GB[FٞWq;)`Q.21|;b/[eֱ֭$qB*ߒ<IT~Ln8_8ȁWlJHKIQ>ͷL zp3(m}9~7t*+tUQ6BnViNV$^!q m#KL;7c9&+70c."w.X驩].n4N2}{[7(W,: U(+-s&`zY!RlPɮX[bi5k;Mm/”l xP9I+J-4rcSQ䨬Os5<tnڮ{AH/:S? 8V> E 鸛'mUgb=U-CX6ȝ\9Sv6o5 EmkddRQ= ŀF0 0rҁ H.Ю1GJ<.u:Χpp{ I TXhPyޯ5(o+H\LDM\]$puDZKw,G4(@Lk)*KU8P Xi50Yלs`NGްFM8Fȣc4Bڠ(O-C1izӕlhuSb꧵sGn[A+'iq n)ۘY@f$<N֣C@AYx ٌ<_*;$k1l>7:ݹR]@Hj!Q`lSړ:.1p%ڣ H? }c:^"4[²ӑb4&RX䘀i:Aҥr!u0²$):\^WZTbG0Keq`*Mj(5>~Rc]7X65Tʍ7rʧĢ0AatJ Sp"IQqH S @,։C9Zm 8p(u(Ww# S@bL,b* ^_:JK*2DN t", hт%rr?Kr҅OӢ -ٳ1בSSn 85$Lo3׀H\1p TZ4U7#r̔w  E А;je inڗd9:)%B[(P? ʁ@]ΞF4`(q4H |4yn*hvsp~HuFY]٢JŸo&HPJT" r,I'ݍRvhh]- " c EǏ`!kx @іENhK|'prVʶ >װ&#,!bO#˗JC9)"Iol ƁS 8ym?N=Ur6:uR)5;RTuj#%q=Ҁ"d+6mXBYr5ʑ:|9:/y:'NףhQFK[tR'vlxo@=@e89- ̬awtZ^/KYvi#pB[RY@(-ڐ.Vǔ;7=*+YH(!I ^Bi#dtmTi<-TlPף6(iՕr8`mwc}5N qg藔ؕlZns>rƒiONNNokh:ވ6p]c_vٌS֭N+ȁ\J/3:֡~JvK$ʗʒ^$>V+;4|=:np1rr!)5_+GrNyGF]甧sxV~Zq~(z11zPr4: qpE97PȂ'Z6pڟ0r*XT\w@k,F0Emr6|ZO.q 7ic,b -K. DT1} XN*hMh*'dF~9O@h N4GNEZ6wik<EO>(KW߷d](Ŏ8] (+ q;m+C|ݣ%aY"*:`nqvb`e2DUuUnFX|,K2,pC?tINx8<mסRKĤhZ*ˁasG-+,ǃd:K*V%RA+cJEۉ~J/i1XkXYt:ؿm4'aE腒:YTi| }#Q e'&GVko̒`Ң-M>úU i^*xT9|;j\Z(piSNTe-w@eT>2&k]jVT%y:'܆Tѿ?>ҠF8:!FU F@#F.Zm 4~*iaMS^ ,e9 5ª\pn*]K]pJ3’͢=t 3[R$pݚ0.r9Xzv=sKV]?uVX7-O^)y\>'#inNXv&m8$;e U(G#fUX@+'_d%,1n ʠ4 Xm`Wy.EojqF\XpE('ԵcCBv`JG9xZF)R?i2)Qvn*UJ7^_Z$yNO `8PD1rLi]52@k8S;$:liV'c#GUZ֛Eu X%J6.oqz4˷H1L"WhIu)0`ЏtQQcUhkyByIWY6a]b R{cQU xb| If!soX"s.S.I댲$NN%ŝx\+W/T[:N)ayi GHUT0̮^㓓Xti5r,EǿItr`R9z Ӿв _, ?%-bd4Nsy5i|.DY\dè36ewBĄ r:nYdSZ-``K4&1ΤEKw'wvB ,J\\:H+KjK*huKIx?$٘|L7aFfgKXp__ dbVJd䤋,q!X?0 GW^_+v'0f a);Zs.IjT'\JS]OOs"YlLϪɞ6*XeAz*Hr~wQhkhhH}Oԏ#OG(Vι5GW)c؎ҵ\T\:)Y@&[4[]2D'XWǶO dgas@̪x Qi'O4}G($Ҁi:mx`dTA*DRJjC+Yn097`@l+5nc ܠY(F Qy`fQGaE1Xyssx4WrJRVuJ;1$9M6b7 >*XpB?YE7hP`&=!ˆȿWJ? 5O9xi\:F΁MLbKZah/YzVX#' Dѱ13YR@GN&Ry9GK7Eu&N*@IMEX\&[ՆfSoְۘ ۜ\`-_tN >rl'Es|4{QY9Te[DQL΅vNcy2t}LGpr}b pV-T'Sb1i.Y,/ƣ\2c @h RMU֩H&gFZ&t06>e˖ 5QU0kBGK4%mdr%=Q]]t3u{xO|FZԽFxN> щ ԩS(OqwL 484]$9S Ma秮WYA @Y۪ (\'Oa/i*ڔ:]4 Ju `T=@igibRY5HqKyZ10:YK'@IvkD=.*Ky1y-R}rSNPk*>1Əim)Ň{@ БeJ;ۆc%Novs5ht?^XtMu >Mбy{ 䔙+2 (g0t&~\st2Rrr-}yѱ^l*I@ r+!N8avp^Qŷ^_IT𙭟\]cQjk+DuS,\G%/ CŎ./,EG˷V4`TpHMNoma,OJ6喟INKK2k GVO;vCC:lR)ņ|k9X/>S)SΥ[1biVϧǔr.u^7 5ՏFz XPGl,`N?z9꟔' dOe)#6drl.P@ԉ=K+v̋? `84٤Nӡs3eS^)7OU޺Q] \|JZ1٘F"(9v#kܜg9eGUXu,V)mƩR`m9eXXr4>6EP2`u+n!mMjZL%&KM:Wiy\$:|>L@s00( 6Jo}餭eRM726 4YK;ry8&'qwd\k/ > U᧟_o=sN[@Xtby<c21:i4爎ߏN+kxS86:|iR!IsX[{ xf u:]lj\ ـ{EY56V>4Y::N K"ߴ~"sMB=ߏ̾0h*Vۧ'0r`\OTrJ4yXkGW?=S=uPS7LvT[ ~#L|)5@4.\l,m RA=fȗs@jߐ|\Y뉮0 ^ne쾪3q}YM &:M 6uJ1^ rU)`WqО==O0O/| |ôtnK{tZ4@{|8)Ke~~7* /Dӓ86~7ʥT_b@_XR\oE]SSS8o_SUL-ԩwaj?&ٗ@ҫ JٰNڮkm- O>Notuٯ pOp'/ *erT~]~jR5u}sSCf):*6%ߏۇ'|zbtv.b) Vej˯t${9dU:5E5996v:_ ը+[ޥstsub 1?_voZ]k^ etM[M X_އ3ݧg5W^ɆEjiBMb8ٻ>=Q?S@.q2! 8BT9uS*xqRkۤؖ(/=Øy-CKjܦ#,>3=5}=Tb- T?RU|يٶ\^*@iTZ@va_=M-8`]^n>q F_eٺpu <ΞT/Vg66MAsbn0˰ў50pKi8`0p}=ԳwcNh6f)<6HR@,w`ǀF*ꃥ?6&0= &fTF.Ѭ~44w_#4 J\Y#a.k!0ObtwdslЬleY{uT@F/lw;tHU$Vr2Zrӓ-^xf :}gf LoS&p{Z: `4?52ǯRnY 9l,mJ7Z@X0M'Tj⮥ N.gW$\~'`XʋcSRž7q47l*4'yICu,ݙҩ~W>W],]MX6R;D{r2=*<4g},KnLV&A*r;~ /vw.H5 ukiL]}ߏÜ o~1pDӜ5n[ ?@{bO~:BҹA,/WNM/8\TuVnk t1gesEs X)ů~IDAT cǰϝs.|Sҹ2)B ыpbÒo&oi_-mɡ+bxzG  /ǁj4[GX.۳ąͱysƋ fkrAJjC*,мa`b xSln1T;6Mh^NtO4UNȫ`,Kv92chۇq>WWǦ0¬$LUA41P$mMirJʳDHRtoz{pe|(5 |yXp%ބ}𓣣a^9XS-i`8^ |bf1vvp[V+p˱e)ozE%q{)6tZH  )c/aciTǜWeR)4|eYb ٺPJ@N4D `{(ǿ>`)\)S B)fvx#< ͺy+>  (Ev}K8s96[S*FZuǶMŮ/~'|f}8'aZ[O!{6vx#i:|9S:~M|lhQQ<tUFyZbdFsIͲlf)bY)~`P- ,e6G[Wg^ r̈ԦNgⲖx,Qdi;_ƔMȋ ]7U ᮨjLjL(F,]]-KiC/?]^&uL+^B!Fb*z-(36 *={eMMCͦMHzلFB-^62FRl (׏%,4Ex/.!@a#~T}[KEcyEhi8N@;\t3uKof[br)9X\cr["_8e@G2]]&52#XL*N+G";oWa; [8eؗt[m2wu5M;wҸ};LʌhZ,e.X͎avLz~3K*8,,& /ڠ.Ecg'כ2 ҉wBOv\/ 雗/m?i_Dz,| M+.ձGhض ɭkpW`3uZtiԹy ==Dϟ׻#g$S}_;$`eIrO@SW < ?eEXRi#\VN+abb5 2qѡ"}k/Ȝ,mZG>>HΝxB;++*`)ӉW\r2O 0}T@jb,Q(A+ʡNrYTYy}Jǡ'Jt\ KŘc&?R|.~tۃ#X6cS Py3+w젶M]%P軬lb':uĬ|y 0m8t\?C>{jkiؾ;vPz=.*h0ٺ,Jlj9C fu~KrY௭k^6V ~ | Ь$numjn7oʊ{jF{U&LPW2{<~Į]KwS Jpۊ4G3P_dM:Dpm܈{w]x #,ʓ$YFFy-ZXUpNp` 4t/`-zvrȸJ{wu5QnS~=U6r"X}EZJ$1 end(.Θ] x}ءp8Lk_KL㩫܌sxV][+W̅`)% )c!auC!PHΛ@J$,N>A~po;,ӡqu "3n؂˅wmOO]:@7&Ta55r#"bHɕ`ՋXD$B"aazD4J"E4i'8c.8Yc `(CpGB% !]%OYgAiVqbׅHp/*AwJſ[Xa3\пp?r D"Jnv;ٿ4b`De 8AgpVa oTB&A.U`^u[tfK888888,kpWa7IENDB`mx-1.4.7/tests/style/redhand.png000066400000000000000000000200721201047117600165640ustar00rootroot00000000000000PNG  IHDRbKGD pHYs  tIME 3x<,IDATx]y|M~>( "HQV> Q)UU:hPzRJu]\PBiBEDD99Z9kwg=Z888888888888888888888888D),rU0H#ۜ#Tىm!8Ad' ` (@9cɂ 4$ p.w Y) $m"!Eچ.RT 3 49m0L)c&f=(cl+TRJRJ/e-JcPJ#VboL)}RYp'l0t0;]X Lfse(4VuC]e J)SDؐmC)N)-۸6?! [R:ֆ^l*xE%#wo&&+b}i)JFuZ ` aa+<\0N<5uWT@m:t@P>M芋Qv~ 0Cpu٪EeB:8:A.BK-۸W`v2ϖ-vDN M^KKݠz }=s&&u156v- l0Dھ4^^6O x1J~ζqwGOHر$$t !dBJSRpaLzγuktZÆ:3CMw?J:l:/^ m|^f=H3y6z…h9tlu1{Fm i"KXn*b)ANJwe:\0AZ|ot7n|c.Ńpv_ԀE;wZɢEۆR\ۺ6דE-ÑҔW2QmӺPq٦637lZCEΉ 0AWµz%"%lnb9/GDRKca+.bi9R4ټ.TuiY^@-s +W \ApuvApipU],L],s +Ws +W \Ap \Ap +W . TNJ^Z v]ލX[ S}=P @􄛏. .AX}yC+A8x cp +W vBJhPrWSXUgQaKvѣ;wX ׯa(;reRW֮qMouuq3V(ؼ`= @WRf<3gx RG>\s^O0Jqu2Ǐ󞼛ۥ!sB{ _c O2EA4h||  D3F6264^GqӮ5d ҿ!t Ovh֩uxm Z-A[v)5@u:XX}֬m3A68!!d ̞ ]QdT?d1h4>C؜w#gX"ƍCsϡ>?׶l-[k(6rUwfwDz _}E?eP061Bj:bOfqqDj6jBG=^8䷻K qHX:ur=VawNkxic 4.Wx8]Ĥ$:>:|88)S xxpK8E˸1iՙH??͝>w,gXz}=̓Sw_.u+:&;w!߿]j!fL$&%eK>]ٳiɁ v'VD7ް+7C-͛ǹ pGϤ$9/D<qehw40憸3kVNcQp&-ZmyNt>OO\7O3)<Q~~HܴBD2O 1{wދ3- Ɛ vI'ӵZ$Xx l ܯ|rⷄիp#$Vpr?>13XDA_3,l1nZ],Gw<,B|h'A@ŋ pTKۼ9by9s t,:k>z4lq ~I`hSviT>J{o%5/t%GV._v0 u99RJDj rĀb }yWjxAkkjԔD< R)8NEco 6*)E4NՁJJ# TA:Aj)j1$_1iR[gh2y<+lljF4XM6?||NΜ.K$HCa" VԢԫbWCHgEgQgUEa[#X vv_)KRPX[ =GZ HQTCcQ'(u[tŋ5O8|wX>OV?XFwTCvZ/*eF~qDRJ*RSq7we/n&)VRE/ A0Jk|ˡC[ 5xy)VdiJU]aQ/OwMQtY#0ŗK~ c|Ʈ]3, ݖ-y"Ed%Q/j%o鸾cw< :#-[YG]vR]s7DԔ)h+vG$~*+n a lwg^ $UmD9zmx6VWO?aH}oL ۷\bfB>jƢϏ??!A oWD b@jU'h}m3֒O]i^3W.:.:i5rZScj LLD={9q7bBeˬZ\߱Ckޜ d/o.]$ULw 6oV=@hݹ-~&hat?xP_L ":H1c$W.grȦ߭w.?dq|cb{Yz[j)Q:<.Q1 hva,/[CUJCiJ ʏEcQ݉L4Cp<ݻJf4S92(1#!DA6j7mTÆ)zm 5 R_G}~>srTܚ5gVn׮FY_@ƍ>cJ! Qjpxß~/)t8t}0TVި_D܂bjA?(ꋘy+v^۲ nwEd̟/;9BH-dzE7Ғ%uT**pbh4+~N~ [˟+9I,Nhd;SݔҖo}fƐ1o2,*SUׯctD3IVb5A*2Gʳ//$\)֜q~tlvFȑ]T$c$qalYܤC a*V *OƩ瞃X:40i'gh:B2%^Ba Bڴie#G1~<í}wъĬ"kL򬩡#GYYA &0VW㷷FmN>"'L@W_l c Rڌr _ǯh((NO=N qV[Y2uJݳf[="}t(!Drݚt CUlulqs"tpTcu5.})~K-R qfJM1\2E b.x3g>_~(N-CFG;MQ8Î\Ih}w$,Q fW$WrN=쇤4h;q"^z -Z8(3*Ϝ%K\z}w풲30!d1/!jQ̤I6I 6>^gǎ!{R8w܄n˗OJvA0)N3I^$Xò4%ƏYXZ?$"'NOt](TGiJ rWDّ# pG T f~+YSSqz8+*lVO"?8iUWnߎk?<΂ q&)7pIL5=Xڬ,3W//48-~!<VQףEߏw*-Q"1nO|72O5I҆r Ut%%8=a*S迧 zkWxGFB4h<}Ǐ)4.5XG}>62WUIYE ͮooxGF«MxiPh' ``24>/ h((3Ȗ 13!$@ﺶe .̞ cM M C e%K6 \`M`9O>~{"gO>28Ɨ qQ|MScAv'93f~e An"9H"h1E`^|09D" 4}w Uh>pqڍb  D^p'縺aO0*A$;~IW}_!_ 1ßŀ#G1nbpr&H!kQڅQ}^| / ;!qj*o:M1ďMm}E򓒐Դeopxݿ<"O!3 7d IrɄ_~Aߢx^氹9s.gnsE2"/ cM Cю(=|bAVs"rD8EY]p"t8~%QԤDtvht^Y},!$ɡ r𪛱UgϢ*- 騹pOs;_Fѣل(#ljףh((iny9Luuz}jh<= ot4~]u."p(ܤ&X+κs4B,8b )] !mP!_v,j ' AӽٗxsX`r8,A?n#$x.x1TSD99k !;n&\yXG$KqfPJ#<+r]RRBsUq dx|8AĹ_]0@;8BT3 !ܟ3)420&hJsXsWե?nf$ cs 6M Ncx3a"54+ rR SpքpRDsVIENDB`mx-1.4.7/tests/style/rounded-corner.png000066400000000000000000000047001201047117600201050ustar00rootroot00000000000000PNG  IHDRp`6sBIT|d pHYs B(xtEXtSoftwarewww.inkscape.org< =IDATxmLT;PV0H"m4jm\Vjkbv+n٤$jK\hh6itJ4i5eh. dqQ`{Quw|fVlN8if~̞}GْwdNϳ~CO<(3;bf4j] ' 9s8;;;|?b7(bfO(- PU\__X>ΞVz&>`f0A HDo8(W 5<H^ٳg{Icc{) P]~MĒ7%%Fnﮩ뫌N}9LlnnƊ+fNzLDSܹ % ǩ1|Yu`SSS4=,IPlݺ5@-`#me۷#1XAFdee.3;a`) Q2^W>1sa`|X@"Ϙ1c2lQJFeڵ߯>ແͰr^~}}cAzzǽ0tCD6,Zǣ%g%gΜ1 QQQ?vp(Ǐ;tP0zpVTT;8I48~Ypa\>"} Һm۶)P!o[%̬;"IH޸qZ]]u ve1sxЮ1s$3P".^xZf$&&zu]1)pXVE%T0@[pޢ wPQEgMMM7srr;(I(O "zN@y"#-SR@!$P!!bCn?1e@!! `n#֘ KiB)...@At]ՆN^ADPٍn͛sUJJJO#pkhh8l{.ז'l!!2YYY2PBjbnn6nܸK R@!! `I#*6$kRWW_TTT.kѣ2e!b2XJ;vXƚ5k"m6/IBbP .;wpG$ #?d-T0RSS{d f3+n3ˎ}j2䵠v-6%THykؐ, Jkkk D޿@qVWWO,$nWkjj}9~xU[{{{˗'Z`WآESSSd[[[3 ;e˖H8p`|KK9߿nOt8]*RWWgmnnu}o`lQUٳ?9sfГ]$555:C /@D5Ο??rŊ?2?0@DT^'''7Ĕ_"Z_oMMM'5|֭4AfDӴ 33~X>5Ǽg1\Bѣ@Ę;K^ ]r,W\Qu]F?(ȑ#p8%ܼySu\E|m,Ç+*\t"%}(.K{%|z gHUU-u} zg͚'&&6Q_\\>Я~v OQv|zHKKViAAA3% $D~%%%w$جPZZ#SVnٲed;uF'ٳvҥیl111_|ᇿ1544$TU}|#ڵ1"kֱnݺN;wvՐ4fIENDB`mx-1.4.7/tests/test-box-layout.c000066400000000000000000000152011201047117600165330ustar00rootroot00000000000000/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright 2009, 2010 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * Boston, MA 02111-1307, USA. * */ #include #include #include #include static ClutterActor *hover_actor; static void find_last_child (ClutterActor *actor, ClutterActor **child) { *child = actor; } static void enter_event (ClutterActor *actor, ClutterEvent *event, gpointer data) { ClutterColor color = { 0x00, 0x00, 0x00, 0xff }; clutter_rectangle_set_border_width (CLUTTER_RECTANGLE (actor), 2); clutter_rectangle_set_border_color (CLUTTER_RECTANGLE (actor), &color); hover_actor = actor; } static void leave_event (ClutterActor *actor, ClutterEvent *event, gpointer data) { clutter_rectangle_set_border_width (CLUTTER_RECTANGLE (actor), 0); hover_actor = NULL; } static void button_release_event (ClutterActor *actor, ClutterButtonEvent *event, ClutterContainer *box) { gboolean xfill, yfill; gint xalign, yalign; if (event->button == 1) { clutter_container_child_get (box, actor, "x-fill", &xfill, "y-fill", &yfill, NULL); clutter_container_child_set (box, actor, "x-fill", !xfill, "y-fill", !yfill, NULL); } else if (event->button == 3) { clutter_container_child_get (box, actor, "x-align", &xalign, "y-align", &yalign, NULL); if (xalign < 2) xalign++; else xalign = 0; if (yalign < 2) yalign++; else yalign = 0; clutter_container_child_set (box, actor, "x-align", xalign, "y-align", yalign, NULL); } else if (event->button == 2) { gboolean expand; clutter_container_child_get (box, actor, "expand", &expand, NULL); clutter_container_child_set (box, actor, "expand", !expand, NULL); } } static void add_actor (ClutterContainer *container) { ClutterActor *rect; ClutterColor color = { 0xff, 0xff, 0xff, 255 }; static gboolean expand = TRUE; clutter_color_from_hls (&color, g_random_double_range (0.0, 360.0), 0.5, 0.5); rect = clutter_rectangle_new_with_color (&color); clutter_actor_set_size (rect, 32, 64); clutter_container_add_actor (container, rect); clutter_actor_set_reactive (rect, TRUE); g_signal_connect (rect, "enter-event", G_CALLBACK (enter_event), NULL); g_signal_connect (rect, "leave-event", G_CALLBACK (leave_event), NULL); g_signal_connect (rect, "button-release-event", G_CALLBACK (button_release_event), container); clutter_container_child_set (container, rect, "expand", expand, NULL); expand = !expand; } static gboolean key_release_cb (ClutterActor *actor, ClutterKeyEvent *event, MxBoxLayout *box) { if (event->keyval == 'v') { mx_box_layout_set_orientation (box, !mx_box_layout_get_orientation (box)); } if (event->keyval == '=') { add_actor ((ClutterContainer*) box); } if (event->keyval == '-') { ClutterActor *child = NULL; clutter_container_foreach (CLUTTER_CONTAINER (box), (ClutterCallback) find_last_child, &child); if (child) clutter_container_remove_actor (CLUTTER_CONTAINER (box), child); } if (event->keyval == 'c') { gboolean clip; g_object_get (actor, "clip-to-allocation", &clip, NULL); g_object_set (actor, "clip-to-allocation", !clip, NULL); } if (event->keyval == 's') { guint spacing; spacing = mx_box_layout_get_spacing (box); if (spacing > 6) spacing = 0; else spacing++; mx_box_layout_set_spacing (box, spacing); } if (event->keyval == 'e') { if (hover_actor) { gboolean expand; clutter_container_child_get ((ClutterContainer*) box, hover_actor, "expand", &expand, NULL); clutter_container_child_set ((ClutterContainer*) box, hover_actor, "expand", !expand, NULL); } } if (event->keyval == 'a') { mx_box_layout_set_enable_animations (box, !mx_box_layout_get_enable_animations (box)); } return FALSE; } static void stage_size_changed_cb (ClutterActor *stage, GParamSpec *pspec, ClutterActor *scrollview) { gfloat width, height; clutter_actor_get_size (stage, &width, &height); clutter_actor_set_size (scrollview, width - 100, height - 100); } int main (int argc, char *argv[]) { ClutterActor *stage, *box, *scrollview; if (clutter_init (&argc, &argv) != CLUTTER_INIT_SUCCESS) return 1; mx_style_load_from_file (mx_style_get_default(), "style/default.css", NULL); stage = clutter_stage_get_default (); clutter_actor_set_size (stage, 640, 480); clutter_stage_set_user_resizable ((ClutterStage *) stage, TRUE); scrollview = mx_scroll_view_new (); clutter_container_add_actor (CLUTTER_CONTAINER (stage), scrollview); clutter_actor_set_position (scrollview, 50, 50); clutter_actor_set_size (scrollview, 500, 300); box = mx_box_layout_new (); clutter_container_add_actor (CLUTTER_CONTAINER (scrollview), box); add_actor ((ClutterContainer*) box); add_actor ((ClutterContainer*) box); add_actor ((ClutterContainer*) box); g_signal_connect (stage, "key-release-event", G_CALLBACK (key_release_cb), box); g_signal_connect (stage, "notify::allocation", G_CALLBACK (stage_size_changed_cb), scrollview); clutter_actor_show (stage); clutter_main (); return EXIT_SUCCESS; } mx-1.4.7/tests/test-button-group.c000066400000000000000000000100001201047117600170650ustar00rootroot00000000000000/* * Copyright 2009 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * Boston, MA 02111-1307, USA. * */ #include "test-mx.h" static ClutterActor *button_box; static MxButtonGroup *button_group; static void _add_button_clicked_cb (MxButton *add_button, gpointer userdata) { ClutterActor *button; gchar *button_str; GList *children; children = clutter_container_get_children (CLUTTER_CONTAINER (button_box)); button_str = g_strdup_printf ("Button %d", g_list_length (children)); button = mx_button_new_with_label (button_str); g_free (button_str); g_list_free (children); mx_button_set_is_toggle ((MxButton *) button, TRUE); clutter_container_add_actor (CLUTTER_CONTAINER (button_box), button); mx_button_group_add (button_group, (MxButton *) button); } static void _rm_button_clicked_cb (MxButton *button, gpointer userdata) { GList *children; ClutterActor *actor; children = clutter_container_get_children (CLUTTER_CONTAINER (button_box)); if (!children) return; actor = (ClutterActor *)g_list_last (children)->data; mx_button_group_remove (button_group, (MxButton *)actor); clutter_container_remove_actor (CLUTTER_CONTAINER (button_box), actor); g_list_free (children); } static void _destroy_button_clicked_cb (MxButton *button, gpointer userdata) { MxButton *active_button; active_button = mx_button_group_get_active_button (button_group); if (active_button) clutter_actor_destroy ((ClutterActor *) active_button); } static void _no_active_toggled_cb (MxButton *button, GParamSpec *pspec, gpointer userdata) { mx_button_group_set_allow_no_active (button_group, mx_button_get_toggled (button)); } void button_group_main (ClutterContainer *stage) { ClutterActor *button; ClutterActor *control_box; button_group = mx_button_group_new (); button_box = mx_box_layout_new (); control_box = mx_box_layout_new (); button = mx_button_new_with_label ("Add button"); g_signal_connect (button, "clicked", (GCallback)_add_button_clicked_cb, NULL); clutter_container_add_actor (CLUTTER_CONTAINER (control_box), button); button = mx_button_new_with_label ("Remove button"); g_signal_connect (button, "clicked", (GCallback)_rm_button_clicked_cb, NULL); clutter_container_add_actor (CLUTTER_CONTAINER (control_box), button); button = mx_button_new_with_label ("Destroy active"); g_signal_connect (button, "clicked", (GCallback)_destroy_button_clicked_cb, NULL); clutter_container_add_actor (CLUTTER_CONTAINER (control_box), button); button = mx_button_new_with_label ("Allow no active"); mx_button_set_is_toggle ((MxButton *) button, TRUE); g_signal_connect (button, "notify::toggled", (GCallback) _no_active_toggled_cb, NULL); mx_button_set_toggled ((MxButton *)button, mx_button_group_get_allow_no_active (button_group)); clutter_container_add_actor (CLUTTER_CONTAINER (control_box), button); clutter_container_add_actor (CLUTTER_CONTAINER (stage), control_box); clutter_container_add_actor (CLUTTER_CONTAINER (stage), button_box); clutter_actor_set_position (control_box, 100, 100); clutter_actor_set_position (button_box, 100, 200); } mx-1.4.7/tests/test-buttons.c000066400000000000000000000037741201047117600161420ustar00rootroot00000000000000/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright 2009 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * Boston, MA 02111-1307, USA. * */ #include "test-mx.h" static void button_clicked_cb (MxButton *button, gchar *name) { printf ("%s button clicked!\n", name); } static gboolean button_long_press_cb (MxButton *button, gfloat x, gfloat y, MxLongPressAction action) { if (action == MX_LONG_PRESS_ACTION) printf ("long press detected\n"); else if (action == MX_LONG_PRESS_CANCEL) printf ("long press cancelled\n"); return TRUE; } void buttons_main (ClutterContainer *stage) { ClutterActor *button; button = mx_button_new_with_label ("Normal Button"); g_signal_connect (button, "long-press", G_CALLBACK (button_long_press_cb), NULL); clutter_container_add_actor (CLUTTER_CONTAINER (stage), button); clutter_actor_set_position (button, 100, 50); button = mx_button_new_with_label ("Toggle Button"); g_signal_connect (button, "clicked", G_CALLBACK (button_clicked_cb), "toggle"); g_signal_connect (button, "long-press", G_CALLBACK (button_long_press_cb), NULL); mx_button_set_is_toggle (MX_BUTTON (button), TRUE); clutter_container_add_actor (CLUTTER_CONTAINER (stage), button); clutter_actor_set_position (button, 100, 100); } mx-1.4.7/tests/test-combo-box.c000066400000000000000000000061331201047117600163210ustar00rootroot00000000000000/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright 2009 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * * Written by: Thomas Wood */ #include "test-mx.h" static void title_changed_cb (MxComboBox *box) { printf ("title now: %s\n", mx_combo_box_get_active_text (box)); } static void index_changed_cb (MxComboBox *box) { printf ("index now: %d\n", mx_combo_box_get_index (box)); } static gboolean stage_key_press_cb (ClutterActor *actor, ClutterKeyEvent *event, MxComboBox *box) { if (event->keyval == 'r') { mx_combo_box_set_active_text (box, "London"); } if (event->keyval >= '0' && event->keyval <= '9') { mx_combo_box_set_index (box, event->keyval - 48); } return FALSE; } void combo_box_main (ClutterContainer *stage) { ClutterActor *combo; combo = (ClutterActor*) mx_combo_box_new (); clutter_container_add_actor (CLUTTER_CONTAINER (stage), combo); clutter_actor_set_position (combo, 10, 10); mx_combo_box_append_text (MX_COMBO_BOX (combo), "Strand"); mx_combo_box_append_text (MX_COMBO_BOX (combo), "Fleet Street"); mx_combo_box_append_text (MX_COMBO_BOX (combo), "Trafalgar Square"); mx_combo_box_append_text (MX_COMBO_BOX (combo), "Leicester Square"); mx_combo_box_append_text (MX_COMBO_BOX (combo), "Coventry Street"); mx_combo_box_append_text (MX_COMBO_BOX (combo), "Piccadilly"); mx_combo_box_set_active_text (MX_COMBO_BOX (combo), "London"); g_signal_connect (combo, "notify::title", G_CALLBACK (title_changed_cb), NULL); g_signal_connect (combo, "notify::index", G_CALLBACK (index_changed_cb), NULL); g_signal_connect (stage, "key-press-event", G_CALLBACK (stage_key_press_cb), combo); combo = mx_combo_box_new (); clutter_actor_set_position (combo, 20, 40); mx_combo_box_insert_text_with_icon (MX_COMBO_BOX (combo), 0, "Cloudy", "weather-few-clouds"); mx_combo_box_insert_text_with_icon (MX_COMBO_BOX (combo), 0, "Clear", "weather-clear"); mx_combo_box_insert_text_with_icon (MX_COMBO_BOX (combo), 0, "Snow", "weather-snow"); mx_combo_box_insert_text_with_icon (MX_COMBO_BOX (combo), 0, "Overcast", "weather-overcast"); clutter_container_add_actor (CLUTTER_CONTAINER (stage), combo); } mx-1.4.7/tests/test-containers.c000066400000000000000000000427601201047117600166070ustar00rootroot00000000000000/* * Copyright 2011 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * Boston, MA 02111-1307, USA. * */ #include #include #include static struct { MxApplication *app; ClutterActor *table; ClutterActor *frame; ClutterActor *scroll; ClutterActor *inspector; ClutterActor *child_inspector; ClutterActor *container; } data; /* skip properties that have no effect or could cause other problems */ static gchar *skip_properties[] = { "fixed-position-set", "fixed-x", "fixed-y", "height", "min-height", "min-height-set", "min-width", "min-width-set", "natural-height", "natural-height-set", "natural-width", "natural-width-set", "request-mode", "show-on-parent-set", "width", "x", "y" }; static gboolean num_to_string (GBinding *binding, const GValue *source_value, GValue *target_value, gpointer userdata) { gchar *string; if (G_VALUE_HOLDS_INT (source_value)) string = g_strdup_printf ("%d", g_value_get_int (source_value)); else if (G_VALUE_HOLDS_UINT (source_value)) string = g_strdup_printf ("%d", g_value_get_uint (source_value)); else if (G_VALUE_HOLDS_DOUBLE (source_value)) string = g_strdup_printf ("%.2f", g_value_get_double (source_value)); else if (G_VALUE_HOLDS_FLOAT (source_value)) string = g_strdup_printf ("%.2f", g_value_get_float (source_value)); else return FALSE; g_value_take_string (target_value, string); return TRUE; } static gboolean string_to_num (GBinding *binding, const GValue *source_value, GValue *target_value, gpointer userdata) { if (G_VALUE_HOLDS_INT (target_value)) g_value_set_int (target_value, atoi (g_value_get_string (source_value))); else if (G_VALUE_HOLDS_UINT (target_value)) g_value_set_uint (target_value, atoi (g_value_get_string (source_value))); else if (G_VALUE_HOLDS_DOUBLE (target_value)) g_value_set_double (target_value, strtod (g_value_get_string (source_value), NULL)); else if (G_VALUE_HOLDS_FLOAT (target_value)) g_value_set_float (target_value, strtof (g_value_get_string (source_value), NULL)); else return FALSE; return TRUE; } static ClutterActor * create_property_editor (GObject *object, GParamSpec *pspec) { ClutterActor *box, *label, *value; gint i; /* skip properties that are not writable */ if (!(pspec->flags & G_PARAM_WRITABLE)) return NULL; /* skip other properties */ for (i = 0; i < G_N_ELEMENTS (skip_properties); i++) { if (g_str_equal (pspec->name, skip_properties[i])) return NULL; } box = mx_box_layout_new (); label = mx_label_new_with_text (pspec->name); clutter_actor_set_width (label, 150); clutter_container_add_actor (CLUTTER_CONTAINER (box), label); if (pspec->value_type == G_TYPE_BOOLEAN) { value = mx_toggle_new (); g_object_bind_property (object, pspec->name, value, "active", G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); } else if (pspec->value_type == G_TYPE_STRING) { value = mx_entry_new (); g_object_bind_property (object, pspec->name, value, "text", G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); } else if (pspec->value_type == G_TYPE_INT || pspec->value_type == G_TYPE_UINT || pspec->value_type == G_TYPE_FLOAT || pspec->value_type == G_TYPE_DOUBLE) { value = mx_entry_new (); g_object_bind_property_full (object, pspec->name, value, "text", G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE, num_to_string, string_to_num, NULL, NULL); } else if (g_type_is_a (pspec->value_type, G_TYPE_ENUM)) { GEnumValue *evalue; GEnumClass *eclass; gint init = 0; value = mx_combo_box_new (); clutter_actor_set_width (value, 100); eclass = g_type_class_ref (pspec->value_type); while ((evalue = g_enum_get_value (eclass, init))) { mx_combo_box_append_text (MX_COMBO_BOX (value), evalue->value_nick); init++; } g_type_class_unref (eclass); g_object_bind_property (object, pspec->name, value, "index", G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); } else value = NULL; if (value) { clutter_container_add_actor (CLUTTER_CONTAINER (box), value); return box; } else return NULL; } static void create_child_inspector (ClutterActor *button) { GObjectClass *class; ClutterActor *vbox, *label; guint n_properties = 0; GParamSpec **properties; gint i; ClutterChildMeta *meta; if (data.child_inspector) clutter_actor_destroy (data.child_inspector); data.child_inspector = vbox = mx_box_layout_new (); mx_box_layout_set_orientation (MX_BOX_LAYOUT (vbox), MX_ORIENTATION_VERTICAL); class = G_OBJECT_GET_CLASS (data.container); properties = clutter_container_class_list_child_properties (class, &n_properties); meta = clutter_container_get_child_meta (CLUTTER_CONTAINER (data.container), button); if (n_properties > 0) { gchar *text; const gchar *name = clutter_actor_get_name (button); /* title */ text = g_strconcat ("Child Properties", (name) ? " (" : "", (name) ? name : "", (name) ? ")" : "", "", NULL); label = mx_label_new_with_text (text); mx_label_set_use_markup (MX_LABEL (label), TRUE); clutter_container_add_actor (CLUTTER_CONTAINER (vbox), label); } for (i = n_properties - 1; i >= 0; i--) { ClutterActor *editor; editor = create_property_editor (G_OBJECT (meta), properties[i]); if (editor) clutter_container_add_actor (CLUTTER_CONTAINER (vbox), editor); } clutter_actor_set_width (vbox, 300); mx_table_add_actor_with_properties (MX_TABLE (data.table), vbox, 0, 1, "x-expand", FALSE, "y-expand", FALSE, NULL); } static void change_widget (MxComboBox *box, GParamSpec *pspec, gpointer userdata) { GObjectClass *class; const gchar *typename; guint id; ClutterActor *actor, *vbox, *label; GParamSpec **properties; guint n_properties; gint i; typename = mx_combo_box_get_active_text (box); id = g_type_from_name (typename); if (data.container) clutter_actor_destroy (data.container); data.container = actor = g_object_new (id, NULL); class = G_OBJECT_GET_CLASS (actor); if (MX_IS_SCROLLABLE (actor)) { clutter_container_add_actor (CLUTTER_CONTAINER (data.scroll), actor); clutter_actor_show (data.scroll); clutter_actor_hide (data.frame); } else { mx_bin_set_child (MX_BIN (data.frame), actor); clutter_actor_hide (data.scroll); clutter_actor_show (data.frame); } if (data.inspector) clutter_actor_destroy (data.inspector); if (data.child_inspector) clutter_actor_destroy (data.child_inspector); vbox = mx_box_layout_new (); mx_box_layout_set_orientation (MX_BOX_LAYOUT (vbox), MX_ORIENTATION_VERTICAL); /* title */ label = mx_label_new_with_text ("Container Properties"); mx_label_set_use_markup (MX_LABEL (label), TRUE); clutter_container_add_actor (CLUTTER_CONTAINER (vbox), label); properties = g_object_class_list_properties (class, &n_properties); for (i = n_properties - 1; i >= 0; i--) { ClutterActor *editor; editor = create_property_editor (G_OBJECT (actor), properties[i]); if (editor) clutter_container_add_actor (CLUTTER_CONTAINER (vbox), editor); } if (MX_IS_VIEWPORT (actor) || MX_IS_KINETIC_SCROLL_VIEW (actor)) { ClutterActor *child, *texture; gint x, y; child = clutter_group_new (); for (x = 0; x < 10; x++) for (y = 0; y < 10; y++) { texture = clutter_texture_new_from_file ("redhand.png", NULL); clutter_texture_set_keep_aspect_ratio (CLUTTER_TEXTURE (texture), TRUE); clutter_container_add_actor (CLUTTER_CONTAINER (child), texture); clutter_actor_set_width (texture, 100); clutter_actor_set_position (texture, x * 100, y * 100); } if (MX_IS_KINETIC_SCROLL_VIEW (actor)) { ClutterActor *viewport = mx_viewport_new (); clutter_container_add_actor (CLUTTER_CONTAINER (viewport), child); child = viewport; } clutter_container_add_actor (CLUTTER_CONTAINER (actor), child); } else { gint row = 0, col = 0; gint n_children = 200; if (MX_IS_STACK (actor)) { n_children = 5; } else if (MX_IS_TABLE (actor)) n_children = 50; for (i = 0; i < n_children; i++) { ClutterActor *button; gchar text[11]; g_snprintf (text, 11, "Button %d", i + 1); button = mx_button_new_with_label (text); clutter_actor_set_name (button, text); g_signal_connect (button, "clicked", G_CALLBACK (create_child_inspector), NULL); if (MX_IS_TABLE (actor)) { mx_table_add_actor (MX_TABLE (actor), button, row, col); row++; if (row == 10) { col++; row = 0; } } else clutter_container_add_actor (CLUTTER_CONTAINER (actor), button); if (MX_IS_STACK (actor)) { gfloat offset; offset = n_children * 5; clutter_actor_set_anchor_point (button, offset, offset - 5); offset -= 5; mx_stack_child_set_x_fill (MX_STACK (actor), button, FALSE); mx_stack_child_set_y_fill (MX_STACK (actor), button, FALSE); } } } data.inspector = mx_scroll_view_new (); clutter_actor_set_width (data.inspector, 300); clutter_container_add_actor (CLUTTER_CONTAINER (data.inspector), vbox); mx_table_add_actor_with_properties (MX_TABLE (data.table), data.inspector, 1, 1, "x-expand", FALSE, NULL); } static ClutterActor* create_combo_box (gchar *selected_widget) { ClutterActor *combo; gint i, set_index = 0; gchar *needle; GType types[] = { mx_grid_get_type (), mx_box_layout_get_type (), mx_table_get_type (), mx_stack_get_type (), mx_viewport_get_type (), mx_kinetic_scroll_view_get_type () }; combo = mx_combo_box_new (); needle = g_ascii_strdown (selected_widget, -1); for (i = 0; i < G_N_ELEMENTS (types); i++) { gchar *haystack; haystack = g_ascii_strdown (g_type_name (types[i]), -1); if (strstr (haystack, needle)) set_index = i; g_free (haystack); mx_combo_box_append_text (MX_COMBO_BOX (combo), g_type_name (types[i])); } g_free (needle); g_signal_connect (combo, "notify::index", G_CALLBACK (change_widget), NULL); mx_combo_box_set_index (MX_COMBO_BOX (combo), set_index); return combo; } static void rotate_left_clicked_cb (ClutterActor *button, MxWindow *window) { MxWindowRotation rotation = mx_window_get_window_rotation (window); switch (rotation) { case MX_WINDOW_ROTATION_0: rotation = MX_WINDOW_ROTATION_270; break; case MX_WINDOW_ROTATION_90: rotation = MX_WINDOW_ROTATION_0; break; case MX_WINDOW_ROTATION_180: rotation = MX_WINDOW_ROTATION_90; break; case MX_WINDOW_ROTATION_270: rotation = MX_WINDOW_ROTATION_180; break; } mx_window_set_window_rotation (window, rotation); } static void rotate_right_clicked_cb (ClutterActor *button, MxWindow *window) { MxWindowRotation rotation = mx_window_get_window_rotation (window); switch (rotation) { case MX_WINDOW_ROTATION_0: rotation = MX_WINDOW_ROTATION_90; break; case MX_WINDOW_ROTATION_90: rotation = MX_WINDOW_ROTATION_180; break; case MX_WINDOW_ROTATION_180: rotation = MX_WINDOW_ROTATION_270; break; case MX_WINDOW_ROTATION_270: rotation = MX_WINDOW_ROTATION_0; break; } mx_window_set_window_rotation (window, rotation); } static void rotate_180_clicked_cb (ClutterActor *button, MxWindow *window) { MxWindowRotation rotation = mx_window_get_window_rotation (window); switch (rotation) { case MX_WINDOW_ROTATION_0: rotation = MX_WINDOW_ROTATION_180; break; case MX_WINDOW_ROTATION_90: rotation = MX_WINDOW_ROTATION_270; break; case MX_WINDOW_ROTATION_180: rotation = MX_WINDOW_ROTATION_0; break; case MX_WINDOW_ROTATION_270: rotation = MX_WINDOW_ROTATION_90; break; } mx_window_set_window_rotation (window, rotation); } static ClutterActor * create_rotate_box (MxWindow *window) { ClutterActor *button, *icon, *icon2, *layout2; ClutterActor *layout = mx_box_layout_new (); /* Create rotate-left button */ icon = mx_icon_new (); mx_icon_set_icon_name (MX_ICON (icon), "object-rotate-left"); mx_icon_set_icon_size (MX_ICON (icon), 16); button = mx_button_new (); mx_bin_set_child (MX_BIN (button), icon); g_signal_connect (button, "clicked", G_CALLBACK (rotate_left_clicked_cb), window); clutter_container_add_actor (CLUTTER_CONTAINER (layout), button); /* Create rotate-180 button */ icon = mx_icon_new (); mx_icon_set_icon_name (MX_ICON (icon), "object-rotate-left"); mx_icon_set_icon_size (MX_ICON (icon), 16); icon2 = mx_icon_new (); mx_icon_set_icon_name (MX_ICON (icon2), "object-rotate-right"); mx_icon_set_icon_size (MX_ICON (icon2), 16); layout2 = mx_box_layout_new (); clutter_container_add (CLUTTER_CONTAINER (layout2), icon, icon2, NULL); button = mx_button_new (); mx_bin_set_child (MX_BIN (button), layout2); g_signal_connect (button, "clicked", G_CALLBACK (rotate_180_clicked_cb), window); clutter_container_add_actor (CLUTTER_CONTAINER (layout), button); /* Create rotate-right button */ icon = mx_icon_new (); mx_icon_set_icon_name (MX_ICON (icon), "object-rotate-right"); mx_icon_set_icon_size (MX_ICON (icon), 16); button = mx_button_new (); mx_bin_set_child (MX_BIN (button), icon); g_signal_connect (button, "clicked", G_CALLBACK (rotate_right_clicked_cb), window); clutter_container_add_actor (CLUTTER_CONTAINER (layout), button); return layout; } int main (int argc, char **argv) { MxApplication *application; MxWindow *window; ClutterActor *box; MxToolbar *toolbar; gchar *selected_widget; application = mx_application_new (&argc, &argv, "test-widgets", 0); window = mx_application_create_window (application); if (argc > 1) selected_widget = argv[1]; /* main content */ data.table = mx_table_new (); data.scroll = mx_scroll_view_new (); data.frame = mx_frame_new (); mx_bin_set_fill (MX_BIN (data.frame), TRUE, TRUE); data.container = NULL; data.inspector = NULL; data.child_inspector = NULL; mx_table_add_actor_with_properties (MX_TABLE (data.table), data.frame, 0, 0, "row-span", 2, NULL); mx_table_add_actor_with_properties (MX_TABLE (data.table), data.scroll, 0, 0, "row-span", 2, NULL); clutter_actor_set_height (data.scroll, 300); mx_window_set_child (window, data.table); /* toolbar */ box = mx_box_layout_new (); mx_box_layout_add_actor_with_properties (MX_BOX_LAYOUT (box), create_combo_box (selected_widget), 0, "expand", TRUE, "x-fill", FALSE, "x-align", MX_ALIGN_START, NULL); mx_box_layout_add_actor (MX_BOX_LAYOUT (box), create_rotate_box (window), 1); toolbar = mx_window_get_toolbar (window); mx_bin_set_child (MX_BIN (toolbar), box); mx_bin_set_fill (MX_BIN (toolbar), TRUE, TRUE); /* show the window */ mx_window_show (window); /* run the application */ mx_application_run (application); return 0; } mx-1.4.7/tests/test-deform-texture-interactive.c000066400000000000000000000224531201047117600217240ustar00rootroot00000000000000/* * Copyright 2010 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * Boston, MA 02111-1307, USA. * */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #ifdef HAVE_X11 #include #endif typedef struct { ClutterActor *texture; const char *name; double factor; ClutterActor *value_label; } PropertyInfo; static PropertyInfo properties_info [3] = { { NULL, "period", 1.0 , NULL }, { NULL, "angle" , 2 * G_PI, NULL }, { NULL, "radius", 100 , NULL } }; static void on_value_changed (MxSlider *slider, GParamSpec *pspec, PropertyInfo *info) { gdouble value; char *label; value = mx_slider_get_value (slider); value *= info->factor; g_object_set (info->texture, info->name, value, NULL); label = g_strdup_printf ("%.2f", value); mx_label_set_text (MX_LABEL (info->value_label), label); g_free (label); } int main (int argc, char *argv[]) { MxWindow *window; MxApplication *app; gfloat width, height; ClutterActor *stage, *table, *slider, *label, *texture, *front, *back; ClutterColor stage_color = { 0xcc, 0xcc, 0xcc, 0xb0 }; #if defined (HAVE_X11) && CLUTTER_CHECK_VERSION(1,2,0) /* Enable argb visuals for coolness with compositors */ clutter_x11_set_use_argb_visual (TRUE); #endif app = mx_application_new (&argc, &argv, "Pimp My Page Turn", 0); window = mx_application_create_window (app); stage = (ClutterActor *)mx_window_get_clutter_stage (window); clutter_stage_set_color (CLUTTER_STAGE (stage), &stage_color); /* Set a size so we don't just get our minimum size on map */ clutter_actor_set_size (stage, 480, 480); /* Create a page-turn deformation */ texture = mx_deform_page_turn_new (); if (argc > 1) { front = clutter_texture_new_from_file (argv[1], NULL); } else { front = mx_offscreen_new (); mx_offscreen_set_child (MX_OFFSCREEN (front), mx_button_new_with_label ("Front face")); } if (argc > 2) { back = clutter_texture_new_from_file (argv[2], NULL); } else { back = mx_offscreen_new (); mx_offscreen_set_child (MX_OFFSCREEN (back), mx_button_new_with_label ("Back face")); } mx_deform_texture_set_textures (MX_DEFORM_TEXTURE (texture), (ClutterTexture *)front, (ClutterTexture *)back); properties_info[0].texture = texture; properties_info[1].texture = texture; properties_info[2].texture = texture; /* Make the subdivision size a bit higher than default so it looks nicer */ clutter_actor_get_preferred_size (texture, NULL, NULL, &width, &height); mx_deform_texture_set_resolution (MX_DEFORM_TEXTURE (texture), 64, 64); /* Create a table that will be our top level container */ table = mx_table_new (); mx_table_set_row_spacing (MX_TABLE (table), 12); /* Add the texture first */ mx_table_add_actor_with_properties (MX_TABLE (table), texture, 0, 0, "column-span", 3, NULL); label = mx_label_new_with_text ("Period"); slider = mx_slider_new (); properties_info[0].value_label = mx_label_new_with_text ("0.0"); clutter_actor_set_width (slider, 200); g_signal_connect (slider, "notify::value", G_CALLBACK (on_value_changed), &properties_info[0]); mx_table_add_actor_with_properties (MX_TABLE (table), label, 1, 0, "x-expand", TRUE, "y-expand", FALSE, "x-align", MX_ALIGN_END, "y-fill", FALSE, "x-fill", TRUE, NULL); mx_table_add_actor_with_properties (MX_TABLE (table), slider, 1, 1, "x-expand", TRUE, "y-expand", FALSE, "x-align", MX_ALIGN_MIDDLE, "y-fill", FALSE, "x-fill", FALSE, NULL); mx_table_add_actor_with_properties (MX_TABLE (table), properties_info[0].value_label, 1, 2, "x-expand", TRUE, "y-expand", FALSE, "x-align", MX_ALIGN_MIDDLE, "y-fill", FALSE, "x-fill", FALSE, NULL); label = mx_label_new_with_text ("Angle"); slider = mx_slider_new (); properties_info[1].value_label = mx_label_new_with_text ("0.00"); clutter_actor_set_width (slider, 200); g_signal_connect (slider, "notify::value", G_CALLBACK (on_value_changed), &properties_info[1]); mx_table_add_actor_with_properties (MX_TABLE (table), label, 2, 0, "x-expand", TRUE, "y-expand", TRUE, "x-align", MX_ALIGN_END, "y-expand", FALSE, "y-fill", FALSE, "x-fill", TRUE, NULL); mx_table_add_actor_with_properties (MX_TABLE (table), slider, 2, 1, "x-expand", TRUE, "y-expand", FALSE, "x-align", MX_ALIGN_MIDDLE, "y-fill", FALSE, "x-fill", FALSE, NULL); mx_table_add_actor_with_properties (MX_TABLE (table), properties_info[1].value_label, 2, 2, "x-expand", TRUE, "y-expand", FALSE, "x-align", MX_ALIGN_MIDDLE, "y-fill", FALSE, "x-fill", FALSE, NULL); label = mx_label_new_with_text ("Radius"); slider = mx_slider_new (); properties_info[2].value_label = mx_label_new_with_text ("24.00"); clutter_actor_set_width (slider, 200); mx_slider_set_value (MX_SLIDER (slider), 0.24); g_signal_connect (slider, "notify::value", G_CALLBACK (on_value_changed), &properties_info[2]); mx_table_add_actor_with_properties (MX_TABLE (table), label, 3, 0, "x-expand", TRUE, "y-expand", TRUE, "x-align", MX_ALIGN_END, "y-expand", FALSE, "y-fill", FALSE, "x-fill", TRUE, NULL); mx_table_add_actor_with_properties (MX_TABLE (table), slider, 3, 1, "x-expand", TRUE, "y-expand", FALSE, "x-align", MX_ALIGN_MIDDLE, "y-fill", FALSE, "x-fill", FALSE, NULL); mx_table_add_actor_with_properties (MX_TABLE (table), properties_info[2].value_label, 3, 2, "x-expand", TRUE, "y-expand", FALSE, "x-align", MX_ALIGN_MIDDLE, "y-fill", FALSE, "x-fill", FALSE, NULL); /* Add the table to the window */ mx_window_set_child (window, table); /* Begin */ clutter_actor_show (stage); mx_application_run (app); return 0; } mx-1.4.7/tests/test-deform-texture.c000066400000000000000000000144771201047117600174200ustar00rootroot00000000000000/* * Copyright 2010 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * Boston, MA 02111-1307, USA. * */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #ifdef HAVE_X11 #include #endif static gint func = 0; static ClutterActor * replace_deformation (ClutterActor *texture, GType type) { ClutterTexture *front, *back; MxWindow *window; gint x, y; window = mx_window_get_for_stage (CLUTTER_STAGE (clutter_actor_get_stage (texture))); mx_deform_texture_get_resolution (MX_DEFORM_TEXTURE (texture), &x, &y); mx_deform_texture_get_textures (MX_DEFORM_TEXTURE (texture), &front, &back); if (front) g_object_ref (front); if (back) g_object_ref (back); mx_deform_texture_set_textures (MX_DEFORM_TEXTURE (texture), NULL, NULL); texture = g_object_new (type, NULL); mx_deform_texture_set_resolution (MX_DEFORM_TEXTURE (texture), x, y); mx_deform_texture_set_textures (MX_DEFORM_TEXTURE (texture), front, back); if (front) g_object_unref (front); if (back) g_object_unref (back); mx_window_set_child (window, texture); return texture; } static void completed_cb (ClutterAnimation *animation, ClutterActor *texture) { switch (func) { case 0: /* Change direction of page-turn animation */ clutter_actor_animate (texture, CLUTTER_EASE_IN_OUT_SINE, 5000, "period", 0.0, "signal-after::completed", completed_cb, texture, NULL); break; case 1: /* Replace page-turn deformation with bow-tie deformation */ texture = replace_deformation (texture, MX_TYPE_DEFORM_BOW_TIE); clutter_actor_animate (texture, CLUTTER_EASE_IN_OUT_SINE, 5000, "period", 1.0, "signal-after::completed", completed_cb, texture, NULL); break; case 2: /* Change direction of bow-tie animation */ clutter_actor_animate (texture, CLUTTER_EASE_IN_OUT_SINE, 5000, "period", 0.0, "signal-after::completed", completed_cb, texture, NULL); break; case 3: /* Replace bow-tie deformation with waves deformation */ texture = replace_deformation (texture, MX_TYPE_DEFORM_WAVES); g_object_set (G_OBJECT (texture), "amplitude", 0.0, NULL); clutter_actor_animate (texture, CLUTTER_EASE_IN_QUAD, 5000, "period", 2.0, "amplitude", 1.0, "signal-after::completed", completed_cb, texture, NULL); break; case 4: /* Reverse direction of waves deformation */ clutter_actor_animate (texture, CLUTTER_EASE_OUT_QUAD, 5000, "period", 4.0, "amplitude", 0.0, "signal-after::completed", completed_cb, texture, NULL); break; case 5: /* Replace waves deformation with page-turn deformation */ texture = replace_deformation (texture, MX_TYPE_DEFORM_PAGE_TURN); clutter_actor_animate (texture, CLUTTER_EASE_IN_OUT_SINE, 5000, "period", 1.0, "signal-after::completed", completed_cb, texture, NULL); break; } if (++func == 6) func = 0; } int main (int argc, char *argv[]) { MxWindow *window; MxApplication *app; gfloat width, height; ClutterActor *stage, *texture, *front, *back; ClutterColor stage_color = { 0xcc, 0xcc, 0xcc, 0xb0 }; #if defined (HAVE_X11) && CLUTTER_CHECK_VERSION(1,2,0) /* Enable argb visuals for coolness with compositors */ clutter_x11_set_use_argb_visual (TRUE); #endif app = mx_application_new (&argc, &argv, "Test deformations", 0); window = mx_application_create_window (app); stage = (ClutterActor *)mx_window_get_clutter_stage (window); clutter_stage_set_color (CLUTTER_STAGE (stage), &stage_color); /* Set a size so we don't just get our minimum size on map */ clutter_actor_set_size (stage, 480, 480); /* Create a page-turn deformation */ texture = mx_deform_page_turn_new (); if (argc > 1) { front = clutter_texture_new_from_file (argv[1], NULL); } else { front = mx_offscreen_new (); mx_offscreen_set_child (MX_OFFSCREEN (front), mx_button_new_with_label ("Front face")); } if (argc > 2) { back = clutter_texture_new_from_file (argv[2], NULL); } else { back = mx_offscreen_new (); mx_offscreen_set_child (MX_OFFSCREEN (back), mx_button_new_with_label ("Back face")); } mx_deform_texture_set_textures (MX_DEFORM_TEXTURE (texture), (ClutterTexture *)front, (ClutterTexture *)back); /* Make the subdivision size a bit higher than default so it looks nicer */ clutter_actor_get_preferred_size (texture, NULL, NULL, &width, &height); mx_deform_texture_set_resolution (MX_DEFORM_TEXTURE (texture), 64, 64); /* Add it to the window */ mx_window_set_child (window, texture); /* Start animation */ clutter_actor_animate (texture, CLUTTER_EASE_IN_OUT_SINE, 5000, "period", 1.0, "signal-after::completed", completed_cb, texture, NULL); /* Begin */ clutter_actor_show (stage); mx_application_run (app); return 0; } mx-1.4.7/tests/test-dialog.c000066400000000000000000000036751201047117600157030ustar00rootroot00000000000000/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright 2010 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * Boston, MA 02111-1307, USA. * */ #include "test-mx.h" static void close_dialog_cb (gpointer unused, ClutterActor *dialog) { clutter_actor_hide (dialog); } void dialog_main (ClutterContainer *group) { ClutterActor *stage, *launch, *dialog, *label; MxAction *action; launch = mx_button_new_with_label ("Launch dialog"); dialog = mx_dialog_new (); clutter_actor_set_position (launch, 50, 50); clutter_container_add (group, launch, NULL); g_signal_connect_swapped (launch, "clicked", G_CALLBACK (clutter_actor_show), dialog); label = mx_label_new_with_text ("This is a dialog"); mx_bin_set_child (MX_BIN (dialog), label); action = mx_action_new_full ("nothing", "Do nothing", NULL, dialog); mx_dialog_add_action (MX_DIALOG (dialog), action); action = mx_action_new_full ("close", "Close", G_CALLBACK (close_dialog_cb), dialog); mx_dialog_add_action (MX_DIALOG (dialog), action); stage = clutter_actor_get_stage (CLUTTER_ACTOR (group)); mx_dialog_set_transient_parent (MX_DIALOG (dialog), stage ? stage : CLUTTER_ACTOR (group)); } mx-1.4.7/tests/test-draggable.c000066400000000000000000000271611201047117600163500ustar00rootroot00000000000000/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright 2009 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * */ #include #include #include #include #include #include #include #define DRAGGABLE_TYPE_RECTANGLE (draggable_rectangle_get_type ()) #define DRAGGABLE_RECTANGLE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), DRAGGABLE_TYPE_RECTANGLE, DraggableRectangle)) #define DRAGGABLE_IS_RECTANGLE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), DRAGGABLE_TYPE_RECTANGLE)) typedef struct _DraggableRectangle DraggableRectangle; typedef struct _DraggableRectangleClass DraggableRectangleClass; struct _DraggableRectangle { ClutterRectangle parent_instance; /* Draggable properties */ guint threshold; MxDragAxis axis; #if 0 MxDragContainment containment; #endif ClutterActorBox area; ClutterActor *actor; guint is_enabled : 1; gboolean drag_clone; }; struct _DraggableRectangleClass { ClutterRectangleClass parent_class; }; enum { PROP_0, PROP_DRAG_THRESHOLD, PROP_AXIS, #if 0 PROP_CONTAINMENT_TYPE, PROP_CONTAINMENT_AREA, #endif PROP_ENABLED, PROP_ACTOR, }; static void mx_draggable_iface_init (MxDraggableIface *iface); static GType draggable_rectangle_get_type (); G_DEFINE_TYPE_WITH_CODE (DraggableRectangle, draggable_rectangle, CLUTTER_TYPE_RECTANGLE, G_IMPLEMENT_INTERFACE (MX_TYPE_DRAGGABLE, mx_draggable_iface_init)); static void draggable_rectangle_drag_begin (MxDraggable *draggable, gfloat event_x, gfloat event_y, gint event_button, ClutterModifierType modifiers) { gfloat x, y; ClutterActor *self = CLUTTER_ACTOR (draggable); ClutterActor *actor = mx_draggable_get_drag_actor (draggable); ClutterActor *stage = clutter_actor_get_stage (self); g_debug ("%s: drag of '%s' begin at %.2f, %.2f", G_STRLOC, clutter_actor_get_name (self), event_x, event_y); if (actor) { clutter_actor_get_position (self, &x, &y); clutter_actor_set_position (actor, x, y); clutter_container_add_actor (CLUTTER_CONTAINER (stage), actor); } else actor = self; clutter_actor_set_opacity (actor, 224); clutter_actor_set_rotation (actor, CLUTTER_Y_AXIS, 30.0, clutter_actor_get_width (actor) / 2.0, 0, clutter_actor_get_height (actor) / 2.0); } static void draggable_rectangle_drag_motion (MxDraggable *draggable, gfloat delta_x, gfloat delta_y) { ClutterActor *actor = mx_draggable_get_drag_actor (draggable); g_debug ("%s: drag motion of '%s' (dx: %.2f, dy: %.2f)", G_STRLOC, clutter_actor_get_name (CLUTTER_ACTOR (draggable)), delta_x, delta_y); if (!actor) actor = CLUTTER_ACTOR (draggable); clutter_actor_move_by (actor, delta_x, delta_y); } static void draggable_rectangle_drag_end (MxDraggable *draggable, gfloat event_x, gfloat event_y) { gfloat x, y; ClutterActor *self = CLUTTER_ACTOR (draggable); ClutterActor *actor = mx_draggable_get_drag_actor (draggable); ClutterActor *stage = clutter_actor_get_stage (self); g_debug ("%s: drag of '%s' end at %.2f, %.2f", G_STRLOC, clutter_actor_get_name (CLUTTER_ACTOR (draggable)), event_x, event_y); if (!actor) { clutter_actor_set_rotation (self, CLUTTER_Y_AXIS, 0.0, 0, 0, 0); clutter_actor_set_opacity (self, 255); return; } clutter_actor_get_position (actor, &x, &y); clutter_actor_animate (self, CLUTTER_EASE_OUT_QUAD, 150, "x", x, "y", y, NULL); clutter_container_remove_actor (CLUTTER_CONTAINER (stage), actor); } static void mx_draggable_iface_init (MxDraggableIface *iface) { iface->drag_begin = draggable_rectangle_drag_begin; iface->drag_motion = draggable_rectangle_drag_motion; iface->drag_end = draggable_rectangle_drag_end; } static void draggable_rectangle_set_property (GObject *gobject, guint prop_id, const GValue *value, GParamSpec *pspec) { DraggableRectangle *rect = DRAGGABLE_RECTANGLE (gobject); switch (prop_id) { case PROP_DRAG_THRESHOLD: rect->threshold = g_value_get_uint (value); break; case PROP_AXIS: rect->axis = g_value_get_enum (value); break; #if 0 case PROP_CONTAINMENT_TYPE: rect->containment = g_value_get_enum (value); break; case PROP_CONTAINMENT_AREA: { ClutterActorBox *box = g_value_get_boxed (value); if (box) rect->area = *box; else memset (&rect->area, 0, sizeof (ClutterActorBox)); } break; #endif case PROP_ENABLED: rect->is_enabled = g_value_get_boolean (value); if (rect->is_enabled) mx_draggable_enable (MX_DRAGGABLE (gobject)); else mx_draggable_disable (MX_DRAGGABLE (gobject)); break; case PROP_ACTOR: break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); break; } } static void draggable_rectangle_get_property (GObject *gobject, guint prop_id, GValue *value, GParamSpec *pspec) { DraggableRectangle *rect = DRAGGABLE_RECTANGLE (gobject); switch (prop_id) { case PROP_DRAG_THRESHOLD: g_value_set_uint (value, rect->threshold); break; case PROP_AXIS: g_value_set_enum (value, rect->axis); break; #if 0 case PROP_CONTAINMENT_TYPE: g_value_set_enum (value, rect->containment); break; case PROP_CONTAINMENT_AREA: g_value_set_boxed (value, &rect->area); break; #endif case PROP_ENABLED: g_value_set_boolean (value, rect->is_enabled); break; case PROP_ACTOR: if (rect->drag_clone) g_value_set_object (value, rect->actor); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); break; } } static void draggable_rectangle_paint (ClutterActor *actor) { gfloat text_x, text_y; ClutterActorBox box = { 0, }; gint layout_width, layout_height; CoglColor color = { 0, }; PangoLayout *layout; const gchar *name; CLUTTER_ACTOR_CLASS (draggable_rectangle_parent_class)->paint (actor); name = clutter_actor_get_name (actor); clutter_actor_get_allocation_box (actor, &box); layout = clutter_actor_create_pango_layout (actor, name); pango_layout_get_size (layout, &layout_width, &layout_height); text_x = ((box.x2 - box.x1) - (layout_width / 1024)) / 2; text_y = ((box.y2 - box.y1) - (layout_height / 1024)) / 2; cogl_color_set_from_4ub (&color, 0, 0, 0, 255); cogl_pango_render_layout (layout, (int) text_x, (int) text_y, &color, 0); g_object_unref (layout); } static void draggable_rectangle_dispose (GObject *object) { DraggableRectangle *rect = DRAGGABLE_RECTANGLE (object); if (rect->actor) { g_object_unref (rect->actor); rect->actor = NULL; } G_OBJECT_CLASS (draggable_rectangle_parent_class)->dispose (object); } static void draggable_rectangle_class_init (DraggableRectangleClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass); gobject_class->set_property = draggable_rectangle_set_property; gobject_class->get_property = draggable_rectangle_get_property; gobject_class->dispose = draggable_rectangle_dispose; actor_class->paint = draggable_rectangle_paint; g_object_class_override_property (gobject_class, PROP_DRAG_THRESHOLD, "drag-threshold"); g_object_class_override_property (gobject_class, PROP_AXIS, "axis"); #if 0 g_object_class_override_property (gobject_class, PROP_CONTAINMENT_TYPE, "containment-type"); g_object_class_override_property (gobject_class, PROP_CONTAINMENT_AREA, "containment-area"); #endif g_object_class_override_property (gobject_class, PROP_ENABLED, "drag-enabled"); g_object_class_override_property (gobject_class, PROP_ACTOR, "drag-actor"); } static void draggable_rectangle_init (DraggableRectangle *self) { self->threshold = 0; self->axis = 0; #if 0 self->containment = MX_DISABLE_CONTAINMENT; #endif self->is_enabled = FALSE; self->actor = g_object_ref_sink (clutter_clone_new (CLUTTER_ACTOR (self))); } int main (int argc, char *argv[]) { ClutterActor *stage; ClutterActor *draggable; ClutterColor rect_color = { 204, 204, 204, 255 }; if (clutter_init (&argc, &argv) != CLUTTER_INIT_SUCCESS) return 1; stage = clutter_stage_get_default (); clutter_stage_set_title (CLUTTER_STAGE (stage), "Draggable Example"); clutter_actor_set_size (stage, 800, 600); draggable = g_object_new (DRAGGABLE_TYPE_RECTANGLE, NULL); ((DraggableRectangle*)draggable)->drag_clone = TRUE; clutter_container_add_actor (CLUTTER_CONTAINER (stage), draggable); clutter_rectangle_set_color (CLUTTER_RECTANGLE (draggable), &rect_color); clutter_actor_set_size (draggable, 100, 100); clutter_actor_set_position (draggable, 350, 100); clutter_actor_set_reactive (draggable, TRUE); clutter_actor_set_name (draggable, "h-handle"); mx_draggable_set_axis (MX_DRAGGABLE (draggable), MX_DRAG_AXIS_X); mx_draggable_enable (MX_DRAGGABLE (draggable)); draggable = g_object_new (DRAGGABLE_TYPE_RECTANGLE, NULL); clutter_container_add_actor (CLUTTER_CONTAINER (stage), draggable); clutter_rectangle_set_color (CLUTTER_RECTANGLE (draggable), &rect_color); clutter_actor_set_size (draggable, 100, 100); clutter_actor_set_position (draggable, 350, 300); clutter_actor_set_reactive (draggable, TRUE); clutter_actor_set_name (draggable, "v-handle"); mx_draggable_set_axis (MX_DRAGGABLE (draggable), MX_DRAG_AXIS_Y); mx_draggable_enable (MX_DRAGGABLE (draggable)); clutter_actor_show_all (stage); clutter_main (); return EXIT_SUCCESS; } mx-1.4.7/tests/test-droppable.c000066400000000000000000000403651201047117600164110ustar00rootroot00000000000000/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright 2009 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * */ #include #include #include #include #include /* Droppable */ #define DROPPABLE_TYPE_GROUP (droppable_group_get_type ()) #define DROPPABLE_GROUP(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), DROPPABLE_TYPE_GROUP, DroppableGroup)) #define DROPPABLE_IS_GROUP(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), DROPPABLE_TYPE_GROUP)) typedef struct _DroppableGroup DroppableGroup; typedef struct _DroppableGroupClass DroppableGroupClass; struct _DroppableGroup { ClutterGroup parent_instance; ClutterActor *background; guint is_enabled : 1; }; struct _DroppableGroupClass { ClutterGroupClass parent_class; }; enum { DROP_PROP_0, DROP_PROP_ENABLED }; static const ClutterColor default_background_color = { 204, 204, 0, 255 }; static void mx_droppable_iface_init (MxDroppableIface *iface); static GType droppable_group_get_type (); G_DEFINE_TYPE_WITH_CODE (DroppableGroup, droppable_group, CLUTTER_TYPE_GROUP, G_IMPLEMENT_INTERFACE (MX_TYPE_DROPPABLE, mx_droppable_iface_init)); static void droppable_group_dispose (GObject *gobject) { DroppableGroup *group = DROPPABLE_GROUP (gobject); if (group->background) { clutter_actor_destroy (group->background); group->background = NULL; } G_OBJECT_CLASS (droppable_group_parent_class)->dispose (gobject); } static void droppable_group_over_in (MxDroppable *droppable, MxDraggable *draggable) { g_debug (G_STRLOC ": over-in"); clutter_actor_animate (CLUTTER_ACTOR (droppable), CLUTTER_EASE_IN_CUBIC, 250, "opacity", 255, NULL); } static void droppable_group_over_out (MxDroppable *droppable, MxDraggable *draggable) { g_debug (G_STRLOC ": over-out"); clutter_actor_animate (CLUTTER_ACTOR (droppable), CLUTTER_EASE_IN_CUBIC, 250, "opacity", 128, NULL); } static void droppable_group_drop (MxDroppable *droppable, MxDraggable *draggable, gfloat event_x, gfloat event_y, gint button, ClutterModifierType modifiers) { ClutterActor *self = CLUTTER_ACTOR (droppable); ClutterActor *child = CLUTTER_ACTOR (draggable); g_debug ("%s: dropped %s on '%s' (%s) at %.2f, %.2f", G_STRLOC, G_OBJECT_TYPE_NAME (draggable), clutter_actor_get_name (self), G_OBJECT_TYPE_NAME (droppable), event_x, event_y); g_object_ref (draggable); clutter_actor_reparent (child, self); clutter_actor_set_position (CLUTTER_ACTOR (draggable), (event_x < 100) ? 50 : 100, (event_y < 100) ? 50 : 100); g_object_unref (draggable); } static void mx_droppable_iface_init (MxDroppableIface *iface) { iface->over_in = droppable_group_over_in; iface->over_out = droppable_group_over_out; iface->drop = droppable_group_drop; } static void on_actor_added (ClutterContainer *container, ClutterActor *actor) { g_debug ("%s: added child `%s'", G_STRLOC, G_OBJECT_TYPE_NAME (actor)); } static void droppable_group_set_property (GObject *gobject, guint prop_id, const GValue *value, GParamSpec *pspec) { DroppableGroup *group = DROPPABLE_GROUP (gobject); switch (prop_id) { case DROP_PROP_ENABLED: group->is_enabled = g_value_get_boolean (value); if (group->is_enabled) mx_droppable_enable (MX_DROPPABLE (gobject)); else mx_droppable_disable (MX_DROPPABLE (gobject)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); break; } } static void droppable_group_get_property (GObject *gobject, guint prop_id, GValue *value, GParamSpec *pspec) { DroppableGroup *group = DROPPABLE_GROUP (gobject); switch (prop_id) { case DROP_PROP_ENABLED: g_value_set_boolean (value, group->is_enabled); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); break; } } static void droppable_group_class_init (DroppableGroupClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); gobject_class->set_property = droppable_group_set_property; gobject_class->get_property = droppable_group_get_property; gobject_class->dispose = droppable_group_dispose; g_object_class_override_property (gobject_class, DROP_PROP_ENABLED, "drop-enabled"); } static void droppable_group_init (DroppableGroup *group) { clutter_actor_set_opacity (CLUTTER_ACTOR (group), 128); group->background = clutter_rectangle_new (); clutter_container_add_actor (CLUTTER_CONTAINER (group), group->background); clutter_rectangle_set_color (CLUTTER_RECTANGLE (group->background), &default_background_color); clutter_actor_set_size (group->background, 200, 200); g_signal_connect (group, "actor-added", G_CALLBACK (on_actor_added), NULL); } /* Draggable */ #define DRAGGABLE_TYPE_RECTANGLE (draggable_rectangle_get_type ()) #define DRAGGABLE_RECTANGLE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), DRAGGABLE_TYPE_RECTANGLE, DraggableRectangle)) #define DRAGGABLE_IS_RECTANGLE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), DRAGGABLE_TYPE_RECTANGLE)) typedef struct _DraggableRectangle DraggableRectangle; typedef struct _DraggableRectangleClass DraggableRectangleClass; struct _DraggableRectangle { ClutterRectangle parent_instance; /* Draggable properties */ guint threshold; MxDragAxis axis; #if 0 MxDragContainment containment; #endif ClutterActorBox area; guint is_enabled : 1; }; struct _DraggableRectangleClass { ClutterRectangleClass parent_class; }; enum { DRAG_PROP_0, DRAG_PROP_DRAG_THRESHOLD, DRAG_PROP_AXIS, #if 0 DRAG_PROP_CONTAINMENT_TYPE, DRAG_PROP_CONTAINMENT_AREA, #endif DRAG_PROP_ENABLED, DRAG_PROP_ACTOR, }; static void mx_draggable_iface_init (MxDraggableIface *iface); static GType draggable_rectangle_get_type (); G_DEFINE_TYPE_WITH_CODE (DraggableRectangle, draggable_rectangle, CLUTTER_TYPE_RECTANGLE, G_IMPLEMENT_INTERFACE (MX_TYPE_DRAGGABLE, mx_draggable_iface_init)); static void draggable_rectangle_drag_begin (MxDraggable *draggable, gfloat event_x, gfloat event_y, gint event_button, ClutterModifierType modifiers) { ClutterActor *self = CLUTTER_ACTOR (draggable); ClutterActor *stage = clutter_actor_get_stage (self); gfloat orig_x, orig_y; g_object_ref (self); clutter_actor_get_transformed_position (self, &orig_x, &orig_y); clutter_actor_reparent (self, stage); clutter_actor_set_position (self, orig_x, orig_y); g_object_unref (self); clutter_actor_animate (self, CLUTTER_EASE_OUT_CUBIC, 250, "opacity", 224, NULL); } static void draggable_rectangle_drag_motion (MxDraggable *draggable, gfloat delta_x, gfloat delta_y) { clutter_actor_move_by (CLUTTER_ACTOR (draggable), delta_x, delta_y); } static void draggable_rectangle_drag_end (MxDraggable *draggable, gfloat event_x, gfloat event_y) { ClutterActor *self = CLUTTER_ACTOR (draggable); clutter_actor_animate (self, CLUTTER_EASE_OUT_CUBIC, 250, "opacity", 255, NULL); } static void mx_draggable_iface_init (MxDraggableIface *iface) { iface->drag_begin = draggable_rectangle_drag_begin; iface->drag_motion = draggable_rectangle_drag_motion; iface->drag_end = draggable_rectangle_drag_end; } static void draggable_rectangle_parent_set (ClutterActor *actor, ClutterActor *old_parent) { ClutterActor *new_parent = clutter_actor_get_parent (actor); g_debug ("%s: old_parent: %s, new_parent: %s (%s)", G_STRLOC, old_parent ? G_OBJECT_TYPE_NAME (old_parent) : "none", new_parent ? clutter_actor_get_name (new_parent) : "Unknown", new_parent ? G_OBJECT_TYPE_NAME (new_parent) : "none"); } static void draggable_rectangle_set_property (GObject *gobject, guint prop_id, const GValue *value, GParamSpec *pspec) { DraggableRectangle *rect = DRAGGABLE_RECTANGLE (gobject); switch (prop_id) { case DRAG_PROP_DRAG_THRESHOLD: rect->threshold = g_value_get_uint (value); break; case DRAG_PROP_AXIS: rect->axis = g_value_get_enum (value); break; #if 0 case DRAG_PROP_CONTAINMENT_TYPE: rect->containment = g_value_get_enum (value); break; case DRAG_PROP_CONTAINMENT_AREA: { ClutterActorBox *box = g_value_get_boxed (value); if (box) rect->area = *box; else memset (&rect->area, 0, sizeof (ClutterActorBox)); } break; #endif case DRAG_PROP_ENABLED: rect->is_enabled = g_value_get_boolean (value); if (rect->is_enabled) mx_draggable_enable (MX_DRAGGABLE (gobject)); else mx_draggable_disable (MX_DRAGGABLE (gobject)); break; case DRAG_PROP_ACTOR: break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); break; } } static void draggable_rectangle_get_property (GObject *gobject, guint prop_id, GValue *value, GParamSpec *pspec) { DraggableRectangle *rect = DRAGGABLE_RECTANGLE (gobject); switch (prop_id) { case DRAG_PROP_DRAG_THRESHOLD: g_value_set_uint (value, rect->threshold); break; case DRAG_PROP_AXIS: g_value_set_enum (value, rect->axis); break; #if 0 case DRAG_PROP_CONTAINMENT_TYPE: g_value_set_enum (value, rect->containment); break; case DRAG_PROP_CONTAINMENT_AREA: g_value_set_boxed (value, &rect->area); break; #endif case DRAG_PROP_ENABLED: g_value_set_boolean (value, rect->is_enabled); break; case DRAG_PROP_ACTOR: break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); break; } } static void draggable_rectangle_class_init (DraggableRectangleClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass); gobject_class->set_property = draggable_rectangle_set_property; gobject_class->get_property = draggable_rectangle_get_property; actor_class->parent_set = draggable_rectangle_parent_set; g_object_class_override_property (gobject_class, DRAG_PROP_DRAG_THRESHOLD, "drag-threshold"); g_object_class_override_property (gobject_class, DRAG_PROP_AXIS, "axis"); #if 0 g_object_class_override_property (gobject_class, DRAG_PROP_CONTAINMENT_TYPE, "containment-type"); g_object_class_override_property (gobject_class, DRAG_PROP_CONTAINMENT_AREA, "containment-area"); #endif g_object_class_override_property (gobject_class, DRAG_PROP_ENABLED, "drag-enabled"); g_object_class_override_property (gobject_class, DRAG_PROP_ACTOR, "drag-actor"); } static void draggable_rectangle_init (DraggableRectangle *self) { self->threshold = 0; self->axis = 0; #if 0 self->containment = MX_DISABLE_CONTAINMENT; #endif self->is_enabled = FALSE; } /* main */ int main (int argc, char *argv[]) { ClutterActor *stage; ClutterActor *draggable, *droppable; ClutterColor rect_color1 = { 146, 123, 81, 255 }; ClutterColor rect_color2 = { 128, 195, 28, 255 }; ClutterColor rect_color3 = { 255, 122, 2, 255 }; ClutterColor rect_color4 = { 141, 195, 233, 255 }; if (clutter_init (&argc, &argv) != CLUTTER_INIT_SUCCESS) return 1; stage = clutter_stage_get_default (); clutter_stage_set_title (CLUTTER_STAGE (stage), "Droppable Example"); clutter_actor_set_size (stage, 800, 600); droppable = g_object_new (DROPPABLE_TYPE_GROUP, NULL); clutter_container_add_actor (CLUTTER_CONTAINER (stage), droppable); clutter_actor_set_position (droppable, 500, 50); clutter_actor_set_reactive (droppable, TRUE); clutter_actor_set_name (droppable, "Drop Target 1"); mx_droppable_enable (MX_DROPPABLE (droppable)); droppable = g_object_new (DROPPABLE_TYPE_GROUP, NULL); clutter_container_add_actor (CLUTTER_CONTAINER (stage), droppable); clutter_actor_set_position (droppable, 500, 350); clutter_actor_set_reactive (droppable, TRUE); clutter_actor_set_name (droppable, "Drop Target 2"); mx_droppable_enable (MX_DROPPABLE (droppable)); draggable = g_object_new (DRAGGABLE_TYPE_RECTANGLE, "color", &rect_color1, NULL); clutter_container_add_actor (CLUTTER_CONTAINER (stage), draggable); clutter_actor_set_size (draggable, 50, 50); clutter_actor_set_position (draggable, 75, 250); clutter_actor_set_reactive (draggable, TRUE); mx_draggable_enable (MX_DRAGGABLE (draggable)); draggable = g_object_new (DRAGGABLE_TYPE_RECTANGLE, "color", &rect_color2, NULL); clutter_container_add_actor (CLUTTER_CONTAINER (stage), draggable); clutter_actor_set_size (draggable, 50, 50); clutter_actor_set_position (draggable, 125, 250); clutter_actor_set_reactive (draggable, TRUE); mx_draggable_enable (MX_DRAGGABLE (draggable)); draggable = g_object_new (DRAGGABLE_TYPE_RECTANGLE, "color", &rect_color3, NULL); clutter_container_add_actor (CLUTTER_CONTAINER (stage), draggable); clutter_actor_set_size (draggable, 50, 50); clutter_actor_set_position (draggable, 75, 300); clutter_actor_set_reactive (draggable, TRUE); mx_draggable_enable (MX_DRAGGABLE (draggable)); draggable = g_object_new (DRAGGABLE_TYPE_RECTANGLE, "color", &rect_color4, NULL); clutter_container_add_actor (CLUTTER_CONTAINER (stage), draggable); clutter_actor_set_size (draggable, 50, 50); clutter_actor_set_position (draggable, 125, 300); clutter_actor_set_reactive (draggable, TRUE); mx_draggable_enable (MX_DRAGGABLE (draggable)); clutter_actor_show_all (stage); clutter_main (); return EXIT_SUCCESS; } mx-1.4.7/tests/test-entry.c000066400000000000000000000075511201047117600156020ustar00rootroot00000000000000/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright 2009 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * Boston, MA 02111-1307, USA. * */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "test-mx.h" #ifdef HAVE_CLUTTER_IMCONTEXT #include #endif static void btn_clicked_cb (ClutterActor *button, MxEntry *entry) { mx_entry_set_text (entry, "Here is some text"); } static void clear_btn_clicked_cb (ClutterActor *button, MxEntry *entry) { mx_entry_set_text (entry, ""); } static void text_changed_cb (MxEntry *entry, GParamSpec *pspec, void *user_data) { printf ("Text: %s\n", mx_entry_get_text (entry)); } static void print_notice (ClutterText *text, gchar *message) { printf ("%s\n", message); } void entry_main (ClutterContainer *stage) { ClutterActor *entry, *button, *clear_button; entry = mx_entry_new_with_text ("Hello World!"); clutter_actor_set_position (entry, 20, 20); clutter_actor_set_width (entry, 150); clutter_container_add_actor (stage, entry); clutter_stage_set_key_focus (CLUTTER_STAGE (clutter_actor_get_stage (entry)), mx_entry_get_clutter_text (MX_ENTRY (entry))); entry = mx_entry_new (); clutter_actor_set_position (entry, 20, 70); clutter_container_add_actor (stage, entry); mx_entry_set_hint_text (MX_ENTRY (entry), "hint hint..."); #ifdef HAVE_CLUTTER_IMCONTEXT clutter_imtext_set_autoshow_im (CLUTTER_IMTEXT (mx_entry_get_clutter_text (MX_ENTRY (entry))), TRUE); #else g_debug ("Input method support is disabled"); #endif g_signal_connect (G_OBJECT (entry), "notify::text", G_CALLBACK (text_changed_cb), NULL); button = mx_button_new_with_label ("Set"); clutter_actor_set_position (button, 20, 120); g_signal_connect (button, "clicked", G_CALLBACK (btn_clicked_cb), entry); clear_button = mx_button_new_with_label ("clear"); clutter_actor_set_position (clear_button, 70, 120); g_signal_connect (clear_button, "clicked", G_CALLBACK (clear_btn_clicked_cb), entry); clutter_container_add (stage, button, clear_button, NULL); entry = mx_entry_new (); clutter_actor_set_position (entry, 20, 170); clutter_container_add_actor (stage, entry); mx_entry_set_hint_text (MX_ENTRY (entry), "Search..."); mx_entry_set_primary_icon_from_file (MX_ENTRY (entry), "edit-find.png"); mx_entry_set_secondary_icon_from_file (MX_ENTRY (entry), "edit-clear.png"); mx_entry_set_icon_highlight_suffix (MX_ENTRY (entry), "-highlight"); mx_entry_set_secondary_icon_tooltip_text (MX_ENTRY (entry), "one"); mx_entry_set_primary_icon_tooltip_text (MX_ENTRY (entry), "two"); g_signal_connect (entry, "primary-icon-clicked", G_CALLBACK (print_notice), "primary icon clicked\n"); g_signal_connect (entry, "secondary-icon-clicked", G_CALLBACK (print_notice), "secondary icon clicked\n"); entry = mx_entry_new (); clutter_actor_set_position (entry, 20, 220); clutter_container_add_actor (stage, entry); mx_entry_set_hint_text (MX_ENTRY (entry), "Secret!"); mx_entry_set_password_char (MX_ENTRY (entry), 0x2022); } mx-1.4.7/tests/test-expander.c000066400000000000000000000055321201047117600162440ustar00rootroot00000000000000/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright 2009 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * Boston, MA 02111-1307, USA. * */ #include "test-mx.h" static void expand_complete_cb (MxExpander *expander, gpointer user_data) { gboolean expanded; expanded = mx_expander_get_expanded (expander); printf ("expand complete (%s)\n", (expanded) ? "open": "closed"); } static void set_expanded (ClutterActor *actor, ClutterKeyEvent *event, MxExpander *expander) { gboolean expand; if (event->keyval != 32) return; expand = mx_expander_get_expanded (expander); mx_expander_set_expanded (expander, !expand); } static void stage_size_notify_cb (ClutterActor *stage, GParamSpec *pspec, ClutterActor *table) { gfloat width, height; clutter_actor_get_size (stage, &width, &height); } void expander_main (ClutterContainer *stage) { ClutterActor *expander, *scroll, *grid; int i; expander = mx_expander_new (); mx_expander_set_label (MX_EXPANDER (expander), "Expander"); clutter_container_add_actor (stage, expander); clutter_actor_set_position (expander, 10, 10); g_signal_connect (expander, "expand-complete", G_CALLBACK (expand_complete_cb), NULL); scroll = mx_scroll_view_new (); clutter_container_add_actor (CLUTTER_CONTAINER (expander), scroll); clutter_actor_set_size (scroll, 320, 240); grid = mx_grid_new (); clutter_container_add_actor (CLUTTER_CONTAINER (scroll), grid); for (i = 1; i <= 50; i++) { ClutterActor *button; gchar *label; label = g_strdup_printf ("Button %d", i); button = mx_button_new_with_label (label); clutter_container_add_actor (CLUTTER_CONTAINER (grid), button); g_free (label); } g_signal_connect (stage, "notify::width", G_CALLBACK (stage_size_notify_cb), expander); g_signal_connect (stage, "notify::height", G_CALLBACK (stage_size_notify_cb), expander); g_signal_connect (stage, "key-release-event", G_CALLBACK (set_expanded), expander); } mx-1.4.7/tests/test-focus.c000066400000000000000000000051731201047117600155560ustar00rootroot00000000000000/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright 2011 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * Boston, MA 02111-1307, USA. * */ #include #include "test-mx.h" static void button_clicked_cb (MxButton *button) { clutter_actor_destroy (CLUTTER_ACTOR (button)); } int main (int argc, char *argv[]) { ClutterActor *button, *box, *stage; MxApplication *application; MxWindow *window; application = mx_application_new (&argc, &argv, "Test Mx focus handling", MX_APPLICATION_SINGLE_INSTANCE); window = mx_application_create_window (application); stage = (ClutterActor *)mx_window_get_clutter_stage (window); box = mx_box_layout_new (); mx_box_layout_set_orientation (MX_BOX_LAYOUT (box), MX_ORIENTATION_VERTICAL); mx_window_set_child (window, box); button = mx_button_new_with_label ("button #1 (activate to remove)"); g_signal_connect (button, "clicked", G_CALLBACK (button_clicked_cb), NULL); mx_box_layout_add_actor (MX_BOX_LAYOUT (box), button, -1); mx_focus_manager_push_focus (mx_focus_manager_get_for_stage (CLUTTER_STAGE (stage)), MX_FOCUSABLE (button)); button = mx_button_new_with_label ("button #2 (activate to remove)"); g_signal_connect (button, "clicked", G_CALLBACK (button_clicked_cb), NULL); mx_box_layout_add_actor (MX_BOX_LAYOUT (box), button, -1); button = mx_button_new_with_label ("button #3 (activate to remove)"); g_signal_connect (button, "clicked", G_CALLBACK (button_clicked_cb), NULL); mx_box_layout_add_actor (MX_BOX_LAYOUT (box), button, -1); button = mx_button_new_with_label ("button #4 (activate to remove)"); g_signal_connect (button, "clicked", G_CALLBACK (button_clicked_cb), NULL); mx_box_layout_add_actor (MX_BOX_LAYOUT (box), button, -1); clutter_actor_show (stage); mx_application_run (application); return EXIT_SUCCESS; } mx-1.4.7/tests/test-grid.c000066400000000000000000000373461201047117600153730ustar00rootroot00000000000000/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright 2009, 2010 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * Boston, MA 02111-1307, USA. * */ /* * A layout test, based on a layout test for another toolkit, * based on a layout test for a different layout manager * by Neil Roberts. */ #include #include #define CHILD_COUNT 11 #define CORNER_SIZE 10 typedef struct _CallbackData CallbackData; struct _CallbackData { ClutterActor *stage; ClutterActor *layout; ClutterActor *rect; ClutterActor *corners[4]; int corner; }; /* minimalistic actors needed to modify gobject properties */ ClutterActor *boolean_toggle (gpointer object, const gchar *prop_name, const gchar *label_text); ClutterActor *normalized_slider (GObject *object, const gchar *prop_name, const gchar *label_text); ClutterActor *units_slider (GObject *object, const gchar *prop_name, const gchar *label_text); static void add_one (ClutterActor *container) { ClutterActor *rectangle; ClutterColor color = {0x0,0x0,0x0,0xff}; clutter_color_from_hls (&color, g_random_double_range (0.0, 360.0), g_random_double_range (0.0, 1.0), g_random_double_range (0.0, 1.0)); rectangle = clutter_rectangle_new_with_color (&color); clutter_container_add_actor (CLUTTER_CONTAINER (container), rectangle); color.alpha = 255; clutter_actor_set_size (rectangle, g_random_int_range (20, 50), g_random_int_range (20, 50)); } static void fill_container (ClutterActor *container) { gint i; for (i=0; i< CHILD_COUNT; i++) add_one (container); } static gboolean adder_timeout (gpointer data) { static gint count = 0; add_one (CLUTTER_ACTOR (data)); if (count ++ > CHILD_COUNT) return FALSE; return TRUE; } static void update_size (CallbackData *data) { ClutterGeometry geom; clutter_actor_get_geometry (data->rect, &geom); clutter_actor_set_geometry (data->layout, &geom); clutter_actor_set_position (data->corners[0], geom.x, geom.y); clutter_actor_set_position (data->corners[1], geom.x + geom.width, geom.y); clutter_actor_set_position (data->corners[2], geom.x, geom.y + geom.height); clutter_actor_set_position (data->corners[3], geom.x + geom.width, geom.y + geom.height); } static gboolean on_button_press (ClutterActor *stage, ClutterButtonEvent *event, CallbackData *data) { int corner; if (event->button == 1) { ClutterActor *actor = clutter_stage_get_actor_at_pos (CLUTTER_STAGE (stage), CLUTTER_PICK_ALL, event->x, event->y); for (corner = 0; corner < 4; corner++) if (actor == data->corners[corner]) break; if (corner < 4) data->corner = corner; } return FALSE; } static gboolean on_motion (ClutterActor *stage, ClutterMotionEvent *event, CallbackData *data) { if ((event->modifier_state & CLUTTER_BUTTON1_MASK) && data->corner != -1) { ClutterGeometry geom; clutter_actor_get_geometry (data->rect, &geom); switch (data->corner) { case 0: geom.width += geom.x - event->x; geom.height += geom.y - event->y; geom.x = event->x; geom.y = event->y; break; case 1: geom.width += event->x - geom.width - geom.x; geom.height += geom.y - event->y; geom.y = event->y; break; case 2: geom.width += geom.x - event->x; geom.height += event->y - geom.height - geom.y; geom.x = event->x; break; case 3: geom.width += event->x - geom.width - geom.x; geom.height += event->y - geom.height - geom.y; break; } clutter_actor_set_geometry (data->rect, &geom); update_size (data); } return FALSE; } static gboolean on_button_release (ClutterActor *stage, ClutterButtonEvent *event, CallbackData *data) { if (event->button == 1) data->corner = -1; return FALSE; } int main (int argc, char **argv) { CallbackData data; gfloat layout_width, layout_height; int i; if (clutter_init (&argc, &argv) != CLUTTER_INIT_SUCCESS) return 1; data.corner = -1; data.stage = clutter_stage_get_default (); data.rect = clutter_rectangle_new_with_color (&(ClutterColor) { 0xd0, 0xd0, 0xff, 0xff}); data.layout =g_object_new (MX_TYPE_GRID, "child-x-align", MX_ALIGN_MIDDLE, "child-y-align", MX_ALIGN_MIDDLE, "row-spacing", 5.0f, "column-spacing", 5.0f, NULL); { /* add controls */ ClutterActor *vbox = g_object_new (MX_TYPE_GRID, "row-spacing", 5.0f, "orientation", MX_ORIENTATION_VERTICAL, "x", 60.0f, "y", 60.0f, NULL); clutter_container_add (CLUTTER_CONTAINER (data.stage), CLUTTER_ACTOR (vbox), NULL); clutter_container_add (CLUTTER_CONTAINER (vbox), clutter_text_new_with_text ("Sans 20px", "MxGrid properties:"), boolean_toggle (G_OBJECT(data.layout), "line-alignment", "line-alignment"), boolean_toggle (G_OBJECT(data.layout), "orientation", "columns"), boolean_toggle (G_OBJECT(data.layout), "homogenous-columns", "homogenous columns"), boolean_toggle (G_OBJECT(data.layout), "homogenous-rows", "homogenous rows"), // boolean_toggle (G_OBJECT(data.layout), "child-x-align", "child-x-align"), // boolean_toggle (G_OBJECT(data.layout), "child-y-align", "child-y-align"), units_slider (G_OBJECT(data.layout), "column-spacing", "column-gap"), units_slider (G_OBJECT(data.layout), "row-spacing", "row-gap"), NULL); } clutter_container_add (CLUTTER_CONTAINER (data.stage), data.rect, NULL); clutter_container_add (CLUTTER_CONTAINER (data.stage), data.layout, NULL); for (i = 0; i < 4; i++) { data.corners[i] = clutter_rectangle_new (); clutter_rectangle_set_color (CLUTTER_RECTANGLE (data.corners[i]), &(ClutterColor) { 0xff, 0xa0, 0xa0, 0xff }); clutter_actor_set_size (data.corners[i], CORNER_SIZE, CORNER_SIZE); clutter_actor_set_anchor_point (data.corners[i], CORNER_SIZE / 2, CORNER_SIZE / 2); clutter_container_add (CLUTTER_CONTAINER (data.stage), data.corners[i], NULL); } clutter_actor_get_preferred_size (data.layout, NULL, NULL, &layout_width, &layout_height); clutter_actor_set_position (data.rect, clutter_actor_get_width (data.stage) / 2 - layout_width / 2, clutter_actor_get_height (data.stage) / 2 - layout_height / 2); clutter_actor_set_size (data.rect, layout_width, layout_height); g_timeout_add (150, adder_timeout, data.layout); update_size (&data); clutter_actor_show (data.stage); g_signal_connect (data.stage, "button-press-event", G_CALLBACK (on_button_press), &data); g_signal_connect (data.stage, "motion-event", G_CALLBACK (on_motion), &data); g_signal_connect (data.stage, "button-release-event", G_CALLBACK (on_button_release), &data); { ClutterActor *layout = g_object_new (MX_TYPE_GRID, "x", 5.0f, "y", 5.0f, "max-stride", 1, NULL); clutter_container_add (CLUTTER_CONTAINER (data.stage), layout, NULL); fill_container (layout); } { ClutterActor *layout = g_object_new (MX_TYPE_GRID, "x", 500.0f, "y", 5.0f, "max-stride", 1, "child-x-align", MX_ALIGN_MIDDLE, "homogenous-columns", TRUE, NULL); clutter_container_add (CLUTTER_CONTAINER (data.stage), layout, NULL); fill_container (layout); } { ClutterActor *layout = g_object_new (MX_TYPE_GRID, "x", 60.0f, "y", 5.0f, "max-stride", 1, "orientation", MX_ORIENTATION_VERTICAL, NULL); clutter_container_add (CLUTTER_CONTAINER (data.stage), layout, NULL); fill_container (layout); } { ClutterActor *layout = g_object_new (MX_TYPE_GRID, "x", 60.0f, "y", 420.0f, "child-y-align", MX_ALIGN_END, "max-stride", 1, "column-spacing", 4.0f, NULL); clutter_container_add (CLUTTER_CONTAINER (data.stage), layout, NULL); fill_container (layout); } clutter_main (); return 0; } /* utility routines for creating visual controls for gobject properties */ #define CONTROL_FONT "Sans 12px" static gboolean boolean_pressed (ClutterActor *toggle, ClutterEvent *event, gpointer data) { const gchar *prop_name; gboolean current; prop_name = g_object_get_data (G_OBJECT (toggle), "prop-name"); g_object_get (data, prop_name, ¤t, NULL); if (event) { current = !current; } g_object_set (data, prop_name, current, NULL); if (current) clutter_text_set_text (CLUTTER_TEXT (toggle), "[x]"); else clutter_text_set_text (CLUTTER_TEXT (toggle), "[ ]"); return TRUE; } ClutterActor *boolean_toggle (gpointer object, const gchar *prop_name, const gchar *label_text) { ClutterActor *group = clutter_group_new (); ClutterActor *label = clutter_text_new_with_text (CONTROL_FONT, label_text); ClutterActor *toggle = clutter_text_new_with_text (CONTROL_FONT, ".."); clutter_container_add_actor (CLUTTER_CONTAINER (group), label); clutter_container_add_actor (CLUTTER_CONTAINER (group), toggle); clutter_actor_set_x (toggle, clutter_actor_get_width (label) + 5); g_object_set_data (G_OBJECT (toggle), "prop-name", (void*) g_intern_string (prop_name)); g_signal_connect (toggle, "button-press-event", G_CALLBACK (boolean_pressed), object); clutter_actor_set_reactive (toggle, TRUE); boolean_pressed (toggle, NULL, object); return group; } static gboolean slider_motion (ClutterActor *actor, ClutterEvent *event, gpointer data) { if (event == NULL || clutter_event_get_state (event) & CLUTTER_BUTTON1_MASK ) { gfloat x; const gchar *prop_name; gdouble current; ClutterActor *indicator; indicator = g_object_get_data (G_OBJECT (actor), "indicator"); prop_name = g_object_get_data (G_OBJECT (actor), "prop-name"); g_object_get (data, prop_name, ¤t, NULL); if (event) { clutter_actor_transform_stage_point (actor, (float) event->motion.x, (float) event->motion.y, &x, NULL); current = x / (clutter_actor_get_width (actor) * 65535.0); } clutter_actor_set_x (indicator, clutter_actor_get_x (actor) + clutter_actor_get_width (actor) * current); g_object_set (data, prop_name, current, NULL); } return TRUE; } ClutterActor *normalized_slider (GObject *object, const gchar *prop_name, const gchar *label_text) { ClutterActor *group = clutter_group_new (); ClutterActor *label = clutter_text_new_with_text (CONTROL_FONT, label_text); ClutterActor *through = clutter_text_new_with_text (CONTROL_FONT, "[ ]"); ClutterActor *indicator = clutter_text_new_with_text (CONTROL_FONT, "X"); clutter_container_add_actor (CLUTTER_CONTAINER (group), label); clutter_container_add_actor (CLUTTER_CONTAINER (group), through); clutter_container_add_actor (CLUTTER_CONTAINER (group), indicator); clutter_actor_set_x (through, clutter_actor_get_width (label) + 5); clutter_actor_set_x (indicator, clutter_actor_get_x (through)); g_object_set_data (G_OBJECT (through), "prop-name", (void*) g_intern_string (prop_name)); g_object_set_data (G_OBJECT (through), "indicator", indicator); g_signal_connect (through, "motion-event", G_CALLBACK (slider_motion),object); clutter_actor_set_reactive (through, TRUE); slider_motion (through, NULL, object); return group; } static gboolean unit_slider_motion (ClutterActor *actor, ClutterEvent *event, gpointer data) { if (event == NULL || clutter_event_get_state (event) & CLUTTER_BUTTON1_MASK ) { gfloat x; const gchar *prop_name; gfloat current; ClutterActor *indicator; indicator = g_object_get_data (G_OBJECT (actor), "indicator"); prop_name = g_object_get_data (G_OBJECT (actor), "prop-name"); g_object_get (data, prop_name, ¤t, NULL); if (event) { clutter_actor_transform_stage_point (actor, (gfloat) event->motion.x, (gfloat) event->motion.y, &x, NULL); current = x; } clutter_actor_set_x (indicator, clutter_actor_get_x (actor) + current); g_object_set (data, prop_name, current, NULL); } return TRUE; } ClutterActor *units_slider (GObject *object, const gchar *prop_name, const gchar *label_text) { ClutterActor *group = clutter_group_new (); ClutterActor *label = clutter_text_new_with_text (CONTROL_FONT, label_text); ClutterActor *through = clutter_text_new_with_text (CONTROL_FONT, "[ ]"); ClutterActor *indicator = clutter_text_new_with_text (CONTROL_FONT, "X"); clutter_container_add_actor (CLUTTER_CONTAINER (group), label); clutter_container_add_actor (CLUTTER_CONTAINER (group), through); clutter_container_add_actor (CLUTTER_CONTAINER (group), indicator); clutter_actor_set_x (through, clutter_actor_get_width (label) + 5); clutter_actor_set_x (indicator, clutter_actor_get_x (through)); g_object_set_data (G_OBJECT (through), "prop-name", (void*) g_intern_string (prop_name)); g_object_set_data (G_OBJECT (through), "indicator", indicator); g_signal_connect (through, "motion-event", G_CALLBACK (unit_slider_motion), object); clutter_actor_set_reactive (through, TRUE); unit_slider_motion (through, NULL, object); return group; } mx-1.4.7/tests/test-gtk.c000066400000000000000000000055531201047117600152260ustar00rootroot00000000000000/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ #include /* * Copyright 2009 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * Boston, MA 02111-1307, USA. * */ #include #include static void _toggle_cb (GtkToggleButton *toggle, gpointer user_data) { gboolean active = gtk_toggle_button_get_active (toggle); g_debug ("toggling to '%d'", active); mx_gtk_light_switch_set_active (MX_GTK_LIGHT_SWITCH (user_data), active); } int main (int argc, char **argv) { GtkWidget *window, *vbox, *frame, *swtch, *swtch2, *toggle, *vbox2; gboolean is_active = FALSE; gtk_init (&argc, &argv); if (argc > 1) is_active = atoi (argv[1]); g_debug ("setting switch to '%d'", is_active); window = gtk_window_new (GTK_WINDOW_TOPLEVEL); gtk_window_set_default_size (GTK_WINDOW (window), 320, 240); vbox = gtk_vbox_new (FALSE, 12); gtk_container_set_border_width (GTK_CONTAINER (vbox), 10); gtk_container_add (GTK_CONTAINER (window), vbox); frame = mx_gtk_frame_new (); gtk_box_pack_start (GTK_BOX (vbox), frame, TRUE, TRUE, 10); gtk_frame_set_label (GTK_FRAME (frame), "Frame Title"); vbox2 = gtk_vbox_new (FALSE, 12); gtk_container_set_border_width (GTK_CONTAINER (vbox2), 10); gtk_container_add (GTK_CONTAINER (frame), vbox2); swtch = mx_gtk_light_switch_new (); mx_gtk_light_switch_set_active (MX_GTK_LIGHT_SWITCH (swtch), is_active); gtk_container_add (GTK_CONTAINER (vbox2), swtch); swtch2 = mx_gtk_light_switch_new (); gtk_widget_set_sensitive (swtch2, FALSE); mx_gtk_light_switch_set_active (MX_GTK_LIGHT_SWITCH (swtch2), is_active); gtk_container_add (GTK_CONTAINER (vbox2), swtch2); frame = gtk_alignment_new (0, 0, 0, 0); gtk_alignment_set_padding (GTK_ALIGNMENT (frame), 10, 10, 10, 10); gtk_container_add (GTK_CONTAINER (vbox), frame); toggle = gtk_toggle_button_new_with_label ("Toggle"); gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), is_active); gtk_container_add (GTK_CONTAINER (frame), toggle); g_signal_connect (toggle, "toggled", G_CALLBACK (_toggle_cb), swtch); gtk_widget_show_all (window); g_signal_connect (window, "delete-event", gtk_main_quit, NULL); gtk_main (); return 0; } mx-1.4.7/tests/test-label.c000066400000000000000000000024441201047117600155140ustar00rootroot00000000000000/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright 2009 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * Boston, MA 02111-1307, USA. * */ #include "test-mx.h" void label_main (ClutterContainer *stage) { ClutterActor *label; MxStyle *style; GError *err = NULL; style = mx_style_new (); mx_style_load_from_file (style, "style/default.css", &err); if (err) { g_warning ("%s", err->message); } label = mx_label_new_with_text ("Hello World!"); clutter_actor_set_position (label, 50, 50); mx_stylable_set_style (MX_STYLABLE (label), style); clutter_container_add (stage, label, NULL); } mx-1.4.7/tests/test-mx.c000066400000000000000000000266631201047117600150720ustar00rootroot00000000000000/* * Copyright 2009, 2010 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * Boston, MA 02111-1307, USA. * * Author: Thomas Wood * */ #include #include #include "test-mx.h" static void rotate_left_clicked_cb (ClutterActor *button, MxWindow *window) { MxWindowRotation rotation = mx_window_get_window_rotation (window); switch (rotation) { case MX_WINDOW_ROTATION_0: rotation = MX_WINDOW_ROTATION_270; break; case MX_WINDOW_ROTATION_90: rotation = MX_WINDOW_ROTATION_0; break; case MX_WINDOW_ROTATION_180: rotation = MX_WINDOW_ROTATION_90; break; case MX_WINDOW_ROTATION_270: rotation = MX_WINDOW_ROTATION_180; break; } mx_window_set_window_rotation (window, rotation); } static void rotate_right_clicked_cb (ClutterActor *button, MxWindow *window) { MxWindowRotation rotation = mx_window_get_window_rotation (window); switch (rotation) { case MX_WINDOW_ROTATION_0: rotation = MX_WINDOW_ROTATION_90; break; case MX_WINDOW_ROTATION_90: rotation = MX_WINDOW_ROTATION_180; break; case MX_WINDOW_ROTATION_180: rotation = MX_WINDOW_ROTATION_270; break; case MX_WINDOW_ROTATION_270: rotation = MX_WINDOW_ROTATION_0; break; } mx_window_set_window_rotation (window, rotation); } static void rotate_180_clicked_cb (ClutterActor *button, MxWindow *window) { MxWindowRotation rotation = mx_window_get_window_rotation (window); switch (rotation) { case MX_WINDOW_ROTATION_0: rotation = MX_WINDOW_ROTATION_180; break; case MX_WINDOW_ROTATION_90: rotation = MX_WINDOW_ROTATION_270; break; case MX_WINDOW_ROTATION_180: rotation = MX_WINDOW_ROTATION_0; break; case MX_WINDOW_ROTATION_270: rotation = MX_WINDOW_ROTATION_90; break; } mx_window_set_window_rotation (window, rotation); } static ClutterActor * create_rotate_box (MxWindow *window) { ClutterActor *button, *icon, *icon2, *layout2; ClutterActor *layout = mx_box_layout_new (); /* Create rotate-left button */ icon = mx_icon_new (); mx_icon_set_icon_name (MX_ICON (icon), "object-rotate-left"); mx_icon_set_icon_size (MX_ICON (icon), 16); button = mx_button_new (); mx_bin_set_child (MX_BIN (button), icon); g_signal_connect (button, "clicked", G_CALLBACK (rotate_left_clicked_cb), window); clutter_container_add_actor (CLUTTER_CONTAINER (layout), button); /* Create rotate-180 button */ icon = mx_icon_new (); mx_icon_set_icon_name (MX_ICON (icon), "object-rotate-left"); mx_icon_set_icon_size (MX_ICON (icon), 16); icon2 = mx_icon_new (); mx_icon_set_icon_name (MX_ICON (icon2), "object-rotate-right"); mx_icon_set_icon_size (MX_ICON (icon2), 16); layout2 = mx_box_layout_new (); clutter_container_add (CLUTTER_CONTAINER (layout2), icon, icon2, NULL); button = mx_button_new (); mx_bin_set_child (MX_BIN (button), layout2); g_signal_connect (button, "clicked", G_CALLBACK (rotate_180_clicked_cb), window); clutter_container_add_actor (CLUTTER_CONTAINER (layout), button); /* Create rotate-right button */ icon = mx_icon_new (); mx_icon_set_icon_name (MX_ICON (icon), "object-rotate-right"); mx_icon_set_icon_size (MX_ICON (icon), 16); button = mx_button_new (); mx_bin_set_child (MX_BIN (button), icon); g_signal_connect (button, "clicked", G_CALLBACK (rotate_right_clicked_cb), window); clutter_container_add_actor (CLUTTER_CONTAINER (layout), button); return layout; } static void show_page (MxButton *button, GParamSpec *pspec, ClutterContainer *holder) { GList *children; TestMxCallback callback; if (!mx_button_get_toggled (button)) return; children = clutter_container_get_children (holder); g_list_foreach (children, (GFunc) clutter_actor_destroy, NULL); callback = g_object_get_data (G_OBJECT (button), "callback"); callback ((gpointer)holder); } static void next_tab_activated_cb (MxAction *action, MxButtonGroup *group) { GSList *b; gboolean activate_next = FALSE; MxButton *button = mx_button_group_get_active_button (group); const GSList *buttons = mx_button_group_get_buttons (group); if (!buttons) return; if (button) for (b = (GSList *)buttons; b; b = b->next) { MxButton *current_button = (MxButton *)b->data; if (activate_next) { button = current_button; break; } if ((current_button == button) && b->next) activate_next = TRUE; } if (!activate_next) button = (MxButton *)buttons->data; mx_button_group_set_active_button (group, button); } static void prev_tab_activated_cb (MxAction *action, MxButtonGroup *group) { GSList *b; MxButton *last_button = NULL; MxButton *button = mx_button_group_get_active_button (group); const GSList *buttons = mx_button_group_get_buttons (group); if (!buttons) return; for (b = (GSList *)buttons; b; b = b->next) { MxButton *current_button = (MxButton *)b->data; if (current_button == button) break; last_button = current_button; } if (!last_button) last_button = (MxButton *)g_slist_last ((GSList *)buttons)->data; mx_button_group_set_active_button (group, last_button); } static void add_tab (ClutterContainer *box, MxButtonGroup *group, const gchar *name, GCallback callback, ClutterContainer *holder) { ClutterActor *button; button = mx_button_new_with_label (name); mx_button_set_is_toggle (MX_BUTTON (button), TRUE); clutter_container_add_actor (box, button); mx_button_group_add (group, MX_BUTTON (button)); if (callback) { g_object_set_data (G_OBJECT (button), "callback", callback); g_signal_connect (button, "notify::toggled", G_CALLBACK (show_page), holder); } } int main (int argc, char *argv[]) { ClutterActor *stage, *vbox, *hbox, *holder, *mainbox, *toolbar, *combo, *layout; MxAction *prev, *next; MxButtonGroup *group; MxApplication *application; MxWindow *window; application = mx_application_new (&argc, &argv, "Test Mx", MX_APPLICATION_SINGLE_INSTANCE); window = mx_application_create_window (application); stage = (ClutterActor *)mx_window_get_clutter_stage (window); mainbox = mx_box_layout_new (); clutter_actor_set_size (mainbox, 800, 600); mx_box_layout_set_orientation (MX_BOX_LAYOUT (mainbox), MX_ORIENTATION_VERTICAL); mx_window_set_child (window, mainbox); /* create the toolbar */ toolbar = (ClutterActor *)mx_window_get_toolbar (window); mx_bin_set_fill (MX_BIN (toolbar), TRUE, FALSE); layout = mx_box_layout_new (); combo = mx_combo_box_new (); clutter_container_add_actor (CLUTTER_CONTAINER (layout), combo); mx_combo_box_set_active_text (MX_COMBO_BOX (combo), "Select"); mx_combo_box_set_active_icon_name (MX_COMBO_BOX (combo), "dialog-question"); mx_combo_box_append_text (MX_COMBO_BOX (combo), "Hello"); mx_combo_box_append_text (MX_COMBO_BOX (combo), "World"); mx_box_layout_add_actor_with_properties (MX_BOX_LAYOUT (layout), create_rotate_box (window), 1, "x-fill", FALSE, "x-align", MX_ALIGN_END, "expand", TRUE, NULL); clutter_container_add_actor (CLUTTER_CONTAINER (toolbar), layout); /* create the horizontal layout */ hbox = mx_box_layout_new (); /* add horizontal layout to main container */ clutter_container_add (CLUTTER_CONTAINER (mainbox), hbox, NULL); /* create a vbox for the list of tests */ vbox = mx_box_layout_new (); mx_box_layout_set_orientation (MX_BOX_LAYOUT (vbox), MX_ORIENTATION_VERTICAL); clutter_container_add_actor (CLUTTER_CONTAINER (hbox), vbox); /* create a place holder for the tests */ holder = clutter_group_new (); clutter_container_add_actor (CLUTTER_CONTAINER (hbox), holder); group = mx_button_group_new (); add_tab (CLUTTER_CONTAINER (vbox), group, "Label", (GCallback) label_main, CLUTTER_CONTAINER (holder)); add_tab (CLUTTER_CONTAINER (vbox), group, "Entry", (GCallback) entry_main, CLUTTER_CONTAINER (holder)); add_tab (CLUTTER_CONTAINER (vbox), group, "Button", (GCallback) buttons_main, CLUTTER_CONTAINER (holder)); add_tab (CLUTTER_CONTAINER (vbox), group, "Button group", (GCallback) button_group_main, CLUTTER_CONTAINER (holder)); add_tab (CLUTTER_CONTAINER (vbox), group, "Combo Box", (GCallback) combo_box_main, CLUTTER_CONTAINER (holder)); add_tab (CLUTTER_CONTAINER (vbox), group, "Progress Bar", (GCallback) progress_bar_main, CLUTTER_CONTAINER (holder)); add_tab (CLUTTER_CONTAINER (vbox), group, "Slider", (GCallback) slider_main, CLUTTER_CONTAINER (holder)); add_tab (CLUTTER_CONTAINER (vbox), group, "Toggle", (GCallback) toggle_main, CLUTTER_CONTAINER (holder)); add_tab (CLUTTER_CONTAINER (vbox), group, "Tooltips", (GCallback) tooltips_main, CLUTTER_CONTAINER (holder)); add_tab (CLUTTER_CONTAINER (vbox), group, "Expander", (GCallback) expander_main, CLUTTER_CONTAINER (holder)); add_tab (CLUTTER_CONTAINER (vbox), group, "Scroll Grid", (GCallback) scroll_grid_main, CLUTTER_CONTAINER (holder)); add_tab (CLUTTER_CONTAINER (vbox), group, "Scroll Bar", (GCallback) scroll_bar_main, CLUTTER_CONTAINER (holder)); add_tab (CLUTTER_CONTAINER (vbox), group, "Scroll View", (GCallback) scroll_view_main, CLUTTER_CONTAINER (holder)); add_tab (CLUTTER_CONTAINER (vbox), group, "Styles", (GCallback) styles_main, CLUTTER_CONTAINER (holder)); add_tab (CLUTTER_CONTAINER (vbox), group, "Dialog", (GCallback) dialog_main, CLUTTER_CONTAINER (holder)); add_tab (CLUTTER_CONTAINER (vbox), group, "Spinner", (GCallback) spinner_main, CLUTTER_CONTAINER (holder)); prev = mx_action_new_full ("Previous tab", "Previous tab", G_CALLBACK (prev_tab_activated_cb), group); next = mx_action_new_full ("Next tab", "Next tab", G_CALLBACK (next_tab_activated_cb), group); mx_application_add_action (application, prev); mx_application_add_action (application, next); clutter_actor_show (stage); mx_application_run (application); return EXIT_SUCCESS; } mx-1.4.7/tests/test-mx.h000066400000000000000000000032151201047117600150630ustar00rootroot00000000000000/* * Copyright 2009 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * Boston, MA 02111-1307, USA. * */ #include #include typedef void (*TestMxCallback)(ClutterContainer *container); void label_main (ClutterContainer *container); void entry_main (ClutterContainer *container); void tooltips_main (ClutterContainer *container); void buttons_main (ClutterContainer *container); void button_group_main (ClutterContainer *container); void combo_box_main (ClutterContainer *container); void progress_bar_main (ClutterContainer *container); void slider_main (ClutterContainer *container); void expander_main (ClutterContainer *container); void scroll_grid_main (ClutterContainer *container); void scroll_bar_main (ClutterContainer *container); void scroll_view_main (ClutterContainer *container); void styles_main (ClutterContainer *container); void toggle_main (ClutterContainer *container); void dialog_main (ClutterContainer *container); void spinner_main (ClutterContainer *container); mx-1.4.7/tests/test-path-bar.c000066400000000000000000000073401201047117600161330ustar00rootroot00000000000000/* * Copyright 2010 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * Boston, MA 02111-1307, USA. * */ #include static void add_cb (MxPathBar *bar) { gchar *string = g_strdup_printf ("Crumb %d", mx_path_bar_get_level (bar) + 1); mx_path_bar_push (bar, string); g_free (string); } static void remove_cb (MxPathBar *bar) { mx_path_bar_pop (bar); } static void toggle_editable_cb (MxPathBar *bar) { ClutterActor *parent = clutter_actor_get_parent (CLUTTER_ACTOR (bar)); gboolean editable = !mx_path_bar_get_editable (bar); mx_path_bar_set_editable (bar, editable); if (MX_IS_BIN (parent)) mx_bin_set_fill (MX_BIN (parent), editable, FALSE); else if (MX_IS_BOX_LAYOUT (parent)) clutter_container_child_set (CLUTTER_CONTAINER (parent), CLUTTER_ACTOR (bar), "x-fill", editable, NULL); } static void relabel_cb (MxPathBar *bar) { const gchar *string = mx_path_bar_get_text (bar); mx_path_bar_set_label (bar, 1, string ? string : ""); } int main (int argc, char **argv) { MxWindow *window; MxApplication *app; ClutterActor *stage, *bar, *button, *hbox; app = mx_application_new (&argc, &argv, "Test PathBar", 0); window = mx_application_create_window (app); stage = (ClutterActor *)mx_window_get_clutter_stage (window); bar = mx_path_bar_new (); mx_path_bar_set_clear_on_change (MX_PATH_BAR (bar), TRUE); hbox = mx_box_layout_new (); button = mx_button_new_with_label ("Add crumb"); g_signal_connect_swapped (button, "clicked", G_CALLBACK (add_cb), bar); clutter_container_add_actor (CLUTTER_CONTAINER (hbox), button); button = mx_button_new_with_label ("Remove crumb"); g_signal_connect_swapped (button, "clicked", G_CALLBACK (remove_cb), bar); clutter_container_add_actor (CLUTTER_CONTAINER (hbox), button); button = mx_button_new_with_label ("Toggle editable"); g_signal_connect_swapped (button, "clicked", G_CALLBACK (toggle_editable_cb), bar); clutter_container_add_actor (CLUTTER_CONTAINER (hbox), button); button = mx_button_new_with_label ("Re-label first crumb"); g_signal_connect_swapped (button, "clicked", G_CALLBACK (relabel_cb), bar); clutter_container_add_actor (CLUTTER_CONTAINER (hbox), button); /* -a for 'alternative packing'... */ if (argc > 1 && g_str_equal (argv[1], "-a")) { ClutterActor *vbox = mx_box_layout_new (); mx_box_layout_set_orientation (MX_BOX_LAYOUT (vbox), MX_ORIENTATION_VERTICAL); clutter_container_add_actor (CLUTTER_CONTAINER (vbox), bar); clutter_container_add_actor (CLUTTER_CONTAINER (vbox), hbox); mx_window_set_child (window, vbox); mx_path_bar_set_editable (MX_PATH_BAR (bar), TRUE); } else { MxToolbar *toolbar = mx_window_get_toolbar (window); clutter_container_add_actor (CLUTTER_CONTAINER (toolbar), bar); mx_window_set_child (window, hbox); } clutter_actor_show (stage); mx_application_run (app); return 0; } mx-1.4.7/tests/test-progress-bar.c000066400000000000000000000026621201047117600170450ustar00rootroot00000000000000/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright 2009 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * Boston, MA 02111-1307, USA. * */ #include "test-mx.h" void progress_bar_main (ClutterContainer *stage) { ClutterActor *progress_bar; ClutterAnimation *animation; progress_bar = mx_progress_bar_new (); animation = clutter_actor_animate (progress_bar, CLUTTER_LINEAR, 5000, "progress", 1.0, NULL); clutter_animation_set_loop (animation, TRUE); clutter_container_add_actor (stage, progress_bar); clutter_actor_set_width (progress_bar, 280); clutter_actor_set_position (progress_bar, 20, 20); } mx-1.4.7/tests/test-script.c000066400000000000000000000035421201047117600157410ustar00rootroot00000000000000/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright 2009 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * Boston, MA 02111-1307, USA. * */ #include #include #include #include #include static ClutterScript *script = NULL; int main (int argc, char *argv[]) { GObject *stage; GError *error = NULL; gint res; clutter_init (&argc, &argv); /* load the style sheet */ mx_style_load_from_file (mx_style_get_default (), "style/default.css", NULL); script = clutter_script_new (); g_assert (CLUTTER_IS_SCRIPT (script)); clutter_script_load_from_file (script, "test-script.json", &error); if (error) { g_print ("*** Error:\n" "*** %s\n", error->message); g_error_free (error); g_object_unref (script); return EXIT_FAILURE; } clutter_script_connect_signals (script, NULL); res = clutter_script_get_objects (script, "main-stage", &stage, NULL); clutter_actor_show (CLUTTER_ACTOR (stage)); clutter_main (); g_object_unref (script); return EXIT_SUCCESS; } mx-1.4.7/tests/test-script.json000066400000000000000000000012421201047117600164630ustar00rootroot00000000000000{ "My Scene" : { "id" : "main-stage", "type" : "ClutterStage", "title" : "ClutterScript test", "color" : "black", "is-default" : true, "children" : [ { "id" : "left-button", "type" : "MxButton", "x" : 50, "y" : 50, "width" : 100, "height" : 100, }, { "id" : "right-button", "type" : "MxButton", "x" : 200, "y" : 50, "width" : 100, "height" : 100, }, { "id" : "red-button", "name" : "red-button", "type" : "MxButton", "x" : 125, "y" : 150, "width" : 100, "height" : 100, } ] } } mx-1.4.7/tests/test-scroll-bar.c000066400000000000000000000050331201047117600164720ustar00rootroot00000000000000/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright 2009, 2010 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * Boston, MA 02111-1307, USA. * */ #include "test-mx.h" static void changed_cb (MxAdjustment *adjustment, gpointer data) { printf ("%s() %.2f\n", __FUNCTION__, mx_adjustment_get_value (adjustment)); } static gboolean key_press_event (ClutterActor *actor, ClutterKeyEvent *event, MxAdjustment *adjustment) { gdouble value, lower, upper, step_increment, page_increment, page_size; mx_adjustment_get_values (adjustment, &value, &lower, &upper, &step_increment, &page_increment, &page_size); if (event->keyval == CLUTTER_Up) { page_size += 5; } else if (event->keyval == CLUTTER_Down) { page_size -= 5; } mx_adjustment_set_values (adjustment, value, lower, upper, step_increment, page_increment, page_size); printf ("value: %f, lower: %f, upper: %f, step-inc: %f, page-inc: %f, " "page: %f\n", value, lower, upper, step_increment, page_increment, page_size); return TRUE; } void scroll_bar_main (ClutterContainer *stage) { ClutterActor *scroll; MxAdjustment *adjustment; adjustment = mx_adjustment_new_with_values (50., 0., 100., 1., 10., 10.); g_signal_connect (adjustment, "notify::value", G_CALLBACK (changed_cb), NULL); scroll = mx_scroll_bar_new_with_adjustment (adjustment); clutter_actor_set_position (scroll, 50, 100); clutter_container_add_actor (stage, scroll); g_signal_connect (stage, "key-press-event", G_CALLBACK (key_press_event), adjustment); } mx-1.4.7/tests/test-scroll-grid.c000066400000000000000000000102421201047117600166510ustar00rootroot00000000000000/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright 2009, 2010 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * Boston, MA 02111-1307, USA. * */ #include "test-mx.h" #include static void swap_orientation (ClutterActor *button, MxGrid *grid) { mx_grid_set_orientation (grid, !mx_grid_get_orientation (grid)); } static void ensure_visible (ClutterText *text, MxGrid *grid) { GList *children; ClutterActor *child; const gchar *string = clutter_text_get_text (text); gint number = atoi (string) - 1; children = clutter_container_get_children (CLUTTER_CONTAINER (grid)); child = g_list_nth_data (children, number); if (child) { ClutterGeometry geom; MxScrollView *scroll = (MxScrollView *) clutter_actor_get_parent (CLUTTER_ACTOR (grid)); clutter_actor_get_allocation_geometry (child, &geom); mx_scroll_view_ensure_visible (scroll, &geom); printf ("Making child %d visible\n", number); } else printf ("Couldn't make child %d visible\n", number); g_list_free (children); } static void set_max_stride (ClutterText *text, MxGrid *grid) { const gchar *string = clutter_text_get_text (text); mx_grid_set_max_stride (grid, atoi (string)); printf ("Max Stride: %d\n", atoi (string)); } void scroll_grid_main (ClutterContainer *stage) { ClutterActor *scroll, *grid, *table, *label, *visible_entry, *stride_entry; int i; scroll = mx_scroll_view_new (); clutter_container_add_actor (stage, scroll); clutter_actor_set_position (scroll, 10, 10); clutter_actor_set_size (scroll, 400, 400); grid = mx_grid_new (); clutter_container_add_actor (CLUTTER_CONTAINER (scroll), grid); for (i = 1; i <= 200; i++) { ClutterActor *button; gchar *text; text = g_strdup_printf ("Button %d", i); button = mx_button_new_with_label (text); clutter_container_add_actor (CLUTTER_CONTAINER (grid), button); mx_widget_set_tooltip_text (MX_WIDGET (button), "test"); if (i == 1) g_signal_connect (button, "clicked", G_CALLBACK (swap_orientation), grid); g_free (text); } table = mx_table_new (); label = mx_label_new_with_text ("Make button visible:"); visible_entry = mx_entry_new (); mx_table_add_actor_with_properties (MX_TABLE (table), label, 0, 0, "x-expand", FALSE, NULL); mx_table_add_actor (MX_TABLE (table), visible_entry, 0, 1); label = mx_label_new_with_text ("Set max-stride:"); stride_entry = mx_entry_new (); mx_table_add_actor_with_properties (MX_TABLE (table), label, 1, 0, "x-expand", FALSE, NULL); mx_table_add_actor (MX_TABLE (table), stride_entry, 1, 1); clutter_actor_set_position (table, 10, 420); clutter_actor_set_width (table, 400); clutter_container_add_actor (stage, table); g_signal_connect (mx_entry_get_clutter_text (MX_ENTRY (visible_entry)), "activate", G_CALLBACK (ensure_visible), grid); g_signal_connect (mx_entry_get_clutter_text (MX_ENTRY (stride_entry)), "activate", G_CALLBACK (set_max_stride), grid); } mx-1.4.7/tests/test-scroll-view.c000066400000000000000000000102601201047117600166760ustar00rootroot00000000000000/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright 2009 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * Boston, MA 02111-1307, USA. * */ #include "test-mx.h" static void notify_overshoot_cb (MxToggle *toggle, GParamSpec *pspec, MxKineticScrollView *view) { gboolean on = mx_toggle_get_active (toggle); mx_kinetic_scroll_view_set_overshoot (view, on ? 0.2 : 0.0); } static void notify_elastic_cb (MxToggle *toggle, GParamSpec *pspec, MxScrollable *view) { MxAdjustment *hadjust, *vadjust; gboolean on = mx_toggle_get_active (toggle); mx_scrollable_get_adjustments (view, &hadjust, &vadjust); mx_adjustment_set_elastic (hadjust, on); mx_adjustment_set_elastic (vadjust, on); } static gboolean true_cb () { return TRUE; } void scroll_view_main (ClutterContainer *stage) { gint width, height; MxAdjustment *hadjust, *vadjust; ClutterActor *label, *elastic, *overshoot, *scroll, *kinetic, *view, *texture; scroll = mx_scroll_view_new (); /* Make sure something underneath the kinetic scroll view swallows events * so that we don't end up moving the window. */ g_signal_connect (scroll, "button-press-event", G_CALLBACK (true_cb), NULL); kinetic = mx_kinetic_scroll_view_new (); clutter_container_add_actor (stage, scroll); clutter_actor_set_position (scroll, 10, 10); clutter_actor_set_size (scroll, 300, 300); view = mx_viewport_new (); mx_viewport_set_sync_adjustments (MX_VIEWPORT (view), FALSE); clutter_container_add_actor (CLUTTER_CONTAINER (kinetic), view); clutter_container_add_actor (CLUTTER_CONTAINER (scroll), kinetic); texture = clutter_texture_new_from_file ("redhand.png", NULL); clutter_container_add_actor (CLUTTER_CONTAINER (view), texture); g_object_set (texture, "repeat-x", TRUE, "repeat-y", TRUE, NULL); clutter_actor_set_size (texture, 1280, 1280); clutter_texture_get_base_size (CLUTTER_TEXTURE (texture), &width, &height); mx_scrollable_get_adjustments (MX_SCROLLABLE (view), &hadjust, &vadjust); mx_adjustment_set_values (hadjust, 0, 0, 1280, width, width * 3, 300); mx_adjustment_set_values (vadjust, 0, 0, 1280, height, height * 3, 300); label = mx_label_new_with_text ("Toggle over-shooting:"); overshoot = mx_toggle_new (); clutter_actor_set_position (label, 320, 10); clutter_actor_set_position (overshoot, 330 + clutter_actor_get_width (label), 10); clutter_container_add (stage, label, overshoot, NULL); g_signal_connect (overshoot, "notify::active", G_CALLBACK (notify_overshoot_cb), kinetic); label = mx_label_new_with_text ("Toggle elasticity:"); elastic = mx_toggle_new (); clutter_actor_set_position (label, 320, 20 + clutter_actor_get_height (overshoot)); clutter_actor_set_position (elastic, clutter_actor_get_x (overshoot), clutter_actor_get_y (label)); clutter_container_add (stage, label, elastic, NULL); g_signal_connect (elastic, "notify::active", G_CALLBACK (notify_elastic_cb), kinetic); } mx-1.4.7/tests/test-shaders.c000066400000000000000000000120061201047117600160610ustar00rootroot00000000000000 #include #ifdef COGL_HAS_GLES2 #define GLES2_VARS \ "precision mediump float;\n" \ "varying vec2 tex_coord;\n" \ "varying vec4 frag_color;\n" #define TEX_COORD "tex_coord" #define COLOR_VAR "frag_color" #else #define GLES2_VARS "" #define TEX_COORD "gl_TexCoord[0]" #define COLOR_VAR "gl_Color" #endif static gchar *blur = GLES2_VARS "uniform sampler2D tex;\n" "uniform float x_step, y_step, x_radius, y_radius;\n" "void\n" "main ()\n" " {\n" " float u, v, samples;\n" " vec4 color = vec4 (0.0, 0.0, 0.0, 0.0);\n" " samples = 0.0;\n" " for (v = floor (-y_radius); v < ceil (y_radius); v++)\n" " for (u = floor (-x_radius); u < ceil (x_radius); u++)\n" " {\n" " float s, t, mult;\n" " mult = 1.0;\n" " if (u < x_radius)\n" " mult *= x_radius - u;\n" " if (u > x_radius)\n" " mult *= u - x_radius;\n" " if (v < y_radius)\n" " mult *= y_radius - v;\n" " if (v > y_radius)\n" " mult *= v - y_radius;\n" " color += texture2D (tex, vec2("TEX_COORD".s + u * x_step,\n" " "TEX_COORD".y + v * y_step)) *\n" " mult;\n" " samples += mult;\n" " }\n" " color.rgba /= samples;\n" " gl_FragColor = color * "COLOR_VAR";\n" " }\n"; static int next_p2 (gint a) { /* find the next power of two */ int rval = 1; while (rval < a) rval <<= 1; return rval; } static void size_change_cb (ClutterActor *texture, gint width, gint height) { clutter_actor_set_shader_param_float (texture, "x_step", 1.0f / next_p2 (width)); clutter_actor_set_shader_param_float (texture, "y_step", 1.0f / next_p2 (height)); } static void value_cb (MxSlider *slider, GParamSpec *pspec, ClutterActor *actor) { gdouble value = mx_slider_get_value (slider); clutter_actor_set_shader_param_float (actor, "x_radius", (value * 3.f) + 1.f); clutter_actor_set_shader_param_float (actor, "y_radius", (value * 3.f) + 1.f); } int main (int argc, char **argv) { MxWindow *window; gint width, height; ClutterShader *shader; ClutterActor *stage, *offscreen, *button, *texture, *box, *slider; if (clutter_init (&argc, &argv) != CLUTTER_INIT_SUCCESS) return 1; window = mx_window_new (); stage = (ClutterActor *)mx_window_get_clutter_stage (window); clutter_actor_set_size (stage, 500, 250); button = mx_button_new_with_label ("Button"); texture = clutter_texture_new_from_file ("redhand.png", NULL); clutter_texture_set_keep_aspect_ratio (CLUTTER_TEXTURE (texture), TRUE); offscreen = mx_offscreen_new (); mx_offscreen_set_pick_child (MX_OFFSCREEN (offscreen), TRUE); clutter_container_add_actor (CLUTTER_CONTAINER (offscreen), button); box = mx_box_layout_new (); mx_box_layout_add_actor_with_properties (MX_BOX_LAYOUT (box), offscreen, 0, "expand", TRUE, NULL); mx_box_layout_add_actor (MX_BOX_LAYOUT (box), texture, 1); mx_window_set_child (window, box); /* Set Clutter shader */ shader = clutter_shader_new (); clutter_shader_set_fragment_source (shader, blur, -1); clutter_shader_compile (shader, NULL); clutter_actor_set_shader (texture, shader); clutter_actor_set_shader (offscreen, shader); /* Set shader parameters */ clutter_actor_set_shader_param_int (texture, "tex", 0); clutter_actor_set_shader_param_int (offscreen, "tex", 0); clutter_actor_set_shader_param_float (texture, "x_radius", 1.f); clutter_actor_set_shader_param_float (texture, "y_radius", 1.f); clutter_actor_set_shader_param_float (offscreen, "x_radius", 1.f); clutter_actor_set_shader_param_float (offscreen, "y_radius", 1.f); /* Hook onto texture size change for step parameters */ g_signal_connect (texture, "size-change", G_CALLBACK (size_change_cb), NULL); clutter_texture_get_base_size (CLUTTER_TEXTURE (texture), &width, &height); size_change_cb (texture, width, height); g_signal_connect (offscreen, "size-change", G_CALLBACK (size_change_cb), NULL); clutter_texture_get_base_size (CLUTTER_TEXTURE (offscreen), &width, &height); size_change_cb (offscreen, width, height); /* Set up a slider to control the shader radius parameter */ slider = mx_slider_new (); clutter_container_add_actor ( CLUTTER_CONTAINER (mx_window_get_toolbar (window)), slider); mx_bin_set_fill (MX_BIN (mx_window_get_toolbar (window)), TRUE, FALSE); g_signal_connect (slider, "notify::value", G_CALLBACK (value_cb), texture); g_signal_connect (slider, "notify::value", G_CALLBACK (value_cb), offscreen); g_signal_connect (window, "destroy", G_CALLBACK (clutter_main_quit), NULL); clutter_actor_show (stage); clutter_main (); return 0; } mx-1.4.7/tests/test-slider.c000066400000000000000000000035721201047117600157220ustar00rootroot00000000000000/* * Copyright 2009 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * Boston, MA 02111-1307, USA. * * Written by: Damien Lespiau * */ #include "test-mx.h" static void on_value_changed (MxSlider *slider, GParamSpec *pspec, ClutterRectangle *rectangle) { ClutterColor color = {0xff, 0, 0, 0xff}; gdouble value; value = mx_slider_get_value (slider); color.alpha = (guint8) (value * 0xff); clutter_rectangle_set_color (rectangle, &color); mx_slider_set_buffer_value (slider, MIN (0.1 + value * 1.5, 1.0)); } void slider_main (ClutterContainer *stage) { ClutterActor *rectangle, *slider; ClutterColor color = {0xff, 0, 0, 0}; slider = mx_slider_new (); rectangle = clutter_rectangle_new_with_color (&color); g_signal_connect (slider, "notify::value", G_CALLBACK (on_value_changed), rectangle); clutter_container_add_actor (stage, slider); clutter_actor_set_width (slider, 280); clutter_actor_set_position (slider, 20, 20); clutter_container_add_actor (stage, rectangle); clutter_actor_set_size (rectangle, 64, 64); clutter_actor_set_position (rectangle, 108, 52); mx_slider_set_value (MX_SLIDER (slider), 0.5); } mx-1.4.7/tests/test-spinner.c000066400000000000000000000040621201047117600161110ustar00rootroot00000000000000/* * Copyright 2011 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * Boston, MA 02111-1307, USA. * * Written by: Chris Lord * */ #include "test-mx.h" static void on_toggle (MxButton *toggle, GParamSpec *pspec, MxSpinner *spinner) { if (mx_button_get_toggled (toggle)) { mx_spinner_set_animating (spinner, TRUE); mx_button_set_icon_name (MX_BUTTON (toggle), "media-playback-stop"); } else { mx_spinner_set_animating (spinner, FALSE); mx_button_set_icon_name (MX_BUTTON (toggle), "media-playback-start"); } } static void on_looped (MxSpinner *spinner, MxButton *button) { mx_button_set_toggled (button, FALSE); } void spinner_main (ClutterContainer *stage) { ClutterActor *toggle, *spinner; toggle = mx_button_new (); mx_button_set_is_toggle (MX_BUTTON (toggle), TRUE); spinner = mx_spinner_new (); on_toggle (MX_BUTTON (toggle), NULL, MX_SPINNER (spinner)); g_signal_connect (toggle, "notify::toggled", G_CALLBACK (on_toggle), spinner); g_signal_connect (spinner, "looped", G_CALLBACK (on_looped), toggle); clutter_container_add_actor (stage, toggle); clutter_actor_set_position (toggle, 20, 20); clutter_container_add_actor (stage, spinner); clutter_actor_set_position (spinner, 36 + clutter_actor_get_width (toggle), 20); } mx-1.4.7/tests/test-stack.c000066400000000000000000000074251201047117600155460ustar00rootroot00000000000000/* * Copyright 2010 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * Boston, MA 02111-1307, USA. * */ #include static void animate_rotation (ClutterContainer *stack) { gfloat angle = 0; GList *c, *children = clutter_container_get_children (stack); children = g_list_reverse (children); for (c = children; c; c = c->next, angle += 5) clutter_actor_animate (CLUTTER_ACTOR (c->data), CLUTTER_EASE_OUT_QUAD, 250, "opacity", 0xff, "rotation-angle-z", angle, "fixed::rotation-center-z-gravity", CLUTTER_GRAVITY_SOUTH_EAST, NULL); g_list_free (children); } static void lower_bottom (ClutterAnimation *animation, ClutterActor *actor) { ClutterContainer *stack = CLUTTER_CONTAINER (clutter_actor_get_parent (actor)); clutter_container_lower_child (stack, actor, NULL); } static void button_clicked_cb (ClutterActor *button, ClutterContainer *stack) { clutter_actor_animate (CLUTTER_ACTOR (button), CLUTTER_EASE_IN_OUT_QUAD, 400, "opacity", 0x00, "signal::completed", lower_bottom, button, "signal-swapped-after::completed", animate_rotation, stack, NULL); } int main (int argc, char **argv) { gint i; MxWindow *window; MxApplication *app; MxTextureCache *cache; ClutterActor *stage, *stack1, *stack2, *label; app = mx_application_new (&argc, &argv, "Test Stack", 0); window = mx_application_create_window (app); stage = (ClutterActor *)mx_window_get_clutter_stage (window); stack1 = mx_stack_new (); stack2 = mx_stack_new (); label = mx_label_new_with_text ("This is a test of the stack"); clutter_container_add (CLUTTER_CONTAINER (stack1), stack2, label, NULL); clutter_container_child_set (CLUTTER_CONTAINER (stack1), label, "x-fill", FALSE, "y-fill", FALSE, "x-align", MX_ALIGN_START, "y-align", MX_ALIGN_END, NULL); cache = mx_texture_cache_get_default (); for (i = 0; i < 10; i++) { ClutterActor *button = mx_button_new (); ClutterActor *texture = mx_texture_cache_get_actor (cache, "redhand.png"); mx_bin_set_child (MX_BIN (button), texture); clutter_container_add_actor (CLUTTER_CONTAINER (stack2), button); clutter_container_child_set (CLUTTER_CONTAINER (stack2), button, "x-fill", FALSE, "y-fill", FALSE, NULL); g_signal_connect (button, "clicked", G_CALLBACK (button_clicked_cb), stack2); } animate_rotation (CLUTTER_CONTAINER (stack2)); mx_window_set_child (window, stack1); clutter_actor_show (stage); mx_application_run (app); return 0; } mx-1.4.7/tests/test-styles.c000066400000000000000000000050241201047117600157550ustar00rootroot00000000000000/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright 2009 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * Boston, MA 02111-1307, USA. * */ #include "test-mx.h" static ClutterActor * create_button (ClutterContainer *parent, const gchar *text, gint x, gint y) { ClutterActor *button; button = mx_button_new_with_label (text); clutter_container_add_actor (CLUTTER_CONTAINER (parent), button); clutter_actor_set_position (button, x, y); clutter_actor_set_size (button, 150, 100); return button; } void styles_main (ClutterContainer *stage) { ClutterActor *button, *table; MxStyle *style; /* load the style sheet */ style = mx_style_new (); mx_style_load_from_file (style, "style/default.css", NULL); button = create_button (stage, "Default Style", 10, 10); mx_stylable_set_style (MX_STYLABLE (button), style); clutter_actor_set_name (button, "default-button"); button = create_button (stage, "Red Style", 10, 150); mx_stylable_set_style (MX_STYLABLE (button), style); clutter_actor_set_name (button, "red-button"); button = create_button (stage, "Green Style", 200, 10); mx_stylable_set_style (MX_STYLABLE (button), style); clutter_actor_set_name (button, "green-button"); button = create_button (stage, "Blue Style", 200, 150); mx_stylable_set_style (MX_STYLABLE (button), style); clutter_actor_set_name (button, "blue-button"); table = mx_table_new (); mx_stylable_set_style (MX_STYLABLE (table), style); clutter_actor_set_size (table, 200, 80); clutter_container_add_actor (stage, table); clutter_actor_set_position (table, 10, 300); button = mx_button_new_with_label ("Container Test"); mx_stylable_set_style (MX_STYLABLE (button), style); clutter_actor_set_name (button, "container-button"); mx_table_add_actor (MX_TABLE (table), button, 0, 0); } mx-1.4.7/tests/test-table-2.c000066400000000000000000000053551201047117600156670ustar00rootroot00000000000000/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright 2009 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * Boston, MA 02111-1307, USA. * */ #include #include #include #include int main (int argc, char **argv) { ClutterActor *stage, *txt, *table, *label, *label2; if (clutter_init (&argc, &argv) != CLUTTER_INIT_SUCCESS) return 1; mx_style_load_from_file (mx_style_get_default (), "style/default.css", NULL); stage = clutter_stage_get_default (); clutter_actor_set_size (stage, 640, 1000); table = mx_table_new (); mx_table_set_column_spacing (MX_TABLE (table), 10); clutter_actor_set_position (table, 50, 50); clutter_actor_set_width (table, 300); clutter_container_add (CLUTTER_CONTAINER (stage), table, NULL); label = mx_label_new_with_text ("Short top text"); mx_table_add_actor (MX_TABLE (table), label, 0, 0); label2 = mx_label_new_with_text (""); txt = mx_label_get_clutter_text(MX_LABEL(label2)); clutter_text_set_ellipsize (CLUTTER_TEXT (txt), PANGO_ELLIPSIZE_NONE); clutter_text_set_line_alignment (CLUTTER_TEXT (txt), PANGO_ALIGN_LEFT); clutter_text_set_line_wrap (CLUTTER_TEXT (txt), TRUE); mx_table_add_actor (MX_TABLE (table), label2, 1, 0); clutter_container_child_set (CLUTTER_CONTAINER (table), label2, "y-expand", FALSE, "x-expand", FALSE, NULL); label = mx_label_new_with_text ("Short Bottom text"); mx_table_add_actor (MX_TABLE (table), label, 2, 0); mx_label_set_text (MX_LABEL (label2), "Really really long long long long long long long long long long long long long long long long long long (ooooh this is verrrrrrry long!) long longlong long long longlong long long long \nlong longlong long long long longlonglonglonglonglonglonglonglonglonglonglonglong long long long long long long long long long Loooooooooooooooong text"); clutter_actor_show (stage); clutter_main (); return EXIT_SUCCESS; } mx-1.4.7/tests/test-table.c000066400000000000000000000215021201047117600155200ustar00rootroot00000000000000/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright 2009 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * Boston, MA 02111-1307, USA. * */ #include #include #include #include #include static void toggle_expand (MxButton *expand_button, ClutterActor *button) { gboolean x_expand; gchar *label; ClutterContainer *table = CLUTTER_CONTAINER (clutter_actor_get_parent (button)); clutter_container_child_get (table, button, "x-expand", &x_expand, NULL); x_expand = !x_expand; clutter_container_child_set (table, button, "x-expand", x_expand, NULL); label = g_strdup_printf ("button6 x-expand = %d", x_expand); mx_button_set_label (expand_button, label); g_free (label); } static void switch_align (ClutterActor *button, ClutterContainer *table) { static gint pattern = 1; MxAlign x_align, y_align; const gchar *label; switch (pattern) { default: case 1: x_align = y_align = MX_ALIGN_MIDDLE; label = "Align (Middle, Middle)"; break; case 2: x_align = y_align = MX_ALIGN_START; label = "Align (Start, Start)"; break; case 3: x_align = MX_ALIGN_MIDDLE; y_align = MX_ALIGN_START; label = "Align (Middle, Start)"; break; case 4: x_align = MX_ALIGN_END; y_align = MX_ALIGN_START; label = "Align (End, Start)"; break; case 5: x_align = MX_ALIGN_END; y_align = MX_ALIGN_MIDDLE; label = "Align (End, Middle)"; break; case 6: x_align = MX_ALIGN_END; y_align = MX_ALIGN_END; label = "Align (End, End)"; break; case 7: x_align = MX_ALIGN_MIDDLE; y_align = MX_ALIGN_END; label = "Align (Middle, End)"; break; case 8: x_align = MX_ALIGN_START; y_align = MX_ALIGN_END; label = "Align (Start, End)"; break; case 9: x_align = MX_ALIGN_START; y_align = MX_ALIGN_MIDDLE; label = "Align (Start, Middle)"; break; } if (++pattern > 9) pattern = 1; clutter_container_child_set (table, button, "x-align", x_align, "y-align", y_align, NULL); mx_button_set_label (MX_BUTTON (button), label); } static void toggle_visible (ClutterActor *button) { clutter_actor_hide (button); } gboolean drag = FALSE; static gboolean button_press (ClutterActor *actor, ClutterButtonEvent *event, ClutterActor *table) { if (event->button == 1) { drag = TRUE; return TRUE; } return FALSE; } static gboolean button_release (ClutterActor *actor, ClutterButtonEvent *event, ClutterActor *table) { if (event->button == 1) { drag = FALSE; return TRUE; } if (event->button == 3) clutter_actor_set_size (table, -1, -1); return FALSE; } static void motion_event (ClutterActor *actor, ClutterMotionEvent *event, ClutterActor *table) { if (drag) clutter_actor_set_size (table, event->x - 5, event->y - 5); } int main (int argc, char *argv[]) { MxApplication *application; MxWindow *window; ClutterActor *stage, *button2, *table; ClutterActor *button1, *button3, *button4, *button5, *button6, *button7, *button8, *button9, *button10; application = mx_application_new (&argc, &argv, "Test Table", 0); window = mx_application_create_window (application); stage = (ClutterActor *) mx_window_get_clutter_stage (window); table = mx_table_new (); mx_table_set_column_spacing (MX_TABLE (table), 10); mx_table_set_row_spacing (MX_TABLE (table), 10); button1 = mx_button_new_with_label ("button1"); button2 = clutter_texture_new_from_file ("redhand.png", NULL); button3 = mx_button_new_with_label ("button3"); button4 = mx_button_new_with_label ("button6 x-expand = 1"); button5 = mx_button_new_with_label ("button5"); button6 = mx_button_new_with_label ("button6"); button7 = mx_button_new (); button8 = mx_button_new_with_label ("button8"); button9 = mx_button_new_with_label ("button9"); button10 = mx_button_new_with_label ("button10"); mx_table_add_actor (MX_TABLE (table), button1, 0, 0); mx_table_add_actor (MX_TABLE (table), button2, 0, 1); mx_table_add_actor (MX_TABLE (table), button3, 1, 1); mx_table_add_actor (MX_TABLE (table), button4, 2, 0); mx_table_add_actor (MX_TABLE (table), button5, 3, 0); mx_table_add_actor (MX_TABLE (table), button6, 3, 1); mx_table_add_actor (MX_TABLE (table), button7, 4, 1); mx_table_add_actor (MX_TABLE (table), button8, 4, 0); mx_table_add_actor (MX_TABLE (table), button9, 5, 0); mx_table_add_actor (MX_TABLE (table), button10, -1, 0); mx_table_child_set_row_span (MX_TABLE (table), button1, 2); mx_table_child_set_row_span (MX_TABLE (table), button7, 2); mx_table_child_set_column_span (MX_TABLE (table), button4, 2); clutter_actor_set_size (button1, 100, 100); clutter_actor_set_width (button4, 250); clutter_container_child_set (CLUTTER_CONTAINER (table), button1, "x-expand", TRUE, "y-expand", FALSE, NULL); clutter_container_child_set (CLUTTER_CONTAINER (table), button2, "x-expand", FALSE, "y-expand", TRUE, "x-fill", FALSE, "y-fill", FALSE, NULL); clutter_container_child_set (CLUTTER_CONTAINER (table), button3, "x-expand", FALSE, "y-expand", FALSE, NULL); clutter_container_child_set (CLUTTER_CONTAINER (table), button4, "x-expand", FALSE, "y-expand", FALSE, NULL); clutter_container_child_set (CLUTTER_CONTAINER (table), button5, "x-expand", FALSE, "y-expand", FALSE, NULL); clutter_container_child_set (CLUTTER_CONTAINER (table), button6, "x-expand", TRUE, "y-expand", FALSE, NULL); clutter_container_child_set (CLUTTER_CONTAINER (table), button7, "x-expand", FALSE, "y-expand", FALSE, "x-fill", FALSE, "y-fill", FALSE, NULL); switch_align (button7, CLUTTER_CONTAINER (table)); clutter_container_child_set (CLUTTER_CONTAINER (table), button8, "x-expand", FALSE, "y-expand", TRUE, NULL); clutter_container_child_set (CLUTTER_CONTAINER (table), button9, "x-expand", FALSE, "y-expand", FALSE, NULL); clutter_container_child_set (CLUTTER_CONTAINER (table), button10, "x-expand", FALSE, "y-expand", FALSE, NULL); g_object_set (G_OBJECT (button2), "keep-aspect-ratio", TRUE, NULL); mx_window_set_child (window, table); g_signal_connect (button4, "clicked", G_CALLBACK (toggle_expand), button6); g_signal_connect (button7, "clicked", G_CALLBACK (switch_align), table); g_signal_connect (button10, "clicked", G_CALLBACK (toggle_visible), NULL); g_signal_connect (stage, "button-press-event", G_CALLBACK (button_press), table); g_signal_connect (stage, "motion-event", G_CALLBACK (motion_event), table); g_signal_connect (stage, "button-release-event", G_CALLBACK (button_release), table); clutter_actor_show (stage); g_debug ("table row count = %d", mx_table_get_row_count (MX_TABLE (table))); g_debug ("table column count = %d", mx_table_get_column_count (MX_TABLE (table))); mx_application_run (application); return EXIT_SUCCESS; } mx-1.4.7/tests/test-texture-frame.c000066400000000000000000000050751201047117600172300ustar00rootroot00000000000000/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright 2009 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * Boston, MA 02111-1307, USA. * */ #include #include #include #include static gboolean in_drag = FALSE; static gboolean on_button_press (ClutterActor *stage, ClutterButtonEvent *event, gpointer data) { in_drag = (event->button == 1); return FALSE; } static gboolean on_button_release (ClutterActor *stage, ClutterButtonEvent *event, gpointer data) { if (in_drag) in_drag = FALSE; return FALSE; } static gboolean on_motion (ClutterActor *stage, ClutterMotionEvent *event, ClutterActor *frame) { if (in_drag) clutter_actor_set_size (frame, event->x - 50, event->y - 50); return FALSE; } int main (int argc, char *argv[]) { ClutterActor *stage, *texture, *frame; GError *err = NULL; if (clutter_init (&argc, &argv) != CLUTTER_INIT_SUCCESS) return 1; stage = clutter_stage_get_default (); clutter_actor_set_size (stage, 500, 300); texture = clutter_texture_new_from_file ("test-texture-frame.png", &err); if (err) { g_critical ("%s", err->message); return 1; } frame = mx_texture_frame_new (CLUTTER_TEXTURE (texture), 10, 10, 10, 10); clutter_actor_set_position (frame, 50, 50); clutter_actor_set_size (frame, 400, 200); clutter_container_add (CLUTTER_CONTAINER (stage), frame, NULL); clutter_actor_show (stage); g_signal_connect (stage, "motion-event", G_CALLBACK (on_motion), frame); g_signal_connect (stage, "button-press-event", G_CALLBACK (on_button_press), NULL); g_signal_connect (stage, "button-release-event", G_CALLBACK (on_button_release), NULL); clutter_main (); return EXIT_SUCCESS; } mx-1.4.7/tests/test-texture-frame.png000066400000000000000000000002061201047117600175610ustar00rootroot00000000000000PNG  IHDRR9sRGB@IDATHcπ'؈Gf`Q,c#Ѱ5zhh8jуh 9IENDB`mx-1.4.7/tests/test-toggle.c000066400000000000000000000017541201047117600157210ustar00rootroot00000000000000 /* * Copyright 2009 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * Boston, MA 02111-1307, USA. * */ #include "test-mx.h" void toggle_main (ClutterContainer *stage) { ClutterActor *toggle; toggle = mx_toggle_new (); clutter_container_add_actor (CLUTTER_CONTAINER (stage), toggle); clutter_actor_set_position (toggle, 50, 50); } mx-1.4.7/tests/test-tooltips.c000066400000000000000000000071231201047117600163110ustar00rootroot00000000000000/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright 2009 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * Boston, MA 02111-1307, USA. * */ #include "test-mx.h" void tooltips_main (ClutterContainer *stage) { ClutterActor *button, *bin; guint delay; button = mx_button_new_with_label ("Hide Me"); g_signal_connect (button, "clicked", G_CALLBACK (clutter_actor_hide), NULL); mx_widget_set_tooltip_text (MX_WIDGET (button), "Disappear"); g_object_set (G_OBJECT (button), "tooltip-delay", 1000, NULL); g_object_get (G_OBJECT (button), "tooltip-delay", &delay, NULL); g_assert (delay == 1000); clutter_container_add_actor (stage, button); clutter_actor_set_position (button, 50, 100); bin = mx_frame_new (); clutter_container_add_actor (stage, bin); clutter_actor_set_position (bin, 50, 200); button = mx_button_new_with_label ("Testing 123"); mx_widget_set_tooltip_text (MX_WIDGET (button), "Testing tooltips in a container..."); mx_widget_set_tooltip_delay (MX_WIDGET (button), 300); delay = mx_widget_get_tooltip_delay (MX_WIDGET (button)); g_assert (delay == 300); clutter_container_add_actor (CLUTTER_CONTAINER (bin), button); button = mx_button_new_with_label ("Testing Long Text on left"); mx_widget_set_tooltip_text (MX_WIDGET (button), "Here is some really really" " long text to test the handling in MxTooltip"); mx_widget_set_tooltip_delay (MX_WIDGET (button), 0); delay = mx_widget_get_tooltip_delay (MX_WIDGET (button)); g_assert (delay == 0); clutter_container_add_actor (stage, button); clutter_actor_set_position (button, 0, 300); button = mx_button_new_with_label ("Testing Long Text on right"); mx_widget_set_tooltip_text (MX_WIDGET (button), "Here is some really really" " long text to test the handling in MxTooltip"); clutter_container_add_actor (stage, button); clutter_actor_set_position (button, 460, 300); button = mx_button_new_with_label ("Crazy"); mx_widget_set_tooltip_text (MX_WIDGET (button), "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" " aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" " aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" " aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"); clutter_container_add_actor (stage, button); clutter_actor_set_position (button, 250, 5); button = mx_button_new_with_label ("Bottom"); mx_widget_set_tooltip_text (MX_WIDGET (button), "Hello Hello Hello"); clutter_container_add_actor (stage, button); clutter_actor_set_position (button, 250, 570); button = mx_button_new_with_label ("Bottom"); mx_widget_set_tooltip_text (MX_WIDGET (button), "Hello Hello Hello"); clutter_container_add_actor (stage, button); clutter_actor_set_position (button, 250, 700); } mx-1.4.7/tests/test-views.c000066400000000000000000000115071201047117600155720ustar00rootroot00000000000000/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright 2009 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * Boston, MA 02111-1307, USA. * */ #include #include #include #include static gint sort_func (ClutterModel *model, const GValue *a, const GValue *b, gpointer user_data) { const ClutterColor *ca, *cb; gfloat h1, h2; ca = clutter_value_get_color (a); cb = clutter_value_get_color (b); clutter_color_to_hls (ca, &h1, NULL, NULL); clutter_color_to_hls (cb, &h2, NULL, NULL); return h1 - h2; } static gboolean filter_func (ClutterModel *model, ClutterModelIter *iter, gpointer user_data) { ClutterColor *color; gboolean show; gfloat h; clutter_model_iter_get (iter, 0, &color, -1); clutter_color_to_hls (color, &h, NULL, NULL); show = (h > 90 && h < 180); clutter_color_free (color); return show; } static gboolean key_release_cb (ClutterActor *actor, ClutterKeyEvent *event, ClutterModel *model) { if (event->keyval == 's') { static gboolean sort_set = 0; if (!sort_set) clutter_model_set_sort (model, 0, sort_func, NULL, NULL); else clutter_model_set_sort (model, -1, NULL, NULL, NULL); sort_set = !sort_set; } if (event->keyval == 'f') { static gboolean filter_set = 0; if (!filter_set) clutter_model_set_filter (model, filter_func, NULL, NULL); else clutter_model_set_filter (model, NULL, NULL, NULL); filter_set = !filter_set; } return FALSE; } static void allocation_notify_cb (ClutterActor *stage, GParamSpec *pspec, ClutterActor *scroll) { gfloat width, height; clutter_actor_get_size (stage, &width, &height); clutter_actor_set_size (scroll, width - 100, height - 100); } int main (int argc, char *argv[]) { ClutterActor *stage, *view, *scroll; ClutterModel *model; ClutterColor color = { 0x00, 0xff, 0xff, 0xff }; gint i; gboolean list; if (argc != 2) { printf ("Usage: test-view [list | icon]\n"); return 1; } if (!g_strcmp0 ("list", argv[1])) list = TRUE; else if (!g_strcmp0 ("icon", argv[1])) list = FALSE; else { printf ("Unknown option: %s\n", argv[1]); return 1; } if (clutter_init (&argc, &argv) != CLUTTER_INIT_SUCCESS) return 1; stage = clutter_stage_get_default (); clutter_stage_set_user_resizable ((ClutterStage*) stage, TRUE); scroll = mx_scroll_view_new (); clutter_actor_set_position ((ClutterActor*) scroll, 50, 50); clutter_container_add_actor (CLUTTER_CONTAINER (stage), scroll); if (list) view = mx_list_view_new (); else view = mx_item_view_new (); clutter_container_add_actor (CLUTTER_CONTAINER (scroll), view); model = clutter_list_model_new (2, CLUTTER_TYPE_COLOR, "color", G_TYPE_FLOAT, "size"); for (i = 0; i < 360; i++) { clutter_color_from_hls (&color, g_random_double_range (0.0, 360.0), 0.6, 0.6); clutter_model_append (model, 0, &color, 1, 32.0, -1); } if (list) { mx_list_view_set_model (MX_LIST_VIEW (view), model); mx_list_view_set_item_type (MX_LIST_VIEW (view), CLUTTER_TYPE_RECTANGLE); mx_list_view_add_attribute (MX_LIST_VIEW (view), "color", 0); mx_list_view_add_attribute (MX_LIST_VIEW (view), "width", 1); mx_list_view_add_attribute (MX_LIST_VIEW (view), "height", 1); } else { mx_item_view_set_model (MX_ITEM_VIEW (view), model); mx_item_view_set_item_type (MX_ITEM_VIEW (view), CLUTTER_TYPE_RECTANGLE); mx_item_view_add_attribute (MX_ITEM_VIEW (view), "color", 0); mx_item_view_add_attribute (MX_ITEM_VIEW (view), "width", 1); mx_item_view_add_attribute (MX_ITEM_VIEW (view), "height", 1); } g_signal_connect (stage, "key-release-event", G_CALLBACK (key_release_cb), model); g_signal_connect (stage, "notify::allocation", G_CALLBACK (allocation_notify_cb), scroll); clutter_actor_show (stage); clutter_main (); return EXIT_SUCCESS; } mx-1.4.7/tests/test-widgets.c000066400000000000000000000336031201047117600161040ustar00rootroot00000000000000/* * Copyright 2011 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * Boston, MA 02111-1307, USA. * */ #include #include #include static struct { MxApplication *app; ClutterActor *table; ClutterActor *frame; ClutterActor *inspector; } data; /* skip properties that have no effect or could cause other problems */ static gchar *skip_properties[] = { "fixed-position-set", "fixed-x", "fixed-y", "height", "min-height", "min-height-set", "min-width", "min-width-set", "natural-height", "natural-height-set", "natural-width", "natural-width-set", "request-mode", "show-on-parent-set", "width", "x", "y" }; static gboolean num_to_string (GBinding *binding, const GValue *source_value, GValue *target_value, gpointer userdata) { gchar *string; if (G_VALUE_HOLDS_INT (source_value)) string = g_strdup_printf ("%d", g_value_get_int (source_value)); else if (G_VALUE_HOLDS_UINT (source_value)) string = g_strdup_printf ("%d", g_value_get_uint (source_value)); else if (G_VALUE_HOLDS_DOUBLE (source_value)) string = g_strdup_printf ("%.2f", g_value_get_double (source_value)); else if (G_VALUE_HOLDS_FLOAT (source_value)) string = g_strdup_printf ("%.2f", g_value_get_float (source_value)); else return FALSE; g_value_take_string (target_value, string); return TRUE; } static gboolean string_to_num (GBinding *binding, const GValue *source_value, GValue *target_value, gpointer userdata) { if (G_VALUE_HOLDS_INT (target_value)) g_value_set_int (target_value, atoi (g_value_get_string (source_value))); else if (G_VALUE_HOLDS_UINT (target_value)) g_value_set_uint (target_value, atoi (g_value_get_string (source_value))); else if (G_VALUE_HOLDS_DOUBLE (target_value)) g_value_set_double (target_value, strtod (g_value_get_string (source_value), NULL)); else if (G_VALUE_HOLDS_FLOAT (target_value)) g_value_set_float (target_value, strtof (g_value_get_string (source_value), NULL)); else return FALSE; return TRUE; } static ClutterActor * create_property_editor (GObject *object, GParamSpec *pspec) { ClutterActor *box, *label, *value; gint i; /* skip properties that are not writable */ if (!(pspec->flags & G_PARAM_WRITABLE)) return NULL; /* skip other properties */ for (i = 0; i < G_N_ELEMENTS (skip_properties); i++) { if (g_str_equal (pspec->name, skip_properties[i])) return NULL; } box = mx_box_layout_new (); label = mx_label_new_with_text (pspec->name); clutter_actor_set_width (label, 150); clutter_container_add_actor (CLUTTER_CONTAINER (box), label); if (pspec->value_type == G_TYPE_BOOLEAN) { value = mx_toggle_new (); g_object_bind_property (object, pspec->name, value, "active", G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); } else if (pspec->value_type == G_TYPE_STRING) { value = mx_entry_new (); g_object_bind_property (object, pspec->name, value, "text", G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); } else if (pspec->value_type == G_TYPE_INT || pspec->value_type == G_TYPE_UINT || pspec->value_type == G_TYPE_FLOAT || pspec->value_type == G_TYPE_DOUBLE) { value = mx_entry_new (); g_object_bind_property_full (object, pspec->name, value, "text", G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE, num_to_string, string_to_num, NULL, NULL); } else if (g_type_is_a (pspec->value_type, G_TYPE_ENUM)) { GEnumValue *evalue; GEnumClass *eclass; gint init = 0; value = mx_combo_box_new (); clutter_actor_set_width (value, 100); eclass = g_type_class_ref (pspec->value_type); while ((evalue = g_enum_get_value (eclass, init))) { mx_combo_box_append_text (MX_COMBO_BOX (value), evalue->value_nick); init++; } g_type_class_unref (eclass); g_object_bind_property (object, pspec->name, value, "index", G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); } else value = NULL; if (value) { clutter_container_add_actor (CLUTTER_CONTAINER (box), value); return box; } else return NULL; } static void change_widget (MxComboBox *box, GParamSpec *pspec, gpointer userdata) { GObjectClass *class; const gchar *typename; guint id; ClutterActor *actor, *vbox; GParamSpec **properties; guint n_properties; gint i; /* test the destroy sequence */ if (mx_bin_get_child (MX_BIN (data.frame))) clutter_actor_destroy (mx_bin_get_child (MX_BIN (data.frame))); typename = mx_combo_box_get_active_text (box); id = g_type_from_name (typename); actor = g_object_new (id, NULL); class = G_OBJECT_GET_CLASS (actor); mx_bin_set_child (MX_BIN (data.frame), actor); if (data.inspector) clutter_actor_destroy (data.inspector); vbox = mx_box_layout_new (); mx_box_layout_set_orientation (MX_BOX_LAYOUT (vbox), MX_ORIENTATION_VERTICAL); properties = g_object_class_list_properties (class, &n_properties); for (i = n_properties - 1; i >= 0; i--) { ClutterActor *editor; editor = create_property_editor (G_OBJECT (actor), properties[i]); /* set a default value for label or text property */ if (g_str_equal (properties[i]->name, "label") || g_str_equal (properties[i]->name, "text")) g_object_set (actor, properties[i]->name, typename, NULL); if (editor) clutter_container_add_actor (CLUTTER_CONTAINER (vbox), editor); } /* set up various widgets as needed */ if (g_str_equal (typename, "MxIcon")) g_object_set (actor, "icon-name", "face-smile", NULL); else if (g_str_equal (typename, "MxComboBox")) { mx_combo_box_set_active_text (MX_COMBO_BOX (actor), "MxComboBox"); mx_combo_box_append_text (MX_COMBO_BOX (actor), "Item 1"); mx_combo_box_append_text (MX_COMBO_BOX (actor), "Item 2"); mx_combo_box_append_text (MX_COMBO_BOX (actor), "Item 3"); } else if (g_str_equal (typename, "MxPathBar")) { mx_path_bar_push (MX_PATH_BAR (actor), "Mx"); mx_path_bar_push (MX_PATH_BAR (actor), "Path"); mx_path_bar_push (MX_PATH_BAR (actor), "Bar"); } else if (g_str_equal (typename, "MxProgressBar")) { mx_progress_bar_set_progress (MX_PROGRESS_BAR (actor), 0.6); } else if (g_str_equal (typename, "MxScrollBar")) { mx_scroll_bar_set_adjustment (MX_SCROLL_BAR (actor), mx_adjustment_new_with_values (10, 0, 100, 10, 25, 25)); } data.inspector = mx_scroll_view_new (); clutter_actor_set_width (data.inspector, 300); clutter_container_add_actor (CLUTTER_CONTAINER (data.inspector), vbox); mx_table_add_actor_with_properties (MX_TABLE (data.table), data.inspector, 0, 1, "x-expand", FALSE, NULL); } static ClutterActor* create_combo_box (gchar *selected_widget) { ClutterActor *combo; gint i, set_index = 0; gchar *needle; GType types[] = { mx_button_get_type (), mx_combo_box_get_type (), mx_entry_get_type (), mx_icon_get_type (), mx_label_get_type (), mx_path_bar_get_type (), mx_progress_bar_get_type (), mx_scroll_bar_get_type (), mx_slider_get_type (), mx_toggle_get_type (), mx_spinner_get_type (), }; combo = mx_combo_box_new (); needle = g_ascii_strdown (selected_widget, -1); for (i = 0; i < G_N_ELEMENTS (types); i++) { gchar *haystack; haystack = g_ascii_strdown (g_type_name (types[i]), -1); if (strstr (haystack, needle)) set_index = i; g_free (haystack); mx_combo_box_append_text (MX_COMBO_BOX (combo), g_type_name (types[i])); } g_free (needle); g_signal_connect (combo, "notify::index", G_CALLBACK (change_widget), NULL); mx_combo_box_set_index (MX_COMBO_BOX (combo), set_index); return combo; } static void rotate_left_clicked_cb (ClutterActor *button, MxWindow *window) { MxWindowRotation rotation = mx_window_get_window_rotation (window); switch (rotation) { case MX_WINDOW_ROTATION_0: rotation = MX_WINDOW_ROTATION_270; break; case MX_WINDOW_ROTATION_90: rotation = MX_WINDOW_ROTATION_0; break; case MX_WINDOW_ROTATION_180: rotation = MX_WINDOW_ROTATION_90; break; case MX_WINDOW_ROTATION_270: rotation = MX_WINDOW_ROTATION_180; break; } mx_window_set_window_rotation (window, rotation); } static void rotate_right_clicked_cb (ClutterActor *button, MxWindow *window) { MxWindowRotation rotation = mx_window_get_window_rotation (window); switch (rotation) { case MX_WINDOW_ROTATION_0: rotation = MX_WINDOW_ROTATION_90; break; case MX_WINDOW_ROTATION_90: rotation = MX_WINDOW_ROTATION_180; break; case MX_WINDOW_ROTATION_180: rotation = MX_WINDOW_ROTATION_270; break; case MX_WINDOW_ROTATION_270: rotation = MX_WINDOW_ROTATION_0; break; } mx_window_set_window_rotation (window, rotation); } static void rotate_180_clicked_cb (ClutterActor *button, MxWindow *window) { MxWindowRotation rotation = mx_window_get_window_rotation (window); switch (rotation) { case MX_WINDOW_ROTATION_0: rotation = MX_WINDOW_ROTATION_180; break; case MX_WINDOW_ROTATION_90: rotation = MX_WINDOW_ROTATION_270; break; case MX_WINDOW_ROTATION_180: rotation = MX_WINDOW_ROTATION_0; break; case MX_WINDOW_ROTATION_270: rotation = MX_WINDOW_ROTATION_90; break; } mx_window_set_window_rotation (window, rotation); } static ClutterActor * create_rotate_box (MxWindow *window) { ClutterActor *button, *icon, *icon2, *layout2; ClutterActor *layout = mx_box_layout_new (); /* Create rotate-left button */ icon = mx_icon_new (); mx_icon_set_icon_name (MX_ICON (icon), "object-rotate-left"); mx_icon_set_icon_size (MX_ICON (icon), 16); button = mx_button_new (); mx_bin_set_child (MX_BIN (button), icon); g_signal_connect (button, "clicked", G_CALLBACK (rotate_left_clicked_cb), window); clutter_container_add_actor (CLUTTER_CONTAINER (layout), button); /* Create rotate-180 button */ icon = mx_icon_new (); mx_icon_set_icon_name (MX_ICON (icon), "object-rotate-left"); mx_icon_set_icon_size (MX_ICON (icon), 16); icon2 = mx_icon_new (); mx_icon_set_icon_name (MX_ICON (icon2), "object-rotate-right"); mx_icon_set_icon_size (MX_ICON (icon2), 16); layout2 = mx_box_layout_new (); clutter_container_add (CLUTTER_CONTAINER (layout2), icon, icon2, NULL); button = mx_button_new (); mx_bin_set_child (MX_BIN (button), layout2); g_signal_connect (button, "clicked", G_CALLBACK (rotate_180_clicked_cb), window); clutter_container_add_actor (CLUTTER_CONTAINER (layout), button); /* Create rotate-right button */ icon = mx_icon_new (); mx_icon_set_icon_name (MX_ICON (icon), "object-rotate-right"); mx_icon_set_icon_size (MX_ICON (icon), 16); button = mx_button_new (); mx_bin_set_child (MX_BIN (button), icon); g_signal_connect (button, "clicked", G_CALLBACK (rotate_right_clicked_cb), window); clutter_container_add_actor (CLUTTER_CONTAINER (layout), button); return layout; } int main (int argc, char **argv) { MxApplication *application; MxWindow *window; ClutterActor *box; MxToolbar *toolbar; gchar *selected_widget; application = mx_application_new (&argc, &argv, "test-widgets", 0); window = mx_application_create_window (application); if (argc > 1) selected_widget = argv[1]; /* main content */ data.table = mx_table_new (); data.frame = mx_frame_new (); data.inspector = NULL; mx_table_add_actor (MX_TABLE (data.table), data.frame, 0, 0); mx_window_set_child (window, data.table); /* toolbar */ box = mx_box_layout_new (); mx_box_layout_add_actor_with_properties (MX_BOX_LAYOUT (box), create_combo_box (selected_widget), 0, "expand", TRUE, "x-fill", FALSE, "x-align", MX_ALIGN_START, NULL); mx_box_layout_add_actor (MX_BOX_LAYOUT (box), create_rotate_box (window), 1); toolbar = mx_window_get_toolbar (window); mx_bin_set_child (MX_BIN (toolbar), box); mx_bin_set_fill (MX_BIN (toolbar), TRUE, TRUE); /* show the window */ mx_window_show (window); /* run the application */ mx_application_run (application); return 0; } mx-1.4.7/tests/test-window.c000066400000000000000000000214001201047117600157350ustar00rootroot00000000000000/* * Copyright 2010 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * Boston, MA 02111-1307, USA. * */ #include static void small_screen_cb (MxToggle *toggle, GParamSpec *pspec, MxWindow *window) { mx_window_set_small_screen (window, mx_toggle_get_active (toggle)); } static void fullscreen_cb (MxToggle *toggle, GParamSpec *pspec, ClutterStage *stage) { clutter_stage_set_fullscreen (stage, mx_toggle_get_active (toggle)); } static void icon_cb (MxToggle *toggle, GParamSpec *pspec, MxWindow *window) { gboolean use_custom = mx_toggle_get_active (toggle); if (use_custom) { CoglHandle texture = cogl_texture_new_from_file ("redhand.png", COGL_TEXTURE_NONE, COGL_PIXEL_FORMAT_ANY, NULL); if (texture) mx_window_set_icon_from_cogl_texture (window, texture); cogl_handle_unref (texture); } else mx_window_set_icon_name (window, "window-new"); } static void toolbar_cb (MxToggle *toggle, GParamSpec *pspec, MxWindow *window) { mx_window_set_has_toolbar (window, mx_toggle_get_active (toggle)); } static void resizable_cb (MxToggle *toggle, GParamSpec *pspec, ClutterStage *stage) { clutter_stage_set_user_resizable (stage, mx_toggle_get_active (toggle)); } static void rotate_clicked_cb (ClutterActor *button, MxWindow *window) { MxWindowRotation rotation = mx_window_get_window_rotation (window); switch (rotation) { case MX_WINDOW_ROTATION_0: rotation = MX_WINDOW_ROTATION_90; break; case MX_WINDOW_ROTATION_90: rotation = MX_WINDOW_ROTATION_180; break; case MX_WINDOW_ROTATION_180: rotation = MX_WINDOW_ROTATION_270; break; case MX_WINDOW_ROTATION_270: rotation = MX_WINDOW_ROTATION_0; break; } mx_window_set_window_rotation (window, rotation); } int main (int argc, char **argv) { MxWindow *window; MxApplication *app; ClutterActor *stage, *toggle, *label, *table, *button, *icon; app = mx_application_new (&argc, &argv, "Test PathBar", 0); window = mx_application_create_window (app); stage = (ClutterActor *)mx_window_get_clutter_stage (window); mx_window_set_icon_name (window, "window-new"); clutter_actor_set_size (stage, 480, 320); table = mx_table_new (); mx_table_set_column_spacing (MX_TABLE (table), 8); mx_table_set_row_spacing (MX_TABLE (table), 12); mx_window_set_child (window, table); toggle = mx_toggle_new (); label = mx_label_new_with_text ("Toggle small-screen mode"); g_signal_connect (toggle, "notify::active", G_CALLBACK (small_screen_cb), window); mx_table_add_actor_with_properties (MX_TABLE (table), toggle, 0, 0, "x-expand", TRUE, "x-align", MX_ALIGN_END, "x-fill", FALSE, NULL); mx_table_add_actor_with_properties (MX_TABLE (table), label, 0, 1, "x-expand", TRUE, "x-align", MX_ALIGN_START, "y-fill", FALSE, "x-fill", FALSE, NULL); toggle = mx_toggle_new (); label = mx_label_new_with_text ("Toggle full-screen mode"); g_signal_connect (toggle, "notify::active", G_CALLBACK (fullscreen_cb), stage); mx_table_add_actor_with_properties (MX_TABLE (table), toggle, 1, 0, "x-expand", TRUE, "x-align", MX_ALIGN_END, "x-fill", FALSE, NULL); mx_table_add_actor_with_properties (MX_TABLE (table), label, 1, 1, "x-expand", TRUE, "x-align", MX_ALIGN_START, "y-fill", FALSE, "x-fill", FALSE, NULL); toggle = mx_toggle_new (); label = mx_label_new_with_text ("Toggle custom window icon"); g_signal_connect (toggle, "notify::active", G_CALLBACK (icon_cb), window); mx_table_add_actor_with_properties (MX_TABLE (table), toggle, 2, 0, "x-expand", TRUE, "x-align", MX_ALIGN_END, "x-fill", FALSE, NULL); mx_table_add_actor_with_properties (MX_TABLE (table), label, 2, 1, "x-expand", TRUE, "x-align", MX_ALIGN_START, "y-fill", FALSE, "x-fill", FALSE, NULL); toggle = mx_toggle_new (); mx_toggle_set_active (MX_TOGGLE (toggle), TRUE); label = mx_label_new_with_text ("Toggle user-resizable"); g_signal_connect (toggle, "notify::active", G_CALLBACK (resizable_cb), stage); mx_table_add_actor_with_properties (MX_TABLE (table), toggle, 3, 0, "x-expand", TRUE, "x-align", MX_ALIGN_END, "x-fill", FALSE, NULL); mx_table_add_actor_with_properties (MX_TABLE (table), label, 3, 1, "x-expand", TRUE, "x-align", MX_ALIGN_START, "y-fill", FALSE, "x-fill", FALSE, NULL); toggle = mx_toggle_new (); mx_toggle_set_active (MX_TOGGLE (toggle), TRUE); label = mx_label_new_with_text ("Toggle toolbar"); g_signal_connect (toggle, "notify::active", G_CALLBACK (toolbar_cb), window); mx_table_add_actor_with_properties (MX_TABLE (table), toggle, 4, 0, "x-expand", TRUE, "x-align", MX_ALIGN_END, "x-fill", FALSE, NULL); mx_table_add_actor_with_properties (MX_TABLE (table), label, 4, 1, "x-expand", TRUE, "x-align", MX_ALIGN_START, "y-fill", FALSE, "x-fill", FALSE, NULL); icon = mx_icon_new (); mx_icon_set_icon_name (MX_ICON (icon), "object-rotate-right"); mx_icon_set_icon_size (MX_ICON (icon), 16); button = mx_button_new (); mx_bin_set_child (MX_BIN (button), icon); g_signal_connect (button, "clicked", G_CALLBACK (rotate_clicked_cb), window); clutter_container_add_actor ( CLUTTER_CONTAINER (mx_window_get_toolbar (window)), button); mx_bin_set_alignment (MX_BIN (mx_window_get_toolbar (window)), MX_ALIGN_END, MX_ALIGN_MIDDLE); clutter_actor_show (stage); mx_application_run (app); return 0; }