From b449f84c4f3992af196627d9ed45127401bc72f0 Mon Sep 17 00:00:00 2001 From: Bernd Wachter Date: Wed, 20 Feb 2013 23:11:21 +0200 Subject: [PATCH] Import fingerterm from 1.0.2 tarball --- .gitignore | 6 + COPYING | 339 ++++ data/english.layout | 77 + data/finnish.layout | 77 + data/menu.xml | 29 + dbusadaptor.cpp | 60 + dbusadaptor.h | 54 + fingerterm.desktop | 10 + fingerterm.png | Bin 0 -> 3143 bytes fingerterm.pro | 75 + icons/backspace.png | Bin 0 -> 829 bytes icons/down.png | Bin 0 -> 965 bytes icons/enter.png | Bin 0 -> 1697 bytes icons/left.png | Bin 0 -> 829 bytes icons/menu.png | Bin 0 -> 818 bytes icons/right.png | Bin 0 -> 862 bytes icons/scroll-indicator.png | Bin 0 -> 791 bytes icons/shift.png | Bin 0 -> 1744 bytes icons/tab.png | Bin 0 -> 1422 bytes icons/up.png | Bin 0 -> 929 bytes keyloader.cpp | 232 +++ keyloader.h | 70 + main.cpp | 260 +++ mainwindow.cpp | 91 ++ mainwindow.h | 47 + ptyiface.cpp | 146 ++ ptyiface.h | 63 + qml/Button.qml | 85 + qml/Key.qml | 188 +++ qml/Keyboard.qml | 113 ++ qml/LayoutWindow.qml | 132 ++ qml/Lineview.qml | 108 ++ qml/Main.qml | 374 +++++ qml/Menu.qml | 508 ++++++ qml/NotifyWin.qml | 95 ++ qml/UrlWindow.qml | 133 ++ qtc_packaging/debian_harmattan/changelog | 57 + qtc_packaging/debian_harmattan/compat | 1 + qtc_packaging/debian_harmattan/control | 80 + qtc_packaging/debian_harmattan/copyright | 14 + qtc_packaging/debian_harmattan/manifest.aegis | 0 qtc_packaging/debian_harmattan/rules | 91 ++ resources.qrc | 26 + terminal.cpp | 1398 +++++++++++++++++ terminal.h | 163 ++ textrender.cpp | 316 ++++ textrender.h | 98 ++ updateversion.sh | 11 + util.cpp | 394 +++++ util.h | 110 ++ version.h | 5 + 51 files changed, 6136 insertions(+) create mode 100644 .gitignore create mode 100644 COPYING create mode 100644 data/english.layout create mode 100644 data/finnish.layout create mode 100644 data/menu.xml create mode 100644 dbusadaptor.cpp create mode 100644 dbusadaptor.h create mode 100644 fingerterm.desktop create mode 100644 fingerterm.png create mode 100644 fingerterm.pro create mode 100644 icons/backspace.png create mode 100644 icons/down.png create mode 100644 icons/enter.png create mode 100644 icons/left.png create mode 100644 icons/menu.png create mode 100644 icons/right.png create mode 100644 icons/scroll-indicator.png create mode 100644 icons/shift.png create mode 100644 icons/tab.png create mode 100644 icons/up.png create mode 100644 keyloader.cpp create mode 100644 keyloader.h create mode 100644 main.cpp create mode 100644 mainwindow.cpp create mode 100644 mainwindow.h create mode 100644 ptyiface.cpp create mode 100644 ptyiface.h create mode 100644 qml/Button.qml create mode 100644 qml/Key.qml create mode 100644 qml/Keyboard.qml create mode 100644 qml/LayoutWindow.qml create mode 100644 qml/Lineview.qml create mode 100644 qml/Main.qml create mode 100644 qml/Menu.qml create mode 100644 qml/NotifyWin.qml create mode 100644 qml/UrlWindow.qml create mode 100644 qtc_packaging/debian_harmattan/changelog create mode 100644 qtc_packaging/debian_harmattan/compat create mode 100644 qtc_packaging/debian_harmattan/control create mode 100644 qtc_packaging/debian_harmattan/copyright create mode 100644 qtc_packaging/debian_harmattan/manifest.aegis create mode 100755 qtc_packaging/debian_harmattan/rules create mode 100644 resources.qrc create mode 100644 terminal.cpp create mode 100644 terminal.h create mode 100644 textrender.cpp create mode 100644 textrender.h create mode 100755 updateversion.sh create mode 100644 util.cpp create mode 100644 util.h create mode 100644 version.h diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ab3e468 --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +Makefile +*.pro.user* +*.o +moc_* +./fingerterm +qrc_resources.cpp diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..d159169 --- /dev/null +++ b/COPYING @@ -0,0 +1,339 @@ + 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 + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/data/english.layout b/data/english.layout new file mode 100644 index 0000000..948e997 --- /dev/null +++ b/data/english.layout @@ -0,0 +1,77 @@ +; use keycodes from: +; http://doc.qt.nokia.com/4.7/qt.html#Key-enum +; +; - [] will leave an empty space in the keyboard +; - Recognized modifier keys are shift, alt, ctrl +; - Letters will automatically handle upper/lowercase +; - Empty row in the file acts as a keyboard row separator + +["esc", 0x1000000, "", 0x0] +[">", 0x3e, "<", 0x3c] +["|", 0x7c, "&", 0x26] +["!", 0x21, "?", 0x3f] +["\"", 0x22, "$", 0x24] +["'", 0x27, "`", 0x60] +["_", 0x5f, "@", 0x40] +["=", 0x3d, "~", 0x7e] +["/", 0x2f, "\\", 0x5c] +["*", 0x2a, "del", 0x1000007] +[":backspace", 0x1000003, "", 0x0] + +[":tab", 0x1000001, "", 0x0] +["1", 0x31, "+", 0x2b] +["2", 0x32, "^", 0x5e] +["3", 0x33, "#", 0x23] +["4", 0x34, "%", 0x25] +["5", 0x35, "(", 0x28] +["6", 0x36, ")", 0x29] +["7", 0x37, "[", 0x5b] +["8", 0x38, "]", 0x5d] +["9", 0x39, "{", 0x7b] +["0", 0x30, "}", 0x7d] + +["q", 0x51, "", 0x0] +["w", 0x57, "", 0x0] +["e", 0x45, "", 0x0] +["r", 0x52, "", 0x0] +["t", 0x54, "", 0x0] +["y", 0x59, "", 0x0] +["u", 0x55, "", 0x0] +["i", 0x49, "", 0x0] +["o", 0x4f, "", 0x0] +["p", 0x50, "", 0x0] +[":enter", 0x1000004, "", 0x0] + +["a", 0x41, "", 0x0] +["s", 0x53, "", 0x0] +["d", 0x44, "", 0x0] +["f", 0x46, "", 0x0] +["g", 0x47, "", 0x0] +["h", 0x48, "", 0x0] +["j", 0x4a, "", 0x0] +["k", 0x4b, "", 0x0] +["l", 0x4c, "", 0x0] +[":", 0x3a, "", 0x0] +["pgup", 0x1000016, "home", 0x1000010] + +[":shift", 0x2000000, "", 0x0] +["z", 0x5a, "", 0x0] +["x", 0x58, "", 0x0] +["c", 0x43, "", 0x0] +["v", 0x56, "", 0x0] +["b", 0x42, "", 0x0] +["n", 0x4e, "", 0x0] +["m", 0x4d, "", 0x0] +[";", 0x3b, "", 0x0] +[":up", 0x1000013, "", 0x0] +["pgdn", 0x1000017, "end", 0x1000011] + +["ctrl", 0x4000000, "", 0x0] +["alt", 0x8000000, "", 0x0] +["-", 0x2d, "", 0x0] +[",", 0x2c, "", 0x0] +[[[" ", 0x20, "", 0x0]]] +[".", 0x2e, "", 0x0] +[":left", 0x1000012, "", 0x0] +[":down", 0x1000015, "", 0x0] +[":right", 0x1000014, "", 0x0] diff --git a/data/finnish.layout b/data/finnish.layout new file mode 100644 index 0000000..f76be30 --- /dev/null +++ b/data/finnish.layout @@ -0,0 +1,77 @@ +; use keycodes from: +; http://doc.qt.nokia.com/4.7/qt.html#Key-enum +; +; - [] will leave an empty space in the keyboard +; - Recognized modifier keys are shift, alt, ctrl +; - Letters will automatically handle upper/lowercase +; - Empty row in the file acts as a keyboard row separator + +["esc", 0x1000000, "", 0x0] +[">", 0x3e, "<", 0x3c] +["|", 0x7c, "&", 0x26] +["!", 0x21, "?", 0x3f] +["\"", 0x22, "$", 0x24] +["'", 0x27, "`", 0x60] +["_", 0x5f, "@", 0x40] +["=", 0x3d, "~", 0x7e] +["/", 0x2f, "\\", 0x5c] +["-", 0x2d, "*", 0x2a] +[":backspace", 0x1000003, "", 0x0] + +[":tab", 0x1000001, "", 0x0] +["1", 0x31, "+", 0x2b] +["2", 0x32, "^", 0x5e] +["3", 0x33, "#", 0x23] +["4", 0x34, "%", 0x25] +["5", 0x35, "(", 0x28] +["6", 0x36, ")", 0x29] +["7", 0x37, "[", 0x5b] +["8", 0x38, "]", 0x5d] +["9", 0x39, "{", 0x7b] +["0", 0x30, "}", 0x7d] + +["q", 0x51, "", 0x0] +["w", 0x57, "", 0x0] +["e", 0x45, "", 0x0] +["r", 0x52, "", 0x0] +["t", 0x54, "", 0x0] +["y", 0x59, "", 0x0] +["u", 0x55, "", 0x0] +["i", 0x49, "", 0x0] +["o", 0x4f, "", 0x0] +["p", 0x50, "", 0x0] +[":enter", 0x1000004, "", 0x0] + +["a", 0x41, "", 0x0] +["s", 0x53, "", 0x0] +["d", 0x44, "", 0x0] +["f", 0x46, "", 0x0] +["g", 0x47, "", 0x0] +["h", 0x48, "", 0x0] +["j", 0x4a, "", 0x0] +["k", 0x4b, "", 0x0] +["l", 0x4c, "", 0x0] +["ä", 0xc4, "", 0x0] +["pgup", 0x1000016, "home", 0x1000010] + +[":shift", 0x2000000, "", 0x0] +["z", 0x5a, "", 0x0] +["x", 0x58, "", 0x0] +["c", 0x43, "", 0x0] +["v", 0x56, "", 0x0] +["b", 0x42, "", 0x0] +["n", 0x4e, "", 0x0] +["m", 0x4d, "", 0x0] +["ö", 0xd6, "", 0x0] +[":up", 0x1000013, "", 0x0] +["pgdn", 0x1000017, "end", 0x1000011] + +["ctrl", 0x4000000, "", 0x0] +["alt", 0x8000000, "", 0x0] +["å", 0xc2, "", 0x0] +[",", 0x2c, ";", 0x3b] +[[[" ", 0x20, "", 0x0]]] +[".", 0x2e, ":", 0x3a] +[":left", 0x1000012, "", 0x0] +[":down", 0x1000015, "", 0x0] +[":right", 0x1000014, "", 0x0] diff --git a/data/menu.xml b/data/menu.xml new file mode 100644 index 0000000..702057f --- /dev/null +++ b/data/menu.xml @@ -0,0 +1,29 @@ + + + + ssh screen example + ssh -t user@example.com screen -UDr\r + + irssi + + + ssh example + ssh user@example.com\r + + + screen -Dr + screen -Dr\r + + + Ctrl-A-D + \x01\x04 + + + Ctrl-C + \x03 + + diff --git a/dbusadaptor.cpp b/dbusadaptor.cpp new file mode 100644 index 0000000..d0237f2 --- /dev/null +++ b/dbusadaptor.cpp @@ -0,0 +1,60 @@ +/* + Copyright 2011-2012 Heikki Holstila + + This file is part of FingerTerm. + + FingerTerm is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + FingerTerm is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with FingerTerm. If not, see . +*/ + +#include +#include +#include "dbusadaptor.h" +#include "mainwindow.h" + +#ifdef MEEGO_EDITION_HARMATTAN + +DbusAdaptor::DbusAdaptor(QObject *parent): + MApplicationService("org.hqh.fingerterm", parent), + mainWin(0) +{ +} + +DbusAdaptor::~DbusAdaptor() +{ +} + +void DbusAdaptor::launch() +{ + MApplicationService::launch(); + + if (mainWin) { + mainWin->raise(); + } +} + +void DbusAdaptor::launch(const QStringList ¶meters) +{ + if (parameters.contains("new")) + launchAnotherWithQProcess(); + else + launch(); +} + +void DbusAdaptor::handleServiceRegistrationFailure() +{ + // for some reason the subsequent instances get the default "com.nokia..." prefix + incrementAndRegister(); +} + +#endif // MEEGO_EDITION_HARMATTAN diff --git a/dbusadaptor.h b/dbusadaptor.h new file mode 100644 index 0000000..e78f637 --- /dev/null +++ b/dbusadaptor.h @@ -0,0 +1,54 @@ +/* + Copyright 2011-2012 Heikki Holstila + + This file is part of FingerTerm. + + FingerTerm is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + FingerTerm is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with FingerTerm. If not, see . +*/ + +#ifndef DBUSADAPTOR_H +#define DBUSADAPTOR_H + +#include "qplatformdefs.h" +class MainWindow; + +#ifdef MEEGO_EDITION_HARMATTAN + +// handles dbus registration & multiple instances on harmattan + +#include + +class DbusAdaptor : public MApplicationService +{ + Q_OBJECT + +public: + explicit DbusAdaptor(QObject *parent = 0); + virtual ~DbusAdaptor(); + +public slots: + virtual void launch(); + virtual void launch(const QStringList ¶meters); + virtual void handleServiceRegistrationFailure(); + + void setAppWindow(MainWindow* win) { mainWin = win; } + +private: + Q_DISABLE_COPY(DbusAdaptor) + + MainWindow* mainWin; +}; + +#endif // MEEGO_EDITION_HARMATTAN +#endif // DBUSADAPTOR_H diff --git a/fingerterm.desktop b/fingerterm.desktop new file mode 100644 index 0000000..9205df7 --- /dev/null +++ b/fingerterm.desktop @@ -0,0 +1,10 @@ +[Desktop Entry] +Encoding=UTF-8 +Version=1.0 +Name=FingerTerm +GenericName=FingerTerm +Comment=Terminal emulator +Exec=/usr/bin/invoker --single-instance --type=e /opt/fingerterm/bin/fingerterm +Terminal=false +Type=Application +Icon=/usr/share/icons/hicolor/80x80/apps/fingerterm.png diff --git a/fingerterm.png b/fingerterm.png new file mode 100644 index 0000000000000000000000000000000000000000..bd057c45c9d521196d84a567a7cf4311d275e567 GIT binary patch literal 3143 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 literal 0 HcmV?d00001 diff --git a/fingerterm.pro b/fingerterm.pro new file mode 100644 index 0000000..8a042e3 --- /dev/null +++ b/fingerterm.pro @@ -0,0 +1,75 @@ +QT = core gui declarative opengl dbus + +contains(MEEGO_EDITION,harmattan): { + CONFIG += meegotouch +} + +TEMPLATE = app +TARGET = +DEPENDPATH += . +INCLUDEPATH += . +LIBS += -lutil + +system($$PWD/updateversion.sh) + +# Input +HEADERS += \ + ptyiface.h \ + terminal.h \ + textrender.h \ + version.h \ + util.h \ + keyloader.h \ + mainwindow.h \ + dbusadaptor.h +SOURCES += main.cpp terminal.cpp textrender.cpp \ + ptyiface.cpp \ + util.cpp \ + keyloader.cpp \ + mainwindow.cpp \ + dbusadaptor.cpp + +OTHER_FILES += \ + qml/Main.qml \ + qml/Keyboard.qml \ + qml/Key.qml \ + qml/Lineview.qml \ + qtc_packaging/debian_harmattan/rules \ + qtc_packaging/debian_harmattan/copyright \ + qtc_packaging/debian_harmattan/control \ + qtc_packaging/debian_harmattan/compat \ + qtc_packaging/debian_harmattan/changelog \ + qml/Button.qml \ + qml/Menu.qml \ + qml/NotifyWin.qml \ + qml/UrlWindow.qml \ + qml/LayoutWindow.qml + +RESOURCES += \ + resources.qrc + +unix:!symbian:!maemo5 { + target.path = /opt/fingerterm/bin + INSTALLS += target +} + +maemo5 { + target.path = /opt/fingerterm/bin + INSTALLS += target +} + +contains(MEEGO_EDITION,harmattan) { + desktopfile.files = $${TARGET}fingerterm.desktop + desktopfile.path = /usr/share/applications + INSTALLS += desktopfile +} + +contains(MEEGO_EDITION,harmattan) { + icon.files = fingerterm.png + icon.path = /usr/share/icons/hicolor/80x80/apps + INSTALLS += icon +} + + + + diff --git a/icons/backspace.png b/icons/backspace.png new file mode 100644 index 0000000000000000000000000000000000000000..e1f36e7484fa660025b1fb45852c2923566a9b7a GIT binary patch literal 829 zcmV-D1H$}?P)Px#24YJ`L;(K){{a7>y{D4^000SaNLh0L01FcU01FcV0GgZ_00007bV*G`2ipk< z4=EHWNdS2O000?uMObu0Z*6U5Zgc=ca%Ew3Wn>_CX>@2HM@dakSAh-}0007*Nklizx3H(0L0v-SwI0oJV@BQya&=&}N4w%{7+v_;zD9e&otJMX*jiZ1o zz+GS(cnY)+4-Y$KSyGlIRaH?{6~H~*XA*4BZ`{AF1pz6wQ1o98)Mmes<-!$X7t zH{|fpzOKH98Il1Kj4ULRNwAXLP>i*<4m8ZZ?5YTmc>&Tit#`o~L!RgLj>uXfAkTAv zA3kmra0_?>EcW~T6$v2Bo@CyuuLGT(oqYpr1D~4mfc9?Q2AG_jJOxeJJ8-CZ!I0*=aoGYuP zrKK%kW_5M76Zm4+*W_4VXvX?J@ZaCaB9TZW5{X12k>>afWTwrc8kXXO00000NkvXX Hu0mjftWsiz literal 0 HcmV?d00001 diff --git a/icons/down.png b/icons/down.png new file mode 100644 index 0000000000000000000000000000000000000000..7a4ff65b08f02a9948ec9a734544ce975ab39c92 GIT binary patch literal 965 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 Hu0mjf;9+A- literal 0 HcmV?d00001 diff --git a/icons/menu.png b/icons/menu.png new file mode 100644 index 0000000000000000000000000000000000000000..eb298fddd1c97b15d60821b9f2663eaa712ac1c9 GIT binary patch literal 818 zcmV-21I_%2P)Px#24YJ`L;(K){{a7>y{D4^000SaNLh0L01FcU01FcV0GgZ_00007bV*G`2ipk< z4>BmP5VvFi000?uMObu0Z*6U5Zgc=ca%Ew3Wn>_CX>@2HM@dakSAh-}0007wNklcS!;i3;O`9 zcX4lk@iGfgP19`b@9)1J4u|XBdo6?@#)xxHg%Enim}kH8ODVUNQb8$&wN}pd0N(ot zXJ=>k4-XIDHBB>K<_ML*tCN$H_mjy4?>$mVoO5{ZIX^!qgg};MSZmQ*ljk`~DeAgr zb#)cz94jj;XsyYz3?T$U2to+#?CiV*J}h;FfU2shS?7o`5@V!k8hX7RN-5g5CCf6V z(dMa$bZ|3Mu8>y!QarvI~g7LUH%;a?1vp@EaB%Q;I2^8r5cDjG%gak?tu@x#vZ|_wTU%R?g%C(75kk=K z_rH!tqi=bh*IMgFDHXc;kKTI*gMk4i%YFw`S(cl1UB3XH{Peq&KrVzRV~lIS8jyEq z7Pt^XT*Mf^1DD-x)IecpF>cR4ybqTlaxdV0$7@iBXQdx^sm%q5shFqdF1!CZp51ao&9<`!c9ZX#l=KJs0qg-J{WOt^a{vGU07*qoM6N<$f{~zLU;qFB literal 0 HcmV?d00001 diff --git a/icons/right.png b/icons/right.png new file mode 100644 index 0000000000000000000000000000000000000000..25bf63c6d984c414336096dc16f36990744c256d GIT binary patch literal 862 zcmV-k1EKthP)Px#24YJ`L;(K){{a7>y{D4^000SaNLh0L01FcU01FcV0GgZ_00007bV*G`2ipk< z4=5hchu9_n000?uMObu0Z*6U5Zgc=ca%Ew3Wn>_CX>@2HM@dakSAh-}0008HNkl%t7{`C}b_WL>36SU!xWpK-(S*=oO=3qwbBSNT-cLf$2cV!KcJc*GFjr_S zj3gI`F`?3^V8Su8GZweAF2||Ff<-gGWHWnv`||vsd7l65LL!k!Boc{4B2g8h;^)_Z zNnjQ*fCWAQr@;ADZlEd_Fb&)V9s$e1J)oJ3IR~U3R z4)6|m4IBXI z2vLlCdwU&YOlNIvZ3}n_ycsnc`X)F@v)L>H4}lKuVtr>m>0w}3mN%1VrI zgNZyd*TDneaktxj60@IW8Q%MWMGApZ3S$iQdYz@ErAuWMXJ9gSgJoa=n7Sqbw}JcP zn6(z~eKCd*1`U-9&{|`RDY6%j z$2wPPu>8c|(S^#wq?APr!+nuoKMj~DzyjaK#>S}CY8Qfv`_@{VbHx~EVOT`@Au+pU zcNq>kj`8|(rpuz@bul@>SHMQ;GgbCLuh&~CAN1Z|D$lskH5v`(=H@nm7eF`8z$wrJ zzEt2sNGVs8QWwfrN{RQr*ww- zS?q(@Kk>C9guwj#{Ec3TEpRf*ZCb6?Kk+W~9rzeaMhFoDzw+B?)gLrv_nQG+ZYF;W o{J;4kOC%DBL?V$$Bocqk51~g?vwL6D1^@s607*qoM6N<$f);OfR{#J2 literal 0 HcmV?d00001 diff --git a/icons/scroll-indicator.png b/icons/scroll-indicator.png new file mode 100644 index 0000000000000000000000000000000000000000..f3d29873cba64449121438773ad160943add445e GIT binary patch literal 791 zcmV+y1L*vTP)Px#24YJ`L;(K){{a7>y{D4^000SaNLh0L01FcU01FcV0GgZ_00007bV*G`2ipq} z5-AM9wuBr2000?uMObu0Z*6U5Zgc=ca%Ew3Wn>_CX>@2HM@dakSAh-}0007VNklA#CXLgfG1V2LYUJ&XRDEI+-QH1u~qnDn%7kcY=@D%8$uqST{5-FIl zp2UMWs9^JFoZ0E2O(^|i&6-WJWq+`+?6B}W@9Ye-FI1^grAnz{jH!xH&B9NSyf5zV z?$Yb^@ZKXLgb;Afk!2aim_I&`q6noFN-3`u#pDD=RqXsMTs{t%>6p5kW-AvW)fh^{F^? zUV&z_IjLL-fe-?N!GP7(Rg_W;heP5xW;7ZB;GDx4LmbB_r7*@!WWYJdgW|0ljmE=O5y>@Hc*a}aB8xMZY;A4PZnx=nyPTh&)9G~B+1aTU?0Ac`U$lpk0H}2z-g)8^9X@cnR-5!^fkNkZ-^ccn^H|bu5G>zksj6>xTqO4ZpM! z)4(sCL>~C1k$4LH!X;(_Kan^pG5p*mih!S+M3L}?OUwhlFo}7>&s<^*zqz?NzvFtE zrZbTk!?)XQPEJmy=7^rs*hHh|;&KO=qMZKu;|)$4VVB$=N;y=&bB>4UVUP^d?FFzzonP>mlkgEI_`3C$6 V_sBOWxzPXs002ovPDHLkV1foBYh3^U literal 0 HcmV?d00001 diff --git a/icons/shift.png b/icons/shift.png new file mode 100644 index 0000000000000000000000000000000000000000..fa4254e662723ae8b554bdcd730ddcfc39ea6c82 GIT binary patch literal 1744 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 D@MoZQ literal 0 HcmV?d00001 diff --git a/keyloader.cpp b/keyloader.cpp new file mode 100644 index 0000000..d91d0fb --- /dev/null +++ b/keyloader.cpp @@ -0,0 +1,232 @@ +/* + Copyright 2011-2012 Heikki Holstila + + This file is part of FingerTerm. + + FingerTerm is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + FingerTerm is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with FingerTerm. If not, see . +*/ + +#include +#include + +#include "keyloader.h" +#include "util.h" + +KeyLoader::KeyLoader(QObject *parent) : + QObject(parent), + iVkbRows(0), + iVkbColumns(0), + iUtil(0) +{ +} + +KeyLoader::~KeyLoader() +{ +} + +bool KeyLoader::loadLayout(QString layout) +{ + bool ret = false; + if(layout.isEmpty() || !iUtil) + return false; + + if (layout.at(0)==':') { // load from resources + QResource res(layout); + QByteArray resArr( reinterpret_cast(res.data()) ); + QBuffer resBuf( &resArr ); + ret = loadLayoutInternal(resBuf); + } + else { // load from file + QFile f(iUtil->configPath() + "/" + layout + ".layout"); + ret = loadLayoutInternal(f); + } + + return ret; +} + +bool KeyLoader::loadLayoutInternal(QIODevice &from) +{ + iKeyData.clear(); + bool ret = true; + + iVkbRows = 0; + iVkbColumns = 0; + bool lastLineHadKey = false; + + if( !from.open(QIODevice::ReadOnly | QIODevice::Text) ) + return false; + + QList keyRow; + while(!from.atEnd()) { + QString line = QString::fromUtf8(from.readLine()).simplified(); + if(line.length()>=2 && line.at(0)!=';' && line.at(0)=='[' && line.at(line.length()-1)==']') + { + KeyData key; + key.label = ""; + key.code = 0; + key.label_alt = ""; + key.code_alt = 0; + key.isModifier = false; + + line.replace("\\\\", "\\x5C"); + line.replace("\" \"", "\\x20"); + line.replace(" ", ""); + line.replace("\"[\"", "\\x5B"); + line.replace("\"]\"", "\\x5D"); + line.replace("\",\"", "\\x2C"); + line.replace("\\\"", "\\x22"); + line.replace("\"", ""); + key.width = line.count('['); + line.replace("[", ""); + line.replace("]", ""); + + line.replace("\\x20", " "); + line.replace("\\x22", "\""); + line.replace("\\x5B", "["); + line.replace("\\x5D", "]"); + line.replace("\\x5C", "\\"); + + QStringList parts = line.split(",", QString::KeepEmptyParts); + if(parts.count()>=2) { + bool ok = true; + key.label = parts.at(0); + key.label.replace("\\x2C",","); + parts[1].replace("0x",""); + key.code = parts.at(1).toInt(&ok,16); + if(!ok) { + ret = false; + break; + } + if(key.code==Qt::AltModifier || key.code==Qt::ControlModifier || key.code==Qt::ShiftModifier) + key.isModifier = true; + if(parts.count()>=4 && !key.isModifier) { + key.label_alt = parts.at(2); + key.label_alt.replace("\\x2C",","); + parts[3].replace("0x",""); + key.code_alt = parts.at(3).toInt(&ok,16); + if(!ok) { + ret = false; + break; + } + } + } + lastLineHadKey = true; + cleanUpKey(key); + keyRow.append(key); + } + else if(line.length()==0 && lastLineHadKey) { + if(keyRow.count() > iVkbColumns) { + iVkbColumns = keyRow.count(); + } + iKeyData.append(keyRow); + keyRow.clear(); + lastLineHadKey = false; + } + else { + lastLineHadKey = false; + } + } + if(keyRow.count() > 0) + iKeyData.append(keyRow); + + iVkbRows = iKeyData.count(); + foreach(QList r, iKeyData) { + if(r.count() > iVkbColumns) + iVkbColumns = r.count(); + } + + from.close(); + + if (iVkbColumns <= 0 || iVkbRows <= 0) + ret = false; + + if (!ret) + iKeyData.clear(); + + return ret; +} + +QVariantList KeyLoader::keyAt(int row, int col) +{ + QVariantList ret; + ret.append(""); //label + ret.append(0); //code + ret.append(""); //label_alt + ret.append(0); //code_alt + ret.append(0); //width + ret.append(false); //isModifier + + if(iKeyData.count() <= row) + return ret; + if(iKeyData.at(row).count() <= col) + return ret; + + ret[0] = iKeyData.at(row).at(col).label; + ret[1] = iKeyData.at(row).at(col).code; + ret[2] = iKeyData.at(row).at(col).label_alt; + ret[3] = iKeyData.at(row).at(col).code_alt; + ret[4] = iKeyData.at(row).at(col).width; + ret[5] = iKeyData.at(row).at(col).isModifier; + + return ret; +} + +const QStringList KeyLoader::availableLayouts() +{ + if (!iUtil) + return QStringList(); + + QDir confDir(iUtil->configPath()); + QStringList filter("*.layout"); + + QStringList results = confDir.entryList(filter, QDir::Files|QDir::Readable, QDir::Name); + + QStringList ret; + foreach(QString s, results) { + ret << s.left(s.lastIndexOf('.')); + } + + return ret; +} + +void KeyLoader::cleanUpKey(KeyData &key) +{ + // make sure that a key does not try to use some (currently) unsupported feature... + + // if the label is an image or a modifier, we do not support an alternative label + if ((key.label.startsWith(':') && key.label.length()>1) || key.isModifier) { + key.label_alt = ""; + key.code_alt = 0; + } + + // if the alternative label is an image (and the default one was not), use it as the (only) default + if (key.label_alt.startsWith(':') && key.label_alt.length()>1) { + key.label = key.label_alt; + key.code = key.code_alt; + key.label_alt = ""; + key.code_alt = 0; + } + + // alphabet letters can't have an alternative, they just switch between lower and upper case + if (key.label.length()==1 && key.label.at(0).isLetter()) { + key.label_alt = ""; + key.code_alt = 0; + } + + // ... also, can't have alphabet letters as an alternative label + if (key.label_alt.length()==1 && key.label_alt.at(0).isLetter()) { + key.label_alt = ""; + key.code_alt = 0; + } +} diff --git a/keyloader.h b/keyloader.h new file mode 100644 index 0000000..0b9202c --- /dev/null +++ b/keyloader.h @@ -0,0 +1,70 @@ +/* + Copyright 2011-2012 Heikki Holstila + + This file is part of FingerTerm. + + FingerTerm is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + FingerTerm is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with FingerTerm. If not, see . +*/ + +#ifndef KEYLOADER_H +#define KEYLOADER_H + +#include +#include + +class Util; + +struct KeyData { + QString label; + int code; + QString label_alt; + int code_alt; + int width; + bool isModifier; +}; + +class KeyLoader : public QObject +{ + Q_OBJECT +public: + explicit KeyLoader(QObject *parent = 0); + virtual ~KeyLoader(); + + void setUtil(Util* util) { iUtil = util; } + + Q_INVOKABLE bool loadLayout(QString layout); + + Q_INVOKABLE int vkbRows() { return iVkbRows; } + Q_INVOKABLE int vkbColumns() { return iVkbColumns; } + Q_INVOKABLE QVariantList keyAt(int row, int col); + Q_INVOKABLE const QStringList availableLayouts(); + +signals: + +public slots: + +private: + Q_DISABLE_COPY(KeyLoader) + bool loadLayoutInternal(QIODevice &from); + void cleanUpKey(KeyData &key); + + int iVkbRows; + int iVkbColumns; + + QList > iKeyData; + + Util *iUtil; +}; + +#endif // KEYLOADER_H diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000..a8240fe --- /dev/null +++ b/main.cpp @@ -0,0 +1,260 @@ +/* + Copyright 2011-2012 Heikki Holstila + + This file is part of FingerTerm. + + FingerTerm is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + FingerTerm is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with FingerTerm. If not, see . +*/ + +#include "qplatformdefs.h" + +#include +#include +#include + +extern "C" { +#include +#include +#include +#include +#include +} + +#ifdef MEEGO_EDITION_HARMATTAN +#include +#include "dbusadaptor.h" +#endif + +#include "mainwindow.h" +#include "ptyiface.h" +#include "terminal.h" +#include "textrender.h" +#include "util.h" +#include "version.h" +#include "keyloader.h" + +void defaultSettings(QSettings* settings); +void copyFileFromResources(QString from, QString to); + +int main(int argc, char *argv[]) +{ + QSettings *settings = new QSettings(QDir::homePath()+"/.config/FingerTerm/settings.ini", QSettings::IniFormat); + defaultSettings(settings); + + // fork the child process before creating QApplication + int socketM; + int pid = forkpty(&socketM,NULL,NULL,NULL); + if( pid==-1 ) { + qFatal("forkpty failed"); + exit(1); + } else if( pid==0 ) { + setenv("TERM", settings->value("terminal/envVarTERM").toByteArray(), 1); + + QString execCmd; + for(int i=0; ivalue("general/execCmd").toString(); + } + if(execCmd.isEmpty()) { + // execute the user's default shell + passwd *pwdstruct = getpwuid(getuid()); + execCmd = QString(pwdstruct->pw_shell); + execCmd.append(" --login"); + } + + if(settings) + delete settings; // don't need 'em here + + QStringList execParts = execCmd.split(' ', QString::SkipEmptyParts); + if(execParts.length()==0) + exit(0); + char *ptrs[execParts.length()+1]; + for(int i=0; i("TextRender",1,0,"TextRender"); + MainWindow view; + +#ifdef MEEGO_EDITION_HARMATTAN + DbusAdaptor *dba = new DbusAdaptor(); + dba->setAppWindow(&view); + + // needed for MFeedback, also creates the dbus interface + MComponentData::createInstance(argc, argv, "fingerterm", dba); +#endif + +#ifdef MEEGO_EDITION_HARMATTAN + if(!app.arguments().contains("-nogl")) { + view.setViewport(new QGLWidget(QGLFormat(QGL::DoubleBuffer))); + view.setViewportUpdateMode(QGraphicsView::FullViewportUpdate); + } +#endif + view.setResizeMode(QDeclarativeView::SizeRootObjectToView); + + Terminal term; + Util util(settings); + term.setUtil(&util); + QString startupErrorMsg; + + // copy the default config files to the config dir if they don't already exist + copyFileFromResources(":/data/menu.xml", util.configPath()+"/menu.xml"); + copyFileFromResources(":/data/english.layout", util.configPath()+"/english.layout"); + copyFileFromResources(":/data/finnish.layout", util.configPath()+"/finnish.layout"); + + KeyLoader keyLoader; + keyLoader.setUtil(&util); + bool ret = keyLoader.loadLayout( settings->value("ui/keyboardLayout").toString() ); + if(!ret) { + // on failure, try to load the default one (english) directly from resources + startupErrorMsg = "There was an error loading the keyboard layout.
\nUsing the default one instead."; + settings->setValue("ui/keyboardLayout", "english"); + ret = keyLoader.loadLayout(":/data/english.layout"); + if(!ret) + qFatal("failure loading keyboard layout"); + } + + QDeclarativeContext* context = view.rootContext(); + context->setContextProperty( "term", &term ); + context->setContextProperty( "util", &util ); + context->setContextProperty( "keyLoader", &keyLoader ); + + view.setSource(QUrl("qrc:/qml/Main.qml")); + + QObject *win = view.rootObject(); + if(!win) + qFatal("no root object - qml error"); + + if(!startupErrorMsg.isEmpty()) + QMetaObject::invokeMethod(win, "showErrorMessage", Qt::QueuedConnection, Q_ARG(QVariant, startupErrorMsg)); + + TextRender *tr = win->findChild("textrender"); + tr->setUtil(&util); + tr->setTerminal(&term); + term.setRenderer(tr); + term.setWindow(&view); + util.setWindow(&view); + util.setTerm(&term); + util.setRenderer(tr); + view.scene()->installEventFilter(&util); //for grabbing mouse drags + + QObject::connect(&term,SIGNAL(displayBufferChanged()),win,SLOT(displayBufferChanged())); + QObject::connect(view.engine(),SIGNAL(quit()),&app,SLOT(quit())); + +#ifdef MEEGO_EDITION_HARMATTAN + view.showFullScreen(); +#else + if ((QApplication::desktop()->width() < 1024 || QApplication::desktop()->height() < 768 || app.arguments().contains("-fs")) + && !app.arguments().contains("-nofs")) + view.showFullScreen(); + else + view.show(); +#endif + + PtyIFace ptyiface(pid, socketM, &term, + settings->value("terminal/charset").toString()); + + if( ptyiface.failed() ) + qFatal("pty failure"); + + return app.exec(); +} + +void defaultSettings(QSettings* settings) +{ + if(!settings->contains("general/execCmd")) + settings->setValue("general/execCmd", ""); + if(!settings->contains("general/visualBell")) + settings->setValue("general/visualBell", true); + if(!settings->contains("general/backgroundBellNotify")) + settings->setValue("general/backgroundBellNotify", true); + if(!settings->contains("general/grabUrlsFromBackbuffer")) + settings->setValue("general/grabUrlsFromBackbuffer", false); + + if(!settings->contains("terminal/envVarTERM")) + settings->setValue("terminal/envVarTERM", "xterm"); + if(!settings->contains("terminal/charset")) + settings->setValue("terminal/charset", "UTF-8"); + + if(!settings->contains("ui/keyboardLayout")) + settings->setValue("ui/keyboardLayout", "english"); + if(!settings->contains("ui/fontFamily")) + settings->setValue("ui/fontFamily", "monospace"); + if(!settings->contains("ui/fontSize")) + settings->setValue("ui/fontSize", 11); + if(!settings->contains("ui/keyboardMargins")) + settings->setValue("ui/keyboardMargins", 10); + if(!settings->contains("ui/allowSwipe")) + settings->setValue("ui/allowSwipe", "auto"); // "true", "false", "auto" + if(!settings->contains("ui/keyboardFadeOutDelay")) + settings->setValue("ui/keyboardFadeOutDelay", 2500); + if(!settings->contains("ui/showExtraLinesFromCursor")) + settings->setValue("ui/showExtraLinesFromCursor", 1); + if(!settings->contains("ui/vkbShowMethod")) + settings->setValue("ui/vkbShowMethod", "fade"); // "fade", "move", "off" + if(!settings->contains("ui/keyPressFeedback")) + settings->setValue("ui/keyPressFeedback", true); + if(!settings->contains("ui/dragMode")) + settings->setValue("ui/dragMode", "gestures"); // "gestures, "scroll", "select" ("off" would also be ok) + + if(!settings->contains("state/showWelcomeScreen")) + settings->setValue("state/showWelcomeScreen", true); + if(!settings->contains("state/createdByVersion")) + settings->setValue("state/createdByVersion", PROGRAM_VERSION); + + if(!settings->contains("gestures/panLeftTitle")) + settings->setValue("gestures/panLeftTitle", "Alt-Right"); + if(!settings->contains("gestures/panLeftCommand")) + settings->setValue("gestures/panLeftCommand", "\\e\\e[C"); + if(!settings->contains("gestures/panRightTitle")) + settings->setValue("gestures/panRightTitle", "Alt-Left"); + if(!settings->contains("gestures/panRightCommand")) + settings->setValue("gestures/panRightCommand", "\\e\\e[D"); + if(!settings->contains("gestures/panUpTitle")) + settings->setValue("gestures/panUpTitle", "Page Down"); + if(!settings->contains("gestures/panUpCommand")) + settings->setValue("gestures/panUpCommand", "\\e[6~"); + if(!settings->contains("gestures/panDownTitle")) + settings->setValue("gestures/panDownTitle", "Page Up"); + if(!settings->contains("gestures/panDownCommand")) + settings->setValue("gestures/panDownCommand", "\\e[5~"); +} + +void copyFileFromResources(QString from, QString to) +{ + // copy a file from resources to the config dir if it does not exist there + QFileInfo toFile(to); + if(!toFile.exists()) { + QFile newToFile(toFile.absoluteFilePath()); + QResource res(from); + if (newToFile.open(QIODevice::WriteOnly)) { + newToFile.write( reinterpret_cast(res.data()) ); + newToFile.close(); + } else { + qFatal("failed to copy default config from resources"); + } + } +} diff --git a/mainwindow.cpp b/mainwindow.cpp new file mode 100644 index 0000000..8b47c67 --- /dev/null +++ b/mainwindow.cpp @@ -0,0 +1,91 @@ +/* + Copyright 2011-2012 Heikki Holstila + + This file is part of FingerTerm. + + FingerTerm is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + FingerTerm is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with FingerTerm. If not, see . +*/ + +#include "qplatformdefs.h" + +#include +#include "mainwindow.h" + +#ifdef MEEGO_EDITION_HARMATTAN +#include +#include +#include +#include +#include +#endif //MEEGO_EDITION_HARMATTAN + +MainWindow::MainWindow(QWidget* parent): QDeclarativeView(parent) +{ +} + +MainWindow::~MainWindow() +{ +} + +void MainWindow::focusInEvent(QFocusEvent *event) +{ + QDeclarativeView::focusInEvent(event); + emit focusChanged(true); +} + +void MainWindow::focusOutEvent(QFocusEvent *event) +{ + QDeclarativeView::focusOutEvent(event); + emit focusChanged(false); +} + +void MainWindow::minimize() +{ + setWindowState(windowState() | Qt::WindowMinimized); +} + +void MainWindow::disableSwipe() +{ +#ifdef MEEGO_EDITION_HARMATTAN + resize(MApplication::desktop()->screenGeometry().width(), + MApplication::desktop()->screenGeometry().height()); + showFullScreen(); + + unsigned int customRegion[] = + { + rect().x(), + rect().y(), + rect().width(), + rect().height() + }; + + Display *dpy = QX11Info::display(); + Atom customRegionAtom = XInternAtom(dpy, "_MEEGOTOUCH_CUSTOM_REGION", False); + + XChangeProperty(dpy, effectiveWinId(), customRegionAtom, + XA_CARDINAL, 32, PropModeReplace, + reinterpret_cast(&customRegion[0]), 4); + +#endif //MEEGO_EDITION_HARMATTAN +} + +void MainWindow::enableSwipe() +{ +#ifdef MEEGO_EDITION_HARMATTAN + Display *dpy = QX11Info::display(); + Atom customRegionAtom = XInternAtom(dpy, "_MEEGOTOUCH_CUSTOM_REGION", False); + + XDeleteProperty(dpy, effectiveWinId(), customRegionAtom); +#endif //MEEGO_EDITION_HARMATTAN +} diff --git a/mainwindow.h b/mainwindow.h new file mode 100644 index 0000000..b572973 --- /dev/null +++ b/mainwindow.h @@ -0,0 +1,47 @@ +/* + Copyright 2011-2012 Heikki Holstila + + This file is part of FingerTerm. + + FingerTerm is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + FingerTerm is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with FingerTerm. If not, see . +*/ + +#ifndef MAINWINDOW_H +#define MAINWINDOW_H + +#include + +class MainWindow : public QDeclarativeView +{ + Q_OBJECT +public: + explicit MainWindow(QWidget* parent=0); + virtual ~MainWindow(); + + void minimize(); + void enableSwipe(); + void disableSwipe(); + +protected: + virtual void focusInEvent(QFocusEvent *event); + virtual void focusOutEvent(QFocusEvent *event); + +signals: + void focusChanged(bool in); + +private: + Q_DISABLE_COPY(MainWindow) +}; + +#endif // MAINWINDOW_H diff --git a/ptyiface.cpp b/ptyiface.cpp new file mode 100644 index 0000000..e279856 --- /dev/null +++ b/ptyiface.cpp @@ -0,0 +1,146 @@ +/* + Copyright 2011-2012 Heikki Holstila + + This file is part of FingerTerm. + + FingerTerm is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + FingerTerm is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with FingerTerm. If not, see . +*/ + +#include + +extern "C" { +#include +#include +#include +#include +#include +#include +#include +#include +#include +} + +#include "terminal.h" +#include "ptyiface.h" + +static bool childProcessQuit = false; +static int childProcessPid = 0; + +void sighandler(int sig) +{ + if(sig==SIGCHLD) { + int pid = wait(NULL); + + if(pid > 0 && childProcessPid > 0 && pid==childProcessPid) { + childProcessQuit = true; + childProcessPid = 0; + qApp->quit(); + } + } +} + +PtyIFace::PtyIFace(int pid, int masterFd, Terminal *term, QString charset, QObject *parent) : + QObject(parent), + iTerm(term), + iPid(pid), + iMasterFd(masterFd), + iFailed(false), + iReadNotifier(0), + iTextCodec(0) +{ + childProcessPid = iPid; + + if(!iTerm || childProcessQuit) { + iFailed = true; + qFatal("PtyIFace: null Terminal pointer"); + } + + iTerm->setPtyIFace(this); + + resize(iTerm->termSize()); + connect(iTerm,SIGNAL(termSizeChanged(QSize)),this,SLOT(resize(QSize))); + + iReadNotifier = new QSocketNotifier(iMasterFd, QSocketNotifier::Read, this); + connect(iReadNotifier,SIGNAL(activated(int)),this,SLOT(readActivated())); + + signal(SIGCHLD,&sighandler); + fcntl(iMasterFd, F_SETFL, O_NONBLOCK); // reads from the descriptor should be non-blocking + + if (!charset.isEmpty()) + iTextCodec = QTextCodec::codecForName(charset.toAscii()); + if (!iTextCodec) + iTextCodec = QTextCodec::codecForName("UTF-8"); + if (!iTextCodec) + qFatal("No valid text codec"); +} + +PtyIFace::~PtyIFace() +{ + if(!childProcessQuit) { + // make the process quit + kill(iPid, SIGHUP); + kill(iPid, SIGTERM); + int status=0; + waitpid(-1,&status,0); + } +} + +void PtyIFace::readActivated() +{ + QByteArray data; + readTerm(data); + if(iTerm) + iTerm->insertInBuffer( iTextCodec->toUnicode(data) ); +} + +void PtyIFace::resize(QSize newSize) +{ + if(childProcessQuit) + return; + + winsize winp; + winp.ws_col = newSize.width(); + winp.ws_row = newSize.height(); + + ioctl(iMasterFd, TIOCSWINSZ, &winp); +} + +void PtyIFace::writeTerm(const QString &chars) +{ + writeTerm( iTextCodec->fromUnicode(chars) ); +} + +void PtyIFace::writeTerm(const QByteArray &chars) +{ + if(childProcessQuit) + return; + + int ret = write(iMasterFd, chars, chars.size()); + if(ret != chars.size()) + qDebug() << "write error!"; +} + +void PtyIFace::readTerm(QByteArray &chars) +{ + if(childProcessQuit) + return; + + int ret = 0; + char ch[64]; + while(ret != -1) { + ret = read(iMasterFd, &ch, 64); + if(ret > 0) + chars.append((char*)&ch, ret); + } +} diff --git a/ptyiface.h b/ptyiface.h new file mode 100644 index 0000000..952dbf2 --- /dev/null +++ b/ptyiface.h @@ -0,0 +1,63 @@ +/* + Copyright 2011-2012 Heikki Holstila + + This file is part of FingerTerm. + + FingerTerm is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + FingerTerm is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with FingerTerm. If not, see . +*/ + +#ifndef PTYIFACE_H +#define PTYIFACE_H + +#include +#include +#include +#include +#include + +class Terminal; + +class PtyIFace : public QObject +{ + Q_OBJECT +public: + explicit PtyIFace(int pid, int masterFd, Terminal *term, QString charset, QObject *parent = 0); + virtual ~PtyIFace(); + + void writeTerm(const QString &chars); + bool failed() { return iFailed; } + +public slots: + void resize(QSize newSize); + +private slots: + void readActivated(); + +private: + Q_DISABLE_COPY(PtyIFace) + + void writeTerm(const QByteArray &chars); + void readTerm(QByteArray &chars); + + Terminal *iTerm; + int iPid; + int iMasterFd; + bool iFailed; + + QSocketNotifier *iReadNotifier; + + QTextCodec *iTextCodec; +}; + +#endif // PTYIFACE_H diff --git a/qml/Button.qml b/qml/Button.qml new file mode 100644 index 0000000..69fa634 --- /dev/null +++ b/qml/Button.qml @@ -0,0 +1,85 @@ +/* + Copyright 2011-2012 Heikki Holstila + + This file is part of FingerTerm. + + FingerTerm is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + FingerTerm is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with FingerTerm. If not, see . +*/ + +import QtQuick 1.1 + +Rectangle { + id: button + property string text: "" + property string textColor: "#ffffff" + property bool enabled: true + property bool highlighted: false + signal clicked(); + + // for custom user menu actions + property bool isShellCommand: false + + color: highlighted ? "#606060" : "#202020" + border.color: "#303030" + border.width: 1 + radius: 5 + z: 0 + clip: true + + width: 180 + height: 68 + + onHighlightedChanged: { + if(highlighted) + button.color = "#606060" + else + button.color = "#202020" + } + + Text { + // decoration for user-defined command buttons + visible: isShellCommand + anchors.fill: parent + font.pointSize: 46 + text: "$" + color: "#305030" + } + + Text { + text: button.text + color: button.enabled ? button.textColor : "#606060" + anchors.centerIn: parent + font.pointSize: util.uiFontSize(); + } + + MouseArea { + id: btnMouseArea + enabled: button.enabled + anchors.fill: parent + onClicked: { + button.clicked(); + } + onPressedChanged: { + if(pressed) { + button.color = "#ffffff" + } else { + if(highlighted) + button.color = "#606060" + else + button.color = "#202020" + } + } + } + +} diff --git a/qml/Key.qml b/qml/Key.qml new file mode 100644 index 0000000..d10cb7a --- /dev/null +++ b/qml/Key.qml @@ -0,0 +1,188 @@ +/* + Copyright 2011-2012 Heikki Holstila + + This file is part of FingerTerm. + + FingerTerm is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + FingerTerm is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with FingerTerm. If not, see . +*/ + +import QtQuick 1.1 + +Rectangle { + id: key + property string label: "" + property string label_alt: "" + property int code: 0 + property int code_alt: 0 + property int currentCode: code + property string currentLabel: keyLabel.text + property bool sticky: false // can key be stickied? + property int stickiness: 0 // current stickiness status + + width: window.width/12 // some default + height: window.height/8 < 55 ? window.height/8 : 55 + color: label=="" ? "transparent" : keyboard.keyBgColor + border.color: label=="" ? "transparent" : keyboard.keyBorderColor + border.width: 1 + radius: 5 + + Connections { + target: keyboard + onKeyModifiersChanged: { + if( (keyboard.keyModifiers & Qt.ShiftModifier) && !sticky ) { + if(key.label_alt!="") { + keyLabel.text = key.label_alt + keyAltLabel.text = key.label + key.currentCode = key.code_alt + } else if(key.label.length==1) { + keyLabel.text = key.label.toUpperCase() + } + } else if(!sticky) { + if(key.label.length==1) + keyLabel.text = key.label.toLowerCase() + else + keyLabel.text = key.label + + keyAltLabel.text = key.label_alt + key.currentCode = key.code + } + } + } + + Image { + id: keyImage + anchors.centerIn: parent + opacity: 0.4 + source: { if(key.label.length>1 && key.label.charAt(0)==':') return "qrc:/icons/"+key.label.substring(1)+".png"; else return ""; } + } + + Text { + visible: keyImage.source == "" + id: keyLabel + anchors.centerIn: parent + text: key.label + color: keyboard.keyFgColor + font.pointSize: key.label.length>1 ? 18 : 26 + } + Text { + id: keyAltLabel + anchors.bottom: parent.bottom + anchors.right: parent.right + text: key.label_alt + color: keyboard.keyFgColor + font.pointSize: key.label.length>1 ? 10 : 13 + } + Rectangle { + id: stickIndicator + visible: sticky && stickiness>0 + color: keyboard.keyHilightBgColor + anchors.fill: parent + radius: key.radius + opacity: 0.5 + z: 1 + anchors.topMargin: key.height/2 + } + + MouseArea { + enabled: label!="" + id: keyMouseArea + anchors.fill: parent + anchors.margins: -3 // -(half of keyspacing) + onExited: { + keyRepeatStarter.stop(); + keyRepeatTimer.stop(); + + key.color = keyboard.keyBgColor + keyboard.currentKeyPressed = 0; + } + onPressedChanged: { + if(pressed) { + key.color = keyboard.keyHilightBgColor + keyboard.currentKeyPressed = key; + util.keyPressFeedback(); + + keyRepeatStarter.start(); + } else { + keyboard.currentKeyPressed = 0; + if(containsMouse) { + + util.keyReleaseFeedback(); + + keyRepeatStarter.stop(); + keyRepeatTimer.stop(); + + key.color = keyboard.keyBgColor + + setStickiness(-1) + vkbKeypress(currentCode, keyboard.keyModifiers); + + if( !sticky && keyboard.resetSticky != 0 && keyboard.resetSticky !== key ) { + resetSticky.setStickiness(0); + } + } + } + } + } + + Timer { + id: keyRepeatStarter + running: false + repeat: false + interval: 400 + triggeredOnStart: false + onTriggered: { + keyRepeatTimer.start(); + } + } + + Timer { + id: keyRepeatTimer + running: false + repeat: true + triggeredOnStart: true + interval: 80 + onTriggered: { + vkbKeypress(currentCode, keyboard.keyModifiers); + } + } + + function setStickiness(val) + { + if(sticky) { + if( keyboard.resetSticky != 0 && keyboard.resetSticky !== key ) { + resetSticky.setStickiness(0) + } + + if(val===-1) + stickiness = (stickiness+1) % 3 + else + stickiness = val + + if(stickiness>0) { + keyboard.keyModifiers |= code + } else { + keyboard.keyModifiers &= ~code + } + + keyboard.resetSticky = 0 + + if(stickiness==1) { + stickIndicator.anchors.topMargin = key.height/2 + keyboard.resetSticky = key + } else if(stickiness==2) { + stickIndicator.anchors.topMargin = 0 + } + } + } +} diff --git a/qml/Keyboard.qml b/qml/Keyboard.qml new file mode 100644 index 0000000..48c7fba --- /dev/null +++ b/qml/Keyboard.qml @@ -0,0 +1,113 @@ +/* + Copyright 2011-2012 Heikki Holstila + + This file is part of FingerTerm. + + FingerTerm is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + FingerTerm is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with FingerTerm. If not, see . +*/ + +import QtQuick 1.1 + +Rectangle { + id: keyboard + color: "transparent" + + width: parent.width + height: childrenRect.height + outmargins + + property int keyModifiers: 0 + property variant resetSticky: 0 + property variant currentKeyPressed: 0 + + property string keyFgColor: "#565656" + property string keyBgColor: "#202020" + property string keyHilightBgColor: "#ffffff" + property string keyBorderColor: "#303030" + + property bool active: false + + property int outmargins: util.settingsValue("ui/keyboardMargins") + property int keyspacing: 6 + property int keysPerRow: keyLoader.vkbColumns() + property real keywidth: (keyboard.width - keyspacing*keysPerRow - outmargins*2)/keysPerRow; + + Component { + id: keyboardContents + Column { + id: col + x: (keyboard.width-width)/2 + spacing: keyboard.keyspacing + Repeater { + id: rowRepeater + model: keyLoader.vkbRows() + delegate: Row { + spacing: keyboard.keyspacing + Repeater { + id: colRepeater + property int rowIndex: index + model: keyLoader.vkbColumns() + delegate: Key { + property variant keydata: keyLoader.keyAt(colRepeater.rowIndex, index) + label: keydata[0] + code: keydata[1] + label_alt: keydata[2] + code_alt: keydata[3] + width: keyboard.keywidth * keydata[4] + ((keydata[4]-1)*keyboard.keyspacing) + 1 + sticky: keydata[5] + } + } + } + } + } + } + + Loader { + id: keyboardLoader + } + + Component.onCompleted: { + keyboardLoader.sourceComponent = keyboardContents; + } + + onCurrentKeyPressedChanged: { + if(currentKeyPressed != 0 && currentKeyPressed.currentLabel.length === 1 && currentKeyPressed.currentLabel !== " ") { + visualKeyFeedbackRect.label = currentKeyPressed.currentLabel + visualKeyFeedbackRect.width = currentKeyPressed.width*1.5 + visualKeyFeedbackRect.height = currentKeyPressed.height*1.5 + var mappedCoord = window.mapFromItem(currentKeyPressed, 0, 0); + visualKeyFeedbackRect.x = mappedCoord.x - (visualKeyFeedbackRect.width-currentKeyPressed.width)/2 + visualKeyFeedbackRect.y = mappedCoord.y - currentKeyPressed.height*1.5 + visualKeyFeedbackRect.visible = true; + } else { + visualKeyFeedbackRect.visible = false; + } + } + + function reloadLayout() + { + var ret = keyLoader.loadLayout(util.settingsValue("ui/keyboardLayout")); + if (!ret) { + showErrorMessage("There was an error loading the keyboard layout.
\nUsing the default one instead."); + util.setSettingsValue("ui/keyboardLayout", "english"); + ret = keyLoader.loadLayout(":/data/english.layout"); //try the default as a fallback (load from resources to ensure it will succeed) + if (!ret) { + console.log("keyboard layout fail"); + Qt.quit(); + } + } + // makes the keyboard component reload itself with new data + keyboardLoader.sourceComponent = undefined + keyboardLoader.sourceComponent = keyboardContents + } +} diff --git a/qml/LayoutWindow.qml b/qml/LayoutWindow.qml new file mode 100644 index 0000000..7e9ac1b --- /dev/null +++ b/qml/LayoutWindow.qml @@ -0,0 +1,132 @@ +/* + Copyright 2011-2012 Heikki Holstila + + This file is part of FingerTerm. + + FingerTerm is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + FingerTerm is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with FingerTerm. If not, see . +*/ + +import QtQuick 1.1 + +Rectangle { + id: layoutWindow + property string currentLayout: util.settingsValue("ui/keyboardLayout"); + + width: window.width-1 + height: window.height-1 + color: "#000000" + z: 100 + property variant layouts: [""] + state: "" + y: -(height+1) + border.color: "#c0c0c0" + border.width: 1 + radius: 10 + + MouseArea { + // event eater + anchors.fill: parent + } + + Component { + id: listDelegate + Rectangle { + color: currentLayout === modelData ? "#909090" : "#404040" + width: parent.width + height: selectButton.height+4 + border.width: 1 + border.color: "#ffffff" + radius: 5 + clip: true + + Text { + text: modelData + color: "#ffffff" + anchors.verticalCenter: parent.verticalCenter + x: 8 + width: selectButton.x - x + font.pointSize: util.uiFontSize(); + elide: Text.ElideRight + } + Button { + id: selectButton + anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + text: "Select" + width: 70 + anchors.rightMargin: 5 + onClicked: { + util.setSettingsValue("ui/keyboardLayout", modelData); + vkb.reloadLayout(); + layoutWindow.state = ""; + util.notifyText(modelData); + } + } + } + } + + Text { + id: titleText + anchors.horizontalCenter: parent.horizontalCenter + anchors.top: parent.top + color: "#ffffff" + text: "Keyboard layout" + font.pointSize: util.uiFontSize() + 4; + } + + ListView { + anchors.fill: parent + anchors.topMargin: titleText.height + 4 + delegate: listDelegate + model: layoutWindow.layouts + spacing: 5 + anchors.margins: 5 + clip: true + } + + Button { + anchors.horizontalCenter: parent.horizontalCenter + anchors.bottom: parent.bottom + anchors.bottomMargin: 10 + text: "Back" + onClicked: { + layoutWindow.state = "" + } + } + + states: [ + State { + name: "visible" + PropertyChanges { + target: layoutWindow + y: 0 + } + StateChangeScript { + script: + currentLayout = util.settingsValue("ui/keyboardLayout"); + } + } + ] + + transitions: [ + Transition { + from: "*" + to: "*" + SequentialAnimation { + PropertyAnimation { target: layoutWindow; properties: "y"; duration: 200; easing.type: Easing.InOutCubic } + ScriptAction { script: updateGesturesAllowed(); } + } + } + ] +} diff --git a/qml/Lineview.qml b/qml/Lineview.qml new file mode 100644 index 0000000..e90385f --- /dev/null +++ b/qml/Lineview.qml @@ -0,0 +1,108 @@ +/* + Copyright 2011-2012 Heikki Holstila + + This file is part of FingerTerm. + + FingerTerm is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + FingerTerm is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with FingerTerm. If not, see . +*/ + +import QtQuick 1.1 + +Rectangle { + id: lineView + property variant lines: [""] + property int fontPointSize: util.settingsValue("ui/fontSize"); + property int cursorX: 1 + property int cursorWidth: 10 + property int cursorHeight: 10 + property int extraLines: 1 + + color: "#404040" + border.width: 2 + border.color: "#909090" + radius: 5 + height: 0 + width: parent.width + + Text { + id: fontHeightHack + visible: false + text: "X" + font.family: util.settingsValue("ui/fontFamily"); + font.pointSize: lineView.fontPointSize + } + + Rectangle { + visible: vkb.active + x: cursorX + y: lineTextCol.y + fontHeightHack.height*(extraLines+1) - cursorHeight - 3 + width: cursorWidth + height: cursorHeight + color: "transparent" + border.color: "#ffffff" + border.width: 1 + } + + Column { + id: lineTextCol + anchors.verticalCenter: parent.verticalCenter + anchors.left: parent.left + anchors.right: parent.right + anchors.leftMargin: 2 + anchors.rightMargin: 2 + Repeater { + model: lines + delegate: + Rectangle { + height: fontHeightHack.height + width: lineTextCol.width + color: "transparent" + Text { + color: "#ffffff" + font.family: util.settingsValue("ui/fontFamily"); + font.pointSize: lineView.fontPointSize + text: modelData + textFormat: Text.PlainText + wrapMode: Text.NoWrap + elide: Text.ElideNone + maximumLineCount: 1 + } + } + } + onHeightChanged: { + if(lineView.visible) + lineView.height = height+8 + setPosition(vkb.active) + } + } + + Component.onCompleted: { + extraLines = util.settingsValue("ui/showExtraLinesFromCursor"); + } + + function setPosition(vkbActive) + { + if( util.settingsValue("ui/vkbShowMethod")==="off" ) { + lineView.visible = false; + return; + } + + lineView.visible = true; + if(vkbActive && util.settingsValue("ui/vkbShowMethod")!=="move") { + y = 0; + } else { + y = -(height+5) + } + } +} diff --git a/qml/Main.qml b/qml/Main.qml new file mode 100644 index 0000000..7037401 --- /dev/null +++ b/qml/Main.qml @@ -0,0 +1,374 @@ +/* + Copyright 2011-2012 Heikki Holstila + + This file is part of FingerTerm. + + FingerTerm is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + FingerTerm is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with FingerTerm. If not, see . +*/ + +import QtQuick 1.1 +import TextRender 1.0 + +Rectangle { + property string fgcolor: "black" + property string bgcolor: "#000000" + property int fontSize: 14 + + property int fadeOutTime: 80 + property int fadeInTime: 350 + + property string windowTitle: util.currentWindowTitle(); + + id: window + objectName: "window" + width: 854 + height: 480 + color: bgcolor + + NotifyWin { + id: aboutDialog + property int termW: 0 + property int termH: 0 + text: { + var str = "FingerTerm " + util.versionString() + "
\n" + + "" + + "by Heikki Holstila <heikki.holstila@gmail.com>

\n\n" + + "Config files for adjusting settings are at:
\n" + + util.configPath() + "/

\n" + + "Documentation:
\nhttp://hqh.unlink.org/harmattan" + if (termH != 0 && termW != 0) { + str += "

Current window title: " + windowTitle.substring(0,40) + ""; //cut long window title + if(windowTitle.length>40) + str += "..."; + str += "
Current terminal size: " + termW + "x" + termH + ""; + str += "
Charset: " + util.settingsValue("terminal/charset") + ""; + } + str += "
"; + return str; + } + onDismissed: { + util.setSettingsValue("state/showWelcomeScreen", false); + } + z: 1001 + } + + NotifyWin { + id: errorDialog + text: "" + z: 1002 + } + + UrlWindow { + id: urlWindow + z: 1000 + } + + LayoutWindow { + id: layoutWindow + z: 1000 + } + + TextRender { + id: textrender + objectName: "textrender" + x: 0 + y: 0 + height: parent.height + width: parent.width + myWidth: width + myHeight: height + opacity: 1.0 + property int duration: 0; + property int cutAfter: height + + Behavior on opacity { + NumberAnimation { duration: textrender.duration; easing.type: Easing.InOutQuad } + } + Behavior on y { + NumberAnimation { duration: textrender.duration; easing.type: Easing.InOutQuad } + } + + onFontSizeChanged: { + lineView.fontPointSize = textrender.fontPointSize; + } + + onCutAfterChanged: { + // this property is used in the paint function, so make sure that the element gets + // painted with the updated value (might not otherwise happen because of caching) + textrender.redraw(); + } + + z: 10 + } + + + Lineview { + id: lineView + x: 0 + y: -(height+1) + z: 20 + property int duration: 0; + onFontPointSizeChanged: { + lineView.setPosition(vkb.active) + } + } + + MouseArea { // the area above the keyboard ("wakes up" the keyboard when tapped) + x:0 + y:0 + z:0 + width: window.width + height: vkb.y + onClicked: { + if (util.settingsValue("ui/dragMode") !== "select") { + if (vkb.active) + sleepVKB(); + else + wakeVKB(); + } + } + } + + Rectangle { + //top right corner menu button + x: window.width - width + y: 0 + z: 1 + width: menuImg.width + 60 + height: menuImg.height + 30 + color: "transparent" + opacity: 0.5 + Image { + anchors.centerIn: parent + id: menuImg + source: "qrc:/icons/menu.png" + height: sourceSize.height + width: sourceSize.width + } + MouseArea { + anchors.fill: parent + onClicked: { + menu.showMenu(); + } + } + } + + Image { + // terminal buffer scroll indicator + source: "qrc:/icons/scroll-indicator.png" + anchors.horizontalCenter: parent.horizontalCenter + anchors.bottom: parent.bottom + visible: textrender.showBufferScrollIndicator + z: 5 + } + + Menu { + id: menu + x: window.width-width + y: 0 + } + + Keyboard { + id: vkb + x: 0 + y: parent.height-vkb.height + z: 0 + } + + focus: true + Keys.onPressed: { + vkbKeypress(event.key,event.modifiers); + } + + Timer { + id: fadeTimer + running: false + repeat: false + interval: menu.keyboardFadeOutDelay + onTriggered: { + sleepVKB(); + } + } + + Timer { + id: bellTimer + running: false + repeat: false + interval: 80 + onTriggered: { + window.color = window.bgcolor; + } + } + + Connections { + target: util + onVisualBell: { + visualBell(); + } + onGestureNotify: { + textNotify.text = msg; + textNotifyAnim.enabled = false; + textNotify.opacity = 1.0; + textNotifyAnim.enabled = true; + textNotify.opacity = 0; + } + onWindowTitleChanged: { + windowTitle = util.currentWindowTitle(); + } + } + + Text { + // shows large text notification in the middle of the screen (for gestures) + id: textNotify + anchors.centerIn: parent + color: "#ffffff" + z: 100 + opacity: 0 + text: "" + font.pointSize: 40 + Behavior on opacity { + id: textNotifyAnim + NumberAnimation { duration: 500; } + } + } + + Rectangle { + // visual key press feedback... + // easier to work with the coordinates if it's here and not under keyboard element + id: visualKeyFeedbackRect + visible: false + x: 0 + y: 0 + z: 200 + width: 0 + height: 0 + radius: 5 + color: "#ffffff" + property string label: "" + Text { + color: "#000000" + font.pointSize: 34 + anchors.centerIn: parent + text: visualKeyFeedbackRect.label + } + } + + function vkbKeypress(key,modifiers) { + wakeVKB(); + term.keyPress(key,modifiers); + } + + function wakeVKB() + { + if(!vkb.visible) + return; + + lineView.duration = window.fadeOutTime; + textrender.duration = window.fadeOutTime; + vkb.keyFgColor = "#cccccc"; + fadeTimer.restart(); + vkb.active = true; + lineView.setPosition(vkb.active); + util.updateSwipeLock(!vkb.active); + setTextRenderAttributes(); + updateGesturesAllowed(); + } + + function sleepVKB() + { + textrender.duration = window.fadeInTime; + lineView.duration = window.fadeInTime; + vkb.keyFgColor = "#565656"; + vkb.active = false; + lineView.setPosition(vkb.active); + util.updateSwipeLock(!vkb.active); + setTextRenderAttributes(); + updateGesturesAllowed(); + } + + function setTextRenderAttributes() + { + if(util.settingsValue("ui/vkbShowMethod")==="move") + { + vkb.visible = true; + textrender.opacity = 1.0; + if(vkb.active) { + var move = textrender.cursorPixelPos().y + textrender.fontHeight/2 + textrender.fontHeight*util.settingsValue("ui/showExtraLinesFromCursor"); + if(move < vkb.y) { + textrender.y = 0; + textrender.cutAfter = vkb.y; + } else { + textrender.y = 0 - move + vkb.y + textrender.cutAfter = move; + } + } else { + textrender.cutAfter = textrender.height; + textrender.y = 0; + } + } + else if(util.settingsValue("ui/vkbShowMethod")==="fade") + { + vkb.visible = true; + textrender.cutAfter = textrender.height; + textrender.y = 0; + if(vkb.active) + textrender.opacity = 0.3; + else + textrender.opacity = 1.0; + } + else // "off" (vkb disabled) + { + vkb.visible = false; + textrender.cutAfter = textrender.height; + textrender.y = 0; + textrender.opacity = 1.0; + } + } + + function displayBufferChanged() + { + lineView.lines = term.printableLinesFromCursor(util.settingsValue("ui/showExtraLinesFromCursor")); + lineView.cursorX = textrender.cursorPixelPos().x; + lineView.cursorWidth = textrender.cursorPixelSize().width; + lineView.cursorHeight = textrender.cursorPixelSize().height; + setTextRenderAttributes(); + } + + Component.onCompleted: { + util.updateSwipeLock(vkb.active) + if( util.settingsValue("state/showWelcomeScreen") === true ) + aboutDialog.state = "visible"; + } + + function showErrorMessage(string) + { + errorDialog.text = "" + string + ""; + errorDialog.state = "visible" + } + + function visualBell() + { + bellTimer.start(); + window.color = "#ffffff" + } + + function updateGesturesAllowed() + { + if(vkb.active || menu.showing || urlWindow.state=="visible" || + aboutDialog.state=="visible" || layoutWindow.state=="visible") + util.allowGestures = false; + else + util.allowGestures = true; + } +} diff --git a/qml/Menu.qml b/qml/Menu.qml new file mode 100644 index 0000000..6f580e5 --- /dev/null +++ b/qml/Menu.qml @@ -0,0 +1,508 @@ +/* + Copyright 2011-2012 Heikki Holstila + + This file is part of FingerTerm. + + FingerTerm is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + FingerTerm is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with FingerTerm. If not, see . +*/ + +import QtQuick 1.1 + +Rectangle { + id: menuWin + color: "transparent" + z: 30 + width: window.width + height: window.height + visible: false + property bool showing: false + property bool enableCopy: false + property bool enablePaste: false + property string currentSwipeLocking: util.settingsValue("ui/allowSwipe") + property string currentShowMethod: util.settingsValue("ui/vkbShowMethod") + property string currentDragMode: util.settingsValue("ui/dragMode") + property int keyboardFadeOutDelay: util.settingsValue("ui/keyboardFadeOutDelay") + + Rectangle { + id: fader + color: "#ffffff" + opacity: 0 + y: 0 + x: 0 + width: menuWin.width + height: menuWin.height + MouseArea { + anchors.fill: parent + onClicked: { + hideMenu(); + } + } + Behavior on opacity { + SequentialAnimation { + NumberAnimation { duration: 100; } + ScriptAction { script: menuWin.visible = menuWin.showing; } + } + } + } + Rectangle { + id: rect + color: "#e0e0e0" + y: 0 + x: menuWin.width+1; + width: flickableContent.width + 22; + height: menuWin.height + z: 35 + + MouseArea { + // event eater + anchors.fill: parent + } + + Behavior on x { + NumberAnimation { duration: 100; easing.type: Easing.InOutQuad; } + } + + XmlListModel { + id: xmlModel + xml: term.getUserMenuXml() + query: "/userMenu/item" + + XmlRole { name: "title"; query: "title/string()" } + XmlRole { name: "command"; query: "command/string()" } + XmlRole { name: "disableOn"; query: "disableOn/string()" } + } + + Component { + id: xmlDelegate + Button { + text: title + isShellCommand: true + enabled: disableOn.length === 0 || window.windowTitle.search(disableOn) === -1 + onClicked: { + hideMenu(); + term.putString(command, true); + } + } + } + + Rectangle { + id: scrollIndicator + y: menuFlickArea.visibleArea.yPosition*menuFlickArea.height + 6 + x: parent.width-10 + width: 6 + height: menuFlickArea.visibleArea.heightRatio*menuFlickArea.height + radius: 3 + color: "#202020" + } + + Flickable { + id: menuFlickArea + anchors.fill: parent + anchors.topMargin: 6 + anchors.bottomMargin: 6 + anchors.leftMargin: 6 + anchors.rightMargin: 16 + contentHeight: flickableContent.height + + Column { + id: flickableContent + spacing: 12 + + Row { + id: menuBlocksRow + spacing: 8 + + Column { + spacing: 12 + Repeater { + model: xmlModel + delegate: xmlDelegate + } + } + + Column { + spacing: 12 + + Row { + Button { + text: "Copy" + onClicked: { + hideMenu(); + term.copySelectionToClipboard(); + } + width: 90 + enabled: menuWin.enableCopy + } + Button { + text: "Paste" + onClicked: { + hideMenu(); + term.pasteFromClipboard(); + } + width: 90 + enabled: menuWin.enablePaste + } + } + Button { + text: "URL grabber" + onClicked: { + hideMenu(); + urlWindow.urls = term.grabURLsFromBuffer(); + urlWindow.state = "visible"; + } + } + Rectangle { + width: 180 + height: 68 + radius: 5 + color: "#606060" + border.color: "#000000" + border.width: 1 + Column { + Text { + width: 180 + height: 20 + color: "#ffffff" + font.pointSize: util.uiFontSize()-1; + text: "Font size" + horizontalAlignment: Text.AlignHCenter + } + Row { + Button { + text: "+" + onClicked: { + textrender.fontPointSize = textrender.fontPointSize + 1; + lineView.fontPointSize = textrender.fontPointSize; + util.notifyText(term.termSize().width+"x"+term.termSize().height); + } + width: 90 + height: 48 + } + Button { + text: "-" + onClicked: { + textrender.fontPointSize = textrender.fontPointSize - 1; + lineView.fontPointSize = textrender.fontPointSize; + util.notifyText(term.termSize().width+"x"+term.termSize().height); + } + width: 90 + height: 48 + } + } + } + } + Rectangle { + width: 180 + height: 68 + radius: 5 + color: "#606060" + border.color: "#000000" + border.width: 1 + Column { + Text { + width: 180 + height: 20 + color: "#ffffff" + font.pointSize: util.uiFontSize()-1; + text: "Drag mode" + horizontalAlignment: Text.AlignHCenter + } + Row { + Button { + text: "Gesture" + highlighted: currentDragMode=="gestures" + onClicked: { + util.setSettingsValue("ui/dragMode", "gestures"); + term.clearSelection(); + currentDragMode = "gestures"; + hideMenu(); + } + width: 60 + height: 48 + } + Button { + text: "Scroll" + highlighted: currentDragMode=="scroll" + onClicked: { + util.setSettingsValue("ui/dragMode", "scroll"); + currentDragMode = "scroll"; + term.clearSelection(); + hideMenu(); + } + width: 60 + height: 48 + } + Button { + text: "Select" + highlighted: currentDragMode=="select" + onClicked: { + util.setSettingsValue("ui/dragMode", "select"); + currentDragMode = "select"; + hideMenu(); + } + width: 60 + height: 48 + } + } + } + } + Rectangle { + width: 180 + height: 68 + radius: 5 + color: "#606060" + border.color: "#000000" + border.width: 1 + Column { + Text { + width: 180 + height: 20 + color: "#ffffff" + font.pointSize: util.uiFontSize()-1; + text: "VKB behavior" + horizontalAlignment: Text.AlignHCenter + } + Row { + Button { + text: "Off" + highlighted: currentShowMethod=="off" + onClicked: { + util.setSettingsValue("ui/vkbShowMethod", "off"); + currentShowMethod = "off"; + setTextRenderAttributes(); + hideMenu(); + } + width: 60 + height: 48 + } + Button { + text: "Fade" + highlighted: currentShowMethod=="fade" + onClicked: { + util.setSettingsValue("ui/vkbShowMethod", "fade"); + currentShowMethod = "fade"; + setTextRenderAttributes(); + hideMenu(); + } + width: 60 + height: 48 + } + Button { + text: "Move" + highlighted: currentShowMethod=="move" + onClicked: { + util.setSettingsValue("ui/vkbShowMethod", "move"); + currentShowMethod = "move"; + setTextRenderAttributes(); + hideMenu(); + } + width: 60 + height: 48 + } + } + } + } + Rectangle { + visible: util.isHarmattan() + width: 180 + height: 68 + radius: 5 + color: "#606060" + border.color: "#000000" + border.width: 1 + Column { + Text { + width: 180 + height: 20 + color: "#ffffff" + font.pointSize: util.uiFontSize()-1; + text: "Allow swiping" + horizontalAlignment: Text.AlignHCenter + } + Row { + Button { + text: "No" + width: 60 + height: 48 + highlighted: currentSwipeLocking=="false" + onClicked: { + changeSwipeLocking("false") + } + } + Button { + text: "Yes" + width: 60 + height: 48 + highlighted: currentSwipeLocking=="true" + onClicked: { + changeSwipeLocking("true") + } + } + Button { + text: "Auto" + width: 60 + height: 48 + highlighted: currentSwipeLocking=="auto" + onClicked: { + changeSwipeLocking("auto") + } + } + } + } + } + Button { + visible: util.isHarmattan() + text: "New window" + onClicked: { + hideMenu(); + util.openNewWindow(); + } + } + Button { + text: "VKB layout..." + onClicked: { + hideMenu(); + layoutWindow.layouts = keyLoader.availableLayouts(); + layoutWindow.state = "visible"; + } + } + Button { + text: "About" + onClicked: { + hideMenu(); + aboutDialog.termW = term.termSize().width + aboutDialog.termH = term.termSize().height + aboutDialog.state = "visible" + } + } + Button { + visible: (currentSwipeLocking=="false" && util.isHarmattan()) || !util.isHarmattan(); + text: "Minimize" + onClicked: { + hideMenu(); + util.windowMinimize(); + } + } + Button { + text: "Quit" + onClicked: { + hideMenu(); + Qt.quit(); + } + } + } + } + // VKB delay slider + Rectangle { + id: vkbDelaySliderArea + width: menuBlocksRow.width + height: 68 + radius: 5 + color: "#606060" + border.color: "#000000" + border.width: 1 + Text { + width: parent.width + height: 20 + color: "#ffffff" + font.pointSize: util.uiFontSize()-1; + text: "VKB delay: " + vkbDelaySlider.keyboardFadeOutDelayLabel + " ms" + horizontalAlignment: Text.AlignHCenter + } + Rectangle { + x: 5 + y: vkbDelaySlider.y + vkbDelaySlider.height/2 - height/2 + width: menuBlocksRow.width - 10 + height: 10 + radius: 5 + z: 1 + color: "#909090" + } + Rectangle { + id: vkbDelaySlider + property int keyboardFadeOutDelayLabel: keyboardFadeOutDelay + x: (keyboardFadeOutDelay-1000)/9000 * (vkbDelaySliderArea.width - vkbDelaySlider.width) + y: 20 + width: 60 + radius: 15 + height: parent.height-20 + color: "#202020" + z: 2 + onXChanged: { + if (vkbDelaySliderMA.drag.active) + vkbDelaySlider.keyboardFadeOutDelayLabel = + Math.floor((1000+vkbDelaySlider.x/vkbDelaySliderMA.drag.maximumX*9000)/250)*250; + } + MouseArea { + id: vkbDelaySliderMA + anchors.fill: parent + drag.target: vkbDelaySlider + drag.axis: Drag.XAxis + drag.minimumX: 0 + drag.maximumX: vkbDelaySliderArea.width - vkbDelaySlider.width + drag.onActiveChanged: { + if (!drag.active) { + keyboardFadeOutDelay = vkbDelaySlider.keyboardFadeOutDelayLabel + util.setSettingsValue("ui/keyboardFadeOutDelay", keyboardFadeOutDelay); + } + } + } + } + } + } + } + } + + onWidthChanged: { + if(showing) { + showMenu(); + } else { + hideMenu(); + } + } + + Connections { + target: util + onClipboardOrSelectionChanged: { + enableCopy = util.terminalHasSelection(); + enablePaste = util.canPaste(); + } + } + + function showMenu() + { + showing = true; + visible = true; + fader.opacity = 0.5; + rect.x = menuWin.width-rect.width; + updateGesturesAllowed(); + enableCopy = util.terminalHasSelection(); + enablePaste = util.canPaste(); + } + + function hideMenu() + { + showing = false; + fader.opacity = 0; + rect.x = menuWin.width+1; + updateGesturesAllowed(); + } + + function changeSwipeLocking(state) + { + currentSwipeLocking = state + util.setSettingsValue("ui/allowSwipe", state) + util.updateSwipeLock(!vkb.active); + } +} diff --git a/qml/NotifyWin.qml b/qml/NotifyWin.qml new file mode 100644 index 0000000..03026d8 --- /dev/null +++ b/qml/NotifyWin.qml @@ -0,0 +1,95 @@ +/* + Copyright 2011-2012 Heikki Holstila + + This file is part of FingerTerm. + + FingerTerm is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + FingerTerm is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with FingerTerm. If not, see . +*/ + +import QtQuick 1.1 + +Rectangle { + id: notifyWin + + property string text: "" + + width: window.width-1 + height: window.height-1 + color: "#000000" + z: 100 + y: -(height+1) + state: "" + border.color: "#c0c0c0" + border.width: 1 + radius: 10 + + signal dismissed(); + + MouseArea { + // event eater + anchors.fill: parent + } + Rectangle { + color: "transparent" + anchors.top: notifyWin.top + anchors.left: notifyWin.left + anchors.right: notifyWin.right + anchors.bottom: okButton.top + + Text { + anchors.centerIn: parent + + color: "#ffffff" + text: notifyWin.text + font.pointSize: util.uiFontSize(); + + onLinkActivated: { + Qt.openUrlExternally(link) + } + } + } + + Button { + id: okButton + anchors.horizontalCenter: parent.horizontalCenter + anchors.bottom: parent.bottom + anchors.bottomMargin: 10 + text: "OK" + onClicked: { + notifyWin.state = "" + notifyWin.dismissed(); + } + } + + states: [ + State { + name: "visible" + PropertyChanges { + target: notifyWin + y: 0 + } + } + ] + + transitions: [ + Transition { + from: "*" + to: "*" + SequentialAnimation { + PropertyAnimation { target: notifyWin; properties: "y"; duration: 200; easing.type: Easing.InOutCubic } + ScriptAction { script: updateGesturesAllowed(); } + } + } + ] +} diff --git a/qml/UrlWindow.qml b/qml/UrlWindow.qml new file mode 100644 index 0000000..bf9a0ed --- /dev/null +++ b/qml/UrlWindow.qml @@ -0,0 +1,133 @@ +/* + Copyright 2011-2012 Heikki Holstila + + This file is part of FingerTerm. + + FingerTerm is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + FingerTerm is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with FingerTerm. If not, see . +*/ + +import QtQuick 1.1 + +Rectangle { + id: urlWindow + + width: window.width-1 + height: window.height-1 + color: "#000000" + z: 100 + property variant urls: [""] + state: "" + y: -(height+1) + border.color: "#c0c0c0" + border.width: 1 + radius: 10 + + MouseArea { + // event eater + anchors.fill: parent + } + + Component { + id: listDelegate + Rectangle { + color: "#909090" + width: parent.width + height: openButton.height+4 + border.width: 1 + border.color: "#ffffff" + radius: 5 + clip: true + + Text { + text: modelData + color: "#ffffff" + anchors.verticalCenter: parent.verticalCenter + x: 8 + width: openButton.x - x + font.pointSize: util.uiFontSize(); + elide: Text.ElideRight + } + Button { + id: openButton + text: "Open" + anchors.right: copyButton.left + anchors.verticalCenter: parent.verticalCenter + anchors.rightMargin: 5 + width: 70 + onClicked: { + Qt.openUrlExternally(modelData); + } + } + Button { + id: copyButton + anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + text: "Copy" + width: 70 + anchors.rightMargin: 5 + onClicked: { + util.copyTextToClipboard(modelData); + } + } + } + } + + Text { + visible: urlWindow.urls.length == 0 + anchors.centerIn: parent + color: "#ffffff" + text: "No URLs" + font.pointSize: util.uiFontSize() + 4; + } + + ListView { + anchors.fill: parent + delegate: listDelegate + model: urlWindow.urls + spacing: 5 + anchors.margins: 5 + clip: true + } + + Button { + anchors.horizontalCenter: parent.horizontalCenter + anchors.bottom: parent.bottom + anchors.bottomMargin: 10 + text: "Back" + onClicked: { + urlWindow.state = "" + } + } + + states: [ + State { + name: "visible" + PropertyChanges { + target: urlWindow + y: 0 + } + } + ] + + transitions: [ + Transition { + from: "*" + to: "*" + SequentialAnimation { + PropertyAnimation { target: urlWindow; properties: "y"; duration: 200; easing.type: Easing.InOutCubic } + ScriptAction { script: updateGesturesAllowed(); } + } + } + ] +} diff --git a/qtc_packaging/debian_harmattan/changelog b/qtc_packaging/debian_harmattan/changelog new file mode 100644 index 0000000..dcdfbba --- /dev/null +++ b/qtc_packaging/debian_harmattan/changelog @@ -0,0 +1,57 @@ +fingerterm (1.0.2) unstable; urgency=low + + * Fixed bug in handling of certain escape sequences (fixes mosh) + + -- Heikki Holstila Tue, 05 Jun 2012 20:10:19 +0300 + +fingerterm (1.0.1) unstable; urgency=low + + * Workaround for URL grabber functionality with scrolled buffer + * Depend on openssh-client + + -- Heikki Holstila Tue, 14 Feb 2012 18:06:29 +0200 + +fingerterm (1.0.0) unstable; urgency=low + + * UI for choosing a keyboard layout + * slider for adjusting keyboard fade-out delay + * rendering performance improvements & other UI tweaks + * selection bugfixes + + -- Heikki Holstila Thu, 02 Feb 2012 21:13:10 +0200 + +fingerterm (0.9.3~beta) unstable; urgency=low + + * selectable finger drag mode (gestures/scroll/selection) + * support for buffer scrolling and text selection + * support for multiple instances ("new window" menu item) + * improved bell notification when in background + * charset option in config file (default: UTF-8) + * default keyboard layout is now "english" ("finnish" also included) + * performance improvements + + -- Heikki Holstila Sun, 08 Jan 2012 19:04:31 +0200 + +fingerterm (0.9.2~beta) unstable; urgency=low + + * configurable keyboard layout + * virtual keyboard behavior setting: off/fade/move + * keypress feedback + * improved pan gestures + * other smaller tweaks and improvements + + -- Heikki Holstila Thu, 24 Nov 2011 20:26:47 +0200 + +fingerterm (0.9.1~beta) unstable; urgency=low + + * various terminal control sequence related bugfixes + * basic pan gesture support (pan left/right to change channels in irssi) + * some ui tweaks + + -- Heikki Holstila Thu, 20 Oct 2011 23:08:51 +0300 + +fingerterm (0.9.0~beta) unstable; urgency=low + + * Initial Release (beta, work in progress) + + -- Heikki Holstila Thu, 20 Oct 2011 21:39:16 +0300 diff --git a/qtc_packaging/debian_harmattan/compat b/qtc_packaging/debian_harmattan/compat new file mode 100644 index 0000000..7f8f011 --- /dev/null +++ b/qtc_packaging/debian_harmattan/compat @@ -0,0 +1 @@ +7 diff --git a/qtc_packaging/debian_harmattan/control b/qtc_packaging/debian_harmattan/control new file mode 100644 index 0000000..1f68963 --- /dev/null +++ b/qtc_packaging/debian_harmattan/control @@ -0,0 +1,80 @@ +Source: fingerterm +Section: user/other +Priority: optional +Maintainer: Heikki Holstila +Build-Depends: debhelper (>= 5), libc6-dev, libqt4-dev, libmeegotouch-dev +Standards-Version: 3.7.3 +Homepage: http://hqh.unlink.org/harmattan + +Package: fingerterm +Architecture: any +Depends: ${shlibs:Depends}, ${misc:Depends}, openssh-client +Description: A terminal emulator with a custom virtual keyboard + A terminal emulator with a custom virtual keyboard and usability-enhancing + features such as URL grabber, pan gestures and customizable shortcut menu. + Designed especially to be used with screen and irssi. +XSBC-Maemo-Display-Name: FingerTerm +XB-MeeGo-Desktop-Entry-Filename: fingerterm +XB-Maemo-Flags: visible +XB-MeeGo-Desktop-Entry: + [Desktop Entry] + Type=Application + Name=FingerTerm + Icon=/usr/share/icons/hicolor/80x80/apps/fingerterm.png +XB-Maemo-Icon-26: + iVBORw0KGgoAAAANSUhEUgAAAFAAAABQCAYAAACOEfKtAAAABHNCSVQICAgIfAhkiAAAAAlwSFlz + AAALEwAACxMBAJqcGAAAC+lJREFUeJztXG1sHMUZfmZmd8++fDkmoQ6xnZOgBpKSXKgomFjKORH8 + QtRJVVX8SXICItxesPMrFEWYqBT4UYiTGJKqpUlU8QMigUutRiUSdwET0waaU1UlqFbiSwKBqJg4 + kbiP3dmZ/riP7J7vY+989p0tHmmkm5l33pl97n3nexf4HlMCmamK/jE87JWK4mGEeEHpIkqpFwAY + IR7KmKccncI0I6aUEQAQQoQhxHVTyjDhPHJ/R0e4cq3Pj2kh8Eww2JCor/cxQtZTRfGqiuJL53HO + IaWEME0AgBACQghbeSnlpDRKKQghk9IopcnfjIEQAkVRMvkG5yHBediU8qQrFgut7eycqOiDooIE + joyMeBghXYqibFUUxSulBDdNCNME5xxmirCZAmMMiqKAMgYlRS7nPMw5P2pKOdje3h6pRD1TJnBk + ZMSnqWqPqihdQggYnIMbxowTVgyMMSiqClVRQCmFwfmgbhj72tvbQ1PRWzaBKeL6mKL4uGHAMAxw + zqfSlhmDoihQVRWKqsLkPKQbxp5yiSyZwGAw2OB2u/s0Ve3lhoF4PA4pZTl1Vx2EENTV1UFRVeiG + 0R+NRvd0lthPlkTg8PCw1+VyvUsBTywWqzk3LReMMdTX10MAkUQisamjhBHcMYEjw8PbtLq6w4au + I55IALPU6vKCENS5XFA1DXo87m/v6DjiqJgToZHh4W2qy3U4Ho9D1/UptbPWoWka6urqYCQSjkgs + SuDwyZPbtPr6w/FYDIZhVKSRtQ5VVVFXXw89FvN3rF9/pJBsQQKHg0Gv5naficfjMOa45WVDTVmi + Ho2u7ejszNsn5iUwGAw2uDTtjDBNTzwen55W1jjq6upAGYskdH1tvtFZyZUIAIyxPkjpiUaj09fC + Gkc0GsX8+fM9jLE+ADtzyeS0wGAw6HNpWjD63XdzZqpSLhhjcM+bh4Sud3Z2doay83NaIKO0z9D1 + nCuLsbExvNrfj8jYGB5++GHsCAQq3+oaAucchq6DUdoHIJSdT7MTgidO+AghvmgsBiHlpPCfs2cx + OjoKg3O8f+JETpm5FqKxGAghvuCJE75sviZZIGGsxzCMSdtJaVjTCSGzdhlXCqSUMAwDlLEeZFmh + zQKDx497CKVduq5DSpkzzHO7M/KqquaVm2tB13UQSruCx497rJzZLNBU1S6YJniBCbN1U5MQMveW + dHnADQNC05IcAf3pdBuBBNiq63pe9wUwyWULyc416LoORVW3wkJgxoWDwWADAbyGYRQ1Zyuq7Voz + GQzDAAG8wWCwIf38GQtMJBI+hdLim6I5CCwHXZs3AwAG33mnrPLVAOccQghwzn0ABgELgUTK9Ubq + wKcQsnPLJbBS5WcaBueghKxHNoEgxGtyPomgbEwicIoNml30AaZpgqqqNx2/SaCUPm4YkMUGhewj + yCkOIibnmaPJ2QBuGLAe0yoAMDQ0lDyGdHAoVK4LX7lyBSc//BAXxsYwNjaWSf/FY49h8eLFaGxs + REtLC1atXImVd9+NpUuXOtI700ifaw8NDXkfeeSRsAIARAiPdLiqsMnkGJWzEYvHcezYMfzt+PGc + GxNCCIyPj2N8fByjo6P44IMPAADH3nqrlOeaUUgpQYTwAEgSCEK83MEAkk9ZIRw6dAinRkYqrrea + 4MluxwtgUAEAKeUiIUTJFihR+EE//vhjG3mEEPzkvvvw00cfxbO7dwMAjh4+jGvXruHaxASuXLmC + c+fO4dznn9c0gUIIEEIWAak+UEjp3AJLmAee+uQTW3xDZyeefOIJW5rL5UJTUxOamppw9113YeOG + DUX1Vhucc5DU5ai0BRa1pjRKGUQuXbpki7c/8EDOlcxsg5Wr5PxBCM9UpyO5cOPGDVt8/vz5Fa+j + GpBCAMlBJEmgBDzpZUqxkE10Idnly5fbZP/wxhu4dPmybQPCSZ21FnhyweEBrC7sYEqSItseL1Cm + ra0N58+fz8TPnz+PXc88g2YLsRcvXkRra2vRemsJVq7Sg0jSr50ULhK3YlNXFz49fRr/++YbW/oX + X36Z+f3s7t1obGzEmjVr8ON778WaNWsctKK6kEhyBqQtMOWajvrBEpZydS4Xent68Mc33sBYJJJX + 7ttvv0UwGEQwGMTKlSuxbcsWNDU1FW9LlSBx87kzi9DpGg1bWlrwfF8fnnz8cdx1551gjBWUP3v2 + LJ7dvRvh8IxccS4LVq4yFigcnv9OmoY4HL071q1Dx7p1SCQS+O/oKH736qsAgMUNDbg2YT/055zj + T0eO4MUXXrCdwdQKJllgqTuzNmUlltU0DT9atSpTfu8rr2DPc89h9T332PROTEzgr0NDVd+FLsYD + BQBTSgjAUch2dKflsoO1fMuKFejt7cXtt99u0x25eLFs/dMdTOsgAinLP12bat9pKd/p89mmPV9/ + /XXtnvrZRuFUZDq2s0rRt2b1alteLBaryaWe9UJByQRW6lApV3nr/BAAmpuba57A0gcRiyJZpOyL + L7+MTz/7DKZp5h2I0vFYLIa33n7b1tDW1taqDxTFBpHkSiS1F1iyC+eIW3HhwgUc+v3vccstt6Bj + 3Tosv+02/KCpCUuXLMnIRCIRnPzoI5w+fRqJRCKT7na78dDGjTVpgVLefBWt5LVwOS48Pj6Ov7z3 + XiZuvR7y25deylnGv3UrGhsba5ZAmwtDykj2i3yVwDO7duGB+++3vQCYbkA+3NnWhl/v2oXVWQNK + LSF1JygCpC0QiEgpPU7uuYishy9UZkVrK7Zu2YKfbd6Mf54+jctffIGrV6/i6tWrSF8dXrJkCZqX + L0dzczN+eMcdaGtrK6q32qCUQgIRwLIb43gumC3joMz8efOwwefLxE3TRKCnBwDwm+efL1lf1SFl + xpDSLhwGIZktrUJhkq4yAizdRTnlqx2QdOEwkLJAQsh1x6NwdrwMi8mexsw2SClBCLkO3FzKhZ1e + 1y1lGuO0MbMNJNsCIUTE6bnwdK5EZguEEAAhESBFoH/79vCh118HUPyBKuHCALC/v7/k8k/39pZV + V766y0HaU/3bt1ssEAABQoQQX7HpQ6VduBqYSpsppSCWm/o375UREp6OyfRcAyEEICRz3nBziUDp + SQC9Rf+dKlpgf+oYYKqYcpuTXAGwEKhpWshILeYLVWDLqcB+4GxCuv/TNC2UTssQ6Pf7J17bvz9M + CfGahfrBLML+ffXnuJFoqXhjZwotC09hxSJn1+9S/V/Y7/dnTsFsd2sJIUdL7QdnM3kAcPnGg45l + CSEghBy1ptkJVJTBlJBjpQtdlx3L1iJaFp5yJJfmhSjKoC09W3BgYOBdYRhd+d4T/teZM/jzm29m + 4u76+pIaXEvQXC48tHEjHmxvLyrLGANV1cFAILDJmj7pbU3G2D7JeVc+Re6sg+5oLOa8xTWGaCyG + v7//viMCCSFgjO3LTp/0fkF3d3eIUBrKdwVj2bJluPXWW8tpb01ileWQPx8YYyCUhrq7u0PZebnf + WCdkjyDElytv0cKF+OVTT+Grr77CbP+egtvtxrJly4oLEgJGyJ6cWfnKvLZ//17TNHu//2YCA2Os + /1dPP53zoxN5XxFyL1iwhxASmU1vEVUaqY8+RtwLFuS0PqAAgX6/f8KlKJtKndbMFaSf26Uom6wT + 52wUNK/tgUBYVVV/rs9vzmUQQkAphaqq/u2BQMGLio5YOTgwsM0wjMOON11nMazkdQcCR4rJO+rg + ugOBI5Qx/1x35/TzUcYckQeU+AHGgYEBr9D1dyXg6Ax5NiG1URChmrYpUMRtbeVKqSQQCIRNQtYy + RemfK/1i2mWZovSbhKwthTxgCh+hPXDggE8K0SdN0+f4Xk0NIbM5wFiIULpnx44dobL0TLUhBw4c + 8AnT7JGm2QXU/hlJ2msIY4OUsX3lEpfRV4lGAcDBgwc9XNe7hGlulVJmvilQbUKzPhQUpowdVTRt + sLu7O1IR/ZVQko29e/c2aIz5TCHWS8ArTdNnzZ8uUrP7ZMJYiABhRulJ3TRDO3furN1PwRfDwMCA + lwjhEVJ6pZSLJJC0UtP0yNSLe6WCABEwFkn9DhNCrlNCwpLSSKmDwfeoEv4PzkKXcKOscjIAAAAA + SUVORK5CYII= diff --git a/qtc_packaging/debian_harmattan/copyright b/qtc_packaging/debian_harmattan/copyright new file mode 100644 index 0000000..8ac1f10 --- /dev/null +++ b/qtc_packaging/debian_harmattan/copyright @@ -0,0 +1,14 @@ +Copyright 2011-2012 Heikki Holstila + +FingerTerm is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 2 of the License, or +(at your option) any later version. + +FingerTerm is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with FingerTerm. If not, see . diff --git a/qtc_packaging/debian_harmattan/manifest.aegis b/qtc_packaging/debian_harmattan/manifest.aegis new file mode 100644 index 0000000..e69de29 diff --git a/qtc_packaging/debian_harmattan/rules b/qtc_packaging/debian_harmattan/rules new file mode 100755 index 0000000..0f19fc2 --- /dev/null +++ b/qtc_packaging/debian_harmattan/rules @@ -0,0 +1,91 @@ +#!/usr/bin/make -f +# -*- makefile -*- +# Sample debian/rules that uses debhelper. +# This file was originally written by Joey Hess and Craig Small. +# As a special exception, when this file is copied by dh-make into a +# dh-make output file, you may use that output file without restriction. +# This special exception was added by Craig Small in version 0.37 of dh-make. + +# Uncomment this to turn on verbose mode. +#export DH_VERBOSE=1 + + + + + +configure: configure-stamp +configure-stamp: + dh_testdir + # qmake PREFIX=/usr# Uncomment this line for use without Qt Creator + + touch configure-stamp + + +build: build-stamp + +build-stamp: configure-stamp + dh_testdir + + # Add here commands to compile the package. + # $(MAKE) # Uncomment this line for use without Qt Creator + #docbook-to-man debian/fingerterm.sgml > fingerterm.1 + + touch $@ + +clean: + dh_testdir + dh_testroot + rm -f build-stamp configure-stamp + + # Add here commands to clean up after the build process. + $(MAKE) clean + + dh_clean + +install: build + dh_testdir + dh_testroot + dh_clean -k + dh_installdirs + + # Add here commands to install the package into debian/fingerterm. + $(MAKE) INSTALL_ROOT="$(CURDIR)"/debian/fingerterm install + + +# Build architecture-independent files here. +binary-indep: build install +# We have nothing to do by default. + +# Build architecture-dependent files here. +binary-arch: build install + dh_testdir + dh_testroot + dh_installchangelogs + dh_installdocs + dh_installexamples +# dh_install +# dh_installmenu +# dh_installdebconf +# dh_installlogrotate +# dh_installemacsen +# dh_installpam +# dh_installmime +# dh_python +# dh_installinit +# dh_installcron +# dh_installinfo + dh_installman + dh_link + dh_strip + dh_compress + dh_fixperms +# dh_perl +# dh_makeshlibs + dh_installdeb + # dh_shlibdeps # Uncomment this line for use without Qt Creator + dh_gencontrol + dh_md5sums + dh_builddeb + +binary: binary-indep binary-arch +.PHONY: build clean binary-indep binary-arch binary install configure diff --git a/resources.qrc b/resources.qrc new file mode 100644 index 0000000..fb07002 --- /dev/null +++ b/resources.qrc @@ -0,0 +1,26 @@ + + + qml/Main.qml + qml/Key.qml + qml/Keyboard.qml + qml/Lineview.qml + qml/Button.qml + qml/Menu.qml + qml/NotifyWin.qml + icons/backspace.png + icons/down.png + icons/enter.png + icons/left.png + icons/menu.png + icons/right.png + icons/shift.png + icons/tab.png + icons/up.png + qml/UrlWindow.qml + data/menu.xml + icons/scroll-indicator.png + data/english.layout + data/finnish.layout + qml/LayoutWindow.qml + + diff --git a/terminal.cpp b/terminal.cpp new file mode 100644 index 0000000..d2c604a --- /dev/null +++ b/terminal.cpp @@ -0,0 +1,1398 @@ +/* + Copyright 2011-2012 Heikki Holstila + + This file is part of FingerTerm. + + FingerTerm is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + FingerTerm is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with FingerTerm. If not, see . +*/ + +#include +#include + +#include "terminal.h" +#include "ptyiface.h" +#include "textrender.h" +#include "util.h" + +Terminal::Terminal(QObject *parent) : + QObject(parent), iRenderer(0), iPtyIFace(0), iWindow(0), iUtil(0), + iTermSize(0,0), iEmitCursorChangeSignal(true), + iShowCursor(true), iUseAltScreenBuffer(false), iAppCursorKeys(false) +{ + zeroChar.c = ' '; + zeroChar.bgColor = defaultBgColor; + zeroChar.fgColor = defaultFgColor; + zeroChar.attrib = 0; + + escape = -1; + + iTermAttribs.currentFgColor = defaultFgColor; + iTermAttribs.currentBgColor = defaultBgColor; + iTermAttribs.currentAttrib = 0; + iTermAttribs.cursorPos = QPoint(0,0); + iMarginBottom = 0; + iMarginTop = 0; + + resetBackBufferScrollPos(); + + iTermAttribs_saved = iTermAttribs; + iTermAttribs_saved_alt = iTermAttribs; + + resetTerminal(); +} + +void Terminal::setRenderer(TextRender* tr) +{ + iRenderer = tr; + + if(tr) { + tr->updateTermSize(); + connect(this, SIGNAL(displayBufferChanged()), tr, SLOT(redraw())); + connect(this, SIGNAL(cursorPosChanged(QPoint)), tr, SLOT(redraw())); + connect(this, SIGNAL(termSizeChanged(QSize)), tr, SLOT(redraw())); + } else { + qDebug() << "warning: null text renderer"; + } +} + +void Terminal::setPtyIFace(PtyIFace *pty) +{ + iPtyIFace = pty; + if(!pty) { + qDebug() << "warning: null pty iface"; + } +} + +void Terminal::setCursorPos(QPoint pos) +{ + if( iTermAttribs.cursorPos != pos ) { + int tlimit = 1; + int blimit = iTermSize.height(); + if(iTermAttribs.originMode) { + tlimit = iMarginTop; + blimit = iMarginBottom; + } + + if(pos.x() < 1) + pos.setX(1); + if(pos.x() > iTermSize.width()+1) + pos.setX(iTermSize.width()+1); + if(pos.y() < tlimit) + pos.setY(tlimit); + if(pos.y() > blimit) + pos.setY(blimit); + + iTermAttribs.cursorPos=pos; + if(iEmitCursorChangeSignal) + emit cursorPosChanged(pos); + } +} + +QPoint Terminal::cursorPos() +{ + return iTermAttribs.cursorPos; +} + +bool Terminal::showCursor() +{ + if(iBackBufferScrollPos != 0) + return false; + + return iShowCursor; +} + +QList >& Terminal::buffer() +{ + if(iUseAltScreenBuffer) + return iAltBuffer; + + return iBuffer; +} + +void Terminal::setTermSize(QSize size) +{ + if( iTermSize != size ) { + iMarginTop = 1; + iMarginBottom = size.height(); + iTermSize=size; + + resetTabs(); + + emit termSizeChanged(size); + } +} + +void Terminal::putString(QString str, bool unEscape) +{ + if (unEscape) { + str.replace("\\r", "\r"); + str.replace("\\n", "\n"); + str.replace("\\e", QChar(ch_ESC)); + str.replace("\\b", "\b"); + str.replace("\\t", "\t"); + + while(str.indexOf("\\x") != -1) { + int i = str.indexOf("\\x")+2; + QString num; + while(num.length() < 2 && str.length()>i && str.at(i).isNumber() ) { + num.append(str.at(i)); + i++; + } + str.remove(i-2-num.length(), num.length()+2); + bool ok; + str.insert(i-2-num.length(), QChar(num.toInt(&ok,16))); + } + while(str.indexOf("\\0") != -1) { + int i = str.indexOf("\\0")+2; + QString num; + while(num.length() < 3 && str.length()>i && str.at(i).isNumber() ) { + num.append(str.at(i)); + i++; + } + str.remove(i-2-num.length(), num.length()+2); + bool ok; + str.insert(i-2-num.length(), QChar(num.toInt(&ok,8))); + } + } + + if(iPtyIFace) + iPtyIFace->writeTerm(str); +} + +void Terminal::keyPress(int key, int modifiers) +{ + QChar c(key); + //qDebug() << key; + + resetBackBufferScrollPos(); + + if(c.isLetter() && (modifiers & Qt::ShiftModifier)) + c = c.toUpper(); + else if(c.isLetter()) + c = c.toLower(); + + QString toWrite; + + if( key <= 0xFF ) { + char asciiVal = c.toAscii(); + + if(modifiers & Qt::AltModifier) + toWrite.append(ch_ESC); + + if((modifiers & Qt::ControlModifier) && c.isLower()) + asciiVal -= 0x60; + if((modifiers & Qt::ControlModifier) && c.isUpper()) + asciiVal -= 0x40; + toWrite.append(asciiVal); + + if(iPtyIFace) + iPtyIFace->writeTerm(toWrite); + return; + } + + char cursorModif='['; + if(iAppCursorKeys) + cursorModif = 'O'; + + if( key==Qt::Key_Up ) + toWrite += QString("%1%2A").arg(ch_ESC).arg(cursorModif).toAscii(); + if( key==Qt::Key_Down ) + toWrite += QString("%1%2B").arg(ch_ESC).arg(cursorModif).toAscii(); + if( key==Qt::Key_Right ) + toWrite += QString("%1%2C").arg(ch_ESC).arg(cursorModif).toAscii(); + if( key==Qt::Key_Left ) + toWrite += QString("%1%2D").arg(ch_ESC).arg(cursorModif).toAscii(); + + if( key==Qt::Key_Enter || key==Qt::Key_Return ) { + if(iNewLineMode) + toWrite += "\r\n"; + else + toWrite += "\r"; + } + if( key==Qt::Key_Backspace ) + toWrite += "\x7F"; + if( key==Qt::Key_Tab ) + toWrite = "\t"; + + if( key==Qt::Key_PageUp ) + toWrite += QString("%1[5~").arg(ch_ESC).toAscii(); + if( key==Qt::Key_PageDown ) + toWrite += QString("%1[6~").arg(ch_ESC).toAscii(); + if( key==Qt::Key_Home ) + toWrite += QString("%1OH").arg(ch_ESC).toAscii(); + if( key==Qt::Key_End ) + toWrite += QString("%1OF").arg(ch_ESC).toAscii(); + if( key==Qt::Key_Delete ) + toWrite += QString("%1[3~").arg(ch_ESC).toAscii(); + + if( key==Qt::Key_Escape ) { + toWrite += QString(1,ch_ESC); + } + + if(iPtyIFace) + iPtyIFace->writeTerm(toWrite); +} + +void Terminal::insertInBuffer(const QString& chars) +{ + if(iTermSize.isNull()) { + qDebug() << "null size terminal"; + return; + } + + iEmitCursorChangeSignal = false; + + for(int i=0; i cols (terminfo: xenl) + { + if(iNewLineMode) + setCursorPos(QPoint(1,cursorPos().y()+1)); + else + setCursorPos(QPoint(cursorPos().x(), cursorPos().y()+1)); + } + } + else if(ch.toAscii()=='\r') { // carriage return + setCursorPos(QPoint(1,cursorPos().y())); + } + else if(ch.toAscii()=='\b' || ch.toAscii()==127) { //backspace & del (only move cursor, don't erase) + setCursorPos(QPoint(cursorPos().x()-1,cursorPos().y())); + } + else if(ch.toAscii()=='\a') { // BEL + if(escape==']') { // BEL also ends OSC sequence + escape=-1; + oscSequence(oscSeq); + oscSeq.clear(); + } else { + iUtil->bellAlert(); + } + } + else if(ch.toAscii()=='\t') { //tab + if(cursorPos().y() <= iTabStops.size()) { + for(int i=0; i cursorPos().x()) { + setCursorPos(QPoint( iTabStops[cursorPos().y()-1][i], cursorPos().y() )); + break; + } + } + } + } + else if(ch.toAscii()==14 || ch.toAscii()==15) { //SI and SO, related to character set... ignore + } + else { + if( escape>=0 ) { + if( escape==0 && (ch.toAscii()=='[') ) { + escape='['; //ansi sequence + escSeq += ch; + } + else if( escape==0 && (ch.toAscii()==']') ) { + escape=']'; //osc sequence + oscSeq += ch; + } + else if( escape==0 && multiCharEscapes.contains(ch.toAscii())) { + escape = ch.toAscii(); + escSeq += ch; + } + else if( escape==0 && ch.toAscii()=='\\' ) { // ESC\ also ends OSC sequence + escape=-1; + oscSequence(oscSeq); + oscSeq.clear(); + } + else if (ch.toAscii()==ch_ESC) { + escape = 0; + } + else if( escape=='[' || multiCharEscapes.contains(escape) ) { + escSeq += ch; + } + else if( escape==']' ) { + oscSeq += ch; + } + else if( multiCharEscapes.contains(escape) ) { + escSeq += ch; + } + else { + escControlChar(QByteArray(1,ch.toAscii())); + escape=-1; + } + + if( escape=='[' && ch.toAscii() >= 64 && ch.toAscii() <= 126 && ch.toAscii() != '[' ) { + ansiSequence(escSeq); + escape=-1; + escSeq.clear(); + } + if( multiCharEscapes.contains(escape) && escSeq.length()>=2 ) { + escControlChar(escSeq); + escape=-1; + escSeq.clear(); + } + } else { + if (ch.isPrint()) + insertAtCursor(ch, !iReplaceMode); + else if (ch.toAscii()==ch_ESC) + escape=0; + else + qDebug() << "unprintable char" << int(ch.toAscii()); + } + } + } + + iEmitCursorChangeSignal = true; + emit displayBufferChanged(); +} + +void Terminal::insertAtCursor(QChar c, bool overwriteMode, bool advanceCursor) +{ + if(cursorPos().x() > iTermSize.width() && advanceCursor) { + if(iTermAttribs.wrapAroundMode) { + if(cursorPos().y()>=iMarginBottom) { + scrollFwd(1); + setCursorPos(QPoint(1, cursorPos().y())); + } else { + setCursorPos(QPoint(1, cursorPos().y()+1)); + } + } else { + setCursorPos(QPoint(iTermSize.width(), cursorPos().y())); + } + } + + while(currentLine().size() < cursorPos().x() ) + currentLine().append(zeroChar); + + if(!overwriteMode) + currentLine().insert(cursorPos().x()-1,zeroChar); + + currentLine()[cursorPos().x()-1].c = c; + currentLine()[cursorPos().x()-1].fgColor = iTermAttribs.currentFgColor; + currentLine()[cursorPos().x()-1].bgColor = iTermAttribs.currentBgColor; + currentLine()[cursorPos().x()-1].attrib = iTermAttribs.currentAttrib; + + if (advanceCursor) { + setCursorPos(QPoint(cursorPos().x()+1,cursorPos().y())); + } +} + +void Terminal::deleteAt(QPoint pos) +{ + clearAt(pos); + buffer()[pos.y()-1].removeAt(pos.x()-1); +} + +void Terminal::clearAt(QPoint pos) +{ + if(pos.y() <= 0 || pos.y()-1 > buffer().size() || + pos.x() <= 0 || pos.x()-1 > buffer()[pos.y()-1].size()) + { + qDebug() << "warning: trying to clear char out of bounds"; + return; + } + + // just in case... + while(buffer().size() < pos.y()) + buffer().append(QList()); + while(buffer()[pos.y()-1].size() < pos.x() ) + buffer()[pos.y()-1].append(zeroChar); + + buffer()[pos.y()-1][pos.x()-1] = zeroChar; +} + +void Terminal::eraseLineAtCursor(int from, int to) +{ + if(from==-1 && to==-1) { + currentLine().clear(); + return; + } + if(from < 1) + from=1; + from--; + + if(to < 1 || to > currentLine().size()) + to=currentLine().size(); + to--; + + if(from>to) + return; + + for(int i=from; i<=to; i++) { + currentLine()[i] = zeroChar; + } +} + +void Terminal::clearAll(bool wholeBuffer) +{ + clearSelection(); + if(wholeBuffer) { + backBuffer().clear(); + resetBackBufferScrollPos(); + } + buffer().clear(); + setCursorPos(QPoint(1,1)); +} + + +void Terminal::ansiSequence(const QString& seq) +{ + if(seq.length() <= 1 || seq.at(0)!='[') + return; + + QChar cmdChar = seq.at(seq.length()-1); + QString extra; + QList params; + + int x=1; + while(x tmp = seq.mid(x,seq.length()-x-1).split(';'); + foreach(QString b, tmp) { + bool ok=false; + int t = b.toInt(&ok); + if(ok) { + params.append(t); + } + } + if(x>1) + extra = seq.mid(1,x-1); + + bool unhandled = false; + + switch(cmdChar.toAscii()) + { + case 'A': //cursor up + if(!extra.isEmpty()) { + unhandled=true; + break; + } + if(params.count()<1) + params.append(1); + if(params.at(0)==0) + params[0]=1; + setCursorPos(QPoint( cursorPos().x(), qMax(iMarginTop, cursorPos().y()-params.at(0)) )); + break; + case 'B': //cursor down + if(!extra.isEmpty()) { + unhandled=true; + break; + } + if(params.count()<1) + params.append(1); + if(params.at(0)==0) + params[0]=1; + setCursorPos(QPoint( cursorPos().x(), qMin(iMarginBottom, cursorPos().y()+params.at(0)) )); + break; + case 'C': //cursor fwd + if(!extra.isEmpty()) { + unhandled=true; + break; + } + if(params.count()<1) + params.append(1); + if(params.at(0)==0) + params[0]=1; + setCursorPos(QPoint( qMin(iTermSize.width(),cursorPos().x()+params.at(0)), cursorPos().y() )); + break; + case 'D': //cursor back + if(!extra.isEmpty()) { + unhandled=true; + break; + } + if(params.count()<1) + params.append(1); + if(params.at(0)==0) + params[0]=1; + setCursorPos(QPoint( qMax(1,cursorPos().x()-params.at(0)), cursorPos().y() )); + break; + case 'E': //cursor next line + if(!extra.isEmpty()) { + unhandled=true; + break; + } + if(params.count()<1) + params.append(1); + if(params.at(0)==0) + params[0]=1; + setCursorPos(QPoint( 1, qMin(iMarginBottom, cursorPos().y()+params.at(0)) )); + break; + case 'F': //cursor prev line + if(!extra.isEmpty()) { + unhandled=true; + break; + } + if(params.count()<1) + params.append(1); + if(params.at(0)==0) + params[0]=1; + setCursorPos(QPoint( 1, qMax(iMarginTop, cursorPos().y()-params.at(0)) )); + break; + case 'G': //go to column + if(!extra.isEmpty()) { + unhandled=true; + break; + } + if(params.count()<1) + params.append(1); + if(params.at(0)==0) + params[0]=1; + setCursorPos(QPoint( params.at(0), cursorPos().y() )); + break; + case 'H': //cursor pos + case 'f': //cursor pos + if(!extra.isEmpty()) { + unhandled=true; + break; + } + while(params.count()<2) + params.append(1); + if (iTermAttribs.originMode) + setCursorPos(QPoint( params.at(1), params.at(0)+iMarginTop-1 )); + else + setCursorPos(QPoint( params.at(1), params.at(0) )); + break; + case 'J': //erase data + if(!extra.isEmpty() && extra!="?") { + unhandled=true; + break; + } + if(params.count()>=1 && params.at(0)==1) { + eraseLineAtCursor(1,cursorPos().x()); + for(int i=0; i=1 && params.at(0)==2) { + clearAll(); + } else { + eraseLineAtCursor(cursorPos().x()); + for(int i=cursorPos().y(); i=1 && params.at(0)==1) { + eraseLineAtCursor(1,cursorPos().x()); + } + else if(params.count()>=1 && params.at(0)==2) { + currentLine().clear(); + } else { + eraseLineAtCursor(cursorPos().x()); + } + break; + + case 'L': // insert lines + if(!extra.isEmpty()) { + unhandled=true; + break; + } + if(cursorPos().y() < iMarginTop || cursorPos().y() > iMarginBottom) + break; + if(params.count()<1) + params.append(1); + if(params.at(0)==0) + params[0]=1; + if(params.at(0) > iMarginBottom-cursorPos().y()) + scrollBack(iMarginBottom-cursorPos().y(), cursorPos().y()); + else + scrollBack(params.at(0), cursorPos().y()); + setCursorPos(QPoint(1,cursorPos().y())); + break; + case 'M': // delete lines + if(!extra.isEmpty()) { + unhandled=true; + break; + } + if(cursorPos().y() < iMarginTop || cursorPos().y() > iMarginBottom) + break; + if(params.count()<1) + params.append(1); + if(params.at(0)==0) + params[0]=1; + if(params.at(0) > iMarginBottom-cursorPos().y()) + scrollFwd(iMarginBottom-cursorPos().y(), cursorPos().y()); + else + scrollFwd(params.at(0), cursorPos().y()); + setCursorPos(QPoint(1,cursorPos().y())); + break; + + case 'P': // delete characters + if(!extra.isEmpty()) { + unhandled=true; + break; + } + if(params.count()<1) + params.append(1); + if(params.at(0)==0) + params[0]=1; + for(int i=0; iwriteTerm(toWrite); + } else unhandled=true; + break; + + case 'd': //go to row + if(!extra.isEmpty()) { + unhandled=true; + break; + } + if(params.count()<1) + params.append(1); + if(params.at(0)==0) + params[0]=1; + setCursorPos(QPoint( cursorPos().x(), params.at(0) )); + break; + + case 'g': //tab stop manipulation + if(params.count()==0) + params.append(0); + if(params.at(0)==0 && extra=="") { //clear tab at current position + if(cursorPos().y() <= iTabStops.size()) { + int idx = iTabStops[cursorPos().y()-1].indexOf(cursorPos().x()); + if(idx != -1) + iTabStops[cursorPos().y()-1].removeAt(idx); + } + } + else if(params.at(0)==3 && extra=="") { //clear all tabs + iTabStops.clear(); + } + break; + + case 'n': + if(params.count()>=1 && params.at(0)==6 && extra=="") { // write cursor pos + QString toWrite = QString("%1[%2;%3R").arg(ch_ESC).arg(cursorPos().y()).arg(cursorPos().x()).toAscii(); + if(iPtyIFace) + iPtyIFace->writeTerm(toWrite); + } else unhandled=true; + break; + + case 'p': + if(extra=="!") { // reset terminal + resetTerminal(); + } else unhandled=true; + break; + + case 's': //save cursor + if(!extra.isEmpty()) { + unhandled=true; + break; + } + iTermAttribs_saved = iTermAttribs; + break; + case 'u': //restore cursor + if(!extra.isEmpty()) { + unhandled=true; + break; + } + iTermAttribs = iTermAttribs_saved; + break; + + case 'm': //graphics mode + if(!extra.isEmpty()) { + unhandled=true; + break; + } + if(params.count() > 0) { + if(params.contains(0)) { + iTermAttribs.currentFgColor = defaultFgColor; + iTermAttribs.currentBgColor = defaultBgColor; + iTermAttribs.currentAttrib = attribNone; + } + if(params.contains(1)) + iTermAttribs.currentAttrib |= attribBold; + if(params.contains(4)) + iTermAttribs.currentAttrib |= attribUnderline; + if(params.contains(7)) + iTermAttribs.currentAttrib |= attribNegative; + + if(params.contains(22)) + iTermAttribs.currentAttrib &= ~attribBold; + if(params.contains(24)) + iTermAttribs.currentAttrib &= ~attribUnderline; + if(params.contains(27)) + iTermAttribs.currentAttrib &= ~attribNegative; + + foreach(int p, params) { + if(p >= 30 && p<= 37) { + iTermAttribs.currentFgColor = p-30; + } + if(p >= 40 && p<= 47) { + iTermAttribs.currentBgColor = p-40; + } + } + if(params.contains(39)) + iTermAttribs.currentFgColor = defaultFgColor; + if(params.contains(49)) + iTermAttribs.currentBgColor = defaultBgColor; + } else { + iTermAttribs.currentFgColor = defaultFgColor; + iTermAttribs.currentBgColor = defaultBgColor; + iTermAttribs.currentAttrib = attribNone; + } + break; + + case 'h': + if(params.count()>=1 && params.contains(1) && extra=="?") { // application cursor keys + iAppCursorKeys = true; + } + else if(params.count()>=1 && params.contains(3) && extra=="?") { //column mode + // not supported, just clear screen, move cursor home & reset scrolling region + clearAll(); + resetTabs(); + iMarginTop = 1; + iMarginBottom = iTermSize.height(); + } + else if(params.count()>=1 && params.contains(6) && extra=="?") { //origin mode enable + iTermAttribs.originMode = true; + } + else if(params.count()>=1 && params.contains(7) && extra=="?") { //wraparound mode enable + iTermAttribs.wrapAroundMode = true; + } + else if(params.count()>=1 && params.contains(12) && extra=="?") { // start blinking cursor + // just ignore, we don't blink + } + else if(params.count()>=1 && params.contains(25) && extra=="?") { // show cursor + iShowCursor = true; + } + else if(params.count()>=1 && params.contains(1049) && extra=="?") { //use alt screen buffer & save cursor + iTermAttribs_saved_alt = iTermAttribs; + iUseAltScreenBuffer = true; + iMarginTop = 1; + iMarginBottom = iTermSize.height(); + resetBackBufferScrollPos(); + + clearAll(); + resetTabs(); + emit displayBufferChanged(); + } + else if(params.count()>=1 && params.contains(4) && extra=="") { + iReplaceMode = true; + } + else if(params.count()>=1 && params.contains(20) && extra=="") { + iNewLineMode = true; + } + else unhandled=true; + break; + + case 'l': + if(params.count()>=1 && params.contains(1) && extra=="?") { // normal cursor keys + iAppCursorKeys = false; + } + else if(params.count()>=1 && params.contains(3) && extra=="?") { //column mode + // not supported, just clear screen, move cursor home & reset scrolling region + clearAll(); + resetTabs(); + iMarginTop = 1; + iMarginBottom = iTermSize.height(); + } + else if(params.count()>=1 && params.contains(6) && extra=="?") { //origin mode disable + iTermAttribs.originMode = false; + } + else if(params.count()>=1 && params.contains(7) && extra=="?") { //wraparound mode disable + iTermAttribs.wrapAroundMode = false; + } + else if(params.count()>=1 && params.contains(12) && extra=="?") { // stop blinking cursor + // no need to do anything, we don't blink + } + else if(params.count()>=1 && params.contains(25) && extra=="?") { // hide cursor + iShowCursor = false; + } + else if(params.count()>=1 && params.contains(1049) && extra=="?") { //return from alt screen buffer & restore cursor + iUseAltScreenBuffer = false; + iTermAttribs = iTermAttribs_saved_alt; + iMarginBottom = iTermSize.height(); + iMarginTop = 1; + resetBackBufferScrollPos(); + resetTabs(); + emit displayBufferChanged(); + } + + else if(params.count()>=1 && params.contains(4) && extra=="") { + iReplaceMode = false; + } + else if(params.count()>=1 && params.contains(20) && extra=="") { + iNewLineMode = false; + } + else unhandled=true; + break; + + case 'r': // scrolling region + if(!extra.isEmpty()) { + unhandled=true; + break; + } + if(params.count() < 2) { + while(params.count() < 2) + params.append(1); + params[0] = 1; + params[1] = iTermSize.height(); + } + if(params.at(0) < 1) + params[0] = 1; + if(params.at(1) > iTermSize.height()) + params[1] = iTermSize.height(); + iMarginTop = params.at(0); + iMarginBottom = params.at(1); + if(iMarginTop >= iMarginBottom) { + //invalid scroll region + if(iMarginTop == iTermSize.height()) { + iMarginTop = iMarginBottom - 1; + } else { + iMarginBottom = iMarginTop + 1; + } + } + setCursorPos(QPoint( 1, iMarginTop )); + break; + + default: + unhandled=true; + break; + } + + if (unhandled) + qDebug() << "unhandled ansi sequence " << cmdChar << params << extra; +} + +void Terminal::oscSequence(const QString& seq) +{ + if(seq.length() <= 1 || seq.at(0)!=']') + return; + + // set window title + if( seq.length() >= 3 && seq.at(0)==']' && + (seq.at(1)=='0' || seq.at(1)=='2') && + seq.at(2)==';' ) + { + if(iWindow) { + iUtil->setWindowTitle(seq.mid(3)); + } + return; + } + + qDebug() << "unhandled OSC" << seq; +} + +void Terminal::escControlChar(const QString& seq) +{ + QChar ch; + + if(seq.length()==1) { + ch = seq.at(0); + } else if (seq.length()>1 ){ // control sequences longer than 1 characters + if( seq.at(0) == '(' || seq.at(0)==')' ) // character set, ignore this for now... + return; + if( seq.at(0) == '#' && seq.at(1)=='8' ) { // test mode, fill screen with 'E' + clearAll(true); + for(int i=0; i line; + for(int j=0; j' || ch.toAscii()=='=') { //app keypad/normal keypad - ignore these for now... + } + + else if(ch.toAscii()=='H') { // set a tab stop at cursor position + while(iTabStops.size() < cursorPos().y()) + iTabStops.append(QList()); + + iTabStops[cursorPos().y()-1].append(cursorPos().x()); + qSort(iTabStops[cursorPos().y()-1]); + } + else if(ch.toAscii()=='D') { // cursor down/scroll down one line + scrollFwd(1, cursorPos().y()); + } + else if(ch.toAscii()=='M') { // cursor up/scroll up one line + scrollBack(1, cursorPos().y()); + } + + else if(ch.toAscii()=='E') { // new line + if(cursorPos().y()==iMarginBottom) { + scrollFwd(1); + setCursorPos(QPoint(1,cursorPos().y())); + } else { + setCursorPos(QPoint(1,cursorPos().y()+1)); + } + } + else if(ch.toAscii()=='c') { // full reset + resetTerminal(); + } + else if(ch.toAscii()=='g') { // visual bell + iUtil->bellAlert(); + } + else { + qDebug() << "unhandled escape code ESC" << seq; + } +} + +QList& Terminal::currentLine() +{ + while(buffer().size() <= cursorPos().y()-1) + buffer().append(QList()); + + if( cursorPos().y() >= 1 && + cursorPos().y() <= buffer().size() ) + { + return buffer()[cursorPos().y()-1]; + } + + // we shouldn't get here + return buffer()[buffer().size()-1]; +} + +const QStringList Terminal::printableLinesFromCursor(int lines) +{ + QStringList ret; + + int start = cursorPos().y() - lines; + int end = cursorPos().y() + lines; + + for(int l=start-1; l= 0 && l < buffer().size()) { + for(int i=0; i maxScrollBackLines) { + backBuffer().removeFirst(); + } +} + +void Terminal::scrollBack(int lines, int insertAt) +{ + if(lines <= 0) + return; + + adjustSelectionPosition(lines); + + bool useBackbuffer = true; + if(insertAt==-1) { + insertAt = iMarginTop; + useBackbuffer = false; + } + insertAt--; + + while(lines>0) { + if(!iUseAltScreenBuffer) { + if(iBackBuffer.size()>0 && useBackbuffer) + buffer().insert(insertAt, iBackBuffer.takeLast()); + else + buffer().insert(insertAt, QList()); + } else { + buffer().insert(insertAt, QList()); + } + + int rm = iMarginBottom; + if(rm >= buffer().size()) + rm = buffer().size()-1; + + buffer().removeAt(rm); + + lines--; + } +} + +void Terminal::scrollFwd(int lines, int removeAt) +{ + if(lines <= 0) + return; + + adjustSelectionPosition(-lines); + + if(removeAt==-1) { + removeAt = iMarginTop; + } + removeAt--; + + while(buffer().size() < iMarginBottom) + buffer().append(QList()); + + while(lines>0) { + buffer().insert(iMarginBottom, QList()); + + if(!iUseAltScreenBuffer) + iBackBuffer.append( buffer().takeAt(removeAt) ); + else + buffer().removeAt(removeAt); + + lines--; + } + trimBackBuffer(); +} + +void Terminal::resetTerminal() +{ + iBuffer.clear(); + iAltBuffer.clear(); + iBackBuffer.clear(); + + iTermAttribs.currentFgColor = defaultFgColor; + iTermAttribs.currentBgColor = defaultBgColor; + iTermAttribs.currentAttrib = 0; + iTermAttribs.cursorPos = QPoint(1,1); + iTermAttribs.wrapAroundMode = true; + iTermAttribs.originMode = false; + + iTermAttribs_saved = iTermAttribs; + iTermAttribs_saved_alt = iTermAttribs; + + iMarginBottom = iTermSize.height(); + iMarginTop = 1; + + iShowCursor = true; + iUseAltScreenBuffer = false; + iAppCursorKeys = false; + iReplaceMode = false; + iNewLineMode = false; + + resetBackBufferScrollPos(); + + resetTabs(); + clearSelection(); +} + +void Terminal::resetTabs() +{ + iTabStops.clear(); + for(int i=0; i()); + while(tab <= iTermSize.width()) { + iTabStops.last().append(tab); + tab += 8; + } + } +} + +void Terminal::pasteFromClipboard() +{ + QClipboard *cb = QApplication::clipboard(); + if(cb->mimeData()->hasText() && !cb->mimeData()->text().isEmpty()) { + if(iPtyIFace) { + resetBackBufferScrollPos(); + iPtyIFace->writeTerm(cb->mimeData()->text()); + } + } +} + +const QStringList Terminal::grabURLsFromBuffer() +{ + QStringList ret; + QByteArray buf; + + //backbuffer + if ((iUtil->settingsValue("general/grabUrlsFromBackbuffer").toBool() + && !iUseAltScreenBuffer) + || backBufferScrollPos() > 0) //a lazy workaround: just grab everything when the buffer is being scrolled (TODO: make a proper fix) + { + for (int i=0; iconfigPath()+"/menu.xml" ); + if(f.open(QIODevice::ReadOnly|QIODevice::Text)) { + ret = f.readAll(); + f.close(); + } + + return ret; +} + +void Terminal::scrollBackBufferFwd(int lines) +{ + if(iUseAltScreenBuffer || lines<=0) + return; + + clearSelection(); + + iBackBufferScrollPos -= lines; + if(iBackBufferScrollPos < 0) + iBackBufferScrollPos = 0; + + if (iRenderer) { + iRenderer->setShowBufferScrollIndicator(iBackBufferScrollPos != 0); + iRenderer->redraw(); + } +} + +void Terminal::scrollBackBufferBack(int lines) +{ + if (iUseAltScreenBuffer || lines<=0) + return; + + clearSelection(); + + iBackBufferScrollPos += lines; + if (iBackBufferScrollPos > iBackBuffer.size()) + iBackBufferScrollPos = iBackBuffer.size(); + + if (iRenderer) { + iRenderer->setShowBufferScrollIndicator(iBackBufferScrollPos != 0); + iRenderer->redraw(); + } +} + +void Terminal::resetBackBufferScrollPos() +{ + if(iBackBufferScrollPos==0 && iSelection.isNull()) + return; + + iBackBufferScrollPos = 0; + clearSelection(); + + if (iRenderer) { + iRenderer->setShowBufferScrollIndicator(false); + iRenderer->redraw(); + } +} + +void Terminal::copySelectionToClipboard() +{ + if (selection().isNull()) + return; + + QClipboard *cb = QApplication::clipboard(); + cb->clear(); + + QString text; + QString line; + + // backbuffer + if (iBackBufferScrollPos > 0 && !iUseAltScreenBuffer) { + int lineFrom = iBackBuffer.size() - iBackBufferScrollPos + selection().top() - 1; + int lineTo = iBackBuffer.size() - iBackBufferScrollPos + selection().bottom() - 1; + + for (int i=lineFrom; i<=lineTo; i++) { + if (i >= 0 && i < iBackBuffer.size()) { + line.clear(); + int start = 0; + int end = iBackBuffer[i].size()-1; + if (i==lineFrom) + start = selection().left()-1; + if (i==lineTo) + end = selection().right()-1; + for (int j=start; j<=end; j++) { + if (j >= 0 && j < iBackBuffer[i].size() && iBackBuffer[i][j].c.isPrint()) + line += iBackBuffer[i][j].c; + } + text += line.trimmed() + "\n"; + } + } + } + + // main buffer + int lineFrom = selection().top()-1-iBackBufferScrollPos; + int lineTo = selection().bottom()-1-iBackBufferScrollPos; + for (int i=lineFrom; i<=lineTo; i++) { + if (i >= 0 && i < buffer().size()) { + line.clear(); + int start = 0; + int end = buffer()[i].size()-1; + if (i==lineFrom) + start = selection().left()-1; + if (i==lineTo) + end = selection().right()-1; + for (int j=start; j<=end; j++) { + if (j >= 0 && j < buffer()[i].size() && buffer()[i][j].c.isPrint()) + line += buffer()[i][j].c; + } + text += line.trimmed() + "\n"; + } + } + + //qDebug() << text.trimmed(); + + cb->setText(text.trimmed()); +} + +void Terminal::adjustSelectionPosition(int lines) +{ + // adjust selection position when terminal contents move + + if (iSelection.isNull() || lines==0) + return; + + int tx = iSelection.left(); + int ty = iSelection.top() + lines; + int bx = iSelection.right(); + int by = iSelection.bottom() + lines; + + if (ty<1) { + ty = 1; + tx = 1; + } + if (by>iTermSize.height()) { + by = iTermSize.height(); + bx = iTermSize.width(); + } + if (by<1 || ty>iTermSize.height()) { + clearSelection(); + return; + } + + iSelection = QRect(QPoint(tx,ty), QPoint(bx,by)); + + if (iRenderer) + iRenderer->redraw(); +} + +void Terminal::setSelection(QPoint start, QPoint end) +{ + if (start.y() > end.y()) + qSwap(start, end); + if (start.y() == end.y() && start.x() > end.x()) + qSwap(start, end); + + if (start.x() < 1) + start.rx() = 1; + if (start.y() < 1) + start.ry() = 1; + if (end.x() > iTermSize.width()) + end.rx() = iTermSize.width(); + if (end.y() > iTermSize.height()) + end.ry() = iTermSize.height(); + + iSelection = QRect(start, end); + + if (iRenderer) + iRenderer->redraw(); +} + +void Terminal::clearSelection() +{ + if (iSelection.isNull()) + return; + + iSelection = QRect(); + + if (iUtil) + iUtil->selectionFinished(); + if (iRenderer) + iRenderer->redraw(); +} + +QRect Terminal::selection() +{ + return iSelection; +} diff --git a/terminal.h b/terminal.h new file mode 100644 index 0000000..2a31eba --- /dev/null +++ b/terminal.h @@ -0,0 +1,163 @@ +/* + Copyright 2011-2012 Heikki Holstila + + This file is part of FingerTerm. + + FingerTerm is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + FingerTerm is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with FingerTerm. If not, see . +*/ + +#ifndef TERMINAL_H +#define TERMINAL_H + +#include +#include + +class TextRender; +class PtyIFace; +class Util; + +struct TermChar { + QChar c; + int fgColor; + int bgColor; + int attrib; +}; + +const int attribNone = 0; +const int attribBold = 1; +const int attribUnderline = 2; +const int attribNegative = 4; +const QByteArray multiCharEscapes("().*+-/%#"); + +struct TermAttribs { + QPoint cursorPos; + + bool wrapAroundMode; + bool originMode; + + int currentFgColor; + int currentBgColor; + int currentAttrib; +}; + +class Terminal : public QObject +{ + Q_OBJECT +public: + static const int defaultFgColor = 7; + static const int defaultBgColor = 0; + + explicit Terminal(QObject *parent = 0); + virtual ~Terminal() {} + void setRenderer(TextRender* tr); + void setPtyIFace(PtyIFace* pty); + void setWindow(QWidget* win) { iWindow=win; } + void setUtil(Util* util) { iUtil = util; } + + void insertInBuffer(const QString& chars); + + QPoint cursorPos(); + void setCursorPos(QPoint pos); + bool showCursor(); + + Q_INVOKABLE QSize termSize() { return iTermSize; } + void setTermSize(QSize size); + + QList >& buffer(); + QList >& backBuffer() { return iBackBuffer; } + + QList& currentLine(); + + Q_INVOKABLE void keyPress(int key, int modifiers); + Q_INVOKABLE const QStringList printableLinesFromCursor(int lines); + Q_INVOKABLE void putString(QString str, bool unEscape=false); + + Q_INVOKABLE void pasteFromClipboard(); + Q_INVOKABLE void copySelectionToClipboard(); + Q_INVOKABLE const QStringList grabURLsFromBuffer(); + + Q_INVOKABLE QString getUserMenuXml(); + + void scrollBackBufferFwd(int lines); + void scrollBackBufferBack(int lines); + int backBufferScrollPos() { return iBackBufferScrollPos; } + void resetBackBufferScrollPos(); + + void setSelection(QPoint start, QPoint end); + QRect selection(); + Q_INVOKABLE void clearSelection(); + bool hasSelection(); + + TermChar zeroChar; + +signals: + void cursorPosChanged(QPoint newPos); + void termSizeChanged(QSize newSize); + void displayBufferChanged(); + +private: + Q_DISABLE_COPY(Terminal) + static const char ch_ESC = 0x1B; //escape + static const int maxScrollBackLines = 300; + + void insertAtCursor(QChar c, bool overwriteMode=true, bool advanceCursor=true); + void deleteAt(QPoint pos); + void clearAt(QPoint pos); + void eraseLineAtCursor(int from=-1, int to=-1); + void clearAll(bool wholeBuffer=false); + void ansiSequence(const QString& seq); + void oscSequence(const QString& seq); + void escControlChar(const QString& seq); + void trimBackBuffer(); + void scrollBack(int lines, int insertAt=-1); + void scrollFwd(int lines, int removeAt=-1); + void resetTerminal(); + void resetTabs(); + void adjustSelectionPosition(int lines); + + TextRender* iRenderer; + PtyIFace* iPtyIFace; + QWidget* iWindow; + Util* iUtil; + + QList > iBuffer; + QList > iAltBuffer; + QList > iBackBuffer; + QList > iTabStops; + + QSize iTermSize; + bool iEmitCursorChangeSignal; + + bool iShowCursor; + bool iUseAltScreenBuffer; + bool iAppCursorKeys; + bool iReplaceMode; + bool iNewLineMode; + + int iMarginTop; + int iMarginBottom; + + int iBackBufferScrollPos; + + TermAttribs iTermAttribs; + TermAttribs iTermAttribs_saved; + TermAttribs iTermAttribs_saved_alt; + + QString escSeq; + QString oscSeq; + int escape; + QRect iSelection; +}; + +#endif // TERMINAL_H diff --git a/textrender.cpp b/textrender.cpp new file mode 100644 index 0000000..fcc86c1 --- /dev/null +++ b/textrender.cpp @@ -0,0 +1,316 @@ +/* + Copyright 2011-2012 Heikki Holstila + + This file is part of FingerTerm. + + FingerTerm is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + FingerTerm is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with FingerTerm. If not, see . +*/ + +#include +#include "textrender.h" +#include "terminal.h" +#include "util.h" + +TextRender::TextRender(QDeclarativeItem *parent) : + QDeclarativeItem(parent), + iTerm(0), + iUtil(0) +{ + setFlag(QGraphicsItem::ItemHasNoContents, false); + + connect(this,SIGNAL(widthChanged(int)),this,SLOT(updateTermSize())); + connect(this,SIGNAL(heightChanged(int)),this,SLOT(updateTermSize())); + connect(this,SIGNAL(fontSizeChanged()),this,SLOT(updateTermSize())); + + //normal + iColorTable.append(QColor(0, 0, 0)); + iColorTable.append(QColor(210, 0, 0)); + iColorTable.append(QColor(0, 210, 0)); + iColorTable.append(QColor(210, 210, 0)); + iColorTable.append(QColor(0, 0, 240)); + iColorTable.append(QColor(210, 0, 210)); + iColorTable.append(QColor(0, 210, 210)); + iColorTable.append(QColor(235, 235, 235)); + //bright + iColorTable.append(QColor(127, 127, 127)); + iColorTable.append(QColor(255, 0, 0)); + iColorTable.append(QColor(0, 255, 0)); + iColorTable.append(QColor(255, 255, 0)); + iColorTable.append(QColor(92, 92, 255)); + iColorTable.append(QColor(255, 0, 255)); + iColorTable.append(QColor(0, 255, 255)); + iColorTable.append(QColor(255, 255, 255)); + + if(iColorTable.size()!=16) + qFatal("invalid color table"); + + iShowBufferScrollIndicator = false; + + // caching results in considerably faster redrawing during animations + setCacheMode(QGraphicsItem::DeviceCoordinateCache); +} + +TextRender::~TextRender() +{ +} + +void TextRender::paint(QPainter* painter, const QStyleOptionGraphicsItem*, QWidget*) +{ + if (!iTerm) + return; + + painter->save(); + painter->setFont(iFont); + + int y=0; + if (iTerm->backBufferScrollPos() != 0 && iTerm->backBuffer().size()>0) { + int from = iTerm->backBuffer().size() - iTerm->backBufferScrollPos(); + if(from<0) + from=0; + int to = iTerm->backBuffer().size(); + if(to-from > iTerm->termSize().height()) + to = from + iTerm->termSize().height(); + paintFromBuffer(painter, iTerm->backBuffer(), from, to, y); + if(to-from < iTerm->termSize().height() && iTerm->buffer().size()>0) { + int to2 = iTerm->termSize().height() - (to-from); + if(to2 > iTerm->buffer().size()) + to2 = iTerm->buffer().size(); + paintFromBuffer(painter, iTerm->buffer(), 0, to2, y); + } + } else { + int count = qMin(iTerm->termSize().height(), iTerm->buffer().size()); + paintFromBuffer(painter, iTerm->buffer(), 0, count, y); + } + + // cursor + if (iTerm->showCursor()) { + painter->setOpacity(1.0); + QPoint cursor = cursorPixelPos(); + QSize csize = cursorPixelSize(); + painter->setPen( iColorTable[Terminal::defaultFgColor] ); + painter->setBrush(Qt::transparent); + painter->drawRect(cursor.x(), cursor.y(), csize.width(), csize.height()); + } + + // selection + QRect selection = iTerm->selection(); + if (!selection.isNull()) { + painter->setOpacity(0.5); + painter->setPen(Qt::transparent); + painter->setBrush(Qt::blue); + QPoint start, end; + + if (selection.top() == selection.bottom()) { + start = charsToPixels(selection.topLeft()); + end = charsToPixels(selection.bottomRight()); + painter->drawRect(start.x(), start.y(), + end.x()-start.x()+fontWidth(), end.y()-start.y()+fontHeight()); + } else { + start = charsToPixels(selection.topLeft()); + end = charsToPixels(QPoint(iTerm->termSize().width(), selection.top())); + painter->drawRect(start.x(), start.y(), + end.x()-start.x()+fontWidth(), end.y()-start.y()+fontHeight()); + + start = charsToPixels(QPoint(1, selection.top()+1)); + end = charsToPixels(QPoint(iTerm->termSize().width(), selection.bottom()-1)); + painter->drawRect(start.x(), start.y(), + end.x()-start.x()+fontWidth(), end.y()-start.y()+fontHeight()); + + start = charsToPixels(QPoint(1, selection.bottom())); + end = charsToPixels(selection.bottomRight()); + painter->drawRect(start.x(), start.y(), + end.x()-start.x()+fontWidth(), end.y()-start.y()+fontHeight()); + } + } + + painter->restore(); +} + +void TextRender::paintFromBuffer(QPainter* painter, QList >& buffer, int from, int to, int &y) +{ + const int leftmargin = 2; + int cutAfter = property("cutAfter").toInt() + iFontDescent; + + TermChar tmp = iTerm->zeroChar; + TermChar nextAttrib = iTerm->zeroChar; + TermChar currAttrib = iTerm->zeroChar; + int currentX = leftmargin; + for(int i=from; i= cutAfter) + painter->setOpacity(0.3); + else + painter->setOpacity(1.0); + + int xcount = qMin(buffer.at(i).count(), iTerm->termSize().width()); + + // background for the current line + currentX = leftmargin; + int fragWidth = 0; + for(int j=0; jsetPen(Qt::transparent); + painter->setBrush( iColorTable[style.bgColor] ); + painter->drawRect(x, y, width, iFontHeight); +} + +void TextRender::drawTextFragment(QPainter* painter, int x, int y, QString text, TermChar style) +{ + if (style.attrib & attribNegative) { + int c = style.fgColor; + style.fgColor = style.bgColor; + style.bgColor = c; + } + if (style.attrib & attribBold) { + iFont.setBold(true); + painter->setFont(iFont); + if(style.fgColor < 8) + style.fgColor += 8; + } else if(iFont.bold()) { + iFont.setBold(false); + painter->setFont(iFont); + } + + painter->setPen( iColorTable[style.fgColor] ); + painter->setBrush(Qt::transparent); + painter->drawText(x, y, text); +} + +void TextRender::redraw() +{ + update(boundingRect()); +} + +void TextRender::setTerminal(Terminal *term) +{ + if (!iUtil) + qFatal("textrender: util class not set"); + + iTerm = term; + + iFont = QFont(iUtil->settingsValue("ui/fontFamily").toString(), + iUtil->settingsValue("ui/fontSize").toInt()); + iFont.setBold(false); + QFontMetrics fontMetrics(iFont); + iFontHeight = fontMetrics.height(); + iFontWidth = fontMetrics.maxWidth(); + iFontDescent = fontMetrics.descent(); +} + +void TextRender::updateTermSize() +{ + if (!iTerm) + return; + + QSize s((iWidth-4)/iFontWidth, (iHeight-4)/iFontHeight); + iTerm->setTermSize(s); +} + +void TextRender::setFontPointSize(int psize) +{ + if (iFont.pointSize() != psize) + { + iFont.setPointSize(psize); + QFontMetrics fontMetrics(iFont); + iFontHeight = fontMetrics.height(); + iFontWidth = fontMetrics.maxWidth(); + iFontDescent = fontMetrics.descent(); + + iUtil->setSettingsValue("ui/fontSize", psize); + + emit fontSizeChanged(); + } +} + +QPoint TextRender::cursorPixelPos() +{ + QPoint ret = charsToPixels(iTerm->cursorPos()); + ret.rx() += 2; + ret.ry() += iFontHeight/2; + return ret; +} + +QPoint TextRender::charsToPixels(QPoint pos) +{ + // not 100% accurrate with all fonts + return QPoint(2+(pos.x()-1)*iFontWidth, (pos.y()-1)*iFontHeight+iFontDescent+1); +} + +QSize TextRender::cursorPixelSize() +{ + return (QSize(iFontWidth, iFontHeight/2)); +} diff --git a/textrender.h b/textrender.h new file mode 100644 index 0000000..26bf6a4 --- /dev/null +++ b/textrender.h @@ -0,0 +1,98 @@ +/* + Copyright 2011-2012 Heikki Holstila + + This file is part of FingerTerm. + + FingerTerm is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + FingerTerm is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with FingerTerm. If not, see . +*/ + +#ifndef TEXTRENDER_H +#define TEXTRENDER_H + +#include +#include +#include + +#include "terminal.h" + +class Util; + +class TextRender : public QDeclarativeItem +{ + Q_PROPERTY(int myWidth READ width WRITE setWidth NOTIFY widthChanged) + Q_PROPERTY(int myHeight READ height WRITE setHeight NOTIFY heightChanged) + Q_PROPERTY(int fontWidth READ fontWidth NOTIFY fontSizeChanged) + Q_PROPERTY(int fontHeight READ fontHeight NOTIFY fontSizeChanged) + Q_PROPERTY(int fontPointSize READ fontPointSize WRITE setFontPointSize NOTIFY fontSizeChanged) + Q_PROPERTY(bool showBufferScrollIndicator READ showBufferScrollIndicator WRITE setShowBufferScrollIndicator NOTIFY showBufferScrollIndicatorChanged) + + Q_OBJECT +public: + explicit TextRender(QDeclarativeItem *parent = 0); + virtual ~TextRender(); + void paint(QPainter*, const QStyleOptionGraphicsItem*, QWidget*); + + void setTerminal(Terminal* term); + void setUtil(Util* util) { iUtil = util; } + + int width() { return iWidth; } + int height() { return iHeight; } + void setWidth(int w) { if(iWidth!=w) { iWidth=w; emit widthChanged(w); } } + void setHeight(int h) { if(iHeight!=h) { iHeight=h; emit heightChanged(h); } } + int fontWidth() { return iFontWidth; } + int fontHeight() { return iFontHeight; } + int fontDescent() { return iFontDescent; } + int fontPointSize() { return iFont.pointSize(); } + void setFontPointSize(int psize); + bool showBufferScrollIndicator() { return iShowBufferScrollIndicator; } + void setShowBufferScrollIndicator(bool s) { if(iShowBufferScrollIndicator!=s) { iShowBufferScrollIndicator=s; emit showBufferScrollIndicatorChanged(); } } + + Q_INVOKABLE QPoint cursorPixelPos(); + Q_INVOKABLE QSize cursorPixelSize(); + +signals: + void widthChanged(int newWidth); + void heightChanged(int newHeight); + void fontSizeChanged(); + void showBufferScrollIndicatorChanged(); + +public slots: + void redraw(); + void updateTermSize(); + +private: + Q_DISABLE_COPY(TextRender) + + void paintFromBuffer(QPainter* painter, QList >& buffer, int from, int to, int &y); + void drawBgFragment(QPainter* painter, int x, int y, int width, TermChar style); + void drawTextFragment(QPainter* painter, int x, int y, QString text, TermChar style); + QPoint charsToPixels(QPoint pos); + + int iWidth; + int iHeight; + QFont iFont; + int iFontWidth; + int iFontHeight; + int iFontDescent; + bool iShowBufferScrollIndicator; + + Terminal *iTerm; + Util *iUtil; + + QList iColorTable; +}; + +QML_DECLARE_TYPE(TextRender) + +#endif // TEXTRENDER_H diff --git a/updateversion.sh b/updateversion.sh new file mode 100755 index 0000000..3127a94 --- /dev/null +++ b/updateversion.sh @@ -0,0 +1,11 @@ +#!/bin/bash +changelog="qtc_packaging/debian_harmattan/changelog" + +ver=`cat $changelog | sed -n -e '1p'| cut -d ' ' -f 2 | tr -d "()" | cut -d '-' -f 1` + +echo -e \ +"#ifndef VERSION_H\n"\ +"#define VERSION_H\n"\ +"const QString PROGRAM_VERSION=\"$ver\";\n"\ +"#endif\n"\ +> version.h diff --git a/util.cpp b/util.cpp new file mode 100644 index 0000000..3f2c3fc --- /dev/null +++ b/util.cpp @@ -0,0 +1,394 @@ +/* + Copyright 2011-2012 Heikki Holstila + + This file is part of FingerTerm. + + FingerTerm is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + FingerTerm is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with FingerTerm. If not, see . +*/ + +#include "qplatformdefs.h" + +#include +#include +#include + +#include "mainwindow.h" +#include "terminal.h" +#include "util.h" +#include "textrender.h" +#include "version.h" + +#ifdef MEEGO_EDITION_HARMATTAN +#include +#include +#include +#include +#endif //MEEGO_EDITION_HARMATTAN + +Util::Util(QSettings *settings, QObject *parent) : + QObject(parent), + iAllowGestures(false), + newSelection(true), + iSettings(settings), + iWindow(0), + iRenderer(0) +{ + swipeModeSet = false; + swipeAllowed = true; + + connect(QApplication::clipboard(), SIGNAL(dataChanged()), this, SIGNAL(clipboardOrSelectionChanged())); +} + +Util::~Util() +{ + // clear the notifications on quit + clearNotifications(); +} + +void Util::setWindow(QWidget* win) +{ + iWindow = dynamic_cast(win); + if(!iWindow) + qFatal("invalid main window"); + connect(iWindow, SIGNAL(focusChanged(bool)), this, SLOT(onMainWinFocusChanged(bool))); +} + +void Util::setWindowTitle(QString title) +{ + iCurrentWinTitle = title; + iWindow->setWindowTitle(title); + emit windowTitleChanged(); +} + +QString Util::currentWindowTitle() +{ + return iCurrentWinTitle; +} + +void Util::onMainWinFocusChanged(bool in) +{ + if (in) { + clearNotifications(); + + //disable & re-enable swiping when window gains focus (workaround for an "auto mode" bug) + updateSwipeLock(false); + updateSwipeLock(true); + } +} + +void Util::windowMinimize() +{ + iWindow->minimize(); +} + +void Util::openNewWindow() +{ +#ifdef MEEGO_EDITION_HARMATTAN + + QDBusInterface iface(MComponentData::instance()->serviceName(), + "/org/maemo/m", + "com.nokia.MApplicationIf"); + + if (iface.isValid()) { + QStringList params; + params.append("new"); + iface.call("launch", params); + } + +#endif //MEEGO_EDITION_HARMATTAN +} + +void Util::updateSwipeLock(bool suggestedState) +{ +#ifdef MEEGO_EDITION_HARMATTAN + if (settingsValue("ui/allowSwipe").toString()=="auto") { + if(suggestedState) { + enableSwipe(); + } else { + disableSwipe(); + } + } else if (settingsValue("ui/allowSwipe").toString()=="false") { + disableSwipe(); + } else if (settingsValue("ui/allowSwipe").toString()=="true") { + enableSwipe(); + } +#else + Q_UNUSED(suggestedState) +#endif //MEEGO_EDITION_HARMATTAN +} + +void Util::disableSwipe() +{ +#ifdef MEEGO_EDITION_HARMATTAN + if(swipeModeSet && !swipeAllowed) + return; + + if (iWindow) { + iWindow->disableSwipe(); + swipeModeSet = true; + swipeAllowed = false; + } +#endif //MEEGO_EDITION_HARMATTAN +} + +void Util::enableSwipe() +{ +#ifdef MEEGO_EDITION_HARMATTAN + if(swipeModeSet && swipeAllowed) + return; + + if (iWindow) + { + iWindow->enableSwipe(); + swipeModeSet = true; + swipeAllowed = true; + } +#endif //MEEGO_EDITION_HARMATTAN +} + +QString Util::configPath() +{ + if(!iSettings) + return QString(); + + QFileInfo f(iSettings->fileName()); + return f.path(); +} + +QVariant Util::settingsValue(QString key) +{ + if(!iSettings) + return QVariant(); + + return iSettings->value(key); +} + +void Util::setSettingsValue(QString key, QVariant value) +{ + if(iSettings) + iSettings->setValue(key, value); +} + +QString Util::versionString() +{ + return PROGRAM_VERSION; +} + +int Util::uiFontSize() +{ +#ifdef MEEGO_EDITION_HARMATTAN + return 14; +#else + return 12; +#endif +} + +bool Util::isHarmattan() +{ +#ifdef MEEGO_EDITION_HARMATTAN + return true; +#else + return false; +#endif +} + +void Util::keyPressFeedback() +{ + if( !settingsValue("ui/keyPressFeedback").toBool() ) + return; + +#ifdef MEEGO_EDITION_HARMATTAN + MFeedback::play("priority2_static_press"); +#endif +} + +void Util::keyReleaseFeedback() +{ + if( !settingsValue("ui/keyPressFeedback").toBool() ) + return; + +#ifdef MEEGO_EDITION_HARMATTAN + MFeedback::play("priority2_static_release"); +#endif +} + +void Util::bellAlert() +{ + if(!iWindow) + return; + +#ifdef MEEGO_EDITION_HARMATTAN + if(settingsValue("general/backgroundBellNotify").toBool() && + !iWindow->hasFocus()) + { + MRemoteAction act(MComponentData::instance()->serviceName(), + "/org/maemo/m", + "com.nokia.MApplicationIf", + "launch"); + MNotification notif(MNotification::ImReceivedEvent, "FingerTerm", "Terminal alert was received"); + notif.setImage("/usr/share/icons/hicolor/80x80/apps/fingerterm.png"); + notif.setAction(act); + notif.publish(); + } else if( settingsValue("general/visualBell").toBool() ) { + emit visualBell(); + } +#else + if( settingsValue("general/visualBell").toBool() ) + emit visualBell(); +#endif +} + +void Util::clearNotifications() +{ +#ifdef MEEGO_EDITION_HARMATTAN + QList notifs = MNotification::notifications(); + foreach(MNotification* n, notifs) { + if( n->remove() ) + delete n; + } +#endif //MEEGO_EDITION_HARMATTAN +} + +bool Util::eventFilter(QObject *, QEvent *ev) +{ + // event filter used to check if a mouse drag/pan was performed on the scene + + const int reqDragLength = 140; + + if(!iAllowGestures) + return false; + + if(ev->type()==QEvent::GraphicsSceneMousePress) { + QGraphicsSceneMouseEvent *mev = static_cast(ev); + dragOrigin = mev->scenePos(); + newSelection = true; + } + else if(ev->type()==QEvent::GraphicsSceneMouseMove) { + QGraphicsSceneMouseEvent *mev = static_cast(ev); + if(settingsValue("ui/dragMode")=="scroll") { + scrollBackBuffer(mev->scenePos(), mev->lastScenePos()); + } + else if(settingsValue("ui/dragMode")=="select" && iRenderer) { + selectionHelper(mev->scenePos()); + } + } + else if(ev->type()==QEvent::GraphicsSceneMouseRelease) { + QGraphicsSceneMouseEvent *mev = static_cast(ev); + if(settingsValue("ui/dragMode")=="gestures" && mev->lastScenePos() != dragOrigin) { + int xdist = qAbs(mev->scenePos().x() - dragOrigin.x()); + int ydist = qAbs(mev->scenePos().y() - dragOrigin.y()); + if(mev->scenePos().x() < dragOrigin.x()-reqDragLength && xdist > ydist*2) + doGesture(PanLeft); + else if(mev->scenePos().x() > dragOrigin.x()+reqDragLength && xdist > ydist*2) + doGesture(PanRight); + else if(mev->scenePos().y() > dragOrigin.y()+reqDragLength && ydist > xdist*2) + doGesture(PanDown); + else if(mev->scenePos().y() < dragOrigin.y()-reqDragLength && ydist > xdist*2) + doGesture(PanUp); + } + else if(settingsValue("ui/dragMode")=="scroll") { + scrollBackBuffer(mev->scenePos(), mev->lastScenePos()); + } + else if(settingsValue("ui/dragMode")=="select" && iRenderer) { + selectionHelper(mev->scenePos()); + selectionFinished(); + } + } + + return false; +} + +void Util::selectionHelper(QPointF scenePos) +{ + int yCorr = iRenderer->fontDescent(); + + QPoint start(qRound((dragOrigin.x()+2)/iRenderer->fontWidth()), + qRound((dragOrigin.y()+yCorr)/iRenderer->fontHeight())); + QPoint end(qRound((scenePos.x()+2)/iRenderer->fontWidth()), + qRound((scenePos.y()+yCorr)/iRenderer->fontHeight())); + + if (start != end) { + iTerm->setSelection(start, end); + newSelection = false; + } +} + +void Util::scrollBackBuffer(QPointF now, QPointF last) +{ + if(!iTerm) + return; + + int xdist = qAbs(now.x() - last.x()); + int ydist = qAbs(now.y() - last.y()); + + if(now.y() < last.y() && xdist < ydist*2) + iTerm->scrollBackBufferFwd(1); + else if(now.y() > last.y() && xdist < ydist*2) + iTerm->scrollBackBufferBack(1); +} + +void Util::doGesture(Util::PanGesture gesture) +{ + if(!iTerm) + return; + + if( gesture==PanLeft ) { + emit gestureNotify(settingsValue("gestures/panLeftTitle").toString()); + iTerm->putString(settingsValue("gestures/panLeftCommand").toString(), true); + } + else if( gesture==PanRight ) { + emit gestureNotify(settingsValue("gestures/panRightTitle").toString()); + iTerm->putString(settingsValue("gestures/panRightCommand").toString(), true); + } + else if( gesture==PanDown ) { + emit gestureNotify(settingsValue("gestures/panDownTitle").toString()); + iTerm->putString(settingsValue("gestures/panDownCommand").toString(), true); + } + else if( gesture==PanUp ) { + emit gestureNotify(settingsValue("gestures/panUpTitle").toString()); + iTerm->putString(settingsValue("gestures/panUpCommand").toString(), true); + } +} + +void Util::notifyText(QString text) +{ + emit gestureNotify(text); +} + +void Util::copyTextToClipboard(QString str) +{ + QClipboard *cb = QApplication::clipboard(); + cb->clear(); + cb->setText(str); +} + +bool Util::terminalHasSelection() +{ + return !iTerm->selection().isNull(); +} + +bool Util::canPaste() +{ + QClipboard *cb = QApplication::clipboard(); + if(cb->mimeData()->hasText() && !cb->mimeData()->text().isEmpty()) + return true; + + return false; +} + +void Util::selectionFinished() +{ + emit clipboardOrSelectionChanged(); +} + diff --git a/util.h b/util.h new file mode 100644 index 0000000..557a217 --- /dev/null +++ b/util.h @@ -0,0 +1,110 @@ +/* + Copyright 2011-2012 Heikki Holstila + + This file is part of FingerTerm. + + FingerTerm is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + FingerTerm is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with FingerTerm. If not, see . +*/ + +#ifndef UTIL_H +#define UTIL_H + +#include +#include + +class Terminal; +class MainWindow; +class TextRender; + +class Util : public QObject +{ + Q_OBJECT +public: + explicit Util(QSettings* settings, QObject *parent = 0); + virtual ~Util(); + void setWindow(QWidget* win); + void setWindowTitle(QString title); + Q_INVOKABLE QString currentWindowTitle(); + void setTerm(Terminal* term) { iTerm = term; } + void setRenderer(TextRender* r) { iRenderer = r; } + + Q_INVOKABLE void windowMinimize(); + Q_INVOKABLE void openNewWindow(); + Q_INVOKABLE void updateSwipeLock(bool suggestedState); + + Q_INVOKABLE QString versionString(); + Q_INVOKABLE QString configPath(); + Q_INVOKABLE QVariant settingsValue(QString key); + Q_INVOKABLE void setSettingsValue(QString key, QVariant value); + + Q_INVOKABLE int uiFontSize(); + Q_INVOKABLE bool isHarmattan(); + + Q_INVOKABLE void keyPressFeedback(); + Q_INVOKABLE void keyReleaseFeedback(); + Q_INVOKABLE void notifyText(QString text); + + Q_INVOKABLE void copyTextToClipboard(QString str); + Q_INVOKABLE bool canPaste(); + Q_INVOKABLE bool terminalHasSelection(); + + void bellAlert(); + void selectionFinished(); + + bool allowGestures() { return iAllowGestures; } + void setAllowGestures(bool a) { if(iAllowGestures!=a) { iAllowGestures=a; emit allowGesturesChanged(); } } + + Q_PROPERTY(bool allowGestures READ allowGestures WRITE setAllowGestures NOTIFY allowGesturesChanged) + +public slots: + void onMainWinFocusChanged(bool in); + +protected: + virtual bool eventFilter(QObject*, QEvent *ev); + +signals: + void visualBell(); + void allowGesturesChanged(); + void gestureNotify(QString msg); + void clipboardOrSelectionChanged(); + void windowTitleChanged(); + +private: + Q_DISABLE_COPY(Util) + enum PanGesture { PanNone, PanLeft, PanRight, PanUp, PanDown }; + + void enableSwipe(); + void disableSwipe(); + bool swipeModeSet; + bool swipeAllowed; + + void scrollBackBuffer(QPointF now, QPointF last); + void doGesture(PanGesture gesture); + void clearNotifications(); + void selectionHelper(QPointF scenePos); + + QPointF dragOrigin; + + bool iAllowGestures; + bool newSelection; + + QString iCurrentWinTitle; + + QSettings* iSettings; + MainWindow* iWindow; + Terminal* iTerm; + TextRender* iRenderer; +}; + +#endif // UTIL_H diff --git a/version.h b/version.h new file mode 100644 index 0000000..a4a0bce --- /dev/null +++ b/version.h @@ -0,0 +1,5 @@ +#ifndef VERSION_H +#define VERSION_H +const QString PROGRAM_VERSION="1.0.2"; +#endif +