diff --git a/gui/COPYING b/gui/COPYING new file mode 100644 index 00000000..3e1cf82c --- /dev/null +++ b/gui/COPYING @@ -0,0 +1,283 @@ +QSynergy is free software and published under the following +license: + + 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 diff --git a/gui/INSTALL b/gui/INSTALL new file mode 100644 index 00000000..1d5d75a8 --- /dev/null +++ b/gui/INSTALL @@ -0,0 +1,45 @@ +QSynergy for Unix build instructions +==================================== + + +Requirements: +------------- +Qt 4.3 or newer. QSynergy can not be built or used with earlier versions. Get +it from http://trolltech.com/downloads/opensource + +If you install from your distributions's package repository, don't forget to +install the development packages as well. + + +Compilation: +------------ +1. Unpack the sources to a directory. + +2. Run qmake for your Qt4 installtion in that directory (make sure you do not +accidentally run the Qt3-qmake if your Linux distribution installs both, like +Ubuntu seems to do). See the qmake documentation for available command line +options. You might want to enable a release build by passing CONFIG="-debug ++release" to qmake. + +3. qmake will have generated a Makefile now, so just run make in that directory +and wait for the build to finish. + + +Installation: +------------- +There is no installation required. Just copy the qsynergy binary to somewhere +in your path. The binary does not depend on any other files. + + +Building a Debian package +------------------------- +The source tarball comes with a fully configured debian subdir, so it's easy to +build a Debian package. A script is available in the dist subdir, deb.sh, that +will do this automatically. Just run this script. If you have all the tools and +helper programs installed to build a Debian package, you should get a package +for your platform in tmp/debian. + +Please contact QSynergy's author to contribute your newly built package for +download on the QSynergy website: vl@fidra.de + + diff --git a/gui/README b/gui/README new file mode 100644 index 00000000..69b29c36 --- /dev/null +++ b/gui/README @@ -0,0 +1,97 @@ +QSynergy +======== + +Version 0.9.0 +http://www.volker-lanz.de/en/software/qsynergy/ + + +About QSynergy +-------------- +QSynergy is a graphical front end for Synergy. Synergy lets a user control more +than one computer with a single mouse and keyboard (and has lots and lots of +extra features on top of that). Learn more about Synergy itself or get it from: +http://synergy2.sourceforge.net + +Synergy only has a GUI for MS Windows. QSynergy was written to step in and fill +this gap for users on Mac and Unix platforms. Of course, QSynergy can also be +used on MS Windows. + + +Running and using QSynergy +-------------------------- +- Because QSynergy is a graphical frontend for Synergy, it does not make much + sense to run it without having Synergy installed. So if you have not done so + already, get it from http://synergy2.sourceforge.net and install it. + +- See the Synergy documentation (http://synergy2.sourceforge.net/) for all + topics concerning what you can do with Synergy. + +- In QSynergy, first, go to Edit -> Settings and check if the path names for + synergys (the Synergy server binary) and synergyc (the client) are correct. + If they are not, set them by hand or browse to their locations. + +- QSynergy knows three modes: + + 1. Run Synergy as a client (so the computer QSynergy runs on will be + controlled from another computer). + + 2. Run Synergy as a server with an existing configuration you have written + already (that's just like running Synergy from the command line with the "-c" + option). + + 3. Interactively and graphically create a server configuration and run + Synergy with this configuration. Herein lies the main benefit of using + QSynergy instead of the command line Synergy version for Unix and Mac. + +- Running as a client: Simply tick the "be a client" checkbox, enter the name + of the computer to connect to and push the "Start" button. + +- Running as a server with an existing configuration: Tick the "be a server" + checkbox and the radio button that says you would like to use your own + configuration file. Then enter the path to this configuration file (or browse + for it). Finally, push the "Start" button. + +- Using QSynergy to configure the Synergy server: Tick the "be a server" + checkbox and the radio button that says you would like to interactively + configure Synergy. Then push the "Configure server" button. After you have + finished setting up your configuration, push the "Start" button. QSynergy + will remember your configuration across restarts, so there is no need to + configure again the next time you run QSynergy -- unless you change your + computer setup, of course. + +- On MS Windows and X11, QSynergy will minimize to the tray if you close the + main window or pick the Window -> Minimize menu entry. Double click on the + icon in the tray or right click on this icon and pick Restore to restore the + window. + +- On X11, QSynergy will be restarted with X11 if your X11 server correctly + implements session handling. On MS Windows, you will have to add QSynergy to + your Autostart folder. On the Mac, set it to automatically run when the + Finder starts. + + +Known bugs and limitations +-------------------------- +- It is not possible to configure partial links (e.g., only 50% of a screen's + edge linking to another screen) + +- If you configure a hotkey for a specific screen and later delete this screen, + QSynergy does not warn you that this will lead to an invalid configuration. + +- Importing existing synery server configuration files is not possible. + +- There is no true communication channel between Synergy itself and QSynergy. + This means that QSynergy cannot really know if Synergy itself is working or + has encountered any problems. QSynergy only knows "Synergy is running" or + "Synergy has quit with an error". + +- Mac OS X only: The look and feel of QSynergy is not quite right for the + platform. This is due to limitations in Qt. + + +License +------- +QSynergy is written using the Qt Toolkit. It is free software released under +the GPLv2. + + diff --git a/gui/dist/bundle.sh b/gui/dist/bundle.sh new file mode 100644 index 00000000..0d263276 --- /dev/null +++ b/gui/dist/bundle.sh @@ -0,0 +1,43 @@ +#!/bin/bash + +# Make sure to adjust the QTDIR variable to where you have Qt's libraries installed. +# install_name_tool will _silently_ fail if the path is incorrect. Also, it will +# _not_ normalize double slashes. +APPNAME=QSynergy +QTDIR=$HOME/sw/qt/lib +TMPBUNDLE=tmpbundledir +PLATFORMS="ppc x86" + +cd $(dirname $0)/.. + +rm -rf ${APPNAME}.app +make distclean +qmake -spec macx-g++ "CONFIG+=release" "CONFIG-=debug" "CONFIG+=${PLATFORMS}" "QMAKE_MAC_SDK=/Developer/SDKs/MacOSX10.4u.sdk" +make +mkdir ${APPNAME}.app/Contents/Frameworks + +cp -R ${QTDIR}/QtCore.framework ${APPNAME}.app/Contents/Frameworks +cp -R ${QTDIR}/QtGui.framework ${APPNAME}.app/Contents/Frameworks +cp -R ${QTDIR}/QtNetwork.framework ${APPNAME}.app/Contents/Frameworks + +rm -rf ${APPNAME}.app/Contents/Frameworks/Qt*.framework/Versions/4/Qt*_debug +rm -rf ${APPNAME}.app/Contents/Frameworks/Qt*.framework/Versions/4/Headers + +install_name_tool -id @executable_path/../Frameworks/QtCore.framework/Versions/4/QtCore ${APPNAME}.app/Contents/Frameworks/QtCore.framework/Versions/4/QtCore +install_name_tool -id @executable_path/../Frameworks/QtGui.framework/Versions/4/QtGui ${APPNAME}.app/Contents/Frameworks/QtGui.framework/Versions/4/QtGui +install_name_tool -id @executable_path/../Frameworks/QtNetwork.framework/Versions/4/QtNetwork ${APPNAME}.app/Contents/Frameworks/QtNetwork.framework/Versions/4/QtNetwork + +install_name_tool -change ${QTDIR}/QtCore.framework/Versions/4/QtCore @executable_path/../Frameworks/QtCore.framework/Versions/4/QtCore ${APPNAME}.app/Contents/MacOs/$APPNAME +install_name_tool -change ${QTDIR}/QtGui.framework/Versions/4/QtGui @executable_path/../Frameworks/QtGui.framework/Versions/4/QtGui ${APPNAME}.app/Contents/MacOs/$APPNAME +install_name_tool -change ${QTDIR}/QtNetwork.framework/Versions/4/QtNetwork @executable_path/../Frameworks/QtNetwork.framework/Versions/4/QtNetwork ${APPNAME}.app/Contents/MacOs/$APPNAME + +install_name_tool -change ${QTDIR}/QtCore.framework/Versions/4/QtCore @executable_path/../Frameworks/QtCore.framework/Versions/4/QtCore ${APPNAME}.app/Contents/Frameworks/QtGui.framework/Versions/4/QtGui +install_name_tool -change ${QTDIR}/QtCore.framework/Versions/4/QtCore @executable_path/../Frameworks/QtCore.framework/Versions/4/QtCore ${APPNAME}.app/Contents/Frameworks/QtNetwork.framework/Versions/4/QtNetwork + +mkdir ${TMPBUNDLE} +mv ${APPNAME}.app ${TMPBUNDLE} +cp COPYING README ${TMPBUNDLE} +hdiutil create -ov -srcfolder ${TMPBUNDLE} -format UDBZ -volname "$APPNAME" "${APPNAME}.dmg" +hdiutil internet-enable -yes "${APPNAME}.dmg" +mv ${TMPBUNDLE}/${APPNAME}.app . +rm -rf ${TMPBUNDLE} diff --git a/gui/dist/deb.sh b/gui/dist/deb.sh new file mode 100644 index 00000000..baa1b926 --- /dev/null +++ b/gui/dist/deb.sh @@ -0,0 +1,14 @@ +#!/bin/bash + +cd $(dirname $0)/.. + +dist/tarball.sh + +rm -rf tmp/debian +mkdir tmp/debian +cd tmp/debian +tar xfz ../../qsynergy*.tar.gz +cd qsynergy* +ln -s dist/debian debian +dpkg-buildpackage -sgpg -rfakeroot + diff --git a/gui/dist/debian/changelog b/gui/dist/debian/changelog new file mode 100644 index 00000000..61cfc6a4 --- /dev/null +++ b/gui/dist/debian/changelog @@ -0,0 +1,6 @@ +qsynergy (0.9.0-1) unstable; urgency=low + + * Initial release. + + -- Volker Lanz Fri, 27 Jun 2008 17:08:49 +0200 + diff --git a/gui/dist/debian/compat b/gui/dist/debian/compat new file mode 100644 index 00000000..7ed6ff82 --- /dev/null +++ b/gui/dist/debian/compat @@ -0,0 +1 @@ +5 diff --git a/gui/dist/debian/control b/gui/dist/debian/control new file mode 100644 index 00000000..5ca81a20 --- /dev/null +++ b/gui/dist/debian/control @@ -0,0 +1,16 @@ +Source: qsynergy +Section: net +Priority: optional +Maintainer: Volker Lanz +Build-Depends: debhelper (>= 5), qt4-dev-tools (>= 4.3.0), libqt4-dev (>= 4.3.0) +Standards-Version: 3.7.2 + +Package: qsynergy +Architecture: any +Depends: ${shlibs:Depends}, synergy (>= 1.3.1) +Description: A comprehensive and easy to use graphical front end for synergy. + QSynergy is a comprehensive and easy to use graphical front end for synergy. + It allows you to configure synergy as a client or server and gives you + complete and painless access to all of synergy's options. Visit + http://www.volker-lanz.de/en/software/qsynergy/ for more information about + this program. diff --git a/gui/dist/debian/copyright b/gui/dist/debian/copyright new file mode 100644 index 00000000..2f51fa89 --- /dev/null +++ b/gui/dist/debian/copyright @@ -0,0 +1,20 @@ +This package was debianized by Volker Lanz on +Fri, 27 Jun 2008 17:08:49 +0200. + +It was downloaded from http://www.volker-lanz.de/en/software/qsynergy/ + +Upstream Author: Volker Lanz + +Copyright: Copyright (C) 2008 Volker Lanz, Fidra Software Entwicklung. + +License: + +This software is copyright (c) 2008 Volker Lanz, Fidra Software Entwicklung. + +You are free to distribute this software under the terms of the GNU General +Public License version 2. On Debian systems, the complete text of the GNU +General Public License can be found in the file +`/usr/share/common-licenses/GPL-2'. + +The Debian packaging is (C) 2008, Volker Lanz and is licensed +under the GPL, see `/usr/share/common-licenses/GPL'. diff --git a/gui/dist/debian/dirs b/gui/dist/debian/dirs new file mode 100644 index 00000000..a1542cf0 --- /dev/null +++ b/gui/dist/debian/dirs @@ -0,0 +1,3 @@ +usr/bin +usr/share/pixmaps +usr/share/applications diff --git a/gui/dist/debian/docs b/gui/dist/debian/docs new file mode 100644 index 00000000..e845566c --- /dev/null +++ b/gui/dist/debian/docs @@ -0,0 +1 @@ +README diff --git a/gui/dist/debian/install b/gui/dist/debian/install new file mode 100644 index 00000000..5a70dfad --- /dev/null +++ b/gui/dist/debian/install @@ -0,0 +1,2 @@ +debian/qsynergy.desktop usr/share/applications +dist/qsynergy.xpm usr/share/pixmaps diff --git a/gui/dist/debian/menu b/gui/dist/debian/menu new file mode 100644 index 00000000..2c41b132 --- /dev/null +++ b/gui/dist/debian/menu @@ -0,0 +1,4 @@ +?package(qsynergy):needs="X11" section="Applications/System/Administration" \ + title="QSynergy" command="/usr/bin/qsynergy" \ + longtitle="Graphical interface to configure and run Synergy" \ + icon="/usr/share/pixmaps/qsynergy.xpm" diff --git a/gui/dist/debian/qsynergy.desktop b/gui/dist/debian/qsynergy.desktop new file mode 100644 index 00000000..ffed6bfb --- /dev/null +++ b/gui/dist/debian/qsynergy.desktop @@ -0,0 +1,9 @@ +[Desktop Entry] +Encoding=UTF-8 +Name=QSynergy +Comment=A graphical user interface for synergy. +Exec=qsynergy +Icon=qsynergy +Terminal=false +Type=Application +Categories=Utility diff --git a/gui/dist/debian/rules b/gui/dist/debian/rules new file mode 100644 index 00000000..4a534382 --- /dev/null +++ b/gui/dist/debian/rules @@ -0,0 +1,54 @@ +#!/usr/bin/make -f + +# export DH_VERBOSE=1 + +configure: configure-stamp + +configure-stamp: + dh_testdir + -$(MAKE) distclean + qmake-qt4 CONFIG-="debug" CONFIG+="release" DESTDIR=$(CURDIR)/debian/qsynergy/usr/bin/ + touch $@ + +build: build-stamp + +build-stamp: configure-stamp + dh_testdir + $(MAKE) + +clean: + dh_testdir + dh_testroot + rm -f build-stamp configure-stamp + qmake-qt4 CONFIG-="debug" CONFIG+="release" + $(MAKE) distclean + dh_clean + +install: build + dh_testdir + dh_testroot + dh_clean -k + dh_install + dh_installdirs + $(MAKE) + +binary-indep: build install +binary-arch: build install + dh_testdir + dh_testroot + dh_installchangelogs + dh_installdocs + dh_installmenu + dh_strip + dh_compress + dh_desktop + dh_fixperms + dh_installdeb + dh_shlibdeps + dh_gencontrol + dh_md5sums + dh_builddeb + +binary: binary-indep binary-arch +.PHONY: build clean binary-indep binary-arch binary install configure + diff --git a/gui/dist/qsynergy.nsi b/gui/dist/qsynergy.nsi new file mode 100644 index 00000000..2b9fe24f --- /dev/null +++ b/gui/dist/qsynergy.nsi @@ -0,0 +1,180 @@ +!include "MUI.nsh" + +!define MUI_ABORTWARNING +!define MUI_COMPONENTSPAGE_SMALLDESC + +!define MUI_HEADERIMAGE +!define MUI_HEADERIMAGE_BITMAP "${NSISDIR}\Contrib\Graphics\Header\orange.bmp" + +!define MUI_WELCOMEFINISHPAGE_BITMAP "${NSISDIR}\Contrib\Graphics\Wizard\orange.bmp" +!define MUI_UNWELCOMEFINISHPAGE_BITMAP "${NSISDIR}\Contrib\Graphics\Wizard\orange.bmp" + +!define MUI_ICON "${NSISDIR}\Contrib\Graphics\Icons\orange-install.ico" +!define MUI_UNICON "${NSISDIR}\Contrib\Graphics\Icons\orange-uninstall.ico" + +!define VERSION "0.9.0" +!define PRODUCT_VERSION "0.9.0.0" + +Name "QSynergy ${VERSION}" +OutFile "qsynergy-${VERSION}-setup.exe" +InstallDir "$PROGRAMFILES\QSynergy\" + +BrandingText " " + +SetCompressor /FINAL bzip2 + +; Registry key to check for directory (so if you install again, it will +; overwrite the old one automatically) +InstallDirRegKey HKLM "Software\Fidra\QSynergy" "Install_Dir" + +; --------------------------- +; Pages +; --------------------------- + +!insertmacro MUI_PAGE_WELCOME +!insertmacro MUI_PAGE_LICENSE "..\COPYING" + +!insertmacro MUI_PAGE_COMPONENTS +!insertmacro MUI_PAGE_DIRECTORY +!insertmacro MUI_PAGE_INSTFILES +!insertmacro MUI_PAGE_FINISH + +!insertmacro MUI_UNPAGE_CONFIRM +!insertmacro MUI_UNPAGE_INSTFILES +!insertmacro MUI_UNPAGE_FINISH + + + +; --------------------------- +; Languages +; --------------------------- + +!insertmacro MUI_LANGUAGE "English" + +LangString qsynergy ${LANG_ENGLISH} "QSynergy program files" +LangString DESC_qsynergy ${LANG_ENGLISH} "The program files required for QSynergy." + +LangString qt_dlls ${LANG_ENGLISH} "Qt libraries QSynergy needs to run" +LangString DESC_qt_dlls ${LANG_ENGLISH} "If you have Qt 4.3 or newer installed, you don't need to install this." + +LangString startmenu ${LANG_ENGLISH} "Start menu entry" +LangString DESC_startmenu ${LANG_ENGLISH} "Create a start menu entry for QSynergy." + +LangString desktopicon ${LANG_ENGLISH} "Desktop icon" +LangString DESC_desktopicon ${LANG_ENGLISH} "Create an icon on the desktop for QSynergy." + +; --------------------------- +; Environment PATH manipulation +; --------------------------- + +; !include "path_manip.nsh" + + +; --------------------------- +; Version stuff +; --------------------------- + +VIProductVersion "${PRODUCT_VERSION}" + +VIAddVersionKey /LANG=${LANG_ENGLISH} "ProductName" "QSynergy ${VERSION} Setup" +VIAddVersionKey /LANG=${LANG_ENGLISH} "Comments" "Installs QSynergy ${VERSION} on your computer." +VIAddVersionKey /LANG=${LANG_ENGLISH} "CompanyName" "Fidra Software Entwicklung, Volker Lanz" +VIAddVersionKey /LANG=${LANG_ENGLISH} "LegalTrademarks" "" +VIAddVersionKey /LANG=${LANG_ENGLISH} "LegalCopyright" "2008 Fidra Software Entwicklung, Volker Lanz" +VIAddVersionKey /LANG=${LANG_ENGLISH} "FileDescription" "QSynergy ${VERSION} Setup" +VIAddVersionKey /LANG=${LANG_ENGLISH} "FileVersion" "${VERSION}" + + +; --------------------------- +; Callbacks +; --------------------------- + +Function .onInit + !insertmacro MUI_LANGDLL_DISPLAY +FunctionEnd + +Function un.onInit + !insertmacro MUI_LANGDLL_DISPLAY +FunctionEnd + + +; --------------------------- +; The stuff to install +; --------------------------- + +Section $(qsynergy) section_qsynergy + SectionIn RO + + SetOutPath "$INSTDIR" + File "..\release\qsynergy.exe" + File "..\README" + File "..\COPYING" + + ; Write the installation path into the registry + WriteRegStr HKLM "Software\Fidra\QSynergy" "Install_Dir" "$INSTDIR" + + ; Write the uninstall keys for Windows + WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\QSynergy" "DisplayName" "QSynergy ${VERSION}" + WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\QSynergy" "UninstallString" '"$INSTDIR\uninstall.exe"' + WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\QSynergy" "NoModify" 1 + WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\QSynergy" "NoRepair" 1 + WriteUninstaller "uninstall.exe" +SectionEnd + +Section $(qt_dlls) section_qt_dlls + SetOutPath "$INSTDIR" + File "..\..\..\..\sw\qt\lib\qtcore4.dll" + File "..\..\..\..\sw\qt\lib\qtgui4.dll" + File "..\..\..\..\sw\qt\lib\qtnetwork4.dll" +SectionEnd + +Section $(startmenu) section_startmenu + CreateDirectory "$SMPROGRAMS\QSynergy" + CreateShortCut "$SMPROGRAMS\QSynergy\QSynergy.lnk" "$INSTDIR\qsynergy.exe" "" "$INSTDIR\qsynergy.exe" 0 + CreateShortCut "$SMPROGRAMS\QSynergy\README.lnk" "$INSTDIR\README" "" "$INSTDIR\README" 0 + CreateShortCut "$SMPROGRAMS\QSynergy\COPYING.lnk" "$INSTDIR\COPYING" "" "$INSTDIR\COPYING" 0 + CreateShortCut "$SMPROGRAMS\QSynergy\Uninstall.lnk" "$INSTDIR\uninstall.exe" "" "$INSTDIR\uninstall.exe" 0 +SectionEnd + +Section $(desktopicon) section_desktopicon + CreateShortCut "$DESKTOP\QSynergy.lnk" "$INSTDIR\qsynergy.exe" "" "$INSTDIR\qsynergy.exe" 0 +SectionEnd + +; --------------------------- +; Uninstaller +; --------------------------- + +Section "Uninstall" + ; Remove registry keys + DeleteRegKey HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\QSynergy" + DeleteRegKey HKLM SOFTWARE\Fidra\QSynergy + + ; Remove files and uninstaller + Delete "$INSTDIR\uninstall.exe" + + Delete "$INSTDIR\*.exe" + Delete "$INSTDIR\README" + Delete "$INSTDIR\COPYING" + Delete "$INSTDIR\*.dll" + + ; startmenu and desktop icon + Delete "$SMPROGRAMS\QSynergy\*.*" + Delete "$DESKTOP\QSynergy.lnk" + + ; Remove directories used + RMDir "$SMPROGRAMS\QSynergy" + RMDir "$INSTDIR" +SectionEnd + + +; --------------------------- +; Section descriptions +; --------------------------- + +!insertmacro MUI_FUNCTION_DESCRIPTION_BEGIN + !insertmacro MUI_DESCRIPTION_TEXT ${section_qsynergy} $(DESC_qsynergy) + !insertmacro MUI_DESCRIPTION_TEXT ${section_qt_dlls} $(DESC_qt_dlls) + !insertmacro MUI_DESCRIPTION_TEXT ${section_startmenu} $(DESC_startmenu) + !insertmacro MUI_DESCRIPTION_TEXT ${section_desktopicon} $(DESC_desktopicon) +!insertmacro MUI_FUNCTION_DESCRIPTION_END + diff --git a/gui/dist/qsynergy.xpm b/gui/dist/qsynergy.xpm new file mode 100644 index 00000000..d7239a8f --- /dev/null +++ b/gui/dist/qsynergy.xpm @@ -0,0 +1,44 @@ +/* XPM */ +static char *qsynergy[] = { +/* columns rows colors chars-per-pixel */ +"32 32 6 1", +" c #010000", +". c #336666", +"X c #3399CC", +"o c #6699FF", +"O c #99CC33", +"+ c None", +/* pixels */ +"++++++++++++++++++++++++++++++++", +"++++++++++++oooo....++++++++++++", +"+++++++++ooooooo.......+++++++++", +"++++++++oooooooo........++++++++", +"++++++oooooooooo..........++++++", +"+++++ooooooooooo...........+++++", +"++++oooooooooooo............++++", +"++++oooooooooooo............++++", +"+++oooooooooo++++++..........+++", +"++ooooooooo++++++++++.........++", +"++oooooooo++++++++++++........++", +"++ooooooo++++++++++++++.......++", +"+oooooooo++++++++++++++........+", +"+ooooooo++++++++++++++++.......+", +"+ooooooo++++++++++++++++.......+", +"+ooooooo++++++++++++++++.......+", +"+OOOOOOO++++++++++++++++XXXXXXX+", +"+OOOOOOO++++++++++++++++XXXXXXX+", +"+OOOOOOO++++++++++++++++XXXXXXX+", +"+OOOOOOOO++++++++++++++XXXXXXXX+", +"++OOOOOOO++++++++++++++XXXXXXX++", +"++OOOOOOOO++++++++++++XXXXXXXX++", +"++OOOOOOOOO++++++++++XXXXXXXXX++", +"+++OOOOOOOOOO++++++XXXXXXXXXX+++", +"++++OOOOOOOOOOOOXXXXXXXXXXXX++++", +"++++OOOOOOOOOOOOXXXXXXXXXXXX++++", +"+++++OOOOOOOOOOOXXXXXXXXXXX+++++", +"++++++OOOOOOOOOOXXXXXXXXXX++++++", +"++++++++OOOOOOOOXXXXXXXX++++++++", +"+++++++++OOOOOOOXXXXXXX+++++++++", +"++++++++++++OOOOXXXX++++++++++++", +"++++++++++++++++++++++++++++++++" +}; diff --git a/gui/dist/tarball.sh b/gui/dist/tarball.sh new file mode 100644 index 00000000..4cec1e2d --- /dev/null +++ b/gui/dist/tarball.sh @@ -0,0 +1,17 @@ +#!/bin/bash + +APPNAME=qsynergy +VERSION=0.9.0 +TMPBASE=/tmp +TMPDIR=${TMPBASE}/${APPNAME}-${VERSION} + +cd $(dirname $0)/.. +[ -e Makefile ] && make distclean +mkdir -p ${TMPDIR} +cp -a src/ ${TMPDIR} +cp -a res/ ${TMPDIR} +cp -a dist/ ${TMPDIR} +cp qsynergy.pro COPYING INSTALL README ${TMPDIR} +tar cCfz ${TMPBASE} ${APPNAME}-${VERSION}.tar.gz --exclude=.svn ${APPNAME}-${VERSION} +rm -rf ${TMPDIR} + diff --git a/gui/dist/windows/vcredist_x64.exe b/gui/dist/windows/vcredist_x64.exe new file mode 100644 index 00000000..e008d23b Binary files /dev/null and b/gui/dist/windows/vcredist_x64.exe differ diff --git a/gui/dist/windows/vcredist_x86.exe b/gui/dist/windows/vcredist_x86.exe new file mode 100644 index 00000000..378e7f03 Binary files /dev/null and b/gui/dist/windows/vcredist_x86.exe differ diff --git a/gui/qsynergy.pro b/gui/qsynergy.pro new file mode 100644 index 00000000..d0e8dfd0 --- /dev/null +++ b/gui/qsynergy.pro @@ -0,0 +1,85 @@ +QT += network + +TEMPLATE = app +TARGET = qsynergy +DEPENDPATH += . res +INCLUDEPATH += . src + +FORMS += \ + res/MainWindowBase.ui \ + res/AboutDialogBase.ui \ + res/ServerConfigDialogBase.ui \ + res/ScreenSettingsDialogBase.ui \ + res/ActionDialogBase.ui \ + res/HotkeyDialogBase.ui \ + res/SettingsDialogBase.ui \ + res/LogDialogBase.ui +SOURCES += \ + src/main.cpp \ + src/MainWindow.cpp \ + src/AboutDialog.cpp \ + src/ServerConfig.cpp \ + src/ServerConfigDialog.cpp \ + src/ScreenSetupView.cpp \ + src/Screen.cpp \ + src/ScreenSetupModel.cpp \ + src/NewScreenWidget.cpp \ + src/TrashScreenWidget.cpp \ + src/ScreenSettingsDialog.cpp \ + src/BaseConfig.cpp \ + src/HotkeyDialog.cpp \ + src/ActionDialog.cpp \ + src/Hotkey.cpp \ + src/Action.cpp \ + src/KeySequence.cpp \ + src/KeySequenceWidget.cpp \ + src/LogDialog.cpp \ + src/SettingsDialog.cpp \ + src/AppConfig.cpp \ + src/QSynergyApplication.cpp +HEADERS += \ + src/MainWindow.h \ + src/AboutDialog.h \ + src/ServerConfig.h \ + src/ServerConfigDialog.h \ + src/ScreenSetupView.h \ + src/Screen.h \ + src/ScreenSetupModel.h \ + src/NewScreenWidget.h \ + src/TrashScreenWidget.h \ + src/ScreenSettingsDialog.h \ + src/BaseConfig.h \ + src/HotkeyDialog.h \ + src/ActionDialog.h \ + src/Hotkey.h \ + src/Action.h \ + src/KeySequence.h \ + src/KeySequenceWidget.h \ + src/LogDialog.h \ + src/SettingsDialog.h \ + src/AppConfig.h \ + src/QSynergyApplication.h +RESOURCES += res/QSynergy.qrc +RC_FILE = res/win/QSynergy.rc + +macx { + QMAKE_INFO_PLIST = res/mac/QSynergy.plist + QMAKE_MACOSX_DEPLOYMENT_TARGET = 10.4 + TARGET = QSynergy + QSYNERGY_ICON.files = res/mac/QSynergy.icns + QSYNERGY_ICON.path = Contents/Resources + QMAKE_BUNDLE_DATA += QSYNERGY_ICON +} + +debug { + OBJECTS_DIR = tmp/debug + MOC_DIR = tmp/debug + RCC_DIR = tmp/debug +} + +release { + OBJECTS_DIR = tmp/release + MOC_DIR = tmp/release + RCC_DIR = tmp/release +} + diff --git a/gui/res/AboutDialogBase.ui b/gui/res/AboutDialogBase.ui new file mode 100644 index 00000000..5c70ae37 --- /dev/null +++ b/gui/res/AboutDialogBase.ui @@ -0,0 +1,197 @@ + + AboutDialogBase + + + Qt::ApplicationModal + + + + 0 + 0 + 368 + 350 + + + + + 368 + 350 + + + + About QSynergy + + + true + + + + + + <html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:16px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:x-large; font-weight:600;"><span style=" font-size:x-large;">QSynergy</span></p></body></html> + + + true + + + true + + + Qt::LinksAccessibleByMouse|Qt::NoTextInteraction|Qt::TextSelectableByMouse + + + + + + + Version 0.9.0 + +Copyright 2008 Volker Lanz (vl@fidra.de) + +QSynergy is open source and released under the GNU Public License (GPL) Version 2. + +Synergy and the synergy icon: Copyright (C) 2002 Chris Schoeneman. + + + true + + + + + + + Qt::Horizontal + + + + + + + + + + 1 + 0 + + + + Synergy Version: + + + + + + + - + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + 1 + 0 + + + + Hostname: + + + + + + + - + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + 1 + 0 + + + + IP-Address: + + + + + + + - + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + Qt::Vertical + + + + 20 + 78 + + + + + + + + Qt::Horizontal + + + + 131 + 20 + + + + + + + + &Ok + + + + + + + + + buttonOk + clicked() + AboutDialogBase + accept() + + + 315 + 374 + + + 301 + 3 + + + + + diff --git a/gui/res/ActionDialogBase.ui b/gui/res/ActionDialogBase.ui new file mode 100644 index 00000000..f2c2e121 --- /dev/null +++ b/gui/res/ActionDialogBase.ui @@ -0,0 +1,580 @@ + + ActionDialogBase + + + + 0 + 0 + 372 + 484 + + + + Configure action + + + + + + Choose the action to perform + + + + + + Press a hotkey + + + true + + + + + + + Release a hotkey + + + + + + + Press and release a hotkey + + + + + + + + 1 + 0 + + + + + 256 + 0 + + + + dfgsfgsdfgsdfgsd + + + + + + + only on these screens + + + true + + + true + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 128 + 64 + + + + QAbstractItemView::ExtendedSelection + + + + + + + + + + Qt::Horizontal + + + + + + + + + Switch to screen + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + false + + + + + + + + + + + Switch in direction + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + false + + + + left + + + + + right + + + + + up + + + + + down + + + + + + + + + + + + Lock cursor to screen + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + false + + + + toggle + + + + + on + + + + + off + + + + + + + + + + + + + This action is performed when + + + + + + the hotkey is pressed + + + true + + + + + + + the hotkey is released + + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::NoButton|QDialogButtonBox::Ok + + + + + + + + KeySequenceWidget + QLineEdit +
KeySequenceWidget.h
+
+
+ + + + buttonBox + accepted() + ActionDialogBase + accept() + + + 245 + 474 + + + 157 + 274 + + + + + buttonBox + rejected() + ActionDialogBase + reject() + + + 313 + 474 + + + 286 + 274 + + + + + m_pGroupType + toggled(bool) + m_pKeySequenceWidgetHotkey + setDisabled(bool) + + + 104 + 194 + + + 110 + 132 + + + + + m_pRadioSwitchInDirection + toggled(bool) + m_pKeySequenceWidgetHotkey + setDisabled(bool) + + + 118 + 322 + + + 81 + 129 + + + + + m_pRadioLockCursorToScreen + toggled(bool) + m_pKeySequenceWidgetHotkey + setDisabled(bool) + + + 101 + 353 + + + 68 + 126 + + + + + m_pRadioPress + toggled(bool) + m_pKeySequenceWidgetHotkey + setEnabled(bool) + + + 48 + 48 + + + 45 + 129 + + + + + m_pRadioRelease + toggled(bool) + m_pKeySequenceWidgetHotkey + setEnabled(bool) + + + 135 + 70 + + + 148 + 125 + + + + + m_pRadioPressAndRelease + toggled(bool) + m_pKeySequenceWidgetHotkey + setEnabled(bool) + + + 194 + 100 + + + 201 + 125 + + + + + m_pRadioSwitchToScreen + toggled(bool) + m_pComboSwitchToScreen + setEnabled(bool) + + + 148 + 291 + + + 350 + 290 + + + + + m_pRadioSwitchInDirection + toggled(bool) + m_pComboSwitchInDirection + setEnabled(bool) + + + 158 + 322 + + + 350 + 321 + + + + + m_pRadioLockCursorToScreen + toggled(bool) + m_pComboLockCursorToScreen + setEnabled(bool) + + + 180 + 353 + + + 350 + 352 + + + + + m_pRadioPress + toggled(bool) + m_pGroupBoxScreens + setEnabled(bool) + + + 25 + 47 + + + 33 + 155 + + + + + m_pRadioSwitchToScreen + toggled(bool) + m_pGroupBoxScreens + setDisabled(bool) + + + 48 + 278 + + + 98 + 153 + + + + + m_pRadioRelease + toggled(bool) + m_pGroupBoxScreens + setEnabled(bool) + + + 264 + 67 + + + 241 + 158 + + + + + m_pRadioPressAndRelease + toggled(bool) + m_pGroupBoxScreens + setEnabled(bool) + + + 286 + 98 + + + 290 + 156 + + + + + m_pRadioSwitchInDirection + toggled(bool) + m_pGroupBoxScreens + setDisabled(bool) + + + 38 + 313 + + + 64 + 195 + + + + + m_pRadioLockCursorToScreen + toggled(bool) + m_pGroupBoxScreens + setDisabled(bool) + + + 48 + 339 + + + 79 + 234 + + + + + m_pRadioSwitchToScreen + toggled(bool) + m_pKeySequenceWidgetHotkey + setDisabled(bool) + + + 84 + 280 + + + 185 + 123 + + + + +
diff --git a/gui/res/HotkeyDialogBase.ui b/gui/res/HotkeyDialogBase.ui new file mode 100644 index 00000000..1b9a63ee --- /dev/null +++ b/gui/res/HotkeyDialogBase.ui @@ -0,0 +1,81 @@ + + HotkeyDialogBase + + + + 0 + 0 + 344 + 106 + + + + Hotkey + + + + + + Enter the specification for the hotkey: + + + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::NoButton|QDialogButtonBox::Ok + + + + + + + + KeySequenceWidget + QPushButton +
KeySequenceWidget.h
+
+
+ + + + buttonBox + accepted() + HotkeyDialogBase + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + HotkeyDialogBase + reject() + + + 316 + 260 + + + 286 + 274 + + + + +
diff --git a/gui/res/LogDialogBase.ui b/gui/res/LogDialogBase.ui new file mode 100644 index 00000000..e65fcc1c --- /dev/null +++ b/gui/res/LogDialogBase.ui @@ -0,0 +1,98 @@ + + LogDialogBase + + + + 0 + 0 + 761 + 395 + + + + Log Output + + + + + + + Courier + + + + false + + + QTextEdit::NoWrap + + + true + + + + + + + C&lear + + + + + + + Qt::Horizontal + + + + 441 + 27 + + + + + + + + &Close + + + + + + + + + m_pButtonClearLog + clicked() + m_pLogOutput + clear() + + + 27 + 368 + + + 335 + 262 + + + + + pushButton + clicked() + LogDialogBase + accept() + + + 706 + 370 + + + 525 + 366 + + + + + diff --git a/gui/res/MainWindowBase.ui b/gui/res/MainWindowBase.ui new file mode 100644 index 00000000..6eb5a8c5 --- /dev/null +++ b/gui/res/MainWindowBase.ui @@ -0,0 +1,318 @@ + + MainWindowBase + + + + 0 + 0 + 397 + 306 + + + + QSynergy + + + + + + + Control &this computer from another one (be a client) + + + true + + + + + + &Name of the server: + + + m_pLineEditHostname + + + + + + + + + + + + + Use this computer to control &others (be a server) + + + true + + + true + + + + + + Start synergy with an &existing configuration + + + + + + + + + &Configuration file: + + + m_pLineEditConfigFile + + + + + + + false + + + + + + + false + + + &Browse... + + + + + + + + + &Interactively configure synergy + + + true + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + &Configure Server... + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + &Start + + + + + + + + &About QSynergy... + + + + + &Quit + + + Quit QSynergy. + + + Ctrl+Q + + + + + &Start Synergy + + + Run synergy program. + + + Ctrl+S + + + + + false + + + S&top Synergy + + + Stop Synergy program. + + + Ctrl+T + + + + + S&how Status + + + Ctrl+H + + + + + &Minimize + + + + + &Restore + + + + + Save configuration &as... + + + Save the interactively generated server configuration to a file. + + + Ctrl+Alt+S + + + + + Settings + + + Edit settings for QSynergy and synergy itself + + + + + Log output + + + Open a window with synergy's output + + + + + + + m_pButtonStart + clicked() + m_pActionStartSynergy + trigger() + + + 361 + 404 + + + -1 + -1 + + + + + m_pRadioInternalConfig + toggled(bool) + m_pButtonConfigureServer + setEnabled(bool) + + + 204 + 244 + + + 212 + 274 + + + + + m_pRadioExternalConfig + toggled(bool) + m_pLineEditConfigFile + setEnabled(bool) + + + 156 + 179 + + + 169 + 209 + + + + + m_pRadioExternalConfig + toggled(bool) + m_pButtonBrowseConfigFile + setEnabled(bool) + + + 353 + 182 + + + 356 + 211 + + + + + diff --git a/gui/res/QSynergy.qrc b/gui/res/QSynergy.qrc new file mode 100644 index 00000000..b3ba0d0c --- /dev/null +++ b/gui/res/QSynergy.qrc @@ -0,0 +1,10 @@ + + + + + icons/16x16/synergy-connected.png + icons/16x16/synergy-disconnected.png + icons/64x64/video-display.png + icons/64x64/user-trash.png + + diff --git a/gui/res/ScreenSettingsDialogBase.ui b/gui/res/ScreenSettingsDialogBase.ui new file mode 100644 index 00000000..35de6a13 --- /dev/null +++ b/gui/res/ScreenSettingsDialogBase.ui @@ -0,0 +1,542 @@ + + ScreenSettingsDialogBase + + + + 0 + 0 + 434 + 484 + + + + Screen settings + + + + + + + + Screen &name: + + + m_pLineEditName + + + + + + + + + + + + + + true + + + A&liases + + + false + + + + + + + + + false + + + &Add + + + + + + + QAbstractItemView::ExtendedSelection + + + + + + + false + + + &Remove + + + + + + + Qt::Vertical + + + + 20 + 126 + + + + + + + + + + + &Modifier keys + + + false + + + + + + &Shift: + + + m_pComboBoxShift + + + + + + + + Shift + + + + + Ctrl + + + + + Alt + + + + + Meta + + + + + Super + + + + + None + + + + + + + + &Ctrl: + + + m_pComboBoxCtrl + + + + + + + 1 + + + + Shift + + + + + Ctrl + + + + + Alt + + + + + Meta + + + + + Super + + + + + None + + + + + + + + Al&t: + + + m_pComboBoxAlt + + + + + + + 2 + + + + Shift + + + + + Ctrl + + + + + Alt + + + + + Meta + + + + + Super + + + + + None + + + + + + + + M&eta: + + + m_pComboBoxMeta + + + + + + + 3 + + + + Shift + + + + + Ctrl + + + + + Alt + + + + + Meta + + + + + Super + + + + + None + + + + + + + + S&uper: + + + m_pComboBoxSuper + + + + + + + 4 + + + + Shift + + + + + Ctrl + + + + + Alt + + + + + Meta + + + + + Super + + + + + None + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + + + + &Dead corners + + + false + + + + + + Top-left + + + + + + + Top-right + + + + + + + Bottom-left + + + + + + + Bottom-right + + + + + + + + + Corner Si&ze: + + + m_pSpinBoxSwitchCornerSize + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + + + + + &Fixes + + + false + + + + + + Fix CAPS LOCK key + + + + + + + Fix NUM LOCK key + + + + + + + Fix SCROLL LOCK key + + + + + + + Fix XTest for Xinerama + + + false + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::NoButton|QDialogButtonBox::Ok + + + + + + + + + m_pButtonBox + accepted() + ScreenSettingsDialogBase + accept() + + + 222 + 502 + + + 157 + 274 + + + + + m_pButtonBox + rejected() + ScreenSettingsDialogBase + reject() + + + 290 + 508 + + + 286 + 274 + + + + + diff --git a/gui/res/ServerConfigDialogBase.ui b/gui/res/ServerConfigDialogBase.ui new file mode 100644 index 00000000..3c194e5f --- /dev/null +++ b/gui/res/ServerConfigDialogBase.ui @@ -0,0 +1,756 @@ + + ServerConfigDialogBase + + + + 0 + 0 + 740 + 514 + + + + Server Configuration + + + + + + 0 + + + + Screens and links + + + + + + + + true + + + Drag a screen from the grid to the trashcan to remove it. + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + :/res/icons/64x64/user-trash.png + + + + + + + + 1 + 0 + + + + Configure the layout of your synergy server configuration. + + + Qt::AlignCenter + + + true + + + + + + + Drag this button to the grid to add a new screen. + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + :/res/icons/64x64/video-display.png + + + + + + + + + + 0 + 273 + + + + + 16777215 + 273 + + + + true + + + false + + + QFrame::StyledPanel + + + QFrame::Sunken + + + + + + + + 1 + 0 + + + + Drag new screens to the grid or move existing ones around. +Drag a screen to the trashcan to delete it. +Double click on a screen to edit its settings. + + + Qt::AlignCenter + + + true + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + Hotkeys + + + + + + &Hotkeys + + + + + + + + + true + + + &New + + + + + + + false + + + &Edit + + + + + + + false + + + &Remove + + + + + + + Qt::Vertical + + + + 75 + 161 + + + + + + + + + + + A&ctions + + + + + + + + + false + + + Ne&w + + + + + + + false + + + E&dit + + + + + + + false + + + Re&move + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + Advanced server settings + + + + + + &Switch + + + + + + + + true + + + Switch &after waiting + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + false + + + 10 + + + 10000 + + + 10 + + + 250 + + + + + + + ms + + + + + + + + + + + true + + + Switch on double &tap within + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + false + + + 10 + + + 10000 + + + 10 + + + 250 + + + + + + + ms + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + &Options + + + + + + + + true + + + &Check clients every + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + false + + + 1000 + + + 30000 + + + 1000 + + + 5000 + + + + + + + ms + + + + + + + + + true + + + Use &relative mouse moves + + + + + + + true + + + S&ynchronize screen savers + + + + + + + true + + + Don't take &foreground window on Windows servers + + + + + + + Qt::Vertical + + + + 20 + 16 + + + + + + + + + + + &Dead corners + + + false + + + + + + To&p-left + + + + + + + Top-rig&ht + + + + + + + &Bottom-left + + + + + + + Bottom-ri&ght + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + Cor&ner Size: + + + m_pSpinBoxSwitchCornerSize + + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::NoButton|QDialogButtonBox::Ok + + + + + + + + ScreenSetupView + QTableView +
ScreenSetupView.h
+ 1 +
+ + NewScreenWidget + QLabel +
NewScreenWidget.h
+
+ + TrashScreenWidget + QLabel +
TrashScreenWidget.h
+
+
+ + + + + + m_pButtonBox + accepted() + ServerConfigDialogBase + accept() + + + 572 + 424 + + + 377 + -8 + + + + + m_pButtonBox + rejected() + ServerConfigDialogBase + reject() + + + 641 + 424 + + + 595 + 1 + + + + + m_pCheckBoxSwitchDelay + toggled(bool) + m_pSpinBoxSwitchDelay + setEnabled(bool) + + + 110 + 63 + + + 110 + 63 + + + + + m_pCheckBoxSwitchDoubleTap + toggled(bool) + m_pSpinBoxSwitchDoubleTap + setEnabled(bool) + + + 110 + 63 + + + 110 + 63 + + + + + m_pCheckBoxHeartbeat + toggled(bool) + m_pSpinBoxHeartbeat + setEnabled(bool) + + + 110 + 63 + + + 110 + 63 + + + + + m_pListHotkeys + itemDoubleClicked(QListWidgetItem*) + m_pButtonEditHotkey + click() + + + 197 + 115 + + + 304 + 115 + + + + + m_pListActions + itemDoubleClicked(QListWidgetItem*) + m_pButtonEditAction + click() + + + 505 + 120 + + + 677 + 118 + + + + +
diff --git a/gui/res/SettingsDialogBase.ui b/gui/res/SettingsDialogBase.ui new file mode 100644 index 00000000..fc56a921 --- /dev/null +++ b/gui/res/SettingsDialogBase.ui @@ -0,0 +1,252 @@ + + SettingsDialogBase + + + + 0 + 0 + 372 + 390 + + + + Settings + + + + + + &Programs + + + + + + This is the synergy client program, usually called synergyc or synergyc.exe. + + + &Client: + + + m_pLineEditSynergyc + + + + + + + + + + &Browse... + + + + + + + + + + This is the synergy server program, usually called synergys or synergys.exe. + + + S&erver: + + + m_pLineEditSynergys + + + + + + + + + + Bro&wse... + + + + + + + + + + &Advanced + + + + + + Sc&reen name: + + + m_pLineEditScreenName + + + + + + + true + + + + + + + P&ort: + + + m_pSpinBoxPort + + + + + + + true + + + 65535 + + + 24800 + + + + + + + &Interface: + + + m_pLineEditInterface + + + + + + + true + + + + + + + + + + &Start + + + + + + A&utomatically start synergy when QSynergy starts + + + + + + + + + + &Logging level: + + + m_pComboLogLevel + + + + + + + + Error + + + + + Warning + + + + + Note + + + + + Info + + + + + Debug + + + + + Debug1 + + + + + Debug2 + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::NoButton|QDialogButtonBox::Ok + + + + + + + + + buttonBox + accepted() + SettingsDialogBase + accept() + + + 257 + 380 + + + 157 + 274 + + + + + buttonBox + rejected() + SettingsDialogBase + reject() + + + 325 + 380 + + + 286 + 274 + + + + + diff --git a/gui/res/icons/16x16/synergy-connected.png b/gui/res/icons/16x16/synergy-connected.png new file mode 100644 index 00000000..0a311216 Binary files /dev/null and b/gui/res/icons/16x16/synergy-connected.png differ diff --git a/gui/res/icons/16x16/synergy-disconnected.png b/gui/res/icons/16x16/synergy-disconnected.png new file mode 100644 index 00000000..c1aacc9c Binary files /dev/null and b/gui/res/icons/16x16/synergy-disconnected.png differ diff --git a/gui/res/icons/64x64/user-trash.png b/gui/res/icons/64x64/user-trash.png new file mode 100644 index 00000000..05eb80ec Binary files /dev/null and b/gui/res/icons/64x64/user-trash.png differ diff --git a/gui/res/icons/64x64/video-display.png b/gui/res/icons/64x64/video-display.png new file mode 100644 index 00000000..8a47d90f Binary files /dev/null and b/gui/res/icons/64x64/video-display.png differ diff --git a/gui/res/mac/QSynergy.icns b/gui/res/mac/QSynergy.icns new file mode 100644 index 00000000..0d87c5a5 Binary files /dev/null and b/gui/res/mac/QSynergy.icns differ diff --git a/gui/res/mac/QSynergy.plist b/gui/res/mac/QSynergy.plist new file mode 100644 index 00000000..9eb8cab4 --- /dev/null +++ b/gui/res/mac/QSynergy.plist @@ -0,0 +1,16 @@ + + + + + CFBundleIconFile + QSynergy.icns + CFBundlePackageType + APPL + CFBundleGetInfoString + 0.9.0 + CFBundleSignature + ???? + CFBundleExecutable + QSynergy + + diff --git a/gui/res/win/QSynergy.ico b/gui/res/win/QSynergy.ico new file mode 100644 index 00000000..89f965f4 Binary files /dev/null and b/gui/res/win/QSynergy.ico differ diff --git a/gui/res/win/QSynergy.rc b/gui/res/win/QSynergy.rc new file mode 100644 index 00000000..696cc6e6 --- /dev/null +++ b/gui/res/win/QSynergy.rc @@ -0,0 +1 @@ +IDI_ICON1 ICON DISCARDABLE "res\win\QSynergy.ico" diff --git a/gui/src/AboutDialog.cpp b/gui/src/AboutDialog.cpp new file mode 100644 index 00000000..2264b7b3 --- /dev/null +++ b/gui/src/AboutDialog.cpp @@ -0,0 +1,48 @@ +#include "AboutDialog.h" + +#include +#include +#include + +static QString getSynergyVersion(const QString& app) +{ +#if !defined(Q_OS_WIN) + QProcess process; + process.start(app, QStringList() << "--version"); + + process.setReadChannel(QProcess::StandardError); + if (!process.waitForStarted() || !process.waitForFinished()) + return QObject::tr("(unknown)"); + + QRegExp rx("synergy[cs] ([\\d\\.]+)"); + if (rx.indexIn(QString(process.readLine())) != -1) + return rx.cap(1); +#else + Q_UNUSED(app); +#endif + + return QObject::tr("(unknown)"); +} + +static QString getIPAddress() +{ + QList addresses = QNetworkInterface::allAddresses(); + + for (int i = 0; i < addresses.size(); i++) + if (addresses[i].protocol() == QAbstractSocket::IPv4Protocol && addresses[i] != QHostAddress(QHostAddress::LocalHost)) + return addresses[i].toString(); + + return QObject::tr("(unknown)"); +} + +AboutDialog::AboutDialog(QWidget* parent, const QString& synergyApp) : + QDialog(parent, Qt::WindowTitleHint | Qt::WindowSystemMenuHint), + Ui::AboutDialogBase() +{ + setupUi(this); + + m_pLabelSynergyVersion->setText(getSynergyVersion(synergyApp)); + m_pLabelHostname->setText(QHostInfo::localHostName()); + m_pLabelIPAddress->setText(getIPAddress()); +} + diff --git a/gui/src/AboutDialog.h b/gui/src/AboutDialog.h new file mode 100644 index 00000000..95cf3601 --- /dev/null +++ b/gui/src/AboutDialog.h @@ -0,0 +1,21 @@ +#if !defined(ABOUTDIALOG__H) + +#define ABOUTDIALOG__H + +#include + +#include "ui_AboutDialogBase.h" + +class QWidget; +class QString; + +class AboutDialog : public QDialog, public Ui::AboutDialogBase +{ + Q_OBJECT + + public: + AboutDialog(QWidget* parent, const QString& synergyApp = QString()); +}; + +#endif + diff --git a/gui/src/Action.cpp b/gui/src/Action.cpp new file mode 100644 index 00000000..f96daf62 --- /dev/null +++ b/gui/src/Action.cpp @@ -0,0 +1,132 @@ +#include "Action.h" + +#include +#include + +const char* Action::m_ActionTypeNames[] = +{ + "keyDown", "keyUp", "keystroke", + "switchToScreen", "switchInDirection", "lockCursorToScreen", + "mouseDown", "mouseUp", "mousebutton" +}; + +const char* Action::m_SwitchDirectionNames[] = { "left", "right", "up", "down" }; +const char* Action::m_LockCursorModeNames[] = { "toggle", "on", "off" }; + +Action::Action() : + m_KeySequence(), + m_Type(keystroke), + m_TypeScreenNames(), + m_SwitchScreenName(), + m_SwitchDirection(switchLeft), + m_LockCursorMode(lockCursorToggle), + m_ActiveOnRelease(false), + m_HasScreens(false) +{ +} + +QString Action::text() const +{ + QString text = QString(m_ActionTypeNames[keySequence().isMouseButton() ? type() + 6 : type() ]) + "("; + + switch (type()) + { + case keyDown: + case keyUp: + case keystroke: + { + text += keySequence().toString(); + + if (!keySequence().isMouseButton()) + { + const QStringList& screens = typeScreenNames(); + if (haveScreens() && !screens.isEmpty()) + { + text += ","; + + for (int i = 0; i < screens.size(); i++) + { + text += screens[i]; + if (i != screens.size() - 1) + text += ":"; + } + } + else + text += ",*"; + } + } + break; + + case switchToScreen: + text += switchScreenName(); + break; + + case switchInDirection: + text += m_SwitchDirectionNames[m_SwitchDirection]; + break; + + case lockCursorToScreen: + text += m_LockCursorModeNames[m_LockCursorMode]; + break; + + default: + Q_ASSERT(0); + break; + } + + text += ")"; + + return text; +} + +void Action::loadSettings(QSettings& settings) +{ + keySequence().loadSettings(settings); + setType(settings.value("type", keyDown).toInt()); + + typeScreenNames().clear(); + int numTypeScreens = settings.beginReadArray("typeScreenNames"); + for (int i = 0; i < numTypeScreens; i++) + { + settings.setArrayIndex(i); + typeScreenNames().append(settings.value("typeScreenName").toString()); + } + settings.endArray(); + + setSwitchScreenName(settings.value("switchScreenName").toString()); + setSwitchDirection(settings.value("switchInDirection", switchLeft).toInt()); + setLockCursorMode(settings.value("lockCursorToScreen", lockCursorToggle).toInt()); + setActiveOnRelease(settings.value("activeOnRelease", false).toBool()); + setHaveScreens(settings.value("hasScreens", false).toBool()); +} + +void Action::saveSettings(QSettings& settings) const +{ + keySequence().saveSettings(settings); + settings.setValue("type", type()); + + settings.beginWriteArray("typeScreenNames"); + for (int i = 0; i < typeScreenNames().size(); i++) + { + settings.setArrayIndex(i); + settings.setValue("typeScreenName", typeScreenNames()[i]); + } + settings.endArray(); + + settings.setValue("switchScreenName", switchScreenName()); + settings.setValue("switchInDirection", switchDirection()); + settings.setValue("lockCursorToScreen", lockCursorMode()); + settings.setValue("activeOnRelease", activeOnRelease()); + settings.setValue("hasScreens", haveScreens()); +} + +QTextStream& operator<<(QTextStream& outStream, const Action& action) +{ + if (action.activeOnRelease()) + outStream << ";"; + + outStream << action.text(); + + return outStream; +} + diff --git a/gui/src/Action.h b/gui/src/Action.h new file mode 100644 index 00000000..d3b69566 --- /dev/null +++ b/gui/src/Action.h @@ -0,0 +1,71 @@ +#if !defined(ACTION_H) + +#define ACTION_H + +#include "KeySequence.h" + +#include +#include +#include + +class ActionDialog; +class QSettings; +class QTextStream; + +class Action +{ + friend class ActionDialog; + friend QTextStream& operator<<(QTextStream& outStream, const Action& action); + + public: + enum ActionType { keyDown, keyUp, keystroke, switchToScreen, switchInDirection, lockCursorToScreen, mouseDown, mouseUp, mousebutton }; + enum SwitchDirection { switchLeft, switchRight, switchUp, switchDown }; + enum LockCursorMode { lockCursorToggle, lockCursonOn, lockCursorOff }; + + public: + Action(); + + public: + QString text() const; + const KeySequence& keySequence() const { return m_KeySequence; } + void loadSettings(QSettings& settings); + void saveSettings(QSettings& settings) const; + int type() const { return m_Type; } + const QStringList& typeScreenNames() const { return m_TypeScreenNames; } + const QString& switchScreenName() const { return m_SwitchScreenName; } + int switchDirection() const { return m_SwitchDirection; } + int lockCursorMode() const { return m_LockCursorMode; } + bool activeOnRelease() const { return m_ActiveOnRelease; } + bool haveScreens() const { return m_HasScreens; } + + protected: + KeySequence& keySequence() { return m_KeySequence; } + void setKeySequence(const KeySequence& seq) { m_KeySequence = seq; } + void setType(int t) { m_Type = t; } + QStringList& typeScreenNames() { return m_TypeScreenNames; } + void setSwitchScreenName(const QString& n) { m_SwitchScreenName = n; } + void setSwitchDirection(int d) { m_SwitchDirection = d; } + void setLockCursorMode(int m) { m_LockCursorMode = m; } + void setActiveOnRelease(bool b) { m_ActiveOnRelease = b; } + void setHaveScreens(bool b) { m_HasScreens = b; } + + private: + KeySequence m_KeySequence; + int m_Type; + QStringList m_TypeScreenNames; + QString m_SwitchScreenName; + int m_SwitchDirection; + int m_LockCursorMode; + bool m_ActiveOnRelease; + bool m_HasScreens; + + static const char* m_ActionTypeNames[]; + static const char* m_SwitchDirectionNames[]; + static const char* m_LockCursorModeNames[]; +}; + +typedef QList ActionList; + +QTextStream& operator<<(QTextStream& outStream, const Action& action); + +#endif diff --git a/gui/src/ActionDialog.cpp b/gui/src/ActionDialog.cpp new file mode 100644 index 00000000..661f2575 --- /dev/null +++ b/gui/src/ActionDialog.cpp @@ -0,0 +1,91 @@ +#include "ActionDialog.h" + +#include "Hotkey.h" +#include "Action.h" +#include "ServerConfig.h" +#include "KeySequence.h" + +#include +#include + +ActionDialog::ActionDialog(QWidget* parent, ServerConfig& config, Hotkey& hotkey, Action& action) : + QDialog(parent, Qt::WindowTitleHint | Qt::WindowSystemMenuHint), + Ui::ActionDialogBase(), + m_ServerConfig(config), + m_Hotkey(hotkey), + m_Action(action), + m_pButtonGroupType(new QButtonGroup(this)) +{ + setupUi(this); + + // work around Qt Designer's lack of a QButtonGroup; we need it to get + // at the button id of the checked radio button + QRadioButton* const typeButtons[] = { m_pRadioPress, m_pRadioRelease, m_pRadioPressAndRelease, m_pRadioSwitchToScreen, m_pRadioSwitchInDirection, m_pRadioLockCursorToScreen }; + + for (unsigned int i = 0; i < sizeof(typeButtons) / sizeof(typeButtons[0]); i++) + m_pButtonGroupType->addButton(typeButtons[i], i); + + m_pKeySequenceWidgetHotkey->setText(m_Action.keySequence().toString()); + m_pKeySequenceWidgetHotkey->setKeySequence(m_Action.keySequence()); + m_pButtonGroupType->button(m_Action.type())->setChecked(true); + m_pComboSwitchInDirection->setCurrentIndex(m_Action.switchDirection()); + m_pComboLockCursorToScreen->setCurrentIndex(m_Action.lockCursorMode()); + + if (m_Action.activeOnRelease()) + m_pRadioHotkeyReleased->setChecked(true); + else + m_pRadioHotkeyPressed->setChecked(true); + + m_pGroupBoxScreens->setChecked(m_Action.haveScreens()); + + int idx = 0; + foreach(const Screen& screen, serverConfig().screens()) + if (!screen.isNull()) + { + QListWidgetItem *pListItem = new QListWidgetItem(screen.name()); + m_pListScreens->addItem(pListItem); + if (m_Action.typeScreenNames().indexOf(screen.name()) != -1) + m_pListScreens->setCurrentItem(pListItem); + + m_pComboSwitchToScreen->addItem(screen.name()); + if (screen.name() == m_Action.switchScreenName()) + m_pComboSwitchToScreen->setCurrentIndex(idx); + + idx++; + } +} + +void ActionDialog::accept() +{ + if (!sequenceWidget()->valid() && m_pButtonGroupType->checkedId() >= 0 && m_pButtonGroupType->checkedId() < 3) + return; + + m_Action.setKeySequence(sequenceWidget()->keySequence()); + m_Action.setType(m_pButtonGroupType->checkedId()); + m_Action.setHaveScreens(m_pGroupBoxScreens->isChecked()); + + m_Action.typeScreenNames().clear(); + foreach(const QListWidgetItem* pItem, m_pListScreens->selectedItems()) + m_Action.typeScreenNames().append(pItem->text()); + + m_Action.setSwitchScreenName(m_pComboSwitchToScreen->currentText()); + m_Action.setSwitchDirection(m_pComboSwitchInDirection->currentIndex()); + m_Action.setLockCursorMode(m_pComboLockCursorToScreen->currentIndex()); + m_Action.setActiveOnRelease(m_pRadioHotkeyReleased->isChecked()); + + QDialog::accept(); +} + +void ActionDialog::on_m_pKeySequenceWidgetHotkey_keySequenceChanged() +{ + if (sequenceWidget()->keySequence().isMouseButton()) + { + m_pGroupBoxScreens->setEnabled(false); + m_pListScreens->setEnabled(false); + } + else + { + m_pGroupBoxScreens->setEnabled(true); + m_pListScreens->setEnabled(true); + } +} diff --git a/gui/src/ActionDialog.h b/gui/src/ActionDialog.h new file mode 100644 index 00000000..5c7945a4 --- /dev/null +++ b/gui/src/ActionDialog.h @@ -0,0 +1,38 @@ +#if !defined(ACTIONDIALOG_H) + +#define ACTIONDIALOG_H + +#include + +#include "ui_ActionDialogBase.h" + +class Hotkey; +class Action; +class QRadioButton; +class QButtonGroup; +class ServerConfig; + +class ActionDialog : public QDialog, public Ui::ActionDialogBase +{ + Q_OBJECT + + public: + ActionDialog(QWidget* parent, ServerConfig& config, Hotkey& hotkey, Action& action); + + protected slots: + void accept(); + void on_m_pKeySequenceWidgetHotkey_keySequenceChanged(); + + protected: + const KeySequenceWidget* sequenceWidget() const { return m_pKeySequenceWidgetHotkey; } + const ServerConfig& serverConfig() const { return m_ServerConfig; } + + private: + const ServerConfig& m_ServerConfig; + Hotkey& m_Hotkey; + Action& m_Action; + + QButtonGroup* m_pButtonGroupType; +}; + +#endif diff --git a/gui/src/AppConfig.cpp b/gui/src/AppConfig.cpp new file mode 100644 index 00000000..cfb4345f --- /dev/null +++ b/gui/src/AppConfig.cpp @@ -0,0 +1,58 @@ +#include "AppConfig.h" + +#include +#include + +#if defined(Q_OS_WIN) +const char AppConfig::m_SynergysName[] = "synergys.exe"; +const char AppConfig::m_SynergycName[] = "synergyc.exe"; +const char AppConfig::m_SynergyProgramDir[] = "c:/program files/synergy/"; +#else +const char AppConfig::m_SynergysName[] = "synergys"; +const char AppConfig::m_SynergycName[] = "synergyc"; +const char AppConfig::m_SynergyProgramDir[] = "/usr/bin/"; +#endif + + +AppConfig::AppConfig(QSettings* settings) : + m_pSettings(settings), + m_AutoConnect(false), + m_Synergyc(), + m_Synergys(), + m_ScreenName(), + m_Port(24800), + m_Interface(), + m_LogLevel(0) +{ + Q_ASSERT(m_pSettings); + + loadSettings(); +} + +AppConfig::~AppConfig() +{ + saveSettings(); +} + +void AppConfig::loadSettings() +{ + m_AutoConnect = settings().value("autoConnectChecked", false).toBool(); + m_Synergyc = settings().value("synergyc", QString(synergyProgramDir()) + synergycName()).toString(); + m_Synergys = settings().value("synergys", QString(synergyProgramDir()) + synergysName()).toString(); + m_ScreenName = settings().value("screenName", QHostInfo::localHostName()).toString(); + m_Port = settings().value("port", 24800).toInt(); + m_Interface = settings().value("interface").toString(); + m_LogLevel = settings().value("logLevelIndex", 0).toInt(); +} + +void AppConfig::saveSettings() +{ + settings().setValue("autoConnectChecked", m_AutoConnect); + settings().setValue("synergyc", m_Synergyc); + settings().setValue("synergys", m_Synergys); + settings().setValue("screenName", m_ScreenName); + settings().setValue("port", m_Port); + settings().setValue("interface", m_Interface); + settings().setValue("logLevelIndex", m_LogLevel); +} + diff --git a/gui/src/AppConfig.h b/gui/src/AppConfig.h new file mode 100644 index 00000000..b9b453b3 --- /dev/null +++ b/gui/src/AppConfig.h @@ -0,0 +1,60 @@ +#if !defined(APPCONFIG_H) + +#define APPCONFIG_H + +#include + +class QSettings; +class SettingsDialog; + +class AppConfig +{ + friend class SettingsDialog; + friend class MainWindow; + + public: + AppConfig(QSettings* settings); + ~AppConfig(); + + public: + bool autoConnect() const { return m_AutoConnect; } + const QString& synergyc() const { return m_Synergyc; } + const QString& synergys() const { return m_Synergys; } + const QString& screenName() const { return m_ScreenName; } + int port() const { return m_Port; } + const QString& interface() const { return m_Interface; } + int logLevel() const { return m_LogLevel; } + + QString synergysName() const { return m_SynergysName; } + QString synergycName() const { return m_SynergycName; } + QString synergyProgramDir() const { return m_SynergyProgramDir; } + + protected: + QSettings& settings() { return *m_pSettings; } + void setAutoConnect(bool b) { m_AutoConnect = b; } + void setSynergyc(const QString& s) { m_Synergyc = s; } + void setSynergys(const QString& s) { m_Synergys = s; } + void setScreenName(const QString& s) { m_ScreenName = s; } + void setPort(int i) { m_Port = i; } + void setInterface(const QString& s) { m_Interface = s; } + void setLogLevel(int i) { m_LogLevel = i; } + + void loadSettings(); + void saveSettings(); + + private: + QSettings* m_pSettings; + bool m_AutoConnect; + QString m_Synergyc; + QString m_Synergys; + QString m_ScreenName; + int m_Port; + QString m_Interface; + int m_LogLevel; + + static const char m_SynergysName[]; + static const char m_SynergycName[]; + static const char m_SynergyProgramDir[]; +}; + +#endif diff --git a/gui/src/BaseConfig.cpp b/gui/src/BaseConfig.cpp new file mode 100644 index 00000000..c005c694 --- /dev/null +++ b/gui/src/BaseConfig.cpp @@ -0,0 +1,28 @@ +#include "BaseConfig.h" + +const char* BaseConfig::m_ModifierNames[] = +{ + "shift", + "ctrl", + "alt", + "meta", + "super", + "none" +}; + +const char* BaseConfig::m_FixNames[] = +{ + "halfDuplexCapsLock", + "halfDuplexNumLock", + "halfDuplexScrollLock", + "xtestIsXineramaUnaware" +}; + +const char* BaseConfig::m_SwitchCornerNames[] = +{ + "top-left", + "top-right", + "bottom-left", + "bottom-right" +}; + diff --git a/gui/src/BaseConfig.h b/gui/src/BaseConfig.h new file mode 100644 index 00000000..ced6ef2d --- /dev/null +++ b/gui/src/BaseConfig.h @@ -0,0 +1,73 @@ +#if !defined(BASECONFIG_H) + +#define BASECONFIG_H + +#include +#include +#include + +class BaseConfig +{ + public: + enum Modifier { DefaultMod = -1, Shift, Ctrl, Alt, Meta, Super, None, NumModifiers }; + enum SwitchCorner { TopLeft, TopRight, BottomLeft, BottomRight, NumSwitchCorners }; + enum Fix { CapsLock, NumLock, ScrollLock, XTest, NumFixes }; + + protected: + BaseConfig() {} + virtual ~BaseConfig() {} + + protected: + template + void readSettings(QSettings& settings, T1& array, const QString& arrayName, const T2& deflt) + { + int entries = settings.beginReadArray(arrayName + "Array"); + array.clear(); + for (int i = 0; i < entries; i++) + { + settings.setArrayIndex(i); + QVariant v = settings.value(arrayName, deflt); + array.append(v.value()); + } + settings.endArray(); + } + + template + void readSettings(QSettings& settings, T1& array, const QString& arrayName, const T2& deflt, int entries) + { + Q_ASSERT(array.size() >= entries); + settings.beginReadArray(arrayName + "Array"); + for (int i = 0; i < entries; i++) + { + settings.setArrayIndex(i); + QVariant v = settings.value(arrayName, deflt); + array[i] = v.value(); + } + settings.endArray(); + } + + template + void writeSettings(QSettings& settings, const T& array, const QString& arrayName) const + { + settings.beginWriteArray(arrayName + "Array"); + for (int i = 0; i < array.size(); i++) + { + settings.setArrayIndex(i); + settings.setValue(arrayName, array[i]); + } + settings.endArray(); + } + + + public: + static const char* modifierName(int idx) { return m_ModifierNames[idx]; } + static const char* fixName(int idx) { return m_FixNames[idx]; } + static const char* switchCornerName(int idx) { return m_SwitchCornerNames[idx]; } + + private: + static const char* m_ModifierNames[]; + static const char* m_FixNames[]; + static const char* m_SwitchCornerNames[]; +}; + +#endif diff --git a/gui/src/Hotkey.cpp b/gui/src/Hotkey.cpp new file mode 100644 index 00000000..88b7ecf0 --- /dev/null +++ b/gui/src/Hotkey.cpp @@ -0,0 +1,57 @@ +#include "Hotkey.h" + +#include + +Hotkey::Hotkey() : + m_KeySequence(), + m_Actions() +{ +} + +QString Hotkey::text() const +{ + QString text = keySequence().toString(); + + if (keySequence().isMouseButton()) + return "mousebutton(" + text + ")"; + + return "keystroke(" + text + ")"; +} + +void Hotkey::loadSettings(QSettings& settings) +{ + keySequence().loadSettings(settings); + + actions().clear(); + int num = settings.beginReadArray("actions"); + for (int i = 0; i < num; i++) + { + settings.setArrayIndex(i); + Action a; + a.loadSettings(settings); + actions().append(a); + } + + settings.endArray(); +} + +void Hotkey::saveSettings(QSettings& settings) const +{ + keySequence().saveSettings(settings); + + settings.beginWriteArray("actions"); + for (int i = 0; i < actions().size(); i++) + { + settings.setArrayIndex(i); + actions()[i].saveSettings(settings); + } + settings.endArray(); +} + +QTextStream& operator<<(QTextStream& outStream, const Hotkey& hotkey) +{ + for (int i = 0; i < hotkey.actions().size(); i++) + outStream << "\t" << hotkey.text() << " = " << hotkey.actions()[i] << endl; + + return outStream; +} diff --git a/gui/src/Hotkey.h b/gui/src/Hotkey.h new file mode 100644 index 00000000..b39b494d --- /dev/null +++ b/gui/src/Hotkey.h @@ -0,0 +1,48 @@ +#if !defined(HOTKEY_H) + +#define HOTKEY_H + +#include +#include +#include + +#include "Action.h" +#include "KeySequence.h" + +class HotkeyDialog; +class ServerConfigDialog; +class QSettings; + +class Hotkey +{ + friend class HotkeyDialog; + friend class ServerConfigDialog; + friend QTextStream& operator<<(QTextStream& outStream, const Hotkey& hotkey); + + public: + Hotkey(); + + public: + QString text() const; + const KeySequence& keySequence() const { return m_KeySequence; } + const ActionList& actions() const { return m_Actions; } + + void loadSettings(QSettings& settings); + void saveSettings(QSettings& settings) const; + + protected: + KeySequence& keySequence() { return m_KeySequence; } + void setKeySequence(const KeySequence& seq) { m_KeySequence = seq; } + ActionList& actions() { return m_Actions; } + + + private: + KeySequence m_KeySequence; + ActionList m_Actions; +}; + +typedef QList HotkeyList; + +QTextStream& operator<<(QTextStream& outStream, const Hotkey& hotkey); + +#endif diff --git a/gui/src/HotkeyDialog.cpp b/gui/src/HotkeyDialog.cpp new file mode 100644 index 00000000..84981571 --- /dev/null +++ b/gui/src/HotkeyDialog.cpp @@ -0,0 +1,23 @@ +#include "HotkeyDialog.h" + +#include +#include + +HotkeyDialog::HotkeyDialog (QWidget* parent, Hotkey& hotkey) : + QDialog(parent, Qt::WindowTitleHint | Qt::WindowSystemMenuHint), + Ui::HotkeyDialogBase(), + m_Hotkey(hotkey) +{ + setupUi(this); + + m_pKeySequenceWidgetHotkey->setText(m_Hotkey.text()); +} + +void HotkeyDialog::accept() +{ + if (!sequenceWidget()->valid()) + return; + + hotkey().setKeySequence(sequenceWidget()->keySequence()); + QDialog::accept(); +} diff --git a/gui/src/HotkeyDialog.h b/gui/src/HotkeyDialog.h new file mode 100644 index 00000000..917cf3f9 --- /dev/null +++ b/gui/src/HotkeyDialog.h @@ -0,0 +1,31 @@ +#if !defined(HOTKEYDIALOG_H) + +#define HOTKEYDIALOG_H + +#include "ui_HotkeyDialogBase.h" +#include "Hotkey.h" + +#include + +class HotkeyDialog : public QDialog, public Ui::HotkeyDialogBase +{ + Q_OBJECT + + public: + HotkeyDialog(QWidget* parent, Hotkey& hotkey); + + public: + const Hotkey& hotkey() const { return m_Hotkey; } + + protected slots: + void accept(); + + protected: + const KeySequenceWidget* sequenceWidget() const { return m_pKeySequenceWidgetHotkey; } + Hotkey& hotkey() { return m_Hotkey; } + + private: + Hotkey& m_Hotkey; +}; + +#endif diff --git a/gui/src/KeySequence.cpp b/gui/src/KeySequence.cpp new file mode 100644 index 00000000..69588e77 --- /dev/null +++ b/gui/src/KeySequence.cpp @@ -0,0 +1,219 @@ +#include "KeySequence.h" + +#include +#include + +// this table originally comes from Qt sources (gui/kernel/qkeysequence.cpp) +// and is heavily modified for QSynergy +static const struct +{ + int key; + const char* name; +} keyname[] = +{ + { Qt::Key_Space, "Space" }, + { Qt::Key_Escape, "Escape" }, + { Qt::Key_Tab, "Tab" }, + { Qt::Key_Backtab, "LeftTab" }, + { Qt::Key_Backspace, "BackSpace" }, + { Qt::Key_Return, "Return" }, + { Qt::Key_Insert, "Insert" }, + { Qt::Key_Delete, "Delete" }, + { Qt::Key_Pause, "Pause" }, + { Qt::Key_Print, "Print" }, + { Qt::Key_SysReq, "SysReq" }, + { Qt::Key_Home, "Home" }, + { Qt::Key_End, "End" }, + { Qt::Key_Left, "Left" }, + { Qt::Key_Up, "Up" }, + { Qt::Key_Right, "Right" }, + { Qt::Key_Down, "Down" }, + { Qt::Key_PageUp, "PageUp" }, + { Qt::Key_PageDown, "PageDown" }, + { Qt::Key_CapsLock, "CapsLock" }, + { Qt::Key_NumLock, "NumLock" }, + { Qt::Key_ScrollLock, "ScrollLock" }, + { Qt::Key_Menu, "Menu" }, + { Qt::Key_Help, "Help" }, + { Qt::Key_Enter, "KP_Enter" }, + { Qt::Key_Clear, "Clear" }, + + { Qt::Key_Back, "WWWBack" }, + { Qt::Key_Forward, "WWWForward" }, + { Qt::Key_Stop, "WWWStop" }, + { Qt::Key_Refresh, "WWWRefresh" }, + { Qt::Key_VolumeDown, "AudioDown" }, + { Qt::Key_VolumeMute, "AudioMute" }, + { Qt::Key_VolumeUp, "AudioUp" }, + { Qt::Key_MediaPlay, "AudioPlay" }, + { Qt::Key_MediaStop, "AudioStop" }, + { Qt::Key_MediaPrevious,"AudioPrev" }, + { Qt::Key_MediaNext, "AudioNext" }, + { Qt::Key_HomePage, "WWWHome" }, + { Qt::Key_Favorites, "WWWFavorites" }, + { Qt::Key_Search, "WWWSearch" }, + { Qt::Key_Standby, "Sleep" }, + { Qt::Key_LaunchMail, "AppMail" }, + { Qt::Key_LaunchMedia, "AppMedia" }, + { Qt::Key_Launch0, "AppUser1" }, + { Qt::Key_Launch1, "AppUser2" }, + { Qt::Key_Select, "Select" }, + + { 0, 0 } +}; + +KeySequence::KeySequence() : + m_Sequence(), + m_Modifiers(0), + m_IsValid(false) +{ +} + +bool KeySequence::isMouseButton() const +{ + return !m_Sequence.isEmpty() && m_Sequence.last() < Qt::Key_Space; +} + +QString KeySequence::toString() const +{ + QString result; + + for (int i = 0; i < m_Sequence.size(); i++) + { + result += keyToString(m_Sequence[i]); + + if (i != m_Sequence.size() - 1) + result += "+"; + } + + return result; +} + +bool KeySequence::appendMouseButton(int button) +{ + return appendKey(button, 0); +} + +bool KeySequence::appendKey(int key, int modifiers) +{ + if (m_Sequence.size() == 4) + return true; + + switch(key) + { + case Qt::Key_AltGr: + return false; + + case Qt::Key_Control: + case Qt::Key_Alt: + case Qt::Key_Shift: + case Qt::Key_Meta: + case Qt::Key_Menu: + { + int mod = modifiers & (~m_Modifiers); + if (mod) + { + m_Sequence.append(mod); + m_Modifiers |= mod; + } + } + break; + + default: + // see if we can handle this key, if not, don't accept it + if (keyToString(key).isEmpty()) + break; + + m_Sequence.append(key); + setValid(true); + return true; + } + + return false; +} + +void KeySequence::loadSettings(QSettings& settings) +{ + sequence().clear(); + int num = settings.beginReadArray("keys"); + for (int i = 0; i < num; i++) + { + settings.setArrayIndex(i); + sequence().append(settings.value("key", 0).toInt()); + } + settings.endArray(); + + setModifiers(0); + setValid(true); +} + +void KeySequence::saveSettings(QSettings& settings) const +{ + settings.beginWriteArray("keys"); + for (int i = 0; i < sequence().size(); i++) + { + settings.setArrayIndex(i); + settings.setValue("key", sequence()[i]); + } + settings.endArray(); +} + +QString KeySequence::keyToString(int key) +{ + // nothing there? + if (key == 0) + return ""; + + // a hack to handle mouse buttons as if they were keys + if (key < Qt::Key_Space) + { + switch(key) + { + case Qt::LeftButton: return "1"; + case Qt::RightButton: return "2"; + case Qt::MidButton: return "3"; + } + + return "4"; // qt only knows three mouse buttons, so assume it's an unknown fourth one + } + + // modifiers? + if (key & Qt::ShiftModifier) + return "Shift"; + + if (key & Qt::ControlModifier) + return "Control"; + + if (key & Qt::AltModifier) + return "Alt"; + + if (key & Qt::MetaModifier) + return "Meta"; + + // treat key pad like normal keys (FIXME: we should have another lookup table for keypad keys instead) + key &= ~Qt::KeypadModifier; + + // a printable 7 bit character? + if (key < 0x80 && key != Qt::Key_Space) + return QChar(key & 0x7f).toLower(); + + // a function key? + if (key >= Qt::Key_F1 && key <= Qt::Key_F35) + return QString::fromUtf8("F%1").arg(key - Qt::Key_F1 + 1); + + // a special key? + int i=0; + while (keyname[i].name) + { + if (key == keyname[i].key) + return QString::fromUtf8(keyname[i].name); + i++; + } + + // representable in ucs2? + if (key < 0x10000) + return QString("\\u%1").arg(QChar(key).toLower().unicode(), 4, 16, QChar('0')); + + // give up, synergy probably won't handle this + return ""; +} diff --git a/gui/src/KeySequence.h b/gui/src/KeySequence.h new file mode 100644 index 00000000..fd6fa46c --- /dev/null +++ b/gui/src/KeySequence.h @@ -0,0 +1,40 @@ +#if !defined(KEYSEQUENCE_H) + +#define KEYSEQUENCE_H + +#include +#include + +class QSettings; + +class KeySequence +{ + public: + KeySequence(); + + public: + QString toString() const; + bool appendKey(int modifiers, int key); + bool appendMouseButton(int button); + bool isMouseButton() const; + bool valid() const { return m_IsValid; } + int modifiers() const { return m_Modifiers; } + void saveSettings(QSettings& settings) const; + void loadSettings(QSettings& settings); + const QList& sequence() const { return m_Sequence; } + + private: + void setValid(bool b) { m_IsValid = b; } + void setModifiers(int i) { m_Modifiers = i; } + QList& sequence() { return m_Sequence; } + + private: + QList m_Sequence; + int m_Modifiers; + bool m_IsValid; + + static QString keyToString(int key); +}; + +#endif + diff --git a/gui/src/KeySequenceWidget.cpp b/gui/src/KeySequenceWidget.cpp new file mode 100644 index 00000000..729ac642 --- /dev/null +++ b/gui/src/KeySequenceWidget.cpp @@ -0,0 +1,126 @@ +#include "KeySequenceWidget.h" + +#include + +KeySequenceWidget::KeySequenceWidget(QWidget* parent, const KeySequence& seq) : + QPushButton(parent), + m_KeySequence(seq), + m_BackupSequence(seq), + m_Status(Stopped), + m_MousePrefix("mousebutton("), + m_MousePostfix(")"), + m_KeyPrefix("keystroke("), + m_KeyPostfix(")") +{ + setFocusPolicy(Qt::NoFocus); + updateOutput(); +} + +void KeySequenceWidget::setKeySequence(const KeySequence& seq) +{ + keySequence() = seq; + backupSequence() = seq; + + setStatus(Stopped); + updateOutput(); +} + +void KeySequenceWidget::mousePressEvent(QMouseEvent* event) +{ + event->accept(); + + if (status() == Stopped) + { + startRecording(); + return; + } + + if (m_KeySequence.appendMouseButton(event->button())) + stopRecording(); + + updateOutput(); +} + +void KeySequenceWidget::startRecording() +{ + keySequence() = KeySequence(); + setDown(true); + setFocus(); + grabKeyboard(); + setStatus(Recording); +} + +void KeySequenceWidget::stopRecording() +{ + if (!keySequence().valid()) + { + keySequence() = backupSequence(); + updateOutput(); + } + + setDown(false); + focusNextChild(); + releaseKeyboard(); + setStatus(Stopped); + emit keySequenceChanged(); +} + +bool KeySequenceWidget::event(QEvent* event) +{ + if (status() == Recording) + { + switch(event->type()) + { + case QEvent::KeyPress: + keyPressEvent(static_cast(event)); + return true; + + case QEvent::MouseButtonRelease: + event->accept(); + return true; + + case QEvent::ShortcutOverride: + event->accept(); + return true; + + case QEvent::FocusOut: + stopRecording(); + if (!valid()) + { + keySequence() = backupSequence(); + updateOutput(); + } + break; + + default: + break; + } + } + + return QPushButton::event(event); +} + +void KeySequenceWidget::keyPressEvent(QKeyEvent* event) +{ + event->accept(); + + if (status() == Stopped) + return; + + if (m_KeySequence.appendKey(event->key(), event->modifiers())) + stopRecording(); + + updateOutput(); +} + +void KeySequenceWidget::updateOutput() +{ + QString s; + + if (m_KeySequence.isMouseButton()) + s = mousePrefix() + m_KeySequence.toString() + mousePostfix(); + else + s = keyPrefix() + m_KeySequence.toString() + keyPostfix(); + + setText(s); +} diff --git a/gui/src/KeySequenceWidget.h b/gui/src/KeySequenceWidget.h new file mode 100644 index 00000000..db71d8e7 --- /dev/null +++ b/gui/src/KeySequenceWidget.h @@ -0,0 +1,63 @@ +#if !defined(KEYSEQUENCEWIDGET__H) + +#define KEYSEQUENCEWIDGET__H + +#include + +#include "KeySequence.h" + +class KeySequenceWidget : public QPushButton +{ + Q_OBJECT + + public: + KeySequenceWidget(QWidget* parent, const KeySequence& seq = KeySequence()); + + signals: + void keySequenceChanged(); + + public: + const QString& mousePrefix() const { return m_MousePrefix; } + const QString& mousePostfix() const { return m_MousePostfix; } + const QString& keyPrefix() const { return m_KeyPrefix; } + const QString& keyPostfix() const { return m_KeyPostfix; } + + void setMousePrefix(const QString& s) { m_MousePrefix = s; } + void setMousePostfix(const QString& s) { m_MousePostfix = s; } + void setKeyPrefix(const QString& s) { m_KeyPrefix = s; } + void setKeyPostfix(const QString& s) { m_KeyPostfix = s; } + + const KeySequence& keySequence() const { return m_KeySequence; } + const KeySequence& backupSequence() const { return m_BackupSequence; } + void setKeySequence(const KeySequence& seq); + + bool valid() const { return keySequence().valid(); } + + protected: + void mousePressEvent(QMouseEvent*); + void keyPressEvent(QKeyEvent*); + bool event(QEvent* event); + void appendToSequence(int key); + void updateOutput(); + void startRecording(); + void stopRecording(); + KeySequence& keySequence() { return m_KeySequence; } + KeySequence& backupSequence() { return m_BackupSequence; } + + private: + enum Status { Stopped, Recording }; + void setStatus(Status s) { m_Status = s; } + Status status() const { return m_Status; } + + private: + KeySequence m_KeySequence; + KeySequence m_BackupSequence; + Status m_Status; + QString m_MousePrefix; + QString m_MousePostfix; + QString m_KeyPrefix; + QString m_KeyPostfix; +}; + +#endif + diff --git a/gui/src/LogDialog.cpp b/gui/src/LogDialog.cpp new file mode 100644 index 00000000..580b59e5 --- /dev/null +++ b/gui/src/LogDialog.cpp @@ -0,0 +1,29 @@ +#include "LogDialog.h" + +#include + +LogDialog::LogDialog (QWidget* parent, QProcess*& synergy) : + QDialog (parent, Qt::WindowTitleHint | Qt::WindowSystemMenuHint), + Ui::LogDialogBase(), + m_pSynergy(synergy) +{ + setupUi(this); +} + +void LogDialog::append(const QString& s) +{ + m_pLogOutput->append(s); +} + +void LogDialog::readSynergyOutput() +{ + if (m_pSynergy) + { + QByteArray log; + log += m_pSynergy->readAllStandardOutput(); + log += m_pSynergy->readAllStandardError(); + + append(QString(log)); + } +} + diff --git a/gui/src/LogDialog.h b/gui/src/LogDialog.h new file mode 100644 index 00000000..52d8b21f --- /dev/null +++ b/gui/src/LogDialog.h @@ -0,0 +1,28 @@ +#if !defined(LOGDIALOG_H) + +#define LOGDIALOG_H + +#include + +#include "ui_LogDialogBase.h" + +class QProcess; + +class LogDialog : public QDialog, public Ui::LogDialogBase +{ + Q_OBJECT + + public: + LogDialog(QWidget* parent, QProcess*& synergy); + + public: + void append(const QString& s); + + public slots: + void readSynergyOutput(); + + private: + QProcess*& m_pSynergy; +}; + +#endif diff --git a/gui/src/MainWindow.cpp b/gui/src/MainWindow.cpp new file mode 100644 index 00000000..7b3b89c3 --- /dev/null +++ b/gui/src/MainWindow.cpp @@ -0,0 +1,450 @@ +#include "MainWindow.h" +#include "AboutDialog.h" +#include "ServerConfigDialog.h" +#include "SettingsDialog.h" +#include "LogDialog.h" + +#include +#include +#include + +#if defined(Q_OS_WIN) +static const char synergyConfigName[] = "synergy.sgc"; +static const QString synergyConfigFilter(QObject::tr("Synergy Configurations (*.sgc);;All files (*.*)")); +#else +static const char synergyConfigName[] = "synergy.conf"; +static const QString synergyConfigFilter(QObject::tr("Synergy Configurations (*.conf);;All files (*.*)")); +#endif + +static const char* synergyIconFiles[] = +{ + ":/res/icons/16x16/synergy-disconnected.png", + ":/res/icons/16x16/synergy-connected.png" +}; + +static const char* logLevelNames[] = +{ + "ERROR", + "WARNING", + "NOTE", + "INFO", + "DEBUG", + "DEBUG1", + "DEBUG2" +}; + + +MainWindow::MainWindow(QWidget* parent) : + QMainWindow(parent), + MainWindowBase(), + m_Settings(), + m_AppConfig(&m_Settings), + m_pSynergy(NULL), + m_SynergyState(synergyDisconnected), + m_ServerConfig(&m_Settings, 5, 3), + m_pTempConfigFile(NULL), + m_pLogDialog(new LogDialog(this, synergyProcess())), + m_pLabelStatusBar(NULL), + m_pTrayIcon(NULL), + m_pTrayIconMenu(NULL) +{ + setupUi(this); + + createTrayIcon(); + createMenuBar(); + createStatusBar(); + loadSettings(); + initConnections(); + + if (appConfig().autoConnect()) + startSynergy(); +} + +MainWindow::~MainWindow() +{ + stopSynergy(); + saveSettings(); +} + +void MainWindow::createTrayIcon() +{ +#if !defined(Q_OS_MAC) + m_pTrayIconMenu = new QMenu(this); + + m_pTrayIconMenu->addAction(m_pActionStartSynergy); + m_pTrayIconMenu->addAction(m_pActionStopSynergy); + m_pTrayIconMenu->addSeparator(); + + m_pTrayIconMenu->addAction(m_pActionMinimize); + m_pTrayIconMenu->addAction(m_pActionRestore); + m_pTrayIconMenu->addSeparator(); + m_pTrayIconMenu->addAction(m_pActionQuit); + + m_pTrayIcon = new QSystemTrayIcon(this); + m_pTrayIcon->setContextMenu(m_pTrayIconMenu); + + setIcon(synergyDisconnected); + + m_pTrayIcon->show(); +#else + setIcon(synergyDisconnected); +#endif +} + +void MainWindow::createMenuBar() +{ + QMenuBar* menubar = new QMenuBar(this); + QMenu* pMenuFile = new QMenu(tr("&File"), menubar); + QMenu* pMenuEdit = new QMenu(tr("&Edit"), menubar); + QMenu* pMenuView = new QMenu(tr("&View"), menubar); + QMenu* pMenuWindow = new QMenu(tr("&Window"), menubar); + QMenu* pMenuHelp = new QMenu(tr("&Help"), menubar); + + menubar->addAction(pMenuFile->menuAction()); + menubar->addAction(pMenuEdit->menuAction()); + menubar->addAction(pMenuView->menuAction()); +#if !defined(Q_OS_MAC) + menubar->addAction(pMenuWindow->menuAction()); +#endif + menubar->addAction(pMenuHelp->menuAction()); + + pMenuFile->addAction(m_pActionStartSynergy); + pMenuFile->addAction(m_pActionStopSynergy); + pMenuFile->addSeparator(); + pMenuFile->addAction(m_pActionSave); + pMenuFile->addSeparator(); + pMenuFile->addAction(m_pActionQuit); + pMenuEdit->addAction(m_pActionSettings); + pMenuView->addAction(m_pActionLogOutput); + pMenuWindow->addAction(m_pActionMinimize); + pMenuWindow->addAction(m_pActionRestore); + pMenuHelp->addAction(m_pActionAbout); + + setMenuBar(menubar); +} + +void MainWindow::createStatusBar() +{ + m_pLabelStatusBar = new QLabel(tr("Synergy is not running.")); + statusBar()->addPermanentWidget(m_pLabelStatusBar); +} + +void MainWindow::loadSettings() +{ + // gui + QRect rect = settings().value("windowGeometry", geometry()).toRect(); + move(rect.x(), rect.y()); + resize(rect.width(), rect.height()); + +#if !defined(Q_OS_MAC) + setVisible(settings().value("windowVisible", true).toBool()); +#else + setVisible(true); +#endif + + // program settings + + // the next two must come BEFORE loading groupServerChecked and groupClientChecked or + // disabling and/or enabling the right widgets won't automatically work + m_pRadioExternalConfig->setChecked(settings().value("externalConfig", false).toBool()); + m_pRadioInternalConfig->setChecked(settings().value("internalConfig", true).toBool()); + + m_pGroupServer->setChecked(settings().value("groupServerChecked", false).toBool()); + m_pLineEditConfigFile->setText(settings().value("configFile", QDir::homePath() + "/" + synergyConfigName).toString()); + m_pGroupClient->setChecked(settings().value("groupClientChecked", true).toBool()); + m_pLineEditHostname->setText(settings().value("serverHostname").toString()); +} + +void MainWindow::initConnections() +{ + connect(m_pActionMinimize, SIGNAL(triggered()), this, SLOT(hide())); + connect(m_pActionRestore, SIGNAL(triggered()), this, SLOT(showNormal())); + connect(m_pActionStartSynergy, SIGNAL(triggered()), this, SLOT(startSynergy())); + connect(m_pActionStopSynergy, SIGNAL(triggered()), this, SLOT(stopSynergy())); + connect(m_pActionQuit, SIGNAL(triggered()), qApp, SLOT(quit())); + + if (m_pTrayIcon) + connect(m_pTrayIcon, SIGNAL(activated(QSystemTrayIcon::ActivationReason)), this, SLOT(iconActivated(QSystemTrayIcon::ActivationReason))); +} + +void MainWindow::saveSettings() +{ + // gui + settings().setValue("windowGeometry", geometry()); + +#if !defined(Q_OS_MAC) + settings().setValue("windowVisible", isVisible()); +#endif + + // program settings + settings().setValue("groupServerChecked", m_pGroupServer->isChecked()); + settings().setValue("externalConfig", m_pRadioExternalConfig->isChecked()); + settings().setValue("configFile", m_pLineEditConfigFile->text()); + settings().setValue("internalConfig", m_pRadioInternalConfig->isChecked()); + settings().setValue("groupClientChecked", m_pGroupClient->isChecked()); + settings().setValue("serverHostname", m_pLineEditHostname->text()); + + settings().sync(); +} + +void MainWindow::setIcon(qSynergyState state) +{ +#if defined(Q_OS_WIN) + setVisible(state == synergyDisconnected); + m_pTrayIcon->setVisible(state == synergyDisconnected); +#endif + + QIcon icon; + icon.addFile(synergyIconFiles[state]); + + if (m_pTrayIcon) + m_pTrayIcon->setIcon(icon); + + setWindowIcon(icon); +} + +void MainWindow::iconActivated(QSystemTrayIcon::ActivationReason reason) +{ + if (reason == QSystemTrayIcon::DoubleClick) + setVisible(!isVisible()); +} + +void MainWindow::startSynergy() +{ + stopSynergy(); + + QString app; + QStringList args; + + args << "-f" << "--debug" << logLevelNames[appConfig().logLevel()]; + + if (!appConfig().screenName().isEmpty()) + args << "--name" << appConfig().screenName(); + + setSynergyProcess(new QProcess(this)); + + if ((synergyType() == synergyClient && !clientArgs(args, app)) + || synergyType() == synergyServer && !serverArgs(args, app)) + { + stopSynergy(); + return; + } + + connect(synergyProcess(), SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(synergyFinished(int, QProcess::ExitStatus))); + connect(synergyProcess(), SIGNAL(readyReadStandardOutput()), m_pLogDialog, SLOT(readSynergyOutput())); + connect(synergyProcess(), SIGNAL(readyReadStandardError()), m_pLogDialog, SLOT(readSynergyOutput())); + + m_pLogDialog->append(tr("\n\nRunning synergy: %1 %2\n\n").arg(app).arg(args.join(" "))); + + synergyProcess()->start(app, args); + if (!synergyProcess()->waitForStarted()) + { + stopSynergy(); + QMessageBox::warning(this, tr("Program can not be started"), QString(tr("The executable

