Adding upstream version 0.9.0.
Signed-off-by: Andrew Lee (李健秋) <ajqlee@debian.org>
This commit is contained in:
		
						commit
						b2b0b6ccfa
					
				
							
								
								
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,2 @@ | ||||
| build | ||||
| .kdev4 | ||||
							
								
								
									
										9
									
								
								AUTHORS
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								AUTHORS
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,9 @@ | ||||
| Upstream Authors: | ||||
|     LXQt team: http://lxqt.org | ||||
|     Hong Jen Yee (PCMan) <pcman.tw@gmail.com> | ||||
| 
 | ||||
| Copyright: | ||||
|     Copyright (c) 2013-2014 LXQt team | ||||
| 
 | ||||
| License: GPL-2 | ||||
| The full text of the licenses can be found in the 'COPYING' file. | ||||
							
								
								
									
										98
									
								
								CMakeLists.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										98
									
								
								CMakeLists.txt
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,98 @@ | ||||
| cmake_minimum_required(VERSION 2.8.11) | ||||
| project(pcmanfm-qt) | ||||
| 
 | ||||
| set(PCMANFM_QT_VERSION "0.9.0") | ||||
| set(LIBFM_QT_VERSION "0.9.0") | ||||
| # We use the libtool versioning scheme for the internal so name, "current:revision:age" | ||||
| # http://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html#Updating-version-info | ||||
| # https://www.sourceware.org/autobook/autobook/autobook_91.html | ||||
| # http://pusling.com/blog/?p=352 | ||||
| # Actually, libtool uses different ways on different operating systems. So there is no | ||||
| # universal way to translate a libtool version-info to a cmake version. | ||||
| # We use "(current-age).age.revision" as the cmake version. | ||||
| # current: 2, revision: 0, age: 0 => version: 2.0.0 | ||||
| set(LIBFM_QT_LIB_VERSION "2.0.0") | ||||
| set(LIBFM_QT_LIB_SOVERSION "2") | ||||
| 
 | ||||
| # Set default installation paths | ||||
| set(LIB_INSTALL_DIR "lib${LIB_SUFFIX}" CACHE PATH "Installation path for libraries") | ||||
| set(INCLUDE_INSTALL_DIR include CACHE PATH "Installation path for includes") | ||||
| 
 | ||||
| find_package(Qt5Widgets 5.2 REQUIRED) | ||||
| find_package(Qt5DBus 5.2 REQUIRED) | ||||
| find_package(Qt5LinguistTools 5.2 REQUIRED) | ||||
| find_package(Qt5X11Extras 5.2 REQUIRED) | ||||
| 
 | ||||
| find_package(PkgConfig) | ||||
| pkg_check_modules(SYSTEM_LIBS REQUIRED | ||||
|     glib-2.0 | ||||
|     gio-2.0 | ||||
|     gio-unix-2.0 | ||||
|     xcb | ||||
| ) | ||||
| 
 | ||||
| pkg_check_modules(LIBFM REQUIRED libfm>=1.2.0) | ||||
| pkg_check_modules(LIBMENUCACHE REQUIRED libmenu-cache>=0.4.0) | ||||
| 
 | ||||
| include(GNUInstallDirs) | ||||
| 
 | ||||
| add_definitions(-DQT_NO_KEYWORDS) | ||||
| 
 | ||||
| if (CMAKE_COMPILER_IS_GNUCXX) | ||||
|     # set visibility to hidden to hide symbols, unless they're exported manually in the code | ||||
|     set(CMAKE_CXX_FLAGS "-fvisibility=hidden -fvisibility-inlines-hidden -fno-exceptions ${CMAKE_CXX_FLAGS}") | ||||
| endif() | ||||
| 
 | ||||
| set(CMAKE_AUTOMOC TRUE) | ||||
| set(CMAKE_INCLUDE_CURRENT_DIR ON) | ||||
| 
 | ||||
| add_subdirectory(libfm-qt) | ||||
| add_subdirectory(pcmanfm) | ||||
| 
 | ||||
| # update translations | ||||
| add_custom_target(update_translations ALL DEPENDS | ||||
|     libfm_translations | ||||
|     pcmanfm_translations | ||||
| ) | ||||
| 
 | ||||
| # manpage for pcmanfm-qt | ||||
| configure_file( | ||||
|     "${CMAKE_CURRENT_SOURCE_DIR}/pcmanfm-qt.1.in" | ||||
|     "${CMAKE_CURRENT_BINARY_DIR}/pcmanfm-qt.1" | ||||
|     @ONLY | ||||
| ) | ||||
| install(FILES | ||||
|     "${CMAKE_CURRENT_BINARY_DIR}/pcmanfm-qt.1" | ||||
|     DESTINATION "${CMAKE_INSTALL_MANDIR}/man1" | ||||
| ) | ||||
| 
 | ||||
| # add Doxygen support to generate API docs | ||||
| # References: | ||||
| # http://majewsky.wordpress.com/2010/08/14/tip-of-the-day-cmake-and-doxygen/ | ||||
| # http://www.bluequartz.net/projects/EIM_Segmentation/SoftwareDocumentation/html/usewithcmakeproject.html | ||||
| option(BUILD_DOCUMENTATION "Use Doxygen to create the HTML based API documentation" OFF) | ||||
| if(BUILD_DOCUMENTATION) | ||||
|     find_package(Doxygen REQUIRED) | ||||
|     configure_file("${CMAKE_CURRENT_SOURCE_DIR}/Doxyfile.in" "${CMAKE_CURRENT_BINARY_DIR}/Doxyfile" @ONLY) | ||||
|     add_custom_target(doc ALL | ||||
|         ${DOXYGEN_EXECUTABLE} "${CMAKE_CURRENT_BINARY_DIR}/Doxyfile" | ||||
|         WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}" | ||||
|         COMMENT "Generating API documentation with Doxygen" VERBATIM | ||||
|     ) | ||||
|     install(DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/docs" DESTINATION "${CMAKE_INSTALL_DOCDIR}") | ||||
| endif() | ||||
| 
 | ||||
| # building tarball with CPack ------------------------------------------------- | ||||
| # To create a source distribution, type: | ||||
| # make package_source | ||||
| include(InstallRequiredSystemLibraries) | ||||
| set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/COPYING") | ||||
| set(CPACK_RESOURCE_FILE_README "${CMAKE_CURRENT_SOURCE_DIR}/README") | ||||
| set(CPACK_PACKAGE_VENDOR "") | ||||
| set(CPACK_PACKAGE_VERSION_MAJOR "0") | ||||
| set(CPACK_PACKAGE_VERSION_MINOR "8") | ||||
| set(CPACK_PACKAGE_VERSION_PATCH "0") | ||||
| set(CPACK_GENERATOR TBZ2) | ||||
| set(CPACK_SOURCE_GENERATOR TBZ2) | ||||
| set(CPACK_SOURCE_IGNORE_FILES /build/;.gitignore;.*~;.git;.kdev4;temp) | ||||
| include(CPack) | ||||
							
								
								
									
										280
									
								
								COPYING
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										280
									
								
								COPYING
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,280 @@ | ||||
|                     GNU GENERAL PUBLIC LICENSE | ||||
|                        Version 2, June 1991 | ||||
| 
 | ||||
|  Copyright (C) 1989, 1991 Free Software Foundation, Inc., | ||||
|  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | ||||
|  Everyone is permitted to copy and distribute verbatim copies | ||||
|  of this license document, but changing it is not allowed. | ||||
| 
 | ||||
|                             Preamble | ||||
| 
 | ||||
|   The licenses for most software are designed to take away your | ||||
| freedom to share and change it.  By contrast, the GNU General Public | ||||
| License is intended to guarantee your freedom to share and change free | ||||
| software--to make sure the software is free for all its users.  This | ||||
| General Public License applies to most of the Free Software | ||||
| Foundation's software and to any other program whose authors commit to | ||||
| using it.  (Some other Free Software Foundation software is covered by | ||||
| the GNU Lesser General Public License instead.)  You can apply it to | ||||
| your programs, too. | ||||
| 
 | ||||
|   When we speak of free software, we are referring to freedom, not | ||||
| price.  Our General Public Licenses are designed to make sure that you | ||||
| have the freedom to distribute copies of free software (and charge for | ||||
| this service if you wish), that you receive source code or can get it | ||||
| if you want it, that you can change the software or use pieces of it | ||||
| in new free programs; and that you know you can do these things. | ||||
| 
 | ||||
|   To protect your rights, we need to make restrictions that forbid | ||||
| anyone to deny you these rights or to ask you to surrender the rights. | ||||
| These restrictions translate to certain responsibilities for you if you | ||||
| distribute copies of the software, or if you modify it. | ||||
| 
 | ||||
|   For example, if you distribute copies of such a program, whether | ||||
| gratis or for a fee, you must give the recipients all the rights that | ||||
| you have.  You must make sure that they, too, receive or can get the | ||||
| source code.  And you must show them these terms so they know their | ||||
| rights. | ||||
| 
 | ||||
|   We protect your rights with two steps: (1) copyright the software, and | ||||
| (2) offer you this license which gives you legal permission to copy, | ||||
| distribute and/or modify the software. | ||||
| 
 | ||||
|   Also, for each author's protection and ours, we want to make certain | ||||
| that everyone understands that there is no warranty for this free | ||||
| software.  If the software is modified by someone else and passed on, we | ||||
| want its recipients to know that what they have is not the original, so | ||||
| that any problems introduced by others will not reflect on the original | ||||
| authors' reputations. | ||||
| 
 | ||||
|   Finally, any free program is threatened constantly by software | ||||
| patents.  We wish to avoid the danger that redistributors of a free | ||||
| program will individually obtain patent licenses, in effect making the | ||||
| program proprietary.  To prevent this, we have made it clear that any | ||||
| patent must be licensed for everyone's free use or not licensed at all. | ||||
| 
 | ||||
|   The precise terms and conditions for copying, distribution and | ||||
| modification follow. | ||||
| 
 | ||||
|                     GNU GENERAL PUBLIC LICENSE | ||||
|    TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION | ||||
| 
 | ||||
|   0. This License applies to any program or other work which contains | ||||
| a notice placed by the copyright holder saying it may be distributed | ||||
| under the terms of this General Public License.  The "Program", below, | ||||
| refers to any such program or work, and a "work based on the Program" | ||||
| means either the Program or any derivative work under copyright law: | ||||
| that is to say, a work containing the Program or a portion of it, | ||||
| either verbatim or with modifications and/or translated into another | ||||
| language.  (Hereinafter, translation is included without limitation in | ||||
| the term "modification".)  Each licensee is addressed as "you". | ||||
| 
 | ||||
| Activities other than copying, distribution and modification are not | ||||
| covered by this License; they are outside its scope.  The act of | ||||
| running the Program is not restricted, and the output from the Program | ||||
| is covered only if its contents constitute a work based on the | ||||
| Program (independent of having been made by running the Program). | ||||
| Whether that is true depends on what the Program does. | ||||
| 
 | ||||
|   1. You may copy and distribute verbatim copies of the Program's | ||||
| source code as you receive it, in any medium, provided that you | ||||
| conspicuously and appropriately publish on each copy an appropriate | ||||
| copyright notice and disclaimer of warranty; keep intact all the | ||||
| notices that refer to this License and to the absence of any warranty; | ||||
| and give any other recipients of the Program a copy of this License | ||||
| along with the Program. | ||||
| 
 | ||||
| You may charge a fee for the physical act of transferring a copy, and | ||||
| you may at your option offer warranty protection in exchange for a fee. | ||||
| 
 | ||||
|   2. You may modify your copy or copies of the Program or any portion | ||||
| of it, thus forming a work based on the Program, and copy and | ||||
| distribute such modifications or work under the terms of Section 1 | ||||
| above, provided that you also meet all of these conditions: | ||||
| 
 | ||||
|     a) You must cause the modified files to carry prominent notices | ||||
|     stating that you changed the files and the date of any change. | ||||
| 
 | ||||
|     b) You must cause any work that you distribute or publish, that in | ||||
|     whole or in part contains or is derived from the Program or any | ||||
|     part thereof, to be licensed as a whole at no charge to all third | ||||
|     parties under the terms of this License. | ||||
| 
 | ||||
|     c) If the modified program normally reads commands interactively | ||||
|     when run, you must cause it, when started running for such | ||||
|     interactive use in the most ordinary way, to print or display an | ||||
|     announcement including an appropriate copyright notice and a | ||||
|     notice that there is no warranty (or else, saying that you provide | ||||
|     a warranty) and that users may redistribute the program under | ||||
|     these conditions, and telling the user how to view a copy of this | ||||
|     License.  (Exception: if the Program itself is interactive but | ||||
|     does not normally print such an announcement, your work based on | ||||
|     the Program is not required to print an announcement.) | ||||
| 
 | ||||
| These requirements apply to the modified work as a whole.  If | ||||
| identifiable sections of that work are not derived from the Program, | ||||
| and can be reasonably considered independent and separate works in | ||||
| themselves, then this License, and its terms, do not apply to those | ||||
| sections when you distribute them as separate works.  But when you | ||||
| distribute the same sections as part of a whole which is a work based | ||||
| on the Program, the distribution of the whole must be on the terms of | ||||
| this License, whose permissions for other licensees extend to the | ||||
| entire whole, and thus to each and every part regardless of who wrote it. | ||||
| 
 | ||||
| Thus, it is not the intent of this section to claim rights or contest | ||||
| your rights to work written entirely by you; rather, the intent is to | ||||
| exercise the right to control the distribution of derivative or | ||||
| collective works based on the Program. | ||||
| 
 | ||||
| In addition, mere aggregation of another work not based on the Program | ||||
| with the Program (or with a work based on the Program) on a volume of | ||||
| a storage or distribution medium does not bring the other work under | ||||
| the scope of this License. | ||||
| 
 | ||||
|   3. You may copy and distribute the Program (or a work based on it, | ||||
| under Section 2) in object code or executable form under the terms of | ||||
| Sections 1 and 2 above provided that you also do one of the following: | ||||
| 
 | ||||
|     a) Accompany it with the complete corresponding machine-readable | ||||
|     source code, which must be distributed under the terms of Sections | ||||
|     1 and 2 above on a medium customarily used for software interchange; or, | ||||
| 
 | ||||
|     b) Accompany it with a written offer, valid for at least three | ||||
|     years, to give any third party, for a charge no more than your | ||||
|     cost of physically performing source distribution, a complete | ||||
|     machine-readable copy of the corresponding source code, to be | ||||
|     distributed under the terms of Sections 1 and 2 above on a medium | ||||
|     customarily used for software interchange; or, | ||||
| 
 | ||||
|     c) Accompany it with the information you received as to the offer | ||||
|     to distribute corresponding source code.  (This alternative is | ||||
|     allowed only for noncommercial distribution and only if you | ||||
|     received the program in object code or executable form with such | ||||
|     an offer, in accord with Subsection b above.) | ||||
| 
 | ||||
| The source code for a work means the preferred form of the work for | ||||
| making modifications to it.  For an executable work, complete source | ||||
| code means all the source code for all modules it contains, plus any | ||||
| associated interface definition files, plus the scripts used to | ||||
| control compilation and installation of the executable.  However, as a | ||||
| special exception, the source code distributed need not include | ||||
| anything that is normally distributed (in either source or binary | ||||
| form) with the major components (compiler, kernel, and so on) of the | ||||
| operating system on which the executable runs, unless that component | ||||
| itself accompanies the executable. | ||||
| 
 | ||||
| If distribution of executable or object code is made by offering | ||||
| access to copy from a designated place, then offering equivalent | ||||
| access to copy the source code from the same place counts as | ||||
| distribution of the source code, even though third parties are not | ||||
| compelled to copy the source along with the object code. | ||||
| 
 | ||||
|   4. You may not copy, modify, sublicense, or distribute the Program | ||||
| except as expressly provided under this License.  Any attempt | ||||
| otherwise to copy, modify, sublicense or distribute the Program is | ||||
| void, and will automatically terminate your rights under this License. | ||||
| However, parties who have received copies, or rights, from you under | ||||
| this License will not have their licenses terminated so long as such | ||||
| parties remain in full compliance. | ||||
| 
 | ||||
|   5. You are not required to accept this License, since you have not | ||||
| signed it.  However, nothing else grants you permission to modify or | ||||
| distribute the Program or its derivative works.  These actions are | ||||
| prohibited by law if you do not accept this License.  Therefore, by | ||||
| modifying or distributing the Program (or any work based on the | ||||
| Program), you indicate your acceptance of this License to do so, and | ||||
| all its terms and conditions for copying, distributing or modifying | ||||
| the Program or works based on it. | ||||
| 
 | ||||
|   6. Each time you redistribute the Program (or any work based on the | ||||
| Program), the recipient automatically receives a license from the | ||||
| original licensor to copy, distribute or modify the Program subject to | ||||
| these terms and conditions.  You may not impose any further | ||||
| restrictions on the recipients' exercise of the rights granted herein. | ||||
| You are not responsible for enforcing compliance by third parties to | ||||
| this License. | ||||
| 
 | ||||
|   7. If, as a consequence of a court judgment or allegation of patent | ||||
| infringement or for any other reason (not limited to patent issues), | ||||
| conditions are imposed on you (whether by court order, agreement or | ||||
| otherwise) that contradict the conditions of this License, they do not | ||||
| excuse you from the conditions of this License.  If you cannot | ||||
| distribute so as to satisfy simultaneously your obligations under this | ||||
| License and any other pertinent obligations, then as a consequence you | ||||
| may not distribute the Program at all.  For example, if a patent | ||||
| license would not permit royalty-free redistribution of the Program by | ||||
| all those who receive copies directly or indirectly through you, then | ||||
| the only way you could satisfy both it and this License would be to | ||||
| refrain entirely from distribution of the Program. | ||||
| 
 | ||||
| If any portion of this section is held invalid or unenforceable under | ||||
| any particular circumstance, the balance of the section is intended to | ||||
| apply and the section as a whole is intended to apply in other | ||||
| circumstances. | ||||
| 
 | ||||
| It is not the purpose of this section to induce you to infringe any | ||||
| patents or other property right claims or to contest validity of any | ||||
| such claims; this section has the sole purpose of protecting the | ||||
| integrity of the free software distribution system, which is | ||||
| implemented by public license practices.  Many people have made | ||||
| generous contributions to the wide range of software distributed | ||||
| through that system in reliance on consistent application of that | ||||
| system; it is up to the author/donor to decide if he or she is willing | ||||
| to distribute software through any other system and a licensee cannot | ||||
| impose that choice. | ||||
| 
 | ||||
| This section is intended to make thoroughly clear what is believed to | ||||
| be a consequence of the rest of this License. | ||||
| 
 | ||||
|   8. If the distribution and/or use of the Program is restricted in | ||||
| certain countries either by patents or by copyrighted interfaces, the | ||||
| original copyright holder who places the Program under this License | ||||
| may add an explicit geographical distribution limitation excluding | ||||
| those countries, so that distribution is permitted only in or among | ||||
| countries not thus excluded.  In such case, this License incorporates | ||||
| the limitation as if written in the body of this License. | ||||
| 
 | ||||
|   9. The Free Software Foundation may publish revised and/or new versions | ||||
| of the General Public License from time to time.  Such new versions will | ||||
| be similar in spirit to the present version, but may differ in detail to | ||||
| address new problems or concerns. | ||||
| 
 | ||||
| Each version is given a distinguishing version number.  If the Program | ||||
| specifies a version number of this License which applies to it and "any | ||||
| later version", you have the option of following the terms and conditions | ||||
| either of that version or of any later version published by the Free | ||||
| Software Foundation.  If the Program does not specify a version number of | ||||
| this License, you may choose any version ever published by the Free Software | ||||
| Foundation. | ||||
| 
 | ||||
|   10. If you wish to incorporate parts of the Program into other free | ||||
| programs whose distribution conditions are different, write to the author | ||||
| to ask for permission.  For software which is copyrighted by the Free | ||||
| Software Foundation, write to the Free Software Foundation; we sometimes | ||||
| make exceptions for this.  Our decision will be guided by the two goals | ||||
| of preserving the free status of all derivatives of our free software and | ||||
| of promoting the sharing and reuse of software generally. | ||||
| 
 | ||||
|                             NO WARRANTY | ||||
| 
 | ||||
|   11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY | ||||
| FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN | ||||
| OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES | ||||
| PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED | ||||
| OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | ||||
| MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS | ||||
| TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE | ||||
| PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, | ||||
| REPAIR OR CORRECTION. | ||||
| 
 | ||||
|   12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING | ||||
| WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR | ||||
| REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, | ||||
| INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING | ||||
| OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED | ||||
| TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY | ||||
| YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER | ||||
| PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE | ||||
| POSSIBILITY OF SUCH DAMAGES. | ||||
| 
 | ||||
|                      END OF TERMS AND CONDITIONS | ||||
							
								
								
									
										1890
									
								
								Doxyfile.in
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1890
									
								
								Doxyfile.in
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										11
									
								
								README
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								README
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,11 @@ | ||||
| PCManFM-Qt is the Qt port of the LXDE file manager PCManFM. | ||||
| Libfm-Qt is a companion library providing components to build desktop file managers. | ||||
| 
 | ||||
| Issue tracker: | ||||
|   https://github.com/lxde/pcmanfm-qt/issues | ||||
| 
 | ||||
| LXQt website: | ||||
|   http://lxqt.org | ||||
| 
 | ||||
| LXDE website: | ||||
|   http://lxde.org | ||||
							
								
								
									
										148
									
								
								libfm-qt/CMakeLists.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										148
									
								
								libfm-qt/CMakeLists.txt
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,148 @@ | ||||
| project(fm-qt) | ||||
| 
 | ||||
| set(LIBRARY_NAME "fm-qt5") | ||||
| set(QTX_INCLUDE_DIRS "") | ||||
| set(QTX_LIBRARIES Qt5::Widgets Qt5::X11Extras) | ||||
| 
 | ||||
| include_directories( | ||||
|     ${QTX_INCLUDE_DIRS} | ||||
|     ${LIBFM_INCLUDE_DIRS} | ||||
|     "${LIBFM_INCLUDEDIR}/libfm" # to workaround incorrect #include in fm-actions. | ||||
|     ${LIBMENUCACHE_INCLUDE_DIRS} | ||||
|     ${SYSTEM_LIBS_INCLUDE_DIRS} | ||||
|     "${CMAKE_CURRENT_BINARY_DIR}" | ||||
| ) | ||||
| 
 | ||||
| link_directories( | ||||
|     ${LIBFM_LIBRARY_DIRS} | ||||
|     ${LIBMENUCACHE_LIBRARY_DIRS} | ||||
|     ${SYSTEM_LIBS_LIBRARY_DIRS} | ||||
| ) | ||||
| 
 | ||||
| set(libfm_SRCS | ||||
|     libfmqt.cpp | ||||
|     bookmarkaction.cpp | ||||
|     sidepane.cpp | ||||
|     icontheme.cpp | ||||
|     filelauncher.cpp | ||||
|     foldermodel.cpp | ||||
|     foldermodelitem.cpp | ||||
|     cachedfoldermodel.cpp | ||||
|     proxyfoldermodel.cpp | ||||
|     folderview.cpp | ||||
|     folderitemdelegate.cpp | ||||
|     filemenu.cpp | ||||
|     foldermenu.cpp | ||||
|     filepropsdialog.cpp | ||||
|     applaunchcontext.cpp | ||||
|     placesview.cpp | ||||
|     placesmodel.cpp | ||||
|     placesmodelitem.cpp | ||||
|     dirtreeview.cpp | ||||
|     dirtreemodel.cpp | ||||
|     dirtreemodelitem.cpp | ||||
|     dnddest.cpp | ||||
|     mountoperation.cpp | ||||
|     mountoperationpassworddialog.cpp | ||||
|     mountoperationquestiondialog.cpp | ||||
|     fileoperation.cpp | ||||
|     fileoperationdialog.cpp | ||||
|     renamedialog.cpp | ||||
|     pathedit.cpp | ||||
|     colorbutton.cpp | ||||
|     fontbutton.cpp | ||||
|     browsehistory.cpp | ||||
|     utilities.cpp | ||||
|     dndactionmenu.cpp | ||||
|     editbookmarksdialog.cpp | ||||
|     thumbnailloader.cpp | ||||
|     path.cpp | ||||
|     execfiledialog.cpp | ||||
|     appchoosercombobox.cpp | ||||
|     appmenuview.cpp | ||||
|     appchooserdialog.cpp | ||||
| ) | ||||
| 
 | ||||
| set(libfm_UIS | ||||
|     file-props.ui | ||||
|     file-operation-dialog.ui | ||||
|     rename-dialog.ui | ||||
|     mount-operation-password.ui | ||||
|     edit-bookmarks.ui | ||||
|     exec-file.ui | ||||
|     app-chooser-dialog.ui | ||||
| ) | ||||
| 
 | ||||
| qt5_wrap_ui(libfm_UIS_H ${libfm_UIS}) | ||||
| 
 | ||||
| add_library(${LIBRARY_NAME} SHARED | ||||
|     ${libfm_SRCS} | ||||
|     ${libfm_UIS_H} | ||||
| ) | ||||
| 
 | ||||
| set_property( | ||||
|     TARGET ${LIBRARY_NAME} APPEND | ||||
|     PROPERTY COMPILE_DEFINITIONS | ||||
|     LIBFM_QT_COMPILATION=1 | ||||
|     LIBFM_DATA_DIR="${CMAKE_INSTALL_FULL_DATADIR}/libfm-qt" | ||||
| ) | ||||
| 
 | ||||
| # only turn on custom actions support if it is enabled in libfm. | ||||
| if(EXISTS ${LIBFM_INCLUDEDIR}/libfm/fm-actions.h) | ||||
|     set_property(TARGET ${LIBRARY_NAME} APPEND PROPERTY COMPILE_DEFINITIONS CUSTOM_ACTIONS) | ||||
| endif() | ||||
| 
 | ||||
| target_link_libraries(${LIBRARY_NAME} | ||||
|     ${QTX_LIBRARIES} | ||||
|     ${LIBFM_LIBRARIES} | ||||
|     ${LIBMENUCACHE_LIBRARIES} | ||||
|     ${SYSTEM_LIBS_LIBRARIES} | ||||
| ) | ||||
| 
 | ||||
| # set libtool soname | ||||
| set_target_properties(${LIBRARY_NAME} PROPERTIES | ||||
|     VERSION ${LIBFM_QT_LIB_VERSION} | ||||
|     SOVERSION ${LIBFM_QT_LIB_SOVERSION} | ||||
| ) | ||||
| 
 | ||||
| # install include header files (FIXME: can we make this cleaner? should dir name be versioned?) | ||||
| install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} | ||||
|     DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} | ||||
|     FILES_MATCHING PATTERN "*.h" | ||||
|     PATTERN "*_p.h" EXCLUDE # exclude private headers | ||||
| ) | ||||
| 
 | ||||
| # FIXME: add libtool version to the lib (soname) later. | ||||
| # FIXME: only export public symbols | ||||
| 
 | ||||
| install(TARGETS ${LIBRARY_NAME} | ||||
|     LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} | ||||
|     PUBLIC_HEADER | ||||
| ) | ||||
| 
 | ||||
| # install a pkgconfig file for libfm-qt | ||||
| set(REQUIRED_QT "Qt5Core >= 5.1 Qt5DBus >= 5.1") | ||||
| configure_file(libfm-qt.pc.in lib${LIBRARY_NAME}.pc @ONLY) | ||||
| # FreeBSD loves to install files to different locations | ||||
| # http://www.freebsd.org/doc/handbook/dirstructure.html | ||||
| if(${CMAKE_SYSTEM_NAME} STREQUAL "FreeBSD") | ||||
|     install(FILES "${CMAKE_CURRENT_BINARY_DIR}/lib${LIBRARY_NAME}.pc" DESTINATION libdata/pkgconfig) | ||||
| else() | ||||
|     install(FILES "${CMAKE_CURRENT_BINARY_DIR}/lib${LIBRARY_NAME}.pc" DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig") | ||||
| endif() | ||||
| 
 | ||||
| # add translation for pcmanfm-qt | ||||
| # See http://www.cmake.org/Wiki/CMake:How_To_Build_Qt4_Software | ||||
| option (UPDATE_TRANSLATIONS "Update source translation translations/*.ts files") | ||||
| file(GLOB TS_FILES translations/*.ts) | ||||
| if(UPDATE_TRANSLATIONS) | ||||
|     qt5_create_translation(QM_FILES ${libfm_SRCS} ${libfm_UIS} ${TS_FILES}) | ||||
| else() | ||||
|     qt5_add_translation(QM_FILES ${TS_FILES}) | ||||
| endif() | ||||
| 
 | ||||
| add_custom_target(libfm_translations DEPENDS ${QM_FILES}) | ||||
| install(FILES ${QM_FILES} DESTINATION "${CMAKE_INSTALL_DATADIR}/libfm-qt/translations") | ||||
| 
 | ||||
| # prevent the generated files from being deleted during make cleaner | ||||
| set_directory_properties(PROPERTIES CLEAN_NO_CUSTOM true) | ||||
							
								
								
									
										183
									
								
								libfm-qt/app-chooser-dialog.ui
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										183
									
								
								libfm-qt/app-chooser-dialog.ui
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,183 @@ | ||||
| <?xml version="1.0" encoding="UTF-8"?> | ||||
| <ui version="4.0"> | ||||
|  <class>AppChooserDialog</class> | ||||
|  <widget class="QDialog" name="AppChooserDialog"> | ||||
|   <property name="geometry"> | ||||
|    <rect> | ||||
|     <x>0</x> | ||||
|     <y>0</y> | ||||
|     <width>432</width> | ||||
|     <height>387</height> | ||||
|    </rect> | ||||
|   </property> | ||||
|   <property name="windowTitle"> | ||||
|    <string>Choose an Application</string> | ||||
|   </property> | ||||
|   <layout class="QFormLayout" name="formLayout"> | ||||
|    <property name="fieldGrowthPolicy"> | ||||
|     <enum>QFormLayout::AllNonFixedFieldsGrow</enum> | ||||
|    </property> | ||||
|    <item row="0" column="1"> | ||||
|     <widget class="QLabel" name="fileTypeHeader"/> | ||||
|    </item> | ||||
|    <item row="1" column="0" colspan="2"> | ||||
|     <widget class="QTabWidget" name="tabWidget"> | ||||
|      <property name="sizePolicy"> | ||||
|       <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> | ||||
|        <horstretch>0</horstretch> | ||||
|        <verstretch>1</verstretch> | ||||
|       </sizepolicy> | ||||
|      </property> | ||||
|      <property name="currentIndex"> | ||||
|       <number>0</number> | ||||
|      </property> | ||||
|      <widget class="QWidget" name="tab"> | ||||
|       <attribute name="title"> | ||||
|        <string>Installed Applications</string> | ||||
|       </attribute> | ||||
|       <layout class="QVBoxLayout" name="verticalLayout"> | ||||
|        <item> | ||||
|         <widget class="Fm::AppMenuView" name="appMenuView"/> | ||||
|        </item> | ||||
|       </layout> | ||||
|      </widget> | ||||
|      <widget class="QWidget" name="tab_2"> | ||||
|       <attribute name="title"> | ||||
|        <string>Custom Command</string> | ||||
|       </attribute> | ||||
|       <layout class="QFormLayout" name="formLayout_2"> | ||||
|        <item row="0" column="0" colspan="2"> | ||||
|         <widget class="QLabel" name="label_3"> | ||||
|          <property name="text"> | ||||
|           <string>Command line to execute:</string> | ||||
|          </property> | ||||
|         </widget> | ||||
|        </item> | ||||
|        <item row="1" column="0" colspan="2"> | ||||
|         <widget class="QLineEdit" name="cmdLine"/> | ||||
|        </item> | ||||
|        <item row="3" column="0"> | ||||
|         <widget class="QLabel" name="label_4"> | ||||
|          <property name="text"> | ||||
|           <string>Application name:</string> | ||||
|          </property> | ||||
|         </widget> | ||||
|        </item> | ||||
|        <item row="3" column="1"> | ||||
|         <widget class="QLineEdit" name="appName"/> | ||||
|        </item> | ||||
|        <item row="2" column="0" colspan="2"> | ||||
|         <widget class="QLabel" name="label_5"> | ||||
|          <property name="text"> | ||||
|           <string><b>These special codes can be used in the command line:</b> | ||||
| <ul> | ||||
| <li><b>%f</b>: Represents a single file name</li> | ||||
| <li><b>%F</b>: Represents multiple file names</li> | ||||
| <li><b>%u</b>: Represents a single URI of the file</li> | ||||
| <li><b>%U</b>: Represents multiple URIs</li> | ||||
| </ul></string> | ||||
|          </property> | ||||
|          <property name="textFormat"> | ||||
|           <enum>Qt::RichText</enum> | ||||
|          </property> | ||||
|         </widget> | ||||
|        </item> | ||||
|        <item row="5" column="0" colspan="2"> | ||||
|         <widget class="QCheckBox" name="keepTermOpen"> | ||||
|          <property name="enabled"> | ||||
|           <bool>false</bool> | ||||
|          </property> | ||||
|          <property name="text"> | ||||
|           <string>Keep terminal window open after command execution</string> | ||||
|          </property> | ||||
|         </widget> | ||||
|        </item> | ||||
|        <item row="4" column="0" colspan="2"> | ||||
|         <widget class="QCheckBox" name="useTerminal"> | ||||
|          <property name="text"> | ||||
|           <string>Execute in terminal emulator</string> | ||||
|          </property> | ||||
|         </widget> | ||||
|        </item> | ||||
|       </layout> | ||||
|      </widget> | ||||
|     </widget> | ||||
|    </item> | ||||
|    <item row="2" column="0" colspan="2"> | ||||
|     <widget class="QCheckBox" name="setDefault"> | ||||
|      <property name="text"> | ||||
|       <string>Set selected application as default action of this file type</string> | ||||
|      </property> | ||||
|     </widget> | ||||
|    </item> | ||||
|    <item row="3" column="0" colspan="2"> | ||||
|     <widget class="QDialogButtonBox" name="buttonBox"> | ||||
|      <property name="orientation"> | ||||
|       <enum>Qt::Horizontal</enum> | ||||
|      </property> | ||||
|      <property name="standardButtons"> | ||||
|       <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> | ||||
|      </property> | ||||
|     </widget> | ||||
|    </item> | ||||
|   </layout> | ||||
|  </widget> | ||||
|  <customwidgets> | ||||
|   <customwidget> | ||||
|    <class>Fm::AppMenuView</class> | ||||
|    <extends>QTreeView</extends> | ||||
|    <header>appmenuview.h</header> | ||||
|   </customwidget> | ||||
|  </customwidgets> | ||||
|  <resources/> | ||||
|  <connections> | ||||
|   <connection> | ||||
|    <sender>buttonBox</sender> | ||||
|    <signal>accepted()</signal> | ||||
|    <receiver>AppChooserDialog</receiver> | ||||
|    <slot>accept()</slot> | ||||
|    <hints> | ||||
|     <hint type="sourcelabel"> | ||||
|      <x>227</x> | ||||
|      <y>359</y> | ||||
|     </hint> | ||||
|     <hint type="destinationlabel"> | ||||
|      <x>157</x> | ||||
|      <y>274</y> | ||||
|     </hint> | ||||
|    </hints> | ||||
|   </connection> | ||||
|   <connection> | ||||
|    <sender>buttonBox</sender> | ||||
|    <signal>rejected()</signal> | ||||
|    <receiver>AppChooserDialog</receiver> | ||||
|    <slot>reject()</slot> | ||||
|    <hints> | ||||
|     <hint type="sourcelabel"> | ||||
|      <x>295</x> | ||||
|      <y>365</y> | ||||
|     </hint> | ||||
|     <hint type="destinationlabel"> | ||||
|      <x>286</x> | ||||
|      <y>274</y> | ||||
|     </hint> | ||||
|    </hints> | ||||
|   </connection> | ||||
|   <connection> | ||||
|    <sender>useTerminal</sender> | ||||
|    <signal>toggled(bool)</signal> | ||||
|    <receiver>keepTermOpen</receiver> | ||||
|    <slot>setEnabled(bool)</slot> | ||||
|    <hints> | ||||
|     <hint type="sourcelabel"> | ||||
|      <x>72</x> | ||||
|      <y>260</y> | ||||
|     </hint> | ||||
|     <hint type="destinationlabel"> | ||||
|      <x>79</x> | ||||
|      <y>282</y> | ||||
|     </hint> | ||||
|    </hints> | ||||
|   </connection> | ||||
|  </connections> | ||||
| </ui> | ||||
							
								
								
									
										143
									
								
								libfm-qt/appchoosercombobox.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										143
									
								
								libfm-qt/appchoosercombobox.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,143 @@ | ||||
| /*
 | ||||
|  * <one line to give the library's name and an idea of what it does.> | ||||
|  * Copyright (C) 2014  <copyright holder> <email> | ||||
|  * | ||||
|  * 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 | ||||
|  * | ||||
|  */ | ||||
| 
 | ||||
| #include "appchoosercombobox.h" | ||||
| #include "icontheme.h" | ||||
| #include "appchooserdialog.h" | ||||
| #include "utilities.h" | ||||
| 
 | ||||
| namespace Fm { | ||||
| 
 | ||||
| AppChooserComboBox::AppChooserComboBox(QWidget* parent): | ||||
|   QComboBox(parent), | ||||
|   defaultApp_(NULL), | ||||
|   appInfos_(NULL), | ||||
|   defaultAppIndex_(0), | ||||
|   prevIndex_(0), | ||||
|   mimeType_(NULL) { | ||||
| 
 | ||||
|   // the new Qt5 signal/slot syntax cannot handle overloaded methods by default
 | ||||
|   // hence a type-casting is needed here. really ugly!
 | ||||
|   // reference: http://qt-project.org/forums/viewthread/21513
 | ||||
|   connect((QComboBox*)this, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, &AppChooserComboBox::onCurrentIndexChanged); | ||||
| } | ||||
| 
 | ||||
| AppChooserComboBox::~AppChooserComboBox() { | ||||
|   if(mimeType_) | ||||
|     fm_mime_type_unref(mimeType_); | ||||
|   if(defaultApp_) | ||||
|     g_object_unref(defaultApp_); | ||||
|   // delete GAppInfo objects stored for Combobox
 | ||||
|   if(appInfos_) { | ||||
|     g_list_foreach(appInfos_, (GFunc)g_object_unref, NULL); | ||||
|     g_list_free(appInfos_); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| void AppChooserComboBox::setMimeType(FmMimeType* mimeType) { | ||||
|   clear(); | ||||
|   if(mimeType_) | ||||
|     fm_mime_type_unref(mimeType_); | ||||
| 
 | ||||
|   mimeType_ = fm_mime_type_ref(mimeType); | ||||
|   if(mimeType_) { | ||||
|     const char* typeName = fm_mime_type_get_type(mimeType_); | ||||
|     defaultApp_ = g_app_info_get_default_for_type(typeName, FALSE); | ||||
|     appInfos_ = g_app_info_get_all_for_type(typeName); | ||||
|     int i = 0; | ||||
|     for(GList* l = appInfos_; l; l = l->next, ++i) { | ||||
|       GAppInfo* app = G_APP_INFO(l->data); | ||||
|       GIcon* gicon = g_app_info_get_icon(app); | ||||
|       QString name = QString::fromUtf8(g_app_info_get_name(app)); | ||||
|       // QVariant data = qVariantFromValue<void*>(app);
 | ||||
|       // addItem(IconTheme::icon(gicon), name, data);
 | ||||
|       addItem(IconTheme::icon(gicon), name); | ||||
|       if(app == defaultApp_) | ||||
|         defaultAppIndex_ = i; | ||||
|     } | ||||
|     setCurrentIndex(defaultAppIndex_); | ||||
|   } | ||||
|   // add "Other applications" item
 | ||||
|   insertSeparator(count()); | ||||
|   addItem(tr("Customize")); | ||||
| } | ||||
| 
 | ||||
| // returns the currently selected app.
 | ||||
| GAppInfo* AppChooserComboBox::selectedApp() { | ||||
|   return G_APP_INFO(g_list_nth_data(appInfos_, currentIndex())); | ||||
| } | ||||
| 
 | ||||
| bool AppChooserComboBox::isChanged() { | ||||
|   return (defaultAppIndex_ != currentIndex()); | ||||
| } | ||||
| 
 | ||||
| void AppChooserComboBox::onCurrentIndexChanged(int index) { | ||||
|   if(index == -1 || index == prevIndex_) | ||||
|     return; | ||||
| 
 | ||||
|   // the last item is "Customize"
 | ||||
|   if(index == (count() - 1)) { | ||||
|     /* TODO: let the user choose an app or add custom actions here. */ | ||||
|     QWidget* toplevel = topLevelWidget(); | ||||
|     AppChooserDialog dlg(mimeType_, toplevel); | ||||
|     dlg.setWindowModality(Qt::WindowModal); | ||||
|     dlg.setCanSetDefault(false); | ||||
|     if(dlg.exec() == QDialog::Accepted) { | ||||
|       GAppInfo* app = dlg.selectedApp(); | ||||
|       if(app) { | ||||
|         /* see if it's already in the list to prevent duplication */ | ||||
|         GList* found = NULL; | ||||
|         for(found = appInfos_; found; found = found->next) { | ||||
|           if(g_app_info_equal(app, G_APP_INFO(found->data))) | ||||
|             break; | ||||
|         } | ||||
|         /* if it's already in the list, select it */ | ||||
|         if(found) { | ||||
|           setCurrentIndex(g_list_position(appInfos_, found)); | ||||
|           g_object_unref(app); | ||||
|         } | ||||
|         else { /* if it's not found, add it to the list */ | ||||
|           appInfos_ = g_list_prepend(appInfos_, app); | ||||
|           GIcon* gicon = g_app_info_get_icon(app); | ||||
|           QString name = QString::fromUtf8(g_app_info_get_name(app)); | ||||
|           insertItem(0, IconTheme::icon(gicon), name); | ||||
|           setCurrentIndex(0); | ||||
|         } | ||||
|         return; | ||||
|       } | ||||
|     } | ||||
|     // restore to previously selected item
 | ||||
|     setCurrentIndex(prevIndex_); | ||||
|   } | ||||
|   else { | ||||
|     prevIndex_ = index; | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| #if 0 | ||||
| /* get a list of custom apps added with app-chooser.
 | ||||
| * the returned GList is owned by the combo box and shouldn't be freed. */ | ||||
| const GList* AppChooserComboBox::customApps() { | ||||
| 
 | ||||
| } | ||||
| #endif | ||||
| 
 | ||||
| } // namespace Fm
 | ||||
							
								
								
									
										60
									
								
								libfm-qt/appchoosercombobox.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										60
									
								
								libfm-qt/appchoosercombobox.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,60 @@ | ||||
| /*
 | ||||
|  * <one line to give the library's name and an idea of what it does.> | ||||
|  * Copyright (C) 2014  <copyright holder> <email> | ||||
|  * | ||||
|  * 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 | ||||
|  * | ||||
|  */ | ||||
| 
 | ||||
| #ifndef FM_APPCHOOSERCOMBOBOX_H | ||||
| #define FM_APPCHOOSERCOMBOBOX_H | ||||
| 
 | ||||
| #include "libfmqtglobals.h" | ||||
| #include <QComboBox> | ||||
| #include <libfm/fm.h> | ||||
| 
 | ||||
| namespace Fm { | ||||
| 
 | ||||
| class LIBFM_QT_API AppChooserComboBox : public QComboBox { | ||||
|   Q_OBJECT | ||||
| public: | ||||
|   ~AppChooserComboBox(); | ||||
|   AppChooserComboBox(QWidget* parent); | ||||
| 
 | ||||
|   void setMimeType(FmMimeType* mimeType); | ||||
| 
 | ||||
|   FmMimeType* mimeType() { | ||||
|     return mimeType_; | ||||
|   } | ||||
| 
 | ||||
|   GAppInfo* selectedApp(); | ||||
|   // const GList* customApps();
 | ||||
| 
 | ||||
|   bool isChanged(); | ||||
| 
 | ||||
| private Q_SLOTS: | ||||
|   void onCurrentIndexChanged(int index); | ||||
|    | ||||
| private: | ||||
|   FmMimeType* mimeType_; | ||||
|   GList* appInfos_; // applications used to open the file type
 | ||||
|   GAppInfo* defaultApp_; // default application used to open the file type 
 | ||||
|   int defaultAppIndex_; | ||||
|   int prevIndex_; | ||||
| }; | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| #endif // FM_APPCHOOSERCOMBOBOX_H
 | ||||
							
								
								
									
										286
									
								
								libfm-qt/appchooserdialog.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										286
									
								
								libfm-qt/appchooserdialog.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,286 @@ | ||||
| /*
 | ||||
|  * Copyright 2010-2014 Hong Jen Yee (PCMan) <pcman.tw@gmail.com> | ||||
|  * Copyright 2012-2013 Andriy Grytsenko (LStranger) <andrej@rep.kiev.ua> | ||||
|  * | ||||
|  * 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 | ||||
|  * | ||||
|  */ | ||||
| 
 | ||||
| #include "appchooserdialog.h" | ||||
| #include "ui_app-chooser-dialog.h" | ||||
| #include <QPushButton> | ||||
| #include <gio/gdesktopappinfo.h> | ||||
| 
 | ||||
| namespace Fm { | ||||
| 
 | ||||
| AppChooserDialog::AppChooserDialog(FmMimeType* mimeType, QWidget* parent, Qt::WindowFlags f): | ||||
|   QDialog(parent, f), | ||||
|   mimeType_(NULL), | ||||
|   selectedApp_(NULL), | ||||
|   canSetDefault_(true), | ||||
|   ui(new Ui::AppChooserDialog()) { | ||||
|   ui->setupUi(this); | ||||
| 
 | ||||
|   connect(ui->appMenuView, &AppMenuView::selectionChanged, this, &AppChooserDialog::onSelectionChanged); | ||||
|   connect(ui->tabWidget, &QTabWidget::currentChanged, this, &AppChooserDialog::onTabChanged); | ||||
| 
 | ||||
|   if(!ui->appMenuView->isAppSelected()) | ||||
|     ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false); // disable OK button
 | ||||
| 
 | ||||
|   if(mimeType) | ||||
|     setMimeType(mimeType); | ||||
| } | ||||
| 
 | ||||
| AppChooserDialog::~AppChooserDialog() { | ||||
|   delete ui; | ||||
|   if(mimeType_) | ||||
|     fm_mime_type_unref(mimeType_); | ||||
|   if(selectedApp_) | ||||
|     g_object_unref(selectedApp_); | ||||
| } | ||||
| 
 | ||||
| bool AppChooserDialog::isSetDefault() { | ||||
|   return ui->setDefault->isChecked(); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| static void on_temp_appinfo_destroy(gpointer data, GObject* objptr) { | ||||
|   char* filename = (char*)data; | ||||
|   if(g_unlink(filename) < 0) | ||||
|     g_critical("failed to remove %s", filename); | ||||
|   /* else
 | ||||
|       qDebug("temp file %s removed", filename); */ | ||||
|   g_free(filename); | ||||
| } | ||||
| 
 | ||||
| static GAppInfo* app_info_create_from_commandline(const char* commandline, | ||||
|     const char* application_name, | ||||
|     const char* bin_name, | ||||
|     const char* mime_type, | ||||
|     gboolean terminal, gboolean keep) { | ||||
|   GAppInfo* app = NULL; | ||||
|   char* dirname = g_build_filename(g_get_user_data_dir(), "applications", NULL); | ||||
|   const char* app_basename = strrchr(bin_name, '/'); | ||||
| 
 | ||||
|   if(app_basename) | ||||
|     app_basename++; | ||||
|   else | ||||
|     app_basename = bin_name; | ||||
|   if(g_mkdir_with_parents(dirname, 0700) == 0) { | ||||
|     char* filename = g_strdup_printf("%s/userapp-%s-XXXXXX.desktop", dirname, app_basename); | ||||
|     int fd = g_mkstemp(filename); | ||||
|     if(fd != -1) { | ||||
|       GString* content = g_string_sized_new(256); | ||||
|       g_string_printf(content, | ||||
|                       "[" G_KEY_FILE_DESKTOP_GROUP "]\n" | ||||
|                       G_KEY_FILE_DESKTOP_KEY_TYPE "=" G_KEY_FILE_DESKTOP_TYPE_APPLICATION "\n" | ||||
|                       G_KEY_FILE_DESKTOP_KEY_NAME "=%s\n" | ||||
|                       G_KEY_FILE_DESKTOP_KEY_EXEC "=%s\n" | ||||
|                       G_KEY_FILE_DESKTOP_KEY_CATEGORIES "=Other;\n" | ||||
|                       G_KEY_FILE_DESKTOP_KEY_NO_DISPLAY "=true\n", | ||||
|                       application_name, | ||||
|                       commandline | ||||
|                      ); | ||||
|       if(mime_type) | ||||
|         g_string_append_printf(content, | ||||
|                                G_KEY_FILE_DESKTOP_KEY_MIME_TYPE "=%s\n", | ||||
|                                mime_type); | ||||
|       g_string_append_printf(content, | ||||
|                              G_KEY_FILE_DESKTOP_KEY_TERMINAL "=%s\n", | ||||
|                              terminal ? "true" : "false"); | ||||
|       if(terminal) | ||||
|         g_string_append_printf(content, "X-KeepTerminal=%s\n", | ||||
|                                keep ? "true" : "false"); | ||||
|       close(fd); /* g_file_set_contents() may fail creating duplicate */ | ||||
|       if(g_file_set_contents(filename, content->str, content->len, NULL)) { | ||||
|         char* fbname = g_path_get_basename(filename); | ||||
|         app = G_APP_INFO(g_desktop_app_info_new(fbname)); | ||||
|         g_free(fbname); | ||||
|         /* if there is mime_type set then created application will be
 | ||||
|            saved for the mime type (see fm_choose_app_for_mime_type() | ||||
|            below) but if not then we should remove this temp. file */ | ||||
|         if(!mime_type || !application_name[0]) | ||||
|           /* save the name so this file will be removed later */ | ||||
|           g_object_weak_ref(G_OBJECT(app), on_temp_appinfo_destroy, | ||||
|                             g_strdup(filename)); | ||||
|       } | ||||
|       else | ||||
|         g_unlink(filename); | ||||
|       g_string_free(content, TRUE); | ||||
|     } | ||||
|     g_free(filename); | ||||
|   } | ||||
|   g_free(dirname); | ||||
|   return app; | ||||
| } | ||||
| 
 | ||||
| inline static char* get_binary(const char* cmdline, gboolean* arg_found) { | ||||
|   /* see if command line contains %f, %F, %u, or %U. */ | ||||
|   const char* p = strstr(cmdline, " %"); | ||||
|   if(p) { | ||||
|     if(!strchr("fFuU", *(p + 2))) | ||||
|       p = NULL; | ||||
|   } | ||||
|   if(arg_found) | ||||
|     *arg_found = (p != NULL); | ||||
|   if(p) | ||||
|     return g_strndup(cmdline, p - cmdline); | ||||
|   else | ||||
|     return g_strdup(cmdline); | ||||
| } | ||||
| 
 | ||||
| GAppInfo* AppChooserDialog::customCommandToApp() { | ||||
|   GAppInfo* app = NULL; | ||||
|   QByteArray cmdline = ui->cmdLine->text().toLocal8Bit(); | ||||
|   QByteArray app_name = ui->appName->text().toUtf8(); | ||||
|   if(!cmdline.isEmpty()) { | ||||
|     gboolean arg_found = FALSE; | ||||
|     char* bin1 = get_binary(cmdline.constData(), &arg_found); | ||||
|     qDebug("bin1 = %s", bin1); | ||||
|     /* see if command line contains %f, %F, %u, or %U. */ | ||||
|     if(!arg_found) {  /* append %f if no %f, %F, %u, or %U was found. */ | ||||
|       cmdline += " %f"; | ||||
|     } | ||||
| 
 | ||||
|     /* FIXME: is there any better way to do this? */ | ||||
|     /* We need to ensure that no duplicated items are added */ | ||||
|     if(mimeType_) { | ||||
|       MenuCache* menu_cache; | ||||
|       /* see if the command is already in the list of known apps for this mime-type */ | ||||
|       GList* apps = g_app_info_get_all_for_type(fm_mime_type_get_type(mimeType_)); | ||||
|       GList* l; | ||||
|       for(l = apps; l; l = l->next) { | ||||
|         GAppInfo* app2 = G_APP_INFO(l->data); | ||||
|         const char* cmd = g_app_info_get_commandline(app2); | ||||
|         char* bin2 = get_binary(cmd, NULL); | ||||
|         if(g_strcmp0(bin1, bin2) == 0) { | ||||
|           app = G_APP_INFO(g_object_ref(app2)); | ||||
|           qDebug("found in app list"); | ||||
|           g_free(bin2); | ||||
|           break; | ||||
|         } | ||||
|         g_free(bin2); | ||||
|       } | ||||
|       g_list_foreach(apps, (GFunc)g_object_unref, NULL); | ||||
|       g_list_free(apps); | ||||
|       if(app) | ||||
|         goto _out; | ||||
| 
 | ||||
|       /* see if this command can be found in menu cache */ | ||||
|       menu_cache = menu_cache_lookup("applications.menu"); | ||||
|       if(menu_cache) { | ||||
|         MenuCacheDir* root_dir = menu_cache_dup_root_dir(menu_cache); | ||||
|         if(root_dir) { | ||||
|           GSList* all_apps = menu_cache_list_all_apps(menu_cache); | ||||
|           GSList* l; | ||||
|           for(l = all_apps; l; l = l->next) { | ||||
|             MenuCacheApp* ma = MENU_CACHE_APP(l->data); | ||||
|             const char* exec = menu_cache_app_get_exec(ma); | ||||
|             char* bin2; | ||||
|             if(exec == NULL) { | ||||
|               g_warning("application %s has no Exec statement", menu_cache_item_get_id(MENU_CACHE_ITEM(ma))); | ||||
|               continue; | ||||
|             } | ||||
|             bin2 = get_binary(exec, NULL); | ||||
|             if(g_strcmp0(bin1, bin2) == 0) { | ||||
|               app = G_APP_INFO(g_desktop_app_info_new(menu_cache_item_get_id(MENU_CACHE_ITEM(ma)))); | ||||
|               qDebug("found in menu cache"); | ||||
|               menu_cache_item_unref(MENU_CACHE_ITEM(ma)); | ||||
|               g_free(bin2); | ||||
|               break; | ||||
|             } | ||||
|             menu_cache_item_unref(MENU_CACHE_ITEM(ma)); | ||||
|             g_free(bin2); | ||||
|           } | ||||
|           g_slist_free(all_apps); | ||||
|           menu_cache_item_unref(MENU_CACHE_ITEM(root_dir)); | ||||
|         } | ||||
|         menu_cache_unref(menu_cache); | ||||
|       } | ||||
|       if(app) | ||||
|         goto _out; | ||||
|     } | ||||
| 
 | ||||
|     /* FIXME: g_app_info_create_from_commandline force the use of %f or %u, so this is not we need */ | ||||
|     app = app_info_create_from_commandline(cmdline.constData(), app_name.constData(), bin1, | ||||
|                                            mimeType_ ? fm_mime_type_get_type(mimeType_) : NULL, | ||||
|                                            ui->useTerminal->isChecked(), ui->keepTermOpen->isChecked()); | ||||
|   _out: | ||||
|     g_free(bin1); | ||||
|   } | ||||
|   return app; | ||||
| } | ||||
| 
 | ||||
| void AppChooserDialog::accept() { | ||||
|   QDialog::accept(); | ||||
| 
 | ||||
|   if(ui->tabWidget->currentIndex() == 0) { | ||||
|     selectedApp_ = ui->appMenuView->selectedApp(); | ||||
|   } | ||||
|   else { // custom command line
 | ||||
|     selectedApp_ = customCommandToApp(); | ||||
|   } | ||||
| 
 | ||||
|   if(selectedApp_) { | ||||
|     if(mimeType_ && fm_mime_type_get_type(mimeType_) && g_app_info_get_name(selectedApp_)[0]) { | ||||
|       /* add this app to the mime-type */ | ||||
| #if GLIB_CHECK_VERSION(2, 27, 6) | ||||
|       g_app_info_set_as_last_used_for_type(selectedApp_, fm_mime_type_get_type(mimeType_), NULL); | ||||
| #else | ||||
|       g_app_info_add_supports_type(selectedApp_, fm_mime_type_get_type(mimeType_), NULL); | ||||
| #endif | ||||
|       /* if need to set default */ | ||||
|       if(ui->setDefault->isChecked()) | ||||
|         g_app_info_set_as_default_for_type(selectedApp_, fm_mime_type_get_type(mimeType_), NULL); | ||||
|     } | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| void AppChooserDialog::onSelectionChanged() { | ||||
|   bool isAppSelected = ui->appMenuView->isAppSelected(); | ||||
|   ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(isAppSelected); | ||||
| } | ||||
| 
 | ||||
| void AppChooserDialog::setMimeType(FmMimeType* mimeType) { | ||||
|   if(mimeType_) | ||||
|     fm_mime_type_unref(mimeType_); | ||||
| 
 | ||||
|   mimeType_ = mimeType ? fm_mime_type_ref(mimeType) : NULL; | ||||
|   if(mimeType_) { | ||||
|     QString text = tr("Select an application to open \"%1\" files") | ||||
|                    .arg(QString::fromUtf8(fm_mime_type_get_desc(mimeType_))); | ||||
|     ui->fileTypeHeader->setText(text); | ||||
|   } | ||||
|   else { | ||||
|     ui->fileTypeHeader->hide(); | ||||
|     ui->setDefault->hide(); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| void AppChooserDialog::setCanSetDefault(bool value) { | ||||
|   canSetDefault_ = value; | ||||
|   ui->setDefault->setVisible(value); | ||||
| } | ||||
| 
 | ||||
| void AppChooserDialog::onTabChanged(int index) { | ||||
|   if(index == 0) { // app menu view
 | ||||
|     onSelectionChanged(); | ||||
|   } | ||||
|   else if(index == 1) { // custom command
 | ||||
|     ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(true); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| } // namespace Fm
 | ||||
							
								
								
									
										74
									
								
								libfm-qt/appchooserdialog.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										74
									
								
								libfm-qt/appchooserdialog.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,74 @@ | ||||
| /*
 | ||||
|  * Copyright 2010-2014 Hong Jen Yee (PCMan) <pcman.tw@gmail.com> | ||||
|  * Copyright 2012-2013 Andriy Grytsenko (LStranger) <andrej@rep.kiev.ua> | ||||
|  * | ||||
|  * 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 | ||||
|  * | ||||
|  */ | ||||
| 
 | ||||
| #ifndef FM_APPCHOOSERDIALOG_H | ||||
| #define FM_APPCHOOSERDIALOG_H | ||||
| 
 | ||||
| #include <QDialog> | ||||
| #include "libfmqtglobals.h" | ||||
| #include <libfm/fm.h> | ||||
| 
 | ||||
| namespace Ui { | ||||
|   class AppChooserDialog; | ||||
| } | ||||
| 
 | ||||
| namespace Fm { | ||||
| 
 | ||||
| class LIBFM_QT_API AppChooserDialog : public QDialog { | ||||
|   Q_OBJECT | ||||
| public: | ||||
|   explicit AppChooserDialog(FmMimeType* mimeType, QWidget* parent = NULL, Qt::WindowFlags f = 0); | ||||
|   ~AppChooserDialog(); | ||||
| 
 | ||||
|   virtual void accept(); | ||||
| 
 | ||||
|   void setMimeType(FmMimeType* mimeType); | ||||
|   FmMimeType* mimeType() { | ||||
|     return mimeType_; | ||||
|   } | ||||
| 
 | ||||
|   void setCanSetDefault(bool value); | ||||
|   bool canSetDefault() { | ||||
|     return canSetDefault_; | ||||
|   } | ||||
| 
 | ||||
|   GAppInfo* selectedApp() { | ||||
|     return G_APP_INFO(g_object_ref(selectedApp_)); | ||||
|   } | ||||
| 
 | ||||
|   bool isSetDefault(); | ||||
| 
 | ||||
| private: | ||||
|   GAppInfo* customCommandToApp(); | ||||
| 
 | ||||
| private Q_SLOTS: | ||||
|   void onSelectionChanged(); | ||||
|   void onTabChanged(int index); | ||||
| 
 | ||||
| private: | ||||
|   Ui::AppChooserDialog* ui; | ||||
|   FmMimeType* mimeType_; | ||||
|   bool canSetDefault_; | ||||
|   GAppInfo* selectedApp_; | ||||
| }; | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| #endif // FM_APPCHOOSERDIALOG_H
 | ||||
							
								
								
									
										61
									
								
								libfm-qt/applaunchcontext.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										61
									
								
								libfm-qt/applaunchcontext.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,61 @@ | ||||
| /*
 | ||||
|     <one line to give the library's name and an idea of what it does.> | ||||
|     Copyright (C) 2012  Hong Jen Yee (PCMan) <pcman.tw@gmail.com> | ||||
| 
 | ||||
|     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 | ||||
| */ | ||||
| 
 | ||||
| 
 | ||||
| #include "applaunchcontext.h" | ||||
| #include <QX11Info> | ||||
| #include <X11/Xlib.h> | ||||
| 
 | ||||
| typedef struct _FmAppLaunchContext { | ||||
|   GAppLaunchContext parent;   | ||||
| }FmAppLaunchContext; | ||||
| 
 | ||||
| G_DEFINE_TYPE(FmAppLaunchContext, fm_app_launch_context, G_TYPE_APP_LAUNCH_CONTEXT) | ||||
| 
 | ||||
| static char* fm_app_launch_context_get_display(GAppLaunchContext *context, GAppInfo *info, GList *files) { | ||||
|   Display* dpy = QX11Info::display(); | ||||
|   if(dpy) { | ||||
|     char* xstr = DisplayString(dpy); | ||||
|     return g_strdup(xstr); | ||||
|   } | ||||
|   return NULL; | ||||
| } | ||||
| 
 | ||||
| static char* fm_app_launch_context_get_startup_notify_id(GAppLaunchContext *context, GAppInfo *info, GList *files) { | ||||
|   return NULL; | ||||
| } | ||||
| 
 | ||||
| static void fm_app_launch_context_class_init(FmAppLaunchContextClass* klass) { | ||||
|   GAppLaunchContextClass* app_launch_class = G_APP_LAUNCH_CONTEXT_CLASS(klass); | ||||
|   app_launch_class->get_display = fm_app_launch_context_get_display; | ||||
|   app_launch_class->get_startup_notify_id = fm_app_launch_context_get_startup_notify_id; | ||||
| } | ||||
| 
 | ||||
| static void fm_app_launch_context_init(FmAppLaunchContext* context) { | ||||
| } | ||||
| 
 | ||||
| FmAppLaunchContext* fm_app_launch_context_new_for_widget(QWidget* widget) { | ||||
|   FmAppLaunchContext* context = (FmAppLaunchContext*)g_object_new(FM_TYPE_APP_LAUNCH_CONTEXT, NULL); | ||||
|   return context; | ||||
| } | ||||
| 
 | ||||
| FmAppLaunchContext* fm_app_launch_context_new() { | ||||
|   FmAppLaunchContext* context = (FmAppLaunchContext*)g_object_new(FM_TYPE_APP_LAUNCH_CONTEXT, NULL); | ||||
|   return context; | ||||
| } | ||||
							
								
								
									
										50
									
								
								libfm-qt/applaunchcontext.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										50
									
								
								libfm-qt/applaunchcontext.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,50 @@ | ||||
| /*
 | ||||
|     <one line to give the library's name and an idea of what it does.> | ||||
|     Copyright (C) 2012  Hong Jen Yee (PCMan) <pcman.tw@gmail.com> | ||||
| 
 | ||||
|     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 | ||||
| */ | ||||
| 
 | ||||
| 
 | ||||
| #ifndef FM_APP_LAUNCHCONTEXT_H | ||||
| #define FM_APP_LAUNCHCONTEXT_H | ||||
| 
 | ||||
| #include "libfmqtglobals.h" | ||||
| #include <gio/gio.h> | ||||
| #include <QWidget> | ||||
| 
 | ||||
| #define FM_TYPE_APP_LAUNCH_CONTEXT				(fm_app_launch_context_get_type()) | ||||
| #define FM_APP_LAUNCH_CONTEXT(obj)				(G_TYPE_CHECK_INSTANCE_CAST((obj),\ | ||||
| 			FM_TYPE_APP_LAUNCH_CONTEXT, FmAppLaunchContext)) | ||||
| #define FM_APP_LAUNCH_CONTEXT_CLASS(klass)		(G_TYPE_CHECK_CLASS_CAST((klass),\ | ||||
| 			FM_TYPE_APP_LAUNCH_CONTEXT, FmAppLaunchContextClass)) | ||||
| #define FM_IS_APP_LAUNCH_CONTEXT(obj)			(G_TYPE_CHECK_INSTANCE_TYPE((obj),\ | ||||
| 			FM_TYPE_APP_LAUNCH_CONTEXT)) | ||||
| #define FM_IS_APP_LAUNCH_CONTEXT_CLASS(klass)	(G_TYPE_CHECK_CLASS_TYPE((klass),\ | ||||
| 			FM_TYPE_APP_LAUNCH_CONTEXT)) | ||||
| #define FM_APP_LAUNCH_CONTEXT_GET_CLASS(obj)	(G_TYPE_INSTANCE_GET_CLASS((obj),\ | ||||
| 			FM_TYPE_APP_LAUNCH_CONTEXT, FmAppLaunchContextClass)) | ||||
| 
 | ||||
| typedef struct _FmAppLaunchContext FmAppLaunchContext; | ||||
| 
 | ||||
| typedef struct _FmAppLaunchContextClass { | ||||
|   GAppLaunchContextClass parent; | ||||
| }FmAppLaunchContextClass; | ||||
| 
 | ||||
| FmAppLaunchContext* fm_app_launch_context_new(); | ||||
| FmAppLaunchContext* fm_app_launch_context_new_for_widget(QWidget* widget); | ||||
| GType fm_app_launch_context_get_type(); | ||||
| 
 | ||||
| #endif // FM_APPLAUNCHCONTEXT_H
 | ||||
							
								
								
									
										159
									
								
								libfm-qt/appmenuview.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										159
									
								
								libfm-qt/appmenuview.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,159 @@ | ||||
| /*
 | ||||
|  * <one line to give the library's name and an idea of what it does.> | ||||
|  * Copyright (C) 2014  <copyright holder> <email> | ||||
|  * | ||||
|  * 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 | ||||
|  * | ||||
|  */ | ||||
| 
 | ||||
| #include "appmenuview.h" | ||||
| #include <QStandardItemModel> | ||||
| #include "icontheme.h" | ||||
| #include "appmenuview_p.h" | ||||
| #include <gio/gdesktopappinfo.h> | ||||
| 
 | ||||
| namespace Fm { | ||||
| 
 | ||||
| AppMenuView::AppMenuView(QWidget* parent): | ||||
|   model_(new QStandardItemModel()), | ||||
|   menu_cache(NULL), | ||||
|   menu_cache_reload_notify(NULL), | ||||
|   QTreeView(parent) { | ||||
| 
 | ||||
|   setHeaderHidden(true); | ||||
|   setSelectionMode(SingleSelection); | ||||
| 
 | ||||
|   // initialize model
 | ||||
|   // TODO: share one model among all app menu view widgets
 | ||||
|   // ensure that we're using lxmenu-data (FIXME: should we do this?)
 | ||||
|   QByteArray oldenv = qgetenv("XDG_MENU_PREFIX"); | ||||
|   qputenv("XDG_MENU_PREFIX", "lxde-"); | ||||
|   menu_cache = menu_cache_lookup("applications.menu"); | ||||
|   // if(!oldenv.isEmpty())
 | ||||
|   qputenv("XDG_MENU_PREFIX", oldenv); // restore the original value if needed
 | ||||
| 
 | ||||
|   if(menu_cache) { | ||||
|     MenuCacheDir* dir = menu_cache_dup_root_dir(menu_cache); | ||||
|     menu_cache_reload_notify = menu_cache_add_reload_notify(menu_cache, _onMenuCacheReload, this); | ||||
|     if(dir) { /* content of menu is already loaded */ | ||||
|       addMenuItems(NULL, dir); | ||||
|       menu_cache_item_unref(MENU_CACHE_ITEM(dir)); | ||||
|     } | ||||
|   } | ||||
|   setModel(model_); | ||||
|   connect(selectionModel(), &QItemSelectionModel::selectionChanged, this, &AppMenuView::selectionChanged); | ||||
|   selectionModel()->select(model_->index(0, 0), QItemSelectionModel::SelectCurrent); | ||||
| } | ||||
| 
 | ||||
| AppMenuView::~AppMenuView() { | ||||
|   delete model_; | ||||
|   if(menu_cache) { | ||||
|     if(menu_cache_reload_notify) | ||||
|       menu_cache_remove_reload_notify(menu_cache, menu_cache_reload_notify); | ||||
|     menu_cache_unref(menu_cache); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| void AppMenuView::addMenuItems(QStandardItem* parentItem, MenuCacheDir* dir) { | ||||
|   GSList* l; | ||||
|   GSList* list; | ||||
|   /* Iterate over all menu items in this directory. */ | ||||
|   for(l = list = menu_cache_dir_list_children(dir); l != NULL; l = l->next) { | ||||
|     /* Get the menu item. */ | ||||
|     MenuCacheItem* menuItem = MENU_CACHE_ITEM(l->data); | ||||
|     switch(menu_cache_item_get_type(menuItem)) { | ||||
|     case MENU_CACHE_TYPE_NONE: | ||||
|     case MENU_CACHE_TYPE_SEP: | ||||
|       break; | ||||
|     case MENU_CACHE_TYPE_APP: | ||||
|     case MENU_CACHE_TYPE_DIR: { | ||||
|       AppMenuViewItem* newItem = new AppMenuViewItem(menuItem); | ||||
|       if(parentItem) | ||||
|         parentItem->insertRow(parentItem->rowCount(), newItem); | ||||
|       else | ||||
|         model_->insertRow(model_->rowCount(), newItem); | ||||
| 
 | ||||
|       if(menu_cache_item_get_type(menuItem) == MENU_CACHE_TYPE_DIR) | ||||
|         addMenuItems(newItem, MENU_CACHE_DIR(menuItem)); | ||||
|       break; | ||||
|     } | ||||
|     } | ||||
|   } | ||||
|   g_slist_free_full(list, (GDestroyNotify)menu_cache_item_unref); | ||||
| } | ||||
| 
 | ||||
| void AppMenuView::onMenuCacheReload(MenuCache* mc) { | ||||
|   MenuCacheDir* dir = menu_cache_dup_root_dir(mc); | ||||
|   model_->clear(); | ||||
|   /* FIXME: preserve original selection */ | ||||
|   if(dir) { | ||||
|     addMenuItems(NULL, dir); | ||||
|     menu_cache_item_unref(MENU_CACHE_ITEM(dir)); | ||||
|     selectionModel()->select(model_->index(0, 0), QItemSelectionModel::SelectCurrent); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| bool AppMenuView::isAppSelected() { | ||||
|   AppMenuViewItem* item = selectedItem(); | ||||
|   return (item && item->isApp()); | ||||
| } | ||||
| 
 | ||||
| AppMenuViewItem* AppMenuView::selectedItem() { | ||||
|   QModelIndexList selected = selectedIndexes(); | ||||
|   if(!selected.isEmpty()) { | ||||
|     AppMenuViewItem* item = static_cast<AppMenuViewItem*>(model_->itemFromIndex(selected.first() | ||||
|     )); | ||||
|     return item; | ||||
|   } | ||||
|   return NULL; | ||||
| } | ||||
| 
 | ||||
| GAppInfo* AppMenuView::selectedApp() { | ||||
|   const char* id = selectedAppDesktopId(); | ||||
|   return id ? G_APP_INFO(g_desktop_app_info_new(id)) : NULL; | ||||
| } | ||||
| 
 | ||||
| QByteArray AppMenuView::selectedAppDesktopFilePath() { | ||||
|   AppMenuViewItem* item = selectedItem(); | ||||
|   if(item && item->isApp()) { | ||||
|     char* path = menu_cache_item_get_file_path(item->item()); | ||||
|     QByteArray ret(path); | ||||
|     g_free(path); | ||||
|     return ret; | ||||
|   } | ||||
|   return QByteArray(); | ||||
| } | ||||
| 
 | ||||
| const char* AppMenuView::selectedAppDesktopId() { | ||||
|   AppMenuViewItem* item = selectedItem(); | ||||
|   if(item && item->isApp()) { | ||||
|     return menu_cache_item_get_id(item->item()); | ||||
|   } | ||||
|   return NULL; | ||||
| } | ||||
| 
 | ||||
| FmPath* AppMenuView::selectedAppDesktopPath() { | ||||
|   AppMenuViewItem* item = selectedItem(); | ||||
|   if(item && item->isApp()) { | ||||
|     char* mpath = menu_cache_dir_make_path(MENU_CACHE_DIR(item)); | ||||
|     FmPath* path = fm_path_new_relative(fm_path_get_apps_menu(), | ||||
|                                         mpath + 13 /* skip "/Applications" */); | ||||
|     g_free(mpath); | ||||
|     return path; | ||||
|   } | ||||
|   return NULL; | ||||
| } | ||||
| 
 | ||||
| } // namespace Fm
 | ||||
							
								
								
									
										73
									
								
								libfm-qt/appmenuview.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										73
									
								
								libfm-qt/appmenuview.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,73 @@ | ||||
| /*
 | ||||
|  * <one line to give the library's name and an idea of what it does.> | ||||
|  * Copyright (C) 2014  <copyright holder> <email> | ||||
|  * | ||||
|  * 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 | ||||
|  * | ||||
|  */ | ||||
| 
 | ||||
| #ifndef FM_APPMENUVIEW_H | ||||
| #define FM_APPMENUVIEW_H | ||||
| 
 | ||||
| #include <QTreeView> | ||||
| #include "libfmqtglobals.h" | ||||
| #include <libfm/fm.h> | ||||
| #include <menu-cache/menu-cache.h> | ||||
| 
 | ||||
| class QStandardItemModel; | ||||
| class QStandardItem; | ||||
| 
 | ||||
| namespace Fm { | ||||
|    | ||||
| class AppMenuViewItem; | ||||
| 
 | ||||
| class LIBFM_QT_API AppMenuView : public QTreeView { | ||||
|   Q_OBJECT | ||||
| public: | ||||
|   explicit AppMenuView(QWidget* parent = NULL); | ||||
|   ~AppMenuView(); | ||||
| 
 | ||||
|   GAppInfo* selectedApp(); | ||||
| 
 | ||||
|   const char* selectedAppDesktopId(); | ||||
| 
 | ||||
|   QByteArray selectedAppDesktopFilePath(); | ||||
| 
 | ||||
|   FmPath * selectedAppDesktopPath(); | ||||
| 
 | ||||
|   bool isAppSelected(); | ||||
| 
 | ||||
| Q_SIGNALS: | ||||
|   void selectionChanged(); | ||||
|    | ||||
| private: | ||||
|   void addMenuItems(QStandardItem* parentItem, MenuCacheDir* dir); | ||||
|   void onMenuCacheReload(MenuCache* mc); | ||||
|   static void _onMenuCacheReload(MenuCache* mc, gpointer user_data) { | ||||
|     static_cast<AppMenuView*>(user_data)->onMenuCacheReload(mc); | ||||
|   } | ||||
| 
 | ||||
|   AppMenuViewItem* selectedItem(); | ||||
| 
 | ||||
| private: | ||||
|   // gboolean fm_app_menu_view_is_item_app(, GtkTreeIter* it);
 | ||||
|   QStandardItemModel* model_; | ||||
|   MenuCache* menu_cache; | ||||
|   MenuCacheNotifyId menu_cache_reload_notify; | ||||
| }; | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| #endif // FM_APPMENUVIEW_H
 | ||||
							
								
								
									
										74
									
								
								libfm-qt/appmenuview_p.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										74
									
								
								libfm-qt/appmenuview_p.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,74 @@ | ||||
| /*
 | ||||
|  * <one line to give the library's name and an idea of what it does.> | ||||
|  * Copyright (C) 2014  <copyright holder> <email> | ||||
|  * | ||||
|  * 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 | ||||
|  * | ||||
|  */ | ||||
| 
 | ||||
| #ifndef FM_APPMENUVIEW_P_H | ||||
| #define FM_APPMENUVIEW_P_H | ||||
| 
 | ||||
| #include <QStandardItem> | ||||
| #include <menu-cache/menu-cache.h> | ||||
| #include "icontheme.h" | ||||
| 
 | ||||
| namespace Fm { | ||||
| 
 | ||||
| class AppMenuViewItem : public QStandardItem { | ||||
| public: | ||||
|   explicit AppMenuViewItem(MenuCacheItem* item): | ||||
|     item_(menu_cache_item_ref(item)) { | ||||
|     FmIcon* fmicon; | ||||
|     if(menu_cache_item_get_icon(item)) | ||||
|       fmicon = fm_icon_from_name(menu_cache_item_get_icon(item)); | ||||
|     else | ||||
|       fmicon = NULL; | ||||
|     setText(QString::fromUtf8(menu_cache_item_get_name(item))); | ||||
|     setEditable(false); | ||||
|     setDragEnabled(false); | ||||
|     if(fmicon) { | ||||
|       setIcon(IconTheme::icon(fmicon)); | ||||
|       fm_icon_unref(fmicon); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   ~AppMenuViewItem() { | ||||
|     menu_cache_item_unref(item_); | ||||
|   } | ||||
|    | ||||
|   MenuCacheItem* item() { | ||||
|     return item_; | ||||
|   } | ||||
|    | ||||
|   MenuCacheType type() { | ||||
| 	return menu_cache_item_get_type(item_); | ||||
|   } | ||||
|    | ||||
|   bool isApp() { | ||||
|     return type() == MENU_CACHE_TYPE_APP; | ||||
|   } | ||||
| 
 | ||||
|   bool isDir() { | ||||
|     return type() == MENU_CACHE_TYPE_DIR; | ||||
|   } | ||||
| 
 | ||||
| private: | ||||
|   MenuCacheItem* item_; | ||||
| }; | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| #endif // FM_APPMENUVIEW_P_H
 | ||||
							
								
								
									
										30
									
								
								libfm-qt/bookmarkaction.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								libfm-qt/bookmarkaction.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,30 @@ | ||||
| /*
 | ||||
| 
 | ||||
|     Copyright (C) 2013  Hong Jen Yee (PCMan) <pcman.tw@gmail.com> | ||||
| 
 | ||||
|     This program is free software; you can redistribute it and/or modify | ||||
|     it under the terms of the GNU General Public License as published by | ||||
|     the Free Software Foundation; either version 2 of the License, or | ||||
|     (at your option) any later version. | ||||
| 
 | ||||
|     This program is distributed in the hope that it will be useful, | ||||
|     but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|     GNU General Public License for more details. | ||||
| 
 | ||||
|     You should have received a copy of the GNU General Public License along | ||||
|     with this program; if not, write to the Free Software Foundation, Inc., | ||||
|     51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||||
| */ | ||||
| 
 | ||||
| 
 | ||||
| #include "bookmarkaction.h" | ||||
| 
 | ||||
| using namespace Fm; | ||||
| 
 | ||||
| BookmarkAction::BookmarkAction(FmBookmarkItem* item, QObject* parent): | ||||
|   QAction(parent), | ||||
|   item_(fm_bookmark_item_ref(item)) { | ||||
| 
 | ||||
|   setText(QString::fromUtf8(item->name)); | ||||
| } | ||||
							
								
								
									
										54
									
								
								libfm-qt/bookmarkaction.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										54
									
								
								libfm-qt/bookmarkaction.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,54 @@ | ||||
| /*
 | ||||
| 
 | ||||
|     Copyright (C) 2013  Hong Jen Yee (PCMan) <pcman.tw@gmail.com> | ||||
| 
 | ||||
|     This program is free software; you can redistribute it and/or modify | ||||
|     it under the terms of the GNU General Public License as published by | ||||
|     the Free Software Foundation; either version 2 of the License, or | ||||
|     (at your option) any later version. | ||||
| 
 | ||||
|     This program is distributed in the hope that it will be useful, | ||||
|     but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|     GNU General Public License for more details. | ||||
| 
 | ||||
|     You should have received a copy of the GNU General Public License along | ||||
|     with this program; if not, write to the Free Software Foundation, Inc., | ||||
|     51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||||
| */ | ||||
| 
 | ||||
| 
 | ||||
| #ifndef BOOKMARKACTION_H | ||||
| #define BOOKMARKACTION_H | ||||
| 
 | ||||
| #include "libfmqtglobals.h" | ||||
| #include <QAction> | ||||
| #include <libfm/fm.h> | ||||
| 
 | ||||
| namespace Fm { | ||||
| 
 | ||||
| // action used to create bookmark menu items
 | ||||
| class LIBFM_QT_API BookmarkAction : public QAction { | ||||
| public: | ||||
|   explicit BookmarkAction(FmBookmarkItem* item, QObject* parent = 0); | ||||
| 
 | ||||
|   virtual ~BookmarkAction() { | ||||
|     if(item_) | ||||
|       fm_bookmark_item_unref(item_); | ||||
|   } | ||||
| 
 | ||||
|   FmBookmarkItem* bookmark() { | ||||
|     return item_; | ||||
|   } | ||||
|    | ||||
|   FmPath* path() { | ||||
|     return item_->path; | ||||
|   } | ||||
| 
 | ||||
| private: | ||||
|   FmBookmarkItem* item_; | ||||
| }; | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| #endif // BOOKMARKACTION_H
 | ||||
							
								
								
									
										87
									
								
								libfm-qt/browsehistory.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										87
									
								
								libfm-qt/browsehistory.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,87 @@ | ||||
| /*
 | ||||
| 
 | ||||
|     Copyright (C) 2013  Hong Jen Yee (PCMan) <pcman.tw@gmail.com> | ||||
| 
 | ||||
|     This program is free software; you can redistribute it and/or modify | ||||
|     it under the terms of the GNU General Public License as published by | ||||
|     the Free Software Foundation; either version 2 of the License, or | ||||
|     (at your option) any later version. | ||||
| 
 | ||||
|     This program is distributed in the hope that it will be useful, | ||||
|     but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|     GNU General Public License for more details. | ||||
| 
 | ||||
|     You should have received a copy of the GNU General Public License along | ||||
|     with this program; if not, write to the Free Software Foundation, Inc., | ||||
|     51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||||
| */ | ||||
| 
 | ||||
| 
 | ||||
| #include "browsehistory.h" | ||||
| 
 | ||||
| using namespace Fm; | ||||
| 
 | ||||
| BrowseHistory::BrowseHistory(): | ||||
|   currentIndex_(0), | ||||
|   maxCount_(10) { | ||||
| } | ||||
| 
 | ||||
| BrowseHistory::~BrowseHistory() { | ||||
| } | ||||
| 
 | ||||
| void BrowseHistory::add(FmPath* path, int scrollPos) { | ||||
|   int lastIndex = size() - 1; | ||||
|   if(currentIndex_ < lastIndex) { | ||||
|     // if we're not at the last item, remove items after the current one.
 | ||||
|     erase(begin() + currentIndex_ + 1, end()); | ||||
|   } | ||||
| 
 | ||||
|   if(size() + 1 > maxCount_) { | ||||
|     // if there are too many items, remove the oldest one.
 | ||||
|     // FIXME: what if currentIndex_ == 0? remove the last item instead?
 | ||||
|     if(currentIndex_ == 0) | ||||
|       remove(lastIndex); | ||||
|     else { | ||||
|       remove(0); | ||||
|       --currentIndex_; | ||||
|     } | ||||
|   } | ||||
|   // add a path and current scroll position to browse history
 | ||||
|   append(BrowseHistoryItem(path, scrollPos)); | ||||
|   currentIndex_ = size() - 1; | ||||
| } | ||||
| 
 | ||||
| void BrowseHistory::setCurrentIndex(int index) { | ||||
|   if(index >= 0 && index < size()) { | ||||
|     currentIndex_ = index; | ||||
|     // FIXME: should we emit a signal for the change?
 | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| bool BrowseHistory::canBackward() const { | ||||
|   return (currentIndex_ > 0); | ||||
| } | ||||
| 
 | ||||
| int BrowseHistory::backward() { | ||||
|   if(canBackward()) | ||||
|     --currentIndex_; | ||||
|   return currentIndex_; | ||||
| } | ||||
| 
 | ||||
| bool BrowseHistory::canForward() const { | ||||
|   return (currentIndex_ + 1 < size()); | ||||
| } | ||||
| 
 | ||||
| int BrowseHistory::forward() { | ||||
|   if(canForward()) | ||||
|     ++currentIndex_; | ||||
|   return currentIndex_; | ||||
| } | ||||
| 
 | ||||
| void BrowseHistory::setMaxCount(int maxCount) { | ||||
|   maxCount_ = maxCount; | ||||
|   if(size() > maxCount) { | ||||
|     // TODO: remove some items
 | ||||
|   } | ||||
| } | ||||
							
								
								
									
										131
									
								
								libfm-qt/browsehistory.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										131
									
								
								libfm-qt/browsehistory.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,131 @@ | ||||
| /*
 | ||||
| 
 | ||||
|     Copyright (C) 2013  Hong Jen Yee (PCMan) <pcman.tw@gmail.com> | ||||
| 
 | ||||
|     This program is free software; you can redistribute it and/or modify | ||||
|     it under the terms of the GNU General Public License as published by | ||||
|     the Free Software Foundation; either version 2 of the License, or | ||||
|     (at your option) any later version. | ||||
| 
 | ||||
|     This program is distributed in the hope that it will be useful, | ||||
|     but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|     GNU General Public License for more details. | ||||
| 
 | ||||
|     You should have received a copy of the GNU General Public License along | ||||
|     with this program; if not, write to the Free Software Foundation, Inc., | ||||
|     51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||||
| */ | ||||
| 
 | ||||
| 
 | ||||
| #ifndef FM_BROWSEHISTORY_H | ||||
| #define FM_BROWSEHISTORY_H | ||||
| 
 | ||||
| #include "libfmqtglobals.h" | ||||
| #include <QVector> | ||||
| #include <libfm/fm.h> | ||||
| 
 | ||||
| namespace Fm { | ||||
| 
 | ||||
| // class used to story browsing history of folder views
 | ||||
| // We use this class to replace FmNavHistory provided by libfm since
 | ||||
| // the original Libfm API is hard to use and confusing.
 | ||||
| 
 | ||||
| class LIBFM_QT_API BrowseHistoryItem { | ||||
| public: | ||||
| 
 | ||||
|   BrowseHistoryItem(): | ||||
|     path_(NULL), | ||||
|     scrollPos_(0) { | ||||
|   } | ||||
| 
 | ||||
|   BrowseHistoryItem(FmPath* path, int scrollPos = 0): | ||||
|     path_(fm_path_ref(path)), | ||||
|     scrollPos_(scrollPos) { | ||||
|   } | ||||
| 
 | ||||
|   BrowseHistoryItem(const BrowseHistoryItem& other): | ||||
|     path_(other.path_ ? fm_path_ref(other.path_) : NULL), | ||||
|     scrollPos_(other.scrollPos_) { | ||||
|   } | ||||
| 
 | ||||
|   ~BrowseHistoryItem() { | ||||
|     if(path_) | ||||
|       fm_path_unref(path_); | ||||
|   } | ||||
| 
 | ||||
|   BrowseHistoryItem& operator=(const BrowseHistoryItem& other) { | ||||
|     if(path_) | ||||
|       fm_path_unref(path_); | ||||
|     path_ = other.path_ ? fm_path_ref(other.path_) : NULL; | ||||
|     scrollPos_ = other.scrollPos_; | ||||
|     return *this; | ||||
|   } | ||||
|    | ||||
|   FmPath* path() const { | ||||
|     return path_; | ||||
|   } | ||||
| 
 | ||||
|   int scrollPos() const { | ||||
|     return scrollPos_; | ||||
|   } | ||||
| 
 | ||||
|   void setScrollPos(int pos) { | ||||
|     scrollPos_ = pos; | ||||
|   } | ||||
| 
 | ||||
| private: | ||||
|   FmPath* path_; | ||||
|   int scrollPos_; | ||||
|   // TODO: we may need to store current selection as well. reserve room for furutre expansion.
 | ||||
|   // void* reserved1;
 | ||||
|   // void* reserved2;
 | ||||
| }; | ||||
| 
 | ||||
| class LIBFM_QT_API BrowseHistory : public QVector<BrowseHistoryItem> { | ||||
| 
 | ||||
| public: | ||||
|   BrowseHistory(); | ||||
|   virtual ~BrowseHistory(); | ||||
| 
 | ||||
|   int currentIndex() const { | ||||
|     return currentIndex_; | ||||
|   } | ||||
|   void setCurrentIndex(int index); | ||||
| 
 | ||||
|   FmPath* currentPath() const { | ||||
|     return at(currentIndex_).path(); | ||||
|   } | ||||
| 
 | ||||
|   int currentScrollPos() const { | ||||
|     return at(currentIndex_).scrollPos(); | ||||
|   } | ||||
| 
 | ||||
|   BrowseHistoryItem& currentItem() { | ||||
|     return operator[](currentIndex_); | ||||
|   } | ||||
| 
 | ||||
|   void add(FmPath* path, int scrollPos = 0); | ||||
| 
 | ||||
|   bool canForward() const; | ||||
| 
 | ||||
|   bool canBackward() const; | ||||
| 
 | ||||
|   int backward(); | ||||
| 
 | ||||
|   int forward(); | ||||
| 
 | ||||
|   int maxCount() const { | ||||
|     return maxCount_; | ||||
|   } | ||||
| 
 | ||||
|   void setMaxCount(int maxCount); | ||||
| 
 | ||||
| private: | ||||
|   int currentIndex_; | ||||
|   int maxCount_; | ||||
| }; | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| #endif // FM_BROWSEHISTORY_H
 | ||||
							
								
								
									
										74
									
								
								libfm-qt/cachedfoldermodel.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										74
									
								
								libfm-qt/cachedfoldermodel.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,74 @@ | ||||
| /*
 | ||||
|     <one line to give the library's name and an idea of what it does.> | ||||
|     Copyright (C) 2013  <copyright holder> <email> | ||||
| 
 | ||||
|     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 | ||||
| */ | ||||
| 
 | ||||
| 
 | ||||
| #include "cachedfoldermodel.h" | ||||
| 
 | ||||
| using namespace Fm; | ||||
| 
 | ||||
| static GQuark data_id = 0; | ||||
| 
 | ||||
| 
 | ||||
| CachedFolderModel::CachedFolderModel(FmFolder* folder): | ||||
|   FolderModel(), | ||||
|   refCount(1) { | ||||
| 
 | ||||
|   FolderModel::setFolder(folder); | ||||
| } | ||||
| 
 | ||||
| CachedFolderModel::~CachedFolderModel() { | ||||
|   qDebug("delete CachedFolderModel"); | ||||
| } | ||||
| 
 | ||||
| CachedFolderModel* CachedFolderModel::modelFromFolder(FmFolder* folder) { | ||||
|   CachedFolderModel* model = NULL; | ||||
|   if(!data_id) | ||||
|     data_id = g_quark_from_static_string("CachedFolderModel"); | ||||
|   gpointer qdata = g_object_get_qdata(G_OBJECT(folder), data_id); | ||||
|   model = reinterpret_cast<CachedFolderModel*>(qdata); | ||||
|   if(model) { | ||||
|     // qDebug("cache found!!");
 | ||||
|     model->ref(); | ||||
|   } | ||||
|   else { | ||||
|     model = new CachedFolderModel(folder); | ||||
|     g_object_set_qdata(G_OBJECT(folder), data_id, model); | ||||
|   } | ||||
|   return model; | ||||
| } | ||||
| 
 | ||||
| CachedFolderModel* CachedFolderModel::modelFromPath(FmPath* path) { | ||||
|   FmFolder* folder = fm_folder_from_path(path); | ||||
|   if(folder) { | ||||
|     CachedFolderModel* model = modelFromFolder(folder); | ||||
|     g_object_unref(folder); | ||||
|     return model; | ||||
|   } | ||||
|   return NULL; | ||||
| } | ||||
| 
 | ||||
| void CachedFolderModel::unref() { | ||||
|   // qDebug("unref cache");
 | ||||
|   --refCount; | ||||
|   if(refCount <= 0) { | ||||
|     g_object_set_qdata(G_OBJECT(folder()), data_id, NULL); | ||||
|     deleteLater(); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
							
								
								
									
										51
									
								
								libfm-qt/cachedfoldermodel.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								libfm-qt/cachedfoldermodel.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,51 @@ | ||||
| /*
 | ||||
|     <one line to give the library's name and an idea of what it does.> | ||||
|     Copyright (C) 2013  <copyright holder> <email> | ||||
| 
 | ||||
|     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 | ||||
| */ | ||||
| 
 | ||||
| 
 | ||||
| #ifndef FM_CACHEDFOLDERMODEL_H | ||||
| #define FM_CACHEDFOLDERMODEL_H | ||||
| 
 | ||||
| #include "libfmqtglobals.h" | ||||
| #include "foldermodel.h" | ||||
| 
 | ||||
| namespace Fm { | ||||
|    | ||||
| class LIBFM_QT_API CachedFolderModel : public FolderModel { | ||||
|   Q_OBJECT | ||||
| public: | ||||
|   CachedFolderModel(FmFolder* folder); | ||||
|   void ref() { | ||||
|     ++refCount; | ||||
|   } | ||||
|   void unref(); | ||||
| 
 | ||||
|   static CachedFolderModel* modelFromFolder(FmFolder* folder); | ||||
|   static CachedFolderModel* modelFromPath(FmPath* path); | ||||
| 
 | ||||
| private: | ||||
|   virtual ~CachedFolderModel(); | ||||
|   void setFolder(FmFolder* folder); | ||||
| private: | ||||
|   int refCount; | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| #endif // FM_CACHEDFOLDERMODEL_H
 | ||||
							
								
								
									
										51
									
								
								libfm-qt/colorbutton.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								libfm-qt/colorbutton.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,51 @@ | ||||
| /*
 | ||||
| 
 | ||||
|     Copyright (C) 2013  Hong Jen Yee (PCMan) <pcman.tw@gmail.com> | ||||
| 
 | ||||
|     This program is free software; you can redistribute it and/or modify | ||||
|     it under the terms of the GNU General Public License as published by | ||||
|     the Free Software Foundation; either version 2 of the License, or | ||||
|     (at your option) any later version. | ||||
| 
 | ||||
|     This program is distributed in the hope that it will be useful, | ||||
|     but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|     GNU General Public License for more details. | ||||
| 
 | ||||
|     You should have received a copy of the GNU General Public License along | ||||
|     with this program; if not, write to the Free Software Foundation, Inc., | ||||
|     51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||||
| */ | ||||
| 
 | ||||
| 
 | ||||
| #include "colorbutton.h" | ||||
| #include <QColorDialog> | ||||
| 
 | ||||
| using namespace Fm; | ||||
| 
 | ||||
| ColorButton::ColorButton(QWidget* parent): QPushButton(parent) { | ||||
|   connect(this, &QPushButton::clicked, this, &ColorButton::onClicked); | ||||
| } | ||||
| 
 | ||||
| ColorButton::~ColorButton() { | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| void ColorButton::onClicked() { | ||||
|   QColorDialog dlg(color_); | ||||
|   if(dlg.exec() == QDialog::Accepted) { | ||||
|     setColor(dlg.selectedColor()); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| void ColorButton::setColor(const QColor& color) { | ||||
|   if(color != color_) { | ||||
|     color_ = color; | ||||
|     // use qss instead of QPalette to set the background color
 | ||||
|     // otherwise, this won't work when using the gtk style.
 | ||||
|     QString style = QString("QPushButton{background-color:%1;}").arg(color.name()); | ||||
|     setStyleSheet(style); | ||||
|     Q_EMIT changed(); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
							
								
								
									
										55
									
								
								libfm-qt/colorbutton.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										55
									
								
								libfm-qt/colorbutton.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,55 @@ | ||||
| /*
 | ||||
| 
 | ||||
|     Copyright (C) 2013  Hong Jen Yee (PCMan) <pcman.tw@gmail.com> | ||||
| 
 | ||||
|     This program is free software; you can redistribute it and/or modify | ||||
|     it under the terms of the GNU General Public License as published by | ||||
|     the Free Software Foundation; either version 2 of the License, or | ||||
|     (at your option) any later version. | ||||
| 
 | ||||
|     This program is distributed in the hope that it will be useful, | ||||
|     but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|     GNU General Public License for more details. | ||||
| 
 | ||||
|     You should have received a copy of the GNU General Public License along | ||||
|     with this program; if not, write to the Free Software Foundation, Inc., | ||||
|     51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||||
| */ | ||||
| 
 | ||||
| 
 | ||||
| #ifndef FM_COLORBUTTON_H | ||||
| #define FM_COLORBUTTON_H | ||||
| 
 | ||||
| #include "libfmqtglobals.h" | ||||
| #include <QPushButton> | ||||
| #include <QColor> | ||||
| 
 | ||||
| namespace Fm { | ||||
| 
 | ||||
| class LIBFM_QT_API ColorButton : public QPushButton { | ||||
| Q_OBJECT | ||||
| 
 | ||||
| public: | ||||
|   explicit ColorButton(QWidget* parent = 0); | ||||
|   virtual ~ColorButton(); | ||||
| 
 | ||||
|   void setColor(const QColor&); | ||||
| 
 | ||||
|   QColor color() const { | ||||
|     return color_; | ||||
|   } | ||||
|    | ||||
| Q_SIGNALS: | ||||
|   void changed(); | ||||
| 
 | ||||
| private Q_SLOTS: | ||||
|   void onClicked(); | ||||
| 
 | ||||
| private: | ||||
|   QColor color_; | ||||
| }; | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| #endif // FM_COLORBUTTON_H
 | ||||
							
								
								
									
										205
									
								
								libfm-qt/dirtreemodel.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										205
									
								
								libfm-qt/dirtreemodel.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,205 @@ | ||||
| /*
 | ||||
|  * Copyright 2014 Hong Jen Yee (PCMan) <pcman.tw@gmail.com> | ||||
|  * | ||||
|  * This program is free software; you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU General Public License as published by | ||||
|  * the Free Software Foundation; either version 2 of the License, or | ||||
|  * (at your option) any later version. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU General Public License along | ||||
|  * with this program; if not, write to the Free Software Foundation, Inc., | ||||
|  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||||
|  * | ||||
|  */ | ||||
| 
 | ||||
| #include "dirtreemodel.h" | ||||
| #include "dirtreemodelitem.h" | ||||
| #include <QDebug> | ||||
| 
 | ||||
| namespace Fm { | ||||
| 
 | ||||
| DirTreeModel::DirTreeModel(QObject* parent): | ||||
|   showHidden_(false) { | ||||
| } | ||||
| 
 | ||||
| DirTreeModel::~DirTreeModel() { | ||||
| } | ||||
| 
 | ||||
| // QAbstractItemModel implementation
 | ||||
| 
 | ||||
| Qt::ItemFlags DirTreeModel::flags(const QModelIndex& index) const { | ||||
|   DirTreeModelItem* item = itemFromIndex(index); | ||||
|   if(item && item->isPlaceHolder()) | ||||
|     return Qt::ItemIsEnabled; | ||||
|   return QAbstractItemModel::flags(index); | ||||
| } | ||||
| 
 | ||||
| QVariant DirTreeModel::data(const QModelIndex& index, int role) const { | ||||
|   if(!index.isValid() || index.column() > 1) { | ||||
|     return QVariant(); | ||||
|   } | ||||
|   DirTreeModelItem* item = itemFromIndex(index); | ||||
|   if(item) { | ||||
|     FmFileInfo* info = item->fileInfo_; | ||||
|     switch(role) { | ||||
|     case Qt::ToolTipRole: | ||||
|       return QVariant(item->displayName_); | ||||
|     case Qt::DisplayRole: | ||||
|       return QVariant(item->displayName_); | ||||
|     case Qt::DecorationRole: | ||||
|       return QVariant(item->icon_); | ||||
|     case FileInfoRole: | ||||
|       return qVariantFromValue((void*)info); | ||||
|     } | ||||
|   } | ||||
|   return QVariant(); | ||||
| } | ||||
| 
 | ||||
| int DirTreeModel::columnCount(const QModelIndex& parent) const { | ||||
|   return 1; | ||||
| } | ||||
| 
 | ||||
| int DirTreeModel::rowCount(const QModelIndex& parent) const { | ||||
|   if(!parent.isValid()) | ||||
|     return rootItems_.count(); | ||||
|   DirTreeModelItem* item = itemFromIndex(parent); | ||||
|   if(item) | ||||
|     return item->children_.count(); | ||||
|   return 0; | ||||
| } | ||||
| 
 | ||||
| QModelIndex DirTreeModel::parent(const QModelIndex& child) const { | ||||
|   DirTreeModelItem* item = itemFromIndex(child); | ||||
|   if(item && item->parent_) { | ||||
|     item = item->parent_; // go to parent item
 | ||||
|     if(item) { | ||||
|       const QList<DirTreeModelItem*>& items = item->parent_ ? item->parent_->children_ : rootItems_; | ||||
|       int row = items.indexOf(item); // this is Q(n) and may be slow :-(
 | ||||
|       if(row >= 0) | ||||
| 	return createIndex(row, 0, (void*)item); | ||||
|     } | ||||
|   } | ||||
|   return QModelIndex(); | ||||
| } | ||||
| 
 | ||||
| QModelIndex DirTreeModel::index(int row, int column, const QModelIndex& parent) const { | ||||
|   if(row >= 0 && column >= 0 && column == 0) { | ||||
|     if(!parent.isValid()) { // root items
 | ||||
|       if(row < rootItems_.count()) { | ||||
|         const DirTreeModelItem* item = rootItems_.at(row); | ||||
|         return createIndex(row, column, (void*)item); | ||||
|       } | ||||
|     } | ||||
|     else { // child items
 | ||||
|       DirTreeModelItem* parentItem = itemFromIndex(parent); | ||||
|       if(row < parentItem->children_.count()) { | ||||
|         const DirTreeModelItem* item = parentItem->children_.at(row); | ||||
|         return createIndex(row, column, (void*)item); | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|   return QModelIndex(); // invalid index
 | ||||
| } | ||||
| 
 | ||||
| bool DirTreeModel::hasChildren(const QModelIndex& parent) const { | ||||
|   DirTreeModelItem* item = itemFromIndex(parent); | ||||
|   return item ? !item->isPlaceHolder() : true; | ||||
| } | ||||
| 
 | ||||
| QModelIndex DirTreeModel::indexFromItem(DirTreeModelItem* item) const { | ||||
|   Q_ASSERT(item); | ||||
|   const QList<DirTreeModelItem*>& items = item->parent_ ? item->parent_->children_ : rootItems_; | ||||
|   int row = items.indexOf(item); | ||||
|   if(row >= 0) | ||||
|     return createIndex(row, 0, (void*)item); | ||||
|   return QModelIndex(); | ||||
| } | ||||
| 
 | ||||
| // public APIs
 | ||||
| QModelIndex DirTreeModel::addRoot(FmFileInfo* root) { | ||||
|   DirTreeModelItem* item = new DirTreeModelItem(root, this); | ||||
|   int row = rootItems_.count(); | ||||
|   beginInsertRows(QModelIndex(), row, row); | ||||
|   item->fileInfo_ = fm_file_info_ref(root); | ||||
|   rootItems_.append(item); | ||||
|   // add_place_holder_child_item(model, item_l, NULL, FALSE);
 | ||||
|   endInsertRows(); | ||||
|   return QModelIndex(); | ||||
| } | ||||
| 
 | ||||
| DirTreeModelItem* DirTreeModel::itemFromIndex(const QModelIndex& index) const { | ||||
|   return reinterpret_cast<DirTreeModelItem*>(index.internalPointer()); | ||||
| } | ||||
| 
 | ||||
| QModelIndex DirTreeModel::indexFromPath(FmPath* path) const { | ||||
|   DirTreeModelItem* item = itemFromPath(path); | ||||
|   return item ? item->index() : QModelIndex(); | ||||
| } | ||||
| 
 | ||||
| DirTreeModelItem* DirTreeModel::itemFromPath(FmPath* path) const { | ||||
|   Q_FOREACH(DirTreeModelItem* item, rootItems_) { | ||||
|     if(item->fileInfo_ && fm_path_equal(path, fm_file_info_get_path(item->fileInfo_))) { | ||||
|       return item; | ||||
|     } | ||||
|     else { | ||||
|       DirTreeModelItem* child = item->childFromPath(path, true); | ||||
|       if(child) | ||||
| 	return child; | ||||
|     } | ||||
|   } | ||||
|   return NULL; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| void DirTreeModel::loadRow(const QModelIndex& index) { | ||||
|   DirTreeModelItem* item = itemFromIndex(index); | ||||
|   Q_ASSERT(item); | ||||
|   if(item && !item->isPlaceHolder()) | ||||
|     item->loadFolder(); | ||||
| } | ||||
| 
 | ||||
| void DirTreeModel::unloadRow(const QModelIndex& index) { | ||||
|   DirTreeModelItem* item = itemFromIndex(index); | ||||
|   if(item && !item->isPlaceHolder()) | ||||
|     item->unloadFolder(); | ||||
| } | ||||
| 
 | ||||
| bool DirTreeModel::isLoaded(const QModelIndex& index) { | ||||
|   DirTreeModelItem* item = itemFromIndex(index); | ||||
|   return item ? item->loaded_ : false; | ||||
| } | ||||
| 
 | ||||
| QIcon DirTreeModel::icon(const QModelIndex& index) { | ||||
|   DirTreeModelItem* item = itemFromIndex(index); | ||||
|   return item ? item->icon_ : QIcon(); | ||||
| } | ||||
| 
 | ||||
| FmFileInfo* DirTreeModel::fileInfo(const QModelIndex& index) { | ||||
|   DirTreeModelItem* item = itemFromIndex(index); | ||||
|   return item ? item->fileInfo_ : NULL; | ||||
| } | ||||
| 
 | ||||
| FmPath* DirTreeModel::filePath(const QModelIndex& index) { | ||||
|   DirTreeModelItem* item = itemFromIndex(index); | ||||
|   return item && item->fileInfo_ ? fm_file_info_get_path(item->fileInfo_) : NULL; | ||||
| } | ||||
| 
 | ||||
| QString DirTreeModel::dispName(const QModelIndex& index) { | ||||
|   DirTreeModelItem* item = itemFromIndex(index); | ||||
|   return item ? item->displayName_ : QString(); | ||||
| } | ||||
| 
 | ||||
| void DirTreeModel::setShowHidden(bool show_hidden) { | ||||
|   showHidden_ = show_hidden; | ||||
|   Q_FOREACH(DirTreeModelItem* item, rootItems_) { | ||||
|     item->setShowHidden(show_hidden); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| } // namespace Fm
 | ||||
							
								
								
									
										90
									
								
								libfm-qt/dirtreemodel.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										90
									
								
								libfm-qt/dirtreemodel.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,90 @@ | ||||
| /*
 | ||||
|  * <one line to give the program's name and a brief idea of what it does.> | ||||
|  * Copyright (C) 2014  <copyright holder> <email> | ||||
|  * | ||||
|  * This program is free software; you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU General Public License as published by | ||||
|  * the Free Software Foundation; either version 2 of the License, or | ||||
|  * (at your option) any later version. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU General Public License along | ||||
|  * with this program; if not, write to the Free Software Foundation, Inc., | ||||
|  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||||
|  * | ||||
|  */ | ||||
| 
 | ||||
| #ifndef FM_DIRTREEMODEL_H | ||||
| #define FM_DIRTREEMODEL_H | ||||
| 
 | ||||
| #include "libfmqtglobals.h" | ||||
| #include <QModelIndex> | ||||
| #include <QAbstractItemModel> | ||||
| #include <QIcon> | ||||
| #include <QList> | ||||
| #include <QSharedPointer> | ||||
| #include <libfm/fm.h> | ||||
| 
 | ||||
| namespace Fm { | ||||
|    | ||||
| class DirTreeModelItem; | ||||
| class DirTreeView; | ||||
| 
 | ||||
| class LIBFM_QT_API DirTreeModel : public QAbstractItemModel { | ||||
|   Q_OBJECT | ||||
| 
 | ||||
| public: | ||||
|   friend class DirTreeModelItem; // allow direct access of private members in DirTreeModelItem
 | ||||
|   friend class DirTreeView; // allow direct access of private members in DirTreeView
 | ||||
| 
 | ||||
|   enum Role { | ||||
|     FileInfoRole = Qt::UserRole | ||||
|   }; | ||||
| 
 | ||||
|   explicit DirTreeModel(QObject* parent); | ||||
|   ~DirTreeModel(); | ||||
| 
 | ||||
|   QModelIndex addRoot(FmFileInfo* root); | ||||
|   void loadRow(const QModelIndex& index); | ||||
|   void unloadRow(const QModelIndex& index); | ||||
| 
 | ||||
|   bool isLoaded(const QModelIndex& index); | ||||
|   QIcon icon(const QModelIndex& index); | ||||
|   FmFileInfo* fileInfo(const QModelIndex& index); | ||||
|   FmPath* filePath(const QModelIndex& index); | ||||
|   QString dispName(const QModelIndex& index); | ||||
| 
 | ||||
|   void setShowHidden(bool show_hidden); | ||||
|   bool showHidden() const { | ||||
|     return showHidden_; | ||||
|   } | ||||
| 
 | ||||
|   QModelIndex indexFromPath(FmPath* path) const; | ||||
|    | ||||
|   virtual Qt::ItemFlags flags(const QModelIndex& index) const; | ||||
|   virtual QVariant data(const QModelIndex& index, int role) const; | ||||
|   virtual int columnCount(const QModelIndex& parent) const; | ||||
|   virtual int rowCount(const QModelIndex& parent) const; | ||||
|   virtual QModelIndex parent(const QModelIndex& child) const; | ||||
|   virtual QModelIndex index(int row, int column, const QModelIndex& parent) const; | ||||
|   virtual bool hasChildren(const QModelIndex& parent = QModelIndex()) const; | ||||
| 
 | ||||
| private: | ||||
|   DirTreeModelItem* itemFromPath(FmPath* path) const; | ||||
|   DirTreeModelItem* itemFromIndex(const QModelIndex& index) const; | ||||
|   QModelIndex indexFromItem(DirTreeModelItem* item) const; | ||||
| 
 | ||||
| Q_SIGNALS: | ||||
|   void rowLoaded(const QModelIndex& index); | ||||
| 
 | ||||
| private: | ||||
|   bool showHidden_; | ||||
|   QList<DirTreeModelItem*> rootItems_; | ||||
| }; | ||||
| } | ||||
| 
 | ||||
| #endif // FM_DIRTREEMODEL_H
 | ||||
							
								
								
									
										340
									
								
								libfm-qt/dirtreemodelitem.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										340
									
								
								libfm-qt/dirtreemodelitem.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,340 @@ | ||||
| /*
 | ||||
|  * <one line to give the program's name and a brief idea of what it does.> | ||||
|  * Copyright (C) 2014  <copyright holder> <email> | ||||
|  * | ||||
|  * This program is free software; you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU General Public License as published by | ||||
|  * the Free Software Foundation; either version 2 of the License, or | ||||
|  * (at your option) any later version. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU General Public License along | ||||
|  * with this program; if not, write to the Free Software Foundation, Inc., | ||||
|  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||||
|  * | ||||
|  */ | ||||
| 
 | ||||
| #include "dirtreemodelitem.h" | ||||
| #include "dirtreemodel.h" | ||||
| #include "icontheme.h" | ||||
| #include <QDebug> | ||||
| 
 | ||||
| namespace Fm { | ||||
| 
 | ||||
| DirTreeModelItem::DirTreeModelItem(): | ||||
|   model_(NULL), | ||||
|   folder_(NULL), | ||||
|   expanded_(false), | ||||
|   loaded_(false), | ||||
|   fileInfo_(NULL), | ||||
|   placeHolderChild_(NULL), | ||||
|   parent_(NULL) { | ||||
| } | ||||
| 
 | ||||
| DirTreeModelItem::DirTreeModelItem(FmFileInfo* info, DirTreeModel* model, DirTreeModelItem* parent): | ||||
|   model_(model), | ||||
|   folder_(NULL), | ||||
|   expanded_(false), | ||||
|   loaded_(false), | ||||
|   fileInfo_(fm_file_info_ref(info)), | ||||
|   displayName_(QString::fromUtf8(fm_file_info_get_disp_name(info))), | ||||
|   icon_(IconTheme::icon(fm_file_info_get_icon(info))), | ||||
|   placeHolderChild_(NULL), | ||||
|   parent_(parent) { | ||||
| 
 | ||||
|   if(info) | ||||
|     addPlaceHolderChild(); | ||||
| } | ||||
| 
 | ||||
| DirTreeModelItem::~DirTreeModelItem() { | ||||
|   if(fileInfo_) | ||||
|     fm_file_info_unref(fileInfo_); | ||||
| 
 | ||||
|   if(folder_) | ||||
|     freeFolder(); | ||||
| 
 | ||||
|   // delete child items if needed
 | ||||
|   if(!children_.isEmpty()) { | ||||
|     Q_FOREACH(DirTreeModelItem* item, children_) { | ||||
|       delete item; | ||||
|     } | ||||
|   } | ||||
|   if(!hiddenChildren_.isEmpty()) { | ||||
|     Q_FOREACH(DirTreeModelItem* item, hiddenChildren_) { | ||||
|       delete item; | ||||
|     } | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| void DirTreeModelItem::addPlaceHolderChild() { | ||||
|   placeHolderChild_ = new DirTreeModelItem(); | ||||
|   placeHolderChild_->parent_ = this; | ||||
|   placeHolderChild_->model_ = model_; | ||||
|   placeHolderChild_->displayName_ = DirTreeModel::tr("Loading..."); | ||||
|   children_.append(placeHolderChild_); | ||||
| } | ||||
| 
 | ||||
| void DirTreeModelItem::freeFolder() { | ||||
|   if(folder_) { | ||||
|     g_signal_handlers_disconnect_by_func(folder_, gpointer(onFolderFinishLoading), this); | ||||
|     g_signal_handlers_disconnect_by_func(folder_, gpointer(onFolderFilesAdded), this); | ||||
|     g_signal_handlers_disconnect_by_func(folder_, gpointer(onFolderFilesRemoved), this); | ||||
|     g_signal_handlers_disconnect_by_func(folder_, gpointer(onFolderFilesChanged), this); | ||||
|     g_object_unref(folder_); | ||||
|     folder_ = NULL; | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| void DirTreeModelItem::loadFolder() { | ||||
|   if(!expanded_) { | ||||
|     /* dynamically load content of the folder. */ | ||||
|     folder_ = fm_folder_from_path(fm_file_info_get_path(fileInfo_)); | ||||
|     /* g_debug("fm_dir_tree_model_load_row()"); */ | ||||
|     /* associate the data with loaded handler */ | ||||
|     g_signal_connect(folder_, "finish-loading", G_CALLBACK(onFolderFinishLoading), this); | ||||
|     g_signal_connect(folder_, "files-added", G_CALLBACK(onFolderFilesAdded), this); | ||||
|     g_signal_connect(folder_, "files-removed", G_CALLBACK(onFolderFilesRemoved), this); | ||||
|     g_signal_connect(folder_, "files-changed", G_CALLBACK(onFolderFilesChanged), this); | ||||
| 
 | ||||
|     /* set 'expanded' flag beforehand as callback may check it */ | ||||
|     expanded_ = true; | ||||
|     /* if the folder is already loaded, call "loaded" handler ourselves */ | ||||
|     if(fm_folder_is_loaded(folder_)) { // already loaded
 | ||||
|       GList* file_l; | ||||
|       FmFileInfoList* files = fm_folder_get_files(folder_); | ||||
|       for(file_l = fm_file_info_list_peek_head_link(files); file_l; file_l = file_l->next) { | ||||
|         FmFileInfo* fi = FM_FILE_INFO(file_l->data); | ||||
|         if(fm_file_info_is_dir(fi)) { | ||||
|           insertFileInfo(fi); | ||||
|         } | ||||
|       } | ||||
|       onFolderFinishLoading(folder_, this); | ||||
|     } | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| void DirTreeModelItem::unloadFolder() { | ||||
|   if(expanded_) { /* do some cleanup */ | ||||
|     /* remove all children, and replace them with a dummy child
 | ||||
|       * item to keep expander in the tree view around. */ | ||||
| 
 | ||||
|     // delete all visible child items
 | ||||
|     model_->beginRemoveRows(index(), 0, children_.count() - 1); | ||||
|     if(!children_.isEmpty()) { | ||||
|       Q_FOREACH(DirTreeModelItem* item, children_) { | ||||
|         delete item; | ||||
|       } | ||||
|       children_.clear(); | ||||
|     } | ||||
|     model_->endRemoveRows(); | ||||
| 
 | ||||
|     // remove hidden children
 | ||||
|     if(!hiddenChildren_.isEmpty()) { | ||||
|       Q_FOREACH(DirTreeModelItem* item, hiddenChildren_) { | ||||
|         delete item; | ||||
|       } | ||||
|       hiddenChildren_.clear(); | ||||
|     } | ||||
| 
 | ||||
|     /* now, we have no child since all child items are removed.
 | ||||
|      * So we add a place holder child item to keep the expander around. */ | ||||
|     addPlaceHolderChild(); | ||||
|     /* deactivate folder since it will be reactivated on expand */ | ||||
|     freeFolder(); | ||||
|     expanded_ = false; | ||||
|     loaded_ = false; | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| QModelIndex DirTreeModelItem::index() { | ||||
|   Q_ASSERT(model_); | ||||
|   return model_->indexFromItem(this); | ||||
| } | ||||
| 
 | ||||
| /* Add file info to parent node to proper position.
 | ||||
|  * GtkTreePath tp is the tree path of parent node. */ | ||||
| DirTreeModelItem* DirTreeModelItem::insertFileInfo(FmFileInfo* fi) { | ||||
|   // qDebug() << "insertFileInfo: " << fm_file_info_get_disp_name(fi);
 | ||||
|   DirTreeModelItem* item = new DirTreeModelItem(fi, model_); | ||||
|   insertItem(item); | ||||
|   return item; | ||||
| } | ||||
| 
 | ||||
| // find a good position to insert the new item
 | ||||
| int DirTreeModelItem::insertItem(DirTreeModelItem* newItem) { | ||||
|   if(model_->showHidden() || !newItem->fileInfo_ || !fm_file_info_is_hidden(newItem->fileInfo_)) { | ||||
|     const char* new_key = fm_file_info_get_collate_key(newItem->fileInfo_); | ||||
|     int pos = 0; | ||||
|     QList<DirTreeModelItem*>::iterator it; | ||||
|     for(it = children_.begin(); it != children_.end(); ++it) { | ||||
|       DirTreeModelItem* child = *it; | ||||
|       if(G_UNLIKELY(!child->fileInfo_)) | ||||
| 	continue; | ||||
|       const char* key = fm_file_info_get_collate_key(child->fileInfo_); | ||||
|       if(strcmp(new_key, key) <= 0) | ||||
| 	break; | ||||
|       ++pos; | ||||
|     } | ||||
|     // inform the world that we're about to insert the item
 | ||||
|     model_->beginInsertRows(index(), pos, pos); | ||||
|     newItem->parent_ = this; | ||||
|     children_.insert(it, newItem); | ||||
|     model_->endInsertRows(); | ||||
|     return pos; | ||||
|   } | ||||
|   else { // hidden folder
 | ||||
|     hiddenChildren_.append(newItem); | ||||
|   } | ||||
|   return -1; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| // FmFolder signal handlers
 | ||||
| 
 | ||||
| // static
 | ||||
| void DirTreeModelItem::onFolderFinishLoading(FmFolder* folder, gpointer user_data) { | ||||
|   DirTreeModelItem* _this = (DirTreeModelItem*)user_data; | ||||
|   DirTreeModel* model = _this->model_; | ||||
|   /* set 'loaded' flag beforehand as callback may check it */ | ||||
|   _this->loaded_ = true; | ||||
|   QModelIndex index = _this->index(); | ||||
| qDebug() << "folder loaded"; | ||||
|   // remove the placeholder child if needed
 | ||||
|   if(_this->children_.count() == 1) { // we have no other child other than the place holder item, leave it
 | ||||
|     _this->placeHolderChild_->displayName_ = DirTreeModel::tr("<No sub folders>"); | ||||
|     QModelIndex placeHolderIndex = _this->placeHolderChild_->index(); | ||||
|     // qDebug() << "placeHolderIndex: "<<placeHolderIndex;
 | ||||
|     Q_EMIT model->dataChanged(placeHolderIndex, placeHolderIndex); | ||||
|   } | ||||
|   else { | ||||
|     int pos = _this->children_.indexOf(_this->placeHolderChild_); | ||||
|     model->beginRemoveRows(index, pos, pos);  | ||||
|     _this->children_.removeAt(pos); | ||||
|     delete _this->placeHolderChild_; | ||||
|     model->endRemoveRows();  | ||||
|     _this->placeHolderChild_ = NULL; | ||||
|   } | ||||
| 
 | ||||
|   Q_EMIT model->rowLoaded(index); | ||||
| } | ||||
| 
 | ||||
| // static
 | ||||
| void DirTreeModelItem::onFolderFilesAdded(FmFolder* folder, GSList* files, gpointer user_data) { | ||||
|   GSList* l; | ||||
|   DirTreeModelItem* _this = (DirTreeModelItem*)user_data; | ||||
|   DirTreeModel* model = _this->model_; | ||||
|   for(l = files; l; l = l->next) { | ||||
|     FmFileInfo* fi = FM_FILE_INFO(l->data); | ||||
|     if(fm_file_info_is_dir(fi)) { /* FIXME: maybe adding files can be allowed later */ | ||||
|       /* Ideally FmFolder should not emit files-added signals for files that
 | ||||
|        * already exists. So there is no need to check for duplication here. */ | ||||
|       _this->insertFileInfo(fi); | ||||
|     } | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| // static
 | ||||
| void DirTreeModelItem::onFolderFilesRemoved(FmFolder* folder, GSList* files, gpointer user_data) { | ||||
|   DirTreeModelItem* _this = (DirTreeModelItem*)user_data; | ||||
|   DirTreeModel* model = _this->model_; | ||||
| 
 | ||||
|   for(GSList* l = files; l; l = l->next) { | ||||
|     FmFileInfo* fi = FM_FILE_INFO(l->data); | ||||
|     int pos; | ||||
|     DirTreeModelItem* child  = _this->childFromName(fm_file_info_get_name(fi), &pos); | ||||
|     if(child) { | ||||
|       model->beginRemoveRows(_this->index(), pos, pos); | ||||
|       _this->children_.removeAt(pos); | ||||
|       delete child; | ||||
|       model->endRemoveRows(); | ||||
|     } | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| // static
 | ||||
| void DirTreeModelItem::onFolderFilesChanged(FmFolder* folder, GSList* files, gpointer user_data) { | ||||
|   DirTreeModelItem* _this = (DirTreeModelItem*)user_data; | ||||
|   DirTreeModel* model = _this->model_; | ||||
| 
 | ||||
|   for(GSList* l = files; l; l = l->next) { | ||||
|     FmFileInfo* changedFile = FM_FILE_INFO(l->data); | ||||
|     int pos; | ||||
|     DirTreeModelItem* child = _this->childFromName(fm_file_info_get_name(changedFile), &pos); | ||||
|     if(child) { | ||||
|       QModelIndex childIndex = child->index(); | ||||
|       Q_EMIT model->dataChanged(childIndex, childIndex); | ||||
|     } | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| DirTreeModelItem* DirTreeModelItem::childFromName(const char* utf8_name, int* pos) { | ||||
|   int i = 0; | ||||
|   Q_FOREACH(DirTreeModelItem* item, children_) { | ||||
|     if(item->fileInfo_ && strcmp(fm_file_info_get_name(item->fileInfo_), utf8_name) == 0) { | ||||
|       if(pos) | ||||
|         *pos = i; | ||||
|       return item; | ||||
|     } | ||||
|     ++i; | ||||
|   } | ||||
|   return NULL; | ||||
| } | ||||
| 
 | ||||
| DirTreeModelItem* DirTreeModelItem::childFromPath(FmPath* path, bool recursive) const { | ||||
|   Q_ASSERT(path != NULL); | ||||
| 
 | ||||
|   Q_FOREACH(DirTreeModelItem* item, children_) { | ||||
|     // if(item->fileInfo_)
 | ||||
|     //  qDebug() << "child: " << QString::fromUtf8(fm_file_info_get_disp_name(item->fileInfo_));
 | ||||
|     if(item->fileInfo_ && fm_path_equal(fm_file_info_get_path(item->fileInfo_), path)) { | ||||
|       return item; | ||||
|     } | ||||
|     else if(recursive) { | ||||
|       DirTreeModelItem* child = item->childFromPath(path, true); | ||||
|       if(child) | ||||
| 	return child; | ||||
|     } | ||||
|   } | ||||
|   return NULL; | ||||
| } | ||||
| 
 | ||||
| void DirTreeModelItem::setShowHidden(bool show) { | ||||
|   if(show) { | ||||
|     // move all hidden children to visible list
 | ||||
|     Q_FOREACH(DirTreeModelItem* item, hiddenChildren_) { | ||||
|       insertItem(item); | ||||
|     } | ||||
|     hiddenChildren_.clear(); | ||||
|   } | ||||
|   else { // hide hidden folders
 | ||||
|     QModelIndex _index = index(); | ||||
|     QList<DirTreeModelItem*>::iterator it, next; | ||||
|     int pos = 0; | ||||
|     for(it = children_.begin(); it != children_.end(); ++pos) { | ||||
|       DirTreeModelItem* item = *it; | ||||
|       next = it + 1; | ||||
|       if(item->fileInfo_) { | ||||
|         if(fm_file_info_is_hidden(item->fileInfo_)) { // hidden folder
 | ||||
|           // remove from the model and add to the hiddenChildren_ list
 | ||||
|           model_->beginRemoveRows(_index, pos, pos); | ||||
|           children_.erase(it); | ||||
|           hiddenChildren_.append(item); | ||||
|           model_->endRemoveRows(); | ||||
|         } | ||||
|         else { // visible folder, recursively filter its children
 | ||||
|           item->setShowHidden(show); | ||||
|         } | ||||
|       } | ||||
|       it = next; | ||||
|     } | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| } // namespace Fm
 | ||||
| 
 | ||||
							
								
								
									
										84
									
								
								libfm-qt/dirtreemodelitem.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										84
									
								
								libfm-qt/dirtreemodelitem.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,84 @@ | ||||
| /*
 | ||||
|  * <one line to give the program's name and a brief idea of what it does.> | ||||
|  * Copyright (C) 2014  <copyright holder> <email> | ||||
|  * | ||||
|  * This program is free software; you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU General Public License as published by | ||||
|  * the Free Software Foundation; either version 2 of the License, or | ||||
|  * (at your option) any later version. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU General Public License along | ||||
|  * with this program; if not, write to the Free Software Foundation, Inc., | ||||
|  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||||
|  * | ||||
|  */ | ||||
| 
 | ||||
| #ifndef FM_DIRTREEMODELITEM_H | ||||
| #define FM_DIRTREEMODELITEM_H | ||||
| 
 | ||||
| #include "libfmqtglobals.h" | ||||
| #include <libfm/fm.h> | ||||
| #include <QIcon> | ||||
| #include <QList> | ||||
| #include <QModelIndex> | ||||
| 
 | ||||
| namespace Fm { | ||||
| 
 | ||||
| class DirTreeModel; | ||||
| class DirTreeView; | ||||
| 
 | ||||
| class LIBFM_QT_API DirTreeModelItem { | ||||
| public: | ||||
|   friend class DirTreeModel; // allow direct access of private members in DirTreeModel
 | ||||
|   friend class DirTreeView; // allow direct access of private members in DirTreeView
 | ||||
| 
 | ||||
|   explicit DirTreeModelItem(); | ||||
|   explicit DirTreeModelItem(FmFileInfo* info, DirTreeModel* model, DirTreeModelItem* parent = NULL); | ||||
|   ~DirTreeModelItem(); | ||||
| 
 | ||||
|   void loadFolder(); | ||||
|   void unloadFolder(); | ||||
|    | ||||
|   bool isPlaceHolder() { | ||||
|     return (fileInfo_ == NULL); | ||||
|   } | ||||
| 
 | ||||
|   void setShowHidden(bool show); | ||||
| 
 | ||||
| private: | ||||
|   void freeFolder(); | ||||
|   void addPlaceHolderChild(); | ||||
|   DirTreeModelItem* childFromName(const char* utf8_name, int* pos); | ||||
|   DirTreeModelItem* childFromPath(FmPath* path, bool recursive) const; | ||||
| 
 | ||||
|   DirTreeModelItem* insertFileInfo(FmFileInfo* fi); | ||||
|   int insertItem(Fm::DirTreeModelItem* newItem); | ||||
|   QModelIndex index(); | ||||
| 
 | ||||
|   static void onFolderFinishLoading(FmFolder* folder, gpointer user_data); | ||||
|   static void onFolderFilesAdded(FmFolder* folder, GSList* files, gpointer user_data); | ||||
|   static void onFolderFilesRemoved(FmFolder* folder, GSList* files, gpointer user_data); | ||||
|   static void onFolderFilesChanged(FmFolder* folder, GSList* files, gpointer user_data); | ||||
| 
 | ||||
| private: | ||||
|   FmFileInfo* fileInfo_; | ||||
|   FmFolder* folder_; | ||||
|   QString displayName_ ; | ||||
|   QIcon icon_; | ||||
|   bool expanded_; | ||||
|   bool loaded_; | ||||
|   DirTreeModelItem* parent_; | ||||
|   DirTreeModelItem* placeHolderChild_; | ||||
|   QList<DirTreeModelItem*> children_; | ||||
|   QList<DirTreeModelItem*> hiddenChildren_; | ||||
|   DirTreeModel* model_; | ||||
| }; | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| #endif // FM_DIRTREEMODELITEM_H
 | ||||
							
								
								
									
										214
									
								
								libfm-qt/dirtreeview.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										214
									
								
								libfm-qt/dirtreeview.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,214 @@ | ||||
| /*
 | ||||
|  * <one line to give the program's name and a brief idea of what it does.> | ||||
|  * Copyright (C) 2014  <copyright holder> <email> | ||||
|  * | ||||
|  * This program is free software; you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU General Public License as published by | ||||
|  * the Free Software Foundation; either version 2 of the License, or | ||||
|  * (at your option) any later version. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU General Public License along | ||||
|  * with this program; if not, write to the Free Software Foundation, Inc., | ||||
|  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||||
|  * | ||||
|  */ | ||||
| 
 | ||||
| #include "dirtreeview.h" | ||||
| #include <QHeaderView> | ||||
| #include <QDebug> | ||||
| #include <QItemSelection> | ||||
| #include <QGuiApplication> | ||||
| #include "dirtreemodel.h" | ||||
| #include "dirtreemodelitem.h" | ||||
| 
 | ||||
| using namespace Fm; | ||||
| 
 | ||||
| DirTreeView::DirTreeView(QWidget* parent): | ||||
|   currentExpandingItem_(NULL), | ||||
|   currentPath_(NULL) { | ||||
| 
 | ||||
|   setSelectionMode(QAbstractItemView::SingleSelection); | ||||
|   setHeaderHidden(true); | ||||
|   setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded); | ||||
|   header()->setStretchLastSection(false); | ||||
| 
 | ||||
|   connect(this, &DirTreeView::collapsed, this, &DirTreeView::onCollapsed); | ||||
|   connect(this, &DirTreeView::expanded, this, &DirTreeView::onExpanded); | ||||
| } | ||||
| 
 | ||||
| DirTreeView::~DirTreeView() { | ||||
|   if(currentPath_) | ||||
|     fm_path_unref(currentPath_); | ||||
| } | ||||
| 
 | ||||
| void DirTreeView::cancelPendingChdir() { | ||||
|   if(!pathsToExpand_.isEmpty()) { | ||||
|     pathsToExpand_.clear(); | ||||
|     if(!currentExpandingItem_) | ||||
|       return; | ||||
|     DirTreeModel* _model = static_cast<DirTreeModel*>(model()); | ||||
|     disconnect(_model, &DirTreeModel::rowLoaded, this, &DirTreeView::onRowLoaded); | ||||
|     currentExpandingItem_ = NULL; | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| void DirTreeView::expandPendingPath() { | ||||
|   if(pathsToExpand_.isEmpty()) | ||||
|     return; | ||||
| 
 | ||||
|   FmPath* path = pathsToExpand_.first().data(); | ||||
|   // qDebug() << "expanding: " << Path(path).displayBasename();
 | ||||
|   DirTreeModel* _model = static_cast<DirTreeModel*>(model()); | ||||
|   DirTreeModelItem* item = _model->itemFromPath(path); | ||||
|   // qDebug() << "findItem: " << item;
 | ||||
|   if(item) { | ||||
|     currentExpandingItem_ = item; | ||||
|     connect(_model, &DirTreeModel::rowLoaded, this, &DirTreeView::onRowLoaded); | ||||
|     if(item->loaded_) { // the node is already loaded
 | ||||
|       onRowLoaded(item->index()); | ||||
|     } | ||||
|     else { | ||||
|       // _model->loadRow(item->index());
 | ||||
|       item->loadFolder(); | ||||
|     } | ||||
|   } | ||||
|   else { | ||||
|     selectionModel()->clear(); | ||||
|     /* since we never get it loaded we need to update cwd here */ | ||||
|     if(currentPath_) | ||||
|       fm_path_unref(currentPath_); | ||||
|     currentPath_ = fm_path_ref(path); | ||||
| 
 | ||||
|     cancelPendingChdir(); // FIXME: is this correct? this is not done in the gtk+ version of libfm.
 | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| void DirTreeView::onRowLoaded(const QModelIndex& index) { | ||||
|   DirTreeModel* _model = static_cast<DirTreeModel*>(model()); | ||||
|   if(!currentExpandingItem_) | ||||
|     return; | ||||
|   if(currentExpandingItem_ != _model->itemFromIndex(index)) { | ||||
|     return; | ||||
|   } | ||||
|   /* disconnect the handler since we only need it once */ | ||||
|   disconnect(_model, &DirTreeModel::rowLoaded, this, &DirTreeView::onRowLoaded); | ||||
| 
 | ||||
|   DirTreeModelItem* item = _model->itemFromIndex(index); | ||||
|   // qDebug() << "row loaded: " << item->displayName_;
 | ||||
|   /* after the folder is loaded, the files should have been added to
 | ||||
|     * the tree model */ | ||||
|   expand(index); | ||||
| 
 | ||||
|   /* remove the expanded path from pending list */ | ||||
|   pathsToExpand_.removeFirst(); | ||||
|   if(pathsToExpand_.isEmpty()) {  /* this is the last one and we're done, select the item */ | ||||
|     // qDebug() << "Done!";
 | ||||
|     selectionModel()->select(index, QItemSelectionModel::SelectCurrent|QItemSelectionModel::Clear); | ||||
|     scrollTo(index, QAbstractItemView::EnsureVisible); | ||||
|   } | ||||
|   else { /* continue expanding next pending path */ | ||||
|     expandPendingPath(); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| void DirTreeView::setCurrentPath(FmPath* path) { | ||||
|   DirTreeModel* _model = static_cast<DirTreeModel*>(model()); | ||||
|   if(!_model) | ||||
|     return; | ||||
|   int rowCount = _model->rowCount(QModelIndex()); | ||||
|   if(rowCount == 0 || fm_path_equal(currentPath_, path)) | ||||
|     return; | ||||
| 
 | ||||
|   if(currentPath_) | ||||
|     fm_path_unref(currentPath_); | ||||
|   currentPath_ = fm_path_ref(path); | ||||
| 
 | ||||
|   // NOTE: The content of each node is loaded on demand dynamically.
 | ||||
|   // So, when we ask for a chdir operation, some nodes do not exists yet.
 | ||||
|   // We have to wait for the loading of child nodes and continue the
 | ||||
|   // pending chdir operation after the child nodes become available.
 | ||||
| 
 | ||||
|   // cancel previous pending tree expansion
 | ||||
|   cancelPendingChdir(); | ||||
| 
 | ||||
|   /* find a root item containing this path */ | ||||
|   FmPath* root; | ||||
|   for(int row = 0; row < rowCount; ++row) { | ||||
|     QModelIndex index = _model->index(row, 0, QModelIndex()); | ||||
|     root = _model->filePath(index); | ||||
|     if(fm_path_has_prefix(path, root)) | ||||
|       break; | ||||
|     root = NULL; | ||||
|   } | ||||
| 
 | ||||
|   if(root) { /* root item is found */ | ||||
|     do { /* add path elements one by one to a list */ | ||||
|       pathsToExpand_.prepend(path); | ||||
|       // qDebug() << "prepend path: " << Path(path).displayBasename();
 | ||||
|       if(fm_path_equal(path, root)) | ||||
|         break; | ||||
|       path = fm_path_get_parent(path); | ||||
|     } | ||||
|     while(path); | ||||
| 
 | ||||
|     expandPendingPath(); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| void DirTreeView::setModel(QAbstractItemModel* model) { | ||||
|   Q_ASSERT(model->inherits("Fm::DirTreeModel")); | ||||
| 
 | ||||
|   if(!pathsToExpand_.isEmpty()) // if a chdir request is in progress, cancel it
 | ||||
|     cancelPendingChdir(); | ||||
| 
 | ||||
|   QTreeView::setModel(model); | ||||
|   header()->setSectionResizeMode(0, QHeaderView::ResizeToContents); | ||||
|   connect(selectionModel(), &QItemSelectionModel::selectionChanged, this, &DirTreeView::onSelectionChanged); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| void DirTreeView::contextMenuEvent(QContextMenuEvent* event) { | ||||
|   QAbstractScrollArea::contextMenuEvent(event); | ||||
| } | ||||
| 
 | ||||
| void DirTreeView::onCollapsed(const QModelIndex& index) { | ||||
|   DirTreeModel* treeModel = static_cast<DirTreeModel*>(model()); | ||||
|   if(treeModel) { | ||||
|     treeModel->unloadRow(index); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| void DirTreeView::onExpanded(const QModelIndex& index) { | ||||
|   DirTreeModel* treeModel = static_cast<DirTreeModel*>(model()); | ||||
|   if(treeModel) { | ||||
|     treeModel->loadRow(index); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| void DirTreeView::onSelectionChanged(const QItemSelection & selected, const QItemSelection & deselected) { | ||||
|   if(!selected.isEmpty()) { | ||||
|     QModelIndex index = selected.first().topLeft(); | ||||
|     DirTreeModel* _model = static_cast<DirTreeModel*>(model()); | ||||
|     FmPath* path = _model->filePath(index); | ||||
|     if(path && currentPath_ && fm_path_equal(path, currentPath_)) | ||||
|       return; | ||||
|     cancelPendingChdir(); | ||||
|     if(!path) | ||||
|       return; | ||||
|     if(currentPath_) | ||||
|       fm_path_unref(currentPath_); | ||||
|     currentPath_ = fm_path_ref(path); | ||||
|      | ||||
|     // FIXME: use enums for type rather than hard-coded values 0 or 1
 | ||||
|     int type = 0; | ||||
|     if(QGuiApplication::mouseButtons() & Qt::MiddleButton) | ||||
|       type = 1; | ||||
|     Q_EMIT chdirRequested(type, path); | ||||
|   } | ||||
| } | ||||
							
								
								
									
										83
									
								
								libfm-qt/dirtreeview.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										83
									
								
								libfm-qt/dirtreeview.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,83 @@ | ||||
| /*
 | ||||
|  * <one line to give the program's name and a brief idea of what it does.> | ||||
|  * Copyright (C) 2014  <copyright holder> <email> | ||||
|  * | ||||
|  * This program is free software; you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU General Public License as published by | ||||
|  * the Free Software Foundation; either version 2 of the License, or | ||||
|  * (at your option) any later version. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU General Public License along | ||||
|  * with this program; if not, write to the Free Software Foundation, Inc., | ||||
|  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||||
|  * | ||||
|  */ | ||||
| 
 | ||||
| #ifndef FM_DIRTREEVIEW_H | ||||
| #define FM_DIRTREEVIEW_H | ||||
| 
 | ||||
| #include "libfmqtglobals.h" | ||||
| #include <QTreeView> | ||||
| #include <libfm/fm.h> | ||||
| #include "path.h" | ||||
| 
 | ||||
| class QItemSelection; | ||||
| 
 | ||||
| namespace Fm { | ||||
| 
 | ||||
| class DirTreeModelItem; | ||||
|    | ||||
| class LIBFM_QT_API DirTreeView : public QTreeView { | ||||
|   Q_OBJECT | ||||
| 
 | ||||
| public: | ||||
|   DirTreeView(QWidget* parent); | ||||
|   ~DirTreeView(); | ||||
| 
 | ||||
|   FmPath* currentPath() { | ||||
|     return currentPath_; | ||||
|   } | ||||
| 
 | ||||
|   void setCurrentPath(FmPath* path); | ||||
| 
 | ||||
|   // libfm-gtk compatible alias
 | ||||
|   FmPath* getCwd() { | ||||
|     return currentPath(); | ||||
|   } | ||||
| 
 | ||||
|   void chdir(FmPath* path) { | ||||
|     setCurrentPath(path); | ||||
|   } | ||||
| 
 | ||||
|   virtual void setModel(QAbstractItemModel* model); | ||||
| 
 | ||||
| protected: | ||||
|   virtual void contextMenuEvent(QContextMenuEvent* event); | ||||
| 
 | ||||
| private: | ||||
|   void cancelPendingChdir(); | ||||
|   void expandPendingPath(); | ||||
| 
 | ||||
| Q_SIGNALS: | ||||
|   void chdirRequested(int type, FmPath* path); | ||||
| 
 | ||||
| protected Q_SLOTS: | ||||
|   void onCollapsed(const QModelIndex & index); | ||||
|   void onExpanded(const QModelIndex & index); | ||||
|   void onRowLoaded(const QModelIndex& index); | ||||
|   void onSelectionChanged(const QItemSelection & selected, const QItemSelection & deselected); | ||||
| 
 | ||||
| private: | ||||
|   FmPath* currentPath_; | ||||
|   QList<Path> pathsToExpand_; | ||||
|   DirTreeModelItem* currentExpandingItem_; | ||||
| }; | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| #endif // FM_DIRTREEVIEW_H
 | ||||
							
								
								
									
										50
									
								
								libfm-qt/dndactionmenu.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										50
									
								
								libfm-qt/dndactionmenu.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,50 @@ | ||||
| /*
 | ||||
| 
 | ||||
|     Copyright (C) 2013  Hong Jen Yee (PCMan) <pcman.tw@gmail.com> | ||||
| 
 | ||||
|     This program is free software; you can redistribute it and/or modify | ||||
|     it under the terms of the GNU General Public License as published by | ||||
|     the Free Software Foundation; either version 2 of the License, or | ||||
|     (at your option) any later version. | ||||
| 
 | ||||
|     This program is distributed in the hope that it will be useful, | ||||
|     but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|     GNU General Public License for more details. | ||||
| 
 | ||||
|     You should have received a copy of the GNU General Public License along | ||||
|     with this program; if not, write to the Free Software Foundation, Inc., | ||||
|     51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||||
| */ | ||||
| 
 | ||||
| 
 | ||||
| #include "dndactionmenu.h" | ||||
| 
 | ||||
| using namespace Fm; | ||||
| 
 | ||||
| DndActionMenu::DndActionMenu(QWidget* parent): QMenu(parent) { | ||||
|   copyAction = addAction(QIcon::fromTheme("edit-copy"), tr("Copy here")); | ||||
|   moveAction = addAction(tr("Move here")); | ||||
|   linkAction = addAction(tr("Create symlink here")); | ||||
|   addSeparator(); | ||||
|   cancelAction = addAction(tr("Cancel")); | ||||
| } | ||||
| 
 | ||||
| DndActionMenu::~DndActionMenu() { | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| Qt::DropAction DndActionMenu::askUser(QPoint pos) { | ||||
|   Qt::DropAction result; | ||||
|   DndActionMenu menu; | ||||
|   QAction* action = menu.exec(pos); | ||||
|   if(action == menu.copyAction) | ||||
|     result = Qt::CopyAction; | ||||
|   else if(action == menu.moveAction) | ||||
|     result = Qt::MoveAction; | ||||
|   else if(action == menu.linkAction) | ||||
|     result = Qt::LinkAction; | ||||
|   else | ||||
|     result = Qt::IgnoreAction; | ||||
|   return result; | ||||
| } | ||||
							
								
								
									
										47
									
								
								libfm-qt/dndactionmenu.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								libfm-qt/dndactionmenu.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,47 @@ | ||||
| /*
 | ||||
| 
 | ||||
|     Copyright (C) 2013  Hong Jen Yee (PCMan) <pcman.tw@gmail.com> | ||||
| 
 | ||||
|     This program is free software; you can redistribute it and/or modify | ||||
|     it under the terms of the GNU General Public License as published by | ||||
|     the Free Software Foundation; either version 2 of the License, or | ||||
|     (at your option) any later version. | ||||
| 
 | ||||
|     This program is distributed in the hope that it will be useful, | ||||
|     but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|     GNU General Public License for more details. | ||||
| 
 | ||||
|     You should have received a copy of the GNU General Public License along | ||||
|     with this program; if not, write to the Free Software Foundation, Inc., | ||||
|     51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||||
| */ | ||||
| 
 | ||||
| 
 | ||||
| #ifndef FM_DNDACTIONMENU_H | ||||
| #define FM_DNDACTIONMENU_H | ||||
| 
 | ||||
| #include "libfmqtglobals.h" | ||||
| #include <QMenu> | ||||
| #include <QAction> | ||||
| 
 | ||||
| namespace Fm { | ||||
| 
 | ||||
| class DndActionMenu : public QMenu { | ||||
| Q_OBJECT | ||||
| public: | ||||
|   explicit DndActionMenu(QWidget* parent = 0); | ||||
|   virtual ~DndActionMenu(); | ||||
|    | ||||
|   static Qt::DropAction askUser(QPoint pos); | ||||
|    | ||||
| private: | ||||
|   QAction* copyAction; | ||||
|   QAction* moveAction; | ||||
|   QAction* linkAction; | ||||
|   QAction* cancelAction; | ||||
| }; | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| #endif // FM_DNDACTIONMENU_H
 | ||||
							
								
								
									
										71
									
								
								libfm-qt/dnddest.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										71
									
								
								libfm-qt/dnddest.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,71 @@ | ||||
| /*
 | ||||
|  * <one line to give the program's name and a brief idea of what it does.> | ||||
|  * Copyright (C) 2014  <copyright holder> <email> | ||||
|  * | ||||
|  * This program is free software; you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU General Public License as published by | ||||
|  * the Free Software Foundation; either version 2 of the License, or | ||||
|  * (at your option) any later version. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU General Public License along | ||||
|  * with this program; if not, write to the Free Software Foundation, Inc., | ||||
|  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||||
|  * | ||||
|  */ | ||||
| 
 | ||||
| #include "dnddest.h" | ||||
| #include "fileoperation.h" | ||||
| #include "utilities.h" | ||||
| 
 | ||||
| using namespace Fm; | ||||
| 
 | ||||
| const char* supportedMimeTypes[] = { | ||||
|   "text/uri-list" | ||||
|   "XdndDirectSave0"/* X direct save */ | ||||
|   /* TODO: add more targets to support: text types, _NETSCAPE_URL, property/bgimage ... */ | ||||
| }; | ||||
| 
 | ||||
| DndDest::DndDest() { | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| DndDest::~DndDest() { | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| bool DndDest::dropMimeData(const QMimeData* data, Qt::DropAction action) { | ||||
|   // FIXME: should we put this in dropEvent handler of FolderView instead?
 | ||||
|   if(data->hasUrls()) { | ||||
|     qDebug("drop action: %d", action); | ||||
|     FmPathList* srcPaths = pathListFromQUrls(data->urls()); | ||||
|     switch(action) { | ||||
|       case Qt::CopyAction: | ||||
|         FileOperation::copyFiles(srcPaths, destPath_.data()); | ||||
|         break; | ||||
|       case Qt::MoveAction: | ||||
|         FileOperation::moveFiles(srcPaths, destPath_.data()); | ||||
|         break; | ||||
|       case Qt::LinkAction: | ||||
|         FileOperation::symlinkFiles(srcPaths, destPath_.data()); | ||||
|       default: | ||||
|         fm_path_list_unref(srcPaths); | ||||
|         return false; | ||||
|     } | ||||
|     fm_path_list_unref(srcPaths); | ||||
|     return true; | ||||
|   } | ||||
|   return false; | ||||
| } | ||||
| 
 | ||||
| bool DndDest::isSupported(const QMimeData* data) { | ||||
|   return false; | ||||
| } | ||||
| 
 | ||||
| bool DndDest::isSupported(QString mimeType) { | ||||
|   return false; | ||||
| } | ||||
							
								
								
									
										53
									
								
								libfm-qt/dnddest.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										53
									
								
								libfm-qt/dnddest.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,53 @@ | ||||
| /*
 | ||||
|  * <one line to give the program's name and a brief idea of what it does.> | ||||
|  * Copyright (C) 2014  <copyright holder> <email> | ||||
|  * | ||||
|  * This program is free software; you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU General Public License as published by | ||||
|  * the Free Software Foundation; either version 2 of the License, or | ||||
|  * (at your option) any later version. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU General Public License along | ||||
|  * with this program; if not, write to the Free Software Foundation, Inc., | ||||
|  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||||
|  * | ||||
|  */ | ||||
| 
 | ||||
| #ifndef FM_DNDDEST_H | ||||
| #define FM_DNDDEST_H | ||||
| 
 | ||||
| #include <QMimeData> | ||||
| #include "path.h" | ||||
| 
 | ||||
| namespace Fm { | ||||
| 
 | ||||
| class DndDest { | ||||
| public: | ||||
|   DndDest(); | ||||
|   ~DndDest(); | ||||
| 
 | ||||
|   void setDestPath(FmPath* dest) { | ||||
|     destPath_ = dest; | ||||
|   } | ||||
| 
 | ||||
|   const Path& destPath() { | ||||
|     return destPath_; | ||||
|   } | ||||
| 
 | ||||
|   bool isSupported(const QMimeData* data); | ||||
|   bool isSupported(QString mimeType); | ||||
| 
 | ||||
|   bool dropMimeData(const QMimeData* data, Qt::DropAction action); | ||||
| 
 | ||||
| private: | ||||
|   Path destPath_; | ||||
| }; | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| #endif // FM_DNDDEST_H
 | ||||
							
								
								
									
										143
									
								
								libfm-qt/edit-bookmarks.ui
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										143
									
								
								libfm-qt/edit-bookmarks.ui
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,143 @@ | ||||
| <?xml version="1.0" encoding="UTF-8"?> | ||||
| <ui version="4.0"> | ||||
|  <class>EditBookmarksDialog</class> | ||||
|  <widget class="QDialog" name="EditBookmarksDialog"> | ||||
|   <property name="geometry"> | ||||
|    <rect> | ||||
|     <x>0</x> | ||||
|     <y>0</y> | ||||
|     <width>480</width> | ||||
|     <height>320</height> | ||||
|    </rect> | ||||
|   </property> | ||||
|   <property name="windowTitle"> | ||||
|    <string>Edit Bookmarks</string> | ||||
|   </property> | ||||
|   <layout class="QGridLayout" name="gridLayout"> | ||||
|    <item row="1" column="0"> | ||||
|     <widget class="QTreeWidget" name="treeWidget"> | ||||
|      <property name="acceptDrops"> | ||||
|       <bool>true</bool> | ||||
|      </property> | ||||
|      <property name="dragEnabled"> | ||||
|       <bool>true</bool> | ||||
|      </property> | ||||
|      <property name="dragDropMode"> | ||||
|       <enum>QAbstractItemView::InternalMove</enum> | ||||
|      </property> | ||||
|      <property name="defaultDropAction"> | ||||
|       <enum>Qt::MoveAction</enum> | ||||
|      </property> | ||||
|      <property name="rootIsDecorated"> | ||||
|       <bool>false</bool> | ||||
|      </property> | ||||
|      <property name="itemsExpandable"> | ||||
|       <bool>false</bool> | ||||
|      </property> | ||||
|      <attribute name="headerDefaultSectionSize"> | ||||
|       <number>100</number> | ||||
|      </attribute> | ||||
|      <column> | ||||
|       <property name="text"> | ||||
|        <string>Name</string> | ||||
|       </property> | ||||
|      </column> | ||||
|      <column> | ||||
|       <property name="text"> | ||||
|        <string>Location</string> | ||||
|       </property> | ||||
|      </column> | ||||
|     </widget> | ||||
|    </item> | ||||
|    <item row="2" column="0" colspan="2"> | ||||
|     <widget class="QDialogButtonBox" name="buttonBox"> | ||||
|      <property name="orientation"> | ||||
|       <enum>Qt::Horizontal</enum> | ||||
|      </property> | ||||
|      <property name="standardButtons"> | ||||
|       <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> | ||||
|      </property> | ||||
|     </widget> | ||||
|    </item> | ||||
|    <item row="1" column="1"> | ||||
|     <layout class="QVBoxLayout" name="verticalLayout"> | ||||
|      <item> | ||||
|       <widget class="QPushButton" name="addItem"> | ||||
|        <property name="text"> | ||||
|         <string>&Add Item</string> | ||||
|        </property> | ||||
|        <property name="icon"> | ||||
|         <iconset theme="list-add"/> | ||||
|        </property> | ||||
|       </widget> | ||||
|      </item> | ||||
|      <item> | ||||
|       <widget class="QPushButton" name="removeItem"> | ||||
|        <property name="text"> | ||||
|         <string>&Remove Item</string> | ||||
|        </property> | ||||
|        <property name="icon"> | ||||
|         <iconset theme="list-remove"/> | ||||
|        </property> | ||||
|       </widget> | ||||
|      </item> | ||||
|      <item> | ||||
|       <spacer name="verticalSpacer"> | ||||
|        <property name="orientation"> | ||||
|         <enum>Qt::Vertical</enum> | ||||
|        </property> | ||||
|        <property name="sizeHint" stdset="0"> | ||||
|         <size> | ||||
|          <width>20</width> | ||||
|          <height>40</height> | ||||
|         </size> | ||||
|        </property> | ||||
|       </spacer> | ||||
|      </item> | ||||
|     </layout> | ||||
|    </item> | ||||
|    <item row="0" column="0"> | ||||
|     <widget class="QLabel" name="label"> | ||||
|      <property name="text"> | ||||
|       <string>Use drag and drop to reorder the items</string> | ||||
|      </property> | ||||
|     </widget> | ||||
|    </item> | ||||
|   </layout> | ||||
|  </widget> | ||||
|  <resources/> | ||||
|  <connections> | ||||
|   <connection> | ||||
|    <sender>buttonBox</sender> | ||||
|    <signal>accepted()</signal> | ||||
|    <receiver>EditBookmarksDialog</receiver> | ||||
|    <slot>accept()</slot> | ||||
|    <hints> | ||||
|     <hint type="sourcelabel"> | ||||
|      <x>248</x> | ||||
|      <y>254</y> | ||||
|     </hint> | ||||
|     <hint type="destinationlabel"> | ||||
|      <x>157</x> | ||||
|      <y>274</y> | ||||
|     </hint> | ||||
|    </hints> | ||||
|   </connection> | ||||
|   <connection> | ||||
|    <sender>buttonBox</sender> | ||||
|    <signal>rejected()</signal> | ||||
|    <receiver>EditBookmarksDialog</receiver> | ||||
|    <slot>reject()</slot> | ||||
|    <hints> | ||||
|     <hint type="sourcelabel"> | ||||
|      <x>316</x> | ||||
|      <y>260</y> | ||||
|     </hint> | ||||
|     <hint type="destinationlabel"> | ||||
|      <x>286</x> | ||||
|      <y>274</y> | ||||
|     </hint> | ||||
|    </hints> | ||||
|   </connection> | ||||
|  </connections> | ||||
| </ui> | ||||
							
								
								
									
										108
									
								
								libfm-qt/editbookmarksdialog.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										108
									
								
								libfm-qt/editbookmarksdialog.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,108 @@ | ||||
| /*
 | ||||
|     <one line to give the program's name and a brief idea of what it does.> | ||||
|     Copyright (C) 2013  PCMan <email> | ||||
| 
 | ||||
|     This program is free software; you can redistribute it and/or modify | ||||
|     it under the terms of the GNU General Public License as published by | ||||
|     the Free Software Foundation; either version 2 of the License, or | ||||
|     (at your option) any later version. | ||||
| 
 | ||||
|     This program is distributed in the hope that it will be useful, | ||||
|     but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|     GNU General Public License for more details. | ||||
| 
 | ||||
|     You should have received a copy of the GNU General Public License along | ||||
|     with this program; if not, write to the Free Software Foundation, Inc., | ||||
|     51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||||
| */ | ||||
| 
 | ||||
| 
 | ||||
| #include "editbookmarksdialog.h" | ||||
| #include "ui_edit-bookmarks.h" | ||||
| #include <QByteArray> | ||||
| #include <QUrl> | ||||
| #include <QSaveFile> | ||||
| #include <QStandardPaths> | ||||
| #include <QDir> | ||||
| 
 | ||||
| using namespace Fm; | ||||
| 
 | ||||
| EditBookmarksDialog::EditBookmarksDialog(FmBookmarks* bookmarks, QWidget* parent, Qt::WindowFlags f): | ||||
|   QDialog(parent, f), | ||||
|   ui(new Ui::EditBookmarksDialog()), | ||||
|   bookmarks_(FM_BOOKMARKS(g_object_ref(bookmarks))) { | ||||
| 
 | ||||
|   ui->setupUi(this); | ||||
|   setAttribute(Qt::WA_DeleteOnClose); // auto delete on close
 | ||||
| 
 | ||||
|   // load bookmarks
 | ||||
|   GList* l = fm_bookmarks_get_all(bookmarks_); | ||||
|   for(; l; l = l->next) { | ||||
|     FmBookmarkItem* bookmark = reinterpret_cast<FmBookmarkItem*>(l->data); | ||||
|     QTreeWidgetItem* item = new QTreeWidgetItem(); | ||||
|     char* path_str = fm_path_display_name(bookmark->path, false); | ||||
|     item->setData(0, Qt::DisplayRole, QString::fromUtf8(bookmark->name)); | ||||
|     item->setData(1, Qt::DisplayRole, QString::fromUtf8(path_str)); | ||||
|     item->setFlags(Qt::ItemIsSelectable|Qt::ItemIsEditable|Qt::ItemIsDragEnabled|Qt::ItemIsEnabled); | ||||
|     g_free(path_str); | ||||
|     ui->treeWidget->addTopLevelItem(item); | ||||
|   } | ||||
| 
 | ||||
|   connect(ui->addItem, &QPushButton::clicked, this, &EditBookmarksDialog::onAddItem); | ||||
|   connect(ui->removeItem, &QPushButton::clicked, this, &EditBookmarksDialog::onRemoveItem); | ||||
| } | ||||
| 
 | ||||
| EditBookmarksDialog::~EditBookmarksDialog() { | ||||
|   g_object_unref(bookmarks_); | ||||
|   delete ui; | ||||
| } | ||||
| 
 | ||||
| void EditBookmarksDialog::accept() { | ||||
|   // save bookmarks
 | ||||
|   // it's easier to recreate the whole bookmark file than
 | ||||
|   // to manipulate FmBookmarks object. So here we generate the file directly.
 | ||||
|   // FIXME: maybe in the future we should add a libfm API to easily replace all FmBookmarks.
 | ||||
|   // Here we use gtk+ 3.0 bookmarks rather than the gtk+ 2.0 one.
 | ||||
|   // Since gtk+ 2.24.12, gtk+2 reads gtk+3 bookmarks file if it exists.
 | ||||
|   // So it's safe to only save gtk+3 bookmarks file.
 | ||||
|   QString path = QStandardPaths::writableLocation(QStandardPaths::ConfigLocation); | ||||
|   path += QLatin1String("/gtk-3.0"); | ||||
|   if(!QDir().mkpath(path)) | ||||
|     return; // fail to create ~/.config/gtk-3.0 dir
 | ||||
|   path += QLatin1String("/bookmarks"); | ||||
|   QSaveFile file(path); // use QSaveFile for atomic file operation
 | ||||
|   if(file.open(QIODevice::WriteOnly)){ | ||||
|     for(int row = 0; ; ++row) { | ||||
|       QTreeWidgetItem* item = ui->treeWidget->topLevelItem(row); | ||||
|       if(!item) | ||||
|         break; | ||||
|       QString name = item->data(0, Qt::DisplayRole).toString(); | ||||
|       QUrl url = QUrl::fromUserInput(item->data(1, Qt::DisplayRole).toString()); | ||||
|       file.write(url.toEncoded()); | ||||
|       file.write(" "); | ||||
|       file.write(name.toUtf8()); | ||||
|       file.write("\n"); | ||||
|     } | ||||
|     // FIXME: should we support Qt or KDE specific bookmarks in the future?
 | ||||
|     file.commit(); | ||||
|   } | ||||
|   QDialog::accept(); | ||||
| } | ||||
| 
 | ||||
| void EditBookmarksDialog::onAddItem() { | ||||
|   QTreeWidgetItem* item = new QTreeWidgetItem(); | ||||
|   item->setData(0, Qt::DisplayRole, tr("New bookmark")); | ||||
|   item->setFlags(Qt::ItemIsSelectable|Qt::ItemIsEditable|Qt::ItemIsDragEnabled|Qt::ItemIsEnabled); | ||||
|   ui->treeWidget->addTopLevelItem(item); | ||||
|   ui->treeWidget->editItem(item); | ||||
| } | ||||
| 
 | ||||
| void EditBookmarksDialog::onRemoveItem() { | ||||
|   QList<QTreeWidgetItem*> sels = ui->treeWidget->selectedItems(); | ||||
|   Q_FOREACH(QTreeWidgetItem* item, sels) { | ||||
|     delete item; | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
							
								
								
									
										53
									
								
								libfm-qt/editbookmarksdialog.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										53
									
								
								libfm-qt/editbookmarksdialog.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,53 @@ | ||||
| /*
 | ||||
|     <one line to give the program's name and a brief idea of what it does.> | ||||
|     Copyright (C) 2013  PCMan <email> | ||||
| 
 | ||||
|     This program is free software; you can redistribute it and/or modify | ||||
|     it under the terms of the GNU General Public License as published by | ||||
|     the Free Software Foundation; either version 2 of the License, or | ||||
|     (at your option) any later version. | ||||
| 
 | ||||
|     This program is distributed in the hope that it will be useful, | ||||
|     but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|     GNU General Public License for more details. | ||||
| 
 | ||||
|     You should have received a copy of the GNU General Public License along | ||||
|     with this program; if not, write to the Free Software Foundation, Inc., | ||||
|     51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||||
| */ | ||||
| 
 | ||||
| 
 | ||||
| #ifndef FM_EDITBOOKMARKSDIALOG_H | ||||
| #define FM_EDITBOOKMARKSDIALOG_H | ||||
| 
 | ||||
| #include "libfmqtglobals.h" | ||||
| #include <QDialog> | ||||
| #include <libfm/fm.h> | ||||
| 
 | ||||
| namespace Ui { | ||||
| class EditBookmarksDialog; | ||||
| }; | ||||
| 
 | ||||
| namespace Fm { | ||||
| 
 | ||||
| class LIBFM_QT_API EditBookmarksDialog : public QDialog { | ||||
| Q_OBJECT | ||||
| public: | ||||
|   explicit EditBookmarksDialog(FmBookmarks* bookmarks, QWidget* parent = 0, Qt::WindowFlags f = 0); | ||||
|   virtual ~EditBookmarksDialog(); | ||||
| 
 | ||||
|   virtual void accept(); | ||||
| 
 | ||||
| private Q_SLOTS: | ||||
|   void onAddItem(); | ||||
|   void onRemoveItem(); | ||||
|    | ||||
| private: | ||||
|   Ui::EditBookmarksDialog* ui; | ||||
|   FmBookmarks* bookmarks_; | ||||
| }; | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| #endif // FM_EDITBOOKMARKSDIALOG_H
 | ||||
							
								
								
									
										163
									
								
								libfm-qt/exec-file.ui
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										163
									
								
								libfm-qt/exec-file.ui
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,163 @@ | ||||
| <?xml version="1.0" encoding="UTF-8"?> | ||||
| <ui version="4.0"> | ||||
|  <class>ExecFileDialog</class> | ||||
|  <widget class="QDialog" name="ExecFileDialog"> | ||||
|   <property name="geometry"> | ||||
|    <rect> | ||||
|     <x>0</x> | ||||
|     <y>0</y> | ||||
|     <width>487</width> | ||||
|     <height>58</height> | ||||
|    </rect> | ||||
|   </property> | ||||
|   <property name="windowTitle"> | ||||
|    <string>Execute file</string> | ||||
|   </property> | ||||
|   <layout class="QVBoxLayout" name="verticalLayout"> | ||||
|    <item> | ||||
|     <layout class="QHBoxLayout" name="horizontalLayout_2" stretch="0,1"> | ||||
|      <item> | ||||
|       <widget class="QLabel" name="icon"/> | ||||
|      </item> | ||||
|      <item> | ||||
|       <widget class="QLabel" name="msg"> | ||||
|        <property name="text"> | ||||
|         <string/> | ||||
|        </property> | ||||
|        <property name="wordWrap"> | ||||
|         <bool>true</bool> | ||||
|        </property> | ||||
|       </widget> | ||||
|      </item> | ||||
|     </layout> | ||||
|    </item> | ||||
|    <item> | ||||
|     <layout class="QHBoxLayout" name="horizontalLayout"> | ||||
|      <item> | ||||
|       <widget class="QPushButton" name="open"> | ||||
|        <property name="text"> | ||||
|         <string>&Open</string> | ||||
|        </property> | ||||
|        <property name="icon"> | ||||
|         <iconset theme="document-open"/> | ||||
|        </property> | ||||
|        <property name="default"> | ||||
|         <bool>true</bool> | ||||
|        </property> | ||||
|       </widget> | ||||
|      </item> | ||||
|      <item> | ||||
|       <widget class="QPushButton" name="exec"> | ||||
|        <property name="text"> | ||||
|         <string>E&xecute</string> | ||||
|        </property> | ||||
|        <property name="icon"> | ||||
|         <iconset theme="system-run"/> | ||||
|        </property> | ||||
|       </widget> | ||||
|      </item> | ||||
|      <item> | ||||
|       <widget class="QPushButton" name="execTerm"> | ||||
|        <property name="text"> | ||||
|         <string>Execute in &Terminal</string> | ||||
|        </property> | ||||
|        <property name="icon"> | ||||
|         <iconset theme="utilities-terminal"/> | ||||
|        </property> | ||||
|       </widget> | ||||
|      </item> | ||||
|      <item> | ||||
|       <spacer name="horizontalSpacer"> | ||||
|        <property name="orientation"> | ||||
|         <enum>Qt::Horizontal</enum> | ||||
|        </property> | ||||
|        <property name="sizeHint" stdset="0"> | ||||
|         <size> | ||||
|          <width>40</width> | ||||
|          <height>20</height> | ||||
|         </size> | ||||
|        </property> | ||||
|       </spacer> | ||||
|      </item> | ||||
|      <item> | ||||
|       <widget class="QPushButton" name="cancel"> | ||||
|        <property name="text"> | ||||
|         <string>Cancel</string> | ||||
|        </property> | ||||
|        <property name="icon"> | ||||
|         <iconset theme="dialog-cancel"/> | ||||
|        </property> | ||||
|       </widget> | ||||
|      </item> | ||||
|     </layout> | ||||
|    </item> | ||||
|   </layout> | ||||
|  </widget> | ||||
|  <resources/> | ||||
|  <connections> | ||||
|   <connection> | ||||
|    <sender>cancel</sender> | ||||
|    <signal>clicked()</signal> | ||||
|    <receiver>ExecFileDialog</receiver> | ||||
|    <slot>reject()</slot> | ||||
|    <hints> | ||||
|     <hint type="sourcelabel"> | ||||
|      <x>341</x> | ||||
|      <y>39</y> | ||||
|     </hint> | ||||
|     <hint type="destinationlabel"> | ||||
|      <x>196</x> | ||||
|      <y>28</y> | ||||
|     </hint> | ||||
|    </hints> | ||||
|   </connection> | ||||
|   <connection> | ||||
|    <sender>exec</sender> | ||||
|    <signal>clicked()</signal> | ||||
|    <receiver>ExecFileDialog</receiver> | ||||
|    <slot>accept()</slot> | ||||
|    <hints> | ||||
|     <hint type="sourcelabel"> | ||||
|      <x>56</x> | ||||
|      <y>39</y> | ||||
|     </hint> | ||||
|     <hint type="destinationlabel"> | ||||
|      <x>196</x> | ||||
|      <y>28</y> | ||||
|     </hint> | ||||
|    </hints> | ||||
|   </connection> | ||||
|   <connection> | ||||
|    <sender>execTerm</sender> | ||||
|    <signal>clicked()</signal> | ||||
|    <receiver>ExecFileDialog</receiver> | ||||
|    <slot>accept()</slot> | ||||
|    <hints> | ||||
|     <hint type="sourcelabel"> | ||||
|      <x>201</x> | ||||
|      <y>39</y> | ||||
|     </hint> | ||||
|     <hint type="destinationlabel"> | ||||
|      <x>196</x> | ||||
|      <y>28</y> | ||||
|     </hint> | ||||
|    </hints> | ||||
|   </connection> | ||||
|   <connection> | ||||
|    <sender>open</sender> | ||||
|    <signal>clicked()</signal> | ||||
|    <receiver>ExecFileDialog</receiver> | ||||
|    <slot>accept()</slot> | ||||
|    <hints> | ||||
|     <hint type="sourcelabel"> | ||||
|      <x>346</x> | ||||
|      <y>39</y> | ||||
|     </hint> | ||||
|     <hint type="destinationlabel"> | ||||
|      <x>250</x> | ||||
|      <y>28</y> | ||||
|     </hint> | ||||
|    </hints> | ||||
|   </connection> | ||||
|  </connections> | ||||
| </ui> | ||||
							
								
								
									
										70
									
								
								libfm-qt/execfiledialog.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										70
									
								
								libfm-qt/execfiledialog.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,70 @@ | ||||
| /*
 | ||||
|  * <one line to give the library's name and an idea of what it does.> | ||||
|  * Copyright (C) 2014  <copyright holder> <email> | ||||
|  * | ||||
|  * 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 | ||||
|  * | ||||
|  */ | ||||
| 
 | ||||
| #include "execfiledialog_p.h" | ||||
| #include "ui_exec-file.h" | ||||
| #include "icontheme.h" | ||||
| 
 | ||||
| namespace Fm { | ||||
| 
 | ||||
| ExecFileDialog::ExecFileDialog(FmFileInfo* file, QWidget* parent, Qt::WindowFlags f): | ||||
|   QDialog (parent, f), | ||||
|   fileInfo_(fm_file_info_ref(file)), | ||||
|   result_(FM_FILE_LAUNCHER_EXEC_CANCEL), | ||||
|   ui(new Ui::ExecFileDialog()) { | ||||
| 
 | ||||
|   ui->setupUi(this); | ||||
|   // show file icon
 | ||||
|   FmIcon* icon = fm_file_info_get_icon(fileInfo_); | ||||
|   ui->icon->setPixmap(IconTheme::icon(icon).pixmap(QSize(48, 48))); | ||||
| 
 | ||||
|   QString msg; | ||||
|   if(fm_file_info_is_text(file)) { | ||||
|     msg = tr("This text file '%1' seems to be an executable script.\nWhat do you want to do with it?") | ||||
|             .arg(QString::fromUtf8(fm_file_info_get_disp_name(file))); | ||||
|     ui->execTerm->setDefault(true); | ||||
|   } | ||||
|   else { | ||||
|     msg= tr("This file '%1' is executable. Do you want to execute it?") | ||||
|            .arg(QString::fromUtf8(fm_file_info_get_disp_name(file))); | ||||
|     ui->exec->setDefault(true); | ||||
|     ui->open->hide(); | ||||
|   } | ||||
|   ui->msg->setText(msg); | ||||
| } | ||||
| 
 | ||||
| ExecFileDialog::~ExecFileDialog() { | ||||
|   delete ui; | ||||
|   if(fileInfo_) | ||||
|     fm_file_info_unref(fileInfo_); | ||||
| } | ||||
| 
 | ||||
| void ExecFileDialog::accept() { | ||||
|   QObject* _sender = sender(); | ||||
|   if(_sender == ui->exec) | ||||
|     result_ = FM_FILE_LAUNCHER_EXEC; | ||||
|   else if(_sender == ui->execTerm) | ||||
|     result_ = FM_FILE_LAUNCHER_EXEC_IN_TERMINAL; | ||||
|   else if(_sender == ui->open) | ||||
|     result_ = FM_FILE_LAUNCHER_EXEC_OPEN; | ||||
|   QDialog::accept(); | ||||
| } | ||||
| 
 | ||||
| } // namespace Fm
 | ||||
							
								
								
									
										54
									
								
								libfm-qt/execfiledialog_p.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										54
									
								
								libfm-qt/execfiledialog_p.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,54 @@ | ||||
| /*
 | ||||
|  * <one line to give the library's name and an idea of what it does.> | ||||
|  * Copyright (C) 2014  <copyright holder> <email> | ||||
|  * | ||||
|  * 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 | ||||
|  * | ||||
|  */ | ||||
| 
 | ||||
| #ifndef FM_EXECFILEDIALOG_H | ||||
| #define FM_EXECFILEDIALOG_H | ||||
| 
 | ||||
| #include <QDialog> | ||||
| #include <libfm/fm.h> | ||||
| 
 | ||||
| namespace Ui { | ||||
|   class ExecFileDialog; | ||||
| } | ||||
| 
 | ||||
| namespace Fm { | ||||
| 
 | ||||
| class ExecFileDialog : public QDialog { | ||||
|   Q_OBJECT | ||||
| public: | ||||
|   ~ExecFileDialog(); | ||||
|   ExecFileDialog(FmFileInfo* fileInfo, QWidget* parent = 0, Qt::WindowFlags f = 0); | ||||
| 
 | ||||
|   FmFileLauncherExecAction result() { | ||||
|     return result_; | ||||
|   } | ||||
| 
 | ||||
| protected: | ||||
|   virtual void accept(); | ||||
| 
 | ||||
| private: | ||||
|   Ui::ExecFileDialog* ui; | ||||
|   FmFileInfo* fileInfo_; | ||||
|   FmFileLauncherExecAction result_; | ||||
| }; | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| #endif // FM_EXECFILEDIALOG_H
 | ||||
							
								
								
									
										171
									
								
								libfm-qt/file-operation-dialog.ui
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										171
									
								
								libfm-qt/file-operation-dialog.ui
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,171 @@ | ||||
| <?xml version="1.0" encoding="UTF-8"?> | ||||
| <ui version="4.0"> | ||||
|  <class>FileOperationDialog</class> | ||||
|  <widget class="QDialog" name="FileOperationDialog"> | ||||
|   <property name="geometry"> | ||||
|    <rect> | ||||
|     <x>0</x> | ||||
|     <y>0</y> | ||||
|     <width>450</width> | ||||
|     <height>246</height> | ||||
|    </rect> | ||||
|   </property> | ||||
|   <property name="windowTitle"> | ||||
|    <string/> | ||||
|   </property> | ||||
|   <layout class="QFormLayout" name="formLayout_2"> | ||||
|    <item row="0" column="0" colspan="2"> | ||||
|     <widget class="QLabel" name="message"> | ||||
|      <property name="text"> | ||||
|       <string/> | ||||
|      </property> | ||||
|     </widget> | ||||
|    </item> | ||||
|    <item row="1" column="1"> | ||||
|     <layout class="QFormLayout" name="formLayout"> | ||||
|      <property name="fieldGrowthPolicy"> | ||||
|       <enum>QFormLayout::AllNonFixedFieldsGrow</enum> | ||||
|      </property> | ||||
|      <item row="1" column="0"> | ||||
|       <widget class="QLabel" name="destLabel"> | ||||
|        <property name="text"> | ||||
|         <string>Destination:</string> | ||||
|        </property> | ||||
|       </widget> | ||||
|      </item> | ||||
|      <item row="1" column="1"> | ||||
|       <widget class="QLabel" name="dest"> | ||||
|        <property name="sizePolicy"> | ||||
|         <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> | ||||
|          <horstretch>0</horstretch> | ||||
|          <verstretch>0</verstretch> | ||||
|         </sizepolicy> | ||||
|        </property> | ||||
|        <property name="text"> | ||||
|         <string/> | ||||
|        </property> | ||||
|        <property name="wordWrap"> | ||||
|         <bool>true</bool> | ||||
|        </property> | ||||
|       </widget> | ||||
|      </item> | ||||
|      <item row="2" column="0"> | ||||
|       <widget class="QLabel" name="label_3"> | ||||
|        <property name="text"> | ||||
|         <string>Processing:</string> | ||||
|        </property> | ||||
|       </widget> | ||||
|      </item> | ||||
|      <item row="2" column="1"> | ||||
|       <widget class="QLabel" name="curFile"> | ||||
|        <property name="sizePolicy"> | ||||
|         <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> | ||||
|          <horstretch>0</horstretch> | ||||
|          <verstretch>0</verstretch> | ||||
|         </sizepolicy> | ||||
|        </property> | ||||
|        <property name="text"> | ||||
|         <string>Preparing...</string> | ||||
|        </property> | ||||
|       </widget> | ||||
|      </item> | ||||
|      <item row="3" column="0"> | ||||
|       <widget class="QLabel" name="label_4"> | ||||
|        <property name="text"> | ||||
|         <string>Progress</string> | ||||
|        </property> | ||||
|       </widget> | ||||
|      </item> | ||||
|      <item row="3" column="1"> | ||||
|       <widget class="QProgressBar" name="progressBar"> | ||||
|        <property name="alignment"> | ||||
|         <set>Qt::AlignCenter</set> | ||||
|        </property> | ||||
|       </widget> | ||||
|      </item> | ||||
|      <item row="4" column="0"> | ||||
|       <widget class="QLabel" name="label_5"> | ||||
|        <property name="sizePolicy"> | ||||
|         <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> | ||||
|          <horstretch>0</horstretch> | ||||
|          <verstretch>0</verstretch> | ||||
|         </sizepolicy> | ||||
|        </property> | ||||
|        <property name="text"> | ||||
|         <string>Time remaining:</string> | ||||
|        </property> | ||||
|       </widget> | ||||
|      </item> | ||||
|      <item row="4" column="1"> | ||||
|       <widget class="QLabel" name="timeRemaining"> | ||||
|        <property name="sizePolicy"> | ||||
|         <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> | ||||
|          <horstretch>0</horstretch> | ||||
|          <verstretch>0</verstretch> | ||||
|         </sizepolicy> | ||||
|        </property> | ||||
|        <property name="text"> | ||||
|         <string/> | ||||
|        </property> | ||||
|       </widget> | ||||
|      </item> | ||||
|      <item row="0" column="0" colspan="2"> | ||||
|       <widget class="QListWidget" name="sourceFiles"> | ||||
|        <property name="sizePolicy"> | ||||
|         <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> | ||||
|          <horstretch>0</horstretch> | ||||
|          <verstretch>0</verstretch> | ||||
|         </sizepolicy> | ||||
|        </property> | ||||
|       </widget> | ||||
|      </item> | ||||
|     </layout> | ||||
|    </item> | ||||
|    <item row="2" column="0" colspan="2"> | ||||
|     <widget class="QDialogButtonBox" name="buttonBox"> | ||||
|      <property name="orientation"> | ||||
|       <enum>Qt::Horizontal</enum> | ||||
|      </property> | ||||
|      <property name="standardButtons"> | ||||
|       <set>QDialogButtonBox::Cancel</set> | ||||
|      </property> | ||||
|     </widget> | ||||
|    </item> | ||||
|   </layout> | ||||
|  </widget> | ||||
|  <resources/> | ||||
|  <connections> | ||||
|   <connection> | ||||
|    <sender>buttonBox</sender> | ||||
|    <signal>accepted()</signal> | ||||
|    <receiver>FileOperationDialog</receiver> | ||||
|    <slot>accept()</slot> | ||||
|    <hints> | ||||
|     <hint type="sourcelabel"> | ||||
|      <x>248</x> | ||||
|      <y>254</y> | ||||
|     </hint> | ||||
|     <hint type="destinationlabel"> | ||||
|      <x>157</x> | ||||
|      <y>274</y> | ||||
|     </hint> | ||||
|    </hints> | ||||
|   </connection> | ||||
|   <connection> | ||||
|    <sender>buttonBox</sender> | ||||
|    <signal>rejected()</signal> | ||||
|    <receiver>FileOperationDialog</receiver> | ||||
|    <slot>reject()</slot> | ||||
|    <hints> | ||||
|     <hint type="sourcelabel"> | ||||
|      <x>316</x> | ||||
|      <y>260</y> | ||||
|     </hint> | ||||
|     <hint type="destinationlabel"> | ||||
|      <x>286</x> | ||||
|      <y>274</y> | ||||
|     </hint> | ||||
|    </hints> | ||||
|   </connection> | ||||
|  </connections> | ||||
| </ui> | ||||
							
								
								
									
										700
									
								
								libfm-qt/file-props.ui
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										700
									
								
								libfm-qt/file-props.ui
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,700 @@ | ||||
| <?xml version="1.0" encoding="UTF-8"?> | ||||
| <ui version="4.0"> | ||||
|  <class>FilePropsDialog</class> | ||||
|  <widget class="QDialog" name="FilePropsDialog"> | ||||
|   <property name="geometry"> | ||||
|    <rect> | ||||
|     <x>0</x> | ||||
|     <y>0</y> | ||||
|     <width>407</width> | ||||
|     <height>396</height> | ||||
|    </rect> | ||||
|   </property> | ||||
|   <property name="windowTitle"> | ||||
|    <string>File Properties</string> | ||||
|   </property> | ||||
|   <property name="windowIcon"> | ||||
|    <iconset theme="document-properties"> | ||||
|     <normaloff/> | ||||
|    </iconset> | ||||
|   </property> | ||||
|   <layout class="QVBoxLayout" name="verticalLayout"> | ||||
|    <property name="margin"> | ||||
|     <number>10</number> | ||||
|    </property> | ||||
|    <item> | ||||
|     <widget class="QTabWidget" name="tabWidget"> | ||||
|      <property name="currentIndex"> | ||||
|       <number>0</number> | ||||
|      </property> | ||||
|      <widget class="QWidget" name="generalPage"> | ||||
|       <attribute name="title"> | ||||
|        <string>General</string> | ||||
|       </attribute> | ||||
|       <layout class="QFormLayout" name="formLayout_2"> | ||||
|        <property name="horizontalSpacing"> | ||||
|         <number>12</number> | ||||
|        </property> | ||||
|        <property name="verticalSpacing"> | ||||
|         <number>6</number> | ||||
|        </property> | ||||
|        <item row="0" column="0"> | ||||
|         <widget class="QPushButton" name="iconButton"> | ||||
|          <property name="sizePolicy"> | ||||
|           <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> | ||||
|            <horstretch>0</horstretch> | ||||
|            <verstretch>0</verstretch> | ||||
|           </sizepolicy> | ||||
|          </property> | ||||
|          <property name="text"> | ||||
|           <string/> | ||||
|          </property> | ||||
|          <property name="icon"> | ||||
|           <iconset theme="unknown"> | ||||
|            <normaloff/> | ||||
|           </iconset> | ||||
|          </property> | ||||
|          <property name="iconSize"> | ||||
|           <size> | ||||
|            <width>32</width> | ||||
|            <height>32</height> | ||||
|           </size> | ||||
|          </property> | ||||
|         </widget> | ||||
|        </item> | ||||
|        <item row="0" column="1"> | ||||
|         <widget class="QLineEdit" name="fileName"/> | ||||
|        </item> | ||||
|        <item row="1" column="0"> | ||||
|         <widget class="QLabel" name="label_2"> | ||||
|          <property name="text"> | ||||
|           <string>Location:</string> | ||||
|          </property> | ||||
|         </widget> | ||||
|        </item> | ||||
|        <item row="1" column="1"> | ||||
|         <widget class="QLabel" name="location"> | ||||
|          <property name="sizePolicy"> | ||||
|           <sizepolicy hsizetype="Expanding" vsizetype="Preferred"> | ||||
|            <horstretch>0</horstretch> | ||||
|            <verstretch>0</verstretch> | ||||
|           </sizepolicy> | ||||
|          </property> | ||||
|          <property name="text"> | ||||
|           <string/> | ||||
|          </property> | ||||
|          <property name="wordWrap"> | ||||
|           <bool>true</bool> | ||||
|          </property> | ||||
|         </widget> | ||||
|        </item> | ||||
|        <item row="3" column="0"> | ||||
|         <widget class="QLabel" name="label_4"> | ||||
|          <property name="text"> | ||||
|           <string>File type:</string> | ||||
|          </property> | ||||
|         </widget> | ||||
|        </item> | ||||
|        <item row="3" column="1"> | ||||
|         <widget class="QLabel" name="fileType"> | ||||
|          <property name="sizePolicy"> | ||||
|           <sizepolicy hsizetype="Expanding" vsizetype="Preferred"> | ||||
|            <horstretch>0</horstretch> | ||||
|            <verstretch>0</verstretch> | ||||
|           </sizepolicy> | ||||
|          </property> | ||||
|          <property name="text"> | ||||
|           <string/> | ||||
|          </property> | ||||
|         </widget> | ||||
|        </item> | ||||
|        <item row="4" column="0"> | ||||
|         <widget class="QLabel" name="label"> | ||||
|          <property name="text"> | ||||
|           <string>Mime type:</string> | ||||
|          </property> | ||||
|         </widget> | ||||
|        </item> | ||||
|        <item row="4" column="1"> | ||||
|         <widget class="QLabel" name="mimeType"> | ||||
|          <property name="sizePolicy"> | ||||
|           <sizepolicy hsizetype="Expanding" vsizetype="Preferred"> | ||||
|            <horstretch>0</horstretch> | ||||
|            <verstretch>0</verstretch> | ||||
|           </sizepolicy> | ||||
|          </property> | ||||
|          <property name="text"> | ||||
|           <string/> | ||||
|          </property> | ||||
|         </widget> | ||||
|        </item> | ||||
|        <item row="6" column="0"> | ||||
|         <widget class="QLabel" name="label_7"> | ||||
|          <property name="text"> | ||||
|           <string>File size:</string> | ||||
|          </property> | ||||
|         </widget> | ||||
|        </item> | ||||
|        <item row="6" column="1"> | ||||
|         <widget class="QLabel" name="fileSize"> | ||||
|          <property name="sizePolicy"> | ||||
|           <sizepolicy hsizetype="Expanding" vsizetype="Preferred"> | ||||
|            <horstretch>0</horstretch> | ||||
|            <verstretch>0</verstretch> | ||||
|           </sizepolicy> | ||||
|          </property> | ||||
|          <property name="text"> | ||||
|           <string/> | ||||
|          </property> | ||||
|         </widget> | ||||
|        </item> | ||||
|        <item row="7" column="0"> | ||||
|         <widget class="QLabel" name="label_6"> | ||||
|          <property name="text"> | ||||
|           <string>On-disk size:</string> | ||||
|          </property> | ||||
|         </widget> | ||||
|        </item> | ||||
|        <item row="7" column="1"> | ||||
|         <widget class="QLabel" name="onDiskSize"> | ||||
|          <property name="sizePolicy"> | ||||
|           <sizepolicy hsizetype="Expanding" vsizetype="Preferred"> | ||||
|            <horstretch>0</horstretch> | ||||
|            <verstretch>0</verstretch> | ||||
|           </sizepolicy> | ||||
|          </property> | ||||
|          <property name="text"> | ||||
|           <string/> | ||||
|          </property> | ||||
|         </widget> | ||||
|        </item> | ||||
|        <item row="8" column="0"> | ||||
|         <widget class="QLabel" name="label_3"> | ||||
|          <property name="text"> | ||||
|           <string>Last modified:</string> | ||||
|          </property> | ||||
|         </widget> | ||||
|        </item> | ||||
|        <item row="8" column="1"> | ||||
|         <widget class="QLabel" name="lastModified"> | ||||
|          <property name="sizePolicy"> | ||||
|           <sizepolicy hsizetype="Expanding" vsizetype="Preferred"> | ||||
|            <horstretch>0</horstretch> | ||||
|            <verstretch>0</verstretch> | ||||
|           </sizepolicy> | ||||
|          </property> | ||||
|          <property name="text"> | ||||
|           <string/> | ||||
|          </property> | ||||
|         </widget> | ||||
|        </item> | ||||
|        <item row="2" column="0"> | ||||
|         <widget class="QLabel" name="targetLabel"> | ||||
|          <property name="text"> | ||||
|           <string>Link target:</string> | ||||
|          </property> | ||||
|         </widget> | ||||
|        </item> | ||||
|        <item row="2" column="1"> | ||||
|         <widget class="QLabel" name="target"> | ||||
|          <property name="sizePolicy"> | ||||
|           <sizepolicy hsizetype="Expanding" vsizetype="Preferred"> | ||||
|            <horstretch>0</horstretch> | ||||
|            <verstretch>0</verstretch> | ||||
|           </sizepolicy> | ||||
|          </property> | ||||
|          <property name="text"> | ||||
|           <string/> | ||||
|          </property> | ||||
|          <property name="wordWrap"> | ||||
|           <bool>true</bool> | ||||
|          </property> | ||||
|         </widget> | ||||
|        </item> | ||||
|        <item row="5" column="0"> | ||||
|         <widget class="QLabel" name="openWithLabel"> | ||||
|          <property name="text"> | ||||
|           <string>Open With:</string> | ||||
|          </property> | ||||
|         </widget> | ||||
|        </item> | ||||
|        <item row="5" column="1"> | ||||
|         <widget class="Fm::AppChooserComboBox" name="openWith"> | ||||
|          <property name="sizePolicy"> | ||||
|           <sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed"> | ||||
|            <horstretch>0</horstretch> | ||||
|            <verstretch>0</verstretch> | ||||
|           </sizepolicy> | ||||
|          </property> | ||||
|         </widget> | ||||
|        </item> | ||||
|        <item row="9" column="0"> | ||||
|         <widget class="QLabel" name="label_12"> | ||||
|          <property name="text"> | ||||
|           <string>Last accessed:</string> | ||||
|          </property> | ||||
|         </widget> | ||||
|        </item> | ||||
|        <item row="9" column="1"> | ||||
|         <widget class="QLabel" name="lastAccessed"> | ||||
|          <property name="text"> | ||||
|           <string/> | ||||
|          </property> | ||||
|         </widget> | ||||
|        </item> | ||||
|       </layout> | ||||
|      </widget> | ||||
|      <widget class="QWidget" name="permissionsPage"> | ||||
|       <attribute name="title"> | ||||
|        <string>Permissions</string> | ||||
|       </attribute> | ||||
|       <layout class="QVBoxLayout" name="verticalLayout_2"> | ||||
|        <property name="spacing"> | ||||
|         <number>6</number> | ||||
|        </property> | ||||
|        <item> | ||||
|         <widget class="QGroupBox" name="groupBox_2"> | ||||
|          <property name="sizePolicy"> | ||||
|           <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> | ||||
|            <horstretch>0</horstretch> | ||||
|            <verstretch>0</verstretch> | ||||
|           </sizepolicy> | ||||
|          </property> | ||||
|          <property name="title"> | ||||
|           <string>Ownership</string> | ||||
|          </property> | ||||
|          <layout class="QFormLayout" name="formLayout"> | ||||
|           <property name="horizontalSpacing"> | ||||
|            <number>12</number> | ||||
|           </property> | ||||
|           <property name="verticalSpacing"> | ||||
|            <number>6</number> | ||||
|           </property> | ||||
|           <item row="1" column="1"> | ||||
|            <widget class="QLineEdit" name="owner"/> | ||||
|           </item> | ||||
|           <item row="2" column="1"> | ||||
|            <widget class="QLineEdit" name="ownerGroup"/> | ||||
|           </item> | ||||
|           <item row="2" column="0"> | ||||
|            <widget class="QLabel" name="label_9"> | ||||
|             <property name="sizePolicy"> | ||||
|              <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> | ||||
|               <horstretch>0</horstretch> | ||||
|               <verstretch>0</verstretch> | ||||
|              </sizepolicy> | ||||
|             </property> | ||||
|             <property name="text"> | ||||
|              <string>Group:</string> | ||||
|             </property> | ||||
|            </widget> | ||||
|           </item> | ||||
|           <item row="1" column="0"> | ||||
|            <widget class="QLabel" name="label_8"> | ||||
|             <property name="sizePolicy"> | ||||
|              <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> | ||||
|               <horstretch>0</horstretch> | ||||
|               <verstretch>0</verstretch> | ||||
|              </sizepolicy> | ||||
|             </property> | ||||
|             <property name="text"> | ||||
|              <string>Owner:</string> | ||||
|             </property> | ||||
|            </widget> | ||||
|           </item> | ||||
|          </layout> | ||||
|         </widget> | ||||
|        </item> | ||||
|        <item> | ||||
|         <widget class="QGroupBox" name="groupBox"> | ||||
|          <property name="sizePolicy"> | ||||
|           <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> | ||||
|            <horstretch>0</horstretch> | ||||
|            <verstretch>0</verstretch> | ||||
|           </sizepolicy> | ||||
|          </property> | ||||
|          <property name="title"> | ||||
|           <string>Access Control</string> | ||||
|          </property> | ||||
|          <layout class="QVBoxLayout" name="verticalLayout_3"> | ||||
|           <item> | ||||
|            <widget class="QStackedWidget" name="stackedWidget"> | ||||
|             <property name="sizePolicy"> | ||||
|              <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> | ||||
|               <horstretch>0</horstretch> | ||||
|               <verstretch>0</verstretch> | ||||
|              </sizepolicy> | ||||
|             </property> | ||||
|             <property name="currentIndex"> | ||||
|              <number>0</number> | ||||
|             </property> | ||||
|             <widget class="QWidget" name="page"> | ||||
|              <layout class="QFormLayout" name="formLayout_3"> | ||||
|               <item row="0" column="0"> | ||||
|                <widget class="QLabel" name="ownerLabel"> | ||||
|                 <property name="text"> | ||||
|                  <string>Owner:</string> | ||||
|                 </property> | ||||
|                </widget> | ||||
|               </item> | ||||
|               <item row="0" column="1"> | ||||
|                <widget class="QComboBox" name="ownerPerm"> | ||||
|                 <property name="sizePolicy"> | ||||
|                  <sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed"> | ||||
|                   <horstretch>0</horstretch> | ||||
|                   <verstretch>0</verstretch> | ||||
|                  </sizepolicy> | ||||
|                 </property> | ||||
|                </widget> | ||||
|               </item> | ||||
|               <item row="1" column="0"> | ||||
|                <widget class="QLabel" name="groupLabel"> | ||||
|                 <property name="text"> | ||||
|                  <string>Group:</string> | ||||
|                 </property> | ||||
|                </widget> | ||||
|               </item> | ||||
|               <item row="1" column="1"> | ||||
|                <widget class="QComboBox" name="groupPerm"> | ||||
|                 <property name="sizePolicy"> | ||||
|                  <sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed"> | ||||
|                   <horstretch>0</horstretch> | ||||
|                   <verstretch>0</verstretch> | ||||
|                  </sizepolicy> | ||||
|                 </property> | ||||
|                </widget> | ||||
|               </item> | ||||
|               <item row="2" column="0"> | ||||
|                <widget class="QLabel" name="otherLabel"> | ||||
|                 <property name="text"> | ||||
|                  <string>Other:</string> | ||||
|                 </property> | ||||
|                </widget> | ||||
|               </item> | ||||
|               <item row="2" column="1"> | ||||
|                <widget class="QComboBox" name="otherPerm"> | ||||
|                 <property name="sizePolicy"> | ||||
|                  <sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed"> | ||||
|                   <horstretch>0</horstretch> | ||||
|                   <verstretch>0</verstretch> | ||||
|                  </sizepolicy> | ||||
|                 </property> | ||||
|                </widget> | ||||
|               </item> | ||||
|               <item row="3" column="0" colspan="2"> | ||||
|                <widget class="QCheckBox" name="executable"> | ||||
|                 <property name="text"> | ||||
|                  <string>Make the file executable</string> | ||||
|                 </property> | ||||
|                 <property name="tristate"> | ||||
|                  <bool>true</bool> | ||||
|                 </property> | ||||
|                </widget> | ||||
|               </item> | ||||
|              </layout> | ||||
|             </widget> | ||||
|             <widget class="QWidget" name="page_2"> | ||||
|              <layout class="QGridLayout" name="gridLayout_2" columnstretch="1,0,0"> | ||||
|               <item row="0" column="0" rowspan="3"> | ||||
|                <layout class="QGridLayout" name="gridLayout"> | ||||
|                 <property name="rightMargin"> | ||||
|                  <number>0</number> | ||||
|                 </property> | ||||
|                 <property name="spacing"> | ||||
|                  <number>6</number> | ||||
|                 </property> | ||||
|                 <item row="0" column="0"> | ||||
|                  <widget class="QLabel" name="label_5"> | ||||
|                   <property name="sizePolicy"> | ||||
|                    <sizepolicy hsizetype="Fixed" vsizetype="Preferred"> | ||||
|                     <horstretch>0</horstretch> | ||||
|                     <verstretch>0</verstretch> | ||||
|                    </sizepolicy> | ||||
|                   </property> | ||||
|                   <property name="text"> | ||||
|                    <string>Owner:</string> | ||||
|                   </property> | ||||
|                  </widget> | ||||
|                 </item> | ||||
|                 <item row="0" column="1"> | ||||
|                  <widget class="QCheckBox" name="checkBox"> | ||||
|                   <property name="sizePolicy"> | ||||
|                    <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> | ||||
|                     <horstretch>0</horstretch> | ||||
|                     <verstretch>0</verstretch> | ||||
|                    </sizepolicy> | ||||
|                   </property> | ||||
|                   <property name="text"> | ||||
|                    <string>Read</string> | ||||
|                   </property> | ||||
|                  </widget> | ||||
|                 </item> | ||||
|                 <item row="0" column="2"> | ||||
|                  <widget class="QCheckBox" name="checkBox_5"> | ||||
|                   <property name="sizePolicy"> | ||||
|                    <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> | ||||
|                     <horstretch>0</horstretch> | ||||
|                     <verstretch>0</verstretch> | ||||
|                    </sizepolicy> | ||||
|                   </property> | ||||
|                   <property name="text"> | ||||
|                    <string>Write</string> | ||||
|                   </property> | ||||
|                  </widget> | ||||
|                 </item> | ||||
|                 <item row="0" column="3"> | ||||
|                  <widget class="QCheckBox" name="checkBox_8"> | ||||
|                   <property name="sizePolicy"> | ||||
|                    <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> | ||||
|                     <horstretch>0</horstretch> | ||||
|                     <verstretch>0</verstretch> | ||||
|                    </sizepolicy> | ||||
|                   </property> | ||||
|                   <property name="text"> | ||||
|                    <string>Execute</string> | ||||
|                   </property> | ||||
|                  </widget> | ||||
|                 </item> | ||||
|                 <item row="1" column="0"> | ||||
|                  <widget class="QLabel" name="label_10"> | ||||
|                   <property name="sizePolicy"> | ||||
|                    <sizepolicy hsizetype="Fixed" vsizetype="Preferred"> | ||||
|                     <horstretch>0</horstretch> | ||||
|                     <verstretch>0</verstretch> | ||||
|                    </sizepolicy> | ||||
|                   </property> | ||||
|                   <property name="text"> | ||||
|                    <string>Group:</string> | ||||
|                   </property> | ||||
|                  </widget> | ||||
|                 </item> | ||||
|                 <item row="1" column="1"> | ||||
|                  <widget class="QCheckBox" name="checkBox_4"> | ||||
|                   <property name="sizePolicy"> | ||||
|                    <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> | ||||
|                     <horstretch>0</horstretch> | ||||
|                     <verstretch>0</verstretch> | ||||
|                    </sizepolicy> | ||||
|                   </property> | ||||
|                   <property name="text"> | ||||
|                    <string>Read</string> | ||||
|                   </property> | ||||
|                  </widget> | ||||
|                 </item> | ||||
|                 <item row="1" column="2"> | ||||
|                  <widget class="QCheckBox" name="checkBox_9"> | ||||
|                   <property name="sizePolicy"> | ||||
|                    <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> | ||||
|                     <horstretch>0</horstretch> | ||||
|                     <verstretch>0</verstretch> | ||||
|                    </sizepolicy> | ||||
|                   </property> | ||||
|                   <property name="text"> | ||||
|                    <string>Write</string> | ||||
|                   </property> | ||||
|                  </widget> | ||||
|                 </item> | ||||
|                 <item row="1" column="3"> | ||||
|                  <widget class="QCheckBox" name="checkBox_3"> | ||||
|                   <property name="sizePolicy"> | ||||
|                    <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> | ||||
|                     <horstretch>0</horstretch> | ||||
|                     <verstretch>0</verstretch> | ||||
|                    </sizepolicy> | ||||
|                   </property> | ||||
|                   <property name="text"> | ||||
|                    <string>Execute</string> | ||||
|                   </property> | ||||
|                  </widget> | ||||
|                 </item> | ||||
|                 <item row="2" column="0"> | ||||
|                  <widget class="QLabel" name="label_11"> | ||||
|                   <property name="sizePolicy"> | ||||
|                    <sizepolicy hsizetype="Fixed" vsizetype="Preferred"> | ||||
|                     <horstretch>0</horstretch> | ||||
|                     <verstretch>0</verstretch> | ||||
|                    </sizepolicy> | ||||
|                   </property> | ||||
|                   <property name="text"> | ||||
|                    <string>Other:</string> | ||||
|                   </property> | ||||
|                  </widget> | ||||
|                 </item> | ||||
|                 <item row="2" column="1"> | ||||
|                  <widget class="QCheckBox" name="checkBox_7"> | ||||
|                   <property name="sizePolicy"> | ||||
|                    <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> | ||||
|                     <horstretch>0</horstretch> | ||||
|                     <verstretch>0</verstretch> | ||||
|                    </sizepolicy> | ||||
|                   </property> | ||||
|                   <property name="text"> | ||||
|                    <string>Read</string> | ||||
|                   </property> | ||||
|                  </widget> | ||||
|                 </item> | ||||
|                 <item row="2" column="2"> | ||||
|                  <widget class="QCheckBox" name="checkBox_2"> | ||||
|                   <property name="sizePolicy"> | ||||
|                    <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> | ||||
|                     <horstretch>0</horstretch> | ||||
|                     <verstretch>0</verstretch> | ||||
|                    </sizepolicy> | ||||
|                   </property> | ||||
|                   <property name="text"> | ||||
|                    <string>Write</string> | ||||
|                   </property> | ||||
|                  </widget> | ||||
|                 </item> | ||||
|                 <item row="2" column="3"> | ||||
|                  <widget class="QCheckBox" name="checkBox_6"> | ||||
|                   <property name="sizePolicy"> | ||||
|                    <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> | ||||
|                     <horstretch>0</horstretch> | ||||
|                     <verstretch>0</verstretch> | ||||
|                    </sizepolicy> | ||||
|                   </property> | ||||
|                   <property name="text"> | ||||
|                    <string>Execute</string> | ||||
|                   </property> | ||||
|                  </widget> | ||||
|                 </item> | ||||
|                </layout> | ||||
|               </item> | ||||
|               <item row="0" column="2"> | ||||
|                <widget class="QCheckBox" name="checkBox_10"> | ||||
|                 <property name="text"> | ||||
|                  <string>Sticky</string> | ||||
|                 </property> | ||||
|                </widget> | ||||
|               </item> | ||||
|               <item row="1" column="2"> | ||||
|                <widget class="QCheckBox" name="checkBox_11"> | ||||
|                 <property name="text"> | ||||
|                  <string>SetUID</string> | ||||
|                 </property> | ||||
|                </widget> | ||||
|               </item> | ||||
|               <item row="2" column="2"> | ||||
|                <widget class="QCheckBox" name="checkBox_12"> | ||||
|                 <property name="text"> | ||||
|                  <string>SetGID</string> | ||||
|                 </property> | ||||
|                </widget> | ||||
|               </item> | ||||
|               <item row="0" column="1" rowspan="3"> | ||||
|                <widget class="Line" name="line"> | ||||
|                 <property name="orientation"> | ||||
|                  <enum>Qt::Vertical</enum> | ||||
|                 </property> | ||||
|                </widget> | ||||
|               </item> | ||||
|              </layout> | ||||
|             </widget> | ||||
|            </widget> | ||||
|           </item> | ||||
|           <item> | ||||
|            <layout class="QHBoxLayout" name="horizontalLayout"> | ||||
|             <item> | ||||
|              <spacer name="horizontalSpacer"> | ||||
|               <property name="orientation"> | ||||
|                <enum>Qt::Horizontal</enum> | ||||
|               </property> | ||||
|               <property name="sizeHint" stdset="0"> | ||||
|                <size> | ||||
|                 <width>40</width> | ||||
|                 <height>20</height> | ||||
|                </size> | ||||
|               </property> | ||||
|              </spacer> | ||||
|             </item> | ||||
|             <item> | ||||
|              <widget class="QPushButton" name="pushButton"> | ||||
|               <property name="enabled"> | ||||
|                <bool>false</bool> | ||||
|               </property> | ||||
|               <property name="text"> | ||||
|                <string>Advanced Mode</string> | ||||
|               </property> | ||||
|               <property name="checkable"> | ||||
|                <bool>true</bool> | ||||
|               </property> | ||||
|              </widget> | ||||
|             </item> | ||||
|            </layout> | ||||
|           </item> | ||||
|          </layout> | ||||
|         </widget> | ||||
|        </item> | ||||
|        <item> | ||||
|         <spacer name="verticalSpacer"> | ||||
|          <property name="orientation"> | ||||
|           <enum>Qt::Vertical</enum> | ||||
|          </property> | ||||
|          <property name="sizeHint" stdset="0"> | ||||
|           <size> | ||||
|            <width>20</width> | ||||
|            <height>40</height> | ||||
|           </size> | ||||
|          </property> | ||||
|         </spacer> | ||||
|        </item> | ||||
|       </layout> | ||||
|      </widget> | ||||
|     </widget> | ||||
|    </item> | ||||
|    <item> | ||||
|     <widget class="QDialogButtonBox" name="buttonBox"> | ||||
|      <property name="orientation"> | ||||
|       <enum>Qt::Horizontal</enum> | ||||
|      </property> | ||||
|      <property name="standardButtons"> | ||||
|       <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> | ||||
|      </property> | ||||
|     </widget> | ||||
|    </item> | ||||
|   </layout> | ||||
|  </widget> | ||||
|  <customwidgets> | ||||
|   <customwidget> | ||||
|    <class>Fm::AppChooserComboBox</class> | ||||
|    <extends>QComboBox</extends> | ||||
|    <header>appchoosercombobox.h</header> | ||||
|   </customwidget> | ||||
|  </customwidgets> | ||||
|  <resources/> | ||||
|  <connections> | ||||
|   <connection> | ||||
|    <sender>buttonBox</sender> | ||||
|    <signal>accepted()</signal> | ||||
|    <receiver>FilePropsDialog</receiver> | ||||
|    <slot>accept()</slot> | ||||
|    <hints> | ||||
|     <hint type="sourcelabel"> | ||||
|      <x>248</x> | ||||
|      <y>254</y> | ||||
|     </hint> | ||||
|     <hint type="destinationlabel"> | ||||
|      <x>157</x> | ||||
|      <y>274</y> | ||||
|     </hint> | ||||
|    </hints> | ||||
|   </connection> | ||||
|   <connection> | ||||
|    <sender>buttonBox</sender> | ||||
|    <signal>rejected()</signal> | ||||
|    <receiver>FilePropsDialog</receiver> | ||||
|    <slot>reject()</slot> | ||||
|    <hints> | ||||
|     <hint type="sourcelabel"> | ||||
|      <x>316</x> | ||||
|      <y>260</y> | ||||
|     </hint> | ||||
|     <hint type="destinationlabel"> | ||||
|      <x>286</x> | ||||
|      <y>274</y> | ||||
|     </hint> | ||||
|    </hints> | ||||
|   </connection> | ||||
|  </connections> | ||||
| </ui> | ||||
							
								
								
									
										113
									
								
								libfm-qt/filelauncher.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										113
									
								
								libfm-qt/filelauncher.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,113 @@ | ||||
| /*
 | ||||
|     <one line to give the library's name and an idea of what it does.> | ||||
|     Copyright (C) 2012  Hong Jen Yee (PCMan) <pcman.tw@gmail.com> | ||||
| 
 | ||||
|     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 | ||||
| */ | ||||
| 
 | ||||
| 
 | ||||
| #include "filelauncher.h" | ||||
| #include "applaunchcontext.h" | ||||
| #include <QMessageBox> | ||||
| #include <QDebug> | ||||
| #include "execfiledialog_p.h" | ||||
| #include "appchooserdialog.h" | ||||
| #include "utilities.h" | ||||
| 
 | ||||
| using namespace Fm; | ||||
| 
 | ||||
| FmFileLauncher FileLauncher::funcs = { | ||||
|   FileLauncher::_getApp, | ||||
|   /* gboolean (*before_open)(GAppLaunchContext* ctx, GList* folder_infos, gpointer user_data); */ | ||||
|   (FmLaunchFolderFunc)FileLauncher::_openFolder, | ||||
|   FileLauncher::_execFile, | ||||
|   FileLauncher::_error, | ||||
|   FileLauncher::_ask | ||||
| }; | ||||
| 
 | ||||
| FileLauncher::FileLauncher() { | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| FileLauncher::~FileLauncher() { | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| //static
 | ||||
| bool FileLauncher::launchFiles(QWidget* parent, GList* file_infos) { | ||||
|   FmAppLaunchContext* context = fm_app_launch_context_new_for_widget(parent); | ||||
|   bool ret = fm_launch_files(G_APP_LAUNCH_CONTEXT(context), file_infos, &funcs, this); | ||||
|   g_object_unref(context); | ||||
|   return ret; | ||||
| } | ||||
| 
 | ||||
| bool FileLauncher::launchPaths(QWidget* parent, GList* paths) { | ||||
|   FmAppLaunchContext* context = fm_app_launch_context_new_for_widget(parent); | ||||
|   bool ret = fm_launch_paths(G_APP_LAUNCH_CONTEXT(context), paths, &funcs, this); | ||||
|   g_object_unref(context); | ||||
|   return ret; | ||||
| } | ||||
| 
 | ||||
| GAppInfo* FileLauncher::getApp(GList* file_infos, FmMimeType* mime_type, GError** err) { | ||||
|   AppChooserDialog dlg(NULL); | ||||
|   if(mime_type) | ||||
|     dlg.setMimeType(mime_type); | ||||
|   else | ||||
|     dlg.setCanSetDefault(false); | ||||
|   // FIXME: show error properly?
 | ||||
|   if(execModelessDialog(&dlg) == QDialog::Accepted) { | ||||
|     return dlg.selectedApp(); | ||||
|   } | ||||
|   return NULL; | ||||
| } | ||||
| 
 | ||||
| bool FileLauncher::openFolder(GAppLaunchContext* ctx, GList* folder_infos, GError** err) { | ||||
|   for(GList* l = folder_infos; l; l = l->next) { | ||||
|     FmFileInfo* fi = FM_FILE_INFO(l->data); | ||||
|     qDebug() << "  folder:" << QString::fromUtf8(fm_file_info_get_disp_name(fi)); | ||||
|   } | ||||
|   return false; | ||||
| } | ||||
| 
 | ||||
| FmFileLauncherExecAction FileLauncher::execFile(FmFileInfo* file) { | ||||
|   FmFileLauncherExecAction res = FM_FILE_LAUNCHER_EXEC_CANCEL; | ||||
|   ExecFileDialog dlg(file); | ||||
|   if(execModelessDialog(&dlg) == QDialog::Accepted) { | ||||
|     res = dlg.result(); | ||||
|   } | ||||
|   return res; | ||||
| } | ||||
| 
 | ||||
| int FileLauncher::ask(const char* msg, char* const* btn_labels, int default_btn) { | ||||
|   /* FIXME: set default button properly */ | ||||
|   // return fm_askv(data->parent, NULL, msg, btn_labels);
 | ||||
|   return -1; | ||||
| } | ||||
| 
 | ||||
| bool FileLauncher::error(GAppLaunchContext* ctx, GError* err, FmPath* path) { | ||||
|   /* ask for mount if trying to launch unmounted path */ | ||||
|   if(err->domain == G_IO_ERROR) { | ||||
|     if(path && err->code == G_IO_ERROR_NOT_MOUNTED) { | ||||
|       //if(fm_mount_path(data->parent, path, TRUE))
 | ||||
|       //  return FALSE; /* ask to retry */
 | ||||
|     } | ||||
|     else if(err->code == G_IO_ERROR_FAILED_HANDLED) | ||||
|       return true; /* don't show error message */ | ||||
|   } | ||||
|   QMessageBox dlg(QMessageBox::Critical, QObject::tr("Error"), QString::fromUtf8(err->message), QMessageBox::Ok); | ||||
|   execModelessDialog(&dlg); | ||||
|   return true; | ||||
| } | ||||
| 
 | ||||
							
								
								
									
										78
									
								
								libfm-qt/filelauncher.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										78
									
								
								libfm-qt/filelauncher.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,78 @@ | ||||
| /*
 | ||||
|     <one line to give the library's name and an idea of what it does.> | ||||
|     Copyright (C) 2012  Hong Jen Yee (PCMan) <pcman.tw@gmail.com> | ||||
| 
 | ||||
|     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 | ||||
| */ | ||||
| 
 | ||||
| 
 | ||||
| #ifndef FM_FILELAUNCHER_H | ||||
| #define FM_FILELAUNCHER_H | ||||
| 
 | ||||
| #include "libfmqtglobals.h" | ||||
| #include <QWidget> | ||||
| #include <libfm/fm.h> | ||||
| 
 | ||||
| namespace Fm { | ||||
| 
 | ||||
| class LIBFM_QT_API FileLauncher { | ||||
| public: | ||||
|   FileLauncher(); | ||||
|   virtual ~FileLauncher(); | ||||
| 
 | ||||
|   bool launchFiles(QWidget* parent, FmFileInfoList* file_infos) { | ||||
|     GList* fileList = fm_file_info_list_peek_head_link(file_infos); | ||||
|     return Fm::FileLauncher::launchFiles(parent, fileList); | ||||
|   } | ||||
|   bool launchPaths(QWidget* parent, FmPathList* paths) { | ||||
|     GList* pathList = fm_path_list_peek_head_link(paths); | ||||
|     return Fm::FileLauncher::launchPaths(parent, pathList); | ||||
|   } | ||||
| 
 | ||||
|   bool launchFiles(QWidget* parent, GList* file_infos); | ||||
|   bool launchPaths(QWidget* parent, GList* paths); | ||||
| 
 | ||||
| protected: | ||||
| 
 | ||||
|   virtual GAppInfo* getApp(GList* file_infos, FmMimeType* mime_type, GError** err); | ||||
|   virtual bool openFolder(GAppLaunchContext* ctx, GList* folder_infos, GError** err); | ||||
|   virtual FmFileLauncherExecAction execFile(FmFileInfo* file); | ||||
|   virtual bool error(GAppLaunchContext* ctx, GError* err, FmPath* path); | ||||
|   virtual int ask(const char* msg, char* const* btn_labels, int default_btn); | ||||
| 
 | ||||
| private: | ||||
|   static GAppInfo* _getApp(GList* file_infos, FmMimeType* mime_type, gpointer user_data, GError** err) { | ||||
|      return reinterpret_cast<FileLauncher*>(user_data)->getApp(file_infos, mime_type, err); | ||||
|   } | ||||
|   static gboolean _openFolder(GAppLaunchContext* ctx, GList* folder_infos, gpointer user_data, GError** err) { | ||||
|     return reinterpret_cast<FileLauncher*>(user_data)->openFolder(ctx, folder_infos, err); | ||||
|   } | ||||
|   static FmFileLauncherExecAction _execFile(FmFileInfo* file, gpointer user_data) { | ||||
|     return reinterpret_cast<FileLauncher*>(user_data)->execFile(file); | ||||
|   } | ||||
|   static gboolean _error(GAppLaunchContext* ctx, GError* err, FmPath* file, gpointer user_data) { | ||||
|     return reinterpret_cast<FileLauncher*>(user_data)->error(ctx, err, file); | ||||
|   } | ||||
|   static int _ask(const char* msg, char* const* btn_labels, int default_btn, gpointer user_data) { | ||||
|     return reinterpret_cast<FileLauncher*>(user_data)->ask(msg, btn_labels, default_btn); | ||||
|   } | ||||
| 
 | ||||
| private: | ||||
|   static FmFileLauncher funcs; | ||||
| }; | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| #endif // FM_FILELAUNCHER_H
 | ||||
							
								
								
									
										370
									
								
								libfm-qt/filemenu.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										370
									
								
								libfm-qt/filemenu.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,370 @@ | ||||
| /*
 | ||||
|     <one line to give the library's name and an idea of what it does.> | ||||
|     Copyright (C) 2012  Hong Jen Yee (PCMan) <pcman.tw@gmail.com> | ||||
| 
 | ||||
|     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 | ||||
| */ | ||||
| 
 | ||||
| 
 | ||||
| #include "filemenu.h" | ||||
| #include "icontheme.h" | ||||
| #include "filepropsdialog.h" | ||||
| #include "utilities.h" | ||||
| #include "fileoperation.h" | ||||
| #include "filelauncher.h" | ||||
| #include "appchooserdialog.h" | ||||
| #ifdef CUSTOM_ACTIONS | ||||
| #include <libfm/fm-actions.h> | ||||
| #endif | ||||
| #include <QMessageBox> | ||||
| #include <QDebug> | ||||
| #include "filemenu_p.h" | ||||
| 
 | ||||
| namespace Fm { | ||||
| 
 | ||||
| FileMenu::FileMenu(FmFileInfoList* files, FmFileInfo* info, FmPath* cwd, QWidget* parent): | ||||
|   QMenu(parent), | ||||
|   fileLauncher_(NULL) { | ||||
|   createMenu(files, info, cwd); | ||||
| } | ||||
| 
 | ||||
| FileMenu::FileMenu(FmFileInfoList* files, FmFileInfo* info, FmPath* cwd, const QString& title, QWidget* parent): | ||||
|   QMenu(title, parent), | ||||
|   fileLauncher_(NULL), | ||||
|   unTrashAction_(NULL) { | ||||
|   createMenu(files, info, cwd); | ||||
| } | ||||
| 
 | ||||
| FileMenu::~FileMenu() { | ||||
|   if(files_) | ||||
|     fm_file_info_list_unref(files_); | ||||
|   if(info_) | ||||
|     fm_file_info_unref(info_); | ||||
|   if(cwd_) | ||||
|     fm_path_unref(cwd_); | ||||
| } | ||||
| 
 | ||||
| void FileMenu::createMenu(FmFileInfoList* files, FmFileInfo* info, FmPath* cwd) { | ||||
|   useTrash_ = true; | ||||
|   confirmDelete_ = true; | ||||
|   files_ = fm_file_info_list_ref(files); | ||||
|   info_ = info ? fm_file_info_ref(info) : NULL; | ||||
|   cwd_ = cwd ? fm_path_ref(cwd) : NULL; | ||||
| 
 | ||||
|   FmFileInfo* first = fm_file_info_list_peek_head(files); | ||||
|   FmMimeType* mime_type = fm_file_info_get_mime_type(first); | ||||
|   FmPath* path = fm_file_info_get_path(first); | ||||
|   // check if the files are of the same type
 | ||||
|   sameType_ = fm_file_info_list_is_same_type(files); | ||||
|   // check if the files are on the same filesystem
 | ||||
|   sameFilesystem_ = fm_file_info_list_is_same_fs(files); | ||||
|   // check if the files are all virtual
 | ||||
|   allVirtual_ = sameFilesystem_ && fm_path_is_virtual(path); | ||||
|   // check if the files are all in the trash can
 | ||||
|   allTrash_ =  sameFilesystem_ && fm_path_is_trash(path); | ||||
| 
 | ||||
|   openAction_ = new QAction(QIcon::fromTheme("document-open"), tr("Open"), this); | ||||
|   connect(openAction_ , &QAction::triggered, this, &FileMenu::onOpenTriggered); | ||||
|   addAction(openAction_); | ||||
| 
 | ||||
|   openWithMenuAction_ = new QAction(tr("Open With..."), this); | ||||
|   addAction(openWithMenuAction_); | ||||
|   // create the "Open with..." sub menu
 | ||||
|   QMenu* menu = new QMenu(); | ||||
|   openWithMenuAction_->setMenu(menu); | ||||
| 
 | ||||
|   if(sameType_) { /* add specific menu items for this mime type */ | ||||
|     if(mime_type && !allVirtual_) { /* the file has a valid mime-type and its not virtual */ | ||||
|       GList* apps = g_app_info_get_all_for_type(fm_mime_type_get_type(mime_type)); | ||||
|       GList* l; | ||||
|       for(l=apps;l;l=l->next) { | ||||
|         GAppInfo* app = G_APP_INFO(l->data); | ||||
|        | ||||
|         // check if the command really exists
 | ||||
|         gchar * program_path = g_find_program_in_path(g_app_info_get_executable(app)); | ||||
|         if (!program_path) | ||||
|           continue; | ||||
|         g_free(program_path); | ||||
| 
 | ||||
|         // create a QAction for the application.
 | ||||
|         AppInfoAction* action = new AppInfoAction(app); | ||||
|         connect(action, &QAction::triggered, this, &FileMenu::onApplicationTriggered); | ||||
|         menu->addAction(action); | ||||
|       } | ||||
|       g_list_free(apps); /* don't unref GAppInfos now */ | ||||
|     } | ||||
|   } | ||||
|   menu->addSeparator(); | ||||
|   openWithAction_ = new QAction(tr("Other Applications"), this); | ||||
|   connect(openWithAction_ , &QAction::triggered, this, &FileMenu::onOpenWithTriggered); | ||||
|   menu->addAction(openWithAction_); | ||||
| 
 | ||||
|   separator1_ = addSeparator(); | ||||
| 
 | ||||
|   if(allTrash_) { // all selected files are in trash:///
 | ||||
|     bool can_restore = true; | ||||
|     /* only immediate children of trash:/// can be restored. */ | ||||
|     for(GList* l = fm_file_info_list_peek_head_link(files_); l; l=l->next) { | ||||
|         FmPath *trash_path = fm_file_info_get_path(FM_FILE_INFO(l->data)); | ||||
|         if(!fm_path_get_parent(trash_path) || | ||||
|            !fm_path_is_trash_root(fm_path_get_parent(trash_path))) { | ||||
|             can_restore = false; | ||||
|             break; | ||||
|         } | ||||
|     } | ||||
|     if(can_restore) { | ||||
|       unTrashAction_ = new QAction(tr("&Restore"), this); | ||||
|       connect(unTrashAction_, &QAction::triggered, this, &FileMenu::onUnTrashTriggered); | ||||
|       addAction(unTrashAction_); | ||||
|     } | ||||
|   } | ||||
|   else { // ordinary files
 | ||||
|     cutAction_ = new QAction(QIcon::fromTheme("edit-cut"), tr("Cut"), this); | ||||
|     connect(cutAction_, &QAction::triggered, this, &FileMenu::onCutTriggered); | ||||
|     addAction(cutAction_); | ||||
| 
 | ||||
|     copyAction_ = new QAction(QIcon::fromTheme("edit-copy"), tr("Copy"), this); | ||||
|     connect(copyAction_, &QAction::triggered, this, &FileMenu::onCopyTriggered); | ||||
|     addAction(copyAction_); | ||||
| 
 | ||||
|     pasteAction_ = new QAction(QIcon::fromTheme("edit-paste"), tr("Paste"), this); | ||||
|     connect(pasteAction_, &QAction::triggered, this, &FileMenu::onPasteTriggered); | ||||
|     addAction(pasteAction_); | ||||
| 
 | ||||
|     deleteAction_ = new QAction(QIcon::fromTheme("edit-delete"), tr("&Move to Trash"), this); | ||||
|     connect(deleteAction_, &QAction::triggered, this, &FileMenu::onDeleteTriggered); | ||||
|     addAction(deleteAction_); | ||||
| 
 | ||||
|     renameAction_ = new QAction(tr("Rename"), this); | ||||
|     connect(renameAction_, &QAction::triggered, this, &FileMenu::onRenameTriggered); | ||||
|     addAction(renameAction_); | ||||
|   } | ||||
| 
 | ||||
| #ifdef CUSTOM_ACTIONS | ||||
|   // DES-EMA custom actions integration
 | ||||
|   GList* files_list = fm_file_info_list_peek_head_link(files); | ||||
|   GList* items = fm_get_actions_for_files(files_list); | ||||
|   if(items) { | ||||
|     GList* l; | ||||
|     for(l=items; l; l=l->next) { | ||||
|       FmFileActionItem* item = FM_FILE_ACTION_ITEM(l->data); | ||||
|       addCustomActionItem(this, item); | ||||
|     } | ||||
|   }  | ||||
|   g_list_foreach(items, (GFunc)fm_file_action_item_unref, NULL); | ||||
|   g_list_free(items); | ||||
| #endif | ||||
|   // archiver integration
 | ||||
|   // FIXME: we need to modify upstream libfm to include some Qt-based archiver programs.
 | ||||
|   if(!allVirtual_) { | ||||
|     if(sameType_) { | ||||
|       FmArchiver* archiver = fm_archiver_get_default(); | ||||
|       if(archiver) { | ||||
|         if(fm_archiver_is_mime_type_supported(archiver, fm_mime_type_get_type(mime_type))) { | ||||
|           if(cwd_ && archiver->extract_to_cmd) { | ||||
|             QAction* action = new QAction(tr("Extract to..."), this); | ||||
|             connect(action, &QAction::triggered, this, &FileMenu::onExtract); | ||||
|             addAction(action); | ||||
|           } | ||||
|           if(archiver->extract_cmd) { | ||||
|             QAction* action = new QAction(tr("Extract Here"), this); | ||||
|             connect(action, &QAction::triggered, this, &FileMenu::onExtractHere); | ||||
|             addAction(action); | ||||
|           } | ||||
|         } | ||||
|         else { | ||||
|           QAction* action = new QAction(tr("Compress"), this); | ||||
|           connect(action, &QAction::triggered, this, &FileMenu::onCompress); | ||||
|           addAction(action); | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   separator2_ = addSeparator(); | ||||
| 
 | ||||
|   propertiesAction_ = new QAction(QIcon::fromTheme("document-properties"), tr("Properties"), this); | ||||
|   connect(propertiesAction_, &QAction::triggered, this, &FileMenu::onFilePropertiesTriggered); | ||||
|   addAction(propertiesAction_); | ||||
| } | ||||
| 
 | ||||
| #ifdef CUSTOM_ACTIONS | ||||
| void FileMenu::addCustomActionItem(QMenu* menu, FmFileActionItem* item) { | ||||
|   if(!item) { // separator
 | ||||
|     addSeparator(); | ||||
|     return; | ||||
|   } | ||||
| 
 | ||||
|   // this action is not for context menu
 | ||||
|   if(fm_file_action_item_is_action(item) && !(fm_file_action_item_get_target(item) & FM_FILE_ACTION_TARGET_CONTEXT)) | ||||
|       return; | ||||
| 
 | ||||
|   CustomAction* action = new CustomAction(item, menu); | ||||
|   menu->addAction(action); | ||||
|   if(fm_file_action_item_is_menu(item)) { | ||||
|     GList* subitems = fm_file_action_item_get_sub_items(item); | ||||
|     for(GList* l = subitems; l; l = l->next) { | ||||
|       FmFileActionItem* subitem = FM_FILE_ACTION_ITEM(l->data); | ||||
|       QMenu* submenu = new QMenu(menu); | ||||
|       addCustomActionItem(submenu, subitem); | ||||
|       action->setMenu(submenu); | ||||
|     } | ||||
|   } | ||||
|   else if(fm_file_action_item_is_action(item)) { | ||||
|     connect(action, &QAction::triggered, this, &FileMenu::onCustomActionTrigerred); | ||||
|   } | ||||
| } | ||||
| #endif | ||||
| 
 | ||||
| void FileMenu::onOpenTriggered() { | ||||
|   if(fileLauncher_) { | ||||
|     fileLauncher_->launchFiles(NULL, files_); | ||||
|   } | ||||
|   else { // use the default launcher
 | ||||
|     Fm::FileLauncher launcher; | ||||
|     launcher.launchFiles(NULL, files_); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| void FileMenu::onOpenWithTriggered() { | ||||
|   AppChooserDialog dlg(NULL); | ||||
|   if(sameType_) { | ||||
|     dlg.setMimeType(fm_file_info_get_mime_type(info_)); | ||||
|   } | ||||
|   else { // we can only set the selected app as default if all files are of the same type
 | ||||
|     dlg.setCanSetDefault(false); | ||||
|   } | ||||
|    | ||||
|   if(execModelessDialog(&dlg) == QDialog::Accepted) { | ||||
|     GAppInfo* app = dlg.selectedApp(); | ||||
|     if(app) { | ||||
|       openFilesWithApp(app); | ||||
|       g_object_unref(app); | ||||
|     } | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| void FileMenu::openFilesWithApp(GAppInfo* app) { | ||||
|   FmPathList* paths = fm_path_list_new_from_file_info_list(files_); | ||||
|   GList* uris = NULL; | ||||
|   for(GList* l = fm_path_list_peek_head_link(paths); l; l = l->next) { | ||||
|     FmPath* path = FM_PATH(l->data); | ||||
|     char* uri = fm_path_to_uri(path); | ||||
|     uris = g_list_prepend(uris, uri); | ||||
|   } | ||||
|   fm_path_list_unref(paths); | ||||
|   fm_app_info_launch_uris(app, uris, NULL, NULL); | ||||
|   g_list_foreach(uris, (GFunc)g_free, NULL); | ||||
|   g_list_free(uris);   | ||||
| } | ||||
| 
 | ||||
| void FileMenu::onApplicationTriggered() { | ||||
|   AppInfoAction* action = static_cast<AppInfoAction*>(sender()); | ||||
|   openFilesWithApp(action->appInfo()); | ||||
| } | ||||
| 
 | ||||
| #ifdef CUSTOM_ACTIONS | ||||
| void FileMenu::onCustomActionTrigerred() { | ||||
|   CustomAction* action = static_cast<CustomAction*>(sender()); | ||||
|   FmFileActionItem* item = action->item(); | ||||
| 
 | ||||
|   GList* files = fm_file_info_list_peek_head_link(files_); | ||||
|   char* output = NULL; | ||||
|   /* g_debug("item: %s is activated, id:%s", fm_file_action_item_get_name(item),
 | ||||
|       fm_file_action_item_get_id(item)); */ | ||||
|   fm_file_action_item_launch(item, NULL, files, &output); | ||||
|   if(output) { | ||||
|     QMessageBox::information(this, tr("Output"), QString::fromUtf8(output)); | ||||
|     g_free(output); | ||||
|   } | ||||
| } | ||||
| #endif | ||||
| 
 | ||||
| void FileMenu::onFilePropertiesTriggered() { | ||||
|   FilePropsDialog::showForFiles(files_); | ||||
| } | ||||
| 
 | ||||
| void FileMenu::onCopyTriggered() { | ||||
|   FmPathList* paths = fm_path_list_new_from_file_info_list(files_); | ||||
|   Fm::copyFilesToClipboard(paths); | ||||
|   fm_path_list_unref(paths); | ||||
| } | ||||
| 
 | ||||
| void FileMenu::onCutTriggered() { | ||||
|   FmPathList* paths = fm_path_list_new_from_file_info_list(files_); | ||||
|   Fm::cutFilesToClipboard(paths); | ||||
|   fm_path_list_unref(paths); | ||||
| } | ||||
| 
 | ||||
| void FileMenu::onDeleteTriggered() { | ||||
|   FmPathList* paths = fm_path_list_new_from_file_info_list(files_); | ||||
|   if(useTrash_) | ||||
|     FileOperation::trashFiles(paths, confirmDelete_); | ||||
|   else | ||||
|     FileOperation::deleteFiles(paths, confirmDelete_); | ||||
|   fm_path_list_unref(paths); | ||||
| } | ||||
| 
 | ||||
| void FileMenu::onUnTrashTriggered() { | ||||
|   FmPathList* paths = fm_path_list_new_from_file_info_list(files_); | ||||
|   FileOperation::unTrashFiles(paths); | ||||
| } | ||||
| 
 | ||||
| void FileMenu::onPasteTriggered() { | ||||
|   Fm::pasteFilesFromClipboard(cwd_); | ||||
| } | ||||
| 
 | ||||
| void FileMenu::onRenameTriggered() { | ||||
|   for(GList* l = fm_file_info_list_peek_head_link(files_); l; l = l->next) { | ||||
|     FmFileInfo* info = FM_FILE_INFO(l->data); | ||||
|     Fm::renameFile(info, NULL); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| void FileMenu::setUseTrash(bool trash) { | ||||
|   if(useTrash_ != trash) { | ||||
|     useTrash_ = trash; | ||||
|     deleteAction_->setText(useTrash_ ? tr("&Move to Trash") : tr("&Delete")); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| void FileMenu::onCompress() { | ||||
|   FmArchiver* archiver = fm_archiver_get_default(); | ||||
|   if(archiver) { | ||||
|     FmPathList* paths = fm_path_list_new_from_file_info_list(files_); | ||||
|     fm_archiver_create_archive(archiver, NULL, paths); | ||||
|     fm_path_list_unref(paths); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| void FileMenu::onExtract() { | ||||
|   FmArchiver* archiver = fm_archiver_get_default(); | ||||
|   if(archiver) { | ||||
|     FmPathList* paths = fm_path_list_new_from_file_info_list(files_); | ||||
|     fm_archiver_extract_archives(archiver, NULL, paths); | ||||
|     fm_path_list_unref(paths); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| void FileMenu::onExtractHere() { | ||||
|   FmArchiver* archiver = fm_archiver_get_default(); | ||||
|   if(archiver) { | ||||
|     FmPathList* paths = fm_path_list_new_from_file_info_list(files_); | ||||
|     fm_archiver_extract_archives_to(archiver, NULL, paths, cwd_); | ||||
|     fm_path_list_unref(paths); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| } // namespace Fm
 | ||||
							
								
								
									
										198
									
								
								libfm-qt/filemenu.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										198
									
								
								libfm-qt/filemenu.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,198 @@ | ||||
| /*
 | ||||
|     <one line to give the library's name and an idea of what it does.> | ||||
|     Copyright (C) 2012  Hong Jen Yee (PCMan) <pcman.tw@gmail.com> | ||||
| 
 | ||||
|     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 | ||||
| */ | ||||
| 
 | ||||
| 
 | ||||
| #ifndef FM_FILEMENU_H | ||||
| #define FM_FILEMENU_H | ||||
| 
 | ||||
| #include "libfmqtglobals.h" | ||||
| #include <QMenu> | ||||
| #include <qabstractitemmodel.h> | ||||
| #include <libfm/fm.h> | ||||
| 
 | ||||
| class QAction; | ||||
| 
 | ||||
| struct _FmFileActionItem; | ||||
| 
 | ||||
| namespace Fm { | ||||
| 
 | ||||
| class FileLauncher; | ||||
|    | ||||
| class LIBFM_QT_API FileMenu : public QMenu { | ||||
| Q_OBJECT | ||||
| 
 | ||||
| public: | ||||
|   explicit FileMenu(FmFileInfoList* files, FmFileInfo* info, FmPath* cwd, QWidget* parent = 0); | ||||
|   explicit FileMenu(FmFileInfoList* files, FmFileInfo* info, FmPath* cwd, const QString& title, QWidget* parent = 0); | ||||
|   ~FileMenu(); | ||||
| 
 | ||||
|   bool useTrash() { | ||||
|     return useTrash_; | ||||
|   } | ||||
|    | ||||
|   void setUseTrash(bool trash); | ||||
|    | ||||
|   bool confirmDelete() { | ||||
|     return confirmDelete_; | ||||
|   } | ||||
| 
 | ||||
|   void setConfirmDelete(bool confirm) { | ||||
|     confirmDelete_ = confirm; | ||||
|   } | ||||
|    | ||||
|   QAction* openAction() { | ||||
|     return openAction_;   | ||||
|   } | ||||
| 
 | ||||
|   QAction* openWithMenuAction() { | ||||
|     return openWithMenuAction_;   | ||||
|   } | ||||
| 
 | ||||
|   QAction* openWithAction() { | ||||
|     return openWithAction_;   | ||||
|   } | ||||
|    | ||||
|   QAction* separator1() { | ||||
|     return separator1_;   | ||||
|   } | ||||
| 
 | ||||
|   QAction* cutAction() { | ||||
|     return cutAction_;   | ||||
|   } | ||||
| 
 | ||||
|   QAction* copyAction() { | ||||
|     return copyAction_;   | ||||
|   } | ||||
| 
 | ||||
|   QAction* pasteAction() { | ||||
|     return pasteAction_; | ||||
|   } | ||||
|    | ||||
|   QAction* deleteAction() { | ||||
|     return deleteAction_;   | ||||
|   } | ||||
| 
 | ||||
|   QAction* unTrashAction() { | ||||
|     return unTrashAction_;   | ||||
|   } | ||||
| 
 | ||||
|   QAction* renameAction() { | ||||
|     return renameAction_;   | ||||
|   } | ||||
| 
 | ||||
|   QAction* separator2() { | ||||
|     return separator2_;   | ||||
|   } | ||||
| 
 | ||||
|   QAction* propertiesAction() { | ||||
|     return propertiesAction_;   | ||||
|   } | ||||
| 
 | ||||
|   FmFileInfoList* files() { | ||||
|     return files_; | ||||
|   } | ||||
| 
 | ||||
|   FmFileInfo* firstFile() { | ||||
|     return info_; | ||||
|   } | ||||
| 
 | ||||
|   FmPath* cwd() { | ||||
|     return cwd_; | ||||
|   } | ||||
|    | ||||
|   void setFileLauncher(FileLauncher* launcher) { | ||||
|     fileLauncher_ = launcher; | ||||
|   } | ||||
| 
 | ||||
|   FileLauncher* fileLauncher() { | ||||
|     return fileLauncher_; | ||||
|   } | ||||
| 
 | ||||
|   bool sameType() const { | ||||
|     return sameType_; | ||||
|   } | ||||
| 
 | ||||
|   bool sameFilesystem() const { | ||||
|     return sameFilesystem_; | ||||
|   } | ||||
| 
 | ||||
|   bool allVirtual() const { | ||||
|     return allVirtual_; | ||||
|   } | ||||
| 
 | ||||
|   bool allTrash() const { | ||||
|     return allTrash_; | ||||
|   } | ||||
|    | ||||
| protected: | ||||
|   void createMenu(FmFileInfoList* files, FmFileInfo* info, FmPath* cwd); | ||||
| #ifdef CUSTOM_ACTIONS | ||||
|   void addCustomActionItem(QMenu* menu, struct _FmFileActionItem* item); | ||||
| #endif | ||||
|   void openFilesWithApp(GAppInfo* app); | ||||
| 
 | ||||
| protected Q_SLOTS: | ||||
|   void onOpenTriggered(); | ||||
|   void onOpenWithTriggered(); | ||||
|   void onFilePropertiesTriggered(); | ||||
|   void onApplicationTriggered(); | ||||
| #ifdef CUSTOM_ACTIONS | ||||
|   void onCustomActionTrigerred(); | ||||
| #endif | ||||
|   void onCompress(); | ||||
|   void onExtract(); | ||||
|   void onExtractHere(); | ||||
|    | ||||
|   void onCutTriggered(); | ||||
|   void onCopyTriggered(); | ||||
|   void onPasteTriggered(); | ||||
|   void onRenameTriggered(); | ||||
|   void onDeleteTriggered(); | ||||
|   void onUnTrashTriggered(); | ||||
| 
 | ||||
| private: | ||||
|   FmFileInfoList* files_; | ||||
|   FmFileInfo* info_; | ||||
|   FmPath* cwd_; | ||||
|   bool useTrash_; | ||||
|   bool confirmDelete_; | ||||
|   bool sameType_; | ||||
|   bool sameFilesystem_; | ||||
|   bool allVirtual_; | ||||
|   bool allTrash_; | ||||
| 
 | ||||
|   QAction* openAction_; | ||||
|   QAction* openWithMenuAction_; | ||||
|   QAction* openWithAction_; | ||||
|   QAction* separator1_; | ||||
|   QAction* cutAction_; | ||||
|   QAction* copyAction_; | ||||
|   QAction* pasteAction_; | ||||
|   QAction* deleteAction_; | ||||
|   QAction* unTrashAction_; | ||||
|   QAction* renameAction_; | ||||
|   QAction* separator2_; | ||||
|   QAction* propertiesAction_; | ||||
| 
 | ||||
|   FileLauncher* fileLauncher_; | ||||
| }; | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| #endif // FM_FILEMENU_H
 | ||||
							
								
								
									
										84
									
								
								libfm-qt/filemenu_p.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										84
									
								
								libfm-qt/filemenu_p.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,84 @@ | ||||
| /*
 | ||||
|     <one line to give the library's name and an idea of what it does.> | ||||
|     Copyright (C) 2012  Hong Jen Yee (PCMan) <pcman.tw@gmail.com> | ||||
| 
 | ||||
|     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 | ||||
| */ | ||||
| 
 | ||||
| #ifndef FM_FILEMENU_P_H | ||||
| #define FM_FILEMENU_P_H | ||||
| 
 | ||||
| #include "icontheme.h" | ||||
| #ifdef CUSTOM_ACTIONS | ||||
| #include <libfm/fm-actions.h> | ||||
| #endif | ||||
| #include <QDebug> | ||||
| 
 | ||||
| namespace Fm { | ||||
| 
 | ||||
| class AppInfoAction : public QAction { | ||||
|   Q_OBJECT | ||||
| public: | ||||
|   explicit AppInfoAction(GAppInfo* app, QObject* parent = 0): | ||||
|     QAction(QString::fromUtf8(g_app_info_get_name(app)), parent), | ||||
|     appInfo_(G_APP_INFO(g_object_ref(app))) { | ||||
|     setToolTip(QString::fromUtf8(g_app_info_get_description(app))); | ||||
|     GIcon* gicon = g_app_info_get_icon(app); | ||||
|     QIcon icon = IconTheme::icon(gicon); | ||||
|     setIcon(icon); | ||||
|   } | ||||
| 
 | ||||
|   virtual ~AppInfoAction() { | ||||
|     if(appInfo_) | ||||
|       g_object_unref(appInfo_); | ||||
|   } | ||||
| 
 | ||||
|   GAppInfo* appInfo() const { | ||||
|     return appInfo_; | ||||
|   } | ||||
| 
 | ||||
| private: | ||||
|   GAppInfo* appInfo_; | ||||
| }; | ||||
| 
 | ||||
| #ifdef CUSTOM_ACTIONS | ||||
| class CustomAction : public QAction { | ||||
|   Q_OBJECT | ||||
| public: | ||||
|   explicit CustomAction(FmFileActionItem* item, QObject* parent = NULL): | ||||
|     QAction(QString::fromUtf8(fm_file_action_item_get_name(item)), parent), | ||||
|     item_(reinterpret_cast<FmFileActionItem*>(fm_file_action_item_ref(item))) { | ||||
|     const char* icon_name = fm_file_action_item_get_icon(item); | ||||
|     if(icon_name) | ||||
|       setIcon(QIcon::fromTheme(icon_name)); | ||||
|   } | ||||
| 
 | ||||
|   virtual ~CustomAction() { | ||||
|     fm_file_action_item_unref(item_); | ||||
|   } | ||||
| 
 | ||||
|   FmFileActionItem* item() { | ||||
|     return item_; | ||||
|   } | ||||
| 
 | ||||
| private: | ||||
|   FmFileActionItem* item_; | ||||
| }; | ||||
| 
 | ||||
| #endif | ||||
| 
 | ||||
| } // namespace Fm
 | ||||
| 
 | ||||
| #endif | ||||
							
								
								
									
										303
									
								
								libfm-qt/fileoperation.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										303
									
								
								libfm-qt/fileoperation.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,303 @@ | ||||
| /*
 | ||||
| 
 | ||||
|     Copyright (C) 2013  Hong Jen Yee (PCMan) <pcman.tw@gmail.com> | ||||
| 
 | ||||
|     This program is free software; you can redistribute it and/or modify | ||||
|     it under the terms of the GNU General Public License as published by | ||||
|     the Free Software Foundation; either version 2 of the License, or | ||||
|     (at your option) any later version. | ||||
| 
 | ||||
|     This program is distributed in the hope that it will be useful, | ||||
|     but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|     GNU General Public License for more details. | ||||
| 
 | ||||
|     You should have received a copy of the GNU General Public License along | ||||
|     with this program; if not, write to the Free Software Foundation, Inc., | ||||
|     51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||||
| */ | ||||
| 
 | ||||
| 
 | ||||
| #include "fileoperation.h" | ||||
| #include "fileoperationdialog.h" | ||||
| #include <QTimer> | ||||
| #include <QElapsedTimer> | ||||
| #include <QMessageBox> | ||||
| #include <QDebug> | ||||
| 
 | ||||
| using namespace Fm; | ||||
| 
 | ||||
| #define SHOW_DLG_DELAY  1000 | ||||
| 
 | ||||
| FileOperation::FileOperation(Type type, FmPathList* srcFiles, QObject* parent): | ||||
|   QObject(parent), | ||||
|   dlg(NULL), | ||||
|   destPath(NULL), | ||||
|   srcPaths(fm_path_list_ref(srcFiles)), | ||||
|   uiTimer(NULL), | ||||
|   elapsedTimer_(NULL), | ||||
|   lastElapsed_(0), | ||||
|   updateRemainingTime_(true), | ||||
|   autoDestroy_(true), | ||||
|   job_(fm_file_ops_job_new((FmFileOpType)type, srcFiles)) { | ||||
| 
 | ||||
|   g_signal_connect(job_, "ask", G_CALLBACK(onFileOpsJobAsk), this); | ||||
|   g_signal_connect(job_, "ask-rename", G_CALLBACK(onFileOpsJobAskRename), this); | ||||
|   g_signal_connect(job_, "error", G_CALLBACK(onFileOpsJobError), this); | ||||
|   g_signal_connect(job_, "prepared", G_CALLBACK(onFileOpsJobPrepared), this); | ||||
|   g_signal_connect(job_, "cur-file", G_CALLBACK(onFileOpsJobCurFile), this); | ||||
|   g_signal_connect(job_, "percent", G_CALLBACK(onFileOpsJobPercent), this); | ||||
|   g_signal_connect(job_, "finished", G_CALLBACK(onFileOpsJobFinished), this); | ||||
|   g_signal_connect(job_, "cancelled", G_CALLBACK(onFileOpsJobCancelled), this); | ||||
| } | ||||
| 
 | ||||
| void FileOperation::disconnectJob() { | ||||
|   g_signal_handlers_disconnect_by_func(job_, (gpointer)G_CALLBACK(onFileOpsJobAsk), this); | ||||
|   g_signal_handlers_disconnect_by_func(job_, (gpointer)G_CALLBACK(onFileOpsJobAskRename), this); | ||||
|   g_signal_handlers_disconnect_by_func(job_, (gpointer)G_CALLBACK(onFileOpsJobError), this); | ||||
|   g_signal_handlers_disconnect_by_func(job_, (gpointer)G_CALLBACK(onFileOpsJobPrepared), this); | ||||
|   g_signal_handlers_disconnect_by_func(job_, (gpointer)G_CALLBACK(onFileOpsJobCurFile), this); | ||||
|   g_signal_handlers_disconnect_by_func(job_, (gpointer)G_CALLBACK(onFileOpsJobPercent), this); | ||||
|   g_signal_handlers_disconnect_by_func(job_, (gpointer)G_CALLBACK(onFileOpsJobFinished), this); | ||||
|   g_signal_handlers_disconnect_by_func(job_, (gpointer)G_CALLBACK(onFileOpsJobCancelled), this); | ||||
| } | ||||
| 
 | ||||
| FileOperation::~FileOperation() { | ||||
|   if(uiTimer) { | ||||
|     uiTimer->stop(); | ||||
|     delete uiTimer; | ||||
|     uiTimer = NULL; | ||||
|   } | ||||
|   if(elapsedTimer_) { | ||||
|     delete elapsedTimer_; | ||||
|     elapsedTimer_ = NULL; | ||||
|   } | ||||
| 
 | ||||
|   if(job_) { | ||||
|     disconnectJob(); | ||||
|     g_object_unref(job_); | ||||
|   } | ||||
|    | ||||
|   if(srcPaths) | ||||
|     fm_path_list_unref(srcPaths); | ||||
| 
 | ||||
|   if(destPath) | ||||
|     fm_path_unref(destPath); | ||||
| } | ||||
| 
 | ||||
| bool FileOperation::run() { | ||||
|   // run the job
 | ||||
|   uiTimer = new QTimer(); | ||||
|   uiTimer->start(SHOW_DLG_DELAY); | ||||
|   connect(uiTimer, &QTimer::timeout, this, &FileOperation::onUiTimeout); | ||||
| 
 | ||||
|   return fm_job_run_async(FM_JOB(job_)); | ||||
| } | ||||
| 
 | ||||
| void FileOperation::onUiTimeout() { | ||||
|   if(dlg) { | ||||
|     dlg->setCurFile(curFile); | ||||
|     // estimate remaining time based on past history
 | ||||
|     // FIXME: avoid directly access data member of FmFileOpsJob
 | ||||
|     if(Q_LIKELY(job_->percent > 0 && updateRemainingTime_)) { | ||||
|       gint64 remaining = elapsedTime() * ((double(100 - job_->percent) / job_->percent) / 1000); | ||||
|       dlg->setRemainingTime(remaining); | ||||
|     } | ||||
|     // this timeout slot is called every 0.5 second.
 | ||||
|     // by adding this flag, we can update remaining time every 1 second.
 | ||||
|     updateRemainingTime_ = !updateRemainingTime_; | ||||
|   } | ||||
|   else{ | ||||
|     showDialog(); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| void FileOperation::showDialog() { | ||||
|   if(!dlg) { | ||||
|     dlg = new FileOperationDialog(this); | ||||
|     dlg->setSourceFiles(srcPaths); | ||||
| 
 | ||||
|     if(destPath) | ||||
|       dlg->setDestPath(destPath); | ||||
| 
 | ||||
|     if(curFile.isEmpty()) { | ||||
|       dlg->setPrepared(); | ||||
|       dlg->setCurFile(curFile); | ||||
|     } | ||||
|     uiTimer->setInterval(500); // change the interval of the timer
 | ||||
|     // now the timer is used to update current file display
 | ||||
|     dlg->show(); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| gint FileOperation::onFileOpsJobAsk(FmFileOpsJob* job, const char* question, char*const* options, FileOperation* pThis) { | ||||
|   pThis->pauseElapsedTimer(); | ||||
|   pThis->showDialog(); | ||||
|   int ret = pThis->dlg->ask(QString::fromUtf8(question), options); | ||||
|   pThis->resumeElapsedTimer(); | ||||
|   return ret; | ||||
| } | ||||
| 
 | ||||
| gint FileOperation::onFileOpsJobAskRename(FmFileOpsJob* job, FmFileInfo* src, FmFileInfo* dest, char** new_name, FileOperation* pThis) { | ||||
|   pThis->pauseElapsedTimer(); | ||||
|   pThis->showDialog(); | ||||
|   QString newName; | ||||
|   int ret = pThis->dlg->askRename(src, dest, newName); | ||||
|   if(!newName.isEmpty()) { | ||||
|     *new_name = g_strdup(newName.toUtf8().constData()); | ||||
|   } | ||||
|   pThis->resumeElapsedTimer(); | ||||
|   return ret; | ||||
| } | ||||
| 
 | ||||
| void FileOperation::onFileOpsJobCancelled(FmFileOpsJob* job, FileOperation* pThis) { | ||||
|   qDebug("file operation is cancelled!"); | ||||
| } | ||||
| 
 | ||||
| void FileOperation::onFileOpsJobCurFile(FmFileOpsJob* job, const char* cur_file, FileOperation* pThis) { | ||||
|   pThis->curFile = QString::fromUtf8(cur_file); | ||||
|    | ||||
|   // We update the current file name in a timeout slot because drawing a string
 | ||||
|   // in the UI is expansive. Updating the label text too often cause
 | ||||
|   // significant impact on performance.
 | ||||
|   // if(pThis->dlg)
 | ||||
|   //  pThis->dlg->setCurFile(pThis->curFile);
 | ||||
| } | ||||
| 
 | ||||
| FmJobErrorAction FileOperation::onFileOpsJobError(FmFileOpsJob* job, GError* err, FmJobErrorSeverity severity, FileOperation* pThis) { | ||||
|   pThis->pauseElapsedTimer(); | ||||
|   pThis->showDialog(); | ||||
|   FmJobErrorAction act = pThis->dlg->error(err, severity); | ||||
|   pThis->resumeElapsedTimer(); | ||||
|   return act; | ||||
| } | ||||
| 
 | ||||
| void FileOperation::onFileOpsJobFinished(FmFileOpsJob* job, FileOperation* pThis) { | ||||
|   pThis->handleFinish(); | ||||
| } | ||||
| 
 | ||||
| void FileOperation::onFileOpsJobPercent(FmFileOpsJob* job, guint percent, FileOperation* pThis) { | ||||
|   if(pThis->dlg) { | ||||
|     pThis->dlg->setPercent(percent); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| void FileOperation::onFileOpsJobPrepared(FmFileOpsJob* job, FileOperation* pThis) { | ||||
|   if(!pThis->elapsedTimer_) { | ||||
|     pThis->elapsedTimer_ = new QElapsedTimer(); | ||||
|     pThis->elapsedTimer_->start(); | ||||
|   } | ||||
|   if(pThis->dlg) { | ||||
|     pThis->dlg->setPrepared(); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| void FileOperation::handleFinish() { | ||||
|   disconnectJob(); | ||||
| 
 | ||||
|   if(uiTimer) { | ||||
|     uiTimer->stop(); | ||||
|     delete uiTimer; | ||||
|     uiTimer = NULL; | ||||
|   } | ||||
| 
 | ||||
|   if(dlg) { | ||||
|     dlg->done(QDialog::Accepted); | ||||
|     delete dlg; | ||||
|     dlg = NULL; | ||||
|   } | ||||
|   Q_EMIT finished(); | ||||
| 
 | ||||
|   /* sepcial handling for trash
 | ||||
|    * FIXME: need to refactor this to use a more elegant way later. */ | ||||
|   if(job_->type == FM_FILE_OP_TRASH) { /* FIXME: direct access to job struct! */ | ||||
|     FmPathList* unable_to_trash = static_cast<FmPathList*>(g_object_get_data(G_OBJECT(job_), "trash-unsupported")); | ||||
|     /* some files cannot be trashed because underlying filesystems don't support it. */ | ||||
|     if(unable_to_trash) { /* delete them instead */ | ||||
|       /* FIXME: parent window might be already destroyed! */ | ||||
|       QWidget* parent = NULL; // FIXME: currently, parent window is not set
 | ||||
|       if(QMessageBox::question(parent, tr("Error"), | ||||
|                     tr("Some files cannot be moved to trash can because " | ||||
|                     "the underlying file systems don't support this operation.\n" | ||||
|                     "Do you want to delete them instead?")) == QMessageBox::Yes) { | ||||
|         deleteFiles(unable_to_trash, false); | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|   g_object_unref(job_); | ||||
|   job_ = NULL; | ||||
| 
 | ||||
|   if(autoDestroy_) | ||||
|     delete this; | ||||
| } | ||||
| 
 | ||||
| // static
 | ||||
| FileOperation* FileOperation::copyFiles(FmPathList* srcFiles, FmPath* dest, QWidget* parent) { | ||||
|   FileOperation* op = new FileOperation(FileOperation::Copy, srcFiles); | ||||
|   op->setDestination(dest); | ||||
|   op->run(); | ||||
|   return op; | ||||
| } | ||||
| 
 | ||||
| // static
 | ||||
| FileOperation* FileOperation::moveFiles(FmPathList* srcFiles, FmPath* dest, QWidget* parent) { | ||||
|   FileOperation* op = new FileOperation(FileOperation::Move, srcFiles); | ||||
|   op->setDestination(dest); | ||||
|   op->run(); | ||||
|   return op; | ||||
| } | ||||
| 
 | ||||
| //static
 | ||||
| FileOperation* FileOperation::symlinkFiles(FmPathList* srcFiles, FmPath* dest, QWidget* parent) { | ||||
|   FileOperation* op = new FileOperation(FileOperation::Link, srcFiles); | ||||
|   op->setDestination(dest); | ||||
|   op->run(); | ||||
|   return op; | ||||
| } | ||||
| 
 | ||||
| //static
 | ||||
| FileOperation* FileOperation::deleteFiles(FmPathList* srcFiles, bool prompt, QWidget* parent) { | ||||
|   if(prompt) { | ||||
|     int result = QMessageBox::warning(parent, tr("Confirm"), | ||||
|                                       tr("Do you want to delete the selected files?"), | ||||
|                                       QMessageBox::Yes|QMessageBox::No, | ||||
|                                       QMessageBox::No); | ||||
|     if(result != QMessageBox::Yes) | ||||
|       return NULL; | ||||
|   } | ||||
| 
 | ||||
|   FileOperation* op = new FileOperation(FileOperation::Delete, srcFiles); | ||||
|   op->run(); | ||||
|   return op; | ||||
| } | ||||
| 
 | ||||
| //static
 | ||||
| FileOperation* FileOperation::trashFiles(FmPathList* srcFiles, bool prompt, QWidget* parent) { | ||||
|   if(prompt) { | ||||
|     int result = QMessageBox::warning(parent, tr("Confirm"), | ||||
|                                       tr("Do you want to move the selected files to trash can?"), | ||||
|                                       QMessageBox::Yes|QMessageBox::No, | ||||
|                                       QMessageBox::No); | ||||
|     if(result != QMessageBox::Yes) | ||||
|       return NULL; | ||||
|   } | ||||
| 
 | ||||
|   FileOperation* op = new FileOperation(FileOperation::Trash, srcFiles); | ||||
|   op->run(); | ||||
|   return op; | ||||
| } | ||||
| 
 | ||||
| //static
 | ||||
| FileOperation* FileOperation::unTrashFiles(FmPathList* srcFiles, QWidget* parent) { | ||||
|   FileOperation* op = new FileOperation(FileOperation::UnTrash, srcFiles); | ||||
|   op->run(); | ||||
|   return op; | ||||
| } | ||||
| 
 | ||||
| // static
 | ||||
| FileOperation* FileOperation::changeAttrFiles(FmPathList* srcFiles, QWidget* parent) { | ||||
|   //TODO
 | ||||
|   FileOperation* op = new FileOperation(FileOperation::ChangeAttr, srcFiles); | ||||
|   op->run(); | ||||
|   return op; | ||||
| } | ||||
							
								
								
									
										164
									
								
								libfm-qt/fileoperation.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										164
									
								
								libfm-qt/fileoperation.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,164 @@ | ||||
| /*
 | ||||
| 
 | ||||
|     Copyright (C) 2013  Hong Jen Yee (PCMan) <pcman.tw@gmail.com> | ||||
| 
 | ||||
|     This program is free software; you can redistribute it and/or modify | ||||
|     it under the terms of the GNU General Public License as published by | ||||
|     the Free Software Foundation; either version 2 of the License, or | ||||
|     (at your option) any later version. | ||||
| 
 | ||||
|     This program is distributed in the hope that it will be useful, | ||||
|     but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|     GNU General Public License for more details. | ||||
| 
 | ||||
|     You should have received a copy of the GNU General Public License along | ||||
|     with this program; if not, write to the Free Software Foundation, Inc., | ||||
|     51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||||
| */ | ||||
| 
 | ||||
| 
 | ||||
| #ifndef FM_FILEOPERATION_H | ||||
| #define FM_FILEOPERATION_H | ||||
| 
 | ||||
| #include "libfmqtglobals.h" | ||||
| #include <QObject> | ||||
| #include <QElapsedTimer> | ||||
| #include <libfm/fm.h> | ||||
| 
 | ||||
| class QTimer; | ||||
| 
 | ||||
| namespace Fm { | ||||
| 
 | ||||
| class FileOperationDialog; | ||||
| 
 | ||||
| class LIBFM_QT_API FileOperation : public QObject { | ||||
| Q_OBJECT | ||||
| public: | ||||
|   enum Type { | ||||
|     Copy = FM_FILE_OP_COPY, | ||||
|     Move = FM_FILE_OP_MOVE, | ||||
|     Link = FM_FILE_OP_LINK, | ||||
|     Delete = FM_FILE_OP_DELETE, | ||||
|     Trash = FM_FILE_OP_TRASH, | ||||
|     UnTrash = FM_FILE_OP_UNTRASH, | ||||
|     ChangeAttr = FM_FILE_OP_CHANGE_ATTR | ||||
|   }; | ||||
| 
 | ||||
| public: | ||||
|   explicit FileOperation(Type type, FmPathList* srcFiles, QObject* parent = 0); | ||||
|   virtual ~FileOperation(); | ||||
| 
 | ||||
|   void setDestination(FmPath* dest) { | ||||
|     destPath = fm_path_ref(dest); | ||||
|     fm_file_ops_job_set_dest(job_, dest); | ||||
|   } | ||||
| 
 | ||||
|   void setChmod(mode_t newMode, mode_t newModeMask) { | ||||
|     fm_file_ops_job_set_chmod(job_, newMode, newModeMask); | ||||
|   } | ||||
| 
 | ||||
|   void setChown(gint uid, gint gid) { | ||||
|     fm_file_ops_job_set_chown(job_, uid, gid); | ||||
|   } | ||||
| 
 | ||||
|   // This only work for change attr jobs.
 | ||||
|   void setRecursiveChattr(bool recursive) { | ||||
|     fm_file_ops_job_set_recursive(job_, (gboolean)recursive); | ||||
|   } | ||||
| 
 | ||||
|   bool run(); | ||||
| 
 | ||||
|   void cancel() { | ||||
|     if(job_) | ||||
|       fm_job_cancel(FM_JOB(job_)); | ||||
|   } | ||||
| 
 | ||||
|   bool isRunning() const { | ||||
|     return job_ ? fm_job_is_running(FM_JOB(job_)) : false; | ||||
|   } | ||||
| 
 | ||||
|   bool isCancelled() const { | ||||
|     return job_ ? fm_job_is_cancelled(FM_JOB(job_)) : false; | ||||
|   } | ||||
| 
 | ||||
|   FmFileOpsJob* job() { | ||||
|     return job_; | ||||
|   } | ||||
| 
 | ||||
|   bool autoDestroy() { | ||||
|     return autoDestroy_; | ||||
|   } | ||||
|   void setAutoDestroy(bool destroy = true) { | ||||
|     autoDestroy_ = destroy; | ||||
|   } | ||||
| 
 | ||||
|   Type type() { | ||||
|     return (Type)job_->type; | ||||
|   } | ||||
|    | ||||
|   // convinient static functions
 | ||||
|   static FileOperation* copyFiles(FmPathList* srcFiles, FmPath* dest, QWidget* parent = 0); | ||||
|   static FileOperation* moveFiles(FmPathList* srcFiles, FmPath* dest, QWidget* parent = 0); | ||||
|   static FileOperation* symlinkFiles(FmPathList* srcFiles, FmPath* dest, QWidget* parent = 0); | ||||
|   static FileOperation* deleteFiles(FmPathList* srcFiles, bool promp = true, QWidget* parent = 0); | ||||
|   static FileOperation* trashFiles(FmPathList* srcFiles, bool promp = true, QWidget* parent = 0); | ||||
|   static FileOperation* unTrashFiles(FmPathList* srcFiles, QWidget* parent = 0); | ||||
|   static FileOperation* changeAttrFiles(FmPathList* srcFiles, QWidget* parent = 0); | ||||
| 
 | ||||
| Q_SIGNALS: | ||||
|   void finished(); | ||||
|    | ||||
| private: | ||||
|   static gint onFileOpsJobAsk(FmFileOpsJob* job, const char* question, char* const* options, FileOperation* pThis); | ||||
|   static gint onFileOpsJobAskRename(FmFileOpsJob* job, FmFileInfo* src, FmFileInfo* dest, char** new_name, FileOperation* pThis); | ||||
|   static FmJobErrorAction onFileOpsJobError(FmFileOpsJob* job, GError* err, FmJobErrorSeverity severity, FileOperation* pThis); | ||||
|   static void onFileOpsJobPrepared(FmFileOpsJob* job, FileOperation* pThis); | ||||
|   static void onFileOpsJobCurFile(FmFileOpsJob* job, const char* cur_file, FileOperation* pThis); | ||||
|   static void onFileOpsJobPercent(FmFileOpsJob* job, guint percent, FileOperation* pThis); | ||||
|   static void onFileOpsJobFinished(FmFileOpsJob* job, FileOperation* pThis); | ||||
|   static void onFileOpsJobCancelled(FmFileOpsJob* job, FileOperation* pThis); | ||||
| 
 | ||||
|   void handleFinish(); | ||||
|   void disconnectJob(); | ||||
|   void showDialog(); | ||||
| 
 | ||||
|   void pauseElapsedTimer() { | ||||
|     if(Q_LIKELY(elapsedTimer_ != NULL)) { | ||||
|       lastElapsed_ += elapsedTimer_->elapsed(); | ||||
|       elapsedTimer_->invalidate(); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   void resumeElapsedTimer() { | ||||
|     if(Q_LIKELY(elapsedTimer_ != NULL)) { | ||||
|       elapsedTimer_->start(); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   qint64 elapsedTime() { | ||||
|     if(Q_LIKELY(elapsedTimer_ != NULL)) { | ||||
|       return lastElapsed_ + elapsedTimer_->elapsed(); | ||||
|     } | ||||
|     return 0; | ||||
|   } | ||||
| 
 | ||||
| private Q_SLOTS: | ||||
|   void onUiTimeout(); | ||||
|    | ||||
| private: | ||||
|   FmFileOpsJob* job_; | ||||
|   FileOperationDialog* dlg; | ||||
|   FmPath* destPath; | ||||
|   FmPathList* srcPaths; | ||||
|   QTimer* uiTimer; | ||||
|   QElapsedTimer* elapsedTimer_; | ||||
|   qint64 lastElapsed_; | ||||
|   bool updateRemainingTime_; | ||||
|   QString curFile; | ||||
|   bool autoDestroy_; | ||||
| }; | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| #endif // FM_FILEOPERATION_H
 | ||||
							
								
								
									
										176
									
								
								libfm-qt/fileoperationdialog.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										176
									
								
								libfm-qt/fileoperationdialog.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,176 @@ | ||||
| /*
 | ||||
| 
 | ||||
|     Copyright (C) 2013  Hong Jen Yee (PCMan) <pcman.tw@gmail.com> | ||||
| 
 | ||||
|     This program is free software; you can redistribute it and/or modify | ||||
|     it under the terms of the GNU General Public License as published by | ||||
|     the Free Software Foundation; either version 2 of the License, or | ||||
|     (at your option) any later version. | ||||
| 
 | ||||
|     This program is distributed in the hope that it will be useful, | ||||
|     but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|     GNU General Public License for more details. | ||||
| 
 | ||||
|     You should have received a copy of the GNU General Public License along | ||||
|     with this program; if not, write to the Free Software Foundation, Inc., | ||||
|     51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||||
| */ | ||||
| 
 | ||||
| 
 | ||||
| #include "fileoperationdialog.h" | ||||
| #include "fileoperation.h" | ||||
| #include "renamedialog.h" | ||||
| #include <QMessageBox> | ||||
| #include "ui_file-operation-dialog.h" | ||||
| 
 | ||||
| using namespace Fm; | ||||
| 
 | ||||
| FileOperationDialog::FileOperationDialog(FileOperation* _operation): | ||||
|   QDialog(NULL), | ||||
|   operation(_operation), | ||||
|   defaultOption(-1) { | ||||
| 
 | ||||
|   ui = new Ui::FileOperationDialog(); | ||||
|   ui->setupUi(this); | ||||
| 
 | ||||
|   QString title; | ||||
|   QString message; | ||||
|   switch(_operation->type()) { | ||||
|   case FM_FILE_OP_MOVE: | ||||
|       title = tr("Move files"); | ||||
|       message = tr("Moving the following files to destination folder:"); | ||||
|       break; | ||||
|   case FM_FILE_OP_COPY: | ||||
|       title = tr("Copy Files"); | ||||
|       message = tr("Copying the following files to destination folder:"); | ||||
|       break; | ||||
|   case FM_FILE_OP_TRASH: | ||||
|       title = tr("Trash Files"); | ||||
|       message = tr("Moving the following files to trash can:"); | ||||
|       break; | ||||
|   case FM_FILE_OP_DELETE: | ||||
|       title = tr("Delete Files"); | ||||
|       message = tr("Deleting the following files"); | ||||
|       ui->dest->hide(); | ||||
|       ui->destLabel->hide(); | ||||
|       break; | ||||
|   case FM_FILE_OP_LINK: | ||||
|       title = tr("Create Symlinks"); | ||||
|       message = tr("Creating symlinks for the following files:"); | ||||
|       break; | ||||
|   case FM_FILE_OP_CHANGE_ATTR: | ||||
|       title = tr("Change Attributes"); | ||||
|       message = tr("Changing attributes of the following files:"); | ||||
|       ui->dest->hide(); | ||||
|       ui->destLabel->hide(); | ||||
|       break; | ||||
|   case FM_FILE_OP_UNTRASH: | ||||
|       title = tr("Restore Trashed Files"); | ||||
|       message = tr("Restoring the following files from trash can:"); | ||||
|       ui->dest->hide(); | ||||
|       ui->destLabel->hide(); | ||||
|       break; | ||||
|   } | ||||
|   ui->message->setText(message); | ||||
|   setWindowTitle(title); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| FileOperationDialog::~FileOperationDialog() { | ||||
|   delete ui; | ||||
| } | ||||
| 
 | ||||
| void FileOperationDialog::setDestPath(FmPath* dest) { | ||||
|   char* pathStr = fm_path_display_name(dest, false); | ||||
|   ui->dest->setText(QString::fromUtf8(pathStr)); | ||||
|   g_free(pathStr); | ||||
| } | ||||
| 
 | ||||
| void FileOperationDialog::setSourceFiles(FmPathList* srcFiles) { | ||||
|   GList* l; | ||||
|   for(l = fm_path_list_peek_head_link(srcFiles); l; l = l->next) { | ||||
|     FmPath* path = FM_PATH(l->data); | ||||
|     char* pathStr = fm_path_display_name(path, false); | ||||
|     ui->sourceFiles->addItem(QString::fromUtf8(pathStr)); | ||||
|     g_free(pathStr); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| int FileOperationDialog::ask(QString question, char*const* options) { | ||||
|   // TODO: implement FileOperationDialog::ask()
 | ||||
|   return 0; | ||||
| } | ||||
| 
 | ||||
| int FileOperationDialog::askRename(FmFileInfo* src, FmFileInfo* dest, QString& new_name) { | ||||
|   int ret; | ||||
|   if(defaultOption == -1) { // default action is not set, ask the user
 | ||||
|     RenameDialog dlg(src, dest, this); | ||||
|     dlg.exec(); | ||||
|     switch(dlg.action()) { | ||||
|       case RenameDialog::ActionOverwrite: | ||||
|         ret = FM_FILE_OP_OVERWRITE; | ||||
|         if(dlg.applyToAll()) | ||||
|           defaultOption = ret; | ||||
|         break; | ||||
|       case RenameDialog::ActionRename: | ||||
|         ret = FM_FILE_OP_RENAME; | ||||
|         new_name = dlg.newName(); | ||||
|         break; | ||||
|       case RenameDialog::ActionIgnore: | ||||
|         ret = FM_FILE_OP_SKIP; | ||||
|         if(dlg.applyToAll()) | ||||
|           defaultOption = ret; | ||||
|         break; | ||||
|       default: | ||||
|         ret = FM_FILE_OP_CANCEL; | ||||
|         break; | ||||
|     } | ||||
|   } | ||||
|   else | ||||
|     ret = defaultOption; | ||||
|   return ret; | ||||
| } | ||||
| 
 | ||||
| FmJobErrorAction FileOperationDialog::error(GError* err, FmJobErrorSeverity severity) { | ||||
|   if(severity >= FM_JOB_ERROR_MODERATE) { | ||||
|     QMessageBox::critical(this, tr("Error"), QString::fromUtf8(err->message)); | ||||
|     if(severity == FM_JOB_ERROR_CRITICAL) | ||||
|       return FM_JOB_ABORT; | ||||
|   } | ||||
|   return FM_JOB_CONTINUE; | ||||
| } | ||||
| 
 | ||||
| void FileOperationDialog::setCurFile(QString cur_file) { | ||||
|   ui->curFile->setText(cur_file); | ||||
| } | ||||
| 
 | ||||
| void FileOperationDialog::setPercent(unsigned int percent) { | ||||
|   ui->progressBar->setValue(percent); | ||||
| } | ||||
| 
 | ||||
| void FileOperationDialog::setRemainingTime(unsigned int sec) { | ||||
|   unsigned int min = 0; | ||||
|   unsigned int hr = 0; | ||||
|   if(sec > 60) { | ||||
|     min = sec / 60; | ||||
|     sec %= 60; | ||||
|     if(min > 60) { | ||||
|       hr = min / 60; | ||||
|       min %= 60; | ||||
|     } | ||||
|   } | ||||
|   ui->timeRemaining->setText(QString("%1:%2:%3") | ||||
|                              .arg(hr, 2, 10, QChar('0')) | ||||
|                              .arg(min, 2, 10, QChar('0')) | ||||
|                              .arg(sec, 2, 10, QChar('0'))); | ||||
| } | ||||
| 
 | ||||
| void FileOperationDialog::setPrepared() { | ||||
| } | ||||
| 
 | ||||
| void FileOperationDialog::reject() { | ||||
|   operation->cancel(); | ||||
|   QDialog::reject(); | ||||
| } | ||||
| 
 | ||||
							
								
								
									
										63
									
								
								libfm-qt/fileoperationdialog.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										63
									
								
								libfm-qt/fileoperationdialog.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,63 @@ | ||||
| /*
 | ||||
| 
 | ||||
|     Copyright (C) 2013  Hong Jen Yee (PCMan) <pcman.tw@gmail.com> | ||||
| 
 | ||||
|     This program is free software; you can redistribute it and/or modify | ||||
|     it under the terms of the GNU General Public License as published by | ||||
|     the Free Software Foundation; either version 2 of the License, or | ||||
|     (at your option) any later version. | ||||
| 
 | ||||
|     This program is distributed in the hope that it will be useful, | ||||
|     but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|     GNU General Public License for more details. | ||||
| 
 | ||||
|     You should have received a copy of the GNU General Public License along | ||||
|     with this program; if not, write to the Free Software Foundation, Inc., | ||||
|     51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||||
| */ | ||||
| 
 | ||||
| 
 | ||||
| #ifndef FM_FILEOPERATIONDIALOG_H | ||||
| #define FM_FILEOPERATIONDIALOG_H | ||||
| 
 | ||||
| #include "libfmqtglobals.h" | ||||
| #include <QDialog> | ||||
| #include <libfm/fm.h> | ||||
| 
 | ||||
| namespace Ui { | ||||
|   class FileOperationDialog; | ||||
| }; | ||||
| 
 | ||||
| namespace Fm { | ||||
| 
 | ||||
| class FileOperation; | ||||
| 
 | ||||
| class LIBFM_QT_API FileOperationDialog : public QDialog { | ||||
| Q_OBJECT | ||||
| public: | ||||
|   explicit FileOperationDialog(FileOperation* _operation); | ||||
|   virtual ~FileOperationDialog(); | ||||
|    | ||||
|   void setSourceFiles(FmPathList* srcFiles); | ||||
|   void setDestPath(FmPath* dest); | ||||
| 
 | ||||
|   int ask(QString question, char* const* options); | ||||
|   int askRename(FmFileInfo* src, FmFileInfo* dest, QString& new_name); | ||||
|   FmJobErrorAction error(GError* err, FmJobErrorSeverity severity); | ||||
|   void setPrepared(); | ||||
|   void setCurFile(QString cur_file); | ||||
|   void setPercent(unsigned int percent); | ||||
|   void setRemainingTime(unsigned int sec); | ||||
| 
 | ||||
|   virtual void reject(); | ||||
| 
 | ||||
| private: | ||||
|   Ui::FileOperationDialog* ui; | ||||
|   FileOperation* operation; | ||||
|   int defaultOption; | ||||
| }; | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| #endif // FM_FILEOPERATIONDIALOG_H
 | ||||
							
								
								
									
										441
									
								
								libfm-qt/filepropsdialog.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										441
									
								
								libfm-qt/filepropsdialog.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,441 @@ | ||||
| /*
 | ||||
| 
 | ||||
|     Copyright (C) 2013  Hong Jen Yee (PCMan) <pcman.tw@gmail.com> | ||||
| 
 | ||||
|     This program is free software; you can redistribute it and/or modify | ||||
|     it under the terms of the GNU General Public License as published by | ||||
|     the Free Software Foundation; either version 2 of the License, or | ||||
|     (at your option) any later version. | ||||
| 
 | ||||
|     This program is distributed in the hope that it will be useful, | ||||
|     but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|     GNU General Public License for more details. | ||||
| 
 | ||||
|     You should have received a copy of the GNU General Public License along | ||||
|     with this program; if not, write to the Free Software Foundation, Inc., | ||||
|     51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||||
| */ | ||||
| 
 | ||||
| 
 | ||||
| #include "filepropsdialog.h" | ||||
| #include "ui_file-props.h" | ||||
| #include "icontheme.h" | ||||
| #include "utilities.h" | ||||
| #include "fileoperation.h" | ||||
| #include <QStringBuilder> | ||||
| #include <QStringListModel> | ||||
| #include <QMessageBox> | ||||
| #include <qdial.h> | ||||
| #include <sys/types.h> | ||||
| #include <time.h> | ||||
| 
 | ||||
| #define DIFFERENT_UIDS    ((uid)-1) | ||||
| #define DIFFERENT_GIDS    ((gid)-1) | ||||
| #define DIFFERENT_PERMS   ((mode_t)-1) | ||||
| 
 | ||||
| using namespace Fm; | ||||
| 
 | ||||
| enum { | ||||
|   ACCESS_NO_CHANGE = 0, | ||||
|   ACCESS_READ_ONLY, | ||||
|   ACCESS_READ_WRITE, | ||||
|   ACCESS_FORBID | ||||
| }; | ||||
| 
 | ||||
| FilePropsDialog::FilePropsDialog(FmFileInfoList* files, QWidget* parent, Qt::WindowFlags f): | ||||
|   QDialog(parent, f), | ||||
|   fileInfos_(fm_file_info_list_ref(files)), | ||||
|   singleType(fm_file_info_list_is_same_type(files)), | ||||
|   singleFile(fm_file_info_list_get_length(files) == 1 ? true:false), | ||||
|   fileInfo(fm_file_info_list_peek_head(files)), | ||||
|   mimeType(NULL) { | ||||
| 
 | ||||
|   setAttribute(Qt::WA_DeleteOnClose); | ||||
| 
 | ||||
|   ui = new Ui::FilePropsDialog(); | ||||
|   ui->setupUi(this); | ||||
| 
 | ||||
|   if(singleType) { | ||||
|     mimeType = fm_mime_type_ref(fm_file_info_get_mime_type(fileInfo)); | ||||
|   } | ||||
| 
 | ||||
|   FmPathList* paths = fm_path_list_new_from_file_info_list(files); | ||||
|   deepCountJob = fm_deep_count_job_new(paths, FM_DC_JOB_DEFAULT); | ||||
|   fm_path_list_unref(paths);     | ||||
| 
 | ||||
|   initGeneralPage(); | ||||
|   initPermissionsPage(); | ||||
| } | ||||
| 
 | ||||
| FilePropsDialog::~FilePropsDialog() { | ||||
|   delete ui; | ||||
| 
 | ||||
|   if(fileInfos_) | ||||
|     fm_file_info_list_unref(fileInfos_); | ||||
|   if(deepCountJob) | ||||
|     g_object_unref(deepCountJob); | ||||
|   if(fileSizeTimer) { | ||||
|     fileSizeTimer->stop(); | ||||
|     delete fileSizeTimer; | ||||
|     fileSizeTimer = NULL; | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| void FilePropsDialog::initApplications() { | ||||
|   if(singleType && mimeType && !fm_file_info_is_dir(fileInfo)) { | ||||
|     ui->openWith->setMimeType(mimeType); | ||||
|   } | ||||
|   else { | ||||
|     ui->openWith->hide(); | ||||
|     ui->openWithLabel->hide(); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| void FilePropsDialog::initPermissionsPage() { | ||||
|   // ownership handling
 | ||||
|   // get owner/group and mode of the first file in the list
 | ||||
|   uid = fm_file_info_get_uid(fileInfo); | ||||
|   gid = fm_file_info_get_gid(fileInfo); | ||||
|   mode_t mode = fm_file_info_get_mode(fileInfo); | ||||
|   ownerPerm = (mode & (S_IRUSR|S_IWUSR|S_IXUSR)); | ||||
|   groupPerm = (mode & (S_IRGRP|S_IWGRP|S_IXGRP)); | ||||
|   otherPerm = (mode & (S_IROTH|S_IWOTH|S_IXOTH)); | ||||
|   execPerm = (mode & (S_IXUSR|S_IXGRP|S_IXOTH)); | ||||
|   allNative = fm_file_info_is_native(fileInfo); | ||||
|   hasDir = S_ISDIR(mode); | ||||
| 
 | ||||
|   // check if all selected files belongs to the same owner/group or have the same mode
 | ||||
|   // at the same time, check if all files are on native unix filesystems
 | ||||
|   GList* l; | ||||
|   for(l = fm_file_info_list_peek_head_link(fileInfos_)->next; l; l = l->next) { | ||||
|     FmFileInfo* fi = FM_FILE_INFO(l->data); | ||||
|     if(allNative && !fm_file_info_is_native(fi)) | ||||
|       allNative = false; // not all of the files are native
 | ||||
| 
 | ||||
|     mode_t fi_mode = fm_file_info_get_mode(fi); | ||||
|     if(S_ISDIR(fi_mode)) | ||||
|       hasDir = true; // the files list contains dir(s)
 | ||||
| 
 | ||||
|     if(uid != DIFFERENT_UIDS && uid != fm_file_info_get_uid(fi)) | ||||
|       uid = DIFFERENT_UIDS; // not all files have the same owner
 | ||||
|     if(gid != DIFFERENT_GIDS && gid != fm_file_info_get_gid(fi)) | ||||
|       gid = DIFFERENT_GIDS; // not all files have the same owner group
 | ||||
| 
 | ||||
|     if(ownerPerm != DIFFERENT_PERMS && ownerPerm != (fi_mode & (S_IRUSR|S_IWUSR|S_IXUSR))) | ||||
|       ownerPerm = DIFFERENT_PERMS; // not all files have the same permission for owner
 | ||||
|     if(groupPerm != DIFFERENT_PERMS && groupPerm != (fi_mode & (S_IRGRP|S_IWGRP|S_IXGRP))) | ||||
|       groupPerm = DIFFERENT_PERMS; // not all files have the same permission for grop
 | ||||
|     if(otherPerm != DIFFERENT_PERMS && otherPerm != (fi_mode & (S_IROTH|S_IWOTH|S_IXOTH))) | ||||
|       otherPerm = DIFFERENT_PERMS; // not all files have the same permission for other
 | ||||
|     if(execPerm != DIFFERENT_PERMS && execPerm != (fi_mode & (S_IXUSR|S_IXGRP|S_IXOTH))) | ||||
|       execPerm = DIFFERENT_PERMS; // not all files have the same executable permission
 | ||||
|   } | ||||
| 
 | ||||
|   // init owner/group
 | ||||
|   initOwner(); | ||||
| 
 | ||||
|   // if all files are of the same type, and some of them are dirs => all of the items are dirs
 | ||||
|   // rwx values have different meanings for dirs
 | ||||
|   // Let's make it clear for the users
 | ||||
|   // init combo boxes for file permissions here
 | ||||
|   QStringList comboItems; | ||||
|   comboItems.append("---"); // no change
 | ||||
|   if(singleType && hasDir) { // all files are dirs
 | ||||
|     comboItems.append(tr("View folder content")); | ||||
|     comboItems.append(tr("View and modify folder content")); | ||||
|     ui->executable->hide(); | ||||
|   } | ||||
|   else { //not all of the files are dirs
 | ||||
|     comboItems.append(tr("Read")); | ||||
|     comboItems.append(tr("Read and write")); | ||||
|   } | ||||
|   comboItems.append(tr("Forbidden")); | ||||
|   QStringListModel* comboModel = new QStringListModel(comboItems, this); | ||||
|   ui->ownerPerm->setModel(comboModel); | ||||
|   ui->groupPerm->setModel(comboModel); | ||||
|   ui->otherPerm->setModel(comboModel); | ||||
| 
 | ||||
|   // owner
 | ||||
|   ownerPermSel = ACCESS_NO_CHANGE; | ||||
|   if(ownerPerm != DIFFERENT_PERMS) { // permissions for owner are the same among all files
 | ||||
|     if(ownerPerm & S_IRUSR) { // can read
 | ||||
|       if(ownerPerm & S_IWUSR) // can write
 | ||||
|         ownerPermSel = ACCESS_READ_WRITE; | ||||
|       else | ||||
|         ownerPermSel = ACCESS_READ_ONLY; | ||||
|     } | ||||
|     else { | ||||
|       if((ownerPerm & S_IWUSR) == 0) // cannot read or write
 | ||||
|         ownerPermSel = ACCESS_FORBID; | ||||
|     } | ||||
|   } | ||||
|   ui->ownerPerm->setCurrentIndex(ownerPermSel); | ||||
| 
 | ||||
|   // owner and group
 | ||||
|   groupPermSel = ACCESS_NO_CHANGE; | ||||
|   if(groupPerm != DIFFERENT_PERMS) { // permissions for owner are the same among all files
 | ||||
|     if(groupPerm & S_IRGRP) { // can read
 | ||||
|       if(groupPerm & S_IWGRP) // can write
 | ||||
|         groupPermSel = ACCESS_READ_WRITE; | ||||
|       else | ||||
|         groupPermSel = ACCESS_READ_ONLY; | ||||
|     } | ||||
|     else { | ||||
|       if((groupPerm & S_IWGRP) == 0) // cannot read or write
 | ||||
|         groupPermSel = ACCESS_FORBID; | ||||
|     } | ||||
|   } | ||||
|   ui->groupPerm->setCurrentIndex(groupPermSel); | ||||
| 
 | ||||
|   // other
 | ||||
|   otherPermSel = ACCESS_NO_CHANGE; | ||||
|   if(otherPerm != DIFFERENT_PERMS) { // permissions for owner are the same among all files
 | ||||
|     if(otherPerm & S_IROTH) { // can read
 | ||||
|       if(otherPerm & S_IWOTH) // can write
 | ||||
|         otherPermSel = ACCESS_READ_WRITE; | ||||
|       else | ||||
|         otherPermSel = ACCESS_READ_ONLY; | ||||
|     } | ||||
|     else { | ||||
|       if((otherPerm & S_IWOTH) == 0) // cannot read or write
 | ||||
|         otherPermSel = ACCESS_FORBID; | ||||
|     } | ||||
| 
 | ||||
|   } | ||||
|   ui->otherPerm->setCurrentIndex(otherPermSel); | ||||
| 
 | ||||
|   // set the checkbox to partially checked state
 | ||||
|   // when owner, group, and other have different executable flags set.
 | ||||
|   // some of them have exec, and others do not have.
 | ||||
|   execCheckState = Qt::PartiallyChecked; | ||||
|   if(execPerm != DIFFERENT_PERMS) { // if all files have the same executable permission
 | ||||
|     // check if the files are all executable
 | ||||
|     if((mode & (S_IXUSR|S_IXGRP|S_IXOTH)) == (S_IXUSR|S_IXGRP|S_IXOTH)) { | ||||
|       // owner, group, and other all have exec permission.
 | ||||
|       ui->executable->setTristate(false); | ||||
|       execCheckState = Qt::Checked; | ||||
|     } | ||||
|     else if((mode & (S_IXUSR|S_IXGRP|S_IXOTH)) == 0) { | ||||
|       // owner, group, and other all have no exec permission
 | ||||
|       ui->executable->setTristate(false); | ||||
|       execCheckState = Qt::Unchecked; | ||||
|     } | ||||
|   } | ||||
|   ui->executable->setCheckState(execCheckState); | ||||
| } | ||||
| 
 | ||||
| void FilePropsDialog::initGeneralPage() { | ||||
|   // update UI
 | ||||
|   if(singleType) { // all files are of the same mime-type
 | ||||
|     FmIcon* icon = NULL; | ||||
|     // FIXME: handle custom icons for some files
 | ||||
|     // FIXME: display special property pages for special files or
 | ||||
|     // some specified mime-types.
 | ||||
|     if(singleFile) { // only one file is selected.
 | ||||
|       icon = fm_file_info_get_icon(fileInfo); | ||||
|     } | ||||
|     if(mimeType) { | ||||
|       if(!icon) // get an icon from mime type if needed
 | ||||
|         icon = fm_mime_type_get_icon(mimeType); | ||||
|       ui->fileType->setText(QString::fromUtf8(fm_mime_type_get_desc(mimeType))); | ||||
|       ui->mimeType->setText(QString::fromUtf8(fm_mime_type_get_type(mimeType))); | ||||
|     } | ||||
|     if(icon) { | ||||
|       ui->iconButton->setIcon(IconTheme::icon(icon)); | ||||
|     } | ||||
| 
 | ||||
|     if(singleFile && fm_file_info_is_symlink(fileInfo)) { | ||||
|       ui->target->setText(QString::fromUtf8(fm_file_info_get_target(fileInfo))); | ||||
|     } | ||||
|     else { | ||||
|       ui->target->hide(); | ||||
|       ui->targetLabel->hide(); | ||||
|     } | ||||
|   } // end if(singleType)
 | ||||
|   else { // not singleType, multiple files are selected at the same time
 | ||||
|     ui->fileType->setText(tr("Files of different types")); | ||||
|     ui->target->hide(); | ||||
|     ui->targetLabel->hide(); | ||||
|   } | ||||
| 
 | ||||
|   // FIXME: check if all files has the same parent dir, mtime, or atime
 | ||||
|   if(singleFile) { // only one file is selected
 | ||||
|     FmPath* parent_path = fm_path_get_parent(fm_file_info_get_path(fileInfo)); | ||||
|     char* parent_str = parent_path ? fm_path_display_name(parent_path, true) : NULL; | ||||
| 
 | ||||
|     ui->fileName->setText(QString::fromUtf8(fm_file_info_get_disp_name(fileInfo))); | ||||
|     if(parent_str) { | ||||
|       ui->location->setText(QString::fromUtf8(parent_str)); | ||||
|       g_free(parent_str); | ||||
|     } | ||||
|     else | ||||
|       ui->location->clear(); | ||||
| 
 | ||||
|     ui->lastModified->setText(QString::fromUtf8(fm_file_info_get_disp_mtime(fileInfo))); | ||||
| 
 | ||||
|     // FIXME: need to encapsulate this in an libfm API.
 | ||||
|     time_t atime; | ||||
|     struct tm tm; | ||||
|     atime = fm_file_info_get_atime(fileInfo); | ||||
|     localtime_r(&atime, &tm); | ||||
|     char buf[128]; | ||||
|     strftime(buf, sizeof(buf), "%x %R", &tm); | ||||
|     ui->lastAccessed->setText(QString::fromUtf8(buf)); | ||||
|   } | ||||
|   else { | ||||
|     ui->fileName->setText(tr("Multiple Files")); | ||||
|     ui->fileName->setEnabled(false); | ||||
|   } | ||||
| 
 | ||||
|   initApplications(); // init applications combo box
 | ||||
| 
 | ||||
|   // calculate total file sizes
 | ||||
|   fileSizeTimer = new QTimer(this); | ||||
|   connect(fileSizeTimer, &QTimer::timeout, this, &FilePropsDialog::onFileSizeTimerTimeout); | ||||
|   fileSizeTimer->start(600); | ||||
|   g_signal_connect(deepCountJob, "finished", G_CALLBACK(onDeepCountJobFinished), this); | ||||
|   fm_job_run_async(FM_JOB(deepCountJob)); | ||||
| } | ||||
| 
 | ||||
| /*static */ void FilePropsDialog::onDeepCountJobFinished(FmDeepCountJob* job, FilePropsDialog* pThis) { | ||||
| 
 | ||||
|   pThis->onFileSizeTimerTimeout(); // update file size display
 | ||||
| 
 | ||||
|   // free the job
 | ||||
|   g_object_unref(pThis->deepCountJob); | ||||
|   pThis->deepCountJob = NULL; | ||||
| 
 | ||||
|   // stop the timer
 | ||||
|   if(pThis->fileSizeTimer) { | ||||
|     pThis->fileSizeTimer->stop(); | ||||
|     delete pThis->fileSizeTimer; | ||||
|     pThis->fileSizeTimer = NULL; | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| void FilePropsDialog::onFileSizeTimerTimeout() { | ||||
|   if(deepCountJob && !fm_job_is_cancelled(FM_JOB(deepCountJob))) { | ||||
|     char size_str[128]; | ||||
|     fm_file_size_to_str(size_str, sizeof(size_str), deepCountJob->total_size, | ||||
|                         fm_config->si_unit); | ||||
|     // FIXME:
 | ||||
|     // OMG! It's really unbelievable that Qt developers only implement
 | ||||
|     // QObject::tr(... int n). GNU gettext developers are smarter and
 | ||||
|     // they use unsigned long instead of int.
 | ||||
|     // We cannot use Qt here to handle plural forms. So sad. :-(
 | ||||
|     QString str = QString::fromUtf8(size_str) % | ||||
|       QString(" (%1 B)").arg(deepCountJob->total_size); | ||||
|       // tr(" (%n) byte(s)", "", deepCountJob->total_size);
 | ||||
|     ui->fileSize->setText(str); | ||||
| 
 | ||||
|     fm_file_size_to_str(size_str, sizeof(size_str), deepCountJob->total_ondisk_size, | ||||
|                         fm_config->si_unit); | ||||
|     str = QString::fromUtf8(size_str) % | ||||
|       QString(" (%1 B)").arg(deepCountJob->total_ondisk_size); | ||||
|       // tr(" (%n) byte(s)", "", deepCountJob->total_ondisk_size);
 | ||||
|     ui->onDiskSize->setText(str); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| void FilePropsDialog::accept() { | ||||
| 
 | ||||
|   // applications
 | ||||
|   if(mimeType && ui->openWith->isChanged()) { | ||||
|     GAppInfo* currentApp = ui->openWith->selectedApp(); | ||||
|     g_app_info_set_as_default_for_type(currentApp, fm_mime_type_get_type(mimeType), NULL); | ||||
|   } | ||||
| 
 | ||||
|   // check if chown or chmod is needed
 | ||||
|   guint32 newUid = uidFromName(ui->owner->text()); | ||||
|   guint32 newGid = gidFromName(ui->ownerGroup->text()); | ||||
|   bool needChown = (newUid != uid || newGid != gid); | ||||
| 
 | ||||
|   int newOwnerPermSel = ui->ownerPerm->currentIndex(); | ||||
|   int newGroupPermSel = ui->groupPerm->currentIndex(); | ||||
|   int newOtherPermSel = ui->otherPerm->currentIndex(); | ||||
|   Qt::CheckState newExecCheckState = ui->executable->checkState(); | ||||
|   bool needChmod = ((newOwnerPermSel != ownerPermSel) || | ||||
|                     (newGroupPermSel != groupPermSel) || | ||||
|                     (newOtherPermSel != otherPermSel) || | ||||
|                     (newExecCheckState != execCheckState)); | ||||
| 
 | ||||
|   if(needChmod || needChown) { | ||||
|     FmPathList* paths = fm_path_list_new_from_file_info_list(fileInfos_); | ||||
|     FileOperation* op = new FileOperation(FileOperation::ChangeAttr, paths); | ||||
|     fm_path_list_unref(paths); | ||||
|     if(needChown) { | ||||
|       // don't do chown if new uid/gid and the original ones are actually the same.
 | ||||
|       if(newUid == uid) | ||||
|         newUid = -1; | ||||
|       if(newGid == gid) | ||||
|         newGid = -1; | ||||
|       op->setChown(newUid, newGid); | ||||
|     } | ||||
|     if(needChmod) { | ||||
|       mode_t newMode = 0; | ||||
|       mode_t newModeMask = 0; | ||||
|       // FIXME: we need to make sure that folders with "r" permission also have "x"
 | ||||
|       // at the same time. Otherwise, it's not able to browse the folder later.
 | ||||
|       if(newOwnerPermSel != ownerPermSel && newOwnerPermSel != ACCESS_NO_CHANGE) { | ||||
|         // owner permission changed
 | ||||
|         newModeMask |= (S_IRUSR|S_IWUSR); // affect user bits
 | ||||
|         if(newOwnerPermSel == ACCESS_READ_ONLY) | ||||
|           newMode |= S_IRUSR; | ||||
|         else if(newOwnerPermSel == ACCESS_READ_WRITE) | ||||
|           newMode |= (S_IRUSR|S_IWUSR); | ||||
|       } | ||||
|       if(newGroupPermSel != groupPermSel && newGroupPermSel != ACCESS_NO_CHANGE) { | ||||
|         qDebug("newGroupPermSel: %d", newGroupPermSel); | ||||
|         // group permission changed
 | ||||
|         newModeMask |= (S_IRGRP|S_IWGRP); // affect group bits
 | ||||
|         if(newGroupPermSel == ACCESS_READ_ONLY) | ||||
|           newMode |= S_IRGRP; | ||||
|         else if(newGroupPermSel == ACCESS_READ_WRITE) | ||||
|           newMode |= (S_IRGRP|S_IWGRP); | ||||
|       } | ||||
|       if(newOtherPermSel != otherPermSel && newOtherPermSel != ACCESS_NO_CHANGE) { | ||||
|         // other permission changed
 | ||||
|         newModeMask |= (S_IROTH|S_IWOTH); // affect other bits
 | ||||
|         if(newOtherPermSel == ACCESS_READ_ONLY) | ||||
|           newMode |= S_IROTH; | ||||
|         else if(newOtherPermSel == ACCESS_READ_WRITE) | ||||
|           newMode |= (S_IROTH|S_IWOTH); | ||||
|       } | ||||
|       if(newExecCheckState != execCheckState && newExecCheckState != Qt::PartiallyChecked) { | ||||
|         // executable state changed
 | ||||
|         newModeMask |= (S_IXUSR|S_IXGRP|S_IXOTH); | ||||
|         if(newExecCheckState == Qt::Checked) | ||||
|           newMode |= (S_IXUSR|S_IXGRP|S_IXOTH); | ||||
|       } | ||||
|       op->setChmod(newMode, newModeMask); | ||||
|        | ||||
|       if(hasDir) { // if there are some dirs in our selected files
 | ||||
|         QMessageBox::StandardButton r = QMessageBox::question(this, | ||||
|                                           tr("Apply changes"), | ||||
|                                           tr("Do you want to recursively apply these changes to all files and sub-folders?"), | ||||
|                                           QMessageBox::Yes|QMessageBox::No); | ||||
|         if(r == QMessageBox::Yes) | ||||
|           op->setRecursiveChattr(true); | ||||
|       } | ||||
|     } | ||||
|     op->setAutoDestroy(true); | ||||
|     op->run(); | ||||
|   } | ||||
| 
 | ||||
|   QDialog::accept(); | ||||
| } | ||||
| 
 | ||||
| void FilePropsDialog::initOwner() { | ||||
|   if(allNative) { | ||||
|     if(uid != DIFFERENT_UIDS) | ||||
|       ui->owner->setText(uidToName(uid)); | ||||
|     if(gid != DIFFERENT_GIDS) | ||||
|       ui->ownerGroup->setText(gidToName(gid)); | ||||
|      | ||||
|     if(geteuid() != 0) { // on local filesystems, only root can do chown.
 | ||||
|       ui->owner->setEnabled(false); | ||||
|       ui->ownerGroup->setEnabled(false); | ||||
|     } | ||||
|   } | ||||
| } | ||||
							
								
								
									
										97
									
								
								libfm-qt/filepropsdialog.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										97
									
								
								libfm-qt/filepropsdialog.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,97 @@ | ||||
| /*
 | ||||
| 
 | ||||
|     Copyright (C) 2013  Hong Jen Yee (PCMan) <pcman.tw@gmail.com> | ||||
| 
 | ||||
|     This program is free software; you can redistribute it and/or modify | ||||
|     it under the terms of the GNU General Public License as published by | ||||
|     the Free Software Foundation; either version 2 of the License, or | ||||
|     (at your option) any later version. | ||||
| 
 | ||||
|     This program is distributed in the hope that it will be useful, | ||||
|     but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|     GNU General Public License for more details. | ||||
| 
 | ||||
|     You should have received a copy of the GNU General Public License along | ||||
|     with this program; if not, write to the Free Software Foundation, Inc., | ||||
|     51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||||
| */ | ||||
| 
 | ||||
| 
 | ||||
| #ifndef FM_FILEPROPSDIALOG_H | ||||
| #define FM_FILEPROPSDIALOG_H | ||||
| 
 | ||||
| #include "libfmqtglobals.h" | ||||
| #include <QDialog> | ||||
| #include <QTimer> | ||||
| #include <libfm/fm.h> | ||||
| 
 | ||||
| namespace Ui { | ||||
|   class FilePropsDialog; | ||||
| }; | ||||
| 
 | ||||
| namespace Fm { | ||||
| 
 | ||||
| class LIBFM_QT_API FilePropsDialog : public QDialog { | ||||
| Q_OBJECT | ||||
| 
 | ||||
| public: | ||||
|   explicit FilePropsDialog(FmFileInfoList* files, QWidget* parent = 0, Qt::WindowFlags f = 0); | ||||
|   virtual ~FilePropsDialog(); | ||||
| 
 | ||||
|   virtual void accept(); | ||||
|    | ||||
|   static FilePropsDialog* showForFile(FmFileInfo* file, QWidget* parent = 0) { | ||||
|     FmFileInfoList* files = fm_file_info_list_new(); | ||||
|     fm_file_info_list_push_tail(files, file); | ||||
|     FilePropsDialog* dlg = showForFiles(files, parent); | ||||
|     fm_file_info_list_unref(files); | ||||
|     return dlg; | ||||
|   } | ||||
|    | ||||
|   static FilePropsDialog* showForFiles(FmFileInfoList* files, QWidget* parent = 0) { | ||||
|     FilePropsDialog* dlg = new FilePropsDialog(files, parent); | ||||
|     dlg->show(); | ||||
|     return dlg; | ||||
|   } | ||||
| 
 | ||||
| private: | ||||
|   void initGeneralPage(); | ||||
|   void initApplications(); | ||||
|   void initPermissionsPage(); | ||||
|   void initOwner(); | ||||
| 
 | ||||
|   static void onDeepCountJobFinished(FmDeepCountJob* job, FilePropsDialog* pThis); | ||||
| 
 | ||||
| private Q_SLOTS: | ||||
|   void onFileSizeTimerTimeout(); | ||||
|    | ||||
| private: | ||||
|   Ui::FilePropsDialog* ui; | ||||
|   FmFileInfoList* fileInfos_; // list of all file infos
 | ||||
|   FmFileInfo* fileInfo; // file info of the first file in the list
 | ||||
|   bool singleType; // all files are of the same type?
 | ||||
|   bool singleFile; // only one file is selected?
 | ||||
|   bool hasDir; // is there any dir in the files?
 | ||||
|   bool allNative; // all files are on native UNIX filesystems (not virtual or remote)
 | ||||
| 
 | ||||
|   FmMimeType* mimeType; // mime type of the files
 | ||||
| 
 | ||||
|   gint32 uid; // owner uid of the files, -1 means all files do not have the same uid
 | ||||
|   gint32 gid; // owner gid of the files, -1 means all files do not have the same uid
 | ||||
|   mode_t ownerPerm; // read permission of the files, -1 means not all files have the same value
 | ||||
|   int ownerPermSel; | ||||
|   mode_t groupPerm; // read permission of the files, -1 means not all files have the same value
 | ||||
|   int groupPermSel; | ||||
|   mode_t otherPerm; // read permission of the files, -1 means not all files have the same value
 | ||||
|   int otherPermSel; | ||||
|   mode_t execPerm; // exec permission of the files
 | ||||
|   Qt::CheckState execCheckState; | ||||
| 
 | ||||
|   FmDeepCountJob* deepCountJob; // job used to count total size
 | ||||
|   QTimer* fileSizeTimer; | ||||
| }; | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| #endif // FM_FILEPROPSDIALOG_H
 | ||||
							
								
								
									
										218
									
								
								libfm-qt/folderitemdelegate.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										218
									
								
								libfm-qt/folderitemdelegate.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,218 @@ | ||||
| /*
 | ||||
| 
 | ||||
|     Copyright (C) 2013  Hong Jen Yee (PCMan) <pcman.tw@gmail.com> | ||||
| 
 | ||||
|     This program is free software; you can redistribute it and/or modify | ||||
|     it under the terms of the GNU General Public License as published by | ||||
|     the Free Software Foundation; either version 2 of the License, or | ||||
|     (at your option) any later version. | ||||
| 
 | ||||
|     This program is distributed in the hope that it will be useful, | ||||
|     but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|     GNU General Public License for more details. | ||||
| 
 | ||||
|     You should have received a copy of the GNU General Public License along | ||||
|     with this program; if not, write to the Free Software Foundation, Inc., | ||||
|     51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||||
| */ | ||||
| 
 | ||||
| 
 | ||||
| #include "folderitemdelegate.h" | ||||
| #include "foldermodel.h" | ||||
| #include <QPainter> | ||||
| #include <QModelIndex> | ||||
| #include <QStyleOptionViewItemV4> | ||||
| #include <QApplication> | ||||
| #include <QIcon> | ||||
| #include <QTextLayout> | ||||
| #include <QTextOption> | ||||
| #include <QTextLine> | ||||
| #include <QDebug> | ||||
| 
 | ||||
| namespace Fm { | ||||
| 
 | ||||
| FolderItemDelegate::FolderItemDelegate(QAbstractItemView* view, QObject* parent): | ||||
|   QStyledItemDelegate(parent ? parent : view), | ||||
|   symlinkIcon_(QIcon::fromTheme("emblem-symbolic-link")), | ||||
|   view_(view) { | ||||
| } | ||||
| 
 | ||||
| FolderItemDelegate::~FolderItemDelegate() { | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| QSize FolderItemDelegate::sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const { | ||||
|   QVariant value = index.data(Qt::SizeHintRole); | ||||
|   if(value.isValid()) | ||||
|     return qvariant_cast<QSize>(value); | ||||
|   if(option.decorationPosition == QStyleOptionViewItem::Top || | ||||
|     option.decorationPosition == QStyleOptionViewItem::Bottom) { | ||||
| 
 | ||||
|     QStyleOptionViewItemV4 opt = option; | ||||
|     initStyleOption(&opt, index); | ||||
|     opt.decorationAlignment = Qt::AlignHCenter|Qt::AlignTop; | ||||
|     opt.displayAlignment = Qt::AlignTop|Qt::AlignHCenter; | ||||
|     const QWidget* widget = opt.widget; | ||||
|     QStyle* style = widget ? widget->style() : QApplication::style(); | ||||
| 
 | ||||
|     // FIXME: there're some problems in this size hint calculation.
 | ||||
|     Q_ASSERT(gridSize_ != QSize()); | ||||
|     QRectF textRect(0, 0, gridSize_.width() - 4, gridSize_.height() - opt.decorationSize.height() - 4); | ||||
|     drawText(NULL, opt, textRect); // passing NULL for painter will calculate the bounding rect only.
 | ||||
|     int width = qMax((int)textRect.width(), opt.decorationSize.width()) + 4; | ||||
|     int height = opt.decorationSize.height() + textRect.height() + 4; | ||||
|     return QSize(width, height); | ||||
|   } | ||||
|   return QStyledItemDelegate::sizeHint(option, index); | ||||
| } | ||||
| 
 | ||||
| QIcon::Mode FolderItemDelegate::iconModeFromState(QStyle::State state) { | ||||
|   QIcon::Mode iconMode; | ||||
|   if(state & QStyle::State_Enabled) { | ||||
|     if(state & QStyle::State_Selected) | ||||
|       iconMode = QIcon::Selected; | ||||
|     else { | ||||
|       iconMode = QIcon::Normal; | ||||
|     } | ||||
|   } | ||||
|   else | ||||
|     iconMode = QIcon::Disabled; | ||||
|   return iconMode; | ||||
| } | ||||
| 
 | ||||
| // special thanks to Razor-qt developer Alec Moskvin(amoskvin) for providing the fix!
 | ||||
| void FolderItemDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const { | ||||
|   Q_ASSERT(index.isValid()); | ||||
|   FmFileInfo* file = static_cast<FmFileInfo*>(index.data(FolderModel::FileInfoRole).value<void*>()); | ||||
|   bool isSymlink = file && fm_file_info_is_symlink(file); | ||||
| 
 | ||||
|   if(option.decorationPosition == QStyleOptionViewItem::Top || | ||||
|     option.decorationPosition == QStyleOptionViewItem::Bottom) { | ||||
|     painter->save(); | ||||
|     painter->setClipRect(option.rect); | ||||
| 
 | ||||
|     QStyleOptionViewItemV4 opt = option; | ||||
|     initStyleOption(&opt, index); | ||||
|     opt.decorationAlignment = Qt::AlignHCenter|Qt::AlignTop; | ||||
|     opt.displayAlignment = Qt::AlignTop|Qt::AlignHCenter; | ||||
| 
 | ||||
|     // draw the icon
 | ||||
|     QIcon::Mode iconMode = iconModeFromState(opt.state); | ||||
|     QPoint iconPos(opt.rect.x() + (opt.rect.width() - opt.decorationSize.width()) / 2, opt.rect.y()); | ||||
|     QPixmap pixmap = opt.icon.pixmap(opt.decorationSize, iconMode); | ||||
|     painter->drawPixmap(iconPos, pixmap); | ||||
| 
 | ||||
|     // draw some emblems for the item if needed
 | ||||
|     // we only support symlink emblem at the moment
 | ||||
|     if(isSymlink) | ||||
|       painter->drawPixmap(iconPos, symlinkIcon_.pixmap(opt.decorationSize / 2, iconMode)); | ||||
| 
 | ||||
|     // draw the text
 | ||||
|     QRectF textRect(opt.rect.x(), opt.rect.y() + opt.decorationSize.height(), opt.rect.width(), opt.rect.height() - opt.decorationSize.height()); | ||||
|     drawText(painter, opt, textRect); | ||||
|     painter->restore(); | ||||
|   } | ||||
|   else { | ||||
|     // let QStyledItemDelegate does its default painting
 | ||||
|     QStyledItemDelegate::paint(painter, option, index); | ||||
| 
 | ||||
|     // draw emblems if needed
 | ||||
|     if(isSymlink) { | ||||
|       QStyleOptionViewItemV4 opt = option; | ||||
|       initStyleOption(&opt, index); | ||||
|       QIcon::Mode iconMode = iconModeFromState(opt.state); | ||||
|       QPoint iconPos(opt.rect.x(), opt.rect.y() + (opt.rect.height() - opt.decorationSize.height()) / 2); | ||||
|       // draw some emblems for the item if needed
 | ||||
|       // we only support symlink emblem at the moment
 | ||||
|       painter->drawPixmap(iconPos, symlinkIcon_.pixmap(opt.decorationSize / 2, iconMode)); | ||||
|     } | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| // if painter is NULL, the method calculate the bounding rectangle of the text and save it to textRect
 | ||||
| void FolderItemDelegate::drawText(QPainter* painter, QStyleOptionViewItemV4& opt, QRectF& textRect) const { | ||||
|   const QWidget* widget = opt.widget; | ||||
|   QStyle* style = widget->style() ? widget->style() : qApp->style(); | ||||
|   const int focusMargin = style->pixelMetric(QStyle::PM_FocusFrameHMargin, &opt, widget); | ||||
|   QTextLayout layout(opt.text, opt.font); | ||||
|   QTextOption textOption; | ||||
|   textOption.setAlignment(opt.displayAlignment); | ||||
|   textOption.setWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere); | ||||
|   textOption.setTextDirection(opt.direction); | ||||
|   layout.setTextOption(textOption); | ||||
|   qreal height = 0; | ||||
|   qreal width = 0; | ||||
|   int visibleLines = 0; | ||||
|   layout.beginLayout(); | ||||
|   QString elidedText; | ||||
|   for(;;) { | ||||
|     QTextLine line = layout.createLine(); | ||||
|     if(!line.isValid()) | ||||
|       break; | ||||
|     line.setLineWidth(textRect.width()); | ||||
|     height += opt.fontMetrics.leading(); | ||||
|     line.setPosition(QPointF(0, height)); | ||||
|     if((height + line.height() + textRect.y()) > textRect.bottom()) { | ||||
|       // if part of this line falls outside the textRect, ignore it and quit.
 | ||||
|       QTextLine lastLine = layout.lineAt(visibleLines - 1); | ||||
|       elidedText = opt.text.mid(lastLine.textStart()); | ||||
|       elidedText = opt.fontMetrics.elidedText(elidedText, opt.textElideMode, textRect.width()); | ||||
|       if(visibleLines == 1) // this is the only visible line
 | ||||
|         width = textRect.width(); | ||||
|       break; | ||||
|     } | ||||
|     height += line.height(); | ||||
|     width = qMax(width, line.naturalTextWidth()); | ||||
|     ++ visibleLines; | ||||
|   } | ||||
|   layout.endLayout(); | ||||
| 
 | ||||
|   // draw background for selected item
 | ||||
|   QRectF boundRect = layout.boundingRect(); | ||||
|   //qDebug() << "bound rect: " << boundRect << "width: " << width;
 | ||||
|   boundRect.setWidth(width); | ||||
|   boundRect.moveTo(textRect.x() + (textRect.width() - width)/2, textRect.y()); | ||||
| 
 | ||||
|   if(!painter) { // no painter, calculate the bounding rect only
 | ||||
|     textRect = boundRect; | ||||
|     return; | ||||
|   } | ||||
| 
 | ||||
|   QPalette::ColorGroup cg = opt.state & QStyle::State_Enabled ? QPalette::Normal : QPalette::Disabled; | ||||
|   if(opt.state & QStyle::State_Selected) { | ||||
|     painter->fillRect(boundRect, opt.palette.highlight()); | ||||
|     painter->setPen(opt.palette.color(cg, QPalette::HighlightedText)); | ||||
|   } | ||||
|   else | ||||
|     painter->setPen(opt.palette.color(cg, QPalette::Text)); | ||||
| 
 | ||||
|   // draw text
 | ||||
|   for(int i = 0; i < visibleLines; ++i) { | ||||
|     QTextLine line = layout.lineAt(i); | ||||
|     if(i == (visibleLines - 1) && !elidedText.isEmpty()) { // the last line, draw elided text
 | ||||
|       QPointF pos(textRect.x() + line.position().x(), textRect.y() + line.y() + line.ascent()); | ||||
|       painter->drawText(pos, elidedText); | ||||
|     } | ||||
|     else { | ||||
|       line.draw(painter, textRect.topLeft()); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   if(opt.state & QStyle::State_HasFocus) { | ||||
|     // draw focus rect
 | ||||
|     QStyleOptionFocusRect o; | ||||
|     o.QStyleOption::operator=(opt); | ||||
|     o.rect = boundRect.toRect(); // subElementRect(SE_ItemViewItemFocusRect, vopt, widget);
 | ||||
|     o.state |= QStyle::State_KeyboardFocusChange; | ||||
|     o.state |= QStyle::State_Item; | ||||
|     QPalette::ColorGroup cg = (opt.state & QStyle::State_Enabled) | ||||
|                   ? QPalette::Normal : QPalette::Disabled; | ||||
|     o.backgroundColor = opt.palette.color(cg, (opt.state & QStyle::State_Selected) | ||||
|                                   ? QPalette::Highlight : QPalette::Window); | ||||
|     style->drawPrimitive(QStyle::PE_FrameFocusRect, &o, painter, widget); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| } // namespace Fm
 | ||||
							
								
								
									
										59
									
								
								libfm-qt/folderitemdelegate.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										59
									
								
								libfm-qt/folderitemdelegate.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,59 @@ | ||||
| /*
 | ||||
| 
 | ||||
|     Copyright (C) 2013  Hong Jen Yee (PCMan) <pcman.tw@gmail.com> | ||||
| 
 | ||||
|     This program is free software; you can redistribute it and/or modify | ||||
|     it under the terms of the GNU General Public License as published by | ||||
|     the Free Software Foundation; either version 2 of the License, or | ||||
|     (at your option) any later version. | ||||
| 
 | ||||
|     This program is distributed in the hope that it will be useful, | ||||
|     but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|     GNU General Public License for more details. | ||||
| 
 | ||||
|     You should have received a copy of the GNU General Public License along | ||||
|     with this program; if not, write to the Free Software Foundation, Inc., | ||||
|     51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||||
| */ | ||||
| 
 | ||||
| 
 | ||||
| #ifndef FM_FOLDERITEMDELEGATE_H | ||||
| #define FM_FOLDERITEMDELEGATE_H | ||||
| 
 | ||||
| #include "libfmqtglobals.h" | ||||
| #include <QStyledItemDelegate> | ||||
| #include <QAbstractItemView> | ||||
| 
 | ||||
| namespace Fm { | ||||
| 
 | ||||
| class LIBFM_QT_API FolderItemDelegate : public QStyledItemDelegate { | ||||
|   Q_OBJECT | ||||
| public: | ||||
|   explicit FolderItemDelegate(QAbstractItemView* view, QObject* parent = 0); | ||||
|   virtual ~FolderItemDelegate(); | ||||
|    | ||||
|   void setGridSize(QSize size) { | ||||
|     gridSize_ = size; | ||||
|   } | ||||
| 
 | ||||
|   QSize gridSize() { | ||||
|     return gridSize_; | ||||
|   } | ||||
|    | ||||
|   virtual QSize sizeHint(const QStyleOptionViewItem & option, const QModelIndex & index) const; | ||||
|   virtual void paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const; | ||||
| 
 | ||||
| private: | ||||
|   void drawText(QPainter* painter, QStyleOptionViewItemV4& opt, QRectF& textRect) const; | ||||
|   static QIcon::Mode iconModeFromState(QStyle::State state); | ||||
|    | ||||
| private: | ||||
|   QAbstractItemView* view_; | ||||
|   QIcon symlinkIcon_; | ||||
|   QSize gridSize_; | ||||
| }; | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| #endif // FM_FOLDERITEMDELEGATE_H
 | ||||
							
								
								
									
										300
									
								
								libfm-qt/foldermenu.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										300
									
								
								libfm-qt/foldermenu.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,300 @@ | ||||
| /*
 | ||||
| 
 | ||||
|     Copyright (C) 2013-2014  Hong Jen Yee (PCMan) <pcman.tw@gmail.com> | ||||
|     Copyright (C) 2012-2014 Andriy Grytsenko (LStranger) <andrej@rep.kiev.ua> | ||||
| 
 | ||||
|     This program is free software; you can redistribute it and/or modify | ||||
|     it under the terms of the GNU General Public License as published by | ||||
|     the Free Software Foundation; either version 2 of the License, or | ||||
|     (at your option) any later version. | ||||
| 
 | ||||
|     This program is distributed in the hope that it will be useful, | ||||
|     but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|     GNU General Public License for more details. | ||||
| 
 | ||||
|     You should have received a copy of the GNU General Public License along | ||||
|     with this program; if not, write to the Free Software Foundation, Inc., | ||||
|     51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||||
| */ | ||||
| 
 | ||||
| 
 | ||||
| #include "foldermenu.h" | ||||
| #include "filepropsdialog.h" | ||||
| #include "folderview.h" | ||||
| #include "utilities.h" | ||||
| #include <cstring> // for memset
 | ||||
| 
 | ||||
| namespace Fm { | ||||
| 
 | ||||
| FolderMenu::FolderMenu(FolderView* view, QWidget* parent): | ||||
|   QMenu(parent), | ||||
|   view_(view) { | ||||
| 
 | ||||
|   ProxyFolderModel* model = view_->model(); | ||||
| 
 | ||||
|   createAction_ = new QAction(tr("Create &New"), this); | ||||
|   addAction(createAction_); | ||||
| 
 | ||||
|   createCreateNewMenu(); | ||||
|   createAction_->setMenu(createNewMenu_); | ||||
| 
 | ||||
|   separator1_ = addSeparator(); | ||||
| 
 | ||||
|   pasteAction_ = new QAction(QIcon::fromTheme("edit-paste"), tr("&Paste"), this); | ||||
|   addAction(pasteAction_); | ||||
|   connect(pasteAction_, &QAction::triggered, this, &FolderMenu::onPasteActionTriggered); | ||||
| 
 | ||||
|   separator2_ = addSeparator(); | ||||
| 
 | ||||
|   selectAllAction_ = new QAction(tr("Select &All"), this); | ||||
|   addAction(selectAllAction_); | ||||
|   connect(selectAllAction_, &QAction::triggered, this, &FolderMenu::onSelectAllActionTriggered); | ||||
| 
 | ||||
|   invertSelectionAction_ = new QAction(tr("Invert Selection"), this); | ||||
|   addAction(invertSelectionAction_); | ||||
|   connect(invertSelectionAction_, &QAction::triggered, this, &FolderMenu::onInvertSelectionActionTriggered); | ||||
| 
 | ||||
|   separator3_ = addSeparator(); | ||||
| 
 | ||||
|   sortAction_ = new QAction(tr("Sorting"), this); | ||||
|   addAction(sortAction_); | ||||
|   createSortMenu(); | ||||
|   sortAction_->setMenu(sortMenu_); | ||||
| 
 | ||||
|   showHiddenAction_ = new QAction(tr("Show Hidden"), this); | ||||
|   addAction(showHiddenAction_); | ||||
|   showHiddenAction_->setCheckable(true); | ||||
|   showHiddenAction_->setChecked(model->showHidden()); | ||||
|   connect(showHiddenAction_, &QAction::triggered, this, &FolderMenu::onShowHiddenActionTriggered); | ||||
| 
 | ||||
|   separator4_ = addSeparator(); | ||||
| 
 | ||||
|   propertiesAction_ = new QAction(tr("Folder Pr&operties"), this); | ||||
|   addAction(propertiesAction_); | ||||
|   connect(propertiesAction_, &QAction::triggered, this, &FolderMenu::onPropertiesActionTriggered); | ||||
| } | ||||
| 
 | ||||
| FolderMenu::~FolderMenu() { | ||||
| } | ||||
| 
 | ||||
| void FolderMenu::createCreateNewMenu() { | ||||
|   QMenu* createMenu = new QMenu(this); | ||||
|   createNewMenu_ = createMenu; | ||||
| 
 | ||||
|   QAction* action = new QAction(tr("Folder"), this); | ||||
|   connect(action, &QAction::triggered, this, &FolderMenu::onCreateNewFolder); | ||||
|   createMenu->addAction(action); | ||||
| 
 | ||||
|   action = new QAction(tr("Blank File"), this); | ||||
|   connect(action, &QAction::triggered, this, &FolderMenu::onCreateNewFile); | ||||
|   createMenu->addAction(action); | ||||
| 
 | ||||
|   // add more items to "Create New" menu from templates
 | ||||
|   GList* templates = fm_template_list_all(fm_config->only_user_templates); | ||||
|   if(templates) { | ||||
|     createMenu->addSeparator(); | ||||
|     for(GList* l = templates; l; l = l->next) { | ||||
|       FmTemplate* templ = (FmTemplate*)l->data; | ||||
|       /* we support directories differently */ | ||||
|       if(fm_template_is_directory(templ)) | ||||
|         continue; | ||||
|       FmMimeType* mime_type = fm_template_get_mime_type(templ); | ||||
|       const char* label = fm_template_get_label(templ); | ||||
|       QString text = QString("%1 (%2)").arg(QString::fromUtf8(label)).arg(QString::fromUtf8(fm_mime_type_get_desc(mime_type))); | ||||
|       FmIcon* icon = fm_template_get_icon(templ); | ||||
|       if(!icon) | ||||
|         icon = fm_mime_type_get_icon(mime_type); | ||||
|       QAction* action = createMenu->addAction(IconTheme::icon(icon), text); | ||||
|       action->setObjectName(QString::fromUtf8(fm_template_get_name(templ, NULL))); | ||||
|       connect(action, &QAction::triggered, this, &FolderMenu::onCreateNew); | ||||
|     } | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| void FolderMenu::addSortMenuItem(QString title, int id) { | ||||
|   QAction* action = new QAction(title, this); | ||||
|   sortMenu_->addAction(action); | ||||
|   action->setCheckable(true); | ||||
|   sortActionGroup_->addAction(action); | ||||
|   connect(action, &QAction::triggered, this, &FolderMenu::onSortActionTriggered); | ||||
|   sortActions_[id] = action; | ||||
| } | ||||
| 
 | ||||
| void FolderMenu::createSortMenu() { | ||||
|   ProxyFolderModel* model = view_->model(); | ||||
| 
 | ||||
|   sortMenu_ = new QMenu(this); | ||||
|   sortActionGroup_ = new QActionGroup(sortMenu_); | ||||
|   sortActionGroup_->setExclusive(true); | ||||
| 
 | ||||
|   std::memset(sortActions_, 0, sizeof(sortActions_)); | ||||
| 
 | ||||
|   addSortMenuItem(tr("By File Name"), FolderModel::ColumnFileName); | ||||
|   addSortMenuItem(tr("By Modification Time"), FolderModel::ColumnFileMTime); | ||||
|   addSortMenuItem(tr("By File Size"), FolderModel::ColumnFileSize); | ||||
|   addSortMenuItem(tr("By File Type"), FolderModel::ColumnFileType); | ||||
|   addSortMenuItem(tr("By File Owner"), FolderModel::ColumnFileOwner); | ||||
| 
 | ||||
|   int col = model->sortColumn(); | ||||
| 
 | ||||
|   if(col >= 0 && col < FolderModel::NumOfColumns) { | ||||
|     sortActions_[col]->setChecked(true);; | ||||
|   } | ||||
| 
 | ||||
|   sortMenu_->addSeparator(); | ||||
| 
 | ||||
|   QActionGroup* group = new QActionGroup(this); | ||||
|   group->setExclusive(true); | ||||
|   actionAscending_ = new QAction(tr("Ascending"), this); | ||||
|   actionAscending_->setCheckable(true); | ||||
|   sortMenu_->addAction(actionAscending_); | ||||
|   group->addAction(actionAscending_); | ||||
| 
 | ||||
|   actionDescending_ = new QAction(tr("Descending"), this); | ||||
|   actionDescending_->setCheckable(true); | ||||
|   sortMenu_->addAction(actionDescending_); | ||||
|   group->addAction(actionDescending_); | ||||
| 
 | ||||
|   if(model->sortOrder() == Qt::AscendingOrder) | ||||
|     actionAscending_->setChecked(true); | ||||
|   else | ||||
|     actionDescending_->setChecked(true); | ||||
| 
 | ||||
|   connect(actionAscending_, &QAction::triggered, this, &FolderMenu::onSortOrderActionTriggered); | ||||
|   connect(actionDescending_, &QAction::triggered, this, &FolderMenu::onSortOrderActionTriggered); | ||||
| 
 | ||||
|   sortMenu_->addSeparator(); | ||||
| 
 | ||||
|   QAction* actionFolderFirst = new QAction(tr("Folder First"), this); | ||||
|   sortMenu_->addAction(actionFolderFirst); | ||||
|   actionFolderFirst->setCheckable(true); | ||||
| 
 | ||||
|   if(model->folderFirst()) | ||||
|     actionFolderFirst->setChecked(true); | ||||
| 
 | ||||
|   connect(actionFolderFirst, &QAction::triggered, this, &FolderMenu::onFolderFirstActionTriggered); | ||||
| 
 | ||||
|   QAction* actionCaseSensitive = new QAction(tr("Case Sensitive"), this); | ||||
|   sortMenu_->addAction(actionCaseSensitive); | ||||
|   actionCaseSensitive->setCheckable(true); | ||||
| 
 | ||||
|   if(model->sortCaseSensitivity() == Qt::CaseSensitive) | ||||
|     actionCaseSensitive->setChecked(true); | ||||
| 
 | ||||
|   connect(actionCaseSensitive, &QAction::triggered, this, &FolderMenu::onCaseSensitiveActionTriggered); | ||||
| } | ||||
| 
 | ||||
| void FolderMenu::onPasteActionTriggered() { | ||||
|   FmPath* folderPath = view_->path(); | ||||
| 
 | ||||
|   if(folderPath) | ||||
|     pasteFilesFromClipboard(folderPath); | ||||
| } | ||||
| 
 | ||||
| void FolderMenu::onSelectAllActionTriggered() { | ||||
|   view_->selectAll(); | ||||
| } | ||||
| 
 | ||||
| void FolderMenu::onInvertSelectionActionTriggered() { | ||||
|   view_->invertSelection(); | ||||
| } | ||||
| 
 | ||||
| void FolderMenu::onSortActionTriggered(bool checked) { | ||||
|   ProxyFolderModel* model = view_->model(); | ||||
| 
 | ||||
|   if(model) { | ||||
|     QAction* action = static_cast<QAction*>(sender()); | ||||
| 
 | ||||
|     for(int col = 0; col < FolderModel::NumOfColumns; ++col) { | ||||
|       if(action == sortActions_[col]) { | ||||
|         model->sort(col, model->sortOrder()); | ||||
|         break; | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| void FolderMenu::onSortOrderActionTriggered(bool checked) { | ||||
|   ProxyFolderModel* model = view_->model(); | ||||
| 
 | ||||
|   if(model) { | ||||
|     QAction* action = static_cast<QAction*>(sender()); | ||||
|     Qt::SortOrder order; | ||||
| 
 | ||||
|     if(action == actionAscending_) | ||||
|       order = Qt::AscendingOrder; | ||||
|     else | ||||
|       order = Qt::DescendingOrder; | ||||
| 
 | ||||
|     model->sort(model->sortColumn(), order); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| void FolderMenu::onShowHiddenActionTriggered(bool checked) { | ||||
|   ProxyFolderModel* model = view_->model(); | ||||
| 
 | ||||
|   if(model) { | ||||
|     qDebug("show hidden: %d", checked); | ||||
|     model->setShowHidden(checked); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| void FolderMenu::onCaseSensitiveActionTriggered(bool checked) { | ||||
|   ProxyFolderModel* model = view_->model(); | ||||
| 
 | ||||
|   if(model) { | ||||
|     model->setSortCaseSensitivity(checked ? Qt::CaseSensitive : Qt::CaseInsensitive); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| void FolderMenu::onFolderFirstActionTriggered(bool checked) { | ||||
|   ProxyFolderModel* model = view_->model(); | ||||
| 
 | ||||
|   if(model) { | ||||
|     model->setFolderFirst(checked); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| void FolderMenu::onPropertiesActionTriggered() { | ||||
|   FmFileInfo* folderInfo = view_->folderInfo(); | ||||
| 
 | ||||
|   if(folderInfo) | ||||
|     FilePropsDialog::showForFile(folderInfo); | ||||
| } | ||||
| 
 | ||||
| void FolderMenu::onCreateNewFile() { | ||||
|   FmPath* dirPath = view_->path(); | ||||
| 
 | ||||
|   if(dirPath) | ||||
|     createFile(CreateNewTextFile, dirPath); | ||||
| } | ||||
| 
 | ||||
| void FolderMenu::onCreateNewFolder() { | ||||
|   FmPath* dirPath = view_->path(); | ||||
| 
 | ||||
|   if(dirPath) | ||||
|     createFile(CreateNewFolder, dirPath); | ||||
| } | ||||
| 
 | ||||
| void FolderMenu::onCreateNew() { | ||||
|   QAction* action = static_cast<QAction*>(sender()); | ||||
|   QByteArray name = action->objectName().toUtf8(); | ||||
|   GList* templates = fm_template_list_all(fm_config->only_user_templates); | ||||
|   FmTemplate* templ = NULL; | ||||
|   for(GList* l = templates; l; l = l->next) { | ||||
|     FmTemplate* t = (FmTemplate*)l->data; | ||||
|     if(name == fm_template_get_name(t, NULL)) { | ||||
|       templ = t; | ||||
|       break; | ||||
|     } | ||||
|   } | ||||
|   if(templ) { // template found
 | ||||
|     FmPath* dirPath = view_->path(); | ||||
|     if(dirPath) | ||||
|       createFile(CreateWithTemplate, dirPath, templ, view_); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| } // namespace Fm
 | ||||
							
								
								
									
										134
									
								
								libfm-qt/foldermenu.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										134
									
								
								libfm-qt/foldermenu.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,134 @@ | ||||
| /*
 | ||||
| 
 | ||||
|     Copyright (C) 2013  Hong Jen Yee (PCMan) <pcman.tw@gmail.com> | ||||
| 
 | ||||
|     This program is free software; you can redistribute it and/or modify | ||||
|     it under the terms of the GNU General Public License as published by | ||||
|     the Free Software Foundation; either version 2 of the License, or | ||||
|     (at your option) any later version. | ||||
| 
 | ||||
|     This program is distributed in the hope that it will be useful, | ||||
|     but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|     GNU General Public License for more details. | ||||
| 
 | ||||
|     You should have received a copy of the GNU General Public License along | ||||
|     with this program; if not, write to the Free Software Foundation, Inc., | ||||
|     51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||||
| */ | ||||
| 
 | ||||
| 
 | ||||
| #ifndef FM_FOLDERMENU_H | ||||
| #define FM_FOLDERMENU_H | ||||
| 
 | ||||
| #include "libfmqtglobals.h" | ||||
| #include <QMenu> | ||||
| #include <libfm/fm.h> | ||||
| #include "foldermodel.h" | ||||
| 
 | ||||
| class QAction; | ||||
| 
 | ||||
| namespace Fm { | ||||
| 
 | ||||
| class FolderView; | ||||
|    | ||||
| class LIBFM_QT_API FolderMenu : public QMenu { | ||||
| Q_OBJECT | ||||
| 
 | ||||
| public: | ||||
|   explicit FolderMenu(FolderView* view, QWidget* parent = 0); | ||||
|   virtual ~FolderMenu(); | ||||
| 
 | ||||
|   QAction* createAction() { | ||||
|     return createAction_;   | ||||
|   } | ||||
| 
 | ||||
|   QAction* separator1() { | ||||
|     return separator1_;   | ||||
|   } | ||||
| 
 | ||||
|   QAction* pasteAction() { | ||||
|     return pasteAction_;   | ||||
|   } | ||||
| 
 | ||||
|   QAction* separator2() { | ||||
|     return separator2_;   | ||||
|   } | ||||
| 
 | ||||
|   QAction* selectAllAction() { | ||||
|     return selectAllAction_;   | ||||
|   } | ||||
| 
 | ||||
|   QAction* invertSelectionAction() { | ||||
|     return invertSelectionAction_;   | ||||
|   } | ||||
| 
 | ||||
|   QAction* separator3() { | ||||
|     return separator3_;   | ||||
|   } | ||||
| 
 | ||||
|   QAction* sortAction() { | ||||
|     return sortAction_;   | ||||
|   } | ||||
| 
 | ||||
|   QAction* showHiddenAction() { | ||||
|     return showHiddenAction_;   | ||||
|   } | ||||
| 
 | ||||
|   QAction* separator4() { | ||||
|     return separator4_;   | ||||
|   } | ||||
| 
 | ||||
|   QAction* propertiesAction() { | ||||
|     return propertiesAction_;   | ||||
|   } | ||||
| 
 | ||||
|   FolderView* view() { | ||||
|     return view_; | ||||
|   } | ||||
|    | ||||
| protected Q_SLOTS: | ||||
| 
 | ||||
|   void onCreateNewFolder(); | ||||
|   void onCreateNewFile(); | ||||
|   void onCreateNew(); | ||||
| 
 | ||||
|   void onPasteActionTriggered(); | ||||
|   void onSelectAllActionTriggered(); | ||||
|   void onInvertSelectionActionTriggered(); | ||||
|   void onSortActionTriggered(bool checked); | ||||
|   void onSortOrderActionTriggered(bool checked); | ||||
|   void onShowHiddenActionTriggered(bool checked); | ||||
|   void onCaseSensitiveActionTriggered(bool checked); | ||||
|   void onFolderFirstActionTriggered(bool checked); | ||||
|   void onPropertiesActionTriggered(); | ||||
| 
 | ||||
| private: | ||||
|   void createCreateNewMenu(); | ||||
|   void createSortMenu(); | ||||
|   void addSortMenuItem(QString title, int id); | ||||
| 
 | ||||
| private: | ||||
|   FolderView* view_; | ||||
|   QAction* createAction_; | ||||
|   QMenu* createNewMenu_; | ||||
|   QAction* separator1_; | ||||
|   QAction* pasteAction_; | ||||
|   QAction* separator2_; | ||||
|   QAction* selectAllAction_; | ||||
|   QAction* invertSelectionAction_; | ||||
|   QAction* separator3_; | ||||
|   QAction* sortAction_; | ||||
|   QActionGroup* sortActionGroup_; | ||||
|   QMenu* sortMenu_; | ||||
|   QAction* sortActions_[FolderModel::NumOfColumns]; | ||||
|   QAction* actionAscending_; | ||||
|   QAction* actionDescending_; | ||||
|   QAction* showHiddenAction_; | ||||
|   QAction* separator4_; | ||||
|   QAction* propertiesAction_; | ||||
| }; | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| #endif // FM_FOLDERMENU_H
 | ||||
							
								
								
									
										557
									
								
								libfm-qt/foldermodel.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										557
									
								
								libfm-qt/foldermodel.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,557 @@ | ||||
| /*
 | ||||
|     <one line to give the library's name and an idea of what it does.> | ||||
|     Copyright (C) 2012  Hong Jen Yee (PCMan) <pcman.tw@gmail.com> | ||||
| 
 | ||||
|     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 | ||||
| */ | ||||
| 
 | ||||
| 
 | ||||
| #include "foldermodel.h" | ||||
| #include "icontheme.h" | ||||
| #include <iostream> | ||||
| #include <QtAlgorithms> | ||||
| #include <QVector> | ||||
| #include <qmimedata.h> | ||||
| #include <QMimeData> | ||||
| #include <QByteArray> | ||||
| #include <QPixmap> | ||||
| #include <QPainter> | ||||
| #include "utilities.h" | ||||
| #include "fileoperation.h" | ||||
| #include "thumbnailloader.h" | ||||
| 
 | ||||
| using namespace Fm; | ||||
| 
 | ||||
| FolderModel::FolderModel() :  | ||||
|   folder_(NULL) { | ||||
| /*
 | ||||
|     ColumnIcon, | ||||
|     ColumnName, | ||||
|     ColumnFileType, | ||||
|     ColumnMTime, | ||||
|     NumOfColumns | ||||
| */ | ||||
|   thumbnailRefCounts.reserve(4); | ||||
|    | ||||
|   // reload all icons when the icon theme is changed
 | ||||
|   connect(IconTheme::instance(), &IconTheme::changed, this, &FolderModel::updateIcons); | ||||
| } | ||||
| 
 | ||||
| FolderModel::~FolderModel() { | ||||
|   qDebug("delete FolderModel"); | ||||
| 
 | ||||
|   if(folder_) | ||||
|     setFolder(NULL); | ||||
|    | ||||
|   // if the thumbnail requests list is not empty, cancel them
 | ||||
|   if(!thumbnailResults.empty()) { | ||||
|     Q_FOREACH(FmThumbnailLoader* res, thumbnailResults) { | ||||
|       ThumbnailLoader::cancel(res); | ||||
|     } | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| void FolderModel::setFolder(FmFolder* new_folder) { | ||||
|   if(folder_) { | ||||
|     removeAll();        // remove old items
 | ||||
|     g_signal_handlers_disconnect_by_func(folder_, gpointer(onStartLoading), this); | ||||
|     g_signal_handlers_disconnect_by_func(folder_, gpointer(onFinishLoading), this); | ||||
|     g_signal_handlers_disconnect_by_func(folder_, gpointer(onFilesAdded), this); | ||||
|     g_signal_handlers_disconnect_by_func(folder_, gpointer(onFilesChanged), this); | ||||
|     g_signal_handlers_disconnect_by_func(folder_, gpointer(onFilesRemoved), this); | ||||
|     g_object_unref(folder_); | ||||
|   } | ||||
|   if(new_folder) { | ||||
|     folder_ = FM_FOLDER(g_object_ref(new_folder)); | ||||
|     g_signal_connect(folder_, "start-loading", G_CALLBACK(onStartLoading), this); | ||||
|     g_signal_connect(folder_, "finish-loading", G_CALLBACK(onFinishLoading), this); | ||||
|     g_signal_connect(folder_, "files-added", G_CALLBACK(onFilesAdded), this); | ||||
|     g_signal_connect(folder_, "files-changed", G_CALLBACK(onFilesChanged), this); | ||||
|     g_signal_connect(folder_, "files-removed", G_CALLBACK(onFilesRemoved), this); | ||||
|     // handle the case if the folder is already loaded
 | ||||
|     if(fm_folder_is_loaded(folder_)) | ||||
|       insertFiles(0, fm_folder_get_files(folder_)); | ||||
|   } | ||||
|   else | ||||
|     folder_ = NULL; | ||||
| } | ||||
| 
 | ||||
| void FolderModel::onStartLoading(FmFolder* folder, gpointer user_data) { | ||||
|   FolderModel* model = static_cast<FolderModel*>(user_data); | ||||
|   // remove all items
 | ||||
|   model->removeAll(); | ||||
| } | ||||
| 
 | ||||
| void FolderModel::onFinishLoading(FmFolder* folder, gpointer user_data) { | ||||
|   FolderModel* model = static_cast<FolderModel*>(user_data); | ||||
| } | ||||
| 
 | ||||
| void FolderModel::onFilesAdded(FmFolder* folder, GSList* files, gpointer user_data) { | ||||
|   FolderModel* model = static_cast<FolderModel*>(user_data); | ||||
|   int n_files = g_slist_length(files); | ||||
|   model->beginInsertRows(QModelIndex(), model->items.count(), model->items.count() + n_files - 1); | ||||
|   for(GSList* l = files; l; l = l->next) { | ||||
|     FmFileInfo* info = FM_FILE_INFO(l->data); | ||||
|     FolderModelItem item(info); | ||||
| /*
 | ||||
|     if(fm_file_info_is_hidden(info)) { | ||||
|       model->hiddenItems.append(item); | ||||
|       continue; | ||||
|     } | ||||
| */ | ||||
|     model->items.append(item); | ||||
|   } | ||||
|   model->endInsertRows(); | ||||
| } | ||||
| 
 | ||||
| //static
 | ||||
| void FolderModel::onFilesChanged(FmFolder* folder, GSList* files, gpointer user_data) { | ||||
|   FolderModel* model = static_cast<FolderModel*>(user_data); | ||||
|   int n_files = g_slist_length(files); | ||||
|   for(GSList* l = files; l; l = l->next) { | ||||
|     FmFileInfo* info = FM_FILE_INFO(l->data); | ||||
|     int row; | ||||
|     QList<FolderModelItem>::iterator it = model->findItemByFileInfo(info, &row); | ||||
|     if(it != model->items.end()) { | ||||
|       FolderModelItem& item = *it; | ||||
|       // try to update the item
 | ||||
|       item.displayName = QString::fromUtf8(fm_file_info_get_disp_name(info)); | ||||
|       item.updateIcon(); | ||||
|       item.thumbnails.clear(); | ||||
|       QModelIndex index = model->createIndex(row, 0, &item); | ||||
|       Q_EMIT model->dataChanged(index, index); | ||||
|     } | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| //static
 | ||||
| void FolderModel::onFilesRemoved(FmFolder* folder, GSList* files, gpointer user_data) { | ||||
|   FolderModel* model = static_cast<FolderModel*>(user_data); | ||||
|   for(GSList* l = files; l; l = l->next) { | ||||
|     FmFileInfo* info = FM_FILE_INFO(l->data); | ||||
|     const char* name = fm_file_info_get_name(info); | ||||
|     int row; | ||||
|     QList<FolderModelItem>::iterator it = model->findItemByName(name, &row); | ||||
|     if(it != model->items.end()) { | ||||
|       model->beginRemoveRows(QModelIndex(), row, row); | ||||
|       model->items.erase(it); | ||||
|       model->endRemoveRows(); | ||||
|     } | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| void FolderModel::insertFiles(int row, FmFileInfoList* files) { | ||||
|   int n_files = fm_file_info_list_get_length(files); | ||||
|   beginInsertRows(QModelIndex(), row, row + n_files - 1); | ||||
|   for(GList* l = fm_file_info_list_peek_head_link(files); l; l = l->next) { | ||||
|     FolderModelItem item(FM_FILE_INFO(l->data)); | ||||
|     items.append(item); | ||||
|   } | ||||
|   endInsertRows(); | ||||
| } | ||||
| 
 | ||||
| void FolderModel::removeAll() { | ||||
|   if(items.empty()) | ||||
|     return; | ||||
|   beginRemoveRows(QModelIndex(), 0, items.size() - 1); | ||||
|   items.clear(); | ||||
|   endRemoveRows(); | ||||
| } | ||||
| 
 | ||||
| int FolderModel::rowCount(const QModelIndex & parent) const { | ||||
|   if(parent.isValid()) | ||||
|     return 0; | ||||
|   return items.size(); | ||||
| } | ||||
| 
 | ||||
| int FolderModel::columnCount (const QModelIndex & parent = QModelIndex()) const { | ||||
|   if(parent.isValid()) | ||||
|     return 0; | ||||
|   return NumOfColumns; | ||||
| } | ||||
| 
 | ||||
| FolderModelItem* FolderModel::itemFromIndex(const QModelIndex& index) const { | ||||
|   return reinterpret_cast<FolderModelItem*>(index.internalPointer()); | ||||
| } | ||||
| 
 | ||||
| FmFileInfo* FolderModel::fileInfoFromIndex(const QModelIndex& index) const { | ||||
|   FolderModelItem* item = itemFromIndex(index); | ||||
|   return item ? item->info : NULL; | ||||
| } | ||||
| 
 | ||||
| QVariant FolderModel::data(const QModelIndex & index, int role = Qt::DisplayRole) const { | ||||
|   if(!index.isValid() || index.row() > items.size() || index.column() >= NumOfColumns) { | ||||
|     return QVariant(); | ||||
|   } | ||||
|   FolderModelItem* item = itemFromIndex(index); | ||||
|   FmFileInfo* info = item->info; | ||||
| 
 | ||||
|   switch(role) { | ||||
|     case Qt::ToolTipRole: | ||||
|       return QVariant(item->displayName); | ||||
|     case Qt::DisplayRole:  { | ||||
|       switch(index.column()) { | ||||
|         case ColumnFileName: { | ||||
|           return QVariant(item->displayName); | ||||
|         } | ||||
|         case ColumnFileType: { | ||||
|           FmMimeType* mime = fm_file_info_get_mime_type(info); | ||||
|           const char* desc = fm_mime_type_get_desc(mime); | ||||
|           return QString::fromUtf8(desc); | ||||
|         } | ||||
|         case ColumnFileMTime: { | ||||
|           const char* name = fm_file_info_get_disp_mtime(info); | ||||
|           return QString::fromUtf8(name); | ||||
|         } | ||||
|         case ColumnFileSize: { | ||||
|           const char* name = fm_file_info_get_disp_size(info); | ||||
|           return QString::fromUtf8(name); | ||||
|           break; | ||||
|         } | ||||
|         case ColumnFileOwner: { | ||||
|           const char* name = fm_file_info_get_disp_owner(info); | ||||
|           return QString::fromUtf8(name); | ||||
|           break; | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|     case Qt::DecorationRole: { | ||||
|       if(index.column() == 0) { | ||||
|         // QPixmap pix = IconTheme::loadIcon(fm_file_info_get_icon(info), iconSize_);
 | ||||
|         return QVariant(item->icon); | ||||
|         // return QVariant(pix);
 | ||||
|       } | ||||
|     } | ||||
|     case FileInfoRole: | ||||
|       return qVariantFromValue((void*)info); | ||||
|   } | ||||
|   return QVariant(); | ||||
| } | ||||
| 
 | ||||
| QVariant FolderModel::headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const { | ||||
|   if(role == Qt::DisplayRole) { | ||||
|     if(orientation == Qt::Horizontal) { | ||||
|       QString title; | ||||
|       switch(section) { | ||||
|         case ColumnFileName: | ||||
|           title = tr("Name"); | ||||
|           break; | ||||
|         case ColumnFileType: | ||||
|           title = tr("Type"); | ||||
|           break; | ||||
|         case ColumnFileSize: | ||||
|           title = tr("Size"); | ||||
|           break; | ||||
|         case ColumnFileMTime: | ||||
|           title = tr("Modified"); | ||||
|           break; | ||||
|         case ColumnFileOwner: | ||||
|           title = tr("Owner"); | ||||
|           break; | ||||
|       } | ||||
|       return QVariant(title); | ||||
|     } | ||||
|   } | ||||
|   return QVariant(); | ||||
| } | ||||
| 
 | ||||
| QModelIndex FolderModel::index(int row, int column, const QModelIndex & parent) const { | ||||
|   if(row <0 || row >= items.size() || column < 0 || column >= NumOfColumns) | ||||
|     return QModelIndex(); | ||||
|   const FolderModelItem& item = items.at(row); | ||||
|   return createIndex(row, column, (void*)&item); | ||||
| } | ||||
| 
 | ||||
| QModelIndex FolderModel::parent(const QModelIndex & index) const { | ||||
|   return QModelIndex(); | ||||
| } | ||||
| 
 | ||||
| Qt::ItemFlags FolderModel::flags(const QModelIndex& index) const { | ||||
|   // FIXME: should not return same flags unconditionally for all columns
 | ||||
|   Qt::ItemFlags flags; | ||||
|   if(index.isValid()) { | ||||
|     flags = Qt::ItemIsEnabled|Qt::ItemIsSelectable; | ||||
|     if(index.column() == ColumnFileName) | ||||
|       flags |= (Qt::ItemIsDragEnabled|Qt::ItemIsDropEnabled); | ||||
|   } | ||||
|   else { | ||||
|     flags = Qt::ItemIsDropEnabled; | ||||
|   } | ||||
|   return flags; | ||||
| } | ||||
| 
 | ||||
| // FIXME: this is very inefficient and should be replaced with a 
 | ||||
| // more reasonable implementation later.
 | ||||
| QList<FolderModelItem>::iterator FolderModel::findItemByPath(FmPath* path, int* row) { | ||||
|   QList<FolderModelItem>::iterator it = items.begin(); | ||||
|   int i = 0; | ||||
|   while(it != items.end()) { | ||||
|     FolderModelItem& item = *it; | ||||
|     FmPath* item_path = fm_file_info_get_path(item.info); | ||||
|     if(fm_path_equal(item_path, path)) { | ||||
|       *row = i; | ||||
|       return it; | ||||
|     } | ||||
|     ++it; | ||||
|     ++i; | ||||
|   } | ||||
|   return items.end(); | ||||
| } | ||||
| 
 | ||||
| // FIXME: this is very inefficient and should be replaced with a 
 | ||||
| // more reasonable implementation later.
 | ||||
| QList<FolderModelItem>::iterator FolderModel::findItemByName(const char* name, int* row) { | ||||
|   QList<FolderModelItem>::iterator it = items.begin(); | ||||
|   int i = 0; | ||||
|   while(it != items.end()) { | ||||
|     FolderModelItem& item = *it; | ||||
|     const char* item_name = fm_file_info_get_name(item.info); | ||||
|     if(strcmp(name, item_name) == 0) { | ||||
|       *row = i; | ||||
|       return it; | ||||
|     } | ||||
|     ++it; | ||||
|     ++i; | ||||
|   } | ||||
|   return items.end(); | ||||
| } | ||||
| 
 | ||||
| QList< FolderModelItem >::iterator FolderModel::findItemByFileInfo(FmFileInfo* info, int* row) { | ||||
|   QList<FolderModelItem>::iterator it = items.begin(); | ||||
|   int i = 0; | ||||
|   while(it != items.end()) { | ||||
|     FolderModelItem& item = *it; | ||||
|     if(item.info == info) { | ||||
|       *row = i; | ||||
|       return it; | ||||
|     } | ||||
|     ++it; | ||||
|     ++i; | ||||
|   } | ||||
|   return items.end(); | ||||
| } | ||||
| 
 | ||||
| QStringList FolderModel::mimeTypes() const { | ||||
|   qDebug("FolderModel::mimeTypes"); | ||||
|   QStringList types = QAbstractItemModel::mimeTypes(); | ||||
|   // now types contains "application/x-qabstractitemmodeldatalist"
 | ||||
|   types << "text/uri-list"; | ||||
|   // types << "x-special/gnome-copied-files";
 | ||||
|   return types; | ||||
| } | ||||
| 
 | ||||
| QMimeData* FolderModel::mimeData(const QModelIndexList& indexes) const { | ||||
|   QMimeData* data = QAbstractItemModel::mimeData(indexes); | ||||
|   qDebug("FolderModel::mimeData"); | ||||
|   // build a uri list
 | ||||
|   QByteArray urilist; | ||||
|   urilist.reserve(4096); | ||||
|    | ||||
|   QModelIndexList::const_iterator it; | ||||
|   for(it = indexes.constBegin(); it != indexes.end(); ++it) { | ||||
|     const QModelIndex index = *it; | ||||
|     FolderModelItem* item = itemFromIndex(index); | ||||
|     if(item) { | ||||
|       FmPath* path = fm_file_info_get_path(item->info); | ||||
|       char* uri = fm_path_to_uri(path); | ||||
|       urilist.append(uri); | ||||
|       urilist.append('\n'); | ||||
|       g_free(uri); | ||||
|     } | ||||
|   } | ||||
|   data->setData("text/uri-list", urilist); | ||||
|    | ||||
|   return data; | ||||
| } | ||||
| 
 | ||||
| bool FolderModel::dropMimeData(const QMimeData* data, Qt::DropAction action, int row, int column, const QModelIndex& parent) { | ||||
|   qDebug("FolderModel::dropMimeData"); | ||||
|   if(!folder_) | ||||
|     return false; | ||||
|   FmPath* destPath; | ||||
|   if(parent.isValid()) { // drop on an item
 | ||||
|     FmFileInfo* info; | ||||
|     if(row == -1 && column == -1) | ||||
|       info = fileInfoFromIndex(parent); | ||||
|     else { | ||||
|       QModelIndex itemIndex = parent.child(row, column); | ||||
|       info = fileInfoFromIndex(itemIndex); | ||||
|     } | ||||
|     if(info) | ||||
|       destPath = fm_file_info_get_path(info); | ||||
|     else | ||||
|       return false; | ||||
|   } | ||||
|   else { // drop on blank area of the folder
 | ||||
|     destPath = path(); | ||||
|   } | ||||
| 
 | ||||
|   // FIXME: should we put this in dropEvent handler of FolderView instead?
 | ||||
|   if(data->hasUrls()) { | ||||
|     qDebug("drop action: %d", action); | ||||
|     FmPathList* srcPaths = pathListFromQUrls(data->urls()); | ||||
|     switch(action) { | ||||
|       case Qt::CopyAction: | ||||
|         FileOperation::copyFiles(srcPaths, destPath); | ||||
|         break; | ||||
|       case Qt::MoveAction: | ||||
|         FileOperation::moveFiles(srcPaths, destPath); | ||||
|         break; | ||||
|       case Qt::LinkAction: | ||||
|         FileOperation::symlinkFiles(srcPaths, destPath); | ||||
|       default: | ||||
|         fm_path_list_unref(srcPaths); | ||||
|         return false; | ||||
|     } | ||||
|     fm_path_list_unref(srcPaths); | ||||
|     return true; | ||||
|   } | ||||
|   else if(data->hasFormat("application/x-qabstractitemmodeldatalist")) { | ||||
|     return true; | ||||
|   } | ||||
|   return QAbstractListModel::dropMimeData(data, action, row, column, parent); | ||||
| } | ||||
| 
 | ||||
| Qt::DropActions FolderModel::supportedDropActions() const { | ||||
|   qDebug("FolderModel::supportedDropActions"); | ||||
|   return Qt::CopyAction|Qt::MoveAction|Qt::LinkAction; | ||||
| } | ||||
| 
 | ||||
| // ask the model to load thumbnails of the specified size
 | ||||
| void FolderModel::cacheThumbnails(int size) { | ||||
|   QVector<QPair<int, int> >::iterator it; | ||||
|   for(it = thumbnailRefCounts.begin(); it != thumbnailRefCounts.end(); ++it) { | ||||
|     if(it->first == size) { | ||||
|       break; | ||||
|     } | ||||
|   } | ||||
|   if(it != thumbnailRefCounts.end()) | ||||
|     ++it->second; | ||||
|   else | ||||
|     thumbnailRefCounts.append(QPair<int, int>(size, 1)); | ||||
| } | ||||
| 
 | ||||
| // ask the model to free cached thumbnails of the specified size
 | ||||
| void FolderModel::releaseThumbnails(int size) { | ||||
|   QVector<QPair<int, int> >::iterator it; | ||||
|   for(it = thumbnailRefCounts.begin(); it != thumbnailRefCounts.end(); ++it) { | ||||
|     if(it->first == size) { | ||||
|       break; | ||||
|     } | ||||
|   } | ||||
|   if(it != thumbnailRefCounts.end()) { | ||||
|     --it->second; | ||||
|     if(it->second == 0) { | ||||
|       thumbnailRefCounts.erase(it); | ||||
| 
 | ||||
|       // remove thumbnails that ara queued for loading from thumbnailResults
 | ||||
|       QLinkedList<FmThumbnailLoader*>::iterator it; | ||||
|       for(it = thumbnailResults.begin(); it != thumbnailResults.end();) { | ||||
|         QLinkedList<FmThumbnailLoader*>::iterator next = it + 1; | ||||
|         FmThumbnailLoader* res = *it; | ||||
|         if(ThumbnailLoader::size(res) == size) { | ||||
|           ThumbnailLoader::cancel(res); | ||||
|           thumbnailResults.erase(it); | ||||
|         } | ||||
|         it = next; | ||||
|       } | ||||
| 
 | ||||
|       // remove all cached thumbnails of the specified size
 | ||||
|       QList<FolderModelItem>::iterator itemIt; | ||||
|       for(itemIt = items.begin(); itemIt != items.end(); ++itemIt) { | ||||
|         FolderModelItem& item = *itemIt; | ||||
|         item.removeThumbnail(size); | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| void FolderModel::onThumbnailLoaded(FmThumbnailLoader* res, gpointer user_data) { | ||||
|   FolderModel* pThis = reinterpret_cast<FolderModel*>(user_data); | ||||
|   QLinkedList<FmThumbnailLoader*>::iterator it; | ||||
|   for(it = pThis->thumbnailResults.begin(); it != pThis->thumbnailResults.end(); ++it) { | ||||
|     if(*it == res) { // the thumbnail result is in our list
 | ||||
|       pThis->thumbnailResults.erase(it); // remove it from the list
 | ||||
|       FmFileInfo* info = ThumbnailLoader::fileInfo(res); | ||||
|       int row = -1; | ||||
|       // find the model item this thumbnail belongs to
 | ||||
|       QList<FolderModelItem>::iterator it = pThis->findItemByFileInfo(info, &row); | ||||
|       if(it != pThis->items.end()) { | ||||
|         // the file is found in our model
 | ||||
|         FolderModelItem& item = *it; | ||||
|         QModelIndex index = pThis->createIndex(row, 0, (void*)&item); | ||||
|         // store the image in the folder model item.
 | ||||
|         int size = ThumbnailLoader::size(res); | ||||
|         QImage image = ThumbnailLoader::image(res); | ||||
|         FolderModelItem::Thumbnail* thumbnail = item.findThumbnail(size); | ||||
|         thumbnail->image = image; | ||||
|         // qDebug("thumbnail loaded for: %s, size: %d", item.displayName.toUtf8().constData(), size);
 | ||||
|         if(image.isNull()) | ||||
|           thumbnail->status = FolderModelItem::ThumbnailFailed; | ||||
|         else { | ||||
|           thumbnail->status = FolderModelItem::ThumbnailLoaded; | ||||
|           // FIXME: due to bugs in Qt's QStyledItemDelegate, if the image width and height
 | ||||
|           // are not the same, painting errors will happen. It's quite unfortunate.
 | ||||
|           // Let's do some padding to make its width and height equals.
 | ||||
|           // This greatly decrease performance :-(
 | ||||
|           // Later if we can re-implement our own item delegate, this can be avoided.
 | ||||
|           QPixmap pixmap = QPixmap(size, size); | ||||
|           pixmap.fill(QColor(0, 0, 0, 0)); // fill the pixmap with transparent color (alpha:0)
 | ||||
|           QPainter painter(&pixmap); | ||||
|           int x = (size - image.width()) / 2; | ||||
|           int y = (size - image.height()) / 2; | ||||
|           painter.drawImage(QPoint(x, y), image); // draw the image to the pixmap at center.
 | ||||
|           // FIXME: should we cache QPixmap instead for performance reason?
 | ||||
|           thumbnail->image = pixmap.toImage(); // convert it back to image
 | ||||
| 
 | ||||
|           // tell the world that we have the thumbnail loaded
 | ||||
|           Q_EMIT pThis->thumbnailLoaded(index, size); | ||||
|         } | ||||
|       } | ||||
|       break; | ||||
|     } | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| // get a thumbnail of size at the index
 | ||||
| // if a thumbnail is not yet loaded, this will initiate loading of the thumbnail.
 | ||||
| QImage FolderModel::thumbnailFromIndex(const QModelIndex& index, int size) { | ||||
|   FolderModelItem* item = itemFromIndex(index); | ||||
|   if(item) { | ||||
|     FolderModelItem::Thumbnail* thumbnail = item->findThumbnail(size); | ||||
|     // qDebug("FolderModel::thumbnailFromIndex: %d, %s", thumbnail->status, item->displayName.toUtf8().data());
 | ||||
|     switch(thumbnail->status) { | ||||
|       case FolderModelItem::ThumbnailNotChecked: { | ||||
|         // load the thumbnail
 | ||||
|         FmThumbnailLoader* res = ThumbnailLoader::load(item->info, size, onThumbnailLoaded, this); | ||||
|         thumbnailResults.push_back(res); | ||||
|         thumbnail->status = FolderModelItem::ThumbnailLoading; | ||||
|         break; | ||||
|       } | ||||
|       case FolderModelItem::ThumbnailLoaded: | ||||
|         return thumbnail->image; | ||||
|       default: | ||||
|         break; | ||||
|     } | ||||
|   } | ||||
|   return QImage(); | ||||
| } | ||||
| 
 | ||||
| void FolderModel::updateIcons() { | ||||
|   QList<FolderModelItem>::iterator it = items.begin(); | ||||
|   for(;it != items.end(); ++it) { | ||||
|     (*it).updateIcon(); | ||||
|   } | ||||
| } | ||||
							
								
								
									
										121
									
								
								libfm-qt/foldermodel.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										121
									
								
								libfm-qt/foldermodel.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,121 @@ | ||||
| /*
 | ||||
| 
 | ||||
|     Copyright (C) 2012  Hong Jen Yee (PCMan) <pcman.tw@gmail.com> | ||||
| 
 | ||||
|     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 | ||||
| */ | ||||
| 
 | ||||
| 
 | ||||
| #ifndef FM_FOLDERMODEL_H | ||||
| #define FM_FOLDERMODEL_H | ||||
| 
 | ||||
| #include "libfmqtglobals.h" | ||||
| #include <QAbstractListModel> | ||||
| #include <QIcon> | ||||
| #include <QImage> | ||||
| #include <libfm/fm.h> | ||||
| #include <QList> | ||||
| #include <QVector> | ||||
| #include <QLinkedList> | ||||
| #include <QPair> | ||||
| #include "foldermodelitem.h" | ||||
| 
 | ||||
| namespace Fm { | ||||
| 
 | ||||
| class LIBFM_QT_API FolderModel : public QAbstractListModel { | ||||
| Q_OBJECT | ||||
| public: | ||||
| 
 | ||||
|   enum Role { | ||||
|     FileInfoRole = Qt::UserRole | ||||
|   }; | ||||
| 
 | ||||
|   enum ColumnId { | ||||
|     ColumnFileName, | ||||
|     ColumnFileType, | ||||
|     ColumnFileSize, | ||||
|     ColumnFileMTime, | ||||
|     ColumnFileOwner, | ||||
|     NumOfColumns | ||||
|   }; | ||||
| 
 | ||||
| public: | ||||
|   FolderModel(); | ||||
|   virtual ~FolderModel(); | ||||
| 
 | ||||
|   FmFolder* folder() { | ||||
|     return folder_; | ||||
|   } | ||||
|   void setFolder(FmFolder* new_folder); | ||||
| 
 | ||||
|   FmPath* path() { | ||||
|     return folder_ ? fm_folder_get_path(folder_) : NULL; | ||||
|   } | ||||
| 
 | ||||
|   int rowCount(const QModelIndex & parent = QModelIndex()) const; | ||||
|   int columnCount (const QModelIndex & parent) const; | ||||
|   QVariant data(const QModelIndex & index, int role) const; | ||||
|   QVariant headerData(int section, Qt::Orientation orientation, int role) const; | ||||
|   QModelIndex index(int row, int column, const QModelIndex & parent = QModelIndex()) const; | ||||
|   QModelIndex parent( const QModelIndex & index ) const; | ||||
|   // void sort(int column, Qt::SortOrder order = Qt::AscendingOrder);
 | ||||
| 
 | ||||
|   Qt::ItemFlags flags(const QModelIndex & index) const; | ||||
| 
 | ||||
|   virtual QStringList mimeTypes() const; | ||||
|   virtual QMimeData* mimeData(const QModelIndexList & indexes) const; | ||||
|   virtual Qt::DropActions supportedDropActions() const; | ||||
|   virtual bool dropMimeData(const QMimeData* data, Qt::DropAction action, int row, int column, const QModelIndex& parent); | ||||
| 
 | ||||
|   FmFileInfo* fileInfoFromIndex(const QModelIndex& index) const; | ||||
|   FolderModelItem* itemFromIndex(const QModelIndex& index) const; | ||||
|   QImage thumbnailFromIndex(const QModelIndex& index, int size); | ||||
| 
 | ||||
|   void cacheThumbnails(int size); | ||||
|   void releaseThumbnails(int size); | ||||
| 
 | ||||
| Q_SIGNALS: | ||||
|   void thumbnailLoaded(const QModelIndex& index, int size); | ||||
| 
 | ||||
| public Q_SLOTS: | ||||
|   void updateIcons(); | ||||
| 
 | ||||
| protected: | ||||
|   static void onStartLoading(FmFolder* folder, gpointer user_data); | ||||
|   static void onFinishLoading(FmFolder* folder, gpointer user_data); | ||||
|   static void onFilesAdded(FmFolder* folder, GSList* files, gpointer user_data); | ||||
|   static void onFilesChanged(FmFolder* folder, GSList* files, gpointer user_data); | ||||
|   static void onFilesRemoved(FmFolder* folder, GSList* files, gpointer user_data); | ||||
|   static void onThumbnailLoaded(FmThumbnailLoader *res, gpointer user_data); | ||||
| 
 | ||||
|   void insertFiles(int row, FmFileInfoList* files); | ||||
|   void removeAll(); | ||||
|   QList<FolderModelItem>::iterator findItemByPath(FmPath* path, int* row); | ||||
|   QList<FolderModelItem>::iterator findItemByName(const char* name, int* row); | ||||
|   QList<FolderModelItem>::iterator findItemByFileInfo(FmFileInfo* info, int* row); | ||||
| 
 | ||||
| private: | ||||
|   FmFolder* folder_; | ||||
|   // FIXME: should we use a hash table here so item lookup becomes much faster?
 | ||||
|   QList<FolderModelItem> items; | ||||
| 
 | ||||
|   // record what size of thumbnails we should cache in an array of <size, refCount> pairs.
 | ||||
|   QVector<QPair<int, int> > thumbnailRefCounts; | ||||
|   QLinkedList<FmThumbnailLoader*> thumbnailResults; | ||||
| }; | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| #endif // FM_FOLDERMODEL_H
 | ||||
							
								
								
									
										93
									
								
								libfm-qt/foldermodelitem.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										93
									
								
								libfm-qt/foldermodelitem.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,93 @@ | ||||
| /*
 | ||||
|  *  | ||||
|  *  Copyright (C) 2012  Hong Jen Yee (PCMan) <pcman.tw@gmail.com> | ||||
|  *  | ||||
|  *  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 | ||||
|  */ | ||||
| 
 | ||||
| 
 | ||||
| #include "foldermodelitem.h" | ||||
| 
 | ||||
| using namespace Fm; | ||||
| 
 | ||||
| FolderModelItem::FolderModelItem(FmFileInfo* _info): | ||||
|   info(fm_file_info_ref(_info)) { | ||||
|   displayName = QString::fromUtf8(fm_file_info_get_disp_name(info)); | ||||
|   icon = IconTheme::icon(fm_file_info_get_icon(_info)); | ||||
|   thumbnails.reserve(2); | ||||
| } | ||||
| 
 | ||||
| FolderModelItem::FolderModelItem(const FolderModelItem& other) { | ||||
|   info = other.info ? fm_file_info_ref(other.info) : NULL; | ||||
|   displayName = QString::fromUtf8(fm_file_info_get_disp_name(info)); | ||||
|   icon = other.icon; | ||||
|   thumbnails = other.thumbnails; | ||||
| } | ||||
| 
 | ||||
| FolderModelItem::~FolderModelItem() { | ||||
|   if(info) | ||||
|     fm_file_info_unref(info); | ||||
| } | ||||
| 
 | ||||
| // find thumbnail of the specified size
 | ||||
| // The returned thumbnail item is temporary and short-lived
 | ||||
| // If you need to use the struct later, copy it to your own struct to keep it.
 | ||||
| FolderModelItem::Thumbnail* FolderModelItem::findThumbnail(int size) { | ||||
|   QVector<Thumbnail>::iterator it; | ||||
|   for(it = thumbnails.begin(); it != thumbnails.end(); ++it) { | ||||
|     if(it->size == size) { // an image of the same size is found
 | ||||
|       return it; | ||||
|     } | ||||
|   } | ||||
|   if(it == thumbnails.end()) { | ||||
|     Thumbnail thumbnail; | ||||
|     thumbnail.status = ThumbnailNotChecked; | ||||
|     thumbnail.size = size; | ||||
|     thumbnails.append(thumbnail); | ||||
|   } | ||||
|   return &thumbnails.back(); | ||||
| } | ||||
| 
 | ||||
| // remove cached thumbnail of the specified size
 | ||||
| void FolderModelItem::removeThumbnail(int size) { | ||||
|   QVector<Thumbnail>::iterator it; | ||||
|   for(it = thumbnails.begin(); it != thumbnails.end(); ++it) { | ||||
|     if(it->size == size) { // an image of the same size is found
 | ||||
|       thumbnails.erase(it); | ||||
|       break; | ||||
|     } | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| #if 0 | ||||
| // cache the thumbnail of the specified size in the folder item
 | ||||
| void FolderModelItem::setThumbnail(int size, QImage image) { | ||||
|   QVector<Thumbnail>::iterator it; | ||||
|   for(it = thumbnails.begin(); it != thumbnails.end(); ++it) { | ||||
|     if(it->size == size) { // an image of the same size already exists
 | ||||
|       it->image = image; // replace it
 | ||||
|       it->status = ThumbnailLoaded; | ||||
|       break; | ||||
|     } | ||||
|   } | ||||
|   if(it == thumbnails.end()) { // the image is not found
 | ||||
|     Thumbnail thumbnail; | ||||
|     thumbnail.size = size; | ||||
|     thumbnail.status = ThumbnailLoaded; | ||||
|     thumbnail.image = image; | ||||
|     thumbnails.append(thumbnail); // add a new entry
 | ||||
|   } | ||||
| } | ||||
| #endif | ||||
							
								
								
									
										71
									
								
								libfm-qt/foldermodelitem.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										71
									
								
								libfm-qt/foldermodelitem.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,71 @@ | ||||
| /*
 | ||||
|  *  | ||||
|  *  Copyright (C) 2012  Hong Jen Yee (PCMan) <pcman.tw@gmail.com> | ||||
|  *  | ||||
|  *  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 | ||||
|  */ | ||||
| 
 | ||||
| 
 | ||||
| #ifndef FM_FOLDERMODELITEM_H | ||||
| #define FM_FOLDERMODELITEM_H | ||||
| 
 | ||||
| #include "libfmqtglobals.h" | ||||
| #include <libfm/fm.h> | ||||
| #include <QImage> | ||||
| #include <QString> | ||||
| #include <QIcon> | ||||
| #include <QVector> | ||||
| #include "icontheme.h" | ||||
| 
 | ||||
| namespace Fm { | ||||
| 
 | ||||
| class LIBFM_QT_API FolderModelItem { | ||||
| public: | ||||
| 
 | ||||
|   enum ThumbnailStatus { | ||||
|     ThumbnailNotChecked, | ||||
|     ThumbnailLoading, | ||||
|     ThumbnailLoaded, | ||||
|     ThumbnailFailed | ||||
|   }; | ||||
| 
 | ||||
|   struct Thumbnail { | ||||
|     int size; | ||||
|     ThumbnailStatus status; | ||||
|     QImage image; | ||||
|   }; | ||||
| 
 | ||||
| public: | ||||
|   FolderModelItem(FmFileInfo* _info); | ||||
|   FolderModelItem(const FolderModelItem& other); | ||||
|   virtual ~FolderModelItem(); | ||||
| 
 | ||||
|   Thumbnail* findThumbnail(int size); | ||||
|   // void setThumbnail(int size, QImage image);
 | ||||
|   void removeThumbnail(int size); | ||||
| 
 | ||||
|   void updateIcon() { | ||||
|     icon = IconTheme::icon(fm_file_info_get_icon(info)); | ||||
|   } | ||||
| 
 | ||||
|   QString displayName; | ||||
|   QIcon icon; | ||||
|   FmFileInfo* info; | ||||
|   QVector<Thumbnail> thumbnails; | ||||
| }; | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| #endif // FM_FOLDERMODELITEM_H
 | ||||
							
								
								
									
										928
									
								
								libfm-qt/folderview.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										928
									
								
								libfm-qt/folderview.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,928 @@ | ||||
| /*
 | ||||
|     <one line to give the library's name and an idea of what it does.> | ||||
|     Copyright (C) 2012  Hong Jen Yee (PCMan) <pcman.tw@gmail.com> | ||||
| 
 | ||||
|     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 | ||||
| */ | ||||
| 
 | ||||
| 
 | ||||
| #include "folderview.h" | ||||
| #include "foldermodel.h" | ||||
| #include <QHeaderView> | ||||
| #include <QVBoxLayout> | ||||
| #include <QContextMenuEvent> | ||||
| #include "proxyfoldermodel.h" | ||||
| #include "folderitemdelegate.h" | ||||
| #include "dndactionmenu.h" | ||||
| #include "filemenu.h" | ||||
| #include "foldermenu.h" | ||||
| #include "filelauncher.h" | ||||
| #include <QTimer> | ||||
| #include <QDate> | ||||
| #include <QDebug> | ||||
| #include <QMimeData> | ||||
| #include <QHoverEvent> | ||||
| #include <QApplication> | ||||
| #include <QScrollBar> | ||||
| #include <QMetaType> | ||||
| #include "folderview_p.h" | ||||
| 
 | ||||
| Q_DECLARE_OPAQUE_POINTER(FmFileInfo*) | ||||
| 
 | ||||
| namespace Fm { | ||||
| 
 | ||||
| FolderViewListView::FolderViewListView(QWidget* parent): | ||||
|   QListView(parent), | ||||
|   activationAllowed_(true) { | ||||
|   connect(this, &QListView::activated, this, &FolderViewListView::activation); | ||||
| } | ||||
| 
 | ||||
| FolderViewListView::~FolderViewListView() { | ||||
| } | ||||
| 
 | ||||
| void FolderViewListView::startDrag(Qt::DropActions supportedActions) { | ||||
|   if(movement() != Static) | ||||
|     QListView::startDrag(supportedActions); | ||||
|   else | ||||
|     QAbstractItemView::startDrag(supportedActions); | ||||
| } | ||||
| 
 | ||||
| void FolderViewListView::mousePressEvent(QMouseEvent* event) { | ||||
|   QListView::mousePressEvent(event); | ||||
|   static_cast<FolderView*>(parent())->childMousePressEvent(event); | ||||
| } | ||||
| 
 | ||||
| QModelIndex FolderViewListView::indexAt(const QPoint& point) const { | ||||
|   QModelIndex index = QListView::indexAt(point); | ||||
|   // NOTE: QListView has a severe design flaw here. It does hit-testing based on the 
 | ||||
|   // total bound rect of the item. The width of an item is determined by max(icon_width, text_width).
 | ||||
|   // So if the text label is much wider than the icon, when you click outside the icon but
 | ||||
|   // the point is still within the outer bound rect, the item is still selected.
 | ||||
|   // This results in very poor usability. Let's do precise hit-testing here.
 | ||||
|   // An item is hit only when the point is in the icon or text label.
 | ||||
|   // If the point is in the bound rectangle but outside the icon or text, it should not be selected.
 | ||||
|   if(viewMode() == QListView::IconMode && index.isValid()) { | ||||
|     // FIXME: this hack only improves the usability partially. We still need more precise sizeHint handling.
 | ||||
|     // FolderItemDelegate* delegate = static_cast<FolderItemDelegate*>(itemDelegateForColumn(FolderModel::ColumnFileName));
 | ||||
|     // Q_ASSERT(delegate != NULL);
 | ||||
|     // We use the grid size - (2, 2) as the size of the bounding rectangle of the whole item.
 | ||||
|     // The width of the text label hence is gridSize.width - 2, and the width and height of the icon is from iconSize().
 | ||||
|     QRect visRect = visualRect(index); // visibal area on the screen
 | ||||
|     QSize itemSize = gridSize(); | ||||
|     itemSize.setWidth(itemSize.width() - 2); | ||||
|     itemSize.setHeight(itemSize.height() - 2); | ||||
|     QSize _iconSize = iconSize(); | ||||
|     int textHeight = itemSize.height() - _iconSize.height(); | ||||
|     if(point.y() < visRect.bottom() - textHeight) { | ||||
|       // the point is in the icon area, not over the text label
 | ||||
|       int iconXMargin = (itemSize.width() - _iconSize.width()) / 2; | ||||
|       if(point.x() < (visRect.left() + iconXMargin) || point.x() > (visRect.right() - iconXMargin)) | ||||
| 	return QModelIndex(); | ||||
|     } | ||||
|     // qDebug() << "visualRect: " << visRect << "point:" << point;
 | ||||
|   } | ||||
|   return index; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| // NOTE:
 | ||||
| // QListView has a problem which I consider a bug or a design flaw.
 | ||||
| // When you set movement property to Static, theoratically the icons
 | ||||
| // should not be movable. However, if you turned on icon mode,
 | ||||
| // the icons becomes freely movable despite the value of movement is Static.
 | ||||
| // To overcome this bug, we override all drag handling methods, and
 | ||||
| // call QAbstractItemView directly, bypassing QListView.
 | ||||
| // In this way, we can workaround the buggy behavior.
 | ||||
| // The drag handlers of QListView basically does the same things
 | ||||
| // as its parent QAbstractItemView, but it also stores the currently
 | ||||
| // dragged item and paint them in the view as needed.
 | ||||
| // TODO: I really should file a bug report to Qt developers.
 | ||||
| 
 | ||||
| void FolderViewListView::dragEnterEvent(QDragEnterEvent* event) { | ||||
|   if(movement() != Static) | ||||
|     QListView::dragEnterEvent(event); | ||||
|   else | ||||
|     QAbstractItemView::dragEnterEvent(event); | ||||
|   qDebug("dragEnterEvent"); | ||||
|   //static_cast<FolderView*>(parent())->childDragEnterEvent(event);
 | ||||
| } | ||||
| 
 | ||||
| void FolderViewListView::dragLeaveEvent(QDragLeaveEvent* e) { | ||||
|   if(movement() != Static) | ||||
|     QListView::dragLeaveEvent(e); | ||||
|   else | ||||
|     QAbstractItemView::dragLeaveEvent(e); | ||||
|   static_cast<FolderView*>(parent())->childDragLeaveEvent(e); | ||||
| } | ||||
| 
 | ||||
| void FolderViewListView::dragMoveEvent(QDragMoveEvent* e) { | ||||
|   if(movement() != Static) | ||||
|     QListView::dragMoveEvent(e); | ||||
|   else | ||||
|     QAbstractItemView::dragMoveEvent(e); | ||||
|   static_cast<FolderView*>(parent())->childDragMoveEvent(e); | ||||
| } | ||||
| 
 | ||||
| void FolderViewListView::dropEvent(QDropEvent* e) { | ||||
| 
 | ||||
|   static_cast<FolderView*>(parent())->childDropEvent(e); | ||||
| 
 | ||||
|   if(movement() != Static) | ||||
|     QListView::dropEvent(e); | ||||
|   else | ||||
|     QAbstractItemView::dropEvent(e); | ||||
| } | ||||
| 
 | ||||
| void FolderViewListView::mouseReleaseEvent(QMouseEvent* event) { | ||||
|   bool activationWasAllowed = activationAllowed_; | ||||
|   if ((!style()->styleHint(QStyle::SH_ItemView_ActivateItemOnSingleClick, NULL, this)) || (event->button() != Qt::LeftButton)) { | ||||
|     activationAllowed_ = false; | ||||
|   } | ||||
| 
 | ||||
|   QListView::mouseReleaseEvent(event); | ||||
| 
 | ||||
|   activationAllowed_ = activationWasAllowed; | ||||
| } | ||||
| 
 | ||||
| void FolderViewListView::mouseDoubleClickEvent(QMouseEvent* event) { | ||||
|   bool activationWasAllowed = activationAllowed_; | ||||
|   if ((style()->styleHint(QStyle::SH_ItemView_ActivateItemOnSingleClick, NULL, this)) || (event->button() != Qt::LeftButton)) { | ||||
|     activationAllowed_ = false; | ||||
|   } | ||||
| 
 | ||||
|   QListView::mouseDoubleClickEvent(event); | ||||
| 
 | ||||
|   activationAllowed_ = activationWasAllowed; | ||||
| } | ||||
| 
 | ||||
| void FolderViewListView::activation(const QModelIndex &index) { | ||||
|   if (activationAllowed_) { | ||||
|     Q_EMIT activatedFiltered(index); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| //-----------------------------------------------------------------------------
 | ||||
| 
 | ||||
| FolderViewTreeView::FolderViewTreeView(QWidget* parent): | ||||
|   QTreeView(parent), | ||||
|   layoutTimer_(NULL), | ||||
|   doingLayout_(false), | ||||
|   activationAllowed_(true) { | ||||
| 
 | ||||
|   header()->setStretchLastSection(false); | ||||
|   setIndentation(0); | ||||
| 
 | ||||
|   connect(this, &QTreeView::activated, this, &FolderViewTreeView::activation); | ||||
| } | ||||
| 
 | ||||
| FolderViewTreeView::~FolderViewTreeView() { | ||||
|   if(layoutTimer_) | ||||
|     delete layoutTimer_; | ||||
| } | ||||
| 
 | ||||
| void FolderViewTreeView::setModel(QAbstractItemModel* model) { | ||||
|   QTreeView::setModel(model); | ||||
|   layoutColumns(); | ||||
| } | ||||
| 
 | ||||
| void FolderViewTreeView::mousePressEvent(QMouseEvent* event) { | ||||
|   QTreeView::mousePressEvent(event); | ||||
|   static_cast<FolderView*>(parent())->childMousePressEvent(event); | ||||
| } | ||||
| 
 | ||||
| void FolderViewTreeView::dragEnterEvent(QDragEnterEvent* event) { | ||||
|   QTreeView::dragEnterEvent(event); | ||||
|   static_cast<FolderView*>(parent())->childDragEnterEvent(event); | ||||
| } | ||||
| 
 | ||||
| void FolderViewTreeView::dragLeaveEvent(QDragLeaveEvent* e) { | ||||
|   QTreeView::dragLeaveEvent(e); | ||||
|   static_cast<FolderView*>(parent())->childDragLeaveEvent(e); | ||||
| } | ||||
| 
 | ||||
| void FolderViewTreeView::dragMoveEvent(QDragMoveEvent* e) { | ||||
|   QTreeView::dragMoveEvent(e); | ||||
|   static_cast<FolderView*>(parent())->childDragMoveEvent(e); | ||||
| } | ||||
| 
 | ||||
| void FolderViewTreeView::dropEvent(QDropEvent* e) { | ||||
|   static_cast<FolderView*>(parent())->childDropEvent(e); | ||||
|   QTreeView::dropEvent(e); | ||||
| } | ||||
| 
 | ||||
| // the default list mode of QListView handles column widths
 | ||||
| // very badly (worse than gtk+) and it's not very flexible.
 | ||||
| // so, let's handle column widths outselves.
 | ||||
| void FolderViewTreeView::layoutColumns() { | ||||
|   // qDebug("layoutColumns");
 | ||||
|   if(!model()) | ||||
|     return; | ||||
|   doingLayout_ = true; | ||||
|   QHeaderView* headerView = header(); | ||||
|   // the width that's available for showing the columns.
 | ||||
|   int availWidth = viewport()->contentsRect().width(); | ||||
|   int desiredWidth = 0; | ||||
| 
 | ||||
|   // get the width that every column want
 | ||||
|   int numCols = headerView->count(); | ||||
|   int* widths = new int[numCols]; // array to store the widths every column needs
 | ||||
|   int column; | ||||
|   for(column = 0; column < numCols; ++column) { | ||||
|     int columnId = headerView->logicalIndex(column); | ||||
|     // get the size that the column needs
 | ||||
|     widths[column] = sizeHintForColumn(columnId); | ||||
|   } | ||||
| 
 | ||||
|   // the best case is every column can get its full width
 | ||||
|   for(column = 0; column < numCols; ++column) { | ||||
|     desiredWidth += widths[column]; | ||||
|   } | ||||
| 
 | ||||
|   // if the total witdh we want exceeds the available space
 | ||||
|   if(desiredWidth > availWidth) { | ||||
|     // we don't have that much space for every column
 | ||||
|     int filenameColumn = headerView->visualIndex(FolderModel::ColumnFileName); | ||||
|     // shrink the filename column first
 | ||||
|     desiredWidth -= widths[filenameColumn]; // total width of all other columns
 | ||||
| 
 | ||||
|     // see if setting the width of the filename column to 200 solve the problem
 | ||||
|     if(desiredWidth + 200 > availWidth) { | ||||
|       // even when we reduce the width of the filename column to 200,
 | ||||
|       // the available space is not enough. So we give up trying.
 | ||||
|       widths[filenameColumn] = 200; | ||||
|       desiredWidth += 200; | ||||
|     } | ||||
|     else { // we still have more space, so the width of filename column can be increased
 | ||||
|       // expand the filename column to fill all available space.
 | ||||
|       widths[filenameColumn] = availWidth - desiredWidth; | ||||
|       desiredWidth = availWidth; | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   // really do the resizing for every column
 | ||||
|   for(int column = 0; column < numCols; ++column) { | ||||
|     headerView->resizeSection(column, widths[column]); | ||||
|   } | ||||
| 
 | ||||
|   delete []widths; | ||||
|   doingLayout_ = false; | ||||
|    | ||||
|   if(layoutTimer_) { | ||||
|     delete layoutTimer_; | ||||
|     layoutTimer_ = NULL; | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| void FolderViewTreeView::resizeEvent(QResizeEvent* event) { | ||||
|   QAbstractItemView::resizeEvent(event); | ||||
|   if(!doingLayout_) // prevent endless recursion.
 | ||||
|     layoutColumns(); // layoutColumns() also triggers resizeEvent
 | ||||
| } | ||||
| 
 | ||||
| void FolderViewTreeView::rowsInserted(const QModelIndex& parent, int start, int end) { | ||||
|   QTreeView::rowsInserted(parent, start, end); | ||||
|   queueLayoutColumns(); | ||||
| } | ||||
| 
 | ||||
| void FolderViewTreeView::rowsAboutToBeRemoved(const QModelIndex& parent, int start, int end) { | ||||
|   QTreeView::rowsAboutToBeRemoved(parent, start, end); | ||||
|   queueLayoutColumns(); | ||||
| } | ||||
| 
 | ||||
| void FolderViewTreeView::dataChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight) { | ||||
|   QTreeView::dataChanged(topLeft, bottomRight); | ||||
|   // FIXME: this will be very inefficient
 | ||||
|   // queueLayoutColumns();
 | ||||
| } | ||||
| 
 | ||||
| void FolderViewTreeView::queueLayoutColumns() { | ||||
|   // qDebug("queueLayoutColumns");
 | ||||
|   if(!layoutTimer_) { | ||||
|     layoutTimer_ = new QTimer(); | ||||
|     layoutTimer_->setSingleShot(true); | ||||
|     layoutTimer_->setInterval(0); | ||||
|     connect(layoutTimer_, &QTimer::timeout, this, &FolderViewTreeView::layoutColumns); | ||||
|   } | ||||
|   layoutTimer_->start(); | ||||
| } | ||||
| 
 | ||||
| void FolderViewTreeView::mouseReleaseEvent(QMouseEvent* event) { | ||||
|   bool activationWasAllowed = activationAllowed_; | ||||
|   if ((!style()->styleHint(QStyle::SH_ItemView_ActivateItemOnSingleClick, NULL, this)) || (event->button() != Qt::LeftButton)) { | ||||
|     activationAllowed_ = false; | ||||
|   } | ||||
| 
 | ||||
|   QTreeView::mouseReleaseEvent(event); | ||||
| 
 | ||||
|   activationAllowed_ = activationWasAllowed; | ||||
| } | ||||
| 
 | ||||
| void FolderViewTreeView::mouseDoubleClickEvent(QMouseEvent* event) { | ||||
|   bool activationWasAllowed = activationAllowed_; | ||||
|   if ((style()->styleHint(QStyle::SH_ItemView_ActivateItemOnSingleClick, NULL, this)) || (event->button() != Qt::LeftButton)) { | ||||
|     activationAllowed_ = false; | ||||
|   } | ||||
| 
 | ||||
|   QTreeView::mouseDoubleClickEvent(event); | ||||
| 
 | ||||
|   activationAllowed_ = activationWasAllowed; | ||||
| } | ||||
| 
 | ||||
| void FolderViewTreeView::activation(const QModelIndex &index) { | ||||
|   if (activationAllowed_) { | ||||
|     Q_EMIT activatedFiltered(index); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| //-----------------------------------------------------------------------------
 | ||||
| 
 | ||||
| FolderView::FolderView(ViewMode _mode, QWidget* parent): | ||||
|   QWidget(parent), | ||||
|   view(NULL), | ||||
|   mode((ViewMode)0), | ||||
|   autoSelectionDelay_(600), | ||||
|   autoSelectionTimer_(NULL), | ||||
|   selChangedTimer_(NULL), | ||||
|   fileLauncher_(NULL), | ||||
|   model_(NULL) { | ||||
| 
 | ||||
|   iconSize_[IconMode - FirstViewMode] = QSize(48, 48); | ||||
|   iconSize_[CompactMode - FirstViewMode] = QSize(24, 24); | ||||
|   iconSize_[ThumbnailMode - FirstViewMode] = QSize(128, 128); | ||||
|   iconSize_[DetailedListMode - FirstViewMode] = QSize(24, 24); | ||||
| 
 | ||||
|   QVBoxLayout* layout = new QVBoxLayout(); | ||||
|   layout->setMargin(0); | ||||
|   setLayout(layout); | ||||
| 
 | ||||
|   setViewMode(_mode); | ||||
|   setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); | ||||
| 
 | ||||
|   connect(this, &FolderView::clicked, this, &FolderView::onFileClicked); | ||||
| } | ||||
| 
 | ||||
| FolderView::~FolderView() { | ||||
|   // qDebug("delete FolderView");
 | ||||
| } | ||||
| 
 | ||||
| void FolderView::onItemActivated(QModelIndex index) { | ||||
|   if(index.isValid() && index.model()) { | ||||
|     QVariant data = index.model()->data(index, FolderModel::FileInfoRole); | ||||
|     FmFileInfo* info = (FmFileInfo*)data.value<void*>(); | ||||
|     if(info) { | ||||
|       if (!(QApplication::keyboardModifiers() & (Qt::ShiftModifier | Qt::ControlModifier | Qt::AltModifier | Qt::MetaModifier))) { | ||||
|         Q_EMIT clicked(ActivatedClick, info); | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| void FolderView::onSelChangedTimeout() { | ||||
|   selChangedTimer_->deleteLater(); | ||||
|   selChangedTimer_ = NULL; | ||||
| 
 | ||||
|   QItemSelectionModel* selModel = selectionModel(); | ||||
|   int nSel = 0; | ||||
|   if(viewMode() == DetailedListMode) | ||||
|     nSel = selModel->selectedRows().count(); | ||||
|   else { | ||||
|     nSel = selModel->selectedIndexes().count(); | ||||
|   } | ||||
|   // qDebug()<<"selected:" << nSel;
 | ||||
|   Q_EMIT selChanged(nSel); // FIXME: this is inefficient
 | ||||
| } | ||||
| 
 | ||||
| void FolderView::onSelectionChanged(const QItemSelection& selected, const QItemSelection& deselected) { | ||||
|   // It's possible that the selected items change too often and this slot gets called for thousands of times.
 | ||||
|   // For example, when you select thousands of files and delete them, we will get one selectionChanged() event
 | ||||
|   // for every deleted file. So, we use a timer to delay the handling to avoid too frequent updates of the UI.
 | ||||
|   if(!selChangedTimer_) { | ||||
|     selChangedTimer_ = new QTimer(this); | ||||
|     selChangedTimer_->setSingleShot(true); | ||||
|     connect(selChangedTimer_, &QTimer::timeout, this, &FolderView::onSelChangedTimeout); | ||||
|     selChangedTimer_->start(200); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| void FolderView::setViewMode(ViewMode _mode) { | ||||
|   if(_mode == mode) // if it's the same more, ignore
 | ||||
|     return; | ||||
|   // FIXME: retain old selection
 | ||||
| 
 | ||||
|   // since only detailed list mode uses QTreeView, and others 
 | ||||
|   // all use QListView, it's wise to preserve QListView when possible.
 | ||||
|   bool recreateView = false; | ||||
|   if(view && (mode == DetailedListMode || _mode == DetailedListMode)) { | ||||
|     delete view; // FIXME: no virtual dtor?
 | ||||
|     view = NULL; | ||||
|     recreateView = true; | ||||
|   } | ||||
|   mode = _mode; | ||||
|   QSize iconSize = iconSize_[mode - FirstViewMode]; | ||||
| 
 | ||||
|   if(mode == DetailedListMode) { | ||||
|     FolderViewTreeView* treeView = new FolderViewTreeView(this); | ||||
|     connect(treeView, &FolderViewTreeView::activatedFiltered, this, &FolderView::onItemActivated); | ||||
| 
 | ||||
|     view = treeView; | ||||
|     treeView->setItemsExpandable(false); | ||||
|     treeView->setRootIsDecorated(false); | ||||
|     treeView->setAllColumnsShowFocus(false); | ||||
| 
 | ||||
|     // set our own custom delegate
 | ||||
|     FolderItemDelegate* delegate = new FolderItemDelegate(treeView); | ||||
|     treeView->setItemDelegateForColumn(FolderModel::ColumnFileName, delegate); | ||||
|   } | ||||
|   else { | ||||
|     FolderViewListView* listView; | ||||
|     if(view) | ||||
|       listView = static_cast<FolderViewListView*>(view); | ||||
|     else { | ||||
|       listView = new FolderViewListView(this); | ||||
|       connect(listView, &FolderViewListView::activatedFiltered, this, &FolderView::onItemActivated); | ||||
|       view = listView; | ||||
|     } | ||||
|     // set our own custom delegate
 | ||||
|     FolderItemDelegate* delegate = new FolderItemDelegate(listView); | ||||
|     listView->setItemDelegateForColumn(FolderModel::ColumnFileName, delegate); | ||||
|     // FIXME: should we expose the delegate?
 | ||||
|     listView->setMovement(QListView::Static); | ||||
|     listView->setResizeMode(QListView::Adjust); | ||||
|     listView->setWrapping(true); | ||||
|     switch(mode) { | ||||
|       case IconMode: { | ||||
|         listView->setViewMode(QListView::IconMode); | ||||
|         listView->setWordWrap(true); | ||||
|         listView->setFlow(QListView::LeftToRight); | ||||
|         break; | ||||
|       } | ||||
|       case CompactMode: { | ||||
|         listView->setViewMode(QListView::ListMode); | ||||
|         listView->setWordWrap(false); | ||||
|         listView->setFlow(QListView::QListView::TopToBottom); | ||||
|         break; | ||||
|       } | ||||
|       case ThumbnailMode: { | ||||
|         listView->setViewMode(QListView::IconMode); | ||||
|         listView->setWordWrap(true); | ||||
|         listView->setFlow(QListView::LeftToRight); | ||||
|         break; | ||||
|       } | ||||
|       default:; | ||||
|     } | ||||
|     updateGridSize(); | ||||
|   } | ||||
|   if(view) { | ||||
|     // we have to install the event filter on the viewport instead of the view itself.
 | ||||
|     view->viewport()->installEventFilter(this); | ||||
|     // we want the QEvent::HoverMove event for single click + auto-selection support
 | ||||
|     view->viewport()->setAttribute(Qt::WA_Hover, true); | ||||
|     view->setContextMenuPolicy(Qt::NoContextMenu); // defer the context menu handling to parent widgets
 | ||||
|     view->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); | ||||
|     view->setIconSize(iconSize); | ||||
| 
 | ||||
|     view->setSelectionMode(QAbstractItemView::ExtendedSelection); | ||||
|     layout()->addWidget(view); | ||||
| 
 | ||||
|     // enable dnd
 | ||||
|     view->setDragEnabled(true); | ||||
|     view->setAcceptDrops(true); | ||||
|     view->setDragDropMode(QAbstractItemView::DragDrop); | ||||
|     view->setDropIndicatorShown(true); | ||||
| 
 | ||||
|     if(model_) { | ||||
|       // FIXME: preserve selections
 | ||||
|       model_->setThumbnailSize(iconSize.width()); | ||||
|       view->setModel(model_); | ||||
|       if(recreateView) | ||||
|         connect(view->selectionModel(), &QItemSelectionModel::selectionChanged, this, &FolderView::onSelectionChanged); | ||||
|     } | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| // set proper grid size for the QListView based on current view mode, icon size, and font size.
 | ||||
| void FolderView::updateGridSize() { | ||||
|   if(mode == DetailedListMode || !view) | ||||
|     return; | ||||
|   FolderViewListView* listView = static_cast<FolderViewListView*>(view); | ||||
|   QSize icon = iconSize(mode); // size of the icon
 | ||||
|   QFontMetrics fm = fontMetrics(); // size of current font
 | ||||
|   QSize grid; // the final grid size
 | ||||
|   switch(mode) { | ||||
|     case IconMode: | ||||
|     case ThumbnailMode: { | ||||
|       // NOTE by PCMan about finding the optimal text label size:
 | ||||
|       // The average filename length on my root filesystem is roughly 18-20 chars.
 | ||||
|       // So, a reasonable size for the text label is about 10 chars each line since string of this length
 | ||||
|       // can be shown in two lines. If you consider word wrap, then the result is around 10 chars per word.
 | ||||
|       // In average, 10 char per line should be enough to display a "word" in the filename without breaking.
 | ||||
|       // The values can be estimated with this command:
 | ||||
|       // > find / | xargs  basename -a | sed -e s'/[_-]/ /g' | wc -mcw
 | ||||
|       // However, this average only applies to English. For some Asian characters, such as Chinese chars,
 | ||||
|       // each char actually takes doubled space. To be safe, we use 13 chars per line x average char width
 | ||||
|       // to get a nearly optimal width for the text label. As most of the filenames have less than 40 chars
 | ||||
|       // 13 chars x 3 lines should be enough to show the full filenames for most files.
 | ||||
|       int textWidth = fm.averageCharWidth() * 12 + 4; // add 2 px padding for left and right border
 | ||||
|       int textHeight = fm.height() * 3 + 4; // add 2 px padding for top and bottom border
 | ||||
|       grid.setWidth(qMax(icon.width(), textWidth) + 8); // add a margin 4 px for every cell
 | ||||
|       grid.setHeight(icon.height() + textHeight + 8); // add a margin 4 px for every cell
 | ||||
|       break; | ||||
|     } | ||||
|     default: | ||||
|       ; // do not use grid size
 | ||||
|   } | ||||
|   listView->setGridSize(grid); | ||||
|   FolderItemDelegate* delegate = static_cast<FolderItemDelegate*>(listView->itemDelegateForColumn(FolderModel::ColumnFileName)); | ||||
|   delegate->setGridSize(grid); | ||||
| } | ||||
| 
 | ||||
| void FolderView::setIconSize(ViewMode mode, QSize size) { | ||||
|   Q_ASSERT(mode >= FirstViewMode && mode <= LastViewMode); | ||||
|   iconSize_[mode - FirstViewMode] = size; | ||||
|   if(viewMode() == mode) { | ||||
|     view->setIconSize(size); | ||||
|     if(model_) | ||||
|       model_->setThumbnailSize(size.width()); | ||||
|     updateGridSize(); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| QSize FolderView::iconSize(ViewMode mode) const { | ||||
|   Q_ASSERT(mode >= FirstViewMode && mode <= LastViewMode); | ||||
|   return iconSize_[mode - FirstViewMode]; | ||||
| } | ||||
| 
 | ||||
| FolderView::ViewMode FolderView::viewMode() const { | ||||
|   return mode; | ||||
| } | ||||
| 
 | ||||
| void FolderView::setAutoSelectionDelay(int delay) { | ||||
|   autoSelectionDelay_ = delay; | ||||
| } | ||||
| 
 | ||||
| QAbstractItemView* FolderView::childView() const { | ||||
|   return view; | ||||
| } | ||||
| 
 | ||||
| ProxyFolderModel* FolderView::model() const { | ||||
|   return model_; | ||||
| } | ||||
| 
 | ||||
| void FolderView::setModel(ProxyFolderModel* model) { | ||||
|   if(view) { | ||||
|     view->setModel(model); | ||||
|     QSize iconSize = iconSize_[mode - FirstViewMode]; | ||||
|     model->setThumbnailSize(iconSize.width()); | ||||
|     if(view->selectionModel()) | ||||
|       connect(view->selectionModel(), &QItemSelectionModel::selectionChanged, this, &FolderView::onSelectionChanged); | ||||
|   } | ||||
|   if(model_) | ||||
|     delete model_; | ||||
|   model_ = model; | ||||
| } | ||||
| 
 | ||||
| bool FolderView::event(QEvent* event) { | ||||
|   switch(event->type()) { | ||||
|     case QEvent::StyleChange: | ||||
|       break; | ||||
|     case QEvent::FontChange: | ||||
|       updateGridSize(); | ||||
|       break; | ||||
|   }; | ||||
|   return QWidget::event(event); | ||||
| } | ||||
| 
 | ||||
| void FolderView::contextMenuEvent(QContextMenuEvent* event) { | ||||
|   QWidget::contextMenuEvent(event); | ||||
|   QPoint pos = event->pos(); | ||||
|   QPoint view_pos = view->mapFromParent(pos); | ||||
|   QPoint viewport_pos = view->viewport()->mapFromParent(view_pos); | ||||
|   emitClickedAt(ContextMenuClick, viewport_pos); | ||||
| } | ||||
| 
 | ||||
| void FolderView::childMousePressEvent(QMouseEvent* event) { | ||||
|   // called from mousePressEvent() of child view
 | ||||
|   if(event->button() == Qt::MiddleButton) { | ||||
|     emitClickedAt(MiddleClick, event->pos()); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| void FolderView::emitClickedAt(ClickType type, const QPoint& pos) { | ||||
|   // indexAt() needs a point in "viewport" coordinates.
 | ||||
|   QModelIndex index = view->indexAt(pos); | ||||
|   if(index.isValid()) { | ||||
|     QVariant data = index.data(FolderModel::FileInfoRole); | ||||
|     FmFileInfo* info = reinterpret_cast<FmFileInfo*>(data.value<void*>()); | ||||
|     Q_EMIT clicked(type, info); | ||||
|   } | ||||
|   else { | ||||
|     // FIXME: should we show popup menu for the selected files instead
 | ||||
|     // if there are selected files?
 | ||||
|     if(type == ContextMenuClick) { | ||||
|       // clear current selection if clicked outside selected files
 | ||||
|       view->clearSelection(); | ||||
|       Q_EMIT clicked(type, NULL); | ||||
|     } | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| QModelIndexList FolderView::selectedRows(int column) const { | ||||
|   QItemSelectionModel* selModel = selectionModel(); | ||||
|   if(selModel) { | ||||
|     return selModel->selectedRows(column); | ||||
|   } | ||||
|   return QModelIndexList(); | ||||
| } | ||||
| 
 | ||||
| // This returns all selected "cells", which means all cells of the same row are returned.
 | ||||
| QModelIndexList FolderView::selectedIndexes() const { | ||||
|   QItemSelectionModel* selModel = selectionModel(); | ||||
|   if(selModel) { | ||||
|     return selModel->selectedIndexes(); | ||||
|   } | ||||
|   return QModelIndexList(); | ||||
| } | ||||
| 
 | ||||
| QItemSelectionModel* FolderView::selectionModel() const { | ||||
|   return view ? view->selectionModel() : NULL; | ||||
| } | ||||
| 
 | ||||
| FmPathList* FolderView::selectedFilePaths() const { | ||||
|   if(model_) { | ||||
|     QModelIndexList selIndexes = mode == DetailedListMode ? selectedRows() : selectedIndexes(); | ||||
|     if(!selIndexes.isEmpty()) { | ||||
|       FmPathList* paths = fm_path_list_new(); | ||||
|       QModelIndexList::const_iterator it; | ||||
|       for(it = selIndexes.begin(); it != selIndexes.end(); ++it) { | ||||
|         FmFileInfo* file = model_->fileInfoFromIndex(*it); | ||||
|         fm_path_list_push_tail(paths, fm_file_info_get_path(file)); | ||||
|       } | ||||
|       return paths; | ||||
|     } | ||||
|   } | ||||
|   return NULL; | ||||
| } | ||||
| 
 | ||||
| FmFileInfoList* FolderView::selectedFiles() const { | ||||
|   if(model_) { | ||||
|     QModelIndexList selIndexes = mode == DetailedListMode ? selectedRows() : selectedIndexes(); | ||||
|     if(!selIndexes.isEmpty()) { | ||||
|       FmFileInfoList* files = fm_file_info_list_new(); | ||||
|       QModelIndexList::const_iterator it; | ||||
|       for(it = selIndexes.constBegin(); it != selIndexes.constEnd(); it++) { | ||||
|         FmFileInfo* file = model_->fileInfoFromIndex(*it); | ||||
|         fm_file_info_list_push_tail(files, file); | ||||
|       } | ||||
|       return files; | ||||
|     } | ||||
|   } | ||||
|   return NULL; | ||||
| } | ||||
| 
 | ||||
| void FolderView::selectAll() { | ||||
|   if(mode == DetailedListMode) | ||||
|     view->selectAll(); | ||||
|   else { | ||||
|     // NOTE: By default QListView::selectAll() selects all columns in the model.
 | ||||
|     // However, QListView only show the first column. Normal selection by mouse
 | ||||
|     // can only select the first column of every row. I consider this discripancy yet
 | ||||
|     // another design flaw of Qt. To make them consistent, we do it ourselves by only
 | ||||
|     // selecting the first column of every row and do not select all columns as Qt does.
 | ||||
|     // This will trigger one selectionChanged event per row, which is very inefficient,
 | ||||
|     // but we have no other choices to workaround the Qt bug.
 | ||||
|     // I'll report a Qt bug for this later.
 | ||||
|     if(model_) { | ||||
|       int rowCount = model_->rowCount(); | ||||
|       for(int row = 0; row < rowCount; ++row) { | ||||
|         QModelIndex index = model_->index(row, 0); | ||||
|         selectionModel()->select(index, QItemSelectionModel::Select); | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| void FolderView::invertSelection() { | ||||
|   if(model_) { | ||||
|     QItemSelectionModel* selModel = view->selectionModel(); | ||||
|     int rows = model_->rowCount(); | ||||
|     QItemSelectionModel::SelectionFlags flags = QItemSelectionModel::Toggle; | ||||
|     if(mode == DetailedListMode) | ||||
|       flags |= QItemSelectionModel::Rows; | ||||
|     for(int row = 0; row < rows; ++row) { | ||||
|       QModelIndex index = model_->index(row, 0); | ||||
|       selModel->select(index, flags); | ||||
|     } | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| void FolderView::childDragEnterEvent(QDragEnterEvent* event) { | ||||
|   qDebug("drag enter"); | ||||
|   if(event->mimeData()->hasFormat("text/uri-list")) { | ||||
|     event->accept(); | ||||
|   } | ||||
|   else | ||||
|     event->ignore(); | ||||
| } | ||||
| 
 | ||||
| void FolderView::childDragLeaveEvent(QDragLeaveEvent* e) { | ||||
|   qDebug("drag leave"); | ||||
|   e->accept(); | ||||
| } | ||||
| 
 | ||||
| void FolderView::childDragMoveEvent(QDragMoveEvent* e) { | ||||
|   qDebug("drag move"); | ||||
| } | ||||
| 
 | ||||
| void FolderView::childDropEvent(QDropEvent* e) { | ||||
|   qDebug("drop"); | ||||
|   if(e->keyboardModifiers() == Qt::NoModifier) { | ||||
|     // if no key modifiers are used, popup a menu
 | ||||
|     // to ask the user for the action he/she wants to perform.
 | ||||
|     Qt::DropAction action = DndActionMenu::askUser(QCursor::pos()); | ||||
|     e->setDropAction(action); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| bool FolderView::eventFilter(QObject* watched, QEvent* event) { | ||||
|   // NOTE: Instead of simply filtering the drag and drop events of the child view in
 | ||||
|   // the event filter, we overrided each event handler virtual methods in
 | ||||
|   // both QListView and QTreeView and added some childXXXEvent() callbacks.
 | ||||
|   // We did this because of a design flaw of Qt.
 | ||||
|   // All QAbstractScrollArea derived widgets, including QAbstractItemView
 | ||||
|   // contains an internal child widget, which is called a viewport.
 | ||||
|   // The events actually comes from the child viewport, not the parent view itself.
 | ||||
|   // Qt redirects the events of viewport to the viewportEvent() method of
 | ||||
|   // QAbstractScrollArea and let the parent widget handle the events.
 | ||||
|   // Qt implemented this using a event filter installed on the child viewport widget.
 | ||||
|   // That means, when we try to install an event filter on the viewport,
 | ||||
|   // there is already a filter installed by Qt which will be called before ours.
 | ||||
|   // So we can never intercept the event handling of QAbstractItemView by using a filter.
 | ||||
|   // That's why we override respective virtual methods for different events.
 | ||||
|   if(view && watched == view->viewport()) { | ||||
|     switch(event->type()) { | ||||
|     case QEvent::HoverMove: | ||||
|       // activate items on single click
 | ||||
|       if(style()->styleHint(QStyle::SH_ItemView_ActivateItemOnSingleClick)) { | ||||
|         QHoverEvent* hoverEvent = static_cast<QHoverEvent*>(event); | ||||
|         QModelIndex index = view->indexAt(hoverEvent->pos()); // find out the hovered item
 | ||||
|         if(index.isValid()) { // change the cursor to a hand when hovering on an item
 | ||||
|           setCursor(Qt::PointingHandCursor); | ||||
|           if(!selectionModel()->hasSelection()) | ||||
|             selectionModel()->setCurrentIndex(index, QItemSelectionModel::Current); | ||||
|         } | ||||
|         else | ||||
|           setCursor(Qt::ArrowCursor); | ||||
|         // turn on auto-selection for hovered item when single click mode is used.
 | ||||
|         if(autoSelectionDelay_ > 0 && model_) { | ||||
|           if(!autoSelectionTimer_) { | ||||
|             autoSelectionTimer_ = new QTimer(this); | ||||
|             connect(autoSelectionTimer_, &QTimer::timeout, this, &FolderView::onAutoSelectionTimeout); | ||||
|             lastAutoSelectionIndex_ = QModelIndex(); | ||||
|           } | ||||
|           autoSelectionTimer_->start(autoSelectionDelay_); | ||||
|         } | ||||
|         break; | ||||
|       } | ||||
|     case QEvent::HoverLeave: | ||||
|       if(style()->styleHint(QStyle::SH_ItemView_ActivateItemOnSingleClick)) | ||||
|         setCursor(Qt::ArrowCursor); | ||||
|       break; | ||||
|     case QEvent::Wheel: | ||||
|       // This is to fix #85: Scrolling doesn't work in compact view
 | ||||
|       // Actually, I think it's the bug of Qt, not ours.
 | ||||
|       // When in compact mode, only the horizontal scroll bar is used and the vertical one is hidden.
 | ||||
|       // So, when a user scroll his mouse wheel, it's reasonable to scroll the horizontal scollbar.
 | ||||
|       // Qt does not implement such a simple feature, unfortunately.
 | ||||
|       // We do it by forwarding the scroll event in the viewport to the horizontal scrollbar.
 | ||||
|       // FIXME: if someday Qt supports this, we have to disable the workaround.
 | ||||
|       if(mode == CompactMode) { | ||||
|         QScrollBar* scroll = view->horizontalScrollBar(); | ||||
|         if(scroll) { | ||||
|           QApplication::sendEvent(scroll, event); | ||||
|           return true; | ||||
|         } | ||||
|       } | ||||
|       break; | ||||
|     } | ||||
|   } | ||||
|   return QObject::eventFilter(watched, event); | ||||
| } | ||||
| 
 | ||||
| // this slot handles auto-selection of items.
 | ||||
| void FolderView::onAutoSelectionTimeout() { | ||||
|   if(QApplication::mouseButtons() != Qt::NoButton) | ||||
|     return; | ||||
| 
 | ||||
|   Qt::KeyboardModifiers mods = QApplication::keyboardModifiers(); | ||||
|   QPoint pos = view->viewport()->mapFromGlobal(QCursor::pos()); // convert to viewport coordinates
 | ||||
|   QModelIndex index = view->indexAt(pos); // find out the hovered item
 | ||||
|   QItemSelectionModel::SelectionFlags flags = (mode == DetailedListMode ? QItemSelectionModel::Rows : QItemSelectionModel::NoUpdate); | ||||
|   QItemSelectionModel* selModel = view->selectionModel(); | ||||
| 
 | ||||
|   if(mods & Qt::ControlModifier) { // Ctrl key is pressed
 | ||||
|     if(selModel->isSelected(index) && index != lastAutoSelectionIndex_) { | ||||
|       // unselect a previously selected item
 | ||||
|       selModel->select(index, flags|QItemSelectionModel::Deselect); | ||||
|       lastAutoSelectionIndex_ = QModelIndex(); | ||||
|     } | ||||
|     else { | ||||
|       // select an unselected item
 | ||||
|       selModel->select(index, flags|QItemSelectionModel::Select); | ||||
|       lastAutoSelectionIndex_ = index; | ||||
|     } | ||||
|     selModel->setCurrentIndex(index, QItemSelectionModel::NoUpdate); // move the cursor
 | ||||
|   } | ||||
|   else if(mods & Qt::ShiftModifier) { // Shift key is pressed
 | ||||
|     // select all items between current index and the hovered index.
 | ||||
|     QModelIndex current = selModel->currentIndex(); | ||||
|     if(selModel->hasSelection() && current.isValid()) { | ||||
|       selModel->clear(); // clear old selection
 | ||||
|       selModel->setCurrentIndex(current, QItemSelectionModel::NoUpdate); | ||||
|       int begin = current.row(); | ||||
|       int end = index.row(); | ||||
|       if(begin > end) | ||||
|         qSwap(begin, end); | ||||
|       for(int row = begin; row <= end; ++row) { | ||||
|         QModelIndex sel = model_->index(row, 0); | ||||
|         selModel->select(sel, flags|QItemSelectionModel::Select); | ||||
|       } | ||||
|     } | ||||
|     else { // no items are selected, select the hovered item.
 | ||||
|       if(index.isValid()) { | ||||
|         selModel->select(index, flags|QItemSelectionModel::SelectCurrent); | ||||
|         selModel->setCurrentIndex(index, QItemSelectionModel::NoUpdate); | ||||
|       } | ||||
|     } | ||||
|     lastAutoSelectionIndex_ = index; | ||||
|   } | ||||
|   else if(mods == Qt::NoModifier) { // no modifier keys are pressed.
 | ||||
|     if(index.isValid()) { | ||||
|       // select the hovered item
 | ||||
|       view->clearSelection(); | ||||
|       selModel->select(index, flags|QItemSelectionModel::SelectCurrent); | ||||
|       selModel->setCurrentIndex(index, QItemSelectionModel::NoUpdate); | ||||
|     } | ||||
|     lastAutoSelectionIndex_ = index; | ||||
|   } | ||||
| 
 | ||||
|   autoSelectionTimer_->deleteLater(); | ||||
|   autoSelectionTimer_ = NULL; | ||||
| } | ||||
| 
 | ||||
| void FolderView::onFileClicked(int type, FmFileInfo* fileInfo) { | ||||
|   if(type == ActivatedClick) { | ||||
|     if(fileLauncher_) { | ||||
|       GList* files = g_list_append(NULL, fileInfo); | ||||
|       fileLauncher_->launchFiles(NULL, files); | ||||
|       g_list_free(files); | ||||
|     } | ||||
|   } | ||||
|   else if(type == ContextMenuClick) { | ||||
|     FmPath* folderPath = path(); | ||||
|     QMenu* menu = NULL; | ||||
|     if(fileInfo) { | ||||
|       // show context menu
 | ||||
|       if (FmFileInfoList* files = selectedFiles()) { | ||||
|         Fm::FileMenu* fileMenu = new Fm::FileMenu(files, fileInfo, folderPath); | ||||
|         fileMenu->setFileLauncher(fileLauncher_); | ||||
|         prepareFileMenu(fileMenu); | ||||
|         fm_file_info_list_unref(files); | ||||
|         menu = fileMenu; | ||||
|       } | ||||
|     } | ||||
|     else { | ||||
|       FmFolder* _folder = folder(); | ||||
|       FmFileInfo* info = fm_folder_get_info(_folder); | ||||
|       Fm::FolderMenu* folderMenu = new Fm::FolderMenu(this); | ||||
|       prepareFolderMenu(folderMenu); | ||||
|       menu = folderMenu; | ||||
|     } | ||||
|     if (menu) { | ||||
|       menu->popup(QCursor::pos()); | ||||
|       connect(menu, &QMenu::aboutToHide, menu, &QMenu::deleteLater); | ||||
|     } | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| void FolderView::prepareFileMenu(FileMenu* menu) { | ||||
| } | ||||
| 
 | ||||
| void FolderView::prepareFolderMenu(FolderMenu* menu) { | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| } // namespace Fm
 | ||||
							
								
								
									
										167
									
								
								libfm-qt/folderview.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										167
									
								
								libfm-qt/folderview.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,167 @@ | ||||
| /*
 | ||||
|     <one line to give the library's name and an idea of what it does.> | ||||
|     Copyright (C) 2012  Hong Jen Yee (PCMan) <pcman.tw@gmail.com> | ||||
| 
 | ||||
|     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 | ||||
| */ | ||||
| 
 | ||||
| 
 | ||||
| #ifndef FM_FOLDERVIEW_H | ||||
| #define FM_FOLDERVIEW_H | ||||
| 
 | ||||
| #include "libfmqtglobals.h" | ||||
| #include <QWidget> | ||||
| #include <QListView> | ||||
| #include <QTreeView> | ||||
| #include <QMouseEvent> | ||||
| #include <libfm/fm.h> | ||||
| #include "foldermodel.h" | ||||
| #include "proxyfoldermodel.h" | ||||
| 
 | ||||
| class QTimer; | ||||
| 
 | ||||
| namespace Fm { | ||||
| 
 | ||||
| class FileMenu; | ||||
| class FolderMenu; | ||||
| class FileLauncher; | ||||
| class FolderViewStyle; | ||||
| 
 | ||||
| class LIBFM_QT_API FolderView : public QWidget { | ||||
|   Q_OBJECT | ||||
| 
 | ||||
| public: | ||||
|   enum ViewMode { | ||||
|     FirstViewMode = 1, | ||||
|     IconMode = FirstViewMode, | ||||
|     CompactMode, | ||||
|     DetailedListMode, | ||||
|     ThumbnailMode, | ||||
|     LastViewMode = ThumbnailMode, | ||||
|     NumViewModes = (LastViewMode - FirstViewMode + 1) | ||||
|   }; | ||||
| 
 | ||||
|   enum ClickType { | ||||
|     ActivatedClick, | ||||
|     MiddleClick, | ||||
|     ContextMenuClick | ||||
|   }; | ||||
| 
 | ||||
| public: | ||||
| 
 | ||||
|   friend class FolderViewTreeView; | ||||
|   friend class FolderViewListView; | ||||
|    | ||||
|   explicit FolderView(ViewMode _mode = IconMode, QWidget* parent = 0); | ||||
|   virtual ~FolderView(); | ||||
| 
 | ||||
|   void setViewMode(ViewMode _mode); | ||||
|   ViewMode viewMode() const; | ||||
|    | ||||
|   void setIconSize(ViewMode mode, QSize size); | ||||
|   QSize iconSize(ViewMode mode) const; | ||||
| 
 | ||||
|   QAbstractItemView* childView() const; | ||||
| 
 | ||||
|   ProxyFolderModel* model() const; | ||||
|   void setModel(ProxyFolderModel* _model); | ||||
|    | ||||
|   FmFolder* folder() { | ||||
|     return model_ ? static_cast<FolderModel*>(model_->sourceModel())->folder() : NULL; | ||||
|   } | ||||
| 
 | ||||
|   FmFileInfo* folderInfo() { | ||||
|     FmFolder* _folder = folder(); | ||||
|     return _folder ? fm_folder_get_info(_folder) : NULL; | ||||
|   } | ||||
| 
 | ||||
|   FmPath* path() { | ||||
|     FmFolder* _folder = folder(); | ||||
|     return _folder ? fm_folder_get_path(_folder) : NULL; | ||||
|   } | ||||
| 
 | ||||
|   QItemSelectionModel* selectionModel() const; | ||||
|   FmFileInfoList* selectedFiles() const; | ||||
|   FmPathList* selectedFilePaths() const; | ||||
| 
 | ||||
|   void selectAll(); | ||||
| 
 | ||||
|   void invertSelection(); | ||||
| 
 | ||||
|   void setFileLauncher(FileLauncher* launcher) { | ||||
|     fileLauncher_ = launcher; | ||||
|   } | ||||
| 
 | ||||
|   FileLauncher* fileLauncher() { | ||||
|     return fileLauncher_; | ||||
|   } | ||||
| 
 | ||||
|   int autoSelectionDelay() const { | ||||
|     return autoSelectionDelay_; | ||||
|   } | ||||
| 
 | ||||
|   void setAutoSelectionDelay(int delay); | ||||
| 
 | ||||
| protected: | ||||
|   virtual bool event(QEvent* event); | ||||
|   virtual void contextMenuEvent(QContextMenuEvent* event); | ||||
|   virtual void childMousePressEvent(QMouseEvent* event); | ||||
|   virtual void childDragEnterEvent(QDragEnterEvent* event); | ||||
|   virtual void childDragMoveEvent(QDragMoveEvent* e); | ||||
|   virtual void childDragLeaveEvent(QDragLeaveEvent* e); | ||||
|   virtual void childDropEvent(QDropEvent* e); | ||||
|    | ||||
|   void emitClickedAt(ClickType type, const QPoint& pos); | ||||
| 
 | ||||
|   QModelIndexList selectedRows ( int column = 0 ) const; | ||||
|   QModelIndexList selectedIndexes() const; | ||||
| 
 | ||||
|   virtual void prepareFileMenu(Fm::FileMenu* menu); | ||||
|   virtual void prepareFolderMenu(Fm::FolderMenu* menu); | ||||
|    | ||||
|   virtual bool eventFilter(QObject* watched, QEvent* event); | ||||
| 
 | ||||
|   void updateGridSize(); // called when view mode, icon size, or font size is changed
 | ||||
| 
 | ||||
| public Q_SLOTS: | ||||
|   void onItemActivated(QModelIndex index); | ||||
|   void onSelectionChanged(const QItemSelection & selected, const QItemSelection & deselected); | ||||
|   virtual void onFileClicked(int type, FmFileInfo* fileInfo); | ||||
| 
 | ||||
| private Q_SLOTS: | ||||
|   void onAutoSelectionTimeout(); | ||||
|   void onSelChangedTimeout(); | ||||
| 
 | ||||
| Q_SIGNALS: | ||||
|   void clicked(int type, FmFileInfo* file); | ||||
|   void selChanged(int n_sel); | ||||
|   void sortChanged(); | ||||
| 
 | ||||
| private: | ||||
| 
 | ||||
|   QAbstractItemView* view; | ||||
|   ProxyFolderModel* model_; | ||||
|   ViewMode mode; | ||||
|   QSize iconSize_[NumViewModes]; | ||||
|   FileLauncher* fileLauncher_; | ||||
|   int autoSelectionDelay_; | ||||
|   QTimer* autoSelectionTimer_; | ||||
|   QModelIndex lastAutoSelectionIndex_; | ||||
|   QTimer* selChangedTimer_; | ||||
| }; | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| #endif // FM_FOLDERVIEW_H
 | ||||
							
								
								
									
										107
									
								
								libfm-qt/folderview_p.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										107
									
								
								libfm-qt/folderview_p.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,107 @@ | ||||
| /*
 | ||||
|     <one line to give the library's name and an idea of what it does.> | ||||
|     Copyright (C) 2012  Hong Jen Yee (PCMan) <pcman.tw@gmail.com> | ||||
| 
 | ||||
|     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 | ||||
| */ | ||||
| 
 | ||||
| 
 | ||||
| #ifndef FM_FOLDERVIEW_P_H | ||||
| #define FM_FOLDERVIEW_P_H | ||||
| 
 | ||||
| #include <QListView> | ||||
| #include <QTreeView> | ||||
| #include <QMouseEvent> | ||||
| #include "folderview.h" | ||||
| 
 | ||||
| class QTimer; | ||||
| 
 | ||||
| namespace Fm { | ||||
| 
 | ||||
| // override these classes for implementing FolderView
 | ||||
| class FolderViewListView : public QListView { | ||||
|   Q_OBJECT | ||||
| public: | ||||
|   friend class FolderView; | ||||
|   FolderViewListView(QWidget* parent = 0); | ||||
|   virtual ~FolderViewListView(); | ||||
|   virtual void startDrag(Qt::DropActions supportedActions); | ||||
|   virtual void mousePressEvent(QMouseEvent* event); | ||||
|   virtual void mouseReleaseEvent(QMouseEvent* event); | ||||
|   virtual void mouseDoubleClickEvent(QMouseEvent* event); | ||||
|   virtual void dragEnterEvent(QDragEnterEvent* event); | ||||
|   virtual void dragMoveEvent(QDragMoveEvent* e); | ||||
|   virtual void dragLeaveEvent(QDragLeaveEvent* e); | ||||
|   virtual void dropEvent(QDropEvent* e); | ||||
|    | ||||
|   virtual QModelIndex indexAt(const QPoint & point) const; | ||||
| 
 | ||||
|   inline void setPositionForIndex(const QPoint & position, const QModelIndex & index) { | ||||
|     QListView::setPositionForIndex(position, index); | ||||
|   } | ||||
| 
 | ||||
|   inline QRect rectForIndex(const QModelIndex & index) const { | ||||
|     return QListView::rectForIndex(index); | ||||
|   } | ||||
| 
 | ||||
| Q_SIGNALS: | ||||
|   void activatedFiltered(const QModelIndex &index); | ||||
| 
 | ||||
| private Q_SLOTS: | ||||
|   void activation(const QModelIndex &index); | ||||
| 
 | ||||
| private: | ||||
|   bool activationAllowed_; | ||||
| }; | ||||
| 
 | ||||
| class FolderViewTreeView : public QTreeView { | ||||
|   Q_OBJECT | ||||
| public: | ||||
|   friend class FolderView; | ||||
|   FolderViewTreeView(QWidget* parent = 0); | ||||
|   virtual ~FolderViewTreeView(); | ||||
|   virtual void setModel(QAbstractItemModel* model); | ||||
|   virtual void mousePressEvent(QMouseEvent* event); | ||||
|   virtual void mouseReleaseEvent(QMouseEvent* event); | ||||
|   virtual void mouseDoubleClickEvent(QMouseEvent* event); | ||||
|   virtual void dragEnterEvent(QDragEnterEvent* event); | ||||
|   virtual void dragMoveEvent(QDragMoveEvent* e); | ||||
|   virtual void dragLeaveEvent(QDragLeaveEvent* e); | ||||
|   virtual void dropEvent(QDropEvent* e); | ||||
|    | ||||
|   virtual void rowsInserted(const QModelIndex& parent,int start, int end); | ||||
|   virtual void rowsAboutToBeRemoved(const QModelIndex& parent,int start, int end); | ||||
|   virtual void dataChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight); | ||||
| 
 | ||||
|   virtual void resizeEvent(QResizeEvent* event); | ||||
|   void queueLayoutColumns(); | ||||
| 
 | ||||
| Q_SIGNALS: | ||||
|   void activatedFiltered(const QModelIndex &index); | ||||
| 
 | ||||
| private Q_SLOTS: | ||||
|   void layoutColumns(); | ||||
|   void activation(const QModelIndex &index); | ||||
| 
 | ||||
| private: | ||||
|   bool doingLayout_; | ||||
|   QTimer* layoutTimer_; | ||||
|   bool activationAllowed_; | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
| } // namespace Fm
 | ||||
| 
 | ||||
| #endif // FM_FOLDERVIEW_P_H
 | ||||
							
								
								
									
										55
									
								
								libfm-qt/fontbutton.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										55
									
								
								libfm-qt/fontbutton.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,55 @@ | ||||
| /*
 | ||||
| 
 | ||||
|     Copyright (C) 2013  Hong Jen Yee (PCMan) <pcman.tw@gmail.com> | ||||
| 
 | ||||
|     This program is free software; you can redistribute it and/or modify | ||||
|     it under the terms of the GNU General Public License as published by | ||||
|     the Free Software Foundation; either version 2 of the License, or | ||||
|     (at your option) any later version. | ||||
| 
 | ||||
|     This program is distributed in the hope that it will be useful, | ||||
|     but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|     GNU General Public License for more details. | ||||
| 
 | ||||
|     You should have received a copy of the GNU General Public License along | ||||
|     with this program; if not, write to the Free Software Foundation, Inc., | ||||
|     51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||||
| */ | ||||
| 
 | ||||
| 
 | ||||
| #include "fontbutton.h" | ||||
| #include <QFontDialog> | ||||
| #include <X11/X.h> | ||||
| 
 | ||||
| using namespace Fm; | ||||
| 
 | ||||
| FontButton::FontButton(QWidget* parent): QPushButton(parent) { | ||||
|   connect(this, &QPushButton::clicked, this, &FontButton::onClicked); | ||||
| } | ||||
| 
 | ||||
| FontButton::~FontButton() { | ||||
| } | ||||
| 
 | ||||
| void FontButton::onClicked() { | ||||
|   QFontDialog dlg(font_); | ||||
|   if(dlg.exec() == QDialog::Accepted) { | ||||
|     setFont(dlg.selectedFont()); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| void FontButton::setFont(QFont font) { | ||||
|   font_ = font; | ||||
|   QString text = font.family(); | ||||
|   if(font.bold()) { | ||||
|     text += " "; | ||||
|     text += tr("Bold"); | ||||
|   } | ||||
|   if(font.italic()) { | ||||
|     text += " "; | ||||
|     text += tr("Italic"); | ||||
|   } | ||||
|   text += QString(" %1").arg(font.pointSize()); | ||||
|   setText(text); | ||||
|   Q_EMIT changed(); | ||||
| } | ||||
							
								
								
									
										54
									
								
								libfm-qt/fontbutton.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										54
									
								
								libfm-qt/fontbutton.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,54 @@ | ||||
| /*
 | ||||
| 
 | ||||
|     Copyright (C) 2013  Hong Jen Yee (PCMan) <pcman.tw@gmail.com> | ||||
| 
 | ||||
|     This program is free software; you can redistribute it and/or modify | ||||
|     it under the terms of the GNU General Public License as published by | ||||
|     the Free Software Foundation; either version 2 of the License, or | ||||
|     (at your option) any later version. | ||||
| 
 | ||||
|     This program is distributed in the hope that it will be useful, | ||||
|     but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|     GNU General Public License for more details. | ||||
| 
 | ||||
|     You should have received a copy of the GNU General Public License along | ||||
|     with this program; if not, write to the Free Software Foundation, Inc., | ||||
|     51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||||
| */ | ||||
| 
 | ||||
| 
 | ||||
| #ifndef FM_FONTBUTTON_H | ||||
| #define FM_FONTBUTTON_H | ||||
| 
 | ||||
| #include "libfmqtglobals.h" | ||||
| #include <QPushButton> | ||||
| 
 | ||||
| 
 | ||||
| namespace Fm { | ||||
| 
 | ||||
| class LIBFM_QT_API FontButton : public QPushButton { | ||||
| Q_OBJECT | ||||
| public: | ||||
|   explicit FontButton(QWidget* parent = 0); | ||||
|   virtual ~FontButton(); | ||||
| 
 | ||||
|   QFont font() { | ||||
|     return font_; | ||||
|   } | ||||
|    | ||||
|   void setFont(QFont font); | ||||
|    | ||||
| Q_SIGNALS: | ||||
|   void changed(); | ||||
|    | ||||
| private Q_SLOTS: | ||||
|   void onClicked(); | ||||
| 
 | ||||
| private: | ||||
|   QFont font_; | ||||
| }; | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| #endif // FM_FONTBUTTON_H
 | ||||
							
								
								
									
										143
									
								
								libfm-qt/icontheme.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										143
									
								
								libfm-qt/icontheme.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,143 @@ | ||||
| /*
 | ||||
|     <one line to give the library's name and an idea of what it does.> | ||||
|     Copyright (C) 2012  Hong Jen Yee (PCMan) <pcman.tw@gmail.com> | ||||
| 
 | ||||
|     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 | ||||
| */ | ||||
| 
 | ||||
| 
 | ||||
| #include "icontheme.h" | ||||
| #include <libfm/fm.h> | ||||
| #include <QList> | ||||
| #include <QIcon> | ||||
| #include <QtGlobal> | ||||
| #include <QApplication> | ||||
| #include <QDesktopWidget> | ||||
| 
 | ||||
| using namespace Fm; | ||||
| 
 | ||||
| static IconTheme* theIconTheme = NULL; // the global single instance of IconTheme.
 | ||||
| static const char* fallbackNames[] = {"unknown", "application-octet-stream", NULL}; | ||||
| 
 | ||||
| static void fmIconDataDestroy(gpointer data) { | ||||
|   QIcon* picon = reinterpret_cast<QIcon*>(data); | ||||
|   delete picon; | ||||
| } | ||||
| 
 | ||||
| IconTheme::IconTheme(): | ||||
|   currentThemeName_(QIcon::themeName()) { | ||||
|   // NOTE: only one instance is allowed
 | ||||
|   Q_ASSERT(theIconTheme == NULL); | ||||
|   Q_ASSERT(qApp != NULL); // QApplication should exists before contructing IconTheme.
 | ||||
| 
 | ||||
|   theIconTheme = this; | ||||
|   fm_icon_set_user_data_destroy(reinterpret_cast<GDestroyNotify>(fmIconDataDestroy)); | ||||
|   fallbackIcon_ = iconFromNames(fallbackNames); | ||||
| 
 | ||||
|   // We need to get notified when there is a QEvent::StyleChange event so
 | ||||
|   // we can check if the current icon theme name is changed.
 | ||||
|   // To do this, we can filter QApplication object itself to intercept
 | ||||
|   // signals of all widgets, but this may be too inefficient.
 | ||||
|   // So, we only filter the events on QDesktopWidget instead.
 | ||||
|   qApp->desktop()->installEventFilter(this); | ||||
| } | ||||
| 
 | ||||
| IconTheme::~IconTheme() { | ||||
| } | ||||
| 
 | ||||
| IconTheme* IconTheme::instance() { | ||||
|   return theIconTheme; | ||||
| } | ||||
| 
 | ||||
| // check if the icon theme name is changed and emit "changed()" signal if any change is detected.
 | ||||
| void IconTheme::checkChanged() { | ||||
|   if(QIcon::themeName() != theIconTheme->currentThemeName_) { | ||||
|     // if the icon theme is changed
 | ||||
|     theIconTheme->currentThemeName_ = QIcon::themeName(); | ||||
|     // invalidate the cached data
 | ||||
|     fm_icon_reset_user_data_cache(fm_qdata_id); | ||||
| 
 | ||||
|     theIconTheme->fallbackIcon_ = iconFromNames(fallbackNames); | ||||
|     Q_EMIT theIconTheme->changed(); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| QIcon IconTheme::iconFromNames(const char* const* names) { | ||||
|   const gchar* const* name; | ||||
|   // qDebug("names: %p", names);
 | ||||
|   for(name = names; *name; ++name) { | ||||
|     // qDebug("icon name=%s", *name);
 | ||||
|     QString qname = *name; | ||||
|     QIcon qicon = QIcon::fromTheme(qname); | ||||
|     if(!qicon.isNull()) { | ||||
|       return qicon; | ||||
|     } | ||||
|   } | ||||
|   return QIcon(); | ||||
| } | ||||
| 
 | ||||
| QIcon IconTheme::convertFromGIcon(GIcon* gicon) { | ||||
|   if(G_IS_THEMED_ICON(gicon)) { | ||||
|     const gchar * const * names = g_themed_icon_get_names(G_THEMED_ICON(gicon)); | ||||
|     QIcon icon = iconFromNames(names); | ||||
|     if(!icon.isNull()) | ||||
|       return icon; | ||||
|   } | ||||
|   else if(G_IS_FILE_ICON(gicon)) { | ||||
|     GFile* file = g_file_icon_get_file(G_FILE_ICON(gicon)); | ||||
|     char* fpath = g_file_get_path(file); | ||||
|     QString path = fpath; | ||||
|     g_free(fpath); | ||||
|     return QIcon(path); | ||||
|   } | ||||
|   return theIconTheme->fallbackIcon_; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| //static
 | ||||
| QIcon IconTheme::icon(FmIcon* fmicon) { | ||||
|   // check if we have a cached version
 | ||||
|   QIcon* picon = reinterpret_cast<QIcon*>(fm_icon_get_user_data(fmicon)); | ||||
|   if(!picon) { // we don't have a cache yet
 | ||||
|     picon = new QIcon(); // what a waste!
 | ||||
|     *picon = convertFromGIcon(G_ICON(fmicon)); | ||||
|     fm_icon_set_user_data(fmicon, picon); // store it in FmIcon
 | ||||
|   } | ||||
|   return *picon; | ||||
| } | ||||
| 
 | ||||
| //static
 | ||||
| QIcon IconTheme::icon(GIcon* gicon) { | ||||
|   if(G_IS_THEMED_ICON(gicon)) { | ||||
|     FmIcon* fmicon = fm_icon_from_gicon(gicon); | ||||
|     QIcon qicon = icon(fmicon); | ||||
|     fm_icon_unref(fmicon); | ||||
|     return qicon; | ||||
|   } | ||||
|   else if(G_IS_FILE_ICON(gicon)) { | ||||
|     // we do not map GFileIcon to FmIcon deliberately.
 | ||||
|     return convertFromGIcon(gicon); | ||||
|   } | ||||
|   return theIconTheme->fallbackIcon_; | ||||
| } | ||||
| 
 | ||||
| // this method is called whenever there is an event on the QDesktopWidget object.
 | ||||
| bool IconTheme::eventFilter(QObject* obj, QEvent* event) { | ||||
|   // we're only interested in the StyleChange event.
 | ||||
|   if(event->type() == QEvent::StyleChange) { | ||||
|     checkChanged(); // check if the icon theme is changed
 | ||||
|   } | ||||
|   return QObject::eventFilter(obj, event); | ||||
| } | ||||
							
								
								
									
										69
									
								
								libfm-qt/icontheme.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										69
									
								
								libfm-qt/icontheme.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,69 @@ | ||||
| /*
 | ||||
|     <one line to give the library's name and an idea of what it does.> | ||||
|     Copyright (C) 2012  Hong Jen Yee (PCMan) <pcman.tw@gmail.com> | ||||
| 
 | ||||
|     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 | ||||
| */ | ||||
| 
 | ||||
| 
 | ||||
| #ifndef FM_ICONTHEME_H | ||||
| #define FM_ICONTHEME_H | ||||
| 
 | ||||
| #include "libfmqtglobals.h" | ||||
| #include <QIcon> | ||||
| #include <QString> | ||||
| #include "libfm/fm.h" | ||||
| 
 | ||||
| namespace Fm { | ||||
|    | ||||
| // NOTE:
 | ||||
| // Qt seems to has its own QIcon pixmap caching mechanism internally.
 | ||||
| // Besides, it also caches QIcon objects created by QIcon::fromTheme().
 | ||||
| // So maybe we should not duplicate the work.
 | ||||
| // See http://qt.gitorious.org/qt/qt/blobs/4.8/src/gui/image/qicon.cpp
 | ||||
| // QPixmap QPixmapIconEngine::pixmap(const QSize &size, QIcon::Mode mode, QIcon::State state).
 | ||||
| // In addition, QPixmap is actually stored in X11 server, not client side.
 | ||||
| // Hence maybe we should not cache too many pixmaps, I guess?
 | ||||
| // Let's have Qt do its work and only translate GIcon to QIcon here.
 | ||||
| 
 | ||||
| // Nice article about QPixmap from KDE: http://techbase.kde.org/Development/Tutorials/Graphics/Performance
 | ||||
| 
 | ||||
| class LIBFM_QT_API IconTheme: public QObject { | ||||
|   Q_OBJECT | ||||
| public: | ||||
|   IconTheme(); | ||||
|   ~IconTheme(); | ||||
| 
 | ||||
|   static IconTheme* instance(); | ||||
|   static QIcon icon(FmIcon* fmicon); | ||||
|   static QIcon icon(GIcon* gicon); | ||||
|    | ||||
|   static void checkChanged(); // check if current icon theme name is changed
 | ||||
| Q_SIGNALS: | ||||
|   void changed(); // emitted when the name of current icon theme is changed
 | ||||
| 
 | ||||
| protected: | ||||
|   bool eventFilter(QObject *obj, QEvent *event); | ||||
|   static QIcon convertFromGIcon(GIcon* gicon); | ||||
|   static QIcon iconFromNames(const char * const * names); | ||||
| 
 | ||||
| protected: | ||||
|   QIcon fallbackIcon_; | ||||
|   QString currentThemeName_; | ||||
| }; | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| #endif // FM_ICONTHEME_H
 | ||||
							
								
								
									
										12
									
								
								libfm-qt/libfm-qt.pc.in
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								libfm-qt/libfm-qt.pc.in
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,12 @@ | ||||
| prefix=@CMAKE_INSTALL_PREFIX@ | ||||
| exec_prefix=${prefix} | ||||
| libdir=${exec_prefix}/lib | ||||
| includedir=${prefix}/include | ||||
| 
 | ||||
| Name: libfm-qt | ||||
| Description: A Qt/glib/gio-based lib used to develop file managers providing some file management utilities. (This is a Qt port of the original libfm library) | ||||
| URL: http://pcmanfm.sourceforge.net/ | ||||
| Requires: @REQUIRED_QT@ libfm >= 1.2.0 | ||||
| Version: @LIBFM_QT_VERSION@ | ||||
| Libs: -L${libdir} -lfm -l@LIBRARY_NAME@ | ||||
| Cflags: -I${includedir} | ||||
							
								
								
									
										78
									
								
								libfm-qt/libfmqt.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										78
									
								
								libfm-qt/libfmqt.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,78 @@ | ||||
| /*
 | ||||
|     <one line to give the library's name and an idea of what it does.> | ||||
|     Copyright (C) 2012  Hong Jen Yee (PCMan) <pcman.tw@gmail.com> | ||||
| 
 | ||||
|     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 | ||||
| */ | ||||
| 
 | ||||
| #include <libfm/fm.h> | ||||
| #include "libfmqt.h" | ||||
| #include <QLocale> | ||||
| #include "icontheme.h" | ||||
| #include "thumbnailloader.h" | ||||
| 
 | ||||
| namespace Fm { | ||||
| 	 | ||||
| struct LibFmQtData { | ||||
|   LibFmQtData(); | ||||
|   ~LibFmQtData(); | ||||
| 
 | ||||
|   IconTheme* iconTheme; | ||||
|   ThumbnailLoader* thumbnailLoader; | ||||
|   QTranslator translator; | ||||
|   int refCount; | ||||
| }; | ||||
| 
 | ||||
| static LibFmQtData* theLibFmData = NULL; | ||||
| 
 | ||||
| LibFmQtData::LibFmQtData(): refCount(1) { | ||||
| #if !GLIB_CHECK_VERSION(2, 36, 0) | ||||
|   g_type_init(); | ||||
| #endif | ||||
|   fm_init(NULL); | ||||
|   // turn on glib debug message
 | ||||
|   // g_setenv("G_MESSAGES_DEBUG", "all", true);
 | ||||
|   iconTheme = new IconTheme(); | ||||
|   thumbnailLoader = new ThumbnailLoader(); | ||||
|   translator.load("libfm-qt_" + QLocale::system().name(), LIBFM_DATA_DIR "/translations"); | ||||
| } | ||||
| 
 | ||||
| LibFmQtData::~LibFmQtData() { | ||||
|   delete iconTheme; | ||||
|   delete thumbnailLoader; | ||||
|   fm_finalize(); | ||||
| } | ||||
| 
 | ||||
| LibFmQt::LibFmQt() { | ||||
|   if(!theLibFmData) { | ||||
|     theLibFmData = new LibFmQtData(); | ||||
|   } | ||||
|   else | ||||
|     ++theLibFmData->refCount; | ||||
|   d = theLibFmData; | ||||
| } | ||||
| 
 | ||||
| LibFmQt::~LibFmQt() { | ||||
|   if(--d->refCount == 0) { | ||||
|     delete d; | ||||
|     theLibFmData = NULL; | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| QTranslator* LibFmQt::translator() { | ||||
|   return &d->translator; | ||||
| } | ||||
| 
 | ||||
| } // namespace Fm
 | ||||
							
								
								
									
										47
									
								
								libfm-qt/libfmqt.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								libfm-qt/libfmqt.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,47 @@ | ||||
| /*
 | ||||
|     <one line to give the library's name and an idea of what it does.> | ||||
|     Copyright (C) 2012  Hong Jen Yee (PCMan) <pcman.tw@gmail.com> | ||||
| 
 | ||||
|     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 | ||||
| */ | ||||
| 
 | ||||
| 
 | ||||
| #ifndef FM_APPLICATION_H | ||||
| #define FM_APPLICATION_H | ||||
| 
 | ||||
| #include "libfmqtglobals.h" | ||||
| #include <QtGlobal> | ||||
| #include <QTranslator> | ||||
| #include <libfm/fm.h> | ||||
| 
 | ||||
| namespace Fm { | ||||
| 
 | ||||
| struct LibFmQtData; | ||||
| 
 | ||||
| class LIBFM_QT_API LibFmQt { | ||||
| public: | ||||
|   LibFmQt(); | ||||
|   ~LibFmQt(); | ||||
| 
 | ||||
|   QTranslator* translator(); | ||||
| 
 | ||||
| private: | ||||
|   LibFmQt(LibFmQt& other); // disable copy
 | ||||
|   LibFmQtData* d; | ||||
| }; | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| #endif // FM_APPLICATION_H
 | ||||
							
								
								
									
										30
									
								
								libfm-qt/libfmqtglobals.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								libfm-qt/libfmqtglobals.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,30 @@ | ||||
| /*
 | ||||
|     Copyright (C) 2012  Hong Jen Yee (PCMan) <pcman.tw@gmail.com> | ||||
| 
 | ||||
|     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 | ||||
| */ | ||||
| 
 | ||||
| #ifndef _LIBFM_QT_GLOBALS_ | ||||
| #define _LIBFM_QT_GLOBALS_ | ||||
| 
 | ||||
| #include <QtGlobal> | ||||
| 
 | ||||
| #ifdef LIBFM_QT_COMPILATION | ||||
|     #define LIBFM_QT_API    Q_DECL_EXPORT | ||||
| #else | ||||
|     #define LIBFM_QT_API    Q_DECL_IMPORT | ||||
| #endif | ||||
| 
 | ||||
| #endif | ||||
							
								
								
									
										205
									
								
								libfm-qt/mount-operation-password.ui
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										205
									
								
								libfm-qt/mount-operation-password.ui
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,205 @@ | ||||
| <?xml version="1.0" encoding="UTF-8"?> | ||||
| <ui version="4.0"> | ||||
|  <class>MountOperationPasswordDialog</class> | ||||
|  <widget class="QDialog" name="MountOperationPasswordDialog"> | ||||
|   <property name="geometry"> | ||||
|    <rect> | ||||
|     <x>0</x> | ||||
|     <y>0</y> | ||||
|     <width>244</width> | ||||
|     <height>302</height> | ||||
|    </rect> | ||||
|   </property> | ||||
|   <property name="sizePolicy"> | ||||
|    <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> | ||||
|     <horstretch>0</horstretch> | ||||
|     <verstretch>0</verstretch> | ||||
|    </sizepolicy> | ||||
|   </property> | ||||
|   <property name="windowTitle"> | ||||
|    <string>Mount</string> | ||||
|   </property> | ||||
|   <property name="windowIcon"> | ||||
|    <iconset theme="dialog-password"/> | ||||
|   </property> | ||||
|   <property name="sizeGripEnabled"> | ||||
|    <bool>false</bool> | ||||
|   </property> | ||||
|   <property name="modal"> | ||||
|    <bool>false</bool> | ||||
|   </property> | ||||
|   <layout class="QVBoxLayout" name="verticalLayout"> | ||||
|    <item> | ||||
|     <widget class="QLabel" name="message"> | ||||
|      <property name="sizePolicy"> | ||||
|       <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> | ||||
|        <horstretch>0</horstretch> | ||||
|        <verstretch>0</verstretch> | ||||
|       </sizepolicy> | ||||
|      </property> | ||||
|      <property name="text"> | ||||
|       <string/> | ||||
|      </property> | ||||
|     </widget> | ||||
|    </item> | ||||
|    <item> | ||||
|     <widget class="QRadioButton" name="Anonymous"> | ||||
|      <property name="text"> | ||||
|       <string>Connect &anonymously</string> | ||||
|      </property> | ||||
|      <attribute name="buttonGroup"> | ||||
|       <string notr="true">usernameGroup</string> | ||||
|      </attribute> | ||||
|     </widget> | ||||
|    </item> | ||||
|    <item> | ||||
|     <widget class="QRadioButton" name="asUser"> | ||||
|      <property name="text"> | ||||
|       <string>Connect as u&ser:</string> | ||||
|      </property> | ||||
|      <attribute name="buttonGroup"> | ||||
|       <string notr="true">usernameGroup</string> | ||||
|      </attribute> | ||||
|     </widget> | ||||
|    </item> | ||||
|    <item> | ||||
|     <layout class="QGridLayout" name="gridLayout"> | ||||
|      <item row="0" column="1"> | ||||
|       <widget class="QLineEdit" name="username"/> | ||||
|      </item> | ||||
|      <item row="0" column="0"> | ||||
|       <widget class="QLabel" name="label"> | ||||
|        <property name="sizePolicy"> | ||||
|         <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> | ||||
|          <horstretch>0</horstretch> | ||||
|          <verstretch>0</verstretch> | ||||
|         </sizepolicy> | ||||
|        </property> | ||||
|        <property name="text"> | ||||
|         <string>&Username:</string> | ||||
|        </property> | ||||
|        <property name="buddy"> | ||||
|         <cstring>username</cstring> | ||||
|        </property> | ||||
|       </widget> | ||||
|      </item> | ||||
|      <item row="3" column="1"> | ||||
|       <widget class="QLineEdit" name="password"> | ||||
|        <property name="echoMode"> | ||||
|         <enum>QLineEdit::Password</enum> | ||||
|        </property> | ||||
|       </widget> | ||||
|      </item> | ||||
|      <item row="3" column="0"> | ||||
|       <widget class="QLabel" name="label_2"> | ||||
|        <property name="sizePolicy"> | ||||
|         <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> | ||||
|          <horstretch>0</horstretch> | ||||
|          <verstretch>0</verstretch> | ||||
|         </sizepolicy> | ||||
|        </property> | ||||
|        <property name="text"> | ||||
|         <string>&Password:</string> | ||||
|        </property> | ||||
|        <property name="buddy"> | ||||
|         <cstring>password</cstring> | ||||
|        </property> | ||||
|       </widget> | ||||
|      </item> | ||||
|      <item row="2" column="0"> | ||||
|       <widget class="QLabel" name="domainLabel"> | ||||
|        <property name="text"> | ||||
|         <string>&Domain:</string> | ||||
|        </property> | ||||
|        <property name="buddy"> | ||||
|         <cstring>domain</cstring> | ||||
|        </property> | ||||
|       </widget> | ||||
|      </item> | ||||
|      <item row="2" column="1"> | ||||
|       <widget class="QLineEdit" name="domain"/> | ||||
|      </item> | ||||
|     </layout> | ||||
|    </item> | ||||
|    <item> | ||||
|     <widget class="QRadioButton" name="forgetPassword"> | ||||
|      <property name="text"> | ||||
|       <string>Forget password &immediately</string> | ||||
|      </property> | ||||
|      <attribute name="buttonGroup"> | ||||
|       <string notr="true">passwordGroup</string> | ||||
|      </attribute> | ||||
|     </widget> | ||||
|    </item> | ||||
|    <item> | ||||
|     <widget class="QRadioButton" name="sessionPassword"> | ||||
|      <property name="text"> | ||||
|       <string>Remember password until you &logout</string> | ||||
|      </property> | ||||
|      <attribute name="buttonGroup"> | ||||
|       <string notr="true">passwordGroup</string> | ||||
|      </attribute> | ||||
|     </widget> | ||||
|    </item> | ||||
|    <item> | ||||
|     <widget class="QRadioButton" name="storePassword"> | ||||
|      <property name="text"> | ||||
|       <string>Remember &forever</string> | ||||
|      </property> | ||||
|      <attribute name="buttonGroup"> | ||||
|       <string notr="true">passwordGroup</string> | ||||
|      </attribute> | ||||
|     </widget> | ||||
|    </item> | ||||
|    <item> | ||||
|     <widget class="QDialogButtonBox" name="buttonBox"> | ||||
|      <property name="orientation"> | ||||
|       <enum>Qt::Horizontal</enum> | ||||
|      </property> | ||||
|      <property name="standardButtons"> | ||||
|       <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> | ||||
|      </property> | ||||
|     </widget> | ||||
|    </item> | ||||
|   </layout> | ||||
|  </widget> | ||||
|  <resources/> | ||||
|  <connections> | ||||
|   <connection> | ||||
|    <sender>buttonBox</sender> | ||||
|    <signal>accepted()</signal> | ||||
|    <receiver>MountOperationPasswordDialog</receiver> | ||||
|    <slot>accept()</slot> | ||||
|    <hints> | ||||
|     <hint type="sourcelabel"> | ||||
|      <x>248</x> | ||||
|      <y>254</y> | ||||
|     </hint> | ||||
|     <hint type="destinationlabel"> | ||||
|      <x>157</x> | ||||
|      <y>274</y> | ||||
|     </hint> | ||||
|    </hints> | ||||
|   </connection> | ||||
|   <connection> | ||||
|    <sender>buttonBox</sender> | ||||
|    <signal>rejected()</signal> | ||||
|    <receiver>MountOperationPasswordDialog</receiver> | ||||
|    <slot>reject()</slot> | ||||
|    <hints> | ||||
|     <hint type="sourcelabel"> | ||||
|      <x>316</x> | ||||
|      <y>260</y> | ||||
|     </hint> | ||||
|     <hint type="destinationlabel"> | ||||
|      <x>286</x> | ||||
|      <y>274</y> | ||||
|     </hint> | ||||
|    </hints> | ||||
|   </connection> | ||||
|  </connections> | ||||
|  <buttongroups> | ||||
|   <buttongroup name="usernameGroup"/> | ||||
|   <buttongroup name="passwordGroup"/> | ||||
|  </buttongroups> | ||||
| </ui> | ||||
							
								
								
									
										227
									
								
								libfm-qt/mountoperation.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										227
									
								
								libfm-qt/mountoperation.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,227 @@ | ||||
| /*
 | ||||
| 
 | ||||
|     Copyright (C) 2013 - 2014 Hong Jen Yee (PCMan) <pcman.tw@gmail.com> | ||||
| 
 | ||||
|     This program is free software; you can redistribute it and/or modify | ||||
|     it under the terms of the GNU General Public License as published by | ||||
|     the Free Software Foundation; either version 2 of the License, or | ||||
|     (at your option) any later version. | ||||
| 
 | ||||
|     This program is distributed in the hope that it will be useful, | ||||
|     but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|     GNU General Public License for more details. | ||||
| 
 | ||||
|     You should have received a copy of the GNU General Public License along | ||||
|     with this program; if not, write to the Free Software Foundation, Inc., | ||||
|     51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||||
| */ | ||||
| 
 | ||||
| 
 | ||||
| #include "mountoperation.h" | ||||
| #include <glib/gi18n.h> // for _()
 | ||||
| #include <QMessageBox> | ||||
| #include <QPushButton> | ||||
| #include <QEventLoop> | ||||
| #include "mountoperationpassworddialog_p.h" | ||||
| #include "mountoperationquestiondialog_p.h" | ||||
| #include "ui_mount-operation-password.h" | ||||
| 
 | ||||
| namespace Fm { | ||||
| 
 | ||||
| MountOperation::MountOperation(bool interactive, QWidget* parent): | ||||
|   QObject(parent), | ||||
|   interactive_(interactive), | ||||
|   running(false), | ||||
|   op(g_mount_operation_new()), | ||||
|   cancellable_(g_cancellable_new()), | ||||
|   eventLoop(NULL), | ||||
|   autoDestroy_(true) { | ||||
|    | ||||
|   g_signal_connect(op, "ask-password", G_CALLBACK(onAskPassword), this); | ||||
|   g_signal_connect(op, "ask-question", G_CALLBACK(onAskQuestion), this); | ||||
|   // g_signal_connect(op, "reply", G_CALLBACK(onReply), this);
 | ||||
| 
 | ||||
| #if GLIB_CHECK_VERSION(2, 20, 0) | ||||
|   g_signal_connect(op, "aborted", G_CALLBACK(onAbort), this); | ||||
| #endif | ||||
| #if GLIB_CHECK_VERSION(2, 22, 0) | ||||
|   g_signal_connect(op, "show-processes", G_CALLBACK(onShowProcesses), this); | ||||
| #endif | ||||
| #if GLIB_CHECK_VERSION(2, 34, 0) | ||||
|   g_signal_connect(op, "show-unmount-progress", G_CALLBACK(onShowUnmountProgress), this); | ||||
| #endif | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| MountOperation::~MountOperation() { | ||||
|   qDebug("delete MountOperation"); | ||||
|   if(cancellable_) { | ||||
|     cancel(); | ||||
|     g_object_unref(cancellable_); | ||||
|   } | ||||
| 
 | ||||
|   if(eventLoop) { // if wait() is called to block the main loop, but the event loop is still running
 | ||||
|     // NOTE: is this possible?
 | ||||
|     eventLoop->exit(1); | ||||
|   } | ||||
| 
 | ||||
|   if(op) { | ||||
|     g_signal_handlers_disconnect_by_func(op, (gpointer)G_CALLBACK(onAskPassword), this); | ||||
|     g_signal_handlers_disconnect_by_func(op, (gpointer)G_CALLBACK(onAskQuestion), this); | ||||
| #if GLIB_CHECK_VERSION(2, 20, 0) | ||||
|     g_signal_handlers_disconnect_by_func(op, (gpointer)G_CALLBACK(onAbort), this); | ||||
| #endif | ||||
| #if GLIB_CHECK_VERSION(2, 22, 0) | ||||
|     g_signal_handlers_disconnect_by_func(op, (gpointer)G_CALLBACK(onShowProcesses), this); | ||||
| #endif | ||||
| #if GLIB_CHECK_VERSION(2, 34, 0) | ||||
|     g_signal_handlers_disconnect_by_func(op, (gpointer)G_CALLBACK(onShowUnmountProgress), this); | ||||
| #endif | ||||
|     g_object_unref(op); | ||||
|   } | ||||
|   // qDebug("MountOperation deleted");
 | ||||
| } | ||||
| 
 | ||||
| void MountOperation::onAbort(GMountOperation* _op, MountOperation* pThis) { | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| void MountOperation::onAskPassword(GMountOperation* _op, gchar* message, gchar* default_user, gchar* default_domain, GAskPasswordFlags flags, MountOperation* pThis) { | ||||
|   qDebug("ask password"); | ||||
|   MountOperationPasswordDialog dlg(pThis, flags); | ||||
|   dlg.setMessage(QString::fromUtf8(message)); | ||||
|   dlg.setDefaultUser(QString::fromUtf8(default_user)); | ||||
|   dlg.setDefaultDomain(QString::fromUtf8(default_domain)); | ||||
|   dlg.exec(); | ||||
| } | ||||
| 
 | ||||
| void MountOperation::onAskQuestion(GMountOperation* _op, gchar* message, GStrv choices, MountOperation* pThis) { | ||||
|   qDebug("ask question"); | ||||
|   MountOperationQuestionDialog dialog(pThis, message, choices); | ||||
|   dialog.exec(); | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
| void MountOperation::onReply(GMountOperation* _op, GMountOperationResult result, MountOperation* pThis) { | ||||
|   qDebug("reply"); | ||||
| } | ||||
| */ | ||||
| 
 | ||||
| void MountOperation::onShowProcesses(GMountOperation* _op, gchar* message, GArray* processes, GStrv choices, MountOperation* pThis) { | ||||
|   qDebug("show processes"); | ||||
| } | ||||
| 
 | ||||
| void MountOperation::onShowUnmountProgress(GMountOperation* _op, gchar* message, gint64 time_left, gint64 bytes_left, MountOperation* pThis) { | ||||
|   qDebug("show unmount progress"); | ||||
| } | ||||
| 
 | ||||
| void MountOperation::onEjectMountFinished(GMount* mount, GAsyncResult* res, QPointer< MountOperation >* pThis) { | ||||
|   if(*pThis) { | ||||
|     GError* error = NULL; | ||||
|     g_mount_eject_with_operation_finish(mount, res, &error); | ||||
|     (*pThis)->handleFinish(error); | ||||
|   } | ||||
|   delete pThis; | ||||
| } | ||||
| 
 | ||||
| void MountOperation::onEjectVolumeFinished(GVolume* volume, GAsyncResult* res, QPointer< MountOperation >* pThis) { | ||||
|   if(*pThis) { | ||||
|     GError* error = NULL; | ||||
|     g_volume_eject_with_operation_finish(volume, res, &error); | ||||
|     (*pThis)->handleFinish(error); | ||||
|   } | ||||
|   delete pThis; | ||||
| } | ||||
| 
 | ||||
| void MountOperation::onMountFileFinished(GFile* file, GAsyncResult* res, QPointer< MountOperation >* pThis) { | ||||
|   if(*pThis) { | ||||
|     GError* error = NULL; | ||||
|     g_file_mount_enclosing_volume_finish(file, res, &error); | ||||
|     (*pThis)->handleFinish(error); | ||||
|   } | ||||
|   delete pThis; | ||||
| } | ||||
| 
 | ||||
| void MountOperation::onMountVolumeFinished(GVolume* volume, GAsyncResult* res, QPointer< MountOperation >* pThis) { | ||||
|   if(*pThis) { | ||||
|     GError* error = NULL; | ||||
|     g_volume_mount_finish(volume, res, &error); | ||||
|     (*pThis)->handleFinish(error); | ||||
|   } | ||||
|   delete pThis; | ||||
| } | ||||
| 
 | ||||
| void MountOperation::onUnmountMountFinished(GMount* mount, GAsyncResult* res, QPointer< MountOperation >* pThis) { | ||||
|   if(*pThis) { | ||||
|     GError* error = NULL; | ||||
|     g_mount_unmount_with_operation_finish(mount, res, &error); | ||||
|     (*pThis)->handleFinish(error); | ||||
|   } | ||||
|   delete pThis; | ||||
| } | ||||
| 
 | ||||
| void MountOperation::handleFinish(GError* error) { | ||||
|   qDebug("operation finished: %p", error); | ||||
|   if(error) { | ||||
|     bool showError = interactive_; | ||||
|     if(error->domain == G_IO_ERROR) { | ||||
|       if(error->code == G_IO_ERROR_FAILED) { | ||||
|         // Generate a more human-readable error message instead of using a gvfs one.
 | ||||
|         // The original error message is something like:
 | ||||
|         // Error unmounting: umount exited with exit code 1:
 | ||||
|         // helper failed with: umount: only root can unmount
 | ||||
|         // UUID=18cbf00c-e65f-445a-bccc-11964bdea05d from /media/sda4 */
 | ||||
|         // Why they pass this back to us? This is not human-readable for the users at all.
 | ||||
|         if(strstr(error->message, "only root can ")) { | ||||
|           g_free(error->message); | ||||
|           error->message = g_strdup(_("Only system administrators have the permission to do this.")); | ||||
|         } | ||||
|       } | ||||
|       else if(error->code == G_IO_ERROR_FAILED_HANDLED) | ||||
|         showError = false; | ||||
|     } | ||||
|     if(showError) | ||||
|       QMessageBox::critical(NULL, QObject::tr("Error"), QString::fromUtf8(error->message)); | ||||
|   } | ||||
| 
 | ||||
|   Q_EMIT finished(error); | ||||
| 
 | ||||
|   if(eventLoop) { // if wait() is called to block the main loop
 | ||||
|     eventLoop->exit(error != NULL ? 1 : 0); | ||||
|     eventLoop = NULL; | ||||
|   } | ||||
| 
 | ||||
|   if(error) | ||||
|     g_error_free(error); | ||||
| 
 | ||||
|   // free ourself here!!
 | ||||
|   if(autoDestroy_) | ||||
|     deleteLater(); | ||||
| } | ||||
| 
 | ||||
| void MountOperation::prepareUnmount(GMount* mount) { | ||||
|   /* ensure that CWD is not on the mounted filesystem. */ | ||||
|   char* cwd_str = g_get_current_dir(); | ||||
|   GFile* cwd = g_file_new_for_path(cwd_str); | ||||
|   GFile* root = g_mount_get_root(mount); | ||||
|   g_free(cwd_str); | ||||
|   /* FIXME: This cannot cover 100% cases since symlinks are not checked.
 | ||||
|     * There may be other cases that cwd is actually under mount root | ||||
|     * but checking prefix is not enough. We already did our best, though. */ | ||||
|   if(g_file_has_prefix(cwd, root)) | ||||
|       g_chdir("/"); | ||||
|   g_object_unref(cwd); | ||||
|   g_object_unref(root); | ||||
| } | ||||
| 
 | ||||
| // block the operation used an internal QEventLoop and returns
 | ||||
| // only after the whole operation is finished.
 | ||||
| bool MountOperation::wait() { | ||||
|   QEventLoop loop; | ||||
|   eventLoop = &loop; | ||||
|   int exitCode = loop.exec(); | ||||
|   return exitCode == 0 ? true : false; | ||||
| } | ||||
| 
 | ||||
| } // namespace Fm
 | ||||
							
								
								
									
										155
									
								
								libfm-qt/mountoperation.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										155
									
								
								libfm-qt/mountoperation.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,155 @@ | ||||
| /*
 | ||||
| 
 | ||||
|     Copyright (C) 2013  Hong Jen Yee (PCMan) <pcman.tw@gmail.com> | ||||
| 
 | ||||
|     This program is free software; you can redistribute it and/or modify | ||||
|     it under the terms of the GNU General Public License as published by | ||||
|     the Free Software Foundation; either version 2 of the License, or | ||||
|     (at your option) any later version. | ||||
| 
 | ||||
|     This program is distributed in the hope that it will be useful, | ||||
|     but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|     GNU General Public License for more details. | ||||
| 
 | ||||
|     You should have received a copy of the GNU General Public License along | ||||
|     with this program; if not, write to the Free Software Foundation, Inc., | ||||
|     51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||||
| */ | ||||
| 
 | ||||
| 
 | ||||
| #ifndef FM_MOUNTOPERATION_H | ||||
| #define FM_MOUNTOPERATION_H | ||||
| 
 | ||||
| #include "libfmqtglobals.h" | ||||
| #include <QWidget> | ||||
| #include <QDialog> | ||||
| #include <libfm/fm.h> | ||||
| #include <gio/gio.h> | ||||
| #include <QPointer> | ||||
| 
 | ||||
| class QEventLoop; | ||||
| 
 | ||||
| namespace Fm { | ||||
| 
 | ||||
| // FIXME: the original APIs in gtk+ version of libfm for mounting devices is poor.
 | ||||
| // Need to find a better API design which make things fully async and cancellable.
 | ||||
| 
 | ||||
| // FIXME: parent_ does not work. All dialogs shown by the mount operation has no parent window assigned.
 | ||||
| // FIXME: Need to reconsider the propery way of API design. Blocking sync calls are handy, but
 | ||||
| // indeed causes some problems. :-(
 | ||||
| 
 | ||||
| class LIBFM_QT_API MountOperation: public QObject { | ||||
| Q_OBJECT | ||||
| 
 | ||||
| public: | ||||
|   explicit MountOperation(bool interactive = true, QWidget* parent = 0); | ||||
|   ~MountOperation(); | ||||
|    | ||||
|   void mount(FmPath* path) { | ||||
|     GFile* gf = fm_path_to_gfile(path); | ||||
|     g_file_mount_enclosing_volume(gf, G_MOUNT_MOUNT_NONE, op, cancellable_, (GAsyncReadyCallback)onMountFileFinished, new QPointer<MountOperation>(this)); | ||||
|     g_object_unref(gf); | ||||
|   } | ||||
| 
 | ||||
|   void mount(GVolume* volume) { | ||||
|     g_volume_mount(volume, G_MOUNT_MOUNT_NONE, op, cancellable_, (GAsyncReadyCallback)onMountVolumeFinished, new QPointer<MountOperation>(this));     | ||||
|   } | ||||
| 
 | ||||
|   void unmount(GMount* mount) { | ||||
|     prepareUnmount(mount); | ||||
|     g_mount_unmount_with_operation(mount, G_MOUNT_UNMOUNT_NONE, op, cancellable_, (GAsyncReadyCallback)onUnmountMountFinished, new QPointer<MountOperation>(this)); | ||||
|   } | ||||
| 
 | ||||
|   void unmount(GVolume* volume) { | ||||
|     GMount* mount = g_volume_get_mount(volume); | ||||
|     if(!mount) | ||||
|       return; | ||||
|     unmount(mount); | ||||
|     g_object_unref(mount); | ||||
|   } | ||||
|    | ||||
|   void eject(GMount* mount) { | ||||
|     prepareUnmount(mount); | ||||
|     g_mount_eject_with_operation(mount, G_MOUNT_UNMOUNT_NONE, op, cancellable_, (GAsyncReadyCallback)onEjectMountFinished, new QPointer<MountOperation>(this)); | ||||
|   } | ||||
| 
 | ||||
|   void eject(GVolume* volume) { | ||||
|     GMount* mnt = g_volume_get_mount(volume); | ||||
|     prepareUnmount(mnt); | ||||
|     g_object_unref(mnt); | ||||
|     g_volume_eject_with_operation(volume, G_MOUNT_UNMOUNT_NONE, op, cancellable_, (GAsyncReadyCallback)onEjectVolumeFinished, new QPointer<MountOperation>(this)); | ||||
|   } | ||||
| 
 | ||||
|   QWidget* parent() const { | ||||
|     return parent_; | ||||
|   } | ||||
| 
 | ||||
|   void setParent(QWidget* parent) { | ||||
|     parent_ = parent; | ||||
|   } | ||||
| 
 | ||||
|   GCancellable* cancellable() const { | ||||
|     return cancellable_; | ||||
|   } | ||||
|    | ||||
|   GMountOperation* mountOperation() { | ||||
|     return op; | ||||
|   } | ||||
|    | ||||
|   void cancel() { | ||||
|     g_cancellable_cancel(cancellable_); | ||||
|   } | ||||
| 
 | ||||
|   bool isRunning() const { | ||||
|     return running; | ||||
|   } | ||||
| 
 | ||||
|   // block the operation used an internal QEventLoop and returns
 | ||||
|   // only after the whole operation is finished.
 | ||||
|   bool wait(); | ||||
| 
 | ||||
|   bool autoDestroy() { | ||||
|     return autoDestroy_; | ||||
|   } | ||||
| 
 | ||||
|   void setAutoDestroy(bool destroy = true) { | ||||
|     autoDestroy_ = destroy; | ||||
|   } | ||||
| 
 | ||||
| Q_SIGNALS: | ||||
|   void finished(GError* error = NULL); | ||||
| 
 | ||||
| private: | ||||
|   void prepareUnmount(GMount* mount); | ||||
| 
 | ||||
|   static void onAskPassword(GMountOperation *_op, gchar* message, gchar* default_user, gchar* default_domain, GAskPasswordFlags flags, MountOperation* pThis); | ||||
|   static void onAskQuestion(GMountOperation *_op, gchar* message, GStrv choices, MountOperation* pThis); | ||||
|   // static void onReply(GMountOperation *_op, GMountOperationResult result, MountOperation* pThis);
 | ||||
| 
 | ||||
|   static void onAbort(GMountOperation *_op, MountOperation* pThis); | ||||
|   static void onShowProcesses(GMountOperation *_op, gchar* message, GArray* processes, GStrv choices, MountOperation* pThis); | ||||
|   static void onShowUnmountProgress(GMountOperation *_op, gchar* message, gint64 time_left, gint64 bytes_left, MountOperation* pThis); | ||||
| 
 | ||||
|   // it's possible that this object is freed when the callback is called by gio, so guarding with QPointer is needed here.
 | ||||
|   static void onMountFileFinished(GFile* file, GAsyncResult *res, QPointer<MountOperation>* pThis); | ||||
|   static void onMountVolumeFinished(GVolume* volume, GAsyncResult *res, QPointer<MountOperation>* pThis); | ||||
|   static void onUnmountMountFinished(GMount* mount, GAsyncResult *res, QPointer<MountOperation>* pThis); | ||||
|   static void onEjectMountFinished(GMount* mount, GAsyncResult *res, QPointer<MountOperation>* pThis); | ||||
|   static void onEjectVolumeFinished(GVolume* volume, GAsyncResult *res, QPointer<MountOperation>* pThis); | ||||
| 
 | ||||
|   void handleFinish(GError* error); | ||||
| 
 | ||||
| private: | ||||
|   GMountOperation* op; | ||||
|   GCancellable* cancellable_; | ||||
|   QWidget* parent_; | ||||
|   bool running; | ||||
|   bool interactive_; | ||||
|   QEventLoop* eventLoop; | ||||
|   bool autoDestroy_; | ||||
| }; | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| #endif // FM_MOUNTOPERATION_H
 | ||||
							
								
								
									
										125
									
								
								libfm-qt/mountoperationpassworddialog.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										125
									
								
								libfm-qt/mountoperationpassworddialog.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,125 @@ | ||||
| /*
 | ||||
| 
 | ||||
|     Copyright (C) 2013  Hong Jen Yee (PCMan) <pcman.tw@gmail.com> | ||||
| 
 | ||||
|     This program is free software; you can redistribute it and/or modify | ||||
|     it under the terms of the GNU General Public License as published by | ||||
|     the Free Software Foundation; either version 2 of the License, or | ||||
|     (at your option) any later version. | ||||
| 
 | ||||
|     This program is distributed in the hope that it will be useful, | ||||
|     but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|     GNU General Public License for more details. | ||||
| 
 | ||||
|     You should have received a copy of the GNU General Public License along | ||||
|     with this program; if not, write to the Free Software Foundation, Inc., | ||||
|     51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||||
| */ | ||||
| 
 | ||||
| 
 | ||||
| #include "mountoperationpassworddialog_p.h" | ||||
| #include "ui_mount-operation-password.h" | ||||
| #include "mountoperation.h" | ||||
| 
 | ||||
| namespace Fm { | ||||
| 
 | ||||
| MountOperationPasswordDialog::MountOperationPasswordDialog(MountOperation* op, GAskPasswordFlags flags): | ||||
|   QDialog(), | ||||
|   mountOperation(op), | ||||
|   canAnonymous(flags & G_ASK_PASSWORD_ANONYMOUS_SUPPORTED ? true : false), | ||||
|   canSavePassword(flags & G_ASK_PASSWORD_SAVING_SUPPORTED ? true : false), | ||||
|   needUserName(flags & G_ASK_PASSWORD_NEED_USERNAME ? true : false), | ||||
|   needPassword(flags & G_ASK_PASSWORD_NEED_PASSWORD ? true : false), | ||||
|   needDomain(flags & G_ASK_PASSWORD_NEED_DOMAIN ? true : false) { | ||||
| 
 | ||||
|   ui = new Ui::MountOperationPasswordDialog(); | ||||
|   ui->setupUi(this); | ||||
| 
 | ||||
|   // change the text of Ok button to Connect
 | ||||
|   ui->buttonBox->buttons().first()->setText(tr("&Connect")); | ||||
|   connect(ui->Anonymous, &QAbstractButton::toggled, this, &MountOperationPasswordDialog::onAnonymousToggled); | ||||
| 
 | ||||
|   if(canAnonymous) { | ||||
|     // select ananymous by default if applicable.
 | ||||
|     ui->Anonymous->setChecked(true); | ||||
|   } | ||||
|   else { | ||||
|     ui->Anonymous->setEnabled(false); | ||||
|   } | ||||
|   if(!needUserName) { | ||||
|     ui->username->setEnabled(false); | ||||
|   } | ||||
|   if(!needPassword) { | ||||
|     ui->password->setEnabled(false); | ||||
|   } | ||||
|   if(!needDomain) { | ||||
|     ui->domain->hide(); | ||||
|     ui->domainLabel->hide(); | ||||
|   } | ||||
|   if(canSavePassword) { | ||||
|     ui->sessionPassword->setChecked(true); | ||||
|   } | ||||
|   else { | ||||
|     ui->storePassword->setEnabled(false); | ||||
|     ui->sessionPassword->setEnabled(false); | ||||
|     ui->forgetPassword->setChecked(true); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| MountOperationPasswordDialog::~MountOperationPasswordDialog() { | ||||
|   delete ui; | ||||
| } | ||||
| 
 | ||||
| void MountOperationPasswordDialog::onAnonymousToggled(bool checked) { | ||||
|   // disable username/password entries if anonymous mode is used
 | ||||
|   bool useUserPassword = !checked; | ||||
|   if(needUserName) | ||||
|     ui->username->setEnabled(useUserPassword); | ||||
|   if(needPassword) | ||||
|     ui->password->setEnabled(useUserPassword); | ||||
|   if(needDomain) | ||||
|     ui->domain->setEnabled(useUserPassword); | ||||
| 
 | ||||
|   if(canSavePassword) { | ||||
|     ui->forgetPassword->setEnabled(useUserPassword); | ||||
|     ui->sessionPassword->setEnabled(useUserPassword); | ||||
|     ui->storePassword->setEnabled(useUserPassword); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| void MountOperationPasswordDialog::setMessage(QString message) { | ||||
|   ui->message->setText(message); | ||||
| } | ||||
| 
 | ||||
| void MountOperationPasswordDialog::setDefaultDomain(QString domain) { | ||||
|   ui->domain->setText(domain); | ||||
| } | ||||
| 
 | ||||
| void MountOperationPasswordDialog::setDefaultUser(QString user) { | ||||
|   ui->username->setText(user); | ||||
| } | ||||
| 
 | ||||
| void MountOperationPasswordDialog::done(int r) { | ||||
|   GMountOperation* gmop = mountOperation->mountOperation(); | ||||
| 
 | ||||
|   if(r == QDialog::Accepted) { | ||||
|      | ||||
|     if(needUserName) | ||||
|       g_mount_operation_set_username(gmop, ui->username->text().toUtf8()); | ||||
|     if(needDomain) | ||||
|       g_mount_operation_set_domain(gmop, ui->domain->text().toUtf8()); | ||||
|     if(needPassword) | ||||
|       g_mount_operation_set_password(gmop, ui->password->text().toUtf8()); | ||||
|     if(canAnonymous) | ||||
|       g_mount_operation_set_anonymous(gmop, ui->Anonymous->isChecked()); | ||||
| 
 | ||||
|     g_mount_operation_reply(gmop, G_MOUNT_OPERATION_HANDLED); | ||||
|   } | ||||
|   else { | ||||
|     g_mount_operation_reply(gmop, G_MOUNT_OPERATION_ABORTED); | ||||
|   } | ||||
|   QDialog::done(r); | ||||
| } | ||||
| 
 | ||||
| } // namespace Fm
 | ||||
							
								
								
									
										64
									
								
								libfm-qt/mountoperationpassworddialog_p.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										64
									
								
								libfm-qt/mountoperationpassworddialog_p.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,64 @@ | ||||
| /*
 | ||||
| 
 | ||||
|     Copyright (C) 2013  Hong Jen Yee (PCMan) <pcman.tw@gmail.com> | ||||
| 
 | ||||
|     This program is free software; you can redistribute it and/or modify | ||||
|     it under the terms of the GNU General Public License as published by | ||||
|     the Free Software Foundation; either version 2 of the License, or | ||||
|     (at your option) any later version. | ||||
| 
 | ||||
|     This program is distributed in the hope that it will be useful, | ||||
|     but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|     GNU General Public License for more details. | ||||
| 
 | ||||
|     You should have received a copy of the GNU General Public License along | ||||
|     with this program; if not, write to the Free Software Foundation, Inc., | ||||
|     51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||||
| */ | ||||
| 
 | ||||
| 
 | ||||
| #ifndef FM_MOUNTOPERATIONPASSWORDDIALOG_H | ||||
| #define FM_MOUNTOPERATIONPASSWORDDIALOG_H | ||||
| 
 | ||||
| #include "libfmqtglobals.h" | ||||
| #include <QDialog> | ||||
| #include <gio/gio.h> | ||||
| 
 | ||||
| namespace Ui { | ||||
|   class MountOperationPasswordDialog; | ||||
| }; | ||||
| 
 | ||||
| namespace Fm { | ||||
|    | ||||
| class MountOperation; | ||||
| 
 | ||||
| class MountOperationPasswordDialog : public QDialog { | ||||
| Q_OBJECT | ||||
| 
 | ||||
| public: | ||||
|   explicit MountOperationPasswordDialog(MountOperation* op, GAskPasswordFlags flags); | ||||
|   virtual ~MountOperationPasswordDialog(); | ||||
| 
 | ||||
|   void setMessage(QString message); | ||||
|   void setDefaultUser(QString user); | ||||
|   void setDefaultDomain(QString domain); | ||||
| 
 | ||||
|   virtual void done(int r); | ||||
| 
 | ||||
| private Q_SLOTS: | ||||
|   void onAnonymousToggled(bool checked); | ||||
|    | ||||
| private: | ||||
|   Ui::MountOperationPasswordDialog* ui; | ||||
|   MountOperation* mountOperation; | ||||
|   bool needPassword; | ||||
|   bool needUserName; | ||||
|   bool needDomain; | ||||
|   bool canSavePassword; | ||||
|   bool canAnonymous; | ||||
| }; | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| #endif // FM_MOUNTOPERATIONPASSWORDDIALOG_H
 | ||||
							
								
								
									
										71
									
								
								libfm-qt/mountoperationquestiondialog.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										71
									
								
								libfm-qt/mountoperationquestiondialog.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,71 @@ | ||||
| /*
 | ||||
| 
 | ||||
|     Copyright (C) 2013  Hong Jen Yee (PCMan) <pcman.tw@gmail.com> | ||||
| 
 | ||||
|     This program is free software; you can redistribute it and/or modify | ||||
|     it under the terms of the GNU General Public License as published by | ||||
|     the Free Software Foundation; either version 2 of the License, or | ||||
|     (at your option) any later version. | ||||
| 
 | ||||
|     This program is distributed in the hope that it will be useful, | ||||
|     but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|     GNU General Public License for more details. | ||||
| 
 | ||||
|     You should have received a copy of the GNU General Public License along | ||||
|     with this program; if not, write to the Free Software Foundation, Inc., | ||||
|     51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||||
| */ | ||||
| 
 | ||||
| 
 | ||||
| #include "mountoperationquestiondialog_p.h" | ||||
| #include "mountoperation.h" | ||||
| #include <QPushButton> | ||||
| 
 | ||||
| namespace Fm { | ||||
| 
 | ||||
| MountOperationQuestionDialog::MountOperationQuestionDialog(MountOperation* op, gchar* message, GStrv choices): | ||||
|   QMessageBox(), | ||||
|   mountOperation(op) { | ||||
| 
 | ||||
|   setIcon(QMessageBox::Question); | ||||
|   setText(QString::fromUtf8(message)); | ||||
| 
 | ||||
|   choiceCount = g_strv_length(choices); | ||||
|   choiceButtons = new QAbstractButton*[choiceCount]; | ||||
|   for(int i = 0; i < choiceCount; ++i) { | ||||
|     // It's not allowed to add custom buttons without standard roles
 | ||||
|     // to QMessageBox. So we set role of all buttons to AcceptRole and
 | ||||
|     // handle their clicked() signals in our own slots.
 | ||||
|     // When anyone of the buttons is clicked, exec() always returns "accept".
 | ||||
|     QPushButton* button = new QPushButton(QString::fromUtf8(choices[i])); | ||||
|     addButton(button, QMessageBox::AcceptRole); | ||||
|     choiceButtons[i] = button; | ||||
|   } | ||||
|   connect(this, &MountOperationQuestionDialog::buttonClicked, this, &MountOperationQuestionDialog::onButtonClicked); | ||||
| } | ||||
| 
 | ||||
| MountOperationQuestionDialog::~MountOperationQuestionDialog() { | ||||
|   delete []choiceButtons; | ||||
| } | ||||
| 
 | ||||
| void MountOperationQuestionDialog::done(int r) { | ||||
|   if(r != QDialog::Accepted) { | ||||
|     GMountOperation* op = mountOperation->mountOperation(); | ||||
|     g_mount_operation_reply(op, G_MOUNT_OPERATION_ABORTED); | ||||
|   } | ||||
|   QDialog::done(r); | ||||
| } | ||||
| 
 | ||||
| void MountOperationQuestionDialog::onButtonClicked(QAbstractButton* button) { | ||||
|   GMountOperation* op = mountOperation->mountOperation(); | ||||
|   for(int i = 0; i < choiceCount; ++i) { | ||||
|     if(choiceButtons[i] == button) { | ||||
|       g_mount_operation_set_choice(op, i); | ||||
|       g_mount_operation_reply(op, G_MOUNT_OPERATION_HANDLED); | ||||
|       break; | ||||
|     } | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| } // namespace Fm
 | ||||
							
								
								
									
										51
									
								
								libfm-qt/mountoperationquestiondialog_p.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								libfm-qt/mountoperationquestiondialog_p.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,51 @@ | ||||
| /*
 | ||||
| 
 | ||||
|     Copyright (C) 2013  Hong Jen Yee (PCMan) <pcman.tw@gmail.com> | ||||
| 
 | ||||
|     This program is free software; you can redistribute it and/or modify | ||||
|     it under the terms of the GNU General Public License as published by | ||||
|     the Free Software Foundation; either version 2 of the License, or | ||||
|     (at your option) any later version. | ||||
| 
 | ||||
|     This program is distributed in the hope that it will be useful, | ||||
|     but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|     GNU General Public License for more details. | ||||
| 
 | ||||
|     You should have received a copy of the GNU General Public License along | ||||
|     with this program; if not, write to the Free Software Foundation, Inc., | ||||
|     51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||||
| */ | ||||
| 
 | ||||
| 
 | ||||
| #ifndef FM_MOUNTOPERATIONQUESTIONDIALOG_H | ||||
| #define FM_MOUNTOPERATIONQUESTIONDIALOG_H | ||||
| 
 | ||||
| #include "libfmqtglobals.h" | ||||
| #include <QMessageBox> | ||||
| #include <gio/gio.h> | ||||
| 
 | ||||
| namespace Fm { | ||||
| 
 | ||||
| class MountOperation; | ||||
| 
 | ||||
| class MountOperationQuestionDialog : public QMessageBox { | ||||
| Q_OBJECT | ||||
| public: | ||||
|   MountOperationQuestionDialog(MountOperation* op, gchar* message, GStrv choices); | ||||
|   virtual ~MountOperationQuestionDialog(); | ||||
| 
 | ||||
|   virtual void done(int r); | ||||
| 
 | ||||
| private Q_SLOTS: | ||||
|   void onButtonClicked(QAbstractButton* button); | ||||
|    | ||||
| private: | ||||
|   MountOperation* mountOperation; | ||||
|   QAbstractButton** choiceButtons; | ||||
|   int choiceCount; | ||||
| }; | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| #endif // FM_MOUNTOPERATIONQUESTIONDIALOG_H
 | ||||
							
								
								
									
										22
									
								
								libfm-qt/path.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								libfm-qt/path.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,22 @@ | ||||
| /*
 | ||||
|  * Copyright (C) 2014  Hong Jen Yee (PCMan) <pcman.tw@gmail.com> | ||||
|  * | ||||
|  * This program is free software; you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU General Public License as published by | ||||
|  * the Free Software Foundation; either version 2 of the License, or | ||||
|  * (at your option) any later version. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU General Public License along | ||||
|  * with this program; if not, write to the Free Software Foundation, Inc., | ||||
|  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||||
|  * | ||||
|  */ | ||||
| 
 | ||||
| #include "path.h" | ||||
| 
 | ||||
| using namespace Fm; | ||||
							
								
								
									
										241
									
								
								libfm-qt/path.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										241
									
								
								libfm-qt/path.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,241 @@ | ||||
| /*
 | ||||
|  * Copyright (C) 2014  Hong Jen Yee (PCMan) <pcman.tw@gmail.com> | ||||
|  * | ||||
|  * This program is free software; you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU General Public License as published by | ||||
|  * the Free Software Foundation; either version 2 of the License, or | ||||
|  * (at your option) any later version. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU General Public License along | ||||
|  * with this program; if not, write to the Free Software Foundation, Inc., | ||||
|  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||||
|  * | ||||
|  */ | ||||
| 
 | ||||
| #ifndef FM_PATH_H | ||||
| #define FM_PATH_H | ||||
| 
 | ||||
| #include "libfmqtglobals.h" | ||||
| #include <libfm/fm.h> | ||||
| #include <QString> | ||||
| #include <QMetaType> | ||||
| 
 | ||||
| namespace Fm { | ||||
| 
 | ||||
| class LIBFM_QT_API Path { | ||||
| public: | ||||
|    | ||||
|   Path(): data_(NULL) { | ||||
|   } | ||||
| 
 | ||||
|   Path(FmPath* path, bool takeOwnership = false): data_(path) { | ||||
|     if(path && !takeOwnership) | ||||
|       fm_path_ref(data_); | ||||
|   } | ||||
| 
 | ||||
|   Path(const Path& other): data_(other.data_ ? fm_path_ref(other.data_) : NULL) { | ||||
|   } | ||||
| 
 | ||||
|   Path(GFile* gf): data_(fm_path_new_for_gfile(gf)) { | ||||
|   } | ||||
| 
 | ||||
|   ~Path() { | ||||
|     if(data_) | ||||
|       fm_path_unref(data_); | ||||
|   } | ||||
| 
 | ||||
|   static Path fromPathName(const char* path_name) { | ||||
|     return Path(fm_path_new_for_path(path_name), true); | ||||
|   } | ||||
| 
 | ||||
|   static Path fromUri(const char* uri) { | ||||
|     return Path(fm_path_new_for_uri(uri), true); | ||||
|   } | ||||
| 
 | ||||
|   static Path fromDisplayName(const char* path_name) { | ||||
|     return Path(fm_path_new_for_display_name(path_name), true); | ||||
|   } | ||||
| 
 | ||||
|   static Path fromString(const char* path_str) { | ||||
|     return Path(fm_path_new_for_str(path_str), true); | ||||
|   } | ||||
| 
 | ||||
|   static Path fromCommandlineArg(const char* arg) { | ||||
|     return Path(fm_path_new_for_commandline_arg(arg), true); | ||||
|   } | ||||
| 
 | ||||
|   Path child(const char* basename) { | ||||
|     return Path(fm_path_new_child(data_, basename), true); | ||||
|   } | ||||
| 
 | ||||
|   Path child(const char* basename, int name_len) { | ||||
|     return Path(fm_path_new_child_len(data_, basename, name_len), true); | ||||
|   } | ||||
| 
 | ||||
|   Path relative(const char* rel) { | ||||
|     return Path(fm_path_new_relative(data_, rel), true); | ||||
|   } | ||||
| 
 | ||||
|   /* predefined paths */ | ||||
|   static Path root(void) { /* / */ | ||||
|     return Path(fm_path_get_root(), false); | ||||
|   } | ||||
| 
 | ||||
|   static Path home(void) { /* home directory */ | ||||
|     return Path(fm_path_get_home(), false); | ||||
|   } | ||||
| 
 | ||||
|   static Path desktop(void) { /* $HOME/Desktop */ | ||||
|     return Path(fm_path_get_desktop(), false); | ||||
|   } | ||||
| 
 | ||||
|   static Path trash(void) { /* trash:/// */ | ||||
|     return Path(fm_path_get_trash(), false); | ||||
|   } | ||||
| 
 | ||||
|   static Path appsMenu(void) { /* menu://applications.menu/ */ | ||||
|     return Path(fm_path_get_apps_menu(), false); | ||||
|   } | ||||
| 
 | ||||
|   Path parent() { | ||||
|     return Path(fm_path_get_parent(data_), false); | ||||
|   } | ||||
| 
 | ||||
|   const char* basename() { | ||||
|     return fm_path_get_basename(data_); | ||||
|   } | ||||
| 
 | ||||
|   FmPathFlags flags() { | ||||
|     return fm_path_get_flags(data_); | ||||
|   } | ||||
| 
 | ||||
|   bool hasPrefix(FmPath* prefix) { | ||||
|     return fm_path_has_prefix(data_, prefix); | ||||
|   } | ||||
| 
 | ||||
|   Path schemePath() { | ||||
|     return Path(fm_path_get_scheme_path(data_), true); | ||||
|   } | ||||
| 
 | ||||
|   bool isNative() { | ||||
|     return fm_path_is_native(data_); | ||||
|   } | ||||
| 
 | ||||
|   bool isTrash() { | ||||
|     return fm_path_is_trash(data_); | ||||
|   } | ||||
|    | ||||
|   bool isTrashRoot() { | ||||
|     return fm_path_is_trash_root(data_); | ||||
|   } | ||||
| 
 | ||||
|   bool isNativeOrTrash() { | ||||
|     return fm_path_is_native_or_trash(data_); | ||||
|   } | ||||
| 
 | ||||
|   char* toString() { | ||||
|     return fm_path_to_str(data_); | ||||
|   } | ||||
| 
 | ||||
|   QByteArray toByteArray() { | ||||
|     char* s = fm_path_to_str(data_); | ||||
|     QByteArray str(s); | ||||
|     g_free(s); | ||||
|     return str; | ||||
|   } | ||||
| 
 | ||||
|   char* toUri() { | ||||
|     return fm_path_to_uri(data_); | ||||
|   } | ||||
| 
 | ||||
|   GFile* toGfile() { | ||||
|     return fm_path_to_gfile(data_); | ||||
|   } | ||||
| 
 | ||||
|   /*
 | ||||
|   char* displayName(bool human_readable = true) { | ||||
|     return fm_path_display_name(data_, human_readable); | ||||
|   } | ||||
|   */ | ||||
| 
 | ||||
|   QString displayName(bool human_readable = true) { | ||||
|     char* dispname = fm_path_display_name(data_, human_readable); | ||||
|     QString str = QString::fromUtf8(dispname); | ||||
|     g_free(dispname); | ||||
|     return str; | ||||
|   } | ||||
|    | ||||
|   /*
 | ||||
|   char* displayBasename() { | ||||
|     return fm_path_display_basename(data_); | ||||
|   } | ||||
|   */ | ||||
|    | ||||
|   QString displayBasename() { | ||||
|     char* basename = fm_path_display_basename(data_); | ||||
|     QString s = QString::fromUtf8(basename); | ||||
|     g_free(basename); | ||||
|     return s; | ||||
|   } | ||||
| 
 | ||||
|   /* For used in hash tables */ | ||||
|   guint hash() { | ||||
|     return fm_path_hash(data_); | ||||
|   } | ||||
| 
 | ||||
|   Path& operator = (const Path& other) { | ||||
|     if(data_) | ||||
|       fm_path_unref(data_); | ||||
|     data_ = fm_path_ref(other.data_); | ||||
|     return *this; | ||||
|   } | ||||
| 
 | ||||
|   bool operator == (const Path& other) const { | ||||
|     return fm_path_equal(data_, other.data_); | ||||
|   } | ||||
| 
 | ||||
|   bool operator != (const Path& other) const { | ||||
|     return !fm_path_equal(data_, other.data_); | ||||
|   } | ||||
| 
 | ||||
|   bool operator < (const Path& other) const { | ||||
|     return compare(other); | ||||
|   } | ||||
| 
 | ||||
|   bool operator > (const Path& other) const { | ||||
|     return (other < *this); | ||||
|   } | ||||
| 
 | ||||
|   /* can be used for sorting */ | ||||
|   int compare(const Path& other) const { | ||||
|     return fm_path_compare(data_, other.data_); | ||||
|   } | ||||
| 
 | ||||
|   /* used for completion in entry */ | ||||
|   bool equal(const gchar *str, int n) const { | ||||
|     return fm_path_equal_str(data_, str, n); | ||||
|   } | ||||
| 
 | ||||
|   /* calculate how many elements are in this path. */ | ||||
|   int depth() const { | ||||
|     return fm_path_depth(data_); | ||||
|   } | ||||
| 
 | ||||
|   FmPath* data() const { | ||||
|     return data_; | ||||
|   } | ||||
| 
 | ||||
| private: | ||||
|   FmPath* data_; | ||||
| }; | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| Q_DECLARE_OPAQUE_POINTER(FmPath*) | ||||
| 
 | ||||
| #endif // FM_PATH_H
 | ||||
							
								
								
									
										195
									
								
								libfm-qt/pathedit.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										195
									
								
								libfm-qt/pathedit.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,195 @@ | ||||
| /*
 | ||||
| 
 | ||||
|     Copyright (C) 2013  Hong Jen Yee (PCMan) <pcman.tw@gmail.com> | ||||
| 
 | ||||
|     This program is free software; you can redistribute it and/or modify | ||||
|     it under the terms of the GNU General Public License as published by | ||||
|     the Free Software Foundation; either version 2 of the License, or | ||||
|     (at your option) any later version. | ||||
| 
 | ||||
|     This program is distributed in the hope that it will be useful, | ||||
|     but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|     GNU General Public License for more details. | ||||
| 
 | ||||
|     You should have received a copy of the GNU General Public License along | ||||
|     with this program; if not, write to the Free Software Foundation, Inc., | ||||
|     51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||||
| */ | ||||
| 
 | ||||
| 
 | ||||
| #include "pathedit.h" | ||||
| #include <QCompleter> | ||||
| #include <QStringListModel> | ||||
| #include <QStringBuilder> | ||||
| #include <QDebug> | ||||
| #include <libfm/fm.h> | ||||
| 
 | ||||
| namespace Fm { | ||||
| 
 | ||||
| PathEdit::PathEdit(QWidget* parent): | ||||
|   QLineEdit(parent), | ||||
|   cancellable_(NULL), | ||||
|   model_(new QStringListModel()), | ||||
|   completer_(new QCompleter()) { | ||||
|   setCompleter(completer_); | ||||
|   completer_->setModel(model_); | ||||
|   connect(this, &PathEdit::textChanged, this, &PathEdit::onTextChanged); | ||||
| } | ||||
| 
 | ||||
| PathEdit::~PathEdit() { | ||||
|   delete completer_; | ||||
|   if(model_) | ||||
|     delete model_; | ||||
|   if(cancellable_) { | ||||
|     g_cancellable_cancel(cancellable_); | ||||
|     g_object_unref(cancellable_); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| void PathEdit::focusInEvent(QFocusEvent* e) { | ||||
|   QLineEdit::focusInEvent(e); | ||||
|   // build the completion list only when we have the keyboard focus
 | ||||
|   reloadCompleter(true); | ||||
| } | ||||
| 
 | ||||
| void PathEdit::focusOutEvent(QFocusEvent* e) { | ||||
|   QLineEdit::focusOutEvent(e); | ||||
|   // free the completion list since we don't need it anymore
 | ||||
|   freeCompleter(); | ||||
| } | ||||
| 
 | ||||
| void PathEdit::onTextChanged(const QString& text) { | ||||
|   int pos = text.lastIndexOf('/'); | ||||
|   if(pos >= 0) | ||||
|     ++pos; | ||||
|   else | ||||
|     pos = text.length(); | ||||
|   QString newPrefix = text.left(pos); | ||||
|   if(currentPrefix_ != newPrefix) { | ||||
|     currentPrefix_ = newPrefix; | ||||
|     // only build the completion list if we have the keyboard focus
 | ||||
|     // if we don't have the focus now, then we'll rebuild the completion list
 | ||||
|     // when focusInEvent happens. this avoid unnecessary dir loading.
 | ||||
|     if(hasFocus()) | ||||
|       reloadCompleter(false); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| struct JobData { | ||||
|   GCancellable* cancellable; | ||||
|   GFile* dirName; | ||||
|   QStringList subDirs; | ||||
|   PathEdit* edit; | ||||
|   bool triggeredByFocusInEvent; | ||||
| 
 | ||||
|   ~JobData() { | ||||
|     g_object_unref(dirName); | ||||
|     g_object_unref(cancellable); | ||||
|   } | ||||
| 
 | ||||
|   static void freeMe(JobData* data) { | ||||
|     delete data; | ||||
|   } | ||||
| }; | ||||
| 
 | ||||
| void PathEdit::reloadCompleter(bool triggeredByFocusInEvent) { | ||||
|   // parent dir has been changed, reload dir list
 | ||||
|   // if(currentPrefix_[0] == "~") { // special case for home dir
 | ||||
|   // cancel running dir-listing jobs, if there's any
 | ||||
|   if(cancellable_) { | ||||
|     g_cancellable_cancel(cancellable_); | ||||
|     g_object_unref(cancellable_); | ||||
|   } | ||||
|   // launch a new job to do dir listing
 | ||||
|   JobData* data = new JobData(); | ||||
|   data->edit = this; | ||||
|   data->triggeredByFocusInEvent = triggeredByFocusInEvent; | ||||
|   // need to use fm_file_new_for_commandline_arg() rather than g_file_new_for_commandline_arg().
 | ||||
|   // otherwise, our own vfs, such as menu://, won't be loaded.
 | ||||
|   data->dirName = fm_file_new_for_commandline_arg(currentPrefix_.toLocal8Bit().constData()); | ||||
|   // qDebug("load: %s", g_file_get_uri(data->dirName));
 | ||||
|   cancellable_ = g_cancellable_new(); | ||||
|   data->cancellable = (GCancellable*)g_object_ref(cancellable_); | ||||
|   g_io_scheduler_push_job((GIOSchedulerJobFunc)jobFunc, | ||||
|                           data, (GDestroyNotify)JobData::freeMe, | ||||
|                           G_PRIORITY_LOW, cancellable_); | ||||
| } | ||||
| 
 | ||||
| void PathEdit::freeCompleter() { | ||||
|   if(cancellable_) { | ||||
|     g_cancellable_cancel(cancellable_); | ||||
|     g_object_unref(cancellable_); | ||||
|     cancellable_ = NULL; | ||||
|   } | ||||
|   model_->setStringList(QStringList()); | ||||
| } | ||||
| 
 | ||||
| gboolean PathEdit::jobFunc(GIOSchedulerJob* job, GCancellable* cancellable, gpointer user_data) { | ||||
|   JobData* data = reinterpret_cast<JobData*>(user_data); | ||||
|   GError *err = NULL; | ||||
|   GFileEnumerator* enu = g_file_enumerate_children(data->dirName, | ||||
|                                                    // G_FILE_ATTRIBUTE_STANDARD_NAME","
 | ||||
|                                                    G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME"," | ||||
|                                                    G_FILE_ATTRIBUTE_STANDARD_TYPE, | ||||
|                                                    G_FILE_QUERY_INFO_NONE, cancellable, | ||||
|                                                    &err); | ||||
|   if(enu) { | ||||
|     while(!g_cancellable_is_cancelled(cancellable)) { | ||||
|       GFileInfo* inf = g_file_enumerator_next_file(enu, cancellable, &err); | ||||
|       if(inf) { | ||||
|         GFileType type = g_file_info_get_file_type(inf); | ||||
|         if(type == G_FILE_TYPE_DIRECTORY) { | ||||
|           const char* name = g_file_info_get_display_name(inf); | ||||
|           // FIXME: encoding conversion here?
 | ||||
|           data->subDirs.append(QString::fromUtf8(name)); | ||||
|         } | ||||
|         g_object_unref(inf); | ||||
|       } | ||||
|       else { | ||||
|         if(err) { | ||||
|           g_error_free(err); | ||||
|           err = NULL; | ||||
|         } | ||||
|         else /* EOF */ | ||||
|           break; | ||||
|       } | ||||
|     } | ||||
|     g_file_enumerator_close(enu, cancellable, NULL); | ||||
|     g_object_unref(enu); | ||||
|   } | ||||
|   // finished! let's update the UI in the main thread
 | ||||
|   g_io_scheduler_job_send_to_mainloop(job, _onJobFinished, data, NULL); | ||||
|   return FALSE; | ||||
| } | ||||
| 
 | ||||
| // static
 | ||||
| gboolean PathEdit::_onJobFinished(gpointer user_data) { | ||||
|   JobData* data = reinterpret_cast<JobData*>(user_data); | ||||
|   data->edit->onJobFinished(data); | ||||
|   return TRUE; | ||||
| } | ||||
| 
 | ||||
| // This callback function is called from main thread so it's safe to access the GUI
 | ||||
| void PathEdit::onJobFinished(JobData* data) { | ||||
|   if(!g_cancellable_is_cancelled(data->cancellable)) { | ||||
|     // update the completer only if the job is not cancelled
 | ||||
|     QStringList::iterator it; | ||||
|     for(it = data->subDirs.begin(); it != data->subDirs.end(); ++it) { | ||||
|       // qDebug("%s", it->toUtf8().constData());
 | ||||
|       *it = (currentPrefix_ % *it); | ||||
|     } | ||||
|     model_->setStringList(data->subDirs); | ||||
|     // trigger completion manually
 | ||||
|     if(hasFocus() && !data->triggeredByFocusInEvent) | ||||
|       completer_->complete(); | ||||
|   } | ||||
|   else | ||||
|     model_->setStringList(QStringList()); | ||||
|   if(cancellable_) { | ||||
|     g_object_unref(cancellable_); | ||||
|     cancellable_ = NULL; | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| } // namespace Fm
 | ||||
							
								
								
									
										64
									
								
								libfm-qt/pathedit.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										64
									
								
								libfm-qt/pathedit.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,64 @@ | ||||
| /*
 | ||||
| 
 | ||||
|     Copyright (C) 2013  Hong Jen Yee (PCMan) <pcman.tw@gmail.com> | ||||
| 
 | ||||
|     This program is free software; you can redistribute it and/or modify | ||||
|     it under the terms of the GNU General Public License as published by | ||||
|     the Free Software Foundation; either version 2 of the License, or | ||||
|     (at your option) any later version. | ||||
| 
 | ||||
|     This program is distributed in the hope that it will be useful, | ||||
|     but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|     GNU General Public License for more details. | ||||
| 
 | ||||
|     You should have received a copy of the GNU General Public License along | ||||
|     with this program; if not, write to the Free Software Foundation, Inc., | ||||
|     51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||||
| */ | ||||
| 
 | ||||
| 
 | ||||
| #ifndef FM_PATHEDIT_H | ||||
| #define FM_PATHEDIT_H | ||||
| 
 | ||||
| #include "libfmqtglobals.h" | ||||
| #include <QLineEdit> | ||||
| #include <gio/gio.h> | ||||
| 
 | ||||
| class QCompleter; | ||||
| class QStringListModel; | ||||
| 
 | ||||
| namespace Fm { | ||||
| 
 | ||||
| struct JobData; | ||||
| 
 | ||||
| class LIBFM_QT_API PathEdit : public QLineEdit { | ||||
| Q_OBJECT | ||||
| public: | ||||
|   explicit PathEdit(QWidget* parent = 0); | ||||
|   virtual ~PathEdit(); | ||||
| 
 | ||||
| protected: | ||||
|   virtual void focusInEvent(QFocusEvent* e); | ||||
|   virtual void focusOutEvent(QFocusEvent* e); | ||||
| 
 | ||||
| private Q_SLOTS: | ||||
|   void onTextChanged(const QString & text); | ||||
| 
 | ||||
| private: | ||||
|   void reloadCompleter(bool triggeredByFocusInEvent = false); | ||||
|   void freeCompleter(); | ||||
|   static gboolean jobFunc(GIOSchedulerJob *job, GCancellable *cancellable, gpointer user_data); | ||||
|   static gboolean _onJobFinished(gpointer user_data); | ||||
|   void onJobFinished(JobData* data); | ||||
| 
 | ||||
| private: | ||||
|   QCompleter* completer_; | ||||
|   QStringListModel* model_; | ||||
|   QString currentPrefix_; | ||||
|   GCancellable* cancellable_; | ||||
| }; | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| #endif // FM_PATHEDIT_H
 | ||||
							
								
								
									
										519
									
								
								libfm-qt/placesmodel.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										519
									
								
								libfm-qt/placesmodel.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,519 @@ | ||||
| /*
 | ||||
| 
 | ||||
|     Copyright (C) 2013  Hong Jen Yee (PCMan) <pcman.tw@gmail.com> | ||||
| 
 | ||||
|     This program is free software; you can redistribute it and/or modify | ||||
|     it under the terms of the GNU General Public License as published by | ||||
|     the Free Software Foundation; either version 2 of the License, or | ||||
|     (at your option) any later version. | ||||
| 
 | ||||
|     This program is distributed in the hope that it will be useful, | ||||
|     but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|     GNU General Public License for more details. | ||||
| 
 | ||||
|     You should have received a copy of the GNU General Public License along | ||||
|     with this program; if not, write to the Free Software Foundation, Inc., | ||||
|     51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||||
| */ | ||||
| 
 | ||||
| 
 | ||||
| #include "placesmodel.h" | ||||
| #include "icontheme.h" | ||||
| #include <gio/gio.h> | ||||
| #include <QDebug> | ||||
| #include <QMimeData> | ||||
| #include <QTimer> | ||||
| #include "utilities.h" | ||||
| #include "placesmodelitem.h" | ||||
| 
 | ||||
| using namespace Fm; | ||||
| 
 | ||||
| PlacesModel::PlacesModel(QObject* parent): | ||||
|   QStandardItemModel(parent), | ||||
|   showApplications_(true), | ||||
|   showDesktop_(true), | ||||
|   ejectIcon_(QIcon::fromTheme("media-eject")) { | ||||
| 
 | ||||
|   setColumnCount(2); | ||||
| 
 | ||||
|   PlacesModelItem* item; | ||||
|   placesRoot = new QStandardItem(tr("Places")); | ||||
|   placesRoot->setSelectable(false); | ||||
|   placesRoot->setColumnCount(2); | ||||
|   appendRow(placesRoot); | ||||
| 
 | ||||
|   homeItem = new PlacesModelItem("user-home", g_get_user_name(), fm_path_get_home()); | ||||
|   placesRoot->appendRow(homeItem); | ||||
| 
 | ||||
|   desktopItem = new PlacesModelItem("user-desktop", tr("Desktop"), fm_path_get_desktop()); | ||||
|   placesRoot->appendRow(desktopItem); | ||||
| 
 | ||||
|   createTrashItem(); | ||||
| 
 | ||||
|   FmPath* path; | ||||
|   if(isUriSchemeSupported("computer")) { | ||||
|     path = fm_path_new_for_uri("computer:///"); | ||||
|     computerItem = new PlacesModelItem("computer", tr("Computer"), path); | ||||
|     fm_path_unref(path); | ||||
|     placesRoot->appendRow(computerItem); | ||||
|   } | ||||
|   else | ||||
|     computerItem = NULL; | ||||
| 
 | ||||
|   const char* applicaion_icon_names[] = {"system-software-install", "applications-accessories", "application-x-executable"}; | ||||
|   // NOTE: g_themed_icon_new_from_names() accepts char**, but actually const char** is OK.
 | ||||
|   GIcon* gicon = g_themed_icon_new_from_names((char**)applicaion_icon_names, G_N_ELEMENTS(applicaion_icon_names)); | ||||
|   FmIcon* fmicon = fm_icon_from_gicon(gicon); | ||||
|   g_object_unref(gicon); | ||||
|   applicationsItem = new PlacesModelItem(fmicon, tr("Applications"), fm_path_get_apps_menu()); | ||||
|   fm_icon_unref(fmicon); | ||||
|   placesRoot->appendRow(applicationsItem); | ||||
| 
 | ||||
|   if(isUriSchemeSupported("network")) { | ||||
|     const char* network_icon_names[] = {"network", "folder-network", "folder"}; | ||||
|     // NOTE: g_themed_icon_new_from_names() accepts char**, but actually const char** is OK.
 | ||||
|     gicon = g_themed_icon_new_from_names((char**)network_icon_names, G_N_ELEMENTS(network_icon_names)); | ||||
|     fmicon = fm_icon_from_gicon(gicon); | ||||
|     g_object_unref(gicon); | ||||
|     path = fm_path_new_for_uri("network:///"); | ||||
|     networkItem = new PlacesModelItem(fmicon, tr("Network"), path); | ||||
|     fm_icon_unref(fmicon); | ||||
|     fm_path_unref(path); | ||||
|     placesRoot->appendRow(networkItem); | ||||
|   } | ||||
|   else | ||||
|     networkItem = NULL; | ||||
| 
 | ||||
|   devicesRoot = new QStandardItem(tr("Devices")); | ||||
|   devicesRoot->setSelectable(false); | ||||
|   devicesRoot->setColumnCount(2); | ||||
|   appendRow(devicesRoot); | ||||
| 
 | ||||
|   // volumes
 | ||||
|   volumeMonitor = g_volume_monitor_get(); | ||||
|   if(volumeMonitor) { | ||||
|     g_signal_connect(volumeMonitor, "volume-added", G_CALLBACK(onVolumeAdded), this); | ||||
|     g_signal_connect(volumeMonitor, "volume-removed", G_CALLBACK(onVolumeRemoved), this); | ||||
|     g_signal_connect(volumeMonitor, "volume-changed", G_CALLBACK(onVolumeChanged), this); | ||||
|     g_signal_connect(volumeMonitor, "mount-added", G_CALLBACK(onMountAdded), this); | ||||
|     g_signal_connect(volumeMonitor, "mount-changed", G_CALLBACK(onMountChanged), this); | ||||
|     g_signal_connect(volumeMonitor, "mount-removed", G_CALLBACK(onMountRemoved), this); | ||||
| 
 | ||||
|     // add volumes to side-pane
 | ||||
|     GList* vols = g_volume_monitor_get_volumes(volumeMonitor); | ||||
|     GList* l; | ||||
|     for(l = vols; l; l = l->next) { | ||||
|         GVolume* volume = G_VOLUME(l->data); | ||||
|         onVolumeAdded(volumeMonitor, volume, this); | ||||
|         g_object_unref(volume); | ||||
|     } | ||||
|     g_list_free(vols); | ||||
| 
 | ||||
|     /* add mounts to side-pane */ | ||||
|     vols = g_volume_monitor_get_mounts(volumeMonitor); | ||||
|     for(l = vols; l; l = l->next) { | ||||
|         GMount* mount = G_MOUNT(l->data); | ||||
|         GVolume* volume = g_mount_get_volume(mount); | ||||
|         if(volume) | ||||
|         g_object_unref(volume); | ||||
|         else { /* network mounts or others */ | ||||
|         item = new PlacesModelMountItem(mount); | ||||
|         devicesRoot->appendRow(item); | ||||
|         } | ||||
|         g_object_unref(mount); | ||||
|     } | ||||
|     g_list_free(vols); | ||||
|   } | ||||
| 
 | ||||
|   // bookmarks
 | ||||
|   bookmarksRoot = new QStandardItem(tr("Bookmarks")); | ||||
|   bookmarksRoot->setSelectable(false); | ||||
|   bookmarksRoot->setColumnCount(2); | ||||
|   appendRow(bookmarksRoot); | ||||
| 
 | ||||
|   bookmarks = fm_bookmarks_dup(); | ||||
|   loadBookmarks(); | ||||
|   g_signal_connect(bookmarks, "changed", G_CALLBACK(onBookmarksChanged), this); | ||||
| 
 | ||||
|   // update some icons when the icon theme is changed
 | ||||
|   connect(IconTheme::instance(), &IconTheme::changed, this, &PlacesModel::updateIcons); | ||||
| } | ||||
| 
 | ||||
| void PlacesModel::loadBookmarks() { | ||||
|   GList* allBookmarks = fm_bookmarks_get_all(bookmarks); | ||||
|   for(GList* l = allBookmarks; l; l = l->next) { | ||||
|     FmBookmarkItem* bm_item = (FmBookmarkItem*)l->data; | ||||
|     PlacesModelBookmarkItem* item = new PlacesModelBookmarkItem(bm_item); | ||||
|     bookmarksRoot->appendRow(item); | ||||
|   } | ||||
|   g_list_free_full(allBookmarks, (GDestroyNotify)fm_bookmark_item_unref); | ||||
| } | ||||
| 
 | ||||
| PlacesModel::~PlacesModel() { | ||||
|   if(bookmarks) { | ||||
|     g_signal_handlers_disconnect_by_func(bookmarks, (gpointer)onBookmarksChanged, this); | ||||
|     g_object_unref(bookmarks); | ||||
|   } | ||||
|   if(volumeMonitor) { | ||||
|     g_signal_handlers_disconnect_by_func(volumeMonitor, (gpointer)G_CALLBACK(onVolumeAdded), this); | ||||
|     g_signal_handlers_disconnect_by_func(volumeMonitor, (gpointer)G_CALLBACK(onVolumeRemoved), this); | ||||
|     g_signal_handlers_disconnect_by_func(volumeMonitor, (gpointer)G_CALLBACK(onVolumeChanged), this); | ||||
|     g_signal_handlers_disconnect_by_func(volumeMonitor, (gpointer)G_CALLBACK(onMountAdded), this); | ||||
|     g_signal_handlers_disconnect_by_func(volumeMonitor, (gpointer)G_CALLBACK(onMountChanged), this); | ||||
|     g_signal_handlers_disconnect_by_func(volumeMonitor, (gpointer)G_CALLBACK(onMountRemoved), this); | ||||
|     g_object_unref(volumeMonitor); | ||||
|   } | ||||
|   if(trashMonitor_) { | ||||
|     g_signal_handlers_disconnect_by_func(trashMonitor_, (gpointer)G_CALLBACK(onTrashChanged), this); | ||||
|     g_object_unref(trashMonitor_); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| // static
 | ||||
| void PlacesModel::onTrashChanged(GFileMonitor* monitor, GFile* gf, GFile* other, GFileMonitorEvent evt, PlacesModel* pThis) { | ||||
|   QTimer::singleShot(0, pThis, SLOT(updateTrash())); | ||||
| } | ||||
| 
 | ||||
| void PlacesModel::updateTrash() { | ||||
|   if(trashItem_) { | ||||
|     GFile* gf = fm_file_new_for_uri("trash:///"); | ||||
|     GFileInfo* inf = g_file_query_info(gf, G_FILE_ATTRIBUTE_TRASH_ITEM_COUNT, G_FILE_QUERY_INFO_NONE, NULL, NULL); | ||||
|     g_object_unref(gf); | ||||
|     if(inf) { | ||||
|       guint32 n = g_file_info_get_attribute_uint32(inf, G_FILE_ATTRIBUTE_TRASH_ITEM_COUNT); | ||||
|       g_object_unref(inf); | ||||
|       const char* icon_name = n > 0 ? "user-trash-full" : "user-trash"; | ||||
|       FmIcon* icon = fm_icon_from_name(icon_name); | ||||
|       trashItem_->setIcon(icon); | ||||
|       fm_icon_unref(icon); | ||||
|     } | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| void PlacesModel::createTrashItem() { | ||||
|   GFile* gf; | ||||
|   gf = fm_file_new_for_uri("trash:///"); | ||||
|   // check if trash is supported by the current vfs
 | ||||
|   // if gvfs is not installed, this can be unavailable.
 | ||||
|   if(!g_file_query_exists(gf, NULL)) { | ||||
|     g_object_unref(gf); | ||||
|     trashItem_ = NULL; | ||||
|     trashMonitor_ = NULL; | ||||
|     return; | ||||
|   } | ||||
|   trashItem_ = new PlacesModelItem("user-trash", tr("Trash"), fm_path_get_trash()); | ||||
| 
 | ||||
|   trashMonitor_ = fm_monitor_directory(gf, NULL); | ||||
|   if(trashMonitor_) | ||||
|     g_signal_connect(trashMonitor_, "changed", G_CALLBACK(onTrashChanged), this); | ||||
|   g_object_unref(gf); | ||||
| 
 | ||||
|   placesRoot->insertRow(desktopItem->row() + 1, trashItem_); | ||||
|   QTimer::singleShot(0, this, SLOT(updateTrash())); | ||||
| } | ||||
| 
 | ||||
| void PlacesModel::setShowApplications(bool show) { | ||||
|   if(showApplications_ != show) { | ||||
|     showApplications_ = show; | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| void PlacesModel::setShowDesktop(bool show) { | ||||
|   if(showDesktop_ != show) { | ||||
|     showDesktop_ = show; | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| void PlacesModel::setShowTrash(bool show) { | ||||
|   if(show) { | ||||
|     if(!trashItem_) | ||||
|       createTrashItem(); | ||||
|   } | ||||
|   else { | ||||
|     if(trashItem_) { | ||||
|       if(trashMonitor_) { | ||||
|         g_signal_handlers_disconnect_by_func(trashMonitor_, (gpointer)G_CALLBACK(onTrashChanged), this); | ||||
|         g_object_unref(trashMonitor_); | ||||
|         trashMonitor_ = NULL; | ||||
|       } | ||||
|       placesRoot->removeRow(trashItem_->row()); // delete trashItem_;
 | ||||
|       trashItem_ = NULL; | ||||
|     } | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| PlacesModelItem* PlacesModel::itemFromPath(FmPath* path) { | ||||
|   PlacesModelItem* item = itemFromPath(placesRoot, path); | ||||
|   if(!item) | ||||
|     item = itemFromPath(devicesRoot, path); | ||||
|   if(!item) | ||||
|     item = itemFromPath(bookmarksRoot, path); | ||||
|   return item; | ||||
| } | ||||
| 
 | ||||
| PlacesModelItem* PlacesModel::itemFromPath(QStandardItem* rootItem, FmPath* path) { | ||||
|   int rowCount = rootItem->rowCount(); | ||||
|   for(int i = 0; i < rowCount; ++i) { | ||||
|     PlacesModelItem* item = static_cast<PlacesModelItem*>(rootItem->child(i, 0)); | ||||
|     if(fm_path_equal(item->path(), path)) | ||||
|       return item; | ||||
|   } | ||||
|   return NULL; | ||||
| } | ||||
| 
 | ||||
| PlacesModelVolumeItem* PlacesModel::itemFromVolume(GVolume* volume) { | ||||
|   int rowCount = devicesRoot->rowCount(); | ||||
|   for(int i = 0; i < rowCount; ++i) { | ||||
|     PlacesModelItem* item = static_cast<PlacesModelItem*>(devicesRoot->child(i, 0)); | ||||
|     if(item->type() == PlacesModelItem::Volume) { | ||||
|       PlacesModelVolumeItem* volumeItem = static_cast<PlacesModelVolumeItem*>(item); | ||||
|       if(volumeItem->volume() == volume) | ||||
|         return volumeItem; | ||||
|     } | ||||
|   } | ||||
|   return NULL; | ||||
| } | ||||
| 
 | ||||
| PlacesModelMountItem* PlacesModel::itemFromMount(GMount* mount) { | ||||
|   int rowCount = devicesRoot->rowCount(); | ||||
|   for(int i = 0; i < rowCount; ++i) { | ||||
|     PlacesModelItem* item = static_cast<PlacesModelItem*>(devicesRoot->child(i, 0)); | ||||
|     if(item->type() == PlacesModelItem::Mount) { | ||||
|       PlacesModelMountItem* mountItem = static_cast<PlacesModelMountItem*>(item); | ||||
|       if(mountItem->mount() == mount) | ||||
|         return mountItem; | ||||
|     } | ||||
|   } | ||||
|   return NULL; | ||||
| } | ||||
| 
 | ||||
| PlacesModelBookmarkItem* PlacesModel::itemFromBookmark(FmBookmarkItem* bkitem) { | ||||
|   int rowCount = bookmarksRoot->rowCount(); | ||||
|   for(int i = 0; i < rowCount; ++i) { | ||||
|     PlacesModelBookmarkItem* item = static_cast<PlacesModelBookmarkItem*>(bookmarksRoot->child(i, 0)); | ||||
|     if(item->bookmark() == bkitem) | ||||
|       return item; | ||||
|   } | ||||
|   return NULL; | ||||
| } | ||||
| 
 | ||||
| void PlacesModel::onMountAdded(GVolumeMonitor* monitor, GMount* mount, PlacesModel* pThis) { | ||||
|   GVolume* vol = g_mount_get_volume(mount); | ||||
|   if(vol) { // mount-added is also emitted when a volume is newly mounted.
 | ||||
|     PlacesModelVolumeItem* item = pThis->itemFromVolume(vol); | ||||
|     if(item && !item->path()) { | ||||
|       // update the mounted volume and show a button for eject.
 | ||||
|       GFile* gf = g_mount_get_root(mount); | ||||
|       FmPath* path = fm_path_new_for_gfile(gf); | ||||
|       g_object_unref(gf); | ||||
|       item->setPath(path); | ||||
|       if(path) | ||||
|         fm_path_unref(path); | ||||
|       // update the mount indicator (eject button)
 | ||||
|       QStandardItem* ejectBtn = item->parent()->child(item->row(), 1); | ||||
|       Q_ASSERT(ejectBtn); | ||||
|       ejectBtn->setIcon(pThis->ejectIcon_); | ||||
|     } | ||||
|     g_object_unref(vol); | ||||
|   } | ||||
|   else { // network mounts and others
 | ||||
|     PlacesModelMountItem* item = pThis->itemFromMount(mount); | ||||
|     /* for some unknown reasons, sometimes we get repeated mount-added
 | ||||
|      * signals and added a device more than one. So, make a sanity check here. */ | ||||
|     if(!item) { | ||||
|       item = new PlacesModelMountItem(mount); | ||||
|       QStandardItem* eject_btn = new QStandardItem(pThis->ejectIcon_, ""); | ||||
|       pThis->devicesRoot->appendRow(QList<QStandardItem*>() << item << eject_btn); | ||||
|     } | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| void PlacesModel::onMountChanged(GVolumeMonitor* monitor, GMount* mount, PlacesModel* pThis) { | ||||
|   PlacesModelMountItem* item = pThis->itemFromMount(mount); | ||||
|   if(item) | ||||
|     item->update(); | ||||
| } | ||||
| 
 | ||||
| void PlacesModel::onMountRemoved(GVolumeMonitor* monitor, GMount* mount, PlacesModel* pThis) { | ||||
|   GVolume* vol = g_mount_get_volume(mount); | ||||
|   qDebug() << "volume umounted: " << vol; | ||||
|   if(vol) { | ||||
|     // a volume is unmounted
 | ||||
|     g_object_unref(vol); | ||||
|   } | ||||
|   else { // network mounts and others
 | ||||
|     PlacesModelMountItem* item = pThis->itemFromMount(mount); | ||||
|     if(item) { | ||||
|       pThis->devicesRoot->removeRow(item->row()); | ||||
|     } | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| void PlacesModel::onVolumeAdded(GVolumeMonitor* monitor, GVolume* volume, PlacesModel* pThis) { | ||||
|   // for some unknown reasons, sometimes we get repeated volume-added
 | ||||
|   // signals and added a device more than one. So, make a sanity check here.
 | ||||
|   PlacesModelVolumeItem* volumeItem = pThis->itemFromVolume(volume); | ||||
|   if(!volumeItem) { | ||||
|     volumeItem = new PlacesModelVolumeItem(volume); | ||||
|     QStandardItem* ejectBtn = new QStandardItem(); | ||||
|     if(volumeItem->isMounted()) | ||||
|       ejectBtn->setIcon(pThis->ejectIcon_); | ||||
|     pThis->devicesRoot->appendRow(QList<QStandardItem*>() << volumeItem << ejectBtn); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| void PlacesModel::onVolumeChanged(GVolumeMonitor* monitor, GVolume* volume, PlacesModel* pThis) { | ||||
|   PlacesModelVolumeItem* item = pThis->itemFromVolume(volume); | ||||
|   if(item) { | ||||
|     item->update(); | ||||
|     if(!item->isMounted()) { // the volume is unmounted, remove the eject button if needed
 | ||||
|       // remove the eject button for the volume (at column 1 of the same row)
 | ||||
|       QStandardItem* ejectBtn = item->parent()->child(item->row(), 1); | ||||
|       Q_ASSERT(ejectBtn); | ||||
|       ejectBtn->setIcon(QIcon()); | ||||
|     } | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| void PlacesModel::onVolumeRemoved(GVolumeMonitor* monitor, GVolume* volume, PlacesModel* pThis) { | ||||
|   PlacesModelVolumeItem* item = pThis->itemFromVolume(volume); | ||||
|   if(item) { | ||||
|     pThis->devicesRoot->removeRow(item->row()); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| void PlacesModel::onBookmarksChanged(FmBookmarks* bookmarks, PlacesModel* pThis) { | ||||
|   // remove all items
 | ||||
|   pThis->bookmarksRoot->removeRows(0, pThis->bookmarksRoot->rowCount()); | ||||
|   pThis->loadBookmarks(); | ||||
| } | ||||
| 
 | ||||
| void PlacesModel::updateIcons() { | ||||
|   // the icon theme is changed and we need to update the icons
 | ||||
|   PlacesModelItem* item; | ||||
|   int row; | ||||
|   int n = placesRoot->rowCount(); | ||||
|   for(row = 0; row < n; ++row) { | ||||
|     item = static_cast<PlacesModelItem*>(placesRoot->child(row)); | ||||
|     item->updateIcon(); | ||||
|   } | ||||
|   n = devicesRoot->rowCount(); | ||||
|   for(row = 0; row < n; ++row) { | ||||
|     item = static_cast<PlacesModelItem*>(devicesRoot->child(row)); | ||||
|     item->updateIcon(); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| Qt::ItemFlags PlacesModel::flags(const QModelIndex& index) const { | ||||
|   if(index.column() == 1) // make 2nd column of every row selectable.
 | ||||
|     return Qt::ItemIsSelectable | Qt::ItemIsEnabled; | ||||
|   if(!index.parent().isValid()) { // root items
 | ||||
|     if(index.row() == 2) // bookmarks root
 | ||||
|       return Qt::ItemIsEnabled | Qt::ItemIsDropEnabled; | ||||
|     else | ||||
|       return Qt::ItemIsEnabled; | ||||
|   } | ||||
|   return QStandardItemModel::flags(index); | ||||
| } | ||||
| 
 | ||||
| bool PlacesModel::dropMimeData(const QMimeData* data, Qt::DropAction action, int row, int column, const QModelIndex& parent) { | ||||
|   QStandardItem* item = itemFromIndex(parent); | ||||
|   if(data->hasFormat("application/x-bookmark-row")) { // the data being dopped is a bookmark row
 | ||||
|     // decode it and do bookmark reordering
 | ||||
|     QByteArray buf = data->data("application/x-bookmark-row"); | ||||
|     QDataStream stream(&buf, QIODevice::ReadOnly); | ||||
|     int oldPos = -1; | ||||
|     char* pathStr = NULL; | ||||
|     stream >> oldPos >> pathStr; | ||||
|     // find the source bookmark item being dragged
 | ||||
|     GList* allBookmarks = fm_bookmarks_get_all(bookmarks); | ||||
|     FmBookmarkItem* draggedItem = static_cast<FmBookmarkItem*>(g_list_nth_data(allBookmarks, oldPos)); | ||||
|     // If we cannot find the dragged bookmark item at position <oldRow>, or we find an item,
 | ||||
|     // but the path of the item is not the same as what we expected, than it's the wrong item.
 | ||||
|     // This means that the bookmarks are changed during our dnd processing, which is an extremely rare case.
 | ||||
|     if(!draggedItem || !fm_path_equal_str(draggedItem->path, pathStr, -1)) { | ||||
|       delete []pathStr; | ||||
|       return false; | ||||
|     } | ||||
|     delete []pathStr; | ||||
| 
 | ||||
|     int newPos = -1; | ||||
|     if(row == -1 && column == -1) { // drop on an item
 | ||||
|       // we only allow dropping on an bookmark item
 | ||||
|       if(item && item->parent() == bookmarksRoot) | ||||
|         newPos = parent.row(); | ||||
|     } | ||||
|     else { // drop on a position between items
 | ||||
|       if(item == bookmarksRoot) // we only allow dropping on a bookmark item
 | ||||
|         newPos = row; | ||||
|     } | ||||
|     if(newPos != -1 && newPos != oldPos) // reorder the bookmark item
 | ||||
|       fm_bookmarks_reorder(bookmarks, draggedItem, newPos); | ||||
|   } | ||||
|   else if(data->hasUrls()) { // files uris are dropped
 | ||||
|     if(row == -1 && column == -1) { // drop uris on an item
 | ||||
|       if(item && item->parent()) { // need to be a child item
 | ||||
|         PlacesModelItem* placesItem = static_cast<PlacesModelItem*>(item); | ||||
|         if(placesItem->path()) { | ||||
|           qDebug() << "dropped dest:" << placesItem->text(); | ||||
|           // TODO: copy or move the dragged files to the dir pointed by the item.
 | ||||
|           qDebug() << "drop on" << item->text(); | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|     else { // drop uris on a position between items
 | ||||
|       if(item == bookmarksRoot) { // we only allow dropping on blank row of bookmarks section
 | ||||
|         FmPathList* paths = pathListFromQUrls(data->urls()); | ||||
|         for(GList* l = fm_path_list_peek_head_link(paths); l; l = l->next) { | ||||
|           FmPath* path = FM_PATH(l->data); | ||||
|           GFile* gf = fm_path_to_gfile(path); | ||||
|           // FIXME: this is a blocking call
 | ||||
|           if(g_file_query_file_type(gf, G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, | ||||
|                                     NULL) == G_FILE_TYPE_DIRECTORY) { | ||||
|             char* disp_name = fm_path_display_basename(path); | ||||
|             fm_bookmarks_insert(bookmarks, path, disp_name, row); | ||||
|             g_free(disp_name); | ||||
|           } | ||||
|           g_object_unref(gf); | ||||
|           return true; | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|   return false; | ||||
| } | ||||
| 
 | ||||
| // we only support dragging bookmark items and use our own
 | ||||
| // custom pseudo-mime-type: application/x-bookmark-row
 | ||||
| QMimeData* PlacesModel::mimeData(const QModelIndexList& indexes) const { | ||||
|   if(!indexes.isEmpty()) { | ||||
|     // we only allow dragging one bookmark item at a time, so handle the first index only.
 | ||||
|     QModelIndex index = indexes.first(); | ||||
|     QStandardItem* item = itemFromIndex(index); | ||||
|     // ensure that it's really a bookmark item
 | ||||
|     if(item && item->parent() == bookmarksRoot) { | ||||
|       PlacesModelBookmarkItem* bookmarkItem = static_cast<PlacesModelBookmarkItem*>(item); | ||||
|       QMimeData* mime = new QMimeData(); | ||||
|       QByteArray data; | ||||
|       QDataStream stream(&data, QIODevice::WriteOnly); | ||||
|       // There is no safe and cross-process way to store a reference of a row.
 | ||||
|       // Let's store the pos, name, and path of the bookmark item instead.
 | ||||
|       char* pathStr = fm_path_to_str(bookmarkItem->path()); | ||||
|       stream << index.row() << pathStr; | ||||
|       g_free(pathStr); | ||||
|       mime->setData("application/x-bookmark-row", data); | ||||
|       return mime; | ||||
|     } | ||||
|   } | ||||
|   return NULL; | ||||
| } | ||||
| 
 | ||||
| QStringList PlacesModel::mimeTypes() const { | ||||
|   return QStringList() << "application/x-bookmark-row" << "text/uri-list"; | ||||
| } | ||||
| 
 | ||||
| Qt::DropActions PlacesModel::supportedDropActions() const { | ||||
|   return QStandardItemModel::supportedDropActions(); | ||||
| } | ||||
| 
 | ||||
							
								
								
									
										131
									
								
								libfm-qt/placesmodel.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										131
									
								
								libfm-qt/placesmodel.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,131 @@ | ||||
| /*
 | ||||
| 
 | ||||
|     Copyright (C) 2013  Hong Jen Yee (PCMan) <pcman.tw@gmail.com> | ||||
| 
 | ||||
|     This program is free software; you can redistribute it and/or modify | ||||
|     it under the terms of the GNU General Public License as published by | ||||
|     the Free Software Foundation; either version 2 of the License, or | ||||
|     (at your option) any later version. | ||||
| 
 | ||||
|     This program is distributed in the hope that it will be useful, | ||||
|     but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|     GNU General Public License for more details. | ||||
| 
 | ||||
|     You should have received a copy of the GNU General Public License along | ||||
|     with this program; if not, write to the Free Software Foundation, Inc., | ||||
|     51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||||
| */ | ||||
| 
 | ||||
| 
 | ||||
| #ifndef FM_PLACESMODEL_H | ||||
| #define FM_PLACESMODEL_H | ||||
| 
 | ||||
| #include "libfmqtglobals.h" | ||||
| #include <QStandardItemModel> | ||||
| #include <QStandardItem> | ||||
| #include <QList> | ||||
| #include <QAction> | ||||
| #include <libfm/fm.h> | ||||
| 
 | ||||
| namespace Fm { | ||||
|    | ||||
| class PlacesModelItem; | ||||
| class PlacesModelVolumeItem; | ||||
| class PlacesModelMountItem; | ||||
| class PlacesModelBookmarkItem; | ||||
| 
 | ||||
| class LIBFM_QT_API PlacesModel : public QStandardItemModel { | ||||
| Q_OBJECT | ||||
| friend class PlacesView; | ||||
| public: | ||||
| 
 | ||||
|   // QAction used for popup menus
 | ||||
|   class ItemAction : public QAction { | ||||
|   public: | ||||
|     ItemAction(const QModelIndex& index, QString text, QObject* parent = 0): | ||||
|       QAction(text, parent), | ||||
|       index_(index) { | ||||
|     } | ||||
| 
 | ||||
|     QPersistentModelIndex& index() { | ||||
|       return index_; | ||||
|     } | ||||
|   private: | ||||
|     QPersistentModelIndex index_; | ||||
|   }; | ||||
| 
 | ||||
| public: | ||||
|   explicit PlacesModel(QObject* parent = 0); | ||||
|   virtual ~PlacesModel(); | ||||
| 
 | ||||
|   bool showTrash() { | ||||
|     return trashItem_ != NULL; | ||||
|   } | ||||
|   void setShowTrash(bool show); | ||||
| 
 | ||||
|   bool showApplications() { | ||||
|     return showApplications_; | ||||
|   } | ||||
|   void setShowApplications(bool show); | ||||
| 
 | ||||
|   bool showDesktop() { | ||||
|     return showDesktop_; | ||||
|   } | ||||
|   void setShowDesktop(bool show); | ||||
| 
 | ||||
| public Q_SLOTS: | ||||
|   void updateIcons(); | ||||
|   void updateTrash(); | ||||
| 
 | ||||
| protected: | ||||
|    | ||||
|   PlacesModelItem* itemFromPath(FmPath* path); | ||||
|   PlacesModelItem* itemFromPath(QStandardItem* rootItem, FmPath* path); | ||||
|   PlacesModelVolumeItem* itemFromVolume(GVolume* volume); | ||||
|   PlacesModelMountItem* itemFromMount(GMount* mount); | ||||
|   PlacesModelBookmarkItem* itemFromBookmark(FmBookmarkItem* bkitem); | ||||
| 
 | ||||
|   virtual Qt::ItemFlags flags(const QModelIndex& index) const; | ||||
|   virtual QStringList mimeTypes() const; | ||||
|   virtual QMimeData* mimeData(const QModelIndexList& indexes) const; | ||||
|   virtual bool dropMimeData(const QMimeData* data, Qt::DropAction action, int row, int column, const QModelIndex& parent); | ||||
|   Qt::DropActions supportedDropActions() const; | ||||
| 
 | ||||
|   void createTrashItem(); | ||||
|    | ||||
| private: | ||||
|   void loadBookmarks(); | ||||
|    | ||||
|   static void onVolumeAdded(GVolumeMonitor* monitor, GVolume* volume, PlacesModel* pThis); | ||||
|   static void onVolumeRemoved(GVolumeMonitor* monitor, GVolume* volume, PlacesModel* pThis); | ||||
|   static void onVolumeChanged(GVolumeMonitor* monitor, GVolume* volume, PlacesModel* pThis); | ||||
|   static void onMountAdded(GVolumeMonitor* monitor, GMount* mount, PlacesModel* pThis); | ||||
|   static void onMountRemoved(GVolumeMonitor* monitor, GMount* mount, PlacesModel* pThis); | ||||
|   static void onMountChanged(GVolumeMonitor* monitor, GMount* mount, PlacesModel* pThis); | ||||
|    | ||||
|   static void onBookmarksChanged(FmBookmarks* bookmarks, PlacesModel* pThis); | ||||
| 
 | ||||
|   static void onTrashChanged(GFileMonitor *monitor, GFile *gf, GFile *other, GFileMonitorEvent evt, PlacesModel* pThis); | ||||
| private: | ||||
|   FmBookmarks* bookmarks; | ||||
|   GVolumeMonitor* volumeMonitor; | ||||
|   QList<FmJob*> jobs; | ||||
|   bool showApplications_; | ||||
|   bool showDesktop_; | ||||
|   QStandardItem* placesRoot; | ||||
|   QStandardItem* devicesRoot; | ||||
|   QStandardItem* bookmarksRoot; | ||||
|   PlacesModelItem* trashItem_; | ||||
|   GFileMonitor* trashMonitor_; | ||||
|   PlacesModelItem* desktopItem; | ||||
|   PlacesModelItem* homeItem; | ||||
|   PlacesModelItem* computerItem; | ||||
|   PlacesModelItem* networkItem; | ||||
|   PlacesModelItem* applicationsItem; | ||||
|   QIcon ejectIcon_; | ||||
| }; | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| #endif // FM_PLACESMODEL_H
 | ||||
							
								
								
									
										188
									
								
								libfm-qt/placesmodelitem.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										188
									
								
								libfm-qt/placesmodelitem.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,188 @@ | ||||
| /*
 | ||||
| 
 | ||||
|     Copyright (C) 2013  Hong Jen Yee (PCMan) <pcman.tw@gmail.com> | ||||
| 
 | ||||
|     This program is free software; you can redistribute it and/or modify | ||||
|     it under the terms of the GNU General Public License as published by | ||||
|     the Free Software Foundation; either version 2 of the License, or | ||||
|     (at your option) any later version. | ||||
| 
 | ||||
|     This program is distributed in the hope that it will be useful, | ||||
|     but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|     GNU General Public License for more details. | ||||
| 
 | ||||
|     You should have received a copy of the GNU General Public License along | ||||
|     with this program; if not, write to the Free Software Foundation, Inc., | ||||
|     51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||||
| */ | ||||
| 
 | ||||
| 
 | ||||
| #include "placesmodelitem.h" | ||||
| #include "icontheme.h" | ||||
| #include <gio/gio.h> | ||||
| 
 | ||||
| namespace Fm { | ||||
| 
 | ||||
| PlacesModelItem::PlacesModelItem(): | ||||
|   QStandardItem(), | ||||
|   path_(NULL), | ||||
|   icon_(NULL), | ||||
|   fileInfo_(NULL) { | ||||
| } | ||||
| 
 | ||||
| PlacesModelItem::PlacesModelItem(const char* iconName, QString title, FmPath* path): | ||||
|   QStandardItem(title), | ||||
|   path_(path ? fm_path_ref(path) : NULL), | ||||
|   icon_(fm_icon_from_name(iconName)), | ||||
|   fileInfo_(NULL) { | ||||
|   if(icon_) | ||||
|     QStandardItem::setIcon(IconTheme::icon(icon_)); | ||||
|   setEditable(false); | ||||
| } | ||||
| 
 | ||||
| PlacesModelItem::PlacesModelItem(FmIcon* icon, QString title, FmPath* path): | ||||
|   QStandardItem(title), | ||||
|   path_(path ? fm_path_ref(path) : NULL), | ||||
|   icon_(icon ? fm_icon_ref(icon) : NULL), | ||||
|   fileInfo_(NULL) { | ||||
|   if(icon_) | ||||
|     QStandardItem::setIcon(IconTheme::icon(icon)); | ||||
|   setEditable(false); | ||||
| } | ||||
| 
 | ||||
| PlacesModelItem::PlacesModelItem(QIcon icon, QString title, FmPath* path): | ||||
|   QStandardItem(icon, title), | ||||
|   icon_(NULL), | ||||
|   path_(path ? fm_path_ref(path) : NULL), | ||||
|   fileInfo_(NULL) { | ||||
|   setEditable(false); | ||||
| } | ||||
| 
 | ||||
| PlacesModelItem::~PlacesModelItem() { | ||||
|   if(path_) | ||||
|     fm_path_unref(path_); | ||||
|   if(fileInfo_) | ||||
|     g_object_unref(fileInfo_); | ||||
|   if(icon_) | ||||
|     fm_icon_unref(icon_); | ||||
| } | ||||
| 
 | ||||
| void PlacesModelItem::setPath(FmPath* path) { | ||||
|   if(path_) | ||||
|     fm_path_unref(path_); | ||||
|   path_ = path ? fm_path_ref(path) : NULL; | ||||
| } | ||||
| 
 | ||||
| void PlacesModelItem::setIcon(FmIcon* icon) { | ||||
|   if(icon_) | ||||
|     fm_icon_unref(icon_); | ||||
|   if(icon) { | ||||
|     icon_ = fm_icon_ref(icon); | ||||
|     QStandardItem::setIcon(IconTheme::icon(icon_)); | ||||
|   } | ||||
|   else { | ||||
|     icon_ = NULL; | ||||
|     QStandardItem::setIcon(QIcon()); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| void PlacesModelItem::setIcon(GIcon* gicon) { | ||||
|   FmIcon* icon = gicon ? fm_icon_from_gicon(gicon) : NULL; | ||||
|   setIcon(icon); | ||||
|   fm_icon_unref(icon); | ||||
| } | ||||
| 
 | ||||
| void PlacesModelItem::updateIcon() { | ||||
|   if(icon_) | ||||
|     QStandardItem::setIcon(IconTheme::icon(icon_)); | ||||
| } | ||||
| 
 | ||||
| QVariant PlacesModelItem::data(int role) const { | ||||
|   // we use a QPixmap from FmIcon cache rather than QIcon object for decoration role.
 | ||||
|   return QStandardItem::data(role); | ||||
| } | ||||
| 
 | ||||
| void PlacesModelItem::setFileInfo(FmFileInfo* fileInfo) { | ||||
|   // FIXME: how can we correctly update icon?
 | ||||
|   if(fileInfo_) | ||||
|     fm_file_info_unref(fileInfo_); | ||||
| 
 | ||||
|   if(fileInfo) { | ||||
|     fileInfo_ = fm_file_info_ref(fileInfo); | ||||
|   } | ||||
|   else | ||||
|     fileInfo_ = NULL; | ||||
| } | ||||
| 
 | ||||
| PlacesModelBookmarkItem::PlacesModelBookmarkItem(FmBookmarkItem* bm_item): | ||||
|   PlacesModelItem(QIcon::fromTheme("folder"), QString::fromUtf8(bm_item->name), bm_item->path), | ||||
|   bookmarkItem_(fm_bookmark_item_ref(bm_item)) { | ||||
|   setEditable(true); | ||||
| } | ||||
| 
 | ||||
| PlacesModelVolumeItem::PlacesModelVolumeItem(GVolume* volume): | ||||
|   PlacesModelItem(), | ||||
|   volume_(reinterpret_cast<GVolume*>(g_object_ref(volume))) { | ||||
|   update(); | ||||
|   setEditable(false); | ||||
| } | ||||
| 
 | ||||
| void PlacesModelVolumeItem::update() { | ||||
|   // set title
 | ||||
|   setText(QString::fromUtf8(g_volume_get_name(volume_))); | ||||
|    | ||||
|   // set icon
 | ||||
|   GIcon* gicon = g_volume_get_icon(volume_); | ||||
|   setIcon(gicon); | ||||
|   g_object_unref(gicon); | ||||
| 
 | ||||
|   // set dir path
 | ||||
|   GMount* mount = g_volume_get_mount(volume_); | ||||
|   if(mount) { | ||||
|     GFile* mount_root = g_mount_get_root(mount); | ||||
|     FmPath* mount_path = fm_path_new_for_gfile(mount_root); | ||||
|     setPath(mount_path); | ||||
|     fm_path_unref(mount_path); | ||||
|     g_object_unref(mount_root); | ||||
|     g_object_unref(mount); | ||||
|   } | ||||
|   else { | ||||
|     setPath(NULL); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| bool PlacesModelVolumeItem::isMounted() { | ||||
|   GMount* mount = g_volume_get_mount(volume_); | ||||
|   if(mount) | ||||
|     g_object_unref(mount); | ||||
|   return mount != NULL ? true : false; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| PlacesModelMountItem::PlacesModelMountItem(GMount* mount): | ||||
|   PlacesModelItem(), | ||||
|   mount_(reinterpret_cast<GMount*>(mount)) { | ||||
|   update(); | ||||
|   setEditable(false); | ||||
| } | ||||
| 
 | ||||
| void PlacesModelMountItem::update() { | ||||
|   // set title
 | ||||
|   setText(QString::fromUtf8(g_mount_get_name(mount_))); | ||||
|    | ||||
|   // set path
 | ||||
|   GFile* mount_root = g_mount_get_root(mount_); | ||||
|   FmPath* mount_path = fm_path_new_for_gfile(mount_root); | ||||
|   setPath(mount_path); | ||||
|   fm_path_unref(mount_path); | ||||
|   g_object_unref(mount_root); | ||||
|    | ||||
|   // set icon
 | ||||
|   GIcon* gicon = g_mount_get_icon(mount_); | ||||
|   setIcon(gicon); | ||||
|   g_object_unref(gicon); | ||||
| } | ||||
| 
 | ||||
| } | ||||
							
								
								
									
										130
									
								
								libfm-qt/placesmodelitem.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										130
									
								
								libfm-qt/placesmodelitem.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,130 @@ | ||||
| /*
 | ||||
| 
 | ||||
|     Copyright (C) 2013  Hong Jen Yee (PCMan) <pcman.tw@gmail.com> | ||||
| 
 | ||||
|     This program is free software; you can redistribute it and/or modify | ||||
|     it under the terms of the GNU General Public License as published by | ||||
|     the Free Software Foundation; either version 2 of the License, or | ||||
|     (at your option) any later version. | ||||
| 
 | ||||
|     This program is distributed in the hope that it will be useful, | ||||
|     but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|     GNU General Public License for more details. | ||||
| 
 | ||||
|     You should have received a copy of the GNU General Public License along | ||||
|     with this program; if not, write to the Free Software Foundation, Inc., | ||||
|     51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||||
| */ | ||||
| 
 | ||||
| 
 | ||||
| #ifndef FM_PLACESMODELITEM_H | ||||
| #define FM_PLACESMODELITEM_H | ||||
| 
 | ||||
| #include "libfmqtglobals.h" | ||||
| #include <QStandardItemModel> | ||||
| #include <QStandardItem> | ||||
| #include <QList> | ||||
| #include <QAction> | ||||
| #include <libfm/fm.h> | ||||
| 
 | ||||
| namespace Fm { | ||||
| 
 | ||||
| // model item
 | ||||
| class LIBFM_QT_API PlacesModelItem : public QStandardItem { | ||||
| public: | ||||
|   enum Type { | ||||
|     Places = QStandardItem::UserType + 1, | ||||
|     Volume, | ||||
|     Mount, | ||||
|     Bookmark | ||||
|   }; | ||||
| 
 | ||||
| public: | ||||
|   PlacesModelItem(); | ||||
|   PlacesModelItem(QIcon icon, QString title, FmPath* path = NULL); | ||||
|   PlacesModelItem(const char* iconName, QString title, FmPath* path = NULL); | ||||
|   PlacesModelItem(FmIcon* icon, QString title, FmPath* path = NULL); | ||||
|   ~PlacesModelItem(); | ||||
| 
 | ||||
|   FmFileInfo* fileInfo() { | ||||
|     return fileInfo_; | ||||
|   } | ||||
|   void setFileInfo(FmFileInfo* fileInfo); | ||||
| 
 | ||||
|   FmPath* path() { | ||||
|     return path_; | ||||
|   } | ||||
|   void setPath(FmPath* path); | ||||
| 
 | ||||
|   FmIcon* icon() { | ||||
|     return icon_; | ||||
|   } | ||||
|   void setIcon(FmIcon* icon); | ||||
|   void setIcon(GIcon* gicon); | ||||
|   void updateIcon(); | ||||
| 
 | ||||
|   QVariant data(int role = Qt::UserRole + 1) const; | ||||
| 
 | ||||
|   virtual int type() const { | ||||
|     return Places; | ||||
|   } | ||||
| 
 | ||||
| private: | ||||
|   FmPath* path_; | ||||
|   FmFileInfo* fileInfo_; | ||||
|   FmIcon* icon_; | ||||
| }; | ||||
| 
 | ||||
| class LIBFM_QT_API PlacesModelVolumeItem : public PlacesModelItem { | ||||
| public: | ||||
|   PlacesModelVolumeItem(GVolume* volume); | ||||
|   bool isMounted(); | ||||
|   bool canEject() { | ||||
|     return g_volume_can_eject(volume_); | ||||
|   } | ||||
|   virtual int type() const { | ||||
|     return Volume; | ||||
|   } | ||||
|   GVolume* volume() { | ||||
|     return volume_; | ||||
|   } | ||||
|   void update(); | ||||
| private: | ||||
|   GVolume* volume_; | ||||
| }; | ||||
| 
 | ||||
| class LIBFM_QT_API PlacesModelMountItem : public PlacesModelItem { | ||||
| public: | ||||
|   PlacesModelMountItem(GMount* mount); | ||||
|   virtual int type() const { | ||||
|     return Mount; | ||||
|   } | ||||
|   GMount* mount() const { | ||||
|     return mount_; | ||||
|   } | ||||
|   void update(); | ||||
| private: | ||||
|   GMount* mount_; | ||||
| }; | ||||
| 
 | ||||
| class LIBFM_QT_API PlacesModelBookmarkItem : public PlacesModelItem { | ||||
| public: | ||||
|   virtual int type() const { | ||||
|     return Bookmark; | ||||
|   } | ||||
|   PlacesModelBookmarkItem(FmBookmarkItem* bm_item); | ||||
|   virtual ~PlacesModelBookmarkItem() { | ||||
|     if(bookmarkItem_) | ||||
|       fm_bookmark_item_unref(bookmarkItem_); | ||||
|   } | ||||
|   FmBookmarkItem* bookmark() const { | ||||
|     return bookmarkItem_; | ||||
|   } | ||||
| private: | ||||
|   FmBookmarkItem* bookmarkItem_; | ||||
| }; | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| #endif // FM_PLACESMODELITEM_H
 | ||||
							
								
								
									
										332
									
								
								libfm-qt/placesview.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										332
									
								
								libfm-qt/placesview.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,332 @@ | ||||
| /*
 | ||||
| 
 | ||||
|     Copyright (C) 2012  Hong Jen Yee (PCMan) <pcman.tw@gmail.com> | ||||
| 
 | ||||
|     This program is free software; you can redistribute it and/or modify | ||||
|     it under the terms of the GNU General Public License as published by | ||||
|     the Free Software Foundation; either version 2 of the License, or | ||||
|     (at your option) any later version. | ||||
| 
 | ||||
|     This program is distributed in the hope that it will be useful, | ||||
|     but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|     GNU General Public License for more details. | ||||
| 
 | ||||
|     You should have received a copy of the GNU General Public License along | ||||
|     with this program; if not, write to the Free Software Foundation, Inc., | ||||
|     51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||||
| */ | ||||
| 
 | ||||
| 
 | ||||
| #include "placesview.h" | ||||
| #include "placesmodel.h" | ||||
| #include "placesmodelitem.h" | ||||
| #include "mountoperation.h" | ||||
| #include "fileoperation.h" | ||||
| #include <QMenu> | ||||
| #include <QContextMenuEvent> | ||||
| #include <QHeaderView> | ||||
| #include <QDebug> | ||||
| #include <QGuiApplication> | ||||
| 
 | ||||
| using namespace Fm; | ||||
| 
 | ||||
| PlacesView::PlacesView(QWidget* parent): | ||||
|   QTreeView(parent), | ||||
|   currentPath_(NULL) { | ||||
|   setRootIsDecorated(false); | ||||
|   setHeaderHidden(true); | ||||
|   setIndentation(12); | ||||
| 
 | ||||
|   connect(this, &QTreeView::clicked, this, &PlacesView::onClicked); | ||||
|   connect(this, &QTreeView::pressed, this, &PlacesView::onPressed); | ||||
| 
 | ||||
|   setIconSize(QSize(24, 24)); | ||||
| 
 | ||||
|   // FIXME: we may share this model amont all views
 | ||||
|   model_ = new PlacesModel(this); | ||||
|   setModel(model_); | ||||
| 
 | ||||
|   QHeaderView* headerView = header(); | ||||
|   headerView->setSectionResizeMode(0, QHeaderView::Stretch); | ||||
|   headerView->setSectionResizeMode(1, QHeaderView::ResizeToContents); | ||||
|   headerView->setStretchLastSection(false); | ||||
|   expandAll(); | ||||
| 
 | ||||
|   // FIXME: is there any better way to make the first column span the whole row?
 | ||||
|   setFirstColumnSpanned(0, QModelIndex(), true); // places root
 | ||||
|   setFirstColumnSpanned(1, QModelIndex(), true); // devices root
 | ||||
|   setFirstColumnSpanned(2, QModelIndex(), true); // bookmarks root
 | ||||
| 
 | ||||
|   // the 2nd column is for the eject buttons
 | ||||
|   setSelectionBehavior(QAbstractItemView::SelectRows); // FIXME: why this does not work?
 | ||||
|   setAllColumnsShowFocus(false); | ||||
| 
 | ||||
|   setAcceptDrops(true); | ||||
|   setDragEnabled(true); | ||||
| } | ||||
| 
 | ||||
| PlacesView::~PlacesView() { | ||||
|   if(currentPath_) | ||||
|     fm_path_unref(currentPath_); | ||||
|   // qDebug("delete PlacesView");
 | ||||
| } | ||||
| 
 | ||||
| void PlacesView::activateRow(int type, const QModelIndex& index) { | ||||
|   if(!index.parent().isValid()) // ignore root items
 | ||||
|     return; | ||||
|   PlacesModelItem* item = static_cast<PlacesModelItem*>(model_->itemFromIndex(index)); | ||||
|   if(item) { | ||||
|     FmPath* path = item->path(); | ||||
|     if(!path) { | ||||
|       // check if mounting volumes is needed
 | ||||
|       if(item->type() == PlacesModelItem::Volume) { | ||||
|         PlacesModelVolumeItem* volumeItem = static_cast<PlacesModelVolumeItem*>(item); | ||||
|         if(!volumeItem->isMounted()) { | ||||
|           // Mount the volume
 | ||||
|           GVolume* volume = volumeItem->volume(); | ||||
|           MountOperation* op = new MountOperation(true, this); | ||||
|           op->mount(volume); | ||||
|           // connect(op, SIGNAL(finished(GError*)), SLOT(onMountOperationFinished(GError*)));
 | ||||
|           // blocking here until the mount operation is finished?
 | ||||
| 
 | ||||
|           // FIXME: update status of the volume after mount is finished!!
 | ||||
|           if(!op->wait()) | ||||
|             return; | ||||
|           path = item->path(); | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|     if(path) { | ||||
|       Q_EMIT chdirRequested(type, path); | ||||
|     } | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| // mouse button pressed
 | ||||
| void PlacesView::onPressed(const QModelIndex& index) { | ||||
|   // if middle button is pressed
 | ||||
|   if(QGuiApplication::mouseButtons() & Qt::MiddleButton) { | ||||
|     activateRow(1, index); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| void PlacesView::onEjectButtonClicked(PlacesModelItem* item) { | ||||
|   // The eject button is clicked for a device item (volume or mount)
 | ||||
|   if(item->type() == PlacesModelItem::Volume) { | ||||
|     PlacesModelVolumeItem* volumeItem = static_cast<PlacesModelVolumeItem*>(item); | ||||
|     MountOperation* op = new MountOperation(true, this); | ||||
|     if(volumeItem->canEject()) // do eject if applicable
 | ||||
|       op->eject(volumeItem->volume()); | ||||
|     else // otherwise, do unmount instead
 | ||||
|       op->unmount(volumeItem->volume()); | ||||
|   } | ||||
|   else if(item->type() == PlacesModelItem::Mount) { | ||||
|     PlacesModelMountItem* mountItem = static_cast<PlacesModelMountItem*>(item); | ||||
|     MountOperation* op = new MountOperation(true, this); | ||||
|     op->unmount(mountItem->mount()); | ||||
|   } | ||||
|   qDebug("PlacesView::onEjectButtonClicked"); | ||||
| } | ||||
| 
 | ||||
| void PlacesView::onClicked(const QModelIndex& index) { | ||||
|   if(!index.parent().isValid()) // ignore root items
 | ||||
|     return; | ||||
| 
 | ||||
|   if(index.column() == 0) { | ||||
|     activateRow(0, index); | ||||
|   } | ||||
|   else if(index.column() == 1) { // column 1 contains eject buttons of the mounted devices
 | ||||
|     if(index.parent() == model_->devicesRoot->index()) { // this is a mounted device
 | ||||
|       // the eject button is clicked
 | ||||
|       QModelIndex itemIndex = index.sibling(index.row(), 0); // the real item is at column 0
 | ||||
|       PlacesModelItem* item = static_cast<PlacesModelItem*>(model_->itemFromIndex(itemIndex)); | ||||
|       if(item) { | ||||
|         // eject the volume or the mount
 | ||||
|         onEjectButtonClicked(item); | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| void PlacesView::setCurrentPath(FmPath* path) { | ||||
|   if(currentPath_) | ||||
|     fm_path_unref(currentPath_); | ||||
|   if(path) { | ||||
|     currentPath_ = fm_path_ref(path); | ||||
|     // TODO: search for item with the path in model_ and select it.
 | ||||
|     PlacesModelItem* item = model_->itemFromPath(currentPath_); | ||||
|     if(item) { | ||||
|       selectionModel()->select(item->index(), QItemSelectionModel::SelectCurrent|QItemSelectionModel::Rows); | ||||
|     } | ||||
|     else | ||||
|       clearSelection(); | ||||
|   } | ||||
|   else { | ||||
|     currentPath_ = NULL; | ||||
|     clearSelection(); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| void PlacesView::dragMoveEvent(QDragMoveEvent* event) { | ||||
|   QTreeView::dragMoveEvent(event); | ||||
|   /*
 | ||||
|   QModelIndex index = indexAt(event->pos()); | ||||
|   if(event->isAccepted() && index.isValid() && index.parent() == model_->bookmarksRoot->index()) { | ||||
|     if(dropIndicatorPosition() != OnItem) { | ||||
|       event->setDropAction(Qt::LinkAction); | ||||
|       event->accept(); | ||||
|     } | ||||
|   } | ||||
|   */ | ||||
| } | ||||
| 
 | ||||
| void PlacesView::dropEvent(QDropEvent* event) { | ||||
|   QTreeView::dropEvent(event); | ||||
| } | ||||
| 
 | ||||
| void PlacesView::onEmptyTrash() { | ||||
|   FmPathList* files = fm_path_list_new(); | ||||
|   fm_path_list_push_tail(files, fm_path_get_trash()); | ||||
|   Fm::FileOperation::deleteFiles(files); | ||||
|   fm_path_list_unref(files); | ||||
| } | ||||
| 
 | ||||
| void PlacesView::onDeleteBookmark() { | ||||
|   PlacesModel::ItemAction* action = static_cast<PlacesModel::ItemAction*>(sender()); | ||||
|   if(!action->index().isValid()) | ||||
|     return; | ||||
|   PlacesModelBookmarkItem* item = static_cast<PlacesModelBookmarkItem*>(model_->itemFromIndex(action->index())); | ||||
|   FmBookmarkItem* bookmarkItem = item->bookmark(); | ||||
|   FmBookmarks* bookmarks = fm_bookmarks_dup(); | ||||
|   fm_bookmarks_remove(bookmarks, bookmarkItem); | ||||
|   g_object_unref(bookmarks); | ||||
| } | ||||
| 
 | ||||
| // virtual
 | ||||
| void PlacesView::commitData(QWidget * editor) { | ||||
|   QTreeView::commitData(editor); | ||||
|   PlacesModelBookmarkItem* item = static_cast<PlacesModelBookmarkItem*>(model_->itemFromIndex(currentIndex())); | ||||
|   FmBookmarkItem* bookmarkItem = item->bookmark(); | ||||
|   FmBookmarks* bookmarks = fm_bookmarks_dup(); | ||||
|   // rename bookmark
 | ||||
|   fm_bookmarks_rename(bookmarks, bookmarkItem, item->text().toUtf8().constData()); | ||||
|   g_object_unref(bookmarks); | ||||
| } | ||||
| 
 | ||||
| void PlacesView::onRenameBookmark() { | ||||
|   PlacesModel::ItemAction* action = static_cast<PlacesModel::ItemAction*>(sender()); | ||||
|   if(!action->index().isValid()) | ||||
|     return; | ||||
|   PlacesModelBookmarkItem* item = static_cast<PlacesModelBookmarkItem*>(model_->itemFromIndex(action->index())); | ||||
|   setFocus(); | ||||
|   setCurrentIndex(item->index()); | ||||
|   edit(item->index()); | ||||
| } | ||||
| 
 | ||||
| void PlacesView::onMountVolume() { | ||||
|   PlacesModel::ItemAction* action = static_cast<PlacesModel::ItemAction*>(sender()); | ||||
|   if(!action->index().isValid()) | ||||
|     return; | ||||
|   PlacesModelVolumeItem* item = static_cast<PlacesModelVolumeItem*>(model_->itemFromIndex(action->index())); | ||||
|   MountOperation* op = new MountOperation(true, this); | ||||
|   op->mount(item->volume()); | ||||
|   op->wait(); | ||||
| } | ||||
| 
 | ||||
| void PlacesView::onUnmountVolume() { | ||||
|   PlacesModel::ItemAction* action = static_cast<PlacesModel::ItemAction*>(sender()); | ||||
|   if(!action->index().isValid()) | ||||
|     return; | ||||
|   PlacesModelVolumeItem* item = static_cast<PlacesModelVolumeItem*>(model_->itemFromIndex(action->index())); | ||||
|   GMount* mount = NULL; | ||||
|   MountOperation* op = new MountOperation(true, this); | ||||
|   op->unmount(item->volume()); | ||||
|   op->wait(); | ||||
| } | ||||
| 
 | ||||
| void PlacesView::onUnmountMount() { | ||||
|   PlacesModel::ItemAction* action = static_cast<PlacesModel::ItemAction*>(sender()); | ||||
|   if(!action->index().isValid()) | ||||
|     return; | ||||
|   PlacesModelMountItem* item = static_cast<PlacesModelMountItem*>(model_->itemFromIndex(action->index())); | ||||
|   GMount* mount = item->mount(); | ||||
|   MountOperation* op = new MountOperation(true, this); | ||||
|   op->unmount(mount); | ||||
|   op->wait(); | ||||
| } | ||||
| 
 | ||||
| void PlacesView::onEjectVolume() { | ||||
|   PlacesModel::ItemAction* action = static_cast<PlacesModel::ItemAction*>(sender()); | ||||
|   if(!action->index().isValid()) | ||||
|     return; | ||||
|   PlacesModelVolumeItem* item = static_cast<PlacesModelVolumeItem*>(model_->itemFromIndex(action->index())); | ||||
|   MountOperation* op = new MountOperation(true, this); | ||||
|   op->eject(item->volume()); | ||||
|   op->wait(); | ||||
| } | ||||
| 
 | ||||
| void PlacesView::contextMenuEvent(QContextMenuEvent* event) { | ||||
|   QModelIndex index = indexAt(event->pos()); | ||||
|   if(index.isValid() && index.parent().isValid()) { | ||||
|     QMenu* menu = NULL; | ||||
|     QAction* action; | ||||
|     PlacesModelItem* item = static_cast<PlacesModelItem*>(model_->itemFromIndex(index)); | ||||
|     switch(item->type()) { | ||||
|       case PlacesModelItem::Places: { | ||||
|         FmPath* path = item->path(); | ||||
|         if(path && fm_path_equal(fm_path_get_trash(), path)) { | ||||
|           menu = new QMenu(this); | ||||
|           action = new PlacesModel::ItemAction(item->index(), tr("Empty Trash"), menu); | ||||
|           connect(action, &QAction::triggered, this, &PlacesView::onEmptyTrash); | ||||
|           menu->addAction(action); | ||||
|         } | ||||
|         break; | ||||
|       } | ||||
|       case PlacesModelItem::Bookmark: { | ||||
|         // create context menu for bookmark item
 | ||||
|         menu = new QMenu(this); | ||||
|         action = new PlacesModel::ItemAction(item->index(), tr("Rename"), menu); | ||||
|         connect(action, &QAction::triggered, this, &PlacesView::onRenameBookmark); | ||||
|         menu->addAction(action); | ||||
|         action = new PlacesModel::ItemAction(item->index(), tr("Delete"), menu); | ||||
|         connect(action, &QAction::triggered, this, &PlacesView::onDeleteBookmark); | ||||
|         menu->addAction(action); | ||||
|         break; | ||||
|       } | ||||
|       case PlacesModelItem::Volume: { | ||||
|         PlacesModelVolumeItem* volumeItem = static_cast<PlacesModelVolumeItem*>(item); | ||||
|         menu = new QMenu(this); | ||||
| 
 | ||||
|         if(volumeItem->isMounted()) { | ||||
|           action = new PlacesModel::ItemAction(item->index(), tr("Unmount"), menu); | ||||
|           connect(action, &QAction::triggered, this, &PlacesView::onUnmountVolume); | ||||
|         } | ||||
|         else { | ||||
|           action = new PlacesModel::ItemAction(item->index(), tr("Mount"), menu); | ||||
|           connect(action, &QAction::triggered, this, &PlacesView::onMountVolume); | ||||
|         } | ||||
|         menu->addAction(action); | ||||
| 
 | ||||
|         if(volumeItem->canEject()) { | ||||
|           action = new PlacesModel::ItemAction(item->index(), tr("Eject"), menu); | ||||
|           connect(action, &QAction::triggered, this, &PlacesView::onEjectVolume); | ||||
|           menu->addAction(action); | ||||
|         } | ||||
|         break; | ||||
|       } | ||||
|       case PlacesModelItem::Mount: { | ||||
|         menu = new QMenu(this); | ||||
|         action = new PlacesModel::ItemAction(item->index(), tr("Unmount"), menu); | ||||
|         connect(action, &QAction::triggered, this, &PlacesView::onUnmountMount); | ||||
|         menu->addAction(action); | ||||
|         break; | ||||
|       } | ||||
|     } | ||||
|     if(menu) { | ||||
|       menu->popup(mapToGlobal(event->pos())); | ||||
|       connect(menu, &QMenu::aboutToHide, menu, &QMenu::deleteLater); | ||||
|     } | ||||
|   } | ||||
| } | ||||
							
								
								
									
										94
									
								
								libfm-qt/placesview.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										94
									
								
								libfm-qt/placesview.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,94 @@ | ||||
| /*
 | ||||
| 
 | ||||
|     Copyright (C) 2012  Hong Jen Yee (PCMan) <pcman.tw@gmail.com> | ||||
| 
 | ||||
|     This program is free software; you can redistribute it and/or modify | ||||
|     it under the terms of the GNU General Public License as published by | ||||
|     the Free Software Foundation; either version 2 of the License, or | ||||
|     (at your option) any later version. | ||||
| 
 | ||||
|     This program is distributed in the hope that it will be useful, | ||||
|     but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|     GNU General Public License for more details. | ||||
| 
 | ||||
|     You should have received a copy of the GNU General Public License along | ||||
|     with this program; if not, write to the Free Software Foundation, Inc., | ||||
|     51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||||
| */ | ||||
| 
 | ||||
| 
 | ||||
| #ifndef FM_PLACESVIEW_H | ||||
| #define FM_PLACESVIEW_H | ||||
| 
 | ||||
| #include "libfmqtglobals.h" | ||||
| #include <QTreeView> | ||||
| #include <libfm/fm.h> | ||||
| 
 | ||||
| namespace Fm { | ||||
| 
 | ||||
| class PlacesModel; | ||||
| class PlacesModelItem; | ||||
|    | ||||
| class LIBFM_QT_API PlacesView : public QTreeView { | ||||
| Q_OBJECT | ||||
| 
 | ||||
| public: | ||||
|   explicit PlacesView(QWidget* parent = 0); | ||||
|   virtual ~PlacesView(); | ||||
| 
 | ||||
|   void setCurrentPath(FmPath* path); | ||||
|   FmPath* currentPath() { | ||||
|     return currentPath_; | ||||
|   } | ||||
| 
 | ||||
|   // libfm-gtk compatible alias
 | ||||
|   FmPath* getCwd() { | ||||
|     return currentPath(); | ||||
|   } | ||||
| 
 | ||||
|   void chdir(FmPath* path) { | ||||
|     setCurrentPath(path); | ||||
|   } | ||||
|    | ||||
| Q_SIGNALS: | ||||
|   void chdirRequested(int type, FmPath* path); | ||||
| 
 | ||||
| protected Q_SLOTS: | ||||
|   void onClicked(const QModelIndex & index); | ||||
|   void onPressed(const QModelIndex & index); | ||||
|   // void onMountOperationFinished(GError* error);
 | ||||
| 
 | ||||
|   void onEmptyTrash(); | ||||
| 
 | ||||
|   void onMountVolume(); | ||||
|   void onUnmountVolume(); | ||||
|   void onEjectVolume(); | ||||
|   void onUnmountMount(); | ||||
| 
 | ||||
|   void onDeleteBookmark(); | ||||
|   void onRenameBookmark(); | ||||
| 
 | ||||
| protected: | ||||
|   void drawBranches ( QPainter * painter, const QRect & rect, const QModelIndex & index ) const { | ||||
|     // override this method to inhibit drawing of the branch grid lines by Qt.
 | ||||
|   } | ||||
| 
 | ||||
|   virtual void dragMoveEvent(QDragMoveEvent* event); | ||||
|   virtual void dropEvent(QDropEvent* event); | ||||
|   virtual void contextMenuEvent(QContextMenuEvent* event); | ||||
|    | ||||
|   virtual void commitData(QWidget * editor); | ||||
| 
 | ||||
| private: | ||||
|   void onEjectButtonClicked(PlacesModelItem* item); | ||||
|   void activateRow(int type, const QModelIndex& index); | ||||
| 
 | ||||
| private: | ||||
|   PlacesModel* model_; | ||||
|   FmPath* currentPath_; | ||||
| }; | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| #endif // FM_PLACESVIEW_H
 | ||||
							
								
								
									
										269
									
								
								libfm-qt/proxyfoldermodel.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										269
									
								
								libfm-qt/proxyfoldermodel.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,269 @@ | ||||
| /*
 | ||||
|     <one line to give the library's name and an idea of what it does.> | ||||
|     Copyright (C) 2012  Hong Jen Yee (PCMan) <pcman.tw@gmail.com> | ||||
| 
 | ||||
|     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 | ||||
| */ | ||||
| 
 | ||||
| 
 | ||||
| #include "proxyfoldermodel.h" | ||||
| #include "foldermodel.h" | ||||
| #include <QCollator> | ||||
| 
 | ||||
| using namespace Fm; | ||||
| 
 | ||||
| ProxyFolderModel::ProxyFolderModel(QObject * parent): | ||||
|   QSortFilterProxyModel(parent), | ||||
|   thumbnailSize_(0), | ||||
|   showHidden_(false), | ||||
|   showThumbnails_(false), | ||||
|   folderFirst_(true) { | ||||
|   setDynamicSortFilter(true); | ||||
|   setSortCaseSensitivity(Qt::CaseInsensitive); | ||||
| } | ||||
| 
 | ||||
| ProxyFolderModel::~ProxyFolderModel() { | ||||
|   qDebug("delete ProxyFolderModel"); | ||||
| 
 | ||||
|   if(showThumbnails_ && thumbnailSize_ != 0) { | ||||
|     FolderModel* srcModel = static_cast<FolderModel*>(sourceModel()); | ||||
|     // tell the source model that we don't need the thumnails anymore
 | ||||
|     if(srcModel) { | ||||
|       srcModel->releaseThumbnails(thumbnailSize_); | ||||
|       disconnect(srcModel, SIGNAL(thumbnailLoaded(QModelIndex,int))); | ||||
|     } | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| void ProxyFolderModel::setSourceModel(QAbstractItemModel* model) { | ||||
|   if(model) { | ||||
|     // we only support Fm::FolderModel
 | ||||
|     Q_ASSERT(model->inherits("Fm::FolderModel")); | ||||
| 
 | ||||
|     if(showThumbnails_ && thumbnailSize_ != 0) { // if we're showing thumbnails
 | ||||
|       FolderModel* oldSrcModel = static_cast<FolderModel*>(sourceModel()); | ||||
|       FolderModel* newSrcModel = static_cast<FolderModel*>(model); | ||||
|       if(oldSrcModel) { // we need to release cached thumbnails for the old source model
 | ||||
|         oldSrcModel->releaseThumbnails(thumbnailSize_); | ||||
|         disconnect(oldSrcModel, SIGNAL(thumbnailLoaded(QModelIndex,int))); | ||||
|       } | ||||
|       if(newSrcModel) { // tell the new source model that we want thumbnails of this size
 | ||||
|         newSrcModel->cacheThumbnails(thumbnailSize_); | ||||
|         connect(newSrcModel, &FolderModel::thumbnailLoaded, this, &ProxyFolderModel::onThumbnailLoaded); | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|   QSortFilterProxyModel::setSourceModel(model); | ||||
| } | ||||
| 
 | ||||
| void ProxyFolderModel::sort(int column, Qt::SortOrder order) { | ||||
|   int oldColumn = sortColumn(); | ||||
|   Qt::SortOrder oldOrder = sortOrder(); | ||||
|   QSortFilterProxyModel::sort(column, order); | ||||
|   if(column != oldColumn || order != oldOrder) { | ||||
|     Q_EMIT sortFilterChanged(); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| void ProxyFolderModel::setShowHidden(bool show) { | ||||
|   if(show != showHidden_) { | ||||
|     showHidden_ = show; | ||||
|     invalidateFilter(); | ||||
|     Q_EMIT sortFilterChanged(); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| // need to call invalidateFilter() manually.
 | ||||
| void ProxyFolderModel::setFolderFirst(bool folderFirst) { | ||||
|   if(folderFirst != folderFirst_) { | ||||
|     folderFirst_ = folderFirst; | ||||
|     invalidate(); | ||||
|     Q_EMIT sortFilterChanged(); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| bool ProxyFolderModel::filterAcceptsRow(int source_row, const QModelIndex & source_parent) const { | ||||
|   if(!showHidden_) { | ||||
|     QAbstractItemModel* srcModel = sourceModel(); | ||||
|     QString name = srcModel->data(srcModel->index(source_row, 0, source_parent)).toString(); | ||||
|     if(name.startsWith(".") || name.endsWith("~")) | ||||
|       return false; | ||||
|   } | ||||
|   // apply additional filters if there're any
 | ||||
|   Q_FOREACH(ProxyFolderModelFilter* filter, filters_) { | ||||
|     FolderModel* srcModel = static_cast<FolderModel*>(sourceModel()); | ||||
|     FmFileInfo* fileInfo = srcModel->fileInfoFromIndex(srcModel->index(source_row, 0, source_parent)); | ||||
|     if(!filter->filterAcceptsRow(this, fileInfo)) | ||||
|       return false; | ||||
|   } | ||||
|   return true; | ||||
| } | ||||
| 
 | ||||
| bool ProxyFolderModel::lessThan(const QModelIndex& left, const QModelIndex& right) const { | ||||
|   FolderModel* srcModel = static_cast<FolderModel*>(sourceModel()); | ||||
|   // left and right are indexes of source model, not the proxy model.
 | ||||
|   if(srcModel) { | ||||
|     FmFileInfo* leftInfo = srcModel->fileInfoFromIndex(left); | ||||
|     FmFileInfo* rightInfo = srcModel->fileInfoFromIndex(right); | ||||
|      | ||||
|     if(folderFirst_) { | ||||
|       bool leftIsFolder = (bool)fm_file_info_is_dir(leftInfo); | ||||
|       bool rightIsFolder = (bool)fm_file_info_is_dir(rightInfo); | ||||
|       if(leftIsFolder != rightIsFolder) | ||||
|         return leftIsFolder ? true : false; | ||||
|     } | ||||
| 
 | ||||
|     switch(sortColumn()) { | ||||
|       case FolderModel::ColumnFileName: | ||||
|         if(sortCaseSensitivity() == Qt::CaseSensitive) { | ||||
|           // fm_file_info_get_collate_key_nocasefold() uses g_utf8_casefold() from glib internally, which
 | ||||
|           // is only an approximation not working correctly in some locales.
 | ||||
|           // FIXME: we may use QCollator (since Qt 5.2) for this, but the performance impact is unknown
 | ||||
|           return strcmp(fm_file_info_get_collate_key_nocasefold(leftInfo), fm_file_info_get_collate_key_nocasefold(rightInfo)) < 0; | ||||
|           /*
 | ||||
|           QCollator coll; | ||||
|           coll.setCaseSensitivity(Qt::CaseSensitive); | ||||
|           coll.setIgnorePunctuation(false); | ||||
|           coll.setNumericMode(true); | ||||
|           return coll.compare(QString::fromUtf8(fm_file_info_get_disp_name(leftInfo)), QString::fromUtf8(fm_file_info_get_disp_name(rightInfo))) < 0; | ||||
|           */ | ||||
|         } | ||||
|         else { | ||||
|           // linguistic case insensitive ordering
 | ||||
|           return strcmp(fm_file_info_get_collate_key(leftInfo), fm_file_info_get_collate_key(rightInfo)) < 0; | ||||
|         } | ||||
|       case FolderModel::ColumnFileMTime: | ||||
|         return fm_file_info_get_mtime(leftInfo) < fm_file_info_get_mtime(rightInfo); | ||||
|       case FolderModel::ColumnFileSize: | ||||
|         return fm_file_info_get_size(leftInfo) < fm_file_info_get_size(rightInfo); | ||||
|       case FolderModel::ColumnFileOwner: | ||||
|         // TODO: sort by owner
 | ||||
|         break; | ||||
|       case FolderModel::ColumnFileType: | ||||
|         break; | ||||
|     } | ||||
|   } | ||||
|   return QSortFilterProxyModel::lessThan(left, right); | ||||
| } | ||||
| 
 | ||||
| FmFileInfo* ProxyFolderModel::fileInfoFromIndex(const QModelIndex& index) const { | ||||
|   FolderModel* srcModel = static_cast<FolderModel*>(sourceModel()); | ||||
|   if(srcModel) { | ||||
|     QModelIndex srcIndex = mapToSource(index); | ||||
|     return srcModel->fileInfoFromIndex(srcIndex); | ||||
|   } | ||||
|   return NULL; | ||||
| } | ||||
| 
 | ||||
| void ProxyFolderModel::setShowThumbnails(bool show) { | ||||
|   if(show != showThumbnails_) { | ||||
|     showThumbnails_ = show; | ||||
|     FolderModel* srcModel = static_cast<FolderModel*>(sourceModel()); | ||||
|     if(srcModel && thumbnailSize_ != 0) { | ||||
|       if(show) { | ||||
|         // ask for cache of thumbnails of the new size in source model
 | ||||
|         srcModel->cacheThumbnails(thumbnailSize_); | ||||
|         // connect to the srcModel so we can be notified when a thumbnail is loaded.
 | ||||
|         connect(srcModel, &FolderModel::thumbnailLoaded, this, &ProxyFolderModel::onThumbnailLoaded); | ||||
|       } | ||||
|       else { // turn off thumbnails
 | ||||
|         // free cached old thumbnails in souce model
 | ||||
|         srcModel->releaseThumbnails(thumbnailSize_); | ||||
|         disconnect(srcModel, SIGNAL(thumbnailLoaded(QModelIndex,int))); | ||||
|       } | ||||
|       // reload all items, FIXME: can we only update items previously having thumbnails
 | ||||
|       Q_EMIT dataChanged(index(0, 0), index(rowCount() - 1, 0)); | ||||
|     } | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| void ProxyFolderModel::setThumbnailSize(int size) { | ||||
|   if(size != thumbnailSize_) { | ||||
|     FolderModel* srcModel = static_cast<FolderModel*>(sourceModel()); | ||||
|     if(showThumbnails_ && srcModel) { | ||||
|       // free cached thumbnails of the old size
 | ||||
|       if(thumbnailSize_ != 0) | ||||
|         srcModel->releaseThumbnails(thumbnailSize_); | ||||
|       else { | ||||
|         // if the old thumbnail size is 0, we did not turn on thumbnail initially
 | ||||
|         connect(srcModel, &FolderModel::thumbnailLoaded, this, &ProxyFolderModel::onThumbnailLoaded); | ||||
|       } | ||||
|       // ask for cache of thumbnails of the new size in source model
 | ||||
|       srcModel->cacheThumbnails(size); | ||||
|       // reload all items, FIXME: can we only update items previously having thumbnails
 | ||||
|       Q_EMIT dataChanged(index(0, 0), index(rowCount() - 1, 0)); | ||||
|     } | ||||
|      | ||||
|     thumbnailSize_ = size; | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| QVariant ProxyFolderModel::data(const QModelIndex& index, int role) const { | ||||
|   if(index.column() == 0) { // only show the decoration role for the first column
 | ||||
|     if(role == Qt::DecorationRole && showThumbnails_ && thumbnailSize_) { | ||||
|       // we need to show thumbnails instead of icons
 | ||||
|       FolderModel* srcModel = static_cast<FolderModel*>(sourceModel()); | ||||
|       QModelIndex srcIndex = mapToSource(index); | ||||
|       QImage image = srcModel->thumbnailFromIndex(srcIndex, thumbnailSize_); | ||||
|       if(!image.isNull()) // if we got a thumbnail of the desired size, use it
 | ||||
|         return QVariant(image); | ||||
|     } | ||||
|   } | ||||
|   // fallback to icons if thumbnails are not available
 | ||||
|   return QSortFilterProxyModel::data(index, role); | ||||
| } | ||||
| 
 | ||||
| void ProxyFolderModel::onThumbnailLoaded(const QModelIndex& srcIndex, int size) { | ||||
|    | ||||
|   FolderModel* srcModel = static_cast<FolderModel*>(sourceModel()); | ||||
|   FolderModelItem* item = srcModel->itemFromIndex(srcIndex); | ||||
|   // qDebug("ProxyFolderModel::onThumbnailLoaded: %d, %s", size, item->displayName.toUtf8().data());
 | ||||
|    | ||||
|   if(size == thumbnailSize_) { // if a thumbnail of the size we want is loaded
 | ||||
|     QModelIndex index = mapFromSource(srcIndex); | ||||
|     Q_EMIT dataChanged(index, index); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| void ProxyFolderModel::addFilter(ProxyFolderModelFilter* filter) { | ||||
|   filters_.append(filter); | ||||
|   invalidateFilter(); | ||||
|   Q_EMIT sortFilterChanged(); | ||||
| } | ||||
| 
 | ||||
| void ProxyFolderModel::removeFilter(ProxyFolderModelFilter* filter) { | ||||
|   filters_.removeOne(filter); | ||||
|   invalidateFilter(); | ||||
|   Q_EMIT sortFilterChanged();   | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| #if 0 | ||||
| void ProxyFolderModel::reloadAllThumbnails() { | ||||
|   // reload all thumbnails and update UI
 | ||||
|   FolderModel* srcModel = static_cast<FolderModel*>(sourceModel()); | ||||
|   if(srcModel) { | ||||
|     int rows= rowCount(); | ||||
|     for(int row = 0; row < rows; ++row) { | ||||
|       QModelIndex index = this->index(row, 0); | ||||
|       QModelIndex srcIndex = mapToSource(index); | ||||
|       QImage image = srcModel->thumbnailFromIndex(srcIndex, size); | ||||
|       // tell the world that the item is changed to trigger a UI update
 | ||||
|       if(!image.isNull()) | ||||
|         Q_EMIT dataChanged(index, index); | ||||
|     } | ||||
|   } | ||||
| } | ||||
| #endif | ||||
							
								
								
									
										110
									
								
								libfm-qt/proxyfoldermodel.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										110
									
								
								libfm-qt/proxyfoldermodel.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,110 @@ | ||||
| /*
 | ||||
|     <one line to give the library's name and an idea of what it does.> | ||||
|     Copyright (C) 2012  Hong Jen Yee (PCMan) <pcman.tw@gmail.com> | ||||
| 
 | ||||
|     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 | ||||
| */ | ||||
| 
 | ||||
| 
 | ||||
| #ifndef FM_PROXYFOLDERMODEL_H | ||||
| #define FM_PROXYFOLDERMODEL_H | ||||
| 
 | ||||
| #include "libfmqtglobals.h" | ||||
| #include <QSortFilterProxyModel> | ||||
| #include <libfm/fm.h> | ||||
| #include <QList> | ||||
| 
 | ||||
| namespace Fm { | ||||
| 
 | ||||
| // a proxy model used to sort and filter FolderModel
 | ||||
| 
 | ||||
| class FolderModelItem; | ||||
| class ProxyFolderModel; | ||||
| 
 | ||||
| class LIBFM_QT_API ProxyFolderModelFilter { | ||||
| public: | ||||
|   virtual bool filterAcceptsRow(const ProxyFolderModel* model, FmFileInfo* info) const = 0; | ||||
|   virtual ~ProxyFolderModelFilter() {} | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
| class LIBFM_QT_API ProxyFolderModel : public QSortFilterProxyModel { | ||||
|   Q_OBJECT | ||||
| public: | ||||
| 
 | ||||
| public: | ||||
|   explicit ProxyFolderModel(QObject * parent = 0); | ||||
|   virtual ~ProxyFolderModel(); | ||||
| 
 | ||||
|   // only Fm::FolderModel is allowed for being sourceModel
 | ||||
|   virtual void setSourceModel(QAbstractItemModel* model); | ||||
| 
 | ||||
|   void setShowHidden(bool show); | ||||
|   bool showHidden() { | ||||
|     return showHidden_; | ||||
|   } | ||||
| 
 | ||||
|   void setFolderFirst(bool folderFirst); | ||||
|   bool folderFirst() { | ||||
|     return folderFirst_; | ||||
|   } | ||||
| 
 | ||||
|   void setSortCaseSensitivity(Qt::CaseSensitivity cs) { | ||||
|     QSortFilterProxyModel::setSortCaseSensitivity(cs); | ||||
|     Q_EMIT sortFilterChanged(); | ||||
|   } | ||||
| 
 | ||||
|   bool showThumbnails() { | ||||
|     return showThumbnails_; | ||||
|   } | ||||
|   void setShowThumbnails(bool show); | ||||
| 
 | ||||
|   int thumbnailSize() { | ||||
|     return thumbnailSize_; | ||||
|   } | ||||
|   void setThumbnailSize(int size); | ||||
| 
 | ||||
|   FmFileInfo* fileInfoFromIndex(const QModelIndex& index) const; | ||||
| 
 | ||||
|   virtual void sort(int column, Qt::SortOrder order = Qt::AscendingOrder); | ||||
|   virtual QVariant data(const QModelIndex & index, int role = Qt::DisplayRole) const; | ||||
| 
 | ||||
|   void addFilter(ProxyFolderModelFilter* filter); | ||||
|   void removeFilter(ProxyFolderModelFilter* filter); | ||||
| 
 | ||||
| Q_SIGNALS: | ||||
|   void sortFilterChanged(); | ||||
| 
 | ||||
| protected Q_SLOTS: | ||||
|   void onThumbnailLoaded(const QModelIndex& srcIndex, int size); | ||||
| 
 | ||||
| protected: | ||||
|   bool filterAcceptsRow(int source_row, const QModelIndex & source_parent) const; | ||||
|   bool lessThan(const QModelIndex & left, const QModelIndex & right) const; | ||||
|   // void reloadAllThumbnails();
 | ||||
| 
 | ||||
| private: | ||||
| 
 | ||||
| private: | ||||
|   bool showHidden_; | ||||
|   bool folderFirst_; | ||||
|   bool showThumbnails_; | ||||
|   int thumbnailSize_; | ||||
|   QList<ProxyFolderModelFilter*> filters_; | ||||
| }; | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| #endif // FM_PROXYFOLDERMODEL_H
 | ||||
							
								
								
									
										204
									
								
								libfm-qt/rename-dialog.ui
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										204
									
								
								libfm-qt/rename-dialog.ui
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,204 @@ | ||||
| <?xml version="1.0" encoding="UTF-8"?> | ||||
| <ui version="4.0"> | ||||
|  <class>RenameDialog</class> | ||||
|  <widget class="QDialog" name="RenameDialog"> | ||||
|   <property name="geometry"> | ||||
|    <rect> | ||||
|     <x>0</x> | ||||
|     <y>0</y> | ||||
|     <width>398</width> | ||||
|     <height>220</height> | ||||
|    </rect> | ||||
|   </property> | ||||
|   <property name="windowTitle"> | ||||
|    <string>Confirm to replace files</string> | ||||
|   </property> | ||||
|   <property name="sizeGripEnabled"> | ||||
|    <bool>false</bool> | ||||
|   </property> | ||||
|   <layout class="QVBoxLayout" name="verticalLayout"> | ||||
|    <property name="spacing"> | ||||
|     <number>6</number> | ||||
|    </property> | ||||
|    <property name="margin"> | ||||
|     <number>10</number> | ||||
|    </property> | ||||
|    <item> | ||||
|     <widget class="QLabel" name="label"> | ||||
|      <property name="sizePolicy"> | ||||
|       <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> | ||||
|        <horstretch>0</horstretch> | ||||
|        <verstretch>0</verstretch> | ||||
|       </sizepolicy> | ||||
|      </property> | ||||
|      <property name="text"> | ||||
|       <string><html><head/><body><p><span style=" font-weight:600;">There is already a file with the same name in this location.</span></p><p>Do you want to replace the existing file?</p></body></html></string> | ||||
|      </property> | ||||
|     </widget> | ||||
|    </item> | ||||
|    <item> | ||||
|     <layout class="QGridLayout" name="gridLayout"> | ||||
|      <property name="horizontalSpacing"> | ||||
|       <number>12</number> | ||||
|      </property> | ||||
|      <property name="verticalSpacing"> | ||||
|       <number>6</number> | ||||
|      </property> | ||||
|      <item row="0" column="0"> | ||||
|       <widget class="QLabel" name="destIcon"> | ||||
|        <property name="sizePolicy"> | ||||
|         <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> | ||||
|          <horstretch>0</horstretch> | ||||
|          <verstretch>0</verstretch> | ||||
|         </sizepolicy> | ||||
|        </property> | ||||
|        <property name="text"> | ||||
|         <string>dest</string> | ||||
|        </property> | ||||
|       </widget> | ||||
|      </item> | ||||
|      <item row="1" column="0" colspan="2"> | ||||
|       <widget class="QLabel" name="label_3"> | ||||
|        <property name="text"> | ||||
|         <string>with the following file?</string> | ||||
|        </property> | ||||
|       </widget> | ||||
|      </item> | ||||
|      <item row="2" column="1"> | ||||
|       <widget class="QLabel" name="srcInfo"> | ||||
|        <property name="sizePolicy"> | ||||
|         <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> | ||||
|          <horstretch>0</horstretch> | ||||
|          <verstretch>0</verstretch> | ||||
|         </sizepolicy> | ||||
|        </property> | ||||
|        <property name="text"> | ||||
|         <string>src file info</string> | ||||
|        </property> | ||||
|       </widget> | ||||
|      </item> | ||||
|      <item row="0" column="1"> | ||||
|       <widget class="QLabel" name="destInfo"> | ||||
|        <property name="sizePolicy"> | ||||
|         <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> | ||||
|          <horstretch>0</horstretch> | ||||
|          <verstretch>0</verstretch> | ||||
|         </sizepolicy> | ||||
|        </property> | ||||
|        <property name="text"> | ||||
|         <string>dest file info</string> | ||||
|        </property> | ||||
|       </widget> | ||||
|      </item> | ||||
|      <item row="2" column="0"> | ||||
|       <widget class="QLabel" name="srcIcon"> | ||||
|        <property name="sizePolicy"> | ||||
|         <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> | ||||
|          <horstretch>0</horstretch> | ||||
|          <verstretch>0</verstretch> | ||||
|         </sizepolicy> | ||||
|        </property> | ||||
|        <property name="text"> | ||||
|         <string>src</string> | ||||
|        </property> | ||||
|       </widget> | ||||
|      </item> | ||||
|     </layout> | ||||
|    </item> | ||||
|    <item> | ||||
|     <layout class="QHBoxLayout" name="horizontalLayout"> | ||||
|      <property name="spacing"> | ||||
|       <number>12</number> | ||||
|      </property> | ||||
|      <item> | ||||
|       <widget class="QLabel" name="label_6"> | ||||
|        <property name="sizePolicy"> | ||||
|         <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> | ||||
|          <horstretch>0</horstretch> | ||||
|          <verstretch>0</verstretch> | ||||
|         </sizepolicy> | ||||
|        </property> | ||||
|        <property name="text"> | ||||
|         <string>&File name:</string> | ||||
|        </property> | ||||
|        <property name="buddy"> | ||||
|         <cstring>fileName</cstring> | ||||
|        </property> | ||||
|       </widget> | ||||
|      </item> | ||||
|      <item> | ||||
|       <widget class="QLineEdit" name="fileName"/> | ||||
|      </item> | ||||
|     </layout> | ||||
|    </item> | ||||
|    <item> | ||||
|     <widget class="QCheckBox" name="applyToAll"> | ||||
|      <property name="text"> | ||||
|       <string>Apply this option to all existing files</string> | ||||
|      </property> | ||||
|     </widget> | ||||
|    </item> | ||||
|    <item> | ||||
|     <spacer name="verticalSpacer"> | ||||
|      <property name="orientation"> | ||||
|       <enum>Qt::Vertical</enum> | ||||
|      </property> | ||||
|      <property name="sizeType"> | ||||
|       <enum>QSizePolicy::Expanding</enum> | ||||
|      </property> | ||||
|      <property name="sizeHint" stdset="0"> | ||||
|       <size> | ||||
|        <width>0</width> | ||||
|        <height>0</height> | ||||
|       </size> | ||||
|      </property> | ||||
|     </spacer> | ||||
|    </item> | ||||
|    <item> | ||||
|     <widget class="QDialogButtonBox" name="buttonBox"> | ||||
|      <property name="orientation"> | ||||
|       <enum>Qt::Horizontal</enum> | ||||
|      </property> | ||||
|      <property name="standardButtons"> | ||||
|       <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ignore|QDialogButtonBox::Ok</set> | ||||
|      </property> | ||||
|     </widget> | ||||
|    </item> | ||||
|   </layout> | ||||
|  </widget> | ||||
|  <resources/> | ||||
|  <connections> | ||||
|   <connection> | ||||
|    <sender>buttonBox</sender> | ||||
|    <signal>accepted()</signal> | ||||
|    <receiver>RenameDialog</receiver> | ||||
|    <slot>accept()</slot> | ||||
|    <hints> | ||||
|     <hint type="sourcelabel"> | ||||
|      <x>248</x> | ||||
|      <y>254</y> | ||||
|     </hint> | ||||
|     <hint type="destinationlabel"> | ||||
|      <x>157</x> | ||||
|      <y>274</y> | ||||
|     </hint> | ||||
|    </hints> | ||||
|   </connection> | ||||
|   <connection> | ||||
|    <sender>buttonBox</sender> | ||||
|    <signal>rejected()</signal> | ||||
|    <receiver>RenameDialog</receiver> | ||||
|    <slot>reject()</slot> | ||||
|    <hints> | ||||
|     <hint type="sourcelabel"> | ||||
|      <x>316</x> | ||||
|      <y>260</y> | ||||
|     </hint> | ||||
|     <hint type="destinationlabel"> | ||||
|      <x>286</x> | ||||
|      <y>274</y> | ||||
|     </hint> | ||||
|    </hints> | ||||
|   </connection> | ||||
|  </connections> | ||||
| </ui> | ||||
							
								
								
									
										137
									
								
								libfm-qt/renamedialog.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										137
									
								
								libfm-qt/renamedialog.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,137 @@ | ||||
| /*
 | ||||
| 
 | ||||
|     Copyright (C) 2013  Hong Jen Yee (PCMan) <pcman.tw@gmail.com> | ||||
| 
 | ||||
|     This program is free software; you can redistribute it and/or modify | ||||
|     it under the terms of the GNU General Public License as published by | ||||
|     the Free Software Foundation; either version 2 of the License, or | ||||
|     (at your option) any later version. | ||||
| 
 | ||||
|     This program is distributed in the hope that it will be useful, | ||||
|     but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|     GNU General Public License for more details. | ||||
| 
 | ||||
|     You should have received a copy of the GNU General Public License along | ||||
|     with this program; if not, write to the Free Software Foundation, Inc., | ||||
|     51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||||
| */ | ||||
| 
 | ||||
| 
 | ||||
| #include "renamedialog.h" | ||||
| #include "ui_rename-dialog.h" | ||||
| #include <QStringBuilder> | ||||
| #include <QPushButton> | ||||
| #include "icontheme.h" | ||||
| 
 | ||||
| using namespace Fm; | ||||
| 
 | ||||
| RenameDialog::RenameDialog(FmFileInfo* src, FmFileInfo* dest, QWidget* parent, Qt::WindowFlags f): | ||||
|   QDialog(parent, f), | ||||
|   action_(ActionIgnore), | ||||
|   applyToAll_(false) { | ||||
| 
 | ||||
|   ui = new Ui::RenameDialog(); | ||||
|   ui->setupUi(this); | ||||
| 
 | ||||
|   FmPath* path = fm_file_info_get_path(dest); | ||||
|   FmIcon* srcIcon = fm_file_info_get_icon(src); | ||||
|   FmIcon* destIcon = fm_file_info_get_icon(dest); | ||||
| 
 | ||||
|   // show info for the source file
 | ||||
|   QIcon icon = IconTheme::icon(srcIcon); | ||||
|   QSize iconSize(fm_config->big_icon_size, fm_config->big_icon_size); | ||||
|   QPixmap pixmap = icon.pixmap(iconSize); | ||||
|   ui->srcIcon->setPixmap(pixmap); | ||||
| 
 | ||||
|   QString infoStr; | ||||
|   const char* disp_size = fm_file_info_get_disp_size(src); | ||||
|   if(disp_size) { | ||||
|     infoStr = QString(tr("Type: %1\nSize: %2\nModified: %3")) | ||||
|                 .arg(QString::fromUtf8(fm_file_info_get_desc(src))) | ||||
|                 .arg(QString::fromUtf8(disp_size)) | ||||
|                 .arg(QString::fromUtf8(fm_file_info_get_disp_mtime(src))); | ||||
|   } | ||||
|   else { | ||||
|     infoStr = QString(tr("Type: %1\nModified: %2")) | ||||
|                 .arg(QString::fromUtf8(fm_file_info_get_desc(src))) | ||||
|                 .arg(QString::fromUtf8(fm_file_info_get_disp_mtime(src))); | ||||
|   } | ||||
|   ui->srcInfo->setText(infoStr); | ||||
| 
 | ||||
|   // show info for the dest file
 | ||||
|   icon = IconTheme::icon(destIcon); | ||||
|   pixmap = icon.pixmap(iconSize); | ||||
|   ui->destIcon->setPixmap(pixmap); | ||||
| 
 | ||||
|   disp_size = fm_file_info_get_disp_size(dest); | ||||
|   if(disp_size) { | ||||
|     infoStr = QString(tr("Type: %1\nSize: %2\nModified: %3")) | ||||
|                 .arg(QString::fromUtf8(fm_file_info_get_desc(dest))) | ||||
|                 .arg(QString::fromUtf8(disp_size)) | ||||
|                 .arg(QString::fromUtf8(fm_file_info_get_disp_mtime(dest))); | ||||
|   } | ||||
|   else { | ||||
|     infoStr = QString(tr("Type: %1\nModified: %3")) | ||||
|                 .arg(QString::fromUtf8(fm_file_info_get_desc(src))) | ||||
|                 .arg(QString::fromUtf8(fm_file_info_get_disp_mtime(src))); | ||||
|   } | ||||
|   ui->destInfo->setText(infoStr); | ||||
| 
 | ||||
|   char* basename = fm_path_display_basename(path); | ||||
|   ui->fileName->setText(QString::fromUtf8(basename)); | ||||
|   oldName_ = basename; | ||||
|   g_free(basename); | ||||
|   connect(ui->fileName, &QLineEdit::textChanged, this, &RenameDialog::onFileNameChanged); | ||||
| 
 | ||||
|   // add "Rename" button
 | ||||
|   QAbstractButton* button = ui->buttonBox->button(QDialogButtonBox::Ok); | ||||
|   button->setText(tr("&Overwrite")); | ||||
|   // FIXME: there seems to be no way to place the Rename button next to the overwrite one.
 | ||||
|   renameButton_ = ui->buttonBox->addButton(tr("&Rename"), QDialogButtonBox::ActionRole); | ||||
|   connect(renameButton_, &QPushButton::clicked, this, &RenameDialog::onRenameClicked); | ||||
|   renameButton_->setEnabled(false); // disabled by default
 | ||||
| 
 | ||||
|   button = ui->buttonBox->button(QDialogButtonBox::Ignore); | ||||
|   connect(button, &QPushButton::clicked, this, &RenameDialog::onIgnoreClicked); | ||||
| } | ||||
| 
 | ||||
| RenameDialog::~RenameDialog() { | ||||
|   delete ui; | ||||
| } | ||||
| 
 | ||||
| void RenameDialog::onRenameClicked() { | ||||
|   action_ = ActionRename; | ||||
|   QDialog::done(QDialog::Accepted); | ||||
| } | ||||
| 
 | ||||
| void RenameDialog::onIgnoreClicked() { | ||||
|   action_ = ActionIgnore; | ||||
| } | ||||
| 
 | ||||
| // the overwrite button
 | ||||
| void RenameDialog::accept() { | ||||
|   action_ = ActionOverwrite; | ||||
|   applyToAll_ = ui->applyToAll->isChecked(); | ||||
|   QDialog::accept(); | ||||
| } | ||||
| 
 | ||||
| // cancel, or close the dialog
 | ||||
| void RenameDialog::reject() { | ||||
|   action_ = ActionCancel; | ||||
|   QDialog::reject(); | ||||
| } | ||||
| 
 | ||||
| void RenameDialog::onFileNameChanged(QString newName) { | ||||
|   newName_ = newName; | ||||
|   // FIXME: check if the name already exists in the current dir
 | ||||
|   bool hasNewName = (newName_ != oldName_); | ||||
|   renameButton_->setEnabled(hasNewName); | ||||
|   renameButton_->setDefault(hasNewName); | ||||
| 
 | ||||
|   // change default button to rename rather than overwrire 
 | ||||
|   // if the user typed a new filename
 | ||||
|   QPushButton* overwriteButton = static_cast<QPushButton*>(ui->buttonBox->button(QDialogButtonBox::Ok)); | ||||
|   overwriteButton->setEnabled(!hasNewName); | ||||
|   overwriteButton->setDefault(!hasNewName); | ||||
| } | ||||
							
								
								
									
										83
									
								
								libfm-qt/renamedialog.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										83
									
								
								libfm-qt/renamedialog.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,83 @@ | ||||
| /*
 | ||||
| 
 | ||||
|     Copyright (C) 2013  Hong Jen Yee (PCMan) <pcman.tw@gmail.com> | ||||
| 
 | ||||
|     This program is free software; you can redistribute it and/or modify | ||||
|     it under the terms of the GNU General Public License as published by | ||||
|     the Free Software Foundation; either version 2 of the License, or | ||||
|     (at your option) any later version. | ||||
| 
 | ||||
|     This program is distributed in the hope that it will be useful, | ||||
|     but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|     GNU General Public License for more details. | ||||
| 
 | ||||
|     You should have received a copy of the GNU General Public License along | ||||
|     with this program; if not, write to the Free Software Foundation, Inc., | ||||
|     51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||||
| */ | ||||
| 
 | ||||
| 
 | ||||
| #ifndef FM_RENAMEDIALOG_H | ||||
| #define FM_RENAMEDIALOG_H | ||||
| 
 | ||||
| #include "libfmqtglobals.h" | ||||
| #include <QDialog> | ||||
| #include <libfm/fm.h> | ||||
| 
 | ||||
| namespace Ui { | ||||
|   class RenameDialog; | ||||
| }; | ||||
| 
 | ||||
| class QPushButton; | ||||
| 
 | ||||
| namespace Fm { | ||||
| 
 | ||||
| class LIBFM_QT_API RenameDialog : public QDialog { | ||||
| Q_OBJECT | ||||
| 
 | ||||
| public: | ||||
|   enum Action { | ||||
|     ActionCancel, | ||||
|     ActionRename, | ||||
|     ActionOverwrite, | ||||
|     ActionIgnore | ||||
|   }; | ||||
| 
 | ||||
| public: | ||||
|   explicit RenameDialog(FmFileInfo* src, FmFileInfo* dest, QWidget* parent = 0, Qt::WindowFlags f = 0); | ||||
|   virtual ~RenameDialog(); | ||||
| 
 | ||||
|   Action action() { | ||||
|     return action_; | ||||
|   } | ||||
| 
 | ||||
|   bool applyToAll() { | ||||
|     return applyToAll_; | ||||
|   } | ||||
|    | ||||
|   QString newName() { | ||||
|     return newName_; | ||||
|   } | ||||
| 
 | ||||
| protected Q_SLOTS: | ||||
|   void onRenameClicked(); | ||||
|   void onIgnoreClicked(); | ||||
|   void onFileNameChanged(QString newName); | ||||
| 
 | ||||
| protected: | ||||
|   void accept(); | ||||
|   void reject(); | ||||
| 
 | ||||
| private: | ||||
|   Ui::RenameDialog* ui; | ||||
|   QPushButton* renameButton_; | ||||
|   Action action_; | ||||
|   bool applyToAll_; | ||||
|   QString oldName_; | ||||
|   QString newName_; | ||||
| }; | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| #endif // FM_RENAMEDIALOG_H
 | ||||
							
								
								
									
										234
									
								
								libfm-qt/sidepane.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										234
									
								
								libfm-qt/sidepane.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,234 @@ | ||||
| /*
 | ||||
| 
 | ||||
|     Copyright (C) 2013  Hong Jen Yee (PCMan) <pcman.tw@gmail.com> | ||||
| 
 | ||||
|     This program is free software; you can redistribute it and/or modify | ||||
|     it under the terms of the GNU General Public License as published by | ||||
|     the Free Software Foundation; either version 2 of the License, or | ||||
|     (at your option) any later version. | ||||
| 
 | ||||
|     This program is distributed in the hope that it will be useful, | ||||
|     but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|     GNU General Public License for more details. | ||||
| 
 | ||||
|     You should have received a copy of the GNU General Public License along | ||||
|     with this program; if not, write to the Free Software Foundation, Inc., | ||||
|     51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||||
| */ | ||||
| 
 | ||||
| 
 | ||||
| #include "sidepane.h" | ||||
| #include <QComboBox> | ||||
| #include <QVBoxLayout> | ||||
| #include <QHeaderView> | ||||
| #include "placesview.h" | ||||
| #include "dirtreeview.h" | ||||
| #include "dirtreemodel.h" | ||||
| #include "path.h" | ||||
| 
 | ||||
| namespace Fm { | ||||
| 
 | ||||
| SidePane::SidePane(QWidget* parent): | ||||
|   QWidget(parent), | ||||
|   showHidden_(false), | ||||
|   mode_(ModeNone), | ||||
|   view_(NULL), | ||||
|   combo_(NULL), | ||||
|   currentPath_(NULL), | ||||
|   iconSize_(24, 24) { | ||||
| 
 | ||||
|   verticalLayout = new QVBoxLayout(this); | ||||
|   verticalLayout->setContentsMargins(0, 0, 0, 0); | ||||
| 
 | ||||
|   combo_ = new QComboBox(this); | ||||
|   combo_->setFrame(false); | ||||
|   combo_->addItem(tr("Places")); | ||||
|   combo_->addItem(tr("Directory Tree")); | ||||
|   connect(combo_, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, &SidePane::onComboCurrentIndexChanged); | ||||
|   verticalLayout->addWidget(combo_); | ||||
| } | ||||
| 
 | ||||
| SidePane::~SidePane() { | ||||
|   if(currentPath_) | ||||
|     fm_path_unref(currentPath_); | ||||
|   // qDebug("delete SidePane");
 | ||||
| } | ||||
| 
 | ||||
| void SidePane::onPlacesViewChdirRequested(int type, FmPath* path) { | ||||
|   Q_EMIT chdirRequested(type, path); | ||||
| } | ||||
| 
 | ||||
| void SidePane::onDirTreeViewChdirRequested(int type, FmPath* path) { | ||||
|   Q_EMIT chdirRequested(type, path); | ||||
| } | ||||
| 
 | ||||
| void SidePane::onComboCurrentIndexChanged(int current) { | ||||
|   if(current != mode_) { | ||||
|     setMode(Mode(current)); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| void SidePane::setIconSize(QSize size) { | ||||
|   iconSize_ = size; | ||||
|   switch(mode_) { | ||||
|     case ModePlaces: | ||||
|     case ModeDirTree: | ||||
|       static_cast<QTreeView*>(view_)->setIconSize(size); | ||||
|       break; | ||||
|     default:; | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| void SidePane::setCurrentPath(FmPath* path) { | ||||
|   Q_ASSERT(path != NULL); | ||||
|   if(currentPath_) | ||||
|     fm_path_unref(currentPath_); | ||||
|   currentPath_ = fm_path_ref(path); | ||||
|   switch(mode_) { | ||||
|     case ModePlaces: | ||||
|       static_cast<PlacesView*>(view_)->setCurrentPath(path); | ||||
|       break; | ||||
|     case ModeDirTree: | ||||
|       static_cast<DirTreeView*>(view_)->setCurrentPath(path); | ||||
|       break; | ||||
|     default:; | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| SidePane::Mode SidePane::modeByName(const char* str) { | ||||
|   if(str == NULL) | ||||
|     return ModeNone; | ||||
|   if(strcmp(str, "places") == 0) | ||||
|     return ModePlaces; | ||||
|   if(strcmp(str, "dirtree") == 0) | ||||
|     return ModeDirTree; | ||||
|   return ModeNone; | ||||
| } | ||||
| 
 | ||||
| const char* SidePane::modeName(SidePane::Mode mode) { | ||||
|   switch(mode) { | ||||
|   case ModePlaces: | ||||
|     return "places"; | ||||
|   case ModeDirTree: | ||||
|     return "dirtree"; | ||||
|   default: | ||||
|     return NULL; | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| #if 0 // FIXME: are these APIs from libfm-qt needed?
 | ||||
| 
 | ||||
| QString SidePane::modeLabel(SidePane::Mode mode) { | ||||
|   switch(mode) { | ||||
|   case ModePlaces: | ||||
|     return tr("Places"); | ||||
|   case ModeDirTree: | ||||
|     return tr("Directory Tree"); | ||||
|   } | ||||
|   return QString(); | ||||
| } | ||||
| 
 | ||||
| QString SidePane::modeTooltip(SidePane::Mode mode) { | ||||
|   switch(mode) { | ||||
|   case ModePlaces: | ||||
|     return tr("Shows list of common places, devices, and bookmarks in sidebar"); | ||||
|   case ModeDirTree: | ||||
|     return tr("Shows tree of directories in sidebar"); | ||||
|   } | ||||
|   return QString(); | ||||
| } | ||||
| #endif | ||||
| 
 | ||||
| bool SidePane::setHomeDir(const char* home_dir) { | ||||
|   if(view_ == NULL) | ||||
|     return false; | ||||
|   // TODO: SidePane::setHomeDir
 | ||||
| 
 | ||||
|   switch(mode_) { | ||||
|   case ModePlaces: | ||||
|     // static_cast<PlacesView*>(view_);
 | ||||
|     return true; | ||||
|   case ModeDirTree: | ||||
|     // static_cast<PlacesView*>(view_);
 | ||||
|     return true; | ||||
|   default:; | ||||
|   } | ||||
|   return true; | ||||
| } | ||||
| 
 | ||||
| void SidePane::initDirTree() { | ||||
|   // TODO
 | ||||
|   DirTreeModel* model = new DirTreeModel(view_); | ||||
|   FmFileInfoJob* job = fm_file_info_job_new(NULL, FM_FILE_INFO_JOB_NONE); | ||||
| 
 | ||||
|   GList* l; | ||||
|   /* query FmFileInfo for home dir and root dir, and then,
 | ||||
|     * add them to dir tree model */ | ||||
|   fm_file_info_job_add(job, fm_path_get_home()); | ||||
|   fm_file_info_job_add(job, fm_path_get_root()); | ||||
|   /* FIXME: maybe it's cleaner to use run_async here? */ | ||||
|   fm_job_run_sync_with_mainloop(FM_JOB(job)); | ||||
|   for(l = fm_file_info_list_peek_head_link(job->file_infos); l; l = l->next) { | ||||
|       FmFileInfo* fi = FM_FILE_INFO(l->data); | ||||
|       model->addRoot(fi); | ||||
|   } | ||||
|   g_object_unref(job); | ||||
| 
 | ||||
|   static_cast<DirTreeView*>(view_)->setModel(model); | ||||
| } | ||||
| 
 | ||||
| void SidePane::setMode(Mode mode) { | ||||
|   if(mode == mode_) | ||||
|     return; | ||||
| 
 | ||||
|   if(view_) { | ||||
|     delete view_; | ||||
|     view_ = NULL; | ||||
|     //if(sp->update_popup)
 | ||||
|     //  g_signal_handlers_disconnect_by_func(sp->view, on_item_popup, sp);
 | ||||
|   } | ||||
|   mode_ = mode; | ||||
| 
 | ||||
|   combo_->setCurrentIndex(mode); | ||||
|   switch(mode) { | ||||
|   case ModePlaces: { | ||||
|     PlacesView* placesView = new Fm::PlacesView(this); | ||||
|     view_ = placesView; | ||||
|     placesView->setIconSize(iconSize_); | ||||
|     placesView->setCurrentPath(currentPath_); | ||||
|     connect(placesView, &PlacesView::chdirRequested, this, &SidePane::onPlacesViewChdirRequested); | ||||
|     break; | ||||
|   } | ||||
|   case ModeDirTree: { | ||||
|     DirTreeView* dirTreeView = new Fm::DirTreeView(this); | ||||
|     view_ = dirTreeView; | ||||
|     initDirTree(); | ||||
|     dirTreeView->setIconSize(iconSize_); | ||||
|     dirTreeView->setCurrentPath(currentPath_); | ||||
|     connect(dirTreeView, &DirTreeView::chdirRequested, this, &SidePane::onDirTreeViewChdirRequested); | ||||
|     break; | ||||
|   } | ||||
|   default:; | ||||
|   } | ||||
|   if(view_) { | ||||
|     // if(sp->update_popup)
 | ||||
|     //  g_signal_connect(sp->view, "item-popup", G_CALLBACK(on_item_popup), sp);
 | ||||
|     verticalLayout->addWidget(view_); | ||||
|   } | ||||
|   Q_EMIT modeChanged(); | ||||
| } | ||||
| 
 | ||||
| bool SidePane::setShowHidden(bool show_hidden) { | ||||
|   if(view_ == NULL) | ||||
|     return false; | ||||
|   // TODO: SidePane::setShowHidden
 | ||||
| 
 | ||||
|   // spec = g_object_class_find_property(klass, "show-hidden");
 | ||||
|   //if(spec == NULL || spec->value_type != G_TYPE_BOOLEAN)
 | ||||
|   //  return false; /* isn't supported by view */
 | ||||
|   // g_object_set(sp->view, "show-hidden", show_hidden, NULL);
 | ||||
|   return true; | ||||
| } | ||||
| 
 | ||||
| } // namespace Fm
 | ||||
							
								
								
									
										126
									
								
								libfm-qt/sidepane.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										126
									
								
								libfm-qt/sidepane.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,126 @@ | ||||
| /*
 | ||||
| 
 | ||||
|     Copyright (C) 2013  Hong Jen Yee (PCMan) <pcman.tw@gmail.com> | ||||
| 
 | ||||
|     This program is free software; you can redistribute it and/or modify | ||||
|     it under the terms of the GNU General Public License as published by | ||||
|     the Free Software Foundation; either version 2 of the License, or | ||||
|     (at your option) any later version. | ||||
| 
 | ||||
|     This program is distributed in the hope that it will be useful, | ||||
|     but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|     GNU General Public License for more details. | ||||
| 
 | ||||
|     You should have received a copy of the GNU General Public License along | ||||
|     with this program; if not, write to the Free Software Foundation, Inc., | ||||
|     51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||||
| */ | ||||
| 
 | ||||
| 
 | ||||
| #ifndef FM_SIDEPANE_H | ||||
| #define FM_SIDEPANE_H | ||||
| 
 | ||||
| #include "libfmqtglobals.h" | ||||
| #include <libfm/fm.h> | ||||
| #include <QWidget> | ||||
| 
 | ||||
| class QComboBox; | ||||
| class QVBoxLayout; | ||||
| class QWidget; | ||||
| 
 | ||||
| namespace Fm { | ||||
| 
 | ||||
| class LIBFM_QT_API SidePane : public QWidget { | ||||
|   Q_OBJECT | ||||
| 
 | ||||
| public: | ||||
|   enum Mode { | ||||
|       ModeNone = -1, | ||||
|       ModePlaces = 0, | ||||
|       ModeDirTree, | ||||
|       NumModes | ||||
|   }; | ||||
| 
 | ||||
| public: | ||||
|   explicit SidePane(QWidget* parent = 0); | ||||
|   virtual ~SidePane(); | ||||
| 
 | ||||
|   QSize iconSize() { | ||||
|     return iconSize_; | ||||
|   } | ||||
| 
 | ||||
|   void setIconSize(QSize size); | ||||
|    | ||||
|   FmPath* currentPath() { | ||||
|     return currentPath_; | ||||
|   } | ||||
| 
 | ||||
|   void setCurrentPath(FmPath* path); | ||||
| 
 | ||||
|   void setMode(Mode mode); | ||||
| 
 | ||||
|   Mode mode() { | ||||
|     return mode_; | ||||
|   } | ||||
| 
 | ||||
|   QWidget* view() { | ||||
|     return view_; | ||||
|   } | ||||
| 
 | ||||
|   const char *modeName(Mode mode); | ||||
| 
 | ||||
|   Mode modeByName(const char *str); | ||||
| 
 | ||||
| #if 0 // FIXME: are these APIs from libfm-qt needed?
 | ||||
|   int modeCount(void) { | ||||
|     return NumModes; | ||||
|   } | ||||
| 
 | ||||
|   QString modeLabel(Mode mode); | ||||
| 
 | ||||
|   QString modeTooltip(Mode mode); | ||||
| #endif | ||||
| 
 | ||||
|   bool setShowHidden(bool show_hidden); | ||||
| 
 | ||||
|   bool showHidden() { | ||||
|     return showHidden_; | ||||
|   } | ||||
| 
 | ||||
|   bool setHomeDir(const char *home_dir); | ||||
| 
 | ||||
|   // libfm-gtk compatible alias
 | ||||
|   FmPath* getCwd() { | ||||
|     return currentPath(); | ||||
|   } | ||||
| 
 | ||||
|   void chdir(FmPath* path) { | ||||
|     setCurrentPath(path); | ||||
|   } | ||||
| 
 | ||||
| Q_SIGNALS: | ||||
|   void chdirRequested(int type, FmPath* path); | ||||
|   void modeChanged(); | ||||
| 
 | ||||
| protected Q_SLOTS: | ||||
|   void onPlacesViewChdirRequested(int type, FmPath* path); | ||||
|   void onDirTreeViewChdirRequested(int type, FmPath* path); | ||||
|   void onComboCurrentIndexChanged(int current); | ||||
| 
 | ||||
| private: | ||||
|   void initDirTree(); | ||||
|    | ||||
| private: | ||||
|   FmPath* currentPath_; | ||||
|   QWidget* view_; | ||||
|   QComboBox* combo_; | ||||
|   QVBoxLayout* verticalLayout; | ||||
|   QSize iconSize_; | ||||
|   Mode mode_; | ||||
|   bool showHidden_; | ||||
| }; | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| #endif // FM_SIDEPANE_H
 | ||||
							
								
								
									
										195
									
								
								libfm-qt/thumbnailloader.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										195
									
								
								libfm-qt/thumbnailloader.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,195 @@ | ||||
| /*
 | ||||
|     <one line to give the program's name and a brief idea of what it does.> | ||||
|     Copyright (C) 2013  PCMan <email> | ||||
| 
 | ||||
|     This program is free software; you can redistribute it and/or modify | ||||
|     it under the terms of the GNU General Public License as published by | ||||
|     the Free Software Foundation; either version 2 of the License, or | ||||
|     (at your option) any later version. | ||||
| 
 | ||||
|     This program is distributed in the hope that it will be useful, | ||||
|     but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|     GNU General Public License for more details. | ||||
| 
 | ||||
|     You should have received a copy of the GNU General Public License along | ||||
|     with this program; if not, write to the Free Software Foundation, Inc., | ||||
|     51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||||
| */ | ||||
| 
 | ||||
| 
 | ||||
| #include "thumbnailloader.h" | ||||
| #include <new> | ||||
| #include <QByteArray> | ||||
| 
 | ||||
| using namespace Fm; | ||||
| 
 | ||||
| // FmQImageWrapper is a GObject used to wrap QImage objects and use in glib-based libfm
 | ||||
| #define FM_TYPE_QIMAGE_WRAPPER              (fm_qimage_wrapper_get_type()) | ||||
| #define FM_QIMAGE_WRAPPER(obj)              (G_TYPE_CHECK_INSTANCE_CAST((obj),\ | ||||
| FM_TYPE_QIMAGE_WRAPPER, FmQImageWrapper)) | ||||
| #define FM_QIMAGE_WRAPPER_CLASS(klass)      (G_TYPE_CHECK_CLASS_CAST((klass),\ | ||||
| FM_TYPE_QIMAGE_WRAPPER, FmQImageWrapperClass)) | ||||
| #define FM_IS_QIMAGE_WRAPPER(obj)           (G_TYPE_CHECK_INSTANCE_TYPE((obj),\ | ||||
| FM_TYPE_QIMAGE_WRAPPER)) | ||||
| #define FM_IS_QIMAGE_WRAPPER_CLASS(klass)   (G_TYPE_CHECK_CLASS_TYPE((klass),\ | ||||
| FM_TYPE_QIMAGE_WRAPPER)) | ||||
| #define FM_QIMAGE_WRAPPER_GET_CLASS(obj)    (G_TYPE_INSTANCE_GET_CLASS((obj),\ | ||||
| FM_TYPE_QIMAGE_WRAPPER, FmQImageWrapperClass)) | ||||
| 
 | ||||
| typedef struct _FmQImageWrapper         FmQImageWrapper; | ||||
| typedef struct _FmQImageWrapperClass        FmQImageWrapperClass; | ||||
| 
 | ||||
| struct _FmQImageWrapper { | ||||
|   GObject parent; | ||||
|   QImage image; | ||||
| }; | ||||
| 
 | ||||
| struct _FmQImageWrapperClass { | ||||
|   GObjectClass parent_class; | ||||
| }; | ||||
| 
 | ||||
| GType       fm_qimage_wrapper_get_type(void); | ||||
| GObject*    fm_qimage_wrapper_new(void); | ||||
| static void fm_qimage_wrapper_finalize(GObject *self); | ||||
| 
 | ||||
| G_DEFINE_TYPE(FmQImageWrapper, fm_qimage_wrapper, G_TYPE_OBJECT) | ||||
| 
 | ||||
| static void fm_qimage_wrapper_class_init(FmQImageWrapperClass *klass) { | ||||
|   GObjectClass* object_class = G_OBJECT_CLASS(klass); | ||||
|   object_class->finalize = fm_qimage_wrapper_finalize; | ||||
| } | ||||
| 
 | ||||
| static void fm_qimage_wrapper_init(FmQImageWrapper *self) { | ||||
|   // placement new for QImage
 | ||||
|   new(&self->image) QImage(); | ||||
| } | ||||
| 
 | ||||
| static void fm_qimage_wrapper_finalize(GObject *self) { | ||||
|   FmQImageWrapper *wrapper = FM_QIMAGE_WRAPPER(self); | ||||
|   // placement delete
 | ||||
|   wrapper->image.~QImage(); | ||||
| } | ||||
| 
 | ||||
| GObject *fm_qimage_wrapper_new(QImage& image) { | ||||
|   FmQImageWrapper *wrapper = (FmQImageWrapper*)g_object_new(FM_TYPE_QIMAGE_WRAPPER, NULL); | ||||
|   wrapper->image = image; | ||||
|   return (GObject*)wrapper; | ||||
| } | ||||
| 
 | ||||
| ThumbnailLoader* ThumbnailLoader::theThumbnailLoader = NULL; | ||||
| bool ThumbnailLoader::localFilesOnly_ = true; | ||||
| int ThumbnailLoader::maxThumbnailFileSize_ = 0; | ||||
| 
 | ||||
| ThumbnailLoader::ThumbnailLoader() { | ||||
|   gboolean success; | ||||
|   // apply the settings to libfm
 | ||||
|   fm_config->thumbnail_local = localFilesOnly_; | ||||
|   fm_config->thumbnail_max = maxThumbnailFileSize_; | ||||
| 
 | ||||
|   FmThumbnailLoaderBackend qt_backend = { | ||||
|     readImageFromFile, | ||||
|     readImageFromStream, | ||||
|     writeImage, | ||||
|     scaleImage, | ||||
|     rotateImage, | ||||
|     getImageWidth, | ||||
|     getImageHeight, | ||||
|     getImageText, | ||||
|     setImageText | ||||
|   }; | ||||
|   success = fm_thumbnail_loader_set_backend(&qt_backend); | ||||
| } | ||||
| 
 | ||||
| ThumbnailLoader::~ThumbnailLoader() { | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| GObject* ThumbnailLoader::readImageFromFile(const char* filename) { | ||||
|   QImage image; | ||||
|   image.load(QString(filename)); | ||||
|   // qDebug("readImageFromFile: %s, %d", filename, image.isNull());
 | ||||
|   return image.isNull() ? NULL : fm_qimage_wrapper_new(image); | ||||
| } | ||||
| 
 | ||||
| GObject* ThumbnailLoader::readImageFromStream(GInputStream* stream, guint64 len, GCancellable* cancellable) { | ||||
|   // qDebug("readImageFromStream: %p, %llu", stream, len);
 | ||||
|   // FIXME: should we set a limit here? Otherwise if len is too large, we can run out of memory.
 | ||||
|   unsigned char* buffer = new unsigned char[len]; // allocate enough buffer
 | ||||
|   unsigned char* pbuffer = buffer; | ||||
|   int totalReadSize = 0; | ||||
|   while(!g_cancellable_is_cancelled(cancellable) && totalReadSize < len) { | ||||
|     int bytesToRead = totalReadSize + 4096 > len ? len - totalReadSize : 4096; | ||||
|     gssize readSize = g_input_stream_read(stream, pbuffer, bytesToRead, cancellable, NULL); | ||||
|     if(readSize == 0) // end of file
 | ||||
|       break; | ||||
|     else if(readSize == -1) // error
 | ||||
|       return NULL; | ||||
|     totalReadSize += readSize; | ||||
|     pbuffer += readSize; | ||||
|   } | ||||
|   QImage image; | ||||
|   image.loadFromData(buffer, totalReadSize); | ||||
|   delete []buffer; | ||||
|   return image.isNull() ? NULL : fm_qimage_wrapper_new(image); | ||||
| } | ||||
| 
 | ||||
| gboolean ThumbnailLoader::writeImage(GObject* image, const char* filename) { | ||||
|   FmQImageWrapper* wrapper = FM_QIMAGE_WRAPPER(image); | ||||
|   if(wrapper->image.isNull()) | ||||
|     return FALSE; | ||||
|   return (gboolean)wrapper->image.save(filename, "PNG"); | ||||
| } | ||||
| 
 | ||||
| GObject* ThumbnailLoader::scaleImage(GObject* ori_pix, int new_width, int new_height) { | ||||
|   // qDebug("scaleImage: %d, %d", new_width, new_height);
 | ||||
|   FmQImageWrapper* ori_wrapper = FM_QIMAGE_WRAPPER(ori_pix); | ||||
|   QImage scaled = ori_wrapper->image.scaled(new_width, new_height, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); | ||||
|   return scaled.isNull() ? NULL : fm_qimage_wrapper_new(scaled); | ||||
| } | ||||
| 
 | ||||
| GObject* ThumbnailLoader::rotateImage(GObject* image, int degree) { | ||||
|   FmQImageWrapper* wrapper = FM_QIMAGE_WRAPPER(image); | ||||
|   // degree values are 0, 90, 180, and 270 counterclockwise.
 | ||||
|   // In Qt, QMatrix does rotation counterclockwise as well.
 | ||||
|   // However, because the y axis of widget coordinate system is downward,
 | ||||
|   // the real effect of the coordinate transformation becomes clockwise rotation.
 | ||||
|   // So we need to use (360 - degree) here.
 | ||||
|   // Quote from QMatrix API doc:
 | ||||
|   // Note that if you apply a QMatrix to a point defined in widget 
 | ||||
|   // coordinates, the direction of the rotation will be clockwise because
 | ||||
|   // the y-axis points downwards.
 | ||||
|   QImage rotated = wrapper->image.transformed(QMatrix().rotate(360 - degree)); | ||||
|   return rotated.isNull() ? NULL : fm_qimage_wrapper_new(rotated); | ||||
| } | ||||
| 
 | ||||
| int ThumbnailLoader::getImageWidth(GObject* image) { | ||||
|   FmQImageWrapper* wrapper = FM_QIMAGE_WRAPPER(image); | ||||
|   return wrapper->image.width(); | ||||
| } | ||||
| 
 | ||||
| int ThumbnailLoader::getImageHeight(GObject* image) { | ||||
|   FmQImageWrapper* wrapper = FM_QIMAGE_WRAPPER(image); | ||||
|   return wrapper->image.height(); | ||||
| } | ||||
| 
 | ||||
| char* ThumbnailLoader::getImageText(GObject* image, const char* key) { | ||||
|   FmQImageWrapper* wrapper = FM_QIMAGE_WRAPPER(image); | ||||
|   QByteArray text = wrapper->image.text(key).toLatin1(); | ||||
|   return (char*)g_memdup(text.constData(), text.length()); | ||||
| } | ||||
| 
 | ||||
| gboolean ThumbnailLoader::setImageText(GObject* image, const char* key, const char* val) { | ||||
|   FmQImageWrapper* wrapper = FM_QIMAGE_WRAPPER(image); | ||||
|   wrapper->image.setText(key, val); | ||||
|   return TRUE; | ||||
| } | ||||
| 
 | ||||
| QImage ThumbnailLoader::image(FmThumbnailLoader* result) { | ||||
|   FmQImageWrapper* wrapper = FM_QIMAGE_WRAPPER(fm_thumbnail_loader_get_data(result)); | ||||
|   if(wrapper) { | ||||
|     return wrapper->image; | ||||
|   } | ||||
|   return QImage(); | ||||
| } | ||||
| 
 | ||||
							
								
								
									
										99
									
								
								libfm-qt/thumbnailloader.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										99
									
								
								libfm-qt/thumbnailloader.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,99 @@ | ||||
| /*
 | ||||
|     <one line to give the program's name and a brief idea of what it does.> | ||||
|     Copyright (C) 2013  PCMan <email> | ||||
| 
 | ||||
|     This program is free software; you can redistribute it and/or modify | ||||
|     it under the terms of the GNU General Public License as published by | ||||
|     the Free Software Foundation; either version 2 of the License, or | ||||
|     (at your option) any later version. | ||||
| 
 | ||||
|     This program is distributed in the hope that it will be useful, | ||||
|     but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|     GNU General Public License for more details. | ||||
| 
 | ||||
|     You should have received a copy of the GNU General Public License along | ||||
|     with this program; if not, write to the Free Software Foundation, Inc., | ||||
|     51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||||
| */ | ||||
| 
 | ||||
| 
 | ||||
| #ifndef FM_THUMBNAILLOADER_H | ||||
| #define FM_THUMBNAILLOADER_H | ||||
| 
 | ||||
| #include "libfmqtglobals.h" | ||||
| #include <QImage> | ||||
| #include <libfm/fm.h> | ||||
| #include <gio/gio.h> | ||||
| 
 | ||||
| namespace Fm { | ||||
| 
 | ||||
| class LIBFM_QT_API ThumbnailLoader { | ||||
| 
 | ||||
| public: | ||||
|   ThumbnailLoader(); | ||||
|   virtual ~ThumbnailLoader(); | ||||
|    | ||||
|   static ThumbnailLoader* instance() { | ||||
|     return theThumbnailLoader; | ||||
|   } | ||||
| 
 | ||||
|   static FmThumbnailLoader* load(FmFileInfo* fileInfo, int size, FmThumbnailLoaderCallback callback, gpointer user_data) { | ||||
|     // qDebug("load thumbnail: %s", fm_file_info_get_disp_name(fileInfo));
 | ||||
|     return fm_thumbnail_loader_load(fileInfo, size, callback, user_data); | ||||
|   } | ||||
| 
 | ||||
|   static FmFileInfo* fileInfo(FmThumbnailLoader* result) { | ||||
|     return fm_thumbnail_loader_get_file_info(result); | ||||
|   } | ||||
| 
 | ||||
|   static void cancel(FmThumbnailLoader* result) { | ||||
|     fm_thumbnail_loader_cancel(result); | ||||
|   } | ||||
| 
 | ||||
|   static QImage image(FmThumbnailLoader* result); | ||||
| 
 | ||||
|   static int size(FmThumbnailLoader* result) { | ||||
|     return fm_thumbnail_loader_get_size(result); | ||||
|   } | ||||
| 
 | ||||
|   static void setLocalFilesOnly(bool value) { | ||||
|     localFilesOnly_ = value; | ||||
|     if(fm_config) | ||||
|       fm_config->thumbnail_local = localFilesOnly_; | ||||
|   } | ||||
| 
 | ||||
|   static bool localFilesOnly() { | ||||
|     return localFilesOnly_; | ||||
|   } | ||||
| 
 | ||||
|   static int maxThumbnailFileSize() { | ||||
|     return maxThumbnailFileSize_; | ||||
|   } | ||||
|    | ||||
|   static void setMaxThumbnailFileSize(int size) { | ||||
|     maxThumbnailFileSize_ = size; | ||||
|     if(fm_config) | ||||
|       fm_config->thumbnail_max = maxThumbnailFileSize_; | ||||
|   } | ||||
| 
 | ||||
| private: | ||||
|   static GObject* readImageFromFile(const char* filename); | ||||
|   static GObject* readImageFromStream(GInputStream* stream, guint64 len, GCancellable* cancellable); | ||||
|   static gboolean writeImage(GObject* image, const char* filename); | ||||
|   static GObject* scaleImage(GObject* ori_pix, int new_width, int new_height); | ||||
|   static int getImageWidth(GObject* image); | ||||
|   static int getImageHeight(GObject* image); | ||||
|   static char* getImageText(GObject* image, const char* key); | ||||
|   static gboolean setImageText(GObject* image, const char* key, const char* val); | ||||
|   static GObject* rotateImage(GObject* image, int degree); | ||||
| 
 | ||||
| private: | ||||
|   static ThumbnailLoader* theThumbnailLoader; | ||||
|   static bool localFilesOnly_; | ||||
|   static int maxThumbnailFileSize_; | ||||
| }; | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| #endif // FM_THUMBNAILLOADER_H
 | ||||
							
								
								
									
										1019
									
								
								libfm-qt/translations/libfm-qt_ar.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1019
									
								
								libfm-qt/translations/libfm-qt_ar.ts
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							Some files were not shown because too many files have changed in this diff Show More
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user