Cherry-picking upstream version 0.8.0.

* Switched to experimental
* Bumped Standards to 4.1.1
* Bumped libqtermwidget5-0-dev to >= 0.8.0
* Added dependency libutf8proc-dev
* Bumped lxqt-build-tools to >= 0.4.0
* Fixed copyright
* Dropped appdata-patch, applied upstream
ubuntu/cosmic debian/0.8.0-1
Alf Gaida 7 years ago
parent 78aea4c018
commit 1ae4ff16d1

2
.gitignore vendored

@ -1,2 +0,0 @@
*.pro.user*
src/translations/qterminal

@ -1,7 +1,53 @@
qterminal-0.7.1 / 2016-12-21
qterminal-0.8.0 / 2017-10-21
============================
* Set version to 0.8.0
* Update information on distribution package
* Added legacy font setting support
* Made font in settings file human readable
* Fix action inconsistency when switching tabs
* Lithuanian translation
* Added Lithuanian language
* Don't export github templates
* correct spelling mistake
* Adapt to QTermWidget API changes after DECSCUSR handling
* liblxqt dont make sense here
* Copied issue template
* Drops Qt5Core_VERSION_STRING
* Update qterminal_drop.desktop
* Update qterminal.desktop
* Make disabled actions consistent at all times (#331)
* DBus DropMode (#325)
* Change subterminal shortcuts to avoid breaking qtermwidget scrolling (#327)
* Update main.cpp (#322)
* Fix toggle menu action.
* Focus highlight (#266)
* Replace numbered terminals with directional navigation (#255)
* Fix '1 Terminal' preset (#324)
* Change "Clear Current Tab" into "Clear Active Terminal" (#268)
* Fix duplicated items, rework MainWindow memory-management (#313)
* Fixed comment - needless compiler warnings are annoying (#321)
* DBus integration (#307)
* Restore filter actions (#310)
* New features: trim \n from pasted strings, confirm multiline pastes (#309)
* Support custom QSS styles
* Call QApp destructor (#306)
* Fixes (#318)
* Refactor dangling actions, delete windows on close
* Adds superbuild support
* Improve translation (.ts) handling
* Removes Qt5X11Extras_DEFINITIONS
* Stops adding not exist entries to CMAKE_MODULE_PATH
* Use the LXQtCompilerSettings module
* Adapt to QTermWidget improved CMake
* Fix a copy/paste error from 4afcc4d0d0922f526f89aadf16ec0517f6b5267e
* Update dependencies and cleanup trailing spaces
0.7.1 / 2016-12-21
==================
* Release 0.7.1: Update changelog
* Bump patch version (#294)
* Add common shortcuts for switching tabs (#275)
* Fix tabstop order in properties dialog & add buddy relations for labels. (#290)

@ -4,29 +4,16 @@ project(qterminal)
include(GNUInstallDirs)
set(STR_VERSION "0.7.1")
set(LXQTBT_MINIMUM_VERSION "0.3.0")
# qterminal version
set(STR_VERSION "0.8.0")
set(LXQTBT_MINIMUM_VERSION "0.4.0")
# additional cmake files
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake")
set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake/modules")
option(UPDATE_TRANSLATIONS "Update source translation translations/*.ts files" OFF)
if(NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE Release)
endif()
include(CheckCXXCompilerFlag)
CHECK_CXX_COMPILER_FLAG("-std=c++11" COMPILER_SUPPORTS_CXX11)
CHECK_CXX_COMPILER_FLAG("-std=c++0x" COMPILER_SUPPORTS_CXX0X)
if(COMPILER_SUPPORTS_CXX11)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
elseif(COMPILER_SUPPORTS_CXX0X)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x")
else()
message(FATAL "The compiler ${CMAKE_CXX_COMPILER} has no C++11 support. C++11 support is required")
endif()
# we need qpa/qplatformnativeinterface.h for global shortcut
find_package(Qt5Gui REQUIRED)
find_package(Qt5Widgets REQUIRED)
@ -34,13 +21,13 @@ find_package(Qt5LinguistTools REQUIRED)
if(APPLE)
elseif(UNIX)
find_package(Qt5X11Extras REQUIRED)
find_package(Qt5DBus)
endif()
find_package(QTermWidget5 REQUIRED)
find_package(lxqt-build-tools ${LXQTBT_MINIMUM_VERSION} REQUIRED)
include(LXQtTranslateTs)
message(STATUS "Qt version: ${Qt5Core_VERSION_STRING}")
include(${QTERMWIDGET_USE_FILE})
include(LXQtCompilerSettings NO_POLICY_SCOPE)
message(STATUS "Qt version: ${Qt5Core_VERSION}")
# TODO remove Qxt
message(STATUS "Using bundled Qxt...")
@ -56,8 +43,6 @@ elseif(UNIX)
endif()
add_definitions(-DSTR_VERSION=\"${STR_VERSION}\")
add_definitions(${Qt5X11Extras_DEFINITIONS})
set(EXE_NAME qterminal)
@ -67,13 +52,16 @@ set(QTERM_SRC
src/tabwidget.cpp
src/termwidget.cpp
src/termwidgetholder.cpp
src/terminalconfig.cpp
src/properties.cpp
src/propertiesdialog.cpp
src/bookmarkswidget.cpp
src/fontdialog.cpp
src/dbusaddressable.cpp
)
set(QTERM_MOC_SRC
src/qterminalapp.h
src/mainwindow.h
src/tabwidget.h
src/termwidget.h
@ -83,6 +71,17 @@ set(QTERM_MOC_SRC
src/fontdialog.h
)
if (Qt5DBus_FOUND)
add_definitions(-DHAVE_QDBUS)
QT5_ADD_DBUS_ADAPTOR(QTERM_SRC src/org.lxqt.QTerminal.Window.xml mainwindow.h MainWindow)
QT5_ADD_DBUS_ADAPTOR(QTERM_SRC src/org.lxqt.QTerminal.Tab.xml termwidgetholder.h TermWidgetHolder)
QT5_ADD_DBUS_ADAPTOR(QTERM_SRC src/org.lxqt.QTerminal.Terminal.xml termwidget.h TermWidget)
QT5_ADD_DBUS_ADAPTOR(QTERM_SRC src/org.lxqt.QTerminal.Process.xml qterminalapp.h QTerminalApp)
set(QTERM_MOC_SRC ${QTERM_MOC_SRC} src/dbusaddressable.h)
message(STATUS "Building with Qt5DBus support")
endif()
if(NOT QXT_FOUND)
set(QTERM_SRC ${QTERM_SRC} src/third-party/qxtglobalshortcut.cpp)
set(QTERM_MOC_SRC ${QTERM_MOC_SRC} src/third-party/qxtglobalshortcut.h)
@ -111,6 +110,12 @@ qt5_wrap_ui( QTERM_UI ${QTERM_UI_SRC} )
qt5_wrap_cpp( QTERM_MOC ${QTERM_MOC_SRC} )
qt5_add_resources( QTERM_RCC ${QTERM_RCC_SRC} )
lxqt_translate_ts(QTERM_QM
UPDATE_TRANSLATIONS
${UPDATE_TRANSLATIONS}
SOURCES
${QTERM_SRC}
${QTERM_UI_SRC}
${QTERM_MOC_SRC}
TRANSLATION_DIR "src/translations"
PULL_TRANSLATIONS
${PULL_TRANSLATIONS}
@ -123,10 +128,9 @@ lxqt_translate_ts(QTERM_QM
)
include_directories(
"${CMAKE_SOURCE_DIR}"
"${CMAKE_SOURCE_DIR}/src"
"${CMAKE_BINARY_DIR}"
${QTERMWIDGET_INCLUDE_DIRS}
"${PROJECT_SOURCE_DIR}"
"${PROJECT_SOURCE_DIR}/src"
"${PROJECT_BINARY_DIR}"
${QXT_INCLUDE_DIRS}
)
if(X11_FOUND)
@ -170,14 +174,18 @@ add_executable(${EXE_NAME} ${GUI_TYPE}
${QTERM_QM}
)
target_link_libraries(${EXE_NAME}
${QTERMWIDGET_QT_LIBRARIES}
${QTERMWIDGET_LIBRARIES}
Qt5::Gui
qtermwidget5
util
)
if(QXT_FOUND)
target_link_libraries(${EXE_NAME} ${QXT_CORE_LIB} ${QXT_GUI_LIB})
endif()
if (Qt5DBus_FOUND)
target_link_libraries(${EXE_NAME} ${Qt5DBus_LIBRARIES})
endif()
if(APPLE)
target_link_libraries(${EXE_NAME} ${CARBON_LIBRARY})
elseif(UNIX)
@ -226,12 +234,3 @@ else()
install(FILES ${QTERM_QM} DESTINATION ${CMAKE_INSTALL_PREFIX}/${EXE_NAME}.app/Contents/translations)
endif()
# make lupdate
# it generates new translation files
add_custom_target(lupdate
${QT_QMAKE_EXECUTABLE} -project -o "${CMAKE_CURRENT_BINARY_DIR}/qterminal.pro"
COMMAND ${QT_LUPDATE_EXECUTABLE} "${CMAKE_CURRENT_BINARY_DIR}/qterminal.pro"
WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}"
)

@ -282,7 +282,7 @@ TYPEDEF_HIDES_STRUCT = NO
# causing a significant performance penality.
# If the system has enough physical memory increasing the cache will improve the
# performance by keeping more symbols in memory. Note that the value works on
# a logarithmic scale so increasing the size by one will rougly double the
# a logarithmic scale so increasing the size by one will roughly double the
# memory usage. The cache size is given by this formula:
# 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0,
# corresponding to a cache size of 2^16 = 65536 symbols

@ -2,24 +2,24 @@
## Overview
QTerminal is a lightweight Qt terminal emulator based on [QTermWidget](https://github.com/lxde/qtermwidget).
QTerminal is a lightweight Qt terminal emulator based on [QTermWidget](https://github.com/lxde/qtermwidget).
It is maintained by the LXQt project but can be used independently from this desktop environment. The only bonds are [liblxqt](https://github.com/lxde/liblxqt) representing a build dependency and the localization files which were outsourced to LXQt repository [lxqt-l10n](https://github.com/lxde/lxqt-l10n).
It is maintained by the LXQt project but can be used independently from this desktop environment. The only bonds are [lxqt-build-tools](https://github.com/lxde/lxqt-build-tools) representing a build dependency and the localization files which were outsourced to LXQt repository [lxqt-l10n](https://github.com/lxde/lxqt-l10n).
This project is licensed under the terms of the [GPLv2](https://www.gnu.org/licenses/gpl-2.0.en.html) or any later version. See the LICENSE file for the full text of the license.
This project is licensed under the terms of the [GPLv2](https://www.gnu.org/licenses/gpl-2.0.en.html) or any later version. See the LICENSE file for the full text of the license.
## Installation
### Compiling sources
Runtime dependencies are qtx11extras ≥ 5.2 and [QTermWidget](https://github.com/lxde/qtermwidget).
In order to build CMake ≥ 3.0.2 and [liblxqt](https://github.com/lxde/liblxqt) are needed as well as optionally Git to pull latest VCS checkouts. The localization files were outsourced to repository [lxqt-l10n](https://github.com/lxde/lxqt-l10n) so the corresponding dependencies are needed, too. Please refer to this repository's `README.md` for further information.
Dependencies are qtx11extras ≥ 5.2 and [QTermWidget](https://github.com/lxde/qtermwidget).
In order to build CMake ≥ 3.0.2 and [lxqt-build-tools](https://github.com/lxde/lxqt-build-tools) are needed as well as optionally Git to pull latest VCS checkouts. The localization files were outsourced to repository [lxqt-l10n](https://github.com/lxde/lxqt-l10n) so the corresponding dependencies are needed, too. Please refer to this repository's `README.md` for further information.
Code configuration is handled by CMake. Building out of source is strongly recommended. CMake variable `CMAKE_INSTALL_PREFIX` will normally have to be set to `/usr`.
Code configuration is handled by CMake. Building out of source is strongly recommended. CMake variable `CMAKE_INSTALL_PREFIX` will normally have to be set to `/usr`.
To build run `make`, to install `make install` which accepts variable `DESTDIR` as usual.
To build run `make`, to install `make install` which accepts variable `DESTDIR` as usual.
### Binary packages
QTerminal is provided by all major Linux distributions like Arch Linux ([AUR](https://aur.archlinux.org) only so far), Debian (as of Debian stretch), Fedora and openSUSE (Tumbleweed only so far).
QTerminal is provided by all major Linux distributions like [Arch Linux](https://www.archlinux.org/packages/?q=qterminal), Debian (as of Debian stretch), Fedora and openSUSE.
Just use the distributions' package managers to search for string `qterminal`.

13
debian/changelog vendored

@ -1,3 +1,16 @@
qterminal (0.8.0-1) experimental; urgency=medium
* Cherry-picking upstream version 0.8.0.
* Switched to experimental
* Bumped Standards to 4.1.1
* Bumped libqtermwidget5-0-dev to >= 0.8.0
* Added dependency libutf8proc-dev
* Bumped lxqt-build-tools to >= 0.4.0
* Fixed copyright
* Dropped appdata-patch, applied upstream
-- Alf Gaida <agaida@siduction.org> Mon, 23 Oct 2017 01:47:22 +0200
qterminal (0.7.1-3) unstable; urgency=medium
* Bumped Standards to 4.1.0 - no changes needed

11
debian/control vendored

@ -7,15 +7,16 @@ Section: x11
Priority: optional
Build-Depends: debhelper (>= 10),
libkf5windowsystem-dev,
libqtermwidget5-0-dev,
libqtermwidget5-0-dev (>= 0.8.0),
libqt5svg5-dev,
libqt5x11extras5-dev,
libutf8proc-dev,
libx11-dev,
lxqt-build-tools,
lxqt-build-tools (>= 0.4.0),
qtbase5-private-dev
Standards-Version: 4.1.0
Vcs-Browser: https://anonscm.debian.org/cgit/pkg-lxqt/qterminal.git/?h=debian/sid
Vcs-Git: https://anonscm.debian.org/git/pkg-lxqt/qterminal.git -b debian/sid
Standards-Version: 4.1.1
Vcs-Browser: https://anonscm.debian.org/cgit/pkg-lxqt/qterminal.git/?h=debian/experimental
Vcs-Git: https://anonscm.debian.org/git/pkg-lxqt/qterminal.git -b debian/experimental
Homepage: https://github.com/lxde/qterminal
Package: qterminal

1
debian/copyright vendored

@ -4,6 +4,7 @@ Source: https://github.com/lxde/qterminal.git
Files: *
Copyright: 2010-2016 Petr Vanek <petr@scribus.info>
2016-2017 LXQt Team
License: GPL-2+
Files: src/config.h

2
debian/gbp.conf vendored

@ -1,5 +1,5 @@
[DEFAULT]
debian-branch = debian/sid
debian-branch = debian/experimental
upstream-branch = upstream/latest
pristine-tar = True

@ -1,31 +0,0 @@
Description: Fixes appdata format
* application -> component
Author: Alf Gaida <agaida@siduction.org>
Last-Update: 2017-08-13
--- a/qterminal.appdata.xml
+++ b/qterminal.appdata.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
-<application>
+<component>
<id type="desktop">qterminal.desktop</id>
<metadata_license>CC0-1.0</metadata_license>
<project_license>GPL-2</project_license>
@@ -40,4 +40,4 @@
</screenshot>
</screenshots>
<url type="homepage">https://github.com/lxde/qterminal</url>
-</application>
+</component>
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -197,7 +197,7 @@
install(FILES
qterminal.appdata.xml
- DESTINATION "${CMAKE_INSTALL_DATADIR}/appdata"
+ DESTINATION "${CMAKE_INSTALL_DATADIR}/metainfo"
)
if(NOT APPLEBUNDLE)

@ -1 +0,0 @@
fix-appdata-format.patch

@ -7,6 +7,8 @@ Comment=Terminal emulator
Comment[de]=Befehlszeile verwenden
Comment[el]=Προσομοιωτής τερματικού
Comment[fr]=Terminal
Comment[lt]=Terminalo emuliatorius
Comment[pl]=Emulator terminala
Comment[pt]=Emulador de terminal
Comment[pt_BR]=Emulador de terminal
Comment[ru_RU]=Эмулятор терминала
@ -40,10 +42,12 @@ Name[it]=Terminale a discesa
Name[ja]=ドロップダウン式ターミナル
Name[km]=ស្ថានីយ​ទម្លាក់​ចុះ
Name[ko]=위에서 내려오는 터미널
Name[lt]=Išskleidžiamasis terminalas
Name[nb]=Nedtrekksterminal
Name[nds]=Utklapp-Konsool
Name[nl]=Uitvouwbare terminalemulator
Name[pa]=ਲਟਕਦਾ ਟਰਮੀਨਲ
Name[pl]=Rozwijany emulator terminala
Name[pt]=Terminal suspenso
Name[pt_BR]=Terminal suspenso
Name[ro]=Terminal derulant

@ -8,6 +8,8 @@ Icon=utilities-terminal
Name=QTerminal drop down
Name[de]=QTerminal herabhängend
Name[el]=QTerminal αναπτυσσόμενο
Name[lt]=QTerminal išskleidžiamasis
Name[pl]=QTerminal (tryb rozwijany)
Name[pt]=QTerminal suspenso
Name[pt_BR]=QTerminal suspenso
Name[ja]=QTerminal ドロップダウン
@ -30,10 +32,12 @@ GenericName[it]=Terminale a discesa
GenericName[ja]=ドロップダウン式ターミナル
GenericName[km]=ស្ថានីយ​ទម្លាក់​ចុះ
GenericName[ko]=위에서 내려오는 터미널
GenericName[lt]=Išskleidžiamasis terminalas
GenericName[nb]=Nedtrekksterminal
GenericName[nds]=Utklapp-Konsool
GenericName[nl]=Uitvouwbare terminalemulator
GenericName[pa]=ਲਟਕਦਾ ਟਰਮੀਨਲ
GenericName[pl]=Rozwijany emulator terminala
GenericName[pt]=Terminal suspenso
GenericName[pt_BR]=Terminal suspenso
GenericName[ro]=Terminal derulant
@ -50,6 +54,7 @@ GenericName[zh_TW]=下拉式終端機
Comment=A drop-down terminal emulator.
Comment[de]=Ein Ausklapp-Terminalemulator.
Comment[el]=Ένας αναπτυσσόμενος προσομοιωτής τερματικού.
Comment[lt]=Išskleidžiamasis terminalo emuliatorius.
Comment[pt]=Um emulador de terminal suspenso.
Comment[pt_BR]=Um emulador de terminal suspenso.
Comment[ru]=Вападающий эмулятор терминала.

@ -140,7 +140,7 @@ public:
// system env - include dirs in the tree
QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
foreach (QString i, env.keys())
foreach (const QString &i, env.keys())
{
path = env.value(i);
if (!d.exists(path) || !QFileInfo(path).isDir())
@ -217,7 +217,7 @@ public:
break;
}
case QXmlStreamReader::Invalid:
qDebug() << "XML error: " << xml.errorString().data()
qDebug() << "XML error: " << xml.errorString().constData()
<< xml.lineNumber() << xml.columnNumber();
m_map.clear();
return;

@ -37,8 +37,10 @@
#define SPLIT_VERTICAL "Split Terminal Vertically"
#define SUB_COLLAPSE "Collapse Subterminal"
#define SUB_NEXT "Next Subterminal"
#define SUB_PREV "Previous Subterminal"
#define SUB_LEFT "Left Subterminal"
#define SUB_RIGHT "Right Subterminal"
#define SUB_TOP "Top Subterminal"
#define SUB_BOTTOM "Bottom Subterminal"
#define MOVE_LEFT "Move Tab Left"
#define MOVE_RIGHT "Move Tab Right"
@ -69,10 +71,13 @@
// ACTIONS
#define CLEAR_TERMINAL_SHORTCUT "Ctrl+Shift+X"
#define TAB_PREV_SHORTCUT "Shift+Left|Ctrl+PgUp|Ctrl+Shift+Tab"
#define TAB_NEXT_SHORTCUT "Shift+Right|Ctrl+PgDown|Ctrl+Tab"
#define SUB_PREV_SHORTCUT "Shift+Down"
#define SUB_NEXT_SHORTCUT "Shift+Up"
#define TAB_PREV_SHORTCUT "Ctrl+PgUp|Ctrl+Shift+Tab"
#define TAB_NEXT_SHORTCUT "Ctrl+PgDown|Ctrl+Tab"
#define SUB_BOTTOM_SHORTCUT "Alt+Down"
#define SUB_TOP_SHORTCUT "Alt+Up"
#define SUB_LEFT_SHORTCUT "Alt+Left"
#define SUB_RIGHT_SHORTCUT "Alt+Right"
#ifdef Q_WS_MAC
// It's tricky - Ctrl is "command" key on mac's keyboards

@ -0,0 +1,25 @@
#include "dbusaddressable.h"
#ifdef HAVE_QDBUS
Q_DECLARE_METATYPE(QList<QDBusObjectPath>)
QString DBusAddressable::getDbusPathString()
{
return m_path;
}
QDBusObjectPath DBusAddressable::getDbusPath()
{
return QDBusObjectPath(m_path);
}
#endif
DBusAddressable::DBusAddressable(QString prefix)
{
#ifdef HAVE_QDBUS
QString uuidString = QUuid::createUuid().toString();
m_path = prefix + "/" + uuidString.replace(QRegExp("[\\{\\}\\-]"), "");
#endif
}

@ -0,0 +1,34 @@
#ifndef DBUSADDRESSABLE_H
#define DBUSADDRESSABLE_H
#include <QString>
#ifdef HAVE_QDBUS
#include <QtDBus/QtDBus>
#include <QUuid>
#endif
class DBusAddressable
{
#ifdef HAVE_QDBUS
private:
QString m_path;
#endif
public:
#ifdef HAVE_QDBUS
QDBusObjectPath getDbusPath();
QString getDbusPathString();
#endif
DBusAddressable(QString prefix);
};
#ifdef HAVE_QDBUS
template <class AClass, class WClass> void registerAdapter(WClass *obj)
{
new AClass(obj);
QString path = dynamic_cast<DBusAddressable*>(obj)->getDbusPathString();
QDBusConnection::sessionBus().registerObject(path, obj);
}
#endif
#endif

@ -423,6 +423,20 @@
</property>
</widget>
</item>
<item row="3" column="0" colspan="3">
<widget class="QCheckBox" name="confirmMultilinePasteCheckBox">
<property name="text">
<string>Confirm multiline paste</string>
</property>
</widget>
</item>
<item row="4" column="0" colspan="3">
<widget class="QCheckBox" name="trimPastedTrailingNewlinesCheckBox">
<property name="text">
<string>Trim trailing newlines in pasted text</string>
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="QSpinBox" name="historyLimitedTo">
<property name="minimum">
@ -436,7 +450,7 @@
</property>
</widget>
</item>
<item row="7" column="1">
<item row="9" column="1">
<spacer name="verticalSpacer_4">
<property name="orientation">
<enum>Qt::Vertical</enum>
@ -449,28 +463,28 @@
</property>
</spacer>
</item>
<item row="6" column="0" colspan="3">
<item row="8" column="0" colspan="3">
<widget class="QCheckBox" name="useCwdCheckBox">
<property name="text">
<string>Open new terminals in current working directory</string>
</property>
</widget>
</item>
<item row="5" column="0" colspan="3">
<item row="7" column="0" colspan="3">
<widget class="QCheckBox" name="saveSizeOnExitCheckBox">
<property name="text">
<string>Save Size when closing</string>
</property>
</widget>
</item>
<item row="4" column="0" colspan="3">
<item row="6" column="0" colspan="3">
<widget class="QCheckBox" name="savePosOnExitCheckBox">
<property name="text">
<string>Save Position when closing</string>
</property>
</widget>
</item>
<item row="3" column="0" colspan="3">
<item row="5" column="0" colspan="3">
<widget class="QCheckBox" name="askOnExitCheckBox">
<property name="text">
<string>Ask for confirmation when closing</string>

@ -111,21 +111,6 @@
<string>About &amp;Qt...</string>
</property>
</action>
<action name="actProperties">
<property name="text">
<string>&amp;Preferences...</string>
</property>
</action>
<action name="actQuit">
<property name="icon">
<iconset theme="application-exit">
<normaloff/>
</iconset>
</property>
<property name="text">
<string>&amp;Quit</string>
</property>
</action>
</widget>
<customwidgets>
<customwidget>

@ -19,11 +19,20 @@
#include <QApplication>
#include <QtGlobal>
#include <assert.h>
#include <stdio.h>
#include <getopt.h>
#include <stdlib.h>
#ifdef HAVE_QDBUS
#include <QtDBus/QtDBus>
#include <unistd.h>
#include "processadaptor.h"
#endif
#include "mainwindow.h"
#include "mainwindow.h"
#include "qterminalapp.h"
#include "terminalconfig.h"
#define out
@ -39,6 +48,8 @@ const struct option long_options[] = {
{NULL, 0, NULL, 0}
};
QTerminalApp * QTerminalApp::m_instance = NULL;
void print_usage_and_exit(int code)
{
printf("QTerminal %s\n", STR_VERSION);
@ -111,16 +122,35 @@ int main(int argc, char *argv[])
// Warning: do not change settings format. It can screw bookmarks later.
QSettings::setDefaultFormat(QSettings::IniFormat);
QApplication app(argc, argv);
QTerminalApp *app = QTerminalApp::Instance(argc, argv);
#ifdef HAVE_QDBUS
app->registerOnDbus();
#endif
QString workdir, shell_command;
bool dropMode;
parse_args(argc, argv, workdir, shell_command, dropMode);
if (workdir.isEmpty())
workdir = QDir::currentPath();
app->setWorkingDirectory(workdir);
const QSettings settings;
const QFileInfo customStyle = QFileInfo(
QFileInfo(settings.fileName()).canonicalPath() +
"/style.qss"
);
if (customStyle.isFile() && customStyle.isReadable())
{
QFile style(customStyle.canonicalFilePath());
style.open(QFile::ReadOnly);
QString styleString = QLatin1String(style.readAll());
app->setStyleSheet(styleString);
}
// icons
/* setup our custom icon theme if there is no system theme (OS X, Windows) */
QCoreApplication::instance()->setAttribute(Qt::AA_UseHighDpiPixmaps); //Fix for High-DPI systems
if (QIcon::themeName().isEmpty())
QIcon::setThemeName("QTerminal");
@ -135,25 +165,151 @@ int main(int argc, char *argv[])
qDebug() << "APPLE_BUNDLE: Loading translator file" << fname << "from dir" << QApplication::applicationDirPath()+"../translations";
qDebug() << "load success:" << translator.load(fname, QApplication::applicationDirPath()+"../translations", "_");
#endif
app.installTranslator(&translator);
app->installTranslator(&translator);
MainWindow *window;
TerminalConfig initConfig = TerminalConfig(workdir, shell_command);
app->newWindow(dropMode, initConfig);
int ret = app->exec();
delete Properties::Instance();
app->cleanup();
return ret;
}
MainWindow *QTerminalApp::newWindow(bool dropMode, TerminalConfig &cfg)
{
MainWindow *window = NULL;
if (dropMode)
{
QWidget *hiddenPreviewParent = new QWidget(0, Qt::Tool);
window = new MainWindow(workdir, shell_command, dropMode, hiddenPreviewParent);
window = new MainWindow(cfg, dropMode, hiddenPreviewParent);
if (Properties::Instance()->dropShowOnStart)
window->show();
}
else
{
window = new MainWindow(workdir, shell_command, dropMode);
window = new MainWindow(cfg, dropMode);
window->show();
}
return window;
}
int ret = app.exec();
delete Properties::Instance();
delete window;
QTerminalApp *QTerminalApp::Instance()
{
assert(m_instance != NULL);
return m_instance;
}
return ret;
QTerminalApp *QTerminalApp::Instance(int &argc, char **argv)
{
assert(m_instance == NULL);
m_instance = new QTerminalApp(argc, argv);
return m_instance;
}
QTerminalApp::QTerminalApp(int &argc, char **argv)
:QApplication(argc, argv)
{
}
QString &QTerminalApp::getWorkingDirectory()
{
return m_workDir;
}
void QTerminalApp::setWorkingDirectory(const QString &wd)
{
m_workDir = wd;
}
void QTerminalApp::cleanup() {
delete m_instance;
m_instance = NULL;
}
void QTerminalApp::addWindow(MainWindow *window)
{
m_windowList.append(window);
}
void QTerminalApp::removeWindow(MainWindow *window)
{
m_windowList.removeOne(window);
}
QList<MainWindow *> QTerminalApp::getWindowList()
{
return m_windowList;
}
#ifdef HAVE_QDBUS
void QTerminalApp::registerOnDbus()
{
if (!QDBusConnection::sessionBus().isConnected())
{
fprintf(stderr, "Cannot connect to the D-Bus session bus.\n"
"To start it, run:\n"
"\teval `dbus-launch --auto-syntax`\n");
return;
}
QString serviceName = QStringLiteral("org.lxqt.QTerminal-%1").arg(getpid());
if (!QDBusConnection::sessionBus().registerService(serviceName))
{
fprintf(stderr, "%s\n", qPrintable(QDBusConnection::sessionBus().lastError().message()));
return;
}
new ProcessAdaptor(this);
QDBusConnection::sessionBus().registerObject("/", this);
}
QList<QDBusObjectPath> QTerminalApp::getWindows()
{
QList<QDBusObjectPath> windows;
foreach (MainWindow *wnd, m_windowList)
{
windows.push_back(wnd->getDbusPath());
}
return windows;
}
QDBusObjectPath QTerminalApp::newWindow(const QHash<QString,QVariant> &termArgs)
{
TerminalConfig cfg = TerminalConfig::fromDbus(termArgs);
MainWindow *wnd = newWindow(false, cfg);
assert(wnd != NULL);
return wnd->getDbusPath();
}
QDBusObjectPath QTerminalApp::getActiveWindow()
{
QWidget *aw = activeWindow();
if (aw == NULL)
return QDBusObjectPath("/");
return qobject_cast<MainWindow*>(aw)->getDbusPath();
}
bool QTerminalApp::isDropMode() {
if (m_windowList.count() == 0) {
return false;
}
MainWindow *wnd = m_windowList.at(0);
return wnd->dropMode();
}
bool QTerminalApp::toggleDropdown() {
if (m_windowList.count() == 0) {
return false;
}
MainWindow *wnd = m_windowList.at(0);
if (!wnd->dropMode()) {
return false;
}
wnd->showHide();
return true;
}
#endif

@ -22,6 +22,12 @@
#include <QMessageBox>
#include <functional>
#ifdef HAVE_QDBUS
#include <QtDBus/QtDBus>
#include "windowadaptor.h"
#endif
#include "terminalconfig.h"
#include "mainwindow.h"
#include "tabwidget.h"
#include "termwidgetholder.h"
@ -29,7 +35,8 @@
#include "properties.h"
#include "propertiesdialog.h"
#include "bookmarkswidget.h"
#include "qterminalapp.h"
#include "dbusaddressable.h"
typedef std::function<bool(MainWindow&)> checkfn;
Q_DECLARE_METATYPE(checkfn)
@ -37,18 +44,30 @@ Q_DECLARE_METATYPE(checkfn)
// TODO/FXIME: probably remove. QSS makes it unusable on mac...
#define QSS_DROP "MainWindow {border: 1px solid rgba(0, 0, 0, 50%);}\n"
MainWindow::MainWindow(const QString& work_dir,
const QString& command,
MainWindow::MainWindow(TerminalConfig &cfg,
bool dropMode,
QWidget * parent,
Qt::WindowFlags f)
: QMainWindow(parent,f),
m_initShell(command),
m_initWorkDir(work_dir),
DBusAddressable("/windows"),
tabPosition(NULL),
scrollBarPosition(NULL),
keyboardCursorShape(NULL),
tabPosMenu(NULL),
scrollPosMenu(NULL),
keyboardCursorShapeMenu(NULL),
settingOwner(NULL),
presetsMenu(NULL),
m_config(cfg),
m_dropLockButton(0),
m_dropMode(dropMode)
{
#ifdef HAVE_QDBUS
registerAdapter<WindowAdaptor, MainWindow>(this);
#endif
QTerminalApp::Instance()->addWindow(this);
setAttribute(Qt::WA_TranslucentBackground);
setAttribute(Qt::WA_DeleteOnClose);
setupUi(this);
Properties::Instance()->migrate_settings();
@ -88,26 +107,39 @@ MainWindow::MainWindow(const QString& work_dir,
consoleTabulator->setAutoFillBackground(true);
connect(consoleTabulator, SIGNAL(closeTabNotification()), SLOT(close()));
consoleTabulator->setWorkDirectory(work_dir);
consoleTabulator->setTabPosition((QTabWidget::TabPosition)Properties::Instance()->tabsPos);
//consoleTabulator->setShellProgram(command);
setup_FileMenu_Actions();
setup_ActionsMenu_Actions();
setup_ViewMenu_Actions();
// apply props
propertiesChanged();
setupCustomDirs();
connect(consoleTabulator, &TabWidget::currentTitleChanged, this, &MainWindow::onCurrentTitleChanged);
connect(menu_Actions, SIGNAL(aboutToShow()), this, SLOT(aboutToShowActionsMenu()));
connect(menu_Actions, SIGNAL(aboutToShow()), this, SLOT(updateDisabledActions()));
/* The tab should be added after all changes are made to
the main window; otherwise, the initial prompt might
get jumbled because of changes in internal geometry. */
consoleTabulator->addNewTab(command);
consoleTabulator->addNewTab(m_config);
}
void MainWindow::rebuildActions()
{
// Delete all setting-related QObjects
delete settingOwner;
settingOwner = new QWidget(this);
settingOwner->setGeometry(0,0,0,0);
// Then create them again
setup_FileMenu_Actions();
setup_ActionsMenu_Actions();
setup_ViewMenu_Actions();
}
MainWindow::~MainWindow()
{
QTerminalApp::Instance()->removeWindow(this);
}
void MainWindow::enableDropMode()
@ -145,22 +177,22 @@ void MainWindow::setup_Action(const char *name, QAction *action, const char *def
QList<QKeySequence> shortcuts;
Properties::Instance()->actions[name] = action;
actions[name] = action;
foreach (const QString &sequenceString, settings.value(name, defaultShortcut).toString().split('|'))
shortcuts.append(QKeySequence::fromString(sequenceString));
Properties::Instance()->actions[name]->setShortcuts(shortcuts);
actions[name]->setShortcuts(shortcuts);
if (receiver)
{
connect(Properties::Instance()->actions[name], SIGNAL(triggered(bool)), receiver, slot);
addAction(Properties::Instance()->actions[name]);
connect(actions[name], SIGNAL(triggered(bool)), receiver, slot);
addAction(actions[name]);
}
if (menu)
menu->addAction(Properties::Instance()->actions[name]);
menu->addAction(actions[name]);
if (!data.isNull())
Properties::Instance()->actions[name]->setData(data);
actions[name]->setData(data);
}
void MainWindow::setup_ActionsMenu_Actions()
@ -170,68 +202,77 @@ void MainWindow::setup_ActionsMenu_Actions()
const checkfn checkTabs = &MainWindow::hasMultipleTabs;
const checkfn checkSubterminals = &MainWindow::hasMultipleSubterminals;
setup_Action(CLEAR_TERMINAL, new QAction(QIcon::fromTheme("edit-clear"), tr("&Clear Current Tab"), this),
menu_Actions->clear();
setup_Action(CLEAR_TERMINAL, new QAction(QIcon::fromTheme("edit-clear"), tr("&Clear Active Terminal"), settingOwner),
CLEAR_TERMINAL_SHORTCUT, consoleTabulator, SLOT(clearActiveTerminal()), menu_Actions);
menu_Actions->addSeparator();
data.setValue(checkTabs);
setup_Action(TAB_NEXT, new QAction(QIcon::fromTheme("go-next"), tr("&Next Tab"), this),
setup_Action(TAB_NEXT, new QAction(QIcon::fromTheme("go-next"), tr("&Next Tab"), settingOwner),
TAB_NEXT_SHORTCUT, consoleTabulator, SLOT(switchToRight()), menu_Actions, data);
setup_Action(TAB_PREV, new QAction(QIcon::fromTheme("go-previous"), tr("&Previous Tab"), this),
setup_Action(TAB_PREV, new QAction(QIcon::fromTheme("go-previous"), tr("&Previous Tab"), settingOwner),
TAB_PREV_SHORTCUT, consoleTabulator, SLOT(switchToLeft()), menu_Actions, data);
setup_Action(MOVE_LEFT, new QAction(tr("Move Tab &Left"), this),
setup_Action(MOVE_LEFT, new QAction(tr("Move Tab &Left"), settingOwner),
MOVE_LEFT_SHORTCUT, consoleTabulator, SLOT(moveLeft()), menu_Actions, data);
setup_Action(MOVE_RIGHT, new QAction(tr("Move Tab &Right"), this),
setup_Action(MOVE_RIGHT, new QAction(tr("Move Tab &Right"), settingOwner),
MOVE_RIGHT_SHORTCUT, consoleTabulator, SLOT(moveRight()), menu_Actions, data);
menu_Actions->addSeparator();
setup_Action(SPLIT_HORIZONTAL, new QAction(tr("Split Terminal &Horizontally"), this),
setup_Action(SPLIT_HORIZONTAL, new QAction(tr("Split Terminal &Horizontally"), settingOwner),
NULL, consoleTabulator, SLOT(splitHorizontally()), menu_Actions);
setup_Action(SPLIT_VERTICAL, new QAction(tr("Split Terminal &Vertically"), this),
setup_Action(SPLIT_VERTICAL, new QAction(tr("Split Terminal &Vertically"), settingOwner),
NULL, consoleTabulator, SLOT(splitVertically()), menu_Actions);
data.setValue(checkSubterminals);
setup_Action(SUB_COLLAPSE, new QAction(tr("&Collapse Subterminal"), this),
setup_Action(SUB_COLLAPSE, new QAction(tr("&Collapse Subterminal"), settingOwner),
NULL, consoleTabulator, SLOT(splitCollapse()), menu_Actions, data);
setup_Action(SUB_NEXT, new QAction(QIcon::fromTheme("go-up"), tr("N&ext Subterminal"), this),
SUB_NEXT_SHORTCUT, consoleTabulator, SLOT(switchNextSubterminal()), menu_Actions, data);
setup_Action(SUB_TOP, new QAction(QIcon::fromTheme("go-up"), tr("&Top Subterminal"), settingOwner),
SUB_TOP_SHORTCUT, consoleTabulator, SLOT(switchTopSubterminal()), menu_Actions, data);
setup_Action(SUB_BOTTOM, new QAction(QIcon::fromTheme("go-down"), tr("&Bottom Subterminal"), settingOwner),
SUB_BOTTOM_SHORTCUT, consoleTabulator, SLOT(switchBottomSubterminal()), menu_Actions, data);
setup_Action(SUB_LEFT, new QAction(QIcon::fromTheme("go-previous"), tr("L&eft Subterminal"), settingOwner),
SUB_LEFT_SHORTCUT, consoleTabulator, SLOT(switchLeftSubterminal()), menu_Actions, data);
setup_Action(SUB_RIGHT, new QAction(QIcon::fromTheme("go-next"), tr("R&ight Subterminal"), settingOwner),
SUB_RIGHT_SHORTCUT, consoleTabulator, SLOT(switchRightSubterminal()), menu_Actions, data);
setup_Action(SUB_PREV, new QAction(QIcon::fromTheme("go-down"), tr("P&revious Subterminal"), this),
SUB_PREV_SHORTCUT, consoleTabulator, SLOT(switchPrevSubterminal()), menu_Actions, data);
menu_Actions->addSeparator();
// Copy and Paste are only added to the table for the sake of bindings at the moment; there is no Edit menu, only a context menu.
setup_Action(COPY_SELECTION, new QAction(QIcon::fromTheme("edit-copy"), tr("Copy &Selection"), this),
setup_Action(COPY_SELECTION, new QAction(QIcon::fromTheme("edit-copy"), tr("Copy &Selection"), settingOwner),
COPY_SELECTION_SHORTCUT, consoleTabulator, SLOT(copySelection()), menu_Edit);
setup_Action(PASTE_CLIPBOARD, new QAction(QIcon::fromTheme("edit-paste"), tr("Paste Clip&board"), this),
setup_Action(PASTE_CLIPBOARD, new QAction(QIcon::fromTheme("edit-paste"), tr("Paste Clip&board"), settingOwner),
PASTE_CLIPBOARD_SHORTCUT, consoleTabulator, SLOT(pasteClipboard()), menu_Edit);
setup_Action(PASTE_SELECTION, new QAction(QIcon::fromTheme("edit-paste"), tr("Paste S&election"), this),
setup_Action(PASTE_SELECTION, new QAction(QIcon::fromTheme("edit-paste"), tr("Paste S&election"), settingOwner),
PASTE_SELECTION_SHORTCUT, consoleTabulator, SLOT(pasteSelection()), menu_Edit);
setup_Action(ZOOM_IN, new QAction(QIcon::fromTheme("zoom-in"), tr("Zoom &in"), this),
setup_Action(ZOOM_IN, new QAction(QIcon::fromTheme("zoom-in"), tr("Zoom &in"), settingOwner),
ZOOM_IN_SHORTCUT, consoleTabulator, SLOT(zoomIn()), menu_Edit);
setup_Action(ZOOM_OUT, new QAction(QIcon::fromTheme("zoom-out"), tr("Zoom &out"), this),
setup_Action(ZOOM_OUT, new QAction(QIcon::fromTheme("zoom-out"), tr("Zoom &out"), settingOwner),
ZOOM_OUT_SHORTCUT, consoleTabulator, SLOT(zoomOut()), menu_Edit);
setup_Action(ZOOM_RESET, new QAction(QIcon::fromTheme("zoom-original"), tr("Zoom rese&t"), this),
setup_Action(ZOOM_RESET, new QAction(QIcon::fromTheme("zoom-original"), tr("Zoom rese&t"), settingOwner),
ZOOM_RESET_SHORTCUT, consoleTabulator, SLOT(zoomReset()), menu_Edit);
menu_Actions->addSeparator();
setup_Action(FIND, new QAction(QIcon::fromTheme("edit-find"), tr("&Find..."), this),
setup_Action(FIND, new QAction(QIcon::fromTheme("edit-find"), tr("&Find..."), settingOwner),
FIND_SHORTCUT, this, SLOT(find()), menu_Actions);
#if 0
@ -252,63 +293,66 @@ void MainWindow::setup_ActionsMenu_Actions()
connect(act, SIGNAL(triggered()), consoleTabulator, SLOT(loadSession()));
#endif
setup_Action(TOGGLE_MENU, new QAction(tr("&Toggle Menu"), this),
TOGGLE_MENU_SHORTCUT, this, SLOT(find()));
setup_Action(TOGGLE_MENU, new QAction(tr("&Toggle Menu"), settingOwner),
TOGGLE_MENU_SHORTCUT, this, SLOT(toggleMenu()));
// this is correct - add action to main window - not to menu to keep toggle working
// Add global rename current session shortcut
setup_Action(RENAME_SESSION, new QAction(tr("Rename session"), this),
setup_Action(RENAME_SESSION, new QAction(tr("Rename session"), settingOwner),
RENAME_SESSION_SHORTCUT, consoleTabulator, SLOT(renameCurrentSession()));
// this is correct - add action to main window - not to menu
// apply props
propertiesChanged();
}
void MainWindow::setup_FileMenu_Actions()
{
setup_Action(ADD_TAB, new QAction(QIcon::fromTheme("list-add"), tr("&New Tab"), this),
menu_File->clear();
setup_Action(ADD_TAB, new QAction(QIcon::fromTheme("list-add"), tr("&New Tab"), settingOwner),
ADD_TAB_SHORTCUT, this, SLOT(addNewTab()), menu_File);
QMenu *presetsMenu = new QMenu(tr("New Tab From &Preset"), this);
presetsMenu->addAction(QIcon(), tr("1 &Terminal"),
consoleTabulator, SLOT(addNewTab()));
presetsMenu->addAction(QIcon(), tr("2 &Horizontal Terminals"),
consoleTabulator, SLOT(preset2Horizontal()));
presetsMenu->addAction(QIcon(), tr("2 &Vertical Terminals"),
consoleTabulator, SLOT(preset2Vertical()));
presetsMenu->addAction(QIcon(), tr("4 Terminal&s"),
consoleTabulator, SLOT(preset4Terminals()));
if (presetsMenu == NULL) {
presetsMenu = new QMenu(tr("New Tab From &Preset"), this);
presetsMenu->addAction(QIcon(), tr("1 &Terminal"),
this, SLOT(addNewTab()));
presetsMenu->addAction(QIcon(), tr("2 &Horizontal Terminals"),
consoleTabulator, SLOT(preset2Horizontal()));
presetsMenu->addAction(QIcon(), tr("2 &Vertical Terminals"),
consoleTabulator, SLOT(preset2Vertical()));
presetsMenu->addAction(QIcon(), tr("4 Terminal&s"),
consoleTabulator, SLOT(preset4Terminals()));
}
menu_File->addMenu(presetsMenu);
setup_Action(CLOSE_TAB, new QAction(QIcon::fromTheme("list-remove"), tr("&Close Tab"), this),
setup_Action(CLOSE_TAB, new QAction(QIcon::fromTheme("list-remove"), tr("&Close Tab"), settingOwner),
CLOSE_TAB_SHORTCUT, consoleTabulator, SLOT(removeCurrentTab()), menu_File);
setup_Action(NEW_WINDOW, new QAction(QIcon::fromTheme("window-new"), tr("&New Window"), this),
setup_Action(NEW_WINDOW, new QAction(QIcon::fromTheme("window-new"), tr("&New Window"), settingOwner),
NEW_WINDOW_SHORTCUT, this, SLOT(newTerminalWindow()), menu_File);
menu_File->addSeparator();
setup_Action(PREFERENCES, actProperties, "", this, SLOT(actProperties_triggered()), menu_File);
setup_Action(PREFERENCES, new QAction(tr("&Preferences..."), settingOwner), "", this, SLOT(actProperties_triggered()), menu_File);
menu_File->addSeparator();
setup_Action(QUIT, actQuit, "", this, SLOT(close()), menu_File);
setup_Action(QUIT, new QAction(QIcon::fromTheme("application-exit"), tr("&Quit"), settingOwner), "", this, SLOT(close()), menu_File);
}
void MainWindow::setup_ViewMenu_Actions()
{
QAction *hideBordersAction = new QAction(tr("&Hide Window Borders"), this);
menu_Window->clear();
QAction *hideBordersAction = new QAction(tr("&Hide Window Borders"), settingOwner);
hideBordersAction->setCheckable(true);
hideBordersAction->setVisible(!m_dropMode);
setup_Action(HIDE_WINDOW_BORDERS, hideBordersAction,
NULL, this, SLOT(toggleBorderless()), menu_Window);
//Properties::Instance()->actions[HIDE_WINDOW_BORDERS]->setObjectName("toggle_Borderless");
// TODO/FIXME: it's broken somehow. When I call toggleBorderless() here the non-responsive window appear
// Properties::Instance()->actions[HIDE_WINDOW_BORDERS]->setChecked(Properties::Instance()->borderless);
// actions[HIDE_WINDOW_BORDERS]->setChecked(Properties::Instance()->borderless);
// if (Properties::Instance()->borderless)
// toggleBorderless();
QAction *showTabBarAction = new QAction(tr("&Show Tab Bar"), this);
QAction *showTabBarAction = new QAction(tr("&Show Tab Bar"), settingOwner);
//toggleTabbar->setObjectName("toggle_TabBar");
showTabBarAction->setCheckable(true);
showTabBarAction->setChecked(!Properties::Instance()->tabBarless);
@ -316,30 +360,33 @@ void MainWindow::setup_ViewMenu_Actions()
NULL, this, SLOT(toggleTabBar()), menu_Window);
toggleTabBar();
QAction *toggleFullscreen = new QAction(tr("Fullscreen"), this);
QAction *toggleFullscreen = new QAction(tr("Fullscreen"), settingOwner);
toggleFullscreen->setCheckable(true);
toggleFullscreen->setChecked(false);
setup_Action(FULLSCREEN, toggleFullscreen,
FULLSCREEN_SHORTCUT, this, SLOT(showFullscreen(bool)), menu_Window);
setup_Action(TOGGLE_BOOKMARKS, m_bookmarksDock->toggleViewAction(),
setup_Action(TOGGLE_BOOKMARKS, new QAction(tr("Toggle Bookmarks"), settingOwner),
TOGGLE_BOOKMARKS_SHORTCUT, NULL, NULL, menu_Window);
menu_Window->addSeparator();
/* tabs position */
tabPosition = new QActionGroup(this);
QAction *tabBottom = new QAction(tr("&Bottom"), this);
QAction *tabTop = new QAction(tr("&Top"), this);
QAction *tabRight = new QAction(tr("&Right"), this);
QAction *tabLeft = new QAction(tr("&Left"), this);
tabPosition->addAction(tabTop);
tabPosition->addAction(tabBottom);
tabPosition->addAction(tabLeft);
tabPosition->addAction(tabRight);
for(int i = 0; i < tabPosition->actions().size(); ++i)
tabPosition->actions().at(i)->setCheckable(true);
if (tabPosition == NULL) {
tabPosition = new QActionGroup(this);
QAction *tabBottom = new QAction(tr("&Bottom"), this);
QAction *tabTop = new QAction(tr("&Top"), this);
QAction *tabRight = new QAction(tr("&Right"), this);
QAction *tabLeft = new QAction(tr("&Left"), this);
tabPosition->addAction(tabTop);
tabPosition->addAction(tabBottom);
tabPosition->addAction(tabLeft);
tabPosition->addAction(tabRight);
for(int i = 0; i < tabPosition->actions().size(); ++i)
tabPosition->actions().at(i)->setCheckable(true);
}
if( tabPosition->actions().count() > Properties::Instance()->tabsPos )
tabPosition->actions().at(Properties::Instance()->tabsPos)->setChecked(true);
@ -347,96 +394,84 @@ void MainWindow::setup_ViewMenu_Actions()
connect(tabPosition, SIGNAL(triggered(QAction *)),
consoleTabulator, SLOT(changeTabPosition(QAction *)) );
tabPosMenu = new QMenu(tr("&Tabs Layout"), menu_Window);
tabPosMenu->setObjectName("tabPosMenu");
if (tabPosMenu == NULL) {
tabPosMenu = new QMenu(tr("&Tabs Layout"), menu_Window);
tabPosMenu->setObjectName("tabPosMenu");
for(int i=0; i < tabPosition->actions().size(); ++i) {
tabPosMenu->addAction(tabPosition->actions().at(i));
}
for(int i=0; i < tabPosition->actions().size(); ++i) {
tabPosMenu->addAction(tabPosition->actions().at(i));
}
connect(menu_Window, SIGNAL(hovered(QAction *)),
this, SLOT(updateActionGroup(QAction *)));
connect(menu_Window, SIGNAL(hovered(QAction *)),
this, SLOT(updateActionGroup(QAction *)));
}
menu_Window->addMenu(tabPosMenu);
/* */
/* Scrollbar */
scrollBarPosition = new QActionGroup(this);
QAction *scrollNone = new QAction(tr("&None"), this);
QAction *scrollRight = new QAction(tr("&Right"), this);
QAction *scrollLeft = new QAction(tr("&Left"), this);
/* order of insertion is dep. on QTermWidget::ScrollBarPosition enum */
scrollBarPosition->addAction(scrollNone);
scrollBarPosition->addAction(scrollLeft);
scrollBarPosition->addAction(scrollRight);
for(int i = 0; i < scrollBarPosition->actions().size(); ++i)
scrollBarPosition->actions().at(i)->setCheckable(true);
if( Properties::Instance()->scrollBarPos < scrollBarPosition->actions().size() )
scrollBarPosition->actions().at(Properties::Instance()->scrollBarPos)->setChecked(true);
connect(scrollBarPosition, SIGNAL(triggered(QAction *)),
if (scrollBarPosition == NULL) {
scrollBarPosition = new QActionGroup(this);
QAction *scrollNone = new QAction(tr("&None"), this);
QAction *scrollRight = new QAction(tr("&Right"), this);
QAction *scrollLeft = new QAction(tr("&Left"), this);
/* order of insertion is dep. on QTermWidget::ScrollBarPosition enum */
scrollBarPosition->addAction(scrollNone);
scrollBarPosition->addAction(scrollLeft);
scrollBarPosition->addAction(scrollRight);
for(int i = 0; i < scrollBarPosition->actions().size(); ++i)
scrollBarPosition->actions().at(i)->setCheckable(true);
if( Properties::Instance()->scrollBarPos < scrollBarPosition->actions().size() )
scrollBarPosition->actions().at(Properties::Instance()->scrollBarPos)->setChecked(true);
connect(scrollBarPosition, SIGNAL(triggered(QAction *)),
consoleTabulator, SLOT(changeScrollPosition(QAction *)) );
scrollPosMenu = new QMenu(tr("S&crollbar Layout"), menu_Window);
scrollPosMenu->setObjectName("scrollPosMenu");
}
if (scrollPosMenu == NULL) {
scrollPosMenu = new QMenu(tr("S&crollbar Layout"), menu_Window);
scrollPosMenu->setObjectName("scrollPosMenu");
for(int i=0; i < scrollBarPosition->actions().size(); ++i) {
scrollPosMenu->addAction(scrollBarPosition->actions().at(i));
for(int i=0; i < scrollBarPosition->actions().size(); ++i) {
scrollPosMenu->addAction(scrollBarPosition->actions().at(i));
}
}
menu_Window->addMenu(scrollPosMenu);
/* Keyboard cursor shape */
keyboardCursorShape = new QActionGroup(this);
QAction *block = new QAction(tr("&BlockCursor"), this);
QAction *underline = new QAction(tr("&UnderlineCursor"), this);
QAction *ibeam = new QAction(tr("&IBeamCursor"), this);
/* order of insertion is dep. on QTermWidget::KeyboardCursorShape enum */
keyboardCursorShape->addAction(block);
keyboardCursorShape->addAction(underline);
keyboardCursorShape->addAction(ibeam);
for(int i = 0; i < keyboardCursorShape->actions().size(); ++i)
keyboardCursorShape->actions().at(i)->setCheckable(true);
if( Properties::Instance()->keyboardCursorShape < keyboardCursorShape->actions().size() )
keyboardCursorShape->actions().at(Properties::Instance()->keyboardCursorShape)->setChecked(true);
connect(keyboardCursorShape, SIGNAL(triggered(QAction *)),
consoleTabulator, SLOT(changeKeyboardCursorShape(QAction *)) );
if (keyboardCursorShape == NULL) {
keyboardCursorShape = new QActionGroup(this);
QAction *block = new QAction(tr("&BlockCursor"), this);
QAction *underline = new QAction(tr("&UnderlineCursor"), this);
QAction *ibeam = new QAction(tr("&IBeamCursor"), this);
/* order of insertion is dep. on QTermWidget::KeyboardCursorShape enum */
keyboardCursorShape->addAction(block);
keyboardCursorShape->addAction(underline);
keyboardCursorShape->addAction(ibeam);
for(int i = 0; i < keyboardCursorShape->actions().size(); ++i)
keyboardCursorShape->actions().at(i)->setCheckable(true);
if( Properties::Instance()->keyboardCursorShape < keyboardCursorShape->actions().size() )
keyboardCursorShape->actions().at(Properties::Instance()->keyboardCursorShape)->setChecked(true);
connect(keyboardCursorShape, SIGNAL(triggered(QAction *)),
consoleTabulator, SLOT(changeKeyboardCursorShape(QAction *)) );
}
keyboardCursorShapeMenu = new QMenu(tr("&Keyboard Cursor Shape"), menu_Window);
keyboardCursorShapeMenu->setObjectName("keyboardCursorShapeMenu");
if (keyboardCursorShapeMenu == NULL) {
keyboardCursorShapeMenu = new QMenu(tr("&Keyboard Cursor Shape"), menu_Window);
keyboardCursorShapeMenu->setObjectName("keyboardCursorShapeMenu");
for(int i=0; i < keyboardCursorShape->actions().size(); ++i) {
keyboardCursorShapeMenu->addAction(keyboardCursorShape->actions().at(i));
for(int i=0; i < keyboardCursorShape->actions().size(); ++i) {
keyboardCursorShapeMenu->addAction(keyboardCursorShape->actions().at(i));
}
}
menu_Window->addMenu(keyboardCursorShapeMenu);
}
void MainWindow::setup_ContextMenu_Actions(QMenu* contextMenu) const
{
contextMenu->addAction(Properties::Instance()->actions[COPY_SELECTION]);
contextMenu->addAction(Properties::Instance()->actions[PASTE_CLIPBOARD]);
contextMenu->addAction(Properties::Instance()->actions[PASTE_SELECTION]);
contextMenu->addAction(Properties::Instance()->actions[ZOOM_IN]);
contextMenu->addAction(Properties::Instance()->actions[ZOOM_OUT]);
contextMenu->addAction(Properties::Instance()->actions[ZOOM_RESET]);
contextMenu->addSeparator();
contextMenu->addAction(Properties::Instance()->actions[CLEAR_TERMINAL]);
contextMenu->addAction(Properties::Instance()->actions[SPLIT_HORIZONTAL]);
contextMenu->addAction(Properties::Instance()->actions[SPLIT_VERTICAL]);
#warning TODO/FIXME: disable the action when there is only one terminal
contextMenu->addAction(Properties::Instance()->actions[SUB_COLLAPSE]);
contextMenu->addSeparator();
contextMenu->addAction(Properties::Instance()->actions[TOGGLE_MENU]);
contextMenu->addAction(Properties::Instance()->actions[PREFERENCES]);
}
void MainWindow::setupCustomDirs()
{
const QSettings settings;
@ -451,7 +486,7 @@ void MainWindow::on_consoleTabulator_currentChanged(int)
void MainWindow::toggleTabBar()
{
Properties::Instance()->tabBarless
= !Properties::Instance()->actions[SHOW_TAB_BAR]->isChecked();
= !actions[SHOW_TAB_BAR]->isChecked();
consoleTabulator->showHideTabBar();
}
@ -461,7 +496,7 @@ void MainWindow::toggleBorderless()
show();
setWindowState(Qt::WindowActive); /* don't loose focus on the window */
Properties::Instance()->borderless
= Properties::Instance()->actions[HIDE_WINDOW_BORDERS]->isChecked(); realign();
= actions[HIDE_WINDOW_BORDERS]->isChecked(); realign();
}
void MainWindow::toggleMenu()
@ -478,6 +513,12 @@ void MainWindow::showFullscreen(bool fullscreen)
setWindowState(windowState() & ~Qt::WindowFullScreen);
}
void MainWindow::toggleBookmarks()
{
m_bookmarksDock->toggleViewAction()->trigger();
}
void MainWindow::closeEvent(QCloseEvent *ev)
{
if (!Properties::Instance()->askOnExit
@ -550,6 +591,8 @@ void MainWindow::actProperties_triggered()
void MainWindow::propertiesChanged()
{
rebuildActions();
QApplication::setStyle(Properties::Instance()->guiStyle);
setWindowOpacity(1.0 - Properties::Instance()->appTransparency/100.0);
consoleTabulator->setTabPosition((QTabWidget::TabPosition)Properties::Instance()->tabsPos);
@ -560,7 +603,7 @@ void MainWindow::propertiesChanged()
m_bookmarksDock->setVisible(Properties::Instance()->useBookmarks
&& Properties::Instance()->bookmarksVisible);
m_bookmarksDock->toggleViewAction()->setVisible(Properties::Instance()->useBookmarks);
actions[TOGGLE_BOOKMARKS]->setVisible(Properties::Instance()->useBookmarks);
if (Properties::Instance()->useBookmarks)
{
@ -569,7 +612,6 @@ void MainWindow::propertiesChanged()
onCurrentTitleChanged(consoleTabulator->currentIndex());
Properties::Instance()->saveSettings();
realign();
}
@ -585,8 +627,9 @@ void MainWindow::realign()
geometry.moveCenter(desktop.center());
// do not use 0 here - we need to calculate with potential panel on top
geometry.setTop(desktop.top());
setGeometry(geometry);
if (geometry != this->geometry()) {
setGeometry(geometry);
}
}
}
@ -645,7 +688,12 @@ bool MainWindow::event(QEvent *event)
void MainWindow::newTerminalWindow()
{
MainWindow *w = new MainWindow(m_initWorkDir, m_initShell, false);
TerminalConfig cfg;
TermWidgetHolder *ch = consoleTabulator->terminalHolder();
if (ch)
cfg.provideCurrentDirectory(ch->currentTerminal()->impl()->workingDirectory());
MainWindow *w = new MainWindow(cfg, false);
w->show();
}
@ -662,6 +710,7 @@ void MainWindow::bookmarksDock_visibilityChanged(bool visible)
void MainWindow::addNewTab()
{
TerminalConfig cfg;
if (Properties::Instance()->terminalsPreset == 3)
consoleTabulator->preset4Terminals();
else if (Properties::Instance()->terminalsPreset == 2)
@ -669,7 +718,8 @@ void MainWindow::addNewTab()
else if (Properties::Instance()->terminalsPreset == 1)
consoleTabulator->preset2Horizontal();
else
consoleTabulator->addNewTab();
consoleTabulator->addNewTab(cfg);
updateDisabledActions();
}
void MainWindow::onCurrentTitleChanged(int index)
@ -695,7 +745,7 @@ bool MainWindow::hasMultipleSubterminals()
return consoleTabulator->terminalHolder()->findChildren<TermWidget*>().count() > 1;
}
void MainWindow::aboutToShowActionsMenu()
void MainWindow::updateDisabledActions()
{
const QList<QAction*> actions = menu_Actions->actions();
for (QAction *action : actions) {
@ -705,3 +755,39 @@ void MainWindow::aboutToShowActionsMenu()
}
}
}
QMap< QString, QAction * >& MainWindow::leaseActions() {
return actions;
}
#ifdef HAVE_QDBUS
QDBusObjectPath MainWindow::getActiveTab()
{
return qobject_cast<TermWidgetHolder*>(consoleTabulator->currentWidget())->getDbusPath();
}
QList<QDBusObjectPath> MainWindow::getTabs()
{
QList<QDBusObjectPath> tabs;
for (int i = 0; i<consoleTabulator->count(); ++i)
{
tabs.push_back(qobject_cast<TermWidgetHolder*>(consoleTabulator->widget(i))->getDbusPath());
}
return tabs;
}
QDBusObjectPath MainWindow::newTab(const QHash<QString,QVariant> &termArgs)
{
TerminalConfig cfg = TerminalConfig::fromDbus(termArgs);
int idx = consoleTabulator->addNewTab(cfg);
return qobject_cast<TermWidgetHolder*>(consoleTabulator->widget(idx))->getDbusPath();
}
void MainWindow::closeWindow()
{
close();
}
#endif

@ -22,22 +22,34 @@
#include "ui_qterminal.h"
#include <QMainWindow>
#include <QAction>
#include "qxtglobalshortcut.h"
#include "terminalconfig.h"
#include "dbusaddressable.h"
class QToolButton;
class MainWindow : public QMainWindow , private Ui::mainWindow
class MainWindow : public QMainWindow, private Ui::mainWindow, public DBusAddressable
{
Q_OBJECT
public:
MainWindow(const QString& work_dir, const QString& command,
MainWindow(TerminalConfig& cfg,
bool dropMode,
QWidget * parent = 0, Qt::WindowFlags f = 0);
~MainWindow();
bool dropMode() const { return m_dropMode; }
void setup_ContextMenu_Actions(QMenu* contextMenu) const;
bool dropMode() { return m_dropMode; }
QMap<QString, QAction*> & leaseActions();
#ifdef HAVE_QDBUS
QDBusObjectPath getActiveTab();
QList<QDBusObjectPath> getTabs();
QDBusObjectPath newTab(const QHash<QString,QVariant> &termArgs);
void closeWindow();
#endif
protected:
bool event(QEvent* event);
@ -46,16 +58,26 @@ private:
QActionGroup *tabPosition, *scrollBarPosition, *keyboardCursorShape;
QMenu *tabPosMenu, *scrollPosMenu, *keyboardCursorShapeMenu;
QString m_initWorkDir;
QString m_initShell;
// A parent object for QObjects that are created dynamically based on settings
// Used to simplify the setting cleanup on reconfiguration: deleting settingOwner frees all related QObjects
QWidget *settingOwner;
QMenu *presetsMenu;
TerminalConfig m_config;
QDockWidget *m_bookmarksDock;
void setup_Action(const char *name, QAction *action, const char *defaultShortcut, const QObject *receiver,
const char *slot, QMenu *menu = NULL, const QVariant &data = QVariant());
QMap< QString, QAction * > actions;
void rebuildActions();
void setup_FileMenu_Actions();
void setup_ActionsMenu_Actions();
void setup_ViewMenu_Actions();
void setup_ContextMenu_Actions();
void setupCustomDirs();
void closeEvent(QCloseEvent*);
@ -70,6 +92,10 @@ private:
bool hasMultipleTabs();
bool hasMultipleSubterminals();
public slots:
void showHide();
void updateDisabledActions();
private slots:
void on_consoleTabulator_currentChanged(int);
void propertiesChanged();
@ -77,12 +103,12 @@ private slots:
void actProperties_triggered();
void updateActionGroup(QAction *);
void toggleBookmarks();
void toggleBorderless();
void toggleTabBar();
void toggleMenu();
void showFullscreen(bool fullscreen);
void showHide();
void setKeepOpen(bool value);
void find();
@ -93,6 +119,5 @@ private slots:
void addNewTab();
void onCurrentTitleChanged(int index);
void aboutToShowActionsMenu();
};
#endif //MAINWINDOW_H

@ -0,0 +1,23 @@
<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
<node>
<interface name="org.lxqt.QTerminal.Process">
<method name="getWindows">
<arg name="windows" type="ao" direction="out"/>
</method>
<method name="newWindow">
<annotation name="org.qtproject.QtDBus.QtTypeName.In0" value="QHash&lt;QString,QVariant&gt;"/>
<arg name="termArgs" type="a{sv}" direction="in"/>
</method>
<method name="getActiveWindow">
<arg name="window" type="o" direction="out"/>
</method>
<method name="isDropMode">
<arg name="isDropMode" type="b" direction="out"/>
</method>
<method name="toggleDropdown">
<arg name="success" type="b" direction="out"/>
</method>
</interface>
</node>

@ -0,0 +1,17 @@
<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
<node>
<interface name="org.lxqt.QTerminal.Tab">
<method name="getActiveTerminal">
<arg name="terminal" type="o" direction="out"/>
</method>
<method name="getTerminals">
<arg name="terminals" type="ao" direction="out"/>
</method>
<method name="getWindow">
<arg name="window" type="o" direction="out"/>
</method>
<method name="closeTab"/>
</interface>
</node>

@ -0,0 +1,24 @@
<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
<node>
<interface name="org.lxqt.QTerminal.Terminal">
<method name="splitVertical">
<annotation name="org.qtproject.QtDBus.QtTypeName.In0" value="QHash&lt;QString,QVariant&gt;"/>
<arg name="termArgs" type="a{sv}" direction="in"/>
<arg name="newTerminal" type="o" direction="out"/>
</method>
<method name="splitHorizontal">
<annotation name="org.qtproject.QtDBus.QtTypeName.In0" value="QHash&lt;QString,QVariant&gt;"/>
<arg name="termArgs" type="a{sv}" direction="in"/>
<arg name="newTerminal" type="o" direction="out"/>
</method>
<method name="getTab">
<arg name="tab" type="o" direction="out"/>
</method>
<method name="sendText">
<arg name="text" type="s" direction="in"/>
</method>
<method name="closeTerminal"/>
</interface>
</node>

@ -0,0 +1,20 @@
<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
<node>
<interface name="org.lxqt.QTerminal.Window">
<method name="getTabs">
<arg name="tabs" type="ao" direction="out"/>
</method>
<method name="getActiveTab">
<arg name="tab" type="o" direction="out"/>
</method>
<method name="newTab">
<annotation name="org.qtproject.QtDBus.QtTypeName.In0" value="QHash&lt;QString,QVariant&gt;"/>
<arg name="termArgs" type="a{sv}" direction="in"/>
<arg name="newTerminal" type="o" direction="out"/>
</method>
<method name="closeWindow"/>
<method name="activateWindow"/>
</interface>
</node>

@ -17,10 +17,12 @@
***************************************************************************/
#include <qtermwidget.h>
#include <assert.h>
#include "properties.h"
#include "config.h"
#include "mainwindow.h"
#include "qterminalapp.h"
Properties * Properties::m_instance = 0;
@ -45,7 +47,6 @@ Properties::Properties(const QString& filename)
Properties::~Properties()
{
qDebug("Properties destructor called");
saveSettings();
delete m_settings;
m_instance = 0;
}
@ -69,17 +70,10 @@ void Properties::loadSettings()
highlightCurrentTerminal = m_settings->value("highlightCurrentTerminal", true).toBool();
font = qvariant_cast<QFont>(m_settings->value("font", defaultFont()));
m_settings->beginGroup("Shortcuts");
QStringList keys = m_settings->childKeys();
foreach( QString key, keys )
{
QKeySequence sequence = QKeySequence( m_settings->value( key ).toString() );
if( Properties::Instance()->actions.contains( key ) )
Properties::Instance()->actions[ key ]->setShortcut( sequence );
}
m_settings->endGroup();
font = QFont(qvariant_cast<QString>(m_settings->value("fontFamily", defaultFont().family())),
qvariant_cast<int>(m_settings->value("fontSize", defaultFont().pointSize())));
//Legacy font setting
font = qvariant_cast<QFont>(m_settings->value("font", font));
mainWindowSize = m_settings->value("MainWindow/size").toSize();
mainWindowPosition = m_settings->value("MainWindow/pos").toPoint();
@ -127,7 +121,8 @@ void Properties::loadSettings()
// bookmarks
useBookmarks = m_settings->value("UseBookmarks", false).toBool();
bookmarksVisible = m_settings->value("BookmarksVisible", true).toBool();
bookmarksFile = m_settings->value("BookmarksFile", QFileInfo(m_settings->fileName()).canonicalPath()+"/qterminal_bookmarks.xml").toString();
const QString s = QFileInfo(m_settings->fileName()).canonicalPath() + QString::fromLatin1("/qterminal_bookmarks.xml");
bookmarksFile = m_settings->value("BookmarksFile", s).toString();
terminalsPreset = m_settings->value("TerminalsPreset", 0).toInt();
@ -141,6 +136,9 @@ void Properties::loadSettings()
changeWindowTitle = m_settings->value("ChangeWindowTitle", true).toBool();
changeWindowIcon = m_settings->value("ChangeWindowIcon", true).toBool();
confirmMultilinePaste = m_settings->value("ConfirmMultilinePaste", false).toBool();
trimPastedTrailingNewlines = m_settings->value("TrimPastedTrailingNewlines", false).toBool();
}
void Properties::saveSettings()
@ -148,15 +146,21 @@ void Properties::saveSettings()
m_settings->setValue("guiStyle", guiStyle);
m_settings->setValue("colorScheme", colorScheme);
m_settings->setValue("highlightCurrentTerminal", highlightCurrentTerminal);
m_settings->setValue("font", font);
m_settings->setValue("fontFamily", font.family());
m_settings->setValue("fontSize", font.pointSize());
//Clobber legacy setting
m_settings->remove("font");
m_settings->beginGroup("Shortcuts");
QMapIterator< QString, QAction * > it(actions);
MainWindow *mainWindow = QTerminalApp::Instance()->getWindowList()[0];
assert(mainWindow != NULL);
QMapIterator< QString, QAction * > it(mainWindow->leaseActions());
while( it.hasNext() )
{
it.next();
QStringList sequenceStrings;
foreach (QKeySequence shortcut, it.value()->shortcuts())
foreach (const QKeySequence &shortcut, it.value()->shortcuts())
sequenceStrings.append(shortcut.toString());
m_settings->setValue(it.key(), sequenceStrings.join('|'));
}
@ -218,6 +222,10 @@ void Properties::saveSettings()
m_settings->setValue("ChangeWindowTitle", changeWindowTitle);
m_settings->setValue("ChangeWindowIcon", changeWindowIcon);
m_settings->setValue("ConfirmMultilinePaste", confirmMultilinePaste);
m_settings->setValue("TrimPastedTrailingNewlines", trimPastedTrailingNewlines);
}
void Properties::migrate_settings()

@ -22,7 +22,6 @@
#include <QApplication>
#include <QtCore>
#include <QFont>
#include <QAction>
typedef QString Session;
@ -95,8 +94,8 @@ class Properties
bool changeWindowTitle;
bool changeWindowIcon;
QMap< QString, QAction * > actions;
bool confirmMultilinePaste;
bool trimPastedTrailingNewlines;
private:

@ -26,6 +26,7 @@
#include "properties.h"
#include "fontdialog.h"
#include "config.h"
#include "qterminalapp.h"
PropertiesDialog::PropertiesDialog(QWidget *parent)
@ -136,6 +137,10 @@ PropertiesDialog::PropertiesDialog(QWidget *parent)
changeWindowTitleCheckBox->setChecked(Properties::Instance()->changeWindowTitle);
changeWindowIconCheckBox->setChecked(Properties::Instance()->changeWindowIcon);
trimPastedTrailingNewlinesCheckBox->setChecked(Properties::Instance()->trimPastedTrailingNewlines);
confirmMultilinePasteCheckBox->setChecked(Properties::Instance()->confirmMultilinePaste);
}
@ -203,6 +208,9 @@ void PropertiesDialog::apply()
Properties::Instance()->changeWindowTitle = changeWindowTitleCheckBox->isChecked();
Properties::Instance()->changeWindowIcon = changeWindowIconCheckBox->isChecked();
Properties::Instance()->trimPastedTrailingNewlines = trimPastedTrailingNewlinesCheckBox->isChecked();
Properties::Instance()->confirmMultilinePaste = confirmMultilinePasteCheckBox->isChecked();
emit propertiesChanged();
}
@ -226,7 +234,7 @@ void PropertiesDialog::changeFontButton_clicked()
void PropertiesDialog::chooseBackgroundImageButton_clicked()
{
QString filename = QFileDialog::getOpenFileName(
this, tr("Open or create bookmarks file"),
this, tr("Choose a background image"),
QString(), tr("Images (*.bmp *.png *.xpm *.jpg)"));
if (!filename.isNull())
backgroundImageLineEdit->setText(filename);
@ -234,7 +242,8 @@ void PropertiesDialog::chooseBackgroundImageButton_clicked()
void PropertiesDialog::saveShortcuts()
{
QList< QString > shortcutKeys = Properties::Instance()->actions.keys();
QMap<QString, QAction*> actions = QTerminalApp::Instance()->getWindowList()[0]->leaseActions();
QList< QString > shortcutKeys = actions.keys();
int shortcutCount = shortcutKeys.count();
shortcutsWidget->setRowCount( shortcutCount );
@ -242,7 +251,7 @@ void PropertiesDialog::saveShortcuts()
for( int x=0; x < shortcutCount; x++ )
{
QString keyValue = shortcutKeys.at(x);
QAction *keyAction = Properties::Instance()->actions[keyValue];
QAction *keyAction = actions[keyValue];
QTableWidgetItem *item = shortcutsWidget->item(x, 1);
QKeySequence sequence = QKeySequence(item->text());
@ -253,11 +262,13 @@ void PropertiesDialog::saveShortcuts()
shortcuts.append(QKeySequence(sequenceString));
keyAction->setShortcuts(shortcuts);
}
Properties::Instance()->saveSettings();
}
void PropertiesDialog::setupShortcuts()
{
QList< QString > shortcutKeys = Properties::Instance()->actions.keys();
QMap<QString, QAction*> actions = QTerminalApp::Instance()->getWindowList()[0]->leaseActions();
QList< QString > shortcutKeys = actions.keys();
int shortcutCount = shortcutKeys.count();
shortcutsWidget->setRowCount( shortcutCount );
@ -265,10 +276,10 @@ void PropertiesDialog::setupShortcuts()
for( int x=0; x < shortcutCount; x++ )
{
QString keyValue = shortcutKeys.at(x);
QAction *keyAction = Properties::Instance()->actions[keyValue];
QAction *keyAction = actions[keyValue];
QStringList sequenceStrings;
foreach (QKeySequence shortcut, keyAction->shortcuts())
foreach (const QKeySequence &shortcut, keyAction->shortcuts())
sequenceStrings.append(shortcut.toString());
QTableWidgetItem *itemName = new QTableWidgetItem( tr(keyValue.toStdString().c_str()) );

@ -0,0 +1,62 @@
#ifndef QTERMINALAPP_H
#define QTERMINALAPP_H
#include <QApplication>
#ifdef HAVE_QDBUS
#include <QtDBus/QtDBus>
#endif
#include "mainwindow.h"
class QTerminalApp : public QApplication
{
Q_OBJECT
public:
MainWindow *newWindow(bool dropMode, TerminalConfig &cfg);
QList<MainWindow*> getWindowList();
void addWindow(MainWindow *window);
void removeWindow(MainWindow *window);
static QTerminalApp *Instance(int &argc, char **argv);
static QTerminalApp *Instance();
QString &getWorkingDirectory();
void setWorkingDirectory(const QString &wd);
#ifdef HAVE_QDBUS
void registerOnDbus();
QList<QDBusObjectPath> getWindows();
QDBusObjectPath newWindow(const QHash<QString,QVariant> &termArgs);
QDBusObjectPath getActiveWindow();
bool isDropMode();
bool toggleDropdown();
#endif
static void cleanup();
private:
QString m_workDir;
QList<MainWindow *> m_windowList;
static QTerminalApp *m_instance;
QTerminalApp(int &argc, char **argv);
~QTerminalApp(){};
};
template <class T> T* findParent(QObject *child)
{
QObject *maybeT = child;
while (true)
{
if (maybeT == NULL)
{
return NULL;
}
T *holder = qobject_cast<T*>(maybeT);
if (holder)
return holder;
maybeT = maybeT->parent();
}
}
#endif

@ -21,10 +21,12 @@
#include <QMouseEvent>
#include <QMenu>
#include "mainwindow.h"
#include "termwidgetholder.h"
#include "tabwidget.h"
#include "config.h"
#include "properties.h"
#include "qterminalapp.h"
#define TAB_INDEX_PROPERTY "tab_index"
@ -59,26 +61,17 @@ TermWidgetHolder * TabWidget::terminalHolder()
return reinterpret_cast<TermWidgetHolder*>(widget(currentIndex()));
}
void TabWidget::setWorkDirectory(const QString& dir)
{
this->work_dir = dir;
}
int TabWidget::addNewTab(const QString & shell_program)
int TabWidget::addNewTab(TerminalConfig config)
{
tabNumerator++;
QString label = QString(tr("Shell No. %1")).arg(tabNumerator);
TermWidgetHolder *ch = terminalHolder();
QString cwd(work_dir);
if (Properties::Instance()->useCWD && ch)
{
cwd = ch->currentTerminal()->impl()->workingDirectory();
if (cwd.isEmpty())
cwd = work_dir;
}
if (ch)
config.provideCurrentDirectory(ch->currentTerminal()->impl()->workingDirectory());
TermWidgetHolder *console = new TermWidgetHolder(cwd, shell_program, this);
TermWidgetHolder *console = new TermWidgetHolder(config, this);
console->setWindowTitle(label);
connect(console, SIGNAL(finished()), SLOT(removeFinished()));
connect(console, SIGNAL(lastTerminalClosed()), this, SLOT(removeFinished()));
@ -96,29 +89,40 @@ int TabWidget::addNewTab(const QString & shell_program)
return index;
}
void TabWidget::switchNextSubterminal()
void TabWidget::switchLeftSubterminal()
{
terminalHolder()->switchNextSubterminal();
terminalHolder()->directionalNavigation(NavigationDirection::Left);
}
void TabWidget::switchPrevSubterminal()
void TabWidget::switchRightSubterminal()
{
terminalHolder()->switchPrevSubterminal();
terminalHolder()->directionalNavigation(NavigationDirection::Right);
}
void TabWidget::switchTopSubterminal() {
terminalHolder()->directionalNavigation(NavigationDirection::Top);
}
void TabWidget::switchBottomSubterminal() {
terminalHolder()->directionalNavigation(NavigationDirection::Bottom);
}
void TabWidget::splitHorizontally()
{
terminalHolder()->splitHorizontal(terminalHolder()->currentTerminal());
findParent<MainWindow>(this)->updateDisabledActions();
}
void TabWidget::splitVertically()
{
terminalHolder()->splitVertical(terminalHolder()->currentTerminal());
findParent<MainWindow>(this)->updateDisabledActions();
}
void TabWidget::splitCollapse()
{
terminalHolder()->splitCollapse(terminalHolder()->currentTerminal());
findParent<MainWindow>(this)->updateDisabledActions();
}
void TabWidget::copySelection()
@ -206,10 +210,11 @@ void TabWidget::renameTabsAfterRemove()
void TabWidget::contextMenuEvent(QContextMenuEvent *event)
{
QMenu menu(this);
QMap< QString, QAction * > actions = findParent<MainWindow>(this)->leaseActions();
QAction *close = menu.addAction(QIcon::fromTheme("document-close"), tr("Close session"));
QAction *rename = menu.addAction(Properties::Instance()->actions[RENAME_SESSION]->text());
rename->setShortcut(Properties::Instance()->actions[RENAME_SESSION]->shortcut());
QAction *rename = menu.addAction(actions[RENAME_SESSION]->text());
rename->setShortcut(actions[RENAME_SESSION]->shortcut());
rename->blockSignals(true);
int tabIndex = tabBar()->tabAt(event->pos());
@ -230,7 +235,10 @@ bool TabWidget::eventFilter(QObject *obj, QEvent *event)
// clicks on free space - open new tab
int index = tabBar()->tabAt(e->pos());
if (index == -1)
addNewTab();
{
TerminalConfig defaultConfig;
addNewTab(defaultConfig);
}
else
renameSession(index);
return true;
@ -299,6 +307,7 @@ int TabWidget::switchToRight()
setCurrentIndex(next_pos);
else
setCurrentIndex(0);
findParent<MainWindow>(this)->updateDisabledActions();
return currentIndex();
}
@ -309,6 +318,7 @@ int TabWidget::switchToLeft()
setCurrentIndex(count() - 1);
else
setCurrentIndex(previous_pos);
findParent<MainWindow>(this)->updateDisabledActions();
return currentIndex();
}
@ -426,33 +436,36 @@ void TabWidget::loadSession()
void TabWidget::preset2Horizontal()
{
int ix = TabWidget::addNewTab();
TerminalConfig defaultConfig;
int ix = TabWidget::addNewTab(defaultConfig);
TermWidgetHolder* term = reinterpret_cast<TermWidgetHolder*>(widget(ix));
term->splitHorizontal(term->currentTerminal());
// switch to the 1st terminal
term->switchNextSubterminal();
term->directionalNavigation(NavigationDirection::Left);
}
void TabWidget::preset2Vertical()
{
int ix = TabWidget::addNewTab();
TerminalConfig defaultConfig;
int ix = TabWidget::addNewTab(defaultConfig);
TermWidgetHolder* term = reinterpret_cast<TermWidgetHolder*>(widget(ix));
term->splitVertical(term->currentTerminal());
// switch to the 1st terminal
term->switchNextSubterminal();
term->directionalNavigation(NavigationDirection::Left);
}
void TabWidget::preset4Terminals()
{
int ix = TabWidget::addNewTab();
TerminalConfig defaultConfig;
int ix = TabWidget::addNewTab(defaultConfig);
TermWidgetHolder* term = reinterpret_cast<TermWidgetHolder*>(widget(ix));
term->splitVertical(term->currentTerminal());
term->splitHorizontal(term->currentTerminal());
term->switchNextSubterminal();
term->switchNextSubterminal();
term->directionalNavigation(NavigationDirection::Left);
term->splitHorizontal(term->currentTerminal());
// switch to the 1st terminal
term->switchNextSubterminal();
term->directionalNavigation(NavigationDirection::Top);
}
void TabWidget::showHideTabBar()

@ -21,7 +21,14 @@
#include <QTabWidget>
#include <QMap>
#include <QAction>
#ifdef HAVE_QDBUS
#include <QtDBus/QtDBus>
#include "dbusaddressable.h"
#endif
#include "terminalconfig.h"
#include "properties.h"
class TermWidgetHolder;
@ -40,7 +47,7 @@ public:
void showHideTabBar();
public slots:
int addNewTab(const QString& shell_program = QString());
int addNewTab(TerminalConfig cfg);
void removeTab(int);
void removeCurrentTab();
int switchToRight();
@ -50,10 +57,12 @@ public slots:
void moveRight();
void renameSession(int);
void renameCurrentSession();
void setWorkDirectory(const QString&);
void switchNextSubterminal();
void switchPrevSubterminal();
void switchLeftSubterminal();
void switchRightSubterminal();
void switchTopSubterminal();
void switchBottomSubterminal();
void splitHorizontally();
void splitVertically();
void splitCollapse();
@ -100,7 +109,6 @@ protected slots:
private:
int tabNumerator;
QString work_dir;
/* re-order naming of the tabs then removeCurrentTab() */
void renameTabsAfterRemove();
};

@ -0,0 +1,105 @@
#include <QHash>
#include <QString>
#include "qterminalapp.h"
#include "terminalconfig.h"
#include "properties.h"
#include "termwidget.h"
TerminalConfig::TerminalConfig(const QString & wdir, const QString & shell)
{
m_workingDirectory = wdir;
m_shell = shell;
}
TerminalConfig::TerminalConfig()
{
}
TerminalConfig::TerminalConfig(const TerminalConfig &cfg)
: m_currentDirectory(cfg.m_currentDirectory),
m_workingDirectory(cfg.m_workingDirectory),
m_shell(cfg.m_shell) {}
QString TerminalConfig::getWorkingDirectory()
{
if (!m_workingDirectory.isEmpty())
return m_workingDirectory;
if (Properties::Instance()->useCWD && !m_currentDirectory.isEmpty())
return m_currentDirectory;
return QTerminalApp::Instance()->getWorkingDirectory();
}
QString TerminalConfig::getShell()
{
if (!m_shell.isEmpty())
return m_shell;
if (!Properties::Instance()->shell.isEmpty())
return Properties::Instance()->shell;
QByteArray envShell = qgetenv("SHELL");
if (envShell.constData() != NULL)
{
QString shellString = QString(envShell);
if (!shellString.isEmpty())
return shellString;
}
return QString();
}
void TerminalConfig::setWorkingDirectory(const QString &val)
{
m_workingDirectory = val;
}
void TerminalConfig::setShell(const QString &val)
{
m_shell = val;
}
void TerminalConfig::provideCurrentDirectory(const QString &val)
{
m_currentDirectory = val;
}
#if HAVE_QDBUS
#define DBUS_ARG_WORKDIR "workingDirectory"
#define DBUS_ARG_SHELL "shell"
TerminalConfig TerminalConfig::fromDbus(const QHash<QString,QVariant> &termArgsConst, TermWidget *toSplit)
{
QHash<QString,QVariant> termArgs(termArgsConst);
if (toSplit != NULL && !termArgs.contains(DBUS_ARG_WORKDIR))
{
termArgs[DBUS_ARG_WORKDIR] = QVariant(toSplit->impl()->workingDirectory());
}
return TerminalConfig::fromDbus(termArgs);
}
static QString variantToString(QVariant variant, QString &defaultVal)
{
if (variant.type() == QVariant::String)
return qvariant_cast<QString>(variant);
return defaultVal;
}
TerminalConfig TerminalConfig::fromDbus(const QHash<QString,QVariant> &termArgs)
{
QString wdir("");
QString shell(Properties::Instance()->shell);
if (termArgs.contains(DBUS_ARG_WORKDIR))
{
wdir = variantToString(termArgs[DBUS_ARG_WORKDIR], wdir);
}
if (termArgs.contains(DBUS_ARG_SHELL)) {
shell = variantToString(termArgs[DBUS_ARG_SHELL], shell);
}
return TerminalConfig(wdir, shell);
}
#endif

@ -0,0 +1,38 @@
#ifndef TERMINALCONFIG_H
#define TERMINALCONFIG_H
#include <QHash>
#include <QString>
#include <QVariant>
class TermWidget;
class TerminalConfig
{
public:
TerminalConfig(const QString & wdir, const QString & shell);
TerminalConfig(const TerminalConfig &cfg);
TerminalConfig();
QString getWorkingDirectory();
QString getShell();
void setWorkingDirectory(const QString &val);
void setShell(const QString &val);
void provideCurrentDirectory(const QString &val);
#ifdef HAVE_QDBUS
static TerminalConfig fromDbus(const QHash<QString,QVariant> &termArgs);
static TerminalConfig fromDbus(const QHash<QString,QVariant> &termArgs, TermWidget *toSplit);
#endif
private:
// True when
QString m_currentDirectory;
QString m_workingDirectory;
QString m_shell;
};
#endif

@ -20,16 +20,28 @@
#include <QVBoxLayout>
#include <QPainter>
#include <QDesktopServices>
#include <QMessageBox>
#include <QAbstractButton>
#include <QMouseEvent>
#include <assert.h>
#ifdef HAVE_QDBUS
#include <QtDBus/QtDBus>
#include "termwidgetholder.h"
#include "terminaladaptor.h"
#endif
#include "mainwindow.h"
#include "termwidget.h"
#include "config.h"
#include "properties.h"
#include "mainwindow.h"
#include "qterminalapp.h"
static int TermWidgetCount = 0;
TermWidgetImpl::TermWidgetImpl(const QString & wdir, const QString & shell, QWidget * parent)
TermWidgetImpl::TermWidgetImpl(TerminalConfig &cfg, QWidget * parent)
: QTermWidget(0, parent)
{
TermWidgetCount++;
@ -43,17 +55,12 @@ TermWidgetImpl::TermWidgetImpl(const QString & wdir, const QString & shell, QWid
setHistorySize(5000);
if (!wdir.isNull())
setWorkingDirectory(wdir);
setWorkingDirectory(cfg.getWorkingDirectory());
if (shell.isNull())
{
if (!Properties::Instance()->shell.isNull())
setShellProgram(Properties::Instance()->shell);
}
else
QString shell = cfg.getShell();
if (!shell.isEmpty())
{
qDebug() << "Settings custom shell program:" << shell;
qDebug() << "Shell program:" << shell;
QStringList parts = shell.split(QRegExp("\\s+"), QString::SkipEmptyParts);
qDebug() << parts;
setShellProgram(parts.at(0));
@ -109,14 +116,14 @@ void TermWidgetImpl::propertiesChanged()
switch(Properties::Instance()->keyboardCursorShape) {
case 1:
setKeyboardCursorShape(QTermWidget::UnderlineCursor);
setKeyboardCursorShape(QTermWidget::KeyboardCursorShape::UnderlineCursor);
break;
case 2:
setKeyboardCursorShape(QTermWidget::IBeamCursor);
setKeyboardCursorShape(QTermWidget::KeyboardCursorShape::IBeamCursor);
break;
default:
case 0:
setKeyboardCursorShape(QTermWidget::BlockCursor);
setKeyboardCursorShape(QTermWidget::KeyboardCursorShape::BlockCursor);
break;
}
@ -125,20 +132,36 @@ void TermWidgetImpl::propertiesChanged()
void TermWidgetImpl::customContextMenuCall(const QPoint & pos)
{
QMenu* contextMenu = new QMenu(this);
QMenu menu;
QMap<QString, QAction*> actions = findParent<MainWindow>(this)->leaseActions();
QList<QAction*> actions = filterActions(pos);
for (auto& action : actions)
QList<QAction*> extraActions = filterActions(pos);
for (auto& action : extraActions)
{
contextMenu->addAction(action);
menu.addAction(action);
}
contextMenu->addSeparator();
const MainWindow *main = qobject_cast<MainWindow*>(window());
main->setup_ContextMenu_Actions(contextMenu);
if (!actions.isEmpty())
{
menu.addSeparator();
}
contextMenu->exec(mapToGlobal(pos));
menu.addAction(actions[COPY_SELECTION]);
menu.addAction(actions[PASTE_CLIPBOARD]);
menu.addAction(actions[PASTE_SELECTION]);
menu.addAction(actions[ZOOM_IN]);
menu.addAction(actions[ZOOM_OUT]);
menu.addAction(actions[ZOOM_RESET]);
menu.addSeparator();
menu.addAction(actions[CLEAR_TERMINAL]);
menu.addAction(actions[SPLIT_HORIZONTAL]);
menu.addAction(actions[SPLIT_VERTICAL]);
// warning TODO/FIXME: disable the action when there is only one terminal
menu.addAction(actions[SUB_COLLAPSE]);
menu.addSeparator();
menu.addAction(actions[TOGGLE_MENU]);
menu.addAction(actions[PREFERENCES]);
menu.exec(mapToGlobal(pos));
}
void TermWidgetImpl::zoomIn()
@ -171,17 +194,106 @@ void TermWidgetImpl::activateUrl(const QUrl & url, bool fromContextMenu) {
}
}
TermWidget::TermWidget(const QString & wdir, const QString & shell, QWidget * parent)
: QWidget(parent)
void TermWidgetImpl::pasteSelection()
{
paste(QClipboard::Selection);
}
void TermWidgetImpl::pasteClipboard()
{
paste(QClipboard::Clipboard);
}
void TermWidgetImpl::paste(QClipboard::Mode mode)
{
// Paste Clipboard by simulating keypress events
QString text = QApplication::clipboard()->text(mode);
if ( ! text.isEmpty() )
{
text.replace("\r\n", "\n");
text.replace('\n', '\r');
QString trimmedTrailingNl(text);
trimmedTrailingNl.replace(QRegExp("\\r+$"), "");
bool isMultiline = trimmedTrailingNl.contains('\r');
if (!isMultiline && Properties::Instance()->trimPastedTrailingNewlines)
{
text = trimmedTrailingNl;
}
if (Properties::Instance()->confirmMultilinePaste)
{
if (text.contains('\r') && Properties::Instance()->confirmMultilinePaste)
{
QMessageBox confirmation(this);
confirmation.setWindowTitle(tr("Paste multiline text"));
confirmation.setText(tr("Are you sure you want to paste this text?"));
confirmation.setDetailedText(text);
confirmation.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
// Click "Show details..." to show those by default
foreach( QAbstractButton * btn, confirmation.buttons() )
{
if (confirmation.buttonRole(btn) == QMessageBox::ActionRole && btn->text() == QMessageBox::tr("Show Details..."))
{
btn->clicked();
break;
}
}
confirmation.setDefaultButton(QMessageBox::Yes);
confirmation.exec();
if (confirmation.standardButton(confirmation.clickedButton()) != QMessageBox::Yes)
{
return;
}
}
}
/* TODO: Support bracketedPasteMode
if (bracketedPasteMode())
{
text.prepend("\e[200~");
text.append("\e[201~");
}*/
sendText(text);
}
}
bool TermWidget::eventFilter(QObject * obj, QEvent * ev)
{
if (ev->type() == QEvent::MouseButtonPress)
{
QMouseEvent *mev = (QMouseEvent *)ev;
if ( mev->button() == Qt::MidButton )
{
impl()->pasteSelection();
return true;
}
}
return false;
}
TermWidget::TermWidget(TerminalConfig &cfg, QWidget * parent)
: QWidget(parent),
DBusAddressable("/terminals")
{
#ifdef HAVE_QDBUS
registerAdapter<TerminalAdaptor, TermWidget>(this);
#endif
m_border = palette().color(QPalette::Window);
m_term = new TermWidgetImpl(wdir, shell, this);
m_term = new TermWidgetImpl(cfg, this);
setFocusProxy(m_term);
m_layout = new QVBoxLayout;
setLayout(m_layout);
m_layout->addWidget(m_term);
foreach (QObject *o, m_term->children())
{
// Find TerminalDisplay
if (!o->isWidgetType() || qobject_cast<QWidget*>(o)->isHidden())
continue;
o->installEventFilter(this);
}
propertiesChanged();
@ -216,10 +328,52 @@ void TermWidget::term_termLostFocus()
void TermWidget::paintEvent (QPaintEvent *)
{
QPainter p(this);
QPen pen(m_border);
pen.setWidth(30);
pen.setBrush(m_border);
p.setPen(pen);
p.drawRect(0, 0, width()-1, height()-1);
if (Properties::Instance()->highlightCurrentTerminal)
{
QPainter p(this);
QPen pen(m_border);
pen.setWidth(3);
pen.setBrush(m_border);
p.setPen(pen);
p.drawRect(0, 0, width()-1, height()-1);
}
}
#if HAVE_QDBUS
QDBusObjectPath TermWidget::splitHorizontal(const QHash<QString,QVariant> &termArgs)
{
TermWidgetHolder *holder = findParent<TermWidgetHolder>(this);
assert(holder != NULL);
TerminalConfig cfg = TerminalConfig::fromDbus(termArgs, this);
return holder->split(this, Qt::Horizontal, cfg)->getDbusPath();
}
QDBusObjectPath TermWidget::splitVertical(const QHash<QString,QVariant> &termArgs)
{
TermWidgetHolder *holder = findParent<TermWidgetHolder>(this);
assert(holder != NULL);
TerminalConfig cfg = TerminalConfig::fromDbus(termArgs, this);
return holder->split(this, Qt::Vertical, cfg)->getDbusPath();
}
QDBusObjectPath TermWidget::getTab()
{
return findParent<TermWidgetHolder>(this)->getDbusPath();
}
void TermWidget::closeTerminal()
{
TermWidgetHolder *holder = findParent<TermWidgetHolder>(this);
holder->splitCollapse(this);
}
void TermWidget::sendText(const QString text)
{
if (impl())
{
impl()->sendText(text);
}
}
#endif

@ -20,9 +20,11 @@
#define TERMWIDGET_H
#include <qtermwidget.h>
#include "terminalconfig.h"
#include <QClipboard>
#include <QAction>
#include "dbusaddressable.h"
class TermWidgetImpl : public QTermWidget
{
@ -32,8 +34,9 @@ class TermWidgetImpl : public QTermWidget
public:
TermWidgetImpl(const QString & wdir, const QString & shell=QString(), QWidget * parent=0);
TermWidgetImpl(TerminalConfig &cfg, QWidget * parent=0);
void propertiesChanged();
void paste(QClipboard::Mode mode);
signals:
void renameSession();
@ -43,6 +46,8 @@ class TermWidgetImpl : public QTermWidget
void zoomIn();
void zoomOut();
void zoomReset();
void pasteSelection();
void pasteClipboard();
private slots:
void customContextMenuCall(const QPoint & pos);
@ -50,7 +55,7 @@ class TermWidgetImpl : public QTermWidget
};
class TermWidget : public QWidget
class TermWidget : public QWidget, public DBusAddressable
{
Q_OBJECT
@ -59,13 +64,21 @@ class TermWidget : public QWidget
QColor m_border;
public:
TermWidget(const QString & wdir, const QString & shell=QString(), QWidget * parent=0);
TermWidget(TerminalConfig &cfg, QWidget * parent=0);
void propertiesChanged();
QStringList availableKeyBindings() { return m_term->availableKeyBindings(); }
TermWidgetImpl * impl() { return m_term; }
#ifdef HAVE_QDBUS
QDBusObjectPath splitHorizontal(const QHash<QString,QVariant> &termArgs);
QDBusObjectPath splitVertical(const QHash<QString,QVariant> &termArgs);
QDBusObjectPath getTab();
void sendText(const QString text);
void closeTerminal();
#endif
signals:
void finished();
void renameSession();
@ -80,6 +93,7 @@ class TermWidget : public QWidget
protected:
void paintEvent (QPaintEvent * event);
bool eventFilter(QObject * obj, QEvent * evt) override;
private slots:
void term_termGetFocus();

@ -20,18 +20,31 @@
#include <QSplitter>
#include <QInputDialog>
#ifdef HAVE_QDBUS
#include <QtDBus/QtDBus>
#include "tabadaptor.h"
#endif
#include "qterminalapp.h"
#include "mainwindow.h"
#include "termwidgetholder.h"
#include "termwidget.h"
#include "properties.h"
#include <assert.h>
#include <climits>
#include <algorithm>
TermWidgetHolder::TermWidgetHolder(const QString & wdir, const QString & shell, QWidget * parent)
: QWidget(parent),
m_wdir(wdir),
m_shell(shell),
m_currentTerm(0)
TermWidgetHolder::TermWidgetHolder(TerminalConfig &config, QWidget * parent)
: QWidget(parent)
#ifdef HAVE_QDBUS
, DBusAddressable("/tabs")
#endif
{
#ifdef HAVE_QDBUS
new TabAdaptor(this);
QDBusConnection::sessionBus().registerObject(getDbusPathString(), this);
#endif
setFocusPolicy(Qt::NoFocus);
QGridLayout * lay = new QGridLayout(this);
lay->setSpacing(0);
@ -39,9 +52,10 @@ TermWidgetHolder::TermWidgetHolder(const QString & wdir, const QString & shell,
QSplitter *s = new QSplitter(this);
s->setFocusPolicy(Qt::NoFocus);
TermWidget *w = newTerm();
TermWidget *w = newTerm(config);
s->addWidget(w);
lay->addWidget(s);
m_currentTerm = w;
setLayout(lay);
}
@ -124,33 +138,64 @@ void TermWidgetHolder::setWDir(const QString & wdir)
m_wdir = wdir;
}
void TermWidgetHolder::switchNextSubterminal()
{
// TODO/FIXME: merge switchPrevSubterminal with switchNextSubterminal
QList<TermWidget*> l = findChildren<TermWidget*>();
int ix = -1;
foreach (TermWidget * w, l)
{
++ix;
if (w->impl()->hasFocus())
{
typedef struct {
QPoint topLeft;
QPoint middle;
QPoint bottomRight;
} NavigationData;
static void transpose(QPoint *point) {
int x = point->x();
point->setX(point->y());
point->setY(x);
}
static void transposeTransform(NavigationData *point) {
transpose(&point->topLeft);
transpose(&point->middle);
transpose(&point->bottomRight);
}
static void flipTransform(NavigationData *point) {
QPoint oldTopLeft = point->topLeft;
point->topLeft = -(point->bottomRight);
point->bottomRight = -(oldTopLeft);
point->middle = -(point->middle);
}
static void normalizeToRight(NavigationData *point, NavigationDirection dir) {
switch (dir) {
case Left:
flipTransform(point);
break;
}
case Right:
// No-op
break;
case Top:
flipTransform(point);
transposeTransform(point);
break;
case Bottom:
transposeTransform(point);
break;
default:
assert("Invalid navigation");
return;
}
}
if (ix < l.count()-1)
{
l.at(ix+1)->impl()->setFocus(Qt::OtherFocusReason);
}
else if (ix == l.count()-1)
{
l.at(0)->impl()->setFocus(Qt::OtherFocusReason);
}
static NavigationData getNormalizedDimensions(QWidget *w, NavigationDirection dir) {
NavigationData nd;
nd.topLeft = w->mapTo(w->window(), QPoint(0, 0));
nd.middle = w->mapTo(w->window(), QPoint(w->width() / 2, w->height() / 2));
nd.bottomRight = w->mapTo(w->window(), QPoint(w->width(), w->height()));
normalizeToRight(&nd, dir);
return nd;
}
void TermWidgetHolder::switchPrevSubterminal()
{
// TODO/FIXME: merge switchPrevSubterminal with switchNextSubterminal
void TermWidgetHolder::directionalNavigation(NavigationDirection dir) {
// Find an active widget
QList<TermWidget*> l = findChildren<TermWidget*>();
int ix = -1;
foreach (TermWidget * w, l)
@ -161,14 +206,50 @@ void TermWidgetHolder::switchPrevSubterminal()
break;
}
}
if (ix > 0)
if (ix > l.count())
{
l.at(ix-1)->impl()->setFocus(Qt::OtherFocusReason);
l.at(0)->impl()->setFocus(Qt::OtherFocusReason);
return;
}
else if (ix == 0)
// Found an active widget
TermWidget *w = l.at(ix);
NavigationData from = getNormalizedDimensions(w, dir);
// Search parent that contains point of interest (right edge middlepoint)
QPoint poi = QPoint(from.bottomRight.x(), from.middle.y());
// Perform a search for a TermWidget, where x() is strictly higher than
// poi.x(), y() is strictly less than poi.y(), and prioritizing, in order,
// lower x(), and lower distance between poi.y() and corners.
// Only "Right navigation" implementation is necessary -- other cases
// are normalized to this one.
l = findChildren<TermWidget*>();
int lowestX = INT_MAX;
int lowestMidpointDistance = INT_MAX;
TermWidget *fittest = NULL;
foreach (TermWidget * w, l)
{
l.at(l.count()-1)->impl()->setFocus(Qt::OtherFocusReason);
NavigationData contenderDims = getNormalizedDimensions(w, dir);
int midpointDistance = std::min(
abs(poi.y() - contenderDims.topLeft.y()),
abs(poi.y() - contenderDims.bottomRight.y())
);
if (contenderDims.topLeft.x() > poi.x())
{
if (contenderDims.topLeft.x() > lowestX)
continue;
if (midpointDistance > lowestMidpointDistance)
continue;
lowestX = contenderDims.topLeft.x();
lowestMidpointDistance = midpointDistance;
fittest = w;
}
}
if (fittest != NULL) {
fittest->impl()->setFocus(Qt::OtherFocusReason);
}
}
@ -185,12 +266,14 @@ void TermWidgetHolder::propertiesChanged()
void TermWidgetHolder::splitHorizontal(TermWidget * term)
{
split(term, Qt::Vertical);
TerminalConfig defaultConfig;
split(term, Qt::Vertical, defaultConfig);
}
void TermWidgetHolder::splitVertical(TermWidget * term)
{
split(term, Qt::Horizontal);
TerminalConfig defaultConfig;
split(term, Qt::Horizontal, defaultConfig);
}
void TermWidgetHolder::splitCollapse(TermWidget * term)
@ -242,7 +325,7 @@ void TermWidgetHolder::splitCollapse(TermWidget * term)
emit finished();
}
void TermWidgetHolder::split(TermWidget *term, Qt::Orientation orientation)
TermWidget * TermWidgetHolder::split(TermWidget *term, Qt::Orientation orientation, TerminalConfig cfg)
{
QSplitter *parent = qobject_cast<QSplitter *>(term->parent());
assert(parent);
@ -257,16 +340,9 @@ void TermWidgetHolder::split(TermWidget *term, Qt::Orientation orientation)
s->setFocusPolicy(Qt::NoFocus);
s->insertWidget(0, term);
// wdir settings
QString wd(m_wdir);
if (Properties::Instance()->useCWD)
{
wd = term->impl()->workingDirectory();
if (wd.isEmpty())
wd = m_wdir;
}
TermWidget * w = newTerm(wd);
cfg.provideCurrentDirectory(term->impl()->workingDirectory());
TermWidget * w = newTerm(cfg);
s->insertWidget(1, w);
s->setSizes(sizes);
@ -274,19 +350,12 @@ void TermWidgetHolder::split(TermWidget *term, Qt::Orientation orientation)
parent->setSizes(parentSizes);
w->setFocus(Qt::OtherFocusReason);
return w;
}
TermWidget *TermWidgetHolder::newTerm(const QString & wdir, const QString & shell)
TermWidget *TermWidgetHolder::newTerm(TerminalConfig &cfg)
{
QString wd(wdir);
if (wd.isEmpty())
wd = m_wdir;
QString sh(shell);
if (shell.isEmpty())
sh = m_shell;
TermWidget *w = new TermWidget(wd, sh, this);
TermWidget *w = new TermWidget(cfg, this);
// proxy signals
connect(w, SIGNAL(renameSession()), this, SIGNAL(renameSession()));
connect(w, SIGNAL(removeCurrentSession()), this, SIGNAL(lastTerminalClosed()));
@ -339,3 +408,40 @@ void TermWidgetHolder::onTermTitleChanged(QString title, QString icon) const
if (m_currentTerm == term)
emit termTitleChanged(title, icon);
}
#ifdef HAVE_QDBUS
QDBusObjectPath TermWidgetHolder::getActiveTerminal()
{
if (m_currentTerm != NULL)
{
return m_currentTerm->getDbusPath();
}
return QDBusObjectPath();
}
QList<QDBusObjectPath> TermWidgetHolder::getTerminals()
{
QList<QDBusObjectPath> terminals;
foreach (TermWidget* w, findChildren<TermWidget*>())
{
terminals.push_back(w->getDbusPath());
}
return terminals;
}
QDBusObjectPath TermWidgetHolder::getWindow()
{
return findParent<MainWindow>(this)->getDbusPath();
}
void TermWidgetHolder::closeTab()
{
QTabWidget *parent = findParent<QTabWidget>(this);
int idx = parent->indexOf(this);
assert(idx != -1);
parent->tabCloseRequested(idx);
}
#endif

@ -21,10 +21,20 @@
#include <QWidget>
#include "termwidget.h"
#include "terminalconfig.h"
#include "dbusaddressable.h"
class QSplitter;
typedef enum NavigationDirection {
Left,
Right,
Top,
Bottom
} NavigationDirection;
/*! \brief TermWidget group/session manager.
This widget (one per TabWidget tab) is a "proxy" widget beetween TabWidget and
@ -34,11 +44,14 @@ for TabWidget - with its signals and slots.
Splitting and collapsing of TermWidgets is done here.
*/
class TermWidgetHolder : public QWidget
#ifdef HAVE_QDBUS
, public DBusAddressable
#endif
{
Q_OBJECT
public:
TermWidgetHolder(const QString & wdir, const QString & shell=QString(), QWidget * parent=0);
TermWidgetHolder(TerminalConfig &cfg, QWidget * parent=0);
~TermWidgetHolder();
void propertiesChanged();
@ -50,14 +63,22 @@ class TermWidgetHolder : public QWidget
void zoomOut(uint step);
TermWidget* currentTerminal();
TermWidget* split(TermWidget * term, Qt::Orientation orientation, TerminalConfig cfg);
#ifdef HAVE_QDBUS
QDBusObjectPath getActiveTerminal();
QList<QDBusObjectPath> getTerminals();
QDBusObjectPath getWindow();
void closeTab();
#endif
public slots:
void splitHorizontal(TermWidget * term);
void splitVertical(TermWidget * term);
void splitCollapse(TermWidget * term);
void setWDir(const QString & wdir);
void switchNextSubterminal();
void switchPrevSubterminal();
void directionalNavigation(NavigationDirection dir);
void clearActiveTerminal();
void onTermTitleChanged(QString title, QString icon) const;
@ -73,7 +94,7 @@ class TermWidgetHolder : public QWidget
TermWidget * m_currentTerm;
void split(TermWidget * term, Qt::Orientation orientation);
TermWidget * newTerm(const QString & wdir=QString(), const QString & shell=QString());
TermWidget * newTerm(TerminalConfig &cfg);
private slots:
void setCurrentTerminal(TermWidget* term);

Loading…
Cancel
Save