%1

could not be successfully started, although it does exist. Please check if you have sufficient permissions to run this program.").arg(app))); + return; + } + + setSynergyState(synergyConnected); +} + +bool MainWindow::clientArgs(QStringList& args, QString& app) +{ + app = appConfig().synergyc(); + + if (!QFile::exists(app)) + { + if (QMessageBox::warning(this, tr("Synergy client not found"), tr("The executable for the synergy client does not exist. Do you want to browse for the synergy client now?"), QMessageBox::Yes | QMessageBox::No) != QMessageBox::Yes) + return false; + + app = SettingsDialog::browseForSynergyc(this, appConfig().synergyProgramDir(), appConfig().synergycName()); + + if (app.isEmpty()) + return false; + + appConfig().setSynergyc(app); + } + + if (m_pLineEditHostname->text().isEmpty()) + { + QMessageBox::warning(this, tr("Hostname is empty"), tr("Please fill in a hostname for the synergy client to connect to.")); + return false; + } + + args << m_pLineEditHostname->text() + ":" + QString::number(appConfig().port()); + + return true; +} + +bool MainWindow::serverArgs(QStringList& args, QString& app) +{ + app = appConfig().synergys(); + + if (!QFile::exists(app)) + { + if (QMessageBox::warning(this, tr("Synergy server not found"), tr("The executable for the synergy server does not exist. Do you want to browse for the synergy server now?"), QMessageBox::Yes | QMessageBox::No) != QMessageBox::Yes) + return false; + + app = SettingsDialog::browseForSynergys(this, appConfig().synergyProgramDir(), appConfig().synergysName()); + + if (app.isEmpty()) + return false; + + appConfig().setSynergys(app); + } + + if (m_pRadioInternalConfig->isChecked()) + { + m_pTempConfigFile = new QTemporaryFile(); + if (!m_pTempConfigFile->open()) + { + QMessageBox::critical(this, tr("Cannot write configuration file"), tr("The temporary configuration file required to start synergy can not be written.")); + return false; + } + + serverConfig().save(*m_pTempConfigFile); + args << "-c" << m_pTempConfigFile->fileName(); + + m_pTempConfigFile->close(); + // the file will be removed from disk when the object is deleted; this happens in stopSynergy() + } + else + { + if (!QFile::exists(m_pLineEditConfigFile->text())) + { + if (QMessageBox::warning(this, tr("Configuration filename invalid"), + tr("You have not filled in a valid configuration file for the synergy server. " + "Do you want to browse for the configuration file now?"), QMessageBox::Yes | QMessageBox::No) != QMessageBox::Yes + || !on_m_pButtonBrowseConfigFile_clicked()) + return false; + } + + args << "-c" << m_pLineEditConfigFile->text(); + } + + args << "--address" << (!appConfig().interface().isEmpty() ? appConfig().interface() : "") + ":" + QString::number(appConfig().port()); + + return true; +} + +void MainWindow::stopSynergy() +{ + if (synergyProcess()) + { + if (synergyProcess()->isOpen()) + synergyProcess()->close(); + delete synergyProcess(); + setSynergyProcess(NULL); + + setSynergyState(synergyDisconnected); + } + + delete m_pTempConfigFile; + m_pTempConfigFile = NULL; +} + +void MainWindow::synergyFinished(int exitCode, QProcess::ExitStatus) +{ + // on Windows, we always seem to have an exit code != 0. +#if !defined(Q_OS_WIN) + if (exitCode != 0) + { + QMessageBox::critical(this, tr("Synergy terminated with an error"), QString(tr("Synergy terminated unexpectedly with an exit code of %1.

