summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.kdev4/acritoxinstaller-0.2.kdev429
-rw-r--r--AUTHORS1
-rw-r--r--CMakeLists.txt43
-rw-r--r--COPYING674
-rw-r--r--INSTALL20
-rw-r--r--acritoxinstaller-0.2.kdev43
-rw-r--r--backend.cpp250
-rw-r--r--backend.h59
-rwxr-xr-xbackend/backend.sh.in96
-rw-r--r--backend/modules/bootloader158
-rw-r--r--backend/modules/cleanup15
-rw-r--r--backend/modules/common13
-rw-r--r--backend/modules/config68
-rw-r--r--backend/modules/frontend21
-rw-r--r--backend/modules/hdmap340
-rw-r--r--backend/modules/init11
-rw-r--r--backend/modules/install75
-rw-r--r--backend/modules/install_configure152
-rw-r--r--backend/modules/install_main302
-rw-r--r--backend/modules/install_services19
-rw-r--r--backend/modules/partitions251
-rw-r--r--backend/modules/partmgr24
-rw-r--r--busyappfilter.cpp23
-rw-r--r--busyappfilter.h14
-rw-r--r--config.h.in2
-rw-r--r--debian/acritoxinstaller-kanotix.install2
-rw-r--r--debian/acritoxinstaller.install1
-rw-r--r--debian/changelog5
-rw-r--r--debian/compat1
-rw-r--r--debian/control19
-rw-r--r--debian/copyright34
-rwxr-xr-xdebian/rules7
-rw-r--r--debian/source/format1
-rw-r--r--images/banner.pngbin0 -> 72633 bytes
-rw-r--r--kanotix/acritoxinstaller.desktop10
-rw-r--r--kanotix/icons/128x128/apps/acritoxinstaller.pngbin0 -> 21552 bytes
-rw-r--r--kanotix/icons/16x16/apps/acritoxinstaller.pngbin0 -> 1012 bytes
-rw-r--r--kanotix/icons/22x22/apps/acritoxinstaller.pngbin0 -> 1570 bytes
-rw-r--r--kanotix/icons/32x32/apps/acritoxinstaller.pngbin0 -> 2651 bytes
-rw-r--r--kanotix/icons/48x48/apps/acritoxinstaller.pngbin0 -> 4956 bytes
-rw-r--r--kanotix/icons/64x64/apps/acritoxinstaller.pngbin0 -> 7537 bytes
-rw-r--r--listdelegate.cpp77
-rw-r--r--listdelegate.h16
-rw-r--r--listitem.cpp10
-rw-r--r--listitem.h19
-rw-r--r--main.cpp10
-rw-r--r--mainwizard.cpp84
-rw-r--r--mainwizard.h39
-rw-r--r--qtermwidget/AUTHORS1
-rw-r--r--qtermwidget/BlockArray.cpp337
-rw-r--r--qtermwidget/BlockArray.h125
-rw-r--r--qtermwidget/CMakeLists.txt27
-rw-r--r--qtermwidget/COPYING340
-rw-r--r--qtermwidget/Changelog19
-rw-r--r--qtermwidget/Character.h210
-rw-r--r--qtermwidget/CharacterColor.h301
-rw-r--r--qtermwidget/ColorTables.h58
-rw-r--r--qtermwidget/DefaultTranslatorText.h2
-rw-r--r--qtermwidget/Emulation.cpp543
-rw-r--r--qtermwidget/Emulation.h465
-rw-r--r--qtermwidget/ExtendedDefaultTranslator.h74
-rw-r--r--qtermwidget/Filter.cpp562
-rw-r--r--qtermwidget/Filter.h383
-rw-r--r--qtermwidget/History.cpp698
-rw-r--r--qtermwidget/History.h344
-rw-r--r--qtermwidget/KeyboardTranslator.cpp903
-rw-r--r--qtermwidget/KeyboardTranslator.h657
-rw-r--r--qtermwidget/LineFont.h21
-rw-r--r--qtermwidget/LineFont.src786
-rw-r--r--qtermwidget/Pty.cpp320
-rw-r--r--qtermwidget/Pty.h243
-rw-r--r--qtermwidget/README21
-rw-r--r--qtermwidget/Screen.cpp1567
-rw-r--r--qtermwidget/Screen.h662
-rw-r--r--qtermwidget/ScreenWindow.cpp296
-rw-r--r--qtermwidget/ScreenWindow.h256
-rw-r--r--qtermwidget/Session.cpp1021
-rw-r--r--qtermwidget/Session.h621
-rw-r--r--qtermwidget/ShellCommand.cpp168
-rw-r--r--qtermwidget/ShellCommand.h94
-rw-r--r--qtermwidget/TerminalCharacterDecoder.cpp227
-rw-r--r--qtermwidget/TerminalCharacterDecoder.h139
-rw-r--r--qtermwidget/TerminalDisplay.cpp2724
-rw-r--r--qtermwidget/TerminalDisplay.h754
-rw-r--r--qtermwidget/Vt102Emulation.cpp1266
-rw-r--r--qtermwidget/Vt102Emulation.h192
-rw-r--r--qtermwidget/default.keytab128
-rw-r--r--qtermwidget/k3process.cpp1053
-rw-r--r--qtermwidget/k3process.h890
-rw-r--r--qtermwidget/k3processcontroller.cpp334
-rw-r--r--qtermwidget/k3processcontroller.h137
-rw-r--r--qtermwidget/kb-layouts/default.keytab133
-rw-r--r--qtermwidget/kb-layouts/linux.keytab133
-rw-r--r--qtermwidget/kb-layouts/vt420pc.keytab163
-rw-r--r--qtermwidget/konsole_wcwidth.cpp216
-rw-r--r--qtermwidget/konsole_wcwidth.h24
-rw-r--r--qtermwidget/kpty.cpp624
-rw-r--r--qtermwidget/kpty.h188
-rw-r--r--qtermwidget/kpty_p.h44
-rw-r--r--qtermwidget/lib.pro48
-rw-r--r--qtermwidget/qtermwidget.cpp222
-rw-r--r--qtermwidget/qtermwidget.h109
-rw-r--r--wizard/bootloader.cpp74
-rw-r--r--wizard/bootloader.h28
-rw-r--r--wizard/bootloader.ui123
-rw-r--r--wizard/installation.cpp81
-rw-r--r--wizard/installation.h29
-rw-r--r--wizard/installation.ui47
-rw-r--r--wizard/network.cpp40
-rw-r--r--wizard/network.h25
-rw-r--r--wizard/network.ui108
-rw-r--r--wizard/partitions.cpp63
-rw-r--r--wizard/partitions.h27
-rw-r--r--wizard/partitions.ui27
-rw-r--r--wizard/partman.cpp55
-rw-r--r--wizard/partman.h27
-rw-r--r--wizard/partman.ui55
-rw-r--r--wizard/partmansel.cpp93
-rw-r--r--wizard/partmansel.h27
-rw-r--r--wizard/partmansel.ui123
-rw-r--r--wizard/rootpartition.cpp58
-rw-r--r--wizard/rootpartition.h27
-rw-r--r--wizard/rootpartition.ui152
-rw-r--r--wizard/rootpwd.cpp69
-rw-r--r--wizard/rootpwd.h26
-rw-r--r--wizard/rootpwd.ui178
-rw-r--r--wizard/summary.cpp66
-rw-r--r--wizard/summary.h28
-rw-r--r--wizard/summary.ui27
-rw-r--r--wizard/usercfg.cpp52
-rw-r--r--wizard/usercfg.h27
-rw-r--r--wizard/usercfg.ui143
-rw-r--r--wizard/userpwd.cpp69
-rw-r--r--wizard/userpwd.h26
-rw-r--r--wizard/userpwd.ui237
-rw-r--r--wizard/welcome.cpp35
-rw-r--r--wizard/welcome.h26
-rw-r--r--wizard/welcome.ui22
138 files changed, 27191 insertions, 0 deletions
diff --git a/.kdev4/acritoxinstaller-0.2.kdev4 b/.kdev4/acritoxinstaller-0.2.kdev4
new file mode 100644
index 0000000..395a33e
--- /dev/null
+++ b/.kdev4/acritoxinstaller-0.2.kdev4
@@ -0,0 +1,29 @@
+[CMake]
+BuildDirs=/home/aloibl/projects/acritoxinstaller-0.2/build
+CMakeDir=/usr/share/cmake-2.8/Modules
+Current CMake Binary=file:///usr/bin/cmake
+CurrentBuildDir=file:///home/aloibl/projects/acritoxinstaller-0.2/build
+CurrentBuildType=Debug
+CurrentInstallDir=file:///home/aloibl/build
+ProjectRootRelative=./
+
+[Launch]
+Launch Configurations=Launch Configuration 0
+
+[Launch][Launch Configuration 0]
+Configured Launch Modes=execute
+Configured Launchers=nativeAppLauncher
+Name=New Native Application Configuration
+Type=Native Application
+
+[Launch][Launch Configuration 0][Data]
+Arguments=
+Dependencies=@Variant(\x00\x00\x00\t\x00\x00\x00\x00\x00)
+Dependency Action=Nothing
+EnvironmentGroup=default
+Project Target=acritoxinstaller,acritoxinstaller
+Working Directory=
+isExecutable=false
+
+[MakeBuilder]
+Number Of Jobs=1
diff --git a/AUTHORS b/AUTHORS
new file mode 100644
index 0000000..ed7f929
--- /dev/null
+++ b/AUTHORS
@@ -0,0 +1 @@
+Andreas Loibl <andreas@andreas-loibl.de>
diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100644
index 0000000..8fadd73
--- /dev/null
+++ b/CMakeLists.txt
@@ -0,0 +1,43 @@
+project(acritoxinstaller)
+cmake_minimum_required(VERSION 2.6)
+find_package(Qt4 REQUIRED)
+
+include(${QT_USE_FILE})
+add_definitions(${QT_DEFINITIONS})
+
+if(${CMAKE_BUILD_TYPE} MATCHES "Release")
+ add_definitions(-DNDEBUG)
+ add_definitions(-DQT_NO_DEBUG_OUTPUT)
+endif(${CMAKE_BUILD_TYPE} MATCHES "Release")
+
+set(LIB_SUFFIX "" CACHE STRING "Define suffix of directory name (32/64)")
+set(EXEC_INSTALL_PREFIX ${CMAKE_INSTALL_PREFIX} CACHE PATH "Installation prefix for executables and object code libraries" FORCE)
+set(BIN_INSTALL_DIR ${EXEC_INSTALL_PREFIX}/bin CACHE PATH "Installation prefix for user executables" FORCE)
+set(LIB_INSTALL_DIR ${EXEC_INSTALL_PREFIX}/lib${LIB_SUFFIX} CACHE PATH "Installation prefix for object code libraries" FORCE)
+set(DATA_INSTALL_DIR ${EXEC_INSTALL_PREFIX}/share/acritoxinstaller CACHE PATH "Installation prefix where shared data will be installed" FORCE)
+set(INCLUDE_INSTALL_DIR ${CMAKE_INSTALL_PREFIX}/include CACHE PATH "Installation prefix for C header files" FORCE)
+
+set(BACKEND_DIR ${DATA_INSTALL_DIR})
+configure_file(${CMAKE_CURRENT_SOURCE_DIR}/backend/backend.sh.in ${CMAKE_CURRENT_BINARY_DIR}/backend.sh @ONLY)
+install(PROGRAMS ${CMAKE_CURRENT_BINARY_DIR}/backend.sh DESTINATION "${BACKEND_DIR}")
+install(DIRECTORY backend/modules DESTINATION "${DATA_INSTALL_DIR}")
+
+add_subdirectory( qtermwidget )
+
+file(GLOB wizard_SRCS wizard/[a-z]*.cpp)
+file(GLOB wizard_HDRS wizard/[a-z]*.h)
+file(GLOB wizard_UIS wizard/[a-z]*.ui)
+
+configure_file(config.h.in ${CMAKE_CURRENT_BINARY_DIR}/config.h)
+set(acritoxinstaller_SRCS mainwizard.cpp main.cpp backend.cpp listitem.cpp listdelegate.cpp busyappfilter.cpp ${wizard_SRCS})
+set(acritoxinstaller_MOC_HDRS mainwizard.h backend.h listdelegate.h ${wizard_HDRS})
+set(acritoxinstaller_UIS ${wizard_UIS})
+
+qt4_wrap_cpp(acritoxinstaller_MOC_SRCS ${acritoxinstaller_MOC_HDRS})
+qt4_wrap_ui(acritoxinstaller_UIS_H ${acritoxinstaller_UIS})
+include_directories(${CMAKE_CURRENT_BINARY_DIR})
+add_executable(acritoxinstaller ${acritoxinstaller_SRCS} ${acritoxinstaller_MOC_SRCS} ${acritoxinstaller_UIS_H})
+target_link_libraries(acritoxinstaller ${QT_LIBRARIES} qtermwidget crypt)
+
+install(TARGETS acritoxinstaller RUNTIME DESTINATION "${BIN_INSTALL_DIR}" LIBRARY DESTINATION "${LIB_INSTALL_DIR}")
+
diff --git a/COPYING b/COPYING
new file mode 100644
index 0000000..94a9ed0
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,674 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+ The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works. By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users. We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors. 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
+them 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 prevent others from denying you
+these rights or asking you to surrender the rights. Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received. 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.
+
+ Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+ For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software. For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+ Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so. This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software. The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable. Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products. If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+ Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary. To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ TERMS AND CONDITIONS
+
+ 0. Definitions.
+
+ "This License" refers to version 3 of the GNU General Public License.
+
+ "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+ "The Program" refers to any copyrightable work licensed under this
+License. Each licensee is addressed as "you". "Licensees" and
+"recipients" may be individuals or organizations.
+
+ To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy. The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+ A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+ To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy. Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+ To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies. Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+ An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License. If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+ 1. Source Code.
+
+ The "source code" for a work means the preferred form of the work
+for making modifications to it. "Object code" means any non-source
+form of a work.
+
+ A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+ The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form. A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+ The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities. However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work. For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+ The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+ The Corresponding Source for a work in source code form is that
+same work.
+
+ 2. Basic Permissions.
+
+ All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met. This License explicitly affirms your unlimited
+permission to run the unmodified Program. The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work. This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+ You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force. You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright. Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+ Conveying under any other circumstances is permitted solely under
+the conditions stated below. Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+ No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+ When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+ 4. Conveying Verbatim Copies.
+
+ You may convey 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;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+ You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+ 5. Conveying Modified Source Versions.
+
+ You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+ a) The work must carry prominent notices stating that you modified
+ it, and giving a relevant date.
+
+ b) The work must carry prominent notices stating that it is
+ released under this License and any conditions added under section
+ 7. This requirement modifies the requirement in section 4 to
+ "keep intact all notices".
+
+ c) You must license the entire work, as a whole, under this
+ License to anyone who comes into possession of a copy. This
+ License will therefore apply, along with any applicable section 7
+ additional terms, to the whole of the work, and all its parts,
+ regardless of how they are packaged. This License gives no
+ permission to license the work in any other way, but it does not
+ invalidate such permission if you have separately received it.
+
+ d) If the work has interactive user interfaces, each must display
+ Appropriate Legal Notices; however, if the Program has interactive
+ interfaces that do not display Appropriate Legal Notices, your
+ work need not make them do so.
+
+ A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit. Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+ 6. Conveying Non-Source Forms.
+
+ You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+ a) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by the
+ Corresponding Source fixed on a durable physical medium
+ customarily used for software interchange.
+
+ b) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by a
+ written offer, valid for at least three years and valid for as
+ long as you offer spare parts or customer support for that product
+ model, to give anyone who possesses the object code either (1) a
+ copy of the Corresponding Source for all the software in the
+ product that is covered by this License, on a durable physical
+ medium customarily used for software interchange, for a price no
+ more than your reasonable cost of physically performing this
+ conveying of source, or (2) access to copy the
+ Corresponding Source from a network server at no charge.
+
+ c) Convey individual copies of the object code with a copy of the
+ written offer to provide the Corresponding Source. This
+ alternative is allowed only occasionally and noncommercially, and
+ only if you received the object code with such an offer, in accord
+ with subsection 6b.
+
+ d) Convey the object code by offering access from a designated
+ place (gratis or for a charge), and offer equivalent access to the
+ Corresponding Source in the same way through the same place at no
+ further charge. You need not require recipients to copy the
+ Corresponding Source along with the object code. If the place to
+ copy the object code is a network server, the Corresponding Source
+ may be on a different server (operated by you or a third party)
+ that supports equivalent copying facilities, provided you maintain
+ clear directions next to the object code saying where to find the
+ Corresponding Source. Regardless of what server hosts the
+ Corresponding Source, you remain obligated to ensure that it is
+ available for as long as needed to satisfy these requirements.
+
+ e) Convey the object code using peer-to-peer transmission, provided
+ you inform other peers where the object code and Corresponding
+ Source of the work are being offered to the general public at no
+ charge under subsection 6d.
+
+ A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+ A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling. In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage. For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product. A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+ "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source. The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+ If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information. But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+ The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed. Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+ Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+ 7. Additional Terms.
+
+ "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law. If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+ When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it. (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.) You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+ Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+ a) Disclaiming warranty or limiting liability differently from the
+ terms of sections 15 and 16 of this License; or
+
+ b) Requiring preservation of specified reasonable legal notices or
+ author attributions in that material or in the Appropriate Legal
+ Notices displayed by works containing it; or
+
+ c) Prohibiting misrepresentation of the origin of that material, or
+ requiring that modified versions of such material be marked in
+ reasonable ways as different from the original version; or
+
+ d) Limiting the use for publicity purposes of names of licensors or
+ authors of the material; or
+
+ e) Declining to grant rights under trademark law for use of some
+ trade names, trademarks, or service marks; or
+
+ f) Requiring indemnification of licensors and authors of that
+ material by anyone who conveys the material (or modified versions of
+ it) with contractual assumptions of liability to the recipient, for
+ any liability that these contractual assumptions directly impose on
+ those licensors and authors.
+
+ All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10. If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term. If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+ If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+ Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+ 8. Termination.
+
+ You may not propagate or modify a covered work except as expressly
+provided under this License. Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+ However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+ Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+ Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License. If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+ 9. Acceptance Not Required for Having Copies.
+
+ You are not required to accept this License in order to receive or
+run a copy of the Program. Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance. However,
+nothing other than this License grants you permission to propagate or
+modify any covered work. These actions infringe copyright if you do
+not accept this License. Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+ 10. Automatic Licensing of Downstream Recipients.
+
+ Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License. You are not responsible
+for enforcing compliance by third parties with this License.
+
+ An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations. If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+ You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License. For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+ 11. Patents.
+
+ A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based. The
+work thus licensed is called the contributor's "contributor version".
+
+ A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version. For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+ Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+ In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement). To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+ If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients. "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+ If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+ A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License. You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+ Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+ 12. No Surrender of Others' Freedom.
+
+ If 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 convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all. For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+ 13. Use with the GNU Affero General Public License.
+
+ Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work. The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+ 14. Revised Versions of this License.
+
+ The Free Software Foundation may publish revised and/or new versions of
+the GNU 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 that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation. If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+ If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+ Later license versions may give you additional or different
+permissions. However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+ 15. Disclaimer of Warranty.
+
+ 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.
+
+ 16. Limitation of Liability.
+
+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+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.
+
+ 17. Interpretation of Sections 15 and 16.
+
+ If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+ 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
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ 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 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+Also add information on how to contact you by electronic and paper mail.
+
+ If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+ <program> Copyright (C) <year> <name of author>
+ This program 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, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+ You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+<http://www.gnu.org/licenses/>.
+
+ The GNU 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. But first, please read
+<http://www.gnu.org/philosophy/why-not-lgpl.html>.
diff --git a/INSTALL b/INSTALL
new file mode 100644
index 0000000..211b5a2
--- /dev/null
+++ b/INSTALL
@@ -0,0 +1,20 @@
+Basic Installation
+==================
+
+ AcritoxInstaller uses the cmake build system. The simple instructions for
+building acritoxinstaller are these commands:
+
+ mkdir ../acritoxinstaller-build && cd ../acritoxinstaller-build
+ cmake ../acritoxinstaller
+ make
+ make install
+
+ Some components of acritoxinstaller may have dependencies. If these
+are not met when the 'cmake' command is run, those components will not be
+built. The output of 'cmake' should make it clear what these dependencies are
+and how to meet them.
+
+ Arguments can be given to cmake similar to those given to a traditional
+configure script, like the install prefix. See 'cmake --help-full' for more
+information. For example, 'configure --prefix=/usr/lib' translates to
+'cmake -DCMAKE_INSTALL_PREFIX=/usr/lib'.
diff --git a/acritoxinstaller-0.2.kdev4 b/acritoxinstaller-0.2.kdev4
new file mode 100644
index 0000000..20b1a36
--- /dev/null
+++ b/acritoxinstaller-0.2.kdev4
@@ -0,0 +1,3 @@
+[Project]
+Manager=KDevCMakeManager
+Name=acritoxinstaller
diff --git a/backend.cpp b/backend.cpp
new file mode 100644
index 0000000..7580d19
--- /dev/null
+++ b/backend.cpp
@@ -0,0 +1,250 @@
+#include "backend.h"
+#include <QDebug>
+#include <QTime>
+#include <stdlib.h>
+#include "config.h"
+
+Backend* Backend::_instance;
+
+Backend::Backend()
+{
+ qDebug("Backend::Backend() called!");
+ _process = new QProcess();
+
+ QTime time = QTime::currentTime();
+ srand((uint)time.msec());
+ connect(_process, SIGNAL(finished(int)), this, SLOT(slotProcessExited()));
+ connect(_process, SIGNAL(readyReadStandardOutput()), this, SLOT(slotReceiveOutput()));
+}
+
+Backend::~Backend()
+{
+}
+
+void Backend::runBackend()
+{
+ QStringList command = QStringList() << BACKEND_PATH << "-ni";
+ _process->start(command.join(" "));
+ busy = true; emit isBusy(true);
+}
+
+bool Backend::isBusy()
+{
+ return (busy && commandQueue.isEmpty());
+}
+
+bool Backend::flag(QString flag)
+{
+ return flags.contains(flag);
+}
+
+void Backend::_flag(QString flag, bool set)
+{
+ if(set)
+ {
+ if(!flags.contains(flag))
+ flags.append(flag);
+ }
+ else
+ {
+ if(flags.contains(flag))
+ flags.remove(flags.indexOf(flag));
+ }
+}
+
+void Backend::flag(QString flag, bool set)
+{
+ if(set)
+ {
+ exec("flag_set '"+flag.replace("'","\\'")+"'");
+ _flag(flag, true);
+ }
+ else
+ {
+ exec("flag_unset '"+flag.replace("'","\\'")+"'");
+ _flag(flag, false);
+ }
+}
+
+QString Backend::cfg(QString var)
+{
+ return cfgMap.value(var);
+}
+
+void Backend::_cfg(QString var, QString value)
+{
+ cfgMap[var] = value;
+}
+
+void Backend::cfg(QString var, QString value)
+{
+ exec("cfg_set '"+var.replace("'","\\'")+"' '"+value.replace("'","\\'")+"'");
+ _cfg(var, value);
+}
+
+void Backend::exec(QString command)
+{
+ qDebug("Backend::exec(\""+command.toAscii()+"\") called!");
+ commandQueue.enqueue(command);
+ dequeue();
+}
+
+void Backend::dequeue()
+{
+ if(!busy && commandQueue.isEmpty()) emit isBusy(false);
+ if(busy) return;
+ if(commandQueue.isEmpty()) return;
+ busy = true; emit isBusy(true);
+ QString command = commandQueue.dequeue();
+ currentCommand = command;
+ data.clear();
+ _process->write(command.append('\004').toLocal8Bit());
+}
+
+void Backend::exitBackend()
+{
+ qDebug("Backend::exitBackend() called!");
+ _process->kill();
+ _process->waitForFinished();
+}
+
+void Backend::processOutput(QString line)
+{
+ qDebug("Backend: input: "+line.toAscii());
+ if(line.startsWith("<acritoxinstaller "))
+ {
+ QString command = line.section(" ",1,1).section(">",0,0);
+ QString args = line.section(" ",2).trimmed();
+ if(command == "prompt")
+ {
+ busy = false;
+ qDebug("Backend: finished: "+currentCommand.toAscii());
+ emit finishedCommand(currentCommand);
+ dequeue();
+ }
+ else if(command == "flag")
+ {
+ QString setunset = args.section(" ",0,0);
+ QString flag = args.section(" ",1).trimmed();
+ if(setunset == "set")
+ _flag(flag, true);
+ else if(setunset == "unset")
+ _flag(flag, false);
+ }
+ else if(command == "data")
+ {
+ currentData = args.trimmed();
+ data[currentData] = "";
+ }
+ else if(command == "cfg")
+ {
+ _cfg(args.trimmed(), data["value"].trimmed());
+ }
+ else if(command == "progress")
+ {
+ emit receivedProgress(args.toInt());
+ }
+ else
+ emit receivedCommand(command, args);
+ }
+ else
+ {
+ data[currentData].append(line);
+ emit receivedDataLine(currentData, line.trimmed());
+ qDebug("Backend: data: "+line.toAscii());
+ }
+}
+
+QString Backend::encryptPassword(QString password)
+{
+ unsigned long seed[2];
+ char salt[] = "$1$........";
+ char *pwd = password.toAscii().data();
+ const char *const seedchars =
+ "./0123456789ABCDEFGHIJKLMNOPQRST"
+ "UVWXYZabcdefghijklmnopqrstuvwxyz";
+ int i;
+
+ seed[0] = rand(); seed[1] = rand();
+
+ for (i = 0; i < 8; i++)
+ salt[3+i] = seedchars[(seed[i/5] >> (i%5)*6) & 0x3f];
+
+ return(QString(crypt(pwd, salt)));
+}
+
+// function adapted from amarok 2.0 / App.cpp - (C) 2002 by Mark Kretschmann <markey@web.de>
+QString Backend::cleanUsername(const QString &username)
+{
+ QString result = username;
+
+ // german umlauts
+ result.replace( QChar(0x00e4), "ae" ).replace( QChar(0x00c4), "Ae" );
+ result.replace( QChar(0x00f6), "oe" ).replace( QChar(0x00d6), "Oe" );
+ result.replace( QChar(0x00fc), "ue" ).replace( QChar(0x00dc), "Ue" );
+ result.replace( QChar(0x00df), "ss" );
+
+ // some strange accents
+ result.replace( QChar(0x00e7), "c" ).replace( QChar(0x00c7), "C" );
+ result.replace( QChar(0x00fd), "y" ).replace( QChar(0x00dd), "Y" );
+ result.replace( QChar(0x00f1), "n" ).replace( QChar(0x00d1), "N" );
+
+ // czech letters with carons
+ result.replace( QChar(0x0161), "s" ).replace( QChar(0x0160), "S" );
+ result.replace( QChar(0x010d), "c" ).replace( QChar(0x010c), "C" );
+ result.replace( QChar(0x0159), "r" ).replace( QChar(0x0158), "R" );
+ result.replace( QChar(0x017e), "z" ).replace( QChar(0x017d), "Z" );
+ result.replace( QChar(0x0165), "t" ).replace( QChar(0x0164), "T" );
+ result.replace( QChar(0x0148), "n" ).replace( QChar(0x0147), "N" );
+ result.replace( QChar(0x010f), "d" ).replace( QChar(0x010e), "D" );
+
+ // accented vowels
+ QChar a[] = { 'a', 0xe0,0xe1,0xe2,0xe3,0xe5, 0 };
+ QChar A[] = { 'A', 0xc0,0xc1,0xc2,0xc3,0xc5, 0 };
+ QChar E[] = { 'e', 0xe8,0xe9,0xea,0xeb,0x11a, 0 };
+ QChar e[] = { 'E', 0xc8,0xc9,0xca,0xcb,0x11b, 0 };
+ QChar i[] = { 'i', 0xec,0xed,0xee,0xef, 0 };
+ QChar I[] = { 'I', 0xcc,0xcd,0xce,0xcf, 0 };
+ QChar o[] = { 'o', 0xf2,0xf3,0xf4,0xf5,0xf8, 0 };
+ QChar O[] = { 'O', 0xd2,0xd3,0xd4,0xd5,0xd8, 0 };
+ QChar u[] = { 'u', 0xf9,0xfa,0xfb,0x16e, 0 };
+ QChar U[] = { 'U', 0xd9,0xda,0xdb,0x16f, 0 };
+ QChar nul[] = { 0 };
+ QChar *replacements[] = { a, A, e, E, i, I, o, O, u, U, nul };
+
+ for( int i = 0; i < result.length(); i++ )
+ {
+ QChar c = result[ i ];
+ for( uint n = 0; replacements[n][0] != QChar(0); n++ )
+ {
+ for( uint k=0; replacements[n][k] != QChar(0); k++ )
+ {
+ if( replacements[n][k] == c )
+ {
+ c = replacements[n][0];
+ }
+ }
+ }
+ result[ i ] = c;
+ }
+
+ return result.replace(QRegExp("[^a-zA-Z0-9-_.]"), "");
+}
+
+void Backend::slotProcessExited()
+{
+ emit processExited();
+}
+
+void Backend::slotReceiveOutput()
+{
+ _process->setReadChannel(QProcess::StandardOutput);
+ char data[2048]; QString line;
+ while(_process->canReadLine())
+ {
+ int result = _process->readLine(data, sizeof(data));
+ if(result == -1) break;
+ line = QString::fromLocal8Bit(data);
+ processOutput(line);
+ }
+}
diff --git a/backend.h b/backend.h
new file mode 100644
index 0000000..997b580
--- /dev/null
+++ b/backend.h
@@ -0,0 +1,59 @@
+#include <QProcess>
+#include <QQueue>
+#include <QMap>
+#include <QVector>
+
+#ifndef BACKEND_H
+#define BACKEND_H
+
+class Backend : public QObject
+{
+ Q_OBJECT
+
+public:
+ static Backend* instance() {if(!_instance) _instance = new Backend(); return(_instance);}
+ ~Backend();
+ void init();
+ void runBackend();
+ void exec(QString command);
+ void exitBackend();
+ QString encryptPassword(QString password);
+ QString cleanUsername(const QString &username);
+ bool isBusy();
+ bool flag(QString flag);
+ void flag(QString flag, bool set);
+ QString cfg(QString var);
+ void cfg(QString var, QString value);
+
+protected:
+ QProcess* _process;
+
+private slots:
+ void slotProcessExited();
+ void slotReceiveOutput();
+
+private:
+ static Backend *_instance;
+ Backend();
+ void processOutput(QString line);
+ void _flag(QString flag, bool set);
+ void _cfg(QString var, QString value);
+ void dequeue();
+ QMap<QString,QString> data;
+ QString currentData;
+ QMap<QString,QString> cfgMap;
+ QVector<QString> flags;
+ QQueue<QString> commandQueue;
+ QString currentCommand;
+ bool busy;
+
+signals:
+ void processExited();
+ void receivedDataLine(QString data, QString line);
+ void receivedProgress(int percent);
+ void receivedCommand(QString command, QString args);
+ void finishedCommand(QString command);
+ void isBusy(bool busy);
+};
+
+#endif
diff --git a/backend/backend.sh.in b/backend/backend.sh.in
new file mode 100755
index 0000000..4c0c161
--- /dev/null
+++ b/backend/backend.sh.in
@@ -0,0 +1,96 @@
+#!/bin/bash
+
+cd "$(dirname "$0")"
+SEARCHPATH="@DATA_INSTALL_DIR@"
+[ ! -e "$SEARCHPATH" -a -e ./modules ] && SEARCHPATH="."
+
+for i in $(find $SEARCHPATH/modules/ -type f | sort); do
+ source $i
+done
+
+function print_usage()
+{
+cat <<EOF >&2
+$(basename $0):
+
+ -ni
+ NonInteractive mode, no prompt is shown and INSTALLER_OUTPUT is set to 1
+ -e command
+ Run "command"
+
+EOF
+exit 0
+}
+
+function set_debug()
+{
+ case $1 in
+ on)
+ PS4="<acritoxinstaller debug> "
+ set -x
+ ;;
+ off)
+ set +x
+ PS4="+"
+ ;;
+ esac
+}
+
+if ((UID)); then
+ p="$PWD/$(basename "$0")"
+ if [ -x /usr/bin/sudo ]; then
+ if /usr/bin/sudo -n -l "$p" &>/dev/null; then
+ /usr/bin/sudo "$p" "$@"
+ exit $?
+ fi
+ fi
+ if [ -x /usr/lib/kde4/libexec/kdesu ]; then
+ /usr/lib/kde4/libexec/kdesu --noignorebutton -d -- "$p" --pid $$ "$@" &>/dev/null
+ exit $?
+ fi
+ if [ -z "$as_root" ]; then
+ for as_root in /usr/bin/kdesu /usr/bin/gksu exec
+ do
+ [ -x $as_root ] && break
+ done
+ fi
+ $as_root "$p" --pid $$ "$@" &>/dev/null
+ exit $?
+fi
+
+while [ "$1" ]
+do
+ case $1 in
+ "-ni")
+ export INSTALLER_OUTPUT=1
+ ;;
+ "-e")
+ shift
+ eval "$@"
+ exit $?
+ ;;
+ "--pid")
+ exec 0<"/proc/$2/fd/0" 1>"/proc/$2/fd/1" 2>"/proc/$2/fd/2"
+ shift
+ ;;
+ "-h")
+ print_usage
+ ;;
+ esac
+ shift
+done
+
+EOT="$(echo -e '\004')"
+while true
+do
+ if ((INSTALLER_OUTPUT)); then
+ echo "<acritoxinstaller prompt>"
+ IFS=" " read -d "$EOT" -r command params
+ else
+ IFS=" " read -er -p "installer> " command params
+ fi
+ declare -F ${command}_pre &>/dev/null && eval "${command}_pre"
+ eval "$command $params"
+ declare -F ${command}_post &>/dev/null && eval "${command}_post"
+done
+
diff --git a/backend/modules/bootloader b/backend/modules/bootloader
new file mode 100644
index 0000000..a3f7546
--- /dev/null
+++ b/backend/modules/bootloader
@@ -0,0 +1,158 @@
+#!/bin/bash
+
+# Synopsis: list_bootloader_targets
+#
+# This function lists all disks and the root-partition if it is suitable to install a bootloader on it.
+# Output example:
+# /dev/sda:MBR:250059350016
+# /dev/sdb:MBR:400088457216
+# /dev/sdb1:Rootpartition:60003385344
+function list_bootloader_targets()
+{
+ for disk in $(list_all_disks)
+ do
+ echo "$disk - MBR $(blockdev --getsize64 $disk)"
+ done
+ root_dev="$(hdmap_get device of mountpoint /)"
+ root_fs="$(hdmap_get filesystem of mountpoint /)"
+ [ -z "$root_fs" ] && root_fs=$(get_filesystem "$root_dev")
+ case $root_fs in
+ reiserfs|ext2|ext3|ext4)
+ list_linux_partitions | grep -q "^$root_dev$" &&
+ echo "$root_dev - Rootpartition $(blockdev --getsize64 $root_dev)"
+ ;;
+ esac
+}
+
+function send_bootloader_targets()
+{
+ send data bootloader_targets
+ list_bootloader_targets
+}
+
+# Synopsis: list_bootloaders
+#
+# This function lists all available bootloaders
+# Output example:
+# BURG - Brand-new Universal loadeR from GRUB
+# GRUB - GRand Unified Bootloader
+function list_bootloaders()
+{
+ for bl in burg grub
+ do
+ [ -x /usr/sbin/$bl-setup ] || continue
+ case $bl in
+ burg) echo "BURG - Brand-new Universal loadeR from GRUB";;
+ grub) echo "GRUB - GRand Unified Bootloader";;
+ esac
+ done
+}
+
+function send_bootloaders()
+{
+ send data bootloaders
+ list_bootloaders
+}
+
+function install_bootmanager_to_target()
+{
+ send install_step install_bootmanager_to_target
+ # force initrd update
+ [ -d $TARGET/var/lib/initramfs-tools ] && rm -f $TARGET/var/lib/initramfs-tools/*
+ chroot_it update-initramfs -utk all &>/dev/null
+
+ case "$cfg_bootloader" in
+ grub)
+ install_grub
+ ;;
+ burg)
+ install_burg
+ ;;
+ esac
+}
+
+function install_grub()
+{
+ rm -f $TARGET/boot/vmlinuz $TARGET/boot/System.map $TARGET/boot/initrd.img
+
+ # install grub
+ mkdir -p "$TARGET/boot/grub"
+ grub-install --recheck --no-floppy --root-directory=$TARGET "$cfg_bootloader_target" &>/dev/null || \
+ grub-install --force --recheck --no-floppy --root-directory=$TARGET "$cfg_bootloader_target" &>/dev/null
+
+# # create device.map and save it to target
+# export device_map=$TARGET/tmp/device.map
+# get_device_map > $device_map
+# cat $device_map > $TARGET/boot/grub/device.map
+# rm -f $device_map
+
+ # preseed grub-pc with install-target
+ for path in /dev/disk/by-id/*
+ do
+ [ -e "$path" ] || continue
+ if [ "$(readlink -f "$path")" = "$(readlink -f "$cfg_bootloader_target")" ]; then
+ echo "grub-pc grub-pc/install_devices multiselect $path" | chroot_it debconf-set-selections &>/dev/null
+ break
+ fi
+ done
+
+ write_kernel_img_conf
+
+ # update grub
+ chroot_it update-grub &>/dev/null
+ DEBIAN_FRONTEND=noninteractive chroot_it dpkg-reconfigure grub-pc &>/dev/null
+
+ return 0
+}
+
+function install_burg()
+{
+ rm -f $TARGET/boot/vmlinuz $TARGET/boot/System.map $TARGET/boot/initrd.img
+
+ # install burg
+ mkdir -p "$TARGET/boot/burg"
+ burg-install --recheck --no-floppy --root-directory=$TARGET "$cfg_bootloader_target" &>/dev/null || \
+ burg-install --force --recheck --no-floppy --root-directory=$TARGET "$cfg_bootloader_target" &>/dev/null
+
+# # create device.map and save it to target
+# export device_map=$TARGET/tmp/device.map
+# get_device_map > $device_map
+# cat $device_map > $TARGET/boot/burg/device.map
+# rm -f $device_map
+
+ # preseed burg-pc with install-target
+ for path in /dev/disk/by-id/*
+ do
+ [ -e "$path" ] || continue
+ if [ "$(readlink -f "$path")" = "$(readlink -f "$cfg_bootloader_target")" ]; then
+ echo "burg-pc burg-pc/install_devices multiselect $path" | chroot_it debconf-set-selections &>/dev/null
+ break
+ fi
+ done
+
+ write_kernel_img_conf
+
+ # update burg
+ chroot_it update-burg &>/dev/null
+ DEBIAN_FRONTEND=noninteractive chroot_it dpkg-reconfigure burg-pc &>/dev/null
+
+ return 0
+}
+
+function write_kernel_img_conf()
+{
+rm -f $TARGET/etc/kernel-img.conf
+cat << EOT > $TARGET/etc/kernel-img.conf
+# Kernel image management overrides
+# See kernel-img.conf(5) for details
+do_symlinks = yes
+relative_links = yes
+do_bootloader = no
+do_bootfloppy = no
+do_initrd = yes
+link_in_boot = no
+postinst_hook = update-$cfg_bootloader
+postrm_hook = update-$cfg_bootloader
+EOT
+}
+
diff --git a/backend/modules/cleanup b/backend/modules/cleanup
new file mode 100644
index 0000000..fe604a0
--- /dev/null
+++ b/backend/modules/cleanup
@@ -0,0 +1,15 @@
+#!/bin/bash
+
+function cleanup()
+{
+
+ cd /
+ umount "$TARGET/proc" &>/dev/null
+ umount "$TARGET/dev" &>/dev/null
+ umount "$TARGET/sys" &>/dev/null
+ umount_all_affected "$(hdmap_get device of mountpoint /)" &>/dev/null
+ umount "$TARGET" &>/dev/null
+ umount "/live/filesystem" &>/dev/null
+ rmdir "$TARGET" "/live/filesystem" &>/dev/null
+}
+
diff --git a/backend/modules/common b/backend/modules/common
new file mode 100644
index 0000000..5c763f3
--- /dev/null
+++ b/backend/modules/common
@@ -0,0 +1,13 @@
+#!/bin/bash
+
+# Synopsis: dereferce_links_in_list
+#
+# This function reads a list from STDIN, dereferences the links and prints it to STDOUT
+function dereferce_links_in_list()
+{
+ for lnk in $(cat)
+ do
+ readlink -m $lnk
+ done
+}
+
diff --git a/backend/modules/config b/backend/modules/config
new file mode 100644
index 0000000..d073580
--- /dev/null
+++ b/backend/modules/config
@@ -0,0 +1,68 @@
+#!/bin/bash
+
+declare -A cfg flags
+
+function cfg_get()
+{
+ while [ "$1" ];
+ do
+ send data value
+ echo "${cfg["$1"]}"
+ send cfg "$1"
+ shift
+ done
+}
+
+function cfg_set()
+{
+ var="$1"
+ shift
+ cfg["$var"]="$@"
+ cfg_get "$var" # inform the frontend about the change
+ var="$(tr '[:upper:]' '[:lower:]' <<<"$var" | tr -d '\n' | tr -c '[:alnum:]' _)"
+ export cfg_${var}="$@"
+}
+
+function flag()
+{
+ [ "${flags["$1"]}" ] && return 0 || return 1
+}
+
+function flag_set()
+{
+ send flag set "$1" # inform the frontend about the change
+ flags["$1"]=1
+}
+
+function flag_unset()
+{
+ send flag unset "$1" # inform the frontend about the change
+ unset flags["$1"]
+}
+
+function hdmap_set()
+{
+ cfg_set hdmap "$( ( echo "$@"; ( echo "$@"; echo "$cfg_hdmap" ) | sort -u -t: -k1,1 ) | sort -u -t: -k2,2 | grep .)"
+}
+
+# Synopsis: hdmap_get <"device"|"mountpoint"|"filesystem"|"automount"> of <"device"|"mountpoint"> <device|mountpoint>
+#
+# This function returns the given value from the hdmap table
+function hdmap_get()
+{
+ [ "$2" == "of" ] || return 1
+ case "$1" in
+ device) col=1;;
+ mountpoint) col=2;;
+ filesystem) col=3;;
+ automount) col=4;;
+ *) return 1;;
+ esac
+ case "$3" in
+ device) of_col=1;;
+ mountpoint) of_col=2;;
+ *) return 1;;
+ esac
+ gawk "BEGIN{FS=\":\"}{if(\$$of_col==\"$4\"){print \$$col}}" <<<"$cfg_hdmap"
+}
+
diff --git a/backend/modules/frontend b/backend/modules/frontend
new file mode 100644
index 0000000..ffa2d28
--- /dev/null
+++ b/backend/modules/frontend
@@ -0,0 +1,21 @@
+#!/bin/bash
+
+function send()
+{
+ command="$1"
+ shift
+ echo "<acritoxinstaller $command> $@"
+}
+
+function send_error()
+{
+ if [ "$#" -gt 1 ]; then
+ send error "$@" >&2
+ elif [ "$#" -eq 1 ]; then
+ send error "$1" "$(tr -d '\n')" >&2
+ else
+ send error 255 unknown >&2
+ fi
+ return 1
+}
+
diff --git a/backend/modules/hdmap b/backend/modules/hdmap
new file mode 100644
index 0000000..d57d105
--- /dev/null
+++ b/backend/modules/hdmap
@@ -0,0 +1,340 @@
+#!/bin/bash
+
+# Synopsis: mointpoint_demands
+#
+# This function dumps the demands of various mountpoints
+function mointpoint_demands()
+{
+cat <<"EOT"
+/: Type:Linux
+/boot: LVM:no Type:Linux
+/bin: Type:Linux
+/etc: Type:Linux
+/home: Type:Linux
+/lib: Type:Linux
+/opt: Type:Linux
+/root: Type:Linux
+/sbin: Type:Linux
+/tmp: Type:Linux
+/usr: Type:Linux
+/var: Type:Linux
+EOT
+}
+
+# FIXME TODO OLD OUTDATED
+function emit_error()
+{
+send debug "$@"
+cat
+return 1
+}
+# FIXME TODO OLD OUTDATED
+function emit_progress()
+{
+ percent=$1
+ [ "$percent" -gt 100 ] && percent=100
+ send progress "$percent"
+}
+
+
+# Synopsis: is_disk /dev/xyz
+#
+# This function returns 0 if the supplied argument is a disk.
+function is_disk()
+{
+ stat --format "%t %G" "$1" | grep -qe "^3 " -e " disk$" && return 0
+ return 1
+}
+
+# Synopsis: calculate_min_space
+#
+# This function estimates the minimum space needed for a hdinstall
+function calculate_min_space()
+{
+ ESTIMATED_ROOT_MIN=$(df -m "/live/filesystem" | tail -1 | gawk '{print $3}')
+ grep -q squashfs /proc/mounts && ESTIMATED_ROOT_MIN=$(($ESTIMATED_ROOT_MIN*270/100))
+ export ESTIMATED_ROOT_MIN
+}
+
+# Synopsis: handle_mountpoint_demands <device>
+#
+# This function handles the demands of the mountpoints (data from function mountpoint_demands)
+# * check if filesystem matches (Filesystem:reiserfs / Filesystem:ext3 / see get_filesystem() )
+# * check if filesystem-type matches (Type:Linux / Type:Windows / see get_filesystem_type() )
+# * check if <device> is a LVM-device (LVM:yes / LVM:no)
+# TODO: from old installer, not adapted yet.
+function handle_mountpoint_demands()
+{
+ PART="$1";
+ FORMAT_FS="$2";
+ POINT=$(echo $3 | cut -d":" -f1); shift 3
+ while [ "$1" ];
+ do
+ VAR=$(echo $1 | cut -d":" -f1)
+ VAL=$(echo $1 | cut -d":" -f2)
+ case "$VAR" in
+ Filesystem)
+ if [ -n "$FORMAT_FS" -a "$FORMAT_FS" != "$VAL" ]; then
+ echo "ERROR: Filesystem on $PART ($POINT) has to be formatted with $VAL!" \
+ | emit_error 1
+ return 1
+ elif [ -z "$FORMAT_FS" -a "$(get_filesystem "$PART")" != "$VAL" ]; then
+ echo "ERROR: Filesystem on $PART ($POINT) is not $VAL!" \
+ | emit_error 1
+ return 1
+ fi
+ ;;
+ Type)
+ if [ -n "$FORMAT_FS" -a "$(get_filesystem_type -fs "$FORMAT_FS")" != "$VAL" ]; then
+ echo "ERROR: Filesystem on $PART ($POINT) has to be formatted with a $VAL-Filesystem!" \
+ | emit_error 1
+ return 1
+ elif [ -z "$FORMAT_FS" -a "$(get_filesystem_type "$PART")" != "$VAL" ]; then
+ echo "ERROR: Filesystem on $PART ($POINT) is not a $VAL-Filesystem!" \
+ | emit_error 1
+ return 1
+ fi
+ ;;
+ LVM)
+ not=not
+ lvdisplay $PART &>/dev/null; A=$?
+ [ "$VAL" = "yes" ] && unset not; B=$?
+ ((A*B)) || ! ((A-B)) || ( echo ERROR: $PART "($POINT)" must $not be on a LVM-device! | emit_error 1; return 1 ) || return 1
+ ;;
+ esac
+ shift
+ done
+}
+
+# Synopsis: check_partitions_for_install
+#
+# This function processes the hd_map-config:
+# * make sure that the device exists
+# * take care of the mountpoint demands
+# * check if partition has the "automount"-flag
+# * check if all partitions are big enough to install the system on it
+# TODO: from old installer, not adapted yet.
+function check_partitions_for_install()
+{
+ unset found_root
+ local progress_steps=$(( $(wc -l <<<"$cfg_hdmap") * 2 + 1))
+ local progress=0
+ progress=$[progress+1]; emit_progress $[100*progress/progress_steps]
+ # compute partition_min_table
+ local partition_min_table_=$ESTIMATED_ROOT_MIN
+ while IFS=: read device mountpoint filesystem automount
+ do
+ case "$mountpoint" in
+ "") continue;;
+ /) continue;;
+ /usr) MP_MIN=$(( $ESTIMATED_ROOT_MIN - $(du -sm --exclude /live/filesystem/usr /live/filesystem | cut -f1) ));;
+ *) MP_MIN=$(du -sm "/live/filesystem$mountpoint" 2>/dev/null | cut -f1);;
+ esac
+ [ -z "$MP_MIN" ] && MP_MIN=0
+ mp="$mountpoint"
+ while mp="$(dirname "$mp")"
+ do
+ var_mp="$(echo "$mp" | sed 's/[^a-zA-Z0-9]/_/g')"
+ parent_min="$(eval echo \$partition_min_table$var_mp)"
+ if [ -n "$parent_min" ]; then
+ eval local partition_min_table$var_mp=$(( $parent_min-$MP_MIN ))
+ break
+ fi
+ [ "${#mp}" -gt 1 ] || break;
+ done
+ var_mp="$(echo "$mountpoint" | sed 's/[^a-zA-Z0-9]/_/g')"
+ eval local partition_min_table$var_mp=$MP_MIN
+ progress=$[progress+1]; emit_progress $[100*progress/progress_steps]
+ done <<<"$cfg_hdmap"
+
+ isosrc_dev="$(awk '{if($2 == "/isosrc"){print $1}}' /proc/mounts)"
+ while IFS=: read device mountpoint filesystem automount
+ do
+ [ -z "$mountpoint" ] && continue;
+
+ # set flag if there is a root-partition in the hd_map
+ [ "$mountpoint" = / ] && found_root=1
+
+ if [ "$device" = "$isosrc_dev" ]; then
+ if [ "$filesystem" ]; then
+ emit_error 1 "Partition $device ($mountpoint) is mounted to /isosrc... This partition cannot be formatted!"
+ return 1
+ fi
+ fi
+
+ # take care of the mountpoint demands
+ if part_demands="$(mointpoint_demands | egrep "^$mountpoint:" 2>/dev/null)"; then
+ [ "$automount" = "auto" ] || emit_error 1 "Partition $device ($mountpoint) doesn't have the automount-flag set!" || return 1
+ eval handle_mountpoint_demands \"$device\" \"$filesystem\" $part_demands
+ fi
+
+ # Check if the partition is big enough to contain the installation
+ var_mp="$(echo "$mountpoint" | sed 's/[^a-zA-Z0-9]/_/g')"
+ mp_min="$(eval echo \$partition_min_table$var_mp)"
+ partition_size_min=$[mp_min*1024*1024*115/100] # + 15% Filesystem overhead
+ partition_size="$(blockdev --getsize64 $device)" # actual size of device (in bytes)
+ if [ "$partition_size" -lt "$partition_size_min" ]; then
+ emit_error 1 "Partition $device ($mountpoint) is too small! it is $[partition_size/1024/1024] MB big, but it should be at least $[partition_size_min/1024/1024+10] MB big"
+ return 1
+ fi
+ progress=$[progress+1]; emit_progress $[100*progress/progress_steps]
+ done <<<"$cfg_hdmap"
+
+ if [ -z "$found_root" ]; then
+ echo "No root-partition selected!" | emit_error 1
+ fi
+}
+
+# Synopsis: umount_all_affected [-fdisk] <device>
+#
+# This function unlocks a device and all affected devices:
+# * it umounts mounted partitions with "umount" and disables swap-partitions with "swapoff"
+# * if "-fdisk" is set:
+# - it unlocks all partitions that are on the same device as the one given
+# (e.g. "umount_all_affected -fdisk /dev/hda3" is called, so /dev/hda* is unlocked)
+# - it takes care of LVM-Volumes and unlocks them if they are affected
+# - it takes care of dm_crypt-Disks and unlocks them if they are affected
+# TODO: from old installer, not fully adapted yet.
+function umount_all_affected()
+{
+ unset DEV NR EXHAUSITIVE
+ if [ "$1" = "-fdisk" ]; then
+ # partition itself and its "sister" partitions (just to be sure, for fdisk):
+ shift
+ #DEV="${1%%[0-9]*}"
+ DEV="$(get_disk "$1")"
+ NR="${1:${#disk}}"
+ EXHAUSITIVE=1
+ fi
+ [ -z "$DEV" ] && DEV="$(echo "$1" | dereferce_links_in_list)"
+ # Mounts
+ while IFS=" " read mnt_dev mnt_point mnt_dummy
+ do
+ [ "$mnt_point" = "/live/filesystem" -o -z "$mnt_point" ] && continue
+ case "$mnt_point" in
+ /|/cdrom|/dev|/dev/*|/isosrc|/proc|/proc/*|/ramdisk|/sys|/tmp) ;;
+ *)
+ echo $mnt_dev | dereferce_links_in_list | grep -q ^$DEV$ || continue
+ for umount_point in $(gawk '{print $2}' /proc/mounts | grep -e "^$mnt_point$" -e "^$mnt_point/" | sort -r)
+ do
+ fuser -km "$umount_point"
+ umount "$umount_point" &>/dev/null || \
+ umount -l "$umount_point"
+ done
+ ;;
+ esac
+ done < /proc/mounts
+ # Swap
+ for i in $(grep -e "^$DEV" /proc/swaps | cut -d\ -f1)
+ do
+ swapoff "$i";
+ done
+# # LVM
+# for i in $(pvdisplay -c 2>/dev/null | grep "$DEV" | cut -d: -f2 | sort | uniq)
+# do
+# lv_name="$(lvdisplay $i | gawk '/LV Name/{print $3}')"
+# lv_dev="$(echo $lv_name | dereferce_links_in_list)"
+# for dev in $( ( echo $lv_name; echo $lv_dev ) | sort | uniq)
+# do
+# # recursive function call
+# umount_all_affected $dev
+# done
+# ((EXHAUSITIVE)) && vgchange -a n $i &>/dev/null
+# done
+# # Crypttab
+# for cmapname in $(grep $DEV /etc/crypttab | cut -d\ -f1)
+# do
+# umount_all_affected "/dev/mapper/$cmapname"
+# ((EXHAUSITIVE)) && cryptsetup remove $cmapname
+# done
+}
+
+# Synopsis: prepare_partitions_for_install [--nomount]
+#
+# This function processes the hd_map-config:
+# * create mountpoints
+# * format the partitions if a filesystem is given
+# * take care of LVM-Volumes and dm_crypt-Disks
+# * create the specified mountpoints in $TARGET
+# * mount the partitions to the target
+# * mount (bind) /dev, /proc and /sys to the target
+#
+# --nomount simply doesn't mount or umount partitions
+# TODO: from old installer, not adapted yet.
+function prepare_partitions_for_install()
+{
+ send install_step prepare_partitions_for_install
+ emit_progress 0
+ local progress_steps=$(( $(wc -l <<<"$cfg_hdmap") ))
+ local progress=0
+ while IFS=: read device mountpoint filesystem automount
+ do
+ [ "$1" != "--nomount" ] && umount_all_affected $device
+
+ # format device with filesystem (if specified)
+ if [ "$filesystem" ]; then
+
+ dd if=/dev/zero of=$device bs=1k count=16 >/dev/null 2>/dev/null # shutup! :-)
+
+ TMP=/tmp/mkfs.$$
+ case "$filesystem" in
+ xfs)
+ mkfs.$filesystem -f $device 2> $TMP 1>&2
+ ;;
+ reiser*)
+ echo y | mkfs.$filesystem $device 2> $TMP 1>&2
+ ;;
+ jfs)
+ echo y | mkfs.$filesystem $device 2> $TMP 1>&2
+ ;;
+ *)
+ mkfs.$filesystem $device 2> $TMP 1>&2
+ ;;
+ esac
+
+ RC="$?"
+ if [ $RC -ne 0 ]; then
+ ERROR_MESSAGES=$(tail -8 $TMP)
+ echo "$ERROR_MESSAGES"
+ emit_error 1 "mkfs failed" || return 1
+ fi
+
+ # Deactivate dir_index-feature of ext2/ext3/ext4-partitions
+ case $filesystem in
+ *ext*)
+ tune2fs -O ^dir_index $device &>/dev/null
+ ;;
+ esac
+ fi
+
+ mkdir -p ${TARGET}${mountpoint} # make sure mountpoint exists
+
+ # only mount the partition if: 1. it has the automount flag
+ # 2. the mountpoint exists on the Live system
+ # and 3. prepare_partitions_for_install was not called with "--nomount"
+ if [ "$automount" = "auto" -a "$1" != "--nomount" -a -d "/live/filesystem$mountpoint" ]; then
+ # mount device to mountpoint
+ EC="$(LC_ALL=C mount $device ${TARGET}${mountpoint} 2>&1)"
+ if (($?)); then
+ case "$EC" in
+ "*already mounted*") # then let's try "mount --bind"
+ already_mp="$(grep ^$device /proc/mounts | cut -d\ -f2)"
+ if [ -d "$already_mp" ]; then
+ awk '/^\/dev/{if($4 ~ /^rw/){print $1}}' /proc/mounts | grep -qw $device || mount -o remount,rw "$already_mp"
+ mount --bind "$already_mp" ${TARGET}${mountpoint} 2>&1
+ fi
+ ;;
+ *)
+ emit_error 1 "mount failed: $EC" || return 1
+ ;;
+ esac
+ fi
+ if ! awk '/^\/dev/{if($4 ~ /^rw/){print $1}}' /proc/mounts | grep -qw $device; then
+ emit_error 1 "mount failed: $EC" || return 1
+ fi
+ fi
+
+ # update progress
+ progress=$[progress+1]; emit_progress $[100*progress/progress_steps]
+ done <<<"$cfg_hdmap"
+}
diff --git a/backend/modules/init b/backend/modules/init
new file mode 100644
index 0000000..f7a7628
--- /dev/null
+++ b/backend/modules/init
@@ -0,0 +1,11 @@
+#!/bin/bash
+
+function init_installer()
+{
+ [ -f /etc/default/distro ] && . /etc/default/distro
+ export FLL_DISTRO_MODE FLL_DISTRO_NAME FLL_LIVE_USER
+ mkdir -p /live/filesystem
+ [ -d /live/filesystem/usr ] || mount -o ro /dev/loop0 /live/filesystem
+ cfg_set hostname "${FLL_DISTRO_NAME}Box"
+}
+
diff --git a/backend/modules/install b/backend/modules/install
new file mode 100644
index 0000000..760f40d
--- /dev/null
+++ b/backend/modules/install
@@ -0,0 +1,75 @@
+#!/bin/bash
+
+function do_install()
+{
+ send install_step check_partitions_for_install
+ check_partitions_for_install
+ prepare_target
+ prepare_partitions_for_install
+ copy_system_to_target
+ update_fstab_on_target
+ update_passwd_on_target
+ copy_home_to_target
+ copy_etc_to_target
+ configure_target_update_files
+ configure_target_purge_live_only_stuff
+ configure_target_services
+ install_bootmanager_to_target
+ send install_step cleanup
+ cleanup
+}
+
+function prepare_target()
+{
+ send install_step prepare_target
+ mkdir -p /live/hdinstall
+ export TARGET=/live/hdinstall
+}
+
+# Synopsis: chroot_it <...>
+#
+# execute the supplied command in the target
+function chroot_it()
+{
+ [ -n "$TARGET" -a $UID -eq 0 ] && chroot $TARGET "$@"
+}
+
+# Synopsis: copy_system_to_target
+#
+# This function copies the system to the target.
+# Therefore $TARGET and all partitions of hd_map have to be mounted
+# this should have be done in the prepare-stage (see prepare_partitions_for_install)
+#
+function copy_system_to_target()
+{
+ send install_step copy_system_to_target
+ emit_progress 0
+ calculate_min_space
+ orig_size="$ESTIMATED_ROOT_MIN"
+ dest_size_before=$(di -dm -fSMu | gawk '/\/live\/hdinstall/{used+=int($3)} END{print int(used)}')
+ dest_size_diff=0
+
+ cd /live/filesystem
+ cp -a * $TARGET &
+
+ cp=$!
+ old_percent=0
+ while ps --pid $cp &>/dev/null
+ do
+ dest_size=$(di -dm -fSMu | gawk '/\/live\/hdinstall/{used+=int($3)} END{print int(used)}')
+ dest_size_diff=$(( $dest_size - $dest_size_before ))
+ percent=$((( 100 * $dest_size_diff ) / $orig_size ))
+ if [ "$percent" != "$old_percent" ]; then
+ emit_progress $percent
+ old_percent="$percent"
+ fi
+ sleep 5
+ done
+
+ sync
+ mkdir -p "$TARGET/proc" "$TARGET/dev" "$TARGET/sys"
+ mount --bind /proc "$TARGET/proc"
+ mount --bind /dev "$TARGET/dev"
+ mount --bind /sys "$TARGET/sys"
+}
+
diff --git a/backend/modules/install_configure b/backend/modules/install_configure
new file mode 100644
index 0000000..28e351e
--- /dev/null
+++ b/backend/modules/install_configure
@@ -0,0 +1,152 @@
+#!/bin/bash
+
+# Synopsis: configure_target_update_files
+#
+# This function is adapted from the Knoppix-Installer
+function configure_target_update_files()
+{
+ send install_step configure_target_update_files
+ # set up hostname
+ cat <<EOF >$TARGET/etc/hosts
+127.0.0.1 localhost
+127.0.1.1 $cfg_hostname
+
+# The following lines are desirable for IPv6 capable hosts
+# (added automatically by netbase upgrade)
+
+::1 ip6-localhost ip6-loopback
+fe00::0 ip6-localnet
+ff00::0 ip6-mcastprefix
+ff02::1 ip6-allnodes
+ff02::2 ip6-allrouters
+ff02::3 ip6-allhosts
+EOF
+ echo "$cfg_hostname" > $TARGET/etc/hostname
+ echo "$cfg_hostname" > $TARGET/etc/mailname
+
+ # remove LiveCD-user from /etc/sudoers
+ cat > "$TARGET/etc/sudoers" <<EOF
+# /etc/sudoers
+#
+# This file MUST be edited with the 'visudo' command as root.
+#
+# See the man page for details on how to write a sudoers file.
+#
+
+Defaults env_reset
+
+# Host alias specification
+
+# User alias specification
+
+# Cmnd alias specification
+
+# User privilege specification
+root ALL=(ALL) ALL
+EOF
+ chown root:root "$TARGET/etc/sudoers"
+ chmod 0440 "$TARGET/etc/sudoers"
+
+ # "normalize" /etc/inittab
+ rm -f "$TARGET/etc/inittab"
+ sed 's/id\:[0-6]\:initdefault\:/id\:5\:initdefault\:/;s/\([1-6]:23\):/\145:/' "$TARGET/usr/share/sysvinit/inittab" > "$TARGET/etc/inittab"
+
+ # create "real" /tmp with mode 1777
+ rm -f $TARGET/tmp 2>/dev/null
+ mkdir -p $TARGET/tmp
+ chmod 1777 $TARGET/tmp
+
+ # create /etc/mtab as a regular file
+ rm -f $TARGET/etc/mtab
+ touch $TARGET/etc/mtab
+
+ # configure /etc/kernel-pkg.conf
+ if [ -f "$TARGET/etc/kernel-pkg.conf" ]; then
+ perl -pi -e "s/^maintainer.*\=.*/maintainer \:\= $cfg_realname/;s/^email.*\=.*/email \:\= $cfg_username\@$cfg_hostname\.local/" "$TARGET/etc/kernel-pkg.conf"
+ NUMCPUS=$(grep -c "model name" /proc/cpuinfo)
+ [ $NUMCPUS -ge 2 ] && echo CONCURRENCY_LEVEL := $NUMCPUS >> $TARGET/etc/kernel-pkg.conf
+ fi
+
+ # enable KDE sounds
+ rm -f "$TARGET/home/$cfg_username/.kde/share/config/knotifyrc"
+
+ # profile
+ cat "$TARGET/usr/share/base-files/profile" > "$TARGET/etc/profile"
+
+ # For us users use an us-mirror
+ case "$LANGUAGE" in
+ us*)
+ #perl -pi -e "s/ftp2.de.debian.org/ftp.uk.debian.org/" $TARGET/etc/apt/sources.list
+ :
+ ;;
+ esac
+
+ # install hooks
+ if [ "$(ls $TARGET/usr/share/acritoxinstaller/target-config)" ]; then
+ for hook in $TARGET/usr/share/acritoxinstaller/target-config/*
+ do
+ chroot_it "${hook#$TARGET}"
+ done
+ fi
+
+ # install overlay
+ if [ "$(ls $TARGET/usr/share/acritoxinstaller/target-overlay)" ]; then
+ cp -a --target-directory=$TARGET $TARGET/usr/share/acritoxinstaller/target-overlay/*
+ fi
+
+ # add correct keymap
+ [ -f /etc/sysconfig/keyboard ] && . /etc/sysconfig/keyboard
+
+ [ -n "$KEYTABLE" ] && chroot_it install-keymap "$KEYTABLE" 2>/dev/null
+}
+
+# Synopsis: configure_target_purge_live_only_stuff
+#
+# This function removes everything that is only useful on live-systems
+function configure_target_purge_live_only_stuff()
+{
+ send install_step configure_target_purge_live_only_stuff
+ # remove live-only-packages
+ chroot_it dpkg --purge \
+ busybox \
+ acritoxinstaller \
+ acritoxinstaller-kanotix \
+ live-boot-initramfs-tools \
+ live-boot \
+ live-initramfs \
+ live-build-cgi \
+ live-build \
+ live-helper \
+ live-config-runit \
+ live-config-sysvinit \
+ live-config-upstart \
+ live-config \
+ live-magic \
+ live-manual-epub \
+ live-manual-html \
+ live-manual-odf \
+ live-manual-pdf \
+ live-manual-txt \
+ live-manual \
+ mknbi \
+ syslinux \
+ tftpd-hpa &> /dev/null
+
+ # disable live config
+ [ -f "$TARGET/etc/default/distro" ] && \
+ perl -pi -e "s/^FLL_DISTRO_MODE\=.*/FLL_DISTRO_MODE\=\"installed\"/" "$TARGET/etc/default/distro"
+
+ # remove temporary kde- user files
+ rm -f "$TARGET/home/$cfg_username/.DCOPserver_*_*"
+ rm -f "$TARGET/home/$cfg_username/.kde/cache-*"
+ rm -f "$TARGET/home/$cfg_username/.kde/socket-*"
+ rm -f "$TARGET/home/$cfg_username/.kde/tmp-*"
+ rm -f "$TARGET/home/$cfg_username/.*uthority"
+
+ rm -rf "$TARGET/etc/sysconfig"
+ rm -f "$TARGET/home/$cfg_username/Desktop/install-gui.desktop"
+
+ # remove kdm live shutdown hack
+ rm -f "$TARGET/home/$cfg_username/.kde/shutdown/kdm-force-shutdown-hack"
+}
+
diff --git a/backend/modules/install_main b/backend/modules/install_main
new file mode 100644
index 0000000..bee41af
--- /dev/null
+++ b/backend/modules/install_main
@@ -0,0 +1,302 @@
+#!/bin/bash
+
+# Synopsis: update_fstab_on_target
+#
+# This function is partly adapted from the Knoppix-Installer
+# It creates a new fstab for the new installed system in $TARGET.
+# * proc, usbfs, sysfs, tmpfs: hardcoded
+# * all mountpoints of the hd_map
+# * cdroms, floppy
+# * remove not needed device links
+function update_fstab_on_target()
+{
+ send install_step update_fstab_on_target
+ emit_progress 0
+ local progress_steps=$(( $(wc -l <<<"$cfg_hdmap") + 2 ))
+ local progress=0
+
+ # Charset stuff for FAT/NTFS-Filesystems
+ unset utf_option nls
+ if [ "$(locale charmap)" = "UTF-8" ]; then
+ utf_option=",utf8"
+ nls=",nls=utf8"
+ fi
+ chroot_it locale-gen &>/dev/null
+
+ # Build new /etc/fstab
+ cat <<EOF >$TARGET/etc/fstab
+# /etc/fstab: static file system information.
+#
+# <file system> <mount point> <type> <options> <dump> <pass>
+proc /proc proc defaults 0 0
+EOF
+
+ [ -x $TARGET/etc/init.d/mountkernfs.sh ] || cat <<EOF >>$TARGET/etc/fstab
+sysfs /sys sysfs defaults 0 0
+tmpfs /dev/shm tmpfs defaults 0 0
+EOF
+
+ while IFS=: read device mountpoint filesystem automount
+ do
+ fstab_options=""; fstab_dump=0; fstab_pass=2; fstab_type=""
+ [ -z "$fstab_type" ] && fstab_type=$filesystem
+ [ -z "$fstab_type" ] && fstab_type=$(get_filesystem $device)
+ [ -z "$fstab_type" ] && fstab_type=auto
+
+ case "$automount" in
+ auto)
+ fstab_options="defaults";;
+ *)
+ fstab_options="noauto,users";;
+ esac
+
+ case "$fstab_type" in
+ msdos)
+ fstab_options="${fstab_options},umask=000,quiet${utf_option}"; fstab_pass=0;;
+ vfat)
+ fstab_options="${fstab_options},umask=000,shortname=mixed,quiet${utf_option}"; fstab_pass=0;;
+ esac
+
+ if [ "$mountpoint" = "/" ]; then
+ fstab_pass=1
+ case $fstab_type in
+ reiser*|xfs|jfs)
+ fstab_options="defaults"
+ ;;
+ *)
+ fstab_options="defaults,errors=remount-ro"
+ ;;
+ esac
+ elif [ "${mountpoint:0:7}" = "/media/" ]; then
+ # don't run fsck on boot for all /media/* mountpoints
+ fstab_pass=0
+ else
+ # don't run fsck on boot if device is a removable disk (if group of /dev/XXX == floppy)
+ [ "$(stat --format "%G" $device)" = "floppy" ] && fstab_pass=0
+ fi
+
+ fstab_options="${fstab_options##defaults,}"
+
+ unset DEV UUID
+ case $device in
+ /dev/mapper/*) ;;
+ /dev/disk/by-uuid/*) DEV=/dev/$(readlink $device|sed s@../../@@)
+ UUID="UUID=${device#/dev/disk/by-uuid/}"
+ ;;
+ /dev/*) DEV=$device
+ UUID="UUID=$(get_partition_uuid $device)"
+ ;;
+ UUID=*) UUID=$device
+ DEV=/dev/$(readlink /dev/disk/by-uuid/${device#UUID=}|sed s@../../@@)
+ ;;
+ esac
+
+ [ -n "${DEV#/dev/}" ] && echo "# $DEV" >> $TARGET/etc/fstab
+ if [ -n "${UUID#UUID=}" ]; then
+ printf "%-15s %-15s %-7s %-15s %-7s %s\n" "$UUID" "$mountpoint" "$fstab_type" "$fstab_options" "$fstab_dump" "$fstab_pass" >> $TARGET/etc/fstab
+ else
+ # this partition doesn't have an UUID
+ printf "%-15s %-15s %-7s %-15s %-7s %s\n" "$device" "$mountpoint" "$fstab_type" "$fstab_options" "$fstab_dump" "$fstab_pass" >> $TARGET/etc/fstab
+ fi
+
+ # Remove not needed device links
+ [ "/media/${device##*/}" != "$mountpoint" ] && rmdir "$TARGET/media/${device##*/}" &>/dev/null
+ rm -f "$TARGET/home/$cfg_username/Desktop/${device##*/}"
+
+ # update progress
+ progress=$[progress+1]; emit_progress $[100*progress/progress_steps]
+ done <<<"$cfg_hdmap"
+
+ # Add swap to /etc/fstab
+ while read device
+ do
+ UUID="$(get_partition_uuid $device)"
+ if [ -z "$UUID" ]; then
+ # swap partition without UUID
+ swapoff $device
+ mkswap $device
+ swapon $device
+ UUID="$(get_partition_uuid $device)"
+ fi
+ [ -z "$UUID" ] && continue # should never happen...
+
+ printf "%-15s %-15s %-7s %-15s %-7s %s\n" "UUID=$UUID" "none" "swap" "sw" "0" "0" >> $TARGET/etc/fstab
+ done < <(list_swap_partitions)
+
+ # Add cdrom devices to /etc/fstab
+
+ CDROM=0
+ for c in $(gawk '/name/{for (i=NF;i>=3;i--) {print $i}}' /proc/sys/dev/cdrom/info 2>/dev/null); do
+ [ -d $TARGET/media/cdrom$CDROM ] || mkdir -p $TARGET/media/cdrom$CDROM
+ if [ "$CDROM" = "0" ]; then
+ if [ "$(readlink $TARGET/media/cdrom)" != "cdrom0" ]; then
+ rm -f $TARGET/media/cdrom
+ ln -s cdrom0 $TARGET/media/cdrom
+ fi
+ if [ "$(readlink $TARGET/cdrom)" != "media/cdrom" ]; then
+ rm -f $TARGET/cdrom
+ ln -s media/cdrom $TARGET/cdrom
+ fi
+ fi
+ printf "%-15s %-15s %-7s %-15s %-7s %s\n" "/dev/$c" "/media/cdrom$CDROM" "udf,iso9660" "user,noauto" "0" "0" >> $TARGET/etc/fstab
+ CDROM=$(($CDROM+1))
+ done
+ if [ "$CDROM" = "0" ]; then
+ rm -f $TARGET/cdrom $TARGET/media/cdrom
+ fi
+
+ # Add floppy devices to /etc/fstab
+
+ for f in $(ls -d /sys/block/fd* 2>/dev/null); do
+ [ -d $TARGET/media/floppy${f#/sys/block/fd} ] || mkdir -p $TARGET/media/floppy${f#/sys/block/fd}
+ printf "%-15s %-15s %-7s %-15s %-7s %s\n" "/dev${f#/sys/block}" "/media/floppy${f#/sys/block/fd}" "auto" "rw,user,noauto" "0" "0" >> $TARGET/etc/fstab
+ done
+
+}
+
+# Synopsis: update_passwd_on_target
+#
+function update_passwd_on_target()
+{
+ send install_step update_passwd_on_target
+ chroot $TARGET sh -c "usermod --password '$cfg_rootpwd' root"
+
+ if [ -x $ROOT/usr/sbin/adduser ]; then
+ LC_ALL=C chroot $TARGET adduser --disabled-password --force-badname --no-create-home --gecos "$cfg_realname,,," --uid 1000 "$cfg_username" >/dev/null
+ else
+ chroot $TARGET useradd -c "$cfg_realname,,," "$cfg_username" -u 1000 >/dev/null
+ fi
+
+ chroot $TARGET sh -c "usermod --password '$cfg_userpwd' '$cfg_username'"
+
+ for group in lpadmin scanner; do
+ chroot $TARGET addgroup --system $group >/dev/null 2>&1
+ done
+ for group in adm audio cdrom dialout floppy video plugdev dip lpadmin lp scanner powerdev netdev; do
+ chroot $TARGET adduser --force-badname "$cfg_username" $group >/dev/null 2>&1
+ done
+}
+
+# Synopsis: copy_home_to_target
+#
+# This function copies the home directory from the live user
+function copy_home_to_target()
+{
+ send install_step copy_home_to_target
+ #check if already data is there then stop
+ if [ -d "$TARGET/home/$cfg_username" ]; then
+ chroot "$TARGET" chown -R "$cfg_username":"$cfg_username" "/home/$cfg_username"
+ return 0
+ fi
+
+ if [ -d "/home/$FLL_LIVE_USER/.kde" ]; then
+ rm -rf "$TARGET/home/$FLL_LIVE_USER"
+ cp -a "/home/$FLL_LIVE_USER" "$TARGET/home"
+ [ "$cfg_username" != "$FLL_LIVE_USER" ] && mv "$TARGET/home/$FLL_LIVE_USER" "$TARGET/home/$cfg_username"
+ else
+ cp -a "$TARGET/etc/skel" "$TARGET/home"
+ mv "$TARGET/home/skel" "$TARGET/home/$cfg_username"
+ fi
+
+ # update home-path in user's config files
+ if [ "$cfg_username" != "$FLL_LIVE_USER" ]; then
+ rm -f "$TARGET/home/$cfg_username/.mozilla/appreg"
+ rm -f "$TARGET/home/$cfg_username/.mozilla/pluginreg.dat"
+ if [ -e "$TARGET/home/$cfg_username/.mozilla/$FLL_LIVE_USER" ]; then
+ [ -e "$TARGET/home/$cfg_username/.mozilla/default" ] || mv "$TARGET/home/$cfg_username/.mozilla/$FLL_LIVE_USER" "$TARGET/home/$cfg_username/.mozilla/default"
+ perl -pi -e 's/.*general.useragent.*\n?//' "$TARGET/home/$cfg_username/.mozilla/default/*/prefs.js"
+ fi
+
+ OLDHOME="/home/$FLL_LIVE_USER"
+ NEWHOME="/home/$cfg_username"
+ PART="$TARGET"
+ for f in $(find "$PART$NEWHOME" -exec grep -ls "$OLDHOME" {} \;|grep -v $0); do
+ perl -pi -e "s|$OLDHOME|$NEWHOME|g" "$f"
+ done
+ fi
+
+ # revert to plain debian .bashrc
+ cat "$TARGET/etc/skel/.bashrc" > "$TARGET/home/$cfg_username/.bashrc"
+
+ # revert kdesu/sudo workaround
+ rm -f "$TARGET/home/$cfg_username/.kde/share/apps/konsole/su.desktop" \
+ "$TARGET/home/$cfg_username/.kde/share/apps/konsole/sumc.desktop" \
+ "$TARGET/home/$cfg_username/.kde/share/config/kdesurc" \
+ "$TARGET/home/$cfg_username/.kde4/share/config/kdesurc" \
+ "$TARGET/home/$cfg_username/.su-to-rootrc"
+ rm -rf "$TARGET/home/$cfg_username/.gconf/apps/gksu"
+
+ # force kde first time configuration
+ if [ -f /etc/skel/.kde/share/config/kpersonalizerrc ]; then
+ perl -pi -e 's/FirstLogin=false/FirstLogin=true/g' "$TARGET/etc/skel/.kde/share/config/kpersonalizerrc"
+
+ # The users kde should be perfect, unless we just copied from template ...
+ [ ! -d "/home/$FLL_LIVE_USER/.kde" ] && perl -pi -e 's/FirstLogin=false/FirstLogin=true/g' "$TARGET/home/$cfg_username/.kde/share/config/kpersonalizerrc"
+ fi
+
+ chroot "$TARGET" chown -R "$cfg_username":"$cfg_username" "/home/$cfg_username"
+}
+
+# Synopsis: copy_etc_to_target
+#
+# This function is partly adapted from the Knoppix-Installer
+function copy_etc_to_target()
+{
+ send install_step copy_etc_to_target
+ # UTC=no fix
+ if [ -f /etc/default/rcS -a -f $TARGET/etc/default/rcS ]; then
+ cat /etc/default/rcS > $TARGET/etc/default/rcS
+ fi
+
+ cp -a /etc/timezone $TARGET/etc/timezone
+ cp -a /etc/localtime $TARGET/etc/localtime
+ cp -a /etc/default/keyboard $TARGET/etc/default/keyboard
+ cp -a /etc/default/locale $TARGET/etc/default/locale
+ cp -a /etc/locale.gen $TARGET/etc/locale.gen
+ #cp -a /etc/console/* $TARGET/etc/console/
+ #cp -a /etc/environment $TARGET/etc/environment
+
+ # network
+ rm -f $TARGET/etc/network/interfaces
+ cp -a /etc/network/interfaces $TARGET/etc/network/interfaces
+
+ # create locales
+ chroot_it locale-gen &>/dev/null
+ chroot_it dpkg-reconfigure -fnoninteractive keyboard-configuration &>/dev/null
+
+ # nvidia autoinstall trigger
+ chroot_it update-rc.d -f kanotix remove &>/dev/null
+ rm -f $TARGET/etc/init.d/kanotix
+
+ # xorg.conf
+ debconf-get-selections | grep -e xserver-xorg -e tzdata | chroot_it debconf-set-selections &>/dev/null
+ rm -f $TARGET/etc/X11/xorg.conf*
+ chroot_it dpkg-reconfigure -phigh xserver-xorg &>/dev/null
+
+ # Save ALSA sound volume
+ if [ -e /proc/asound/modules ] && [ -x /usr/sbin/alsactl ]; then
+ /usr/sbin/alsactl store
+ if [ -f /var/lib/alsa/asound.state ]; then
+ cp /var/lib/alsa/asound.state "$TARGET/var/lib/alsa"
+ fi
+ fi
+
+ # KDM: auto login
+ kdmrc=$TARGET/etc/kde3/kdm/kdmrc
+ [ -f $TARGET/etc/kde4/kdm/kdmrc ] && kdmrc=$TARGET/etc/kde4/kdm/kdmrc
+ if [ -e $kdmrc ]; then
+ perl -pi -e "s|^[#\s]*(AutoLoginUser).*|\1=$cfg_username|" $kdmrc
+ [ "$cfg_autologin" = "on" ] && autologin="true" || autologin="false"
+ perl -pi -e "s|^[#\s]*(AutoLoginEnable).*|\1=$autologin|" $kdmrc
+ fi
+
+ # Crypto
+ cp -a /etc/crypttab $TARGET/etc/crypttab
+
+ # PolicyKit
+ if [ -e /etc/PolicyKit/PolicyKit.conf -a -e $TARGET/etc/PolicyKit/PolicyKit.conf ]; then
+ sed '/<!-- .* user in live session -->/d; s/user="'"$FLL_LIVE_USER"'"/user="'"$cfg_username"'"/;' \
+ < /etc/PolicyKit/PolicyKit.conf > $TARGET/etc/PolicyKit/PolicyKit.conf
+ fi
+}
+
diff --git a/backend/modules/install_services b/backend/modules/install_services
new file mode 100644
index 0000000..e3b74c3
--- /dev/null
+++ b/backend/modules/install_services
@@ -0,0 +1,19 @@
+#!/bin/bash
+
+# Synopsis: configure_target_services
+#
+# This function configures the services and their autostart
+function configure_target_services()
+{
+ send install_step configure_target_services
+ # prepare ssh
+ if [ /etc/init.d/ssh ]; then
+ if [ ! -e "$TARGET/etc/ssh/ssh_host_rsa_key" ]; then
+ ssh-keygen -q -t rsa -f "$TARGET/etc/ssh/ssh_host_rsa_key" -C '' -N ''
+ fi
+ if [ ! -e "$TARGET/etc/ssh/ssh_host_dsa_key" ]; then
+ ssh-keygen -q -t dsa -f "$TARGET/etc/ssh/ssh_host_dsa_key" -C '' -N ''
+ fi
+ fi
+}
+
diff --git a/backend/modules/partitions b/backend/modules/partitions
new file mode 100644
index 0000000..0d59323
--- /dev/null
+++ b/backend/modules/partitions
@@ -0,0 +1,251 @@
+#!/bin/bash
+
+# Synopsis: list_all_disks
+#
+# This function lists all disks
+# Output example:
+# /dev/sda
+# /dev/sdb
+function list_all_disks()
+{
+ awk -vli="$(awk '{if($2=="sd") print $1;}' /proc/devices)" 'BEGIN{m=split(li,list," ")}{for(i=1;i<=m;i++) if($1==list[i]&&$2%16==0) print "/dev/"$4;}' /proc/partitions
+}
+
+# Synopsis: send_list_of_disks
+#
+# This script sends a list of all disks with size-details to the frontend.
+# Output example:
+# <installer data> list_of_disks
+# /dev/sda 10001908224
+# /dev/sdb 64023257088
+function send_list_of_disks()
+{
+ send data list_of_disks
+ list_all_disks | partitions_size_details
+}
+
+# Synopsis: list_all_partitions
+#
+# This function lists all partitions from all disks
+# Output example:
+# /dev/sda1
+# /dev/sda2
+# /dev/sdb5
+function list_all_partitions()
+{
+ awk -vli="$(awk '{if($2=="sd") print $1;}' /proc/devices)" 'BEGIN{m=split(li,list," ")}{for(i=1;i<=m;i++) if($1==list[i]&&$2%16!=0) print "/dev/"$4;}' /proc/partitions
+}
+
+# Synopsis: (e.g.) list_all_partitions | partitions_usage_details
+#
+# This function lists usage details for a list of partitions from STDIN
+# Output example:
+# /dev/sda1 filesystem reiserfs
+# /dev/sda2 filesystem ext3
+# /dev/sda3 other swap
+function partitions_usage_details()
+{
+ while read part x
+ do
+ [ -e "$part" ] || continue
+ ID_FS_USAGE="$(/sbin/blkid -p -s USAGE -o value "$part")"
+ [ "$ID_FS_USAGE" ] || continue
+ ID_FS_TYPE="$(/sbin/blkid -p -s TYPE -o value "$part")"
+ echo "$part $ID_FS_USAGE $ID_FS_TYPE"
+ done
+}
+
+# Synopsis: (e.g.) list_all_partitions | partitions_size_details
+#
+# This function lists size details for a list of partitions from STDIN
+# Output example:
+# /dev/sda 64023257088
+# /dev/sda1 10001908224
+function partitions_size_details()
+{
+ while read part x
+ do
+ [ -e "$part" ] || continue
+ echo "$part $(blockdev --getsize64 "$part")"
+ done
+}
+
+# Synopsis: list_partitions [-type <filesystem>] [-disk <disk>] [-usage <usage>]
+#
+# This function lists all partitions that match all the given parameters
+# Output example:
+# /dev/sda1
+# /dev/sda2
+# /dev/sdb5
+function list_partitions()
+{
+ unset required_type required_disk;
+ required_usage="filesystem"
+
+ while [ "$1" ]
+ do
+ case $1 in
+ -type)
+ required_type="$2";
+ shift
+ ;;
+ -disk)
+ required_disk="$2";
+ shift
+ ;;
+ -usage)
+ required_usage="$2";
+ shift
+ ;;
+ esac
+ shift
+ done
+
+ while IFS=" " read part usage type
+ do
+ if [ "$usage" = "$required_usage" ]; then
+ [ "$required_type" -a "$required_type" != "$(get_filesystem $part)" ] && continue
+ [ "$required_disk" ] && ! echo "$part" | grep -q "^$required_disk" && continue
+ echo $part
+ fi
+ done < <(list_all_partitions | partitions_usage_details)
+}
+
+
+# Synopsis: list_linux_partitions
+#
+# This function lists all partitions from the disks (by list_all_disks) which have partition Id 0x83 (= Linux)
+# Output example:
+# /dev/sda4
+# /dev/sdb1
+function list_linux_partitions()
+{
+ for disk in $(list_all_disks)
+ do
+ LC_ALL=C sfdisk -l "$disk" 2>/dev/null | sed 's/[*+]//g;' | gawk '/^\/dev/{if($6 == 83){print $1}}'
+ done
+}
+
+# Synopsis: list_swap_partitions
+#
+# This function lists all partitions from the disks (by list_all_disks) which have partition Id 0x82 (= Linux swap)
+# Output example:
+# /dev/sda4
+# /dev/sdb1
+function list_swap_partitions()
+{
+ for disk in $(list_all_disks)
+ do
+ LC_ALL=C sfdisk -l "$disk" 2>/dev/null | sed 's/[*+]//g;' | gawk '/^\/dev/{if($6 == 82){print $1}}'
+ done
+}
+
+# Synopsis: get_disk <partition>
+#
+# This function returns the disk containing the given partition
+# Output example:
+# /dev/sda
+function get_disk()
+{
+ path="$(udevadm info -q path -n "$1")"
+ while [ "$path" ]
+ do
+ udevadm info -q env -p "$path" | grep -q "^DEVTYPE=disk$" && break
+ path="$(dirname "$path")"
+ done
+ [ "$path" ] && udevadm info --root -q name -p "$path" && return 0
+ return 1
+}
+
+# Synopsis: is_removeable <disk/partition>
+#
+# This function checks if a disk or partition is removeable (returncode 0) or not (returncode 1)
+function is_removeable()
+{
+ DEV="$(get_disk "$1")"
+ REM=$(cat /sys/block/${DEV#/dev/}/removable 2>/dev/null)
+ case $(readlink -f /sys/block/${DEV#/dev}) in *usb*) REM=1; esac
+ [ "$REM" = "1" ] && return 0 || return 1
+}
+
+# Synopsis: list_possible_root_partitions
+#
+# This script lists all possible root-partitions.
+# * all linux-partitions and all partitions that have a linux-filesystem
+# Output example:
+# /dev/sda4
+# /dev/sdb1
+function list_possible_root_partitions()
+{
+ ( list_linux_partitions; list_partitions -type Linux ) | sort -u
+}
+
+# Synopsis: send_possible_root_partitions
+#
+# This script sends a list of all possible root-partitions with size-details to the frontend.
+# Output example:
+# <installer data> possible_root_partitions
+# /dev/sda4 10001908224
+# /dev/sdb1 64023257088
+function send_possible_root_partitions()
+{
+ send data possible_root_partitions
+ list_possible_root_partitions | partitions_size_details
+}
+
+# Synopsis: send_possible_root_filesystems
+#
+# This script sends a list of all possible filesystems to format the root-partition with to the frontend.
+# Output example:
+# <installer data> possible_root_filesystems
+# ext4
+# ext3
+# reiserfs
+function send_possible_root_filesystems()
+{
+ send data possible_root_filesystems
+ for fs in ext4 ext3 reiserfs xfs jfs; do echo $fs; done
+}
+
+# Synopsis: get_filesystem <device>
+#
+# This function returns the filesystem on the supplied device.
+# Returned filesystem could be:
+# ext4, ext3, ext2, reiserfs, xfs, minix, hfs, efs, reiser4,
+# jfs, iso9660, vfat, ntfs, swap, oracleasm, jbd, gfs,
+# gfs2, vxfs, romfs, bfs, cramfs, qnx4, udf, ufs, hpfs,
+# sysv, swsuspend, ocfs, ocfs2
+# Output example:
+# ext4
+function get_filesystem()
+{
+ [ -x /sbin/blkid ] && /sbin/blkid -s TYPE -o value "$1"
+}
+
+# Synopsis: get_filesystem_type [-fs <filesystem>]|<device>
+#
+# This function returns the type of the filesystem on the supplied device (or filesystem).
+# Returned type could be:
+# Linux, CD-ROM, Windows, Swap, (not recognized type -> filesystem)
+# Output example:
+# Linux
+function get_filesystem_type()
+{
+ if [ "$1" = "-fs" ]; then
+ filesystem="$2"
+ else
+ filesystem="$(get_filesystem "$1")"
+ fi
+ case $filesystem in
+ ext4|ext3|ext2|reiserfs|xfs|minix|hfs|efs|reiser4|jfs) echo Linux;;
+ iso9660) echo CD-ROM;;
+ vfat|ntfs) echo Windows;;
+ swap) echo Swap;;
+ *) echo "$filesystem";;
+ esac
+ # Other (not recognized) Filesystems:
+ # oracleasm, jbd, gfs, gfs2, vxfs, romfs, bfs, cramfs,
+ # qnx4, udf, ufs, hpfs, sysv, swsuspend, ocfs, ocfs2
+}
+
+
diff --git a/backend/modules/partmgr b/backend/modules/partmgr
new file mode 100644
index 0000000..c773e8d
--- /dev/null
+++ b/backend/modules/partmgr
@@ -0,0 +1,24 @@
+#!/bin/bash
+
+function run_partmgr()
+{
+ case "$1" in
+ cfdisk|fdisk)
+ TERM=xterm /sbin/"$1" "$2"
+ ;;
+ gparted|qtparted)
+ "$1" "$2"
+ ;;
+ esac
+}
+
+function send_partition_managers()
+{
+ send data partition_managers
+ for app in cfdisk gparted qtparted fdisk
+ do
+ which $app &>/dev/null || continue
+ echo $app
+ done
+}
+
diff --git a/busyappfilter.cpp b/busyappfilter.cpp
new file mode 100644
index 0000000..3f14de2
--- /dev/null
+++ b/busyappfilter.cpp
@@ -0,0 +1,23 @@
+#include "busyappfilter.h"
+
+bool BusyAppFilter::eventFilter(QObject *obj, QEvent *event)
+{
+ switch ( event->type() )
+ {
+ case QEvent::KeyPress:
+ case QEvent::KeyRelease:
+ case QEvent::MouseButtonPress:
+ case QEvent::MouseButtonDblClick:
+ case QEvent::MouseMove:
+ case QEvent::HoverEnter:
+ case QEvent::HoverLeave:
+ case QEvent::HoverMove:
+ case QEvent::DragEnter:
+ case QEvent::DragLeave:
+ case QEvent::DragMove:
+ case QEvent::Drop:
+ return true;
+ default:
+ return QObject::eventFilter( obj, event );
+ }
+}
diff --git a/busyappfilter.h b/busyappfilter.h
new file mode 100644
index 0000000..6d80f39
--- /dev/null
+++ b/busyappfilter.h
@@ -0,0 +1,14 @@
+#include <QObject>
+#include <QEvent>
+
+#ifndef BUSYAPPFILTER_H
+#define BUSYAPPFILTER_H
+
+
+class BusyAppFilter : public QObject
+{
+ protected:
+ bool eventFilter( QObject *obj, QEvent *event );
+};
+
+#endif // BUSYAPPFILTER_H
diff --git a/config.h.in b/config.h.in
new file mode 100644
index 0000000..cad8199
--- /dev/null
+++ b/config.h.in
@@ -0,0 +1,2 @@
+#define BACKEND_DIR "@BACKEND_DIR@"
+#define BACKEND_PATH "@BACKEND_DIR@/backend.sh"
diff --git a/debian/acritoxinstaller-kanotix.install b/debian/acritoxinstaller-kanotix.install
new file mode 100644
index 0000000..9feb853
--- /dev/null
+++ b/debian/acritoxinstaller-kanotix.install
@@ -0,0 +1,2 @@
+kanotix/acritoxinstaller.desktop /usr/share/applnk/Kanotix/
+kanotix/icons/* /usr/share/icons/hicolor/
diff --git a/debian/acritoxinstaller.install b/debian/acritoxinstaller.install
new file mode 100644
index 0000000..8563384
--- /dev/null
+++ b/debian/acritoxinstaller.install
@@ -0,0 +1 @@
+debian/tmp/usr
diff --git a/debian/changelog b/debian/changelog
new file mode 100644
index 0000000..826bc8c
--- /dev/null
+++ b/debian/changelog
@@ -0,0 +1,5 @@
+acritoxinstaller (0.2-1) unstable; urgency=low
+
+ * Initial release.
+
+ -- Andreas Loibl <andreas@andreas-loibl.de> Fri, 11 Mar 2011 10:21:10 +0100
diff --git a/debian/compat b/debian/compat
new file mode 100644
index 0000000..7f8f011
--- /dev/null
+++ b/debian/compat
@@ -0,0 +1 @@
+7
diff --git a/debian/control b/debian/control
new file mode 100644
index 0000000..2d571d8
--- /dev/null
+++ b/debian/control
@@ -0,0 +1,19 @@
+Source: acritoxinstaller
+Section: admin
+Priority: optional
+Maintainer: Andreas Loibl <andreas@andreas-loibl.de>
+Uploaders: Joerg Schirottke <master@kanotix.com>
+Build-Depends: cdbs, debhelper (>= 7.0.50~), cmake, libqt4-dev
+Standards-Version: 3.8.4
+
+Package: acritoxinstaller
+Architecture: any
+Depends: ${shlibs:Depends}, ${misc:Depends}
+Description: AcritoxInstaller
+ Qt4 frontend and bash backend to install debian-live-LiveCDs
+
+Package: acritoxinstaller-kanotix
+Architecture: any
+Depends: acritoxinstaller (= ${source:Version})
+Description: AcritoxInstaller for KANOTIX
+
diff --git a/debian/copyright b/debian/copyright
new file mode 100644
index 0000000..b7b3b6b
--- /dev/null
+++ b/debian/copyright
@@ -0,0 +1,34 @@
+This work was packaged for Debian by:
+
+ Andreas Loibl <andreas@andreas-loibl.de> on Fri, 11 Mar 2011 09:42:47 +0100
+
+It was downloaded from:
+
+ http://www.andreas-loibl.de/content/linux/projekte/acritoxinstaller/
+
+Upstream Author(s):
+
+ Andreas Loibl <andreas@andreas-loibl.de>
+
+Copyright:
+
+ Copyright (C) 2011 Andreas Loibl <andreas@andreas-loibl.de>
+
+License:
+
+ GPL, see "/usr/share/common-licenses/GPL".
+
+The Debian packaging is:
+
+ Copyright (C) 2011 Andreas Loibl <andreas@andreas-loibl.de>
+
+and is licensed under the GPL version 3,
+see "/usr/share/common-licenses/GPL-3".
+
+libqtermwidget is written by:
+
+ JK <e_k@users.sourceforge.net>
+
+and is licensed under the GPL version 2,
+see "/usr/share/common-licenses/GPL-2".
+
diff --git a/debian/rules b/debian/rules
new file mode 100755
index 0000000..fca5ea9
--- /dev/null
+++ b/debian/rules
@@ -0,0 +1,7 @@
+#!/usr/bin/make -f
+
+include /usr/share/cdbs/1/rules/debhelper.mk
+include /usr/share/cdbs/1/class/cmake.mk
+
+
+# Add here any variable or target overrides you need.
diff --git a/debian/source/format b/debian/source/format
new file mode 100644
index 0000000..89ae9db
--- /dev/null
+++ b/debian/source/format
@@ -0,0 +1 @@
+3.0 (native)
diff --git a/images/banner.png b/images/banner.png
new file mode 100644
index 0000000..3b366c4
--- /dev/null
+++ b/images/banner.png
Binary files differ
diff --git a/kanotix/acritoxinstaller.desktop b/kanotix/acritoxinstaller.desktop
new file mode 100644
index 0000000..676498b
--- /dev/null
+++ b/kanotix/acritoxinstaller.desktop
@@ -0,0 +1,10 @@
+[Desktop Entry]
+Encoding=UTF-8
+Name=acritoxinstaller
+Exec=acritoxinstaller
+Icon=acritoxinstaller
+Type=Application
+GenericName=Install Kanotix
+GenericName[de]=Kanotix installieren
+Comment=Install Kanotix
+Comment[de]=Kanotix installieren
diff --git a/kanotix/icons/128x128/apps/acritoxinstaller.png b/kanotix/icons/128x128/apps/acritoxinstaller.png
new file mode 100644
index 0000000..dea945c
--- /dev/null
+++ b/kanotix/icons/128x128/apps/acritoxinstaller.png
Binary files differ
diff --git a/kanotix/icons/16x16/apps/acritoxinstaller.png b/kanotix/icons/16x16/apps/acritoxinstaller.png
new file mode 100644
index 0000000..2ce1954
--- /dev/null
+++ b/kanotix/icons/16x16/apps/acritoxinstaller.png
Binary files differ
diff --git a/kanotix/icons/22x22/apps/acritoxinstaller.png b/kanotix/icons/22x22/apps/acritoxinstaller.png
new file mode 100644
index 0000000..1ee8be7
--- /dev/null
+++ b/kanotix/icons/22x22/apps/acritoxinstaller.png
Binary files differ
diff --git a/kanotix/icons/32x32/apps/acritoxinstaller.png b/kanotix/icons/32x32/apps/acritoxinstaller.png
new file mode 100644
index 0000000..b6beadd
--- /dev/null
+++ b/kanotix/icons/32x32/apps/acritoxinstaller.png
Binary files differ
diff --git a/kanotix/icons/48x48/apps/acritoxinstaller.png b/kanotix/icons/48x48/apps/acritoxinstaller.png
new file mode 100644
index 0000000..f920210
--- /dev/null
+++ b/kanotix/icons/48x48/apps/acritoxinstaller.png
Binary files differ
diff --git a/kanotix/icons/64x64/apps/acritoxinstaller.png b/kanotix/icons/64x64/apps/acritoxinstaller.png
new file mode 100644
index 0000000..b51b529
--- /dev/null
+++ b/kanotix/icons/64x64/apps/acritoxinstaller.png
Binary files differ
diff --git a/listdelegate.cpp b/listdelegate.cpp
new file mode 100644
index 0000000..5777813
--- /dev/null
+++ b/listdelegate.cpp
@@ -0,0 +1,77 @@
+#include "listdelegate.h"
+#include "listitem.h"
+#include <algorithm>
+#include <QModelIndex>
+#include <QPainter>
+#include <QApplication>
+#include <QTextDocument>
+#include <QIcon>
+
+ListDelegate::ListDelegate(QObject *parent)
+: QStyledItemDelegate(parent)
+{
+}
+
+void ListDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
+{
+ QString title = index.data(ListItem::ItemTitle).toString();
+ QString description = index.data(ListItem::ItemDescription).toString();
+ QIcon icon = QIcon::fromTheme(index.data(ListItem::ItemIcon).toString());
+// QVariant data = index.data(ListItem::ItemData);
+
+ painter->save();
+ QPalette p;
+ painter->fillRect(option.rect, p.brush((index.row() % 2 ) ? QPalette::Base : QPalette::AlternateBase));
+ QStyle *style = QApplication::style();
+ style->drawPrimitive(QStyle::PE_PanelItemViewItem, &option, painter);
+
+ QTextDocument doc;
+ painter->translate(option.rect.topLeft());
+ if (option.state & QStyle::State_Selected)
+ {
+ painter->setPen(option.palette.color(QPalette::Normal, QPalette::HighlightedText));
+ doc.setDefaultStyleSheet("* { color: "+option.palette.color(QPalette::HighlightedText).name()+"; }");
+ }
+ else
+ {
+ painter->setPen(option.palette.color(QPalette::Normal, QPalette::Text));
+ doc.setDefaultStyleSheet("* { color: "+option.palette.color(QPalette::Text).name()+"; }");
+ }
+
+ if(!icon.isNull())
+ {
+ painter->drawPixmap(5,5,64,64, icon.pixmap(QSize(64,64)));
+ }
+
+ QFont f = painter->font();
+ f.setBold(true);
+ painter->setFont(f);
+ painter->drawText(74,20, title);
+ f.setBold(false);
+
+ doc.setUndoRedoEnabled(false);
+ doc.setDocumentMargin(0);
+ doc.setTextWidth(option.rect.width()-79);
+ doc.setUseDesignMetrics(true);
+ doc.setHtml("<p>"+description+"</p>");
+ QRectF rect = QRectF(QPoint(0,0),doc.size());
+ painter->translate(74,20+QFontMetrics::QFontMetrics(f).height());
+ doc.drawContents(painter, rect);
+ painter->translate(-74,-20-QFontMetrics::QFontMetrics(f).height());
+
+ painter->restore();
+}
+
+QSize ListDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const
+{
+ QString description = index.data(ListItem::ItemDescription).toString();
+ QFont f = QApplication::font();
+ QTextDocument doc;
+ doc.setUndoRedoEnabled(false);
+ doc.setDocumentMargin(0);
+ doc.setTextWidth(option.rect.width()-79);
+ doc.setUseDesignMetrics(true);
+ doc.setHtml("<p>"+description+"</p>");
+ return QSize(option.rect.width()-10,std::max(69, int(25+QFontMetrics::QFontMetrics(f).height()+doc.size().height())));
+}
+
diff --git a/listdelegate.h b/listdelegate.h
new file mode 100644
index 0000000..d364b04
--- /dev/null
+++ b/listdelegate.h
@@ -0,0 +1,16 @@
+#ifndef LISTDELEGATE_H
+#define LISTDELEGATE_H
+
+#include <QStyledItemDelegate>
+
+class ListDelegate : public QStyledItemDelegate
+{
+ Q_OBJECT
+public:
+ ListDelegate(QObject *parent = 0);
+ void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const;
+ QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const;
+
+};
+
+#endif
diff --git a/listitem.cpp b/listitem.cpp
new file mode 100644
index 0000000..ea1de8e
--- /dev/null
+++ b/listitem.cpp
@@ -0,0 +1,10 @@
+#include "listitem.h"
+
+ListItem::ListItem(const QString &title, const QString &description, const QString &icon, const QVariant &data)
+: QListWidgetItem(0, UserType)
+{
+ setData(ItemTitle, title);
+ setData(ItemDescription, description);
+ setData(ItemIcon, icon);
+ setData(ItemData, data);
+}
diff --git a/listitem.h b/listitem.h
new file mode 100644
index 0000000..aacfbfb
--- /dev/null
+++ b/listitem.h
@@ -0,0 +1,19 @@
+#ifndef LISTITEM_H
+#define LISTITEM_H
+
+#include <QListWidgetItem>
+
+class ListItem : public QListWidgetItem
+{
+public:
+ enum DataRole
+ {
+ ItemTitle = 32,
+ ItemDescription = 33,
+ ItemIcon = 34,
+ ItemData = 35
+ };
+ ListItem(const QString &title, const QString &description, const QString &icon = "", const QVariant &data = QVariant());
+};
+
+#endif
diff --git a/main.cpp b/main.cpp
new file mode 100644
index 0000000..6423dd2
--- /dev/null
+++ b/main.cpp
@@ -0,0 +1,10 @@
+#include <QtGui/QApplication>
+#include "mainwizard.h"
+
+int main(int argc, char** argv)
+{
+ QApplication app(argc, argv);
+ MainWizard *l = new MainWizard;
+ l->show();
+ return app.exec();
+}
diff --git a/mainwizard.cpp b/mainwizard.cpp
new file mode 100644
index 0000000..25df6f1
--- /dev/null
+++ b/mainwizard.cpp
@@ -0,0 +1,84 @@
+#include <QtGui>
+#include "mainwizard.h"
+#include "listdelegate.h"
+#include "listitem.h"
+#include "wizard/welcome.h"
+#include "wizard/partitions.h"
+#include "wizard/partmansel.h"
+#include "wizard/partman.h"
+#include "wizard/rootpartition.h"
+#include "wizard/bootloader.h"
+#include "wizard/rootpwd.h"
+#include "wizard/usercfg.h"
+#include "wizard/userpwd.h"
+#include "wizard/network.h"
+#include "wizard/summary.h"
+#include "wizard/installation.h"
+
+MainWizard::MainWizard()
+{
+ backend = Backend::instance();
+ backend->runBackend();
+
+ connect(backend, SIGNAL(isBusy(bool)), this, SLOT(backendBusy(bool)));
+ connect(backend, SIGNAL(receivedCommand(QString,QString)), this, SLOT(processCommand(QString,QString)));
+
+ setPage(Page_Welcome, new wpWelcome(this));
+ setPage(Page_Partitions, new wpPartitions(this));
+ setPage(Page_PartManSel, new wpPartManSel(this));
+ setPage(Page_PartMan, new wpPartMan(this));
+ setPage(Page_RootPartition, new wpRootPartition(this));
+ setPage(Page_Bootloader, new wpBootloader(this));
+ setPage(Page_RootPwd, new wpRootPwd(this));
+ setPage(Page_UserCfg, new wpUserCfg(this));
+ setPage(Page_UserPwd, new wpUserPwd(this));
+ setPage(Page_Network, new wpNetwork(this));
+ setPage(Page_Summary, new wpSummary(this));
+ setPage(Page_Installation, new wpInstallation(this));
+ setStartId(Page_Welcome);
+ setWizardStyle(ModernStyle);
+ setWindowTitle(tr("Installer"));
+
+ filter = new BusyAppFilter;
+
+ setPixmap(QWizard::LogoPixmap, QIcon::fromTheme("acritoxinstaller").pixmap(64,64));
+}
+
+void MainWizard::reject()
+{
+ backend->exitBackend();
+ QWizard::reject();
+}
+
+void MainWizard::backendBusy(bool busy)
+{
+ qDebug((QString("MainWizard::backendBusy: ")+(busy ? "yes" : "no")).toUtf8());
+ if(!busy)
+ {
+ QApplication::restoreOverrideCursor();
+ QApplication::instance()->removeEventFilter(filter);
+ }
+ else if(!QApplication::overrideCursor())
+ {
+ QApplication::setOverrideCursor(Qt::WaitCursor);
+ QApplication::instance()->installEventFilter(filter);
+ }
+}
+
+void MainWizard::processCommand(QString command, QString args)
+{
+ if(command == "loadpage")
+ {
+ QList<int> pages = pageIds();
+ for(int i = 0; i < pages.size(); ++i)
+ if(page(i)->objectName() == args)
+ {
+ break;
+ }
+ }
+}
+
+QSize MainWizard::sizeHint() const
+{
+ return QSize(800,600);
+}
diff --git a/mainwizard.h b/mainwizard.h
new file mode 100644
index 0000000..9fa9839
--- /dev/null
+++ b/mainwizard.h
@@ -0,0 +1,39 @@
+#ifndef mainwizard_H
+#define mainwizard_H
+
+#include <QWizard>
+#include "backend.h"
+#include "busyappfilter.h"
+
+class MainWizard : public QWizard
+{
+ Q_OBJECT
+
+ public:
+ enum { Page_Welcome,
+ Page_Partitions,
+ Page_PartManSel,
+ Page_PartMan,
+ Page_RootPartition,
+ Page_Bootloader,
+ Page_RootPwd,
+ Page_UserCfg,
+ Page_UserPwd,
+ Page_Network,
+ Page_Summary,
+ Page_Installation };
+ MainWizard();
+ void reject();
+ Backend* backend;
+ QSize sizeHint() const;
+
+ private:
+ BusyAppFilter *filter;
+
+ private slots:
+ void processCommand(QString command, QString args);
+ void backendBusy(bool busy);
+
+};
+
+#endif // mainwizard_H
diff --git a/qtermwidget/AUTHORS b/qtermwidget/AUTHORS
new file mode 100644
index 0000000..bfa5fa3
--- /dev/null
+++ b/qtermwidget/AUTHORS
@@ -0,0 +1 @@
+e_k@users.sourceforge.net \ No newline at end of file
diff --git a/qtermwidget/BlockArray.cpp b/qtermwidget/BlockArray.cpp
new file mode 100644
index 0000000..39ef499
--- /dev/null
+++ b/qtermwidget/BlockArray.cpp
@@ -0,0 +1,337 @@
+/*
+ This file is part of Konsole, an X terminal.
+ Copyright (C) 2000 by Stephan Kulow <coolo@kde.org>
+
+ Rewritten for QT4 by e_k <e_k at users.sourceforge.net>, Copyright (C)2008
+
+ 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.
+
+*/
+
+// Own
+#include "BlockArray.h"
+
+#include <QtCore>
+
+// System
+#include <assert.h>
+#include <sys/mman.h>
+#include <sys/param.h>
+#include <unistd.h>
+#include <stdio.h>
+
+
+using namespace Konsole;
+
+static int blocksize = 0;
+
+BlockArray::BlockArray()
+ : size(0),
+ current(size_t(-1)),
+ index(size_t(-1)),
+ lastmap(0),
+ lastmap_index(size_t(-1)),
+ lastblock(0), ion(-1),
+ length(0)
+{
+ // lastmap_index = index = current = size_t(-1);
+ if (blocksize == 0)
+ blocksize = ((sizeof(Block) / getpagesize()) + 1) * getpagesize();
+
+}
+
+BlockArray::~BlockArray()
+{
+ setHistorySize(0);
+ assert(!lastblock);
+}
+
+size_t BlockArray::append(Block *block)
+{
+ if (!size)
+ return size_t(-1);
+
+ ++current;
+ if (current >= size) current = 0;
+
+ int rc;
+ rc = lseek(ion, current * blocksize, SEEK_SET); if (rc < 0) { perror("HistoryBuffer::add.seek"); setHistorySize(0); return size_t(-1); }
+ rc = write(ion, block, blocksize); if (rc < 0) { perror("HistoryBuffer::add.write"); setHistorySize(0); return size_t(-1); }
+
+ length++;
+ if (length > size) length = size;
+
+ ++index;
+
+ delete block;
+ return current;
+}
+
+size_t BlockArray::newBlock()
+{
+ if (!size)
+ return size_t(-1);
+ append(lastblock);
+
+ lastblock = new Block();
+ return index + 1;
+}
+
+Block *BlockArray::lastBlock() const
+{
+ return lastblock;
+}
+
+bool BlockArray::has(size_t i) const
+{
+ if (i == index + 1)
+ return true;
+
+ if (i > index)
+ return false;
+ if (index - i >= length)
+ return false;
+ return true;
+}
+
+const Block* BlockArray::at(size_t i)
+{
+ if (i == index + 1)
+ return lastblock;
+
+ if (i == lastmap_index)
+ return lastmap;
+
+ if (i > index) {
+ qDebug() << "BlockArray::at() i > index\n";
+ return 0;
+ }
+
+// if (index - i >= length) {
+// kDebug(1211) << "BlockArray::at() index - i >= length\n";
+// return 0;
+// }
+
+ size_t j = i; // (current - (index - i) + (index/size+1)*size) % size ;
+
+ assert(j < size);
+ unmap();
+
+ Block *block = (Block*)mmap(0, blocksize, PROT_READ, MAP_PRIVATE, ion, j * blocksize);
+
+ if (block == (Block*)-1) { perror("mmap"); return 0; }
+
+ lastmap = block;
+ lastmap_index = i;
+
+ return block;
+}
+
+void BlockArray::unmap()
+{
+ if (lastmap) {
+ int res = munmap((char*)lastmap, blocksize);
+ if (res < 0) perror("munmap");
+ }
+ lastmap = 0;
+ lastmap_index = size_t(-1);
+}
+
+bool BlockArray::setSize(size_t newsize)
+{
+ return setHistorySize(newsize * 1024 / blocksize);
+}
+
+bool BlockArray::setHistorySize(size_t newsize)
+{
+// kDebug(1211) << "setHistorySize " << size << " " << newsize;
+
+ if (size == newsize)
+ return false;
+
+ unmap();
+
+ if (!newsize) {
+ delete lastblock;
+ lastblock = 0;
+ if (ion >= 0) close(ion);
+ ion = -1;
+ current = size_t(-1);
+ return true;
+ }
+
+ if (!size) {
+ FILE* tmp = tmpfile();
+ if (!tmp) {
+ perror("konsole: cannot open temp file.\n");
+ } else {
+ ion = dup(fileno(tmp));
+ if (ion<0) {
+ perror("konsole: cannot dup temp file.\n");
+ fclose(tmp);
+ }
+ }
+ if (ion < 0)
+ return false;
+
+ assert(!lastblock);
+
+ lastblock = new Block();
+ size = newsize;
+ return false;
+ }
+
+ if (newsize > size) {
+ increaseBuffer();
+ size = newsize;
+ return false;
+ } else {
+ decreaseBuffer(newsize);
+ ftruncate(ion, length*blocksize);
+ size = newsize;
+
+ return true;
+ }
+}
+
+void moveBlock(FILE *fion, int cursor, int newpos, char *buffer2)
+{
+ int res = fseek(fion, cursor * blocksize, SEEK_SET);
+ if (res)
+ perror("fseek");
+ res = fread(buffer2, blocksize, 1, fion);
+ if (res != 1)
+ perror("fread");
+
+ res = fseek(fion, newpos * blocksize, SEEK_SET);
+ if (res)
+ perror("fseek");
+ res = fwrite(buffer2, blocksize, 1, fion);
+ if (res != 1)
+ perror("fwrite");
+ // printf("moving block %d to %d\n", cursor, newpos);
+}
+
+void BlockArray::decreaseBuffer(size_t newsize)
+{
+ if (index < newsize) // still fits in whole
+ return;
+
+ int offset = (current - (newsize - 1) + size) % size;
+
+ if (!offset)
+ return;
+
+ // The Block constructor could do somthing in future...
+ char *buffer1 = new char[blocksize];
+
+ FILE *fion = fdopen(dup(ion), "w+b");
+ if (!fion) {
+ delete [] buffer1;
+ perror("fdopen/dup");
+ return;
+ }
+
+ int firstblock;
+ if (current <= newsize) {
+ firstblock = current + 1;
+ } else {
+ firstblock = 0;
+ }
+
+ size_t oldpos;
+ for (size_t i = 0, cursor=firstblock; i < newsize; i++) {
+ oldpos = (size + cursor + offset) % size;
+ moveBlock(fion, oldpos, cursor, buffer1);
+ if (oldpos < newsize) {
+ cursor = oldpos;
+ } else
+ cursor++;
+ }
+
+ current = newsize - 1;
+ length = newsize;
+
+ delete [] buffer1;
+
+ fclose(fion);
+
+}
+
+void BlockArray::increaseBuffer()
+{
+ if (index < size) // not even wrapped once
+ return;
+
+ int offset = (current + size + 1) % size;
+ if (!offset) // no moving needed
+ return;
+
+ // The Block constructor could do somthing in future...
+ char *buffer1 = new char[blocksize];
+ char *buffer2 = new char[blocksize];
+
+ int runs = 1;
+ int bpr = size; // blocks per run
+
+ if (size % offset == 0) {
+ bpr = size / offset;
+ runs = offset;
+ }
+
+ FILE *fion = fdopen(dup(ion), "w+b");
+ if (!fion) {
+ perror("fdopen/dup");
+ delete [] buffer1;
+ delete [] buffer2;
+ return;
+ }
+
+ int res;
+ for (int i = 0; i < runs; i++)
+ {
+ // free one block in chain
+ int firstblock = (offset + i) % size;
+ res = fseek(fion, firstblock * blocksize, SEEK_SET);
+ if (res)
+ perror("fseek");
+ res = fread(buffer1, blocksize, 1, fion);
+ if (res != 1)
+ perror("fread");
+ int newpos = 0;
+ for (int j = 1, cursor=firstblock; j < bpr; j++)
+ {
+ cursor = (cursor + offset) % size;
+ newpos = (cursor - offset + size) % size;
+ moveBlock(fion, cursor, newpos, buffer2);
+ }
+ res = fseek(fion, i * blocksize, SEEK_SET);
+ if (res)
+ perror("fseek");
+ res = fwrite(buffer1, blocksize, 1, fion);
+ if (res != 1)
+ perror("fwrite");
+ }
+ current = size - 1;
+ length = size;
+
+ delete [] buffer1;
+ delete [] buffer2;
+
+ fclose(fion);
+
+}
+
diff --git a/qtermwidget/BlockArray.h b/qtermwidget/BlockArray.h
new file mode 100644
index 0000000..ca47388
--- /dev/null
+++ b/qtermwidget/BlockArray.h
@@ -0,0 +1,125 @@
+/*
+ This file is part of Konsole, an X terminal.
+ Copyright (C) 2000 by Stephan Kulow <coolo@kde.org>
+
+ Rewritten for QT4 by e_k <e_k at users.sourceforge.net>, Copyright (C)2008
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ 02110-1301 USA.
+*/
+
+#ifndef BLOCKARRAY_H
+#define BLOCKARRAY_H
+
+#include <unistd.h>
+
+//#error Do not use in KDE 2.1
+
+#define BlockSize (1 << 12)
+#define ENTRIES ((BlockSize - sizeof(size_t) ) / sizeof(unsigned char))
+
+namespace Konsole
+{
+
+struct Block {
+ Block() { size = 0; }
+ unsigned char data[ENTRIES];
+ size_t size;
+};
+
+// ///////////////////////////////////////////////////////
+
+class BlockArray {
+public:
+ /**
+ * Creates a history file for holding
+ * maximal size blocks. If more blocks
+ * are requested, then it drops earlier
+ * added ones.
+ */
+ BlockArray();
+
+ /// destructor
+ ~BlockArray();
+
+ /**
+ * adds the Block at the end of history.
+ * This may drop other blocks.
+ *
+ * The ownership on the block is transfered.
+ * An unique index number is returned for accessing
+ * it later (if not yet dropped then)
+ *
+ * Note, that the block may be dropped completely
+ * if history is turned off.
+ */
+ size_t append(Block *block);
+
+ /**
+ * gets the block at the index. Function may return
+ * 0 if the block isn't available any more.
+ *
+ * The returned block is strictly readonly as only
+ * maped in memory - and will be invalid on the next
+ * operation on this class.
+ */
+ const Block *at(size_t index);
+
+ /**
+ * reorders blocks as needed. If newsize is null,
+ * the history is emptied completely. The indices
+ * returned on append won't change their semantic,
+ * but they may not be valid after this call.
+ */
+ bool setHistorySize(size_t newsize);
+
+ size_t newBlock();
+
+ Block *lastBlock() const;
+
+ /**
+ * Convenient function to set the size in KBytes
+ * instead of blocks
+ */
+ bool setSize(size_t newsize);
+
+ size_t len() const { return length; }
+
+ bool has(size_t index) const;
+
+ size_t getCurrent() const { return current; }
+
+private:
+ void unmap();
+ void increaseBuffer();
+ void decreaseBuffer(size_t newsize);
+
+ size_t size;
+ // current always shows to the last inserted block
+ size_t current;
+ size_t index;
+
+ Block *lastmap;
+ size_t lastmap_index;
+ Block *lastblock;
+
+ int ion;
+ size_t length;
+
+};
+
+}
+
+#endif
diff --git a/qtermwidget/CMakeLists.txt b/qtermwidget/CMakeLists.txt
new file mode 100644
index 0000000..e864bc0
--- /dev/null
+++ b/qtermwidget/CMakeLists.txt
@@ -0,0 +1,27 @@
+# Set version number
+#set(QTERMWIDGET_VERSION_MAJOR 0)
+#set(QTERMWIDGET_VERSION_MINOR 1)
+#set(QTERMWIDGET_VERSION_RELEASE 0)
+#set(QTERMWIDGET_VERSION "${QTERMWIDGET_VERSION_MAJOR}.${QTERMWIDGET_VERSION_MINOR}.${QTERMWIDGET_VERSION_RELEASE}")
+
+add_definitions(${QT_DEFINITIONS})
+add_definitions(-DHAVE_POSIX_OPENPT)
+add_definitions(-DQT_PLUGIN)
+add_definitions(-DQT_NO_DEBUG)
+add_definitions(-DQT_SHARED)
+
+set(qtermwidget_SRCS TerminalCharacterDecoder.cpp KeyboardTranslator.cpp Screen.cpp History.cpp BlockArray.cpp konsole_wcwidth.cpp ScreenWindow.cpp Emulation.cpp Vt102Emulation.cpp TerminalDisplay.cpp Filter.cpp Pty.cpp kpty.cpp k3process.cpp k3processcontroller.cpp Session.cpp ShellCommand.cpp qtermwidget.cpp)
+set(qtermwidget_MOC_HDRS ScreenWindow.h Emulation.h Vt102Emulation.h TerminalDisplay.h Filter.h Pty.h k3process.h k3processcontroller.h Session.h qtermwidget.h)
+
+
+qt4_wrap_cpp(qtermwidget_MOC_SRCS ${qtermwidget_MOC_HDRS})
+include_directories(${CMAKE_CURRENT_SOURCE_DIR})
+add_library(qtermwidget SHARED ${qtermwidget_SRCS} ${qtermwidget_MOC_SRCS})
+target_link_libraries(qtermwidget ${QT_LIBRARIES})
+#set_target_properties(qtermwidget PROPERTIES
+# VERSION ${QTERMWIDGET_VERSION}
+# SOVERSION ${QTERMWIDGET_VERSION_MAJOR}
+# )
+
+install(TARGETS qtermwidget RUNTIME DESTINATION "${BIN_INSTALL_DIR}" LIBRARY DESTINATION "${LIB_INSTALL_DIR}")
+
diff --git a/qtermwidget/COPYING b/qtermwidget/COPYING
new file mode 100644
index 0000000..5b6e7c6
--- /dev/null
+++ b/qtermwidget/COPYING
@@ -0,0 +1,340 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/qtermwidget/Changelog b/qtermwidget/Changelog
new file mode 100644
index 0000000..291a3ff
--- /dev/null
+++ b/qtermwidget/Changelog
@@ -0,0 +1,19 @@
+31.07.2008
+Interface class from c-style conversions rewritten with pimpl support.
+
+
+16.07.2008
+Added optional scrollbar
+
+
+06.06.2008
+Some artefacts were removed, some added...
+Also added support for color schemes, and 3 color schemes provided (classical - white on black, green on black, black on light yellow). Is it enough or not?
+
+
+26.05.2008
+Added file release as an archive with source code. But preferrable way is still getting code from CVS, cause file release can be outdated.
+
+
+11.05.2008
+Initial CVS import - first version comes with number 0.0.1 \ No newline at end of file
diff --git a/qtermwidget/Character.h b/qtermwidget/Character.h
new file mode 100644
index 0000000..0978ce5
--- /dev/null
+++ b/qtermwidget/Character.h
@@ -0,0 +1,210 @@
+/*
+ This file is part of Konsole, KDE's terminal.
+
+ Copyright (C) 2007 by Robert Knight <robertknight@gmail.com>
+ Copyright (C) 1997,1998 by Lars Doelle <lars.doelle@on-line.de>
+
+ Rewritten for QT4 by e_k <e_k at users.sourceforge.net>, Copyright (C)2008
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ 02110-1301 USA.
+*/
+
+#ifndef CHARACTER_H
+#define CHARACTER_H
+
+// Qt
+#include <QtCore/QHash>
+
+// Local
+#include "CharacterColor.h"
+
+namespace Konsole
+{
+
+typedef unsigned char LineProperty;
+
+static const int LINE_DEFAULT = 0;
+static const int LINE_WRAPPED = (1 << 0);
+static const int LINE_DOUBLEWIDTH = (1 << 1);
+static const int LINE_DOUBLEHEIGHT = (1 << 2);
+
+#define DEFAULT_RENDITION 0
+#define RE_BOLD (1 << 0)
+#define RE_BLINK (1 << 1)
+#define RE_UNDERLINE (1 << 2)
+#define RE_REVERSE (1 << 3) // Screen only
+#define RE_INTENSIVE (1 << 3) // Widget only
+#define RE_CURSOR (1 << 4)
+#define RE_EXTENDED_CHAR (1 << 5)
+
+/**
+ * A single character in the terminal which consists of a unicode character
+ * value, foreground and background colors and a set of rendition attributes
+ * which specify how it should be drawn.
+ */
+class Character
+{
+public:
+ /**
+ * Constructs a new character.
+ *
+ * @param _c The unicode character value of this character.
+ * @param _f The foreground color used to draw the character.
+ * @param _b The color used to draw the character's background.
+ * @param _r A set of rendition flags which specify how this character is to be drawn.
+ */
+ inline Character(quint16 _c = ' ',
+ CharacterColor _f = CharacterColor(COLOR_SPACE_DEFAULT,DEFAULT_FORE_COLOR),
+ CharacterColor _b = CharacterColor(COLOR_SPACE_DEFAULT,DEFAULT_BACK_COLOR),
+ quint8 _r = DEFAULT_RENDITION)
+ : character(_c), rendition(_r), foregroundColor(_f), backgroundColor(_b) {}
+
+ union
+ {
+ /** The unicode character value for this character. */
+ quint16 character;
+ /**
+ * Experimental addition which allows a single Character instance to contain more than
+ * one unicode character.
+ *
+ * charSequence is a hash code which can be used to look up the unicode
+ * character sequence in the ExtendedCharTable used to create the sequence.
+ */
+ quint16 charSequence;
+ };
+
+ /** A combination of RENDITION flags which specify options for drawing the character. */
+ quint8 rendition;
+
+ /** The foreground color used to draw this character. */
+ CharacterColor foregroundColor;
+ /** The color used to draw this character's background. */
+ CharacterColor backgroundColor;
+
+ /**
+ * Returns true if this character has a transparent background when
+ * it is drawn with the specified @p palette.
+ */
+ bool isTransparent(const ColorEntry* palette) const;
+ /**
+ * Returns true if this character should always be drawn in bold when
+ * it is drawn with the specified @p palette, independent of whether
+ * or not the character has the RE_BOLD rendition flag.
+ */
+ bool isBold(const ColorEntry* base) const;
+
+ /**
+ * Compares two characters and returns true if they have the same unicode character value,
+ * rendition and colors.
+ */
+ friend bool operator == (const Character& a, const Character& b);
+ /**
+ * Compares two characters and returns true if they have different unicode character values,
+ * renditions or colors.
+ */
+ friend bool operator != (const Character& a, const Character& b);
+};
+
+inline bool operator == (const Character& a, const Character& b)
+{
+ return a.character == b.character &&
+ a.rendition == b.rendition &&
+ a.foregroundColor == b.foregroundColor &&
+ a.backgroundColor == b.backgroundColor;
+}
+
+inline bool operator != (const Character& a, const Character& b)
+{
+ return a.character != b.character ||
+ a.rendition != b.rendition ||
+ a.foregroundColor != b.foregroundColor ||
+ a.backgroundColor != b.backgroundColor;
+}
+
+inline bool Character::isTransparent(const ColorEntry* base) const
+{
+ return ((backgroundColor._colorSpace == COLOR_SPACE_DEFAULT) &&
+ base[backgroundColor._u+0+(backgroundColor._v?BASE_COLORS:0)].transparent)
+ || ((backgroundColor._colorSpace == COLOR_SPACE_SYSTEM) &&
+ base[backgroundColor._u+2+(backgroundColor._v?BASE_COLORS:0)].transparent);
+}
+
+inline bool Character::isBold(const ColorEntry* base) const
+{
+ return ((backgroundColor._colorSpace == COLOR_SPACE_DEFAULT) &&
+ base[backgroundColor._u+0+(backgroundColor._v?BASE_COLORS:0)].bold)
+ || ((backgroundColor._colorSpace == COLOR_SPACE_SYSTEM) &&
+ base[backgroundColor._u+2+(backgroundColor._v?BASE_COLORS:0)].bold);
+}
+
+extern unsigned short vt100_graphics[32];
+
+
+/**
+ * A table which stores sequences of unicode characters, referenced
+ * by hash keys. The hash key itself is the same size as a unicode
+ * character ( ushort ) so that it can occupy the same space in
+ * a structure.
+ */
+class ExtendedCharTable
+{
+public:
+ /** Constructs a new character table. */
+ ExtendedCharTable();
+ ~ExtendedCharTable();
+
+ /**
+ * Adds a sequences of unicode characters to the table and returns
+ * a hash code which can be used later to look up the sequence
+ * using lookupExtendedChar()
+ *
+ * If the same sequence already exists in the table, the hash
+ * of the existing sequence will be returned.
+ *
+ * @param unicodePoints An array of unicode character points
+ * @param length Length of @p unicodePoints
+ */
+ ushort createExtendedChar(ushort* unicodePoints , ushort length);
+ /**
+ * Looks up and returns a pointer to a sequence of unicode characters
+ * which was added to the table using createExtendedChar().
+ *
+ * @param hash The hash key returned by createExtendedChar()
+ * @param length This variable is set to the length of the
+ * character sequence.
+ *
+ * @return A unicode character sequence of size @p length.
+ */
+ ushort* lookupExtendedChar(ushort hash , ushort& length) const;
+
+ /** The global ExtendedCharTable instance. */
+ static ExtendedCharTable instance;
+private:
+ // calculates the hash key of a sequence of unicode points of size 'length'
+ ushort extendedCharHash(ushort* unicodePoints , ushort length) const;
+ // tests whether the entry in the table specified by 'hash' matches the
+ // character sequence 'unicodePoints' of size 'length'
+ bool extendedCharMatch(ushort hash , ushort* unicodePoints , ushort length) const;
+ // internal, maps hash keys to character sequence buffers. The first ushort
+ // in each value is the length of the buffer, followed by the ushorts in the buffer
+ // themselves.
+ QHash<ushort,ushort*> extendedCharTable;
+};
+
+}
+
+#endif // CHARACTER_H
+
diff --git a/qtermwidget/CharacterColor.h b/qtermwidget/CharacterColor.h
new file mode 100644
index 0000000..1b86674
--- /dev/null
+++ b/qtermwidget/CharacterColor.h
@@ -0,0 +1,301 @@
+/*
+ This file is part of Konsole, KDE's terminal.
+
+ Copyright (C) 2007 by Robert Knight <robertknight@gmail.com>
+ Copyright (C) 1997,1998 by Lars Doelle <lars.doelle@on-line.de>
+
+ Rewritten for QT4 by e_k <e_k at users.sourceforge.net>, Copyright (C)2008
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ 02110-1301 USA.
+*/
+
+#ifndef CHARACTERCOLOR_H
+#define CHARACTERCOLOR_H
+
+// Qt
+#include <QtGui/QColor>
+
+namespace Konsole
+{
+
+/**
+ * An entry in a terminal display's color palette.
+ *
+ * A color palette is an array of 16 ColorEntry instances which map
+ * system color indexes (from 0 to 15) into actual colors.
+ *
+ * Each entry can be set as bold, in which case any text
+ * drawn using the color should be drawn in bold.
+ *
+ * Each entry can also be transparent, in which case the terminal
+ * display should avoid drawing the background for any characters
+ * using the entry as a background.
+ */
+class ColorEntry
+{
+public:
+ /**
+ * Constructs a new color palette entry.
+ *
+ * @param c The color value for this entry.
+ * @param tr Specifies that the color should be transparent when used as a background color.
+ * @param b Specifies that text drawn with this color should be bold.
+ */
+ ColorEntry(QColor c, bool tr, bool b) : color(c), transparent(tr), bold(b) {}
+
+ /**
+ * Constructs a new color palette entry with an undefined color, and
+ * with the transparent and bold flags set to false.
+ */
+ ColorEntry() : transparent(false), bold(false) {}
+
+ /**
+ * Sets the color, transparency and boldness of this color to those of @p rhs.
+ */
+ void operator=(const ColorEntry& rhs)
+ {
+ color = rhs.color;
+ transparent = rhs.transparent;
+ bold = rhs.bold;
+ }
+
+ /** The color value of this entry for display. */
+ QColor color;
+
+ /**
+ * If true character backgrounds using this color should be transparent.
+ * This is not applicable when the color is used to render text.
+ */
+ bool transparent;
+ /**
+ * If true characters drawn using this color should be bold.
+ * This is not applicable when the color is used to draw a character's background.
+ */
+ bool bold;
+};
+
+
+// Attributed Character Representations ///////////////////////////////
+
+// Colors
+
+#define BASE_COLORS (2+8)
+#define INTENSITIES 2
+#define TABLE_COLORS (INTENSITIES*BASE_COLORS)
+
+#define DEFAULT_FORE_COLOR 0
+#define DEFAULT_BACK_COLOR 1
+
+//a standard set of colors using black text on a white background.
+//defined in TerminalDisplay.cpp
+
+static const ColorEntry base_color_table[TABLE_COLORS] =
+// The following are almost IBM standard color codes, with some slight
+// gamma correction for the dim colors to compensate for bright X screens.
+// It contains the 8 ansiterm/xterm colors in 2 intensities.
+{
+ // Fixme: could add faint colors here, also.
+ // normal
+ ColorEntry(QColor(0x00,0x00,0x00), 0, 0 ), ColorEntry( QColor(0xB2,0xB2,0xB2), 1, 0 ), // Dfore, Dback
+ ColorEntry(QColor(0x00,0x00,0x00), 0, 0 ), ColorEntry( QColor(0xB2,0x18,0x18), 0, 0 ), // Black, Red
+ ColorEntry(QColor(0x18,0xB2,0x18), 0, 0 ), ColorEntry( QColor(0xB2,0x68,0x18), 0, 0 ), // Green, Yellow
+ ColorEntry(QColor(0x18,0x18,0xB2), 0, 0 ), ColorEntry( QColor(0xB2,0x18,0xB2), 0, 0 ), // Blue, Magenta
+ ColorEntry(QColor(0x18,0xB2,0xB2), 0, 0 ), ColorEntry( QColor(0xB2,0xB2,0xB2), 0, 0 ), // Cyan, White
+ // intensiv
+ ColorEntry(QColor(0x00,0x00,0x00), 0, 1 ), ColorEntry( QColor(0xFF,0xFF,0xFF), 1, 0 ),
+ ColorEntry(QColor(0x68,0x68,0x68), 0, 0 ), ColorEntry( QColor(0xFF,0x54,0x54), 0, 0 ),
+ ColorEntry(QColor(0x54,0xFF,0x54), 0, 0 ), ColorEntry( QColor(0xFF,0xFF,0x54), 0, 0 ),
+ ColorEntry(QColor(0x54,0x54,0xFF), 0, 0 ), ColorEntry( QColor(0xFF,0x54,0xFF), 0, 0 ),
+ ColorEntry(QColor(0x54,0xFF,0xFF), 0, 0 ), ColorEntry( QColor(0xFF,0xFF,0xFF), 0, 0 )
+};
+
+/* CharacterColor is a union of the various color spaces.
+
+ Assignment is as follows:
+
+ Type - Space - Values
+
+ 0 - Undefined - u: 0, v:0 w:0
+ 1 - Default - u: 0..1 v:intense w:0
+ 2 - System - u: 0..7 v:intense w:0
+ 3 - Index(256) - u: 16..255 v:0 w:0
+ 4 - RGB - u: 0..255 v:0..256 w:0..256
+
+ Default colour space has two separate colours, namely
+ default foreground and default background colour.
+*/
+
+#define COLOR_SPACE_UNDEFINED 0
+#define COLOR_SPACE_DEFAULT 1
+#define COLOR_SPACE_SYSTEM 2
+#define COLOR_SPACE_256 3
+#define COLOR_SPACE_RGB 4
+
+/**
+ * Describes the color of a single character in the terminal.
+ */
+class CharacterColor
+{
+ friend class Character;
+
+public:
+ /** Constructs a new CharacterColor whoose color and color space are undefined. */
+ CharacterColor()
+ : _colorSpace(COLOR_SPACE_UNDEFINED),
+ _u(0),
+ _v(0),
+ _w(0)
+ {}
+
+ /**
+ * Constructs a new CharacterColor using the specified @p colorSpace and with
+ * color value @p co
+ *
+ * The meaning of @p co depends on the @p colorSpace used.
+ *
+ * TODO : Document how @p co relates to @p colorSpace
+ *
+ * TODO : Add documentation about available color spaces.
+ */
+ CharacterColor(quint8 colorSpace, int co)
+ : _colorSpace(colorSpace),
+ _u(0),
+ _v(0),
+ _w(0)
+ {
+ switch (colorSpace)
+ {
+ case COLOR_SPACE_DEFAULT:
+ _u = co & 1;
+ break;
+ case COLOR_SPACE_SYSTEM:
+ _u = co & 7;
+ _v = (co >> 3) & 1;
+ break;
+ case COLOR_SPACE_256:
+ _u = co & 255;
+ break;
+ case COLOR_SPACE_RGB:
+ _u = co >> 16;
+ _v = co >> 8;
+ _w = co;
+ break;
+ default:
+ _colorSpace = COLOR_SPACE_UNDEFINED;
+ }
+ }
+
+ /**
+ * Returns true if this character color entry is valid.
+ */
+ bool isValid()
+ {
+ return _colorSpace != COLOR_SPACE_UNDEFINED;
+ }
+
+ /**
+ * Toggles the value of this color between a normal system color and the corresponding intensive
+ * system color.
+ *
+ * This is only applicable if the color is using the COLOR_SPACE_DEFAULT or COLOR_SPACE_SYSTEM
+ * color spaces.
+ */
+ void toggleIntensive();
+
+ /**
+ * Returns the color within the specified color @palette
+ *
+ * The @p palette is only used if this color is one of the 16 system colors, otherwise
+ * it is ignored.
+ */
+ QColor color(const ColorEntry* palette) const;
+
+ /**
+ * Compares two colors and returns true if they represent the same color value and
+ * use the same color space.
+ */
+ friend bool operator == (const CharacterColor& a, const CharacterColor& b);
+ /**
+ * Compares two colors and returns true if they represent different color values
+ * or use different color spaces.
+ */
+ friend bool operator != (const CharacterColor& a, const CharacterColor& b);
+
+private:
+ quint8 _colorSpace;
+
+ // bytes storing the character color
+ quint8 _u;
+ quint8 _v;
+ quint8 _w;
+};
+
+inline bool operator == (const CharacterColor& a, const CharacterColor& b)
+{
+ return *reinterpret_cast<const quint32*>(&a._colorSpace) ==
+ *reinterpret_cast<const quint32*>(&b._colorSpace);
+}
+
+inline bool operator != (const CharacterColor& a, const CharacterColor& b)
+{
+ return *reinterpret_cast<const quint32*>(&a._colorSpace) !=
+ *reinterpret_cast<const quint32*>(&b._colorSpace);
+}
+
+inline const QColor color256(quint8 u, const ColorEntry* base)
+{
+ // 0.. 16: system colors
+ if (u < 8) return base[u+2 ].color; u -= 8;
+ if (u < 8) return base[u+2+BASE_COLORS].color; u -= 8;
+
+ // 16..231: 6x6x6 rgb color cube
+ if (u < 216) return QColor(255*((u/36)%6)/5,
+ 255*((u/ 6)%6)/5,
+ 255*((u/ 1)%6)/5); u -= 216;
+
+ // 232..255: gray, leaving out black and white
+ int gray = u*10+8; return QColor(gray,gray,gray);
+}
+
+inline QColor CharacterColor::color(const ColorEntry* base) const
+{
+ switch (_colorSpace)
+ {
+ case COLOR_SPACE_DEFAULT: return base[_u+0+(_v?BASE_COLORS:0)].color;
+ case COLOR_SPACE_SYSTEM: return base[_u+2+(_v?BASE_COLORS:0)].color;
+ case COLOR_SPACE_256: return color256(_u,base);
+ case COLOR_SPACE_RGB: return QColor(_u,_v,_w);
+ case COLOR_SPACE_UNDEFINED: return QColor();
+ }
+
+ Q_ASSERT(false); // invalid color space
+
+ return QColor();
+}
+
+inline void CharacterColor::toggleIntensive()
+{
+ if (_colorSpace == COLOR_SPACE_SYSTEM || _colorSpace == COLOR_SPACE_DEFAULT)
+ {
+ _v = !_v;
+ }
+}
+
+
+}
+
+#endif // CHARACTERCOLOR_H
+
diff --git a/qtermwidget/ColorTables.h b/qtermwidget/ColorTables.h
new file mode 100644
index 0000000..321d6db
--- /dev/null
+++ b/qtermwidget/ColorTables.h
@@ -0,0 +1,58 @@
+#ifndef _COLOR_TABLE_H
+#define _COLOR_TABLE_H
+
+#include "CharacterColor.h"
+
+using namespace Konsole;
+
+static const ColorEntry whiteonblack_color_table[TABLE_COLORS] =
+{
+ // normal
+ ColorEntry(QColor(0xFF,0xFF,0xFF), 0, 0 ), ColorEntry( QColor(0x00,0x00,0x00), 1, 0 ), // Dfore, Dback
+ ColorEntry(QColor(0x00,0x00,0x00), 0, 0 ), ColorEntry( QColor(0xB2,0x18,0x18), 0, 0 ), // Black, Red
+ ColorEntry(QColor(0x18,0xB2,0x18), 0, 0 ), ColorEntry( QColor(0xB2,0x68,0x18), 0, 0 ), // Green, Yellow
+ ColorEntry(QColor(0x18,0x18,0xB2), 0, 0 ), ColorEntry( QColor(0xB2,0x18,0xB2), 0, 0 ), // Blue, Magenta
+ ColorEntry(QColor(0x18,0xB2,0xB2), 0, 0 ), ColorEntry( QColor(0xB2,0xB2,0xB2), 0, 0 ), // Cyan, White
+ // intensiv
+ ColorEntry(QColor(0x00,0x00,0x00), 0, 1 ), ColorEntry( QColor(0xFF,0xFF,0xFF), 1, 0 ),
+ ColorEntry(QColor(0x68,0x68,0x68), 0, 0 ), ColorEntry( QColor(0xFF,0x54,0x54), 0, 0 ),
+ ColorEntry(QColor(0x54,0xFF,0x54), 0, 0 ), ColorEntry( QColor(0xFF,0xFF,0x54), 0, 0 ),
+ ColorEntry(QColor(0x54,0x54,0xFF), 0, 0 ), ColorEntry( QColor(0xFF,0x54,0xFF), 0, 0 ),
+ ColorEntry(QColor(0x54,0xFF,0xFF), 0, 0 ), ColorEntry( QColor(0xFF,0xFF,0xFF), 0, 0 )
+};
+
+static const ColorEntry greenonblack_color_table[TABLE_COLORS] =
+{
+ ColorEntry(QColor( 24, 240, 24), 0, 0), ColorEntry(QColor( 0, 0, 0), 1, 0),
+ ColorEntry(QColor( 0, 0, 0), 0, 0), ColorEntry(QColor( 178, 24, 24), 0, 0),
+ ColorEntry(QColor( 24, 178, 24), 0, 0), ColorEntry(QColor( 178, 104, 24), 0, 0),
+ ColorEntry(QColor( 24, 24, 178), 0, 0), ColorEntry(QColor( 178, 24, 178), 0, 0),
+ ColorEntry(QColor( 24, 178, 178), 0, 0), ColorEntry(QColor( 178, 178, 178), 0, 0),
+ // intensive colors
+ ColorEntry(QColor( 24, 240, 24), 0, 1 ), ColorEntry(QColor( 0, 0, 0), 1, 0 ),
+ ColorEntry(QColor( 104, 104, 104), 0, 0 ), ColorEntry(QColor( 255, 84, 84), 0, 0 ),
+ ColorEntry(QColor( 84, 255, 84), 0, 0 ), ColorEntry(QColor( 255, 255, 84), 0, 0 ),
+ ColorEntry(QColor( 84, 84, 255), 0, 0 ), ColorEntry(QColor( 255, 84, 255), 0, 0 ),
+ ColorEntry(QColor( 84, 255, 255), 0, 0 ), ColorEntry(QColor( 255, 255, 255), 0, 0 )
+};
+
+static const ColorEntry blackonlightyellow_color_table[TABLE_COLORS] =
+{
+ ColorEntry(QColor( 0, 0, 0), 0, 0), ColorEntry(QColor( 255, 255, 221), 1, 0),
+ ColorEntry(QColor( 0, 0, 0), 0, 0), ColorEntry(QColor( 178, 24, 24), 0, 0),
+ ColorEntry(QColor( 24, 178, 24), 0, 0), ColorEntry(QColor( 178, 104, 24), 0, 0),
+ ColorEntry(QColor( 24, 24, 178), 0, 0), ColorEntry(QColor( 178, 24, 178), 0, 0),
+ ColorEntry(QColor( 24, 178, 178), 0, 0), ColorEntry(QColor( 178, 178, 178), 0, 0),
+ ColorEntry(QColor( 0, 0, 0), 0, 1), ColorEntry(QColor( 255, 255, 221), 1, 0),
+ ColorEntry(QColor(104, 104, 104), 0, 0), ColorEntry(QColor( 255, 84, 84), 0, 0),
+ ColorEntry(QColor( 84, 255, 84), 0, 0), ColorEntry(QColor( 255, 255, 84), 0, 0),
+ ColorEntry(QColor( 84, 84, 255), 0, 0), ColorEntry(QColor( 255, 84, 255), 0, 0),
+ ColorEntry(QColor( 84, 255, 255), 0, 0), ColorEntry(QColor( 255, 255, 255), 0, 0)
+};
+
+
+
+
+
+#endif
+
diff --git a/qtermwidget/DefaultTranslatorText.h b/qtermwidget/DefaultTranslatorText.h
new file mode 100644
index 0000000..e47417c
--- /dev/null
+++ b/qtermwidget/DefaultTranslatorText.h
@@ -0,0 +1,2 @@
+"keyboard \"Fallback Key Translator\"\n"
+"key Tab : \"\\t\" \0"
diff --git a/qtermwidget/Emulation.cpp b/qtermwidget/Emulation.cpp
new file mode 100644
index 0000000..e767a42
--- /dev/null
+++ b/qtermwidget/Emulation.cpp
@@ -0,0 +1,543 @@
+/*
+ This file is part of Konsole, an X terminal.
+
+ Copyright (C) 2007 Robert Knight <robertknight@gmail.com>
+ Copyright (C) 1997,1998 by Lars Doelle <lars.doelle@on-line.de>
+ Copyright (C) 1996 by Matthias Ettrich <ettrich@kde.org>
+
+ Rewritten for QT4 by e_k <e_k at users.sourceforge.net>, Copyright (C)2008
+
+ 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.
+*/
+
+// Own
+#include "Emulation.h"
+
+// System
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+// Qt
+#include <QtGui/QApplication>
+#include <QtGui/QClipboard>
+#include <QtCore/QHash>
+#include <QtGui/QKeyEvent>
+#include <QtCore/QRegExp>
+#include <QtCore/QTextStream>
+#include <QtCore/QThread>
+
+#include <QtCore/QTime>
+
+// Konsole
+#include "KeyboardTranslator.h"
+#include "Screen.h"
+#include "TerminalCharacterDecoder.h"
+#include "ScreenWindow.h"
+
+using namespace Konsole;
+
+/* ------------------------------------------------------------------------- */
+/* */
+/* Emulation */
+/* */
+/* ------------------------------------------------------------------------- */
+
+//#define CNTL(c) ((c)-'@')
+
+/*!
+*/
+
+Emulation::Emulation() :
+ _currentScreen(0),
+ _codec(0),
+ _decoder(0),
+ _keyTranslator(0),
+ _usesMouse(false)
+{
+
+ // create screens with a default size
+ _screen[0] = new Screen(40,80);
+ _screen[1] = new Screen(40,80);
+ _currentScreen = _screen[0];
+
+ QObject::connect(&_bulkTimer1, SIGNAL(timeout()), this, SLOT(showBulk()) );
+ QObject::connect(&_bulkTimer2, SIGNAL(timeout()), this, SLOT(showBulk()) );
+
+ // listen for mouse status changes
+ connect( this , SIGNAL(programUsesMouseChanged(bool)) ,
+ SLOT(usesMouseChanged(bool)) );
+}
+
+bool Emulation::programUsesMouse() const
+{
+ return _usesMouse;
+}
+
+void Emulation::usesMouseChanged(bool usesMouse)
+{
+ _usesMouse = usesMouse;
+}
+
+ScreenWindow* Emulation::createWindow()
+{
+ ScreenWindow* window = new ScreenWindow();
+ window->setScreen(_currentScreen);
+ _windows << window;
+
+ connect(window , SIGNAL(selectionChanged()),
+ this , SLOT(bufferedUpdate()));
+
+ connect(this , SIGNAL(outputChanged()),
+ window , SLOT(notifyOutputChanged()) );
+ return window;
+}
+
+/*!
+*/
+
+Emulation::~Emulation()
+{
+ QListIterator<ScreenWindow*> windowIter(_windows);
+
+ while (windowIter.hasNext())
+ {
+ delete windowIter.next();
+ }
+
+ delete _screen[0];
+ delete _screen[1];
+ delete _decoder;
+}
+
+/*! change between primary and alternate _screen
+*/
+
+void Emulation::setScreen(int n)
+{
+ Screen *old = _currentScreen;
+ _currentScreen = _screen[n&1];
+ if (_currentScreen != old)
+ {
+ old->setBusySelecting(false);
+
+ // tell all windows onto this emulation to switch to the newly active _screen
+ QListIterator<ScreenWindow*> windowIter(_windows);
+ while ( windowIter.hasNext() )
+ {
+ windowIter.next()->setScreen(_currentScreen);
+ }
+ }
+}
+
+void Emulation::clearHistory()
+{
+ _screen[0]->setScroll( _screen[0]->getScroll() , false );
+}
+void Emulation::setHistory(const HistoryType& t)
+{
+ _screen[0]->setScroll(t);
+
+ showBulk();
+}
+
+const HistoryType& Emulation::history()
+{
+ return _screen[0]->getScroll();
+}
+
+void Emulation::setCodec(const QTextCodec * qtc)
+{
+ Q_ASSERT( qtc );
+
+ _codec = qtc;
+ delete _decoder;
+ _decoder = _codec->makeDecoder();
+
+ emit useUtf8Request(utf8());
+}
+
+void Emulation::setCodec(EmulationCodec codec)
+{
+ if ( codec == Utf8Codec )
+ setCodec( QTextCodec::codecForName("utf8") );
+ else if ( codec == LocaleCodec )
+ setCodec( QTextCodec::codecForLocale() );
+}
+
+void Emulation::setKeyBindings(const QString& name)
+{
+ _keyTranslator = KeyboardTranslatorManager::instance()->findTranslator(name);
+}
+
+QString Emulation::keyBindings()
+{
+ return _keyTranslator->name();
+}
+
+
+// Interpreting Codes ---------------------------------------------------------
+
+/*
+ This section deals with decoding the incoming character stream.
+ Decoding means here, that the stream is first separated into `tokens'
+ which are then mapped to a `meaning' provided as operations by the
+ `Screen' class.
+*/
+
+/*!
+*/
+
+void Emulation::receiveChar(int c)
+// process application unicode input to terminal
+// this is a trivial scanner
+{
+ c &= 0xff;
+ switch (c)
+ {
+ case '\b' : _currentScreen->BackSpace(); break;
+ case '\t' : _currentScreen->Tabulate(); break;
+ case '\n' : _currentScreen->NewLine(); break;
+ case '\r' : _currentScreen->Return(); break;
+ case 0x07 : emit stateSet(NOTIFYBELL);
+ break;
+ default : _currentScreen->ShowCharacter(c); break;
+ };
+}
+
+/* ------------------------------------------------------------------------- */
+/* */
+/* Keyboard Handling */
+/* */
+/* ------------------------------------------------------------------------- */
+
+/*!
+*/
+
+void Emulation::sendKeyEvent( QKeyEvent* ev )
+{
+ emit stateSet(NOTIFYNORMAL);
+
+ if (!ev->text().isEmpty())
+ { // A block of text
+ // Note that the text is proper unicode.
+ // We should do a conversion here, but since this
+ // routine will never be used, we simply emit plain ascii.
+ //emit sendBlock(ev->text().toAscii(),ev->text().length());
+ emit sendData(ev->text().toUtf8(),ev->text().length());
+ }
+}
+
+void Emulation::sendString(const char*,int)
+{
+ // default implementation does nothing
+}
+
+void Emulation::sendMouseEvent(int /*buttons*/, int /*column*/, int /*row*/, int /*eventType*/)
+{
+ // default implementation does nothing
+}
+
+// Unblocking, Byte to Unicode translation --------------------------------- --
+
+/*
+ We are doing code conversion from locale to unicode first.
+TODO: Character composition from the old code. See #96536
+*/
+
+void Emulation::receiveData(const char* text, int length)
+{
+ emit stateSet(NOTIFYACTIVITY);
+
+ bufferedUpdate();
+
+ QString unicodeText = _decoder->toUnicode(text,length);
+
+ //send characters to terminal emulator
+ for (int i=0;i<unicodeText.length();i++)
+ {
+ receiveChar(unicodeText[i].unicode());
+ }
+
+ //look for z-modem indicator
+ //-- someone who understands more about z-modems that I do may be able to move
+ //this check into the above for loop?
+ for (int i=0;i<length;i++)
+ {
+ if (text[i] == '\030')
+ {
+ if ((length-i-1 > 3) && (strncmp(text+i+1, "B00", 3) == 0))
+ emit zmodemDetected();
+ }
+ }
+}
+
+//OLDER VERSION
+//This version of onRcvBlock was commented out because
+// a) It decoded incoming characters one-by-one, which is slow in the current version of Qt (4.2 tech preview)
+// b) It messed up decoding of non-ASCII characters, with the result that (for example) chinese characters
+// were not printed properly.
+//
+//There is something about stopping the _decoder if "we get a control code halfway a multi-byte sequence" (see below)
+//which hasn't been ported into the newer function (above). Hopefully someone who understands this better
+//can find an alternative way of handling the check.
+
+
+/*void Emulation::onRcvBlock(const char *s, int len)
+{
+ emit notifySessionState(NOTIFYACTIVITY);
+
+ bufferedUpdate();
+ for (int i = 0; i < len; i++)
+ {
+
+ QString result = _decoder->toUnicode(&s[i],1);
+ int reslen = result.length();
+
+ // If we get a control code halfway a multi-byte sequence
+ // we flush the _decoder and continue with the control code.
+ if ((s[i] < 32) && (s[i] > 0))
+ {
+ // Flush _decoder
+ while(!result.length())
+ result = _decoder->toUnicode(&s[i],1);
+ reslen = 1;
+ result.resize(reslen);
+ result[0] = QChar(s[i]);
+ }
+
+ for (int j = 0; j < reslen; j++)
+ {
+ if (result[j].characterategory() == QChar::Mark_NonSpacing)
+ _currentScreen->compose(result.mid(j,1));
+ else
+ onRcvChar(result[j].unicode());
+ }
+ if (s[i] == '\030')
+ {
+ if ((len-i-1 > 3) && (strncmp(s+i+1, "B00", 3) == 0))
+ emit zmodemDetected();
+ }
+ }
+}*/
+
+// Selection --------------------------------------------------------------- --
+
+#if 0
+void Emulation::onSelectionBegin(const int x, const int y, const bool columnmode) {
+ if (!connected) return;
+ _currentScreen->setSelectionStart( x,y,columnmode);
+ showBulk();
+}
+
+void Emulation::onSelectionExtend(const int x, const int y) {
+ if (!connected) return;
+ _currentScreen->setSelectionEnd(x,y);
+ showBulk();
+}
+
+void Emulation::setSelection(const bool preserve_line_breaks) {
+ if (!connected) return;
+ QString t = _currentScreen->selectedText(preserve_line_breaks);
+ if (!t.isNull())
+ {
+ QListIterator< TerminalDisplay* > viewIter(_views);
+
+ while (viewIter.hasNext())
+ viewIter.next()->setSelection(t);
+ }
+}
+
+void Emulation::testIsSelected(const int x, const int y, bool &selected)
+{
+ if (!connected) return;
+ selected=_currentScreen->isSelected(x,y);
+}
+
+void Emulation::clearSelection() {
+ if (!connected) return;
+ _currentScreen->clearSelection();
+ showBulk();
+}
+
+#endif
+
+void Emulation::writeToStream( TerminalCharacterDecoder* _decoder ,
+ int startLine ,
+ int endLine)
+{
+ _currentScreen->writeToStream(_decoder,startLine,endLine);
+}
+
+int Emulation::lineCount()
+{
+ // sum number of lines currently on _screen plus number of lines in history
+ return _currentScreen->getLines() + _currentScreen->getHistLines();
+}
+
+// Refreshing -------------------------------------------------------------- --
+
+#define BULK_TIMEOUT1 10
+#define BULK_TIMEOUT2 40
+
+/*!
+*/
+void Emulation::showBulk()
+{
+ _bulkTimer1.stop();
+ _bulkTimer2.stop();
+
+ emit outputChanged();
+
+ _currentScreen->resetScrolledLines();
+ _currentScreen->resetDroppedLines();
+}
+
+void Emulation::bufferedUpdate()
+{
+ _bulkTimer1.setSingleShot(true);
+ _bulkTimer1.start(BULK_TIMEOUT1);
+ if (!_bulkTimer2.isActive())
+ {
+ _bulkTimer2.setSingleShot(true);
+ _bulkTimer2.start(BULK_TIMEOUT2);
+ }
+}
+
+char Emulation::getErase() const
+{
+ return '\b';
+}
+
+void Emulation::setImageSize(int lines, int columns)
+{
+ //kDebug() << "Resizing image to: " << lines << "by" << columns << QTime::currentTime().msec();
+ Q_ASSERT( lines > 0 );
+ Q_ASSERT( columns > 0 );
+
+ _screen[0]->resizeImage(lines,columns);
+ _screen[1]->resizeImage(lines,columns);
+
+ emit imageSizeChanged(lines,columns);
+
+ bufferedUpdate();
+}
+
+QSize Emulation::imageSize()
+{
+ return QSize(_currentScreen->getColumns(), _currentScreen->getLines());
+}
+
+ushort ExtendedCharTable::extendedCharHash(ushort* unicodePoints , ushort length) const
+{
+ ushort hash = 0;
+ for ( ushort i = 0 ; i < length ; i++ )
+ {
+ hash = 31*hash + unicodePoints[i];
+ }
+ return hash;
+}
+bool ExtendedCharTable::extendedCharMatch(ushort hash , ushort* unicodePoints , ushort length) const
+{
+ ushort* entry = extendedCharTable[hash];
+
+ // compare given length with stored sequence length ( given as the first ushort in the
+ // stored buffer )
+ if ( entry == 0 || entry[0] != length )
+ return false;
+ // if the lengths match, each character must be checked. the stored buffer starts at
+ // entry[1]
+ for ( int i = 0 ; i < length ; i++ )
+ {
+ if ( entry[i+1] != unicodePoints[i] )
+ return false;
+ }
+ return true;
+}
+ushort ExtendedCharTable::createExtendedChar(ushort* unicodePoints , ushort length)
+{
+ // look for this sequence of points in the table
+ ushort hash = extendedCharHash(unicodePoints,length);
+
+ // check existing entry for match
+ while ( extendedCharTable.contains(hash) )
+ {
+ if ( extendedCharMatch(hash,unicodePoints,length) )
+ {
+ // this sequence already has an entry in the table,
+ // return its hash
+ return hash;
+ }
+ else
+ {
+ // if hash is already used by another, different sequence of unicode character
+ // points then try next hash
+ hash++;
+ }
+ }
+
+
+ // add the new sequence to the table and
+ // return that index
+ ushort* buffer = new ushort[length+1];
+ buffer[0] = length;
+ for ( int i = 0 ; i < length ; i++ )
+ buffer[i+1] = unicodePoints[i];
+
+ extendedCharTable.insert(hash,buffer);
+
+ return hash;
+}
+
+ushort* ExtendedCharTable::lookupExtendedChar(ushort hash , ushort& length) const
+{
+ // lookup index in table and if found, set the length
+ // argument and return a pointer to the character sequence
+
+ ushort* buffer = extendedCharTable[hash];
+ if ( buffer )
+ {
+ length = buffer[0];
+ return buffer+1;
+ }
+ else
+ {
+ length = 0;
+ return 0;
+ }
+}
+
+ExtendedCharTable::ExtendedCharTable()
+{
+}
+ExtendedCharTable::~ExtendedCharTable()
+{
+ // free all allocated character buffers
+ QHashIterator<ushort,ushort*> iter(extendedCharTable);
+ while ( iter.hasNext() )
+ {
+ iter.next();
+ delete[] iter.value();
+ }
+}
+
+// global instance
+ExtendedCharTable ExtendedCharTable::instance;
+
+
+//#include "moc_Emulation.cpp"
+
diff --git a/qtermwidget/Emulation.h b/qtermwidget/Emulation.h
new file mode 100644
index 0000000..2782df7
--- /dev/null
+++ b/qtermwidget/Emulation.h
@@ -0,0 +1,465 @@
+/*
+ This file is part of Konsole, an X terminal.
+
+ Copyright (C) 2007 by Robert Knight <robertknight@gmail.com>
+ Copyright (C) 1997,1998 by Lars Doelle <lars.doelle@on-line.de>
+
+ Rewritten for QT4 by e_k <e_k at users.sourceforge.net>, Copyright (C)2008
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ 02110-1301 USA.
+*/
+
+#ifndef EMULATION_H
+#define EMULATION_H
+
+// System
+#include <stdio.h>
+
+// Qt
+#include <QtGui/QKeyEvent>
+//#include <QPointer>
+#include <QtCore/QTextCodec>
+#include <QtCore/QTextStream>
+#include <QtCore/QTimer>
+
+
+namespace Konsole
+{
+
+class KeyboardTranslator;
+class HistoryType;
+class Screen;
+class ScreenWindow;
+class TerminalCharacterDecoder;
+
+/**
+ * This enum describes the available states which
+ * the terminal emulation may be set to.
+ *
+ * These are the values used by Emulation::stateChanged()
+ */
+enum
+{
+ /** The emulation is currently receiving user input. */
+ NOTIFYNORMAL=0,
+ /**
+ * The terminal program has triggered a bell event
+ * to get the user's attention.
+ */
+ NOTIFYBELL=1,
+ /**
+ * The emulation is currently receiving data from its
+ * terminal input.
+ */
+ NOTIFYACTIVITY=2,
+
+ // unused here?
+ NOTIFYSILENCE=3
+};
+
+/**
+ * Base class for terminal emulation back-ends.
+ *
+ * The back-end is responsible for decoding an incoming character stream and
+ * producing an output image of characters.
+ *
+ * When input from the terminal is received, the receiveData() slot should be called with
+ * the data which has arrived. The emulation will process the data and update the
+ * screen image accordingly. The codec used to decode the incoming character stream
+ * into the unicode characters used internally can be specified using setCodec()
+ *
+ * The size of the screen image can be specified by calling setImageSize() with the
+ * desired number of lines and columns. When new lines are added, old content
+ * is moved into a history store, which can be set by calling setHistory().
+ *
+ * The screen image can be accessed by creating a ScreenWindow onto this emulation
+ * by calling createWindow(). Screen windows provide access to a section of the
+ * output. Each screen window covers the same number of lines and columns as the
+ * image size returned by imageSize(). The screen window can be moved up and down
+ * and provides transparent access to both the current on-screen image and the
+ * previous output. The screen windows emit an outputChanged signal
+ * when the section of the image they are looking at changes.
+ * Graphical views can then render the contents of a screen window, listening for notifications
+ * of output changes from the screen window which they are associated with and updating
+ * accordingly.
+ *
+ * The emulation also is also responsible for converting input from the connected views such
+ * as keypresses and mouse activity into a character string which can be sent
+ * to the terminal program. Key presses can be processed by calling the sendKeyEvent() slot,
+ * while mouse events can be processed using the sendMouseEvent() slot. When the character
+ * stream has been produced, the emulation will emit a sendData() signal with a pointer
+ * to the character buffer. This data should be fed to the standard input of the terminal
+ * process. The translation of key presses into an output character stream is performed
+ * using a lookup in a set of key bindings which map key sequences to output
+ * character sequences. The name of the key bindings set used can be specified using
+ * setKeyBindings()
+ *
+ * The emulation maintains certain state information which changes depending on the
+ * input received. The emulation can be reset back to its starting state by calling
+ * reset().
+ *
+ * The emulation also maintains an activity state, which specifies whether
+ * terminal is currently active ( when data is received ), normal
+ * ( when the terminal is idle or receiving user input ) or trying
+ * to alert the user ( also known as a "Bell" event ). The stateSet() signal
+ * is emitted whenever the activity state is set. This can be used to determine
+ * how long the emulation has been active/idle for and also respond to
+ * a 'bell' event in different ways.
+ */
+class Emulation : public QObject
+{
+Q_OBJECT
+
+public:
+
+ /** Constructs a new terminal emulation */
+ Emulation();
+ ~Emulation();
+
+ /**
+ * Creates a new window onto the output from this emulation. The contents
+ * of the window are then rendered by views which are set to use this window using the
+ * TerminalDisplay::setScreenWindow() method.
+ */
+ ScreenWindow* createWindow();
+
+ /** Returns the size of the screen image which the emulation produces */
+ QSize imageSize();
+
+ /**
+ * Returns the total number of lines, including those stored in the history.
+ */
+ int lineCount();
+
+
+ /**
+ * Sets the history store used by this emulation. When new lines
+ * are added to the output, older lines at the top of the screen are transferred to a history
+ * store.
+ *
+ * The number of lines which are kept and the storage location depend on the
+ * type of store.
+ */
+ void setHistory(const HistoryType&);
+ /** Returns the history store used by this emulation. See setHistory() */
+ const HistoryType& history();
+ /** Clears the history scroll. */
+ void clearHistory();
+
+ /**
+ * Copies the output history from @p startLine to @p endLine
+ * into @p stream, using @p decoder to convert the terminal
+ * characters into text.
+ *
+ * @param decoder A decoder which converts lines of terminal characters with
+ * appearance attributes into output text. PlainTextDecoder is the most commonly
+ * used decoder.
+ * @param startLine The first
+ */
+ virtual void writeToStream(TerminalCharacterDecoder* decoder,int startLine,int endLine);
+
+
+ /** Returns the codec used to decode incoming characters. See setCodec() */
+ const QTextCodec* codec() { return _codec; }
+ /** Sets the codec used to decode incoming characters. */
+ void setCodec(const QTextCodec*);
+
+ /**
+ * Convenience method.
+ * Returns true if the current codec used to decode incoming
+ * characters is UTF-8
+ */
+ bool utf8() { Q_ASSERT(_codec); return _codec->mibEnum() == 106; }
+
+
+ /** TODO Document me */
+ virtual char getErase() const;
+
+ /**
+ * Sets the key bindings used to key events
+ * ( received through sendKeyEvent() ) into character
+ * streams to send to the terminal.
+ */
+ void setKeyBindings(const QString& name);
+ /**
+ * Returns the name of the emulation's current key bindings.
+ * See setKeyBindings()
+ */
+ QString keyBindings();
+
+ /**
+ * Copies the current image into the history and clears the screen.
+ */
+ virtual void clearEntireScreen() =0;
+
+ /** Resets the state of the terminal. */
+ virtual void reset() =0;
+
+ /**
+ * Returns true if the active terminal program wants
+ * mouse input events.
+ *
+ * The programUsesMouseChanged() signal is emitted when this
+ * changes.
+ */
+ bool programUsesMouse() const;
+
+public slots:
+
+ /** Change the size of the emulation's image */
+ virtual void setImageSize(int lines, int columns);
+
+ /**
+ * Interprets a sequence of characters and sends the result to the terminal.
+ * This is equivalent to calling sendKeyEvent() for each character in @p text in succession.
+ */
+ virtual void sendText(const QString& text) = 0;
+
+ /**
+ * Interprets a key press event and emits the sendData() signal with
+ * the resulting character stream.
+ */
+ virtual void sendKeyEvent(QKeyEvent*);
+
+ /**
+ * Converts information about a mouse event into an xterm-compatible escape
+ * sequence and emits the character sequence via sendData()
+ */
+ virtual void sendMouseEvent(int buttons, int column, int line, int eventType);
+
+ /**
+ * Sends a string of characters to the foreground terminal process.
+ *
+ * @param string The characters to send.
+ * @param length Length of @p string or if set to a negative value, @p string will
+ * be treated as a null-terminated string and its length will be determined automatically.
+ */
+ virtual void sendString(const char* string, int length = -1) = 0;
+
+ /**
+ * Processes an incoming stream of characters. receiveData() decodes the incoming
+ * character buffer using the current codec(), and then calls receiveChar() for
+ * each unicode character in the resulting buffer.
+ *
+ * receiveData() also starts a timer which causes the outputChanged() signal
+ * to be emitted when it expires. The timer allows multiple updates in quick
+ * succession to be buffered into a single outputChanged() signal emission.
+ *
+ * @param buffer A string of characters received from the terminal program.
+ * @param len The length of @p buffer
+ */
+ void receiveData(const char* buffer,int len);
+
+signals:
+
+ /**
+ * Emitted when a buffer of data is ready to send to the
+ * standard input of the terminal.
+ *
+ * @param data The buffer of data ready to be sent
+ * @paran len The length of @p data in bytes
+ */
+ void sendData(const char* data,int len);
+
+ /**
+ * Requests that sending of input to the emulation
+ * from the terminal process be suspended or resumed.
+ *
+ * @param suspend If true, requests that sending of
+ * input from the terminal process' stdout be
+ * suspended. Otherwise requests that sending of
+ * input be resumed.
+ */
+ void lockPtyRequest(bool suspend);
+
+ /**
+ * Requests that the pty used by the terminal process
+ * be set to UTF 8 mode.
+ *
+ * TODO: More documentation
+ */
+ void useUtf8Request(bool);
+
+ /**
+ * Emitted when the activity state of the emulation is set.
+ *
+ * @param state The new activity state, one of NOTIFYNORMAL, NOTIFYACTIVITY
+ * or NOTIFYBELL
+ */
+ void stateSet(int state);
+
+ /** TODO Document me */
+ void zmodemDetected();
+
+
+ /**
+ * Requests that the color of the text used
+ * to represent the tabs associated with this
+ * emulation be changed. This is a Konsole-specific
+ * extension from pre-KDE 4 times.
+ *
+ * TODO: Document how the parameter works.
+ */
+ void changeTabTextColorRequest(int color);
+
+ /**
+ * This is emitted when the program running in the shell indicates whether or
+ * not it is interested in mouse events.
+ *
+ * @param usesMouse This will be true if the program wants to be informed about
+ * mouse events or false otherwise.
+ */
+ void programUsesMouseChanged(bool usesMouse);
+
+ /**
+ * Emitted when the contents of the screen image change.
+ * The emulation buffers the updates from successive image changes,
+ * and only emits outputChanged() at sensible intervals when
+ * there is a lot of terminal activity.
+ *
+ * Normally there is no need for objects other than the screen windows
+ * created with createWindow() to listen for this signal.
+ *
+ * ScreenWindow objects created using createWindow() will emit their
+ * own outputChanged() signal in response to this signal.
+ */
+ void outputChanged();
+
+ /**
+ * Emitted when the program running in the terminal wishes to update the
+ * session's title. This also allows terminal programs to customize other
+ * aspects of the terminal emulation display.
+ *
+ * This signal is emitted when the escape sequence "\033]ARG;VALUE\007"
+ * is received in the input string, where ARG is a number specifying what
+ * should change and VALUE is a string specifying the new value.
+ *
+ * TODO: The name of this method is not very accurate since this method
+ * is used to perform a whole range of tasks besides just setting
+ * the user-title of the session.
+ *
+ * @param title Specifies what to change.
+ * <ul>
+ * <li>0 - Set window icon text and session title to @p newTitle</li>
+ * <li>1 - Set window icon text to @p newTitle</li>
+ * <li>2 - Set session title to @p newTitle</li>
+ * <li>11 - Set the session's default background color to @p newTitle,
+ * where @p newTitle can be an HTML-style string (#RRGGBB) or a named
+ * color (eg 'red', 'blue').
+ * See http://doc.trolltech.com/4.2/qcolor.html#setNamedColor for more
+ * details.
+ * </li>
+ * <li>31 - Supposedly treats @p newTitle as a URL and opens it (NOT IMPLEMENTED)</li>
+ * <li>32 - Sets the icon associated with the session. @p newTitle is the name
+ * of the icon to use, which can be the name of any icon in the current KDE icon
+ * theme (eg: 'konsole', 'kate', 'folder_home')</li>
+ * </ul>
+ * @param newTitle Specifies the new title
+ */
+
+ void titleChanged(int title,const QString& newTitle);
+
+ /**
+ * Emitted when the program running in the terminal changes the
+ * screen size.
+ */
+ void imageSizeChanged(int lineCount , int columnCount);
+
+ /**
+ * Emitted when the terminal program requests to change various properties
+ * of the terminal display.
+ *
+ * A profile change command occurs when a special escape sequence, followed
+ * by a string containing a series of name and value pairs is received.
+ * This string can be parsed using a ProfileCommandParser instance.
+ *
+ * @param text A string expected to contain a series of key and value pairs in
+ * the form: name=value;name2=value2 ...
+ */
+ void profileChangeCommandReceived(const QString& text);
+
+protected:
+ virtual void setMode (int mode) = 0;
+ virtual void resetMode(int mode) = 0;
+
+ /**
+ * Processes an incoming character. See receiveData()
+ * @p ch A unicode character code.
+ */
+ virtual void receiveChar(int ch);
+
+ /**
+ * Sets the active screen. The terminal has two screens, primary and alternate.
+ * The primary screen is used by default. When certain interactive programs such
+ * as Vim are run, they trigger a switch to the alternate screen.
+ *
+ * @param index 0 to switch to the primary screen, or 1 to switch to the alternate screen
+ */
+ void setScreen(int index);
+
+ enum EmulationCodec
+ {
+ LocaleCodec = 0,
+ Utf8Codec = 1
+ };
+ void setCodec(EmulationCodec codec); // codec number, 0 = locale, 1=utf8
+
+
+ QList<ScreenWindow*> _windows;
+
+ Screen* _currentScreen; // pointer to the screen which is currently active,
+ // this is one of the elements in the screen[] array
+
+ Screen* _screen[2]; // 0 = primary screen ( used by most programs, including the shell
+ // scrollbars are enabled in this mode )
+ // 1 = alternate ( used by vi , emacs etc.
+ // scrollbars are not enabled in this mode )
+
+
+ //decodes an incoming C-style character stream into a unicode QString using
+ //the current text codec. (this allows for rendering of non-ASCII characters in text files etc.)
+ const QTextCodec* _codec;
+ QTextDecoder* _decoder;
+
+ const KeyboardTranslator* _keyTranslator; // the keyboard layout
+
+protected slots:
+ /**
+ * Schedules an update of attached views.
+ * Repeated calls to bufferedUpdate() in close succession will result in only a single update,
+ * much like the Qt buffered update of widgets.
+ */
+ void bufferedUpdate();
+
+private slots:
+
+ // triggered by timer, causes the emulation to send an updated screen image to each
+ // view
+ void showBulk();
+
+ void usesMouseChanged(bool usesMouse);
+
+private:
+
+ bool _usesMouse;
+ QTimer _bulkTimer1;
+ QTimer _bulkTimer2;
+
+};
+
+}
+
+#endif // ifndef EMULATION_H
diff --git a/qtermwidget/ExtendedDefaultTranslator.h b/qtermwidget/ExtendedDefaultTranslator.h
new file mode 100644
index 0000000..6403c72
--- /dev/null
+++ b/qtermwidget/ExtendedDefaultTranslator.h
@@ -0,0 +1,74 @@
+"keyboard \"Default (XFree 4)\""
+"key Escape : \"\\E\""
+"key Tab -Shift : \"\\t\"\n"
+"key Tab +Shift+Ansi : \"\\E[Z\"\n"
+"key Tab +Shift-Ansi : \"\\t\"\n"
+"key Backtab +Ansi : \"\\E[Z\"\n"
+"key Backtab -Ansi : \"\\t\"\n"
+"key Return-Shift-NewLine : \"\\r\"\n"
+"key Return-Shift+NewLine : \"\\r\\n\"\n"
+"key Return+Shift : \"\\EOM\"\n"
+"key Backspace : \"\\x7f\"\n"
+"key Up -Shift-Ansi : \"\\EA\"\n"
+"key Down -Shift-Ansi : \"\\EB\"\n"
+"key Right-Shift-Ansi : \"\\EC\"\n"
+"key Left -Shift-Ansi : \"\\ED\"\n"
+"key Up -Shift-AnyMod+Ansi+AppCuKeys : \"\\EOA\"\n"
+"key Down -Shift-AnyMod+Ansi+AppCuKeys : \"\\EOB\"\n"
+"key Right -Shift-AnyMod+Ansi+AppCuKeys : \"\\EOC\"\n"
+"key Left -Shift-AnyMod+Ansi+AppCuKeys : \"\\EOD\"\n"
+"key Up -Shift-AnyMod+Ansi-AppCuKeys : \"\\E[A\"\n"
+"key Down -Shift-AnyMod+Ansi-AppCuKeys : \"\\E[B\"\n"
+"key Right -Shift-AnyMod+Ansi-AppCuKeys : \"\\E[C\"\n"
+"key Left -Shift-AnyMod+Ansi-AppCuKeys : \"\\E[D\"\n"
+"key Up -Shift+AnyMod+Ansi : \"\\E[1;*A\"\n"
+"key Down -Shift+AnyMod+Ansi : \"\\E[1;*B\"\n"
+"key Right -Shift+AnyMod+Ansi : \"\\E[1;*C\"\n"
+"key Left -Shift+AnyMod+Ansi : \"\\E[1;*D\"\n"
+"key Enter+NewLine : \"\\r\\n\"\n"
+"key Enter-NewLine : \"\\r\"\n"
+"key Home -AnyMod -AppCuKeys : \"\\E[H\" \n"
+"key End -AnyMod -AppCuKeys : \"\\E[F\" \n"
+"key Home -AnyMod +AppCuKeys : \"\\EOH\" \n"
+"key End -AnyMod +AppCuKeys : \"\\EOF\" \n"
+"key Home +AnyMod : \"\\E[1;*H\"\n"
+"key End +AnyMod : \"\\E[1;*F\"\n"
+"key Insert -AnyMod : \"\\E[2~\"\n"
+"key Delete -AnyMod : \"\\E[3~\"\n"
+"key Insert +AnyMod : \"\\E[2;*~\"\n"
+"key Delete +AnyMod : \"\\E[3;*~\"\n"
+"key Prior -Shift-AnyMod : \"\\E[5~\"\n"
+"key Next -Shift-AnyMod : \"\\E[6~\"\n"
+"key Prior -Shift+AnyMod : \"\\E[5;*~\"\n"
+"key Next -Shift+AnyMod : \"\\E[6;*~\"\n"
+"key F1 -AnyMod : \"\\EOP\"\n"
+"key F2 -AnyMod : \"\\EOQ\"\n"
+"key F3 -AnyMod : \"\\EOR\"\n"
+"key F4 -AnyMod : \"\\EOS\"\n"
+"key F5 -AnyMod : \"\\E[15~\"\n"
+"key F6 -AnyMod : \"\\E[17~\"\n"
+"key F7 -AnyMod : \"\\E[18~\"\n"
+"key F8 -AnyMod : \"\\E[19~\"\n"
+"key F9 -AnyMod : \"\\E[20~\"\n"
+"key F10 -AnyMod : \"\\E[21~\"\n"
+"key F11 -AnyMod : \"\\E[23~\"\n"
+"key F12 -AnyMod : \"\\E[24~\"\n"
+"key F1 +AnyMod : \"\\EO*P\"\n"
+"key F2 +AnyMod : \"\\EO*Q\"\n"
+"key F3 +AnyMod : \"\\EO*R\"\n"
+"key F4 +AnyMod : \"\\EO*S\"\n"
+"key F5 +AnyMod : \"\\E[15;*~\"\n"
+"key F6 +AnyMod : \"\\E[17;*~\"\n"
+"key F7 +AnyMod : \"\\E[18;*~\"\n"
+"key F8 +AnyMod : \"\\E[19;*~\"\n"
+"key F9 +AnyMod : \"\\E[20;*~\"\n"
+"key F10 +AnyMod : \"\\E[21;*~\"\n"
+"key F11 +AnyMod : \"\\E[23;*~\"\n"
+"key F12 +AnyMod : \"\\E[24;*~\"\n"
+"key Space +Control : \"\\x00\"\n"
+"key Up +Shift-AppScreen : scrollLineUp\n"
+"key Prior +Shift-AppScreen : scrollPageUp\n"
+"key Down +Shift-AppScreen : scrollLineDown\n"
+"key Next +Shift-AppScreen : scrollPageDown\n"
+"key ScrollLock : scrollLock\n"
+"\0"
diff --git a/qtermwidget/Filter.cpp b/qtermwidget/Filter.cpp
new file mode 100644
index 0000000..c3f4919
--- /dev/null
+++ b/qtermwidget/Filter.cpp
@@ -0,0 +1,562 @@
+/*
+ Copyright (C) 2007 by Robert Knight <robertknight@gmail.com>
+
+ Rewritten for QT4 by e_k <e_k at users.sourceforge.net>, Copyright (C)2008
+
+ 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.
+*/
+
+// Own
+#include "Filter.h"
+
+// System
+#include <iostream>
+
+// Qt
+#include <QtGui/QAction>
+#include <QtGui/QApplication>
+#include <QtGui/QClipboard>
+#include <QtCore/QString>
+
+#include <QtCore/QSharedData>
+#include <QtCore>
+
+// KDE
+//#include <KLocale>
+//#include <KRun>
+
+// Konsole
+#include "TerminalCharacterDecoder.h"
+
+using namespace Konsole;
+
+FilterChain::~FilterChain()
+{
+ QMutableListIterator<Filter*> iter(*this);
+
+ while ( iter.hasNext() )
+ {
+ Filter* filter = iter.next();
+ iter.remove();
+ delete filter;
+ }
+}
+
+void FilterChain::addFilter(Filter* filter)
+{
+ append(filter);
+}
+void FilterChain::removeFilter(Filter* filter)
+{
+ removeAll(filter);
+}
+bool FilterChain::containsFilter(Filter* filter)
+{
+ return contains(filter);
+}
+void FilterChain::reset()
+{
+ QListIterator<Filter*> iter(*this);
+ while (iter.hasNext())
+ iter.next()->reset();
+}
+void FilterChain::setBuffer(const QString* buffer , const QList<int>* linePositions)
+{
+ QListIterator<Filter*> iter(*this);
+ while (iter.hasNext())
+ iter.next()->setBuffer(buffer,linePositions);
+}
+void FilterChain::process()
+{
+ QListIterator<Filter*> iter(*this);
+ while (iter.hasNext())
+ iter.next()->process();
+}
+void FilterChain::clear()
+{
+ QList<Filter*>::clear();
+}
+Filter::HotSpot* FilterChain::hotSpotAt(int line , int column) const
+{
+ QListIterator<Filter*> iter(*this);
+ while (iter.hasNext())
+ {
+ Filter* filter = iter.next();
+ Filter::HotSpot* spot = filter->hotSpotAt(line,column);
+ if ( spot != 0 )
+ {
+ return spot;
+ }
+ }
+
+ return 0;
+}
+
+QList<Filter::HotSpot*> FilterChain::hotSpots() const
+{
+ QList<Filter::HotSpot*> list;
+ QListIterator<Filter*> iter(*this);
+ while (iter.hasNext())
+ {
+ Filter* filter = iter.next();
+ list << filter->hotSpots();
+ }
+ return list;
+}
+//QList<Filter::HotSpot*> FilterChain::hotSpotsAtLine(int line) const;
+
+TerminalImageFilterChain::TerminalImageFilterChain()
+: _buffer(0)
+, _linePositions(0)
+{
+}
+
+TerminalImageFilterChain::~TerminalImageFilterChain()
+{
+ delete _buffer;
+ delete _linePositions;
+}
+
+void TerminalImageFilterChain::setImage(const Character* const image , int lines , int columns, const QVector<LineProperty>& lineProperties)
+{
+//qDebug("%s %d", __FILE__, __LINE__);
+ if (empty())
+ return;
+//qDebug("%s %d", __FILE__, __LINE__);
+
+ // reset all filters and hotspots
+ reset();
+//qDebug("%s %d", __FILE__, __LINE__);
+
+ PlainTextDecoder decoder;
+ decoder.setTrailingWhitespace(false);
+
+//qDebug("%s %d", __FILE__, __LINE__);
+ // setup new shared buffers for the filters to process on
+ QString* newBuffer = new QString();
+ QList<int>* newLinePositions = new QList<int>();
+ setBuffer( newBuffer , newLinePositions );
+
+ // free the old buffers
+ delete _buffer;
+ delete _linePositions;
+
+ _buffer = newBuffer;
+ _linePositions = newLinePositions;
+
+ QTextStream lineStream(_buffer);
+ decoder.begin(&lineStream);
+
+ for (int i=0 ; i < lines ; i++)
+ {
+ _linePositions->append(_buffer->length());
+ decoder.decodeLine(image + i*columns,columns,LINE_DEFAULT);
+
+ // pretend that each line ends with a newline character.
+ // this prevents a link that occurs at the end of one line
+ // being treated as part of a link that occurs at the start of the next line
+ //
+ // the downside is that links which are spread over more than one line are not
+ // highlighted.
+ //
+ // TODO - Use the "line wrapped" attribute associated with lines in a
+ // terminal image to avoid adding this imaginary character for wrapped
+ // lines
+ if ( !(lineProperties.value(i,LINE_DEFAULT) & LINE_WRAPPED) )
+ lineStream << QChar('\n');
+ }
+ decoder.end();
+// qDebug("%s %d", __FILE__, __LINE__);
+}
+
+Filter::Filter() :
+_linePositions(0),
+_buffer(0)
+{
+}
+
+Filter::~Filter()
+{
+ QListIterator<HotSpot*> iter(_hotspotList);
+ while (iter.hasNext())
+ {
+ delete iter.next();
+ }
+}
+void Filter::reset()
+{
+ _hotspots.clear();
+ _hotspotList.clear();
+}
+
+void Filter::setBuffer(const QString* buffer , const QList<int>* linePositions)
+{
+ _buffer = buffer;
+ _linePositions = linePositions;
+}
+
+void Filter::getLineColumn(int position , int& startLine , int& startColumn)
+{
+ Q_ASSERT( _linePositions );
+ Q_ASSERT( _buffer );
+
+
+ for (int i = 0 ; i < _linePositions->count() ; i++)
+ {
+ //kDebug() << "line position at " << i << " = " << _linePositions[i];
+ int nextLine = 0;
+
+ if ( i == _linePositions->count()-1 )
+ {
+ nextLine = _buffer->length() + 1;
+ }
+ else
+ {
+ nextLine = _linePositions->value(i+1);
+ }
+
+ // kDebug() << "pos - " << position << " line pos(" << i<< ") " << _linePositions->value(i) <<
+ // " next = " << nextLine << " buffer len = " << _buffer->length();
+
+ if ( _linePositions->value(i) <= position && position < nextLine )
+ {
+ startLine = i;
+ startColumn = position - _linePositions->value(i);
+ return;
+ }
+ }
+}
+
+
+/*void Filter::addLine(const QString& text)
+{
+ _linePositions << _buffer.length();
+ _buffer.append(text);
+}*/
+
+const QString* Filter::buffer()
+{
+ return _buffer;
+}
+Filter::HotSpot::~HotSpot()
+{
+}
+void Filter::addHotSpot(HotSpot* spot)
+{
+ _hotspotList << spot;
+
+ for (int line = spot->startLine() ; line <= spot->endLine() ; line++)
+ {
+ _hotspots.insert(line,spot);
+ }
+}
+QList<Filter::HotSpot*> Filter::hotSpots() const
+{
+ return _hotspotList;
+}
+QList<Filter::HotSpot*> Filter::hotSpotsAtLine(int line) const
+{
+ return _hotspots.values(line);
+}
+
+Filter::HotSpot* Filter::hotSpotAt(int line , int column) const
+{
+ QListIterator<HotSpot*> spotIter(_hotspots.values(line));
+
+ while (spotIter.hasNext())
+ {
+ HotSpot* spot = spotIter.next();
+
+ if ( spot->startLine() == line && spot->startColumn() > column )
+ continue;
+ if ( spot->endLine() == line && spot->endColumn() < column )
+ continue;
+
+ return spot;
+ }
+
+ return 0;
+}
+
+Filter::HotSpot::HotSpot(int startLine , int startColumn , int endLine , int endColumn)
+ : _startLine(startLine)
+ , _startColumn(startColumn)
+ , _endLine(endLine)
+ , _endColumn(endColumn)
+ , _type(NotSpecified)
+{
+}
+QString Filter::HotSpot::tooltip() const
+{
+ return QString();
+}
+QList<QAction*> Filter::HotSpot::actions()
+{
+ return QList<QAction*>();
+}
+int Filter::HotSpot::startLine() const
+{
+ return _startLine;
+}
+int Filter::HotSpot::endLine() const
+{
+ return _endLine;
+}
+int Filter::HotSpot::startColumn() const
+{
+ return _startColumn;
+}
+int Filter::HotSpot::endColumn() const
+{
+ return _endColumn;
+}
+Filter::HotSpot::Type Filter::HotSpot::type() const
+{
+ return _type;
+}
+void Filter::HotSpot::setType(Type type)
+{
+ _type = type;
+}
+
+RegExpFilter::RegExpFilter()
+{
+}
+
+RegExpFilter::HotSpot::HotSpot(int startLine,int startColumn,int endLine,int endColumn)
+ : Filter::HotSpot(startLine,startColumn,endLine,endColumn)
+{
+ setType(Marker);
+}
+
+void RegExpFilter::HotSpot::activate(QObject*)
+{
+}
+
+void RegExpFilter::HotSpot::setCapturedTexts(const QStringList& texts)
+{
+ _capturedTexts = texts;
+}
+QStringList RegExpFilter::HotSpot::capturedTexts() const
+{
+ return _capturedTexts;
+}
+
+void RegExpFilter::setRegExp(const QRegExp& regExp)
+{
+ _searchText = regExp;
+}
+QRegExp RegExpFilter::regExp() const
+{
+ return _searchText;
+}
+/*void RegExpFilter::reset(int)
+{
+ _buffer = QString();
+}*/
+void RegExpFilter::process()
+{
+ int pos = 0;
+ const QString* text = buffer();
+
+ Q_ASSERT( text );
+
+ // ignore any regular expressions which match an empty string.
+ // otherwise the while loop below will run indefinitely
+ static const QString emptyString("");
+ if ( _searchText.exactMatch(emptyString) )
+ return;
+
+ while(pos >= 0)
+ {
+ pos = _searchText.indexIn(*text,pos);
+
+ if ( pos >= 0 )
+ {
+
+ int startLine = 0;
+ int endLine = 0;
+ int startColumn = 0;
+ int endColumn = 0;
+
+
+ //kDebug() << "pos from " << pos << " to " << pos + _searchText.matchedLength();
+
+ getLineColumn(pos,startLine,startColumn);
+ getLineColumn(pos + _searchText.matchedLength(),endLine,endColumn);
+
+ //kDebug() << "start " << startLine << " / " << startColumn;
+ //kDebug() << "end " << endLine << " / " << endColumn;
+
+ RegExpFilter::HotSpot* spot = newHotSpot(startLine,startColumn,
+ endLine,endColumn);
+ spot->setCapturedTexts(_searchText.capturedTexts());
+
+ addHotSpot( spot );
+ pos += _searchText.matchedLength();
+
+ // if matchedLength == 0, the program will get stuck in an infinite loop
+ Q_ASSERT( _searchText.matchedLength() > 0 );
+ }
+ }
+}
+
+RegExpFilter::HotSpot* RegExpFilter::newHotSpot(int startLine,int startColumn,
+ int endLine,int endColumn)
+{
+ return new RegExpFilter::HotSpot(startLine,startColumn,
+ endLine,endColumn);
+}
+RegExpFilter::HotSpot* UrlFilter::newHotSpot(int startLine,int startColumn,int endLine,
+ int endColumn)
+{
+ return new UrlFilter::HotSpot(startLine,startColumn,
+ endLine,endColumn);
+}
+UrlFilter::HotSpot::HotSpot(int startLine,int startColumn,int endLine,int endColumn)
+: RegExpFilter::HotSpot(startLine,startColumn,endLine,endColumn)
+, _urlObject(new FilterObject(this))
+{
+ setType(Link);
+}
+QString UrlFilter::HotSpot::tooltip() const
+{
+ QString url = capturedTexts().first();
+
+ const UrlType kind = urlType();
+
+ if ( kind == StandardUrl )
+ return QString();
+ else if ( kind == Email )
+ return QString();
+ else
+ return QString();
+}
+UrlFilter::HotSpot::UrlType UrlFilter::HotSpot::urlType() const
+{
+ QString url = capturedTexts().first();
+
+ if ( FullUrlRegExp.exactMatch(url) )
+ return StandardUrl;
+ else if ( EmailAddressRegExp.exactMatch(url) )
+ return Email;
+ else
+ return Unknown;
+}
+
+void UrlFilter::HotSpot::activate(QObject* object)
+{
+ QString url = capturedTexts().first();
+
+ const UrlType kind = urlType();
+
+ const QString& actionName = object ? object->objectName() : QString();
+
+ if ( actionName == "copy-action" )
+ {
+ //kDebug() << "Copying url to clipboard:" << url;
+
+ QApplication::clipboard()->setText(url);
+ return;
+ }
+
+ if ( !object || actionName == "open-action" )
+ {
+ if ( kind == StandardUrl )
+ {
+ // if the URL path does not include the protocol ( eg. "www.kde.org" ) then
+ // prepend http:// ( eg. "www.kde.org" --> "http://www.kde.org" )
+ if (!url.contains("://"))
+ {
+ url.prepend("http://");
+ }
+ }
+ else if ( kind == Email )
+ {
+ url.prepend("mailto:");
+ }
+
+// new KRun(url,QApplication::activeWindow());
+ }
+}
+
+// Note: Altering these regular expressions can have a major effect on the performance of the filters
+// used for finding URLs in the text, especially if they are very general and could match very long
+// pieces of text.
+// Please be careful when altering them.
+
+//regexp matches:
+// full url:
+// protocolname:// or www. followed by anything other than whitespaces, <, >, ' or ", and ends before whitespaces, <, >, ', ", ], !, comma and dot
+const QRegExp UrlFilter::FullUrlRegExp("(www\\.(?!\\.)|[a-z][a-z0-9+.-]*://)[^\\s<>'\"]+[^!,\\.\\s<>'\"\\]]");
+// email address:
+// [word chars, dots or dashes]@[word chars, dots or dashes].[word chars]
+const QRegExp UrlFilter::EmailAddressRegExp("\\b(\\w|\\.|-)+@(\\w|\\.|-)+\\.\\w+\\b");
+
+// matches full url or email address
+const QRegExp UrlFilter::CompleteUrlRegExp('('+FullUrlRegExp.pattern()+'|'+
+ EmailAddressRegExp.pattern()+')');
+
+UrlFilter::UrlFilter()
+{
+ setRegExp( CompleteUrlRegExp );
+}
+UrlFilter::HotSpot::~HotSpot()
+{
+ delete _urlObject;
+}
+void FilterObject::activated()
+{
+ _filter->activate(sender());
+}
+QList<QAction*> UrlFilter::HotSpot::actions()
+{
+ QList<QAction*> list;
+
+ const UrlType kind = urlType();
+
+ QAction* openAction = new QAction(_urlObject);
+ QAction* copyAction = new QAction(_urlObject);;
+
+ Q_ASSERT( kind == StandardUrl || kind == Email );
+
+ if ( kind == StandardUrl )
+ {
+ openAction->setText(("Open Link"));
+ copyAction->setText(("Copy Link Address"));
+ }
+ else if ( kind == Email )
+ {
+ openAction->setText(("Send Email To..."));
+ copyAction->setText(("Copy Email Address"));
+ }
+
+ // object names are set here so that the hotspot performs the
+ // correct action when activated() is called with the triggered
+ // action passed as a parameter.
+ openAction->setObjectName("open-action");
+ copyAction->setObjectName("copy-action");
+
+ QObject::connect( openAction , SIGNAL(triggered()) , _urlObject , SLOT(activated()) );
+ QObject::connect( copyAction , SIGNAL(triggered()) , _urlObject , SLOT(activated()) );
+
+ list << openAction;
+ list << copyAction;
+
+ return list;
+}
+
+//#include "moc_Filter.cpp"
diff --git a/qtermwidget/Filter.h b/qtermwidget/Filter.h
new file mode 100644
index 0000000..06ea5e3
--- /dev/null
+++ b/qtermwidget/Filter.h
@@ -0,0 +1,383 @@
+/*
+ Copyright (C) 2007 by Robert Knight <robertknight@gmail.com>
+
+ Rewritten for QT4 by e_k <e_k at users.sourceforge.net>, Copyright (C)2008
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ 02110-1301 USA.
+*/
+
+#ifndef FILTER_H
+#define FILTER_H
+
+// Qt
+#include <QtGui/QAction>
+#include <QtCore/QList>
+#include <QtCore/QObject>
+#include <QtCore/QStringList>
+#include <QtCore/QHash>
+#include <QtCore/QRegExp>
+
+// Local
+#include "Character.h"
+
+namespace Konsole
+{
+
+/**
+ * A filter processes blocks of text looking for certain patterns (such as URLs or keywords from a list)
+ * and marks the areas which match the filter's patterns as 'hotspots'.
+ *
+ * Each hotspot has a type identifier associated with it ( such as a link or a highlighted section ),
+ * and an action. When the user performs some activity such as a mouse-click in a hotspot area ( the exact
+ * action will depend on what is displaying the block of text which the filter is processing ), the hotspot's
+ * activate() method should be called. Depending on the type of hotspot this will trigger a suitable response.
+ *
+ * For example, if a hotspot represents a URL then a suitable action would be opening that URL in a web browser.
+ * Hotspots may have more than one action, in which case the list of actions can be obtained using the
+ * actions() method.
+ *
+ * Different subclasses of filter will return different types of hotspot.
+ * Subclasses must reimplement the process() method to examine a block of text and identify sections of interest.
+ * When processing the text they should create instances of Filter::HotSpot subclasses for sections of interest
+ * and add them to the filter's list of hotspots using addHotSpot()
+ */
+class Filter
+{
+public:
+ /**
+ * Represents an area of text which matched the pattern a particular filter has been looking for.
+ *
+ * Each hotspot has a type identifier associated with it ( such as a link or a highlighted section ),
+ * and an action. When the user performs some activity such as a mouse-click in a hotspot area ( the exact
+ * action will depend on what is displaying the block of text which the filter is processing ), the hotspot's
+ * activate() method should be called. Depending on the type of hotspot this will trigger a suitable response.
+ *
+ * For example, if a hotspot represents a URL then a suitable action would be opening that URL in a web browser.
+ * Hotspots may have more than one action, in which case the list of actions can be obtained using the
+ * actions() method. These actions may then be displayed in a popup menu or toolbar for example.
+ */
+ class HotSpot
+ {
+ public:
+ /**
+ * Constructs a new hotspot which covers the area from (@p startLine,@p startColumn) to (@p endLine,@p endColumn)
+ * in a block of text.
+ */
+ HotSpot(int startLine , int startColumn , int endLine , int endColumn);
+ virtual ~HotSpot();
+
+ enum Type
+ {
+ // the type of the hotspot is not specified
+ NotSpecified,
+ // this hotspot represents a clickable link
+ Link,
+ // this hotspot represents a marker
+ Marker
+ };
+
+ /** Returns the line when the hotspot area starts */
+ int startLine() const;
+ /** Returns the line where the hotspot area ends */
+ int endLine() const;
+ /** Returns the column on startLine() where the hotspot area starts */
+ int startColumn() const;
+ /** Returns the column on endLine() where the hotspot area ends */
+ int endColumn() const;
+ /**
+ * Returns the type of the hotspot. This is usually used as a hint for views on how to represent
+ * the hotspot graphically. eg. Link hotspots are typically underlined when the user mouses over them
+ */
+ Type type() const;
+ /**
+ * Causes the an action associated with a hotspot to be triggered.
+ *
+ * @param object The object which caused the hotspot to be triggered. This is
+ * typically null ( in which case the default action should be performed ) or
+ * one of the objects from the actions() list. In which case the associated
+ * action should be performed.
+ */
+ virtual void activate(QObject* object = 0) = 0;
+ /**
+ * Returns a list of actions associated with the hotspot which can be used in a
+ * menu or toolbar
+ */
+ virtual QList<QAction*> actions();
+
+ /**
+ * Returns the text of a tooltip to be shown when the mouse moves over the hotspot, or
+ * an empty string if there is no tooltip associated with this hotspot.
+ *
+ * The default implementation returns an empty string.
+ */
+ virtual QString tooltip() const;
+
+ protected:
+ /** Sets the type of a hotspot. This should only be set once */
+ void setType(Type type);
+
+ private:
+ int _startLine;
+ int _startColumn;
+ int _endLine;
+ int _endColumn;
+ Type _type;
+
+ };
+
+ /** Constructs a new filter. */
+ Filter();
+ virtual ~Filter();
+
+ /** Causes the filter to process the block of text currently in its internal buffer */
+ virtual void process() = 0;
+
+ /**
+ * Empties the filters internal buffer and resets the line count back to 0.
+ * All hotspots are deleted.
+ */
+ void reset();
+
+ /** Adds a new line of text to the filter and increments the line count */
+ //void addLine(const QString& string);
+
+ /** Returns the hotspot which covers the given @p line and @p column, or 0 if no hotspot covers that area */
+ HotSpot* hotSpotAt(int line , int column) const;
+
+ /** Returns the list of hotspots identified by the filter */
+ QList<HotSpot*> hotSpots() const;
+
+ /** Returns the list of hotspots identified by the filter which occur on a given line */
+ QList<HotSpot*> hotSpotsAtLine(int line) const;
+
+ /**
+ * TODO: Document me
+ */
+ void setBuffer(const QString* buffer , const QList<int>* linePositions);
+
+protected:
+ /** Adds a new hotspot to the list */
+ void addHotSpot(HotSpot*);
+ /** Returns the internal buffer */
+ const QString* buffer();
+ /** Converts a character position within buffer() to a line and column */
+ void getLineColumn(int position , int& startLine , int& startColumn);
+
+private:
+ QMultiHash<int,HotSpot*> _hotspots;
+ QList<HotSpot*> _hotspotList;
+
+ const QList<int>* _linePositions;
+ const QString* _buffer;
+};
+
+/**
+ * A filter which searches for sections of text matching a regular expression and creates a new RegExpFilter::HotSpot
+ * instance for them.
+ *
+ * Subclasses can reimplement newHotSpot() to return custom hotspot types when matches for the regular expression
+ * are found.
+ */
+class RegExpFilter : public Filter
+{
+public:
+ /**
+ * Type of hotspot created by RegExpFilter. The capturedTexts() method can be used to find the text
+ * matched by the filter's regular expression.
+ */
+ class HotSpot : public Filter::HotSpot
+ {
+ public:
+ HotSpot(int startLine, int startColumn, int endLine , int endColumn);
+ virtual void activate(QObject* object = 0);
+
+ /** Sets the captured texts associated with this hotspot */
+ void setCapturedTexts(const QStringList& texts);
+ /** Returns the texts found by the filter when matching the filter's regular expression */
+ QStringList capturedTexts() const;
+ private:
+ QStringList _capturedTexts;
+ };
+
+ /** Constructs a new regular expression filter */
+ RegExpFilter();
+
+ /**
+ * Sets the regular expression which the filter searches for in blocks of text.
+ *
+ * Regular expressions which match the empty string are treated as not matching
+ * anything.
+ */
+ void setRegExp(const QRegExp& text);
+ /** Returns the regular expression which the filter searches for in blocks of text */
+ QRegExp regExp() const;
+
+ /**
+ * Reimplemented to search the filter's text buffer for text matching regExp()
+ *
+ * If regexp matches the empty string, then process() will return immediately
+ * without finding results.
+ */
+ virtual void process();
+
+protected:
+ /**
+ * Called when a match for the regular expression is encountered. Subclasses should reimplement this
+ * to return custom hotspot types
+ */
+ virtual RegExpFilter::HotSpot* newHotSpot(int startLine,int startColumn,
+ int endLine,int endColumn);
+
+private:
+ QRegExp _searchText;
+};
+
+class FilterObject;
+
+/** A filter which matches URLs in blocks of text */
+class UrlFilter : public RegExpFilter
+{
+public:
+ /**
+ * Hotspot type created by UrlFilter instances. The activate() method opens a web browser
+ * at the given URL when called.
+ */
+ class HotSpot : public RegExpFilter::HotSpot
+ {
+ public:
+ HotSpot(int startLine,int startColumn,int endLine,int endColumn);
+ virtual ~HotSpot();
+
+ virtual QList<QAction*> actions();
+
+ /**
+ * Open a web browser at the current URL. The url itself can be determined using
+ * the capturedTexts() method.
+ */
+ virtual void activate(QObject* object = 0);
+
+ virtual QString tooltip() const;
+ private:
+ enum UrlType
+ {
+ StandardUrl,
+ Email,
+ Unknown
+ };
+ UrlType urlType() const;
+
+ FilterObject* _urlObject;
+ };
+
+ UrlFilter();
+
+protected:
+ virtual RegExpFilter::HotSpot* newHotSpot(int,int,int,int);
+
+private:
+
+ static const QRegExp FullUrlRegExp;
+ static const QRegExp EmailAddressRegExp;
+
+ // combined OR of FullUrlRegExp and EmailAddressRegExp
+ static const QRegExp CompleteUrlRegExp;
+};
+
+class FilterObject : public QObject
+{
+Q_OBJECT
+public:
+ FilterObject(Filter::HotSpot* filter) : _filter(filter) {}
+private slots:
+ void activated();
+private:
+ Filter::HotSpot* _filter;
+};
+
+/**
+ * A chain which allows a group of filters to be processed as one.
+ * The chain owns the filters added to it and deletes them when the chain itself is destroyed.
+ *
+ * Use addFilter() to add a new filter to the chain.
+ * When new text to be filtered arrives, use addLine() to add each additional
+ * line of text which needs to be processed and then after adding the last line, use
+ * process() to cause each filter in the chain to process the text.
+ *
+ * After processing a block of text, the reset() method can be used to set the filter chain's
+ * internal cursor back to the first line.
+ *
+ * The hotSpotAt() method will return the first hotspot which covers a given position.
+ *
+ * The hotSpots() and hotSpotsAtLine() method return all of the hotspots in the text and on
+ * a given line respectively.
+ */
+class FilterChain : protected QList<Filter*>
+{
+public:
+ virtual ~FilterChain();
+
+ /** Adds a new filter to the chain. The chain will delete this filter when it is destroyed */
+ void addFilter(Filter* filter);
+ /** Removes a filter from the chain. The chain will no longer delete the filter when destroyed */
+ void removeFilter(Filter* filter);
+ /** Returns true if the chain contains @p filter */
+ bool containsFilter(Filter* filter);
+ /** Removes all filters from the chain */
+ void clear();
+
+ /** Resets each filter in the chain */
+ void reset();
+ /**
+ * Processes each filter in the chain
+ */
+ void process();
+
+ /** Sets the buffer for each filter in the chain to process. */
+ void setBuffer(const QString* buffer , const QList<int>* linePositions);
+
+ /** Returns the first hotspot which occurs at @p line, @p column or 0 if no hotspot was found */
+ Filter::HotSpot* hotSpotAt(int line , int column) const;
+ /** Returns a list of all the hotspots in all the chain's filters */
+ QList<Filter::HotSpot*> hotSpots() const;
+ /** Returns a list of all hotspots at the given line in all the chain's filters */
+ QList<Filter::HotSpot> hotSpotsAtLine(int line) const;
+
+};
+
+/** A filter chain which processes character images from terminal displays */
+class TerminalImageFilterChain : public FilterChain
+{
+public:
+ TerminalImageFilterChain();
+ virtual ~TerminalImageFilterChain();
+
+ /**
+ * Set the current terminal image to @p image.
+ *
+ * @param image The terminal image
+ * @param lines The number of lines in the terminal image
+ * @param columns The number of columns in the terminal image
+ */
+ void setImage(const Character* const image , int lines , int columns,
+ const QVector<LineProperty>& lineProperties);
+
+private:
+ QString* _buffer;
+ QList<int>* _linePositions;
+};
+
+}
+#endif //FILTER_H
diff --git a/qtermwidget/History.cpp b/qtermwidget/History.cpp
new file mode 100644
index 0000000..1e3d721
--- /dev/null
+++ b/qtermwidget/History.cpp
@@ -0,0 +1,698 @@
+/*
+ This file is part of Konsole, an X terminal.
+ Copyright (C) 1997,1998 by Lars Doelle <lars.doelle@on-line.de>
+
+ Rewritten for QT4 by e_k <e_k at users.sourceforge.net>, Copyright (C)2008
+
+ 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.
+*/
+
+// Own
+#include "History.h"
+
+// System
+#include <iostream>
+#include <stdlib.h>
+#include <assert.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/mman.h>
+#include <unistd.h>
+#include <errno.h>
+
+
+// Reasonable line size
+#define LINE_SIZE 1024
+
+using namespace Konsole;
+
+/*
+ An arbitrary long scroll.
+
+ One can modify the scroll only by adding either cells
+ or newlines, but access it randomly.
+
+ The model is that of an arbitrary wide typewriter scroll
+ in that the scroll is a serie of lines and each line is
+ a serie of cells with no overwriting permitted.
+
+ The implementation provides arbitrary length and numbers
+ of cells and line/column indexed read access to the scroll
+ at constant costs.
+
+KDE4: Can we use QTemporaryFile here, instead of KTempFile?
+
+FIXME: some complain about the history buffer comsuming the
+ memory of their machines. This problem is critical
+ since the history does not behave gracefully in cases
+ where the memory is used up completely.
+
+ I put in a workaround that should handle it problem
+ now gracefully. I'm not satisfied with the solution.
+
+FIXME: Terminating the history is not properly indicated
+ in the menu. We should throw a signal.
+
+FIXME: There is noticeable decrease in speed, also. Perhaps,
+ there whole feature needs to be revisited therefore.
+ Disadvantage of a more elaborated, say block-oriented
+ scheme with wrap around would be it's complexity.
+*/
+
+//FIXME: tempory replacement for tmpfile
+// this is here one for debugging purpose.
+
+//#define tmpfile xTmpFile
+
+// History File ///////////////////////////////////////////
+
+/*
+ A Row(X) data type which allows adding elements to the end.
+*/
+
+HistoryFile::HistoryFile()
+ : ion(-1),
+ length(0),
+ fileMap(0)
+{
+ if (tmpFile.open())
+ {
+ tmpFile.setAutoRemove(true);
+ ion = tmpFile.handle();
+ }
+}
+
+HistoryFile::~HistoryFile()
+{
+ if (fileMap)
+ unmap();
+}
+
+//TODO: Mapping the entire file in will cause problems if the history file becomes exceedingly large,
+//(ie. larger than available memory). HistoryFile::map() should only map in sections of the file at a time,
+//to avoid this.
+void HistoryFile::map()
+{
+ assert( fileMap == 0 );
+
+ fileMap = (char*)mmap( 0 , length , PROT_READ , MAP_PRIVATE , ion , 0 );
+
+ //if mmap'ing fails, fall back to the read-lseek combination
+ if ( fileMap == MAP_FAILED )
+ {
+ readWriteBalance = 0;
+ fileMap = 0;
+ qDebug() << ": mmap'ing history failed. errno = " << errno;
+ }
+}
+
+void HistoryFile::unmap()
+{
+ int result = munmap( fileMap , length );
+ assert( result == 0 );
+
+ fileMap = 0;
+}
+
+bool HistoryFile::isMapped()
+{
+ return (fileMap != 0);
+}
+
+void HistoryFile::add(const unsigned char* bytes, int len)
+{
+ if ( fileMap )
+ unmap();
+
+ readWriteBalance++;
+
+ int rc = 0;
+
+ rc = lseek(ion,length,SEEK_SET); if (rc < 0) { perror("HistoryFile::add.seek"); return; }
+ rc = write(ion,bytes,len); if (rc < 0) { perror("HistoryFile::add.write"); return; }
+ length += rc;
+}
+
+void HistoryFile::get(unsigned char* bytes, int len, int loc)
+{
+ //count number of get() calls vs. number of add() calls.
+ //If there are many more get() calls compared with add()
+ //calls (decided by using MAP_THRESHOLD) then mmap the log
+ //file to improve performance.
+ readWriteBalance--;
+ if ( !fileMap && readWriteBalance < MAP_THRESHOLD )
+ map();
+
+ if ( fileMap )
+ {
+ for (int i=0;i<len;i++)
+ bytes[i]=fileMap[loc+i];
+ }
+ else
+ {
+ int rc = 0;
+
+ if (loc < 0 || len < 0 || loc + len > length)
+ fprintf(stderr,"getHist(...,%d,%d): invalid args.\n",len,loc);
+ rc = lseek(ion,loc,SEEK_SET); if (rc < 0) { perror("HistoryFile::get.seek"); return; }
+ rc = read(ion,bytes,len); if (rc < 0) { perror("HistoryFile::get.read"); return; }
+ }
+}
+
+int HistoryFile::len()
+{
+ return length;
+}
+
+
+// History Scroll abstract base class //////////////////////////////////////
+
+
+HistoryScroll::HistoryScroll(HistoryType* t)
+ : m_histType(t)
+{
+}
+
+HistoryScroll::~HistoryScroll()
+{
+ delete m_histType;
+}
+
+bool HistoryScroll::hasScroll()
+{
+ return true;
+}
+
+// History Scroll File //////////////////////////////////////
+
+/*
+ The history scroll makes a Row(Row(Cell)) from
+ two history buffers. The index buffer contains
+ start of line positions which refere to the cells
+ buffer.
+
+ Note that index[0] addresses the second line
+ (line #1), while the first line (line #0) starts
+ at 0 in cells.
+*/
+
+HistoryScrollFile::HistoryScrollFile(const QString &logFileName)
+ : HistoryScroll(new HistoryTypeFile(logFileName)),
+ m_logFileName(logFileName)
+{
+}
+
+HistoryScrollFile::~HistoryScrollFile()
+{
+}
+
+int HistoryScrollFile::getLines()
+{
+ return index.len() / sizeof(int);
+}
+
+int HistoryScrollFile::getLineLen(int lineno)
+{
+ return (startOfLine(lineno+1) - startOfLine(lineno)) / sizeof(Character);
+}
+
+bool HistoryScrollFile::isWrappedLine(int lineno)
+{
+ if (lineno>=0 && lineno <= getLines()) {
+ unsigned char flag;
+ lineflags.get((unsigned char*)&flag,sizeof(unsigned char),(lineno)*sizeof(unsigned char));
+ return flag;
+ }
+ return false;
+}
+
+int HistoryScrollFile::startOfLine(int lineno)
+{
+ if (lineno <= 0) return 0;
+ if (lineno <= getLines())
+ {
+
+ if (!index.isMapped())
+ index.map();
+
+ int res;
+ index.get((unsigned char*)&res,sizeof(int),(lineno-1)*sizeof(int));
+ return res;
+ }
+ return cells.len();
+}
+
+void HistoryScrollFile::getCells(int lineno, int colno, int count, Character res[])
+{
+ cells.get((unsigned char*)res,count*sizeof(Character),startOfLine(lineno)+colno*sizeof(Character));
+}
+
+void HistoryScrollFile::addCells(const Character text[], int count)
+{
+ cells.add((unsigned char*)text,count*sizeof(Character));
+}
+
+void HistoryScrollFile::addLine(bool previousWrapped)
+{
+ if (index.isMapped())
+ index.unmap();
+
+ int locn = cells.len();
+ index.add((unsigned char*)&locn,sizeof(int));
+ unsigned char flags = previousWrapped ? 0x01 : 0x00;
+ lineflags.add((unsigned char*)&flags,sizeof(unsigned char));
+}
+
+
+// History Scroll Buffer //////////////////////////////////////
+HistoryScrollBuffer::HistoryScrollBuffer(unsigned int maxLineCount)
+ : HistoryScroll(new HistoryTypeBuffer(maxLineCount))
+ ,_historyBuffer()
+ ,_maxLineCount(0)
+ ,_usedLines(0)
+ ,_head(0)
+{
+ setMaxNbLines(maxLineCount);
+}
+
+HistoryScrollBuffer::~HistoryScrollBuffer()
+{
+ delete[] _historyBuffer;
+}
+
+void HistoryScrollBuffer::addCellsVector(const QVector<Character>& cells)
+{
+ _head++;
+ if ( _usedLines < _maxLineCount )
+ _usedLines++;
+
+ if ( _head >= _maxLineCount )
+ {
+ _head = 0;
+ }
+
+ _historyBuffer[bufferIndex(_usedLines-1)] = cells;
+ _wrappedLine[bufferIndex(_usedLines-1)] = false;
+}
+void HistoryScrollBuffer::addCells(const Character a[], int count)
+{
+ HistoryLine newLine(count);
+ qCopy(a,a+count,newLine.begin());
+
+ addCellsVector(newLine);
+}
+
+void HistoryScrollBuffer::addLine(bool previousWrapped)
+{
+ _wrappedLine[bufferIndex(_usedLines-1)] = previousWrapped;
+}
+
+int HistoryScrollBuffer::getLines()
+{
+ return _usedLines;
+}
+
+int HistoryScrollBuffer::getLineLen(int lineNumber)
+{
+ Q_ASSERT( lineNumber >= 0 && lineNumber < _maxLineCount );
+
+ if ( lineNumber < _usedLines )
+ {
+ return _historyBuffer[bufferIndex(lineNumber)].size();
+ }
+ else
+ {
+ return 0;
+ }
+}
+
+bool HistoryScrollBuffer::isWrappedLine(int lineNumber)
+{
+ Q_ASSERT( lineNumber >= 0 && lineNumber < _maxLineCount );
+
+ if (lineNumber < _usedLines)
+ {
+ //kDebug() << "Line" << lineNumber << "wrapped is" << _wrappedLine[bufferIndex(lineNumber)];
+ return _wrappedLine[bufferIndex(lineNumber)];
+ }
+ else
+ return false;
+}
+
+void HistoryScrollBuffer::getCells(int lineNumber, int startColumn, int count, Character* buffer)
+{
+ if ( count == 0 ) return;
+
+ Q_ASSERT( lineNumber < _maxLineCount );
+
+ if (lineNumber >= _usedLines)
+ {
+ memset(buffer, 0, count * sizeof(Character));
+ return;
+ }
+
+ const HistoryLine& line = _historyBuffer[bufferIndex(lineNumber)];
+
+ //kDebug() << "startCol " << startColumn;
+ //kDebug() << "line.size() " << line.size();
+ //kDebug() << "count " << count;
+
+ Q_ASSERT( startColumn <= line.size() - count );
+
+ memcpy(buffer, line.constData() + startColumn , count * sizeof(Character));
+}
+
+void HistoryScrollBuffer::setMaxNbLines(unsigned int lineCount)
+{
+ HistoryLine* oldBuffer = _historyBuffer;
+ HistoryLine* newBuffer = new HistoryLine[lineCount];
+
+ for ( int i = 0 ; i < qMin(_usedLines,(int)lineCount) ; i++ )
+ {
+ newBuffer[i] = oldBuffer[bufferIndex(i)];
+ }
+
+ _usedLines = qMin(_usedLines,(int)lineCount);
+ _maxLineCount = lineCount;
+ _head = ( _usedLines == _maxLineCount ) ? 0 : _usedLines-1;
+
+ _historyBuffer = newBuffer;
+ delete[] oldBuffer;
+
+ _wrappedLine.resize(lineCount);
+}
+
+int HistoryScrollBuffer::bufferIndex(int lineNumber)
+{
+ Q_ASSERT( lineNumber >= 0 );
+ Q_ASSERT( lineNumber < _maxLineCount );
+ Q_ASSERT( (_usedLines == _maxLineCount) || lineNumber <= _head );
+
+ if ( _usedLines == _maxLineCount )
+ {
+ return (_head+lineNumber+1) % _maxLineCount;
+ }
+ else
+ {
+ return lineNumber;
+ }
+}
+
+
+// History Scroll None //////////////////////////////////////
+
+HistoryScrollNone::HistoryScrollNone()
+ : HistoryScroll(new HistoryTypeNone())
+{
+}
+
+HistoryScrollNone::~HistoryScrollNone()
+{
+}
+
+bool HistoryScrollNone::hasScroll()
+{
+ return false;
+}
+
+int HistoryScrollNone::getLines()
+{
+ return 0;
+}
+
+int HistoryScrollNone::getLineLen(int)
+{
+ return 0;
+}
+
+bool HistoryScrollNone::isWrappedLine(int /*lineno*/)
+{
+ return false;
+}
+
+void HistoryScrollNone::getCells(int, int, int, Character [])
+{
+}
+
+void HistoryScrollNone::addCells(const Character [], int)
+{
+}
+
+void HistoryScrollNone::addLine(bool)
+{
+}
+
+// History Scroll BlockArray //////////////////////////////////////
+
+HistoryScrollBlockArray::HistoryScrollBlockArray(size_t size)
+ : HistoryScroll(new HistoryTypeBlockArray(size))
+{
+ m_blockArray.setHistorySize(size); // nb. of lines.
+}
+
+HistoryScrollBlockArray::~HistoryScrollBlockArray()
+{
+}
+
+int HistoryScrollBlockArray::getLines()
+{
+ return m_lineLengths.count();
+}
+
+int HistoryScrollBlockArray::getLineLen(int lineno)
+{
+ if ( m_lineLengths.contains(lineno) )
+ return m_lineLengths[lineno];
+ else
+ return 0;
+}
+
+bool HistoryScrollBlockArray::isWrappedLine(int /*lineno*/)
+{
+ return false;
+}
+
+void HistoryScrollBlockArray::getCells(int lineno, int colno,
+ int count, Character res[])
+{
+ if (!count) return;
+
+ const Block *b = m_blockArray.at(lineno);
+
+ if (!b) {
+ memset(res, 0, count * sizeof(Character)); // still better than random data
+ return;
+ }
+
+ assert(((colno + count) * sizeof(Character)) < ENTRIES);
+ memcpy(res, b->data + (colno * sizeof(Character)), count * sizeof(Character));
+}
+
+void HistoryScrollBlockArray::addCells(const Character a[], int count)
+{
+ Block *b = m_blockArray.lastBlock();
+
+ if (!b) return;
+
+ // put cells in block's data
+ assert((count * sizeof(Character)) < ENTRIES);
+
+ memset(b->data, 0, ENTRIES);
+
+ memcpy(b->data, a, count * sizeof(Character));
+ b->size = count * sizeof(Character);
+
+ size_t res = m_blockArray.newBlock();
+ assert (res > 0);
+ Q_UNUSED( res );
+
+ m_lineLengths.insert(m_blockArray.getCurrent(), count);
+}
+
+void HistoryScrollBlockArray::addLine(bool)
+{
+}
+
+//////////////////////////////////////////////////////////////////////
+// History Types
+//////////////////////////////////////////////////////////////////////
+
+HistoryType::HistoryType()
+{
+}
+
+HistoryType::~HistoryType()
+{
+}
+
+//////////////////////////////
+
+HistoryTypeNone::HistoryTypeNone()
+{
+}
+
+bool HistoryTypeNone::isEnabled() const
+{
+ return false;
+}
+
+HistoryScroll* HistoryTypeNone::scroll(HistoryScroll *old) const
+{
+ delete old;
+ return new HistoryScrollNone();
+}
+
+int HistoryTypeNone::maximumLineCount() const
+{
+ return 0;
+}
+
+//////////////////////////////
+
+HistoryTypeBlockArray::HistoryTypeBlockArray(size_t size)
+ : m_size(size)
+{
+}
+
+bool HistoryTypeBlockArray::isEnabled() const
+{
+ return true;
+}
+
+int HistoryTypeBlockArray::maximumLineCount() const
+{
+ return m_size;
+}
+
+HistoryScroll* HistoryTypeBlockArray::scroll(HistoryScroll *old) const
+{
+ delete old;
+ return new HistoryScrollBlockArray(m_size);
+}
+
+
+//////////////////////////////
+
+HistoryTypeBuffer::HistoryTypeBuffer(unsigned int nbLines)
+ : m_nbLines(nbLines)
+{
+}
+
+bool HistoryTypeBuffer::isEnabled() const
+{
+ return true;
+}
+
+int HistoryTypeBuffer::maximumLineCount() const
+{
+ return m_nbLines;
+}
+
+HistoryScroll* HistoryTypeBuffer::scroll(HistoryScroll *old) const
+{
+ if (old)
+ {
+ HistoryScrollBuffer *oldBuffer = dynamic_cast<HistoryScrollBuffer*>(old);
+ if (oldBuffer)
+ {
+ oldBuffer->setMaxNbLines(m_nbLines);
+ return oldBuffer;
+ }
+
+ HistoryScroll *newScroll = new HistoryScrollBuffer(m_nbLines);
+ int lines = old->getLines();
+ int startLine = 0;
+ if (lines > (int) m_nbLines)
+ startLine = lines - m_nbLines;
+
+ Character line[LINE_SIZE];
+ for(int i = startLine; i < lines; i++)
+ {
+ int size = old->getLineLen(i);
+ if (size > LINE_SIZE)
+ {
+ Character *tmp_line = new Character[size];
+ old->getCells(i, 0, size, tmp_line);
+ newScroll->addCells(tmp_line, size);
+ newScroll->addLine(old->isWrappedLine(i));
+ delete [] tmp_line;
+ }
+ else
+ {
+ old->getCells(i, 0, size, line);
+ newScroll->addCells(line, size);
+ newScroll->addLine(old->isWrappedLine(i));
+ }
+ }
+ delete old;
+ return newScroll;
+ }
+ return new HistoryScrollBuffer(m_nbLines);
+}
+
+//////////////////////////////
+
+HistoryTypeFile::HistoryTypeFile(const QString& fileName)
+ : m_fileName(fileName)
+{
+}
+
+bool HistoryTypeFile::isEnabled() const
+{
+ return true;
+}
+
+const QString& HistoryTypeFile::getFileName() const
+{
+ return m_fileName;
+}
+
+HistoryScroll* HistoryTypeFile::scroll(HistoryScroll *old) const
+{
+ if (dynamic_cast<HistoryFile *>(old))
+ return old; // Unchanged.
+
+ HistoryScroll *newScroll = new HistoryScrollFile(m_fileName);
+
+ Character line[LINE_SIZE];
+ int lines = (old != 0) ? old->getLines() : 0;
+ for(int i = 0; i < lines; i++)
+ {
+ int size = old->getLineLen(i);
+ if (size > LINE_SIZE)
+ {
+ Character *tmp_line = new Character[size];
+ old->getCells(i, 0, size, tmp_line);
+ newScroll->addCells(tmp_line, size);
+ newScroll->addLine(old->isWrappedLine(i));
+ delete [] tmp_line;
+ }
+ else
+ {
+ old->getCells(i, 0, size, line);
+ newScroll->addCells(line, size);
+ newScroll->addLine(old->isWrappedLine(i));
+ }
+ }
+
+ delete old;
+ return newScroll;
+}
+
+int HistoryTypeFile::maximumLineCount() const
+{
+ return 0;
+}
diff --git a/qtermwidget/History.h b/qtermwidget/History.h
new file mode 100644
index 0000000..a26a367
--- /dev/null
+++ b/qtermwidget/History.h
@@ -0,0 +1,344 @@
+/*
+ This file is part of Konsole, an X terminal.
+ Copyright (C) 1997,1998 by Lars Doelle <lars.doelle@on-line.de>
+
+ Rewritten for QT4 by e_k <e_k at users.sourceforge.net>, Copyright (C)2008
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ 02110-1301 USA.
+*/
+
+#ifndef TEHISTORY_H
+#define TEHISTORY_H
+
+// Qt
+#include <QtCore/QBitRef>
+#include <QtCore/QHash>
+#include <QtCore>
+
+// Konsole
+#include "BlockArray.h"
+#include "Character.h"
+
+namespace Konsole
+{
+
+#if 1
+/*
+ An extendable tmpfile(1) based buffer.
+*/
+
+class HistoryFile
+{
+public:
+ HistoryFile();
+ virtual ~HistoryFile();
+
+ virtual void add(const unsigned char* bytes, int len);
+ virtual void get(unsigned char* bytes, int len, int loc);
+ virtual int len();
+
+ //mmaps the file in read-only mode
+ void map();
+ //un-mmaps the file
+ void unmap();
+ //returns true if the file is mmap'ed
+ bool isMapped();
+
+
+private:
+ int ion;
+ int length;
+ QTemporaryFile tmpFile;
+
+ //pointer to start of mmap'ed file data, or 0 if the file is not mmap'ed
+ char* fileMap;
+
+ //incremented whenver 'add' is called and decremented whenever
+ //'get' is called.
+ //this is used to detect when a large number of lines are being read and processed from the history
+ //and automatically mmap the file for better performance (saves the overhead of many lseek-read calls).
+ int readWriteBalance;
+
+ //when readWriteBalance goes below this threshold, the file will be mmap'ed automatically
+ static const int MAP_THRESHOLD = -1000;
+};
+#endif
+
+//////////////////////////////////////////////////////////////////////
+
+//////////////////////////////////////////////////////////////////////
+// Abstract base class for file and buffer versions
+//////////////////////////////////////////////////////////////////////
+class HistoryType;
+
+class HistoryScroll
+{
+public:
+ HistoryScroll(HistoryType*);
+ virtual ~HistoryScroll();
+
+ virtual bool hasScroll();
+
+ // access to history
+ virtual int getLines() = 0;
+ virtual int getLineLen(int lineno) = 0;
+ virtual void getCells(int lineno, int colno, int count, Character res[]) = 0;
+ virtual bool isWrappedLine(int lineno) = 0;
+
+ // backward compatibility (obsolete)
+ Character getCell(int lineno, int colno) { Character res; getCells(lineno,colno,1,&res); return res; }
+
+ // adding lines.
+ virtual void addCells(const Character a[], int count) = 0;
+ // convenience method - this is virtual so that subclasses can take advantage
+ // of QVector's implicit copying
+ virtual void addCellsVector(const QVector<Character>& cells)
+ {
+ addCells(cells.data(),cells.size());
+ }
+
+ virtual void addLine(bool previousWrapped=false) = 0;
+
+ //
+ // FIXME: Passing around constant references to HistoryType instances
+ // is very unsafe, because those references will no longer
+ // be valid if the history scroll is deleted.
+ //
+ const HistoryType& getType() { return *m_histType; }
+
+protected:
+ HistoryType* m_histType;
+
+};
+
+#if 1
+
+//////////////////////////////////////////////////////////////////////
+// File-based history (e.g. file log, no limitation in length)
+//////////////////////////////////////////////////////////////////////
+
+class HistoryScrollFile : public HistoryScroll
+{
+public:
+ HistoryScrollFile(const QString &logFileName);
+ virtual ~HistoryScrollFile();
+
+ virtual int getLines();
+ virtual int getLineLen(int lineno);
+ virtual void getCells(int lineno, int colno, int count, Character res[]);
+ virtual bool isWrappedLine(int lineno);
+
+ virtual void addCells(const Character a[], int count);
+ virtual void addLine(bool previousWrapped=false);
+
+private:
+ int startOfLine(int lineno);
+
+ QString m_logFileName;
+ HistoryFile index; // lines Row(int)
+ HistoryFile cells; // text Row(Character)
+ HistoryFile lineflags; // flags Row(unsigned char)
+};
+
+
+//////////////////////////////////////////////////////////////////////
+// Buffer-based history (limited to a fixed nb of lines)
+//////////////////////////////////////////////////////////////////////
+class HistoryScrollBuffer : public HistoryScroll
+{
+public:
+ typedef QVector<Character> HistoryLine;
+
+ HistoryScrollBuffer(unsigned int maxNbLines = 1000);
+ virtual ~HistoryScrollBuffer();
+
+ virtual int getLines();
+ virtual int getLineLen(int lineno);
+ virtual void getCells(int lineno, int colno, int count, Character res[]);
+ virtual bool isWrappedLine(int lineno);
+
+ virtual void addCells(const Character a[], int count);
+ virtual void addCellsVector(const QVector<Character>& cells);
+ virtual void addLine(bool previousWrapped=false);
+
+ void setMaxNbLines(unsigned int nbLines);
+ unsigned int maxNbLines() { return _maxLineCount; }
+
+
+private:
+ int bufferIndex(int lineNumber);
+
+ HistoryLine* _historyBuffer;
+ QBitArray _wrappedLine;
+ int _maxLineCount;
+ int _usedLines;
+ int _head;
+
+ //QVector<histline*> m_histBuffer;
+ //QBitArray m_wrappedLine;
+ //unsigned int m_maxNbLines;
+ //unsigned int m_nbLines;
+ //unsigned int m_arrayIndex;
+ //bool m_buffFilled;
+};
+
+/*class HistoryScrollBufferV2 : public HistoryScroll
+{
+public:
+ virtual int getLines();
+ virtual int getLineLen(int lineno);
+ virtual void getCells(int lineno, int colno, int count, Character res[]);
+ virtual bool isWrappedLine(int lineno);
+
+ virtual void addCells(const Character a[], int count);
+ virtual void addCells(const QVector<Character>& cells);
+ virtual void addLine(bool previousWrapped=false);
+
+};*/
+
+#endif
+
+//////////////////////////////////////////////////////////////////////
+// Nothing-based history (no history :-)
+//////////////////////////////////////////////////////////////////////
+class HistoryScrollNone : public HistoryScroll
+{
+public:
+ HistoryScrollNone();
+ virtual ~HistoryScrollNone();
+
+ virtual bool hasScroll();
+
+ virtual int getLines();
+ virtual int getLineLen(int lineno);
+ virtual void getCells(int lineno, int colno, int count, Character res[]);
+ virtual bool isWrappedLine(int lineno);
+
+ virtual void addCells(const Character a[], int count);
+ virtual void addLine(bool previousWrapped=false);
+};
+
+//////////////////////////////////////////////////////////////////////
+// BlockArray-based history
+//////////////////////////////////////////////////////////////////////
+class HistoryScrollBlockArray : public HistoryScroll
+{
+public:
+ HistoryScrollBlockArray(size_t size);
+ virtual ~HistoryScrollBlockArray();
+
+ virtual int getLines();
+ virtual int getLineLen(int lineno);
+ virtual void getCells(int lineno, int colno, int count, Character res[]);
+ virtual bool isWrappedLine(int lineno);
+
+ virtual void addCells(const Character a[], int count);
+ virtual void addLine(bool previousWrapped=false);
+
+protected:
+ BlockArray m_blockArray;
+ QHash<int,size_t> m_lineLengths;
+};
+
+//////////////////////////////////////////////////////////////////////
+// History type
+//////////////////////////////////////////////////////////////////////
+
+class HistoryType
+{
+public:
+ HistoryType();
+ virtual ~HistoryType();
+
+ /**
+ * Returns true if the history is enabled ( can store lines of output )
+ * or false otherwise.
+ */
+ virtual bool isEnabled() const = 0;
+ /**
+ * Returns true if the history size is unlimited.
+ */
+ bool isUnlimited() const { return maximumLineCount() == 0; }
+ /**
+ * Returns the maximum number of lines which this history type
+ * can store or 0 if the history can store an unlimited number of lines.
+ */
+ virtual int maximumLineCount() const = 0;
+
+ virtual HistoryScroll* scroll(HistoryScroll *) const = 0;
+};
+
+class HistoryTypeNone : public HistoryType
+{
+public:
+ HistoryTypeNone();
+
+ virtual bool isEnabled() const;
+ virtual int maximumLineCount() const;
+
+ virtual HistoryScroll* scroll(HistoryScroll *) const;
+};
+
+class HistoryTypeBlockArray : public HistoryType
+{
+public:
+ HistoryTypeBlockArray(size_t size);
+
+ virtual bool isEnabled() const;
+ virtual int maximumLineCount() const;
+
+ virtual HistoryScroll* scroll(HistoryScroll *) const;
+
+protected:
+ size_t m_size;
+};
+
+#if 1
+class HistoryTypeFile : public HistoryType
+{
+public:
+ HistoryTypeFile(const QString& fileName=QString());
+
+ virtual bool isEnabled() const;
+ virtual const QString& getFileName() const;
+ virtual int maximumLineCount() const;
+
+ virtual HistoryScroll* scroll(HistoryScroll *) const;
+
+protected:
+ QString m_fileName;
+};
+
+
+class HistoryTypeBuffer : public HistoryType
+{
+public:
+ HistoryTypeBuffer(unsigned int nbLines);
+
+ virtual bool isEnabled() const;
+ virtual int maximumLineCount() const;
+
+ virtual HistoryScroll* scroll(HistoryScroll *) const;
+
+protected:
+ unsigned int m_nbLines;
+};
+
+#endif
+
+}
+
+#endif // TEHISTORY_H
diff --git a/qtermwidget/KeyboardTranslator.cpp b/qtermwidget/KeyboardTranslator.cpp
new file mode 100644
index 0000000..1f7f112
--- /dev/null
+++ b/qtermwidget/KeyboardTranslator.cpp
@@ -0,0 +1,903 @@
+/*
+ This source file was part of Konsole, a terminal emulator.
+
+ Copyright (C) 2007 by Robert Knight <robertknight@gmail.com>
+
+ Rewritten for QT4 by e_k <e_k at users.sourceforge.net>, Copyright (C)2008
+
+ 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.
+*/
+
+// Own
+#include "KeyboardTranslator.h"
+
+// System
+#include <ctype.h>
+#include <stdio.h>
+
+// Qt
+#include <QtCore/QBuffer>
+//#include <KDebug>
+#include <QtCore/QFile>
+#include <QtCore/QFileInfo>
+#include <QtCore>
+#include <QtGui>
+
+// KDE
+//#include <KDebug>
+//#include <KLocale>
+//#include <KStandardDirs>
+
+using namespace Konsole;
+
+//this is for default REALLY fallback translator.
+
+//const char* KeyboardTranslatorManager::defaultTranslatorText =
+//#include "DefaultTranslatorText.h"
+//;
+
+//and this is default now translator - default.keytab from original Konsole
+const char* KeyboardTranslatorManager::defaultTranslatorText =
+#include "ExtendedDefaultTranslator.h"
+;
+
+KeyboardTranslatorManager::KeyboardTranslatorManager()
+ : _haveLoadedAll(false)
+{
+}
+KeyboardTranslatorManager::~KeyboardTranslatorManager()
+{
+ qDeleteAll(_translators.values());
+}
+QString KeyboardTranslatorManager::findTranslatorPath(const QString& name)
+{
+ return QString("kb-layouts/" + name + ".keytab");
+}
+void KeyboardTranslatorManager::findTranslators()
+{
+ QDir dir("kb-layouts/");
+ QStringList filters;
+ filters << "*.keytab";
+ dir.setNameFilters(filters);
+ QStringList list = dir.entryList(filters); //(".keytab"); // = KGlobal::dirs()->findAllResources("data",
+ // "konsole/*.keytab",
+ // KStandardDirs::NoDuplicates);
+ list = dir.entryList(filters);
+ // add the name of each translator to the list and associated
+ // the name with a null pointer to indicate that the translator
+ // has not yet been loaded from disk
+ QStringListIterator listIter(list);
+ while (listIter.hasNext())
+ {
+ QString translatorPath = listIter.next();
+
+ QString name = QFileInfo(translatorPath).baseName();
+
+ if ( !_translators.contains(name) ) {
+ _translators.insert(name,0);
+ }
+ }
+ _haveLoadedAll = true;
+}
+
+const KeyboardTranslator* KeyboardTranslatorManager::findTranslator(const QString& name)
+{
+ if ( name.isEmpty() )
+ return defaultTranslator();
+
+//here was smth wrong in original Konsole source
+ findTranslators();
+
+ if ( _translators.contains(name) && _translators[name] != 0 ) {
+ return _translators[name];
+ }
+
+ KeyboardTranslator* translator = loadTranslator(name);
+
+ if ( translator != 0 )
+ _translators[name] = translator;
+ else if ( !name.isEmpty() )
+ qWarning() << "Unable to load translator" << name;
+
+ return translator;
+}
+
+bool KeyboardTranslatorManager::saveTranslator(const KeyboardTranslator* translator)
+{
+ const QString path = ".keytab";// = KGlobal::dirs()->saveLocation("data","konsole/")+translator->name()
+// +".keytab";
+
+ qDebug() << "Saving translator to" << path;
+
+ QFile destination(path);
+
+ if (!destination.open(QIODevice::WriteOnly | QIODevice::Text))
+ {
+ qWarning() << "Unable to save keyboard translation:"
+ << destination.errorString();
+
+ return false;
+ }
+
+ {
+ KeyboardTranslatorWriter writer(&destination);
+ writer.writeHeader(translator->description());
+
+ QListIterator<KeyboardTranslator::Entry> iter(translator->entries());
+ while ( iter.hasNext() )
+ writer.writeEntry(iter.next());
+ }
+
+ destination.close();
+
+ return true;
+}
+
+KeyboardTranslator* KeyboardTranslatorManager::loadTranslator(const QString& name)
+{
+ const QString& path = findTranslatorPath(name);
+
+ QFile source(path);
+
+ if (name.isEmpty() || !source.open(QIODevice::ReadOnly | QIODevice::Text))
+ return 0;
+
+ return loadTranslator(&source,name);
+}
+
+const KeyboardTranslator* KeyboardTranslatorManager::defaultTranslator()
+{
+ qDebug() << "Loading default translator from text";
+ QBuffer textBuffer;
+ textBuffer.setData(defaultTranslatorText,strlen(defaultTranslatorText));
+
+ if (!textBuffer.open(QIODevice::ReadOnly))
+ return 0;
+
+ return loadTranslator(&textBuffer,"fallback");
+}
+
+KeyboardTranslator* KeyboardTranslatorManager::loadTranslator(QIODevice* source,const QString& name)
+{
+ KeyboardTranslator* translator = new KeyboardTranslator(name);
+ KeyboardTranslatorReader reader(source);
+ translator->setDescription( reader.description() );
+
+ while ( reader.hasNextEntry() ) {
+ translator->addEntry(reader.nextEntry());
+ }
+
+ source->close();
+
+ if ( !reader.parseError() )
+ {
+ return translator;
+ }
+ else
+ {
+ delete translator;
+ return 0;
+ }
+}
+
+KeyboardTranslatorWriter::KeyboardTranslatorWriter(QIODevice* destination)
+: _destination(destination)
+{
+ Q_ASSERT( destination && destination->isWritable() );
+
+ _writer = new QTextStream(_destination);
+}
+KeyboardTranslatorWriter::~KeyboardTranslatorWriter()
+{
+ delete _writer;
+}
+void KeyboardTranslatorWriter::writeHeader( const QString& description )
+{
+ *_writer << "keyboard \"" << description << '\"' << '\n';
+}
+void KeyboardTranslatorWriter::writeEntry( const KeyboardTranslator::Entry& entry )
+{
+ QString result;
+
+ if ( entry.command() != KeyboardTranslator::NoCommand )
+ result = entry.resultToString();
+ else
+ result = '\"' + entry.resultToString() + '\"';
+
+ *_writer << "key " << entry.conditionToString() << " : " << result << '\n';
+}
+
+
+// each line of the keyboard translation file is one of:
+//
+// - keyboard "name"
+// - key KeySequence : "characters"
+// - key KeySequence : CommandName
+//
+// KeySequence begins with the name of the key ( taken from the Qt::Key enum )
+// and is followed by the keyboard modifiers and state flags ( with + or - in front
+// of each modifier or flag to indicate whether it is required ). All keyboard modifiers
+// and flags are optional, if a particular modifier or state is not specified it is
+// assumed not to be a part of the sequence. The key sequence may contain whitespace
+//
+// eg: "key Up+Shift : scrollLineUp"
+// "key Next-Shift : "\E[6~"
+//
+// (lines containing only whitespace are ignored, parseLine assumes that comments have
+// already been removed)
+//
+
+KeyboardTranslatorReader::KeyboardTranslatorReader( QIODevice* source )
+ : _source(source)
+ , _hasNext(false)
+{
+ // read input until we find the description
+ while ( _description.isEmpty() && !source->atEnd() )
+ {
+ const QList<Token>& tokens = tokenize( QString(source->readLine()) );
+
+ if ( !tokens.isEmpty() && tokens.first().type == Token::TitleKeyword )
+ {
+ _description = (tokens[1].text.toUtf8());
+ }
+ }
+
+ readNext();
+}
+void KeyboardTranslatorReader::readNext()
+{
+ // find next entry
+ while ( !_source->atEnd() )
+ {
+ const QList<Token>& tokens = tokenize( QString(_source->readLine()) );
+ if ( !tokens.isEmpty() && tokens.first().type == Token::KeyKeyword )
+ {
+ KeyboardTranslator::States flags = KeyboardTranslator::NoState;
+ KeyboardTranslator::States flagMask = KeyboardTranslator::NoState;
+ Qt::KeyboardModifiers modifiers = Qt::NoModifier;
+ Qt::KeyboardModifiers modifierMask = Qt::NoModifier;
+
+ int keyCode = Qt::Key_unknown;
+
+ decodeSequence(tokens[1].text.toLower(),
+ keyCode,
+ modifiers,
+ modifierMask,
+ flags,
+ flagMask);
+
+ KeyboardTranslator::Command command = KeyboardTranslator::NoCommand;
+ QByteArray text;
+
+ // get text or command
+ if ( tokens[2].type == Token::OutputText )
+ {
+ text = tokens[2].text.toLocal8Bit();
+ }
+ else if ( tokens[2].type == Token::Command )
+ {
+ // identify command
+ if (!parseAsCommand(tokens[2].text,command))
+ qWarning() << "Command" << tokens[2].text << "not understood.";
+ }
+
+ KeyboardTranslator::Entry newEntry;
+ newEntry.setKeyCode( keyCode );
+ newEntry.setState( flags );
+ newEntry.setStateMask( flagMask );
+ newEntry.setModifiers( modifiers );
+ newEntry.setModifierMask( modifierMask );
+ newEntry.setText( text );
+ newEntry.setCommand( command );
+
+ _nextEntry = newEntry;
+
+ _hasNext = true;
+
+ return;
+ }
+ }
+
+ _hasNext = false;
+}
+
+bool KeyboardTranslatorReader::parseAsCommand(const QString& text,KeyboardTranslator::Command& command)
+{
+ if ( text.compare("erase",Qt::CaseInsensitive) == 0 )
+ command = KeyboardTranslator::EraseCommand;
+ else if ( text.compare("scrollpageup",Qt::CaseInsensitive) == 0 )
+ command = KeyboardTranslator::ScrollPageUpCommand;
+ else if ( text.compare("scrollpagedown",Qt::CaseInsensitive) == 0 )
+ command = KeyboardTranslator::ScrollPageDownCommand;
+ else if ( text.compare("scrolllineup",Qt::CaseInsensitive) == 0 )
+ command = KeyboardTranslator::ScrollLineUpCommand;
+ else if ( text.compare("scrolllinedown",Qt::CaseInsensitive) == 0 )
+ command = KeyboardTranslator::ScrollLineDownCommand;
+ else if ( text.compare("scrolllock",Qt::CaseInsensitive) == 0 )
+ command = KeyboardTranslator::ScrollLockCommand;
+ else
+ return false;
+
+ return true;
+}
+
+bool KeyboardTranslatorReader::decodeSequence(const QString& text,
+ int& keyCode,
+ Qt::KeyboardModifiers& modifiers,
+ Qt::KeyboardModifiers& modifierMask,
+ KeyboardTranslator::States& flags,
+ KeyboardTranslator::States& flagMask)
+{
+ bool isWanted = true;
+ bool endOfItem = false;
+ QString buffer;
+
+ Qt::KeyboardModifiers tempModifiers = modifiers;
+ Qt::KeyboardModifiers tempModifierMask = modifierMask;
+ KeyboardTranslator::States tempFlags = flags;
+ KeyboardTranslator::States tempFlagMask = flagMask;
+
+ for ( int i = 0 ; i < text.count() ; i++ )
+ {
+ const QChar& ch = text[i];
+ bool isLastLetter = ( i == text.count()-1 );
+
+ endOfItem = true;
+ if ( ch.isLetterOrNumber() )
+ {
+ endOfItem = false;
+ buffer.append(ch);
+ }
+
+ if ( (endOfItem || isLastLetter) && !buffer.isEmpty() )
+ {
+ Qt::KeyboardModifier itemModifier = Qt::NoModifier;
+ int itemKeyCode = 0;
+ KeyboardTranslator::State itemFlag = KeyboardTranslator::NoState;
+
+ if ( parseAsModifier(buffer,itemModifier) )
+ {
+ tempModifierMask |= itemModifier;
+
+ if ( isWanted )
+ tempModifiers |= itemModifier;
+ }
+ else if ( parseAsStateFlag(buffer,itemFlag) )
+ {
+ tempFlagMask |= itemFlag;
+
+ if ( isWanted )
+ tempFlags |= itemFlag;
+ }
+ else if ( parseAsKeyCode(buffer,itemKeyCode) )
+ keyCode = itemKeyCode;
+ else
+ qDebug() << "Unable to parse key binding item:" << buffer;
+
+ buffer.clear();
+ }
+
+ // check if this is a wanted / not-wanted flag and update the
+ // state ready for the next item
+ if ( ch == '+' )
+ isWanted = true;
+ else if ( ch == '-' )
+ isWanted = false;
+ }
+
+ modifiers = tempModifiers;
+ modifierMask = tempModifierMask;
+ flags = tempFlags;
+ flagMask = tempFlagMask;
+
+ return true;
+}
+
+bool KeyboardTranslatorReader::parseAsModifier(const QString& item , Qt::KeyboardModifier& modifier)
+{
+ if ( item == "shift" )
+ modifier = Qt::ShiftModifier;
+ else if ( item == "ctrl" || item == "control" )
+ modifier = Qt::ControlModifier;
+ else if ( item == "alt" )
+ modifier = Qt::AltModifier;
+ else if ( item == "meta" )
+ modifier = Qt::MetaModifier;
+ else if ( item == "keypad" )
+ modifier = Qt::KeypadModifier;
+ else
+ return false;
+
+ return true;
+}
+bool KeyboardTranslatorReader::parseAsStateFlag(const QString& item , KeyboardTranslator::State& flag)
+{
+ if ( item == "appcukeys" )
+ flag = KeyboardTranslator::CursorKeysState;
+ else if ( item == "ansi" )
+ flag = KeyboardTranslator::AnsiState;
+ else if ( item == "newline" )
+ flag = KeyboardTranslator::NewLineState;
+ else if ( item == "appscreen" )
+ flag = KeyboardTranslator::AlternateScreenState;
+ else if ( item == "anymod" )
+ flag = KeyboardTranslator::AnyModifierState;
+ else
+ return false;
+
+ return true;
+}
+bool KeyboardTranslatorReader::parseAsKeyCode(const QString& item , int& keyCode)
+{
+ QKeySequence sequence = QKeySequence::fromString(item);
+ if ( !sequence.isEmpty() )
+ {
+ keyCode = sequence[0];
+
+ if ( sequence.count() > 1 )
+ {
+ qDebug() << "Unhandled key codes in sequence: " << item;
+ }
+ }
+ // additional cases implemented for backwards compatibility with KDE 3
+ else if ( item == "prior" )
+ keyCode = Qt::Key_PageUp;
+ else if ( item == "next" )
+ keyCode = Qt::Key_PageDown;
+ else
+ return false;
+
+ return true;
+}
+
+QString KeyboardTranslatorReader::description() const
+{
+ return _description;
+}
+bool KeyboardTranslatorReader::hasNextEntry()
+{
+ return _hasNext;
+}
+KeyboardTranslator::Entry KeyboardTranslatorReader::createEntry( const QString& condition ,
+ const QString& result )
+{
+ QString entryString("keyboard \"temporary\"\nkey ");
+ entryString.append(condition);
+ entryString.append(" : ");
+
+ // if 'result' is the name of a command then the entry result will be that command,
+ // otherwise the result will be treated as a string to echo when the key sequence
+ // specified by 'condition' is pressed
+ KeyboardTranslator::Command command;
+ if (parseAsCommand(result,command))
+ entryString.append(result);
+ else
+ entryString.append('\"' + result + '\"');
+
+ QByteArray array = entryString.toUtf8();
+
+ KeyboardTranslator::Entry entry;
+
+ QBuffer buffer(&array);
+ buffer.open(QIODevice::ReadOnly);
+ KeyboardTranslatorReader reader(&buffer);
+
+ if ( reader.hasNextEntry() )
+ entry = reader.nextEntry();
+
+ return entry;
+}
+
+KeyboardTranslator::Entry KeyboardTranslatorReader::nextEntry()
+{
+ Q_ASSERT( _hasNext );
+
+
+ KeyboardTranslator::Entry entry = _nextEntry;
+
+ readNext();
+
+ return entry;
+}
+bool KeyboardTranslatorReader::parseError()
+{
+ return false;
+}
+QList<KeyboardTranslatorReader::Token> KeyboardTranslatorReader::tokenize(const QString& line)
+{
+ QString text = line.simplified();
+
+ // comment line: # comment
+ static QRegExp comment("\\#.*");
+ // title line: keyboard "title"
+ static QRegExp title("keyboard\\s+\"(.*)\"");
+ // key line: key KeySequence : "output"
+ // key line: key KeySequence : command
+ static QRegExp key("key\\s+([\\w\\+\\s\\-]+)\\s*:\\s*(\"(.*)\"|\\w+)");
+
+ QList<Token> list;
+
+ if ( text.isEmpty() || comment.exactMatch(text) )
+ {
+ return list;
+ }
+
+ if ( title.exactMatch(text) )
+ {
+ Token titleToken = { Token::TitleKeyword , QString() };
+ Token textToken = { Token::TitleText , title.capturedTexts()[1] };
+
+ list << titleToken << textToken;
+ }
+ else if ( key.exactMatch(text) )
+ {
+ Token keyToken = { Token::KeyKeyword , QString() };
+ Token sequenceToken = { Token::KeySequence , key.capturedTexts()[1].remove(' ') };
+
+ list << keyToken << sequenceToken;
+
+ if ( key.capturedTexts()[3].isEmpty() )
+ {
+ // capturedTexts()[2] is a command
+ Token commandToken = { Token::Command , key.capturedTexts()[2] };
+ list << commandToken;
+ }
+ else
+ {
+ // capturedTexts()[3] is the output string
+ Token outputToken = { Token::OutputText , key.capturedTexts()[3] };
+ list << outputToken;
+ }
+ }
+ else
+ {
+ qWarning() << "Line in keyboard translator file could not be understood:" << text;
+ }
+
+ return list;
+}
+
+QList<QString> KeyboardTranslatorManager::allTranslators()
+{
+ if ( !_haveLoadedAll )
+ {
+ findTranslators();
+ }
+
+ return _translators.keys();
+}
+
+KeyboardTranslator::Entry::Entry()
+: _keyCode(0)
+, _modifiers(Qt::NoModifier)
+, _modifierMask(Qt::NoModifier)
+, _state(NoState)
+, _stateMask(NoState)
+, _command(NoCommand)
+{
+}
+
+bool KeyboardTranslator::Entry::operator==(const Entry& rhs) const
+{
+ return _keyCode == rhs._keyCode &&
+ _modifiers == rhs._modifiers &&
+ _modifierMask == rhs._modifierMask &&
+ _state == rhs._state &&
+ _stateMask == rhs._stateMask &&
+ _command == rhs._command &&
+ _text == rhs._text;
+}
+
+bool KeyboardTranslator::Entry::matches(int keyCode ,
+ Qt::KeyboardModifiers modifiers,
+ States state) const
+{
+ if ( _keyCode != keyCode )
+ return false;
+
+ if ( (modifiers & _modifierMask) != (_modifiers & _modifierMask) )
+ return false;
+
+ // if modifiers is non-zero, the 'any modifier' state is implicit
+ if ( modifiers != 0 )
+ state |= AnyModifierState;
+
+ if ( (state & _stateMask) != (_state & _stateMask) )
+ return false;
+
+ // special handling for the 'Any Modifier' state, which checks for the presence of
+ // any or no modifiers. In this context, the 'keypad' modifier does not count.
+ bool anyModifiersSet = modifiers != 0 && modifiers != Qt::KeypadModifier;
+ if ( _stateMask & KeyboardTranslator::AnyModifierState )
+ {
+ // test fails if any modifier is required but none are set
+ if ( (_state & KeyboardTranslator::AnyModifierState) && !anyModifiersSet )
+ return false;
+
+ // test fails if no modifier is allowed but one or more are set
+ if ( !(_state & KeyboardTranslator::AnyModifierState) && anyModifiersSet )
+ return false;
+ }
+
+ return true;
+}
+QByteArray KeyboardTranslator::Entry::escapedText(bool expandWildCards,Qt::KeyboardModifiers modifiers) const
+{
+ QByteArray result(text(expandWildCards,modifiers));
+
+ for ( int i = 0 ; i < result.count() ; i++ )
+ {
+ char ch = result[i];
+ char replacement = 0;
+
+ switch ( ch )
+ {
+ case 27 : replacement = 'E'; break;
+ case 8 : replacement = 'b'; break;
+ case 12 : replacement = 'f'; break;
+ case 9 : replacement = 't'; break;
+ case 13 : replacement = 'r'; break;
+ case 10 : replacement = 'n'; break;
+ default:
+ // any character which is not printable is replaced by an equivalent
+ // \xhh escape sequence (where 'hh' are the corresponding hex digits)
+ if ( !QChar(ch).isPrint() )
+ replacement = 'x';
+ }
+
+ if ( replacement == 'x' )
+ {
+ result.replace(i,1,"\\x"+QByteArray(1,ch).toInt(0, 16));
+ } else if ( replacement != 0 )
+ {
+ result.remove(i,1);
+ result.insert(i,'\\');
+ result.insert(i+1,replacement);
+ }
+ }
+
+ return result;
+}
+QByteArray KeyboardTranslator::Entry::unescape(const QByteArray& input) const
+{
+ QByteArray result(input);
+
+ for ( int i = 0 ; i < result.count()-1 ; i++ )
+ {
+
+ QByteRef ch = result[i];
+ if ( ch == '\\' )
+ {
+ char replacement[2] = {0,0};
+ int charsToRemove = 2;
+ bool escapedChar = true;
+
+ switch ( result[i+1] )
+ {
+ case 'E' : replacement[0] = 27; break;
+ case 'b' : replacement[0] = 8 ; break;
+ case 'f' : replacement[0] = 12; break;
+ case 't' : replacement[0] = 9 ; break;
+ case 'r' : replacement[0] = 13; break;
+ case 'n' : replacement[0] = 10; break;
+ case 'x' :
+ {
+ // format is \xh or \xhh where 'h' is a hexadecimal
+ // digit from 0-9 or A-F which should be replaced
+ // with the corresponding character value
+ char hexDigits[3] = {0};
+
+ if ( (i < result.count()-2) && isxdigit(result[i+2]) )
+ hexDigits[0] = result[i+2];
+ if ( (i < result.count()-3) && isxdigit(result[i+3]) )
+ hexDigits[1] = result[i+3];
+
+ int charValue = 0;
+ sscanf(hexDigits,"%x",&charValue);
+
+ replacement[0] = (char)charValue;
+
+ charsToRemove = 2 + strlen(hexDigits);
+ }
+ break;
+ default:
+ escapedChar = false;
+ }
+
+ if ( escapedChar )
+ result.replace(i,charsToRemove,replacement);
+ }
+ }
+
+ return result;
+}
+
+void KeyboardTranslator::Entry::insertModifier( QString& item , int modifier ) const
+{
+ if ( !(modifier & _modifierMask) )
+ return;
+
+ if ( modifier & _modifiers )
+ item += '+';
+ else
+ item += '-';
+
+ if ( modifier == Qt::ShiftModifier )
+ item += "Shift";
+ else if ( modifier == Qt::ControlModifier )
+ item += "Ctrl";
+ else if ( modifier == Qt::AltModifier )
+ item += "Alt";
+ else if ( modifier == Qt::MetaModifier )
+ item += "Meta";
+ else if ( modifier == Qt::KeypadModifier )
+ item += "KeyPad";
+}
+void KeyboardTranslator::Entry::insertState( QString& item , int state ) const
+{
+ if ( !(state & _stateMask) )
+ return;
+
+ if ( state & _state )
+ item += '+' ;
+ else
+ item += '-' ;
+
+ if ( state == KeyboardTranslator::AlternateScreenState )
+ item += "AppScreen";
+ else if ( state == KeyboardTranslator::NewLineState )
+ item += "NewLine";
+ else if ( state == KeyboardTranslator::AnsiState )
+ item += "Ansi";
+ else if ( state == KeyboardTranslator::CursorKeysState )
+ item += "AppCuKeys";
+ else if ( state == KeyboardTranslator::AnyModifierState )
+ item += "AnyMod";
+}
+QString KeyboardTranslator::Entry::resultToString(bool expandWildCards,Qt::KeyboardModifiers modifiers) const
+{
+ if ( !_text.isEmpty() )
+ return escapedText(expandWildCards,modifiers);
+ else if ( _command == EraseCommand )
+ return "Erase";
+ else if ( _command == ScrollPageUpCommand )
+ return "ScrollPageUp";
+ else if ( _command == ScrollPageDownCommand )
+ return "ScrollPageDown";
+ else if ( _command == ScrollLineUpCommand )
+ return "ScrollLineUp";
+ else if ( _command == ScrollLineDownCommand )
+ return "ScrollLineDown";
+ else if ( _command == ScrollLockCommand )
+ return "ScrollLock";
+
+ return QString();
+}
+QString KeyboardTranslator::Entry::conditionToString() const
+{
+ QString result = QKeySequence(_keyCode).toString();
+
+ // add modifiers
+ insertModifier( result , Qt::ShiftModifier );
+ insertModifier( result , Qt::ControlModifier );
+ insertModifier( result , Qt::AltModifier );
+ insertModifier( result , Qt::MetaModifier );
+
+ // add states
+ insertState( result , KeyboardTranslator::AlternateScreenState );
+ insertState( result , KeyboardTranslator::NewLineState );
+ insertState( result , KeyboardTranslator::AnsiState );
+ insertState( result , KeyboardTranslator::CursorKeysState );
+ insertState( result , KeyboardTranslator::AnyModifierState );
+
+ return result;
+}
+
+KeyboardTranslator::KeyboardTranslator(const QString& name)
+: _name(name)
+{
+}
+
+void KeyboardTranslator::setDescription(const QString& description)
+{
+ _description = description;
+}
+QString KeyboardTranslator::description() const
+{
+ return _description;
+}
+void KeyboardTranslator::setName(const QString& name)
+{
+ _name = name;
+}
+QString KeyboardTranslator::name() const
+{
+ return _name;
+}
+
+QList<KeyboardTranslator::Entry> KeyboardTranslator::entries() const
+{
+ return _entries.values();
+}
+
+void KeyboardTranslator::addEntry(const Entry& entry)
+{
+ const int keyCode = entry.keyCode();
+ _entries.insertMulti(keyCode,entry);
+}
+void KeyboardTranslator::replaceEntry(const Entry& existing , const Entry& replacement)
+{
+ if ( !existing.isNull() )
+ _entries.remove(existing.keyCode());
+ _entries.insertMulti(replacement.keyCode(),replacement);
+}
+void KeyboardTranslator::removeEntry(const Entry& entry)
+{
+ _entries.remove(entry.keyCode());
+}
+KeyboardTranslator::Entry KeyboardTranslator::findEntry(int keyCode, Qt::KeyboardModifiers modifiers, States state) const
+{
+ if ( _entries.contains(keyCode) )
+ {
+ QList<Entry> entriesForKey = _entries.values(keyCode);
+
+ QListIterator<Entry> iter(entriesForKey);
+
+ while (iter.hasNext())
+ {
+ const Entry& next = iter.next();
+ if ( next.matches(keyCode,modifiers,state) )
+ return next;
+ }
+
+ return Entry(); // entry not found
+ }
+ else
+ {
+ return Entry();
+ }
+
+}
+void KeyboardTranslatorManager::addTranslator(KeyboardTranslator* translator)
+{
+ _translators.insert(translator->name(),translator);
+
+ if ( !saveTranslator(translator) )
+ qWarning() << "Unable to save translator" << translator->name()
+ << "to disk.";
+}
+bool KeyboardTranslatorManager::deleteTranslator(const QString& name)
+{
+ Q_ASSERT( _translators.contains(name) );
+
+ // locate and delete
+ QString path = findTranslatorPath(name);
+ if ( QFile::remove(path) )
+ {
+ _translators.remove(name);
+ return true;
+ }
+ else
+ {
+ qWarning() << "Failed to remove translator - " << path;
+ return false;
+ }
+}
+K_GLOBAL_STATIC( KeyboardTranslatorManager , theKeyboardTranslatorManager )
+KeyboardTranslatorManager* KeyboardTranslatorManager::instance()
+{
+ return theKeyboardTranslatorManager;
+}
diff --git a/qtermwidget/KeyboardTranslator.h b/qtermwidget/KeyboardTranslator.h
new file mode 100644
index 0000000..e0082ae
--- /dev/null
+++ b/qtermwidget/KeyboardTranslator.h
@@ -0,0 +1,657 @@
+/*
+ This source file is part of Konsole, a terminal emulator.
+
+ Copyright (C) 2007 by Robert Knight <robertknight@gmail.com>
+
+ Rewritten for QT4 by e_k <e_k at users.sourceforge.net>, Copyright (C)2008
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ 02110-1301 USA.
+*/
+
+#ifndef KEYBOARDTRANSLATOR_H
+#define KEYBOARDTRANSLATOR_H
+
+// Qt
+#include <QtCore/QHash>
+#include <QtCore/QList>
+#include <QtGui/QKeySequence>
+#include <QtCore/QMetaType>
+#include <QtCore/QVarLengthArray>
+#include <QtCore>
+
+typedef void (*CleanUpFunction)();
+
+/**
+ * @internal
+ *
+ * Helper class for K_GLOBAL_STATIC to clean up the object on library unload or application
+ * shutdown.
+ */
+class CleanUpGlobalStatic
+{
+ public:
+ CleanUpFunction func;
+
+ inline ~CleanUpGlobalStatic() { func(); }
+};
+
+
+//these directives are taken from the heart of kdecore
+
+# define K_GLOBAL_STATIC_STRUCT_NAME(NAME)
+
+#if QT_VERSION < 0x040400
+# define Q_BASIC_ATOMIC_INITIALIZER Q_ATOMIC_INIT
+# define testAndSetOrdered testAndSet
+#endif
+
+#define K_GLOBAL_STATIC(TYPE, NAME) K_GLOBAL_STATIC_WITH_ARGS(TYPE, NAME, ())
+
+#define K_GLOBAL_STATIC_WITH_ARGS(TYPE, NAME, ARGS) \
+static QBasicAtomicPointer<TYPE > _k_static_##NAME = Q_BASIC_ATOMIC_INITIALIZER(0); \
+static bool _k_static_##NAME##_destroyed; \
+static struct K_GLOBAL_STATIC_STRUCT_NAME(NAME) \
+{ \
+ bool isDestroyed() \
+ { \
+ return _k_static_##NAME##_destroyed; \
+ } \
+ inline operator TYPE*() \
+ { \
+ return operator->(); \
+ } \
+ inline TYPE *operator->() \
+ { \
+ if (!_k_static_##NAME) { \
+ if (isDestroyed()) { \
+ qFatal("Fatal Error: Accessed global static '%s *%s()' after destruction. " \
+ "Defined at %s:%d", #TYPE, #NAME, __FILE__, __LINE__); \
+ } \
+ TYPE *x = new TYPE ARGS; \
+ if (!_k_static_##NAME.testAndSetOrdered(0, x) \
+ && _k_static_##NAME != x ) { \
+ delete x; \
+ } else { \
+ static CleanUpGlobalStatic cleanUpObject = { destroy }; \
+ } \
+ } \
+ return _k_static_##NAME; \
+ } \
+ inline TYPE &operator*() \
+ { \
+ return *operator->(); \
+ } \
+ static void destroy() \
+ { \
+ _k_static_##NAME##_destroyed = true; \
+ TYPE *x = _k_static_##NAME; \
+ _k_static_##NAME = 0; \
+ delete x; \
+ } \
+} NAME;
+
+
+
+
+
+class QIODevice;
+class QTextStream;
+
+namespace Konsole
+{
+
+/**
+ * A convertor which maps between key sequences pressed by the user and the
+ * character strings which should be sent to the terminal and commands
+ * which should be invoked when those character sequences are pressed.
+ *
+ * Konsole supports multiple keyboard translators, allowing the user to
+ * specify the character sequences which are sent to the terminal
+ * when particular key sequences are pressed.
+ *
+ * A key sequence is defined as a key code, associated keyboard modifiers
+ * (Shift,Ctrl,Alt,Meta etc.) and state flags which indicate the state
+ * which the terminal must be in for the key sequence to apply.
+ */
+class KeyboardTranslator
+{
+public:
+ /**
+ * The meaning of a particular key sequence may depend upon the state which
+ * the terminal emulation is in. Therefore findEntry() may return a different
+ * Entry depending upon the state flags supplied.
+ *
+ * This enum describes the states which may be associated with with a particular
+ * entry in the keyboard translation entry.
+ */
+ enum State
+ {
+ /** Indicates that no special state is active */
+ NoState = 0,
+ /**
+ * TODO More documentation
+ */
+ NewLineState = 1,
+ /**
+ * Indicates that the terminal is in 'Ansi' mode.
+ * TODO: More documentation
+ */
+ AnsiState = 2,
+ /**
+ * TODO More documentation
+ */
+ CursorKeysState = 4,
+ /**
+ * Indicates that the alternate screen ( typically used by interactive programs
+ * such as screen or vim ) is active
+ */
+ AlternateScreenState = 8,
+ /** Indicates that any of the modifier keys is active. */
+ AnyModifierState = 16
+ };
+ Q_DECLARE_FLAGS(States,State)
+
+ /**
+ * This enum describes commands which are associated with particular key sequences.
+ */
+ enum Command
+ {
+ /** Indicates that no command is associated with this command sequence */
+ NoCommand = 0,
+ /** TODO Document me */
+ SendCommand = 1,
+ /** Scroll the terminal display up one page */
+ ScrollPageUpCommand = 2,
+ /** Scroll the terminal display down one page */
+ ScrollPageDownCommand = 4,
+ /** Scroll the terminal display up one line */
+ ScrollLineUpCommand = 8,
+ /** Scroll the terminal display down one line */
+ ScrollLineDownCommand = 16,
+ /** Toggles scroll lock mode */
+ ScrollLockCommand = 32,
+ /** Echos the operating system specific erase character. */
+ EraseCommand = 64
+ };
+ Q_DECLARE_FLAGS(Commands,Command)
+
+ /**
+ * Represents an association between a key sequence pressed by the user
+ * and the character sequence and commands associated with it for a particular
+ * KeyboardTranslator.
+ */
+ class Entry
+ {
+ public:
+ /**
+ * Constructs a new entry for a keyboard translator.
+ */
+ Entry();
+
+ /**
+ * Returns true if this entry is null.
+ * This is true for newly constructed entries which have no properties set.
+ */
+ bool isNull() const;
+
+ /** Returns the commands associated with this entry */
+ Command command() const;
+ /** Sets the command associated with this entry. */
+ void setCommand(Command command);
+
+ /**
+ * Returns the character sequence associated with this entry, optionally replacing
+ * wildcard '*' characters with numbers to indicate the keyboard modifiers being pressed.
+ *
+ * TODO: The numbers used to replace '*' characters are taken from the Konsole/KDE 3 code.
+ * Document them.
+ *
+ * @param expandWildCards Specifies whether wild cards (occurrences of the '*' character) in
+ * the entry should be replaced with a number to indicate the modifier keys being pressed.
+ *
+ * @param modifiers The keyboard modifiers being pressed.
+ */
+ QByteArray text(bool expandWildCards = false,
+ Qt::KeyboardModifiers modifiers = Qt::NoModifier) const;
+
+ /** Sets the character sequence associated with this entry */
+ void setText(const QByteArray& text);
+
+ /**
+ * Returns the character sequence associated with this entry,
+ * with any non-printable characters replaced with escape sequences.
+ *
+ * eg. \\E for Escape, \\t for tab, \\n for new line.
+ *
+ * @param expandWildCards See text()
+ * @param modifiers See text()
+ */
+ QByteArray escapedText(bool expandWildCards = false,
+ Qt::KeyboardModifiers modifiers = Qt::NoModifier) const;
+
+ /** Returns the character code ( from the Qt::Key enum ) associated with this entry */
+ int keyCode() const;
+ /** Sets the character code associated with this entry */
+ void setKeyCode(int keyCode);
+
+ /**
+ * Returns a bitwise-OR of the enabled keyboard modifiers associated with this entry.
+ * If a modifier is set in modifierMask() but not in modifiers(), this means that the entry
+ * only matches when that modifier is NOT pressed.
+ *
+ * If a modifier is not set in modifierMask() then the entry matches whether the modifier
+ * is pressed or not.
+ */
+ Qt::KeyboardModifiers modifiers() const;
+
+ /** Returns the keyboard modifiers which are valid in this entry. See modifiers() */
+ Qt::KeyboardModifiers modifierMask() const;
+
+ /** See modifiers() */
+ void setModifiers( Qt::KeyboardModifiers modifiers );
+ /** See modifierMask() and modifiers() */
+ void setModifierMask( Qt::KeyboardModifiers modifiers );
+
+ /**
+ * Returns a bitwise-OR of the enabled state flags associated with this entry.
+ * If flag is set in stateMask() but not in state(), this means that the entry only
+ * matches when the terminal is NOT in that state.
+ *
+ * If a state is not set in stateMask() then the entry matches whether the terminal
+ * is in that state or not.
+ */
+ States state() const;
+
+ /** Returns the state flags which are valid in this entry. See state() */
+ States stateMask() const;
+
+ /** See state() */
+ void setState( States state );
+ /** See stateMask() */
+ void setStateMask( States mask );
+
+ /**
+ * Returns the key code and modifiers associated with this entry
+ * as a QKeySequence
+ */
+ //QKeySequence keySequence() const;
+
+ /**
+ * Returns this entry's conditions ( ie. its key code, modifier and state criteria )
+ * as a string.
+ */
+ QString conditionToString() const;
+
+ /**
+ * Returns this entry's result ( ie. its command or character sequence )
+ * as a string.
+ *
+ * @param expandWildCards See text()
+ * @param modifiers See text()
+ */
+ QString resultToString(bool expandWildCards = false,
+ Qt::KeyboardModifiers modifiers = Qt::NoModifier) const;
+
+ /**
+ * Returns true if this entry matches the given key sequence, specified
+ * as a combination of @p keyCode , @p modifiers and @p state.
+ */
+ bool matches( int keyCode ,
+ Qt::KeyboardModifiers modifiers ,
+ States flags ) const;
+
+ bool operator==(const Entry& rhs) const;
+
+ private:
+ void insertModifier( QString& item , int modifier ) const;
+ void insertState( QString& item , int state ) const;
+ QByteArray unescape(const QByteArray& text) const;
+
+ int _keyCode;
+ Qt::KeyboardModifiers _modifiers;
+ Qt::KeyboardModifiers _modifierMask;
+ States _state;
+ States _stateMask;
+
+ Command _command;
+ QByteArray _text;
+ };
+
+ /** Constructs a new keyboard translator with the given @p name */
+ KeyboardTranslator(const QString& name);
+
+ //KeyboardTranslator(const KeyboardTranslator& other);
+
+ /** Returns the name of this keyboard translator */
+ QString name() const;
+
+ /** Sets the name of this keyboard translator */
+ void setName(const QString& name);
+
+ /** Returns the descriptive name of this keyboard translator */
+ QString description() const;
+
+ /** Sets the descriptive name of this keyboard translator */
+ void setDescription(const QString& description);
+
+ /**
+ * Looks for an entry in this keyboard translator which matches the given
+ * key code, keyboard modifiers and state flags.
+ *
+ * Returns the matching entry if found or a null Entry otherwise ( ie.
+ * entry.isNull() will return true )
+ *
+ * @param keyCode A key code from the Qt::Key enum
+ * @param modifiers A combination of modifiers
+ * @param state Optional flags which specify the current state of the terminal
+ */
+ Entry findEntry(int keyCode ,
+ Qt::KeyboardModifiers modifiers ,
+ States state = NoState) const;
+
+ /**
+ * Adds an entry to this keyboard translator's table. Entries can be looked up according
+ * to their key sequence using findEntry()
+ */
+ void addEntry(const Entry& entry);
+
+ /**
+ * Replaces an entry in the translator. If the @p existing entry is null,
+ * then this is equivalent to calling addEntry(@p replacement)
+ */
+ void replaceEntry(const Entry& existing , const Entry& replacement);
+
+ /**
+ * Removes an entry from the table.
+ */
+ void removeEntry(const Entry& entry);
+
+ /** Returns a list of all entries in the translator. */
+ QList<Entry> entries() const;
+
+private:
+
+ QHash<int,Entry> _entries; // entries in this keyboard translation,
+ // entries are indexed according to
+ // their keycode
+ QString _name;
+ QString _description;
+};
+Q_DECLARE_OPERATORS_FOR_FLAGS(KeyboardTranslator::States)
+Q_DECLARE_OPERATORS_FOR_FLAGS(KeyboardTranslator::Commands)
+
+/**
+ * Parses the contents of a Keyboard Translator (.keytab) file and
+ * returns the entries found in it.
+ *
+ * Usage example:
+ *
+ * @code
+ * QFile source( "/path/to/keytab" );
+ * source.open( QIODevice::ReadOnly );
+ *
+ * KeyboardTranslator* translator = new KeyboardTranslator( "name-of-translator" );
+ *
+ * KeyboardTranslatorReader reader(source);
+ * while ( reader.hasNextEntry() )
+ * translator->addEntry(reader.nextEntry());
+ *
+ * source.close();
+ *
+ * if ( !reader.parseError() )
+ * {
+ * // parsing succeeded, do something with the translator
+ * }
+ * else
+ * {
+ * // parsing failed
+ * }
+ * @endcode
+ */
+class KeyboardTranslatorReader
+{
+public:
+ /** Constructs a new reader which parses the given @p source */
+ KeyboardTranslatorReader( QIODevice* source );
+
+ /**
+ * Returns the description text.
+ * TODO: More documentation
+ */
+ QString description() const;
+
+ /** Returns true if there is another entry in the source stream */
+ bool hasNextEntry();
+ /** Returns the next entry found in the source stream */
+ KeyboardTranslator::Entry nextEntry();
+
+ /**
+ * Returns true if an error occurred whilst parsing the input or
+ * false if no error occurred.
+ */
+ bool parseError();
+
+ /**
+ * Parses a condition and result string for a translator entry
+ * and produces a keyboard translator entry.
+ *
+ * The condition and result strings are in the same format as in
+ */
+ static KeyboardTranslator::Entry createEntry( const QString& condition ,
+ const QString& result );
+private:
+ struct Token
+ {
+ enum Type
+ {
+ TitleKeyword,
+ TitleText,
+ KeyKeyword,
+ KeySequence,
+ Command,
+ OutputText
+ };
+ Type type;
+ QString text;
+ };
+ QList<Token> tokenize(const QString&);
+ void readNext();
+ bool decodeSequence(const QString& ,
+ int& keyCode,
+ Qt::KeyboardModifiers& modifiers,
+ Qt::KeyboardModifiers& modifierMask,
+ KeyboardTranslator::States& state,
+ KeyboardTranslator::States& stateFlags);
+
+ static bool parseAsModifier(const QString& item , Qt::KeyboardModifier& modifier);
+ static bool parseAsStateFlag(const QString& item , KeyboardTranslator::State& state);
+ static bool parseAsKeyCode(const QString& item , int& keyCode);
+ static bool parseAsCommand(const QString& text , KeyboardTranslator::Command& command);
+
+ QIODevice* _source;
+ QString _description;
+ KeyboardTranslator::Entry _nextEntry;
+ bool _hasNext;
+};
+
+/** Writes a keyboard translation to disk. */
+class KeyboardTranslatorWriter
+{
+public:
+ /**
+ * Constructs a new writer which saves data into @p destination.
+ * The caller is responsible for closing the device when writing is complete.
+ */
+ KeyboardTranslatorWriter(QIODevice* destination);
+ ~KeyboardTranslatorWriter();
+
+ /**
+ * Writes the header for the keyboard translator.
+ * @param description Description of the keyboard translator.
+ */
+ void writeHeader( const QString& description );
+ /** Writes a translator entry. */
+ void writeEntry( const KeyboardTranslator::Entry& entry );
+
+private:
+ QIODevice* _destination;
+ QTextStream* _writer;
+};
+
+/**
+ * Manages the keyboard translations available for use by terminal sessions,
+ * see KeyboardTranslator.
+ */
+class KeyboardTranslatorManager
+{
+public:
+ /**
+ * Constructs a new KeyboardTranslatorManager and loads the list of
+ * available keyboard translations.
+ *
+ * The keyboard translations themselves are not loaded until they are
+ * first requested via a call to findTranslator()
+ */
+ KeyboardTranslatorManager();
+ ~KeyboardTranslatorManager();
+
+ /**
+ * Adds a new translator. If a translator with the same name
+ * already exists, it will be replaced by the new translator.
+ *
+ * TODO: More documentation.
+ */
+ void addTranslator(KeyboardTranslator* translator);
+
+ /**
+ * Deletes a translator. Returns true on successful deletion or false otherwise.
+ *
+ * TODO: More documentation
+ */
+ bool deleteTranslator(const QString& name);
+
+ /** Returns the default translator for Konsole. */
+ const KeyboardTranslator* defaultTranslator();
+
+ /**
+ * Returns the keyboard translator with the given name or 0 if no translator
+ * with that name exists.
+ *
+ * The first time that a translator with a particular name is requested,
+ * the on-disk .keyboard file is loaded and parsed.
+ */
+ const KeyboardTranslator* findTranslator(const QString& name);
+ /**
+ * Returns a list of the names of available keyboard translators.
+ *
+ * The first time this is called, a search for available
+ * translators is started.
+ */
+ QList<QString> allTranslators();
+
+ /** Returns the global KeyboardTranslatorManager instance. */
+ static KeyboardTranslatorManager* instance();
+
+private:
+ static const char* defaultTranslatorText;
+
+ void findTranslators(); // locate the available translators
+ KeyboardTranslator* loadTranslator(const QString& name); // loads the translator
+ // with the given name
+ KeyboardTranslator* loadTranslator(QIODevice* device,const QString& name);
+
+ bool saveTranslator(const KeyboardTranslator* translator);
+ QString findTranslatorPath(const QString& name);
+
+ QHash<QString,KeyboardTranslator*> _translators; // maps translator-name -> KeyboardTranslator
+ // instance
+ bool _haveLoadedAll;
+};
+
+inline int KeyboardTranslator::Entry::keyCode() const { return _keyCode; }
+inline void KeyboardTranslator::Entry::setKeyCode(int keyCode) { _keyCode = keyCode; }
+
+inline void KeyboardTranslator::Entry::setModifiers( Qt::KeyboardModifiers modifier )
+{
+ _modifiers = modifier;
+}
+inline Qt::KeyboardModifiers KeyboardTranslator::Entry::modifiers() const { return _modifiers; }
+
+inline void KeyboardTranslator::Entry::setModifierMask( Qt::KeyboardModifiers mask )
+{
+ _modifierMask = mask;
+}
+inline Qt::KeyboardModifiers KeyboardTranslator::Entry::modifierMask() const { return _modifierMask; }
+
+inline bool KeyboardTranslator::Entry::isNull() const
+{
+ return ( *this == Entry() );
+}
+
+inline void KeyboardTranslator::Entry::setCommand( Command command )
+{
+ _command = command;
+}
+inline KeyboardTranslator::Command KeyboardTranslator::Entry::command() const { return _command; }
+
+inline void KeyboardTranslator::Entry::setText( const QByteArray& text )
+{
+ _text = unescape(text);
+}
+inline int oneOrZero(int value)
+{
+ return value ? 1 : 0;
+}
+inline QByteArray KeyboardTranslator::Entry::text(bool expandWildCards,Qt::KeyboardModifiers modifiers) const
+{
+ QByteArray expandedText = _text;
+
+ if (expandWildCards)
+ {
+ int modifierValue = 1;
+ modifierValue += oneOrZero(modifiers & Qt::ShiftModifier);
+ modifierValue += oneOrZero(modifiers & Qt::AltModifier) << 1;
+ modifierValue += oneOrZero(modifiers & Qt::ControlModifier) << 2;
+
+ for (int i=0;i<_text.length();i++)
+ {
+ if (expandedText[i] == '*')
+ expandedText[i] = '0' + modifierValue;
+ }
+ }
+
+ return expandedText;
+}
+
+inline void KeyboardTranslator::Entry::setState( States state )
+{
+ _state = state;
+}
+inline KeyboardTranslator::States KeyboardTranslator::Entry::state() const { return _state; }
+
+inline void KeyboardTranslator::Entry::setStateMask( States stateMask )
+{
+ _stateMask = stateMask;
+}
+inline KeyboardTranslator::States KeyboardTranslator::Entry::stateMask() const { return _stateMask; }
+
+}
+
+Q_DECLARE_METATYPE(Konsole::KeyboardTranslator::Entry)
+Q_DECLARE_METATYPE(const Konsole::KeyboardTranslator*)
+
+#endif // KEYBOARDTRANSLATOR_H
+
diff --git a/qtermwidget/LineFont.h b/qtermwidget/LineFont.h
new file mode 100644
index 0000000..9b64143
--- /dev/null
+++ b/qtermwidget/LineFont.h
@@ -0,0 +1,21 @@
+// WARNING: Autogenerated by "fontembedder ./linefont.src".
+// You probably do not want to hand-edit this!
+
+static const quint32 LineChars[] = {
+ 0x00007c00, 0x000fffe0, 0x00421084, 0x00e739ce, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00427000, 0x004e7380, 0x00e77800, 0x00ef7bc0,
+ 0x00421c00, 0x00439ce0, 0x00e73c00, 0x00e7bde0, 0x00007084, 0x000e7384, 0x000079ce, 0x000f7bce,
+ 0x00001c84, 0x00039ce4, 0x00003dce, 0x0007bdee, 0x00427084, 0x004e7384, 0x004279ce, 0x00e77884,
+ 0x00e779ce, 0x004f7bce, 0x00ef7bc4, 0x00ef7bce, 0x00421c84, 0x00439ce4, 0x00423dce, 0x00e73c84,
+ 0x00e73dce, 0x0047bdee, 0x00e7bde4, 0x00e7bdee, 0x00427c00, 0x0043fce0, 0x004e7f80, 0x004fffe0,
+ 0x004fffe0, 0x00e7fde0, 0x006f7fc0, 0x00efffe0, 0x00007c84, 0x0003fce4, 0x000e7f84, 0x000fffe4,
+ 0x00007dce, 0x0007fdee, 0x000f7fce, 0x000fffee, 0x00427c84, 0x0043fce4, 0x004e7f84, 0x004fffe4,
+ 0x00427dce, 0x00e77c84, 0x00e77dce, 0x0047fdee, 0x004e7fce, 0x00e7fde4, 0x00ef7f84, 0x004fffee,
+ 0x00efffe4, 0x00e7fdee, 0x00ef7fce, 0x00efffee, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x000f83e0, 0x00a5294a, 0x004e1380, 0x00a57800, 0x00ad0bc0, 0x004390e0, 0x00a53c00, 0x00a5a1e0,
+ 0x000e1384, 0x0000794a, 0x000f0b4a, 0x000390e4, 0x00003d4a, 0x0007a16a, 0x004e1384, 0x00a5694a,
+ 0x00ad2b4a, 0x004390e4, 0x00a52d4a, 0x00a5a16a, 0x004f83e0, 0x00a57c00, 0x00ad83e0, 0x000f83e4,
+ 0x00007d4a, 0x000f836a, 0x004f93e4, 0x00a57d4a, 0x00ad836a, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00001c00, 0x00001084, 0x00007000, 0x00421000,
+ 0x00039ce0, 0x000039ce, 0x000e7380, 0x00e73800, 0x000e7f80, 0x00e73884, 0x0003fce0, 0x004239ce
+};
diff --git a/qtermwidget/LineFont.src b/qtermwidget/LineFont.src
new file mode 100644
index 0000000..6835253
--- /dev/null
+++ b/qtermwidget/LineFont.src
@@ -0,0 +1,786 @@
+#2500: single horizontal line
+2500
+
+
+-----
+
+
+
+#2501: triple horizontal line
+2501
+
+-----
+-----
+-----
+
+
+#2502: single vertical line
+2502
+ |
+ |
+ |
+ |
+ |
+
+#2503: triple vertical line
+2503
+ |||
+ |||
+ |||
+ |||
+ |||
+
+#2504-250B are dashed - not handled
+
+#250C: top-left corner (lines on bottom + right)
+250C
+
+
+ .--
+ |
+ |
+
+#250D: as above, but top line triple-width
+250D
+
+ .--
+ .--
+ |--
+ |
+
+#250E: now the vert line triple-width
+250E
+
+
+ ..--
+ |||
+ |||
+
+#250F: and now both lines triple-width
+250F
+
+ .___
+ |.--
+ ||._
+ |||
+
+#2510: top-right corner
+2510
+
+
+--.
+ |
+ |
+
+2511
+
+==.
+==.
+==|
+ |
+
+2512
+
+
+==..
+ |||
+ |||
+
+2513
+
+===.
+==.|
+=.||
+ |||
+
+#2514: bottom-left corner
+2514
+ |
+ |
+ .==
+
+
+
+2515
+ |
+ |==
+ |==
+ ===
+
+
+
+2516
+ |||
+ |||
+ |.==
+
+
+
+2517
+ |||
+ ||.=
+ |.==
+ .===
+
+
+#2518: bottm-right corner
+2518
+ |
+ |
+==.
+
+
+
+2519
+ |
+==|
+==|
+===
+
+
+
+251A
+ |||
+ |||
+====
+
+
+
+251B
+ |||
+=.||
+==.|
+===.
+
+
+#251C: Join of vertical line and one from the right
+251C
+ |
+ |
+ |==
+ |
+ |
+
+251D
+ |
+ |==
+ |==
+ |==
+ |
+
+251E
+ |||
+ |||
+ ||==
+ |
+ |
+
+251F
+ |
+ |
+ ||==
+ |||
+ |||
+
+
+2520
+ |||
+ |||
+ ||==
+ |||
+ |||
+
+2521
+ |||
+ |||=
+ ||==
+ .|==
+ |
+
+2522
+ |
+ .|==
+ ||==
+ |||=
+ |||
+
+2523
+ |||
+ ||.=
+ ||==
+ ||.=
+ |||
+
+#2524: Join of vertical line and one from the left
+2524
+ |
+ |
+==|
+ |
+ |
+
+2525
+ |
+==|
+==|
+==|
+ |
+
+2526
+ |||
+ |||
+==+|
+ |
+ |
+
+2527
+ |
+ |
+==+|
+ |||
+ |||
+
+2528
+ |||
+ |||
+==+|
+ |||
+ |||
+
+2529
+ |||
+=+||
+==+|
+===+
+ |
+
+252A
+ |
+=+||
+==+|
+===+
+ |||
+
+252B
+ |||
+=+||
+==+|
+=+||
+ |||
+
+#252C: horizontal line joined to from below
+252C
+
+
+=====
+ |
+ |
+
+252D
+
+===
+==|==
+==|
+ |
+
+252E
+
+ ===
+==|==
+ |==
+ |
+
+252F
+
+==+==
+==|==
+==|==
+ |
+
+2530
+
+=====
+=====
+==|==
+ |
+
+2531
+
+===|
+==||=
+=|||
+ |||
+
+2532
+
+ |===
+=||==
+ ||==
+ ||
+
+2533
+
+=====
+==|==
+=+|+=
+ |||
+
+#2534: bottom line, connected to from top
+2534
+ |
+ |
+=====
+
+
+
+2535
+ |
+==|
+=====
+===
+
+
+2536
+ |
+ |==
+=====
+ ===
+
+
+2537
+ |
+==|==
+=====
+=====
+
+
+2538
+ |||
+ |||
+=====
+
+
+
+2539
+ |||
+==||
+=====
+===|
+
+
+
+253A
+ |||
+ ||==
+=|===
+ |===
+
+
+253B
+ |||
+==|==
+=====
+=====
+
+
+#253C: vertical + horizontal lines intersecting
+253C
+ |
+ |
+=====
+ |
+ |
+
+253D
+ |
+==|
+=====
+==|
+ |
+
+253E
+ |
+ |==
+=====
+ |==
+ |
+
+253F
+ |
+==|==
+=====
+==|==
+ |
+
+2540
+ |||
+ |||
+=====
+ |
+ |
+
+2541
+ |
+ |
+=====
+ |||
+ |||
+
+2542
+ |||
+ |||
+=====
+ |||
+ |||
+
+2543
+ |||
+=|||
+=====
+==|+
+ |
+
+2544
+ |||
+ ||==
+=====
+ |==
+ |
+
+2545
+ |
+==|+
+=====
+=|||
+ |||
+
+2546
+ |
+ |==
+=====
+ ||==
+ |||
+
+2547
+ |||
+=|||=
+=====
+=|||=
+ |
+
+2548
+ |
+=|||=
+=====
+=|||=
+ |||
+
+2549
+ |||
+=|||
+=====
+=|||
+ |||
+
+254A
+ |||
+ |||=
+=====
+ |||=
+ |||
+
+254B
+ |||
+=|||=
+=====
+=|||=
+ |||
+
+#254C-254F are dashed
+2550
+
+_____
+
+_____
+
+
+2551
+ | |
+ | |
+ | |
+ | |
+ | |
+
+2552
+
+ |--
+ |
+ |--
+ |
+
+2553
+
+
+ ----
+ | |
+ | |
+
+2554
+
+ +---
+ |
+ + +-
+ | |
+
+2555
+
+--+
+ |
+--+
+ |
+
+2556
+
+
+-+-+
+ | |
+ | |
+
+2557
+
+---+
+ |
+-+ |
+ | |
+
+2558
+ |
+ +--
+ |
+ +--
+
+2559
+ | |
+ | |
+ +-+-
+
+
+
+255A
+ | |
+ | +-
+ |
+ +---
+
+
+255B
+ |
+--+
+ |
+--+
+
+
+255C
+ | |
+ | |
+-+-+
+
+
+255D
+ | |
+-+ |
+ |
+---+
+
+
+255E
+ |
+ +--
+ |
+ +--
+ |
+
+255F
+ | |
+ | |
+ | +-
+ | |
+ | |
+
+2560
+ | |
+ | +-
+ | |
+ | +-
+ | |
+
+2561
+ |
+--+
+ |
+--+
+ |
+
+2562
+ | |
+ | |
+-+ +
+ | |
+ | |
+
+2563
+ | |
+-+ |
+ |
+-+ |
+ | |
+
+2564
+
+-----
+
+--+--
+ |
+
+2565
+
+
+-+-+-
+ | |
+ | |
+
+2566
+
+-----
+
+-+ +-
+ | |
+
+2567
+ |
+--+--
+
+-----
+
+
+2568
+ | |
+ | |
+-+-+-
+
+
+
+2569
+ | |
+-+ +-
+
+-----
+
+
+256A
+ |
+--+--
+ |
+--+--
+ |
+
+256B
+ | |
+ | |
+-+-+-
+ | |
+ | |
+
+256C
+ | |
+-+ +-
+
+-+ +-
+ | |
+
+#256F-2570 are curly,
+#2571-2573 are slashes and X
+
+2574
+
+
+___
+
+
+
+2575
+ |
+ |
+ |
+
+
+
+2576
+
+
+ ___
+
+
+
+2577
+
+
+ |
+ |
+ |
+
+2578
+
+___
+___
+___
+
+
+2579
+ |||
+ |||
+ |||
+
+
+
+257A
+
+ ___
+ ___
+ ___
+
+
+257B
+
+
+ |||
+ |||
+ |||
+
+257C
+
+ ___
+_____
+ ___
+
+
+257D
+ |
+ |
+ |||
+ |||
+ |||
+
+257E
+
+___
+_____
+___
+
+
+257F
+ |||
+ |||
+ |||
+ |
+ |
diff --git a/qtermwidget/Pty.cpp b/qtermwidget/Pty.cpp
new file mode 100644
index 0000000..144e5e2
--- /dev/null
+++ b/qtermwidget/Pty.cpp
@@ -0,0 +1,320 @@
+/*
+ This file is part of Konsole, an X terminal.
+ Copyright (C) 1997,1998 by Lars Doelle <lars.doelle@on-line.de>
+
+ Rewritten for QT4 by e_k <e_k at users.sourceforge.net>, Copyright (C)2008
+
+ 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.
+*/
+
+// Own
+#include "Pty.h"
+
+// System
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <errno.h>
+#include <termios.h>
+
+// Qt
+#include <QtCore>
+
+// KDE
+//#include <KStandardDirs>
+//#include <KLocale>
+//#include <KDebug>
+#include "kpty.h"
+
+using namespace Konsole;
+
+void Pty::donePty()
+{
+ emit done(exitStatus());
+}
+
+void Pty::setWindowSize(int lines, int cols)
+{
+ _windowColumns = cols;
+ _windowLines = lines;
+
+ if (pty()->masterFd() >= 0)
+ pty()->setWinSize(lines, cols);
+}
+QSize Pty::windowSize() const
+{
+ return QSize(_windowColumns,_windowLines);
+}
+
+void Pty::setXonXoff(bool enable)
+{
+ _xonXoff = enable;
+
+ if (pty()->masterFd() >= 0)
+ {
+ struct ::termios ttmode;
+ pty()->tcGetAttr(&ttmode);
+ if (!enable)
+ ttmode.c_iflag &= ~(IXOFF | IXON);
+ else
+ ttmode.c_iflag |= (IXOFF | IXON);
+ if (!pty()->tcSetAttr(&ttmode))
+ qWarning("Unable to set terminal attributes.");
+ }
+}
+
+void Pty::setUtf8Mode(bool enable)
+{
+#ifdef IUTF8 // XXX not a reasonable place to check it.
+ _utf8 = enable;
+
+ if (pty()->masterFd() >= 0)
+ {
+ struct ::termios ttmode;
+ pty()->tcGetAttr(&ttmode);
+ if (!enable)
+ ttmode.c_iflag &= ~IUTF8;
+ else
+ ttmode.c_iflag |= IUTF8;
+ if (!pty()->tcSetAttr(&ttmode))
+ qWarning("Unable to set terminal attributes.");
+ }
+#endif
+}
+
+void Pty::setErase(char erase)
+{
+ _eraseChar = erase;
+
+ if (pty()->masterFd() >= 0)
+ {
+ struct ::termios ttmode;
+
+ pty()->tcGetAttr(&ttmode);
+
+ ttmode.c_cc[VERASE] = erase;
+
+ if (!pty()->tcSetAttr(&ttmode))
+ qWarning("Unable to set terminal attributes.");
+ }
+}
+
+char Pty::erase() const
+{
+ if (pty()->masterFd() >= 0)
+ {
+ qDebug() << "Getting erase char";
+ struct ::termios ttyAttributes;
+ pty()->tcGetAttr(&ttyAttributes);
+ return ttyAttributes.c_cc[VERASE];
+ }
+
+ return _eraseChar;
+}
+
+void Pty::addEnvironmentVariables(const QStringList& environment)
+{
+ QListIterator<QString> iter(environment);
+ while (iter.hasNext())
+ {
+ QString pair = iter.next();
+
+ // split on the first '=' character
+ int pos = pair.indexOf('=');
+
+ if ( pos >= 0 )
+ {
+ QString variable = pair.left(pos);
+ QString value = pair.mid(pos+1);
+
+ //kDebug() << "Setting environment pair" << variable <<
+ // " set to " << value;
+
+ setEnvironment(variable,value);
+ }
+ }
+}
+
+int Pty::start(const QString& program,
+ const QStringList& programArguments,
+ const QStringList& environment,
+ ulong winid,
+ bool addToUtmp
+// const QString& dbusService,
+// const QString& dbusSession)
+ )
+{
+ clearArguments();
+
+ setBinaryExecutable(program.toLatin1());
+
+ addEnvironmentVariables(environment);
+
+ QStringListIterator it( programArguments );
+ while (it.hasNext())
+ arguments.append( it.next().toUtf8() );
+
+// if ( !dbusService.isEmpty() )
+// setEnvironment("KONSOLE_DBUS_SERVICE",dbusService);
+// if ( !dbusSession.isEmpty() )
+// setEnvironment("KONSOLE_DBUS_SESSION", dbusSession);
+
+ setEnvironment("WINDOWID", QString::number(winid));
+
+ // unless the LANGUAGE environment variable has been set explicitly
+ // set it to a null string
+ // this fixes the problem where KCatalog sets the LANGUAGE environment
+ // variable during the application's startup to something which
+ // differs from LANG,LC_* etc. and causes programs run from
+ // the terminal to display mesages in the wrong language
+ //
+ // this can happen if LANG contains a language which KDE
+ // does not have a translation for
+ //
+ // BR:149300
+ if (!environment.contains("LANGUAGE"))
+ setEnvironment("LANGUAGE",QString());
+
+ setUsePty(All, addToUtmp);
+
+ pty()->open();
+
+ struct ::termios ttmode;
+ pty()->tcGetAttr(&ttmode);
+ if (!_xonXoff)
+ ttmode.c_iflag &= ~(IXOFF | IXON);
+ else
+ ttmode.c_iflag |= (IXOFF | IXON);
+#ifdef IUTF8 // XXX not a reasonable place to check it.
+ if (!_utf8)
+ ttmode.c_iflag &= ~IUTF8;
+ else
+ ttmode.c_iflag |= IUTF8;
+#endif
+
+ if (_eraseChar != 0)
+ ttmode.c_cc[VERASE] = _eraseChar;
+
+ if (!pty()->tcSetAttr(&ttmode))
+ qWarning("Unable to set terminal attributes.");
+
+ pty()->setWinSize(_windowLines, _windowColumns);
+
+ if ( K3Process::start(NotifyOnExit, (Communication) (Stdin | Stdout)) == false )
+ return -1;
+
+ resume(); // Start...
+ return 0;
+
+}
+
+void Pty::setWriteable(bool writeable)
+{
+ struct stat sbuf;
+ stat(pty()->ttyName(), &sbuf);
+ if (writeable)
+ chmod(pty()->ttyName(), sbuf.st_mode | S_IWGRP);
+ else
+ chmod(pty()->ttyName(), sbuf.st_mode & ~(S_IWGRP|S_IWOTH));
+}
+
+Pty::Pty()
+ : _bufferFull(false),
+ _windowColumns(0),
+ _windowLines(0),
+ _eraseChar(0),
+ _xonXoff(true),
+ _utf8(true)
+{
+ connect(this, SIGNAL(receivedStdout(K3Process *, char *, int )),
+ this, SLOT(dataReceived(K3Process *,char *, int)));
+ connect(this, SIGNAL(processExited(K3Process *)),
+ this, SLOT(donePty()));
+ connect(this, SIGNAL(wroteStdin(K3Process *)),
+ this, SLOT(writeReady()));
+ _pty = new KPty;
+
+ setUsePty(All, false); // utmp will be overridden later
+}
+
+Pty::~Pty()
+{
+ delete _pty;
+}
+
+void Pty::writeReady()
+{
+ _pendingSendJobs.erase(_pendingSendJobs.begin());
+ _bufferFull = false;
+ doSendJobs();
+}
+
+void Pty::doSendJobs() {
+ if(_pendingSendJobs.isEmpty())
+ {
+ emit bufferEmpty();
+ return;
+ }
+
+ SendJob& job = _pendingSendJobs.first();
+
+
+ if (!writeStdin( job.data(), job.length() ))
+ {
+ qWarning("Pty::doSendJobs - Could not send input data to terminal process.");
+ return;
+ }
+ _bufferFull = true;
+}
+
+void Pty::appendSendJob(const char* s, int len)
+{
+ _pendingSendJobs.append(SendJob(s,len));
+}
+
+void Pty::sendData(const char* s, int len)
+{
+ appendSendJob(s,len);
+ if (!_bufferFull)
+ doSendJobs();
+}
+
+void Pty::dataReceived(K3Process *,char *buf, int len)
+{
+ emit receivedData(buf,len);
+}
+
+void Pty::lockPty(bool lock)
+{
+ if (lock)
+ suspend();
+ else
+ resume();
+}
+
+int Pty::foregroundProcessGroup() const
+{
+ int pid = tcgetpgrp(pty()->masterFd());
+
+ if ( pid != -1 )
+ {
+ return pid;
+ }
+
+ return 0;
+}
+
+//#include "moc_Pty.cpp"
diff --git a/qtermwidget/Pty.h b/qtermwidget/Pty.h
new file mode 100644
index 0000000..f3e9432
--- /dev/null
+++ b/qtermwidget/Pty.h
@@ -0,0 +1,243 @@
+/*
+ This file is part of Konsole, KDE's terminal emulator.
+
+ Copyright (C) 2007 by Robert Knight <robertknight@gmail.com>
+ Copyright (C) 1997,1998 by Lars Doelle <lars.doelle@on-line.de>
+
+ Rewritten for QT4 by e_k <e_k at users.sourceforge.net>, Copyright (C)2008
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ 02110-1301 USA.
+*/
+
+#ifndef PTY_H
+#define PTY_H
+
+// Qt
+#include <QtCore/QStringList>
+#include <QtCore/QVector>
+#include <QtCore/QList>
+#include <QtCore>
+
+#include "k3process.h"
+
+
+namespace Konsole
+{
+
+/**
+ * The Pty class is used to start the terminal process,
+ * send data to it, receive data from it and manipulate
+ * various properties of the pseudo-teletype interface
+ * used to communicate with the process.
+ *
+ * To use this class, construct an instance and connect
+ * to the sendData slot and receivedData signal to
+ * send data to or receive data from the process.
+ *
+ * To start the terminal process, call the start() method
+ * with the program name and appropriate arguments.
+ */
+class Pty: public K3Process
+{
+Q_OBJECT
+
+ public:
+
+ /**
+ * Constructs a new Pty.
+ *
+ * Connect to the sendData() slot and receivedData() signal to prepare
+ * for sending and receiving data from the terminal process.
+ *
+ * To start the terminal process, call the run() method with the
+ * name of the program to start and appropriate arguments.
+ */
+ Pty();
+ ~Pty();
+
+ /**
+ * Starts the terminal process.
+ *
+ * Returns 0 if the process was started successfully or non-zero
+ * otherwise.
+ *
+ * @param program Path to the program to start
+ * @param arguments Arguments to pass to the program being started
+ * @param environment A list of key=value pairs which will be added
+ * to the environment for the new process. At the very least this
+ * should include an assignment for the TERM environment variable.
+ * @param winid Specifies the value of the WINDOWID environment variable
+ * in the process's environment.
+ * @param addToUtmp Specifies whether a utmp entry should be created for
+ * the pty used. See K3Process::setUsePty()
+ * @param dbusService Specifies the value of the KONSOLE_DBUS_SERVICE
+ * environment variable in the process's environment.
+ * @param dbusSession Specifies the value of the KONSOLE_DBUS_SESSION
+ * environment variable in the process's environment.
+ */
+ int start( const QString& program,
+ const QStringList& arguments,
+ const QStringList& environment,
+ ulong winid,
+ bool addToUtmp
+// const QString& dbusService,
+// const QString& dbusSession
+ );
+
+ /** TODO: Document me */
+ void setWriteable(bool writeable);
+
+ /**
+ * Enables or disables Xon/Xoff flow control.
+ */
+ void setXonXoff(bool on);
+
+ /**
+ * Sets the size of the window (in lines and columns of characters)
+ * used by this teletype.
+ */
+ void setWindowSize(int lines, int cols);
+
+ /** Returns the size of the window used by this teletype. See setWindowSize() */
+ QSize windowSize() const;
+
+ /** TODO Document me */
+ void setErase(char erase);
+
+ /** */
+ char erase() const;
+
+ /**
+ * Returns the process id of the teletype's current foreground
+ * process. This is the process which is currently reading
+ * input sent to the terminal via. sendData()
+ *
+ * If there is a problem reading the foreground process group,
+ * 0 will be returned.
+ */
+ int foregroundProcessGroup() const;
+
+ /**
+ * Returns whether the buffer used to send data to the
+ * terminal process is full.
+ */
+ bool bufferFull() const { return _bufferFull; }
+
+
+ public slots:
+
+ /**
+ * Put the pty into UTF-8 mode on systems which support it.
+ */
+ void setUtf8Mode(bool on);
+
+ /**
+ * Suspend or resume processing of data from the standard
+ * output of the terminal process.
+ *
+ * See K3Process::suspend() and K3Process::resume()
+ *
+ * @param lock If true, processing of output is suspended,
+ * otherwise processing is resumed.
+ */
+ void lockPty(bool lock);
+
+ /**
+ * Sends data to the process currently controlling the
+ * teletype ( whose id is returned by foregroundProcessGroup() )
+ *
+ * @param buffer Pointer to the data to send.
+ * @param length Length of @p buffer.
+ */
+ void sendData(const char* buffer, int length);
+
+ signals:
+
+ /**
+ * Emitted when the terminal process terminates.
+ *
+ * @param exitCode The status code which the process exited with.
+ */
+ void done(int exitCode);
+
+ /**
+ * Emitted when a new block of data is received from
+ * the teletype.
+ *
+ * @param buffer Pointer to the data received.
+ * @param length Length of @p buffer
+ */
+ void receivedData(const char* buffer, int length);
+
+ /**
+ * Emitted when the buffer used to send data to the terminal
+ * process becomes empty, i.e. all data has been sent.
+ */
+ void bufferEmpty();
+
+
+ private slots:
+
+ // called when terminal process exits
+ void donePty();
+ // called when data is received from the terminal process
+ void dataReceived(K3Process*, char* buffer, int length);
+ // sends the first enqueued buffer of data to the
+ // terminal process
+ void doSendJobs();
+ // called when the terminal process is ready to
+ // receive more data
+ void writeReady();
+
+ private:
+ // takes a list of key=value pairs and adds them
+ // to the environment for the process
+ void addEnvironmentVariables(const QStringList& environment);
+
+ // enqueues a buffer of data to be sent to the
+ // terminal process
+ void appendSendJob(const char* buffer, int length);
+
+ // a buffer of data in the queue to be sent to the
+ // terminal process
+ class SendJob {
+ public:
+ SendJob() {}
+ SendJob(const char* b, int len) : buffer(len)
+ {
+ memcpy( buffer.data() , b , len );
+ }
+
+ const char* data() const { return buffer.constData(); }
+ int length() const { return buffer.size(); }
+ private:
+ QVector<char> buffer;
+ };
+
+ QList<SendJob> _pendingSendJobs;
+ bool _bufferFull;
+
+ int _windowColumns;
+ int _windowLines;
+ char _eraseChar;
+ bool _xonXoff;
+ bool _utf8;
+ KPty *_pty;
+};
+
+}
+
+#endif // PTY_H
diff --git a/qtermwidget/README b/qtermwidget/README
new file mode 100644
index 0000000..9665053
--- /dev/null
+++ b/qtermwidget/README
@@ -0,0 +1,21 @@
+QTermWidget
+version 0.1.0
+
+QTermWidget is an opensource project based on KDE4 Konsole application.
+The main goal of this project is to provide unicode-enabled, embeddable
+QT widget for using as a built-in console (or terminal emulation widget).
+
+Of course I`m aware about embedding abilities of original Konsole,
+but once I had Qt without KDE, and it was a serious obstacle.
+I decided not to rely on a chance. I cannot find any interesting related project,
+so I had to write it.
+
+The original Konsole`s code was rewritten entirely with QT4 only; also I have to
+include in the project some parts of code from kde core library. All code dealing
+with user interface parts and session managers was removed (maybe later I bring it
+back somehow), and the result is quite useful, I suppose.
+
+This library was compiled and tested on three linux systems,
+based on 2.4.32, 2.6.20, 2.6.23 kernels, x86 and amd64.
+Please inform about its behaviour on other systems.
+
diff --git a/qtermwidget/Screen.cpp b/qtermwidget/Screen.cpp
new file mode 100644
index 0000000..ead0066
--- /dev/null
+++ b/qtermwidget/Screen.cpp
@@ -0,0 +1,1567 @@
+/*
+ This file is part of Konsole, an X terminal.
+ Copyright (C) 1997,1998 by Lars Doelle <lars.doelle@on-line.de>
+
+ Rewritten for QT4 by e_k <e_k at users.sourceforge.net>, Copyright (C)2008
+
+ 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.
+*/
+
+// Own
+#include "Screen.h"
+
+// Standard
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <assert.h>
+#include <string.h>
+#include <ctype.h>
+
+// Qt
+#include <QtCore/QTextStream>
+#include <QtCore/QDate>
+
+// Konsole
+#include "konsole_wcwidth.h"
+#include "TerminalCharacterDecoder.h"
+
+using namespace Konsole;
+
+//FIXME: this is emulation specific. Use false for xterm, true for ANSI.
+//FIXME: see if we can get this from terminfo.
+#define BS_CLEARS false
+
+//Macro to convert x,y position on screen to position within an image.
+//
+//Originally the image was stored as one large contiguous block of
+//memory, so a position within the image could be represented as an
+//offset from the beginning of the block. For efficiency reasons this
+//is no longer the case.
+//Many internal parts of this class still use this representation for parameters and so on,
+//notably moveImage() and clearImage().
+//This macro converts from an X,Y position into an image offset.
+#ifndef loc
+#define loc(X,Y) ((Y)*columns+(X))
+#endif
+
+
+Character Screen::defaultChar = Character(' ',
+ CharacterColor(COLOR_SPACE_DEFAULT,DEFAULT_FORE_COLOR),
+ CharacterColor(COLOR_SPACE_DEFAULT,DEFAULT_BACK_COLOR),
+ DEFAULT_RENDITION);
+
+//#define REVERSE_WRAPPED_LINES // for wrapped line debug
+
+Screen::Screen(int l, int c)
+ : lines(l),
+ columns(c),
+ screenLines(new ImageLine[lines+1] ),
+ _scrolledLines(0),
+ _droppedLines(0),
+ hist(new HistoryScrollNone()),
+ cuX(0), cuY(0),
+ cu_re(0),
+ tmargin(0), bmargin(0),
+ tabstops(0),
+ sel_begin(0), sel_TL(0), sel_BR(0),
+ sel_busy(false),
+ columnmode(false),
+ ef_fg(CharacterColor()), ef_bg(CharacterColor()), ef_re(0),
+ sa_cuX(0), sa_cuY(0),
+ sa_cu_re(0),
+ lastPos(-1)
+{
+ lineProperties.resize(lines+1);
+ for (int i=0;i<lines+1;i++)
+ lineProperties[i]=LINE_DEFAULT;
+
+ initTabStops();
+ clearSelection();
+ reset();
+}
+
+/*! Destructor
+*/
+
+Screen::~Screen()
+{
+ delete[] screenLines;
+ delete[] tabstops;
+ delete hist;
+}
+
+/* ------------------------------------------------------------------------- */
+/* */
+/* Normalized Screen Operations */
+/* */
+/* ------------------------------------------------------------------------- */
+
+// Cursor Setting --------------------------------------------------------------
+
+/*! \section Cursor
+
+ The `cursor' is a location within the screen that is implicitely used in
+ many operations. The operations within this section allow to manipulate
+ the cursor explicitly and to obtain it's value.
+
+ The position of the cursor is guarantied to be between (including) 0 and
+ `columns-1' and `lines-1'.
+*/
+
+/*!
+ Move the cursor up.
+
+ The cursor will not be moved beyond the top margin.
+*/
+
+void Screen::cursorUp(int n)
+//=CUU
+{
+ if (n == 0) n = 1; // Default
+ int stop = cuY < tmargin ? 0 : tmargin;
+ cuX = qMin(columns-1,cuX); // nowrap!
+ cuY = qMax(stop,cuY-n);
+}
+
+/*!
+ Move the cursor down.
+
+ The cursor will not be moved beyond the bottom margin.
+*/
+
+void Screen::cursorDown(int n)
+//=CUD
+{
+ if (n == 0) n = 1; // Default
+ int stop = cuY > bmargin ? lines-1 : bmargin;
+ cuX = qMin(columns-1,cuX); // nowrap!
+ cuY = qMin(stop,cuY+n);
+}
+
+/*!
+ Move the cursor left.
+
+ The cursor will not move beyond the first column.
+*/
+
+void Screen::cursorLeft(int n)
+//=CUB
+{
+ if (n == 0) n = 1; // Default
+ cuX = qMin(columns-1,cuX); // nowrap!
+ cuX = qMax(0,cuX-n);
+}
+
+/*!
+ Move the cursor left.
+
+ The cursor will not move beyond the rightmost column.
+*/
+
+void Screen::cursorRight(int n)
+//=CUF
+{
+ if (n == 0) n = 1; // Default
+ cuX = qMin(columns-1,cuX+n);
+}
+
+void Screen::setMargins(int top, int bot)
+//=STBM
+{
+ if (top == 0) top = 1; // Default
+ if (bot == 0) bot = lines; // Default
+ top = top - 1; // Adjust to internal lineno
+ bot = bot - 1; // Adjust to internal lineno
+ if ( !( 0 <= top && top < bot && bot < lines ) )
+ { qDebug()<<" setRegion("<<top<<","<<bot<<") : bad range.";
+ return; // Default error action: ignore
+ }
+ tmargin = top;
+ bmargin = bot;
+ cuX = 0;
+ cuY = getMode(MODE_Origin) ? top : 0;
+
+}
+
+int Screen::topMargin() const
+{
+ return tmargin;
+}
+int Screen::bottomMargin() const
+{
+ return bmargin;
+}
+
+void Screen::index()
+//=IND
+{
+ if (cuY == bmargin)
+ {
+ scrollUp(1);
+ }
+ else if (cuY < lines-1)
+ cuY += 1;
+}
+
+void Screen::reverseIndex()
+//=RI
+{
+ if (cuY == tmargin)
+ scrollDown(tmargin,1);
+ else if (cuY > 0)
+ cuY -= 1;
+}
+
+/*!
+ Move the cursor to the begin of the next line.
+
+ If cursor is on bottom margin, the region between the
+ actual top and bottom margin is scrolled up.
+*/
+
+void Screen::NextLine()
+//=NEL
+{
+ Return(); index();
+}
+
+void Screen::eraseChars(int n)
+{
+ if (n == 0) n = 1; // Default
+ int p = qMax(0,qMin(cuX+n-1,columns-1));
+ clearImage(loc(cuX,cuY),loc(p,cuY),' ');
+}
+
+void Screen::deleteChars(int n)
+{
+ Q_ASSERT( n >= 0 );
+
+ // always delete at least one char
+ if (n == 0)
+ n = 1;
+
+ // if cursor is beyond the end of the line there is nothing to do
+ if ( cuX >= screenLines[cuY].count() )
+ return;
+
+ if ( cuX+n >= screenLines[cuY].count() )
+ n = screenLines[cuY].count() - 1 - cuX;
+
+ Q_ASSERT( n >= 0 );
+ Q_ASSERT( cuX+n < screenLines[cuY].count() );
+
+ screenLines[cuY].remove(cuX,n);
+}
+
+void Screen::insertChars(int n)
+{
+ if (n == 0) n = 1; // Default
+
+ if ( screenLines[cuY].size() < cuX )
+ screenLines[cuY].resize(cuX);
+
+ screenLines[cuY].insert(cuX,n,' ');
+
+ if ( screenLines[cuY].count() > columns )
+ screenLines[cuY].resize(columns);
+}
+
+void Screen::deleteLines(int n)
+{
+ if (n == 0) n = 1; // Default
+ scrollUp(cuY,n);
+}
+
+/*! insert `n' lines at the cursor position.
+
+ The cursor is not moved by the operation.
+*/
+
+void Screen::insertLines(int n)
+{
+ if (n == 0) n = 1; // Default
+ scrollDown(cuY,n);
+}
+
+// Mode Operations -----------------------------------------------------------
+
+/*! Set a specific mode. */
+
+void Screen::setMode(int m)
+{
+ currParm.mode[m] = true;
+ switch(m)
+ {
+ case MODE_Origin : cuX = 0; cuY = tmargin; break; //FIXME: home
+ }
+}
+
+/*! Reset a specific mode. */
+
+void Screen::resetMode(int m)
+{
+ currParm.mode[m] = false;
+ switch(m)
+ {
+ case MODE_Origin : cuX = 0; cuY = 0; break; //FIXME: home
+ }
+}
+
+/*! Save a specific mode. */
+
+void Screen::saveMode(int m)
+{
+ saveParm.mode[m] = currParm.mode[m];
+}
+
+/*! Restore a specific mode. */
+
+void Screen::restoreMode(int m)
+{
+ currParm.mode[m] = saveParm.mode[m];
+}
+
+bool Screen::getMode(int m) const
+{
+ return currParm.mode[m];
+}
+
+void Screen::saveCursor()
+{
+ sa_cuX = cuX;
+ sa_cuY = cuY;
+ sa_cu_re = cu_re;
+ sa_cu_fg = cu_fg;
+ sa_cu_bg = cu_bg;
+}
+
+void Screen::restoreCursor()
+{
+ cuX = qMin(sa_cuX,columns-1);
+ cuY = qMin(sa_cuY,lines-1);
+ cu_re = sa_cu_re;
+ cu_fg = sa_cu_fg;
+ cu_bg = sa_cu_bg;
+ effectiveRendition();
+}
+
+/* ------------------------------------------------------------------------- */
+/* */
+/* Screen Operations */
+/* */
+/* ------------------------------------------------------------------------- */
+
+/*! Resize the screen image
+
+ The topmost left position is maintained, while lower lines
+ or right hand side columns might be removed or filled with
+ spaces to fit the new size.
+
+ The region setting is reset to the whole screen and the
+ tab positions reinitialized.
+
+ If the new image is narrower than the old image then text on lines
+ which extends past the end of the new image is preserved so that it becomes
+ visible again if the screen is later resized to make it larger.
+*/
+
+void Screen::resizeImage(int new_lines, int new_columns)
+{
+ if ((new_lines==lines) && (new_columns==columns)) return;
+
+ if (cuY > new_lines-1)
+ { // attempt to preserve focus and lines
+ bmargin = lines-1; //FIXME: margin lost
+ for (int i = 0; i < cuY-(new_lines-1); i++)
+ {
+ addHistLine(); scrollUp(0,1);
+ }
+ }
+
+ // create new screen lines and copy from old to new
+
+ ImageLine* newScreenLines = new ImageLine[new_lines+1];
+ for (int i=0; i < qMin(lines-1,new_lines+1) ;i++)
+ newScreenLines[i]=screenLines[i];
+ for (int i=lines;(i > 0) && (i<new_lines+1);i++)
+ newScreenLines[i].resize( new_columns );
+
+ lineProperties.resize(new_lines+1);
+ for (int i=lines;(i > 0) && (i<new_lines+1);i++)
+ lineProperties[i] = LINE_DEFAULT;
+
+ clearSelection();
+
+ delete[] screenLines;
+ screenLines = newScreenLines;
+
+ lines = new_lines;
+ columns = new_columns;
+ cuX = qMin(cuX,columns-1);
+ cuY = qMin(cuY,lines-1);
+
+ // FIXME: try to keep values, evtl.
+ tmargin=0;
+ bmargin=lines-1;
+ initTabStops();
+ clearSelection();
+}
+
+void Screen::setDefaultMargins()
+{
+ tmargin = 0;
+ bmargin = lines-1;
+}
+
+
+/*
+ Clarifying rendition here and in the display.
+
+ currently, the display's color table is
+ 0 1 2 .. 9 10 .. 17
+ dft_fg, dft_bg, dim 0..7, intensive 0..7
+
+ cu_fg, cu_bg contain values 0..8;
+ - 0 = default color
+ - 1..8 = ansi specified color
+
+ re_fg, re_bg contain values 0..17
+ due to the TerminalDisplay's color table
+
+ rendition attributes are
+
+ attr widget screen
+ -------------- ------ ------
+ RE_UNDERLINE XX XX affects foreground only
+ RE_BLINK XX XX affects foreground only
+ RE_BOLD XX XX affects foreground only
+ RE_REVERSE -- XX
+ RE_TRANSPARENT XX -- affects background only
+ RE_INTENSIVE XX -- affects foreground only
+
+ Note that RE_BOLD is used in both widget
+ and screen rendition. Since xterm/vt102
+ is to poor to distinguish between bold
+ (which is a font attribute) and intensive
+ (which is a color attribute), we translate
+ this and RE_BOLD in falls eventually appart
+ into RE_BOLD and RE_INTENSIVE.
+*/
+
+void Screen::reverseRendition(Character& p) const
+{
+ CharacterColor f = p.foregroundColor;
+ CharacterColor b = p.backgroundColor;
+
+ p.foregroundColor = b;
+ p.backgroundColor = f; //p->r &= ~RE_TRANSPARENT;
+}
+
+void Screen::effectiveRendition()
+// calculate rendition
+{
+ //copy "current rendition" straight into "effective rendition", which is then later copied directly
+ //into the image[] array which holds the characters and their appearance properties.
+ //- The old version below filtered out all attributes other than underline and blink at this stage,
+ //so that they would not be copied into the image[] array and hence would not be visible by TerminalDisplay
+ //which actually paints the screen using the information from the image[] array.
+ //I don't know why it did this, but I'm fairly sure it was the wrong thing to do. The net result
+ //was that bold text wasn't printed in bold by Konsole.
+ ef_re = cu_re;
+
+ //OLD VERSION:
+ //ef_re = cu_re & (RE_UNDERLINE | RE_BLINK);
+
+ if (cu_re & RE_REVERSE)
+ {
+ ef_fg = cu_bg;
+ ef_bg = cu_fg;
+ }
+ else
+ {
+ ef_fg = cu_fg;
+ ef_bg = cu_bg;
+ }
+
+ if (cu_re & RE_BOLD)
+ ef_fg.toggleIntensive();
+}
+
+/*!
+ returns the image.
+
+ Get the size of the image by \sa getLines and \sa getColumns.
+
+ NOTE that the image returned by this function must later be
+ freed.
+
+*/
+
+void Screen::copyFromHistory(Character* dest, int startLine, int count) const
+{
+ Q_ASSERT( startLine >= 0 && count > 0 && startLine + count <= hist->getLines() );
+
+ for (int line = startLine; line < startLine + count; line++)
+ {
+ const int length = qMin(columns,hist->getLineLen(line));
+ const int destLineOffset = (line-startLine)*columns;
+
+ hist->getCells(line,0,length,dest + destLineOffset);
+
+ for (int column = length; column < columns; column++)
+ dest[destLineOffset+column] = defaultChar;
+
+ // invert selected text
+ if (sel_begin !=-1)
+ {
+ for (int column = 0; column < columns; column++)
+ {
+ if (isSelected(column,line))
+ {
+ reverseRendition(dest[destLineOffset + column]);
+ }
+ }
+ }
+ }
+}
+
+void Screen::copyFromScreen(Character* dest , int startLine , int count) const
+{
+ Q_ASSERT( startLine >= 0 && count > 0 && startLine + count <= lines );
+
+ for (int line = startLine; line < (startLine+count) ; line++)
+ {
+ int srcLineStartIndex = line*columns;
+ int destLineStartIndex = (line-startLine)*columns;
+
+ for (int column = 0; column < columns; column++)
+ {
+ int srcIndex = srcLineStartIndex + column;
+ int destIndex = destLineStartIndex + column;
+
+ dest[destIndex] = screenLines[srcIndex/columns].value(srcIndex%columns,defaultChar);
+
+ // invert selected text
+ if (sel_begin != -1 && isSelected(column,line + hist->getLines()))
+ reverseRendition(dest[destIndex]);
+ }
+
+ }
+}
+
+void Screen::getImage( Character* dest, int size, int startLine, int endLine ) const
+{
+ Q_ASSERT( startLine >= 0 );
+ Q_ASSERT( endLine >= startLine && endLine < hist->getLines() + lines );
+
+ const int mergedLines = endLine - startLine + 1;
+
+ Q_ASSERT( size >= mergedLines * columns );
+
+ const int linesInHistoryBuffer = qBound(0,hist->getLines()-startLine,mergedLines);
+ const int linesInScreenBuffer = mergedLines - linesInHistoryBuffer;
+
+ // copy lines from history buffer
+ if (linesInHistoryBuffer > 0) {
+ copyFromHistory(dest,startLine,linesInHistoryBuffer);
+ }
+
+ // copy lines from screen buffer
+ if (linesInScreenBuffer > 0) {
+ copyFromScreen(dest + linesInHistoryBuffer*columns,
+ startLine + linesInHistoryBuffer - hist->getLines(),
+ linesInScreenBuffer);
+ }
+
+ // invert display when in screen mode
+ if (getMode(MODE_Screen))
+ {
+ for (int i = 0; i < mergedLines*columns; i++)
+ reverseRendition(dest[i]); // for reverse display
+ }
+
+ // mark the character at the current cursor position
+ int cursorIndex = loc(cuX, cuY + linesInHistoryBuffer);
+ if(getMode(MODE_Cursor) && cursorIndex < columns*mergedLines)
+ dest[cursorIndex].rendition |= RE_CURSOR;
+}
+
+QVector<LineProperty> Screen::getLineProperties( int startLine , int endLine ) const
+{
+ Q_ASSERT( startLine >= 0 );
+ Q_ASSERT( endLine >= startLine && endLine < hist->getLines() + lines );
+
+ const int mergedLines = endLine-startLine+1;
+ const int linesInHistory = qBound(0,hist->getLines()-startLine,mergedLines);
+ const int linesInScreen = mergedLines - linesInHistory;
+
+ QVector<LineProperty> result(mergedLines);
+ int index = 0;
+
+ // copy properties for lines in history
+ for (int line = startLine; line < startLine + linesInHistory; line++)
+ {
+ //TODO Support for line properties other than wrapped lines
+ if (hist->isWrappedLine(line))
+ {
+ result[index] = (LineProperty)(result[index] | LINE_WRAPPED);
+ }
+ index++;
+ }
+
+ // copy properties for lines in screen buffer
+ const int firstScreenLine = startLine + linesInHistory - hist->getLines();
+ for (int line = firstScreenLine; line < firstScreenLine+linesInScreen; line++)
+ {
+ result[index]=lineProperties[line];
+ index++;
+ }
+
+ return result;
+}
+
+/*!
+*/
+
+void Screen::reset(bool clearScreen)
+{
+ setMode(MODE_Wrap ); saveMode(MODE_Wrap ); // wrap at end of margin
+ resetMode(MODE_Origin); saveMode(MODE_Origin); // position refere to [1,1]
+ resetMode(MODE_Insert); saveMode(MODE_Insert); // overstroke
+ setMode(MODE_Cursor); // cursor visible
+ resetMode(MODE_Screen); // screen not inverse
+ resetMode(MODE_NewLine);
+
+ tmargin=0;
+ bmargin=lines-1;
+
+ setDefaultRendition();
+ saveCursor();
+
+ if ( clearScreen )
+ clear();
+}
+
+/*! Clear the entire screen and home the cursor.
+*/
+
+void Screen::clear()
+{
+ clearEntireScreen();
+ home();
+}
+
+void Screen::BackSpace()
+{
+ cuX = qMin(columns-1,cuX); // nowrap!
+ cuX = qMax(0,cuX-1);
+ // if (BS_CLEARS) image[loc(cuX,cuY)].character = ' ';
+
+ if (screenLines[cuY].size() < cuX+1)
+ screenLines[cuY].resize(cuX+1);
+
+ if (BS_CLEARS) screenLines[cuY][cuX].character = ' ';
+}
+
+void Screen::Tabulate(int n)
+{
+ // note that TAB is a format effector (does not write ' ');
+ if (n == 0) n = 1;
+ while((n > 0) && (cuX < columns-1))
+ {
+ cursorRight(1); while((cuX < columns-1) && !tabstops[cuX]) cursorRight(1);
+ n--;
+ }
+}
+
+void Screen::backTabulate(int n)
+{
+ // note that TAB is a format effector (does not write ' ');
+ if (n == 0) n = 1;
+ while((n > 0) && (cuX > 0))
+ {
+ cursorLeft(1); while((cuX > 0) && !tabstops[cuX]) cursorLeft(1);
+ n--;
+ }
+}
+
+void Screen::clearTabStops()
+{
+ for (int i = 0; i < columns; i++) tabstops[i] = false;
+}
+
+void Screen::changeTabStop(bool set)
+{
+ if (cuX >= columns) return;
+ tabstops[cuX] = set;
+}
+
+void Screen::initTabStops()
+{
+ delete[] tabstops;
+ tabstops = new bool[columns];
+
+ // Arrg! The 1st tabstop has to be one longer than the other.
+ // i.e. the kids start counting from 0 instead of 1.
+ // Other programs might behave correctly. Be aware.
+ for (int i = 0; i < columns; i++) tabstops[i] = (i%8 == 0 && i != 0);
+}
+
+/*!
+ This behaves either as IND (Screen::Index) or as NEL (Screen::NextLine)
+ depending on the NewLine Mode (LNM). This mode also
+ affects the key sequence returned for newline ([CR]LF).
+*/
+
+void Screen::NewLine()
+{
+ if (getMode(MODE_NewLine)) Return();
+ index();
+}
+
+/*! put `c' literally onto the screen at the current cursor position.
+
+ VT100 uses the convention to produce an automatic newline (am)
+ with the *first* character that would fall onto the next line (xenl).
+*/
+
+void Screen::checkSelection(int from, int to)
+{
+ if (sel_begin == -1) return;
+ int scr_TL = loc(0, hist->getLines());
+ //Clear entire selection if it overlaps region [from, to]
+ if ( (sel_BR > (from+scr_TL) )&&(sel_TL < (to+scr_TL)) )
+ {
+ clearSelection();
+ }
+}
+
+void Screen::ShowCharacter(unsigned short c)
+{
+ // Note that VT100 does wrapping BEFORE putting the character.
+ // This has impact on the assumption of valid cursor positions.
+ // We indicate the fact that a newline has to be triggered by
+ // putting the cursor one right to the last column of the screen.
+
+ int w = konsole_wcwidth(c);
+
+ if (w <= 0)
+ return;
+
+ if (cuX+w > columns) {
+ if (getMode(MODE_Wrap)) {
+ lineProperties[cuY] = (LineProperty)(lineProperties[cuY] | LINE_WRAPPED);
+ NextLine();
+ }
+ else
+ cuX = columns-w;
+ }
+
+ // ensure current line vector has enough elements
+ int size = screenLines[cuY].size();
+ if (size == 0 && cuY > 0)
+ {
+ screenLines[cuY].resize( qMax(screenLines[cuY-1].size() , cuX+w) );
+ }
+ else
+ {
+ if (size < cuX+w)
+ {
+ screenLines[cuY].resize(cuX+w);
+ }
+ }
+
+ if (getMode(MODE_Insert)) insertChars(w);
+
+ lastPos = loc(cuX,cuY);
+
+ // check if selection is still valid.
+ checkSelection(cuX,cuY);
+
+ Character& currentChar = screenLines[cuY][cuX];
+
+ currentChar.character = c;
+ currentChar.foregroundColor = ef_fg;
+ currentChar.backgroundColor = ef_bg;
+ currentChar.rendition = ef_re;
+
+ int i = 0;
+ int newCursorX = cuX + w--;
+ while(w)
+ {
+ i++;
+
+ if ( screenLines[cuY].size() < cuX + i + 1 )
+ screenLines[cuY].resize(cuX+i+1);
+
+ Character& ch = screenLines[cuY][cuX + i];
+ ch.character = 0;
+ ch.foregroundColor = ef_fg;
+ ch.backgroundColor = ef_bg;
+ ch.rendition = ef_re;
+
+ w--;
+ }
+ cuX = newCursorX;
+}
+
+void Screen::compose(const QString& /*compose*/)
+{
+ Q_ASSERT( 0 /*Not implemented yet*/ );
+
+/* if (lastPos == -1)
+ return;
+
+ QChar c(image[lastPos].character);
+ compose.prepend(c);
+ //compose.compose(); ### FIXME!
+ image[lastPos].character = compose[0].unicode();*/
+}
+
+int Screen::scrolledLines() const
+{
+ return _scrolledLines;
+}
+int Screen::droppedLines() const
+{
+ return _droppedLines;
+}
+void Screen::resetDroppedLines()
+{
+ _droppedLines = 0;
+}
+void Screen::resetScrolledLines()
+{
+ //kDebug() << "scrolled lines reset";
+
+ _scrolledLines = 0;
+}
+
+// Region commands -------------------------------------------------------------
+
+void Screen::scrollUp(int n)
+{
+ if (n == 0) n = 1; // Default
+ if (tmargin == 0) addHistLine(); // hist.history
+ scrollUp(tmargin, n);
+}
+
+/*! scroll up `n' lines within current region.
+ The `n' new lines are cleared.
+ \sa setRegion \sa scrollDown
+*/
+
+QRect Screen::lastScrolledRegion() const
+{
+ return _lastScrolledRegion;
+}
+
+void Screen::scrollUp(int from, int n)
+{
+ if (n <= 0 || from + n > bmargin) return;
+
+ _scrolledLines -= n;
+ _lastScrolledRegion = QRect(0,tmargin,columns-1,(bmargin-tmargin));
+
+ //FIXME: make sure `tmargin', `bmargin', `from', `n' is in bounds.
+ moveImage(loc(0,from),loc(0,from+n),loc(columns-1,bmargin));
+ clearImage(loc(0,bmargin-n+1),loc(columns-1,bmargin),' ');
+}
+
+void Screen::scrollDown(int n)
+{
+ if (n == 0) n = 1; // Default
+ scrollDown(tmargin, n);
+}
+
+/*! scroll down `n' lines within current region.
+ The `n' new lines are cleared.
+ \sa setRegion \sa scrollUp
+*/
+
+void Screen::scrollDown(int from, int n)
+{
+
+ //kDebug() << "Screen::scrollDown( from: " << from << " , n: " << n << ")";
+
+ _scrolledLines += n;
+
+//FIXME: make sure `tmargin', `bmargin', `from', `n' is in bounds.
+ if (n <= 0) return;
+ if (from > bmargin) return;
+ if (from + n > bmargin) n = bmargin - from;
+ moveImage(loc(0,from+n),loc(0,from),loc(columns-1,bmargin-n));
+ clearImage(loc(0,from),loc(columns-1,from+n-1),' ');
+}
+
+void Screen::setCursorYX(int y, int x)
+{
+ setCursorY(y); setCursorX(x);
+}
+
+void Screen::setCursorX(int x)
+{
+ if (x == 0) x = 1; // Default
+ x -= 1; // Adjust
+ cuX = qMax(0,qMin(columns-1, x));
+}
+
+void Screen::setCursorY(int y)
+{
+ if (y == 0) y = 1; // Default
+ y -= 1; // Adjust
+ cuY = qMax(0,qMin(lines -1, y + (getMode(MODE_Origin) ? tmargin : 0) ));
+}
+
+void Screen::home()
+{
+ cuX = 0;
+ cuY = 0;
+}
+
+void Screen::Return()
+{
+ cuX = 0;
+}
+
+int Screen::getCursorX() const
+{
+ return cuX;
+}
+
+int Screen::getCursorY() const
+{
+ return cuY;
+}
+
+// Erasing ---------------------------------------------------------------------
+
+/*! \section Erasing
+
+ This group of operations erase parts of the screen contents by filling
+ it with spaces colored due to the current rendition settings.
+
+ Althought the cursor position is involved in most of these operations,
+ it is never modified by them.
+*/
+
+/*! fill screen between (including) `loca' (start) and `loce' (end) with spaces.
+
+ This is an internal helper functions. The parameter types are internal
+ addresses of within the screen image and make use of the way how the
+ screen matrix is mapped to the image vector.
+*/
+
+void Screen::clearImage(int loca, int loce, char c)
+{
+ int scr_TL=loc(0,hist->getLines());
+ //FIXME: check positions
+
+ //Clear entire selection if it overlaps region to be moved...
+ if ( (sel_BR > (loca+scr_TL) )&&(sel_TL < (loce+scr_TL)) )
+ {
+ clearSelection();
+ }
+
+ int topLine = loca/columns;
+ int bottomLine = loce/columns;
+
+ Character clearCh(c,cu_fg,cu_bg,DEFAULT_RENDITION);
+
+ //if the character being used to clear the area is the same as the
+ //default character, the affected lines can simply be shrunk.
+ bool isDefaultCh = (clearCh == Character());
+
+ for (int y=topLine;y<=bottomLine;y++)
+ {
+ lineProperties[y] = 0;
+
+ int endCol = ( y == bottomLine) ? loce%columns : columns-1;
+ int startCol = ( y == topLine ) ? loca%columns : 0;
+
+ QVector<Character>& line = screenLines[y];
+
+ if ( isDefaultCh && endCol == columns-1 )
+ {
+ line.resize(startCol);
+ }
+ else
+ {
+ if (line.size() < endCol + 1)
+ line.resize(endCol+1);
+
+ Character* data = line.data();
+ for (int i=startCol;i<=endCol;i++)
+ data[i]=clearCh;
+ }
+ }
+}
+
+/*! move image between (including) `sourceBegin' and `sourceEnd' to 'dest'.
+
+ The 'dest', 'sourceBegin' and 'sourceEnd' parameters can be generated using
+ the loc(column,line) macro.
+
+NOTE: moveImage() can only move whole lines.
+
+ This is an internal helper functions. The parameter types are internal
+ addresses of within the screen image and make use of the way how the
+ screen matrix is mapped to the image vector.
+*/
+
+void Screen::moveImage(int dest, int sourceBegin, int sourceEnd)
+{
+ //kDebug() << "moving image from (" << (sourceBegin/columns)
+ // << "," << (sourceEnd/columns) << ") to " <<
+ // (dest/columns);
+
+ Q_ASSERT( sourceBegin <= sourceEnd );
+
+ int lines=(sourceEnd-sourceBegin)/columns;
+
+ //move screen image and line properties:
+ //the source and destination areas of the image may overlap,
+ //so it matters that we do the copy in the right order -
+ //forwards if dest < sourceBegin or backwards otherwise.
+ //(search the web for 'memmove implementation' for details)
+ if (dest < sourceBegin)
+ {
+ for (int i=0;i<=lines;i++)
+ {
+ screenLines[ (dest/columns)+i ] = screenLines[ (sourceBegin/columns)+i ];
+ lineProperties[(dest/columns)+i]=lineProperties[(sourceBegin/columns)+i];
+ }
+ }
+ else
+ {
+ for (int i=lines;i>=0;i--)
+ {
+ screenLines[ (dest/columns)+i ] = screenLines[ (sourceBegin/columns)+i ];
+ lineProperties[(dest/columns)+i]=lineProperties[(sourceBegin/columns)+i];
+ }
+ }
+
+ if (lastPos != -1)
+ {
+ int diff = dest - sourceBegin; // Scroll by this amount
+ lastPos += diff;
+ if ((lastPos < 0) || (lastPos >= (lines*columns)))
+ lastPos = -1;
+ }
+
+ // Adjust selection to follow scroll.
+ if (sel_begin != -1)
+ {
+ bool beginIsTL = (sel_begin == sel_TL);
+ int diff = dest - sourceBegin; // Scroll by this amount
+ int scr_TL=loc(0,hist->getLines());
+ int srca = sourceBegin+scr_TL; // Translate index from screen to global
+ int srce = sourceEnd+scr_TL; // Translate index from screen to global
+ int desta = srca+diff;
+ int deste = srce+diff;
+
+ if ((sel_TL >= srca) && (sel_TL <= srce))
+ sel_TL += diff;
+ else if ((sel_TL >= desta) && (sel_TL <= deste))
+ sel_BR = -1; // Clear selection (see below)
+
+ if ((sel_BR >= srca) && (sel_BR <= srce))
+ sel_BR += diff;
+ else if ((sel_BR >= desta) && (sel_BR <= deste))
+ sel_BR = -1; // Clear selection (see below)
+
+ if (sel_BR < 0)
+ {
+ clearSelection();
+ }
+ else
+ {
+ if (sel_TL < 0)
+ sel_TL = 0;
+ }
+
+ if (beginIsTL)
+ sel_begin = sel_TL;
+ else
+ sel_begin = sel_BR;
+ }
+}
+
+void Screen::clearToEndOfScreen()
+{
+ clearImage(loc(cuX,cuY),loc(columns-1,lines-1),' ');
+}
+
+void Screen::clearToBeginOfScreen()
+{
+ clearImage(loc(0,0),loc(cuX,cuY),' ');
+}
+
+void Screen::clearEntireScreen()
+{
+ // Add entire screen to history
+ for (int i = 0; i < (lines-1); i++)
+ {
+ addHistLine(); scrollUp(0,1);
+ }
+
+ clearImage(loc(0,0),loc(columns-1,lines-1),' ');
+}
+
+/*! fill screen with 'E'
+ This is to aid screen alignment
+*/
+
+void Screen::helpAlign()
+{
+ clearImage(loc(0,0),loc(columns-1,lines-1),'E');
+}
+
+void Screen::clearToEndOfLine()
+{
+ clearImage(loc(cuX,cuY),loc(columns-1,cuY),' ');
+}
+
+void Screen::clearToBeginOfLine()
+{
+ clearImage(loc(0,cuY),loc(cuX,cuY),' ');
+}
+
+void Screen::clearEntireLine()
+{
+ clearImage(loc(0,cuY),loc(columns-1,cuY),' ');
+}
+
+void Screen::setRendition(int re)
+{
+ cu_re |= re;
+ effectiveRendition();
+}
+
+void Screen::resetRendition(int re)
+{
+ cu_re &= ~re;
+ effectiveRendition();
+}
+
+void Screen::setDefaultRendition()
+{
+ setForeColor(COLOR_SPACE_DEFAULT,DEFAULT_FORE_COLOR);
+ setBackColor(COLOR_SPACE_DEFAULT,DEFAULT_BACK_COLOR);
+ cu_re = DEFAULT_RENDITION;
+ effectiveRendition();
+}
+
+void Screen::setForeColor(int space, int color)
+{
+ cu_fg = CharacterColor(space, color);
+
+ if ( cu_fg.isValid() )
+ effectiveRendition();
+ else
+ setForeColor(COLOR_SPACE_DEFAULT,DEFAULT_FORE_COLOR);
+}
+
+void Screen::setBackColor(int space, int color)
+{
+ cu_bg = CharacterColor(space, color);
+
+ if ( cu_bg.isValid() )
+ effectiveRendition();
+ else
+ setBackColor(COLOR_SPACE_DEFAULT,DEFAULT_BACK_COLOR);
+}
+
+/* ------------------------------------------------------------------------- */
+/* */
+/* Marking & Selection */
+/* */
+/* ------------------------------------------------------------------------- */
+
+void Screen::clearSelection()
+{
+ sel_BR = -1;
+ sel_TL = -1;
+ sel_begin = -1;
+}
+
+void Screen::getSelectionStart(int& column , int& line)
+{
+ if ( sel_TL != -1 )
+ {
+ column = sel_TL % columns;
+ line = sel_TL / columns;
+ }
+ else
+ {
+ column = cuX + getHistLines();
+ line = cuY + getHistLines();
+ }
+}
+void Screen::getSelectionEnd(int& column , int& line)
+{
+ if ( sel_BR != -1 )
+ {
+ column = sel_BR % columns;
+ line = sel_BR / columns;
+ }
+ else
+ {
+ column = cuX + getHistLines();
+ line = cuY + getHistLines();
+ }
+}
+void Screen::setSelectionStart(/*const ScreenCursor& viewCursor ,*/ const int x, const int y, const bool mode)
+{
+// kDebug(1211) << "setSelBeginXY(" << x << "," << y << ")";
+ sel_begin = loc(x,y); //+histCursor) ;
+
+ /* FIXME, HACK to correct for x too far to the right... */
+ if (x == columns) sel_begin--;
+
+ sel_BR = sel_begin;
+ sel_TL = sel_begin;
+ columnmode = mode;
+}
+
+void Screen::setSelectionEnd( const int x, const int y)
+{
+// kDebug(1211) << "setSelExtentXY(" << x << "," << y << ")";
+ if (sel_begin == -1) return;
+ int l = loc(x,y); // + histCursor);
+
+ if (l < sel_begin)
+ {
+ sel_TL = l;
+ sel_BR = sel_begin;
+ }
+ else
+ {
+ /* FIXME, HACK to correct for x too far to the right... */
+ if (x == columns) l--;
+
+ sel_TL = sel_begin;
+ sel_BR = l;
+ }
+}
+
+bool Screen::isSelected( const int x,const int y) const
+{
+ if (columnmode) {
+ int sel_Left,sel_Right;
+ if ( sel_TL % columns < sel_BR % columns ) {
+ sel_Left = sel_TL; sel_Right = sel_BR;
+ } else {
+ sel_Left = sel_BR; sel_Right = sel_TL;
+ }
+ return ( x >= sel_Left % columns ) && ( x <= sel_Right % columns ) &&
+ ( y >= sel_TL / columns ) && ( y <= sel_BR / columns );
+ //( y+histCursor >= sel_TL / columns ) && ( y+histCursor <= sel_BR / columns );
+ }
+ else {
+ //int pos = loc(x,y+histCursor);
+ int pos = loc(x,y);
+ return ( pos >= sel_TL && pos <= sel_BR );
+ }
+}
+
+QString Screen::selectedText(bool preserveLineBreaks)
+{
+ QString result;
+ QTextStream stream(&result, QIODevice::ReadWrite);
+
+ PlainTextDecoder decoder;
+ decoder.begin(&stream);
+ writeSelectionToStream(&decoder , preserveLineBreaks);
+ decoder.end();
+
+ return result;
+}
+
+bool Screen::isSelectionValid() const
+{
+ return ( sel_TL >= 0 && sel_BR >= 0 );
+}
+
+void Screen::writeSelectionToStream(TerminalCharacterDecoder* decoder ,
+ bool preserveLineBreaks)
+{
+ // do nothing if selection is invalid
+ if ( !isSelectionValid() )
+ return;
+
+ int top = sel_TL / columns;
+ int left = sel_TL % columns;
+
+ int bottom = sel_BR / columns;
+ int right = sel_BR % columns;
+
+ Q_ASSERT( top >= 0 && left >= 0 && bottom >= 0 && right >= 0 );
+
+ //kDebug() << "sel_TL = " << sel_TL;
+ //kDebug() << "columns = " << columns;
+
+ for (int y=top;y<=bottom;y++)
+ {
+ int start = 0;
+ if ( y == top || columnmode ) start = left;
+
+ int count = -1;
+ if ( y == bottom || columnmode ) count = right - start + 1;
+
+ const bool appendNewLine = ( y != bottom );
+ copyLineToStream( y,
+ start,
+ count,
+ decoder,
+ appendNewLine,
+ preserveLineBreaks );
+ }
+}
+
+
+void Screen::copyLineToStream(int line ,
+ int start,
+ int count,
+ TerminalCharacterDecoder* decoder,
+ bool appendNewLine,
+ bool preserveLineBreaks)
+{
+ //buffer to hold characters for decoding
+ //the buffer is static to avoid initialising every
+ //element on each call to copyLineToStream
+ //(which is unnecessary since all elements will be overwritten anyway)
+ static const int MAX_CHARS = 1024;
+ static Character characterBuffer[MAX_CHARS];
+
+ assert( count < MAX_CHARS );
+
+ LineProperty currentLineProperties = 0;
+
+ //determine if the line is in the history buffer or the screen image
+ if (line < hist->getLines())
+ {
+ const int lineLength = hist->getLineLen(line);
+
+ // ensure that start position is before end of line
+ start = qMin(start,qMax(0,lineLength-1));
+
+ //retrieve line from history buffer
+ if (count == -1)
+ {
+ count = lineLength-start;
+ }
+ else
+ {
+ count = qMin(start+count,lineLength)-start;
+ }
+
+ // safety checks
+ assert( start >= 0 );
+ assert( count >= 0 );
+ assert( (start+count) <= hist->getLineLen(line) );
+
+ hist->getCells(line,start,count,characterBuffer);
+
+ if ( hist->isWrappedLine(line) )
+ currentLineProperties |= LINE_WRAPPED;
+ }
+ else
+ {
+ if ( count == -1 )
+ count = columns - start;
+
+ assert( count >= 0 );
+
+ const int screenLine = line-hist->getLines();
+
+ Character* data = screenLines[screenLine].data();
+ int length = screenLines[screenLine].count();
+
+ //retrieve line from screen image
+ for (int i=start;i < qMin(start+count,length);i++)
+ {
+ characterBuffer[i-start] = data[i];
+ }
+
+ // count cannot be any greater than length
+ count = qBound(0,count,length-start);
+
+ Q_ASSERT( screenLine < lineProperties.count() );
+ currentLineProperties |= lineProperties[screenLine];
+ }
+
+ //do not decode trailing whitespace characters
+ for (int i=count-1 ; i >= 0; i--)
+ if (QChar(characterBuffer[i].character).isSpace())
+ count--;
+ else
+ break;
+
+ // add new line character at end
+ const bool omitLineBreak = (currentLineProperties & LINE_WRAPPED) ||
+ !preserveLineBreaks;
+
+ if ( !omitLineBreak && appendNewLine && (count+1 < MAX_CHARS) )
+ {
+ characterBuffer[count] = '\n';
+ count++;
+ }
+
+ //decode line and write to text stream
+ decoder->decodeLine( (Character*) characterBuffer ,
+ count, currentLineProperties );
+}
+
+// Method below has been removed because of its reliance on 'histCursor'
+// and I want to restrict the methods which have knowledge of the scroll position
+// to just those which deal with selection and supplying final screen images.
+//
+/*void Screen::writeToStream(QTextStream* stream , TerminalCharacterDecoder* decoder) {
+ sel_begin = 0;
+ sel_BR = sel_begin;
+ sel_TL = sel_begin;
+ setSelectionEnd(columns-1,lines-1+hist->getLines()-histCursor);
+
+ writeSelectionToStream(stream,decoder);
+
+ clearSelection();
+}*/
+
+void Screen::writeToStream(TerminalCharacterDecoder* decoder, int from, int to)
+{
+ sel_begin = loc(0,from);
+ sel_TL = sel_begin;
+ sel_BR = loc(columns-1,to);
+ writeSelectionToStream(decoder);
+ clearSelection();
+}
+
+QString Screen::getHistoryLine(int no)
+{
+ sel_begin = loc(0,no);
+ sel_TL = sel_begin;
+ sel_BR = loc(columns-1,no);
+ return selectedText(false);
+}
+
+void Screen::addHistLine()
+{
+ // add line to history buffer
+ // we have to take care about scrolling, too...
+
+ if (hasScroll())
+ {
+ int oldHistLines = hist->getLines();
+
+ hist->addCellsVector(screenLines[0]);
+ hist->addLine( lineProperties[0] & LINE_WRAPPED );
+
+ int newHistLines = hist->getLines();
+
+ bool beginIsTL = (sel_begin == sel_TL);
+
+ // If the history is full, increment the count
+ // of dropped lines
+ if ( newHistLines == oldHistLines )
+ _droppedLines++;
+
+ // Adjust selection for the new point of reference
+ if (newHistLines > oldHistLines)
+ {
+ if (sel_begin != -1)
+ {
+ sel_TL += columns;
+ sel_BR += columns;
+ }
+ }
+
+ if (sel_begin != -1)
+ {
+ // Scroll selection in history up
+ int top_BR = loc(0, 1+newHistLines);
+
+ if (sel_TL < top_BR)
+ sel_TL -= columns;
+
+ if (sel_BR < top_BR)
+ sel_BR -= columns;
+
+ if (sel_BR < 0)
+ {
+ clearSelection();
+ }
+ else
+ {
+ if (sel_TL < 0)
+ sel_TL = 0;
+ }
+
+ if (beginIsTL)
+ sel_begin = sel_TL;
+ else
+ sel_begin = sel_BR;
+ }
+ }
+
+}
+
+int Screen::getHistLines()
+{
+ return hist->getLines();
+}
+
+void Screen::setScroll(const HistoryType& t , bool copyPreviousScroll)
+{
+ clearSelection();
+
+ if ( copyPreviousScroll )
+ hist = t.scroll(hist);
+ else
+ {
+ HistoryScroll* oldScroll = hist;
+ hist = t.scroll(0);
+ delete oldScroll;
+ }
+}
+
+bool Screen::hasScroll()
+{
+ return hist->hasScroll();
+}
+
+const HistoryType& Screen::getScroll()
+{
+ return hist->getType();
+}
+
+void Screen::setLineProperty(LineProperty property , bool enable)
+{
+ if ( enable )
+ {
+ lineProperties[cuY] = (LineProperty)(lineProperties[cuY] | property);
+ }
+ else
+ {
+ lineProperties[cuY] = (LineProperty)(lineProperties[cuY] & ~property);
+ }
+}
+void Screen::fillWithDefaultChar(Character* dest, int count)
+{
+ for (int i=0;i<count;i++)
+ dest[i] = defaultChar;
+}
diff --git a/qtermwidget/Screen.h b/qtermwidget/Screen.h
new file mode 100644
index 0000000..a4ad199
--- /dev/null
+++ b/qtermwidget/Screen.h
@@ -0,0 +1,662 @@
+/*
+ This file is part of Konsole, KDE's terminal.
+
+ Copyright (C) 2007 by Robert Knight <robertknight@gmail.com>
+ Copyright (C) 1997,1998 by Lars Doelle <lars.doelle@on-line.de>
+
+ Rewritten for QT4 by e_k <e_k at users.sourceforge.net>, Copyright (C)2008
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ 02110-1301 USA.
+*/
+
+#ifndef SCREEN_H
+#define SCREEN_H
+
+// Qt
+#include <QtCore/QRect>
+#include <QtCore/QTextStream>
+#include <QtCore/QVarLengthArray>
+
+// Konsole
+#include "Character.h"
+#include "History.h"
+
+#define MODE_Origin 0
+#define MODE_Wrap 1
+#define MODE_Insert 2
+#define MODE_Screen 3
+#define MODE_Cursor 4
+#define MODE_NewLine 5
+#define MODES_SCREEN 6
+
+namespace Konsole
+{
+
+/*!
+*/
+struct ScreenParm
+{
+ int mode[MODES_SCREEN];
+};
+
+class TerminalCharacterDecoder;
+
+/**
+ \brief An image of characters with associated attributes.
+
+ The terminal emulation ( Emulation ) receives a serial stream of
+ characters from the program currently running in the terminal.
+ From this stream it creates an image of characters which is ultimately
+ rendered by the display widget ( TerminalDisplay ). Some types of emulation
+ may have more than one screen image.
+
+ getImage() is used to retrieve the currently visible image
+ which is then used by the display widget to draw the output from the
+ terminal.
+
+ The number of lines of output history which are kept in addition to the current
+ screen image depends on the history scroll being used to store the output.
+ The scroll is specified using setScroll()
+ The output history can be retrieved using writeToStream()
+
+ The screen image has a selection associated with it, specified using
+ setSelectionStart() and setSelectionEnd(). The selected text can be retrieved
+ using selectedText(). When getImage() is used to retrieve the the visible image,
+ characters which are part of the selection have their colours inverted.
+*/
+class Screen
+{
+public:
+ /** Construct a new screen image of size @p lines by @p columns. */
+ Screen(int lines, int columns);
+ ~Screen();
+
+ // VT100/2 Operations
+ // Cursor Movement
+
+ /** Move the cursor up by @p n lines. */
+ void cursorUp (int n);
+ /** Move the cursor down by @p n lines. */
+ void cursorDown (int n);
+ /** Move the cursor to the left by @p n columns. */
+ void cursorLeft (int n);
+ /** Move the cursor to the right by @p n columns. */
+ void cursorRight (int n);
+ /** Position the cursor on line @p y. */
+ void setCursorY (int y);
+ /** Position the cursor at column @p x. */
+ void setCursorX (int x);
+ /** Position the cursor at line @p y, column @p x. */
+ void setCursorYX (int y, int x);
+ /**
+ * Sets the margins for scrolling the screen.
+ *
+ * @param topLine The top line of the new scrolling margin.
+ * @param bottomLine The bottom line of the new scrolling margin.
+ */
+ void setMargins (int topLine , int bottomLine);
+ /** Returns the top line of the scrolling region. */
+ int topMargin() const;
+ /** Returns the bottom line of the scrolling region. */
+ int bottomMargin() const;
+
+ /**
+ * Resets the scrolling margins back to the top and bottom lines
+ * of the screen.
+ */
+ void setDefaultMargins();
+
+ /**
+ * Moves the cursor down one line, if the MODE_NewLine mode
+ * flag is enabled then the cursor is returned to the leftmost
+ * column first.
+ *
+ * Equivalent to NextLine() if the MODE_NewLine flag is set
+ * or index() otherwise.
+ */
+ void NewLine ();
+ /**
+ * Moves the cursor down one line and positions it at the beginning
+ * of the line.
+ */
+ void NextLine ();
+
+ /**
+ * Move the cursor down one line. If the cursor is on the bottom
+ * line of the scrolling region (as returned by bottomMargin()) the
+ * scrolling region is scrolled up by one line instead.
+ */
+ void index ();
+ /**
+ * Move the cursor up one line. If the cursor is on the top line
+ * of the scrolling region (as returned by topMargin()) the scrolling
+ * region is scrolled down by one line instead.
+ */
+ void reverseIndex();
+
+ /**
+ * Scroll the scrolling region of the screen up by @p n lines.
+ * The scrolling region is initially the whole screen, but can be changed
+ * using setMargins()
+ */
+ void scrollUp(int n);
+ /**
+ * Scroll the scrolling region of the screen down by @p n lines.
+ * The scrolling region is initially the whole screen, but can be changed
+ * using setMargins()
+ */
+ void scrollDown(int n);
+
+ /**
+ * Moves the cursor to the beginning of the current line.
+ * Equivalent to setCursorX(0)
+ */
+ void Return ();
+ /**
+ * Moves the cursor one column to the left and erases the character
+ * at the new cursor position.
+ */
+ void BackSpace ();
+ /**
+ * Moves the cursor @p n tab-stops to the right.
+ */
+ void Tabulate (int n = 1);
+ /**
+ * Moves the cursor @p n tab-stops to the left.
+ */
+ void backTabulate(int n);
+
+ // Editing
+
+ /**
+ * Erase @p n characters beginning from the current cursor position.
+ * This is equivalent to over-writing @p n characters starting with the current
+ * cursor position with spaces.
+ * If @p n is 0 then one character is erased.
+ */
+ void eraseChars (int n);
+ /**
+ * Delete @p n characters beginning from the current cursor position.
+ * If @p n is 0 then one character is deleted.
+ */
+ void deleteChars (int n);
+ /**
+ * Insert @p n blank characters beginning from the current cursor position.
+ * The position of the cursor is not altered.
+ * If @p n is 0 then one character is inserted.
+ */
+ void insertChars (int n);
+ /**
+ * Removes @p n lines beginning from the current cursor position.
+ * The position of the cursor is not altered.
+ * If @p n is 0 then one line is removed.
+ */
+ void deleteLines (int n);
+ /**
+ * Inserts @p lines beginning from the current cursor position.
+ * The position of the cursor is not altered.
+ * If @p n is 0 then one line is inserted.
+ */
+ void insertLines (int n);
+ /** Clears all the tab stops. */
+ void clearTabStops();
+ /** Sets or removes a tab stop at the cursor's current column. */
+ void changeTabStop(bool set);
+
+ /** Resets (clears) the specified screen @p mode. */
+ void resetMode (int mode);
+ /** Sets (enables) the specified screen @p mode. */
+ void setMode (int mode);
+ /**
+ * Saves the state of the specified screen @p mode. It can be restored
+ * using restoreMode()
+ */
+ void saveMode (int mode);
+ /** Restores the state of a screen @p mode saved by calling saveMode() */
+ void restoreMode (int mode);
+ /** Returns whether the specified screen @p mode is enabled or not .*/
+ bool getMode (int mode) const;
+
+ /**
+ * Saves the current position and appearence (text color and style) of the cursor.
+ * It can be restored by calling restoreCursor()
+ */
+ void saveCursor ();
+ /** Restores the position and appearence of the cursor. See saveCursor() */
+ void restoreCursor();
+
+ /** Clear the whole screen, moving the current screen contents into the history first. */
+ void clearEntireScreen();
+ /**
+ * Clear the area of the screen from the current cursor position to the end of
+ * the screen.
+ */
+ void clearToEndOfScreen();
+ /**
+ * Clear the area of the screen from the current cursor position to the start
+ * of the screen.
+ */
+ void clearToBeginOfScreen();
+ /** Clears the whole of the line on which the cursor is currently positioned. */
+ void clearEntireLine();
+ /** Clears from the current cursor position to the end of the line. */
+ void clearToEndOfLine();
+ /** Clears from the current cursor position to the beginning of the line. */
+ void clearToBeginOfLine();
+
+ /** Fills the entire screen with the letter 'E' */
+ void helpAlign ();
+
+ /**
+ * Enables the given @p rendition flag. Rendition flags control the appearence
+ * of characters on the screen.
+ *
+ * @see Character::rendition
+ */
+ void setRendition (int rendition);
+ /**
+ * Disables the given @p rendition flag. Rendition flags control the appearence
+ * of characters on the screen.
+ *
+ * @see Character::rendition
+ */
+ void resetRendition(int rendition);
+
+ /**
+ * Sets the cursor's foreground color.
+ * @param space The color space used by the @p color argument
+ * @param color The new foreground color. The meaning of this depends on
+ * the color @p space used.
+ *
+ * @see CharacterColor
+ */
+ void setForeColor (int space, int color);
+ /**
+ * Sets the cursor's background color.
+ * @param space The color space used by the @p color argumnet.
+ * @param color The new background color. The meaning of this depends on
+ * the color @p space used.
+ *
+ * @see CharacterColor
+ */
+ void setBackColor (int space, int color);
+ /**
+ * Resets the cursor's color back to the default and sets the
+ * character's rendition flags back to the default settings.
+ */
+ void setDefaultRendition();
+
+ /** Returns the column which the cursor is positioned at. */
+ int getCursorX() const;
+ /** Returns the line which the cursor is positioned on. */
+ int getCursorY() const;
+
+ /** TODO Document me */
+ void clear();
+ /**
+ * Sets the position of the cursor to the 'home' position at the top-left
+ * corner of the screen (0,0)
+ */
+ void home();
+ /**
+ * Resets the state of the screen. This resets the various screen modes
+ * back to their default states. The cursor style and colors are reset
+ * (as if setDefaultRendition() had been called)
+ *
+ * <ul>
+ * <li>Line wrapping is enabled.</li>
+ * <li>Origin mode is disabled.</li>
+ * <li>Insert mode is disabled.</li>
+ * <li>Cursor mode is enabled. TODO Document me</li>
+ * <li>Screen mode is disabled. TODO Document me</li>
+ * <li>New line mode is disabled. TODO Document me</li>
+ * </ul>
+ *
+ * If @p clearScreen is true then the screen contents are erased entirely,
+ * otherwise they are unaltered.
+ */
+ void reset(bool clearScreen = true);
+
+ /**
+ * Displays a new character at the current cursor position.
+ *
+ * If the cursor is currently positioned at the right-edge of the screen and
+ * line wrapping is enabled then the character is added at the start of a new
+ * line below the current one.
+ *
+ * If the MODE_Insert screen mode is currently enabled then the character
+ * is inserted at the current cursor position, otherwise it will replace the
+ * character already at the current cursor position.
+ */
+ void ShowCharacter(unsigned short c);
+
+ // Do composition with last shown character FIXME: Not implemented yet for KDE 4
+ void compose(const QString& compose);
+
+ /**
+ * Resizes the image to a new fixed size of @p new_lines by @p new_columns.
+ * In the case that @p new_columns is smaller than the current number of columns,
+ * existing lines are not truncated. This prevents characters from being lost
+ * if the terminal display is resized smaller and then larger again.
+ *
+ * (note that in versions of Konsole prior to KDE 4, existing lines were
+ * truncated when making the screen image smaller)
+ */
+ void resizeImage(int new_lines, int new_columns);
+
+ /**
+ * Returns the current screen image.
+ * The result is an array of Characters of size [getLines()][getColumns()] which
+ * must be freed by the caller after use.
+ *
+ * @param dest Buffer to copy the characters into
+ * @param size Size of @p dest in Characters
+ * @param startLine Index of first line to copy
+ * @param endLine Index of last line to copy
+ */
+ void getImage( Character* dest , int size , int startLine , int endLine ) const;
+
+ /**
+ * Returns the additional attributes associated with lines in the image.
+ * The most important attribute is LINE_WRAPPED which specifies that the
+ * line is wrapped,
+ * other attributes control the size of characters in the line.
+ */
+ QVector<LineProperty> getLineProperties( int startLine , int endLine ) const;
+
+
+ /** Return the number of lines. */
+ int getLines() { return lines; }
+ /** Return the number of columns. */
+ int getColumns() { return columns; }
+ /** Return the number of lines in the history buffer. */
+ int getHistLines ();
+ /**
+ * Sets the type of storage used to keep lines in the history.
+ * If @p copyPreviousScroll is true then the contents of the previous
+ * history buffer are copied into the new scroll.
+ */
+ void setScroll(const HistoryType& , bool copyPreviousScroll = true);
+ /** Returns the type of storage used to keep lines in the history. */
+ const HistoryType& getScroll();
+ /**
+ * Returns true if this screen keeps lines that are scrolled off the screen
+ * in a history buffer.
+ */
+ bool hasScroll();
+
+ /**
+ * Sets the start of the selection.
+ *
+ * @param column The column index of the first character in the selection.
+ * @param line The line index of the first character in the selection.
+ * @param columnmode True if the selection is in column mode.
+ */
+ void setSelectionStart(const int column, const int line, const bool columnmode);
+
+ /**
+ * Sets the end of the current selection.
+ *
+ * @param column The column index of the last character in the selection.
+ * @param line The line index of the last character in the selection.
+ */
+ void setSelectionEnd(const int column, const int line);
+
+ /**
+ * Retrieves the start of the selection or the cursor position if there
+ * is no selection.
+ */
+ void getSelectionStart(int& column , int& line);
+
+ /**
+ * Retrieves the end of the selection or the cursor position if there
+ * is no selection.
+ */
+ void getSelectionEnd(int& column , int& line);
+
+ /** Clears the current selection */
+ void clearSelection();
+
+ void setBusySelecting(bool busy) { sel_busy = busy; }
+
+ /**
+ * Returns true if the character at (@p column, @p line) is part of the
+ * current selection.
+ */
+ bool isSelected(const int column,const int line) const;
+
+ /**
+ * Convenience method. Returns the currently selected text.
+ * @param preserveLineBreaks Specifies whether new line characters should
+ * be inserted into the returned text at the end of each terminal line.
+ */
+ QString selectedText(bool preserveLineBreaks);
+
+ /**
+ * Copies part of the output to a stream.
+ *
+ * @param decoder A decoder which coverts terminal characters into text
+ * @param from The first line in the history to retrieve
+ * @param to The last line in the history to retrieve
+ */
+ void writeToStream(TerminalCharacterDecoder* decoder, int from, int to);
+
+ /**
+ * Sets the selection to line @p no in the history and returns
+ * the text of that line from the history buffer.
+ */
+ QString getHistoryLine(int no);
+
+ /**
+ * Copies the selected characters, set using @see setSelBeginXY and @see setSelExtentXY
+ * into a stream.
+ *
+ * @param decoder A decoder which converts terminal characters into text.
+ * PlainTextDecoder is the most commonly used decoder which coverts characters
+ * into plain text with no formatting.
+ * @param preserveLineBreaks Specifies whether new line characters should
+ * be inserted into the returned text at the end of each terminal line.
+ */
+ void writeSelectionToStream(TerminalCharacterDecoder* decoder , bool
+ preserveLineBreaks = true);
+
+ /** TODO Document me */
+ void checkSelection(int from, int to);
+
+ /**
+ * Sets or clears an attribute of the current line.
+ *
+ * @param property The attribute to set or clear
+ * Possible properties are:
+ * LINE_WRAPPED: Specifies that the line is wrapped.
+ * LINE_DOUBLEWIDTH: Specifies that the characters in the current line should be double the normal width.
+ * LINE_DOUBLEHEIGHT:Specifies that the characters in the current line should be double the normal height.
+ * Double-height lines are formed of two lines containing the same characters,
+ * with both having the LINE_DOUBLEHEIGHT attribute. This allows other parts of the
+ * code to work on the assumption that all lines are the same height.
+ *
+ * @param enable true to apply the attribute to the current line or false to remove it
+ */
+ void setLineProperty(LineProperty property , bool enable);
+
+
+ /**
+ * Returns the number of lines that the image has been scrolled up or down by,
+ * since the last call to resetScrolledLines().
+ *
+ * a positive return value indicates that the image has been scrolled up,
+ * a negative return value indicates that the image has been scrolled down.
+ */
+ int scrolledLines() const;
+
+ /**
+ * Returns the region of the image which was last scrolled.
+ *
+ * This is the area of the image from the top margin to the
+ * bottom margin when the last scroll occurred.
+ */
+ QRect lastScrolledRegion() const;
+
+ /**
+ * Resets the count of the number of lines that the image has been scrolled up or down by,
+ * see scrolledLines()
+ */
+ void resetScrolledLines();
+
+ /**
+ * Returns the number of lines of output which have been
+ * dropped from the history since the last call
+ * to resetDroppedLines()
+ *
+ * If the history is not unlimited then it will drop
+ * the oldest lines of output if new lines are added when
+ * it is full.
+ */
+ int droppedLines() const;
+
+ /**
+ * Resets the count of the number of lines dropped from
+ * the history.
+ */
+ void resetDroppedLines();
+
+ /**
+ * Fills the buffer @p dest with @p count instances of the default (ie. blank)
+ * Character style.
+ */
+ static void fillWithDefaultChar(Character* dest, int count);
+
+private:
+
+ //copies a line of text from the screen or history into a stream using a
+ //specified character decoder
+ //line - the line number to copy, from 0 (the earliest line in the history) up to
+ // hist->getLines() + lines - 1
+ //start - the first column on the line to copy
+ //count - the number of characters on the line to copy
+ //decoder - a decoder which coverts terminal characters (an Character array) into text
+ //appendNewLine - if true a new line character (\n) is appended to the end of the line
+ void copyLineToStream(int line,
+ int start,
+ int count,
+ TerminalCharacterDecoder* decoder,
+ bool appendNewLine,
+ bool preserveLineBreaks);
+
+ //fills a section of the screen image with the character 'c'
+ //the parameters are specified as offsets from the start of the screen image.
+ //the loc(x,y) macro can be used to generate these values from a column,line pair.
+ void clearImage(int loca, int loce, char c);
+
+ //move screen image between 'sourceBegin' and 'sourceEnd' to 'dest'.
+ //the parameters are specified as offsets from the start of the screen image.
+ //the loc(x,y) macro can be used to generate these values from a column,line pair.
+ void moveImage(int dest, int sourceBegin, int sourceEnd);
+
+ void scrollUp(int from, int i);
+ void scrollDown(int from, int i);
+
+ void addHistLine();
+
+ void initTabStops();
+
+ void effectiveRendition();
+ void reverseRendition(Character& p) const;
+
+ bool isSelectionValid() const;
+
+ // copies 'count' lines from the screen buffer into 'dest',
+ // starting from 'startLine', where 0 is the first line in the screen buffer
+ void copyFromScreen(Character* dest, int startLine, int count) const;
+ // copies 'count' lines from the history buffer into 'dest',
+ // starting from 'startLine', where 0 is the first line in the history
+ void copyFromHistory(Character* dest, int startLine, int count) const;
+
+
+ // screen image ----------------
+ int lines;
+ int columns;
+
+ typedef QVector<Character> ImageLine; // [0..columns]
+ ImageLine* screenLines; // [lines]
+
+ int _scrolledLines;
+ QRect _lastScrolledRegion;
+
+ int _droppedLines;
+
+ QVarLengthArray<LineProperty,64> lineProperties;
+
+ // history buffer ---------------
+ HistoryScroll *hist;
+
+ // cursor location
+ int cuX;
+ int cuY;
+
+ // cursor color and rendition info
+ CharacterColor cu_fg; // foreground
+ CharacterColor cu_bg; // background
+ quint8 cu_re; // rendition
+
+ // margins ----------------
+ int tmargin; // top margin
+ int bmargin; // bottom margin
+
+ // states ----------------
+ ScreenParm currParm;
+
+ // ----------------------------
+
+ bool* tabstops;
+
+ // selection -------------------
+ int sel_begin; // The first location selected.
+ int sel_TL; // TopLeft Location.
+ int sel_BR; // Bottom Right Location.
+ bool sel_busy; // Busy making a selection.
+ bool columnmode; // Column selection mode
+
+ // effective colors and rendition ------------
+ CharacterColor ef_fg; // These are derived from
+ CharacterColor ef_bg; // the cu_* variables above
+ quint8 ef_re; // to speed up operation
+
+ //
+ // save cursor, rendition & states ------------
+ //
+
+ // cursor location
+ int sa_cuX;
+ int sa_cuY;
+
+ // rendition info
+ quint8 sa_cu_re;
+ CharacterColor sa_cu_fg;
+ CharacterColor sa_cu_bg;
+
+ // last position where we added a character
+ int lastPos;
+
+ // modes
+ ScreenParm saveParm;
+
+ static Character defaultChar;
+};
+
+}
+
+#endif // SCREEN_H
diff --git a/qtermwidget/ScreenWindow.cpp b/qtermwidget/ScreenWindow.cpp
new file mode 100644
index 0000000..f29dcd0
--- /dev/null
+++ b/qtermwidget/ScreenWindow.cpp
@@ -0,0 +1,296 @@
+/*
+ Copyright (C) 2007 by Robert Knight <robertknight@gmail.com>
+
+ Rewritten for QT4 by e_k <e_k at users.sourceforge.net>, Copyright (C)2008
+
+ 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.
+*/
+
+// Own
+#include "ScreenWindow.h"
+
+// Qt
+#include <QtCore>
+
+// Konsole
+#include "Screen.h"
+
+using namespace Konsole;
+
+ScreenWindow::ScreenWindow(QObject* parent)
+ : QObject(parent)
+ , _windowBuffer(0)
+ , _windowBufferSize(0)
+ , _bufferNeedsUpdate(true)
+ , _windowLines(1)
+ , _currentLine(0)
+ , _trackOutput(true)
+ , _scrollCount(0)
+{
+}
+ScreenWindow::~ScreenWindow()
+{
+ delete[] _windowBuffer;
+}
+void ScreenWindow::setScreen(Screen* screen)
+{
+ Q_ASSERT( screen );
+
+ _screen = screen;
+}
+
+Screen* ScreenWindow::screen() const
+{
+ return _screen;
+}
+
+Character* ScreenWindow::getImage()
+{
+ // reallocate internal buffer if the window size has changed
+ int size = windowLines() * windowColumns();
+ if (_windowBuffer == 0 || _windowBufferSize != size)
+ {
+ delete[] _windowBuffer;
+ _windowBufferSize = size;
+ _windowBuffer = new Character[size];
+ _bufferNeedsUpdate = true;
+ }
+
+ if (!_bufferNeedsUpdate)
+ return _windowBuffer;
+
+ _screen->getImage(_windowBuffer,size,
+ currentLine(),endWindowLine());
+
+ // this window may look beyond the end of the screen, in which
+ // case there will be an unused area which needs to be filled
+ // with blank characters
+ fillUnusedArea();
+
+ _bufferNeedsUpdate = false;
+ return _windowBuffer;
+}
+
+void ScreenWindow::fillUnusedArea()
+{
+ int screenEndLine = _screen->getHistLines() + _screen->getLines() - 1;
+ int windowEndLine = currentLine() + windowLines() - 1;
+
+ int unusedLines = windowEndLine - screenEndLine;
+ int charsToFill = unusedLines * windowColumns();
+
+ Screen::fillWithDefaultChar(_windowBuffer + _windowBufferSize - charsToFill,charsToFill);
+}
+
+// return the index of the line at the end of this window, or if this window
+// goes beyond the end of the screen, the index of the line at the end
+// of the screen.
+//
+// when passing a line number to a Screen method, the line number should
+// never be more than endWindowLine()
+//
+int ScreenWindow::endWindowLine() const
+{
+ return qMin(currentLine() + windowLines() - 1,
+ lineCount() - 1);
+}
+QVector<LineProperty> ScreenWindow::getLineProperties()
+{
+ QVector<LineProperty> result = _screen->getLineProperties(currentLine(),endWindowLine());
+
+ if (result.count() != windowLines())
+ result.resize(windowLines());
+
+ return result;
+}
+
+QString ScreenWindow::selectedText( bool preserveLineBreaks ) const
+{
+ return _screen->selectedText( preserveLineBreaks );
+}
+
+void ScreenWindow::getSelectionStart( int& column , int& line )
+{
+ _screen->getSelectionStart(column,line);
+ line -= currentLine();
+}
+void ScreenWindow::getSelectionEnd( int& column , int& line )
+{
+ _screen->getSelectionEnd(column,line);
+ line -= currentLine();
+}
+void ScreenWindow::setSelectionStart( int column , int line , bool columnMode )
+{
+ _screen->setSelectionStart( column , qMin(line + currentLine(),endWindowLine()) , columnMode);
+
+ _bufferNeedsUpdate = true;
+ emit selectionChanged();
+}
+
+void ScreenWindow::setSelectionEnd( int column , int line )
+{
+ _screen->setSelectionEnd( column , qMin(line + currentLine(),endWindowLine()) );
+
+ _bufferNeedsUpdate = true;
+ emit selectionChanged();
+}
+
+bool ScreenWindow::isSelected( int column , int line )
+{
+ return _screen->isSelected( column , qMin(line + currentLine(),endWindowLine()) );
+}
+
+void ScreenWindow::clearSelection()
+{
+ _screen->clearSelection();
+
+ emit selectionChanged();
+}
+
+void ScreenWindow::setWindowLines(int lines)
+{
+ Q_ASSERT(lines > 0);
+ _windowLines = lines;
+}
+int ScreenWindow::windowLines() const
+{
+ return _windowLines;
+}
+
+int ScreenWindow::windowColumns() const
+{
+ return _screen->getColumns();
+}
+
+int ScreenWindow::lineCount() const
+{
+ return _screen->getHistLines() + _screen->getLines();
+}
+
+int ScreenWindow::columnCount() const
+{
+ return _screen->getColumns();
+}
+
+QPoint ScreenWindow::cursorPosition() const
+{
+ QPoint position;
+
+ position.setX( _screen->getCursorX() );
+ position.setY( _screen->getCursorY() );
+
+ return position;
+}
+
+int ScreenWindow::currentLine() const
+{
+ return qBound(0,_currentLine,lineCount()-windowLines());
+}
+
+void ScreenWindow::scrollBy( RelativeScrollMode mode , int amount )
+{
+ if ( mode == ScrollLines )
+ {
+ scrollTo( currentLine() + amount );
+ }
+ else if ( mode == ScrollPages )
+ {
+ scrollTo( currentLine() + amount * ( windowLines() / 2 ) );
+ }
+}
+
+bool ScreenWindow::atEndOfOutput() const
+{
+ return currentLine() == (lineCount()-windowLines());
+}
+
+void ScreenWindow::scrollTo( int line )
+{
+ int maxCurrentLineNumber = lineCount() - windowLines();
+ line = qBound(0,line,maxCurrentLineNumber);
+
+ const int delta = line - _currentLine;
+ _currentLine = line;
+
+ // keep track of number of lines scrolled by,
+ // this can be reset by calling resetScrollCount()
+ _scrollCount += delta;
+
+ _bufferNeedsUpdate = true;
+
+ emit scrolled(_currentLine);
+}
+
+void ScreenWindow::setTrackOutput(bool trackOutput)
+{
+ _trackOutput = trackOutput;
+}
+
+bool ScreenWindow::trackOutput() const
+{
+ return _trackOutput;
+}
+
+int ScreenWindow::scrollCount() const
+{
+ return _scrollCount;
+}
+
+void ScreenWindow::resetScrollCount()
+{
+ _scrollCount = 0;
+}
+
+QRect ScreenWindow::scrollRegion() const
+{
+ bool equalToScreenSize = windowLines() == _screen->getLines();
+
+ if ( atEndOfOutput() && equalToScreenSize )
+ return _screen->lastScrolledRegion();
+ else
+ return QRect(0,0,windowColumns(),windowLines());
+}
+
+void ScreenWindow::notifyOutputChanged()
+{
+ // move window to the bottom of the screen and update scroll count
+ // if this window is currently tracking the bottom of the screen
+ if ( _trackOutput )
+ {
+ _scrollCount -= _screen->scrolledLines();
+ _currentLine = qMax(0,_screen->getHistLines() - (windowLines()-_screen->getLines()));
+ }
+ else
+ {
+ // if the history is not unlimited then it may
+ // have run out of space and dropped the oldest
+ // lines of output - in this case the screen
+ // window's current line number will need to
+ // be adjusted - otherwise the output will scroll
+ _currentLine = qMax(0,_currentLine -
+ _screen->droppedLines());
+
+ // ensure that the screen window's current position does
+ // not go beyond the bottom of the screen
+ _currentLine = qMin( _currentLine , _screen->getHistLines() );
+ }
+
+ _bufferNeedsUpdate = true;
+
+ emit outputChanged();
+}
+
+//#include "moc_ScreenWindow.cpp"
diff --git a/qtermwidget/ScreenWindow.h b/qtermwidget/ScreenWindow.h
new file mode 100644
index 0000000..d2955ab
--- /dev/null
+++ b/qtermwidget/ScreenWindow.h
@@ -0,0 +1,256 @@
+/*
+ Copyright (C) 2007 by Robert Knight <robertknight@gmail.com>
+
+ Rewritten for QT4 by e_k <e_k at users.sourceforge.net>, Copyright (C)2008
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ 02110-1301 USA.
+*/
+
+#ifndef SCREENWINDOW_H
+#define SCREENWINDOW_H
+
+// Qt
+#include <QtCore/QObject>
+#include <QtCore/QPoint>
+#include <QtCore/QRect>
+
+// Konsole
+#include "Character.h"
+
+namespace Konsole
+{
+
+class Screen;
+
+/**
+ * Provides a window onto a section of a terminal screen.
+ * This window can then be rendered by a terminal display widget ( TerminalDisplay ).
+ *
+ * To use the screen window, create a new ScreenWindow() instance and associated it with
+ * a terminal screen using setScreen().
+ * Use the scrollTo() method to scroll the window up and down on the screen.
+ * Call the getImage() method to retrieve the character image which is currently visible in the window.
+ *
+ * setTrackOutput() controls whether the window moves to the bottom of the associated screen when new
+ * lines are added to it.
+ *
+ * Whenever the output from the underlying screen is changed, the notifyOutputChanged() slot should
+ * be called. This in turn will update the window's position and emit the outputChanged() signal
+ * if necessary.
+ */
+class ScreenWindow : public QObject
+{
+Q_OBJECT
+
+public:
+ /**
+ * Constructs a new screen window with the given parent.
+ * A screen must be specified by calling setScreen() before calling getImage() or getLineProperties().
+ *
+ * You should not call this constructor directly, instead use the Emulation::createWindow() method
+ * to create a window on the emulation which you wish to view. This allows the emulation
+ * to notify the window when the associated screen has changed and synchronize selection updates
+ * between all views on a session.
+ */
+ ScreenWindow(QObject* parent = 0);
+ virtual ~ScreenWindow();
+
+ /** Sets the screen which this window looks onto */
+ void setScreen(Screen* screen);
+ /** Returns the screen which this window looks onto */
+ Screen* screen() const;
+
+ /**
+ * Returns the image of characters which are currently visible through this window
+ * onto the screen.
+ *
+ * The buffer is managed by the ScreenWindow instance and does not need to be
+ * deleted by the caller.
+ */
+ Character* getImage();
+
+ /**
+ * Returns the line attributes associated with the lines of characters which
+ * are currently visible through this window
+ */
+ QVector<LineProperty> getLineProperties();
+
+ /**
+ * Returns the number of lines which the region of the window
+ * specified by scrollRegion() has been scrolled by since the last call
+ * to resetScrollCount(). scrollRegion() is in most cases the
+ * whole window, but will be a smaller area in, for example, applications
+ * which provide split-screen facilities.
+ *
+ * This is not guaranteed to be accurate, but allows views to optimise
+ * rendering by reducing the amount of costly text rendering that
+ * needs to be done when the output is scrolled.
+ */
+ int scrollCount() const;
+
+ /**
+ * Resets the count of scrolled lines returned by scrollCount()
+ */
+ void resetScrollCount();
+
+ /**
+ * Returns the area of the window which was last scrolled, this is
+ * usually the whole window area.
+ *
+ * Like scrollCount(), this is not guaranteed to be accurate,
+ * but allows views to optimise rendering.
+ */
+ QRect scrollRegion() const;
+
+ /**
+ * Sets the start of the selection to the given @p line and @p column within
+ * the window.
+ */
+ void setSelectionStart( int column , int line , bool columnMode );
+ /**
+ * Sets the end of the selection to the given @p line and @p column within
+ * the window.
+ */
+ void setSelectionEnd( int column , int line );
+ /**
+ * Retrieves the start of the selection within the window.
+ */
+ void getSelectionStart( int& column , int& line );
+ /**
+ * Retrieves the end of the selection within the window.
+ */
+ void getSelectionEnd( int& column , int& line );
+ /**
+ * Returns true if the character at @p line , @p column is part of the selection.
+ */
+ bool isSelected( int column , int line );
+ /**
+ * Clears the current selection
+ */
+ void clearSelection();
+
+ /** Sets the number of lines in the window */
+ void setWindowLines(int lines);
+ /** Returns the number of lines in the window */
+ int windowLines() const;
+ /** Returns the number of columns in the window */
+ int windowColumns() const;
+
+ /** Returns the total number of lines in the screen */
+ int lineCount() const;
+ /** Returns the total number of columns in the screen */
+ int columnCount() const;
+
+ /** Returns the index of the line which is currently at the top of this window */
+ int currentLine() const;
+
+ /**
+ * Returns the position of the cursor
+ * within the window.
+ */
+ QPoint cursorPosition() const;
+
+ /**
+ * Convenience method. Returns true if the window is currently at the bottom
+ * of the screen.
+ */
+ bool atEndOfOutput() const;
+
+ /** Scrolls the window so that @p line is at the top of the window */
+ void scrollTo( int line );
+
+ enum RelativeScrollMode
+ {
+ ScrollLines,
+ ScrollPages
+ };
+
+ /**
+ * Scrolls the window relative to its current position on the screen.
+ *
+ * @param mode Specifies whether @p amount refers to the number of lines or the number
+ * of pages to scroll.
+ * @param amount The number of lines or pages ( depending on @p mode ) to scroll by. If
+ * this number is positive, the view is scrolled down. If this number is negative, the view
+ * is scrolled up.
+ */
+ void scrollBy( RelativeScrollMode mode , int amount );
+
+ /**
+ * Specifies whether the window should automatically move to the bottom
+ * of the screen when new output is added.
+ *
+ * If this is set to true, the window will be moved to the bottom of the associated screen ( see
+ * screen() ) when the notifyOutputChanged() method is called.
+ */
+ void setTrackOutput(bool trackOutput);
+ /**
+ * Returns whether the window automatically moves to the bottom of the screen as
+ * new output is added. See setTrackOutput()
+ */
+ bool trackOutput() const;
+
+ /**
+ * Returns the text which is currently selected.
+ *
+ * @param preserveLineBreaks See Screen::selectedText()
+ */
+ QString selectedText( bool preserveLineBreaks ) const;
+
+public slots:
+ /**
+ * Notifies the window that the contents of the associated terminal screen have changed.
+ * This moves the window to the bottom of the screen if trackOutput() is true and causes
+ * the outputChanged() signal to be emitted.
+ */
+ void notifyOutputChanged();
+
+signals:
+ /**
+ * Emitted when the contents of the associated terminal screen ( see screen() ) changes.
+ */
+ void outputChanged();
+
+ /**
+ * Emitted when the screen window is scrolled to a different position.
+ *
+ * @param line The line which is now at the top of the window.
+ */
+ void scrolled(int line);
+
+ /**
+ * Emitted when the selection is changed.
+ */
+ void selectionChanged();
+
+private:
+ int endWindowLine() const;
+ void fillUnusedArea();
+
+ Screen* _screen; // see setScreen() , screen()
+ Character* _windowBuffer;
+ int _windowBufferSize;
+ bool _bufferNeedsUpdate;
+
+ int _windowLines;
+ int _currentLine; // see scrollTo() , currentLine()
+ bool _trackOutput; // see setTrackOutput() , trackOutput()
+ int _scrollCount; // count of lines which the window has been scrolled by since
+ // the last call to resetScrollCount()
+};
+
+}
+#endif // SCREENWINDOW_H
diff --git a/qtermwidget/Session.cpp b/qtermwidget/Session.cpp
new file mode 100644
index 0000000..48bed7e
--- /dev/null
+++ b/qtermwidget/Session.cpp
@@ -0,0 +1,1021 @@
+/*
+ This file is part of Konsole
+
+ Copyright (C) 2006-2007 by Robert Knight <robertknight@gmail.com>
+ Copyright (C) 1997,1998 by Lars Doelle <lars.doelle@on-line.de>
+
+ Rewritten for QT4 by e_k <e_k at users.sourceforge.net>, Copyright (C)2008
+
+ 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.
+*/
+
+// Own
+#include "Session.h"
+
+// Standard
+#include <assert.h>
+#include <stdlib.h>
+
+// Qt
+#include <QtGui/QApplication>
+#include <QtCore/QByteRef>
+#include <QtCore/QDir>
+#include <QtCore/QFile>
+#include <QtCore/QRegExp>
+#include <QtCore/QStringList>
+#include <QtCore>
+
+#include "Pty.h"
+#include "TerminalDisplay.h"
+#include "ShellCommand.h"
+#include "Vt102Emulation.h"
+
+using namespace Konsole;
+
+int Session::lastSessionId = 0;
+
+Session::Session() :
+ _shellProcess(0)
+ , _emulation(0)
+ , _monitorActivity(false)
+ , _monitorSilence(false)
+ , _notifiedActivity(false)
+ , _autoClose(true)
+ , _wantedClose(false)
+ , _silenceSeconds(10)
+ , _addToUtmp(false) // disabled by default because of a bug encountered on certain systems
+ // which caused Konsole to hang when closing a tab and then opening a new
+ // one. A 'QProcess destroyed while still running' warning was being
+ // printed to the terminal. Likely a problem in KPty::logout()
+ // or KPty::login() which uses a QProcess to start /usr/bin/utempter
+ , _flowControl(true)
+ , _fullScripting(false)
+ , _sessionId(0)
+// , _zmodemBusy(false)
+// , _zmodemProc(0)
+// , _zmodemProgress(0)
+ , _hasDarkBackground(false)
+{
+ //prepare DBus communication
+// new SessionAdaptor(this);
+ _sessionId = ++lastSessionId;
+// QDBusConnection::sessionBus().registerObject(QLatin1String("/Sessions/")+QString::number(_sessionId), this);
+
+ //create teletype for I/O with shell process
+ _shellProcess = new Pty();
+
+ //create emulation backend
+ _emulation = new Vt102Emulation();
+
+ connect( _emulation, SIGNAL( titleChanged( int, const QString & ) ),
+ this, SLOT( setUserTitle( int, const QString & ) ) );
+ connect( _emulation, SIGNAL( stateSet(int) ),
+ this, SLOT( activityStateSet(int) ) );
+// connect( _emulation, SIGNAL( zmodemDetected() ), this ,
+// SLOT( fireZModemDetected() ) );
+ connect( _emulation, SIGNAL( changeTabTextColorRequest( int ) ),
+ this, SIGNAL( changeTabTextColorRequest( int ) ) );
+ connect( _emulation, SIGNAL(profileChangeCommandReceived(const QString&)),
+ this, SIGNAL( profileChangeCommandReceived(const QString&)) );
+ // TODO
+ // connect( _emulation,SIGNAL(imageSizeChanged(int,int)) , this ,
+ // SLOT(onEmulationSizeChange(int,int)) );
+
+ //connect teletype to emulation backend
+ _shellProcess->setUtf8Mode(_emulation->utf8());
+
+ connect( _shellProcess,SIGNAL(receivedData(const char*,int)),this,
+ SLOT(onReceiveBlock(const char*,int)) );
+ connect( _emulation,SIGNAL(sendData(const char*,int)),_shellProcess,
+ SLOT(sendData(const char*,int)) );
+ connect( _emulation,SIGNAL(lockPtyRequest(bool)),_shellProcess,SLOT(lockPty(bool)) );
+ connect( _emulation,SIGNAL(useUtf8Request(bool)),_shellProcess,SLOT(setUtf8Mode(bool)) );
+
+
+ connect( _shellProcess,SIGNAL(done(int)), this, SLOT(done(int)) );
+
+ //setup timer for monitoring session activity
+ _monitorTimer = new QTimer(this);
+ _monitorTimer->setSingleShot(true);
+ connect(_monitorTimer, SIGNAL(timeout()), this, SLOT(monitorTimerDone()));
+}
+
+WId Session::windowId() const
+{
+ // Returns a window ID for this session which is used
+ // to set the WINDOWID environment variable in the shell
+ // process.
+ //
+ // Sessions can have multiple views or no views, which means
+ // that a single ID is not always going to be accurate.
+ //
+ // If there are no views, the window ID is just 0. If
+ // there are multiple views, then the window ID for the
+ // top-level window which contains the first view is
+ // returned
+
+ if ( _views.count() == 0 )
+ return 0;
+ else
+ {
+ QWidget* window = _views.first();
+
+ Q_ASSERT( window );
+
+ while ( window->parentWidget() != 0 )
+ window = window->parentWidget();
+
+ return window->winId();
+ }
+}
+
+void Session::setDarkBackground(bool darkBackground)
+{
+ _hasDarkBackground = darkBackground;
+}
+bool Session::hasDarkBackground() const
+{
+ return _hasDarkBackground;
+}
+bool Session::isRunning() const
+{
+ return _shellProcess->isRunning();
+}
+
+void Session::setCodec(QTextCodec* codec)
+{
+ emulation()->setCodec(codec);
+}
+
+void Session::setProgram(const QString& program)
+{
+ _program = ShellCommand::expand(program);
+}
+void Session::setInitialWorkingDirectory(const QString& dir)
+{
+ _initialWorkingDir = ShellCommand::expand(dir);
+}
+void Session::setArguments(const QStringList& arguments)
+{
+ _arguments = ShellCommand::expand(arguments);
+}
+
+QList<TerminalDisplay*> Session::views() const
+{
+ return _views;
+}
+
+void Session::addView(TerminalDisplay* widget)
+{
+ Q_ASSERT( !_views.contains(widget) );
+
+ _views.append(widget);
+
+ if ( _emulation != 0 )
+ {
+ // connect emulation - view signals and slots
+ connect( widget , SIGNAL(keyPressedSignal(QKeyEvent*)) , _emulation ,
+ SLOT(sendKeyEvent(QKeyEvent*)) );
+ connect( widget , SIGNAL(mouseSignal(int,int,int,int)) , _emulation ,
+ SLOT(sendMouseEvent(int,int,int,int)) );
+ connect( widget , SIGNAL(sendStringToEmu(const char*)) , _emulation ,
+ SLOT(sendString(const char*)) );
+
+ // allow emulation to notify view when the foreground process
+ // indicates whether or not it is interested in mouse signals
+ connect( _emulation , SIGNAL(programUsesMouseChanged(bool)) , widget ,
+ SLOT(setUsesMouse(bool)) );
+
+ widget->setUsesMouse( _emulation->programUsesMouse() );
+
+ widget->setScreenWindow(_emulation->createWindow());
+ }
+
+ //connect view signals and slots
+ QObject::connect( widget ,SIGNAL(changedContentSizeSignal(int,int)),this,
+ SLOT(onViewSizeChange(int,int)));
+
+ QObject::connect( widget ,SIGNAL(destroyed(QObject*)) , this ,
+ SLOT(viewDestroyed(QObject*)) );
+//slot for close
+ QObject::connect(this, SIGNAL(finished()), widget, SLOT(close()));
+
+}
+
+void Session::viewDestroyed(QObject* view)
+{
+ TerminalDisplay* display = (TerminalDisplay*)view;
+
+ Q_ASSERT( _views.contains(display) );
+
+ removeView(display);
+}
+
+void Session::removeView(TerminalDisplay* widget)
+{
+ _views.removeAll(widget);
+
+ disconnect(widget,0,this,0);
+
+ if ( _emulation != 0 )
+ {
+ // disconnect
+ // - key presses signals from widget
+ // - mouse activity signals from widget
+ // - string sending signals from widget
+ //
+ // ... and any other signals connected in addView()
+ disconnect( widget, 0, _emulation, 0);
+
+ // disconnect state change signals emitted by emulation
+ disconnect( _emulation , 0 , widget , 0);
+ }
+
+ // close the session automatically when the last view is removed
+ if ( _views.count() == 0 )
+ {
+ close();
+ }
+}
+
+void Session::run()
+{
+ //check that everything is in place to run the session
+ if (_program.isEmpty())
+ qDebug() << "Session::run() - program to run not set.";
+ if (_arguments.isEmpty())
+ qDebug() << "Session::run() - no command line arguments specified.";
+
+ // Upon a KPty error, there is no description on what that error was...
+ // Check to see if the given program is executable.
+ QString exec = QFile::encodeName(_program);
+
+ // if 'exec' is not specified, fall back to default shell. if that
+ // is not set then fall back to /bin/sh
+ if ( exec.isEmpty() )
+ exec = getenv("SHELL");
+ if ( exec.isEmpty() )
+ exec = "/bin/sh";
+
+ // if no arguments are specified, fall back to shell
+ QStringList arguments = _arguments.join(QChar(' ')).isEmpty() ?
+ QStringList() << exec : _arguments;
+ QString pexec = exec;
+
+ if ( pexec.isEmpty() ) {
+ qDebug()<<"can not execute "<<exec<<endl;
+ QTimer::singleShot(1, this, SIGNAL(finished()));
+ return;
+ }
+
+ QString cwd_save = QDir::currentPath();
+ if (!_initialWorkingDir.isEmpty())
+ _shellProcess->setWorkingDirectory(_initialWorkingDir);
+ else
+ _shellProcess->setWorkingDirectory(QDir::homePath());
+
+ _shellProcess->setXonXoff(_flowControl);
+ _shellProcess->setErase(_emulation->getErase());
+
+ // this is not strictly accurate use of the COLORFGBG variable. This does not
+ // tell the terminal exactly which colors are being used, but instead approximates
+ // the color scheme as "black on white" or "white on black" depending on whether
+ // the background color is deemed dark or not
+ QString backgroundColorHint = _hasDarkBackground ? "COLORFGBG=15;0" : "COLORFGBG=0;15";
+
+ int result = _shellProcess->start(QFile::encodeName(_program),
+ arguments,
+ _environment << backgroundColorHint,
+ windowId(),
+ _addToUtmp);
+
+ if (result < 0)
+ {
+ return;
+ }
+
+ _shellProcess->setWriteable(false); // We are reachable via kwrited.
+
+ emit started();
+}
+
+void Session::setUserTitle( int what, const QString &caption )
+{
+ //set to true if anything is actually changed (eg. old _nameTitle != new _nameTitle )
+ bool modified = false;
+
+ // (btw: what=0 changes _userTitle and icon, what=1 only icon, what=2 only _nameTitle
+ if ((what == 0) || (what == 2))
+ {
+ if ( _userTitle != caption ) {
+ _userTitle = caption;
+ modified = true;
+ }
+ }
+
+ if ((what == 0) || (what == 1))
+ {
+ if ( _iconText != caption ) {
+ _iconText = caption;
+ modified = true;
+ }
+ }
+
+ if (what == 11)
+ {
+ QString colorString = caption.section(';',0,0);
+ qDebug() << __FILE__ << __LINE__ << ": setting background colour to " << colorString;
+ QColor backColor = QColor(colorString);
+ if (backColor.isValid()){// change color via \033]11;Color\007
+ if (backColor != _modifiedBackground)
+ {
+ _modifiedBackground = backColor;
+
+ // bail out here until the code to connect the terminal display
+ // to the changeBackgroundColor() signal has been written
+ // and tested - just so we don't forget to do this.
+ Q_ASSERT( 0 );
+
+ emit changeBackgroundColorRequest(backColor);
+ }
+ }
+ }
+
+ if (what == 30)
+ {
+ if ( _nameTitle != caption ) {
+ setTitle(Session::NameRole,caption);
+ return;
+ }
+ }
+
+ if (what == 31)
+ {
+ QString cwd=caption;
+ cwd=cwd.replace( QRegExp("^~"), QDir::homePath() );
+ emit openUrlRequest(cwd);
+ }
+
+ // change icon via \033]32;Icon\007
+ if (what == 32)
+ {
+ if ( _iconName != caption ) {
+ _iconName = caption;
+
+ modified = true;
+ }
+ }
+
+ if (what == 50)
+ {
+ emit profileChangeCommandReceived(caption);
+ return;
+ }
+
+ if ( modified )
+ emit titleChanged();
+}
+
+QString Session::userTitle() const
+{
+ return _userTitle;
+}
+void Session::setTabTitleFormat(TabTitleContext context , const QString& format)
+{
+ if ( context == LocalTabTitle )
+ _localTabTitleFormat = format;
+ else if ( context == RemoteTabTitle )
+ _remoteTabTitleFormat = format;
+}
+QString Session::tabTitleFormat(TabTitleContext context) const
+{
+ if ( context == LocalTabTitle )
+ return _localTabTitleFormat;
+ else if ( context == RemoteTabTitle )
+ return _remoteTabTitleFormat;
+
+ return QString();
+}
+
+void Session::monitorTimerDone()
+{
+ //FIXME: The idea here is that the notification popup will appear to tell the user than output from
+ //the terminal has stopped and the popup will disappear when the user activates the session.
+ //
+ //This breaks with the addition of multiple views of a session. The popup should disappear
+ //when any of the views of the session becomes active
+
+
+ //FIXME: Make message text for this notification and the activity notification more descriptive.
+ if (_monitorSilence) {
+// KNotification::event("Silence", ("Silence in session '%1'", _nameTitle), QPixmap(),
+// QApplication::activeWindow(),
+// KNotification::CloseWhenWidgetActivated);
+ emit stateChanged(NOTIFYSILENCE);
+ }
+ else
+ {
+ emit stateChanged(NOTIFYNORMAL);
+ }
+
+ _notifiedActivity=false;
+}
+
+void Session::activityStateSet(int state)
+{
+ if (state==NOTIFYBELL)
+ {
+ QString s; s.sprintf("Bell in session '%s'",_nameTitle.toAscii().data());
+
+ emit bellRequest( s );
+ }
+ else if (state==NOTIFYACTIVITY)
+ {
+ if (_monitorSilence) {
+ _monitorTimer->start(_silenceSeconds*1000);
+ }
+
+ if ( _monitorActivity ) {
+ //FIXME: See comments in Session::monitorTimerDone()
+ if (!_notifiedActivity) {
+// KNotification::event("Activity", ("Activity in session '%1'", _nameTitle), QPixmap(),
+// QApplication::activeWindow(),
+// KNotification::CloseWhenWidgetActivated);
+ _notifiedActivity=true;
+ }
+ }
+ }
+
+ if ( state==NOTIFYACTIVITY && !_monitorActivity )
+ state = NOTIFYNORMAL;
+ if ( state==NOTIFYSILENCE && !_monitorSilence )
+ state = NOTIFYNORMAL;
+
+ emit stateChanged(state);
+}
+
+void Session::onViewSizeChange(int /*height*/, int /*width*/)
+{
+ updateTerminalSize();
+}
+void Session::onEmulationSizeChange(int lines , int columns)
+{
+ setSize( QSize(lines,columns) );
+}
+
+void Session::updateTerminalSize()
+{
+ QListIterator<TerminalDisplay*> viewIter(_views);
+
+ int minLines = -1;
+ int minColumns = -1;
+
+ // minimum number of lines and columns that views require for
+ // their size to be taken into consideration ( to avoid problems
+ // with new view widgets which haven't yet been set to their correct size )
+ const int VIEW_LINES_THRESHOLD = 2;
+ const int VIEW_COLUMNS_THRESHOLD = 2;
+
+ //select largest number of lines and columns that will fit in all visible views
+ while ( viewIter.hasNext() )
+ {
+ TerminalDisplay* view = viewIter.next();
+ if ( view->isHidden() == false &&
+ view->lines() >= VIEW_LINES_THRESHOLD &&
+ view->columns() >= VIEW_COLUMNS_THRESHOLD )
+ {
+ minLines = (minLines == -1) ? view->lines() : qMin( minLines , view->lines() );
+ minColumns = (minColumns == -1) ? view->columns() : qMin( minColumns , view->columns() );
+ }
+ }
+
+ // backend emulation must have a _terminal of at least 1 column x 1 line in size
+ if ( minLines > 0 && minColumns > 0 )
+ {
+ _emulation->setImageSize( minLines , minColumns );
+ _shellProcess->setWindowSize( minLines , minColumns );
+ }
+}
+
+void Session::refresh()
+{
+ // attempt to get the shell process to redraw the display
+ //
+ // this requires the program running in the shell
+ // to cooperate by sending an update in response to
+ // a window size change
+ //
+ // the window size is changed twice, first made slightly larger and then
+ // resized back to its normal size so that there is actually a change
+ // in the window size (some shells do nothing if the
+ // new and old sizes are the same)
+ //
+ // if there is a more 'correct' way to do this, please
+ // send an email with method or patches to konsole-devel@kde.org
+
+ const QSize existingSize = _shellProcess->windowSize();
+ _shellProcess->setWindowSize(existingSize.height(),existingSize.width()+1);
+ _shellProcess->setWindowSize(existingSize.height(),existingSize.width());
+}
+
+bool Session::sendSignal(int signal)
+{
+ return _shellProcess->kill(signal);
+}
+
+void Session::close()
+{
+ _autoClose = true;
+ _wantedClose = true;
+ if (!_shellProcess->isRunning() || !sendSignal(SIGHUP))
+ {
+ // Forced close.
+ QTimer::singleShot(1, this, SIGNAL(finished()));
+ }
+}
+
+void Session::sendText(const QString &text) const
+{
+ _emulation->sendText(text);
+}
+
+Session::~Session()
+{
+ delete _emulation;
+ delete _shellProcess;
+// delete _zmodemProc;
+}
+
+void Session::setProfileKey(const QString& key)
+{
+ _profileKey = key;
+ emit profileChanged(key);
+}
+QString Session::profileKey() const { return _profileKey; }
+
+void Session::done(int exitStatus)
+{
+ if (!_autoClose)
+ {
+ _userTitle = ("<Finished>");
+ emit titleChanged();
+ return;
+ }
+ if (!_wantedClose && (exitStatus || _shellProcess->signalled()))
+ {
+ QString message;
+
+ if (_shellProcess->normalExit())
+ message.sprintf ("Session '%s' exited with status %d.", _nameTitle.toAscii().data(), exitStatus);
+ else if (_shellProcess->signalled())
+ {
+ if (_shellProcess->coreDumped())
+ {
+
+ message.sprintf("Session '%s' exited with signal %d and dumped core.", _nameTitle.toAscii().data(), _shellProcess->exitSignal());
+ }
+ else {
+ message.sprintf("Session '%s' exited with signal %d.", _nameTitle.toAscii().data(), _shellProcess->exitSignal());
+ }
+ }
+ else
+ message.sprintf ("Session '%s' exited unexpectedly.", _nameTitle.toAscii().data());
+
+ //FIXME: See comments in Session::monitorTimerDone()
+// KNotification::event("Finished", message , QPixmap(),
+// QApplication::activeWindow(),
+// KNotification::CloseWhenWidgetActivated);
+ }
+ emit finished();
+}
+
+Emulation* Session::emulation() const
+{
+ return _emulation;
+}
+
+QString Session::keyBindings() const
+{
+ return _emulation->keyBindings();
+}
+
+QStringList Session::environment() const
+{
+ return _environment;
+}
+
+void Session::setEnvironment(const QStringList& environment)
+{
+ _environment = environment;
+}
+
+int Session::sessionId() const
+{
+ return _sessionId;
+}
+
+void Session::setKeyBindings(const QString &id)
+{
+ _emulation->setKeyBindings(id);
+}
+
+void Session::setTitle(TitleRole role , const QString& newTitle)
+{
+ if ( title(role) != newTitle )
+ {
+ if ( role == NameRole )
+ _nameTitle = newTitle;
+ else if ( role == DisplayedTitleRole )
+ _displayTitle = newTitle;
+
+ emit titleChanged();
+ }
+}
+
+QString Session::title(TitleRole role) const
+{
+ if ( role == NameRole )
+ return _nameTitle;
+ else if ( role == DisplayedTitleRole )
+ return _displayTitle;
+ else
+ return QString();
+}
+
+void Session::setIconName(const QString& iconName)
+{
+ if ( iconName != _iconName )
+ {
+ _iconName = iconName;
+ emit titleChanged();
+ }
+}
+
+void Session::setIconText(const QString& iconText)
+{
+ _iconText = iconText;
+ //kDebug(1211)<<"Session setIconText " << _iconText;
+}
+
+QString Session::iconName() const
+{
+ return _iconName;
+}
+
+QString Session::iconText() const
+{
+ return _iconText;
+}
+
+void Session::setHistoryType(const HistoryType &hType)
+{
+ _emulation->setHistory(hType);
+}
+
+const HistoryType& Session::historyType() const
+{
+ return _emulation->history();
+}
+
+void Session::clearHistory()
+{
+ _emulation->clearHistory();
+}
+
+QStringList Session::arguments() const
+{
+ return _arguments;
+}
+
+QString Session::program() const
+{
+ return _program;
+}
+
+// unused currently
+bool Session::isMonitorActivity() const { return _monitorActivity; }
+// unused currently
+bool Session::isMonitorSilence() const { return _monitorSilence; }
+
+void Session::setMonitorActivity(bool _monitor)
+{
+ _monitorActivity=_monitor;
+ _notifiedActivity=false;
+
+ activityStateSet(NOTIFYNORMAL);
+}
+
+void Session::setMonitorSilence(bool _monitor)
+{
+ if (_monitorSilence==_monitor)
+ return;
+
+ _monitorSilence=_monitor;
+ if (_monitorSilence)
+ {
+ _monitorTimer->start(_silenceSeconds*1000);
+ }
+ else
+ _monitorTimer->stop();
+
+ activityStateSet(NOTIFYNORMAL);
+}
+
+void Session::setMonitorSilenceSeconds(int seconds)
+{
+ _silenceSeconds=seconds;
+ if (_monitorSilence) {
+ _monitorTimer->start(_silenceSeconds*1000);
+ }
+}
+
+void Session::setAddToUtmp(bool set)
+{
+ _addToUtmp = set;
+}
+
+void Session::setFlowControlEnabled(bool enabled)
+{
+ if (_flowControl == enabled)
+ return;
+
+ _flowControl = enabled;
+
+ if (_shellProcess)
+ _shellProcess->setXonXoff(_flowControl);
+
+ emit flowControlEnabledChanged(enabled);
+}
+bool Session::flowControlEnabled() const
+{
+ return _flowControl;
+}
+//void Session::fireZModemDetected()
+//{
+// if (!_zmodemBusy)
+// {
+// QTimer::singleShot(10, this, SIGNAL(zmodemDetected()));
+// _zmodemBusy = true;
+// }
+//}
+
+//void Session::cancelZModem()
+//{
+// _shellProcess->sendData("\030\030\030\030", 4); // Abort
+// _zmodemBusy = false;
+//}
+
+//void Session::startZModem(const QString &zmodem, const QString &dir, const QStringList &list)
+//{
+// _zmodemBusy = true;
+// _zmodemProc = new KProcess();
+// _zmodemProc->setOutputChannelMode( KProcess::SeparateChannels );
+//
+// *_zmodemProc << zmodem << "-v" << list;
+//
+// if (!dir.isEmpty())
+// _zmodemProc->setWorkingDirectory(dir);
+//
+// _zmodemProc->start();
+//
+// connect(_zmodemProc,SIGNAL (readyReadStandardOutput()),
+// this, SLOT(zmodemReadAndSendBlock()));
+// connect(_zmodemProc,SIGNAL (readyReadStandardError()),
+// this, SLOT(zmodemReadStatus()));
+// connect(_zmodemProc,SIGNAL (finished(int,QProcess::ExitStatus)),
+// this, SLOT(zmodemFinished()));
+//
+// disconnect( _shellProcess,SIGNAL(block_in(const char*,int)), this, SLOT(onReceiveBlock(const char*,int)) );
+// connect( _shellProcess,SIGNAL(block_in(const char*,int)), this, SLOT(zmodemRcvBlock(const char*,int)) );
+//
+// _zmodemProgress = new ZModemDialog(QApplication::activeWindow(), false,
+// i18n("ZModem Progress"));
+//
+// connect(_zmodemProgress, SIGNAL(user1Clicked()),
+// this, SLOT(zmodemDone()));
+//
+// _zmodemProgress->show();
+//}
+
+/*void Session::zmodemReadAndSendBlock()
+{
+ _zmodemProc->setReadChannel( QProcess::StandardOutput );
+ QByteArray data = _zmodemProc->readAll();
+
+ if ( data.count() == 0 )
+ return;
+
+ _shellProcess->sendData(data.constData(),data.count());
+}
+*/
+/*
+void Session::zmodemReadStatus()
+{
+ _zmodemProc->setReadChannel( QProcess::StandardError );
+ QByteArray msg = _zmodemProc->readAll();
+ while(!msg.isEmpty())
+ {
+ int i = msg.indexOf('\015');
+ int j = msg.indexOf('\012');
+ QByteArray txt;
+ if ((i != -1) && ((j == -1) || (i < j)))
+ {
+ msg = msg.mid(i+1);
+ }
+ else if (j != -1)
+ {
+ txt = msg.left(j);
+ msg = msg.mid(j+1);
+ }
+ else
+ {
+ txt = msg;
+ msg.truncate(0);
+ }
+ if (!txt.isEmpty())
+ _zmodemProgress->addProgressText(QString::fromLocal8Bit(txt));
+ }
+}
+*/
+/*
+void Session::zmodemRcvBlock(const char *data, int len)
+{
+ QByteArray ba( data, len );
+
+ _zmodemProc->write( ba );
+}
+*/
+/*
+void Session::zmodemFinished()
+{
+ if (_zmodemProc)
+ {
+ delete _zmodemProc;
+ _zmodemProc = 0;
+ _zmodemBusy = false;
+
+ disconnect( _shellProcess,SIGNAL(block_in(const char*,int)), this ,SLOT(zmodemRcvBlock(const char*,int)) );
+ connect( _shellProcess,SIGNAL(block_in(const char*,int)), this, SLOT(onReceiveBlock(const char*,int)) );
+
+ _shellProcess->sendData("\030\030\030\030", 4); // Abort
+ _shellProcess->sendData("\001\013\n", 3); // Try to get prompt back
+ _zmodemProgress->transferDone();
+ }
+}
+*/
+void Session::onReceiveBlock( const char* buf, int len )
+{
+ _emulation->receiveData( buf, len );
+ emit receivedData( QString::fromLatin1( buf, len ) );
+}
+
+QSize Session::size()
+{
+ return _emulation->imageSize();
+}
+
+void Session::setSize(const QSize& size)
+{
+ if ((size.width() <= 1) || (size.height() <= 1))
+ return;
+
+ emit resizeRequest(size);
+}
+int Session::foregroundProcessId() const
+{
+ return _shellProcess->foregroundProcessGroup();
+}
+int Session::processId() const
+{
+ return _shellProcess->pid();
+}
+
+SessionGroup::SessionGroup()
+ : _masterMode(0)
+{
+}
+SessionGroup::~SessionGroup()
+{
+ // disconnect all
+ connectAll(false);
+}
+int SessionGroup::masterMode() const { return _masterMode; }
+QList<Session*> SessionGroup::sessions() const { return _sessions.keys(); }
+bool SessionGroup::masterStatus(Session* session) const { return _sessions[session]; }
+
+void SessionGroup::addSession(Session* session)
+{
+ _sessions.insert(session,false);
+
+ QListIterator<Session*> masterIter(masters());
+
+ while ( masterIter.hasNext() )
+ connectPair(masterIter.next(),session);
+}
+void SessionGroup::removeSession(Session* session)
+{
+ setMasterStatus(session,false);
+
+ QListIterator<Session*> masterIter(masters());
+
+ while ( masterIter.hasNext() )
+ disconnectPair(masterIter.next(),session);
+
+ _sessions.remove(session);
+}
+void SessionGroup::setMasterMode(int mode)
+{
+ _masterMode = mode;
+
+ connectAll(false);
+ connectAll(true);
+}
+QList<Session*> SessionGroup::masters() const
+{
+ return _sessions.keys(true);
+}
+void SessionGroup::connectAll(bool connect)
+{
+ QListIterator<Session*> masterIter(masters());
+
+ while ( masterIter.hasNext() )
+ {
+ Session* master = masterIter.next();
+
+ QListIterator<Session*> otherIter(_sessions.keys());
+ while ( otherIter.hasNext() )
+ {
+ Session* other = otherIter.next();
+
+ if ( other != master )
+ {
+ if ( connect )
+ connectPair(master,other);
+ else
+ disconnectPair(master,other);
+ }
+ }
+ }
+}
+void SessionGroup::setMasterStatus(Session* session , bool master)
+{
+ bool wasMaster = _sessions[session];
+ _sessions[session] = master;
+
+ if ( !wasMaster && !master
+ || wasMaster && master )
+ return;
+
+ QListIterator<Session*> iter(_sessions.keys());
+ while ( iter.hasNext() )
+ {
+ Session* other = iter.next();
+
+ if ( other != session )
+ {
+ if ( master )
+ connectPair(session,other);
+ else
+ disconnectPair(session,other);
+ }
+ }
+}
+void SessionGroup::connectPair(Session* master , Session* other)
+{
+// qDebug() << k_funcinfo;
+
+ if ( _masterMode & CopyInputToAll )
+ {
+ qDebug() << "Connection session " << master->nameTitle() << "to" << other->nameTitle();
+
+ connect( master->emulation() , SIGNAL(sendData(const char*,int)) , other->emulation() ,
+ SLOT(sendString(const char*,int)) );
+ }
+}
+void SessionGroup::disconnectPair(Session* master , Session* other)
+{
+// qDebug() << k_funcinfo;
+
+ if ( _masterMode & CopyInputToAll )
+ {
+ qDebug() << "Disconnecting session " << master->nameTitle() << "from" << other->nameTitle();
+
+ disconnect( master->emulation() , SIGNAL(sendData(const char*,int)) , other->emulation() ,
+ SLOT(sendString(const char*,int)) );
+ }
+}
+
+//#include "moc_Session.cpp"
diff --git a/qtermwidget/Session.h b/qtermwidget/Session.h
new file mode 100644
index 0000000..7b2ae25
--- /dev/null
+++ b/qtermwidget/Session.h
@@ -0,0 +1,621 @@
+/*
+ This file is part of Konsole, an X terminal.
+
+ Copyright (C) 2007 by Robert Knight <robertknight@gmail.com>
+ Copyright (C) 1997,1998 by Lars Doelle <lars.doelle@on-line.de>
+
+ Rewritten for QT4 by e_k <e_k at users.sourceforge.net>, Copyright (C)2008
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ 02110-1301 USA.
+*/
+
+#ifndef SESSION_H
+#define SESSION_H
+
+// Qt
+#include <QtCore/QStringList>
+#include <QtCore>
+#include <QWidget>
+
+// Konsole
+#include "History.h"
+
+class KProcess;
+
+namespace Konsole
+{
+
+class Emulation;
+class Pty;
+class TerminalDisplay;
+//class ZModemDialog;
+
+/**
+ * Represents a terminal session consisting of a pseudo-teletype and a terminal emulation.
+ * The pseudo-teletype (or PTY) handles I/O between the terminal process and Konsole.
+ * The terminal emulation ( Emulation and subclasses ) processes the output stream from the
+ * PTY and produces a character image which is then shown on views connected to the session.
+ *
+ * Each Session can be connected to one or more views by using the addView() method.
+ * The attached views can then display output from the program running in the terminal
+ * or send input to the program in the terminal in the form of keypresses and mouse
+ * activity.
+ */
+class Session : public QObject
+{
+Q_OBJECT
+
+public:
+ Q_PROPERTY(QString name READ nameTitle)
+ Q_PROPERTY(int processId READ processId)
+ Q_PROPERTY(QString keyBindings READ keyBindings WRITE setKeyBindings)
+ Q_PROPERTY(QSize size READ size WRITE setSize)
+
+ /**
+ * Constructs a new session.
+ *
+ * To start the terminal process, call the run() method,
+ * after specifying the program and arguments
+ * using setProgram() and setArguments()
+ *
+ * If no program or arguments are specified explicitly, the Session
+ * falls back to using the program specified in the SHELL environment
+ * variable.
+ */
+ Session();
+ ~Session();
+
+ /**
+ * Returns true if the session is currently running. This will be true
+ * after run() has been called successfully.
+ */
+ bool isRunning() const;
+
+ /**
+ * Sets the profile associated with this session.
+ *
+ * @param profileKey A key which can be used to obtain the current
+ * profile settings from the SessionManager
+ */
+ void setProfileKey(const QString& profileKey);
+ /**
+ * Returns the profile key associated with this session.
+ * This can be passed to the SessionManager to obtain the current
+ * profile settings.
+ */
+ QString profileKey() const;
+
+ /**
+ * Adds a new view for this session.
+ *
+ * The viewing widget will display the output from the terminal and
+ * input from the viewing widget (key presses, mouse activity etc.)
+ * will be sent to the terminal.
+ *
+ * Views can be removed using removeView(). The session is automatically
+ * closed when the last view is removed.
+ */
+ void addView(TerminalDisplay* widget);
+ /**
+ * Removes a view from this session. When the last view is removed,
+ * the session will be closed automatically.
+ *
+ * @p widget will no longer display output from or send input
+ * to the terminal
+ */
+ void removeView(TerminalDisplay* widget);
+
+ /**
+ * Returns the views connected to this session
+ */
+ QList<TerminalDisplay*> views() const;
+
+ /**
+ * Returns the terminal emulation instance being used to encode / decode
+ * characters to / from the process.
+ */
+ Emulation* emulation() const;
+
+ /**
+ * Returns the environment of this session as a list of strings like
+ * VARIABLE=VALUE
+ */
+ QStringList environment() const;
+ /**
+ * Sets the environment for this session.
+ * @p environment should be a list of strings like
+ * VARIABLE=VALUE
+ */
+ void setEnvironment(const QStringList& environment);
+
+ /** Returns the unique ID for this session. */
+ int sessionId() const;
+
+ /**
+ * Return the session title set by the user (ie. the program running
+ * in the terminal), or an empty string if the user has not set a custom title
+ */
+ QString userTitle() const;
+
+ /**
+ * This enum describes the contexts for which separate
+ * tab title formats may be specified.
+ */
+ enum TabTitleContext
+ {
+ /** Default tab title format */
+ LocalTabTitle,
+ /**
+ * Tab title format used session currently contains
+ * a connection to a remote computer (via SSH)
+ */
+ RemoteTabTitle
+ };
+ /**
+ * Sets the format used by this session for tab titles.
+ *
+ * @param context The context whoose format should be set.
+ * @param format The tab title format. This may be a mixture
+ * of plain text and dynamic elements denoted by a '%' character
+ * followed by a letter. (eg. %d for directory). The dynamic
+ * elements available depend on the @p context
+ */
+ void setTabTitleFormat(TabTitleContext context , const QString& format);
+ /** Returns the format used by this session for tab titles. */
+ QString tabTitleFormat(TabTitleContext context) const;
+
+
+ /** Returns the arguments passed to the shell process when run() is called. */
+ QStringList arguments() const;
+ /** Returns the program name of the shell process started when run() is called. */
+ QString program() const;
+
+ /**
+ * Sets the command line arguments which the session's program will be passed when
+ * run() is called.
+ */
+ void setArguments(const QStringList& arguments);
+ /** Sets the program to be executed when run() is called. */
+ void setProgram(const QString& program);
+
+ /** Returns the session's current working directory. */
+ QString initialWorkingDirectory() { return _initialWorkingDir; }
+
+ /**
+ * Sets the initial working directory for the session when it is run
+ * This has no effect once the session has been started.
+ */
+ void setInitialWorkingDirectory( const QString& dir );
+
+ /**
+ * Sets the type of history store used by this session.
+ * Lines of output produced by the terminal are added
+ * to the history store. The type of history store
+ * used affects the number of lines which can be
+ * remembered before they are lost and the storage
+ * (in memory, on-disk etc.) used.
+ */
+ void setHistoryType(const HistoryType& type);
+ /**
+ * Returns the type of history store used by this session.
+ */
+ const HistoryType& historyType() const;
+ /**
+ * Clears the history store used by this session.
+ */
+ void clearHistory();
+
+ /**
+ * Enables monitoring for activity in the session.
+ * This will cause notifySessionState() to be emitted
+ * with the NOTIFYACTIVITY state flag when output is
+ * received from the terminal.
+ */
+ void setMonitorActivity(bool);
+ /** Returns true if monitoring for activity is enabled. */
+ bool isMonitorActivity() const;
+
+ /**
+ * Enables monitoring for silence in the session.
+ * This will cause notifySessionState() to be emitted
+ * with the NOTIFYSILENCE state flag when output is not
+ * received from the terminal for a certain period of
+ * time, specified with setMonitorSilenceSeconds()
+ */
+ void setMonitorSilence(bool);
+ /**
+ * Returns true if monitoring for inactivity (silence)
+ * in the session is enabled.
+ */
+ bool isMonitorSilence() const;
+ /** See setMonitorSilence() */
+ void setMonitorSilenceSeconds(int seconds);
+
+ /**
+ * Sets the key bindings used by this session. The bindings
+ * specify how input key sequences are translated into
+ * the character stream which is sent to the terminal.
+ *
+ * @param id The name of the key bindings to use. The
+ * names of available key bindings can be determined using the
+ * KeyboardTranslatorManager class.
+ */
+ void setKeyBindings(const QString& id);
+ /** Returns the name of the key bindings used by this session. */
+ QString keyBindings() const;
+
+ /**
+ * This enum describes the available title roles.
+ */
+ enum TitleRole
+ {
+ /** The name of the session. */
+ NameRole,
+ /** The title of the session which is displayed in tabs etc. */
+ DisplayedTitleRole
+ };
+
+ /** Sets the session's title for the specified @p role to @p title. */
+ void setTitle(TitleRole role , const QString& title);
+ /** Returns the session's title for the specified @p role. */
+ QString title(TitleRole role) const;
+ /** Convenience method used to read the name property. Returns title(Session::NameRole). */
+ QString nameTitle() const { return title(Session::NameRole); }
+
+ /** Sets the name of the icon associated with this session. */
+ void setIconName(const QString& iconName);
+ /** Returns the name of the icon associated with this session. */
+ QString iconName() const;
+
+ /** Sets the text of the icon associated with this session. */
+ void setIconText(const QString& iconText);
+ /** Returns the text of the icon associated with this session. */
+ QString iconText() const;
+
+ /** Specifies whether a utmp entry should be created for the pty used by this session. */
+ void setAddToUtmp(bool);
+
+ /** Sends the specified @p signal to the terminal process. */
+ bool sendSignal(int signal);
+
+ /**
+ * Specifies whether to close the session automatically when the terminal
+ * process terminates.
+ */
+ void setAutoClose(bool b) { _autoClose = b; }
+
+ /**
+ * Sets whether flow control is enabled for this terminal
+ * session.
+ */
+ void setFlowControlEnabled(bool enabled);
+
+ /** Returns whether flow control is enabled for this terminal session. */
+ bool flowControlEnabled() const;
+
+ /**
+ * Sends @p text to the current foreground terminal program.
+ */
+ void sendText(const QString& text) const;
+
+ /**
+ * Returns the process id of the terminal process.
+ * This is the id used by the system API to refer to the process.
+ */
+ int processId() const;
+
+ /**
+ * Returns the process id of the terminal's foreground process.
+ * This is initially the same as processId() but can change
+ * as the user starts other programs inside the terminal.
+ */
+ int foregroundProcessId() const;
+
+ /** Returns the terminal session's window size in lines and columns. */
+ QSize size();
+ /**
+ * Emits a request to resize the session to accommodate
+ * the specified window size.
+ *
+ * @param size The size in lines and columns to request.
+ */
+ void setSize(const QSize& size);
+
+ /** Sets the text codec used by this session's terminal emulation. */
+ void setCodec(QTextCodec* codec);
+
+ /**
+ * Sets whether the session has a dark background or not. The session
+ * uses this information to set the COLORFGBG variable in the process's
+ * environment, which allows the programs running in the terminal to determine
+ * whether the background is light or dark and use appropriate colors by default.
+ *
+ * This has no effect once the session is running.
+ */
+ void setDarkBackground(bool darkBackground);
+ /**
+ * Returns true if the session has a dark background.
+ * See setDarkBackground()
+ */
+ bool hasDarkBackground() const;
+
+ /**
+ * Attempts to get the shell program to redraw the current display area.
+ * This can be used after clearing the screen, for example, to get the
+ * shell to redraw the prompt line.
+ */
+ void refresh();
+
+// void startZModem(const QString &rz, const QString &dir, const QStringList &list);
+// void cancelZModem();
+// bool isZModemBusy() { return _zmodemBusy; }
+
+public slots:
+
+ /**
+ * Starts the terminal session.
+ *
+ * This creates the terminal process and connects the teletype to it.
+ */
+ void run();
+
+ /**
+ * Closes the terminal session. This sends a hangup signal
+ * (SIGHUP) to the terminal process and causes the done(Session*)
+ * signal to be emitted.
+ */
+ void close();
+
+ /**
+ * Changes the session title or other customizable aspects of the terminal
+ * emulation display. For a list of what may be changed see the
+ * Emulation::titleChanged() signal.
+ */
+ void setUserTitle( int, const QString &caption );
+
+signals:
+
+ /** Emitted when the terminal process starts. */
+ void started();
+
+ /**
+ * Emitted when the terminal process exits.
+ */
+ void finished();
+
+ /**
+ * Emitted when output is received from the terminal process.
+ */
+ void receivedData( const QString& text );
+
+ /** Emitted when the session's title has changed. */
+ void titleChanged();
+
+ /** Emitted when the session's profile has changed. */
+ void profileChanged(const QString& profile);
+
+ /**
+ * Emitted when the activity state of this session changes.
+ *
+ * @param state The new state of the session. This may be one
+ * of NOTIFYNORMAL, NOTIFYSILENCE or NOTIFYACTIVITY
+ */
+ void stateChanged(int state);
+
+ /** Emitted when a bell event occurs in the session. */
+ void bellRequest( const QString& message );
+
+ /**
+ * Requests that the color the text for any tabs associated with
+ * this session should be changed;
+ *
+ * TODO: Document what the parameter does
+ */
+ void changeTabTextColorRequest(int);
+
+ /**
+ * Requests that the background color of views on this session
+ * should be changed.
+ */
+ void changeBackgroundColorRequest(const QColor&);
+
+ /** TODO: Document me. */
+ void openUrlRequest(const QString& url);
+
+ /** TODO: Document me. */
+// void zmodemDetected();
+
+ /**
+ * Emitted when the terminal process requests a change
+ * in the size of the terminal window.
+ *
+ * @param size The requested window size in terms of lines and columns.
+ */
+ void resizeRequest(const QSize& size);
+
+ /**
+ * Emitted when a profile change command is received from the terminal.
+ *
+ * @param text The text of the command. This is a string of the form
+ * "PropertyName=Value;PropertyName=Value ..."
+ */
+ void profileChangeCommandReceived(const QString& text);
+
+ /**
+ * Emitted when the flow control state changes.
+ *
+ * @param enabled True if flow control is enabled or false otherwise.
+ */
+ void flowControlEnabledChanged(bool enabled);
+
+private slots:
+ void done(int);
+
+// void fireZModemDetected();
+
+ void onReceiveBlock( const char* buffer, int len );
+ void monitorTimerDone();
+
+ void onViewSizeChange(int height, int width);
+ void onEmulationSizeChange(int lines , int columns);
+
+ void activityStateSet(int);
+
+ //automatically detach views from sessions when view is destroyed
+ void viewDestroyed(QObject* view);
+
+// void zmodemReadStatus();
+// void zmodemReadAndSendBlock();
+// void zmodemRcvBlock(const char *data, int len);
+// void zmodemFinished();
+
+private:
+
+ void updateTerminalSize();
+ WId windowId() const;
+
+ int _uniqueIdentifier;
+
+ Pty* _shellProcess;
+ Emulation* _emulation;
+
+ QList<TerminalDisplay*> _views;
+
+ bool _monitorActivity;
+ bool _monitorSilence;
+ bool _notifiedActivity;
+ bool _masterMode;
+ bool _autoClose;
+ bool _wantedClose;
+ QTimer* _monitorTimer;
+
+ int _silenceSeconds;
+
+ QString _nameTitle;
+ QString _displayTitle;
+ QString _userTitle;
+
+ QString _localTabTitleFormat;
+ QString _remoteTabTitleFormat;
+
+ QString _iconName;
+ QString _iconText; // as set by: echo -en '\033]1;IconText\007
+ bool _addToUtmp;
+ bool _flowControl;
+ bool _fullScripting;
+
+ QString _program;
+ QStringList _arguments;
+
+ QStringList _environment;
+ int _sessionId;
+
+ QString _initialWorkingDir;
+
+ // ZModem
+// bool _zmodemBusy;
+// KProcess* _zmodemProc;
+// ZModemDialog* _zmodemProgress;
+
+ // Color/Font Changes by ESC Sequences
+
+ QColor _modifiedBackground; // as set by: echo -en '\033]11;Color\007
+
+ QString _profileKey;
+
+ bool _hasDarkBackground;
+
+ static int lastSessionId;
+
+};
+
+/**
+ * Provides a group of sessions which is divided into master and slave sessions.
+ * Activity in master sessions can be propagated to all sessions within the group.
+ * The type of activity which is propagated and method of propagation is controlled
+ * by the masterMode() flags.
+ */
+class SessionGroup : public QObject
+{
+Q_OBJECT
+
+public:
+ /** Constructs an empty session group. */
+ SessionGroup();
+ /** Destroys the session group and removes all connections between master and slave sessions. */
+ ~SessionGroup();
+
+ /** Adds a session to the group. */
+ void addSession( Session* session );
+ /** Removes a session from the group. */
+ void removeSession( Session* session );
+
+ /** Returns the list of sessions currently in the group. */
+ QList<Session*> sessions() const;
+
+ /**
+ * Sets whether a particular session is a master within the group.
+ * Changes or activity in the group's master sessions may be propagated
+ * to all the sessions in the group, depending on the current masterMode()
+ *
+ * @param session The session whoose master status should be changed.
+ * @param master True to make this session a master or false otherwise
+ */
+ void setMasterStatus( Session* session , bool master );
+ /** Returns the master status of a session. See setMasterStatus() */
+ bool masterStatus( Session* session ) const;
+
+ /**
+ * This enum describes the options for propagating certain activity or
+ * changes in the group's master sessions to all sessions in the group.
+ */
+ enum MasterMode
+ {
+ /**
+ * Any input key presses in the master sessions are sent to all
+ * sessions in the group.
+ */
+ CopyInputToAll = 1
+ };
+
+ /**
+ * Specifies which activity in the group's master sessions is propagated
+ * to all sessions in the group.
+ *
+ * @param mode A bitwise OR of MasterMode flags.
+ */
+ void setMasterMode( int mode );
+ /**
+ * Returns a bitwise OR of the active MasterMode flags for this group.
+ * See setMasterMode()
+ */
+ int masterMode() const;
+
+private:
+ void connectPair(Session* master , Session* other);
+ void disconnectPair(Session* master , Session* other);
+ void connectAll(bool connect);
+ QList<Session*> masters() const;
+
+ // maps sessions to their master status
+ QHash<Session*,bool> _sessions;
+
+ int _masterMode;
+};
+
+}
+
+#endif
diff --git a/qtermwidget/ShellCommand.cpp b/qtermwidget/ShellCommand.cpp
new file mode 100644
index 0000000..764dd66
--- /dev/null
+++ b/qtermwidget/ShellCommand.cpp
@@ -0,0 +1,168 @@
+/*
+ Copyright (C) 2007 by Robert Knight <robertknight@gmail.com>
+
+ Rewritten for QT4 by e_k <e_k at users.sourceforge.net>, Copyright (C)2008
+
+ 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.
+*/
+
+// Own
+#include "ShellCommand.h"
+
+//some versions of gcc(4.3) require explicit include
+#include <cstdlib>
+
+
+using namespace Konsole;
+
+// expands environment variables in 'text'
+// function copied from kdelibs/kio/kio/kurlcompletion.cpp
+static bool expandEnv(QString& text);
+
+ShellCommand::ShellCommand(const QString& fullCommand)
+{
+ bool inQuotes = false;
+
+ QString builder;
+
+ for ( int i = 0 ; i < fullCommand.count() ; i++ )
+ {
+ QChar ch = fullCommand[i];
+
+ const bool isLastChar = ( i == fullCommand.count() - 1 );
+ const bool isQuote = ( ch == '\'' || ch == '\"' );
+
+ if ( !isLastChar && isQuote )
+ inQuotes = !inQuotes;
+ else
+ {
+ if ( (!ch.isSpace() || inQuotes) && !isQuote )
+ builder.append(ch);
+
+ if ( (ch.isSpace() && !inQuotes) || ( i == fullCommand.count()-1 ) )
+ {
+ _arguments << builder;
+ builder.clear();
+ }
+ }
+ }
+}
+ShellCommand::ShellCommand(const QString& command , const QStringList& arguments)
+{
+ _arguments = arguments;
+
+ if ( !_arguments.isEmpty() )
+ _arguments[0] == command;
+}
+QString ShellCommand::fullCommand() const
+{
+ return _arguments.join(QChar(' '));
+}
+QString ShellCommand::command() const
+{
+ if ( !_arguments.isEmpty() )
+ return _arguments[0];
+ else
+ return QString();
+}
+QStringList ShellCommand::arguments() const
+{
+ return _arguments;
+}
+bool ShellCommand::isRootCommand() const
+{
+ Q_ASSERT(0); // not implemented yet
+ return false;
+}
+bool ShellCommand::isAvailable() const
+{
+ Q_ASSERT(0); // not implemented yet
+ return false;
+}
+QStringList ShellCommand::expand(const QStringList& items)
+{
+ QStringList result;
+
+ foreach( QString item , items )
+ result << expand(item);
+
+ return result;
+}
+QString ShellCommand::expand(const QString& text)
+{
+ QString result = text;
+ expandEnv(result);
+ return result;
+}
+
+/*
+ * expandEnv
+ *
+ * Expand environment variables in text. Escaped '$' characters are ignored.
+ * Return true if any variables were expanded
+ */
+static bool expandEnv( QString &text )
+{
+ // Find all environment variables beginning with '$'
+ //
+ int pos = 0;
+
+ bool expanded = false;
+
+ while ( (pos = text.indexOf(QLatin1Char('$'), pos)) != -1 ) {
+
+ // Skip escaped '$'
+ //
+ if ( pos > 0 && text.at(pos-1) == QLatin1Char('\\') ) {
+ pos++;
+ }
+ // Variable found => expand
+ //
+ else {
+ // Find the end of the variable = next '/' or ' '
+ //
+ int pos2 = text.indexOf( QLatin1Char(' '), pos+1 );
+ int pos_tmp = text.indexOf( QLatin1Char('/'), pos+1 );
+
+ if ( pos2 == -1 || (pos_tmp != -1 && pos_tmp < pos2) )
+ pos2 = pos_tmp;
+
+ if ( pos2 == -1 )
+ pos2 = text.length();
+
+ // Replace if the variable is terminated by '/' or ' '
+ // and defined
+ //
+ if ( pos2 >= 0 ) {
+ int len = pos2 - pos;
+ QString key = text.mid( pos+1, len-1);
+ QString value =
+ QString::fromLocal8Bit( ::getenv(key.toLocal8Bit()) );
+
+ if ( !value.isEmpty() ) {
+ expanded = true;
+ text.replace( pos, len, value );
+ pos = pos + value.length();
+ }
+ else {
+ pos = pos2;
+ }
+ }
+ }
+ }
+
+ return expanded;
+}
diff --git a/qtermwidget/ShellCommand.h b/qtermwidget/ShellCommand.h
new file mode 100644
index 0000000..44e0db8
--- /dev/null
+++ b/qtermwidget/ShellCommand.h
@@ -0,0 +1,94 @@
+/*
+ Copyright (C) 2007 by Robert Knight <robertknight@gmail.com>
+
+ Rewritten for QT4 by e_k <e_k at users.sourceforge.net>, Copyright (C)2008
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ 02110-1301 USA.
+*/
+
+#ifndef SHELLCOMMAND_H
+#define SHELLCOMMAND_H
+
+// Qt
+#include <QtCore/QStringList>
+
+namespace Konsole
+{
+
+/**
+ * A class to parse and extract information about shell commands.
+ *
+ * ShellCommand can be used to:
+ *
+ * <ul>
+ * <li>Take a command-line (eg "/bin/sh -c /path/to/my/script") and split it
+ * into its component parts (eg. the command "/bin/sh" and the arguments
+ * "-c","/path/to/my/script")
+ * </li>
+ * <li>Take a command and a list of arguments and combine them to
+ * form a complete command line.
+ * </li>
+ * <li>Determine whether the binary specified by a command exists in the
+ * user's PATH.
+ * </li>
+ * <li>Determine whether a command-line specifies the execution of
+ * another command as the root user using su/sudo etc.
+ * </li>
+ * </ul>
+ */
+class ShellCommand
+{
+public:
+ /**
+ * Constructs a ShellCommand from a command line.
+ *
+ * @param fullCommand The command line to parse.
+ */
+ ShellCommand(const QString& fullCommand);
+ /**
+ * Constructs a ShellCommand with the specified @p command and @p arguments.
+ */
+ ShellCommand(const QString& command , const QStringList& arguments);
+
+ /** Returns the command. */
+ QString command() const;
+ /** Returns the arguments. */
+ QStringList arguments() const;
+
+ /**
+ * Returns the full command line.
+ */
+ QString fullCommand() const;
+
+ /** Returns true if this is a root command. */
+ bool isRootCommand() const;
+ /** Returns true if the program specified by @p command() exists. */
+ bool isAvailable() const;
+
+ /** Expands environment variables in @p text .*/
+ static QString expand(const QString& text);
+
+ /** Expands environment variables in each string in @p list. */
+ static QStringList expand(const QStringList& items);
+
+private:
+ QStringList _arguments;
+};
+
+}
+
+#endif // SHELLCOMMAND_H
+
diff --git a/qtermwidget/TerminalCharacterDecoder.cpp b/qtermwidget/TerminalCharacterDecoder.cpp
new file mode 100644
index 0000000..18571d9
--- /dev/null
+++ b/qtermwidget/TerminalCharacterDecoder.cpp
@@ -0,0 +1,227 @@
+/*
+ This file is part of Konsole, an X terminal.
+
+ Copyright (C) 2006 by Robert Knight <robertknight@gmail.com>
+
+ Rewritten for QT4 by e_k <e_k at users.sourceforge.net>, Copyright (C)2008
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ 02110-1301 USA.
+*/
+
+// Own
+#include "TerminalCharacterDecoder.h"
+
+// Qt
+#include <QtCore/QTextStream>
+
+
+using namespace Konsole;
+
+PlainTextDecoder::PlainTextDecoder()
+ : _output(0)
+ , _includeTrailingWhitespace(true)
+{
+
+}
+void PlainTextDecoder::setTrailingWhitespace(bool enable)
+{
+ _includeTrailingWhitespace = enable;
+}
+bool PlainTextDecoder::trailingWhitespace() const
+{
+ return _includeTrailingWhitespace;
+}
+void PlainTextDecoder::begin(QTextStream* output)
+{
+ _output = output;
+}
+void PlainTextDecoder::end()
+{
+ _output = 0;
+}
+void PlainTextDecoder::decodeLine(const Character* const characters, int count, LineProperty /*properties*/
+ )
+{
+ Q_ASSERT( _output );
+
+ //TODO should we ignore or respect the LINE_WRAPPED line property?
+
+ //note: we build up a QString and send it to the text stream rather writing into the text
+ //stream a character at a time because it is more efficient.
+ //(since QTextStream always deals with QStrings internally anyway)
+ QString plainText;
+ plainText.reserve(count);
+
+ int outputCount = count;
+
+ // if inclusion of trailing whitespace is disabled then find the end of the
+ // line
+ if ( !_includeTrailingWhitespace )
+ {
+ for (int i = count-1 ; i >= 0 ; i--)
+ {
+ if ( characters[i].character != ' ' )
+ break;
+ else
+ outputCount--;
+ }
+ }
+
+ for (int i=0;i<outputCount;i++)
+ {
+ plainText.append( QChar(characters[i].character) );
+ }
+
+ *_output << plainText;
+}
+
+HTMLDecoder::HTMLDecoder() :
+ _output(0)
+ ,_colorTable(base_color_table)
+ ,_innerSpanOpen(false)
+ ,_lastRendition(DEFAULT_RENDITION)
+{
+
+}
+
+void HTMLDecoder::begin(QTextStream* output)
+{
+ _output = output;
+
+ QString text;
+
+ //open monospace span
+ openSpan(text,"font-family:monospace");
+
+ *output << text;
+}
+
+void HTMLDecoder::end()
+{
+ Q_ASSERT( _output );
+
+ QString text;
+
+ closeSpan(text);
+
+ *_output << text;
+
+ _output = 0;
+
+}
+
+//TODO: Support for LineProperty (mainly double width , double height)
+void HTMLDecoder::decodeLine(const Character* const characters, int count, LineProperty /*properties*/
+ )
+{
+ Q_ASSERT( _output );
+
+ QString text;
+
+ int spaceCount = 0;
+
+ for (int i=0;i<count;i++)
+ {
+ QChar ch(characters[i].character);
+
+ //check if appearance of character is different from previous char
+ if ( characters[i].rendition != _lastRendition ||
+ characters[i].foregroundColor != _lastForeColor ||
+ characters[i].backgroundColor != _lastBackColor )
+ {
+ if ( _innerSpanOpen )
+ closeSpan(text);
+
+ _lastRendition = characters[i].rendition;
+ _lastForeColor = characters[i].foregroundColor;
+ _lastBackColor = characters[i].backgroundColor;
+
+ //build up style string
+ QString style;
+
+ if ( _lastRendition & RE_BOLD ||
+ (_colorTable && characters[i].isBold(_colorTable)) )
+ style.append("font-weight:bold;");
+
+
+ if ( _lastRendition & RE_UNDERLINE )
+ style.append("font-decoration:underline;");
+
+ //colours - a colour table must have been defined first
+ if ( _colorTable )
+ {
+ style.append( QString("color:%1;").arg(_lastForeColor.color(_colorTable).name() ) );
+
+ if (!characters[i].isTransparent(_colorTable))
+ {
+ style.append( QString("background-color:%1;").arg(_lastBackColor.color(_colorTable).name() ) );
+ }
+ }
+
+ //open the span with the current style
+ openSpan(text,style);
+ _innerSpanOpen = true;
+ }
+
+ //handle whitespace
+ if (ch.isSpace())
+ spaceCount++;
+ else
+ spaceCount = 0;
+
+
+ //output current character
+ if (spaceCount < 2)
+ {
+ //escape HTML tag characters and just display others as they are
+ if ( ch == '<' )
+ text.append("&lt;");
+ else if (ch == '>')
+ text.append("&gt;");
+ else
+ text.append(ch);
+ }
+ else
+ {
+ text.append("&nbsp;"); //HTML truncates multiple spaces, so use a space marker instead
+ }
+
+ }
+
+ //close any remaining open inner spans
+ if ( _innerSpanOpen )
+ closeSpan(text);
+
+ //start new line
+ text.append("<br>");
+
+ *_output << text;
+}
+
+void HTMLDecoder::openSpan(QString& text , const QString& style)
+{
+ text.append( QString("<span style=\"%1\">").arg(style) );
+}
+
+void HTMLDecoder::closeSpan(QString& text)
+{
+ text.append("</span>");
+}
+
+void HTMLDecoder::setColorTable(const ColorEntry* table)
+{
+ _colorTable = table;
+}
diff --git a/qtermwidget/TerminalCharacterDecoder.h b/qtermwidget/TerminalCharacterDecoder.h
new file mode 100644
index 0000000..5d97fc3
--- /dev/null
+++ b/qtermwidget/TerminalCharacterDecoder.h
@@ -0,0 +1,139 @@
+/*
+ This file is part of Konsole, an X terminal.
+
+ Copyright (C) 2006-7 by Robert Knight <robertknight@gmail.com>
+
+ Rewritten for QT4 by e_k <e_k at users.sourceforge.net>, Copyright (C)2008
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ 02110-1301 USA.
+*/
+
+#ifndef TERMINAL_CHARACTER_DECODER_H
+#define TERMINAL_CHARACTER_DECODER_H
+
+#include "Character.h"
+
+class QTextStream;
+
+namespace Konsole
+{
+
+/**
+ * Base class for terminal character decoders
+ *
+ * The decoder converts lines of terminal characters which consist of a unicode character, foreground
+ * and background colours and other appearance-related properties into text strings.
+ *
+ * Derived classes may produce either plain text with no other colour or appearance information, or
+ * they may produce text which incorporates these additional properties.
+ */
+class TerminalCharacterDecoder
+{
+public:
+ virtual ~TerminalCharacterDecoder() {}
+
+ /** Begin decoding characters. The resulting text is appended to @p output. */
+ virtual void begin(QTextStream* output) = 0;
+ /** End decoding. */
+ virtual void end() = 0;
+
+ /**
+ * Converts a line of terminal characters with associated properties into a text string
+ * and writes the string into an output QTextStream.
+ *
+ * @param characters An array of characters of length @p count.
+ * @param properties Additional properties which affect all characters in the line
+ * @param output The output stream which receives the decoded text
+ */
+ virtual void decodeLine(const Character* const characters,
+ int count,
+ LineProperty properties) = 0;
+};
+
+/**
+ * A terminal character decoder which produces plain text, ignoring colours and other appearance-related
+ * properties of the original characters.
+ */
+class PlainTextDecoder : public TerminalCharacterDecoder
+{
+public:
+ PlainTextDecoder();
+
+ /**
+ * Set whether trailing whitespace at the end of lines should be included
+ * in the output.
+ * Defaults to true.
+ */
+ void setTrailingWhitespace(bool enable);
+ /**
+ * Returns whether trailing whitespace at the end of lines is included
+ * in the output.
+ */
+ bool trailingWhitespace() const;
+
+ virtual void begin(QTextStream* output);
+ virtual void end();
+
+ virtual void decodeLine(const Character* const characters,
+ int count,
+ LineProperty properties);
+
+
+private:
+ QTextStream* _output;
+ bool _includeTrailingWhitespace;
+};
+
+/**
+ * A terminal character decoder which produces pretty HTML markup
+ */
+class HTMLDecoder : public TerminalCharacterDecoder
+{
+public:
+ /**
+ * Constructs an HTML decoder using a default black-on-white color scheme.
+ */
+ HTMLDecoder();
+
+ /**
+ * Sets the colour table which the decoder uses to produce the HTML colour codes in its
+ * output
+ */
+ void setColorTable( const ColorEntry* table );
+
+ virtual void decodeLine(const Character* const characters,
+ int count,
+ LineProperty properties);
+
+ virtual void begin(QTextStream* output);
+ virtual void end();
+
+private:
+ void openSpan(QString& text , const QString& style);
+ void closeSpan(QString& text);
+
+ QTextStream* _output;
+ const ColorEntry* _colorTable;
+ bool _innerSpanOpen;
+ quint8 _lastRendition;
+ CharacterColor _lastForeColor;
+ CharacterColor _lastBackColor;
+
+};
+
+}
+
+#endif
diff --git a/qtermwidget/TerminalDisplay.cpp b/qtermwidget/TerminalDisplay.cpp
new file mode 100644
index 0000000..1fcbd09
--- /dev/null
+++ b/qtermwidget/TerminalDisplay.cpp
@@ -0,0 +1,2724 @@
+/*
+ This file is part of Konsole, a terminal emulator for KDE.
+
+ Copyright (C) 2006-7 by Robert Knight <robertknight@gmail.com>
+ Copyright (C) 1997,1998 by Lars Doelle <lars.doelle@on-line.de>
+
+ Rewritten for QT4 by e_k <e_k at users.sourceforge.net>, Copyright (C)2008
+
+ 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.
+*/
+
+// Own
+#include "TerminalDisplay.h"
+
+// Qt
+#include <QtGui/QApplication>
+#include <QtGui/QBoxLayout>
+#include <QtGui/QClipboard>
+#include <QtGui/QKeyEvent>
+#include <QtCore/QEvent>
+#include <QtCore/QTime>
+#include <QtCore/QFile>
+#include <QtGui/QGridLayout>
+#include <QtGui/QLabel>
+#include <QtGui/QLayout>
+#include <QtGui/QPainter>
+#include <QtGui/QPixmap>
+#include <QtGui/QScrollBar>
+#include <QtGui/QStyle>
+#include <QtCore>
+#include <QtGui>
+
+#include "Filter.h"
+#include "konsole_wcwidth.h"
+#include "ScreenWindow.h"
+#include "TerminalCharacterDecoder.h"
+#include "ColorTables.h"
+
+using namespace Konsole;
+
+#ifndef loc
+#define loc(X,Y) ((Y)*_columns+(X))
+#endif
+
+#define yMouseScroll 1
+
+#define REPCHAR "ABCDEFGHIJKLMNOPQRSTUVWXYZ" \
+ "abcdefgjijklmnopqrstuvwxyz" \
+ "0123456789./+@"
+
+// scroll increment used when dragging selection at top/bottom of window.
+
+// static
+bool TerminalDisplay::_antialiasText = true;
+bool TerminalDisplay::HAVE_TRANSPARENCY = false;
+
+/* ------------------------------------------------------------------------- */
+/* */
+/* Colors */
+/* */
+/* ------------------------------------------------------------------------- */
+
+/* Note that we use ANSI color order (bgr), while IBMPC color order is (rgb)
+
+ Code 0 1 2 3 4 5 6 7
+ ----------- ------- ------- ------- ------- ------- ------- ------- -------
+ ANSI (bgr) Black Red Green Yellow Blue Magenta Cyan White
+ IBMPC (rgb) Black Blue Green Cyan Red Magenta Yellow White
+*/
+
+ScreenWindow* TerminalDisplay::screenWindow() const
+{
+ return _screenWindow;
+}
+void TerminalDisplay::setScreenWindow(ScreenWindow* window)
+{
+ // disconnect existing screen window if any
+ if ( _screenWindow )
+ {
+ disconnect( _screenWindow , 0 , this , 0 );
+ }
+
+ _screenWindow = window;
+
+ if ( window )
+ {
+//#warning "The order here is not specified - does it matter whether updateImage or updateLineProperties comes first?"
+ connect( _screenWindow , SIGNAL(outputChanged()) , this , SLOT(updateLineProperties()) );
+ connect( _screenWindow , SIGNAL(outputChanged()) , this , SLOT(updateImage()) );
+ window->setWindowLines(_lines);
+ }
+}
+
+const ColorEntry* TerminalDisplay::colorTable() const
+{
+ return _colorTable;
+}
+
+void TerminalDisplay::setColorTable(const ColorEntry table[])
+{
+ for (int i = 0; i < TABLE_COLORS; i++)
+ _colorTable[i] = table[i];
+
+ QPalette p = palette();
+ p.setColor( backgroundRole(), _colorTable[DEFAULT_BACK_COLOR].color );
+ setPalette( p );
+
+ // Avoid propagating the palette change to the scroll bar
+ _scrollBar->setPalette( QApplication::palette() );
+
+ update();
+}
+
+/* ------------------------------------------------------------------------- */
+/* */
+/* Font */
+/* */
+/* ------------------------------------------------------------------------- */
+
+/*
+ The VT100 has 32 special graphical characters. The usual vt100 extended
+ xterm fonts have these at 0x00..0x1f.
+
+ QT's iso mapping leaves 0x00..0x7f without any changes. But the graphicals
+ come in here as proper unicode characters.
+
+ We treat non-iso10646 fonts as VT100 extended and do the requiered mapping
+ from unicode to 0x00..0x1f. The remaining translation is then left to the
+ QCodec.
+*/
+
+static inline bool isLineChar(quint16 c) { return ((c & 0xFF80) == 0x2500);}
+static inline bool isLineCharString(const QString& string)
+{
+ return (string.length() > 0) && (isLineChar(string.at(0).unicode()));
+}
+
+
+// assert for i in [0..31] : vt100extended(vt100_graphics[i]) == i.
+
+unsigned short Konsole::vt100_graphics[32] =
+{ // 0/8 1/9 2/10 3/11 4/12 5/13 6/14 7/15
+ 0x0020, 0x25C6, 0x2592, 0x2409, 0x240c, 0x240d, 0x240a, 0x00b0,
+ 0x00b1, 0x2424, 0x240b, 0x2518, 0x2510, 0x250c, 0x2514, 0x253c,
+ 0xF800, 0xF801, 0x2500, 0xF803, 0xF804, 0x251c, 0x2524, 0x2534,
+ 0x252c, 0x2502, 0x2264, 0x2265, 0x03C0, 0x2260, 0x00A3, 0x00b7
+};
+
+void TerminalDisplay::fontChange(const QFont&)
+{
+ QFontMetrics fm(font());
+ _fontHeight = fm.height() + _lineSpacing;
+
+
+ // waba TerminalDisplay 1.123:
+ // "Base character width on widest ASCII character. This prevents too wide
+ // characters in the presence of double wide (e.g. Japanese) characters."
+ // Get the width from representative normal width characters
+ _fontWidth = qRound((double)fm.width(REPCHAR)/(double)strlen(REPCHAR));
+
+ _fixedFont = true;
+
+ int fw = fm.width(REPCHAR[0]);
+ for(unsigned int i=1; i< strlen(REPCHAR); i++)
+ {
+ if (fw != fm.width(REPCHAR[i]))
+ {
+ _fixedFont = false;
+ break;
+ }
+ }
+
+ if (_fontWidth < 1)
+ _fontWidth=1;
+
+ _fontAscent = fm.ascent();
+
+ emit changedFontMetricSignal( _fontHeight, _fontWidth );
+ propagateSize();
+ update();
+}
+
+void TerminalDisplay::setVTFont(const QFont& f)
+{
+ QFont font = f;
+
+ QFontMetrics metrics(font);
+
+ if ( metrics.height() < height() && metrics.maxWidth() < width() )
+ {
+ // hint that text should be drawn without anti-aliasing.
+ // depending on the user's font configuration, this may not be respected
+ if (!_antialiasText)
+ font.setStyleStrategy( QFont::NoAntialias );
+
+ // experimental optimization. Konsole assumes that the terminal is using a
+ // mono-spaced font, in which case kerning information should have an effect.
+ // Disabling kerning saves some computation when rendering text.
+ font.setKerning(false);
+
+ QWidget::setFont(font);
+ fontChange(font);
+ }
+}
+
+void TerminalDisplay::setFont(const QFont &)
+{
+ // ignore font change request if not coming from konsole itself
+}
+
+/* ------------------------------------------------------------------------- */
+/* */
+/* Constructor / Destructor */
+/* */
+/* ------------------------------------------------------------------------- */
+
+TerminalDisplay::TerminalDisplay(QWidget *parent)
+:QWidget(parent)
+,_screenWindow(0)
+,_allowBell(true)
+,_gridLayout(0)
+,_fontHeight(1)
+,_fontWidth(1)
+,_fontAscent(1)
+,_lines(1)
+,_columns(1)
+,_usedLines(1)
+,_usedColumns(1)
+,_contentHeight(1)
+,_contentWidth(1)
+,_image(0)
+,_randomSeed(0)
+,_resizing(false)
+,_terminalSizeHint(false)
+,_terminalSizeStartup(true)
+,_bidiEnabled(false)
+,_actSel(0)
+,_wordSelectionMode(false)
+,_lineSelectionMode(false)
+,_preserveLineBreaks(false)
+,_columnSelectionMode(false)
+,_scrollbarLocation(NoScrollBar)
+,_wordCharacters(":@-./_~")
+,_bellMode(SystemBeepBell)
+,_blinking(false)
+,_cursorBlinking(false)
+,_hasBlinkingCursor(false)
+,_ctrlDrag(false)
+,_tripleClickMode(SelectWholeLine)
+,_isFixedSize(false)
+,_possibleTripleClick(false)
+,_resizeWidget(0)
+,_resizeTimer(0)
+,_flowControlWarningEnabled(false)
+,_outputSuspendedLabel(0)
+,_lineSpacing(0)
+,_colorsInverted(false)
+,_blendColor(qRgba(0,0,0,0xff))
+,_filterChain(new TerminalImageFilterChain())
+,_cursorShape(BlockCursor)
+{
+ // terminal applications are not designed with Right-To-Left in mind,
+ // so the layout is forced to Left-To-Right
+ setLayoutDirection(Qt::LeftToRight);
+
+ // The offsets are not yet calculated.
+ // Do not calculate these too often to be more smoothly when resizing
+ // konsole in opaque mode.
+ _topMargin = DEFAULT_TOP_MARGIN;
+ _leftMargin = DEFAULT_LEFT_MARGIN;
+
+ // create scroll bar for scrolling output up and down
+ // set the scroll bar's slider to occupy the whole area of the scroll bar initially
+ _scrollBar = new QScrollBar(this);
+ setScroll(0,0);
+ _scrollBar->setCursor( Qt::ArrowCursor );
+ connect(_scrollBar, SIGNAL(valueChanged(int)), this,
+ SLOT(scrollBarPositionChanged(int)));
+
+ // setup timers for blinking cursor and text
+ _blinkTimer = new QTimer(this);
+ connect(_blinkTimer, SIGNAL(timeout()), this, SLOT(blinkEvent()));
+ _blinkCursorTimer = new QTimer(this);
+ connect(_blinkCursorTimer, SIGNAL(timeout()), this, SLOT(blinkCursorEvent()));
+
+// QCursor::setAutoHideCursor( this, true );
+
+ setUsesMouse(true);
+ setColorTable(whiteonblack_color_table);
+// setColorTable(blackonlightyellow_color_table);
+ setMouseTracking(true);
+
+ // Enable drag and drop
+ setAcceptDrops(true); // attempt
+ dragInfo.state = diNone;
+
+ setFocusPolicy( Qt::WheelFocus );
+
+ // enable input method support
+ setAttribute(Qt::WA_InputMethodEnabled, true);
+
+ // this is an important optimization, it tells Qt
+ // that TerminalDisplay will handle repainting its entire area.
+ setAttribute(Qt::WA_OpaquePaintEvent);
+
+ _gridLayout = new QGridLayout(this);
+ _gridLayout->setMargin(0);
+
+ setLayout( _gridLayout );
+
+ //set up a warning message when the user presses Ctrl+S to avoid confusion
+ connect( this,SIGNAL(flowControlKeyPressed(bool)),this,SLOT(outputSuspended(bool)) );
+}
+
+TerminalDisplay::~TerminalDisplay()
+{
+ qApp->removeEventFilter( this );
+
+ delete[] _image;
+
+ delete _gridLayout;
+ delete _outputSuspendedLabel;
+ delete _filterChain;
+}
+
+/* ------------------------------------------------------------------------- */
+/* */
+/* Display Operations */
+/* */
+/* ------------------------------------------------------------------------- */
+
+/**
+ A table for emulating the simple (single width) unicode drawing chars.
+ It represents the 250x - 257x glyphs. If it's zero, we can't use it.
+ if it's not, it's encoded as follows: imagine a 5x5 grid where the points are numbered
+ 0 to 24 left to top, top to bottom. Each point is represented by the corresponding bit.
+
+ Then, the pixels basically have the following interpretation:
+ _|||_
+ -...-
+ -...-
+ -...-
+ _|||_
+
+where _ = none
+ | = vertical line.
+ - = horizontal line.
+ */
+
+
+enum LineEncode
+{
+ TopL = (1<<1),
+ TopC = (1<<2),
+ TopR = (1<<3),
+
+ LeftT = (1<<5),
+ Int11 = (1<<6),
+ Int12 = (1<<7),
+ Int13 = (1<<8),
+ RightT = (1<<9),
+
+ LeftC = (1<<10),
+ Int21 = (1<<11),
+ Int22 = (1<<12),
+ Int23 = (1<<13),
+ RightC = (1<<14),
+
+ LeftB = (1<<15),
+ Int31 = (1<<16),
+ Int32 = (1<<17),
+ Int33 = (1<<18),
+ RightB = (1<<19),
+
+ BotL = (1<<21),
+ BotC = (1<<22),
+ BotR = (1<<23)
+};
+
+#include "LineFont.h"
+
+static void drawLineChar(QPainter& paint, int x, int y, int w, int h, uchar code)
+{
+ //Calculate cell midpoints, end points.
+ int cx = x + w/2;
+ int cy = y + h/2;
+ int ex = x + w - 1;
+ int ey = y + h - 1;
+
+ quint32 toDraw = LineChars[code];
+
+ //Top _lines:
+ if (toDraw & TopL)
+ paint.drawLine(cx-1, y, cx-1, cy-2);
+ if (toDraw & TopC)
+ paint.drawLine(cx, y, cx, cy-2);
+ if (toDraw & TopR)
+ paint.drawLine(cx+1, y, cx+1, cy-2);
+
+ //Bot _lines:
+ if (toDraw & BotL)
+ paint.drawLine(cx-1, cy+2, cx-1, ey);
+ if (toDraw & BotC)
+ paint.drawLine(cx, cy+2, cx, ey);
+ if (toDraw & BotR)
+ paint.drawLine(cx+1, cy+2, cx+1, ey);
+
+ //Left _lines:
+ if (toDraw & LeftT)
+ paint.drawLine(x, cy-1, cx-2, cy-1);
+ if (toDraw & LeftC)
+ paint.drawLine(x, cy, cx-2, cy);
+ if (toDraw & LeftB)
+ paint.drawLine(x, cy+1, cx-2, cy+1);
+
+ //Right _lines:
+ if (toDraw & RightT)
+ paint.drawLine(cx+2, cy-1, ex, cy-1);
+ if (toDraw & RightC)
+ paint.drawLine(cx+2, cy, ex, cy);
+ if (toDraw & RightB)
+ paint.drawLine(cx+2, cy+1, ex, cy+1);
+
+ //Intersection points.
+ if (toDraw & Int11)
+ paint.drawPoint(cx-1, cy-1);
+ if (toDraw & Int12)
+ paint.drawPoint(cx, cy-1);
+ if (toDraw & Int13)
+ paint.drawPoint(cx+1, cy-1);
+
+ if (toDraw & Int21)
+ paint.drawPoint(cx-1, cy);
+ if (toDraw & Int22)
+ paint.drawPoint(cx, cy);
+ if (toDraw & Int23)
+ paint.drawPoint(cx+1, cy);
+
+ if (toDraw & Int31)
+ paint.drawPoint(cx-1, cy+1);
+ if (toDraw & Int32)
+ paint.drawPoint(cx, cy+1);
+ if (toDraw & Int33)
+ paint.drawPoint(cx+1, cy+1);
+
+}
+
+void TerminalDisplay::drawLineCharString( QPainter& painter, int x, int y, const QString& str,
+ const Character* attributes)
+{
+ const QPen& currentPen = painter.pen();
+
+ if ( attributes->rendition & RE_BOLD )
+ {
+ QPen boldPen(currentPen);
+ boldPen.setWidth(3);
+ painter.setPen( boldPen );
+ }
+
+ for (int i=0 ; i < str.length(); i++)
+ {
+ uchar code = str[i].cell();
+ if (LineChars[code])
+ drawLineChar(painter, x + (_fontWidth*i), y, _fontWidth, _fontHeight, code);
+ }
+
+ painter.setPen( currentPen );
+}
+
+void TerminalDisplay::setKeyboardCursorShape(KeyboardCursorShape shape)
+{
+ _cursorShape = shape;
+}
+TerminalDisplay::KeyboardCursorShape TerminalDisplay::keyboardCursorShape() const
+{
+ return _cursorShape;
+}
+void TerminalDisplay::setKeyboardCursorColor(bool useForegroundColor, const QColor& color)
+{
+ if (useForegroundColor)
+ _cursorColor = QColor(); // an invalid color means that
+ // the foreground color of the
+ // current character should
+ // be used
+
+ else
+ _cursorColor = color;
+}
+QColor TerminalDisplay::keyboardCursorColor() const
+{
+ return _cursorColor;
+}
+
+void TerminalDisplay::setOpacity(qreal opacity)
+{
+ QColor color(_blendColor);
+ color.setAlphaF(opacity);
+
+ // enable automatic background filling to prevent the display
+ // flickering if there is no transparency
+ if ( color.alpha() == 255 )
+ {
+ setAutoFillBackground(true);
+ }
+ else
+ {
+ setAutoFillBackground(false);
+ }
+
+ _blendColor = color.rgba();
+}
+
+void TerminalDisplay::drawBackground(QPainter& painter, const QRect& rect, const QColor& backgroundColor, bool useOpacitySetting )
+{
+ // the area of the widget showing the contents of the terminal display is drawn
+ // using the background color from the color scheme set with setColorTable()
+ //
+ // the area of the widget behind the scroll-bar is drawn using the background
+ // brush from the scroll-bar's palette, to give the effect of the scroll-bar
+ // being outside of the terminal display and visual consistency with other KDE
+ // applications.
+ //
+ QRect scrollBarArea = _scrollBar->isVisible() ?
+ rect.intersected(_scrollBar->geometry()) :
+ QRect();
+ QRegion contentsRegion = QRegion(rect).subtracted(scrollBarArea);
+ QRect contentsRect = contentsRegion.boundingRect();
+
+ if ( HAVE_TRANSPARENCY && qAlpha(_blendColor) < 0xff && useOpacitySetting )
+ {
+ QColor color(backgroundColor);
+ color.setAlpha(qAlpha(_blendColor));
+
+ painter.save();
+ painter.setCompositionMode(QPainter::CompositionMode_Source);
+ painter.fillRect(contentsRect, color);
+ painter.restore();
+ }
+ else {
+ painter.fillRect(contentsRect, backgroundColor);
+ }
+
+ painter.fillRect(scrollBarArea,_scrollBar->palette().background());
+}
+
+void TerminalDisplay::drawCursor(QPainter& painter,
+ const QRect& rect,
+ const QColor& foregroundColor,
+ const QColor& /*backgroundColor*/,
+ bool& invertCharacterColor)
+{
+ QRect cursorRect = rect;
+ cursorRect.setHeight(_fontHeight - _lineSpacing - 1);
+
+ if (!_cursorBlinking)
+ {
+ if ( _cursorColor.isValid() )
+ painter.setPen(_cursorColor);
+ else {
+ painter.setPen(foregroundColor);
+ }
+
+ if ( _cursorShape == BlockCursor )
+ {
+ // draw the cursor outline, adjusting the area so that
+ // it is draw entirely inside 'rect'
+ int penWidth = qMax(1,painter.pen().width());
+
+ painter.drawRect(cursorRect.adjusted(penWidth/2,
+ penWidth/2,
+ - penWidth/2 - penWidth%2,
+ - penWidth/2 - penWidth%2));
+ if ( hasFocus() )
+ {
+ painter.fillRect(cursorRect, _cursorColor.isValid() ? _cursorColor : foregroundColor);
+
+ if ( !_cursorColor.isValid() )
+ {
+ // invert the colour used to draw the text to ensure that the character at
+ // the cursor position is readable
+ invertCharacterColor = true;
+ }
+ }
+ }
+ else if ( _cursorShape == UnderlineCursor )
+ painter.drawLine(cursorRect.left(),
+ cursorRect.bottom(),
+ cursorRect.right(),
+ cursorRect.bottom());
+ else if ( _cursorShape == IBeamCursor )
+ painter.drawLine(cursorRect.left(),
+ cursorRect.top(),
+ cursorRect.left(),
+ cursorRect.bottom());
+
+ }
+}
+
+void TerminalDisplay::drawCharacters(QPainter& painter,
+ const QRect& rect,
+ const QString& text,
+ const Character* style,
+ bool invertCharacterColor)
+{
+ // don't draw text which is currently blinking
+ if ( _blinking && (style->rendition & RE_BLINK) )
+ return;
+
+ // setup bold and underline
+ bool useBold = style->rendition & RE_BOLD || style->isBold(_colorTable) || font().bold();
+ bool useUnderline = style->rendition & RE_UNDERLINE || font().underline();
+
+ QFont font = painter.font();
+ if ( font.bold() != useBold
+ || font.underline() != useUnderline )
+ {
+ font.setBold(useBold);
+ font.setUnderline(useUnderline);
+ painter.setFont(font);
+ }
+
+ const CharacterColor& textColor = ( invertCharacterColor ? style->backgroundColor : style->foregroundColor );
+ const QColor color = textColor.color(_colorTable);
+
+ QPen pen = painter.pen();
+ if ( pen.color() != color )
+ {
+ pen.setColor(color);
+ painter.setPen(color);
+ }
+ // draw text
+ if ( isLineCharString(text) ) {
+ drawLineCharString(painter,rect.x(),rect.y(),text,style);
+ }
+ else
+ {
+ // the drawText(rect,flags,string) overload is used here with null flags
+ // instead of drawText(rect,string) because the (rect,string) overload causes
+ // the application's default layout direction to be used instead of
+ // the widget-specific layout direction, which should always be
+ // Qt::LeftToRight for this widget
+ painter.drawText(rect,0,text);
+ }
+}
+
+void TerminalDisplay::drawTextFragment(QPainter& painter ,
+ const QRect& rect,
+ const QString& text,
+ const Character* style)
+{
+ painter.save();
+
+ // setup painter
+ const QColor foregroundColor = style->foregroundColor.color(_colorTable);
+ const QColor backgroundColor = style->backgroundColor.color(_colorTable);
+
+ // draw background if different from the display's background color
+ if ( backgroundColor != palette().background().color() )
+ drawBackground(painter,rect,backgroundColor, false /* do not use transparency */);
+
+ // draw cursor shape if the current character is the cursor
+ // this may alter the foreground and background colors
+ bool invertCharacterColor = false;
+
+ if ( style->rendition & RE_CURSOR )
+ drawCursor(painter,rect,foregroundColor,backgroundColor,invertCharacterColor);
+ // draw text
+ drawCharacters(painter,rect,text,style,invertCharacterColor);
+
+ painter.restore();
+}
+
+void TerminalDisplay::setRandomSeed(uint randomSeed) { _randomSeed = randomSeed; }
+uint TerminalDisplay::randomSeed() const { return _randomSeed; }
+
+#if 0
+/*!
+ Set XIM Position
+*/
+void TerminalDisplay::setCursorPos(const int curx, const int cury)
+{
+ QPoint tL = contentsRect().topLeft();
+ int tLx = tL.x();
+ int tLy = tL.y();
+
+ int xpos, ypos;
+ ypos = _topMargin + tLy + _fontHeight*(cury-1) + _fontAscent;
+ xpos = _leftMargin + tLx + _fontWidth*curx;
+ //setMicroFocusHint(xpos, ypos, 0, _fontHeight); //### ???
+ // fprintf(stderr, "x/y = %d/%d\txpos/ypos = %d/%d\n", curx, cury, xpos, ypos);
+ _cursorLine = cury;
+ _cursorCol = curx;
+}
+#endif
+
+// scrolls the image by 'lines', down if lines > 0 or up otherwise.
+//
+// the terminal emulation keeps track of the scrolling of the character
+// image as it receives input, and when the view is updated, it calls scrollImage()
+// with the final scroll amount. this improves performance because scrolling the
+// display is much cheaper than re-rendering all the text for the
+// part of the image which has moved up or down.
+// Instead only new lines have to be drawn
+//
+// note: it is important that the area of the display which is
+// scrolled aligns properly with the character grid -
+// which has a top left point at (_leftMargin,_topMargin) ,
+// a cell width of _fontWidth and a cell height of _fontHeight).
+void TerminalDisplay::scrollImage(int lines , const QRect& screenWindowRegion)
+{
+ // if the flow control warning is enabled this will interfere with the
+ // scrolling optimisations and cause artifacts. the simple solution here
+ // is to just disable the optimisation whilst it is visible
+ if ( _outputSuspendedLabel && _outputSuspendedLabel->isVisible() ) {
+ return;
+ }
+
+ // constrain the region to the display
+ // the bottom of the region is capped to the number of lines in the display's
+ // internal image - 2, so that the height of 'region' is strictly less
+ // than the height of the internal image.
+ QRect region = screenWindowRegion;
+ region.setBottom( qMin(region.bottom(),this->_lines-2) );
+
+ if ( lines == 0
+ || _image == 0
+ || !region.isValid()
+ || (region.top() + abs(lines)) >= region.bottom()
+ || this->_lines <= region.height() ) return;
+
+ QRect scrollRect;
+
+ void* firstCharPos = &_image[ region.top() * this->_columns ];
+ void* lastCharPos = &_image[ (region.top() + abs(lines)) * this->_columns ];
+
+ int top = _topMargin + (region.top() * _fontHeight);
+ int linesToMove = region.height() - abs(lines);
+ int bytesToMove = linesToMove *
+ this->_columns *
+ sizeof(Character);
+
+ Q_ASSERT( linesToMove > 0 );
+ Q_ASSERT( bytesToMove > 0 );
+
+ //scroll internal image
+ if ( lines > 0 )
+ {
+ // check that the memory areas that we are going to move are valid
+ Q_ASSERT( (char*)lastCharPos + bytesToMove <
+ (char*)(_image + (this->_lines * this->_columns)) );
+
+ Q_ASSERT( (lines*this->_columns) < _imageSize );
+
+ //scroll internal image down
+ memmove( firstCharPos , lastCharPos , bytesToMove );
+
+ //set region of display to scroll, making sure that
+ //the region aligns correctly to the character grid
+ scrollRect = QRect( _leftMargin , top,
+ this->_usedColumns * _fontWidth ,
+ linesToMove * _fontHeight );
+ }
+ else
+ {
+ // check that the memory areas that we are going to move are valid
+ Q_ASSERT( (char*)firstCharPos + bytesToMove <
+ (char*)(_image + (this->_lines * this->_columns)) );
+
+ //scroll internal image up
+ memmove( lastCharPos , firstCharPos , bytesToMove );
+
+ //set region of the display to scroll, making sure that
+ //the region aligns correctly to the character grid
+ QPoint topPoint( _leftMargin , top + abs(lines)*_fontHeight );
+
+ scrollRect = QRect( topPoint ,
+ QSize( this->_usedColumns*_fontWidth ,
+ linesToMove * _fontHeight ));
+ }
+
+ //scroll the display vertically to match internal _image
+ scroll( 0 , _fontHeight * (-lines) , scrollRect );
+}
+
+QRegion TerminalDisplay::hotSpotRegion() const
+{
+ QRegion region;
+ foreach( Filter::HotSpot* hotSpot , _filterChain->hotSpots() )
+ {
+ QRect rect;
+ rect.setLeft(hotSpot->startColumn());
+ rect.setTop(hotSpot->startLine());
+ rect.setRight(hotSpot->endColumn());
+ rect.setBottom(hotSpot->endLine());
+
+ region |= imageToWidget(rect);
+ }
+ return region;
+}
+
+void TerminalDisplay::processFilters()
+{
+ if (!_screenWindow)
+ return;
+
+ QRegion preUpdateHotSpots = hotSpotRegion();
+
+ // use _screenWindow->getImage() here rather than _image because
+ // other classes may call processFilters() when this display's
+ // ScreenWindow emits a scrolled() signal - which will happen before
+ // updateImage() is called on the display and therefore _image is
+ // out of date at this point
+ _filterChain->setImage( _screenWindow->getImage(),
+ _screenWindow->windowLines(),
+ _screenWindow->windowColumns(),
+ _screenWindow->getLineProperties() );
+ _filterChain->process();
+
+ QRegion postUpdateHotSpots = hotSpotRegion();
+
+ update( preUpdateHotSpots | postUpdateHotSpots );
+}
+
+void TerminalDisplay::updateImage()
+{
+ if ( !_screenWindow )
+ return;
+
+ // optimization - scroll the existing image where possible and
+ // avoid expensive text drawing for parts of the image that
+ // can simply be moved up or down
+ scrollImage( _screenWindow->scrollCount() ,
+ _screenWindow->scrollRegion() );
+ _screenWindow->resetScrollCount();
+
+ Character* const newimg = _screenWindow->getImage();
+ int lines = _screenWindow->windowLines();
+ int columns = _screenWindow->windowColumns();
+
+ setScroll( _screenWindow->currentLine() , _screenWindow->lineCount() );
+
+ if (!_image)
+ updateImageSize(); // Create _image
+
+ Q_ASSERT( this->_usedLines <= this->_lines );
+ Q_ASSERT( this->_usedColumns <= this->_columns );
+
+ int y,x,len;
+
+ QPoint tL = contentsRect().topLeft();
+
+ int tLx = tL.x();
+ int tLy = tL.y();
+ _hasBlinker = false;
+
+ CharacterColor cf; // undefined
+ CharacterColor _clipboard; // undefined
+ int cr = -1; // undefined
+
+ const int linesToUpdate = qMin(this->_lines, qMax(0,lines ));
+ const int columnsToUpdate = qMin(this->_columns,qMax(0,columns));
+
+ QChar *disstrU = new QChar[columnsToUpdate];
+ char *dirtyMask = new char[columnsToUpdate+2];
+ QRegion dirtyRegion;
+
+ // debugging variable, this records the number of lines that are found to
+ // be 'dirty' ( ie. have changed from the old _image to the new _image ) and
+ // which therefore need to be repainted
+ int dirtyLineCount = 0;
+
+ for (y = 0; y < linesToUpdate; y++)
+ {
+ const Character* currentLine = &_image[y*this->_columns];
+ const Character* const newLine = &newimg[y*columns];
+
+ bool updateLine = false;
+
+ // The dirty mask indicates which characters need repainting. We also
+ // mark surrounding neighbours dirty, in case the character exceeds
+ // its cell boundaries
+ memset(dirtyMask, 0, columnsToUpdate+2);
+
+ for( x = 0 ; x < columnsToUpdate ; x++)
+ {
+ if ( newLine[x] != currentLine[x] )
+ {
+ dirtyMask[x] = true;
+ }
+ }
+
+ if (!_resizing) // not while _resizing, we're expecting a paintEvent
+ for (x = 0; x < columnsToUpdate; x++)
+ {
+ _hasBlinker |= (newLine[x].rendition & RE_BLINK);
+
+ // Start drawing if this character or the next one differs.
+ // We also take the next one into account to handle the situation
+ // where characters exceed their cell width.
+ if (dirtyMask[x])
+ {
+ quint16 c = newLine[x+0].character;
+ if ( !c )
+ continue;
+ int p = 0;
+ disstrU[p++] = c; //fontMap(c);
+ bool lineDraw = isLineChar(c);
+ bool doubleWidth = (x+1 == columnsToUpdate) ? false : (newLine[x+1].character == 0);
+ cr = newLine[x].rendition;
+ _clipboard = newLine[x].backgroundColor;
+ if (newLine[x].foregroundColor != cf) cf = newLine[x].foregroundColor;
+ int lln = columnsToUpdate - x;
+ for (len = 1; len < lln; len++)
+ {
+ const Character& ch = newLine[x+len];
+
+ if (!ch.character)
+ continue; // Skip trailing part of multi-col chars.
+
+ bool nextIsDoubleWidth = (x+len+1 == columnsToUpdate) ? false : (newLine[x+len+1].character == 0);
+
+ if ( ch.foregroundColor != cf ||
+ ch.backgroundColor != _clipboard ||
+ ch.rendition != cr ||
+ !dirtyMask[x+len] ||
+ isLineChar(c) != lineDraw ||
+ nextIsDoubleWidth != doubleWidth )
+ break;
+
+ disstrU[p++] = c; //fontMap(c);
+ }
+
+ QString unistr(disstrU, p);
+
+ bool saveFixedFont = _fixedFont;
+ if (lineDraw)
+ _fixedFont = false;
+ if (doubleWidth)
+ _fixedFont = false;
+
+ updateLine = true;
+
+ _fixedFont = saveFixedFont;
+ x += len - 1;
+ }
+
+ }
+
+ //both the top and bottom halves of double height _lines must always be redrawn
+ //although both top and bottom halves contain the same characters, only
+ //the top one is actually
+ //drawn.
+ if (_lineProperties.count() > y)
+ updateLine |= (_lineProperties[y] & LINE_DOUBLEHEIGHT);
+
+ // if the characters on the line are different in the old and the new _image
+ // then this line must be repainted.
+ if (updateLine)
+ {
+ dirtyLineCount++;
+
+ // add the area occupied by this line to the region which needs to be
+ // repainted
+ QRect dirtyRect = QRect( _leftMargin+tLx ,
+ _topMargin+tLy+_fontHeight*y ,
+ _fontWidth * columnsToUpdate ,
+ _fontHeight );
+
+ dirtyRegion |= dirtyRect;
+ }
+
+ // replace the line of characters in the old _image with the
+ // current line of the new _image
+ memcpy((void*)currentLine,(const void*)newLine,columnsToUpdate*sizeof(Character));
+ }
+
+ // if the new _image is smaller than the previous _image, then ensure that the area
+ // outside the new _image is cleared
+ if ( linesToUpdate < _usedLines )
+ {
+ dirtyRegion |= QRect( _leftMargin+tLx ,
+ _topMargin+tLy+_fontHeight*linesToUpdate ,
+ _fontWidth * this->_columns ,
+ _fontHeight * (_usedLines-linesToUpdate) );
+ }
+ _usedLines = linesToUpdate;
+
+ if ( columnsToUpdate < _usedColumns )
+ {
+ dirtyRegion |= QRect( _leftMargin+tLx+columnsToUpdate*_fontWidth ,
+ _topMargin+tLy ,
+ _fontWidth * (_usedColumns-columnsToUpdate) ,
+ _fontHeight * this->_lines );
+ }
+ _usedColumns = columnsToUpdate;
+
+ dirtyRegion |= _inputMethodData.previousPreeditRect;
+
+ // update the parts of the display which have changed
+ update(dirtyRegion);
+
+ if ( _hasBlinker && !_blinkTimer->isActive()) _blinkTimer->start( BLINK_DELAY );
+ if (!_hasBlinker && _blinkTimer->isActive()) { _blinkTimer->stop(); _blinking = false; }
+ delete[] dirtyMask;
+ delete[] disstrU;
+
+}
+
+void TerminalDisplay::showResizeNotification()
+{
+ if (_terminalSizeHint && isVisible())
+ {
+ if (_terminalSizeStartup) {
+ _terminalSizeStartup=false;
+ return;
+ }
+ if (!_resizeWidget)
+ {
+ _resizeWidget = new QLabel(("Size: XXX x XXX"), this);
+ _resizeWidget->setMinimumWidth(_resizeWidget->fontMetrics().width(("Size: XXX x XXX")));
+ _resizeWidget->setMinimumHeight(_resizeWidget->sizeHint().height());
+ _resizeWidget->setAlignment(Qt::AlignCenter);
+
+ _resizeWidget->setStyleSheet("background-color:palette(window);border-style:solid;border-width:1px;border-color:palette(dark)");
+
+ _resizeTimer = new QTimer(this);
+ _resizeTimer->setSingleShot(true);
+ connect(_resizeTimer, SIGNAL(timeout()), _resizeWidget, SLOT(hide()));
+
+ }
+ QString sizeStr;
+ sizeStr.sprintf("Size: %d x %d", _columns, _lines);
+ _resizeWidget->setText(sizeStr);
+ _resizeWidget->move((width()-_resizeWidget->width())/2,
+ (height()-_resizeWidget->height())/2+20);
+ _resizeWidget->show();
+ _resizeTimer->start(1000);
+ }
+}
+
+void TerminalDisplay::setBlinkingCursor(bool blink)
+{
+ _hasBlinkingCursor=blink;
+
+ if (blink && !_blinkCursorTimer->isActive())
+ _blinkCursorTimer->start(BLINK_DELAY);
+
+ if (!blink && _blinkCursorTimer->isActive())
+ {
+ _blinkCursorTimer->stop();
+ if (_cursorBlinking)
+ blinkCursorEvent();
+ else
+ _cursorBlinking = false;
+ }
+}
+
+void TerminalDisplay::paintEvent( QPaintEvent* pe )
+{
+//qDebug("%s %d paintEvent", __FILE__, __LINE__);
+ QPainter paint(this);
+
+ foreach (QRect rect, (pe->region() & contentsRect()).rects())
+ {
+ drawBackground(paint,rect,palette().background().color(), true /* use opacity setting */);
+ drawContents(paint, rect);
+ }
+// drawBackground(paint,contentsRect(),palette().background().color(), true /* use opacity setting */);
+// drawContents(paint, contentsRect());
+ drawInputMethodPreeditString(paint,preeditRect());
+ paintFilters(paint);
+
+ paint.end();
+}
+
+QPoint TerminalDisplay::cursorPosition() const
+{
+ if (_screenWindow)
+ return _screenWindow->cursorPosition();
+ else
+ return QPoint(0,0);
+}
+
+QRect TerminalDisplay::preeditRect() const
+{
+ const int preeditLength = string_width(_inputMethodData.preeditString);
+
+ if ( preeditLength == 0 )
+ return QRect();
+
+ return QRect(_leftMargin + _fontWidth*cursorPosition().x(),
+ _topMargin + _fontHeight*cursorPosition().y(),
+ _fontWidth*preeditLength,
+ _fontHeight);
+}
+
+void TerminalDisplay::drawInputMethodPreeditString(QPainter& painter , const QRect& rect)
+{
+ if ( _inputMethodData.preeditString.isEmpty() ) {
+ return;
+ }
+ const QPoint cursorPos = cursorPosition();
+
+ bool invertColors = false;
+ const QColor background = _colorTable[DEFAULT_BACK_COLOR].color;
+ const QColor foreground = _colorTable[DEFAULT_FORE_COLOR].color;
+ const Character* style = &_image[loc(cursorPos.x(),cursorPos.y())];
+
+ drawBackground(painter,rect,background,true);
+ drawCursor(painter,rect,foreground,background,invertColors);
+ drawCharacters(painter,rect,_inputMethodData.preeditString,style,invertColors);
+
+ _inputMethodData.previousPreeditRect = rect;
+}
+
+FilterChain* TerminalDisplay::filterChain() const
+{
+ return _filterChain;
+}
+
+void TerminalDisplay::paintFilters(QPainter& painter)
+{
+//qDebug("%s %d paintFilters", __FILE__, __LINE__);
+
+ // get color of character under mouse and use it to draw
+ // lines for filters
+ QPoint cursorPos = mapFromGlobal(QCursor::pos());
+ int cursorLine;
+ int cursorColumn;
+ getCharacterPosition( cursorPos , cursorLine , cursorColumn );
+ Character cursorCharacter = _image[loc(cursorColumn,cursorLine)];
+
+ painter.setPen( QPen(cursorCharacter.foregroundColor.color(colorTable())) );
+
+ // iterate over hotspots identified by the display's currently active filters
+ // and draw appropriate visuals to indicate the presence of the hotspot
+
+ QList<Filter::HotSpot*> spots = _filterChain->hotSpots();
+ QListIterator<Filter::HotSpot*> iter(spots);
+ while (iter.hasNext())
+ {
+ Filter::HotSpot* spot = iter.next();
+
+ for ( int line = spot->startLine() ; line <= spot->endLine() ; line++ )
+ {
+ int startColumn = 0;
+ int endColumn = _columns-1; // TODO use number of _columns which are actually
+ // occupied on this line rather than the width of the
+ // display in _columns
+
+ // ignore whitespace at the end of the lines
+ while ( QChar(_image[loc(endColumn,line)].character).isSpace() && endColumn > 0 )
+ endColumn--;
+
+ // increment here because the column which we want to set 'endColumn' to
+ // is the first whitespace character at the end of the line
+ endColumn++;
+
+ if ( line == spot->startLine() )
+ startColumn = spot->startColumn();
+ if ( line == spot->endLine() )
+ endColumn = spot->endColumn();
+
+ // subtract one pixel from
+ // the right and bottom so that
+ // we do not overdraw adjacent
+ // hotspots
+ //
+ // subtracting one pixel from all sides also prevents an edge case where
+ // moving the mouse outside a link could still leave it underlined
+ // because the check below for the position of the cursor
+ // finds it on the border of the target area
+ QRect r;
+ r.setCoords( startColumn*_fontWidth + 1, line*_fontHeight + 1,
+ endColumn*_fontWidth - 1, (line+1)*_fontHeight - 1 );
+
+ // Underline link hotspots
+ if ( spot->type() == Filter::HotSpot::Link )
+ {
+ QFontMetrics metrics(font());
+
+ // find the baseline (which is the invisible line that the characters in the font sit on,
+ // with some having tails dangling below)
+ int baseline = r.bottom() - metrics.descent();
+ // find the position of the underline below that
+ int underlinePos = baseline + metrics.underlinePos();
+
+ if ( r.contains( mapFromGlobal(QCursor::pos()) ) )
+ painter.drawLine( r.left() , underlinePos ,
+ r.right() , underlinePos );
+ }
+ // Marker hotspots simply have a transparent rectanglular shape
+ // drawn on top of them
+ else if ( spot->type() == Filter::HotSpot::Marker )
+ {
+ //TODO - Do not use a hardcoded colour for this
+ painter.fillRect(r,QBrush(QColor(255,0,0,120)));
+ }
+ }
+ }
+}
+void TerminalDisplay::drawContents(QPainter &paint, const QRect &rect)
+{
+//qDebug("%s %d drawContents and rect x=%d y=%d w=%d h=%d", __FILE__, __LINE__, rect.x(), rect.y(),rect.width(),rect.height());
+
+ QPoint tL = contentsRect().topLeft();
+// int tLx = tL.x();
+ int tLy = tL.y();
+
+ int tLx = (_contentWidth - _usedColumns * _fontWidth)/2;
+// int tLy = (_contentHeight - _usedLines * _fontHeight)/2;
+//qDebug("%d %d %d %d", tLx, tLy, _contentWidth, _usedColumns * _fontWidth);
+
+ int lux = qMin(_usedColumns-1, qMax(0,(rect.left() - tLx - _leftMargin ) / _fontWidth));
+ int luy = qMin(_usedLines-1, qMax(0, (rect.top() - tLy - _topMargin ) / _fontHeight));
+ int rlx = qMin(_usedColumns-1, qMax(0, (rect.right() - tLx - _leftMargin ) / _fontWidth));
+ int rly = qMin(_usedLines-1, qMax(0, (rect.bottom() - tLy - _topMargin ) / _fontHeight));
+
+ const int bufferSize = _usedColumns;
+ QChar *disstrU = new QChar[bufferSize];
+ for (int y = luy; y <= rly; y++)
+ {
+ quint16 c = _image[loc(lux,y)].character;
+ int x = lux;
+ if(!c && x)
+ x--; // Search for start of multi-column character
+ for (; x <= rlx; x++)
+ {
+ int len = 1;
+ int p = 0;
+
+ // is this a single character or a sequence of characters ?
+ if ( _image[loc(x,y)].rendition & RE_EXTENDED_CHAR )
+ {
+ // sequence of characters
+ ushort extendedCharLength = 0;
+ ushort* chars = ExtendedCharTable::instance
+ .lookupExtendedChar(_image[loc(x,y)].charSequence,extendedCharLength);
+ for ( int index = 0 ; index < extendedCharLength ; index++ )
+ {
+ Q_ASSERT( p < bufferSize );
+ disstrU[p++] = chars[index];
+ }
+ }
+ else
+ {
+ // single character
+ c = _image[loc(x,y)].character;
+ if (c)
+ {
+ Q_ASSERT( p < bufferSize );
+ disstrU[p++] = c; //fontMap(c);
+ }
+ }
+
+ bool lineDraw = isLineChar(c);
+ bool doubleWidth = (_image[ qMin(loc(x,y)+1,_imageSize) ].character == 0);
+ CharacterColor currentForeground = _image[loc(x,y)].foregroundColor;
+ CharacterColor currentBackground = _image[loc(x,y)].backgroundColor;
+ quint8 currentRendition = _image[loc(x,y)].rendition;
+
+ while (x+len <= rlx &&
+ _image[loc(x+len,y)].foregroundColor == currentForeground &&
+ _image[loc(x+len,y)].backgroundColor == currentBackground &&
+ _image[loc(x+len,y)].rendition == currentRendition &&
+ (_image[ qMin(loc(x+len,y)+1,_imageSize) ].character == 0) == doubleWidth &&
+ isLineChar( c = _image[loc(x+len,y)].character) == lineDraw) // Assignment!
+ {
+ if (c)
+ disstrU[p++] = c; //fontMap(c);
+ if (doubleWidth) // assert((_image[loc(x+len,y)+1].character == 0)), see above if condition
+ len++; // Skip trailing part of multi-column character
+ len++;
+ }
+ if ((x+len < _usedColumns) && (!_image[loc(x+len,y)].character))
+ len++; // Adjust for trailing part of multi-column character
+
+ bool save__fixedFont = _fixedFont;
+ if (lineDraw)
+ _fixedFont = false;
+ if (doubleWidth)
+ _fixedFont = false;
+ QString unistr(disstrU,p);
+
+ if (y < _lineProperties.size())
+ {
+ if (_lineProperties[y] & LINE_DOUBLEWIDTH) {
+ paint.scale(2,1);
+ }
+
+ if (_lineProperties[y] & LINE_DOUBLEHEIGHT) {
+ paint.scale(1,2);
+ }
+ }
+
+ //calculate the area in which the text will be drawn
+ QRect textArea = QRect( _leftMargin+tLx+_fontWidth*x ,
+ _topMargin+tLy+_fontHeight*y ,
+ _fontWidth*len,
+ _fontHeight);
+
+ //move the calculated area to take account of scaling applied to the painter.
+ //the position of the area from the origin (0,0) is scaled
+ //by the opposite of whatever
+ //transformation has been applied to the painter. this ensures that
+ //painting does actually start from textArea.topLeft()
+ //(instead of textArea.topLeft() * painter-scale)
+ QMatrix inverted = paint.matrix().inverted();
+// textArea.moveTopLeft( inverted.map(textArea.topLeft()) );
+ textArea.moveCenter( inverted.map(textArea.center()) );
+
+
+ //paint text fragment
+ drawTextFragment( paint,
+ textArea,
+ unistr,
+ &_image[loc(x,y)] ); //,
+ //0,
+ //!_isPrinting );
+
+ _fixedFont = save__fixedFont;
+
+ //reset back to single-width, single-height _lines
+ paint.resetMatrix();
+
+ if (y < _lineProperties.size()-1)
+ {
+ //double-height _lines are represented by two adjacent _lines
+ //containing the same characters
+ //both _lines will have the LINE_DOUBLEHEIGHT attribute.
+ //If the current line has the LINE_DOUBLEHEIGHT attribute,
+ //we can therefore skip the next line
+ if (_lineProperties[y] & LINE_DOUBLEHEIGHT)
+ y++;
+ }
+
+ x += len - 1;
+ }
+ }
+ delete [] disstrU;
+}
+
+void TerminalDisplay::blinkEvent()
+{
+ _blinking = !_blinking;
+
+ //TODO: Optimise to only repaint the areas of the widget
+ // where there is blinking text
+ // rather than repainting the whole widget.
+ update();
+}
+
+QRect TerminalDisplay::imageToWidget(const QRect& imageArea) const
+{
+//qDebug("%s %d imageToWidget", __FILE__, __LINE__);
+ QRect result;
+ result.setLeft( _leftMargin + _fontWidth * imageArea.left() );
+ result.setTop( _topMargin + _fontHeight * imageArea.top() );
+ result.setWidth( _fontWidth * imageArea.width() );
+ result.setHeight( _fontHeight * imageArea.height() );
+
+ return result;
+}
+
+void TerminalDisplay::blinkCursorEvent()
+{
+ _cursorBlinking = !_cursorBlinking;
+
+ QRect cursorRect = imageToWidget( QRect(cursorPosition(),QSize(1,1)) );
+
+ update(cursorRect);
+}
+
+/* ------------------------------------------------------------------------- */
+/* */
+/* Resizing */
+/* */
+/* ------------------------------------------------------------------------- */
+
+void TerminalDisplay::resizeEvent(QResizeEvent*)
+{
+ updateImageSize();
+}
+
+void TerminalDisplay::propagateSize()
+{
+ if (_isFixedSize)
+ {
+ setSize(_columns, _lines);
+ QWidget::setFixedSize(sizeHint());
+ parentWidget()->adjustSize();
+ parentWidget()->setFixedSize(parentWidget()->sizeHint());
+ return;
+ }
+ if (_image)
+ updateImageSize();
+}
+
+void TerminalDisplay::updateImageSize()
+{
+//qDebug("%s %d updateImageSize", __FILE__, __LINE__);
+ Character* oldimg = _image;
+ int oldlin = _lines;
+ int oldcol = _columns;
+
+ makeImage();
+
+
+ // copy the old image to reduce flicker
+ int lines = qMin(oldlin,_lines);
+ int columns = qMin(oldcol,_columns);
+
+ if (oldimg)
+ {
+ for (int line = 0; line < lines; line++)
+ {
+ memcpy((void*)&_image[_columns*line],
+ (void*)&oldimg[oldcol*line],columns*sizeof(Character));
+ }
+ delete[] oldimg;
+ }
+
+ if (_screenWindow)
+ _screenWindow->setWindowLines(_lines);
+
+ _resizing = (oldlin!=_lines) || (oldcol!=_columns);
+
+ if ( _resizing )
+ {
+ showResizeNotification();
+ emit changedContentSizeSignal(_contentHeight, _contentWidth); // expose resizeEvent
+ }
+
+ _resizing = false;
+}
+
+//showEvent and hideEvent are reimplemented here so that it appears to other classes that the
+//display has been resized when the display is hidden or shown.
+//
+//this allows
+//TODO: Perhaps it would be better to have separate signals for show and hide instead of using
+//the same signal as the one for a content size change
+void TerminalDisplay::showEvent(QShowEvent*)
+{
+ emit changedContentSizeSignal(_contentHeight,_contentWidth);
+}
+void TerminalDisplay::hideEvent(QHideEvent*)
+{
+ emit changedContentSizeSignal(_contentHeight,_contentWidth);
+}
+
+/* ------------------------------------------------------------------------- */
+/* */
+/* Scrollbar */
+/* */
+/* ------------------------------------------------------------------------- */
+
+void TerminalDisplay::scrollBarPositionChanged(int)
+{
+ if ( !_screenWindow )
+ return;
+
+ _screenWindow->scrollTo( _scrollBar->value() );
+
+ // if the thumb has been moved to the bottom of the _scrollBar then set
+ // the display to automatically track new output,
+ // that is, scroll down automatically
+ // to how new _lines as they are added
+ const bool atEndOfOutput = (_scrollBar->value() == _scrollBar->maximum());
+ _screenWindow->setTrackOutput( atEndOfOutput );
+
+ updateImage();
+}
+
+void TerminalDisplay::setScroll(int cursor, int slines)
+{
+//qDebug("%s %d setScroll", __FILE__, __LINE__);
+ // update _scrollBar if the range or value has changed,
+ // otherwise return
+ //
+ // setting the range or value of a _scrollBar will always trigger
+ // a repaint, so it should be avoided if it is not necessary
+ if ( _scrollBar->minimum() == 0 &&
+ _scrollBar->maximum() == (slines - _lines) &&
+ _scrollBar->value() == cursor )
+ {
+ return;
+ }
+
+ disconnect(_scrollBar, SIGNAL(valueChanged(int)), this, SLOT(scrollBarPositionChanged(int)));
+ _scrollBar->setRange(0,slines - _lines);
+ _scrollBar->setSingleStep(1);
+ _scrollBar->setPageStep(_lines);
+ _scrollBar->setValue(cursor);
+ connect(_scrollBar, SIGNAL(valueChanged(int)), this, SLOT(scrollBarPositionChanged(int)));
+}
+
+void TerminalDisplay::setScrollBarPosition(ScrollBarPosition position)
+{
+ if (_scrollbarLocation == position) {
+// return;
+ }
+
+ if ( position == NoScrollBar )
+ _scrollBar->hide();
+ else
+ _scrollBar->show();
+
+ _topMargin = _leftMargin = 1;
+ _scrollbarLocation = position;
+
+ propagateSize();
+ update();
+}
+
+void TerminalDisplay::mousePressEvent(QMouseEvent* ev)
+{
+ if ( _possibleTripleClick && (ev->button()==Qt::LeftButton) ) {
+ mouseTripleClickEvent(ev);
+ return;
+ }
+
+ if ( !contentsRect().contains(ev->pos()) ) return;
+
+ if ( !_screenWindow ) return;
+
+ int charLine;
+ int charColumn;
+ getCharacterPosition(ev->pos(),charLine,charColumn);
+ QPoint pos = QPoint(charColumn,charLine);
+
+ if ( ev->button() == Qt::LeftButton)
+ {
+ _lineSelectionMode = false;
+ _wordSelectionMode = false;
+
+ emit isBusySelecting(true); // Keep it steady...
+ // Drag only when the Control key is hold
+ bool selected = false;
+
+ // The receiver of the testIsSelected() signal will adjust
+ // 'selected' accordingly.
+ //emit testIsSelected(pos.x(), pos.y(), selected);
+
+ selected = _screenWindow->isSelected(pos.x(),pos.y());
+
+ if ((!_ctrlDrag || ev->modifiers() & Qt::ControlModifier) && selected ) {
+ // The user clicked inside selected text
+ dragInfo.state = diPending;
+ dragInfo.start = ev->pos();
+ }
+ else {
+ // No reason to ever start a drag event
+ dragInfo.state = diNone;
+
+ _preserveLineBreaks = !( ( ev->modifiers() & Qt::ControlModifier ) && !(ev->modifiers() & Qt::AltModifier) );
+ _columnSelectionMode = (ev->modifiers() & Qt::AltModifier) && (ev->modifiers() & Qt::ControlModifier);
+
+ if (_mouseMarks || (ev->modifiers() & Qt::ShiftModifier))
+ {
+ _screenWindow->clearSelection();
+
+ //emit clearSelectionSignal();
+ pos.ry() += _scrollBar->value();
+ _iPntSel = _pntSel = pos;
+ _actSel = 1; // left mouse button pressed but nothing selected yet.
+
+ }
+ else
+ {
+ emit mouseSignal( 0, charColumn + 1, charLine + 1 +_scrollBar->value() -_scrollBar->maximum() , 0);
+ }
+ }
+ }
+ else if ( ev->button() == Qt::MidButton )
+ {
+ if ( _mouseMarks || (!_mouseMarks && (ev->modifiers() & Qt::ShiftModifier)) )
+ emitSelection(true,ev->modifiers() & Qt::ControlModifier);
+ else
+ emit mouseSignal( 1, charColumn +1, charLine +1 +_scrollBar->value() -_scrollBar->maximum() , 0);
+ }
+ else if ( ev->button() == Qt::RightButton )
+ {
+ if (_mouseMarks || (ev->modifiers() & Qt::ShiftModifier))
+ {
+ emit configureRequest( this,
+ ev->modifiers() & (Qt::ShiftModifier|Qt::ControlModifier),
+ ev->pos()
+ );
+ }
+ else
+ emit mouseSignal( 2, charColumn +1, charLine +1 +_scrollBar->value() -_scrollBar->maximum() , 0);
+ }
+}
+
+QList<QAction*> TerminalDisplay::filterActions(const QPoint& position)
+{
+ int charLine, charColumn;
+ getCharacterPosition(position,charLine,charColumn);
+
+ Filter::HotSpot* spot = _filterChain->hotSpotAt(charLine,charColumn);
+
+ return spot ? spot->actions() : QList<QAction*>();
+}
+
+void TerminalDisplay::mouseMoveEvent(QMouseEvent* ev)
+{
+ int charLine = 0;
+ int charColumn = 0;
+
+ getCharacterPosition(ev->pos(),charLine,charColumn);
+
+ // handle filters
+ // change link hot-spot appearance on mouse-over
+ Filter::HotSpot* spot = _filterChain->hotSpotAt(charLine,charColumn);
+ if ( spot && spot->type() == Filter::HotSpot::Link)
+ {
+ QRect previousHotspotArea = _mouseOverHotspotArea;
+ _mouseOverHotspotArea.setCoords( qMin(spot->startColumn() , spot->endColumn()) * _fontWidth,
+ spot->startLine() * _fontHeight,
+ qMax(spot->startColumn() , spot->endColumn()) * _fontHeight,
+ (spot->endLine()+1) * _fontHeight );
+
+ // display tooltips when mousing over links
+ // TODO: Extend this to work with filter types other than links
+ const QString& tooltip = spot->tooltip();
+ if ( !tooltip.isEmpty() )
+ {
+ QToolTip::showText( mapToGlobal(ev->pos()) , tooltip , this , _mouseOverHotspotArea );
+ }
+
+ update( _mouseOverHotspotArea | previousHotspotArea );
+ }
+ else if ( _mouseOverHotspotArea.isValid() )
+ {
+ update( _mouseOverHotspotArea );
+ // set hotspot area to an invalid rectangle
+ _mouseOverHotspotArea = QRect();
+ }
+
+ // for auto-hiding the cursor, we need mouseTracking
+ if (ev->buttons() == Qt::NoButton ) return;
+
+ // if the terminal is interested in mouse movements
+ // then emit a mouse movement signal, unless the shift
+ // key is being held down, which overrides this.
+ if (!_mouseMarks && !(ev->modifiers() & Qt::ShiftModifier))
+ {
+ int button = 3;
+ if (ev->buttons() & Qt::LeftButton)
+ button = 0;
+ if (ev->buttons() & Qt::MidButton)
+ button = 1;
+ if (ev->buttons() & Qt::RightButton)
+ button = 2;
+
+
+ emit mouseSignal( button,
+ charColumn + 1,
+ charLine + 1 +_scrollBar->value() -_scrollBar->maximum(),
+ 1 );
+
+ return;
+ }
+
+ if (dragInfo.state == diPending)
+ {
+ // we had a mouse down, but haven't confirmed a drag yet
+ // if the mouse has moved sufficiently, we will confirm
+
+ int distance = 10; //KGlobalSettings::dndEventDelay();
+ if ( ev->x() > dragInfo.start.x() + distance || ev->x() < dragInfo.start.x() - distance ||
+ ev->y() > dragInfo.start.y() + distance || ev->y() < dragInfo.start.y() - distance)
+ {
+ // we've left the drag square, we can start a real drag operation now
+ emit isBusySelecting(false); // Ok.. we can breath again.
+
+ _screenWindow->clearSelection();
+ doDrag();
+ }
+ return;
+ }
+ else if (dragInfo.state == diDragging)
+ {
+ // this isn't technically needed because mouseMoveEvent is suppressed during
+ // Qt drag operations, replaced by dragMoveEvent
+ return;
+ }
+
+ if (_actSel == 0) return;
+
+ // don't extend selection while pasting
+ if (ev->buttons() & Qt::MidButton) return;
+
+ extendSelection( ev->pos() );
+}
+
+#if 0
+void TerminalDisplay::setSelectionEnd()
+{
+ extendSelection( _configureRequestPoint );
+}
+#endif
+
+void TerminalDisplay::extendSelection( const QPoint& position )
+{
+ QPoint pos = position;
+
+ if ( !_screenWindow )
+ return;
+
+ //if ( !contentsRect().contains(ev->pos()) ) return;
+ QPoint tL = contentsRect().topLeft();
+ int tLx = tL.x();
+ int tLy = tL.y();
+ int scroll = _scrollBar->value();
+
+ // we're in the process of moving the mouse with the left button pressed
+ // the mouse cursor will kept caught within the bounds of the text in
+ // this widget.
+
+ // Adjust position within text area bounds. See FIXME above.
+ QPoint oldpos = pos;
+ if ( pos.x() < tLx+_leftMargin )
+ pos.setX( tLx+_leftMargin );
+ if ( pos.x() > tLx+_leftMargin+_usedColumns*_fontWidth-1 )
+ pos.setX( tLx+_leftMargin+_usedColumns*_fontWidth );
+ if ( pos.y() < tLy+_topMargin )
+ pos.setY( tLy+_topMargin );
+ if ( pos.y() > tLy+_topMargin+_usedLines*_fontHeight-1 )
+ pos.setY( tLy+_topMargin+_usedLines*_fontHeight-1 );
+
+ if ( pos.y() == tLy+_topMargin+_usedLines*_fontHeight-1 )
+ {
+ _scrollBar->setValue(_scrollBar->value()+yMouseScroll); // scrollforward
+ }
+ if ( pos.y() == tLy+_topMargin )
+ {
+ _scrollBar->setValue(_scrollBar->value()-yMouseScroll); // scrollback
+ }
+
+ int charColumn = 0;
+ int charLine = 0;
+ getCharacterPosition(pos,charLine,charColumn);
+
+ QPoint here = QPoint(charColumn,charLine); //QPoint((pos.x()-tLx-_leftMargin+(_fontWidth/2))/_fontWidth,(pos.y()-tLy-_topMargin)/_fontHeight);
+ QPoint ohere;
+ QPoint _iPntSelCorr = _iPntSel;
+ _iPntSelCorr.ry() -= _scrollBar->value();
+ QPoint _pntSelCorr = _pntSel;
+ _pntSelCorr.ry() -= _scrollBar->value();
+ bool swapping = false;
+
+ if ( _wordSelectionMode )
+ {
+ // Extend to word boundaries
+ int i;
+ int selClass;
+
+ bool left_not_right = ( here.y() < _iPntSelCorr.y() ||
+ here.y() == _iPntSelCorr.y() && here.x() < _iPntSelCorr.x() );
+ bool old_left_not_right = ( _pntSelCorr.y() < _iPntSelCorr.y() ||
+ _pntSelCorr.y() == _iPntSelCorr.y() && _pntSelCorr.x() < _iPntSelCorr.x() );
+ swapping = left_not_right != old_left_not_right;
+
+ // Find left (left_not_right ? from here : from start)
+ QPoint left = left_not_right ? here : _iPntSelCorr;
+ i = loc(left.x(),left.y());
+ if (i>=0 && i<=_imageSize) {
+ selClass = charClass(_image[i].character);
+ while ( ((left.x()>0) || (left.y()>0 && (_lineProperties[left.y()-1] & LINE_WRAPPED) ))
+ && charClass(_image[i-1].character) == selClass )
+ { i--; if (left.x()>0) left.rx()--; else {left.rx()=_usedColumns-1; left.ry()--;} }
+ }
+
+ // Find left (left_not_right ? from start : from here)
+ QPoint right = left_not_right ? _iPntSelCorr : here;
+ i = loc(right.x(),right.y());
+ if (i>=0 && i<=_imageSize) {
+ selClass = charClass(_image[i].character);
+ while( ((right.x()<_usedColumns-1) || (right.y()<_usedLines-1 && (_lineProperties[right.y()] & LINE_WRAPPED) ))
+ && charClass(_image[i+1].character) == selClass )
+ { i++; if (right.x()<_usedColumns-1) right.rx()++; else {right.rx()=0; right.ry()++; } }
+ }
+
+ // Pick which is start (ohere) and which is extension (here)
+ if ( left_not_right )
+ {
+ here = left; ohere = right;
+ }
+ else
+ {
+ here = right; ohere = left;
+ }
+ ohere.rx()++;
+ }
+
+ if ( _lineSelectionMode )
+ {
+ // Extend to complete line
+ bool above_not_below = ( here.y() < _iPntSelCorr.y() );
+
+ QPoint above = above_not_below ? here : _iPntSelCorr;
+ QPoint below = above_not_below ? _iPntSelCorr : here;
+
+ while (above.y()>0 && (_lineProperties[above.y()-1] & LINE_WRAPPED) )
+ above.ry()--;
+ while (below.y()<_usedLines-1 && (_lineProperties[below.y()] & LINE_WRAPPED) )
+ below.ry()++;
+
+ above.setX(0);
+ below.setX(_usedColumns-1);
+
+ // Pick which is start (ohere) and which is extension (here)
+ if ( above_not_below )
+ {
+ here = above; ohere = below;
+ }
+ else
+ {
+ here = below; ohere = above;
+ }
+
+ QPoint newSelBegin = QPoint( ohere.x(), ohere.y() );
+ swapping = !(_tripleSelBegin==newSelBegin);
+ _tripleSelBegin = newSelBegin;
+
+ ohere.rx()++;
+ }
+
+ int offset = 0;
+ if ( !_wordSelectionMode && !_lineSelectionMode )
+ {
+ int i;
+ int selClass;
+
+ bool left_not_right = ( here.y() < _iPntSelCorr.y() ||
+ here.y() == _iPntSelCorr.y() && here.x() < _iPntSelCorr.x() );
+ bool old_left_not_right = ( _pntSelCorr.y() < _iPntSelCorr.y() ||
+ _pntSelCorr.y() == _iPntSelCorr.y() && _pntSelCorr.x() < _iPntSelCorr.x() );
+ swapping = left_not_right != old_left_not_right;
+
+ // Find left (left_not_right ? from here : from start)
+ QPoint left = left_not_right ? here : _iPntSelCorr;
+
+ // Find left (left_not_right ? from start : from here)
+ QPoint right = left_not_right ? _iPntSelCorr : here;
+ if ( right.x() > 0 && !_columnSelectionMode )
+ {
+ i = loc(right.x(),right.y());
+ if (i>=0 && i<=_imageSize) {
+ selClass = charClass(_image[i-1].character);
+ if (selClass == ' ')
+ {
+ while ( right.x() < _usedColumns-1 && charClass(_image[i+1].character) == selClass && (right.y()<_usedLines-1) &&
+ !(_lineProperties[right.y()] & LINE_WRAPPED))
+ { i++; right.rx()++; }
+ if (right.x() < _usedColumns-1)
+ right = left_not_right ? _iPntSelCorr : here;
+ else
+ right.rx()++; // will be balanced later because of offset=-1;
+ }
+ }
+ }
+
+ // Pick which is start (ohere) and which is extension (here)
+ if ( left_not_right )
+ {
+ here = left; ohere = right; offset = 0;
+ }
+ else
+ {
+ here = right; ohere = left; offset = -1;
+ }
+ }
+
+ if ((here == _pntSelCorr) && (scroll == _scrollBar->value())) return; // not moved
+
+ if (here == ohere) return; // It's not left, it's not right.
+
+ if ( _actSel < 2 || swapping )
+ {
+ if ( _columnSelectionMode && !_lineSelectionMode && !_wordSelectionMode )
+ {
+ _screenWindow->setSelectionStart( ohere.x() , ohere.y() , true );
+ }
+ else
+ {
+ _screenWindow->setSelectionStart( ohere.x()-1-offset , ohere.y() , false );
+ }
+
+ }
+
+ _actSel = 2; // within selection
+ _pntSel = here;
+ _pntSel.ry() += _scrollBar->value();
+
+ if ( _columnSelectionMode && !_lineSelectionMode && !_wordSelectionMode )
+ {
+ _screenWindow->setSelectionEnd( here.x() , here.y() );
+ }
+ else
+ {
+ _screenWindow->setSelectionEnd( here.x()+offset , here.y() );
+ }
+
+}
+
+void TerminalDisplay::mouseReleaseEvent(QMouseEvent* ev)
+{
+ if ( !_screenWindow )
+ return;
+
+ int charLine;
+ int charColumn;
+ getCharacterPosition(ev->pos(),charLine,charColumn);
+
+ if ( ev->button() == Qt::LeftButton)
+ {
+ emit isBusySelecting(false);
+ if(dragInfo.state == diPending)
+ {
+ // We had a drag event pending but never confirmed. Kill selection
+ _screenWindow->clearSelection();
+ //emit clearSelectionSignal();
+ }
+ else
+ {
+ if ( _actSel > 1 )
+ {
+ setSelection( _screenWindow->selectedText(_preserveLineBreaks) );
+ }
+
+ _actSel = 0;
+
+ //FIXME: emits a release event even if the mouse is
+ // outside the range. The procedure used in `mouseMoveEvent'
+ // applies here, too.
+
+ if (!_mouseMarks && !(ev->modifiers() & Qt::ShiftModifier))
+ emit mouseSignal( 3, // release
+ charColumn + 1,
+ charLine + 1 +_scrollBar->value() -_scrollBar->maximum() , 0);
+ }
+ dragInfo.state = diNone;
+ }
+
+
+ if ( !_mouseMarks &&
+ ((ev->button() == Qt::RightButton && !(ev->modifiers() & Qt::ShiftModifier))
+ || ev->button() == Qt::MidButton) )
+ {
+ emit mouseSignal( 3,
+ charColumn + 1,
+ charLine + 1 +_scrollBar->value() -_scrollBar->maximum() ,
+ 0);
+ }
+}
+
+void TerminalDisplay::getCharacterPosition(const QPoint& widgetPoint,int& line,int& column) const
+{
+
+ column = (widgetPoint.x() + _fontWidth/2 -contentsRect().left()-_leftMargin) / _fontWidth;
+ line = (widgetPoint.y()-contentsRect().top()-_topMargin) / _fontHeight;
+
+ if ( line < 0 )
+ line = 0;
+ if ( column < 0 )
+ column = 0;
+
+ if ( line >= _usedLines )
+ line = _usedLines-1;
+
+ // the column value returned can be equal to _usedColumns, which
+ // is the position just after the last character displayed in a line.
+ //
+ // this is required so that the user can select characters in the right-most
+ // column (or left-most for right-to-left input)
+ if ( column > _usedColumns )
+ column = _usedColumns;
+}
+
+void TerminalDisplay::updateLineProperties()
+{
+ if ( !_screenWindow )
+ return;
+
+ _lineProperties = _screenWindow->getLineProperties();
+}
+
+void TerminalDisplay::mouseDoubleClickEvent(QMouseEvent* ev)
+{
+ if ( ev->button() != Qt::LeftButton) return;
+ if ( !_screenWindow ) return;
+
+ int charLine = 0;
+ int charColumn = 0;
+
+ getCharacterPosition(ev->pos(),charLine,charColumn);
+
+ QPoint pos(charColumn,charLine);
+
+ // pass on double click as two clicks.
+ if (!_mouseMarks && !(ev->modifiers() & Qt::ShiftModifier))
+ {
+ // Send just _ONE_ click event, since the first click of the double click
+ // was already sent by the click handler
+ emit mouseSignal( 0,
+ pos.x()+1,
+ pos.y()+1 +_scrollBar->value() -_scrollBar->maximum(),
+ 0 ); // left button
+ return;
+ }
+
+ _screenWindow->clearSelection();
+ QPoint bgnSel = pos;
+ QPoint endSel = pos;
+ int i = loc(bgnSel.x(),bgnSel.y());
+ _iPntSel = bgnSel;
+ _iPntSel.ry() += _scrollBar->value();
+
+ _wordSelectionMode = true;
+
+ // find word boundaries...
+ int selClass = charClass(_image[i].character);
+ {
+ // find the start of the word
+ int x = bgnSel.x();
+ while ( ((x>0) || (bgnSel.y()>0 && (_lineProperties[bgnSel.y()-1] & LINE_WRAPPED) ))
+ && charClass(_image[i-1].character) == selClass )
+ {
+ i--;
+ if (x>0)
+ x--;
+ else
+ {
+ x=_usedColumns-1;
+ bgnSel.ry()--;
+ }
+ }
+
+ bgnSel.setX(x);
+ _screenWindow->setSelectionStart( bgnSel.x() , bgnSel.y() , false );
+
+ // find the end of the word
+ i = loc( endSel.x(), endSel.y() );
+ x = endSel.x();
+ while( ((x<_usedColumns-1) || (endSel.y()<_usedLines-1 && (_lineProperties[endSel.y()] & LINE_WRAPPED) ))
+ && charClass(_image[i+1].character) == selClass )
+ {
+ i++;
+ if (x<_usedColumns-1)
+ x++;
+ else
+ {
+ x=0;
+ endSel.ry()++;
+ }
+ }
+
+ endSel.setX(x);
+
+ // In word selection mode don't select @ (64) if at end of word.
+ if ( ( QChar( _image[i].character ) == '@' ) && ( ( endSel.x() - bgnSel.x() ) > 0 ) )
+ endSel.setX( x - 1 );
+
+
+ _actSel = 2; // within selection
+
+ _screenWindow->setSelectionEnd( endSel.x() , endSel.y() );
+
+ setSelection( _screenWindow->selectedText(_preserveLineBreaks) );
+ }
+
+ _possibleTripleClick=true;
+
+ QTimer::singleShot(QApplication::doubleClickInterval(),this,
+ SLOT(tripleClickTimeout()));
+}
+
+void TerminalDisplay::wheelEvent( QWheelEvent* ev )
+{
+ if (ev->orientation() != Qt::Vertical)
+ return;
+
+ if ( _mouseMarks )
+ _scrollBar->event(ev);
+ else
+ {
+ int charLine;
+ int charColumn;
+ getCharacterPosition( ev->pos() , charLine , charColumn );
+
+ emit mouseSignal( ev->delta() > 0 ? 4 : 5,
+ charColumn + 1,
+ charLine + 1 +_scrollBar->value() -_scrollBar->maximum() ,
+ 0);
+ }
+}
+
+void TerminalDisplay::tripleClickTimeout()
+{
+ _possibleTripleClick=false;
+}
+
+void TerminalDisplay::mouseTripleClickEvent(QMouseEvent* ev)
+{
+ if ( !_screenWindow ) return;
+
+ int charLine;
+ int charColumn;
+ getCharacterPosition(ev->pos(),charLine,charColumn);
+ _iPntSel = QPoint(charColumn,charLine);
+
+ _screenWindow->clearSelection();
+
+ _lineSelectionMode = true;
+ _wordSelectionMode = false;
+
+ _actSel = 2; // within selection
+ emit isBusySelecting(true); // Keep it steady...
+
+ while (_iPntSel.y()>0 && (_lineProperties[_iPntSel.y()-1] & LINE_WRAPPED) )
+ _iPntSel.ry()--;
+
+ if (_tripleClickMode == SelectForwardsFromCursor) {
+ // find word boundary start
+ int i = loc(_iPntSel.x(),_iPntSel.y());
+ int selClass = charClass(_image[i].character);
+ int x = _iPntSel.x();
+
+ while ( ((x>0) ||
+ (_iPntSel.y()>0 && (_lineProperties[_iPntSel.y()-1] & LINE_WRAPPED) )
+ )
+ && charClass(_image[i-1].character) == selClass )
+ {
+ i--;
+ if (x>0)
+ x--;
+ else
+ {
+ x=_columns-1;
+ _iPntSel.ry()--;
+ }
+ }
+
+ _screenWindow->setSelectionStart( x , _iPntSel.y() , false );
+ _tripleSelBegin = QPoint( x, _iPntSel.y() );
+ }
+ else if (_tripleClickMode == SelectWholeLine) {
+ _screenWindow->setSelectionStart( 0 , _iPntSel.y() , false );
+ _tripleSelBegin = QPoint( 0, _iPntSel.y() );
+ }
+
+ while (_iPntSel.y()<_lines-1 && (_lineProperties[_iPntSel.y()] & LINE_WRAPPED) )
+ _iPntSel.ry()++;
+
+ _screenWindow->setSelectionEnd( _columns - 1 , _iPntSel.y() );
+
+ setSelection(_screenWindow->selectedText(_preserveLineBreaks));
+
+ _iPntSel.ry() += _scrollBar->value();
+}
+
+
+bool TerminalDisplay::focusNextPrevChild( bool next )
+{
+ if (next)
+ return false; // This disables changing the active part in konqueror
+ // when pressing Tab
+ return QWidget::focusNextPrevChild( next );
+}
+
+
+int TerminalDisplay::charClass(quint16 ch) const
+{
+ QChar qch=QChar(ch);
+ if ( qch.isSpace() ) return ' ';
+
+ if ( qch.isLetterOrNumber() || _wordCharacters.contains(qch, Qt::CaseInsensitive ) )
+ return 'a';
+
+ // Everything else is weird
+ return 1;
+}
+
+void TerminalDisplay::setWordCharacters(const QString& wc)
+{
+ _wordCharacters = wc;
+}
+
+void TerminalDisplay::setUsesMouse(bool on)
+{
+ _mouseMarks = on;
+ setCursor( _mouseMarks ? Qt::IBeamCursor : Qt::ArrowCursor );
+}
+bool TerminalDisplay::usesMouse() const
+{
+ return _mouseMarks;
+}
+
+/* ------------------------------------------------------------------------- */
+/* */
+/* Clipboard */
+/* */
+/* ------------------------------------------------------------------------- */
+
+#undef KeyPress
+
+void TerminalDisplay::emitSelection(bool useXselection,bool appendReturn)
+{
+ if ( !_screenWindow )
+ return;
+
+ // Paste Clipboard by simulating keypress events
+ QString text = QApplication::clipboard()->text(useXselection ? QClipboard::Selection :
+ QClipboard::Clipboard);
+ if(appendReturn)
+ text.append("\r");
+ if ( ! text.isEmpty() )
+ {
+ text.replace("\n", "\r");
+ QKeyEvent e(QEvent::KeyPress, 0, Qt::NoModifier, text);
+ emit keyPressedSignal(&e); // expose as a big fat keypress event
+
+ _screenWindow->clearSelection();
+ }
+}
+
+void TerminalDisplay::setSelection(const QString& t)
+{
+ QApplication::clipboard()->setText(t, QClipboard::Selection);
+}
+
+void TerminalDisplay::copyClipboard()
+{
+ if ( !_screenWindow )
+ return;
+
+ QString text = _screenWindow->selectedText(_preserveLineBreaks);
+ QApplication::clipboard()->setText(text);
+}
+
+void TerminalDisplay::pasteClipboard()
+{
+ emitSelection(false,false);
+}
+
+void TerminalDisplay::pasteSelection()
+{
+ emitSelection(true,false);
+}
+
+/* ------------------------------------------------------------------------- */
+/* */
+/* Keyboard */
+/* */
+/* ------------------------------------------------------------------------- */
+
+void TerminalDisplay::setFlowControlWarningEnabled( bool enable )
+{
+ _flowControlWarningEnabled = enable;
+
+ // if the dialog is currently visible and the flow control warning has
+ // been disabled then hide the dialog
+ if (!enable)
+ outputSuspended(false);
+}
+
+void TerminalDisplay::keyPressEvent( QKeyEvent* event )
+{
+//qDebug("%s %d keyPressEvent and key is %d", __FILE__, __LINE__, event->key());
+
+ bool emitKeyPressSignal = true;
+
+ // XonXoff flow control
+ if (event->modifiers() & Qt::ControlModifier && _flowControlWarningEnabled)
+ {
+ if ( event->key() == Qt::Key_S ) {
+ //qDebug("%s %d keyPressEvent, output suspended", __FILE__, __LINE__);
+ emit flowControlKeyPressed(true /*output suspended*/);
+ }
+ else if ( event->key() == Qt::Key_Q ) {
+ //qDebug("%s %d keyPressEvent, output enabled", __FILE__, __LINE__);
+ emit flowControlKeyPressed(false /*output enabled*/);
+ }
+ }
+
+ // Keyboard-based navigation
+ if ( event->modifiers() == Qt::ShiftModifier )
+ {
+ bool update = true;
+
+ if ( event->key() == Qt::Key_PageUp )
+ {
+ //qDebug("%s %d pageup", __FILE__, __LINE__);
+ _screenWindow->scrollBy( ScreenWindow::ScrollPages , -1 );
+ }
+ else if ( event->key() == Qt::Key_PageDown )
+ {
+ //qDebug("%s %d pagedown", __FILE__, __LINE__);
+ _screenWindow->scrollBy( ScreenWindow::ScrollPages , 1 );
+ }
+ else if ( event->key() == Qt::Key_Up )
+ {
+ //qDebug("%s %d keyup", __FILE__, __LINE__);
+ _screenWindow->scrollBy( ScreenWindow::ScrollLines , -1 );
+ }
+ else if ( event->key() == Qt::Key_Down )
+ {
+ //qDebug("%s %d keydown", __FILE__, __LINE__);
+ _screenWindow->scrollBy( ScreenWindow::ScrollLines , 1 );
+ }
+ else {
+ update = false;
+ }
+
+ if ( update )
+ {
+ //qDebug("%s %d updating", __FILE__, __LINE__);
+ _screenWindow->setTrackOutput( _screenWindow->atEndOfOutput() );
+
+ updateLineProperties();
+ updateImage();
+
+ // do not send key press to terminal
+ emitKeyPressSignal = false;
+ }
+ }
+
+ _screenWindow->setTrackOutput( true );
+
+ _actSel=0; // Key stroke implies a screen update, so TerminalDisplay won't
+ // know where the current selection is.
+
+ if (_hasBlinkingCursor)
+ {
+ _blinkCursorTimer->start(BLINK_DELAY);
+ if (_cursorBlinking)
+ blinkCursorEvent();
+ else
+ _cursorBlinking = false;
+ }
+
+ if ( emitKeyPressSignal )
+ emit keyPressedSignal(event);
+
+ event->accept();
+}
+
+void TerminalDisplay::inputMethodEvent( QInputMethodEvent* event )
+{
+ QKeyEvent keyEvent(QEvent::KeyPress,0,Qt::NoModifier,event->commitString());
+ emit keyPressedSignal(&keyEvent);
+
+ _inputMethodData.preeditString = event->preeditString();
+ update(preeditRect() | _inputMethodData.previousPreeditRect);
+
+ event->accept();
+}
+QVariant TerminalDisplay::inputMethodQuery( Qt::InputMethodQuery query ) const
+{
+ const QPoint cursorPos = _screenWindow ? _screenWindow->cursorPosition() : QPoint(0,0);
+ switch ( query )
+ {
+ case Qt::ImMicroFocus:
+ return imageToWidget(QRect(cursorPos.x(),cursorPos.y(),1,1));
+ break;
+ case Qt::ImFont:
+ return font();
+ break;
+ case Qt::ImCursorPosition:
+ // return the cursor position within the current line
+ return cursorPos.x();
+ break;
+ case Qt::ImSurroundingText:
+ {
+ // return the text from the current line
+ QString lineText;
+ QTextStream stream(&lineText);
+ PlainTextDecoder decoder;
+ decoder.begin(&stream);
+ decoder.decodeLine(&_image[loc(0,cursorPos.y())],_usedColumns,_lineProperties[cursorPos.y()]);
+ decoder.end();
+ return lineText;
+ }
+ break;
+ case Qt::ImCurrentSelection:
+ return QString();
+ break;
+ }
+
+ return QVariant();
+}
+
+bool TerminalDisplay::event( QEvent *e )
+{
+ if ( e->type() == QEvent::ShortcutOverride )
+ {
+ QKeyEvent* keyEvent = static_cast<QKeyEvent *>( e );
+
+ // a check to see if keyEvent->text() is empty is used
+ // to avoid intercepting the press of the modifier key on its own.
+ //
+ // this is important as it allows a press and release of the Alt key
+ // on its own to focus the menu bar, making it possible to
+ // work with the menu without using the mouse
+ if ( (keyEvent->modifiers() == Qt::AltModifier) &&
+ !keyEvent->text().isEmpty() )
+ {
+ keyEvent->accept();
+ return true;
+ }
+
+ // Override any of the following shortcuts because
+ // they are needed by the terminal
+ int keyCode = keyEvent->key() | keyEvent->modifiers();
+ switch ( keyCode )
+ {
+ // list is taken from the QLineEdit::event() code
+ case Qt::Key_Tab:
+ case Qt::Key_Delete:
+ case Qt::Key_Home:
+ case Qt::Key_End:
+ case Qt::Key_Backspace:
+ case Qt::Key_Left:
+ case Qt::Key_Right:
+ keyEvent->accept();
+ return true;
+ }
+ }
+ return QWidget::event( e );
+}
+
+void TerminalDisplay::setBellMode(int mode)
+{
+ _bellMode=mode;
+}
+
+void TerminalDisplay::enableBell()
+{
+ _allowBell = true;
+}
+
+void TerminalDisplay::bell(const QString&)
+{
+ if (_bellMode==NoBell) return;
+
+ //limit the rate at which bells can occur
+ //...mainly for sound effects where rapid bells in sequence
+ //produce a horrible noise
+ if ( _allowBell )
+ {
+ _allowBell = false;
+ QTimer::singleShot(500,this,SLOT(enableBell()));
+
+ if (_bellMode==SystemBeepBell)
+ {
+// KNotification::beep();
+ }
+ else if (_bellMode==NotifyBell)
+ {
+// KNotification::event("BellVisible", message,QPixmap(),this);
+ }
+ else if (_bellMode==VisualBell)
+ {
+ swapColorTable();
+ QTimer::singleShot(200,this,SLOT(swapColorTable()));
+ }
+ }
+}
+
+void TerminalDisplay::swapColorTable()
+{
+ ColorEntry color = _colorTable[1];
+ _colorTable[1]=_colorTable[0];
+ _colorTable[0]= color;
+ _colorsInverted = !_colorsInverted;
+ update();
+}
+
+void TerminalDisplay::clearImage()
+{
+ // We initialize _image[_imageSize] too. See makeImage()
+ for (int i = 0; i <= _imageSize; i++)
+ {
+ _image[i].character = ' ';
+ _image[i].foregroundColor = CharacterColor(COLOR_SPACE_DEFAULT,
+ DEFAULT_FORE_COLOR);
+ _image[i].backgroundColor = CharacterColor(COLOR_SPACE_DEFAULT,
+ DEFAULT_BACK_COLOR);
+ _image[i].rendition = DEFAULT_RENDITION;
+ }
+}
+
+void TerminalDisplay::calcGeometry()
+{
+ _scrollBar->resize(QApplication::style()->pixelMetric(QStyle::PM_ScrollBarExtent),
+ contentsRect().height());
+ switch(_scrollbarLocation)
+ {
+ case NoScrollBar :
+ _leftMargin = DEFAULT_LEFT_MARGIN;
+ _contentWidth = contentsRect().width() - 2 * DEFAULT_LEFT_MARGIN;
+ break;
+ case ScrollBarLeft :
+ _leftMargin = DEFAULT_LEFT_MARGIN + _scrollBar->width();
+ _contentWidth = contentsRect().width() - 2 * DEFAULT_LEFT_MARGIN - _scrollBar->width();
+ _scrollBar->move(contentsRect().topLeft());
+ break;
+ case ScrollBarRight:
+ _leftMargin = DEFAULT_LEFT_MARGIN;
+ _contentWidth = contentsRect().width() - 2 * DEFAULT_LEFT_MARGIN - _scrollBar->width();
+ _scrollBar->move(contentsRect().topRight() - QPoint(_scrollBar->width()-1,0));
+ break;
+ }
+
+ _topMargin = DEFAULT_TOP_MARGIN;
+ _contentHeight = contentsRect().height() - 2 * DEFAULT_TOP_MARGIN + /* mysterious */ 1;
+
+ if (!_isFixedSize)
+ {
+ // ensure that display is always at least one column wide
+ _columns = qMax(1,_contentWidth / _fontWidth);
+ _usedColumns = qMin(_usedColumns,_columns);
+
+ // ensure that display is always at least one line high
+ _lines = qMax(1,_contentHeight / _fontHeight);
+ _usedLines = qMin(_usedLines,_lines);
+ }
+}
+
+void TerminalDisplay::makeImage()
+{
+//qDebug("%s %d makeImage", __FILE__, __LINE__);
+ calcGeometry();
+
+ // confirm that array will be of non-zero size, since the painting code
+ // assumes a non-zero array length
+ Q_ASSERT( _lines > 0 && _columns > 0 );
+ Q_ASSERT( _usedLines <= _lines && _usedColumns <= _columns );
+
+ _imageSize=_lines*_columns;
+
+ // We over-commit one character so that we can be more relaxed in dealing with
+ // certain boundary conditions: _image[_imageSize] is a valid but unused position
+ _image = new Character[_imageSize+1];
+
+ clearImage();
+}
+
+// calculate the needed size
+void TerminalDisplay::setSize(int columns, int lines)
+{
+ //FIXME - Not quite correct, a small amount of additional space
+ // will be used for margins, the scrollbar etc.
+ // we need to allow for this so that '_size' does allow
+ // enough room for the specified number of columns and lines to fit
+
+ QSize newSize = QSize( columns * _fontWidth ,
+ lines * _fontHeight );
+
+ if ( newSize != size() )
+ {
+ _size = newSize;
+ updateGeometry();
+ }
+}
+
+void TerminalDisplay::setFixedSize(int cols, int lins)
+{
+ _isFixedSize = true;
+
+ //ensure that display is at least one line by one column in size
+ _columns = qMax(1,cols);
+ _lines = qMax(1,lins);
+ _usedColumns = qMin(_usedColumns,_columns);
+ _usedLines = qMin(_usedLines,_lines);
+
+ if (_image)
+ {
+ delete[] _image;
+ makeImage();
+ }
+ setSize(cols, lins);
+ QWidget::setFixedSize(_size);
+}
+
+QSize TerminalDisplay::sizeHint() const
+{
+ return _size;
+}
+
+
+/* --------------------------------------------------------------------- */
+/* */
+/* Drag & Drop */
+/* */
+/* --------------------------------------------------------------------- */
+
+void TerminalDisplay::dragEnterEvent(QDragEnterEvent* event)
+{
+ if (event->mimeData()->hasFormat("text/plain"))
+ event->acceptProposedAction();
+}
+
+void TerminalDisplay::dropEvent(QDropEvent* event)
+{
+// KUrl::List urls = KUrl::List::fromMimeData(event->mimeData());
+
+ QString dropText;
+/* if (!urls.isEmpty())
+ {
+ for ( int i = 0 ; i < urls.count() ; i++ )
+ {
+ KUrl url = KIO::NetAccess::mostLocalUrl( urls[i] , 0 );
+ QString urlText;
+
+ if (url.isLocalFile())
+ urlText = url.path();
+ else
+ urlText = url.url();
+
+ // in future it may be useful to be able to insert file names with drag-and-drop
+ // without quoting them (this only affects paths with spaces in)
+ urlText = KShell::quoteArg(urlText);
+
+ dropText += urlText;
+
+ if ( i != urls.count()-1 )
+ dropText += ' ';
+ }
+ }
+ else
+ {
+ dropText = event->mimeData()->text();
+ }
+*/
+ if(event->mimeData()->hasFormat("text/plain"))
+ {
+ emit sendStringToEmu(dropText.toLocal8Bit());
+ }
+}
+
+void TerminalDisplay::doDrag()
+{
+ dragInfo.state = diDragging;
+ dragInfo.dragObject = new QDrag(this);
+ QMimeData *mimeData = new QMimeData;
+ mimeData->setText(QApplication::clipboard()->text(QClipboard::Selection));
+ dragInfo.dragObject->setMimeData(mimeData);
+ dragInfo.dragObject->start(Qt::CopyAction);
+ // Don't delete the QTextDrag object. Qt will delete it when it's done with it.
+}
+
+void TerminalDisplay::outputSuspended(bool suspended)
+{
+ //create the label when this function is first called
+ if (!_outputSuspendedLabel)
+ {
+ //This label includes a link to an English language website
+ //describing the 'flow control' (Xon/Xoff) feature found in almost
+ //all terminal emulators.
+ //If there isn't a suitable article available in the target language the link
+ //can simply be removed.
+ _outputSuspendedLabel = new QLabel( ("<qt>Output has been "
+ "<a href=\"http://en.wikipedia.org/wiki/XON\">suspended</a>"
+ " by pressing Ctrl+S."
+ " Press <b>Ctrl+Q</b> to resume.</qt>"),
+ this );
+
+ QPalette palette(_outputSuspendedLabel->palette());
+
+ palette.setColor(QPalette::Normal, QPalette::WindowText, QColor(Qt::white));
+ palette.setColor(QPalette::Normal, QPalette::Window, QColor(Qt::black));
+// KColorScheme::adjustForeground(palette,KColorScheme::NeutralText);
+// KColorScheme::adjustBackground(palette,KColorScheme::NeutralBackground);
+ _outputSuspendedLabel->setPalette(palette);
+ _outputSuspendedLabel->setAutoFillBackground(true);
+ _outputSuspendedLabel->setBackgroundRole(QPalette::Base);
+ _outputSuspendedLabel->setFont(QApplication::font());
+ _outputSuspendedLabel->setMargin(5);
+
+ //enable activation of "Xon/Xoff" link in label
+ _outputSuspendedLabel->setTextInteractionFlags(Qt::LinksAccessibleByMouse |
+ Qt::LinksAccessibleByKeyboard);
+ _outputSuspendedLabel->setOpenExternalLinks(true);
+ _outputSuspendedLabel->setVisible(false);
+
+ _gridLayout->addWidget(_outputSuspendedLabel);
+ _gridLayout->addItem( new QSpacerItem(0,0,QSizePolicy::Expanding,
+ QSizePolicy::Expanding),
+ 1,0);
+
+ }
+
+ _outputSuspendedLabel->setVisible(suspended);
+}
+
+uint TerminalDisplay::lineSpacing() const
+{
+ return _lineSpacing;
+}
+
+void TerminalDisplay::setLineSpacing(uint i)
+{
+ _lineSpacing = i;
+ setVTFont(font()); // Trigger an update.
+}
+
+//#include "moc_TerminalDisplay.cpp"
diff --git a/qtermwidget/TerminalDisplay.h b/qtermwidget/TerminalDisplay.h
new file mode 100644
index 0000000..6b3c6d8
--- /dev/null
+++ b/qtermwidget/TerminalDisplay.h
@@ -0,0 +1,754 @@
+/*
+ Copyright (C) 2007 by Robert Knight <robertknight@gmail.com>
+ Copyright (C) 1997,1998 by Lars Doelle <lars.doelle@on-line.de>
+
+ Rewritten for QT4 by e_k <e_k at users.sourceforge.net>, Copyright (C)2008
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ 02110-1301 USA.
+*/
+
+#ifndef TERMINALDISPLAY_H
+#define TERMINALDISPLAY_H
+
+// Qt
+#include <QtGui/QColor>
+#include <QtCore/QPointer>
+#include <QtGui/QWidget>
+
+// Konsole
+#include "Filter.h"
+#include "Character.h"
+#include "ColorTables.h"
+
+class QDrag;
+class QDragEnterEvent;
+class QDropEvent;
+class QLabel;
+class QTimer;
+class QEvent;
+class QFrame;
+class QGridLayout;
+class QKeyEvent;
+class QScrollBar;
+class QShowEvent;
+class QHideEvent;
+class QWidget;
+
+//class KMenu;
+
+namespace Konsole
+{
+
+extern unsigned short vt100_graphics[32];
+
+class ScreenWindow;
+
+/**
+ * A widget which displays output from a terminal emulation and sends input keypresses and mouse activity
+ * to the terminal.
+ *
+ * When the terminal emulation receives new output from the program running in the terminal,
+ * it will update the display by calling updateImage().
+ *
+ * TODO More documentation
+ */
+class TerminalDisplay : public QWidget
+{
+ Q_OBJECT
+
+public:
+ /** Constructs a new terminal display widget with the specified parent. */
+ TerminalDisplay(QWidget *parent=0);
+ virtual ~TerminalDisplay();
+
+ /** Returns the terminal color palette used by the display. */
+ const ColorEntry* colorTable() const;
+ /** Sets the terminal color palette used by the display. */
+ void setColorTable(const ColorEntry table[]);
+ /**
+ * Sets the seed used to generate random colors for the display
+ * (in color schemes that support them).
+ */
+ void setRandomSeed(uint seed);
+ /**
+ * Returns the seed used to generate random colors for the display
+ * (in color schemes that support them).
+ */
+ uint randomSeed() const;
+
+ /** Sets the opacity of the terminal display. */
+ void setOpacity(qreal opacity);
+
+ /**
+ * This enum describes the location where the scroll bar is positioned in the display widget.
+ */
+ enum ScrollBarPosition
+ {
+ /** Do not show the scroll bar. */
+ NoScrollBar=0,
+ /** Show the scroll bar on the left side of the display. */
+ ScrollBarLeft=1,
+ /** Show the scroll bar on the right side of the display. */
+ ScrollBarRight=2
+ };
+ /**
+ * Specifies whether the terminal display has a vertical scroll bar, and if so whether it
+ * is shown on the left or right side of the display.
+ */
+ void setScrollBarPosition(ScrollBarPosition position);
+
+ /**
+ * Sets the current position and range of the display's scroll bar.
+ *
+ * @param cursor The position of the scroll bar's thumb.
+ * @param lines The maximum value of the scroll bar.
+ */
+ void setScroll(int cursor, int lines);
+
+ /**
+ * Returns the display's filter chain. When the image for the display is updated,
+ * the text is passed through each filter in the chain. Each filter can define
+ * hotspots which correspond to certain strings (such as URLs or particular words).
+ * Depending on the type of the hotspots created by the filter ( returned by Filter::Hotspot::type() )
+ * the view will draw visual cues such as underlines on mouse-over for links or translucent
+ * rectangles for markers.
+ *
+ * To add a new filter to the view, call:
+ * viewWidget->filterChain()->addFilter( filterObject );
+ */
+ FilterChain* filterChain() const;
+
+ /**
+ * Updates the filters in the display's filter chain. This will cause
+ * the hotspots to be updated to match the current image.
+ *
+ * WARNING: This function can be expensive depending on the
+ * image size and number of filters in the filterChain()
+ *
+ * TODO - This API does not really allow efficient usage. Revise it so
+ * that the processing can be done in a better way.
+ *
+ * eg:
+ * - Area of interest may be known ( eg. mouse cursor hovering
+ * over an area )
+ */
+ void processFilters();
+
+ /**
+ * Returns a list of menu actions created by the filters for the content
+ * at the given @p position.
+ */
+ QList<QAction*> filterActions(const QPoint& position);
+
+ /** Returns true if the cursor is set to blink or false otherwise. */
+ bool blinkingCursor() { return _hasBlinkingCursor; }
+ /** Specifies whether or not the cursor blinks. */
+ void setBlinkingCursor(bool blink);
+
+ void setCtrlDrag(bool enable) { _ctrlDrag=enable; }
+ bool ctrlDrag() { return _ctrlDrag; }
+
+ /**
+ * This enum describes the methods for selecting text when
+ * the user triple-clicks within the display.
+ */
+ enum TripleClickMode
+ {
+ /** Select the whole line underneath the cursor. */
+ SelectWholeLine,
+ /** Select from the current cursor position to the end of the line. */
+ SelectForwardsFromCursor
+ };
+ /** Sets how the text is selected when the user triple clicks within the display. */
+ void setTripleClickMode(TripleClickMode mode) { _tripleClickMode = mode; }
+ /** See setTripleClickSelectionMode() */
+ TripleClickMode tripleClickMode() { return _tripleClickMode; }
+
+ void setLineSpacing(uint);
+ uint lineSpacing() const;
+
+ void emitSelection(bool useXselection,bool appendReturn);
+
+ /**
+ * This enum describes the available shapes for the keyboard cursor.
+ * See setKeyboardCursorShape()
+ */
+ enum KeyboardCursorShape
+ {
+ /** A rectangular block which covers the entire area of the cursor character. */
+ BlockCursor,
+ /**
+ * A single flat line which occupies the space at the bottom of the cursor
+ * character's area.
+ */
+ UnderlineCursor,
+ /**
+ * An cursor shaped like the capital letter 'I', similar to the IBeam
+ * cursor used in Qt/KDE text editors.
+ */
+ IBeamCursor
+ };
+ /**
+ * Sets the shape of the keyboard cursor. This is the cursor drawn
+ * at the position in the terminal where keyboard input will appear.
+ *
+ * In addition the terminal display widget also has a cursor for
+ * the mouse pointer, which can be set using the QWidget::setCursor()
+ * method.
+ *
+ * Defaults to BlockCursor
+ */
+ void setKeyboardCursorShape(KeyboardCursorShape shape);
+ /**
+ * Returns the shape of the keyboard cursor. See setKeyboardCursorShape()
+ */
+ KeyboardCursorShape keyboardCursorShape() const;
+
+ /**
+ * Sets the color used to draw the keyboard cursor.
+ *
+ * The keyboard cursor defaults to using the foreground color of the character
+ * underneath it.
+ *
+ * @param useForegroundColor If true, the cursor color will change to match
+ * the foreground color of the character underneath it as it is moved, in this
+ * case, the @p color parameter is ignored and the color of the character
+ * under the cursor is inverted to ensure that it is still readable.
+ * @param color The color to use to draw the cursor. This is only taken into
+ * account if @p useForegroundColor is false.
+ */
+ void setKeyboardCursorColor(bool useForegroundColor , const QColor& color);
+
+ /**
+ * Returns the color of the keyboard cursor, or an invalid color if the keyboard
+ * cursor color is set to change according to the foreground color of the character
+ * underneath it.
+ */
+ QColor keyboardCursorColor() const;
+
+ /**
+ * Returns the number of lines of text which can be displayed in the widget.
+ *
+ * This will depend upon the height of the widget and the current font.
+ * See fontHeight()
+ */
+ int lines() { return _lines; }
+ /**
+ * Returns the number of characters of text which can be displayed on
+ * each line in the widget.
+ *
+ * This will depend upon the width of the widget and the current font.
+ * See fontWidth()
+ */
+ int columns() { return _columns; }
+
+ /**
+ * Returns the height of the characters in the font used to draw the text in the display.
+ */
+ int fontHeight() { return _fontHeight; }
+ /**
+ * Returns the width of the characters in the display.
+ * This assumes the use of a fixed-width font.
+ */
+ int fontWidth() { return _fontWidth; }
+
+ void setSize(int cols, int lins);
+ void setFixedSize(int cols, int lins);
+
+ // reimplemented
+ QSize sizeHint() const;
+
+ /**
+ * Sets which characters, in addition to letters and numbers,
+ * are regarded as being part of a word for the purposes
+ * of selecting words in the display by double clicking on them.
+ *
+ * The word boundaries occur at the first and last characters which
+ * are either a letter, number, or a character in @p wc
+ *
+ * @param wc An array of characters which are to be considered parts
+ * of a word ( in addition to letters and numbers ).
+ */
+ void setWordCharacters(const QString& wc);
+ /**
+ * Returns the characters which are considered part of a word for the
+ * purpose of selecting words in the display with the mouse.
+ *
+ * @see setWordCharacters()
+ */
+ QString wordCharacters() { return _wordCharacters; }
+
+ /**
+ * Sets the type of effect used to alert the user when a 'bell' occurs in the
+ * terminal session.
+ *
+ * The terminal session can trigger the bell effect by calling bell() with
+ * the alert message.
+ */
+ void setBellMode(int mode);
+ /**
+ * Returns the type of effect used to alert the user when a 'bell' occurs in
+ * the terminal session.
+ *
+ * See setBellMode()
+ */
+ int bellMode() { return _bellMode; }
+
+ /**
+ * This enum describes the different types of sounds and visual effects which
+ * can be used to alert the user when a 'bell' occurs in the terminal
+ * session.
+ */
+ enum BellMode
+ {
+ /** A system beep. */
+ SystemBeepBell=0,
+ /**
+ * KDE notification. This may play a sound, show a passive popup
+ * or perform some other action depending on the user's settings.
+ */
+ NotifyBell=1,
+ /** A silent, visual bell (eg. inverting the display's colors briefly) */
+ VisualBell=2,
+ /** No bell effects */
+ NoBell=3
+ };
+
+ void setSelection(const QString &t);
+
+ /**
+ * Reimplemented. Has no effect. Use setVTFont() to change the font
+ * used to draw characters in the display.
+ */
+ virtual void setFont(const QFont &);
+
+ /** Returns the font used to draw characters in the display */
+ QFont getVTFont() { return font(); }
+
+ /**
+ * Sets the font used to draw the display. Has no effect if @p font
+ * is larger than the size of the display itself.
+ */
+ void setVTFont(const QFont& font);
+
+ /**
+ * Specified whether anti-aliasing of text in the terminal display
+ * is enabled or not. Defaults to enabled.
+ */
+ static void setAntialias( bool antialias ) { _antialiasText = antialias; }
+ /**
+ * Returns true if anti-aliasing of text in the terminal is enabled.
+ */
+ static bool antialias() { return _antialiasText; }
+
+ /**
+ * Sets whether or not the current height and width of the
+ * terminal in lines and columns is displayed whilst the widget
+ * is being resized.
+ */
+ void setTerminalSizeHint(bool on) { _terminalSizeHint=on; }
+ /**
+ * Returns whether or not the current height and width of
+ * the terminal in lines and columns is displayed whilst the widget
+ * is being resized.
+ */
+ bool terminalSizeHint() { return _terminalSizeHint; }
+ /**
+ * Sets whether the terminal size display is shown briefly
+ * after the widget is first shown.
+ *
+ * See setTerminalSizeHint() , isTerminalSizeHint()
+ */
+ void setTerminalSizeStartup(bool on) { _terminalSizeStartup=on; }
+
+ void setBidiEnabled(bool set) { _bidiEnabled=set; }
+ bool isBidiEnabled() { return _bidiEnabled; }
+
+ /**
+ * Sets the terminal screen section which is displayed in this widget.
+ * When updateImage() is called, the display fetches the latest character image from the
+ * the associated terminal screen window.
+ *
+ * In terms of the model-view paradigm, the ScreenWindow is the model which is rendered
+ * by the TerminalDisplay.
+ */
+ void setScreenWindow( ScreenWindow* window );
+ /** Returns the terminal screen section which is displayed in this widget. See setScreenWindow() */
+ ScreenWindow* screenWindow() const;
+
+ static bool HAVE_TRANSPARENCY;
+
+public slots:
+
+ /**
+ * Causes the terminal display to fetch the latest character image from the associated
+ * terminal screen ( see setScreenWindow() ) and redraw the display.
+ */
+ void updateImage();
+ /**
+ * Causes the terminal display to fetch the latest line status flags from the
+ * associated terminal screen ( see setScreenWindow() ).
+ */
+ void updateLineProperties();
+
+ /** Copies the selected text to the clipboard. */
+ void copyClipboard();
+ /**
+ * Pastes the content of the clipboard into the
+ * display.
+ */
+ void pasteClipboard();
+ /**
+ * Pastes the content of the selection into the
+ * display.
+ */
+ void pasteSelection();
+
+ /**
+ * Changes whether the flow control warning box should be shown when the flow control
+ * stop key (Ctrl+S) are pressed.
+ */
+ void setFlowControlWarningEnabled(bool enabled);
+
+ /**
+ * Causes the widget to display or hide a message informing the user that terminal
+ * output has been suspended (by using the flow control key combination Ctrl+S)
+ *
+ * @param suspended True if terminal output has been suspended and the warning message should
+ * be shown or false to indicate that terminal output has been resumed and that
+ * the warning message should disappear.
+ */
+ void outputSuspended(bool suspended);
+
+ /**
+ * Sets whether the program whoose output is being displayed in the view
+ * is interested in mouse events.
+ *
+ * If this is set to true, mouse signals will be emitted by the view when the user clicks, drags
+ * or otherwise moves the mouse inside the view.
+ * The user interaction needed to create selections will also change, and the user will be required
+ * to hold down the shift key to create a selection or perform other mouse activities inside the
+ * view area - since the program running in the terminal is being allowed to handle normal mouse
+ * events itself.
+ *
+ * @param usesMouse Set to true if the program running in the terminal is interested in mouse events
+ * or false otherwise.
+ */
+ void setUsesMouse(bool usesMouse);
+
+ /** See setUsesMouse() */
+ bool usesMouse() const;
+
+ /**
+ * Shows a notification that a bell event has occurred in the terminal.
+ * TODO: More documentation here
+ */
+ void bell(const QString& message);
+
+signals:
+
+ /**
+ * Emitted when the user presses a key whilst the terminal widget has focus.
+ */
+ void keyPressedSignal(QKeyEvent *e);
+
+ /**
+ * Emitted when the user presses the suspend or resume flow control key combinations
+ *
+ * @param suspend true if the user pressed Ctrl+S (the suspend output key combination) or
+ * false if the user pressed Ctrl+Q (the resume output key combination)
+ */
+ void flowControlKeyPressed(bool suspend);
+
+ /**
+ * A mouse event occurred.
+ * @param button The mouse button (0 for left button, 1 for middle button, 2 for right button, 3 for release)
+ * @param column The character column where the event occurred
+ * @param line The character row where the event occurred
+ * @param eventType The type of event. 0 for a mouse press / release or 1 for mouse motion
+ */
+ void mouseSignal(int button, int column, int line, int eventType);
+ void changedFontMetricSignal(int height, int width);
+ void changedContentSizeSignal(int height, int width);
+
+ /**
+ * Emitted when the user right clicks on the display, or right-clicks with the Shift
+ * key held down if usesMouse() is true.
+ *
+ * This can be used to display a context menu.
+ */
+ void configureRequest( TerminalDisplay*, int state, const QPoint& position );
+
+ void isBusySelecting(bool);
+ void sendStringToEmu(const char*);
+
+protected:
+ virtual bool event( QEvent * );
+
+ virtual void paintEvent( QPaintEvent * );
+
+ virtual void showEvent(QShowEvent*);
+ virtual void hideEvent(QHideEvent*);
+ virtual void resizeEvent(QResizeEvent*);
+
+ virtual void fontChange(const QFont &font);
+
+ virtual void keyPressEvent(QKeyEvent* event);
+ virtual void mouseDoubleClickEvent(QMouseEvent* ev);
+ virtual void mousePressEvent( QMouseEvent* );
+ virtual void mouseReleaseEvent( QMouseEvent* );
+ virtual void mouseMoveEvent( QMouseEvent* );
+ virtual void extendSelection( const QPoint& pos );
+ virtual void wheelEvent( QWheelEvent* );
+
+ virtual bool focusNextPrevChild( bool next );
+
+ // drag and drop
+ virtual void dragEnterEvent(QDragEnterEvent* event);
+ virtual void dropEvent(QDropEvent* event);
+ void doDrag();
+ enum DragState { diNone, diPending, diDragging };
+
+ struct _dragInfo {
+ DragState state;
+ QPoint start;
+ QDrag *dragObject;
+ } dragInfo;
+
+ virtual int charClass(quint16) const;
+
+ void clearImage();
+
+ void mouseTripleClickEvent(QMouseEvent* ev);
+
+ // reimplemented
+ virtual void inputMethodEvent ( QInputMethodEvent* event );
+ virtual QVariant inputMethodQuery( Qt::InputMethodQuery query ) const;
+
+protected slots:
+
+ void scrollBarPositionChanged(int value);
+ void blinkEvent();
+ void blinkCursorEvent();
+
+ //Renables bell noises and visuals. Used to disable further bells for a short period of time
+ //after emitting the first in a sequence of bell events.
+ void enableBell();
+
+private slots:
+
+ void swapColorTable();
+ void tripleClickTimeout(); // resets possibleTripleClick
+
+private:
+
+ // -- Drawing helpers --
+
+ // divides the part of the display specified by 'rect' into
+ // fragments according to their colors and styles and calls
+ // drawTextFragment() to draw the fragments
+ void drawContents(QPainter &paint, const QRect &rect);
+ // draws a section of text, all the text in this section
+ // has a common color and style
+ void drawTextFragment(QPainter& painter, const QRect& rect,
+ const QString& text, const Character* style);
+ // draws the background for a text fragment
+ // if useOpacitySetting is true then the color's alpha value will be set to
+ // the display's transparency (set with setOpacity()), otherwise the background
+ // will be drawn fully opaque
+ void drawBackground(QPainter& painter, const QRect& rect, const QColor& color,
+ bool useOpacitySetting);
+ // draws the cursor character
+ void drawCursor(QPainter& painter, const QRect& rect , const QColor& foregroundColor,
+ const QColor& backgroundColor , bool& invertColors);
+ // draws the characters or line graphics in a text fragment
+ void drawCharacters(QPainter& painter, const QRect& rect, const QString& text,
+ const Character* style, bool invertCharacterColor);
+ // draws a string of line graphics
+ void drawLineCharString(QPainter& painter, int x, int y,
+ const QString& str, const Character* attributes);
+
+ // draws the preedit string for input methods
+ void drawInputMethodPreeditString(QPainter& painter , const QRect& rect);
+
+ // --
+
+ // maps an area in the character image to an area on the widget
+ QRect imageToWidget(const QRect& imageArea) const;
+
+ // maps a point on the widget to the position ( ie. line and column )
+ // of the character at that point.
+ void getCharacterPosition(const QPoint& widgetPoint,int& line,int& column) const;
+
+ // the area where the preedit string for input methods will be draw
+ QRect preeditRect() const;
+
+ // shows a notification window in the middle of the widget indicating the terminal's
+ // current size in columns and lines
+ void showResizeNotification();
+
+ // scrolls the image by a number of lines.
+ // 'lines' may be positive ( to scroll the image down )
+ // or negative ( to scroll the image up )
+ // 'region' is the part of the image to scroll - currently only
+ // the top, bottom and height of 'region' are taken into account,
+ // the left and right are ignored.
+ void scrollImage(int lines , const QRect& region);
+
+ void calcGeometry();
+ void propagateSize();
+ void updateImageSize();
+ void makeImage();
+
+ void paintFilters(QPainter& painter);
+
+ // returns a region covering all of the areas of the widget which contain
+ // a hotspot
+ QRegion hotSpotRegion() const;
+
+ // returns the position of the cursor in columns and lines
+ QPoint cursorPosition() const;
+
+ // the window onto the terminal screen which this display
+ // is currently showing.
+ QPointer<ScreenWindow> _screenWindow;
+
+ bool _allowBell;
+
+ QGridLayout* _gridLayout;
+
+ bool _fixedFont; // has fixed pitch
+ int _fontHeight; // height
+ int _fontWidth; // width
+ int _fontAscent; // ascend
+
+ int _leftMargin; // offset
+ int _topMargin; // offset
+
+ int _lines; // the number of lines that can be displayed in the widget
+ int _columns; // the number of columns that can be displayed in the widget
+
+ int _usedLines; // the number of lines that are actually being used, this will be less
+ // than 'lines' if the character image provided with setImage() is smaller
+ // than the maximum image size which can be displayed
+
+ int _usedColumns; // the number of columns that are actually being used, this will be less
+ // than 'columns' if the character image provided with setImage() is smaller
+ // than the maximum image size which can be displayed
+
+ int _contentHeight;
+ int _contentWidth;
+ Character* _image; // [lines][columns]
+ // only the area [usedLines][usedColumns] in the image contains valid data
+
+ int _imageSize;
+ QVector<LineProperty> _lineProperties;
+
+ ColorEntry _colorTable[TABLE_COLORS];
+ uint _randomSeed;
+
+ bool _resizing;
+ bool _terminalSizeHint;
+ bool _terminalSizeStartup;
+ bool _bidiEnabled;
+ bool _mouseMarks;
+
+ QPoint _iPntSel; // initial selection point
+ QPoint _pntSel; // current selection point
+ QPoint _tripleSelBegin; // help avoid flicker
+ int _actSel; // selection state
+ bool _wordSelectionMode;
+ bool _lineSelectionMode;
+ bool _preserveLineBreaks;
+ bool _columnSelectionMode;
+
+ QClipboard* _clipboard;
+ QScrollBar* _scrollBar;
+ ScrollBarPosition _scrollbarLocation;
+ QString _wordCharacters;
+ int _bellMode;
+
+ bool _blinking; // hide text in paintEvent
+ bool _hasBlinker; // has characters to blink
+ bool _cursorBlinking; // hide cursor in paintEvent
+ bool _hasBlinkingCursor; // has blinking cursor enabled
+ bool _ctrlDrag; // require Ctrl key for drag
+ TripleClickMode _tripleClickMode;
+ bool _isFixedSize; //Columns / lines are locked.
+ QTimer* _blinkTimer; // active when hasBlinker
+ QTimer* _blinkCursorTimer; // active when hasBlinkingCursor
+
+// KMenu* _drop;
+ QString _dropText;
+ int _dndFileCount;
+
+ bool _possibleTripleClick; // is set in mouseDoubleClickEvent and deleted
+ // after QApplication::doubleClickInterval() delay
+
+
+ QLabel* _resizeWidget;
+ QTimer* _resizeTimer;
+
+ bool _flowControlWarningEnabled;
+
+ //widgets related to the warning message that appears when the user presses Ctrl+S to suspend
+ //terminal output - informing them what has happened and how to resume output
+ QLabel* _outputSuspendedLabel;
+
+ uint _lineSpacing;
+
+ bool _colorsInverted; // true during visual bell
+
+ QSize _size;
+
+ QRgb _blendColor;
+
+ // list of filters currently applied to the display. used for links and
+ // search highlight
+ TerminalImageFilterChain* _filterChain;
+ QRect _mouseOverHotspotArea;
+
+ KeyboardCursorShape _cursorShape;
+
+ // custom cursor color. if this is invalid then the foreground
+ // color of the character under the cursor is used
+ QColor _cursorColor;
+
+
+ struct InputMethodData
+ {
+ QString preeditString;
+ QRect previousPreeditRect;
+ };
+ InputMethodData _inputMethodData;
+
+ static bool _antialiasText; // do we antialias or not
+
+ //the delay in milliseconds between redrawing blinking text
+ static const int BLINK_DELAY = 500;
+ static const int DEFAULT_LEFT_MARGIN = 1;
+ static const int DEFAULT_TOP_MARGIN = 1;
+
+public:
+ static void setTransparencyEnabled(bool enable)
+ {
+ HAVE_TRANSPARENCY = enable;
+ }
+};
+
+}
+
+#endif // TERMINALDISPLAY_H
diff --git a/qtermwidget/Vt102Emulation.cpp b/qtermwidget/Vt102Emulation.cpp
new file mode 100644
index 0000000..3ed912f
--- /dev/null
+++ b/qtermwidget/Vt102Emulation.cpp
@@ -0,0 +1,1266 @@
+/*
+ This file is part of Konsole, an X terminal.
+ Copyright (C) 1997,1998 by Lars Doelle <lars.doelle@on-line.de>
+
+ Rewritten for QT4 by e_k <e_k at users.sourceforge.net>, Copyright (C)2008
+
+ 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.
+*/
+
+// Own
+#include "Vt102Emulation.h"
+
+//#include <config-konsole.h>
+
+
+#if defined(__osf__) || defined(__APPLE__)
+#define AVOID_XKB
+#endif
+
+// this allows konsole to be compiled without XKB and XTEST extensions
+// even though it might be available on a particular system.
+#if defined(AVOID_XKB)
+#undef HAVE_XKB
+#endif
+
+// Standard
+#include <stdio.h>
+#include <unistd.h>
+#include <assert.h>
+
+// Qt
+#include <QtCore/QEvent>
+#include <QtGui/QKeyEvent>
+#include <QtCore/QByteRef>
+
+// KDE
+//#include <kdebug.h>
+//#include <klocale.h>
+
+// Konsole
+#include "KeyboardTranslator.h"
+#include "Screen.h"
+
+#if defined(HAVE_XKB)
+void scrolllock_set_off();
+void scrolllock_set_on();
+#endif
+
+using namespace Konsole;
+
+/* VT102 Terminal Emulation
+
+ This class puts together the screens, the pty and the widget to a
+ complete terminal emulation. Beside combining it's componentes, it
+ handles the emulations's protocol.
+
+ This module consists of the following sections:
+
+ - Constructor/Destructor
+ - Incoming Bytes Event pipeline
+ - Outgoing Bytes
+ - Mouse Events
+ - Keyboard Events
+ - Modes and Charset State
+ - Diagnostics
+*/
+
+/* ------------------------------------------------------------------------- */
+/* */
+/* Constructor / Destructor */
+/* */
+/* ------------------------------------------------------------------------- */
+
+
+Vt102Emulation::Vt102Emulation()
+ : Emulation(),
+ _titleUpdateTimer(new QTimer(this))
+{
+ _titleUpdateTimer->setSingleShot(true);
+
+ QObject::connect(_titleUpdateTimer , SIGNAL(timeout()) , this , SLOT(updateTitle()));
+
+ initTokenizer();
+ reset();
+}
+
+Vt102Emulation::~Vt102Emulation()
+{
+}
+
+void Vt102Emulation::clearEntireScreen()
+{
+ _currentScreen->clearEntireScreen();
+
+ bufferedUpdate();
+}
+
+void Vt102Emulation::reset()
+{
+ //kDebug(1211)<<"Vt102Emulation::reset() resetToken()";
+ resetToken();
+ //kDebug(1211)<<"Vt102Emulation::reset() resetModes()";
+ resetModes();
+ //kDebug(1211)<<"Vt102Emulation::reset() resetCharSet()";
+ resetCharset(0);
+ //kDebug(1211)<<"Vt102Emulation::reset() reset screen0()";
+ _screen[0]->reset();
+ //kDebug(1211)<<"Vt102Emulation::reset() resetCharSet()";
+ resetCharset(1);
+ //kDebug(1211)<<"Vt102Emulation::reset() reset _screen 1";
+ _screen[1]->reset();
+ //kDebug(1211)<<"Vt102Emulation::reset() setCodec()";
+ setCodec(LocaleCodec);
+ //kDebug(1211)<<"Vt102Emulation::reset() done";
+
+ bufferedUpdate();
+}
+
+/* ------------------------------------------------------------------------- */
+/* */
+/* Processing the incoming byte stream */
+/* */
+/* ------------------------------------------------------------------------- */
+
+/* Incoming Bytes Event pipeline
+
+ This section deals with decoding the incoming character stream.
+ Decoding means here, that the stream is first separated into `tokens'
+ which are then mapped to a `meaning' provided as operations by the
+ `Screen' class or by the emulation class itself.
+
+ The pipeline proceeds as follows:
+
+ - Tokenizing the ESC codes (onReceiveChar)
+ - VT100 code page translation of plain characters (applyCharset)
+ - Interpretation of ESC codes (tau)
+
+ The escape codes and their meaning are described in the
+ technical reference of this program.
+*/
+
+// Tokens ------------------------------------------------------------------ --
+
+/*
+ Since the tokens are the central notion if this section, we've put them
+ in front. They provide the syntactical elements used to represent the
+ terminals operations as byte sequences.
+
+ They are encodes here into a single machine word, so that we can later
+ switch over them easily. Depending on the token itself, additional
+ argument variables are filled with parameter values.
+
+ The tokens are defined below:
+
+ - CHR - Printable characters (32..255 but DEL (=127))
+ - CTL - Control characters (0..31 but ESC (= 27), DEL)
+ - ESC - Escape codes of the form <ESC><CHR but `[]()+*#'>
+ - ESC_DE - Escape codes of the form <ESC><any of `()+*#%'> C
+ - CSI_PN - Escape codes of the form <ESC>'[' {Pn} ';' {Pn} C
+ - CSI_PS - Escape codes of the form <ESC>'[' {Pn} ';' ... C
+ - CSI_PR - Escape codes of the form <ESC>'[' '?' {Pn} ';' ... C
+ - CSI_PE - Escape codes of the form <ESC>'[' '!' {Pn} ';' ... C
+ - VT52 - VT52 escape codes
+ - <ESC><Chr>
+ - <ESC>'Y'{Pc}{Pc}
+ - XTE_HA - Xterm hacks <ESC>`]' {Pn} `;' {Text} <BEL>
+ note that this is handled differently
+
+ The last two forms allow list of arguments. Since the elements of
+ the lists are treated individually the same way, they are passed
+ as individual tokens to the interpretation. Further, because the
+ meaning of the parameters are names (althought represented as numbers),
+ they are includes within the token ('N').
+
+*/
+
+#define TY_CONSTR(T,A,N) ( ((((int)N) & 0xffff) << 16) | ((((int)A) & 0xff) << 8) | (((int)T) & 0xff) )
+
+#define TY_CHR( ) TY_CONSTR(0,0,0)
+#define TY_CTL(A ) TY_CONSTR(1,A,0)
+#define TY_ESC(A ) TY_CONSTR(2,A,0)
+#define TY_ESC_CS(A,B) TY_CONSTR(3,A,B)
+#define TY_ESC_DE(A ) TY_CONSTR(4,A,0)
+#define TY_CSI_PS(A,N) TY_CONSTR(5,A,N)
+#define TY_CSI_PN(A ) TY_CONSTR(6,A,0)
+#define TY_CSI_PR(A,N) TY_CONSTR(7,A,N)
+
+#define TY_VT52(A ) TY_CONSTR(8,A,0)
+
+#define TY_CSI_PG(A ) TY_CONSTR(9,A,0)
+
+#define TY_CSI_PE(A ) TY_CONSTR(10,A,0)
+
+// Tokenizer --------------------------------------------------------------- --
+
+/* The tokenizers state
+
+ The state is represented by the buffer (pbuf, ppos),
+ and accompanied by decoded arguments kept in (argv,argc).
+ Note that they are kept internal in the tokenizer.
+*/
+
+void Vt102Emulation::resetToken()
+{
+ ppos = 0; argc = 0; argv[0] = 0; argv[1] = 0;
+}
+
+void Vt102Emulation::addDigit(int dig)
+{
+ argv[argc] = 10*argv[argc] + dig;
+}
+
+void Vt102Emulation::addArgument()
+{
+ argc = qMin(argc+1,MAXARGS-1);
+ argv[argc] = 0;
+}
+
+void Vt102Emulation::pushToToken(int cc)
+{
+ pbuf[ppos] = cc;
+ ppos = qMin(ppos+1,MAXPBUF-1);
+}
+
+// Character Classes used while decoding
+
+#define CTL 1
+#define CHR 2
+#define CPN 4
+#define DIG 8
+#define SCS 16
+#define GRP 32
+#define CPS 64
+
+void Vt102Emulation::initTokenizer()
+{ int i; quint8* s;
+ for(i = 0; i < 256; i++) tbl[ i] = 0;
+ for(i = 0; i < 32; i++) tbl[ i] |= CTL;
+ for(i = 32; i < 256; i++) tbl[ i] |= CHR;
+ for(s = (quint8*)"@ABCDGHILMPSTXZcdfry"; *s; s++) tbl[*s] |= CPN;
+// resize = \e[8;<row>;<col>t
+ for(s = (quint8*)"t"; *s; s++) tbl[*s] |= CPS;
+ for(s = (quint8*)"0123456789" ; *s; s++) tbl[*s] |= DIG;
+ for(s = (quint8*)"()+*%" ; *s; s++) tbl[*s] |= SCS;
+ for(s = (quint8*)"()+*#[]%" ; *s; s++) tbl[*s] |= GRP;
+ resetToken();
+}
+
+/* Ok, here comes the nasty part of the decoder.
+
+ Instead of keeping an explicit state, we deduce it from the
+ token scanned so far. It is then immediately combined with
+ the current character to form a scanning decision.
+
+ This is done by the following defines.
+
+ - P is the length of the token scanned so far.
+ - L (often P-1) is the position on which contents we base a decision.
+ - C is a character or a group of characters (taken from 'tbl').
+
+ Note that they need to applied in proper order.
+*/
+
+#define lec(P,L,C) (p == (P) && s[(L)] == (C))
+#define lun( ) (p == 1 && cc >= 32 )
+#define les(P,L,C) (p == (P) && s[L] < 256 && (tbl[s[(L)]] & (C)) == (C))
+#define eec(C) (p >= 3 && cc == (C))
+#define ees(C) (p >= 3 && cc < 256 && (tbl[ cc ] & (C)) == (C))
+#define eps(C) (p >= 3 && s[2] != '?' && s[2] != '!' && s[2] != '>' && cc < 256 && (tbl[ cc ] & (C)) == (C))
+#define epp( ) (p >= 3 && s[2] == '?' )
+#define epe( ) (p >= 3 && s[2] == '!' )
+#define egt( ) (p >= 3 && s[2] == '>' )
+#define Xpe (ppos>=2 && pbuf[1] == ']' )
+#define Xte (Xpe && cc == 7 )
+#define ces(C) ( cc < 256 && (tbl[ cc ] & (C)) == (C) && !Xte)
+
+#define ESC 27
+#define CNTL(c) ((c)-'@')
+
+// process an incoming unicode character
+
+void Vt102Emulation::receiveChar(int cc)
+{
+ int i;
+ if (cc == 127) return; //VT100: ignore.
+
+ if (ces( CTL))
+ { // DEC HACK ALERT! Control Characters are allowed *within* esc sequences in VT100
+ // This means, they do neither a resetToken nor a pushToToken. Some of them, do
+ // of course. Guess this originates from a weakly layered handling of the X-on
+ // X-off protocol, which comes really below this level.
+ if (cc == CNTL('X') || cc == CNTL('Z') || cc == ESC) resetToken(); //VT100: CAN or SUB
+ if (cc != ESC) { tau( TY_CTL(cc+'@' ), 0, 0); return; }
+ }
+
+ pushToToken(cc); // advance the state
+
+ int* s = pbuf;
+ int p = ppos;
+
+ if (getMode(MODE_Ansi)) // decide on proper action
+ {
+ if (lec(1,0,ESC)) { return; }
+ if (lec(1,0,ESC+128)) { s[0] = ESC; receiveChar('['); return; }
+ if (les(2,1,GRP)) { return; }
+ if (Xte ) { XtermHack(); resetToken(); return; }
+ if (Xpe ) { return; }
+ if (lec(3,2,'?')) { return; }
+ if (lec(3,2,'>')) { return; }
+ if (lec(3,2,'!')) { return; }
+ if (lun( )) { tau( TY_CHR(), applyCharset(cc), 0); resetToken(); return; }
+ if (lec(2,0,ESC)) { tau( TY_ESC(s[1]), 0, 0); resetToken(); return; }
+ if (les(3,1,SCS)) { tau( TY_ESC_CS(s[1],s[2]), 0, 0); resetToken(); return; }
+ if (lec(3,1,'#')) { tau( TY_ESC_DE(s[2]), 0, 0); resetToken(); return; }
+ if (eps( CPN)) { tau( TY_CSI_PN(cc), argv[0],argv[1]); resetToken(); return; }
+
+// resize = \e[8;<row>;<col>t
+ if (eps( CPS)) { tau( TY_CSI_PS(cc, argv[0]), argv[1], argv[2]); resetToken(); return; }
+
+ if (epe( )) { tau( TY_CSI_PE(cc), 0, 0); resetToken(); return; }
+ if (ees( DIG)) { addDigit(cc-'0'); return; }
+ if (eec( ';')) { addArgument(); return; }
+ for (i=0;i<=argc;i++)
+ if ( epp( )) { tau( TY_CSI_PR(cc,argv[i]), 0, 0); }
+ else if(egt( )) { tau( TY_CSI_PG(cc ), 0, 0); } // spec. case for ESC]>0c or ESC]>c
+ else if (cc == 'm' && argc - i >= 4 && (argv[i] == 38 || argv[i] == 48) && argv[i+1] == 2)
+ { // ESC[ ... 48;2;<red>;<green>;<blue> ... m -or- ESC[ ... 38;2;<red>;<green>;<blue> ... m
+ i += 2;
+ tau( TY_CSI_PS(cc, argv[i-2]), COLOR_SPACE_RGB, (argv[i] << 16) | (argv[i+1] << 8) | argv[i+2]);
+ i += 2;
+ }
+ else if (cc == 'm' && argc - i >= 2 && (argv[i] == 38 || argv[i] == 48) && argv[i+1] == 5)
+ { // ESC[ ... 48;5;<index> ... m -or- ESC[ ... 38;5;<index> ... m
+ i += 2;
+ tau( TY_CSI_PS(cc, argv[i-2]), COLOR_SPACE_256, argv[i]);
+ }
+ else { tau( TY_CSI_PS(cc,argv[i]), 0, 0); }
+ resetToken();
+ }
+ else // mode VT52
+ {
+ if (lec(1,0,ESC)) return;
+ if (les(1,0,CHR)) { tau( TY_CHR( ), s[0], 0); resetToken(); return; }
+ if (lec(2,1,'Y')) return;
+ if (lec(3,1,'Y')) return;
+ if (p < 4) { tau( TY_VT52(s[1] ), 0, 0); resetToken(); return; }
+ tau( TY_VT52(s[1] ), s[2],s[3]); resetToken(); return;
+ }
+}
+
+void Vt102Emulation::XtermHack()
+{ int i,arg = 0;
+ for (i = 2; i < ppos && '0'<=pbuf[i] && pbuf[i]<'9' ; i++)
+ arg = 10*arg + (pbuf[i]-'0');
+ if (pbuf[i] != ';') { ReportErrorToken(); return; }
+ QChar *str = new QChar[ppos-i-2];
+ for (int j = 0; j < ppos-i-2; j++) str[j] = pbuf[i+1+j];
+ QString unistr(str,ppos-i-2);
+
+ // arg == 1 doesn't change the title. In XTerm it only changes the icon name
+ // (btw: arg=0 changes title and icon, arg=1 only icon, arg=2 only title
+// emit changeTitle(arg,unistr);
+ _pendingTitleUpdates[arg] = unistr;
+ _titleUpdateTimer->start(20);
+
+ delete [] str;
+}
+
+void Vt102Emulation::updateTitle()
+{
+ QListIterator<int> iter( _pendingTitleUpdates.keys() );
+ while (iter.hasNext()) {
+ int arg = iter.next();
+ emit titleChanged( arg , _pendingTitleUpdates[arg] );
+ }
+
+ _pendingTitleUpdates.clear();
+}
+
+// Interpreting Codes ---------------------------------------------------------
+
+/*
+ Now that the incoming character stream is properly tokenized,
+ meaning is assigned to them. These are either operations of
+ the current _screen, or of the emulation class itself.
+
+ The token to be interpreteted comes in as a machine word
+ possibly accompanied by two parameters.
+
+ Likewise, the operations assigned to, come with up to two
+ arguments. One could consider to make up a proper table
+ from the function below.
+
+ The technical reference manual provides more information
+ about this mapping.
+*/
+
+void Vt102Emulation::tau( int token, int p, int q )
+{
+#if 0
+int N = (token>>0)&0xff;
+int A = (token>>8)&0xff;
+switch( N )
+{
+ case 0: printf("%c", (p < 128) ? p : '?');
+ break;
+ case 1: if (A == 'J') printf("\r");
+ else if (A == 'M') printf("\n");
+ else printf("CTL-%c ", (token>>8)&0xff);
+ break;
+ case 2: printf("ESC-%c ", (token>>8)&0xff);
+ break;
+ case 3: printf("ESC_CS-%c-%c ", (token>>8)&0xff, (token>>16)&0xff);
+ break;
+ case 4: printf("ESC_DE-%c ", (token>>8)&0xff);
+ break;
+ case 5: printf("CSI-PS-%c-%d", (token>>8)&0xff, (token>>16)&0xff );
+ break;
+ case 6: printf("CSI-PN-%c [%d]", (token>>8)&0xff, p);
+ break;
+ case 7: printf("CSI-PR-%c-%d", (token>>8)&0xff, (token>>16)&0xff );
+ break;
+ case 8: printf("VT52-%c", (token>>8)&0xff);
+ break;
+ case 9: printf("CSI-PG-%c", (token>>8)&0xff);
+ break;
+ case 10: printf("CSI-PE-%c", (token>>8)&0xff);
+ break;
+}
+#endif
+
+ switch (token)
+ {
+
+ case TY_CHR( ) : _currentScreen->ShowCharacter (p ); break; //UTF16
+
+ // 127 DEL : ignored on input
+
+ case TY_CTL('@' ) : /* NUL: ignored */ break;
+ case TY_CTL('A' ) : /* SOH: ignored */ break;
+ case TY_CTL('B' ) : /* STX: ignored */ break;
+ case TY_CTL('C' ) : /* ETX: ignored */ break;
+ case TY_CTL('D' ) : /* EOT: ignored */ break;
+ case TY_CTL('E' ) : reportAnswerBack ( ); break; //VT100
+ case TY_CTL('F' ) : /* ACK: ignored */ break;
+ case TY_CTL('G' ) : emit stateSet(NOTIFYBELL);
+ break; //VT100
+ case TY_CTL('H' ) : _currentScreen->BackSpace ( ); break; //VT100
+ case TY_CTL('I' ) : _currentScreen->Tabulate ( ); break; //VT100
+ case TY_CTL('J' ) : _currentScreen->NewLine ( ); break; //VT100
+ case TY_CTL('K' ) : _currentScreen->NewLine ( ); break; //VT100
+ case TY_CTL('L' ) : _currentScreen->NewLine ( ); break; //VT100
+ case TY_CTL('M' ) : _currentScreen->Return ( ); break; //VT100
+
+ case TY_CTL('N' ) : useCharset ( 1); break; //VT100
+ case TY_CTL('O' ) : useCharset ( 0); break; //VT100
+
+ case TY_CTL('P' ) : /* DLE: ignored */ break;
+ case TY_CTL('Q' ) : /* DC1: XON continue */ break; //VT100
+ case TY_CTL('R' ) : /* DC2: ignored */ break;
+ case TY_CTL('S' ) : /* DC3: XOFF halt */ break; //VT100
+ case TY_CTL('T' ) : /* DC4: ignored */ break;
+ case TY_CTL('U' ) : /* NAK: ignored */ break;
+ case TY_CTL('V' ) : /* SYN: ignored */ break;
+ case TY_CTL('W' ) : /* ETB: ignored */ break;
+ case TY_CTL('X' ) : _currentScreen->ShowCharacter ( 0x2592); break; //VT100
+ case TY_CTL('Y' ) : /* EM : ignored */ break;
+ case TY_CTL('Z' ) : _currentScreen->ShowCharacter ( 0x2592); break; //VT100
+ case TY_CTL('[' ) : /* ESC: cannot be seen here. */ break;
+ case TY_CTL('\\' ) : /* FS : ignored */ break;
+ case TY_CTL(']' ) : /* GS : ignored */ break;
+ case TY_CTL('^' ) : /* RS : ignored */ break;
+ case TY_CTL('_' ) : /* US : ignored */ break;
+
+ case TY_ESC('D' ) : _currentScreen->index ( ); break; //VT100
+ case TY_ESC('E' ) : _currentScreen->NextLine ( ); break; //VT100
+ case TY_ESC('H' ) : _currentScreen->changeTabStop (true ); break; //VT100
+ case TY_ESC('M' ) : _currentScreen->reverseIndex ( ); break; //VT100
+ case TY_ESC('Z' ) : reportTerminalType ( ); break;
+ case TY_ESC('c' ) : reset ( ); break;
+
+ case TY_ESC('n' ) : useCharset ( 2); break;
+ case TY_ESC('o' ) : useCharset ( 3); break;
+ case TY_ESC('7' ) : saveCursor ( ); break;
+ case TY_ESC('8' ) : restoreCursor ( ); break;
+
+ case TY_ESC('=' ) : setMode (MODE_AppKeyPad); break;
+ case TY_ESC('>' ) : resetMode (MODE_AppKeyPad); break;
+ case TY_ESC('<' ) : setMode (MODE_Ansi ); break; //VT100
+
+ case TY_ESC_CS('(', '0') : setCharset (0, '0'); break; //VT100
+ case TY_ESC_CS('(', 'A') : setCharset (0, 'A'); break; //VT100
+ case TY_ESC_CS('(', 'B') : setCharset (0, 'B'); break; //VT100
+
+ case TY_ESC_CS(')', '0') : setCharset (1, '0'); break; //VT100
+ case TY_ESC_CS(')', 'A') : setCharset (1, 'A'); break; //VT100
+ case TY_ESC_CS(')', 'B') : setCharset (1, 'B'); break; //VT100
+
+ case TY_ESC_CS('*', '0') : setCharset (2, '0'); break; //VT100
+ case TY_ESC_CS('*', 'A') : setCharset (2, 'A'); break; //VT100
+ case TY_ESC_CS('*', 'B') : setCharset (2, 'B'); break; //VT100
+
+ case TY_ESC_CS('+', '0') : setCharset (3, '0'); break; //VT100
+ case TY_ESC_CS('+', 'A') : setCharset (3, 'A'); break; //VT100
+ case TY_ESC_CS('+', 'B') : setCharset (3, 'B'); break; //VT100
+
+ case TY_ESC_CS('%', 'G') : setCodec (Utf8Codec ); break; //LINUX
+ case TY_ESC_CS('%', '@') : setCodec (LocaleCodec ); break; //LINUX
+
+ case TY_ESC_DE('3' ) : /* Double height line, top half */
+ _currentScreen->setLineProperty( LINE_DOUBLEWIDTH , true );
+ _currentScreen->setLineProperty( LINE_DOUBLEHEIGHT , true );
+ break;
+ case TY_ESC_DE('4' ) : /* Double height line, bottom half */
+ _currentScreen->setLineProperty( LINE_DOUBLEWIDTH , true );
+ _currentScreen->setLineProperty( LINE_DOUBLEHEIGHT , true );
+ break;
+ case TY_ESC_DE('5' ) : /* Single width, single height line*/
+ _currentScreen->setLineProperty( LINE_DOUBLEWIDTH , false);
+ _currentScreen->setLineProperty( LINE_DOUBLEHEIGHT , false);
+ break;
+ case TY_ESC_DE('6' ) : /* Double width, single height line*/
+ _currentScreen->setLineProperty( LINE_DOUBLEWIDTH , true);
+ _currentScreen->setLineProperty( LINE_DOUBLEHEIGHT , false);
+ break;
+ case TY_ESC_DE('8' ) : _currentScreen->helpAlign ( ); break;
+
+// resize = \e[8;<row>;<col>t
+ case TY_CSI_PS('t', 8) : setImageSize( q /* colums */, p /* lines */ ); break;
+
+// change tab text color : \e[28;<color>t color: 0-16,777,215
+ case TY_CSI_PS('t', 28) : emit changeTabTextColorRequest ( p ); break;
+
+ case TY_CSI_PS('K', 0) : _currentScreen->clearToEndOfLine ( ); break;
+ case TY_CSI_PS('K', 1) : _currentScreen->clearToBeginOfLine ( ); break;
+ case TY_CSI_PS('K', 2) : _currentScreen->clearEntireLine ( ); break;
+ case TY_CSI_PS('J', 0) : _currentScreen->clearToEndOfScreen ( ); break;
+ case TY_CSI_PS('J', 1) : _currentScreen->clearToBeginOfScreen ( ); break;
+ case TY_CSI_PS('J', 2) : _currentScreen->clearEntireScreen ( ); break;
+ case TY_CSI_PS('g', 0) : _currentScreen->changeTabStop (false ); break; //VT100
+ case TY_CSI_PS('g', 3) : _currentScreen->clearTabStops ( ); break; //VT100
+ case TY_CSI_PS('h', 4) : _currentScreen-> setMode (MODE_Insert ); break;
+ case TY_CSI_PS('h', 20) : setMode (MODE_NewLine ); break;
+ case TY_CSI_PS('i', 0) : /* IGNORE: attached printer */ break; //VT100
+ case TY_CSI_PS('l', 4) : _currentScreen-> resetMode (MODE_Insert ); break;
+ case TY_CSI_PS('l', 20) : resetMode (MODE_NewLine ); break;
+ case TY_CSI_PS('s', 0) : saveCursor ( ); break;
+ case TY_CSI_PS('u', 0) : restoreCursor ( ); break;
+
+ case TY_CSI_PS('m', 0) : _currentScreen->setDefaultRendition ( ); break;
+ case TY_CSI_PS('m', 1) : _currentScreen-> setRendition (RE_BOLD ); break; //VT100
+ case TY_CSI_PS('m', 4) : _currentScreen-> setRendition (RE_UNDERLINE); break; //VT100
+ case TY_CSI_PS('m', 5) : _currentScreen-> setRendition (RE_BLINK ); break; //VT100
+ case TY_CSI_PS('m', 7) : _currentScreen-> setRendition (RE_REVERSE ); break;
+ case TY_CSI_PS('m', 10) : /* IGNORED: mapping related */ break; //LINUX
+ case TY_CSI_PS('m', 11) : /* IGNORED: mapping related */ break; //LINUX
+ case TY_CSI_PS('m', 12) : /* IGNORED: mapping related */ break; //LINUX
+ case TY_CSI_PS('m', 22) : _currentScreen->resetRendition (RE_BOLD ); break;
+ case TY_CSI_PS('m', 24) : _currentScreen->resetRendition (RE_UNDERLINE); break;
+ case TY_CSI_PS('m', 25) : _currentScreen->resetRendition (RE_BLINK ); break;
+ case TY_CSI_PS('m', 27) : _currentScreen->resetRendition (RE_REVERSE ); break;
+
+ case TY_CSI_PS('m', 30) : _currentScreen->setForeColor (COLOR_SPACE_SYSTEM, 0); break;
+ case TY_CSI_PS('m', 31) : _currentScreen->setForeColor (COLOR_SPACE_SYSTEM, 1); break;
+ case TY_CSI_PS('m', 32) : _currentScreen->setForeColor (COLOR_SPACE_SYSTEM, 2); break;
+ case TY_CSI_PS('m', 33) : _currentScreen->setForeColor (COLOR_SPACE_SYSTEM, 3); break;
+ case TY_CSI_PS('m', 34) : _currentScreen->setForeColor (COLOR_SPACE_SYSTEM, 4); break;
+ case TY_CSI_PS('m', 35) : _currentScreen->setForeColor (COLOR_SPACE_SYSTEM, 5); break;
+ case TY_CSI_PS('m', 36) : _currentScreen->setForeColor (COLOR_SPACE_SYSTEM, 6); break;
+ case TY_CSI_PS('m', 37) : _currentScreen->setForeColor (COLOR_SPACE_SYSTEM, 7); break;
+
+ case TY_CSI_PS('m', 38) : _currentScreen->setForeColor (p, q); break;
+
+ case TY_CSI_PS('m', 39) : _currentScreen->setForeColor (COLOR_SPACE_DEFAULT, 0); break;
+
+ case TY_CSI_PS('m', 40) : _currentScreen->setBackColor (COLOR_SPACE_SYSTEM, 0); break;
+ case TY_CSI_PS('m', 41) : _currentScreen->setBackColor (COLOR_SPACE_SYSTEM, 1); break;
+ case TY_CSI_PS('m', 42) : _currentScreen->setBackColor (COLOR_SPACE_SYSTEM, 2); break;
+ case TY_CSI_PS('m', 43) : _currentScreen->setBackColor (COLOR_SPACE_SYSTEM, 3); break;
+ case TY_CSI_PS('m', 44) : _currentScreen->setBackColor (COLOR_SPACE_SYSTEM, 4); break;
+ case TY_CSI_PS('m', 45) : _currentScreen->setBackColor (COLOR_SPACE_SYSTEM, 5); break;
+ case TY_CSI_PS('m', 46) : _currentScreen->setBackColor (COLOR_SPACE_SYSTEM, 6); break;
+ case TY_CSI_PS('m', 47) : _currentScreen->setBackColor (COLOR_SPACE_SYSTEM, 7); break;
+
+ case TY_CSI_PS('m', 48) : _currentScreen->setBackColor (p, q); break;
+
+ case TY_CSI_PS('m', 49) : _currentScreen->setBackColor (COLOR_SPACE_DEFAULT, 1); break;
+
+ case TY_CSI_PS('m', 90) : _currentScreen->setForeColor (COLOR_SPACE_SYSTEM, 8); break;
+ case TY_CSI_PS('m', 91) : _currentScreen->setForeColor (COLOR_SPACE_SYSTEM, 9); break;
+ case TY_CSI_PS('m', 92) : _currentScreen->setForeColor (COLOR_SPACE_SYSTEM, 10); break;
+ case TY_CSI_PS('m', 93) : _currentScreen->setForeColor (COLOR_SPACE_SYSTEM, 11); break;
+ case TY_CSI_PS('m', 94) : _currentScreen->setForeColor (COLOR_SPACE_SYSTEM, 12); break;
+ case TY_CSI_PS('m', 95) : _currentScreen->setForeColor (COLOR_SPACE_SYSTEM, 13); break;
+ case TY_CSI_PS('m', 96) : _currentScreen->setForeColor (COLOR_SPACE_SYSTEM, 14); break;
+ case TY_CSI_PS('m', 97) : _currentScreen->setForeColor (COLOR_SPACE_SYSTEM, 15); break;
+
+ case TY_CSI_PS('m', 100) : _currentScreen->setBackColor (COLOR_SPACE_SYSTEM, 8); break;
+ case TY_CSI_PS('m', 101) : _currentScreen->setBackColor (COLOR_SPACE_SYSTEM, 9); break;
+ case TY_CSI_PS('m', 102) : _currentScreen->setBackColor (COLOR_SPACE_SYSTEM, 10); break;
+ case TY_CSI_PS('m', 103) : _currentScreen->setBackColor (COLOR_SPACE_SYSTEM, 11); break;
+ case TY_CSI_PS('m', 104) : _currentScreen->setBackColor (COLOR_SPACE_SYSTEM, 12); break;
+ case TY_CSI_PS('m', 105) : _currentScreen->setBackColor (COLOR_SPACE_SYSTEM, 13); break;
+ case TY_CSI_PS('m', 106) : _currentScreen->setBackColor (COLOR_SPACE_SYSTEM, 14); break;
+ case TY_CSI_PS('m', 107) : _currentScreen->setBackColor (COLOR_SPACE_SYSTEM, 15); break;
+
+ case TY_CSI_PS('n', 5) : reportStatus ( ); break;
+ case TY_CSI_PS('n', 6) : reportCursorPosition ( ); break;
+ case TY_CSI_PS('q', 0) : /* IGNORED: LEDs off */ break; //VT100
+ case TY_CSI_PS('q', 1) : /* IGNORED: LED1 on */ break; //VT100
+ case TY_CSI_PS('q', 2) : /* IGNORED: LED2 on */ break; //VT100
+ case TY_CSI_PS('q', 3) : /* IGNORED: LED3 on */ break; //VT100
+ case TY_CSI_PS('q', 4) : /* IGNORED: LED4 on */ break; //VT100
+ case TY_CSI_PS('x', 0) : reportTerminalParms ( 2); break; //VT100
+ case TY_CSI_PS('x', 1) : reportTerminalParms ( 3); break; //VT100
+
+ case TY_CSI_PN('@' ) : _currentScreen->insertChars (p ); break;
+ case TY_CSI_PN('A' ) : _currentScreen->cursorUp (p ); break; //VT100
+ case TY_CSI_PN('B' ) : _currentScreen->cursorDown (p ); break; //VT100
+ case TY_CSI_PN('C' ) : _currentScreen->cursorRight (p ); break; //VT100
+ case TY_CSI_PN('D' ) : _currentScreen->cursorLeft (p ); break; //VT100
+ case TY_CSI_PN('G' ) : _currentScreen->setCursorX (p ); break; //LINUX
+ case TY_CSI_PN('H' ) : _currentScreen->setCursorYX (p, q); break; //VT100
+ case TY_CSI_PN('I' ) : _currentScreen->Tabulate (p ); break;
+ case TY_CSI_PN('L' ) : _currentScreen->insertLines (p ); break;
+ case TY_CSI_PN('M' ) : _currentScreen->deleteLines (p ); break;
+ case TY_CSI_PN('P' ) : _currentScreen->deleteChars (p ); break;
+ case TY_CSI_PN('S' ) : _currentScreen->scrollUp (p ); break;
+ case TY_CSI_PN('T' ) : _currentScreen->scrollDown (p ); break;
+ case TY_CSI_PN('X' ) : _currentScreen->eraseChars (p ); break;
+ case TY_CSI_PN('Z' ) : _currentScreen->backTabulate (p ); break;
+ case TY_CSI_PN('c' ) : reportTerminalType ( ); break; //VT100
+ case TY_CSI_PN('d' ) : _currentScreen->setCursorY (p ); break; //LINUX
+ case TY_CSI_PN('f' ) : _currentScreen->setCursorYX (p, q); break; //VT100
+ case TY_CSI_PN('r' ) : setMargins (p, q); break; //VT100
+ case TY_CSI_PN('y' ) : /* IGNORED: Confidence test */ break; //VT100
+
+ case TY_CSI_PR('h', 1) : setMode (MODE_AppCuKeys); break; //VT100
+ case TY_CSI_PR('l', 1) : resetMode (MODE_AppCuKeys); break; //VT100
+ case TY_CSI_PR('s', 1) : saveMode (MODE_AppCuKeys); break; //FIXME
+ case TY_CSI_PR('r', 1) : restoreMode (MODE_AppCuKeys); break; //FIXME
+
+ case TY_CSI_PR('l', 2) : resetMode (MODE_Ansi ); break; //VT100
+
+ case TY_CSI_PR('h', 3) : clearScreenAndSetColumns(132); break; //VT100
+ case TY_CSI_PR('l', 3) : clearScreenAndSetColumns(80); break; //VT100
+
+ case TY_CSI_PR('h', 4) : /* IGNORED: soft scrolling */ break; //VT100
+ case TY_CSI_PR('l', 4) : /* IGNORED: soft scrolling */ break; //VT100
+
+ case TY_CSI_PR('h', 5) : _currentScreen-> setMode (MODE_Screen ); break; //VT100
+ case TY_CSI_PR('l', 5) : _currentScreen-> resetMode (MODE_Screen ); break; //VT100
+
+ case TY_CSI_PR('h', 6) : _currentScreen-> setMode (MODE_Origin ); break; //VT100
+ case TY_CSI_PR('l', 6) : _currentScreen-> resetMode (MODE_Origin ); break; //VT100
+ case TY_CSI_PR('s', 6) : _currentScreen-> saveMode (MODE_Origin ); break; //FIXME
+ case TY_CSI_PR('r', 6) : _currentScreen->restoreMode (MODE_Origin ); break; //FIXME
+
+ case TY_CSI_PR('h', 7) : _currentScreen-> setMode (MODE_Wrap ); break; //VT100
+ case TY_CSI_PR('l', 7) : _currentScreen-> resetMode (MODE_Wrap ); break; //VT100
+ case TY_CSI_PR('s', 7) : _currentScreen-> saveMode (MODE_Wrap ); break; //FIXME
+ case TY_CSI_PR('r', 7) : _currentScreen->restoreMode (MODE_Wrap ); break; //FIXME
+
+ case TY_CSI_PR('h', 8) : /* IGNORED: autorepeat on */ break; //VT100
+ case TY_CSI_PR('l', 8) : /* IGNORED: autorepeat off */ break; //VT100
+ case TY_CSI_PR('s', 8) : /* IGNORED: autorepeat on */ break; //VT100
+ case TY_CSI_PR('r', 8) : /* IGNORED: autorepeat off */ break; //VT100
+
+ case TY_CSI_PR('h', 9) : /* IGNORED: interlace */ break; //VT100
+ case TY_CSI_PR('l', 9) : /* IGNORED: interlace */ break; //VT100
+ case TY_CSI_PR('s', 9) : /* IGNORED: interlace */ break; //VT100
+ case TY_CSI_PR('r', 9) : /* IGNORED: interlace */ break; //VT100
+
+ case TY_CSI_PR('h', 12) : /* IGNORED: Cursor blink */ break; //att610
+ case TY_CSI_PR('l', 12) : /* IGNORED: Cursor blink */ break; //att610
+ case TY_CSI_PR('s', 12) : /* IGNORED: Cursor blink */ break; //att610
+ case TY_CSI_PR('r', 12) : /* IGNORED: Cursor blink */ break; //att610
+
+ case TY_CSI_PR('h', 25) : setMode (MODE_Cursor ); break; //VT100
+ case TY_CSI_PR('l', 25) : resetMode (MODE_Cursor ); break; //VT100
+ case TY_CSI_PR('s', 25) : saveMode (MODE_Cursor ); break; //VT100
+ case TY_CSI_PR('r', 25) : restoreMode (MODE_Cursor ); break; //VT100
+
+ case TY_CSI_PR('h', 41) : /* IGNORED: obsolete more(1) fix */ break; //XTERM
+ case TY_CSI_PR('l', 41) : /* IGNORED: obsolete more(1) fix */ break; //XTERM
+ case TY_CSI_PR('s', 41) : /* IGNORED: obsolete more(1) fix */ break; //XTERM
+ case TY_CSI_PR('r', 41) : /* IGNORED: obsolete more(1) fix */ break; //XTERM
+
+ case TY_CSI_PR('h', 47) : setMode (MODE_AppScreen); break; //VT100
+ case TY_CSI_PR('l', 47) : resetMode (MODE_AppScreen); break; //VT100
+ case TY_CSI_PR('s', 47) : saveMode (MODE_AppScreen); break; //XTERM
+ case TY_CSI_PR('r', 47) : restoreMode (MODE_AppScreen); break; //XTERM
+
+ case TY_CSI_PR('h', 67) : /* IGNORED: DECBKM */ break; //XTERM
+ case TY_CSI_PR('l', 67) : /* IGNORED: DECBKM */ break; //XTERM
+ case TY_CSI_PR('s', 67) : /* IGNORED: DECBKM */ break; //XTERM
+ case TY_CSI_PR('r', 67) : /* IGNORED: DECBKM */ break; //XTERM
+
+ // XTerm defines the following modes:
+ // SET_VT200_MOUSE 1000
+ // SET_VT200_HIGHLIGHT_MOUSE 1001
+ // SET_BTN_EVENT_MOUSE 1002
+ // SET_ANY_EVENT_MOUSE 1003
+ //
+
+ //Note about mouse modes:
+ //There are four mouse modes which xterm-compatible terminals can support - 1000,1001,1002,1003
+ //Konsole currently supports mode 1000 (basic mouse press and release) and mode 1002 (dragging the mouse).
+ //TODO: Implementation of mouse modes 1001 (something called hilight tracking) and
+ //1003 (a slight variation on dragging the mouse)
+ //
+
+ case TY_CSI_PR('h', 1000) : setMode (MODE_Mouse1000); break; //XTERM
+ case TY_CSI_PR('l', 1000) : resetMode (MODE_Mouse1000); break; //XTERM
+ case TY_CSI_PR('s', 1000) : saveMode (MODE_Mouse1000); break; //XTERM
+ case TY_CSI_PR('r', 1000) : restoreMode (MODE_Mouse1000); break; //XTERM
+
+ case TY_CSI_PR('h', 1001) : /* IGNORED: hilite mouse tracking */ break; //XTERM
+ case TY_CSI_PR('l', 1001) : resetMode (MODE_Mouse1001); break; //XTERM
+ case TY_CSI_PR('s', 1001) : /* IGNORED: hilite mouse tracking */ break; //XTERM
+ case TY_CSI_PR('r', 1001) : /* IGNORED: hilite mouse tracking */ break; //XTERM
+
+ case TY_CSI_PR('h', 1002) : setMode (MODE_Mouse1002); break; //XTERM
+ case TY_CSI_PR('l', 1002) : resetMode (MODE_Mouse1002); break; //XTERM
+ case TY_CSI_PR('s', 1002) : saveMode (MODE_Mouse1002); break; //XTERM
+ case TY_CSI_PR('r', 1002) : restoreMode (MODE_Mouse1002); break; //XTERM
+
+ case TY_CSI_PR('h', 1003) : setMode (MODE_Mouse1003); break; //XTERM
+ case TY_CSI_PR('l', 1003) : resetMode (MODE_Mouse1003); break; //XTERM
+ case TY_CSI_PR('s', 1003) : saveMode (MODE_Mouse1003); break; //XTERM
+ case TY_CSI_PR('r', 1003) : restoreMode (MODE_Mouse1003); break; //XTERM
+
+ case TY_CSI_PR('h', 1047) : setMode (MODE_AppScreen); break; //XTERM
+ case TY_CSI_PR('l', 1047) : _screen[1]->clearEntireScreen(); resetMode(MODE_AppScreen); break; //XTERM
+ case TY_CSI_PR('s', 1047) : saveMode (MODE_AppScreen); break; //XTERM
+ case TY_CSI_PR('r', 1047) : restoreMode (MODE_AppScreen); break; //XTERM
+
+ //FIXME: Unitoken: save translations
+ case TY_CSI_PR('h', 1048) : saveCursor ( ); break; //XTERM
+ case TY_CSI_PR('l', 1048) : restoreCursor ( ); break; //XTERM
+ case TY_CSI_PR('s', 1048) : saveCursor ( ); break; //XTERM
+ case TY_CSI_PR('r', 1048) : restoreCursor ( ); break; //XTERM
+
+ //FIXME: every once new sequences like this pop up in xterm.
+ // Here's a guess of what they could mean.
+ case TY_CSI_PR('h', 1049) : saveCursor(); _screen[1]->clearEntireScreen(); setMode(MODE_AppScreen); break; //XTERM
+ case TY_CSI_PR('l', 1049) : resetMode(MODE_AppScreen); restoreCursor(); break; //XTERM
+
+ //FIXME: weird DEC reset sequence
+ case TY_CSI_PE('p' ) : /* IGNORED: reset ( ) */ break;
+
+ //FIXME: when changing between vt52 and ansi mode evtl do some resetting.
+ case TY_VT52('A' ) : _currentScreen->cursorUp ( 1); break; //VT52
+ case TY_VT52('B' ) : _currentScreen->cursorDown ( 1); break; //VT52
+ case TY_VT52('C' ) : _currentScreen->cursorRight ( 1); break; //VT52
+ case TY_VT52('D' ) : _currentScreen->cursorLeft ( 1); break; //VT52
+
+ case TY_VT52('F' ) : setAndUseCharset (0, '0'); break; //VT52
+ case TY_VT52('G' ) : setAndUseCharset (0, 'B'); break; //VT52
+
+ case TY_VT52('H' ) : _currentScreen->setCursorYX (1,1 ); break; //VT52
+ case TY_VT52('I' ) : _currentScreen->reverseIndex ( ); break; //VT52
+ case TY_VT52('J' ) : _currentScreen->clearToEndOfScreen ( ); break; //VT52
+ case TY_VT52('K' ) : _currentScreen->clearToEndOfLine ( ); break; //VT52
+ case TY_VT52('Y' ) : _currentScreen->setCursorYX (p-31,q-31 ); break; //VT52
+ case TY_VT52('Z' ) : reportTerminalType ( ); break; //VT52
+ case TY_VT52('<' ) : setMode (MODE_Ansi ); break; //VT52
+ case TY_VT52('=' ) : setMode (MODE_AppKeyPad); break; //VT52
+ case TY_VT52('>' ) : resetMode (MODE_AppKeyPad); break; //VT52
+
+ case TY_CSI_PG('c' ) : reportSecondaryAttributes( ); break; //VT100
+
+ default : ReportErrorToken(); break;
+ };
+}
+
+void Vt102Emulation::clearScreenAndSetColumns(int columnCount)
+{
+ setImageSize(_currentScreen->getLines(),columnCount);
+ clearEntireScreen();
+ setDefaultMargins();
+ _currentScreen->setCursorYX(0,0);
+}
+
+/* ------------------------------------------------------------------------- */
+/* */
+/* Terminal to Host protocol */
+/* */
+/* ------------------------------------------------------------------------- */
+
+/*
+ Outgoing bytes originate from several sources:
+
+ - Replies to Enquieries.
+ - Mouse Events
+ - Keyboard Events
+*/
+
+/*!
+*/
+
+void Vt102Emulation::sendString(const char* s , int length)
+{
+ if ( length >= 0 )
+ emit sendData(s,length);
+ else
+ emit sendData(s,strlen(s));
+}
+
+// Replies ----------------------------------------------------------------- --
+
+// This section copes with replies send as response to an enquiery control code.
+
+/*!
+*/
+
+void Vt102Emulation::reportCursorPosition()
+{ char tmp[20];
+ sprintf(tmp,"\033[%d;%dR",_currentScreen->getCursorY()+1,_currentScreen->getCursorX()+1);
+ sendString(tmp);
+}
+
+/*
+ What follows here is rather obsolete and faked stuff.
+ The correspondent enquieries are neverthenless issued.
+*/
+
+/*!
+*/
+
+void Vt102Emulation::reportTerminalType()
+{
+ // Primary device attribute response (Request was: ^[[0c or ^[[c (from TT321 Users Guide))
+ // VT220: ^[[?63;1;2;3;6;7;8c (list deps on emul. capabilities)
+ // VT100: ^[[?1;2c
+ // VT101: ^[[?1;0c
+ // VT102: ^[[?6v
+ if (getMode(MODE_Ansi))
+ sendString("\033[?1;2c"); // I'm a VT100
+ else
+ sendString("\033/Z"); // I'm a VT52
+}
+
+void Vt102Emulation::reportSecondaryAttributes()
+{
+ // Seconday device attribute response (Request was: ^[[>0c or ^[[>c)
+ if (getMode(MODE_Ansi))
+ sendString("\033[>0;115;0c"); // Why 115? ;)
+ else
+ sendString("\033/Z"); // FIXME I don't think VT52 knows about it but kept for
+ // konsoles backward compatibility.
+}
+
+void Vt102Emulation::reportTerminalParms(int p)
+// DECREPTPARM
+{ char tmp[100];
+ sprintf(tmp,"\033[%d;1;1;112;112;1;0x",p); // not really true.
+ sendString(tmp);
+}
+
+/*!
+*/
+
+void Vt102Emulation::reportStatus()
+{
+ sendString("\033[0n"); //VT100. Device status report. 0 = Ready.
+}
+
+/*!
+*/
+
+#define ANSWER_BACK "" // This is really obsolete VT100 stuff.
+
+void Vt102Emulation::reportAnswerBack()
+{
+ sendString(ANSWER_BACK);
+}
+
+// Mouse Handling ---------------------------------------------------------- --
+
+/*!
+ Mouse clicks are possibly reported to the client
+ application if it has issued interest in them.
+ They are normally consumed by the widget for copy
+ and paste, but may be propagated from the widget
+ when gui->setMouseMarks is set via setMode(MODE_Mouse1000).
+
+ `x',`y' are 1-based.
+ `ev' (event) indicates the button pressed (0-2)
+ or a general mouse release (3).
+
+ eventType represents the kind of mouse action that occurred:
+ 0 = Mouse button press or release
+ 1 = Mouse drag
+*/
+
+void Vt102Emulation::sendMouseEvent( int cb, int cx, int cy , int eventType )
+{ char tmp[20];
+ if ( cx<1 || cy<1 ) return;
+ // normal buttons are passed as 0x20 + button,
+ // mouse wheel (buttons 4,5) as 0x5c + button
+ if (cb >= 4) cb += 0x3c;
+
+ //Mouse motion handling
+ if ( (getMode(MODE_Mouse1002) || getMode(MODE_Mouse1003)) && eventType == 1 )
+ cb += 0x20; //add 32 to signify motion event
+
+ sprintf(tmp,"\033[M%c%c%c",cb+0x20,cx+0x20,cy+0x20);
+ sendString(tmp);
+}
+
+// Keyboard Handling ------------------------------------------------------- --
+
+#define encodeMode(M,B) BITS(B,getMode(M))
+#define encodeStat(M,B) BITS(B,((ev->modifiers() & (M)) == (M)))
+
+void Vt102Emulation::sendText( const QString& text )
+{
+ if (!text.isEmpty()) {
+ QKeyEvent event(QEvent::KeyPress,
+ 0,
+ Qt::NoModifier,
+ text);
+ sendKeyEvent(&event); // expose as a big fat keypress event
+ }
+
+}
+
+void Vt102Emulation::sendKeyEvent( QKeyEvent* event )
+{
+ Qt::KeyboardModifiers modifiers = event->modifiers();
+ KeyboardTranslator::States states = KeyboardTranslator::NoState;
+
+ // get current states
+ if ( getMode(MODE_NewLine) ) states |= KeyboardTranslator::NewLineState;
+ if ( getMode(MODE_Ansi) ) states |= KeyboardTranslator::AnsiState;
+ if ( getMode(MODE_AppCuKeys)) states |= KeyboardTranslator::CursorKeysState;
+ if ( getMode(MODE_AppScreen)) states |= KeyboardTranslator::AlternateScreenState;
+
+ // lookup key binding
+ if ( _keyTranslator )
+ {
+ KeyboardTranslator::Entry entry = _keyTranslator->findEntry(
+ event->key() ,
+ modifiers,
+ states );
+
+ // send result to terminal
+ QByteArray textToSend;
+
+ // special handling for the Alt (aka. Meta) modifier. pressing
+ // Alt+[Character] results in Esc+[Character] being sent
+ // (unless there is an entry defined for this particular combination
+ // in the keyboard modifier)
+ bool wantsAltModifier = entry.modifiers() & entry.modifierMask() & Qt::AltModifier;
+ bool wantsAnyModifier = entry.state() & entry.stateMask() & KeyboardTranslator::AnyModifierState;
+
+ if ( modifiers & Qt::AltModifier && !(wantsAltModifier || wantsAnyModifier)
+ && !event->text().isEmpty() )
+ {
+ textToSend.prepend("\033");
+ }
+
+ if ( entry.command() != KeyboardTranslator::NoCommand )
+ {
+ if (entry.command() & KeyboardTranslator::EraseCommand)
+ textToSend += getErase();
+ // TODO command handling
+ }
+ else if ( !entry.text().isEmpty() )
+ {
+ textToSend += _codec->fromUnicode(entry.text(true,modifiers));
+ }
+ else
+ textToSend += _codec->fromUnicode(event->text());
+
+ sendData( textToSend.constData() , textToSend.length() );
+ }
+ else
+ {
+ // print an error message to the terminal if no key translator has been
+ // set
+ QString translatorError = ("No keyboard translator available. "
+ "The information needed to convert key presses "
+ "into characters to send to the terminal "
+ "is missing.");
+
+ reset();
+ receiveData( translatorError.toAscii().constData() , translatorError.count() );
+ }
+}
+
+/* ------------------------------------------------------------------------- */
+/* */
+/* VT100 Charsets */
+/* */
+/* ------------------------------------------------------------------------- */
+
+// Character Set Conversion ------------------------------------------------ --
+
+/*
+ The processing contains a VT100 specific code translation layer.
+ It's still in use and mainly responsible for the line drawing graphics.
+
+ These and some other glyphs are assigned to codes (0x5f-0xfe)
+ normally occupied by the latin letters. Since this codes also
+ appear within control sequences, the extra code conversion
+ does not permute with the tokenizer and is placed behind it
+ in the pipeline. It only applies to tokens, which represent
+ plain characters.
+
+ This conversion it eventually continued in TerminalDisplay.C, since
+ it might involve VT100 enhanced fonts, which have these
+ particular glyphs allocated in (0x00-0x1f) in their code page.
+*/
+
+#define CHARSET _charset[_currentScreen==_screen[1]]
+
+// Apply current character map.
+
+unsigned short Vt102Emulation::applyCharset(unsigned short c)
+{
+ if (CHARSET.graphic && 0x5f <= c && c <= 0x7e) return vt100_graphics[c-0x5f];
+ if (CHARSET.pound && c == '#' ) return 0xa3; //This mode is obsolete
+ return c;
+}
+
+/*
+ "Charset" related part of the emulation state.
+ This configures the VT100 _charset filter.
+
+ While most operation work on the current _screen,
+ the following two are different.
+*/
+
+void Vt102Emulation::resetCharset(int scrno)
+{
+ _charset[scrno].cu_cs = 0;
+ strncpy(_charset[scrno].charset,"BBBB",4);
+ _charset[scrno].sa_graphic = false;
+ _charset[scrno].sa_pound = false;
+ _charset[scrno].graphic = false;
+ _charset[scrno].pound = false;
+}
+
+void Vt102Emulation::setCharset(int n, int cs) // on both screens.
+{
+ _charset[0].charset[n&3] = cs; useCharset(_charset[0].cu_cs);
+ _charset[1].charset[n&3] = cs; useCharset(_charset[1].cu_cs);
+}
+
+void Vt102Emulation::setAndUseCharset(int n, int cs)
+{
+ CHARSET.charset[n&3] = cs;
+ useCharset(n&3);
+}
+
+void Vt102Emulation::useCharset(int n)
+{
+ CHARSET.cu_cs = n&3;
+ CHARSET.graphic = (CHARSET.charset[n&3] == '0');
+ CHARSET.pound = (CHARSET.charset[n&3] == 'A'); //This mode is obsolete
+}
+
+void Vt102Emulation::setDefaultMargins()
+{
+ _screen[0]->setDefaultMargins();
+ _screen[1]->setDefaultMargins();
+}
+
+void Vt102Emulation::setMargins(int t, int b)
+{
+ _screen[0]->setMargins(t, b);
+ _screen[1]->setMargins(t, b);
+}
+
+/*! Save the cursor position and the rendition attribute settings. */
+
+void Vt102Emulation::saveCursor()
+{
+ CHARSET.sa_graphic = CHARSET.graphic;
+ CHARSET.sa_pound = CHARSET.pound; //This mode is obsolete
+ // we are not clear about these
+ //sa_charset = charsets[cScreen->_charset];
+ //sa_charset_num = cScreen->_charset;
+ _currentScreen->saveCursor();
+}
+
+/*! Restore the cursor position and the rendition attribute settings. */
+
+void Vt102Emulation::restoreCursor()
+{
+ CHARSET.graphic = CHARSET.sa_graphic;
+ CHARSET.pound = CHARSET.sa_pound; //This mode is obsolete
+ _currentScreen->restoreCursor();
+}
+
+/* ------------------------------------------------------------------------- */
+/* */
+/* Mode Operations */
+/* */
+/* ------------------------------------------------------------------------- */
+
+/*
+ Some of the emulations state is either added to the state of the screens.
+
+ This causes some scoping problems, since different emulations choose to
+ located the mode either to the current _screen or to both.
+
+ For strange reasons, the extend of the rendition attributes ranges over
+ all screens and not over the actual _screen.
+
+ We decided on the precise precise extend, somehow.
+*/
+
+// "Mode" related part of the state. These are all booleans.
+
+void Vt102Emulation::resetModes()
+{
+ resetMode(MODE_Mouse1000); saveMode(MODE_Mouse1000);
+ resetMode(MODE_Mouse1001); saveMode(MODE_Mouse1001);
+ resetMode(MODE_Mouse1002); saveMode(MODE_Mouse1002);
+ resetMode(MODE_Mouse1003); saveMode(MODE_Mouse1003);
+
+ resetMode(MODE_AppScreen); saveMode(MODE_AppScreen);
+ // here come obsolete modes
+ resetMode(MODE_AppCuKeys); saveMode(MODE_AppCuKeys);
+ resetMode(MODE_NewLine );
+ setMode(MODE_Ansi );
+}
+
+void Vt102Emulation::setMode(int m)
+{
+ _currParm.mode[m] = true;
+ switch (m)
+ {
+ case MODE_Mouse1000:
+ case MODE_Mouse1001:
+ case MODE_Mouse1002:
+ case MODE_Mouse1003:
+ emit programUsesMouseChanged(false);
+ break;
+
+ case MODE_AppScreen : _screen[1]->clearSelection();
+ setScreen(1);
+ break;
+ }
+ if (m < MODES_SCREEN || m == MODE_NewLine)
+ {
+ _screen[0]->setMode(m);
+ _screen[1]->setMode(m);
+ }
+}
+
+void Vt102Emulation::resetMode(int m)
+{
+ _currParm.mode[m] = false;
+ switch (m)
+ {
+ case MODE_Mouse1000 :
+ case MODE_Mouse1001 :
+ case MODE_Mouse1002 :
+ case MODE_Mouse1003 :
+ emit programUsesMouseChanged(true);
+ break;
+
+ case MODE_AppScreen : _screen[0]->clearSelection();
+ setScreen(0);
+ break;
+ }
+ if (m < MODES_SCREEN || m == MODE_NewLine)
+ {
+ _screen[0]->resetMode(m);
+ _screen[1]->resetMode(m);
+ }
+}
+
+void Vt102Emulation::saveMode(int m)
+{
+ _saveParm.mode[m] = _currParm.mode[m];
+}
+
+void Vt102Emulation::restoreMode(int m)
+{
+ if (_saveParm.mode[m])
+ setMode(m);
+ else
+ resetMode(m);
+}
+
+bool Vt102Emulation::getMode(int m)
+{
+ return _currParm.mode[m];
+}
+
+char Vt102Emulation::getErase() const
+{
+ KeyboardTranslator::Entry entry = _keyTranslator->findEntry(
+ Qt::Key_Backspace,
+ 0,
+ 0);
+ if ( entry.text().count() > 0 )
+ return entry.text()[0];
+ else
+ return '\b';
+}
+
+/* ------------------------------------------------------------------------- */
+/* */
+/* Diagnostic */
+/* */
+/* ------------------------------------------------------------------------- */
+
+/*! shows the contents of the scan buffer.
+
+ This functions is used for diagnostics. It is called by \e ReportErrorToken
+ to inform about strings that cannot be decoded or handled by the emulation.
+
+ \sa ReportErrorToken
+*/
+
+static void hexdump(int* s, int len)
+{ int i;
+ for (i = 0; i < len; i++)
+ {
+ if (s[i] == '\\')
+ printf("\\\\");
+ else
+ if ((s[i]) > 32 && s[i] < 127)
+ printf("%c",s[i]);
+ else
+ printf("\\%04x(hex)",s[i]);
+ }
+}
+
+void Vt102Emulation::scan_buffer_report()
+{
+ if (ppos == 0 || ppos == 1 && (pbuf[0] & 0xff) >= 32) return;
+ printf("token: "); hexdump(pbuf,ppos); printf("\n");
+}
+
+/*!
+*/
+
+void Vt102Emulation::ReportErrorToken()
+{
+#ifndef NDEBUG
+ printf("undecodable "); scan_buffer_report();
+#endif
+}
+
+//#include "moc_Vt102Emulation.cpp"
+
diff --git a/qtermwidget/Vt102Emulation.h b/qtermwidget/Vt102Emulation.h
new file mode 100644
index 0000000..4554c1b
--- /dev/null
+++ b/qtermwidget/Vt102Emulation.h
@@ -0,0 +1,192 @@
+/*
+ This file is part of Konsole, an X terminal.
+
+ Copyright (C) 2007 by Robert Knight <robertknight@gmail.com>
+ Copyright (C) 1997,1998 by Lars Doelle <lars.doelle@on-line.de>
+
+ Rewritten for QT4 by e_k <e_k at users.sourceforge.net>, Copyright (C)2008
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ 02110-1301 USA.
+*/
+
+#ifndef VT102EMULATION_H
+#define VT102EMULATION_H
+
+// Standard Library
+#include <stdio.h>
+
+// Qt
+#include <QtGui/QKeyEvent>
+#include <QtCore/QHash>
+#include <QtCore/QTimer>
+
+// Konsole
+#include "Emulation.h"
+#include "Screen.h"
+
+#define MODE_AppScreen (MODES_SCREEN+0)
+#define MODE_AppCuKeys (MODES_SCREEN+1)
+#define MODE_AppKeyPad (MODES_SCREEN+2)
+#define MODE_Mouse1000 (MODES_SCREEN+3)
+#define MODE_Mouse1001 (MODES_SCREEN+4)
+#define MODE_Mouse1002 (MODES_SCREEN+5)
+#define MODE_Mouse1003 (MODES_SCREEN+6)
+#define MODE_Ansi (MODES_SCREEN+7)
+#define MODE_total (MODES_SCREEN+8)
+
+namespace Konsole
+{
+
+struct DECpar
+{
+ bool mode[MODE_total];
+};
+
+struct CharCodes
+{
+ // coding info
+ char charset[4]; //
+ int cu_cs; // actual charset.
+ bool graphic; // Some VT100 tricks
+ bool pound ; // Some VT100 tricks
+ bool sa_graphic; // saved graphic
+ bool sa_pound; // saved pound
+};
+
+/**
+ * Provides an xterm compatible terminal emulation based on the DEC VT102 terminal.
+ * A full description of this terminal can be found at http://vt100.net/docs/vt102-ug/
+ *
+ * In addition, various additional xterm escape sequences are supported to provide
+ * features such as mouse input handling.
+ * See http://rtfm.etla.org/xterm/ctlseq.html for a description of xterm's escape
+ * sequences.
+ *
+ */
+class Vt102Emulation : public Emulation
+{
+Q_OBJECT
+
+public:
+
+ /** Constructs a new emulation */
+ Vt102Emulation();
+ ~Vt102Emulation();
+
+ // reimplemented
+ virtual void clearEntireScreen();
+ virtual void reset();
+
+ // reimplemented
+ virtual char getErase() const;
+
+public slots:
+
+ // reimplemented
+ virtual void sendString(const char*,int length = -1);
+ virtual void sendText(const QString& text);
+ virtual void sendKeyEvent(QKeyEvent*);
+ virtual void sendMouseEvent( int buttons, int column, int line , int eventType );
+
+protected:
+ // reimplemented
+ virtual void setMode (int mode);
+ virtual void resetMode (int mode);
+
+ // reimplemented
+ virtual void receiveChar(int cc);
+
+
+private slots:
+
+ //causes changeTitle() to be emitted for each (int,QString) pair in pendingTitleUpdates
+ //used to buffer multiple title updates
+ void updateTitle();
+
+
+private:
+ unsigned short applyCharset(unsigned short c);
+ void setCharset(int n, int cs);
+ void useCharset(int n);
+ void setAndUseCharset(int n, int cs);
+ void saveCursor();
+ void restoreCursor();
+ void resetCharset(int scrno);
+
+ void setMargins(int top, int bottom);
+ //set margins for all screens back to their defaults
+ void setDefaultMargins();
+
+ // returns true if 'mode' is set or false otherwise
+ bool getMode (int mode);
+ // saves the current boolean value of 'mode'
+ void saveMode (int mode);
+ // restores the boolean value of 'mode'
+ void restoreMode(int mode);
+ // resets all modes
+ void resetModes();
+
+ void resetToken();
+#define MAXPBUF 80
+ void pushToToken(int cc);
+ int pbuf[MAXPBUF]; //FIXME: overflow?
+ int ppos;
+#define MAXARGS 15
+ void addDigit(int dig);
+ void addArgument();
+ int argv[MAXARGS];
+ int argc;
+ void initTokenizer();
+ int tbl[256];
+
+ void scan_buffer_report(); //FIXME: rename
+ void ReportErrorToken(); //FIXME: rename
+
+ void tau(int code, int p, int q);
+ void XtermHack();
+
+ void reportTerminalType();
+ void reportSecondaryAttributes();
+ void reportStatus();
+ void reportAnswerBack();
+ void reportCursorPosition();
+ void reportTerminalParms(int p);
+
+ void onScrollLock();
+ void scrollLock(const bool lock);
+
+ // clears the screen and resizes it to the specified
+ // number of columns
+ void clearScreenAndSetColumns(int columnCount);
+
+ CharCodes _charset[2];
+
+ DECpar _currParm;
+ DECpar _saveParm;
+
+ //hash table and timer for buffering calls to the session instance
+ //to update the name of the session
+ //or window title.
+ //these calls occur when certain escape sequences are seen in the
+ //output from the terminal
+ QHash<int,QString> _pendingTitleUpdates;
+ QTimer* _titleUpdateTimer;
+
+};
+
+}
+
+#endif // VT102EMULATION_H
diff --git a/qtermwidget/default.keytab b/qtermwidget/default.keytab
new file mode 100644
index 0000000..f84293d
--- /dev/null
+++ b/qtermwidget/default.keytab
@@ -0,0 +1,128 @@
+# [README.default.Keytab] Buildin Keyboard Table
+#
+# To customize your keyboard, copy this file to something
+# ending with .keytab and change it to meet you needs.
+# Please read the README.KeyTab and the README.keyboard
+# in this case.
+#
+# --------------------------------------------------------------
+
+keyboard "Default (XFree 4)"
+
+# --------------------------------------------------------------
+#
+# Note that this particular table is a "risc" version made to
+# ease customization without bothering with obsolete details.
+# See VT100.keytab for the more hairy stuff.
+#
+# --------------------------------------------------------------
+
+# common keys
+
+key Escape : "\E"
+
+key Tab -Shift : "\t"
+key Tab +Shift+Ansi : "\E[Z"
+key Tab +Shift-Ansi : "\t"
+key Backtab +Ansi : "\E[Z"
+key Backtab -Ansi : "\t"
+
+key Return-Shift-NewLine : "\r"
+key Return-Shift+NewLine : "\r\n"
+
+key Return+Shift : "\EOM"
+
+# Backspace and Delete codes are preserving CTRL-H.
+
+key Backspace : "\x7f"
+
+# Arrow keys in VT52 mode
+# shift up/down are reserved for scrolling.
+# shift left/right are reserved for switching between tabs (this is hardcoded).
+
+key Up -Shift-Ansi : "\EA"
+key Down -Shift-Ansi : "\EB"
+key Right-Shift-Ansi : "\EC"
+key Left -Shift-Ansi : "\ED"
+
+# Arrow keys in ANSI mode with Application - and Normal Cursor Mode)
+
+key Up -Shift-AnyMod+Ansi+AppCuKeys : "\EOA"
+key Down -Shift-AnyMod+Ansi+AppCuKeys : "\EOB"
+key Right -Shift-AnyMod+Ansi+AppCuKeys : "\EOC"
+key Left -Shift-AnyMod+Ansi+AppCuKeys : "\EOD"
+
+key Up -Shift-AnyMod+Ansi-AppCuKeys : "\E[A"
+key Down -Shift-AnyMod+Ansi-AppCuKeys : "\E[B"
+key Right -Shift-AnyMod+Ansi-AppCuKeys : "\E[C"
+key Left -Shift-AnyMod+Ansi-AppCuKeys : "\E[D"
+
+key Up -Shift+AnyMod+Ansi : "\E[1;*A"
+key Down -Shift+AnyMod+Ansi : "\E[1;*B"
+key Right -Shift+AnyMod+Ansi : "\E[1;*C"
+key Left -Shift+AnyMod+Ansi : "\E[1;*D"
+
+# other grey PC keys
+
+key Enter+NewLine : "\r\n"
+key Enter-NewLine : "\r"
+
+key Home -AnyMod -AppCuKeys : "\E[H"
+key End -AnyMod -AppCuKeys : "\E[F"
+key Home -AnyMod +AppCuKeys : "\EOH"
+key End -AnyMod +AppCuKeys : "\EOF"
+key Home +AnyMod : "\E[1;*H"
+key End +AnyMod : "\E[1;*F"
+
+key Insert -AnyMod : "\E[2~"
+key Delete -AnyMod : "\E[3~"
+key Insert +AnyMod : "\E[2;*~"
+key Delete +AnyMod : "\E[3;*~"
+
+key Prior -Shift-AnyMod : "\E[5~"
+key Next -Shift-AnyMod : "\E[6~"
+key Prior -Shift+AnyMod : "\E[5;*~"
+key Next -Shift+AnyMod : "\E[6;*~"
+
+# Function keys
+key F1 -AnyMod : "\EOP"
+key F2 -AnyMod : "\EOQ"
+key F3 -AnyMod : "\EOR"
+key F4 -AnyMod : "\EOS"
+key F5 -AnyMod : "\E[15~"
+key F6 -AnyMod : "\E[17~"
+key F7 -AnyMod : "\E[18~"
+key F8 -AnyMod : "\E[19~"
+key F9 -AnyMod : "\E[20~"
+key F10 -AnyMod : "\E[21~"
+key F11 -AnyMod : "\E[23~"
+key F12 -AnyMod : "\E[24~"
+
+key F1 +AnyMod : "\EO*P"
+key F2 +AnyMod : "\EO*Q"
+key F3 +AnyMod : "\EO*R"
+key F4 +AnyMod : "\EO*S"
+key F5 +AnyMod : "\E[15;*~"
+key F6 +AnyMod : "\E[17;*~"
+key F7 +AnyMod : "\E[18;*~"
+key F8 +AnyMod : "\E[19;*~"
+key F9 +AnyMod : "\E[20;*~"
+key F10 +AnyMod : "\E[21;*~"
+key F11 +AnyMod : "\E[23;*~"
+key F12 +AnyMod : "\E[24;*~"
+
+# Work around dead keys
+
+key Space +Control : "\x00"
+
+# Some keys are used by konsole to cause operations.
+# The scroll* operations refer to the history buffer.
+
+key Up +Shift-AppScreen : scrollLineUp
+key Prior +Shift-AppScreen : scrollPageUp
+key Down +Shift-AppScreen : scrollLineDown
+key Next +Shift-AppScreen : scrollPageDown
+
+key ScrollLock : scrollLock
+
+# keypad characters are not offered differently by Qt.
diff --git a/qtermwidget/k3process.cpp b/qtermwidget/k3process.cpp
new file mode 100644
index 0000000..de91d57
--- /dev/null
+++ b/qtermwidget/k3process.cpp
@@ -0,0 +1,1053 @@
+/*
+ This file is part of the KDE libraries
+ Copyright (C) 1997 Christian Czezatke (e9025461@student.tuwien.ac.at)
+
+ Rewritten for QT4 by e_k <e_k at users.sourceforge.net>, Copyright (C)2008
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+
+#include "k3process.h"
+//#include <config.h>
+
+#include "k3processcontroller.h"
+#include "kpty.h"
+
+#ifdef __osf__
+#define _OSF_SOURCE
+#include <float.h>
+#endif
+
+#ifdef _AIX
+#define _ALL_SOURCE
+#endif
+
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+
+#ifdef HAVE_SYS_SELECT_H
+#include <sys/select.h>
+#endif
+
+#include <errno.h>
+#include <assert.h>
+#include <fcntl.h>
+#include <time.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <pwd.h>
+#include <grp.h>
+
+#include <QtCore/QMap>
+#include <QtCore/QFile>
+#include <QtCore/QSocketNotifier>
+
+//#include <kdebug.h>
+//#include <kstandarddirs.h>
+//#include <kuser.h>
+
+
+//////////////////
+// private data //
+//////////////////
+
+class K3ProcessPrivate {
+public:
+ K3ProcessPrivate() :
+ usePty(K3Process::NoCommunication),
+ addUtmp(false), useShell(false),
+ pty(0),
+ priority(0)
+ {
+ }
+
+ K3Process::Communication usePty;
+ bool addUtmp : 1;
+ bool useShell : 1;
+
+ KPty *pty;
+
+ int priority;
+
+ QMap<QString,QString> env;
+ QString wd;
+ QByteArray shell;
+ QByteArray executable;
+};
+
+/////////////////////////////
+// public member functions //
+/////////////////////////////
+
+K3Process::K3Process( QObject* parent )
+ : QObject( parent ),
+ run_mode(NotifyOnExit),
+ runs(false),
+ pid_(0),
+ status(0),
+ keepPrivs(false),
+ innot(0),
+ outnot(0),
+ errnot(0),
+ communication(NoCommunication),
+ input_data(0),
+ input_sent(0),
+ input_total(0),
+ d(new K3ProcessPrivate)
+{
+ K3ProcessController::ref();
+ K3ProcessController::instance()->addKProcess(this);
+
+
+ out[0] = out[1] = -1;
+ in[0] = in[1] = -1;
+ err[0] = err[1] = -1;
+}
+
+void
+K3Process::setEnvironment(const QString &name, const QString &value)
+{
+ d->env.insert(name, value);
+}
+
+void
+K3Process::setWorkingDirectory(const QString &dir)
+{
+ d->wd = dir;
+}
+
+void
+K3Process::setupEnvironment()
+{
+ QMap<QString,QString>::Iterator it;
+ for(it = d->env.begin(); it != d->env.end(); ++it)
+ {
+ setenv(QFile::encodeName(it.key()).data(),
+ QFile::encodeName(it.value()).data(), 1);
+ }
+ if (!d->wd.isEmpty())
+ {
+ chdir(QFile::encodeName(d->wd).data());
+ }
+}
+
+void
+K3Process::setRunPrivileged(bool keepPrivileges)
+{
+ keepPrivs = keepPrivileges;
+}
+
+bool
+K3Process::runPrivileged() const
+{
+ return keepPrivs;
+}
+
+bool
+K3Process::setPriority(int prio)
+{
+ if (runs) {
+ if (setpriority(PRIO_PROCESS, pid_, prio))
+ return false;
+ } else {
+ if (prio > 19 || prio < (geteuid() ? getpriority(PRIO_PROCESS, 0) : -20))
+ return false;
+ }
+ d->priority = prio;
+ return true;
+}
+
+K3Process::~K3Process()
+{
+ if (run_mode != DontCare)
+ kill(SIGKILL);
+ detach();
+
+ delete d->pty;
+ delete d;
+
+ K3ProcessController::instance()->removeKProcess(this);
+ K3ProcessController::deref();
+}
+
+void K3Process::detach()
+{
+ if (runs) {
+ K3ProcessController::instance()->addProcess(pid_);
+ runs = false;
+ pid_ = 0; // close without draining
+ commClose(); // Clean up open fd's and socket notifiers.
+ }
+}
+
+void K3Process::setBinaryExecutable(const char *filename)
+{
+ d->executable = filename;
+}
+
+K3Process &K3Process::operator<<(const QStringList& args)
+{
+ QStringList::ConstIterator it = args.begin();
+ for ( ; it != args.end() ; ++it )
+ arguments.append(QFile::encodeName(*it));
+ return *this;
+}
+
+K3Process &K3Process::operator<<(const QByteArray& arg)
+{
+ return operator<< (arg.data());
+}
+
+K3Process &K3Process::operator<<(const char* arg)
+{
+ arguments.append(arg);
+ return *this;
+}
+
+K3Process &K3Process::operator<<(const QString& arg)
+{
+ arguments.append(QFile::encodeName(arg));
+ return *this;
+}
+
+void K3Process::clearArguments()
+{
+ arguments.clear();
+}
+
+bool K3Process::start(RunMode runmode, Communication comm)
+{
+ if (runs) {
+ qDebug() << "Attempted to start an already running process" << endl;
+ return false;
+ }
+
+ uint n = arguments.count();
+ if (n == 0) {
+ qDebug() << "Attempted to start a process without arguments" << endl;
+ return false;
+ }
+ char **arglist;
+ QByteArray shellCmd;
+ if (d->useShell)
+ {
+ if (d->shell.isEmpty()) {
+ qDebug() << "Invalid shell specified" << endl;
+ return false;
+ }
+
+ for (uint i = 0; i < n; i++) {
+ shellCmd += arguments[i];
+ shellCmd += ' '; // CC: to separate the arguments
+ }
+
+ arglist = static_cast<char **>(malloc( 4 * sizeof(char *)));
+ arglist[0] = d->shell.data();
+ arglist[1] = (char *) "-c";
+ arglist[2] = shellCmd.data();
+ arglist[3] = 0;
+ }
+ else
+ {
+ arglist = static_cast<char **>(malloc( (n + 1) * sizeof(char *)));
+ for (uint i = 0; i < n; i++)
+ arglist[i] = arguments[i].data();
+ arglist[n] = 0;
+ }
+
+ run_mode = runmode;
+
+ if (!setupCommunication(comm))
+ {
+ qDebug() << "Could not setup Communication!" << endl;
+ free(arglist);
+ return false;
+ }
+
+ // We do this in the parent because if we do it in the child process
+ // gdb gets confused when the application runs from gdb.
+#ifdef HAVE_INITGROUPS
+ struct passwd *pw = geteuid() ? 0 : getpwuid(getuid());
+#endif
+
+ int fd[2];
+ if (pipe(fd))
+ fd[0] = fd[1] = -1; // Pipe failed.. continue
+
+ // we don't use vfork() because
+ // - it has unclear semantics and is not standardized
+ // - we do way too much magic in the child
+ pid_ = fork();
+ if (pid_ == 0) {
+ // The child process
+
+ close(fd[0]);
+ // Closing of fd[1] indicates that the execvp() succeeded!
+ fcntl(fd[1], F_SETFD, FD_CLOEXEC);
+
+ if (!commSetupDoneC())
+ qDebug() << "Could not finish comm setup in child!" << endl;
+
+ // reset all signal handlers
+ struct sigaction act;
+ sigemptyset(&act.sa_mask);
+ act.sa_handler = SIG_DFL;
+ act.sa_flags = 0;
+ for (int sig = 1; sig < NSIG; sig++)
+ sigaction(sig, &act, 0L);
+
+ if (d->priority)
+ setpriority(PRIO_PROCESS, 0, d->priority);
+
+ if (!runPrivileged())
+ {
+ setgid(getgid());
+#ifdef HAVE_INITGROUPS
+ if (pw)
+ initgroups(pw->pw_name, pw->pw_gid);
+#endif
+ if (geteuid() != getuid())
+ setuid(getuid());
+ if (geteuid() != getuid())
+ _exit(1);
+ }
+
+ setupEnvironment();
+
+ if (runmode == DontCare || runmode == OwnGroup)
+ setsid();
+
+ const char *executable = arglist[0];
+ if (!d->executable.isEmpty())
+ executable = d->executable.data();
+ execvp(executable, arglist);
+
+ char resultByte = 1;
+ write(fd[1], &resultByte, 1);
+ _exit(-1);
+ } else if (pid_ == -1) {
+ // forking failed
+
+ // commAbort();
+ pid_ = 0;
+ free(arglist);
+ return false;
+ }
+ // the parent continues here
+ free(arglist);
+
+ if (!commSetupDoneP())
+ qDebug() << "Could not finish comm setup in parent!" << endl;
+
+ // Check whether client could be started.
+ close(fd[1]);
+ for(;;)
+ {
+ char resultByte;
+ int n = ::read(fd[0], &resultByte, 1);
+ if (n == 1)
+ {
+ // exec() failed
+ close(fd[0]);
+ waitpid(pid_, 0, 0);
+ pid_ = 0;
+ commClose();
+ return false;
+ }
+ if (n == -1)
+ {
+ if (errno == EINTR)
+ continue; // Ignore
+ }
+ break; // success
+ }
+ close(fd[0]);
+
+ runs = true;
+ switch (runmode)
+ {
+ case Block:
+ for (;;)
+ {
+ commClose(); // drain only, unless obsolete reimplementation
+ if (!runs)
+ {
+ // commClose detected data on the process exit notifification pipe
+ K3ProcessController::instance()->unscheduleCheck();
+ if (waitpid(pid_, &status, WNOHANG) != 0) // error finishes, too
+ {
+ commClose(); // this time for real (runs is false)
+ K3ProcessController::instance()->rescheduleCheck();
+ break;
+ }
+ runs = true; // for next commClose() iteration
+ }
+ else
+ {
+ // commClose is an obsolete reimplementation and waited until
+ // all output channels were closed (or it was interrupted).
+ // there is a chance that it never gets here ...
+ waitpid(pid_, &status, 0);
+ runs = false;
+ break;
+ }
+ }
+ // why do we do this? i think this signal should be emitted _only_
+ // after the process has successfully run _asynchronously_ --ossi
+ emit processExited(this);
+ break;
+ default: // NotifyOnExit & OwnGroup
+ input_data = 0; // Discard any data for stdin that might still be there
+ break;
+ }
+ return true;
+}
+
+
+
+bool K3Process::kill(int signo)
+{
+ if (runs && pid_ > 0 && !::kill(run_mode == OwnGroup ? -pid_ : pid_, signo))
+ return true;
+ return false;
+}
+
+
+
+bool K3Process::isRunning() const
+{
+ return runs;
+}
+
+
+
+pid_t K3Process::pid() const
+{
+ return pid_;
+}
+
+#ifndef timersub
+# define timersub(a, b, result) \
+ do { \
+ (result)->tv_sec = (a)->tv_sec - (b)->tv_sec; \
+ (result)->tv_usec = (a)->tv_usec - (b)->tv_usec; \
+ if ((result)->tv_usec < 0) { \
+ --(result)->tv_sec; \
+ (result)->tv_usec += 1000000; \
+ } \
+ } while (0)
+#endif
+
+bool K3Process::wait(int timeout)
+{
+ if (!runs)
+ return true;
+
+#ifndef __linux__
+ struct timeval etv;
+#endif
+ struct timeval tv, *tvp;
+ if (timeout < 0)
+ tvp = 0;
+ else
+ {
+#ifndef __linux__
+ gettimeofday(&etv, 0);
+ etv.tv_sec += timeout;
+#else
+ tv.tv_sec = timeout;
+ tv.tv_usec = 0;
+#endif
+ tvp = &tv;
+ }
+
+ int fd = K3ProcessController::instance()->notifierFd();
+ for(;;)
+ {
+ fd_set fds;
+ FD_ZERO( &fds );
+ FD_SET( fd, &fds );
+
+#ifndef __linux__
+ if (tvp)
+ {
+ gettimeofday(&tv, 0);
+ timersub(&etv, &tv, &tv);
+ if (tv.tv_sec < 0)
+ tv.tv_sec = tv.tv_usec = 0;
+ }
+#endif
+
+ switch( select( fd+1, &fds, 0, 0, tvp ) )
+ {
+ case -1:
+ if( errno == EINTR )
+ break;
+ // fall through; should happen if tvp->tv_sec < 0
+ case 0:
+ K3ProcessController::instance()->rescheduleCheck();
+ return false;
+ default:
+ K3ProcessController::instance()->unscheduleCheck();
+ if (waitpid(pid_, &status, WNOHANG) != 0) // error finishes, too
+ {
+ processHasExited(status);
+ K3ProcessController::instance()->rescheduleCheck();
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+
+
+bool K3Process::normalExit() const
+{
+ return (pid_ != 0) && !runs && WIFEXITED(status);
+}
+
+
+bool K3Process::signalled() const
+{
+ return (pid_ != 0) && !runs && WIFSIGNALED(status);
+}
+
+
+bool K3Process::coreDumped() const
+{
+#ifdef WCOREDUMP
+ return signalled() && WCOREDUMP(status);
+#else
+ return false;
+#endif
+}
+
+
+int K3Process::exitStatus() const
+{
+ return WEXITSTATUS(status);
+}
+
+
+int K3Process::exitSignal() const
+{
+ return WTERMSIG(status);
+}
+
+
+bool K3Process::writeStdin(const char *buffer, int buflen)
+{
+ // if there is still data pending, writing new data
+ // to stdout is not allowed (since it could also confuse
+ // kprocess ...)
+ if (input_data != 0)
+ return false;
+
+ if (communication & Stdin) {
+ input_data = buffer;
+ input_sent = 0;
+ input_total = buflen;
+ innot->setEnabled(true);
+ if (input_total)
+ slotSendData(0);
+ return true;
+ } else
+ return false;
+}
+
+void K3Process::suspend()
+{
+ if (outnot)
+ outnot->setEnabled(false);
+}
+
+void K3Process::resume()
+{
+ if (outnot)
+ outnot->setEnabled(true);
+}
+
+bool K3Process::closeStdin()
+{
+ if (communication & Stdin) {
+ communication = communication & ~Stdin;
+ delete innot;
+ innot = 0;
+ if (!(d->usePty & Stdin))
+ close(in[1]);
+ in[1] = -1;
+ return true;
+ } else
+ return false;
+}
+
+bool K3Process::closeStdout()
+{
+ if (communication & Stdout) {
+ communication = communication & ~Stdout;
+ delete outnot;
+ outnot = 0;
+ if (!(d->usePty & Stdout))
+ close(out[0]);
+ out[0] = -1;
+ return true;
+ } else
+ return false;
+}
+
+bool K3Process::closeStderr()
+{
+ if (communication & Stderr) {
+ communication = communication & ~Stderr;
+ delete errnot;
+ errnot = 0;
+ if (!(d->usePty & Stderr))
+ close(err[0]);
+ err[0] = -1;
+ return true;
+ } else
+ return false;
+}
+
+bool K3Process::closePty()
+{
+ if (d->pty && d->pty->masterFd() >= 0) {
+ if (d->addUtmp)
+ d->pty->logout();
+ d->pty->close();
+ return true;
+ } else
+ return false;
+}
+
+void K3Process::closeAll()
+{
+ closeStdin();
+ closeStdout();
+ closeStderr();
+ closePty();
+}
+
+/////////////////////////////
+// protected slots //
+/////////////////////////////
+
+
+
+void K3Process::slotChildOutput(int fdno)
+{
+ if (!childOutput(fdno))
+ closeStdout();
+}
+
+
+void K3Process::slotChildError(int fdno)
+{
+ if (!childError(fdno))
+ closeStderr();
+}
+
+
+void K3Process::slotSendData(int)
+{
+ if (input_sent == input_total) {
+ innot->setEnabled(false);
+ input_data = 0;
+ emit wroteStdin(this);
+ } else {
+ int result = ::write(in[1], input_data+input_sent, input_total-input_sent);
+ if (result >= 0)
+ {
+ input_sent += result;
+ }
+ else if ((errno != EAGAIN) && (errno != EINTR))
+ {
+ qDebug() << "Error writing to stdin of child process" << endl;
+ closeStdin();
+ }
+ }
+}
+
+void K3Process::setUseShell(bool useShell, const char *shell)
+{
+ d->useShell = useShell;
+ if (shell && *shell)
+ d->shell = shell;
+ else
+// #ifdef NON_FREE // ... as they ship non-POSIX /bin/sh
+#if !defined(__linux__) && !defined(__FreeBSD__) && !defined(__NetBSD__) && !defined(__OpenBSD__) && !defined(__GNU__) && !defined(__DragonFly__)
+ // Solaris POSIX ...
+ if (!access( "/usr/xpg4/bin/sh", X_OK ))
+ d->shell = "/usr/xpg4/bin/sh";
+ else
+ // ... which links here anyway
+ if (!access( "/bin/ksh", X_OK ))
+ d->shell = "/bin/ksh";
+ else
+ // dunno, maybe superfluous?
+ if (!access( "/usr/ucb/sh", X_OK ))
+ d->shell = "/usr/ucb/sh";
+ else
+#endif
+ d->shell = "/bin/sh";
+}
+
+void K3Process::setUsePty(Communication usePty, bool addUtmp)
+{
+ d->usePty = usePty;
+ d->addUtmp = addUtmp;
+ if (usePty) {
+ if (!d->pty)
+ d->pty = new KPty;
+ } else {
+ delete d->pty;
+ d->pty = 0;
+ }
+}
+
+KPty *K3Process::pty() const
+{
+ return d->pty;
+}
+
+QString K3Process::quote(const QString &arg)
+{
+ QChar q('\'');
+ return QString(arg).replace(q, "'\\''").prepend(q).append(q);
+}
+
+
+//////////////////////////////
+// private member functions //
+//////////////////////////////
+
+
+void K3Process::processHasExited(int state)
+{
+ // only successfully run NotifyOnExit processes ever get here
+
+ status = state;
+ runs = false; // do this before commClose, so it knows we're dead
+
+ commClose(); // cleanup communication sockets
+
+ if (run_mode != DontCare)
+ emit processExited(this);
+}
+
+
+
+int K3Process::childOutput(int fdno)
+{
+ if (communication & NoRead) {
+ int len = -1;
+ emit receivedStdout(fdno, len);
+ errno = 0; // Make sure errno doesn't read "EAGAIN"
+ return len;
+ }
+ else
+ {
+ char buffer[1025];
+ int len;
+
+ len = ::read(fdno, buffer, 1024);
+
+ if (len > 0) {
+ buffer[len] = 0; // Just in case.
+ emit receivedStdout(this, buffer, len);
+ }
+ return len;
+ }
+}
+
+int K3Process::childError(int fdno)
+{
+ char buffer[1025];
+ int len;
+
+ len = ::read(fdno, buffer, 1024);
+
+ if (len > 0) {
+ buffer[len] = 0; // Just in case.
+ emit receivedStderr(this, buffer, len);
+ }
+ return len;
+}
+
+
+int K3Process::setupCommunication(Communication comm)
+{
+ // PTY stuff //
+ if (d->usePty)
+ {
+ // cannot communicate on both stderr and stdout if they are both on the pty
+ if (!(~(comm & d->usePty) & (Stdout | Stderr))) {
+ qWarning() << "Invalid usePty/communication combination (" << d->usePty << "/" << comm << ")" << endl;
+ return 0;
+ }
+ if (!d->pty->open())
+ return 0;
+
+ int rcomm = comm & d->usePty;
+ int mfd = d->pty->masterFd();
+ if (rcomm & Stdin)
+ in[1] = mfd;
+ if (rcomm & Stdout)
+ out[0] = mfd;
+ if (rcomm & Stderr)
+ err[0] = mfd;
+ }
+
+ communication = comm;
+
+ comm = comm & ~d->usePty;
+ if (comm & Stdin) {
+ if (socketpair(AF_UNIX, SOCK_STREAM, 0, in))
+ goto fail0;
+ fcntl(in[0], F_SETFD, FD_CLOEXEC);
+ fcntl(in[1], F_SETFD, FD_CLOEXEC);
+ }
+ if (comm & Stdout) {
+ if (socketpair(AF_UNIX, SOCK_STREAM, 0, out))
+ goto fail1;
+ fcntl(out[0], F_SETFD, FD_CLOEXEC);
+ fcntl(out[1], F_SETFD, FD_CLOEXEC);
+ }
+ if (comm & Stderr) {
+ if (socketpair(AF_UNIX, SOCK_STREAM, 0, err))
+ goto fail2;
+ fcntl(err[0], F_SETFD, FD_CLOEXEC);
+ fcntl(err[1], F_SETFD, FD_CLOEXEC);
+ }
+ return 1; // Ok
+ fail2:
+ if (comm & Stdout)
+ {
+ close(out[0]);
+ close(out[1]);
+ out[0] = out[1] = -1;
+ }
+ fail1:
+ if (comm & Stdin)
+ {
+ close(in[0]);
+ close(in[1]);
+ in[0] = in[1] = -1;
+ }
+ fail0:
+ communication = NoCommunication;
+ return 0; // Error
+}
+
+
+
+int K3Process::commSetupDoneP()
+{
+ int rcomm = communication & ~d->usePty;
+ if (rcomm & Stdin)
+ close(in[0]);
+ if (rcomm & Stdout)
+ close(out[1]);
+ if (rcomm & Stderr)
+ close(err[1]);
+ in[0] = out[1] = err[1] = -1;
+
+ // Don't create socket notifiers if no interactive comm is to be expected
+ if (run_mode != NotifyOnExit && run_mode != OwnGroup)
+ return 1;
+
+ if (communication & Stdin) {
+ fcntl(in[1], F_SETFL, O_NONBLOCK | fcntl(in[1], F_GETFL));
+ innot = new QSocketNotifier(in[1], QSocketNotifier::Write, this);
+ Q_CHECK_PTR(innot);
+ innot->setEnabled(false); // will be enabled when data has to be sent
+ QObject::connect(innot, SIGNAL(activated(int)),
+ this, SLOT(slotSendData(int)));
+ }
+
+ if (communication & Stdout) {
+ outnot = new QSocketNotifier(out[0], QSocketNotifier::Read, this);
+ Q_CHECK_PTR(outnot);
+ QObject::connect(outnot, SIGNAL(activated(int)),
+ this, SLOT(slotChildOutput(int)));
+ if (communication & NoRead)
+ suspend();
+ }
+
+ if (communication & Stderr) {
+ errnot = new QSocketNotifier(err[0], QSocketNotifier::Read, this );
+ Q_CHECK_PTR(errnot);
+ QObject::connect(errnot, SIGNAL(activated(int)),
+ this, SLOT(slotChildError(int)));
+ }
+
+ return 1;
+}
+
+
+
+int K3Process::commSetupDoneC()
+{
+ int ok = 1;
+ if (d->usePty & Stdin) {
+ if (dup2(d->pty->slaveFd(), STDIN_FILENO) < 0) ok = 0;
+ } else if (communication & Stdin) {
+ if (dup2(in[0], STDIN_FILENO) < 0) ok = 0;
+ } else {
+ int null_fd = open( "/dev/null", O_RDONLY );
+ if (dup2( null_fd, STDIN_FILENO ) < 0) ok = 0;
+ close( null_fd );
+ }
+ struct linger so;
+ memset(&so, 0, sizeof(so));
+ if (d->usePty & Stdout) {
+ if (dup2(d->pty->slaveFd(), STDOUT_FILENO) < 0) ok = 0;
+ } else if (communication & Stdout) {
+ if (dup2(out[1], STDOUT_FILENO) < 0 ||
+ setsockopt(out[1], SOL_SOCKET, SO_LINGER, (char *)&so, sizeof(so)))
+ ok = 0;
+ if (communication & MergedStderr) {
+ if (dup2(out[1], STDERR_FILENO) < 0)
+ ok = 0;
+ }
+ }
+ if (d->usePty & Stderr) {
+ if (dup2(d->pty->slaveFd(), STDERR_FILENO) < 0) ok = 0;
+ } else if (communication & Stderr) {
+ if (dup2(err[1], STDERR_FILENO) < 0 ||
+ setsockopt(err[1], SOL_SOCKET, SO_LINGER, (char *)&so, sizeof(so)))
+ ok = 0;
+ }
+
+ // don't even think about closing all open fds here or anywhere else
+
+ // PTY stuff //
+ if (d->usePty) {
+ d->pty->setCTty();
+ if (d->addUtmp)
+ d->pty->login(getenv("USER"), getenv("DISPLAY"));
+ }
+
+ return ok;
+}
+
+
+
+void K3Process::commClose()
+{
+ closeStdin();
+
+ if (pid_) { // detached, failed, and killed processes have no output. basta. :)
+ // If both channels are being read we need to make sure that one socket
+ // buffer doesn't fill up whilst we are waiting for data on the other
+ // (causing a deadlock). Hence we need to use select.
+
+ int notfd = K3ProcessController::instance()->notifierFd();
+
+ while ((communication & (Stdout | Stderr)) || runs) {
+ fd_set rfds;
+ FD_ZERO(&rfds);
+ struct timeval timeout, *p_timeout;
+
+ int max_fd = 0;
+ if (communication & Stdout) {
+ FD_SET(out[0], &rfds);
+ max_fd = out[0];
+ }
+ if (communication & Stderr) {
+ FD_SET(err[0], &rfds);
+ if (err[0] > max_fd)
+ max_fd = err[0];
+ }
+ if (runs) {
+ FD_SET(notfd, &rfds);
+ if (notfd > max_fd)
+ max_fd = notfd;
+ // If the process is still running we block until we
+ // receive data or the process exits.
+ p_timeout = 0; // no timeout
+ } else {
+ // If the process has already exited, we only check
+ // the available data, we don't wait for more.
+ timeout.tv_sec = timeout.tv_usec = 0; // timeout immediately
+ p_timeout = &timeout;
+ }
+
+ int fds_ready = select(max_fd+1, &rfds, 0, 0, p_timeout);
+ if (fds_ready < 0) {
+ if (errno == EINTR)
+ continue;
+ break;
+ } else if (!fds_ready)
+ break;
+
+ if ((communication & Stdout) && FD_ISSET(out[0], &rfds))
+ slotChildOutput(out[0]);
+
+ if ((communication & Stderr) && FD_ISSET(err[0], &rfds))
+ slotChildError(err[0]);
+
+ if (runs && FD_ISSET(notfd, &rfds)) {
+ runs = false; // hack: signal potential exit
+ return; // don't close anything, we will be called again
+ }
+ }
+ }
+
+ closeStdout();
+ closeStderr();
+
+ closePty();
+}
+
+
+
+///////////////////////////
+// CC: Class K3ShellProcess
+///////////////////////////
+
+K3ShellProcess::K3ShellProcess(const char *shellname):
+ K3Process(), d(0)
+{
+ setUseShell( true, shellname ? shellname : getenv("SHELL") );
+}
+
+K3ShellProcess::~K3ShellProcess() {
+}
+
+QString K3ShellProcess::quote(const QString &arg)
+{
+ return K3Process::quote(arg);
+}
+
+bool K3ShellProcess::start(RunMode runmode, Communication comm)
+{
+ return K3Process::start(runmode, comm);
+}
+
+
+//#include "moc_k3process.cpp"
diff --git a/qtermwidget/k3process.h b/qtermwidget/k3process.h
new file mode 100644
index 0000000..f8388ea
--- /dev/null
+++ b/qtermwidget/k3process.h
@@ -0,0 +1,890 @@
+/* This file is part of the KDE libraries
+ Copyright (C) 1997 Christian Czezakte (e9025461@student.tuwien.ac.at)
+
+ Rewritten for QT4 by e_k <e_k at users.sourceforge.net>, Copyright (C)2008
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#ifndef K3PROCESS_H
+#define K3PROCESS_H
+
+#include <QtCore/QObject>
+
+#include <sys/types.h> // for pid_t
+#include <sys/wait.h>
+#include <signal.h>
+#include <unistd.h>
+
+class QSocketNotifier;
+class K3ProcessPrivate;
+class KPty;
+
+/**
+ * @obsolete Use KProcess and KPtyProcess instead.
+ *
+ * Child process invocation, monitoring and control.
+ * This class works only in the application's main thread.
+ *
+ * <b>General usage and features:</b>\n
+ *
+ * This class allows a KDE application to start child processes without having
+ * to worry about UN*X signal handling issues and zombie process reaping.
+ *
+ * @see K3ProcIO
+ *
+ * Basically, this class distinguishes three different ways of running
+ * child processes:
+ *
+ * @li DontCare -- The child process is invoked and both the child
+ * process and the parent process continue concurrently.
+ *
+ * The process is started in an own session (see setsid(2)).
+ *
+ * @li NotifyOnExit -- The child process is invoked and both the
+ * child and the parent process run concurrently.
+ *
+ * When the child process exits, the K3Process instance
+ * corresponding to it emits the Qt signal processExited().
+ * Since this signal is @em not emitted from within a UN*X
+ * signal handler, arbitrary function calls can be made.
+ *
+ * Be aware: When the K3Process object gets destructed, the child
+ * process will be killed if it is still running!
+ * This means in particular, that it usually makes no sense to use
+ * a K3Process on the stack with NotifyOnExit.
+ *
+ * @li OwnGroup -- like NotifyOnExit, but the child process is started
+ * in an own process group (and an own session, FWIW). The behavior of
+ * kill() changes to killing the whole process group - this makes
+ * this mode useful for implementing primitive job management. It can be
+ * used to work around broken wrapper scripts that don't propagate signals
+ * to the "real" program. However, use this with care, as you disturb the
+ * shell's job management if your program is started from the command line.
+ *
+ * @li Block -- The child process starts and the parent process
+ * is suspended until the child process exits. (@em Really not recommended
+ * for programs with a GUI.)
+ * In this mode the parent can read the child's output, but can't send it any
+ * input.
+ *
+ * K3Process also provides several functions for determining the exit status
+ * and the pid of the child process it represents.
+ *
+ * Furthermore it is possible to supply command-line arguments to the process
+ * in a clean fashion (no null-terminated stringlists and such...)
+ *
+ * A small usage example:
+ * \code
+ * K3Process *proc = new K3Process;
+ *
+ * *proc << "my_executable";
+ * *proc << "These" << "are" << "the" << "command" << "line" << "args";
+ * QApplication::connect(proc, SIGNAL(processExited(K3Process *)),
+ * pointer_to_my_object, SLOT(my_objects_slot(K3Process *)));
+ * proc->start();
+ * \endcode
+ *
+ * This will start "my_executable" with the commandline arguments "These"...
+ *
+ * When the child process exits, the slot will be invoked.
+ *
+ * <b>Communication with the child process:</b>\n
+ *
+ * K3Process supports communication with the child process through
+ * stdin/stdout/stderr.
+ *
+ * The following functions are provided for getting data from the child
+ * process or sending data to the child's stdin (For more information,
+ * have a look at the documentation of each function):
+ *
+ * @li writeStdin()
+ * -- Transmit data to the child process' stdin. When all data was sent, the
+ * signal wroteStdin() is emitted.
+ *
+ * @li When data arrives at stdout or stderr, the signal receivedStdout()
+ * resp. receivedStderr() is emitted.
+ *
+ * @li You can shut down individual communication channels with
+ * closeStdin(), closeStdout(), and closeStderr(), resp.
+ *
+ * @author Christian Czezatke e9025461@student.tuwien.ac.at
+ *
+ **/
+class K3Process : public QObject
+{
+ Q_OBJECT
+
+public:
+
+ /**
+ * Modes in which the communication channels can be opened.
+ *
+ * If communication for more than one channel is required,
+ * the values should be or'ed together, for example to get
+ * communication with stdout as well as with stdin, you would
+ * specify @p Stdin | @p Stdout
+ *
+ */
+ enum CommunicationFlag {
+ NoCommunication = 0, /**< No communication with the process. */
+ Stdin = 1, /**< Connect to write to the process with writeStdin(). */
+ Stdout = 2, /**< Connect to read from the process' output. */
+ Stderr = 4, /**< Connect to read from the process' stderr. */
+ AllOutput = 6, /**< Connects to all output channels. */
+ All = 7, /**< Connects to all channels. */
+ NoRead = 8, /**< If specified with Stdout, no data is actually read from stdout,
+ * only the signal receivedStdout(int fd, int &len) is emitted. */
+ CTtyOnly = NoRead, /**< Tells setUsePty() to create a PTY for the process
+ * and make it the process' controlling TTY, but does not
+ * redirect any I/O channel to the PTY. */
+ MergedStderr = 16 /**< If specified with Stdout, the process' stderr will be
+ * redirected onto the same file handle as its stdout, i.e.,
+ * all error output will be signalled with receivedStdout().
+ * Don't specify Stderr if you specify MergedStderr. */
+ };
+
+ Q_DECLARE_FLAGS(Communication, CommunicationFlag)
+
+ /**
+ * Run-modes for a child process.
+ */
+ enum RunMode {
+ /**
+ * The application does not receive notifications from the subprocess when
+ * it is finished or aborted.
+ */
+ DontCare,
+ /**
+ * The application is notified when the subprocess dies.
+ */
+ NotifyOnExit,
+ /**
+ * The application is suspended until the started process is finished.
+ */
+ Block,
+ /**
+ * Same as NotifyOnExit, but the process is run in an own session,
+ * just like with DontCare.
+ */
+ OwnGroup
+ };
+
+ /**
+ * Constructor
+ */
+ explicit K3Process( QObject* parent=0L );
+
+ /**
+ *Destructor:
+ *
+ * If the process is running when the destructor for this class
+ * is called, the child process is killed with a SIGKILL, but
+ * only if the run mode is not of type @p DontCare.
+ * Processes started as @p DontCare keep running anyway.
+ */
+ virtual ~K3Process();
+
+ /**
+ * Sets the executable and the command line argument list for this process.
+ *
+ * For example, doing an "ls -l /usr/local/bin" can be achieved by:
+ * \code
+ * K3Process p;
+ * ...
+ * p << "ls" << "-l" << "/usr/local/bin"
+ * \endcode
+ *
+ * @param arg the argument to add
+ * @return a reference to this K3Process
+ **/
+ K3Process &operator<<(const QString& arg);
+ /**
+ * Similar to previous method, takes a char *, supposed to be in locale 8 bit already.
+ */
+ K3Process &operator<<(const char * arg);
+ /**
+ * Similar to previous method, takes a QByteArray, supposed to be in locale 8 bit already.
+ * @param arg the argument to add
+ * @return a reference to this K3Process
+ */
+ K3Process &operator<<(const QByteArray & arg);
+
+ /**
+ * Sets the executable and the command line argument list for this process,
+ * in a single method call, or add a list of arguments.
+ * @param args the arguments to add
+ * @return a reference to this K3Process
+ **/
+ K3Process &operator<<(const QStringList& args);
+
+ /**
+ * Clear a command line argument list that has been set by using
+ * operator<<.
+ */
+ void clearArguments();
+
+ /**
+ * Starts the process.
+ * For a detailed description of the
+ * various run modes and communication semantics, have a look at the
+ * general description of the K3Process class. Note that if you use
+ * setUsePty( Stdout | Stderr, \<bool\> ), you cannot use Stdout | Stderr
+ * here - instead, use Stdout only to receive the mixed output.
+ *
+ * The following problems could cause this function to
+ * return false:
+ *
+ * @li The process is already running.
+ * @li The command line argument list is empty.
+ * @li The the @p comm parameter is incompatible with the selected pty usage.
+ * @li The starting of the process failed (could not fork).
+ * @li The executable was not found.
+ *
+ * @param runmode The Run-mode for the process.
+ * @param comm Specifies which communication channels should be
+ * established to the child process (stdin/stdout/stderr). By default,
+ * no communication takes place and the respective communication
+ * signals will never get emitted.
+ *
+ * @return true on success, false on error
+ * (see above for error conditions)
+ **/
+ virtual bool start(RunMode runmode = NotifyOnExit,
+ Communication comm = NoCommunication);
+
+ /**
+ * Stop the process (by sending it a signal).
+ *
+ * @param signo The signal to send. The default is SIGTERM.
+ * @return true if the signal was delivered successfully.
+ */
+ virtual bool kill(int signo = SIGTERM);
+
+ /**
+ * Checks whether the process is running.
+ * @return true if the process is (still) considered to be running
+ */
+ bool isRunning() const;
+
+ /** Returns the process id of the process.
+ *
+ * If it is called after
+ * the process has exited, it returns the process id of the last
+ * child process that was created by this instance of K3Process.
+ *
+ * Calling it before any child process has been started by this
+ * K3Process instance causes pid() to return 0.
+ * @return the pid of the process or 0 if no process has been started yet.
+ **/
+ pid_t pid() const;
+
+ /**
+ * Suspend processing of data from stdout of the child process.
+ */
+ void suspend();
+
+ /**
+ * Resume processing of data from stdout of the child process.
+ */
+ void resume();
+
+ /**
+ * Suspend execution of the current thread until the child process dies
+ * or the timeout hits. This function is not recommended for programs
+ * with a GUI.
+ * @param timeout timeout in seconds. -1 means wait indefinitely.
+ * @return true if the process exited, false if the timeout hit.
+ */
+ bool wait(int timeout = -1);
+
+ /**
+ * Checks whether the process exited cleanly.
+ *
+ * @return true if the process has already finished and has exited
+ * "voluntarily", ie: it has not been killed by a signal.
+ */
+ bool normalExit() const;
+
+ /**
+ * Checks whether the process was killed by a signal.
+ *
+ * @return true if the process has already finished and has not exited
+ * "voluntarily", ie: it has been killed by a signal.
+ */
+ bool signalled() const;
+
+ /**
+ * Checks whether a killed process dumped core.
+ *
+ * @return true if signalled() returns true and the process
+ * dumped core. Note that on systems that don't define the
+ * WCOREDUMP macro, the return value is always false.
+ */
+ bool coreDumped() const;
+
+ /**
+ * Returns the exit status of the process.
+ *
+ * @return the exit status of the process. Note that this value
+ * is not valid if normalExit() returns false.
+ */
+ int exitStatus() const;
+
+ /**
+ * Returns the signal the process was killed by.
+ *
+ * @return the signal number that caused the process to exit.
+ * Note that this value is not valid if signalled() returns false.
+ */
+ int exitSignal() const;
+
+ /**
+ * Transmit data to the child process' stdin.
+ *
+ * This function may return false in the following cases:
+ *
+ * @li The process is not currently running.
+ * This implies that you cannot use this function in Block mode.
+ *
+ * @li Communication to stdin has not been requested in the start() call.
+ *
+ * @li Transmission of data to the child process by a previous call to
+ * writeStdin() is still in progress.
+ *
+ * Please note that the data is sent to the client asynchronously,
+ * so when this function returns, the data might not have been
+ * processed by the child process.
+ * That means that you must not free @p buffer or call writeStdin()
+ * again until either a wroteStdin() signal indicates that the
+ * data has been sent or a processExited() signal shows that
+ * the child process is no longer alive.
+ *
+ * If all the data has been sent to the client, the signal
+ * wroteStdin() will be emitted.
+ *
+ * This function does not work when the process is start()ed in Block mode.
+ *
+ * @param buffer the buffer to write
+ * @param buflen the length of the buffer
+ * @return false if an error has occurred
+ **/
+ bool writeStdin(const char *buffer, int buflen);
+
+ /**
+ * Shuts down the Stdin communication link. If no pty is used, this
+ * causes "EOF" to be indicated on the child's stdin file descriptor.
+ *
+ * @return false if no Stdin communication link exists (any more).
+ */
+ bool closeStdin();
+
+ /**
+ * Shuts down the Stdout communication link. If no pty is used, any further
+ * attempts by the child to write to its stdout file descriptor will cause
+ * it to receive a SIGPIPE.
+ *
+ * @return false if no Stdout communication link exists (any more).
+ */
+ bool closeStdout();
+
+ /**
+ * Shuts down the Stderr communication link. If no pty is used, any further
+ * attempts by the child to write to its stderr file descriptor will cause
+ * it to receive a SIGPIPE.
+ *
+ * @return false if no Stderr communication link exists (any more).
+ */
+ bool closeStderr();
+
+ /**
+ * Deletes the optional utmp entry and closes the pty.
+ *
+ * Make sure to shut down any communication links that are using the pty
+ * before calling this function.
+ *
+ * @return false if the pty is not open (any more).
+ */
+ bool closePty();
+
+ /**
+ * @brief Close stdin, stdout, stderr and the pty
+ *
+ * This is the same that calling all close* functions in a row:
+ * @see closeStdin, @see closeStdout, @see closeStderr and @see closePty
+ */
+ void closeAll();
+
+ /**
+ * Lets you see what your arguments are for debugging.
+ * @return the list of arguments
+ */
+ const QList<QByteArray> &args() /* const */ { return arguments; }
+
+ /**
+ * Controls whether the started process should drop any
+ * setuid/setgid privileges or whether it should keep them.
+ * Note that this function is mostly a dummy, as the KDE libraries
+ * currently refuse to run with setuid/setgid privileges.
+ *
+ * The default is false: drop privileges
+ * @param keepPrivileges true to keep the privileges
+ */
+ void setRunPrivileged(bool keepPrivileges);
+
+ /**
+ * Returns whether the started process will drop any
+ * setuid/setgid privileges or whether it will keep them.
+ * @return true if the process runs privileged
+ */
+ bool runPrivileged() const;
+
+ /**
+ * Adds the variable @p name to the process' environment.
+ * This function must be called before starting the process.
+ * @param name the name of the environment variable
+ * @param value the new value for the environment variable
+ */
+ void setEnvironment(const QString &name, const QString &value);
+
+ /**
+ * Changes the current working directory (CWD) of the process
+ * to be started.
+ * This function must be called before starting the process.
+ * @param dir the new directory
+ */
+ void setWorkingDirectory(const QString &dir);
+
+ /**
+ * Specify whether to start the command via a shell or directly.
+ * The default is to start the command directly.
+ * If @p useShell is true @p shell will be used as shell, or
+ * if shell is empty, /bin/sh will be used.
+ *
+ * When using a shell, the caller should make sure that all filenames etc.
+ * are properly quoted when passed as argument.
+ * @see quote()
+ * @param useShell true if the command should be started via a shell
+ * @param shell the path to the shell that will execute the process, or
+ * 0 to use /bin/sh. Use getenv("SHELL") to use the user's
+ * default shell, but note that doing so is usually a bad idea
+ * for shell compatibility reasons.
+ */
+ void setUseShell(bool useShell, const char *shell = 0);
+
+ /**
+ * This function can be used to quote an argument string such that
+ * the shell processes it properly. This is e. g. necessary for
+ * user-provided file names which may contain spaces or quotes.
+ * It also prevents expansion of wild cards and environment variables.
+ * @param arg the argument to quote
+ * @return the quoted argument
+ */
+ static QString quote(const QString &arg);
+
+ /**
+ * Detaches K3Process from child process. All communication is closed.
+ * No exit notification is emitted any more for the child process.
+ * Deleting the K3Process will no longer kill the child process.
+ * Note that the current process remains the parent process of the
+ * child process.
+ */
+ void detach();
+
+ /**
+ * Specify whether to create a pty (pseudo-terminal) for running the
+ * command.
+ * This function should be called before starting the process.
+ *
+ * @param comm for which stdio handles to use a pty. Note that it is not
+ * allowed to specify Stdout and Stderr at the same time both here and to
+ * start (there is only one pty, so they cannot be distinguished).
+ * @param addUtmp true if a utmp entry should be created for the pty
+ */
+ void setUsePty(Communication comm, bool addUtmp);
+
+ /**
+ * Obtains the pty object used by this process. The return value is
+ * valid only after setUsePty() was used with a non-zero argument.
+ * The pty is open only while the process is running.
+ * @return a pointer to the pty object
+ */
+ KPty *pty() const;
+
+ /**
+ * More or less intuitive constants for use with setPriority().
+ */
+ enum { PrioLowest = 20, PrioLow = 10, PrioLower = 5, PrioNormal = 0,
+ PrioHigher = -5, PrioHigh = -10, PrioHighest = -19 };
+
+ /**
+ * Sets the scheduling priority of the process.
+ * @param prio the new priority in the range -20 (high) to 19 (low).
+ * @return false on error; see setpriority(2) for possible reasons.
+ */
+ bool setPriority(int prio);
+
+Q_SIGNALS:
+ /**
+ * Emitted after the process has terminated when
+ * the process was run in the @p NotifyOnExit (==default option to
+ * start() ) or the Block mode.
+ * @param proc a pointer to the process that has exited
+ **/
+ void processExited(K3Process *proc);
+
+
+ /**
+ * Emitted, when output from the child process has
+ * been received on stdout.
+ *
+ * To actually get this signal, the Stdout communication link
+ * has to be turned on in start().
+ *
+ * @param proc a pointer to the process that has received the output
+ * @param buffer The data received.
+ * @param buflen The number of bytes that are available.
+ *
+ * You should copy the information contained in @p buffer to your private
+ * data structures before returning from the slot.
+ * Example:
+ * \code
+ * QString myBuf = QLatin1String(buffer, buflen);
+ * \endcode
+ **/
+ void receivedStdout(K3Process *proc, char *buffer, int buflen);
+
+ /**
+ * Emitted when output from the child process has
+ * been received on stdout.
+ *
+ * To actually get this signal, the Stdout communication link
+ * has to be turned on in start() and the
+ * NoRead flag must have been passed.
+ *
+ * You will need to explicitly call resume() after your call to start()
+ * to begin processing data from the child process' stdout. This is
+ * to ensure that this signal is not emitted when no one is connected
+ * to it, otherwise this signal will not be emitted.
+ *
+ * The data still has to be read from file descriptor @p fd.
+ * @param fd the file descriptor that provides the data
+ * @param len the number of bytes that have been read from @p fd must
+ * be written here
+ **/
+ void receivedStdout(int fd, int &len); // KDE4: change, broken API
+
+
+ /**
+ * Emitted, when output from the child process has
+ * been received on stderr.
+ *
+ * To actually get this signal, the Stderr communication link
+ * has to be turned on in start().
+ *
+ * You should copy the information contained in @p buffer to your private
+ * data structures before returning from the slot.
+ *
+ * @param proc a pointer to the process that has received the data
+ * @param buffer The data received.
+ * @param buflen The number of bytes that are available.
+ **/
+ void receivedStderr(K3Process *proc, char *buffer, int buflen);
+
+ /**
+ * Emitted after all the data that has been
+ * specified by a prior call to writeStdin() has actually been
+ * written to the child process.
+ * @param proc a pointer to the process
+ **/
+ void wroteStdin(K3Process *proc);
+
+
+protected Q_SLOTS:
+
+ /**
+ * This slot gets activated when data from the child's stdout arrives.
+ * It usually calls childOutput().
+ * @param fdno the file descriptor for the output
+ */
+ void slotChildOutput(int fdno);
+
+ /**
+ * This slot gets activated when data from the child's stderr arrives.
+ * It usually calls childError().
+ * @param fdno the file descriptor for the output
+ */
+ void slotChildError(int fdno);
+
+ /**
+ * Called when another bulk of data can be sent to the child's
+ * stdin. If there is no more data to be sent to stdin currently
+ * available, this function must disable the QSocketNotifier innot.
+ * @param dummy ignore this argument
+ */
+ void slotSendData(int dummy); // KDE 4: remove dummy
+
+protected:
+
+ /**
+ * Sets up the environment according to the data passed via
+ * setEnvironment()
+ */
+ void setupEnvironment();
+
+ /**
+ * The list of the process' command line arguments. The first entry
+ * in this list is the executable itself.
+ */
+ QList<QByteArray> arguments;
+ /**
+ * How to run the process (Block, NotifyOnExit, DontCare). You should
+ * not modify this data member directly from derived classes.
+ */
+ RunMode run_mode;
+ /**
+ * true if the process is currently running. You should not
+ * modify this data member directly from derived classes. Please use
+ * isRunning() for reading the value of this data member since it
+ * will probably be made private in later versions of K3Process.
+ */
+ bool runs;
+
+ /**
+ * The PID of the currently running process.
+ * You should not modify this data member in derived classes.
+ * Please use pid() instead of directly accessing this
+ * member since it will probably be made private in
+ * later versions of K3Process.
+ */
+ pid_t pid_;
+
+ /**
+ * The process' exit status as returned by waitpid(). You should not
+ * modify the value of this data member from derived classes. You should
+ * rather use exitStatus() than accessing this data member directly
+ * since it will probably be made private in further versions of
+ * K3Process.
+ */
+ int status;
+
+
+ /**
+ * If false, the child process' effective uid & gid will be reset to the
+ * real values.
+ * @see setRunPrivileged()
+ */
+ bool keepPrivs;
+
+ /**
+ * This function is called from start() right before a fork() takes
+ * place. According to the @p comm parameter this function has to initialize
+ * the in, out and err data members of K3Process.
+ *
+ * This function should return 1 if setting the needed communication channels
+ * was successful.
+ *
+ * The default implementation is to create UNIX STREAM sockets for the
+ * communication, but you could reimplement this function to establish a
+ * TCP/IP communication for network communication, for example.
+ */
+ virtual int setupCommunication(Communication comm);
+
+ /**
+ * Called right after a (successful) fork() on the parent side. This function
+ * will usually do some communications cleanup, like closing in[0],
+ * out[1] and out[1].
+ *
+ * Furthermore, it must also create the QSocketNotifiers innot,
+ * outnot and errnot and connect their Qt signals to the respective
+ * K3Process slots.
+ *
+ * For a more detailed explanation, it is best to have a look at the default
+ * implementation in kprocess.cpp.
+ */
+ virtual int commSetupDoneP();
+
+ /**
+ * Called right after a (successful) fork(), but before an exec() on the child
+ * process' side. It usually duplicates the in[0], out[1] and
+ * err[1] file handles to the respective standard I/O handles.
+ */
+ virtual int commSetupDoneC();
+
+
+ /**
+ * Immediately called after a successfully started process in NotifyOnExit
+ * mode has exited. This function normally calls commClose()
+ * and emits the processExited() signal.
+ * @param state the exit code of the process as returned by waitpid()
+ */
+ virtual void processHasExited(int state);
+
+ /**
+ * Cleans up the communication links to the child after it has exited.
+ * This function should act upon the values of pid() and runs.
+ * See the kprocess.cpp source for details.
+ * @li If pid() returns zero, the communication links should be closed
+ * only.
+ * @li if pid() returns non-zero and runs is false, all data
+ * immediately available from the communication links should be processed
+ * before closing them.
+ * @li if pid() returns non-zero and runs is true, the communication
+ * links should be monitored for data until the file handle returned by
+ * K3ProcessController::theKProcessController->notifierFd() becomes ready
+ * for reading - when it triggers, runs should be reset to false, and
+ * the function should be immediately left without closing anything.
+ *
+ * The previous semantics of this function are forward-compatible, but should
+ * be avoided, as they are prone to race conditions and can cause K3Process
+ * (and thus the whole program) to lock up under certain circumstances. At the
+ * end the function closes the communication links in any case. Additionally
+ * @li if runs is true, the communication links are monitored for data
+ * until all of them have returned EOF. Note that if any system function is
+ * interrupted (errno == EINTR) the polling loop should be aborted.
+ * @li if runs is false, all data immediately available from the
+ * communication links is processed.
+ */
+ virtual void commClose();
+
+ /* KDE 4 - commClose will be changed to perform cleanup only in all cases *
+ * If @p notfd is -1, all data immediately available from the
+ * communication links should be processed.
+ * If @p notfd is not -1, the communication links should be monitored
+ * for data until the file handle @p notfd becomes ready for reading.
+ */
+// virtual void commDrain(int notfd);
+
+ /**
+ * Specify the actual executable that should be started (first argument to execve)
+ * Normally the the first argument is the executable but you can
+ * override that with this function.
+ */
+ void setBinaryExecutable(const char *filename);
+
+ /**
+ * The socket descriptors for stdout.
+ */
+ int out[2];
+ /**
+ * The socket descriptors for stdin.
+ */
+ int in[2];
+ /**
+ * The socket descriptors for stderr.
+ */
+ int err[2];
+
+ /**
+ * The socket notifier for in[1].
+ */
+ QSocketNotifier *innot;
+ /**
+ * The socket notifier for out[0].
+ */
+ QSocketNotifier *outnot;
+ /**
+ * The socket notifier for err[0].
+ */
+ QSocketNotifier *errnot;
+
+ /**
+ * Lists the communication links that are activated for the child
+ * process. Should not be modified from derived classes.
+ */
+ Communication communication;
+
+ /**
+ * Called by slotChildOutput() this function copies data arriving from
+ * the child process' stdout to the respective buffer and emits the signal
+ * receivedStdout().
+ */
+ int childOutput(int fdno);
+
+ /**
+ * Called by slotChildError() this function copies data arriving from
+ * the child process' stderr to the respective buffer and emits the signal
+ * receivedStderr().
+ */
+ int childError(int fdno);
+
+ /**
+ * The buffer holding the data that has to be sent to the child
+ */
+ const char *input_data;
+ /**
+ * The number of bytes already transmitted
+ */
+ int input_sent;
+ /**
+ * The total length of input_data
+ */
+ int input_total;
+
+ /**
+ * K3ProcessController is a friend of K3Process because it has to have
+ * access to various data members.
+ */
+ friend class K3ProcessController;
+
+private:
+ K3ProcessPrivate* const d;
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(K3Process::Communication)
+
+class K3ShellProcessPrivate;
+
+/**
+* @obsolete
+*
+* Use K3Process and K3Process::setUseShell(true) instead.
+*
+* @short A class derived from K3Process to start child
+* processes through a shell.
+* @author Christian Czezatke <e9025461@student.tuwien.ac.at>
+*/
+class K3ShellProcess : public K3Process
+{
+ Q_OBJECT
+
+public:
+
+ /**
+ * Constructor
+ *
+ * If no shellname is specified, the user's default shell is used.
+ */
+ explicit K3ShellProcess(const char *shellname=0);
+
+ /**
+ * Destructor.
+ */
+ ~K3ShellProcess();
+
+ virtual bool start(RunMode runmode = NotifyOnExit,
+ Communication comm = NoCommunication);
+
+ static QString quote(const QString &arg);
+
+private:
+ K3ShellProcessPrivate* const d;
+};
+
+
+
+#endif
+
diff --git a/qtermwidget/k3processcontroller.cpp b/qtermwidget/k3processcontroller.cpp
new file mode 100644
index 0000000..ccc53c4
--- /dev/null
+++ b/qtermwidget/k3processcontroller.cpp
@@ -0,0 +1,334 @@
+/* This file is part of the KDE libraries
+ Copyright (C) 1997 Christian Czezakte (e9025461@student.tuwien.ac.at)
+
+ Rewritten for QT4 by e_k <e_k at users.sourceforge.net>, Copyright (C)2008
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#include "k3processcontroller.h"
+#include "k3process.h"
+
+//#include <config.h>
+
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <QtCore/QSocketNotifier>
+
+class K3ProcessController::Private
+{
+public:
+ Private()
+ : needcheck( false ),
+ notifier( 0 )
+ {
+ }
+
+ ~Private()
+ {
+ delete notifier;
+ }
+
+ int fd[2];
+ bool needcheck;
+ QSocketNotifier *notifier;
+ QList<K3Process*> kProcessList;
+ QList<int> unixProcessList;
+ static struct sigaction oldChildHandlerData;
+ static bool handlerSet;
+ static int refCount;
+ static K3ProcessController* instance;
+};
+
+K3ProcessController *K3ProcessController::Private::instance = 0;
+int K3ProcessController::Private::refCount = 0;
+
+void K3ProcessController::ref()
+{
+ if ( !Private::refCount ) {
+ Private::instance = new K3ProcessController;
+ setupHandlers();
+ }
+ Private::refCount++;
+}
+
+void K3ProcessController::deref()
+{
+ Private::refCount--;
+ if( !Private::refCount ) {
+ resetHandlers();
+ delete Private::instance;
+ Private::instance = 0;
+ }
+}
+
+K3ProcessController* K3ProcessController::instance()
+{
+ /*
+ * there were no safety guards in previous revisions, is that ok?
+ if ( !Private::instance ) {
+ ref();
+ }
+ */
+
+ return Private::instance;
+}
+
+K3ProcessController::K3ProcessController()
+ : d( new Private )
+{
+ if( pipe( d->fd ) )
+ {
+ perror( "pipe" );
+ abort();
+ }
+
+ fcntl( d->fd[0], F_SETFL, O_NONBLOCK ); // in case slotDoHousekeeping is called without polling first
+ fcntl( d->fd[1], F_SETFL, O_NONBLOCK ); // in case it fills up
+ fcntl( d->fd[0], F_SETFD, FD_CLOEXEC );
+ fcntl( d->fd[1], F_SETFD, FD_CLOEXEC );
+
+ d->notifier = new QSocketNotifier( d->fd[0], QSocketNotifier::Read );
+ d->notifier->setEnabled( true );
+ QObject::connect( d->notifier, SIGNAL(activated(int)),
+ SLOT(slotDoHousekeeping()));
+}
+
+K3ProcessController::~K3ProcessController()
+{
+#ifndef Q_OS_MAC
+/* not sure why, but this is causing lockups */
+ close( d->fd[0] );
+ close( d->fd[1] );
+#else
+#warning FIXME: why does close() freeze up destruction?
+#endif
+
+ delete d;
+}
+
+
+extern "C" {
+static void theReaper( int num )
+{
+ K3ProcessController::theSigCHLDHandler( num );
+}
+}
+
+#ifdef Q_OS_UNIX
+struct sigaction K3ProcessController::Private::oldChildHandlerData;
+#endif
+bool K3ProcessController::Private::handlerSet = false;
+
+void K3ProcessController::setupHandlers()
+{
+ if( Private::handlerSet )
+ return;
+ Private::handlerSet = true;
+
+#ifdef Q_OS_UNIX
+ struct sigaction act;
+ sigemptyset( &act.sa_mask );
+
+ act.sa_handler = SIG_IGN;
+ act.sa_flags = 0;
+ sigaction( SIGPIPE, &act, 0L );
+
+ act.sa_handler = theReaper;
+ act.sa_flags = SA_NOCLDSTOP;
+ // CC: take care of SunOS which automatically restarts interrupted system
+ // calls (and thus does not have SA_RESTART)
+#ifdef SA_RESTART
+ act.sa_flags |= SA_RESTART;
+#endif
+ sigaction( SIGCHLD, &act, &Private::oldChildHandlerData );
+
+ sigaddset( &act.sa_mask, SIGCHLD );
+ // Make sure we don't block this signal. gdb tends to do that :-(
+ sigprocmask( SIG_UNBLOCK, &act.sa_mask, 0 );
+#else
+ //TODO: win32
+#endif
+}
+
+void K3ProcessController::resetHandlers()
+{
+ if( !Private::handlerSet )
+ return;
+ Private::handlerSet = false;
+
+#ifdef Q_OS_UNIX
+ sigset_t mask, omask;
+ sigemptyset( &mask );
+ sigaddset( &mask, SIGCHLD );
+ sigprocmask( SIG_BLOCK, &mask, &omask );
+
+ struct sigaction act;
+ sigaction( SIGCHLD, &Private::oldChildHandlerData, &act );
+ if (act.sa_handler != theReaper) {
+ sigaction( SIGCHLD, &act, 0 );
+ Private::handlerSet = true;
+ }
+
+ sigprocmask( SIG_SETMASK, &omask, 0 );
+#else
+ //TODO: win32
+#endif
+ // there should be no problem with SIGPIPE staying SIG_IGN
+}
+
+// the pipe is needed to sync the child reaping with our event processing,
+// as otherwise there are race conditions, locking requirements, and things
+// generally get harder
+void K3ProcessController::theSigCHLDHandler( int arg )
+{
+ int saved_errno = errno;
+
+ char dummy = 0;
+ ::write( instance()->d->fd[1], &dummy, 1 );
+
+#ifdef Q_OS_UNIX
+ if ( Private::oldChildHandlerData.sa_handler != SIG_IGN &&
+ Private::oldChildHandlerData.sa_handler != SIG_DFL ) {
+ Private::oldChildHandlerData.sa_handler( arg ); // call the old handler
+ }
+#else
+ //TODO: win32
+#endif
+
+ errno = saved_errno;
+}
+
+int K3ProcessController::notifierFd() const
+{
+ return d->fd[0];
+}
+
+void K3ProcessController::unscheduleCheck()
+{
+ char dummy[16]; // somewhat bigger - just in case several have queued up
+ if( ::read( d->fd[0], dummy, sizeof(dummy) ) > 0 )
+ d->needcheck = true;
+}
+
+void
+K3ProcessController::rescheduleCheck()
+{
+ if( d->needcheck )
+ {
+ d->needcheck = false;
+ char dummy = 0;
+ ::write( d->fd[1], &dummy, 1 );
+ }
+}
+
+void K3ProcessController::slotDoHousekeeping()
+{
+ char dummy[16]; // somewhat bigger - just in case several have queued up
+ ::read( d->fd[0], dummy, sizeof(dummy) );
+
+ int status;
+ again:
+ QList<K3Process*>::iterator it( d->kProcessList.begin() );
+ QList<K3Process*>::iterator eit( d->kProcessList.end() );
+ while( it != eit )
+ {
+ K3Process *prc = *it;
+ if( prc->runs && waitpid( prc->pid_, &status, WNOHANG ) > 0 )
+ {
+ prc->processHasExited( status );
+ // the callback can nuke the whole process list and even 'this'
+ if (!instance())
+ return;
+ goto again;
+ }
+ ++it;
+ }
+ QList<int>::iterator uit( d->unixProcessList.begin() );
+ QList<int>::iterator ueit( d->unixProcessList.end() );
+ while( uit != ueit )
+ {
+ if( waitpid( *uit, 0, WNOHANG ) > 0 )
+ {
+ uit = d->unixProcessList.erase( uit );
+ deref(); // counterpart to addProcess, can invalidate 'this'
+ } else
+ ++uit;
+ }
+}
+
+bool K3ProcessController::waitForProcessExit( int timeout )
+{
+#ifdef Q_OS_UNIX
+ for(;;)
+ {
+ struct timeval tv, *tvp;
+ if (timeout < 0)
+ tvp = 0;
+ else
+ {
+ tv.tv_sec = timeout;
+ tv.tv_usec = 0;
+ tvp = &tv;
+ }
+
+ fd_set fds;
+ FD_ZERO( &fds );
+ FD_SET( d->fd[0], &fds );
+
+ switch( select( d->fd[0]+1, &fds, 0, 0, tvp ) )
+ {
+ case -1:
+ if( errno == EINTR )
+ continue;
+ // fall through; should never happen
+ case 0:
+ return false;
+ default:
+ slotDoHousekeeping();
+ return true;
+ }
+ }
+#else
+ //TODO: win32
+ return false;
+#endif
+}
+
+void K3ProcessController::addKProcess( K3Process* p )
+{
+ d->kProcessList.append( p );
+}
+
+void K3ProcessController::removeKProcess( K3Process* p )
+{
+ d->kProcessList.removeAll( p );
+}
+
+void K3ProcessController::addProcess( int pid )
+{
+ d->unixProcessList.append( pid );
+ ref(); // make sure we stay around when the K3Process goes away
+}
+
+//#include "moc_k3processcontroller.cpp"
diff --git a/qtermwidget/k3processcontroller.h b/qtermwidget/k3processcontroller.h
new file mode 100644
index 0000000..c077af2
--- /dev/null
+++ b/qtermwidget/k3processcontroller.h
@@ -0,0 +1,137 @@
+/* This file is part of the KDE libraries
+ Copyright (C) 1997 Christian Czezakte (e9025461@student.tuwien.ac.at)
+
+ Rewritten for QT4 by e_k <e_k at users.sourceforge.net>, Copyright (C)2008
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#ifndef K3PROCCTRL_H
+#define K3PROCCTRL_H
+
+#include <QtCore/QList>
+#include <k3process.h>
+
+
+/**
+ * @short Used internally by K3Process
+ * @internal
+ * @author Christian Czezatke <e9025461@student.tuwien.ac.at>
+ *
+ * A class for internal use by K3Process only. -- Exactly one instance
+ * of this class is created by KApplication.
+ *
+ * This class takes care of the actual (UN*X) signal handling.
+ */
+class K3ProcessController : public QObject
+{
+ Q_OBJECT
+
+public:
+ /**
+ * Create an instance if none exists yet.
+ * Called by KApplication::KApplication()
+ */
+ static void ref();
+
+ /**
+ * Destroy the instance if one exists and it is not referenced any more.
+ * Called by KApplication::~KApplication()
+ */
+ static void deref();
+
+ /**
+ * Only a single instance of this class is allowed at a time.
+ * This method provides access to that instance.
+ */
+ static K3ProcessController *instance();
+
+ /**
+ * Automatically called upon SIGCHLD. Never call it directly.
+ * If your application (or some library it uses) redirects SIGCHLD,
+ * the new signal handler (and only it) should call the old handler
+ * returned by sigaction().
+ * @internal
+ */
+ static void theSigCHLDHandler(int signal); // KDE4: private
+
+ /**
+ * Wait for any process to exit and handle their exit without
+ * starting an event loop.
+ * This function may cause K3Process to emit any of its signals.
+ *
+ * @param timeout the timeout in seconds. -1 means no timeout.
+ * @return true if a process exited, false
+ * if no process exited within @p timeout seconds.
+ */
+ bool waitForProcessExit(int timeout);
+
+ /**
+ * Call this function to defer processing of the data that became available
+ * on notifierFd().
+ */
+ void unscheduleCheck();
+
+ /**
+ * This function @em must be called at some point after calling
+ * unscheduleCheck().
+ */
+ void rescheduleCheck();
+
+ /*
+ * Obtain the file descriptor K3ProcessController uses to get notified
+ * about process exits. select() or poll() on it if you create a custom
+ * event loop that needs to act upon SIGCHLD.
+ * @return the file descriptor of the reading end of the notification pipe
+ */
+ int notifierFd() const;
+
+ /**
+ * @internal
+ */
+ void addKProcess( K3Process* );
+ /**
+ * @internal
+ */
+ void removeKProcess( K3Process* );
+ /**
+ * @internal
+ */
+ void addProcess( int pid );
+
+private Q_SLOTS:
+ void slotDoHousekeeping();
+
+private:
+ friend class I_just_love_gcc;
+
+ static void setupHandlers();
+ static void resetHandlers();
+
+ // Disallow instantiation
+ K3ProcessController();
+ ~K3ProcessController();
+
+ // Disallow assignment and copy-construction
+ K3ProcessController( const K3ProcessController& );
+ K3ProcessController& operator= ( const K3ProcessController& );
+
+ class Private;
+ Private * const d;
+};
+
+#endif
+
diff --git a/qtermwidget/kb-layouts/default.keytab b/qtermwidget/kb-layouts/default.keytab
new file mode 100644
index 0000000..76362cd
--- /dev/null
+++ b/qtermwidget/kb-layouts/default.keytab
@@ -0,0 +1,133 @@
+# [README.default.Keytab] Buildin Keyboard Table
+#
+# To customize your keyboard, copy this file to something
+# ending with .keytab and change it to meet you needs.
+# Please read the README.KeyTab and the README.keyboard
+# in this case.
+#
+# --------------------------------------------------------------
+
+keyboard "Default (XFree 4)"
+
+# --------------------------------------------------------------
+#
+# Note that this particular table is a "risc" version made to
+# ease customization without bothering with obsolete details.
+# See VT100.keytab for the more hairy stuff.
+#
+# --------------------------------------------------------------
+
+# common keys
+
+key Escape : "\E"
+
+key Tab -Shift : "\t"
+key Tab +Shift+Ansi : "\E[Z"
+key Tab +Shift-Ansi : "\t"
+key Backtab +Ansi : "\E[Z"
+key Backtab -Ansi : "\t"
+
+key Return-Shift-NewLine : "\r"
+key Return-Shift+NewLine : "\r\n"
+
+key Return+Shift : "\EOM"
+
+# Backspace and Delete codes are preserving CTRL-H.
+
+key Backspace : "\x7f"
+
+# Arrow keys in VT52 mode
+# shift up/down are reserved for scrolling.
+# shift left/right are reserved for switching between tabs (this is hardcoded).
+
+key Up -Shift-Ansi : "\EA"
+key Down -Shift-Ansi : "\EB"
+key Right-Shift-Ansi : "\EC"
+key Left -Shift-Ansi : "\ED"
+
+# Arrow keys in ANSI mode with Application - and Normal Cursor Mode)
+
+key Up -Shift-AnyMod+Ansi+AppCuKeys : "\EOA"
+key Down -Shift-AnyMod+Ansi+AppCuKeys : "\EOB"
+key Right -Shift-AnyMod+Ansi+AppCuKeys : "\EOC"
+key Left -Shift-AnyMod+Ansi+AppCuKeys : "\EOD"
+
+key Up -Shift-AnyMod+Ansi-AppCuKeys : "\E[A"
+key Down -Shift-AnyMod+Ansi-AppCuKeys : "\E[B"
+key Right -Shift-AnyMod+Ansi-AppCuKeys : "\E[C"
+key Left -Shift-AnyMod+Ansi-AppCuKeys : "\E[D"
+
+key Up -Shift+AnyMod+Ansi : "\E[1;*A"
+key Down -Shift+AnyMod+Ansi : "\E[1;*B"
+key Right -Shift+AnyMod+Ansi : "\E[1;*C"
+key Left -Shift+AnyMod+Ansi : "\E[1;*D"
+
+# other grey PC keys
+
+key Enter+NewLine : "\r\n"
+key Enter-NewLine : "\r"
+
+key Home -AnyMod -AppCuKeys : "\E[H"
+key End -AnyMod -AppCuKeys : "\E[F"
+key Home -AnyMod +AppCuKeys : "\EOH"
+key End -AnyMod +AppCuKeys : "\EOF"
+key Home +AnyMod : "\E[1;*H"
+key End +AnyMod : "\E[1;*F"
+
+key Insert -AnyMod : "\E[2~"
+key Delete -AnyMod : "\E[3~"
+key Insert +AnyMod : "\E[2;*~"
+key Delete +AnyMod : "\E[3;*~"
+
+key Prior -Shift-AnyMod : "\E[5~"
+key Next -Shift-AnyMod : "\E[6~"
+key Prior -Shift+AnyMod : "\E[5;*~"
+key Next -Shift+AnyMod : "\E[6;*~"
+
+# Function keys
+key F1 -AnyMod : "\EOP"
+key F2 -AnyMod : "\EOQ"
+key F3 -AnyMod : "\EOR"
+key F4 -AnyMod : "\EOS"
+key F5 -AnyMod : "\E[15~"
+key F6 -AnyMod : "\E[17~"
+key F7 -AnyMod : "\E[18~"
+key F8 -AnyMod : "\E[19~"
+key F9 -AnyMod : "\E[20~"
+key F10 -AnyMod : "\E[21~"
+key F11 -AnyMod : "\E[23~"
+key F12 -AnyMod : "\E[24~"
+
+key F1 +AnyMod : "\EO*P"
+key F2 +AnyMod : "\EO*Q"
+key F3 +AnyMod : "\EO*R"
+key F4 +AnyMod : "\EO*S"
+key F5 +AnyMod : "\E[15;*~"
+key F6 +AnyMod : "\E[17;*~"
+key F7 +AnyMod : "\E[18;*~"
+key F8 +AnyMod : "\E[19;*~"
+key F9 +AnyMod : "\E[20;*~"
+key F10 +AnyMod : "\E[21;*~"
+key F11 +AnyMod : "\E[23;*~"
+key F12 +AnyMod : "\E[24;*~"
+
+# Work around dead keys
+
+key Space +Control : "\x00"
+
+# Some keys are used by konsole to cause operations.
+# The scroll* operations refer to the history buffer.
+
+key Up +Shift-AppScreen : scrollLineUp
+key Prior +Shift-AppScreen : scrollPageUp
+key Down +Shift-AppScreen : scrollLineDown
+key Next +Shift-AppScreen : scrollPageDown
+
+#key Up +Shift : scrollLineUp
+#key Prior +Shift : scrollPageUp
+#key Down +Shift : scrollLineDown
+#key Next +Shift : scrollPageDown
+
+key ScrollLock : scrollLock
+
+# keypad characters are not offered differently by Qt.
diff --git a/qtermwidget/kb-layouts/linux.keytab b/qtermwidget/kb-layouts/linux.keytab
new file mode 100644
index 0000000..c0fe444
--- /dev/null
+++ b/qtermwidget/kb-layouts/linux.keytab
@@ -0,0 +1,133 @@
+# [linux.keytab] Konsole Keyboard Table (Linux console keys)
+#
+# --------------------------------------------------------------
+
+# NOT TESTED, MAY NEED SOME CLEANUPS
+keyboard "Linux console"
+
+# --------------------------------------------------------------
+#
+# This configuration table allows to customize the
+# meaning of the keys.
+#
+# The syntax is that each entry has the form :
+#
+# "key" Keyname { ("+"|"-") Modename } ":" (String|Operation)
+#
+# Keynames are those defined in <qnamespace.h> with the
+# "Qt::Key_" removed. (We'd better insert the list here)
+#
+# Mode names are :
+#
+# - Shift
+# - Alt
+# - Control
+#
+# The VT100 emulation has two modes that can affect the
+# sequences emitted by certain keys. These modes are
+# under control of the client program.
+#
+# - Newline : effects Return and Enter key.
+# - Application : effects Up and Down key.
+#
+# - Ansi : effects Up and Down key (This is for VT52, really).
+#
+# Operations are
+#
+# - scrollUpLine
+# - scrollUpPage
+# - scrollDownLine
+# - scrollDownPage
+#
+# - emitSelection
+#
+# If the key is not found here, the text of the
+# key event as provided by QT is emitted, possibly
+# preceeded by ESC if the Alt key is pressed.
+#
+# --------------------------------------------------------------
+
+key Escape : "\E"
+key Tab : "\t"
+
+# VT100 can add an extra \n after return.
+# The NewLine mode is set by an escape sequence.
+
+key Return-NewLine : "\r"
+key Return+NewLine : "\r\n"
+
+# Some desperately try to save the ^H.
+
+key Backspace : "\x7f"
+key Delete : "\E[3~"
+
+# These codes are for the VT52 mode of VT100
+# The Ansi mode (i.e. VT100 mode) is set by
+# an escape sequence
+
+key Up -Shift-Ansi : "\EA"
+key Down -Shift-Ansi : "\EB"
+key Right-Shift-Ansi : "\EC"
+key Left -Shift-Ansi : "\ED"
+
+# VT100 emits a mode bit together
+# with the arrow keys.The AppCuKeys
+# mode is set by an escape sequence.
+
+key Up -Shift+Ansi+AppCuKeys : "\EOA"
+key Down -Shift+Ansi+AppCuKeys : "\EOB"
+key Right-Shift+Ansi+AppCuKeys : "\EOC"
+key Left -Shift+Ansi+AppCuKeys : "\EOD"
+
+key Up -Shift+Ansi-AppCuKeys : "\E[A"
+key Down -Shift+Ansi-AppCuKeys : "\E[B"
+key Right-Shift+Ansi-AppCuKeys : "\E[C"
+key Left -Shift+Ansi-AppCuKeys : "\E[D"
+
+# linux functions keys F1-F5 differ from xterm
+
+key F1 : "\E[[A"
+key F2 : "\E[[B"
+key F3 : "\E[[C"
+key F4 : "\E[[D"
+key F5 : "\E[[E"
+
+key F6 : "\E[17~"
+key F7 : "\E[18~"
+key F8 : "\E[19~"
+key F9 : "\E[20~"
+key F10 : "\E[21~"
+key F11 : "\E[23~"
+key F12 : "\E[24~"
+
+key Home : "\E[1~"
+key End : "\E[4~"
+
+key Prior -Shift : "\E[5~"
+key Next -Shift : "\E[6~"
+key Insert-Shift : "\E[2~"
+
+# Keypad-Enter. See comment on Return above.
+
+key Enter+NewLine : "\r\n"
+key Enter-NewLine : "\r"
+
+key Space +Control : "\x00"
+
+# some of keys are used by konsole.
+
+key Up +Shift : scrollLineUp
+key Prior +Shift : scrollPageUp
+key Down +Shift : scrollLineDown
+key Next +Shift : scrollPageDown
+
+key ScrollLock : scrollLock
+
+#----------------------------------------------------------
+
+# keypad characters as offered by Qt
+# cannot be recognized as such.
+
+#----------------------------------------------------------
+
+# Following other strings as emitted by konsole.
diff --git a/qtermwidget/kb-layouts/vt420pc.keytab b/qtermwidget/kb-layouts/vt420pc.keytab
new file mode 100644
index 0000000..2b7102f
--- /dev/null
+++ b/qtermwidget/kb-layouts/vt420pc.keytab
@@ -0,0 +1,163 @@
+# [vt420pc.keytab] Konsole Keyboard Table (VT420pc keys)
+# adapted by ferdinand gassauer f.gassauer@aon.at
+# Nov 2000
+#
+################################################################
+#
+# The escape sequences emmited by the
+# keys Shift+F1 to Shift+F12 might not fit your needs
+#
+################# IMPORTANT NOTICE #############################
+# the key bindings (Kcontrol -> look and feel -> keybindgs)
+# overrule the settings in this file. The key bindings might be
+# changed by the user WITHOUT notification of the maintainer of
+# the keytab file. Konsole will not work as expected by
+# the maintainer of the keytab file.
+################################################################
+#
+# --------------------------------------------------------------
+
+keyboard "DEC VT420 Terminal"
+
+# --------------------------------------------------------------
+#
+# This configuration table allows to customize the
+# meaning of the keys.
+#
+# The syntax is that each entry has the form :
+#
+# "key" Keyname { ("+"|"-") Modename } ":" (String|Operation)
+#
+# Keynames are those defined in <qnamespace.h> with the
+# "Qt::Key_" removed. (We'd better insert the list here)
+#
+# Mode names are :
+#
+# - Shift
+# - Alt
+# - Control
+#
+# The VT100 emulation has two modes that can affect the
+# sequences emitted by certain keys. These modes are
+# under control of the client program.
+#
+# - Newline : effects Return and Enter key.
+# - Application : effects Up and Down key.
+#
+# - Ansi : effects Up and Down key (This is for VT52, really).
+#
+# Operations are
+#
+# - scrollUpLine
+# - scrollUpPage
+# - scrollDownLine
+# - scrollDownPage
+#
+# - emitSelection
+#
+# If the key is not found here, the text of the
+# key event as provided by QT is emitted, possibly
+# preceeded by ESC if the Alt key is pressed.
+#
+# --------------------------------------------------------------
+
+key Escape : "\E"
+key Tab : "\t"
+key Backtab: "\E[Z"
+
+# VT100 can add an extra \n after return.
+# The NewLine mode is set by an escape sequence.
+
+key Return-NewLine : "\r"
+key Return+NewLine : "\r\n"
+
+# Some desperately try to save the ^H.
+# may be not everyone wants this
+
+key Backspace : "\x08" # Control H
+key Delete : "\x7f"
+
+# These codes are for the VT420pc
+# The Ansi mode (i.e. VT100 mode) is set by
+# an escape sequence
+
+key Up -Shift-Ansi : "\EA"
+key Down -Shift-Ansi : "\EB"
+key Right-Shift-Ansi : "\EC"
+key Left -Shift-Ansi : "\ED"
+
+# VT100 emits a mode bit together
+# with the arrow keys.The AppCuKeys
+# mode is set by an escape sequence.
+
+key Up -Shift+Ansi+AppCuKeys : "\EOA"
+key Down -Shift+Ansi+AppCuKeys : "\EOB"
+key Right-Shift+Ansi+AppCuKeys : "\EOC"
+key Left -Shift+Ansi+AppCuKeys : "\EOD"
+
+key Up -Shift+Ansi-AppCuKeys : "\E[A"
+key Down -Shift+Ansi-AppCuKeys : "\E[B"
+key Right-Shift+Ansi-AppCuKeys : "\E[C"
+key Left -Shift+Ansi-AppCuKeys : "\E[D"
+
+# function keys
+
+key F1 -Shift : "\E[11~"
+key F2 -Shift : "\E[12~"
+key F3 -Shift : "\E[13~"
+key F4 -Shift : "\E[14~"
+key F5 -Shift : "\E[15~"
+key F6 -Shift : "\E[17~"
+key F7 -Shift : "\E[18~"
+key F8 -Shift : "\E[19~"
+key F9 -Shift : "\E[20~"
+key F10-Shift : "\E[21~"
+key F11-Shift : "\E[23~"
+key F12-Shift : "\E[24~"
+#
+# Shift F1-F12
+#
+key F1 +Shift : "\E[11;2~"
+key F2 +Shift : "\E[12;2~"
+key F3 +Shift : "\E[13;2~"
+key F4 +Shift : "\E[14;2~"
+key F5 +Shift : "\E[15;2~"
+key F6 +Shift : "\E[17;2~"
+key F7 +Shift : "\E[18;2~"
+key F8 +Shift : "\E[19;2~"
+key F9 +Shift : "\E[20;2~"
+key F10+Shift : "\E[21;2~"
+key F11+Shift : "\E[23;2~"
+key F12+Shift : "\E[24;2~"
+
+key Home : "\E[H"
+key End : "\E[F"
+
+key Prior -Shift : "\E[5~"
+key Next -Shift : "\E[6~"
+key Insert-Shift : "\E[2~"
+
+# Keypad-Enter. See comment on Return above.
+
+key Enter+NewLine : "\r\n"
+key Enter-NewLine : "\r"
+
+key Space +Control : "\x00"
+
+# some of keys are used by konsole.
+
+key Up +Shift : scrollLineUp
+key Prior +Shift : scrollPageUp
+key Down +Shift : scrollLineDown
+key Next +Shift : scrollPageDown
+
+key ScrollLock : scrollLock
+
+#----------------------------------------------------------
+
+# keypad characters as offered by Qt
+# cannot be recognized as such.
+
+#----------------------------------------------------------
+
+# Following other strings as emitted by konsole.
diff --git a/qtermwidget/konsole_wcwidth.cpp b/qtermwidget/konsole_wcwidth.cpp
new file mode 100644
index 0000000..e4ef117
--- /dev/null
+++ b/qtermwidget/konsole_wcwidth.cpp
@@ -0,0 +1,216 @@
+/* $XFree86: xc/programs/xterm/wcwidth.character,v 1.3 2001/07/29 22:08:16 tsi Exp $ */
+/*
+ * This is an implementation of wcwidth() and wcswidth() as defined in
+ * "The Single UNIX Specification, Version 2, The Open Group, 1997"
+ * <http://www.UNIX-systems.org/online.html>
+ *
+ * Markus Kuhn -- 2001-01-12 -- public domain
+ */
+
+#include "konsole_wcwidth.h"
+
+struct interval {
+ unsigned short first;
+ unsigned short last;
+};
+
+/* auxiliary function for binary search in interval table */
+static int bisearch(quint16 ucs, const struct interval *table, int max) {
+ int min = 0;
+ int mid;
+
+ if (ucs < table[0].first || ucs > table[max].last)
+ return 0;
+ while (max >= min) {
+ mid = (min + max) / 2;
+ if (ucs > table[mid].last)
+ min = mid + 1;
+ else if (ucs < table[mid].first)
+ max = mid - 1;
+ else
+ return 1;
+ }
+
+ return 0;
+}
+
+
+/* The following functions define the column width of an ISO 10646
+ * character as follows:
+ *
+ * - The null character (U+0000) has a column width of 0.
+ *
+ * - Other C0/C1 control characters and DEL will lead to a return
+ * value of -1.
+ *
+ * - Non-spacing and enclosing combining characters (general
+ * category code Mn or Me in the Unicode database) have a
+ * column width of 0.
+ *
+ * - Other format characters (general category code Cf in the Unicode
+ * database) and ZERO WIDTH SPACE (U+200B) have a column width of 0.
+ *
+ * - Hangul Jamo medial vowels and final consonants (U+1160-U+11FF)
+ * have a column width of 0.
+ *
+ * - Spacing characters in the East Asian Wide (W) or East Asian
+ * FullWidth (F) category as defined in Unicode Technical
+ * Report #11 have a column width of 2.
+ *
+ * - All remaining characters (including all printable
+ * ISO 8859-1 and WGL4 characters, Unicode control characters,
+ * etc.) have a column width of 1.
+ *
+ * This implementation assumes that quint16 characters are encoded
+ * in ISO 10646.
+ */
+
+int konsole_wcwidth(quint16 ucs)
+{
+ /* sorted list of non-overlapping intervals of non-spacing characters */
+ static const struct interval combining[] = {
+ { 0x0300, 0x034E }, { 0x0360, 0x0362 }, { 0x0483, 0x0486 },
+ { 0x0488, 0x0489 }, { 0x0591, 0x05A1 }, { 0x05A3, 0x05B9 },
+ { 0x05BB, 0x05BD }, { 0x05BF, 0x05BF }, { 0x05C1, 0x05C2 },
+ { 0x05C4, 0x05C4 }, { 0x064B, 0x0655 }, { 0x0670, 0x0670 },
+ { 0x06D6, 0x06E4 }, { 0x06E7, 0x06E8 }, { 0x06EA, 0x06ED },
+ { 0x070F, 0x070F }, { 0x0711, 0x0711 }, { 0x0730, 0x074A },
+ { 0x07A6, 0x07B0 }, { 0x0901, 0x0902 }, { 0x093C, 0x093C },
+ { 0x0941, 0x0948 }, { 0x094D, 0x094D }, { 0x0951, 0x0954 },
+ { 0x0962, 0x0963 }, { 0x0981, 0x0981 }, { 0x09BC, 0x09BC },
+ { 0x09C1, 0x09C4 }, { 0x09CD, 0x09CD }, { 0x09E2, 0x09E3 },
+ { 0x0A02, 0x0A02 }, { 0x0A3C, 0x0A3C }, { 0x0A41, 0x0A42 },
+ { 0x0A47, 0x0A48 }, { 0x0A4B, 0x0A4D }, { 0x0A70, 0x0A71 },
+ { 0x0A81, 0x0A82 }, { 0x0ABC, 0x0ABC }, { 0x0AC1, 0x0AC5 },
+ { 0x0AC7, 0x0AC8 }, { 0x0ACD, 0x0ACD }, { 0x0B01, 0x0B01 },
+ { 0x0B3C, 0x0B3C }, { 0x0B3F, 0x0B3F }, { 0x0B41, 0x0B43 },
+ { 0x0B4D, 0x0B4D }, { 0x0B56, 0x0B56 }, { 0x0B82, 0x0B82 },
+ { 0x0BC0, 0x0BC0 }, { 0x0BCD, 0x0BCD }, { 0x0C3E, 0x0C40 },
+ { 0x0C46, 0x0C48 }, { 0x0C4A, 0x0C4D }, { 0x0C55, 0x0C56 },
+ { 0x0CBF, 0x0CBF }, { 0x0CC6, 0x0CC6 }, { 0x0CCC, 0x0CCD },
+ { 0x0D41, 0x0D43 }, { 0x0D4D, 0x0D4D }, { 0x0DCA, 0x0DCA },
+ { 0x0DD2, 0x0DD4 }, { 0x0DD6, 0x0DD6 }, { 0x0E31, 0x0E31 },
+ { 0x0E34, 0x0E3A }, { 0x0E47, 0x0E4E }, { 0x0EB1, 0x0EB1 },
+ { 0x0EB4, 0x0EB9 }, { 0x0EBB, 0x0EBC }, { 0x0EC8, 0x0ECD },
+ { 0x0F18, 0x0F19 }, { 0x0F35, 0x0F35 }, { 0x0F37, 0x0F37 },
+ { 0x0F39, 0x0F39 }, { 0x0F71, 0x0F7E }, { 0x0F80, 0x0F84 },
+ { 0x0F86, 0x0F87 }, { 0x0F90, 0x0F97 }, { 0x0F99, 0x0FBC },
+ { 0x0FC6, 0x0FC6 }, { 0x102D, 0x1030 }, { 0x1032, 0x1032 },
+ { 0x1036, 0x1037 }, { 0x1039, 0x1039 }, { 0x1058, 0x1059 },
+ { 0x1160, 0x11FF }, { 0x17B7, 0x17BD }, { 0x17C6, 0x17C6 },
+ { 0x17C9, 0x17D3 }, { 0x180B, 0x180E }, { 0x18A9, 0x18A9 },
+ { 0x200B, 0x200F }, { 0x202A, 0x202E }, { 0x206A, 0x206F },
+ { 0x20D0, 0x20E3 }, { 0x302A, 0x302F }, { 0x3099, 0x309A },
+ { 0xFB1E, 0xFB1E }, { 0xFE20, 0xFE23 }, { 0xFEFF, 0xFEFF },
+ { 0xFFF9, 0xFFFB }
+ };
+
+ /* test for 8-bit control characters */
+ if (ucs == 0)
+ return 0;
+ if (ucs < 32 || (ucs >= 0x7f && ucs < 0xa0))
+ return -1;
+
+ /* binary search in table of non-spacing characters */
+ if (bisearch(ucs, combining,
+ sizeof(combining) / sizeof(struct interval) - 1))
+ return 0;
+
+ /* if we arrive here, ucs is not a combining or C0/C1 control character */
+
+ return 1 +
+ (ucs >= 0x1100 &&
+ (ucs <= 0x115f || /* Hangul Jamo init. consonants */
+ (ucs >= 0x2e80 && ucs <= 0xa4cf && (ucs & ~0x0011) != 0x300a &&
+ ucs != 0x303f) || /* CJK ... Yi */
+ (ucs >= 0xac00 && ucs <= 0xd7a3) || /* Hangul Syllables */
+ (ucs >= 0xf900 && ucs <= 0xfaff) || /* CJK Compatibility Ideographs */
+ (ucs >= 0xfe30 && ucs <= 0xfe6f) || /* CJK Compatibility Forms */
+ (ucs >= 0xff00 && ucs <= 0xff5f) || /* Fullwidth Forms */
+ (ucs >= 0xffe0 && ucs <= 0xffe6) /* do not compare UINT16 with 0x20000 ||
+ (ucs >= 0x20000 && ucs <= 0x2ffff) */));
+}
+
+#if 0
+/*
+ * The following function is the same as konsole_wcwidth(), except that
+ * spacing characters in the East Asian Ambiguous (A) category as
+ * defined in Unicode Technical Report #11 have a column width of 2.
+ * This experimental variant might be useful for users of CJK legacy
+ * encodings who want to migrate to UCS. It is not otherwise
+ * recommended for general use.
+ */
+int konsole_wcwidth_cjk(quint16 ucs)
+{
+ /* sorted list of non-overlapping intervals of East Asian Ambiguous
+ * characters */
+ static const struct interval ambiguous[] = {
+ { 0x00A1, 0x00A1 }, { 0x00A4, 0x00A4 }, { 0x00A7, 0x00A8 },
+ { 0x00AA, 0x00AA }, { 0x00AD, 0x00AD }, { 0x00B0, 0x00B4 },
+ { 0x00B6, 0x00BA }, { 0x00BC, 0x00BF }, { 0x00C6, 0x00C6 },
+ { 0x00D0, 0x00D0 }, { 0x00D7, 0x00D8 }, { 0x00DE, 0x00E1 },
+ { 0x00E6, 0x00E6 }, { 0x00E8, 0x00EA }, { 0x00EC, 0x00ED },
+ { 0x00F0, 0x00F0 }, { 0x00F2, 0x00F3 }, { 0x00F7, 0x00FA },
+ { 0x00FC, 0x00FC }, { 0x00FE, 0x00FE }, { 0x0101, 0x0101 },
+ { 0x0111, 0x0111 }, { 0x0113, 0x0113 }, { 0x011B, 0x011B },
+ { 0x0126, 0x0127 }, { 0x012B, 0x012B }, { 0x0131, 0x0133 },
+ { 0x0138, 0x0138 }, { 0x013F, 0x0142 }, { 0x0144, 0x0144 },
+ { 0x0148, 0x014A }, { 0x014D, 0x014D }, { 0x0152, 0x0153 },
+ { 0x0166, 0x0167 }, { 0x016B, 0x016B }, { 0x01CE, 0x01CE },
+ { 0x01D0, 0x01D0 }, { 0x01D2, 0x01D2 }, { 0x01D4, 0x01D4 },
+ { 0x01D6, 0x01D6 }, { 0x01D8, 0x01D8 }, { 0x01DA, 0x01DA },
+ { 0x01DC, 0x01DC }, { 0x0251, 0x0251 }, { 0x0261, 0x0261 },
+ { 0x02C7, 0x02C7 }, { 0x02C9, 0x02CB }, { 0x02CD, 0x02CD },
+ { 0x02D0, 0x02D0 }, { 0x02D8, 0x02DB }, { 0x02DD, 0x02DD },
+ { 0x0391, 0x03A1 }, { 0x03A3, 0x03A9 }, { 0x03B1, 0x03C1 },
+ { 0x03C3, 0x03C9 }, { 0x0401, 0x0401 }, { 0x0410, 0x044F },
+ { 0x0451, 0x0451 }, { 0x2010, 0x2010 }, { 0x2013, 0x2016 },
+ { 0x2018, 0x2019 }, { 0x201C, 0x201D }, { 0x2020, 0x2021 },
+ { 0x2025, 0x2027 }, { 0x2030, 0x2030 }, { 0x2032, 0x2033 },
+ { 0x2035, 0x2035 }, { 0x203B, 0x203B }, { 0x2074, 0x2074 },
+ { 0x207F, 0x207F }, { 0x2081, 0x2084 }, { 0x20AC, 0x20AC },
+ { 0x2103, 0x2103 }, { 0x2105, 0x2105 }, { 0x2109, 0x2109 },
+ { 0x2113, 0x2113 }, { 0x2121, 0x2122 }, { 0x2126, 0x2126 },
+ { 0x212B, 0x212B }, { 0x2154, 0x2155 }, { 0x215B, 0x215B },
+ { 0x215E, 0x215E }, { 0x2160, 0x216B }, { 0x2170, 0x2179 },
+ { 0x2190, 0x2199 }, { 0x21D2, 0x21D2 }, { 0x21D4, 0x21D4 },
+ { 0x2200, 0x2200 }, { 0x2202, 0x2203 }, { 0x2207, 0x2208 },
+ { 0x220B, 0x220B }, { 0x220F, 0x220F }, { 0x2211, 0x2211 },
+ { 0x2215, 0x2215 }, { 0x221A, 0x221A }, { 0x221D, 0x2220 },
+ { 0x2223, 0x2223 }, { 0x2225, 0x2225 }, { 0x2227, 0x222C },
+ { 0x222E, 0x222E }, { 0x2234, 0x2237 }, { 0x223C, 0x223D },
+ { 0x2248, 0x2248 }, { 0x224C, 0x224C }, { 0x2252, 0x2252 },
+ { 0x2260, 0x2261 }, { 0x2264, 0x2267 }, { 0x226A, 0x226B },
+ { 0x226E, 0x226F }, { 0x2282, 0x2283 }, { 0x2286, 0x2287 },
+ { 0x2295, 0x2295 }, { 0x2299, 0x2299 }, { 0x22A5, 0x22A5 },
+ { 0x22BF, 0x22BF }, { 0x2312, 0x2312 }, { 0x2460, 0x24BF },
+ { 0x24D0, 0x24E9 }, { 0x2500, 0x254B }, { 0x2550, 0x2574 },
+ { 0x2580, 0x258F }, { 0x2592, 0x2595 }, { 0x25A0, 0x25A1 },
+ { 0x25A3, 0x25A9 }, { 0x25B2, 0x25B3 }, { 0x25B6, 0x25B7 },
+ { 0x25BC, 0x25BD }, { 0x25C0, 0x25C1 }, { 0x25C6, 0x25C8 },
+ { 0x25CB, 0x25CB }, { 0x25CE, 0x25D1 }, { 0x25E2, 0x25E5 },
+ { 0x25EF, 0x25EF }, { 0x2605, 0x2606 }, { 0x2609, 0x2609 },
+ { 0x260E, 0x260F }, { 0x261C, 0x261C }, { 0x261E, 0x261E },
+ { 0x2640, 0x2640 }, { 0x2642, 0x2642 }, { 0x2660, 0x2661 },
+ { 0x2663, 0x2665 }, { 0x2667, 0x266A }, { 0x266C, 0x266D },
+ { 0x266F, 0x266F }, { 0x300A, 0x300B }, { 0x301A, 0x301B },
+ { 0xE000, 0xF8FF }, { 0xFFFD, 0xFFFD }
+ };
+
+ /* binary search in table of non-spacing characters */
+ if (bisearch(ucs, ambiguous,
+ sizeof(ambiguous) / sizeof(struct interval) - 1))
+ return 2;
+
+ return konsole_wcwidth(ucs);
+}
+#endif
+
+// single byte char: +1, multi byte char: +2
+int string_width( const QString &txt )
+{
+ int w = 0;
+ for ( int i = 0; i < txt.length(); ++i )
+ w += konsole_wcwidth( txt[ i ].unicode() );
+ return w;
+}
diff --git a/qtermwidget/konsole_wcwidth.h b/qtermwidget/konsole_wcwidth.h
new file mode 100644
index 0000000..6f4e46a
--- /dev/null
+++ b/qtermwidget/konsole_wcwidth.h
@@ -0,0 +1,24 @@
+/* $XFree86: xc/programs/xterm/wcwidth.h,v 1.2 2001/06/18 19:09:27 dickey Exp $ */
+
+/* Markus Kuhn -- 2001-01-12 -- public domain */
+/* Adaptions for KDE by Waldo Bastian <bastian@kde.org> */
+/*
+ Rewritten for QT4 by e_k <e_k at users.sourceforge.net>
+*/
+
+
+#ifndef _KONSOLE_WCWIDTH_H_
+#define _KONSOLE_WCWIDTH_H_
+
+// Qt
+#include <QtCore/QBool>
+#include <QtCore/QString>
+
+int konsole_wcwidth(quint16 ucs);
+#if 0
+int konsole_wcwidth_cjk(Q_UINT16 ucs);
+#endif
+
+int string_width( const QString &txt );
+
+#endif
diff --git a/qtermwidget/kpty.cpp b/qtermwidget/kpty.cpp
new file mode 100644
index 0000000..953b956
--- /dev/null
+++ b/qtermwidget/kpty.cpp
@@ -0,0 +1,624 @@
+/*
+
+ This file is part of the KDE libraries
+ Copyright (C) 2002 Waldo Bastian <bastian@kde.org>
+ Copyright (C) 2002-2003,2007 Oswald Buddenhagen <ossi@kde.org>
+
+ Rewritten for QT4 by e_k <e_k at users.sourceforge.net>, Copyright (C)2008
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#include "kpty_p.h"
+
+#ifdef __sgi
+#define __svr4__
+#endif
+
+#ifdef __osf__
+#define _OSF_SOURCE
+#include <float.h>
+#endif
+
+#ifdef _AIX
+#define _ALL_SOURCE
+#endif
+
+// __USE_XOPEN isn't defined by default in ICC
+// (needed for ptsname(), grantpt() and unlockpt())
+#ifdef __INTEL_COMPILER
+# ifndef __USE_XOPEN
+# define __USE_XOPEN
+# endif
+#endif
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <sys/stat.h>
+#include <sys/param.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <time.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <grp.h>
+
+#if defined(HAVE_PTY_H)
+# include <pty.h>
+#endif
+
+#ifdef HAVE_LIBUTIL_H
+# include <libutil.h>
+#elif defined(HAVE_UTIL_H)
+# include <util.h>
+#endif
+
+#ifdef HAVE_UTEMPTER
+extern "C" {
+# include <utempter.h>
+}
+#else
+# include <utmp.h>
+# ifdef HAVE_UTMPX
+# include <utmpx.h>
+# endif
+# if !defined(_PATH_UTMPX) && defined(_UTMPX_FILE)
+# define _PATH_UTMPX _UTMPX_FILE
+# endif
+# if !defined(_PATH_WTMPX) && defined(_WTMPX_FILE)
+# define _PATH_WTMPX _WTMPX_FILE
+# endif
+#endif
+
+/* for HP-UX (some versions) the extern C is needed, and for other
+ platforms it doesn't hurt */
+extern "C" {
+#include <termios.h>
+#if defined(HAVE_TERMIO_H)
+# include <termio.h> // struct winsize on some systems
+#endif
+}
+
+#if defined (_HPUX_SOURCE)
+# define _TERMIOS_INCLUDED
+# include <bsdtty.h>
+#endif
+
+#ifdef HAVE_SYS_STROPTS_H
+# include <sys/stropts.h> // Defines I_PUSH
+# define _NEW_TTY_CTRL
+#endif
+
+#if defined (__FreeBSD__) || defined (__NetBSD__) || defined (__OpenBSD__) || defined (__bsdi__) || defined(__APPLE__) || defined (__DragonFly__)
+# define _tcgetattr(fd, ttmode) ioctl(fd, TIOCGETA, (char *)ttmode)
+#else
+# if defined(_HPUX_SOURCE) || defined(__Lynx__) || defined (__CYGWIN__)
+# define _tcgetattr(fd, ttmode) tcgetattr(fd, ttmode)
+# else
+# define _tcgetattr(fd, ttmode) ioctl(fd, TCGETS, (char *)ttmode)
+# endif
+#endif
+
+#if defined (__FreeBSD__) || defined (__NetBSD__) || defined (__OpenBSD__) || defined (__bsdi__) || defined(__APPLE__) || defined (__DragonFly__)
+# define _tcsetattr(fd, ttmode) ioctl(fd, TIOCSETA, (char *)ttmode)
+#else
+# if defined(_HPUX_SOURCE) || defined(__CYGWIN__)
+# define _tcsetattr(fd, ttmode) tcsetattr(fd, TCSANOW, ttmode)
+# else
+# define _tcsetattr(fd, ttmode) ioctl(fd, TCSETS, (char *)ttmode)
+# endif
+#endif
+
+//#include <kdebug.h>
+//#include <kstandarddirs.h> // findExe
+
+#include <QtCore>
+
+// not defined on HP-UX for example
+#ifndef CTRL
+# define CTRL(x) ((x) & 037)
+#endif
+
+#define TTY_GROUP "tty"
+
+///////////////////////
+// private functions //
+///////////////////////
+
+//////////////////
+// private data //
+//////////////////
+
+KPtyPrivate::KPtyPrivate() :
+ masterFd(-1), slaveFd(-1)
+{
+}
+
+bool KPtyPrivate::chownpty(bool)
+{
+// return !QProcess::execute(KStandardDirs::findExe("kgrantpty"),
+// QStringList() << (grant?"--grant":"--revoke") << QString::number(masterFd));
+ return true;
+}
+
+/////////////////////////////
+// public member functions //
+/////////////////////////////
+
+KPty::KPty() :
+ d_ptr(new KPtyPrivate)
+{
+ d_ptr->q_ptr = this;
+}
+
+KPty::KPty(KPtyPrivate *d) :
+ d_ptr(d)
+{
+ d_ptr->q_ptr = this;
+}
+
+KPty::~KPty()
+{
+ close();
+ delete d_ptr;
+}
+
+bool KPty::open()
+{
+ Q_D(KPty);
+
+ if (d->masterFd >= 0)
+ return true;
+
+ QByteArray ptyName;
+
+ // Find a master pty that we can open ////////////////////////////////
+
+ // Because not all the pty animals are created equal, they want to
+ // be opened by several different methods.
+
+ // We try, as we know them, one by one.
+
+#ifdef HAVE_OPENPTY
+
+ char ptsn[PATH_MAX];
+ if (::openpty( &d->masterFd, &d->slaveFd, ptsn, 0, 0))
+ {
+ d->masterFd = -1;
+ d->slaveFd = -1;
+ kWarning(175) << "Can't open a pseudo teletype";
+ return false;
+ }
+ d->ttyName = ptsn;
+
+#else
+
+#ifdef HAVE__GETPTY // irix
+
+ char *ptsn = _getpty(&d->masterFd, O_RDWR|O_NOCTTY, S_IRUSR|S_IWUSR, 0);
+ if (ptsn) {
+ d->ttyName = ptsn;
+ goto grantedpt;
+ }
+
+#elif defined(HAVE_PTSNAME) || defined(TIOCGPTN)
+
+#ifdef HAVE_POSIX_OPENPT
+ d->masterFd = ::posix_openpt(O_RDWR|O_NOCTTY);
+#elif defined(HAVE_GETPT)
+ d->masterFd = ::getpt();
+#elif defined(PTM_DEVICE)
+ d->masterFd = ::open(PTM_DEVICE, O_RDWR|O_NOCTTY);
+#else
+# error No method to open a PTY master detected.
+#endif
+
+ if (d->masterFd >= 0)
+ {
+
+#ifdef HAVE_PTSNAME
+ char *ptsn = ptsname(d->masterFd);
+ if (ptsn) {
+ d->ttyName = ptsn;
+#else
+ int ptyno;
+ if (!ioctl(d->masterFd, TIOCGPTN, &ptyno)) {
+ d->ttyName = QByteArray("/dev/pts/") + QByteArray::number(ptyno);
+#endif
+#ifdef HAVE_GRANTPT
+ if (!grantpt(d->masterFd))
+ goto grantedpt;
+#else
+
+ goto gotpty;
+#endif
+ }
+ ::close(d->masterFd);
+ d->masterFd = -1;
+ }
+#endif // HAVE_PTSNAME || TIOCGPTN
+
+ // Linux device names, FIXME: Trouble on other systems?
+ for (const char* s3 = "pqrstuvwxyzabcde"; *s3; s3++)
+ {
+ for (const char* s4 = "0123456789abcdef"; *s4; s4++)
+ {
+ ptyName = QString().sprintf("/dev/pty%c%c", *s3, *s4).toAscii();
+ d->ttyName = QString().sprintf("/dev/tty%c%c", *s3, *s4).toAscii();
+
+ d->masterFd = ::open(ptyName.data(), O_RDWR);
+ if (d->masterFd >= 0)
+ {
+#ifdef Q_OS_SOLARIS
+ /* Need to check the process group of the pty.
+ * If it exists, then the slave pty is in use,
+ * and we need to get another one.
+ */
+ int pgrp_rtn;
+ if (ioctl(d->masterFd, TIOCGPGRP, &pgrp_rtn) == 0 || errno != EIO) {
+ ::close(d->masterFd);
+ d->masterFd = -1;
+ continue;
+ }
+#endif /* Q_OS_SOLARIS */
+ if (!access(d->ttyName.data(),R_OK|W_OK)) // checks availability based on permission bits
+ {
+ if (!geteuid())
+ {
+ struct group* p = getgrnam(TTY_GROUP);
+ if (!p)
+ p = getgrnam("wheel");
+ gid_t gid = p ? p->gr_gid : getgid ();
+
+ chown(d->ttyName.data(), getuid(), gid);
+ chmod(d->ttyName.data(), S_IRUSR|S_IWUSR|S_IWGRP);
+ }
+ goto gotpty;
+ }
+ ::close(d->masterFd);
+ d->masterFd = -1;
+ }
+ }
+ }
+
+ qWarning() << "Can't open a pseudo teletype";
+ return false;
+
+ gotpty:
+ struct stat st;
+ if (stat(d->ttyName.data(), &st)) {
+ return false; // this just cannot happen ... *cough* Yeah right, I just
+ // had it happen when pty #349 was allocated. I guess
+ // there was some sort of leak? I only had a few open.
+ }
+ if (((st.st_uid != getuid()) ||
+ (st.st_mode & (S_IRGRP|S_IXGRP|S_IROTH|S_IWOTH|S_IXOTH))) &&
+ !d->chownpty(true))
+ {
+ qWarning()
+ << "chownpty failed for device " << ptyName << "::" << d->ttyName
+ << "\nThis means the communication can be eavesdropped." << endl;
+ }
+
+#if defined (HAVE__GETPTY) || defined (HAVE_GRANTPT)
+ grantedpt:
+#endif
+
+#ifdef HAVE_REVOKE
+ revoke(d->ttyName.data());
+#endif
+
+#ifdef HAVE_UNLOCKPT
+ unlockpt(d->masterFd);
+#elif defined(TIOCSPTLCK)
+ int flag = 0;
+ ioctl(d->masterFd, TIOCSPTLCK, &flag);
+#endif
+
+ d->slaveFd = ::open(d->ttyName.data(), O_RDWR | O_NOCTTY);
+ if (d->slaveFd < 0)
+ {
+ qWarning() << "Can't open slave pseudo teletype";
+ ::close(d->masterFd);
+ d->masterFd = -1;
+ return false;
+ }
+
+#if (defined(__svr4__) || defined(__sgi__))
+ // Solaris
+ ioctl(d->slaveFd, I_PUSH, "ptem");
+ ioctl(d->slaveFd, I_PUSH, "ldterm");
+#endif
+
+#endif /* HAVE_OPENPTY */
+
+ fcntl(d->masterFd, F_SETFD, FD_CLOEXEC);
+ fcntl(d->slaveFd, F_SETFD, FD_CLOEXEC);
+
+ return true;
+}
+
+void KPty::closeSlave()
+{
+ Q_D(KPty);
+
+ if (d->slaveFd < 0)
+ return;
+ ::close(d->slaveFd);
+ d->slaveFd = -1;
+}
+
+void KPty::close()
+{
+ Q_D(KPty);
+
+ if (d->masterFd < 0)
+ return;
+ closeSlave();
+ // don't bother resetting unix98 pty, it will go away after closing master anyway.
+ if (memcmp(d->ttyName.data(), "/dev/pts/", 9)) {
+ if (!geteuid()) {
+ struct stat st;
+ if (!stat(d->ttyName.data(), &st)) {
+ chown(d->ttyName.data(), 0, st.st_gid == getgid() ? 0 : -1);
+ chmod(d->ttyName.data(), S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH);
+ }
+ } else {
+ fcntl(d->masterFd, F_SETFD, 0);
+ d->chownpty(false);
+ }
+ }
+ ::close(d->masterFd);
+ d->masterFd = -1;
+}
+
+void KPty::setCTty()
+{
+ Q_D(KPty);
+
+ // Setup job control //////////////////////////////////
+
+ // Become session leader, process group leader,
+ // and get rid of the old controlling terminal.
+ setsid();
+
+ // make our slave pty the new controlling terminal.
+#ifdef TIOCSCTTY
+ ioctl(d->slaveFd, TIOCSCTTY, 0);
+#else
+ // __svr4__ hack: the first tty opened after setsid() becomes controlling tty
+ ::close(::open(d->ttyName, O_WRONLY, 0));
+#endif
+
+ // make our new process group the foreground group on the pty
+ int pgrp = getpid();
+#if defined(_POSIX_VERSION) || defined(__svr4__)
+ tcsetpgrp(d->slaveFd, pgrp);
+#elif defined(TIOCSPGRP)
+ ioctl(d->slaveFd, TIOCSPGRP, (char *)&pgrp);
+#endif
+}
+
+void KPty::login(const char *user, const char *remotehost)
+{
+#ifdef HAVE_UTEMPTER
+ Q_D(KPty);
+
+ addToUtmp(d->ttyName, remotehost, d->masterFd);
+ Q_UNUSED(user);
+#else
+# ifdef HAVE_UTMPX
+ struct utmpx l_struct;
+# else
+ struct utmp l_struct;
+# endif
+ memset(&l_struct, 0, sizeof(l_struct));
+ // note: strncpy without terminators _is_ correct here. man 4 utmp
+
+ if (user)
+ strncpy(l_struct.ut_name, user, sizeof(l_struct.ut_name));
+
+ if (remotehost) {
+ strncpy(l_struct.ut_host, remotehost, sizeof(l_struct.ut_host));
+# ifdef HAVE_STRUCT_UTMP_UT_SYSLEN
+ l_struct.ut_syslen = qMin(strlen(remotehost), sizeof(l_struct.ut_host));
+# endif
+ }
+
+# ifndef __GLIBC__
+ Q_D(KPty);
+ const char *str_ptr = d->ttyName.data();
+ if (!memcmp(str_ptr, "/dev/", 5))
+ str_ptr += 5;
+ strncpy(l_struct.ut_line, str_ptr, sizeof(l_struct.ut_line));
+# ifdef HAVE_STRUCT_UTMP_UT_ID
+ strncpy(l_struct.ut_id,
+ str_ptr + strlen(str_ptr) - sizeof(l_struct.ut_id),
+ sizeof(l_struct.ut_id));
+# endif
+# endif
+
+# ifdef HAVE_UTMPX
+ gettimeofday(&l_struct.ut_tv, 0);
+# else
+ l_struct.ut_time = time(0);
+# endif
+
+# ifdef HAVE_LOGIN
+# ifdef HAVE_LOGINX
+ ::loginx(&l_struct);
+# else
+ ::login(&l_struct);
+# endif
+# else
+# ifdef HAVE_STRUCT_UTMP_UT_TYPE
+ l_struct.ut_type = USER_PROCESS;
+# endif
+# ifdef HAVE_STRUCT_UTMP_UT_PID
+ l_struct.ut_pid = getpid();
+# ifdef HAVE_STRUCT_UTMP_UT_SESSION
+ l_struct.ut_session = getsid(0);
+# endif
+# endif
+# ifdef HAVE_UTMPX
+ utmpxname(_PATH_UTMPX);
+ setutxent();
+ pututxline(&l_struct);
+ endutxent();
+ updwtmpx(_PATH_WTMPX, &l_struct);
+# else
+ utmpname(_PATH_UTMP);
+ setutent();
+ pututline(&l_struct);
+ endutent();
+ updwtmp(_PATH_WTMP, &l_struct);
+# endif
+# endif
+#endif
+}
+
+void KPty::logout()
+{
+#ifdef HAVE_UTEMPTER
+ Q_D(KPty);
+
+ removeLineFromUtmp(d->ttyName, d->masterFd);
+#else
+ Q_D(KPty);
+
+ const char *str_ptr = d->ttyName.data();
+ if (!memcmp(str_ptr, "/dev/", 5))
+ str_ptr += 5;
+# ifdef __GLIBC__
+ else {
+ const char *sl_ptr = strrchr(str_ptr, '/');
+ if (sl_ptr)
+ str_ptr = sl_ptr + 1;
+ }
+# endif
+# ifdef HAVE_LOGIN
+# ifdef HAVE_LOGINX
+ ::logoutx(str_ptr, 0, DEAD_PROCESS);
+# else
+ ::logout(str_ptr);
+# endif
+# else
+# ifdef HAVE_UTMPX
+ struct utmpx l_struct, *ut;
+# else
+ struct utmp l_struct, *ut;
+# endif
+ memset(&l_struct, 0, sizeof(l_struct));
+
+ strncpy(l_struct.ut_line, str_ptr, sizeof(l_struct.ut_line));
+
+# ifdef HAVE_UTMPX
+ utmpxname(_PATH_UTMPX);
+ setutxent();
+ if ((ut = getutxline(&l_struct))) {
+# else
+ utmpname(_PATH_UTMP);
+ setutent();
+ if ((ut = getutline(&l_struct))) {
+# endif
+ memset(ut->ut_name, 0, sizeof(*ut->ut_name));
+ memset(ut->ut_host, 0, sizeof(*ut->ut_host));
+# ifdef HAVE_STRUCT_UTMP_UT_SYSLEN
+ ut->ut_syslen = 0;
+# endif
+# ifdef HAVE_STRUCT_UTMP_UT_TYPE
+ ut->ut_type = DEAD_PROCESS;
+# endif
+# ifdef HAVE_UTMPX
+ gettimeofday(ut->ut_tv, 0);
+ pututxline(ut);
+ }
+ endutxent();
+# else
+ ut->ut_time = time(0);
+ pututline(ut);
+ }
+ endutent();
+# endif
+# endif
+#endif
+}
+
+// XXX Supposedly, tc[gs]etattr do not work with the master on Solaris.
+// Please verify.
+
+bool KPty::tcGetAttr(struct ::termios *ttmode) const
+{
+ Q_D(const KPty);
+
+ return _tcgetattr(d->masterFd, ttmode) == 0;
+}
+
+bool KPty::tcSetAttr(struct ::termios *ttmode)
+{
+ Q_D(KPty);
+
+ return _tcsetattr(d->masterFd, ttmode) == 0;
+}
+
+bool KPty::setWinSize(int lines, int columns)
+{
+ Q_D(KPty);
+
+ struct winsize winSize;
+ memset(&winSize, 0, sizeof(winSize));
+ winSize.ws_row = (unsigned short)lines;
+ winSize.ws_col = (unsigned short)columns;
+ return ioctl(d->masterFd, TIOCSWINSZ, (char *)&winSize) == 0;
+}
+
+bool KPty::setEcho(bool echo)
+{
+ struct ::termios ttmode;
+ if (!tcGetAttr(&ttmode))
+ return false;
+ if (!echo)
+ ttmode.c_lflag &= ~ECHO;
+ else
+ ttmode.c_lflag |= ECHO;
+ return tcSetAttr(&ttmode);
+}
+
+const char *KPty::ttyName() const
+{
+ Q_D(const KPty);
+
+ return d->ttyName.data();
+}
+
+int KPty::masterFd() const
+{
+ Q_D(const KPty);
+
+ return d->masterFd;
+}
+
+int KPty::slaveFd() const
+{
+ Q_D(const KPty);
+
+ return d->slaveFd;
+}
diff --git a/qtermwidget/kpty.h b/qtermwidget/kpty.h
new file mode 100644
index 0000000..8286834
--- /dev/null
+++ b/qtermwidget/kpty.h
@@ -0,0 +1,188 @@
+/* This file is part of the KDE libraries
+
+ Copyright (C) 2003,2007 Oswald Buddenhagen <ossi@kde.org>
+
+ Rewritten for QT4 by e_k <e_k at users.sourceforge.net>, Copyright (C)2008
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#ifndef kpty_h
+#define kpty_h
+
+#include <QtCore>
+
+struct KPtyPrivate;
+struct termios;
+
+/**
+ * Provides primitives for opening & closing a pseudo TTY pair, assigning the
+ * controlling TTY, utmp registration and setting various terminal attributes.
+ */
+class KPty {
+ Q_DECLARE_PRIVATE(KPty)
+
+public:
+
+ /**
+ * Constructor
+ */
+ KPty();
+
+ /**
+ * Destructor:
+ *
+ * If the pty is still open, it will be closed. Note, however, that
+ * an utmp registration is @em not undone.
+ */
+ ~KPty();
+
+ /**
+ * Create a pty master/slave pair.
+ *
+ * @return true if a pty pair was successfully opened
+ */
+ bool open();
+
+ /**
+ * Close the pty master/slave pair.
+ */
+ void close();
+
+ /**
+ * Close the pty slave descriptor.
+ *
+ * When creating the pty, KPty also opens the slave and keeps it open.
+ * Consequently the master will never receive an EOF notification.
+ * Usually this is the desired behavior, as a closed pty slave can be
+ * reopened any time - unlike a pipe or socket. However, in some cases
+ * pipe-alike behavior might be desired.
+ *
+ * After this function was called, slaveFd() and setCTty() cannot be
+ * used.
+ */
+ void closeSlave();
+
+ /**
+ * Creates a new session and process group and makes this pty the
+ * controlling tty.
+ */
+ void setCTty();
+
+ /**
+ * Creates an utmp entry for the tty.
+ * This function must be called after calling setCTty and
+ * making this pty the stdin.
+ * @param user the user to be logged on
+ * @param remotehost the host from which the login is coming. This is
+ * @em not the local host. For remote logins it should be the hostname
+ * of the client. For local logins from inside an X session it should
+ * be the name of the X display. Otherwise it should be empty.
+ */
+ void login(const char *user = 0, const char *remotehost = 0);
+
+ /**
+ * Removes the utmp entry for this tty.
+ */
+ void logout();
+
+ /**
+ * Wrapper around tcgetattr(3).
+ *
+ * This function can be used only while the PTY is open.
+ * You will need an #include &lt;termios.h&gt; to do anything useful
+ * with it.
+ *
+ * @param ttmode a pointer to a termios structure.
+ * Note: when declaring ttmode, @c struct @c ::termios must be used -
+ * without the '::' some version of HP-UX thinks, this declares
+ * the struct in your class, in your method.
+ * @return @c true on success, false otherwise
+ */
+ bool tcGetAttr(struct ::termios *ttmode) const;
+
+ /**
+ * Wrapper around tcsetattr(3) with mode TCSANOW.
+ *
+ * This function can be used only while the PTY is open.
+ *
+ * @param ttmode a pointer to a termios structure.
+ * @return @c true on success, false otherwise. Note that success means
+ * that @em at @em least @em one attribute could be set.
+ */
+ bool tcSetAttr(struct ::termios *ttmode);
+
+ /**
+ * Change the logical (screen) size of the pty.
+ * The default is 24 lines by 80 columns.
+ *
+ * This function can be used only while the PTY is open.
+ *
+ * @param lines the number of rows
+ * @param columns the number of columns
+ * @return @c true on success, false otherwise
+ */
+ bool setWinSize(int lines, int columns);
+
+ /**
+ * Set whether the pty should echo input.
+ *
+ * Echo is on by default.
+ * If the output of automatically fed (non-interactive) PTY clients
+ * needs to be parsed, disabling echo often makes it much simpler.
+ *
+ * This function can be used only while the PTY is open.
+ *
+ * @param echo true if input should be echoed.
+ * @return @c true on success, false otherwise
+ */
+ bool setEcho(bool echo);
+
+ /**
+ * @return the name of the slave pty device.
+ *
+ * This function should be called only while the pty is open.
+ */
+ const char *ttyName() const;
+
+ /**
+ * @return the file descriptor of the master pty
+ *
+ * This function should be called only while the pty is open.
+ */
+ int masterFd() const;
+
+ /**
+ * @return the file descriptor of the slave pty
+ *
+ * This function should be called only while the pty slave is open.
+ */
+ int slaveFd() const;
+
+protected:
+ /**
+ * @internal
+ */
+ KPty(KPtyPrivate *d);
+
+ /**
+ * @internal
+ */
+ KPtyPrivate * const d_ptr;
+};
+
+#endif
+
diff --git a/qtermwidget/kpty_p.h b/qtermwidget/kpty_p.h
new file mode 100644
index 0000000..1702b44
--- /dev/null
+++ b/qtermwidget/kpty_p.h
@@ -0,0 +1,44 @@
+/* This file is part of the KDE libraries
+
+ Copyright (C) 2003,2007 Oswald Buddenhagen <ossi@kde.org>
+
+ Rewritten for QT4 by e_k <e_k at users.sourceforge.net>, Copyright (C)2008
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#ifndef kpty_p_h
+#define kpty_p_h
+
+#include "kpty.h"
+
+#include <QtCore/QByteArray>
+
+struct KPtyPrivate {
+ Q_DECLARE_PUBLIC(KPty)
+
+ KPtyPrivate();
+ bool chownpty(bool grant);
+
+ int masterFd;
+ int slaveFd;
+
+ QByteArray ttyName;
+
+ KPty *q_ptr;
+};
+
+#endif
diff --git a/qtermwidget/lib.pro b/qtermwidget/lib.pro
new file mode 100644
index 0000000..1290291
--- /dev/null
+++ b/qtermwidget/lib.pro
@@ -0,0 +1,48 @@
+TEMPLATE = lib
+VERSION = 0.1.0
+DESTDIR = ..
+
+TARGET = qtermwidget
+
+CONFIG += qt debug_and_release warn_on build_all staticlib dll
+
+QT += core gui
+
+MOC_DIR = ../.moc
+
+CONFIG(debug, debug|release) {
+ OBJECTS_DIR = ../.objs_d
+ TARGET = qtermwidget_d
+} else {
+ OBJECTS_DIR = ../.objs
+ TARGET = qtermwidget
+}
+
+DEFINES += HAVE_POSIX_OPENPT
+#or DEFINES += HAVE_GETPT
+
+HEADERS = TerminalCharacterDecoder.h Character.h CharacterColor.h \
+ KeyboardTranslator.h \
+ ExtendedDefaultTranslator.h \
+ Screen.h History.h BlockArray.h konsole_wcwidth.h \
+ ScreenWindow.h \
+ Emulation.h \
+ Vt102Emulation.h TerminalDisplay.h Filter.h LineFont.h \
+ Pty.h kpty.h kpty_p.h k3process.h k3processcontroller.h \
+ Session.h ShellCommand.h \
+ qtermwidget.h
+
+SOURCES = TerminalCharacterDecoder.cpp \
+ KeyboardTranslator.cpp \
+ Screen.cpp History.cpp BlockArray.cpp konsole_wcwidth.cpp \
+ ScreenWindow.cpp \
+ Emulation.cpp \
+ Vt102Emulation.cpp TerminalDisplay.cpp Filter.cpp \
+ Pty.cpp kpty.cpp k3process.cpp k3processcontroller.cpp \
+ Session.cpp ShellCommand.cpp \
+ qtermwidget.cpp
+
+
+
+
+
diff --git a/qtermwidget/qtermwidget.cpp b/qtermwidget/qtermwidget.cpp
new file mode 100644
index 0000000..8b53a6f
--- /dev/null
+++ b/qtermwidget/qtermwidget.cpp
@@ -0,0 +1,222 @@
+/* Copyright (C) 2008 e_k (e_k@users.sourceforge.net)
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+
+#include "qtermwidget.h"
+
+#include "Session.h"
+#include "TerminalDisplay.h"
+
+using namespace Konsole;
+
+void *createTermWidget(void *parent, int startnow)
+{
+ return (void*) new QTermWidget((QWidget*)parent, startnow);
+}
+
+struct TermWidgetImpl
+{
+ TermWidgetImpl(QWidget* parent = 0);
+
+ TerminalDisplay *m_terminalDisplay;
+ Session *m_session;
+
+ Session* createSession();
+ TerminalDisplay* createTerminalDisplay(Session *session, QWidget* parent);
+};
+
+TermWidgetImpl::TermWidgetImpl(QWidget* parent)
+{
+ this->m_session = createSession();
+ this->m_terminalDisplay = createTerminalDisplay(this->m_session, parent);
+}
+
+
+Session *TermWidgetImpl::createSession()
+{
+ Session *session = new Session();
+
+ session->setTitle(Session::NameRole, "QTermWidget");
+ session->setProgram("/bin/bash");
+ QStringList args("");
+ session->setArguments(args);
+ session->setAutoClose(true);
+
+ session->setCodec(QTextCodec::codecForName("UTF-8"));
+
+ session->setFlowControlEnabled(true);
+ session->setHistoryType(HistoryTypeBuffer(1000));
+
+ session->setDarkBackground(true);
+
+ session->setKeyBindings("");
+ return session;
+}
+
+TerminalDisplay *TermWidgetImpl::createTerminalDisplay(Session *session, QWidget* parent)
+{
+// TerminalDisplay* display = new TerminalDisplay(this);
+ TerminalDisplay* display = new TerminalDisplay(parent);
+
+ display->setBellMode(TerminalDisplay::NotifyBell);
+ display->setTerminalSizeHint(true);
+ display->setTripleClickMode(TerminalDisplay::SelectWholeLine);
+ display->setTerminalSizeStartup(true);
+
+ display->setRandomSeed(session->sessionId() * 31);
+
+ return display;
+}
+
+
+QTermWidget::QTermWidget(QWidget *parent, int startnow)
+:QWidget(parent)
+{
+ m_impl = new TermWidgetImpl(this);
+
+ init();
+
+ if (startnow && m_impl->m_session) {
+ m_impl->m_session->run();
+ }
+
+ this->setFocus( Qt::OtherFocusReason );
+ m_impl->m_terminalDisplay->resize(this->size());
+
+ this->setFocusProxy(m_impl->m_terminalDisplay);
+}
+
+void QTermWidget::startShellProgram()
+{
+ if ( m_impl->m_session->isRunning() )
+ return;
+
+ m_impl->m_session->run();
+}
+
+void QTermWidget::init()
+{
+ m_impl->m_terminalDisplay->setSize(80, 40);
+
+ QFont font = QApplication::font();
+ font.setFamily("Monospace");
+ font.setPointSize(10);
+ font.setStyleHint(QFont::TypeWriter);
+ setTerminalFont(font);
+ setScrollBarPosition(NoScrollBar);
+
+ m_impl->m_session->addView(m_impl->m_terminalDisplay);
+
+ connect(m_impl->m_session, SIGNAL(finished()), this, SLOT(sessionFinished()));
+}
+
+
+QTermWidget::~QTermWidget()
+{
+ emit destroyed();
+}
+
+
+void QTermWidget::setTerminalFont(QFont &font)
+{
+ if (!m_impl->m_terminalDisplay)
+ return;
+ m_impl->m_terminalDisplay->setVTFont(font);
+}
+
+void QTermWidget::setShellProgram(QString &progname)
+{
+ if (!m_impl->m_session)
+ return;
+ m_impl->m_session->setProgram(progname);
+}
+
+void QTermWidget::setArgs(QStringList &args)
+{
+ if (!m_impl->m_session)
+ return;
+ m_impl->m_session->setArguments(args);
+}
+
+void QTermWidget::setTextCodec(QTextCodec *codec)
+{
+ if (!m_impl->m_session)
+ return;
+ m_impl->m_session->setCodec(codec);
+}
+
+void QTermWidget::setColorScheme(int scheme)
+{
+ switch(scheme) {
+ case COLOR_SCHEME_WHITE_ON_BLACK:
+ m_impl->m_terminalDisplay->setColorTable(whiteonblack_color_table);
+ break;
+ case COLOR_SCHEME_GREEN_ON_BLACK:
+ m_impl->m_terminalDisplay->setColorTable(greenonblack_color_table);
+ break;
+ case COLOR_SCHEME_BLACK_ON_LIGHT_YELLOW:
+ m_impl->m_terminalDisplay->setColorTable(blackonlightyellow_color_table);
+ break;
+ default: //do nothing
+ break;
+ };
+}
+
+void QTermWidget::setSize(int h, int v)
+{
+ if (!m_impl->m_terminalDisplay)
+ return;
+ m_impl->m_terminalDisplay->setSize(h, v);
+}
+
+void QTermWidget::setHistorySize(int lines)
+{
+ if (lines < 0)
+ m_impl->m_session->setHistoryType(HistoryTypeFile());
+ else
+ m_impl->m_session->setHistoryType(HistoryTypeBuffer(lines));
+}
+
+void QTermWidget::setScrollBarPosition(ScrollBarPosition pos)
+{
+ if (!m_impl->m_terminalDisplay)
+ return;
+ m_impl->m_terminalDisplay->setScrollBarPosition((TerminalDisplay::ScrollBarPosition)pos);
+}
+
+void QTermWidget::sendText(QString &text)
+{
+ m_impl->m_session->sendText(text);
+}
+
+void QTermWidget::resizeEvent(QResizeEvent*)
+{
+//qDebug("global window resizing...with %d %d", this->size().width(), this->size().height());
+ m_impl->m_terminalDisplay->resize(this->size());
+}
+
+
+
+void QTermWidget::sessionFinished()
+{
+ emit finished();
+}
+
+
+//#include "moc_consoleq.cpp"
+
diff --git a/qtermwidget/qtermwidget.h b/qtermwidget/qtermwidget.h
new file mode 100644
index 0000000..42e8763
--- /dev/null
+++ b/qtermwidget/qtermwidget.h
@@ -0,0 +1,109 @@
+/* Copyright (C) 2008 e_k (e_k@users.sourceforge.net)
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+
+#ifndef _Q_TERM_WIDGET
+#define _Q_TERM_WIDGET
+
+#include <QtGui>
+
+struct TermWidgetImpl;
+
+enum COLOR_SCHEME { COLOR_SCHEME_WHITE_ON_BLACK = 1,
+ COLOR_SCHEME_GREEN_ON_BLACK,
+ COLOR_SCHEME_BLACK_ON_LIGHT_YELLOW };
+
+class QTermWidget : public QWidget
+{
+ Q_OBJECT
+public:
+
+ enum ScrollBarPosition
+ {
+ /** Do not show the scroll bar. */
+ NoScrollBar=0,
+ /** Show the scroll bar on the left side of the display. */
+ ScrollBarLeft=1,
+ /** Show the scroll bar on the right side of the display. */
+ ScrollBarRight=2
+ };
+
+
+ //Creation of widget
+ QTermWidget(QWidget *parent = 0,
+ int startnow = 0); // don't start shell programm immediatelly
+
+ ~QTermWidget();
+
+ //start shell program if it was not started in constructor
+ void startShellProgram();
+
+ //look-n-feel, if you don`t like defaults
+
+ // Terminal font
+ // Default is application font with family Monospace, size 10
+ void setTerminalFont(QFont &font);
+
+ // Shell program, default is /bin/bash
+ void setShellProgram(QString &progname);
+
+ // Shell program args, default is none
+ void setArgs(QStringList &args);
+
+ //Text codec, default is UTF-8
+ void setTextCodec(QTextCodec *codec);
+
+ //Color scheme, default is white on black
+ void setColorScheme(int scheme);
+
+ //set size
+ void setSize(int h, int v);
+
+ // History size for scrolling
+ void setHistorySize(int lines); //infinite if lines < 0
+
+ // Presence of scrollbar
+ void setScrollBarPosition(ScrollBarPosition);
+
+ // Send some text to terminal
+ void sendText(QString &text);
+
+signals:
+ void finished();
+
+protected:
+ virtual void resizeEvent(QResizeEvent *);
+
+protected slots:
+ void sessionFinished();
+
+private:
+ void init();
+ TermWidgetImpl *m_impl;
+};
+
+
+//Maybe useful, maybe not
+
+#ifdef __cplusplus
+extern "C"
+#endif
+void *createTermWidget(int startnow, void *parent);
+
+#endif
+
diff --git a/wizard/bootloader.cpp b/wizard/bootloader.cpp
new file mode 100644
index 0000000..a066d05
--- /dev/null
+++ b/wizard/bootloader.cpp
@@ -0,0 +1,74 @@
+#include <QtGui>
+#include "bootloader.h"
+#include "../listdelegate.h"
+#include "../listitem.h"
+
+wpBootloader::wpBootloader(QWidget *parent) : QWizardPage(parent)
+{
+ setupUi(this);
+ backend = Backend::instance();
+ connect(backend, SIGNAL(receivedDataLine(QString,QString)), this, SLOT(receivedDataLine(QString,QString)));
+ connect(backend, SIGNAL(finishedCommand(QString)), this, SLOT(backendFinishedCommand(QString)));
+ connect(bootloader, SIGNAL(currentItemChanged(QListWidgetItem*,QListWidgetItem*)), this, SLOT(updateComplete()));
+ connect(bootloaderTarget, SIGNAL(currentItemChanged(QListWidgetItem*,QListWidgetItem*)), this, SLOT(updateComplete()));
+ //bootloader->setItemDelegate(new ListDelegate(this));
+}
+
+void wpBootloader::initializePage()
+{
+ clearPage();
+}
+
+void wpBootloader::clearPage()
+{
+ backend->exec("send_bootloaders");
+ bootloader->clear();
+ backend->exec("send_bootloader_targets");
+ bootloaderTarget->clear();
+}
+
+void wpBootloader::receivedDataLine(QString data, QString line)
+{
+ if(data == "bootloaders")
+ {
+ QListWidgetItem *item = new QListWidgetItem(QIcon::fromTheme("system-run"), line);
+ bootloader->addItem(item);
+ }
+ if(data == "bootloader_targets")
+ {
+ QListWidgetItem *item = new QListWidgetItem(QIcon::fromTheme("drive-harddisk"), line);
+ bootloaderTarget->addItem(item);
+ }
+}
+
+void wpBootloader::backendFinishedCommand(QString command)
+{
+ if(command == "send_bootloaders")
+ {
+ bootloader->setCurrentRow(0);
+ }
+ if(command == "send_bootloader_targets")
+ {
+ bootloaderTarget->setCurrentRow(0);
+ }
+}
+
+void wpBootloader::updateComplete()
+{
+ emit completeChanged();
+}
+
+bool wpBootloader::isComplete() const
+{
+ if(!bootloader->currentItem()) return false;
+ if(!bootloaderTarget->currentItem()) return false;
+ return true;
+}
+
+bool wpBootloader::validatePage()
+{
+ if(!isComplete()) return false;
+ backend->cfg("bootloader", bootloader->currentItem()->text().section(" ",0,0).toLower());
+ backend->cfg("bootloader_target", bootloaderTarget->currentItem()->text().section(" ",0,0).toLower());
+ return true;
+}
diff --git a/wizard/bootloader.h b/wizard/bootloader.h
new file mode 100644
index 0000000..87bcd4f
--- /dev/null
+++ b/wizard/bootloader.h
@@ -0,0 +1,28 @@
+#ifndef bootloader_H
+#define bootloader_H
+
+#include "ui_bootloader.h"
+#include "../backend.h"
+
+class wpBootloader : public QWizardPage, Ui::wpBootloader
+{
+ Q_OBJECT
+
+ public:
+ wpBootloader(QWidget *parent = 0);
+ void initializePage();
+ void clearPage();
+ bool isComplete() const;
+ bool validatePage();
+
+ private:
+ Backend* backend;
+
+ private slots:
+ void receivedDataLine(QString data, QString line);
+ void backendFinishedCommand(QString command);
+ void updateComplete();
+
+};
+
+#endif // bootloader_H
diff --git a/wizard/bootloader.ui b/wizard/bootloader.ui
new file mode 100644
index 0000000..f7b39b5
--- /dev/null
+++ b/wizard/bootloader.ui
@@ -0,0 +1,123 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>wpBootloader</class>
+ <widget class="QWizardPage" name="wpBootloader">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>540</width>
+ <height>400</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>WizardPage</string>
+ </property>
+ <property name="title">
+ <string>Bootloader</string>
+ </property>
+ <property name="subTitle">
+ <string>Select a bootloader and its installation target</string>
+ </property>
+ <layout class="QGridLayout" name="gridLayout">
+ <item row="0" column="0">
+ <widget class="QLabel" name="lblHelpText">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="MinimumExpanding" vsizetype="Expanding">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>150</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="text">
+ <string>HelpText</string>
+ </property>
+ <property name="textFormat">
+ <enum>Qt::RichText</enum>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
+ </property>
+ <property name="wordWrap">
+ <bool>true</bool>
+ </property>
+ <property name="openExternalLinks">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QListWidget" name="bootloader">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+ <horstretch>0</horstretch>
+ <verstretch>3</verstretch>
+ </sizepolicy>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0" colspan="2">
+ <widget class="Line" name="line">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="0" rowspan="2">
+ <widget class="QLabel" name="lblHelpText_2">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="MinimumExpanding" vsizetype="Expanding">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>150</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="text">
+ <string>HelpText</string>
+ </property>
+ <property name="textFormat">
+ <enum>Qt::RichText</enum>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
+ </property>
+ <property name="wordWrap">
+ <bool>true</bool>
+ </property>
+ <property name="openExternalLinks">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="1">
+ <widget class="QListWidget" name="bootloaderTarget">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+ <horstretch>0</horstretch>
+ <verstretch>2</verstretch>
+ </sizepolicy>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="1">
+ <spacer name="verticalSpacer">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/wizard/installation.cpp b/wizard/installation.cpp
new file mode 100644
index 0000000..452273f
--- /dev/null
+++ b/wizard/installation.cpp
@@ -0,0 +1,81 @@
+#include <QtGui>
+#include "installation.h"
+#include "../listdelegate.h"
+#include "../listitem.h"
+
+wpInstallation::wpInstallation(QWidget *parent) : QWizardPage(parent)
+{
+ setComplete(false);
+ setupUi(this);
+ backend = Backend::instance();
+// connect(backend, SIGNAL(processExited()), this, SLOT(WeAreDone()));
+ listWidget->setItemDelegate(new ListDelegate(this));
+ setCommitPage(true);
+// QListWidgetItem *item = new ListItem("Start", "Let's go!", "dialog-ok");
+// listWidget->addItem(item);
+}
+
+void wpInstallation::initializePage()
+{
+ listWidget->clear();
+ progressCompleted->setRange(0,12);
+ connect(backend, SIGNAL(receivedProgress(int)), this, SLOT(setProgress(int)));
+ connect(backend, SIGNAL(receivedCommand(QString,QString)), this, SLOT(receivedCommand(QString,QString)));
+ connect(backend, SIGNAL(finishedCommand(QString)), this, SLOT(finishedCommand(QString)));
+ backend->exec("do_install");
+}
+
+void wpInstallation::cleanupPage()
+{
+ initializePage();
+}
+
+void wpInstallation::setProgress(int percent)
+{
+ progressCurrent->setRange(0, 100);
+ progressCurrent->setValue(percent);
+}
+
+void wpInstallation::receivedCommand(QString command, QString args)
+{
+ if(command != "install_step") return;
+ QListWidgetItem *item = new ListItem(args, tr("TODO: use descriptive titles, descriptions and icons in this list..."), "acritoxinstaller");
+ listWidget->addItem(item);
+ listWidget->scrollToItem(item);
+ progressCompleted->setValue(progressCompleted->value()+1);
+ progressCurrent->reset();
+ progressCurrent->setRange(0,0);
+}
+
+void wpInstallation::finishedCommand(QString command)
+{
+ if(command != "do_install") return;
+ progressCompleted->setRange(0,100);
+ progressCompleted->setValue(100);
+ progressCurrent->setRange(0,100);
+ progressCurrent->setValue(100);
+ setComplete(true);
+}
+
+// void wpInstallation::()
+// {
+// QListWidgetItem *item = new ListItem("Finished.", "The backend has finished its job.", "dialog-ok");
+// listWidget->addItem(item);
+// setComplete(true);
+// }
+
+void wpInstallation::setComplete(bool c)
+{
+ complete = c;
+ emit completeChanged();
+}
+
+bool wpInstallation::isComplete() const
+{
+ return complete;
+}
+
+bool wpInstallation::validatePage()
+{
+ return complete;
+}
diff --git a/wizard/installation.h b/wizard/installation.h
new file mode 100644
index 0000000..0209007
--- /dev/null
+++ b/wizard/installation.h
@@ -0,0 +1,29 @@
+#ifndef installation_H
+#define installation_H
+
+#include "ui_installation.h"
+#include "../backend.h"
+
+class wpInstallation : public QWizardPage, Ui::wpInstallation
+{
+ Q_OBJECT
+
+ public:
+ wpInstallation(QWidget *parent = 0);
+ void initializePage();
+ void cleanupPage();
+ bool isComplete() const;
+ bool validatePage();
+
+ private:
+ Backend* backend;
+ bool complete;
+ void setComplete(bool c);
+
+ private slots:
+ void setProgress(int percent);
+ void receivedCommand(QString command, QString args);
+ void finishedCommand(QString command);
+};
+
+#endif // installation_H
diff --git a/wizard/installation.ui b/wizard/installation.ui
new file mode 100644
index 0000000..67cdb81
--- /dev/null
+++ b/wizard/installation.ui
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>wpInstallation</class>
+ <widget class="QWizardPage" name="wpInstallation">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>515</width>
+ <height>389</height>
+ </rect>
+ </property>
+ <property name="title">
+ <string>Installation</string>
+ </property>
+ <property name="subTitle">
+ <string>This may take some time...</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <item>
+ <widget class="QListWidget" name="listWidget"/>
+ </item>
+ <item>
+ <widget class="QLabel" name="lblCurrent">
+ <property name="text">
+ <string>Current task:</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QProgressBar" name="progressCurrent"/>
+ </item>
+ <item>
+ <widget class="QLabel" name="lblCompleted">
+ <property name="text">
+ <string>Installation completed:</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QProgressBar" name="progressCompleted"/>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/wizard/network.cpp b/wizard/network.cpp
new file mode 100644
index 0000000..9d45fc5
--- /dev/null
+++ b/wizard/network.cpp
@@ -0,0 +1,40 @@
+#include <QtGui>
+#include "network.h"
+#include "../listdelegate.h"
+#include "../listitem.h"
+
+wpNetwork::wpNetwork(QWidget *parent) : QWizardPage(parent)
+{
+ setupUi(this);
+ connect(hostname, SIGNAL(textChanged(QString)), this, SLOT(updateStatus()));
+
+ QValidator* hostnameValidator = new QRegExpValidator(QRegExp("[a-zA-Z0-9][a-zA-Z0-9.-]*[a-zA-Z0-9]"), this);
+ hostname->setValidator( hostnameValidator );
+
+ backend = Backend::instance();
+}
+
+void wpNetwork::initializePage()
+{
+ hostname->setText(backend->cfg("hostname"));
+ updateStatus();
+}
+
+void wpNetwork::updateStatus()
+{
+ emit completeChanged();
+}
+
+bool wpNetwork::isComplete() const
+{
+ if(hostname->text().length()) return true;
+ return false;
+}
+
+bool wpNetwork::validatePage()
+{
+ if(!isComplete()) return false;
+ backend->cfg("hostname", hostname->text());
+ return true;
+}
+
diff --git a/wizard/network.h b/wizard/network.h
new file mode 100644
index 0000000..4242a5a
--- /dev/null
+++ b/wizard/network.h
@@ -0,0 +1,25 @@
+#ifndef network_H
+#define network_H
+
+#include "ui_network.h"
+#include "../backend.h"
+
+class wpNetwork : public QWizardPage, Ui::wpNetwork
+{
+ Q_OBJECT
+
+ public:
+ wpNetwork(QWidget *parent = 0);
+ void initializePage();
+ bool isComplete() const;
+ bool validatePage();
+
+ private:
+ Backend* backend;
+
+ private slots:
+ void updateStatus();
+
+};
+
+#endif // network_H
diff --git a/wizard/network.ui b/wizard/network.ui
new file mode 100644
index 0000000..7f6a36c
--- /dev/null
+++ b/wizard/network.ui
@@ -0,0 +1,108 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>wpNetwork</class>
+ <widget class="QWizardPage" name="wpNetwork">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>546</width>
+ <height>338</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>WizardPage</string>
+ </property>
+ <property name="title">
+ <string>Network configuration</string>
+ </property>
+ <property name="subTitle">
+ <string>Set your hostname</string>
+ </property>
+ <layout class="QGridLayout" name="gridLayout">
+ <item row="0" column="0" rowspan="2">
+ <widget class="QLabel" name="lblHelpText">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="MinimumExpanding" vsizetype="Expanding">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>150</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="text">
+ <string>HelpText</string>
+ </property>
+ <property name="textFormat">
+ <enum>Qt::RichText</enum>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
+ </property>
+ <property name="wordWrap">
+ <bool>true</bool>
+ </property>
+ <property name="openExternalLinks">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <layout class="QVBoxLayout" name="verticalLayout_2">
+ <item>
+ <widget class="QLabel" name="lblHostname">
+ <property name="text">
+ <string>Hostname:</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout">
+ <item>
+ <widget class="QLineEdit" name="hostname"/>
+ </item>
+ <item>
+ <widget class="QLabel" name="hostnameStatus">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>25</width>
+ <height>25</height>
+ </size>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </item>
+ <item row="1" column="1">
+ <spacer name="verticalSpacer">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>264</width>
+ <height>197</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </widget>
+ <tabstops>
+ <tabstop>hostname</tabstop>
+ </tabstops>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/wizard/partitions.cpp b/wizard/partitions.cpp
new file mode 100644
index 0000000..d35e94f
--- /dev/null
+++ b/wizard/partitions.cpp
@@ -0,0 +1,63 @@
+#include <QtGui>
+#include "partitions.h"
+#include "../listdelegate.h"
+#include "../listitem.h"
+#include "../mainwizard.h"
+
+wpPartitions::wpPartitions(QWidget *parent) : QWizardPage(parent)
+{
+ setupUi(this);
+ backend = Backend::instance();
+ connect(listWidget, SIGNAL(currentItemChanged(QListWidgetItem*,QListWidgetItem*)), this, SLOT(updateComplete()));
+ listWidget->setItemDelegate(new ListDelegate(this));
+}
+
+void wpPartitions::initializePage()
+{
+ listWidget->clear();
+ listWidget->addItem(new ListItem(tr("Manual partitioning"), tr("You will have to do the partitioning with a partition-manager manually. This way you have more possibilities to adjust the installation to your needs. The automatic partitioning lacks several features and is only a (computer generated) list of some possible options."), "partitionmanager", "manual"));
+ listWidget->addItem(new ListItem(tr("Already partitioned"), tr("If you have already partitioned for the installation and don't want to start a partition manager then select this."), "drive-harddisk", "already"));
+ clearPage();
+}
+
+void wpPartitions::clearPage()
+{
+}
+
+void wpPartitions::receivedDataLine(QString data, QString line)
+{
+ if(data == "install_choice")
+ {
+ listWidget->addItem(new ListItem(line, line, "tools-wizard", line.section(" ",0,0)));
+ }
+}
+
+void wpPartitions::updateComplete()
+{
+ emit completeChanged();
+}
+
+bool wpPartitions::isComplete() const
+{
+ if(!listWidget->currentItem()) return false;
+ return true;
+}
+
+bool wpPartitions::validatePage()
+{
+ if(!isComplete()) return false;
+// backend->cfg("install_choice", listWidget->currentItem()->data(ListItem::ItemData).toString());
+ return true;
+}
+
+int wpPartitions::nextId() const
+{
+ if(listWidget->currentItem())
+ {
+ QString choice = listWidget->currentItem()->data(ListItem::ItemData).toString();
+ if(choice == "manual")
+ return MainWizard::Page_PartManSel;
+ else if(choice == "already")
+ return MainWizard::Page_RootPartition;
+ }
+}
diff --git a/wizard/partitions.h b/wizard/partitions.h
new file mode 100644
index 0000000..4f442a6
--- /dev/null
+++ b/wizard/partitions.h
@@ -0,0 +1,27 @@
+#ifndef partitions_H
+#define partitions_H
+
+#include "ui_partitions.h"
+#include "../backend.h"
+
+class wpPartitions : public QWizardPage, Ui::wpPartitions
+{
+ Q_OBJECT
+
+ public:
+ wpPartitions(QWidget *parent = 0);
+ void initializePage();
+ void clearPage();
+ int nextId() const;
+ bool isComplete() const;
+ bool validatePage();
+
+ private:
+ Backend* backend;
+
+ private slots:
+ void receivedDataLine(QString data, QString line);
+ void updateComplete();
+};
+
+#endif // partitions_H
diff --git a/wizard/partitions.ui b/wizard/partitions.ui
new file mode 100644
index 0000000..f9badf4
--- /dev/null
+++ b/wizard/partitions.ui
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>wpPartitions</class>
+ <widget class="QWizardPage" name="wpPartitions">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>515</width>
+ <height>389</height>
+ </rect>
+ </property>
+ <property name="title">
+ <string>Partitions</string>
+ </property>
+ <property name="subTitle">
+ <string>Mmmmmh! Partitions!</string>
+ </property>
+ <layout class="QGridLayout" name="gridLayout">
+ <item row="0" column="0">
+ <widget class="QListWidget" name="listWidget"/>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/wizard/partman.cpp b/wizard/partman.cpp
new file mode 100644
index 0000000..3e232f4
--- /dev/null
+++ b/wizard/partman.cpp
@@ -0,0 +1,55 @@
+#include <QtGui>
+#include "partman.h"
+#include "config.h"
+#include "../listdelegate.h"
+#include "../listitem.h"
+
+wpPartMan::wpPartMan(QWidget *parent) : QWizardPage(parent)
+{
+ setupUi(this);
+ backend = Backend::instance();
+}
+
+void wpPartMan::initializePage()
+{
+ clearPage();
+}
+
+void wpPartMan::clearPage()
+{
+ setComplete(false);
+ delete terminal;
+ delete gridLayout;
+ delete label;
+ setupUi(this);
+ connect(terminal, SIGNAL(finished()), this, SLOT(terminalFinished()));
+ QStringList args;
+ args << "/bin/bash" << BACKEND_PATH << "-e" << "run_partmgr" << backend->cfg("partman_program") << backend->cfg("partman_disk");
+ terminal->setArgs(args);
+ terminal->startShellProgram();
+ terminal->setFocus(Qt::OtherFocusReason);
+}
+
+void wpPartMan::terminalFinished()
+{
+ terminal->releaseKeyboard();
+ terminal->clearFocus();
+ setComplete(true);
+}
+
+void wpPartMan::setComplete(bool c)
+{
+ complete = c;
+ emit completeChanged();
+}
+
+bool wpPartMan::isComplete() const
+{
+ return complete;
+}
+
+bool wpPartMan::validatePage()
+{
+ return complete;
+}
+
diff --git a/wizard/partman.h b/wizard/partman.h
new file mode 100644
index 0000000..240d8f7
--- /dev/null
+++ b/wizard/partman.h
@@ -0,0 +1,27 @@
+#ifndef partman_H
+#define partman_H
+
+#include "ui_partman.h"
+#include "../backend.h"
+
+class wpPartMan : public QWizardPage, Ui::wpPartMan
+{
+ Q_OBJECT
+
+ public:
+ wpPartMan(QWidget *parent = 0);
+ void initializePage();
+ void clearPage();
+ bool isComplete() const;
+ bool validatePage();
+
+ private:
+ Backend* backend;
+ bool complete;
+ void setComplete(bool c);
+
+ private slots:
+ void terminalFinished();
+};
+
+#endif // partman_H
diff --git a/wizard/partman.ui b/wizard/partman.ui
new file mode 100644
index 0000000..2ddf54f
--- /dev/null
+++ b/wizard/partman.ui
@@ -0,0 +1,55 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>wpPartMan</class>
+ <widget class="QWizardPage" name="wpPartMan">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>400</width>
+ <height>300</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>WizardPage</string>
+ </property>
+ <property name="title">
+ <string>Partition Manager</string>
+ </property>
+ <property name="subTitle">
+ <string>Partition your disk</string>
+ </property>
+ <layout class="QGridLayout" name="gridLayout">
+ <item row="0" column="0">
+ <widget class="QTermWidget" name="terminal" native="true">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QLabel" name="label">
+ <property name="text">
+ <string>save and/or quit the partition manager to continue</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <customwidgets>
+ <customwidget>
+ <class>QTermWidget</class>
+ <extends>QWidget</extends>
+ <header>../qtermwidget/qtermwidget.h</header>
+ <container>1</container>
+ </customwidget>
+ </customwidgets>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/wizard/partmansel.cpp b/wizard/partmansel.cpp
new file mode 100644
index 0000000..ae74f0e
--- /dev/null
+++ b/wizard/partmansel.cpp
@@ -0,0 +1,93 @@
+#include <QtGui>
+#include "partmansel.h"
+#include "../listdelegate.h"
+#include "../listitem.h"
+
+wpPartManSel::wpPartManSel(QWidget *parent) : QWizardPage(parent)
+{
+ setupUi(this);
+ backend = Backend::instance();
+ connect(backend, SIGNAL(receivedDataLine(QString,QString)), this, SLOT(receivedDataLine(QString,QString)));
+ connect(partMan, SIGNAL(currentItemChanged(QListWidgetItem*,QListWidgetItem*)), this, SLOT(updateComplete()));
+ connect(partDisk, SIGNAL(currentItemChanged(QListWidgetItem*,QListWidgetItem*)), this, SLOT(updateComplete()));
+ partMan->setItemDelegate(new ListDelegate(this));
+}
+
+void wpPartManSel::initializePage()
+{
+ clearPage();
+}
+
+void wpPartManSel::clearPage()
+{
+ backend->exec("send_partition_managers");
+ partMan->clear();
+ backend->exec("send_list_of_disks");
+ partDisk->clear();
+}
+
+void wpPartManSel::receivedDataLine(QString data, QString line)
+{
+ if(data == "partition_managers")
+ {
+ QString app = line.section(" ",0,0);
+ QString title, desc, icon;
+ if(app == "cfdisk")
+ {
+ title = tr("cfdisk");
+ desc = tr("cfdisk is a curses-based partition editor. It is text-only (curses), but it is easy to use [recommended]");
+ icon = "terminal";
+ }
+ else if(app == "fdisk")
+ {
+ title = tr("fdisk");
+ desc = tr("fdisk is a classic partition table manipulator for Linux. It has a Command Line Interface.");
+ icon = "terminal";
+ }
+ else if(app == "gparted")
+ {
+ title = tr("GParted");
+ desc = tr("GParted is the Gnome Partition Editor application. It has a Graphical User Interface.");
+ icon = "gparted";
+ }
+ else if(app == "qtparted")
+ {
+ title = tr("QtParted");
+ desc = tr("QTParted is a Partition Magic clone, so it has a GUI. Sometimes it's a bit buggy, so it is not recommended to use for huge changes on the partition table.");
+ icon = "qtparted";
+ }
+ else
+ {
+ title = app;
+ desc = tr("No description available...");
+ icon = "partitionmanager";
+ }
+ QListWidgetItem *item = new ListItem(title, desc, icon, app);
+ partMan->addItem(item);
+ }
+ if(data == "list_of_disks")
+ {
+ QListWidgetItem *item = new QListWidgetItem(QIcon::fromTheme("drive-harddisk"), line);
+ partDisk->addItem(item);
+ }
+}
+
+void wpPartManSel::updateComplete()
+{
+ emit completeChanged();
+}
+
+bool wpPartManSel::isComplete() const
+{
+ if(!partMan->currentItem()) return false;
+ if(!partDisk->currentItem()) return false;
+ return true;
+}
+
+bool wpPartManSel::validatePage()
+{
+ if(!isComplete()) return false;
+ backend->cfg("partman_program", partMan->currentItem()->data(ListItem::ItemData).toString());
+ backend->cfg("partman_disk", partDisk->currentItem()->text().section(" ",0,0));
+ return true;
+}
diff --git a/wizard/partmansel.h b/wizard/partmansel.h
new file mode 100644
index 0000000..7bfabd8
--- /dev/null
+++ b/wizard/partmansel.h
@@ -0,0 +1,27 @@
+#ifndef partmansel_H
+#define partmansel_H
+
+#include "ui_partmansel.h"
+#include "../backend.h"
+
+class wpPartManSel : public QWizardPage, Ui::wpPartManSel
+{
+ Q_OBJECT
+
+ public:
+ wpPartManSel(QWidget *parent = 0);
+ void initializePage();
+ void clearPage();
+ bool isComplete() const;
+ bool validatePage();
+
+ private:
+ Backend* backend;
+
+ private slots:
+ void receivedDataLine(QString data, QString line);
+ void updateComplete();
+
+};
+
+#endif // partmansel_H
diff --git a/wizard/partmansel.ui b/wizard/partmansel.ui
new file mode 100644
index 0000000..15a0302
--- /dev/null
+++ b/wizard/partmansel.ui
@@ -0,0 +1,123 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>wpPartManSel</class>
+ <widget class="QWizardPage" name="wpPartManSel">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>584</width>
+ <height>358</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>WizardPage</string>
+ </property>
+ <property name="title">
+ <string>Partitions</string>
+ </property>
+ <property name="subTitle">
+ <string>Select partition manager</string>
+ </property>
+ <layout class="QGridLayout" name="gridLayout">
+ <item row="0" column="0">
+ <widget class="QLabel" name="lblHelpText">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="MinimumExpanding" vsizetype="Expanding">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>150</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="text">
+ <string>HelpText</string>
+ </property>
+ <property name="textFormat">
+ <enum>Qt::RichText</enum>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
+ </property>
+ <property name="wordWrap">
+ <bool>true</bool>
+ </property>
+ <property name="openExternalLinks">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QListWidget" name="partMan">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+ <horstretch>0</horstretch>
+ <verstretch>3</verstretch>
+ </sizepolicy>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0" rowspan="2" colspan="2">
+ <widget class="Line" name="line">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="0" rowspan="4">
+ <widget class="QLabel" name="lblHelpText_2">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="MinimumExpanding" vsizetype="Expanding">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>150</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="text">
+ <string>HelpText</string>
+ </property>
+ <property name="textFormat">
+ <enum>Qt::RichText</enum>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
+ </property>
+ <property name="wordWrap">
+ <bool>true</bool>
+ </property>
+ <property name="openExternalLinks">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item row="6" column="1">
+ <spacer name="verticalSpacer">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ </spacer>
+ </item>
+ <item row="3" column="1">
+ <widget class="QListWidget" name="partDisk">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+ <horstretch>0</horstretch>
+ <verstretch>2</verstretch>
+ </sizepolicy>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/wizard/rootpartition.cpp b/wizard/rootpartition.cpp
new file mode 100644
index 0000000..6341a0f
--- /dev/null
+++ b/wizard/rootpartition.cpp
@@ -0,0 +1,58 @@
+#include <QtGui>
+#include "rootpartition.h"
+#include "../listdelegate.h"
+#include "../listitem.h"
+
+wpRootPartition::wpRootPartition(QWidget *parent) : QWizardPage(parent)
+{
+ setupUi(this);
+ backend = Backend::instance();
+ connect(backend, SIGNAL(receivedDataLine(QString,QString)), this, SLOT(receivedDataLine(QString,QString)));
+ connect(rootPartitionDev, SIGNAL(currentItemChanged(QListWidgetItem*,QListWidgetItem*)), this, SLOT(updateComplete()));
+}
+
+void wpRootPartition::initializePage()
+{
+ clearPage();
+}
+
+void wpRootPartition::clearPage()
+{
+ backend->exec("send_possible_root_partitions");
+ rootPartitionDev->clear();
+ backend->exec("send_possible_root_filesystems");
+ rootPartitionFs->clear();
+}
+
+void wpRootPartition::receivedDataLine(QString data, QString line)
+{
+ if(data == "possible_root_partitions")
+ {
+ QListWidgetItem *item = new QListWidgetItem(QIcon::fromTheme("drive-harddisk"), line);
+ rootPartitionDev->addItem(item);
+ }
+ if(data == "possible_root_filesystems")
+ {
+ rootPartitionFs->addItem(line);
+ }
+}
+
+void wpRootPartition::updateComplete()
+{
+ emit completeChanged();
+}
+
+bool wpRootPartition::isComplete() const
+{
+ if(!rootPartitionDev->currentItem()) return false;
+ return true;
+}
+
+bool wpRootPartition::validatePage()
+{
+ if(!isComplete()) return false;
+ backend->exec(QString("hdmap_set %1:/:%2:auto")
+ .arg(rootPartitionDev->currentItem()->text().section(" ",0,0))
+ .arg(chkFormat->isChecked() ? rootPartitionFs->currentText() : ""));
+ return true;
+}
diff --git a/wizard/rootpartition.h b/wizard/rootpartition.h
new file mode 100644
index 0000000..16e95ef
--- /dev/null
+++ b/wizard/rootpartition.h
@@ -0,0 +1,27 @@
+#ifndef rootpartition_H
+#define rootpartition_H
+
+#include "ui_rootpartition.h"
+#include "../backend.h"
+
+class wpRootPartition : public QWizardPage, Ui::wpRootPartition
+{
+ Q_OBJECT
+
+ public:
+ wpRootPartition(QWidget *parent = 0);
+ void initializePage();
+ void clearPage();
+ bool isComplete() const;
+ bool validatePage();
+
+ private:
+ Backend* backend;
+
+ private slots:
+ void receivedDataLine(QString data, QString line);
+ void updateComplete();
+
+};
+
+#endif // rootpartition_H
diff --git a/wizard/rootpartition.ui b/wizard/rootpartition.ui
new file mode 100644
index 0000000..119c00f
--- /dev/null
+++ b/wizard/rootpartition.ui
@@ -0,0 +1,152 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>wpRootPartition</class>
+ <widget class="QWizardPage" name="wpRootPartition">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>584</width>
+ <height>405</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>WizardPage</string>
+ </property>
+ <property name="title">
+ <string>Partitions</string>
+ </property>
+ <property name="subTitle">
+ <string>Select root partition</string>
+ </property>
+ <layout class="QGridLayout" name="gridLayout">
+ <item row="0" column="0" rowspan="3">
+ <widget class="QLabel" name="lblHelpText">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="MinimumExpanding" vsizetype="Expanding">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>150</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="text">
+ <string>HelpText</string>
+ </property>
+ <property name="textFormat">
+ <enum>Qt::RichText</enum>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
+ </property>
+ <property name="wordWrap">
+ <bool>true</bool>
+ </property>
+ <property name="openExternalLinks">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QListWidget" name="rootPartitionDev"/>
+ </item>
+ <item row="1" column="1">
+ <widget class="QCheckBox" name="chkFormat">
+ <property name="text">
+ <string>format partition with filesystem:</string>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="1">
+ <widget class="QComboBox" name="rootPartitionFs"/>
+ </item>
+ <item row="3" column="0" colspan="2">
+ <widget class="Line" name="line">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ </widget>
+ </item>
+ <item row="4" column="0" rowspan="2">
+ <widget class="QLabel" name="lblHelpText_2">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="MinimumExpanding" vsizetype="Expanding">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>150</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="text">
+ <string>HelpText</string>
+ </property>
+ <property name="textFormat">
+ <enum>Qt::RichText</enum>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
+ </property>
+ <property name="wordWrap">
+ <bool>true</bool>
+ </property>
+ <property name="openExternalLinks">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item row="4" column="1">
+ <widget class="QCheckBox" name="chkAdvanced">
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>advanced partition options</string>
+ </property>
+ </widget>
+ </item>
+ <item row="5" column="1">
+ <spacer name="verticalSpacer">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>228</width>
+ <height>108</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections>
+ <connection>
+ <sender>chkFormat</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>rootPartitionFs</receiver>
+ <slot>setEnabled(bool)</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>388</x>
+ <y>213</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>390</x>
+ <y>246</y>
+ </hint>
+ </hints>
+ </connection>
+ </connections>
+</ui>
diff --git a/wizard/rootpwd.cpp b/wizard/rootpwd.cpp
new file mode 100644
index 0000000..9ec4417
--- /dev/null
+++ b/wizard/rootpwd.cpp
@@ -0,0 +1,69 @@
+#include <QtGui>
+#include "rootpwd.h"
+#include "../listdelegate.h"
+#include "../listitem.h"
+
+wpRootPwd::wpRootPwd(QWidget *parent) : QWizardPage(parent)
+{
+ setupUi(this);
+ connect(password, SIGNAL(textChanged(QString)), this, SLOT(updateStatus()));
+ connect(retypePassword, SIGNAL(textChanged(QString)), this, SLOT(updateStatus()));
+ backend = Backend::instance();
+ updateStatus();
+}
+
+void wpRootPwd::initializePage()
+{
+}
+
+void wpRootPwd::updateStatus()
+{
+ QString pw1 = password->text();
+ QString pw2 = retypePassword->text();
+
+ passwordStatus->setPixmap(QPixmap());
+ retypePasswordStatus->setPixmap(QPixmap());
+
+ complete = true;
+
+ if(!pw1.length())
+ {
+ passwordStatus->setToolTip(tr("Please enter a password!"));
+ passwordStatus->setPixmap(QIcon::fromTheme("dialog-warning").pixmap(24,24));
+ complete = false;
+ }
+ else if(pw1.length() < 6)
+ {
+ passwordStatus->setToolTip(tr("Password must have at least 6 characters!"));
+ passwordStatus->setPixmap(QIcon::fromTheme("dialog-warning").pixmap(24,24));
+ complete = false;
+ }
+ else
+ {
+ passwordStatus->setPixmap(QIcon::fromTheme("dialog-ok").pixmap(24,24));
+
+ if(pw1 != pw2)
+ {
+ retypePasswordStatus->setToolTip(tr("Passwords don't match!"));
+ retypePasswordStatus->setPixmap(QIcon::fromTheme("dialog-warning").pixmap(24,24));
+ complete = false;
+ }
+ else
+ retypePasswordStatus->setPixmap(QIcon::fromTheme("dialog-ok").pixmap(24,24));
+ }
+
+ emit completeChanged();
+}
+
+bool wpRootPwd::isComplete() const
+{
+ return complete;
+}
+
+bool wpRootPwd::validatePage()
+{
+ if(!complete) return false;
+ backend->cfg("rootpwd", backend->encryptPassword(password->text()));
+ return true;
+}
+
diff --git a/wizard/rootpwd.h b/wizard/rootpwd.h
new file mode 100644
index 0000000..62bcb7b
--- /dev/null
+++ b/wizard/rootpwd.h
@@ -0,0 +1,26 @@
+#ifndef rootpwd_H
+#define rootpwd_H
+
+#include "ui_rootpwd.h"
+#include "../backend.h"
+
+class wpRootPwd : public QWizardPage, Ui::wpRootPwd
+{
+ Q_OBJECT
+
+ public:
+ wpRootPwd(QWidget *parent = 0);
+ void initializePage();
+ bool isComplete() const;
+ bool validatePage();
+
+ private:
+ Backend* backend;
+ bool complete;
+
+ private slots:
+ void updateStatus();
+
+};
+
+#endif // rootpwd_H
diff --git a/wizard/rootpwd.ui b/wizard/rootpwd.ui
new file mode 100644
index 0000000..2e47950
--- /dev/null
+++ b/wizard/rootpwd.ui
@@ -0,0 +1,178 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>wpRootPwd</class>
+ <widget class="QWizardPage" name="wpRootPwd">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>546</width>
+ <height>338</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>WizardPage</string>
+ </property>
+ <property name="title">
+ <string>User configuration</string>
+ </property>
+ <property name="subTitle">
+ <string>Password for the system administrator account (root)</string>
+ </property>
+ <layout class="QGridLayout" name="gridLayout">
+ <item row="0" column="0" rowspan="3">
+ <widget class="QLabel" name="lblHelpText">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="MinimumExpanding" vsizetype="Expanding">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>150</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="text">
+ <string>HelpText</string>
+ </property>
+ <property name="textFormat">
+ <enum>Qt::RichText</enum>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
+ </property>
+ <property name="wordWrap">
+ <bool>true</bool>
+ </property>
+ <property name="openExternalLinks">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <layout class="QVBoxLayout" name="verticalLayout_2">
+ <item>
+ <widget class="QLabel" name="lblPwd">
+ <property name="text">
+ <string>Password:</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout">
+ <item>
+ <widget class="QLineEdit" name="password">
+ <property name="maxLength">
+ <number>20</number>
+ </property>
+ <property name="echoMode">
+ <enum>QLineEdit::Password</enum>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLabel" name="passwordStatus">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>25</width>
+ <height>25</height>
+ </size>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </item>
+ <item row="1" column="1">
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <item>
+ <widget class="QLabel" name="lblRetypePwd">
+ <property name="text">
+ <string>Retype Password:</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout_2">
+ <item>
+ <widget class="QLineEdit" name="retypePassword">
+ <property name="maxLength">
+ <number>20</number>
+ </property>
+ <property name="echoMode">
+ <enum>QLineEdit::Password</enum>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLabel" name="retypePasswordStatus">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>25</width>
+ <height>25</height>
+ </size>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </item>
+ <item row="2" column="1">
+ <spacer name="verticalSpacer">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>264</width>
+ <height>197</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item row="3" column="0" colspan="2">
+ <layout class="QVBoxLayout" name="verticalLayout_3">
+ <item>
+ <widget class="Line" name="line">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLabel" name="lblPwdLenNote">
+ <property name="text">
+ <string>&lt;b&gt;Note that all passwords must have 6 - 20 characters!&lt;/b&gt;</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ <tabstops>
+ <tabstop>password</tabstop>
+ <tabstop>retypePassword</tabstop>
+ </tabstops>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/wizard/summary.cpp b/wizard/summary.cpp
new file mode 100644
index 0000000..b09f2c1
--- /dev/null
+++ b/wizard/summary.cpp
@@ -0,0 +1,66 @@
+#include <QtGui>
+#include "summary.h"
+#include "../listdelegate.h"
+#include "../listitem.h"
+
+wpSummary::wpSummary(QWidget *parent) : QWizardPage(parent)
+{
+ setComplete(true);
+ setupUi(this);
+ backend = Backend::instance();
+// connect(backend, SIGNAL(processExited()), this, SLOT(WeAreDone()));
+ listWidget->setItemDelegate(new ListDelegate(this));
+ setCommitPage(true);
+ setButtonText(QWizard::CommitButton, tr("Start Installation"));
+// QListWidgetItem *item = new ListItem("Start", "Let's go!", "dialog-ok");
+// listWidget->addItem(item);
+}
+
+void wpSummary::initializePage()
+{
+ listWidget->clear();
+ listWidget->addItem(new ListItem(tr("Installation type"), tr("HD install"), "acritoxinstaller"));
+ QStringList hdMapList = backend->cfg("hdmap").split("\n");
+ for (QStringList::Iterator it = hdMapList.begin(); it != hdMapList.end(); ++it)
+ {
+ QString line = *it;
+ QString device = line.section(":",0,0);
+ QString mountpoint = line.section(":",1,1);
+ QString filesystem = line.section(":",2,2);
+ QString automount = line.section(":",3,3);
+ QString title = tr("%1 will be used as %2").arg(device).arg(mountpoint);
+ QString description;
+ if(filesystem.length()) description = QString("<li>") + tr("It will be formatted with %1").arg(filesystem) + QString("</li>");
+ if(automount == "auto") description += QString("<li>") + tr("It will be mounted automatically on boot") + QString("</li>");
+ listWidget->addItem(new ListItem(title, "<ul>" + description + "</ul>", "drive-harddisk"));
+ }
+ listWidget->addItem(new ListItem(tr("Bootloader"), tr("%1 will be installed to %2").arg(backend->cfg("bootloader")).arg(backend->cfg("bootloader_target")), "system-run"));
+}
+
+void wpSummary::cleanupPage()
+{
+ initializePage();
+}
+
+// void wpSummary::()
+// {
+// QListWidgetItem *item = new ListItem("Finished.", "The backend has finished its job.", "dialog-ok");
+// listWidget->addItem(item);
+// setComplete(true);
+// }
+
+void wpSummary::setComplete(bool c)
+{
+ complete = c;
+ emit completeChanged();
+}
+
+bool wpSummary::isComplete() const
+{
+ return complete;
+}
+
+bool wpSummary::validatePage()
+{
+ return complete;
+}
diff --git a/wizard/summary.h b/wizard/summary.h
new file mode 100644
index 0000000..677340b
--- /dev/null
+++ b/wizard/summary.h
@@ -0,0 +1,28 @@
+#ifndef summary_H
+#define summary_H
+
+#include "ui_summary.h"
+#include "../backend.h"
+
+class wpSummary : public QWizardPage, Ui::wpSummary
+{
+ Q_OBJECT
+
+ public:
+ wpSummary(QWidget *parent = 0);
+ void initializePage();
+ void cleanupPage();
+ bool isComplete() const;
+ bool validatePage();
+
+ private:
+ Backend* backend;
+ bool complete;
+ void setComplete(bool c);
+
+ private slots:
+
+
+};
+
+#endif // summary_H
diff --git a/wizard/summary.ui b/wizard/summary.ui
new file mode 100644
index 0000000..9a36791
--- /dev/null
+++ b/wizard/summary.ui
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>wpSummary</class>
+ <widget class="QWizardPage" name="wpSummary">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>515</width>
+ <height>389</height>
+ </rect>
+ </property>
+ <property name="title">
+ <string>Installation</string>
+ </property>
+ <property name="subTitle">
+ <string>Summary</string>
+ </property>
+ <layout class="QGridLayout" name="gridLayout">
+ <item row="0" column="0">
+ <widget class="QListWidget" name="listWidget"/>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/wizard/usercfg.cpp b/wizard/usercfg.cpp
new file mode 100644
index 0000000..0e625d6
--- /dev/null
+++ b/wizard/usercfg.cpp
@@ -0,0 +1,52 @@
+#include <QtGui>
+#include "usercfg.h"
+#include "../listdelegate.h"
+#include "../listitem.h"
+
+wpUserCfg::wpUserCfg(QWidget *parent) : QWizardPage(parent)
+{
+ setupUi(this);
+ connect(realname, SIGNAL(editingFinished()), this, SLOT(realnameChanged()));
+ connect(realname, SIGNAL(textChanged(QString)), this, SLOT(updateStatus()));
+ connect(username, SIGNAL(textChanged(QString)), this, SLOT(updateStatus()));
+
+ QValidator* usernameValidator = new QRegExpValidator(QRegExp("[a-zA-Z0-9-_.]*"), this);
+ username->setValidator( usernameValidator );
+
+ backend = Backend::instance();
+ complete = false;
+ updateStatus();
+}
+
+void wpUserCfg::initializePage()
+{
+}
+
+void wpUserCfg::realnameChanged()
+{
+ if(username->text().length()) return;
+ if(realname->text().contains(" "))
+ username->setText(backend->cleanUsername(realname->text().section(" ",0,0).toLower()));
+ updateStatus();
+}
+
+void wpUserCfg::updateStatus()
+{
+ complete = false;
+ if(realname->text().length() && username->text().length()) complete = true;
+ emit completeChanged();
+}
+
+bool wpUserCfg::isComplete() const
+{
+ return complete;
+}
+
+bool wpUserCfg::validatePage()
+{
+ if(!complete) return false;
+ backend->cfg("username", username->text());
+ backend->cfg("realname", realname->text());
+ return true;
+}
+
diff --git a/wizard/usercfg.h b/wizard/usercfg.h
new file mode 100644
index 0000000..17efa9a
--- /dev/null
+++ b/wizard/usercfg.h
@@ -0,0 +1,27 @@
+#ifndef usercfg_H
+#define usercfg_H
+
+#include "ui_usercfg.h"
+#include "../backend.h"
+
+class wpUserCfg : public QWizardPage, Ui::wpUserCfg
+{
+ Q_OBJECT
+
+ public:
+ wpUserCfg(QWidget *parent = 0);
+ void initializePage();
+ bool isComplete() const;
+ bool validatePage();
+
+ private:
+ Backend* backend;
+ bool complete;
+
+ private slots:
+ void realnameChanged();
+ void updateStatus();
+
+};
+
+#endif // usercfg_H
diff --git a/wizard/usercfg.ui b/wizard/usercfg.ui
new file mode 100644
index 0000000..3e5ded9
--- /dev/null
+++ b/wizard/usercfg.ui
@@ -0,0 +1,143 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>wpUserCfg</class>
+ <widget class="QWizardPage" name="wpUserCfg">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>546</width>
+ <height>338</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>WizardPage</string>
+ </property>
+ <property name="title">
+ <string>User configuration</string>
+ </property>
+ <property name="subTitle">
+ <string>Username</string>
+ </property>
+ <layout class="QGridLayout" name="gridLayout">
+ <item row="0" column="0" rowspan="3">
+ <widget class="QLabel" name="lblHelpText">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="MinimumExpanding" vsizetype="Expanding">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>150</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="text">
+ <string>HelpText</string>
+ </property>
+ <property name="textFormat">
+ <enum>Qt::RichText</enum>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
+ </property>
+ <property name="wordWrap">
+ <bool>true</bool>
+ </property>
+ <property name="openExternalLinks">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <layout class="QVBoxLayout" name="verticalLayout_2">
+ <item>
+ <widget class="QLabel" name="lblRealname">
+ <property name="text">
+ <string>Realname:</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout">
+ <item>
+ <widget class="QLineEdit" name="realname"/>
+ </item>
+ <item>
+ <widget class="QLabel" name="realnameStatus">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>25</width>
+ <height>25</height>
+ </size>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </item>
+ <item row="1" column="1">
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <item>
+ <widget class="QLabel" name="lblUsername">
+ <property name="text">
+ <string>Username:</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout_2">
+ <item>
+ <widget class="QLineEdit" name="username"/>
+ </item>
+ <item>
+ <widget class="QLabel" name="usernameStatus">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>25</width>
+ <height>25</height>
+ </size>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </item>
+ <item row="2" column="1">
+ <spacer name="verticalSpacer">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>264</width>
+ <height>197</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </widget>
+ <tabstops>
+ <tabstop>realname</tabstop>
+ <tabstop>username</tabstop>
+ </tabstops>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/wizard/userpwd.cpp b/wizard/userpwd.cpp
new file mode 100644
index 0000000..48a9e91
--- /dev/null
+++ b/wizard/userpwd.cpp
@@ -0,0 +1,69 @@
+#include <QtGui>
+#include "userpwd.h"
+#include "../listdelegate.h"
+#include "../listitem.h"
+
+wpUserPwd::wpUserPwd(QWidget *parent) : QWizardPage(parent)
+{
+ setupUi(this);
+ connect(password, SIGNAL(textChanged(QString)), this, SLOT(updateStatus()));
+ connect(retypePassword, SIGNAL(textChanged(QString)), this, SLOT(updateStatus()));
+ backend = Backend::instance();
+ updateStatus();
+}
+
+void wpUserPwd::initializePage()
+{
+}
+
+void wpUserPwd::updateStatus()
+{
+ QString pw1 = password->text();
+ QString pw2 = retypePassword->text();
+
+ passwordStatus->setPixmap(QPixmap());
+ retypePasswordStatus->setPixmap(QPixmap());
+
+ complete = true;
+
+ if(!pw1.length())
+ {
+ passwordStatus->setToolTip(tr("Please enter a password!"));
+ passwordStatus->setPixmap(QIcon::fromTheme("dialog-warning").pixmap(24,24));
+ complete = false;
+ }
+ else if(pw1.length() < 6)
+ {
+ passwordStatus->setToolTip(tr("Password must have at least 6 characters!"));
+ passwordStatus->setPixmap(QIcon::fromTheme("dialog-warning").pixmap(24,24));
+ complete = false;
+ }
+ else
+ {
+ passwordStatus->setPixmap(QIcon::fromTheme("dialog-ok").pixmap(24,24));
+
+ if(pw1 != pw2)
+ {
+ retypePasswordStatus->setToolTip(tr("Passwords don't match!"));
+ retypePasswordStatus->setPixmap(QIcon::fromTheme("dialog-warning").pixmap(24,24));
+ complete = false;
+ }
+ else
+ retypePasswordStatus->setPixmap(QIcon::fromTheme("dialog-ok").pixmap(24,24));
+ }
+ emit completeChanged();
+}
+
+bool wpUserPwd::isComplete() const
+{
+ return complete;
+}
+
+bool wpUserPwd::validatePage()
+{
+ if(!complete) return false;
+ backend->cfg("userpwd", backend->encryptPassword(password->text()));
+ backend->cfg("autologin", (autologin->isChecked() ? "on" : "off"));
+ return true;
+}
+
diff --git a/wizard/userpwd.h b/wizard/userpwd.h
new file mode 100644
index 0000000..7ea209d
--- /dev/null
+++ b/wizard/userpwd.h
@@ -0,0 +1,26 @@
+#ifndef userpwd_H
+#define userpwd_H
+
+#include "ui_userpwd.h"
+#include "../backend.h"
+
+class wpUserPwd : public QWizardPage, Ui::wpUserPwd
+{
+ Q_OBJECT
+
+ public:
+ wpUserPwd(QWidget *parent = 0);
+ void initializePage();
+ bool isComplete() const;
+ bool validatePage();
+
+ private:
+ Backend* backend;
+ bool complete;
+
+ private slots:
+ void updateStatus();
+
+};
+
+#endif // userpwd_H
diff --git a/wizard/userpwd.ui b/wizard/userpwd.ui
new file mode 100644
index 0000000..ff559a2
--- /dev/null
+++ b/wizard/userpwd.ui
@@ -0,0 +1,237 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>wpUserPwd</class>
+ <widget class="QWizardPage" name="wpUserPwd">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>546</width>
+ <height>338</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>WizardPage</string>
+ </property>
+ <property name="title">
+ <string>User configuration</string>
+ </property>
+ <property name="subTitle">
+ <string>Password for your user account</string>
+ </property>
+ <layout class="QGridLayout" name="gridLayout">
+ <item row="0" column="0" rowspan="3">
+ <widget class="QLabel" name="lblHelpText">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="MinimumExpanding" vsizetype="Expanding">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>150</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="text">
+ <string>HelpText</string>
+ </property>
+ <property name="textFormat">
+ <enum>Qt::RichText</enum>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
+ </property>
+ <property name="wordWrap">
+ <bool>true</bool>
+ </property>
+ <property name="openExternalLinks">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <layout class="QVBoxLayout" name="verticalLayout_2">
+ <item>
+ <widget class="QLabel" name="lblPwd">
+ <property name="text">
+ <string>Password:</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout">
+ <item>
+ <widget class="QLineEdit" name="password">
+ <property name="maxLength">
+ <number>20</number>
+ </property>
+ <property name="echoMode">
+ <enum>QLineEdit::Password</enum>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLabel" name="passwordStatus">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>25</width>
+ <height>25</height>
+ </size>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </item>
+ <item row="1" column="1">
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <item>
+ <widget class="QLabel" name="lblRetypePwd">
+ <property name="text">
+ <string>Retype Password:</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout_2">
+ <item>
+ <widget class="QLineEdit" name="retypePassword">
+ <property name="maxLength">
+ <number>20</number>
+ </property>
+ <property name="echoMode">
+ <enum>QLineEdit::Password</enum>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLabel" name="retypePasswordStatus">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>25</width>
+ <height>25</height>
+ </size>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </item>
+ <item row="2" column="1">
+ <spacer name="verticalSpacer">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>264</width>
+ <height>91</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item row="3" column="0" colspan="2">
+ <widget class="Line" name="line_2">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ </widget>
+ </item>
+ <item row="4" column="0" rowspan="3">
+ <widget class="QLabel" name="lblHelpText_2">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="MinimumExpanding" vsizetype="Expanding">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>150</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="text">
+ <string>HelpText</string>
+ </property>
+ <property name="textFormat">
+ <enum>Qt::RichText</enum>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
+ </property>
+ <property name="wordWrap">
+ <bool>true</bool>
+ </property>
+ <property name="openExternalLinks">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item row="5" column="1">
+ <widget class="QCheckBox" name="autologin">
+ <property name="text">
+ <string>Enable Auto-Login</string>
+ </property>
+ </widget>
+ </item>
+ <item row="6" column="1">
+ <spacer name="verticalSpacer_2">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>264</width>
+ <height>59</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item row="7" column="0" colspan="2">
+ <layout class="QVBoxLayout" name="verticalLayout_3">
+ <item>
+ <widget class="Line" name="line">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLabel" name="lblPwdLenNote">
+ <property name="text">
+ <string>&lt;b&gt;Note that all passwords must have 6 - 20 characters!&lt;/b&gt;</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ <tabstops>
+ <tabstop>password</tabstop>
+ <tabstop>retypePassword</tabstop>
+ <tabstop>autologin</tabstop>
+ </tabstops>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/wizard/welcome.cpp b/wizard/welcome.cpp
new file mode 100644
index 0000000..56210e0
--- /dev/null
+++ b/wizard/welcome.cpp
@@ -0,0 +1,35 @@
+#include <QtGui>
+#include "welcome.h"
+#include "../listdelegate.h"
+#include "../listitem.h"
+
+wpWelcome::wpWelcome(QWidget *parent) : QWizardPage(parent)
+{
+ setupUi(this);
+ backend = Backend::instance();
+ connect(backend, SIGNAL(finishedCommand(QString)), this, SLOT(backendFinishedCommand(QString)));
+ setComplete(false);
+ backend->exec("init_installer");
+}
+
+void wpWelcome::backendFinishedCommand(QString command)
+{
+ if(command == "init_installer") setComplete(true);
+}
+
+void wpWelcome::setComplete(bool c)
+{
+ complete = c;
+ emit completeChanged();
+}
+
+bool wpWelcome::isComplete() const
+{
+ return complete;
+}
+
+bool wpWelcome::validatePage()
+{
+ return complete;
+}
+
diff --git a/wizard/welcome.h b/wizard/welcome.h
new file mode 100644
index 0000000..792f127
--- /dev/null
+++ b/wizard/welcome.h
@@ -0,0 +1,26 @@
+#ifndef welcome_H
+#define welcome_H
+
+#include "ui_welcome.h"
+#include "../backend.h"
+
+class wpWelcome : public QWizardPage, Ui::wpWelcome
+{
+ Q_OBJECT
+
+ public:
+ wpWelcome(QWidget *parent = 0);
+ bool isComplete() const;
+ bool validatePage();
+
+ private:
+ Backend* backend;
+ bool complete;
+ void setComplete(bool c);
+
+ private slots:
+ void backendFinishedCommand(QString command);
+
+};
+
+#endif // welcome_H
diff --git a/wizard/welcome.ui b/wizard/welcome.ui
new file mode 100644
index 0000000..6fbc95f
--- /dev/null
+++ b/wizard/welcome.ui
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>wpWelcome</class>
+ <widget class="QWizardPage" name="wpWelcome">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>400</width>
+ <height>300</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>WizardPage</string>
+ </property>
+ <property name="title">
+ <string>Installer</string>
+ </property>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>