Please see the log output for details.")).arg(exitCode)); + stopSynergy(); + } +#else + Q_UNUSED(exitCode); +#endif + + setSynergyState(synergyDisconnected); + + // do not call stopSynergy() in case of clean synergy shutdown, because this must have (well, should have...) + // come from our own delete synergyProcess() in stopSynergy(), so we would do a double-delete... +} + +void MainWindow::setSynergyState(qSynergyState state) +{ + if (synergyState() == state) + return; + + if (state == synergyConnected) + { + disconnect (m_pButtonStart, SIGNAL(clicked()), m_pActionStartSynergy, SLOT(trigger())); + connect (m_pButtonStart, SIGNAL(clicked()), m_pActionStopSynergy, SLOT(trigger())); + m_pButtonStart->setText(tr("&Stop")); + } + else + { + disconnect (m_pButtonStart, SIGNAL(clicked()), m_pActionStopSynergy, SLOT(trigger())); + connect (m_pButtonStart, SIGNAL(clicked()), m_pActionStartSynergy, SLOT(trigger())); + m_pButtonStart->setText(tr("&Start")); + } + + m_pGroupClient->setEnabled(state == synergyDisconnected); + m_pGroupServer->setEnabled(state == synergyDisconnected); + m_pActionStartSynergy->setEnabled(state == synergyDisconnected); + m_pActionStopSynergy->setEnabled(state == synergyConnected); + m_pLabelStatusBar->setText(state == synergyConnected ? QString(tr("Synergy %1 is running.")).arg(synergyType() == synergyServer ? tr("server") : tr("client")) : tr("Synergy is not running.")); + setIcon(state); + m_SynergyState = state; +} + +void MainWindow::setVisible(bool visible) +{ + m_pActionMinimize->setEnabled(visible); + m_pActionRestore->setEnabled(!visible); + QMainWindow::setVisible(visible); +} + +bool MainWindow::on_m_pButtonBrowseConfigFile_clicked() +{ + QString fileName = QFileDialog::getOpenFileName(this, tr("Browse for a synergys config file"), QString(), synergyConfigFilter); + + if (!fileName.isEmpty()) + { + m_pLineEditConfigFile->setText(fileName); + return true; + } + + return false; +} + +bool MainWindow::on_m_pActionSave_triggered() +{ + QString fileName = QFileDialog::getSaveFileName(this, tr("Save configuration as...")); + + if (!fileName.isEmpty() && !serverConfig().save(fileName)) + { + QMessageBox::warning(this, tr("Save failed"), tr("Could not save configuration to file.")); + return true; + } + + return false; +} + +void MainWindow::on_m_pActionAbout_triggered() +{ + AboutDialog dlg(this, appConfig().synergyc()); + dlg.exec(); +} + +void MainWindow::on_m_pActionSettings_triggered() +{ + SettingsDialog dlg(this, appConfig()); + dlg.exec(); +} + +void MainWindow::on_m_pActionLogOutput_triggered() +{ + Q_ASSERT(m_pLogDialog); + + m_pLogDialog->show(); + m_pLogDialog->raise(); + m_pLogDialog->activateWindow(); +} + +void MainWindow::on_m_pButtonConfigureServer_clicked() +{ + ServerConfigDialog dlg(this, serverConfig(), appConfig().screenName()); + dlg.exec(); +} + diff --git a/gui/src/MainWindow.h b/gui/src/MainWindow.h new file mode 100644 index 00000000..23b4edcb --- /dev/null +++ b/gui/src/MainWindow.h @@ -0,0 +1,105 @@ +#if !defined(MAINWINDOW__H) + +#define MAINWINDOW__H + +#include +#include +#include +#include + +#include "ui_MainWindowBase.h" + +#include "ServerConfig.h" +#include "AppConfig.h" + +class QAction; +class QMenu; +class QLineEdit; +class QGroupBox; +class QPushButton; +class QTextEdit; +class QComboBox; +class QTabWidget; +class QCheckBox; +class QRadioButton; +class QTemporaryFile; + +class LogDialog; +class QSynergyApplication; + +class MainWindow : public QMainWindow, public Ui::MainWindowBase +{ + Q_OBJECT + + friend class QSynergyApplication; + + public: + enum qSynergyState + { + synergyDisconnected, + synergyConnected + }; + + enum qSynergyType + { + synergyClient, + synergyServer + }; + + public: + MainWindow(QWidget* parent); + ~MainWindow(); + + public: + void setVisible(bool visible); + int synergyType() const { return m_pGroupClient->isChecked() ? synergyClient : synergyServer; } + int synergyState() const { return m_SynergyState; } + + protected slots: + void on_m_pGroupClient_toggled(bool on) { m_pGroupServer->setChecked(!on); } + void on_m_pGroupServer_toggled(bool on) { m_pGroupClient->setChecked(!on); } + bool on_m_pButtonBrowseConfigFile_clicked(); + void on_m_pButtonConfigureServer_clicked(); + bool on_m_pActionSave_triggered(); + void on_m_pActionAbout_triggered(); + void on_m_pActionSettings_triggered(); + void on_m_pActionLogOutput_triggered(); + void synergyFinished(int exitCode, QProcess::ExitStatus); + void iconActivated(QSystemTrayIcon::ActivationReason reason); + void startSynergy(); + void stopSynergy(); + + protected: + QSettings& settings() { return m_Settings; } + AppConfig& appConfig() { return m_AppConfig; } + QProcess*& synergyProcess() { return m_pSynergy; } + void setSynergyProcess(QProcess* p) { m_pSynergy = p; } + ServerConfig& serverConfig() { return m_ServerConfig; } + void initConnections(); + void createMenuBar(); + void createStatusBar(); + void createTrayIcon(); + void loadSettings(); + void saveSettings(); + void setIcon(qSynergyState state); + void setSynergyState(qSynergyState state); + bool checkForApp(int which, QString& app); + bool clientArgs(QStringList& args, QString& app); + bool serverArgs(QStringList& args, QString& app); + + private: + QSettings m_Settings; + AppConfig m_AppConfig; + QProcess* m_pSynergy; + int m_SynergyState; + ServerConfig m_ServerConfig; + QTemporaryFile* m_pTempConfigFile; + LogDialog* m_pLogDialog; + + QLabel* m_pLabelStatusBar; + QSystemTrayIcon* m_pTrayIcon; + QMenu* m_pTrayIconMenu; +}; + +#endif + diff --git a/gui/src/NewScreenWidget.cpp b/gui/src/NewScreenWidget.cpp new file mode 100644 index 00000000..5c31db17 --- /dev/null +++ b/gui/src/NewScreenWidget.cpp @@ -0,0 +1,30 @@ +#include "NewScreenWidget.h" +#include "ScreenSetupModel.h" + +#include +#include + +NewScreenWidget::NewScreenWidget(QWidget* parent) : + QLabel(parent) +{ +} + +void NewScreenWidget::mousePressEvent(QMouseEvent* event) +{ + Screen newScreen(tr("Unnamed")); + + QByteArray itemData; + QDataStream dataStream(&itemData, QIODevice::WriteOnly); + dataStream << -1 << -1 << newScreen; + + QMimeData* pMimeData = new QMimeData; + pMimeData->setData(ScreenSetupModel::mimeType(), itemData); + + QDrag* pDrag = new QDrag(this); + pDrag->setMimeData(pMimeData); + pDrag->setPixmap(*pixmap()); + pDrag->setHotSpot(event->pos()); + + pDrag->exec(Qt::CopyAction, Qt::CopyAction); +} + diff --git a/gui/src/NewScreenWidget.h b/gui/src/NewScreenWidget.h new file mode 100644 index 00000000..402bc068 --- /dev/null +++ b/gui/src/NewScreenWidget.h @@ -0,0 +1,22 @@ +#if !defined(NEWSCREENWIDGET__H) + +#define NEWSCREENWIDGET__H + +#include + +class QMouseEvent; +class QWidget; + +class NewScreenWidget : public QLabel +{ + Q_OBJECT + + public: + NewScreenWidget(QWidget* parent); + + protected: + void mousePressEvent(QMouseEvent* event); +}; + +#endif + diff --git a/gui/src/QSynergyApplication.cpp b/gui/src/QSynergyApplication.cpp new file mode 100644 index 00000000..ab5b125c --- /dev/null +++ b/gui/src/QSynergyApplication.cpp @@ -0,0 +1,21 @@ +#include "QSynergyApplication.h" +#include "MainWindow.h" + +#include +#include + +QSynergyApplication::QSynergyApplication(int& argc, char** argv) : + QApplication(argc, argv) +{ +} + +void QSynergyApplication::commitData(QSessionManager&) +{ + foreach(QWidget* widget, topLevelWidgets()) + { + MainWindow* mainWindow = qobject_cast(widget); + if (mainWindow) + mainWindow->saveSettings(); + } +} + diff --git a/gui/src/QSynergyApplication.h b/gui/src/QSynergyApplication.h new file mode 100644 index 00000000..a2b9de19 --- /dev/null +++ b/gui/src/QSynergyApplication.h @@ -0,0 +1,19 @@ +#if !defined(QSYNERGYAPPLICATION__H) + +#define QSYNERGYAPPLICATION__H + +#include + +class QSessionManager; + +class QSynergyApplication : public QApplication +{ + public: + QSynergyApplication(int& argc, char** argv); + + public: + void commitData(QSessionManager& manager); +}; + +#endif + diff --git a/gui/src/Screen.cpp b/gui/src/Screen.cpp new file mode 100644 index 00000000..5b71023a --- /dev/null +++ b/gui/src/Screen.cpp @@ -0,0 +1,129 @@ +#include "Screen.h" + +#include +#include + +Screen::Screen() : + m_Pixmap(QPixmap(":res/icons/64x64/video-display.png")), + m_Swapped(false) +{ + init(); +} + +Screen::Screen(const QString& name) : + m_Pixmap(QPixmap(":res/icons/64x64/video-display.png")), + m_Swapped(false) +{ + init(); + setName(name); +} + +void Screen::init() +{ + name().clear(); + aliases().clear(); + modifiers().clear(); + switchCorners().clear(); + fixes().clear(); + setSwitchCornerSize(0); + + // m_Modifiers, m_SwitchCorners and m_Fixes are QLists we use like fixed-size arrays, + // thus we need to make sure to fill them with the required number of elements. + for (int i = 0; i < NumModifiers; i++) + modifiers() << i; + + for (int i = 0; i < NumSwitchCorners; i++) + switchCorners() << false; + + for (int i = 0; i < NumFixes; i++) + fixes() << false; +} + +void Screen::loadSettings(QSettings& settings) +{ + setName(settings.value("name").toString()); + + if (name().isEmpty()) + return; + + setSwitchCornerSize(settings.value("switchCornerSize").toInt()); + + readSettings(settings, aliases(), "alias", QString("")); + readSettings(settings, modifiers(), "modifier", static_cast(DefaultMod), NumModifiers); + readSettings(settings, switchCorners(), "switchCorner", false, NumSwitchCorners); + readSettings(settings, fixes(), "fix", false, NumFixes); +} + +void Screen::saveSettings(QSettings& settings) const +{ + settings.setValue("name", name()); + + if (name().isEmpty()) + return; + + settings.setValue("switchCornerSize", switchCornerSize()); + + writeSettings(settings, aliases(), "alias"); + writeSettings(settings, modifiers(), "modifier"); + writeSettings(settings, switchCorners(), "switchCorner"); + writeSettings(settings, fixes(), "fix"); +} + +QTextStream& Screen::writeScreensSection(QTextStream& outStream) const +{ + outStream << "\t" << name() << ":" << endl; + + for (int i = 0; i < modifiers().size(); i++) + if (modifier(i) != i) + outStream << "\t\t" << modifierName(i) << " = " << modifierName(modifier(i)) << endl; + + for (int i = 0; i < fixes().size(); i++) + outStream << "\t\t" << fixName(i) << " = " << (fixes()[i] ? "true" : "false") << endl; + + outStream << "\t\t" << "switchCorners = none "; + for (int i = 0; i < switchCorners().size(); i++) + if (switchCorners()[i]) + outStream << "+" << switchCornerName(i) << " "; + outStream << endl; + + outStream << "\t\t" << "switchCornerSize = " << switchCornerSize() << endl; + + return outStream; +} + +QTextStream& Screen::writeAliasesSection(QTextStream& outStream) const +{ + if (!aliases().isEmpty()) + { + outStream << "\t" << name() << ":" << endl; + + foreach (const QString& alias, aliases()) + outStream << "\t\t" << alias << endl; + } + + return outStream; +} + +QDataStream& operator<<(QDataStream& outStream, const Screen& screen) +{ + return outStream + << screen.name() + << screen.switchCornerSize() + << screen.aliases() + << screen.modifiers() + << screen.switchCorners() + << screen.fixes() + ; +} + +QDataStream& operator>>(QDataStream& inStream, Screen& screen) +{ + return inStream + >> screen.m_Name + >> screen.m_SwitchCornerSize + >> screen.m_Aliases + >> screen.m_Modifiers + >> screen.m_SwitchCorners + >> screen.m_Fixes + ; +} diff --git a/gui/src/Screen.h b/gui/src/Screen.h new file mode 100644 index 00000000..2b75a1e2 --- /dev/null +++ b/gui/src/Screen.h @@ -0,0 +1,87 @@ +#if !defined(SCREEN__H) + +#define SCREEN__H + +#include +#include +#include +#include + +#include "BaseConfig.h" + +class QSettings; +class QTextStream; + +class ScreenSettingsDialog; + +class Screen : public BaseConfig +{ + friend QDataStream& operator<<(QDataStream& outStream, const Screen& screen); + friend QDataStream& operator>>(QDataStream& inStream, Screen& screen); + friend class ScreenSettingsDialog; + friend class ScreenSetupModel; + friend class ScreenSetupView; + + public: + Screen(); + Screen(const QString& name); + + public: + const QPixmap* pixmap() const { return &m_Pixmap; } + const QString& name() const { return m_Name; } + const QStringList& aliases() const { return m_Aliases; } + + bool isNull() const { return m_Name.isEmpty(); } + int modifier(int m) const { return m_Modifiers[m] == DefaultMod ? m : m_Modifiers[m]; } + const QList& modifiers() const { return m_Modifiers; } + bool switchCorner(int c) const { return m_SwitchCorners[c]; } + const QList& switchCorners() const { return m_SwitchCorners; } + int switchCornerSize() const { return m_SwitchCornerSize; } + bool fix(Fix f) const { return m_Fixes[f]; } + const QList& fixes() const { return m_Fixes; } + + void loadSettings(QSettings& settings); + void saveSettings(QSettings& settings) const; + QTextStream& writeScreensSection(QTextStream& outStream) const; + QTextStream& writeAliasesSection(QTextStream& outStream) const; + + bool swapped() const { return m_Swapped; } + + protected: + void init(); + void setName(const QString& name) { m_Name = name; } + QPixmap* pixmap() { return &m_Pixmap; } + QString& name() { return m_Name; } + + void setPixmap(const QPixmap& pixmap) { m_Pixmap = pixmap; } + QStringList& aliases() { return m_Aliases; } + void setModifier(int m, int n) { m_Modifiers[m] = n; } + QList& modifiers() { return m_Modifiers; } + void addAlias(const QString& alias) { m_Aliases.append(alias); } + void setSwitchCorner(int c, bool on) { m_SwitchCorners[c] = on; } + QList& switchCorners() { return m_SwitchCorners; } + void setSwitchCornerSize(int val) { m_SwitchCornerSize = val; } + void setFix(int f, bool on) { m_Fixes[f] = on; } + QList& fixes() { return m_Fixes; } + void setSwapped(bool on) { m_Swapped = on; } + + private: + QPixmap m_Pixmap; + QString m_Name; + + QStringList m_Aliases; + QList m_Modifiers; + QList m_SwitchCorners; + int m_SwitchCornerSize; + QList m_Fixes; + + bool m_Swapped; +}; + +typedef QList ScreenList; + +QDataStream& operator<<(QDataStream& outStream, const Screen& screen); +QDataStream& operator>>(QDataStream& inStream, Screen& screen); + +#endif + diff --git a/gui/src/ScreenSettingsDialog.cpp b/gui/src/ScreenSettingsDialog.cpp new file mode 100644 index 00000000..f240eda5 --- /dev/null +++ b/gui/src/ScreenSettingsDialog.cpp @@ -0,0 +1,104 @@ +#include "ScreenSettingsDialog.h" +#include "Screen.h" + +#include +#include + +ScreenSettingsDialog::ScreenSettingsDialog(QWidget* parent, Screen* pScreen) : + QDialog(parent, Qt::WindowTitleHint | Qt::WindowSystemMenuHint), + Ui::ScreenSettingsDialogBase(), + m_pScreen(pScreen) +{ + setupUi(this); + + QRegExp validScreenName("[a-z_][a-z0-9\\._-]{,31}", Qt::CaseInsensitive); + + m_pLineEditName->setText(m_pScreen->name()); + m_pLineEditName->setValidator(new QRegExpValidator(validScreenName, m_pLineEditName)); + m_pLineEditName->selectAll(); + + m_pLineEditAlias->setValidator(new QRegExpValidator(validScreenName, m_pLineEditName)); + + for (int i = 0; i < m_pScreen->aliases().count(); i++) + new QListWidgetItem(m_pScreen->aliases()[i], m_pListAliases); + + m_pComboBoxShift->setCurrentIndex(m_pScreen->modifier(Screen::Shift)); + m_pComboBoxCtrl->setCurrentIndex(m_pScreen->modifier(Screen::Ctrl)); + m_pComboBoxAlt->setCurrentIndex(m_pScreen->modifier(Screen::Alt)); + m_pComboBoxMeta->setCurrentIndex(m_pScreen->modifier(Screen::Meta)); + m_pComboBoxSuper->setCurrentIndex(m_pScreen->modifier(Screen::Super)); + + m_pCheckBoxCornerTopLeft->setChecked(m_pScreen->switchCorner(Screen::TopLeft)); + m_pCheckBoxCornerTopRight->setChecked(m_pScreen->switchCorner(Screen::TopRight)); + m_pCheckBoxCornerBottomLeft->setChecked(m_pScreen->switchCorner(Screen::BottomLeft)); + m_pCheckBoxCornerBottomRight->setChecked(m_pScreen->switchCorner(Screen::BottomRight)); + m_pSpinBoxSwitchCornerSize->setValue(m_pScreen->switchCornerSize()); + + m_pCheckBoxCapsLock->setChecked(m_pScreen->fix(Screen::CapsLock)); + m_pCheckBoxNumLock->setChecked(m_pScreen->fix(Screen::NumLock)); + m_pCheckBoxScrollLock->setChecked(m_pScreen->fix(Screen::ScrollLock)); + m_pCheckBoxXTest->setChecked(m_pScreen->fix(Screen::XTest)); +} + +void ScreenSettingsDialog::accept() +{ + if (m_pLineEditName->text().isEmpty()) + { + QMessageBox::warning(this, tr("Screen name is empty"), tr("The name for a screen can not be empty. Please fill in a name or cancel the dialog.")); + return; + } + + m_pScreen->init(); + + m_pScreen->setName(m_pLineEditName->text()); + + for (int i = 0; i < m_pListAliases->count(); i++) + m_pScreen->addAlias(m_pListAliases->item(i)->text()); + + m_pScreen->setModifier(Screen::Shift, m_pComboBoxShift->currentIndex()); + m_pScreen->setModifier(Screen::Ctrl, m_pComboBoxCtrl->currentIndex()); + m_pScreen->setModifier(Screen::Alt, m_pComboBoxAlt->currentIndex()); + m_pScreen->setModifier(Screen::Meta, m_pComboBoxMeta->currentIndex()); + m_pScreen->setModifier(Screen::Super, m_pComboBoxSuper->currentIndex()); + + m_pScreen->setSwitchCorner(Screen::TopLeft, m_pCheckBoxCornerTopLeft->isChecked()); + m_pScreen->setSwitchCorner(Screen::TopRight, m_pCheckBoxCornerTopRight->isChecked()); + m_pScreen->setSwitchCorner(Screen::BottomLeft, m_pCheckBoxCornerBottomLeft->isChecked()); + m_pScreen->setSwitchCorner(Screen::BottomRight, m_pCheckBoxCornerBottomRight->isChecked()); + m_pScreen->setSwitchCornerSize(m_pSpinBoxSwitchCornerSize->value()); + + m_pScreen->setFix(Screen::CapsLock, m_pCheckBoxCapsLock->isChecked()); + m_pScreen->setFix(Screen::NumLock, m_pCheckBoxNumLock->isChecked()); + m_pScreen->setFix(Screen::ScrollLock, m_pCheckBoxScrollLock->isChecked()); + m_pScreen->setFix(Screen::XTest, m_pCheckBoxXTest->isChecked()); + + QDialog::accept(); +} + +void ScreenSettingsDialog::on_m_pButtonAddAlias_clicked() +{ + if (!m_pLineEditAlias->text().isEmpty() && m_pListAliases->findItems(m_pLineEditAlias->text(), Qt::MatchFixedString).isEmpty()) + { + new QListWidgetItem(m_pLineEditAlias->text(), m_pListAliases); + m_pLineEditAlias->clear(); + } +} + +void ScreenSettingsDialog::on_m_pLineEditAlias_textChanged(const QString& text) +{ + m_pButtonAddAlias->setEnabled(!text.isEmpty()); +} + +void ScreenSettingsDialog::on_m_pButtonRemoveAlias_clicked() +{ + QList items = m_pListAliases->selectedItems(); + + for (int i = 0; i < items.count(); i++) + delete items[i]; +} + +void ScreenSettingsDialog::on_m_pListAliases_itemSelectionChanged() +{ + m_pButtonRemoveAlias->setEnabled(!m_pListAliases->selectedItems().isEmpty()); +} + diff --git a/gui/src/ScreenSettingsDialog.h b/gui/src/ScreenSettingsDialog.h new file mode 100644 index 00000000..14c2a1fc --- /dev/null +++ b/gui/src/ScreenSettingsDialog.h @@ -0,0 +1,35 @@ +#if !defined(SCREENSETTINGSDIALOG__H) + +#define SCREENSETTINGSDIALOG__H + +#include + +#include "ui_ScreenSettingsDialogBase.h" + +class QWidget; +class QString; + +class Screen; + +class ScreenSettingsDialog : public QDialog, public Ui::ScreenSettingsDialogBase +{ + Q_OBJECT + + public: + ScreenSettingsDialog(QWidget* parent, Screen* pScreen = NULL); + + public slots: + void accept(); + + private slots: + void on_m_pButtonAddAlias_clicked(); + void on_m_pButtonRemoveAlias_clicked(); + void on_m_pLineEditAlias_textChanged(const QString& text); + void on_m_pListAliases_itemSelectionChanged(); + + private: + Screen* m_pScreen; +}; + +#endif + diff --git a/gui/src/ScreenSetupModel.cpp b/gui/src/ScreenSetupModel.cpp new file mode 100644 index 00000000..42c50af6 --- /dev/null +++ b/gui/src/ScreenSetupModel.cpp @@ -0,0 +1,125 @@ +#include "ScreenSetupModel.h" +#include "Screen.h" + +#include +#include + +const QString ScreenSetupModel::m_MimeType = "application/x-qsynergy-screen"; + +ScreenSetupModel::ScreenSetupModel(ScreenList& screens, int numColumns, int numRows) : + QAbstractTableModel(NULL), + m_Screens(screens), + m_NumColumns(numColumns), + m_NumRows(numRows) +{ + if (m_NumColumns * m_NumRows > screens.size()) + qFatal("Not enough elements (%u) in screens QList for %d columns and %d rows", screens.size(), m_NumColumns, m_NumRows); +} + +QVariant ScreenSetupModel::data(const QModelIndex& index, int role) const +{ + if (index.isValid() && index.row() < m_NumRows && index.column() < m_NumColumns) + { + switch(role) + { + case Qt::DecorationRole: + if (screen(index).isNull()) + break; + return QIcon(*screen(index).pixmap()); + + case Qt::ToolTipRole: + if (screen(index).isNull()) + break; + return QString(tr( + "
Screen: %1
" + "
Double click to edit settings" + "
Drag screen to the trashcan to remove it")).arg(screen(index).name()); + + case Qt::DisplayRole: + if (screen(index).isNull()) + break; + return screen(index).name(); + } + } + + return QVariant(); +} + +Qt::ItemFlags ScreenSetupModel::flags(const QModelIndex& index) const +{ + if (!index.isValid() || index.row() >= m_NumRows || index.column() >= m_NumColumns) + return 0; + + if (!screen(index).isNull()) + return Qt::ItemIsEnabled | Qt::ItemIsDragEnabled | Qt::ItemIsSelectable | Qt::ItemIsDropEnabled; + + return Qt::ItemIsDropEnabled; +} + +Qt::DropActions ScreenSetupModel::supportedDropActions() const +{ + return Qt::MoveAction | Qt::CopyAction; +} + +QStringList ScreenSetupModel::mimeTypes() const +{ + return QStringList() << m_MimeType; +} + +QMimeData* ScreenSetupModel::mimeData(const QModelIndexList& indexes) const +{ + QMimeData* pMimeData = new QMimeData(); + QByteArray encodedData; + + QDataStream stream(&encodedData, QIODevice::WriteOnly); + + foreach (const QModelIndex& index, indexes) + if (index.isValid()) + stream << index.column() << index.row() << screen(index); + + pMimeData->setData(m_MimeType, encodedData); + + return pMimeData; +} + +bool ScreenSetupModel::dropMimeData(const QMimeData* data, Qt::DropAction action, int row, int column, const QModelIndex& parent) +{ + if (action == Qt::IgnoreAction) + return true; + + if (!data->hasFormat(m_MimeType)) + return false; + + if (!parent.isValid() || row != -1 || column != -1) + return false; + + QByteArray encodedData = data->data(m_MimeType); + QDataStream stream(&encodedData, QIODevice::ReadOnly); + + int sourceColumn = -1; + int sourceRow = -1; + + stream >> sourceColumn; + stream >> sourceRow; + + // don't drop screen onto itself + if (sourceColumn == parent.column() && sourceRow == parent.row()) + return false; + + Screen droppedScreen; + stream >> droppedScreen; + + Screen oldScreen = screen(parent.column(), parent.row()); + if (!oldScreen.isNull() && sourceColumn != -1 && sourceRow != -1) + { + // mark the screen so it isn't deleted after the dragndrop succeeded + // see ScreenSetupView::startDrag() + oldScreen.setSwapped(true); + screen(sourceColumn, sourceRow) = oldScreen; + } + + screen(parent.column(), parent.row()) = droppedScreen; + + return true; +} + diff --git a/gui/src/ScreenSetupModel.h b/gui/src/ScreenSetupModel.h new file mode 100644 index 00000000..11cec0fb --- /dev/null +++ b/gui/src/ScreenSetupModel.h @@ -0,0 +1,53 @@ +#if !defined(SCREENSETUPMODEL__H) + +#define SCREENSETUPMODEL__H + +#include +#include +#include +#include + +#include "Screen.h" + +class ScreenSetupView; +class ServerConfigDialog; + +class ScreenSetupModel : public QAbstractTableModel +{ + Q_OBJECT + + friend class ScreenSetupView; + friend class ServerConfigDialog; + + public: + ScreenSetupModel(ScreenList& screens, int numColumns, int numRows); + + public: + static const QString& mimeType() { return m_MimeType; } + QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const; + int rowCount() const { return m_NumRows; } + int columnCount() const { return m_NumColumns; } + int rowCount(const QModelIndex&) const { return rowCount(); } + int columnCount(const QModelIndex&) const { return columnCount(); } + Qt::DropActions supportedDropActions() const; + Qt::ItemFlags flags(const QModelIndex& index) const; + QStringList mimeTypes() const; + QMimeData* mimeData(const QModelIndexList& indexes) const; + + protected: + bool dropMimeData(const QMimeData* data, Qt::DropAction action, int row, int column, const QModelIndex& parent); + const Screen& screen(const QModelIndex& index) const { return screen(index.column(), index.row()); } + Screen& screen(const QModelIndex& index) { return screen(index.column(), index.row()); } + const Screen& screen(int column, int row) const { return m_Screens[row * m_NumColumns + column]; } + Screen& screen(int column, int row) { return m_Screens[row * m_NumColumns + column]; } + + private: + ScreenList& m_Screens; + const int m_NumColumns; + const int m_NumRows; + + static const QString m_MimeType; +}; + +#endif + diff --git a/gui/src/ScreenSetupView.cpp b/gui/src/ScreenSetupView.cpp new file mode 100644 index 00000000..e461787b --- /dev/null +++ b/gui/src/ScreenSetupView.cpp @@ -0,0 +1,143 @@ +#include "ScreenSetupView.h" +#include "ScreenSetupModel.h" +#include "ScreenSettingsDialog.h" + +#include +#include + +ScreenSetupView::ScreenSetupView(QWidget* parent) : + QTableView(parent) +{ + setDropIndicatorShown(true); + setDragDropMode(DragDrop); + setSelectionMode(SingleSelection); + + setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + + setIconSize(QSize(64, 64)); + horizontalHeader()->hide(); + verticalHeader()->hide(); +} + +void ScreenSetupView::setModel(ScreenSetupModel* model) +{ + QTableView::setModel(model); + setTableSize(); +} + +ScreenSetupModel* ScreenSetupView::model() const +{ + return qobject_cast(QTableView::model()); +} + +void ScreenSetupView::setTableSize() +{ + for (int i = 0; i < model()->columnCount(); i++) + setColumnWidth(i, width() / model()->columnCount()); + + for (int i = 0; i < model()->rowCount(); i++) + setRowHeight(i, height() / model()->rowCount()); +} + +void ScreenSetupView::resizeEvent(QResizeEvent* event) +{ + setTableSize(); + event->ignore(); +} + +void ScreenSetupView::mouseDoubleClickEvent(QMouseEvent* event) +{ + if (event->buttons() & Qt::LeftButton) + { + int col = columnAt(event->pos().x()); + int row = rowAt(event->pos().y()); + + if (!model()->screen(col, row).isNull()) + { + ScreenSettingsDialog dlg(this, &model()->screen(col, row)); + dlg.exec(); + } + } + else + event->ignore(); +} + +void ScreenSetupView::dragEnterEvent(QDragEnterEvent* event) +{ + // we accept anything that enters us by a drag as long as the + // mime type is okay. anything else is dealt with in dragMoveEvent() + if (event->mimeData()->hasFormat(ScreenSetupModel::mimeType())) + event->accept(); + else + event->ignore(); +} + +void ScreenSetupView::dragMoveEvent(QDragMoveEvent* event) +{ + if (event->mimeData()->hasFormat(ScreenSetupModel::mimeType())) + { + // where does the event come from? myself or someone else? + if (event->source() == this) + { + // myself is ok, but then it must be a move action, never a copy + event->setDropAction(Qt::MoveAction); + event->accept(); + } + else + { + int col = columnAt(event->pos().x()); + int row = rowAt(event->pos().y()); + + // a drop from outside is not allowed if there's a screen already there. + if (!model()->screen(col, row).isNull()) + event->ignore(); + else + event->acceptProposedAction(); + } + } + else + event->ignore(); +} + +// this is reimplemented from QAbstractItemView::startDrag() +void ScreenSetupView::startDrag(Qt::DropActions) +{ + QModelIndexList indexes = selectedIndexes(); + + if (indexes.count() != 1) + return; + + QMimeData* pData = model()->mimeData(indexes); + if (pData == NULL) + return; + + QPixmap pixmap = *model()->screen(indexes[0]).pixmap(); + QDrag* pDrag = new QDrag(this); + pDrag->setPixmap(pixmap); + pDrag->setMimeData(pData); + pDrag->setHotSpot(QPoint(pixmap.width() / 2, pixmap.height() / 2)); + + if (pDrag->exec(Qt::MoveAction, Qt::MoveAction) == Qt::MoveAction) + { + selectionModel()->clear(); + + // make sure to only delete the drag source if screens weren't swapped + // see ScreenSetupModel::dropMimeData + if (!model()->screen(indexes[0]).swapped()) + model()->screen(indexes[0]) = Screen(); + else + model()->screen(indexes[0]).setSwapped(false); + } +} + +QStyleOptionViewItem ScreenSetupView::viewOptions() const +{ + QStyleOptionViewItem option = QTableView::viewOptions(); + option.showDecorationSelected = true; + option.decorationPosition = QStyleOptionViewItem::Top; + option.displayAlignment = Qt::AlignCenter; + option.textElideMode = Qt::ElideMiddle; + return option; +} + diff --git a/gui/src/ScreenSetupView.h b/gui/src/ScreenSetupView.h new file mode 100644 index 00000000..8ed9a2b0 --- /dev/null +++ b/gui/src/ScreenSetupView.h @@ -0,0 +1,39 @@ +#if !defined(SCREENSETUPVIEW__H) + +#define SCREENSETUPVIEW__H + +#include +#include + +#include "Screen.h" + +class QWidget; +class QMouseEvent; +class QResizeEvent; +class QDragEnterEvent; +class ScreenSetupModel; + +class ScreenSetupView : public QTableView +{ + Q_OBJECT + + public: + ScreenSetupView(QWidget* parent); + + public: + void setModel(ScreenSetupModel* model); + ScreenSetupModel* model() const; + + protected: + void mouseDoubleClickEvent(QMouseEvent*); + void setTableSize(); + void resizeEvent(QResizeEvent*); + void dragEnterEvent(QDragEnterEvent* event); + void dragMoveEvent(QDragMoveEvent* event); + void startDrag(Qt::DropActions supportedActions); + QStyleOptionViewItem viewOptions() const; + void scrollTo(const QModelIndex&, ScrollHint) {} +}; + +#endif + diff --git a/gui/src/ServerConfig.cpp b/gui/src/ServerConfig.cpp new file mode 100644 index 00000000..9895d5bf --- /dev/null +++ b/gui/src/ServerConfig.cpp @@ -0,0 +1,247 @@ +#include "ServerConfig.h" +#include "Hotkey.h" + +#include + +static const struct +{ + int x; + int y; + const char* name; +} neighbourDirs[] = +{ + { 0, -1, "up" }, + { 1, 0, "right" }, + { 0, 1, "down" }, + { -1, 0, "left" }, +}; + + +ServerConfig::ServerConfig(QSettings* settings, int numColumns, int numRows) : + m_pSettings(settings), + m_Screens(), + m_NumColumns(numColumns), + m_NumRows(numRows) +{ + Q_ASSERT(m_pSettings); + + loadSettings(); +} + +ServerConfig::~ServerConfig() +{ + saveSettings(); +} + +bool ServerConfig::save(const QString& fileName) const +{ + QFile file(fileName); + if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) + return false; + + save(file); + file.close(); + + return true; +} + +void ServerConfig::save(QFile& file) const +{ + QTextStream outStream(&file); + outStream << *this; +} + +void ServerConfig::init() +{ + switchCorners().clear(); + screens().clear(); + + // m_NumSwitchCorners is used as a fixed size array. See Screen::init() + for (int i = 0; i < NumSwitchCorners; i++) + switchCorners() << false; + + // There must always be screen objects for each cell in the screens QList. Unused screens + // are identified by having an empty name. + for (int i = 0; i < numColumns() * numRows(); i++) + addScreen(Screen()); +} + +void ServerConfig::saveSettings() +{ + settings().beginGroup("internalConfig"); + settings().remove(""); + + settings().setValue("numColumns", numColumns()); + settings().setValue("numRows", numRows()); + + settings().setValue("hasHeartbeat", hasHeartbeat()); + settings().setValue("heartbeat", heartbeat()); + settings().setValue("relativeMouseMoves", relativeMouseMoves()); + settings().setValue("screenSaverSync", screenSaverSync()); + settings().setValue("win32KeepForeground", win32KeepForeground()); + settings().setValue("hasSwitchDelay", hasSwitchDelay()); + settings().setValue("switchDelay", switchDelay()); + settings().setValue("hasSwitchDoubleTap", hasSwitchDoubleTap()); + settings().setValue("switchDoubleTap", switchDoubleTap()); + settings().setValue("switchCornerSize", switchCornerSize()); + + writeSettings(settings(), switchCorners(), "switchCorner"); + + settings().beginWriteArray("screens"); + for (int i = 0; i < screens().size(); i++) + { + settings().setArrayIndex(i); + screens()[i].saveSettings(settings()); + } + settings().endArray(); + + settings().beginWriteArray("hotkeys"); + for (int i = 0; i < hotkeys().size(); i++) + { + settings().setArrayIndex(i); + hotkeys()[i].saveSettings(settings()); + } + settings().endArray(); + + settings().endGroup(); +} + +void ServerConfig::loadSettings() +{ + settings().beginGroup("internalConfig"); + + setNumColumns(settings().value("numColumns", 5).toInt()); + setNumRows(settings().value("numRows", 3).toInt()); + + // we need to know the number of columns and rows before we can set up ourselves + init(); + + haveHeartbeat(settings().value("hasHeartbeat", false).toBool()); + setHeartbeat(settings().value("heartbeat", 5000).toInt()); + setRelativeMouseMoves(settings().value("relativeMouseMoves", false).toBool()); + setScreenSaverSync(settings().value("screenSaverSync", true).toBool()); + setWin32KeepForeground(settings().value("win32KeepForeground", false).toBool()); + haveSwitchDelay(settings().value("hasSwitchDelay", false).toBool()); + setSwitchDelay(settings().value("switchDelay", 250).toInt()); + haveSwitchDoubleTap(settings().value("hasSwitchDoubleTap", false).toBool()); + setSwitchDoubleTap(settings().value("switchDoubleTap", 250).toInt()); + setSwitchCornerSize(settings().value("switchCornerSize").toInt()); + + readSettings(settings(), switchCorners(), "switchCorner", false, NumSwitchCorners); + + int numScreens = settings().beginReadArray("screens"); + Q_ASSERT(numScreens <= screens().size()); + for (int i = 0; i < numScreens; i++) + { + settings().setArrayIndex(i); + screens()[i].loadSettings(settings()); + } + settings().endArray(); + + int numHotkeys = settings().beginReadArray("hotkeys"); + for (int i = 0; i < numHotkeys; i++) + { + settings().setArrayIndex(i); + Hotkey h; + h.loadSettings(settings()); + hotkeys().append(h); + } + settings().endArray(); + + settings().endGroup(); +} + +int ServerConfig::adjacentScreenIndex(int idx, int deltaColumn, int deltaRow) const +{ + if (screens()[idx].isNull()) + return -1; + + // if we're at the left or right end of the table, don't find results going further left or right + if ((deltaColumn > 0 && (idx+1) % numColumns() == 0) + || (deltaColumn < 0 && idx % numColumns() == 0)) + return -1; + + int arrayPos = idx + deltaColumn + deltaRow * numColumns(); + + if (arrayPos >= screens().size() || arrayPos < 0) + return -1; + + return arrayPos; +} + +QTextStream& operator<<(QTextStream& outStream, const ServerConfig& config) +{ + outStream << "section: screens" << endl; + + foreach (const Screen& s, config.screens()) + if (!s.isNull()) + s.writeScreensSection(outStream); + + outStream << "end" << endl << endl; + + outStream << "section: aliases" << endl; + + foreach (const Screen& s, config.screens()) + if (!s.isNull()) + s.writeAliasesSection(outStream); + + outStream << "end" << endl << endl; + + outStream << "section: links" << endl; + + for (int i = 0; i < config.screens().size(); i++) + if (!config.screens()[i].isNull()) + { + outStream << "\t" << config.screens()[i].name() << ":" << endl; + + for (unsigned int j = 0; j < sizeof(neighbourDirs) / sizeof(neighbourDirs[0]); j++) + { + int idx = config.adjacentScreenIndex(i, neighbourDirs[j].x, neighbourDirs[j].y); + if (idx != -1 && !config.screens()[idx].isNull()) + outStream << "\t\t" << neighbourDirs[j].name << " = " << config.screens()[idx].name() << endl; + } + } + + outStream << "end" << endl << endl; + + outStream << "section: options" << endl; + + if (config.hasHeartbeat()) + outStream << "\t" << "heartbeat = " << config.heartbeat() << endl; + + outStream << "\t" << "relativeMouseMoves = " << (config.relativeMouseMoves() ? "true" : "false") << endl; + outStream << "\t" << "screenSaverSync = " << (config.screenSaverSync() ? "true" : "false") << endl; + outStream << "\t" << "win32KeepForeground = " << (config.win32KeepForeground() ? "true" : "false") << endl; + + if (config.hasSwitchDelay()) + outStream << "\t" << "switchDelay = " << config.switchDelay() << endl; + + if (config.hasSwitchDoubleTap()) + outStream << "\t" << "switchDoubleTap = " << config.switchDoubleTap() << endl; + + outStream << "\t" << "switchCorners = none "; + for (int i = 0; i < config.switchCorners().size(); i++) + if (config.switchCorners()[i]) + outStream << "+" << config.switchCornerName(i) << " "; + outStream << endl; + + outStream << "\t" << "switchCornerSize = " << config.switchCornerSize() << endl; + + foreach(const Hotkey& hotkey, config.hotkeys()) + outStream << hotkey; + + outStream << "end" << endl << endl; + + return outStream; +} + +int ServerConfig::numScreens() const +{ + int rval = 0; + + foreach(const Screen& s, screens()) + if (!s.isNull()) + rval++; + + return rval; +} diff --git a/gui/src/ServerConfig.h b/gui/src/ServerConfig.h new file mode 100644 index 00000000..a94e33d4 --- /dev/null +++ b/gui/src/ServerConfig.h @@ -0,0 +1,96 @@ +#if !defined(SERVERCONFIG__H) + +#define SERVERCONFIG__H + +#include + +#include "Screen.h" +#include "BaseConfig.h" +#include "Hotkey.h" + +class QTextStream; +class QSettings; +class QString; +class QFile; +class ServerConfigDialog; + +class ServerConfig : public BaseConfig +{ + friend class ServerConfigDialog; + friend QTextStream& operator<<(QTextStream& outStream, const ServerConfig& config); + + public: + ServerConfig(QSettings* settings, int numColumns, int numRows); + ~ServerConfig(); + + public: + const ScreenList& screens() const { return m_Screens; } + int numColumns() const { return m_NumColumns; } + int numRows() const { return m_NumRows; } + bool hasHeartbeat() const { return m_HasHeartbeat; } + int heartbeat() const { return m_Heartbeat; } + bool relativeMouseMoves() const { return m_RelativeMouseMoves; } + bool screenSaverSync() const { return m_ScreenSaverSync; } + bool win32KeepForeground() const { return m_Win32KeepForeground; } + bool hasSwitchDelay() const { return m_HasSwitchDelay; } + int switchDelay() const { return m_SwitchDelay; } + bool hasSwitchDoubleTap() const { return m_HasSwitchDoubleTap; } + int switchDoubleTap() const { return m_SwitchDoubleTap; } + bool switchCorner(int c) const { return m_SwitchCorners[c]; } + int switchCornerSize() const { return m_SwitchCornerSize; } + const QList& switchCorners() const { return m_SwitchCorners; } + const HotkeyList& hotkeys() const { return m_Hotkeys; } + + void saveSettings(); + void loadSettings(); + bool save(const QString& fileName) const; + void save(QFile& file) const; + int numScreens() const; + + protected: + QSettings& settings() { return *m_pSettings; } + ScreenList& screens() { return m_Screens; } + void setScreens(const ScreenList& screens) { m_Screens = screens; } + void addScreen(const Screen& screen) { m_Screens.append(screen); } + void setNumColumns(int n) { m_NumColumns = n; } + void setNumRows(int n) { m_NumRows = n; } + void haveHeartbeat(bool on) { m_HasHeartbeat = on; } + void setHeartbeat(int val) { m_Heartbeat = val; } + void setRelativeMouseMoves(bool on) { m_RelativeMouseMoves = on; } + void setScreenSaverSync(bool on) { m_ScreenSaverSync = on; } + void setWin32KeepForeground(bool on) { m_Win32KeepForeground = on; } + void haveSwitchDelay(bool on) { m_HasSwitchDelay = on; } + void setSwitchDelay(int val) { m_SwitchDelay = val; } + void haveSwitchDoubleTap(bool on) { m_HasSwitchDoubleTap = on; } + void setSwitchDoubleTap(int val) { m_SwitchDoubleTap = val; } + void setSwitchCorner(int c, bool on) { m_SwitchCorners[c] = on; } + void setSwitchCornerSize(int val) { m_SwitchCornerSize = val; } + QList& switchCorners() { return m_SwitchCorners; } + HotkeyList& hotkeys() { return m_Hotkeys; } + + void init(); + int adjacentScreenIndex(int idx, int deltaColumn, int deltaRow) const; + + private: + QSettings* m_pSettings; + ScreenList m_Screens; + int m_NumColumns; + int m_NumRows; + bool m_HasHeartbeat; + int m_Heartbeat; + bool m_RelativeMouseMoves; + bool m_ScreenSaverSync; + bool m_Win32KeepForeground; + bool m_HasSwitchDelay; + int m_SwitchDelay; + bool m_HasSwitchDoubleTap; + int m_SwitchDoubleTap; + int m_SwitchCornerSize; + QList m_SwitchCorners; + HotkeyList m_Hotkeys; +}; + +QTextStream& operator<<(QTextStream& outStream, const ServerConfig& config); + +#endif + diff --git a/gui/src/ServerConfigDialog.cpp b/gui/src/ServerConfigDialog.cpp new file mode 100644 index 00000000..509a0b3a --- /dev/null +++ b/gui/src/ServerConfigDialog.cpp @@ -0,0 +1,179 @@ +#include "ServerConfigDialog.h" +#include "ServerConfig.h" +#include "HotkeyDialog.h" +#include "ActionDialog.h" + +#include +#include + +ServerConfigDialog::ServerConfigDialog(QWidget* parent, ServerConfig& config, const QString& defaultScreenName) : + QDialog(parent, Qt::WindowTitleHint | Qt::WindowSystemMenuHint), + Ui::ServerConfigDialogBase(), + m_OrigServerConfig(config), + m_ServerConfig(config), + m_ScreenSetupModel(serverConfig().screens(), serverConfig().numColumns(), serverConfig().numRows()) +{ + setupUi(this); + + m_pCheckBoxHeartbeat->setChecked(serverConfig().hasHeartbeat()); + m_pSpinBoxHeartbeat->setValue(serverConfig().heartbeat()); + + m_pCheckBoxRelativeMouseMoves->setChecked(serverConfig().relativeMouseMoves()); + m_pCheckBoxScreenSaverSync->setChecked(serverConfig().screenSaverSync()); + m_pCheckBoxWin32KeepForeground->setChecked(serverConfig().win32KeepForeground()); + + m_pCheckBoxSwitchDelay->setChecked(serverConfig().hasSwitchDelay()); + m_pSpinBoxSwitchDelay->setValue(serverConfig().switchDelay()); + + m_pCheckBoxSwitchDoubleTap->setChecked(serverConfig().hasSwitchDoubleTap()); + m_pSpinBoxSwitchDoubleTap->setValue(serverConfig().switchDoubleTap()); + + m_pCheckBoxCornerTopLeft->setChecked(serverConfig().switchCorner(BaseConfig::TopLeft)); + m_pCheckBoxCornerTopRight->setChecked(serverConfig().switchCorner(BaseConfig::TopRight)); + m_pCheckBoxCornerBottomLeft->setChecked(serverConfig().switchCorner(BaseConfig::BottomLeft)); + m_pCheckBoxCornerBottomRight->setChecked(serverConfig().switchCorner(BaseConfig::BottomRight)); + m_pSpinBoxSwitchCornerSize->setValue(serverConfig().switchCornerSize()); + + foreach(const Hotkey& hotkey, serverConfig().hotkeys()) + m_pListHotkeys->addItem(hotkey.text()); + + m_pScreenSetupView->setModel(&m_ScreenSetupModel); + + if (serverConfig().numScreens() == 0) + model().screen(serverConfig().numColumns() / 2, serverConfig().numRows() / 2) = Screen(defaultScreenName); +} + +void ServerConfigDialog::accept() +{ + serverConfig().haveHeartbeat(m_pCheckBoxHeartbeat->isChecked()); + serverConfig().setHeartbeat(m_pSpinBoxHeartbeat->value()); + + serverConfig().setRelativeMouseMoves(m_pCheckBoxRelativeMouseMoves->isChecked()); + serverConfig().setScreenSaverSync(m_pCheckBoxScreenSaverSync->isChecked()); + serverConfig().setWin32KeepForeground(m_pCheckBoxWin32KeepForeground->isChecked()); + + serverConfig().haveSwitchDelay(m_pCheckBoxSwitchDelay->isChecked()); + serverConfig().setSwitchDelay(m_pSpinBoxSwitchDelay->value()); + + serverConfig().haveSwitchDoubleTap(m_pCheckBoxSwitchDoubleTap->isChecked()); + serverConfig().setSwitchDoubleTap(m_pSpinBoxSwitchDoubleTap->value()); + + serverConfig().setSwitchCorner(BaseConfig::TopLeft, m_pCheckBoxCornerTopLeft->isChecked()); + serverConfig().setSwitchCorner(BaseConfig::TopRight, m_pCheckBoxCornerTopRight->isChecked()); + serverConfig().setSwitchCorner(BaseConfig::BottomLeft, m_pCheckBoxCornerBottomLeft->isChecked()); + serverConfig().setSwitchCorner(BaseConfig::BottomRight, m_pCheckBoxCornerBottomRight->isChecked()); + serverConfig().setSwitchCornerSize(m_pSpinBoxSwitchCornerSize->value()); + + // now that the dialog has been accepted, copy the new server config to the original one, + // which is a reference to the one in MainWindow. + setOrigServerConfig(serverConfig()); + + QDialog::accept(); +} + +void ServerConfigDialog::on_m_pButtonNewHotkey_clicked() +{ + Hotkey hotkey; + HotkeyDialog dlg(this, hotkey); + if (dlg.exec() == QDialog::Accepted) + { + serverConfig().hotkeys().append(hotkey); + m_pListHotkeys->addItem(hotkey.text()); + } +} + +void ServerConfigDialog::on_m_pButtonEditHotkey_clicked() +{ + int idx = m_pListHotkeys->currentRow(); + Q_ASSERT(idx >= 0 && idx < serverConfig().hotkeys().size()); + Hotkey& hotkey = serverConfig().hotkeys()[idx]; + HotkeyDialog dlg(this, hotkey); + if (dlg.exec() == QDialog::Accepted) + m_pListHotkeys->currentItem()->setText(hotkey.text()); +} + +void ServerConfigDialog::on_m_pButtonRemoveHotkey_clicked() +{ + int idx = m_pListHotkeys->currentRow(); + Q_ASSERT(idx >= 0 && idx < serverConfig().hotkeys().size()); + serverConfig().hotkeys().removeAt(idx); + m_pListActions->clear(); + delete m_pListHotkeys->item(idx); +} + +void ServerConfigDialog::on_m_pListHotkeys_itemSelectionChanged() +{ + bool itemsSelected = !m_pListHotkeys->selectedItems().isEmpty(); + m_pButtonEditHotkey->setEnabled(itemsSelected); + m_pButtonRemoveHotkey->setEnabled(itemsSelected); + m_pButtonNewAction->setEnabled(itemsSelected); + + if (itemsSelected && serverConfig().hotkeys().size() > 0) + { + m_pListActions->clear(); + + int idx = m_pListHotkeys->row(m_pListHotkeys->selectedItems()[0]); + + // There's a bug somewhere around here: We get idx == 1 right after we deleted the next to last item, so idx can + // only possibly be 0. GDB shows we got called indirectly from the delete line in + // on_m_pButtonRemoveHotkey_clicked() above, but the delete is of course necessary and seems correct. + // The while() is a generalized workaround for all that and shouldn't be required. + while (idx >= 0 && idx >= serverConfig().hotkeys().size()) + idx--; + + Q_ASSERT(idx >= 0 && idx < serverConfig().hotkeys().size()); + + const Hotkey& hotkey = serverConfig().hotkeys()[idx]; + foreach(const Action& action, hotkey.actions()) + m_pListActions->addItem(action.text()); + } +} + +void ServerConfigDialog::on_m_pButtonNewAction_clicked() +{ + int idx = m_pListHotkeys->currentRow(); + Q_ASSERT(idx >= 0 && idx < serverConfig().hotkeys().size()); + Hotkey& hotkey = serverConfig().hotkeys()[idx]; + + Action action; + ActionDialog dlg(this, serverConfig(), hotkey, action); + if (dlg.exec() == QDialog::Accepted) + { + hotkey.actions().append(action); + m_pListActions->addItem(action.text()); + } +} + +void ServerConfigDialog::on_m_pButtonEditAction_clicked() +{ + int idxHotkey = m_pListHotkeys->currentRow(); + Q_ASSERT(idxHotkey >= 0 && idxHotkey < serverConfig().hotkeys().size()); + Hotkey& hotkey = serverConfig().hotkeys()[idxHotkey]; + + int idxAction = m_pListActions->currentRow(); + Q_ASSERT(idxAction >= 0 && idxAction < hotkey.actions().size()); + Action& action = hotkey.actions()[idxAction]; + + ActionDialog dlg(this, serverConfig(), hotkey, action); + if (dlg.exec() == QDialog::Accepted) + m_pListActions->currentItem()->setText(action.text()); +} + +void ServerConfigDialog::on_m_pButtonRemoveAction_clicked() +{ + int idxHotkey = m_pListHotkeys->currentRow(); + Q_ASSERT(idxHotkey >= 0 && idxHotkey < serverConfig().hotkeys().size()); + Hotkey& hotkey = serverConfig().hotkeys()[idxHotkey]; + + int idxAction = m_pListActions->currentRow(); + Q_ASSERT(idxAction >= 0 && idxAction < hotkey.actions().size()); + + hotkey.actions().removeAt(idxAction); + delete m_pListActions->currentItem(); +} + +void ServerConfigDialog::on_m_pListActions_itemSelectionChanged() +{ + m_pButtonEditAction->setEnabled(!m_pListActions->selectedItems().isEmpty()); + m_pButtonRemoveAction->setEnabled(!m_pListActions->selectedItems().isEmpty()); +} diff --git a/gui/src/ServerConfigDialog.h b/gui/src/ServerConfigDialog.h new file mode 100644 index 00000000..9e84144e --- /dev/null +++ b/gui/src/ServerConfigDialog.h @@ -0,0 +1,45 @@ +#if !defined(SERVERCONFIGDIALOG__H) + +#define SERVERCONFIGDIALOG__H + +#include "ScreenSetupModel.h" +#include "ServerConfig.h" + +#include "ui_ServerConfigDialogBase.h" + +#include + +class ServerConfigDialog : public QDialog, public Ui::ServerConfigDialogBase +{ + Q_OBJECT + + public: + ServerConfigDialog(QWidget* parent, ServerConfig& config, const QString& defaultScreenName); + + public slots: + void accept(); + + protected slots: + void on_m_pButtonNewHotkey_clicked(); + void on_m_pListHotkeys_itemSelectionChanged(); + void on_m_pButtonEditHotkey_clicked(); + void on_m_pButtonRemoveHotkey_clicked(); + + void on_m_pButtonNewAction_clicked(); + void on_m_pListActions_itemSelectionChanged(); + void on_m_pButtonEditAction_clicked(); + void on_m_pButtonRemoveAction_clicked(); + + protected: + ServerConfig& serverConfig() { return m_ServerConfig; } + void setOrigServerConfig(const ServerConfig& s) { m_OrigServerConfig = s; } + ScreenSetupModel& model() { return m_ScreenSetupModel; } + + private: + ServerConfig& m_OrigServerConfig; + ServerConfig m_ServerConfig; + ScreenSetupModel m_ScreenSetupModel; +}; + +#endif + diff --git a/gui/src/SettingsDialog.cpp b/gui/src/SettingsDialog.cpp new file mode 100644 index 00000000..df20a177 --- /dev/null +++ b/gui/src/SettingsDialog.cpp @@ -0,0 +1,71 @@ +#include "SettingsDialog.h" + +#include +#include + +#include "AppConfig.h" + +SettingsDialog::SettingsDialog(QWidget* parent, AppConfig& config) : + QDialog(parent, Qt::WindowTitleHint | Qt::WindowSystemMenuHint), + Ui::SettingsDialogBase(), + m_AppConfig(config) +{ + setupUi(this); + + m_pCheckBoxAutoConnect->setChecked(appConfig().autoConnect()); + m_pLineEditSynergyc->setText(appConfig().synergyc()); + m_pLineEditSynergys->setText(appConfig().synergys()); + m_pLineEditScreenName->setText(appConfig().screenName()); + m_pSpinBoxPort->setValue(appConfig().port()); + m_pLineEditInterface->setText(appConfig().interface()); + m_pComboLogLevel->setCurrentIndex(appConfig().logLevel()); +} + +QString SettingsDialog::browseForSynergyc(QWidget* parent, const QString& programDir, const QString& synergycName) +{ + return QFileDialog::getOpenFileName(parent, tr("Browse for synergyc executable"), programDir, synergycName); +} + +QString SettingsDialog::browseForSynergys(QWidget* parent, const QString& programDir, const QString& synergysName) +{ + return QFileDialog::getOpenFileName(parent, tr("Browse for synergys executable"), programDir, synergysName); +} +bool SettingsDialog::on_m_pButtonBrowseSynergys_clicked() +{ + QString fileName = browseForSynergys(this, appConfig().synergyProgramDir(), appConfig().synergysName()); + + if (!fileName.isEmpty()) + { + m_pLineEditSynergys->setText(fileName); + return true; + } + + return false; +} + +bool SettingsDialog::on_m_pButtonBrowseSynergyc_clicked() +{ + QString fileName = browseForSynergyc(this, appConfig().synergyProgramDir(), appConfig().synergycName()); + + if (!fileName.isEmpty()) + { + m_pLineEditSynergyc->setText(fileName); + return true; + } + + return false; +} + +void SettingsDialog::accept() +{ + appConfig().setAutoConnect(m_pCheckBoxAutoConnect->isChecked()); + appConfig().setSynergyc(m_pLineEditSynergyc->text()); + appConfig().setSynergys(m_pLineEditSynergys->text()); + appConfig().setScreenName(m_pLineEditScreenName->text()); + appConfig().setPort(m_pSpinBoxPort->value()); + appConfig().setInterface(m_pLineEditInterface->text()); + appConfig().setLogLevel(m_pComboLogLevel->currentIndex()); + + QDialog::accept(); +} + diff --git a/gui/src/SettingsDialog.h b/gui/src/SettingsDialog.h new file mode 100644 index 00000000..03842703 --- /dev/null +++ b/gui/src/SettingsDialog.h @@ -0,0 +1,33 @@ +#if !defined(SETTINGSDIALOG_H) + +#define SETTINGSDIALOG_H + +#include +#include "ui_SettingsDialogBase.h" + +class AppConfig; + +class SettingsDialog : public QDialog, public Ui::SettingsDialogBase +{ + Q_OBJECT + + public: + SettingsDialog(QWidget* parent, AppConfig& config); + + public: + static QString browseForSynergyc(QWidget* parent, const QString& programDir, const QString& synergycName); + static QString browseForSynergys(QWidget* parent, const QString& programDir, const QString& synergysName); + + protected slots: + bool on_m_pButtonBrowseSynergys_clicked(); + bool on_m_pButtonBrowseSynergyc_clicked(); + + protected: + void accept(); + AppConfig& appConfig() { return m_AppConfig; } + + private: + AppConfig& m_AppConfig; +}; + +#endif diff --git a/gui/src/TrashScreenWidget.cpp b/gui/src/TrashScreenWidget.cpp new file mode 100644 index 00000000..2aac8d21 --- /dev/null +++ b/gui/src/TrashScreenWidget.cpp @@ -0,0 +1,25 @@ +#include "TrashScreenWidget.h" +#include "ScreenSetupModel.h" + +#include +#include + +void TrashScreenWidget::dragEnterEvent(QDragEnterEvent* event) +{ + if (event->mimeData()->hasFormat(ScreenSetupModel::mimeType())) + { + event->setDropAction(Qt::MoveAction); + event->accept(); + } + else + event->ignore(); +} + +void TrashScreenWidget::dropEvent(QDropEvent* event) +{ + if (event->mimeData()->hasFormat(ScreenSetupModel::mimeType())) + event->acceptProposedAction(); + else + event->ignore(); +} + diff --git a/gui/src/TrashScreenWidget.h b/gui/src/TrashScreenWidget.h new file mode 100644 index 00000000..d12ff564 --- /dev/null +++ b/gui/src/TrashScreenWidget.h @@ -0,0 +1,24 @@ +#if !defined(TRASHSCREENWIDGET__H) + +#define TRASHSCREENWIDGET__H + +#include + +class QWidget; +class QDragEnterEvent; +class QDropEvent; + +class TrashScreenWidget : public QLabel +{ + Q_OBJECT + + public: + TrashScreenWidget(QWidget* parent) : QLabel(parent) {} + + public: + void dragEnterEvent(QDragEnterEvent* event); + void dropEvent(QDropEvent* event); +}; + +#endif + diff --git a/gui/src/main.cpp b/gui/src/main.cpp new file mode 100644 index 00000000..3087fe0b --- /dev/null +++ b/gui/src/main.cpp @@ -0,0 +1,36 @@ +#include "QSynergyApplication.h" +#include "MainWindow.h" + +#include +#include + +int main(int argc, char* argv[]) +{ + QCoreApplication::setOrganizationName("Fidra"); + QCoreApplication::setOrganizationDomain("www.fidra.de"); + QCoreApplication::setApplicationName("QSynergy"); + + QSynergyApplication app(argc, argv); + +#if !defined(Q_OS_MAC) + if (!QSystemTrayIcon::isSystemTrayAvailable()) + { + QMessageBox::critical(NULL, "QSynergy", QObject::tr("There doesn't seem to be a system tray available. Quitting.")); + return -1; + } + + QApplication::setQuitOnLastWindowClosed(false); +#endif + + QTranslator qtTranslator; + qtTranslator.load("qt_" + QLocale::system().name()); + app.installTranslator(&qtTranslator); + + QTranslator myappTranslator; + myappTranslator.load("res/lang/QSynergy_" + QLocale::system().name()); + app.installTranslator(&myappTranslator); + + MainWindow mainWindow(NULL); + return app.exec(); +} +