diff --git a/.gitignore b/.gitignore index e190163..99f72db 100644 --- a/.gitignore +++ b/.gitignore @@ -16,3 +16,4 @@ /CopyQ-2.9.0.tar.gz /CopyQ-3.0.0.tar.gz /CopyQ-3.0.1.tar.gz +/CopyQ-3.0.2.tar.gz diff --git a/CopyQ-3.0.2/.gitignore b/CopyQ-3.0.2/.gitignore new file mode 100644 index 0000000..645d1d5 --- /dev/null +++ b/CopyQ-3.0.2/.gitignore @@ -0,0 +1,49 @@ +# Generated CMake files +CMakeFiles/ +CMakeCache.txt +*.cmake + +# Generated Visual Studio files +/plugins/Debug/ +/plugins/Release/ +/plugins/Win*/ +/Debug/ +/Release/ +/Win*/ +*.sln +*.suo +*.*sdf +*.vc?proj* + +# Generated Makefiles +/plugins/Makefile +/plugins/*/Makefile +/src/Makefile +/Makefile + +# Generated Qt files +/plugins/**/*.dir/ +/src/copyqcon.dir/ +/src/copyq.dir/ +/src/copyq_*.qm +/src/copyq_*.qm.rule +/src/qrc_copyq.cxx +/src/translations.qrc +/src/qrc_translations.cxx + +moc_* +ui_* +*.depends +*.o +*.dylib +*.moc +**/qrc_*.cpp +/.qmake.cache +/copyq.app +copyq.pro.user +.DS_Store +.qmake.stash +*.pyc +*.dmg +/build +*.qm diff --git a/CopyQ-3.0.2/.gitlab-ci.yml b/CopyQ-3.0.2/.gitlab-ci.yml new file mode 100644 index 0000000..496d086 --- /dev/null +++ b/CopyQ-3.0.2/.gitlab-ci.yml @@ -0,0 +1,59 @@ +# Use latest Ubuntu LTS docker image. +image: ubuntu:xenial + +variables: + BUILD_DIR: "build" + INSTALL_PREFIX: "copyq" + SCREENSHOT_DIR: "screenshots" + TESTS_LOG_DIR: "logs" + +build: + stage: build + + before_script: + - utils/gitlab/build-before_script.sh + + script: + - utils/gitlab/build-script.sh + + # Upload installed application. + artifacts: + paths: + - "$INSTALL_PREFIX" + + cache: + paths: + - build + +# Run simple tests (doesn't require GUI) +test: + stage: test + + before_script: + - utils/gitlab/test-before_script.sh + + script: + - utils/gitlab/test-script.sh + + dependencies: + - build + +# GUI tests (requires X11) +test_gui: + stage: test + + before_script: + - utils/gitlab/test_gui-before_script.sh + + script: + - utils/gitlab/test_gui-script.sh + + # Upload screenshots on failure. + artifacts: + when: on_failure + paths: + - "$SCREENSHOT_DIR" + - "$TESTS_LOG_DIR" + + dependencies: + - build diff --git a/CopyQ-3.0.2/.travis.yml b/CopyQ-3.0.2/.travis.yml new file mode 100644 index 0000000..2b83315 --- /dev/null +++ b/CopyQ-3.0.2/.travis.yml @@ -0,0 +1,62 @@ +language: cpp + +matrix: + include: + - os: osx + compiler: clang + + - os: linux + compiler: gcc + env: + - COMPILER=g++-4.8 + - GCOV=gcov-4.8 + addons: + apt: + sources: + - ubuntu-toolchain-r-test + packages: + - g++-4.8 + + - os: linux + compiler: clang + env: + - COMPILER=clang++-3.6 + addons: + apt: + sources: + - ubuntu-toolchain-r-test + - llvm-toolchain-precise-3.6 + packages: + - clang-3.6 + +cache: + apt: true + ccache: true + directories: + - $HOME/.wheelhouse + +before_install: + - utils/travis/before-install-${TRAVIS_OS_NAME}.sh + +install: + - utils/travis/install-${TRAVIS_OS_NAME}.sh + +script: + - utils/travis/script-${TRAVIS_OS_NAME}.sh + +after_success: + - utils/travis/after_success-${TRAVIS_OS_NAME}.sh + +deploy: + provider: releases + api_key: + secure: Vax27ifQsc8SlTsLYVbxVJANDAxDroegN6nOPXCN1MLaoh4W2DQ/iGGx+waIOSYig8Sh+AUz2JhCFuMLMVqwFoWY2rxNPBrxhTBjm3aDhylbB+mRECnbInNb0kS3qv4lNDN6lHD4B6K01FWUUiHX14s2JQx4ut+KuwMxxhxyO4Y= + file: 'build/*.dmg' + file_glob: true + skip_cleanup: true + overwrite: true + on: + condition: "$TRAVIS_OS_NAME = osx" + tags: true + all_branches: true + repo: hluk/CopyQ diff --git a/CopyQ-3.0.2/AUTHORS b/CopyQ-3.0.2/AUTHORS new file mode 100644 index 0000000..28f09d3 --- /dev/null +++ b/CopyQ-3.0.2/AUTHORS @@ -0,0 +1,21 @@ +Adam Batkin +Giacomo Margarito +Greg Carp +Ilya Plenne +Jörg Thalheim +Kim Jzhone +Kos Ivantsov +lightonflux +Lukas Holecek +Marjolein Hoekstra +Martin Lepadusch +Matt d'Entremont +Michal Čihař +Patricio M. Ros +Robert Orzanna +Ryan Wooden +Scott Kostyshak +Sebastian Schuberth +Tomas Nilzon +Wilfried Caruel +x2357 diff --git a/CopyQ-3.0.2/CHANGES b/CopyQ-3.0.2/CHANGES new file mode 100644 index 0000000..95a3278 --- /dev/null +++ b/CopyQ-3.0.2/CHANGES @@ -0,0 +1,461 @@ +v3.0.2 +- Added script functions for listing synchronized tabs and their paths +- Fixed showing window on current screen +- Fixed notification position with multiple screens +- Fixed rendering items when scrolling +- Fixed pasting from main window after switching tabs +- Fixed copy/paste to some apps on OS X +- Fixed focusing editor when closing completion popup on OS X +- Fixed setting temporary file template from script + +v3.0.1 +- Install themes on OS X +- Improve pasting to current window +- Fix crash when the first tab is not loaded +- Fix crash when reloading tab after closing editor +- Fix item rendering and UI elements for high DPI displays +- Fix window focus after closing menu or main window on OS X +- Fix opening main window on current space on OS X +- Fix pasting to some windows on OS X +- Fix navigating item list +- Fix getting boolean from checkbox in dialog() +- Fix default move action for drag'n'drop +- Fix exitting on logout when tray is disabled + +v3.0.0 +- Pinned and protected items +- Export/import tabs, configuration and commands in one file +- Create and modify commands from script +- Create temporary files from script +- Create notifications with buttons from script +- Take screenshots using script +- Allow to process lines on stdout from execute() scriptable using a function +- Safer and faster encrypt/decrypt commands (need to be re-added) +- Improved menu scriptable function +- Improved icon sharpness +- Improved plugin architecture +- Improved logging and displaying log +- Performance and memory consumption improvements +- Implemented copy() on OS X +- Fixed focusing menu and windows on OS X +- Fixed configuration folder path for portable version on Windows +- Fixed opening menu for a tab +- Fixed using correct GPG version for encryption +- Fixed tray menu position in KDE/Plasma + +v2.9.0 +- Set text style in editor +- Search in editor +- Quick help in completion popup menu for commands +- Easier text selection in item preview +- Show whole text and unscaled image in item preview +- Improved pasting to windows on Linux/X11 +- Fixed global shortcuts at application start on Linux/X11 +- Fixed closing application from installer on Windows +- Fixed showing item preview at start +- Fixed saving position of new tabs and tab lists + +v2.8.3 +- Search items from tray menu +- Added support for animated gifs (played when selected) +- Added special formats for automatic commands to sync and store clipboard +- Added auto-completion for command editor +- Added scriptable variables for MIME types +- Fix encryption with new OpenPGP +- Fix passing big data to commands on Windows + +v2.8.2 +- Simplify appearance of items with notes and tags +- Support for drag'n'dropping images to more applications +- Added list widget for custom dialog +- Fixed opening windows on current screen +- Fixed tray icon appearance on Linux +- Fixed focusing tray menu from command +- Fixed dialog button translation on Windows +- Fixed passing big data to commands + +v2.8.1 +- All Qt messages are logged +- Fixed and improved commands for Tags plugin +- Fixed removing last items when changing item limit +- Fixed library paths for OS X +- Fixed pasting items on Windows +- Fixed copying from script on Windows + +v2.8.0 +- Insert images in editor +- Show simple items options +- Item preview window +- Move to Qt 5 on Windows and newer Linux distros +- Faster item content listing +- Simple filter for Log dialog +- Smooth icons on OS X +- Fixed system icons +- Fixed pasting animated images +- Fixed occasional crashes when finalizing commands with Qt 5 +- Fixed opening log speed on Windows +- Lithuanian translation + +v2.7.1 +- Colorize items with command +- Drag'n'drop items in selection order +- Fixed item selection with "next" and "previous" commands +- Fixed encrypting/decrypting items on Windows +- Fixed occasional client crashes at exit +- Fixed editor command on OS X + +v2.7.0 +- Log accessible from GUI +- Performance and memory usage improvements +- Added scriptable function to set current tab (setCurrentTab()) +- Added scriptable function to modify new items (setData()) +- Appearance fixes +- Simplified window management +- Improved pasting to current window on Windows +- Window geometry fixes +- Command with Enter shortcut overrides item activate action + +v2.6.1 +- Moved configuration from registry on Windows +- Fixed shortcuts on Windows +- Fixed window geometry restoring + +v2.6.0 +- Show item notes in tray and window title +- Removed broken console executable on Windows +- Dutch translation +- Added env() and setEnv() to access and modify environment variables +- Access shortcut which activated command +- Fixed closing the application at shutdown on Windows +- Fixed some global shortcuts on Windows +- Fixed capturing some shortcuts + +v2.5.0 +- Smarter tab name matching (ignore key hints '&') +- Fixed omit passing global shortcuts to widgets +- Fixed autostart option on Ubuntu +- Fixed window geometry saving and restoring +- Fixed reading binary input on Windows +- Fixed clearing configuration + +v2.4.9 +- Added new light theme +- Added scriptable function focused() to test main window focus +- Customizable shortcuts for tab navigation +- Extended item selection +- Fixed tab expiration and updating context menu +- Fixed passing text to command from action dialog + +v2.4.8 +- New command to show main window under mouse cursor or at a position with custom size +- Hide clipboard content when "application/x-copyq-hidden" is "1" +- "Copy next/previous item" command waits for clipboard to be set +- Fixed updating window title and tray tool tip on X11 +- Fixed modifying multiple commands in Command dialog +- Fixed implicit date to string conversions + +v2.4.7 +- Separate dialog for command help +- Added scriptable function visible() to check main window visibility +- Linux: Install bitmap icons for menus +- Linux: Install AppData file +- Allow to search for specific MIME types stored in items +- Menu items and customizable shortcut for cycling item format +- Fixed icon alignment +- Fixed moving tabs with Qt 5 +- Fixed overriding socket file path (Linux and OS X) +- Fixed "Paste as Plain Text" command (Windows and OS X) +- Fixed tab tree layout and changing icons for tab groups +- Fixed URL encoding + +v2.4.6 +- Fixed crash when removing command +- Fixed encryption/decryption selected items +- Fixed reading from standard input +- GUI fixes for high-DPI displays + +v2.4.5 +- Option to save/restore history for filtering items +- Clipboard changes with unchanged content is ignored +- Notify about unsaved changes in command dialog +- Use application icons from current icon theme on Linux +- Simple error checking for user scripts +- Fix blocked system shutdown on Linux/X11 + +v2.4.4 +- Option to choose tab for storing clipboard +- Fixed overriding mouse selection (Linux/X11) +- Fixed window title updates from user commands +- Fixed toggling window visibility with Qt 5 +- Minor GUI improvements and user command fixes + +v2.4.3 +- Plugin for tagging items +- Plugins can provide script functions and commands +- Improved automatic commands execution +- Fixed gradients, transparency and other style improvements +- Fixed decryption with newer version of GnuPG +- Fixes for Qt 5 version + +v2.4.2 +- Send input data to execute() +- Better clipboard encoding guessing +- Set tab icon from commands using tabicon() +- Fixed window title encoding on Windows +- Fixed restoring window geometry +- Performance fixes +- Various bug and usability fixes +- New logo + +v2.4.1 +- Added scriptable classes File and Dir +- Added scriptable function settings() for saving custom user data +- Improved dialog() command +- Windows: Qt translated strings bundled with application +- Fixed %1 in command +- Fixed building with tests and Qt5 + +v2.4.0 +- Separate dialog for user commands and global shortcuts +- Search for item by row number +- Command highlighting +- More shortcuts can be mapped on Windows and X11 +- New "copy" command to copy from current window to clipboard +- New "dialog" command to show dialog with custom input fields +- Fixed crash on log out on Windows +- Fixed clipboard monitoring on Windows +- Fixed argument encoding from client on Windows +- Fixed log output when printing messages from multiple processes +- GUI fixes + +v2.3.0 +- Support for OS X +- Japanese translation +- Custom icons for tabs +- Show item count next to each tab name (optional) +- Added Process Manager for running and finished commands +- Scripting improvements +- Nicer format for copied user commands +- GUI fixes + +v2.2.0 +- Custom system shortcuts for any user command +- Drag'n'drop items to tabs +- Options to set position and maximum size for notifications +- Option to open windows on same desktop +- Weblate service for translations (https://hosted.weblate.org/projects/copyq/master/) +- Commands input and output is UTF-8 only (this fixes encoding issues on Windows) +- Scripting engine improvements +- Various GUI improvements and fixes +- Fix main window position in various X11 window managers +- Fix crashing with Oxygen GUI style +- Fix storing images from clipboard on Windows +- Various GUI improvements and fixes + +v2.1.0 +- French translation +- Save/load and copy/paste user commands +- Easier way to write longer commands and scripts +- Remove formats in clipboard and item content dialogs +- Command "toggle" focuses main window if unfocused (instead of closing) +- Choose log file and amount of information to log +- Lot of bugfixes and GUI improvements + +v2.0.1 +- Initial OS X support +- Configuration moved into installed directory in Windows +- Change language in configuration +- New tool bar with item actions +- Option to apply color theme in tabs, tool bar and menus +- Allow to match items using a command +- Focus output item of the last executed command +- Allow to cancel exit if there are active commands +- Removed option to hide menu bar (inconsistent behavior) +- Fix showing lock icon in encrypted items + +v2.0.0 +- Synchronize items with files on disk +- Faster tab loading and saving (data format was changed; only backward compatible) +- User can limit size of text items +- Opening external image editor fixed on Windows +- New logo and website +- Lot of other fixes + +v1.9.3 +- Item and tab encryption (using GnuPG) +- FakeVim plugin for editing items (Vim editor emulation) +- Drag'n'drop items from and to list +- Improved appearance for notes +- Improved search bar +- New GUI for application and system-wide shortcuts +- Option to unload tabs after an interval +- Fixed item sizes and disabling font anti-aliasing +- Major bug fixes (mainly for Windows) and performance improvements + +v1.9.2 +- Better performance +- GUI improvements and bugfixes + +v1.9.1 +- Notifications -- customizable theme, timeout and position on screen +- Optional notification for new clipboard content +- Autostart option on Linux +- Reset empty clipboard to previous content +- More user-friendly item editor +- Optional font antialiasing +- Changed layout of configuration dialog +- Other fixes + +v1.9.0 +- User notes +- Improved appearance settings with some example themes +- Tree view for tabs with groups +- Sessions, i.e. run multiple independent instances +- Lot of GUI improvements +- Compatibility with Qt5 +- Bugfixes (crashing on Unity, icon colors etc.) + +v1.8.3 +- Options to hide tab bar and main menu +- Automatic paste works with more applications under Linux/X11 +- Multi-monitor support +- Lot of GUI fixes and improvements + +v1.8.2 +- Added shortcut to paste current and copy next/previous item +- Bugfixes (paste to correct window, show tray menu on Unity, GUI and usability fixes) + +v1.8.1 +- Spanish translation +- Option and system-wide shortcuts to temporarily disable clipboard storing +- Option for main window transparency +- Custom action on item activation +- Various GUI improvements and bugfixes + +v1.8.0 +- New shortcuts: "Next/previous item to clipboard", "Paste as plain text" +- Show clipboard content in main window title and tray tooltip +- New options for commands (transform current item, close main window) +- GUI enhancements, faster application start with many tabs and items, lot of bugfixes + +v1.7.5 +- User-settable editor for images +- Command-line fixes for Windows +- Commands for items of specified format (MIME type) +- Tray menu fixes + +v1.7.4 +- Improved automatic paste from tray + +v1.7.3 +- Paste immediately after choosing tray item +- German translation +- Support for system-wide shortcuts on Qt 5 + +v1.7.2 +- Clipboard content visible in tray tooltip + +v1.7.1 +- Bugfixes for text encoding + +v1.7.0 +- Plugins for saving and displaying clipboard content +- Bugfixes (lot of refactoring and tests happened) + +v1.6.3 +- Some important bugfixes + +v1.6.2 +- Dialog for viewing item content +- Improved tray menu +- Minor GUI updates + +v1.6.1 +- Configurable tray menu +- Lot of fixes in GUI and bugfixes + +v1.6.0 +- Highlight text and copy text in items +- Interactive web view +- Commands for any MIME type +- e.g. it's possible to create QR Code image from an URL and save it in list +- Pipe commands using '|' character + +v1.5.0 +- Option to use WebKit to render HTML +- Wrap text with long lines +- Faster list rendering +- Icons from FontAwesome +- Desktop icon on Linux + +v1.4.1 +- Support for other languages -- right now supports only English and Czech (any help is welcome) +- New "insert" command +- More safe item saving + +v1.4.0 +- lot of GUI Improvements, faster interaction +- Automatic commands for matched windows (only on Linux and Windows) + +v1.3.3 +- GUI Improvements +- New system-wide shortcuts +- Item editing improved + +v1.3.2 +- Drag'n'Drop to clipboard +- "Always on Top" option +- Change tab bar position +- Fix parsing arguments + +v1.3.1 +- GUI improvements +- Mode for Vi navigation (h, j, k, l keys for movement) +- Better performance + +v1.3.0 +- Import/export items to/from a file (not compatible with older saved format) +- Use scripts to handle item history +- Improved performance + +v1.2.5 +- Save/load items to/from a file +- Sort selected items +- Easier tab browsing (left/right arrow keys) +- GUI improvements +- More shortcut combinations work on Linux + +v1.2.4 +- Improved commands +- Fixed and faster scrolling +- Better tab manipulation + +v1.2.3 +- Bugfixes and major clean-up + +v1.2.2 +- Performance improved + +v1.2.1 +- Save items from commands in other tabs +- Missing icons in Windows version + +v1.2.0 +- Appearance settings +- Tab manipulation from command line +- Copy/paste items from/to tabs +- Faster searching + +v1.1.0 +- Better performance +- New configuration options +- Improved command line + +v1.0.2 +- Improved Windows compatibility +- Global shortcuts +- Automatic commands + +v1.0.1 +- Compatibility with different platforms + diff --git a/CopyQ-3.0.2/CMakeLists.txt b/CopyQ-3.0.2/CMakeLists.txt new file mode 100644 index 0000000..c164438 --- /dev/null +++ b/CopyQ-3.0.2/CMakeLists.txt @@ -0,0 +1,202 @@ +cmake_minimum_required(VERSION 2.8.6) +project(copyq) + +# C++11 +if (${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION} GREATER 3.1) + set(CMAKE_CXX_STANDARD 11) +else() + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") +endif() + +if(CMAKE_BUILD_TYPE MATCHES Debug) + set(COPYQ_DEBUG ON) + add_definitions( -DCOPYQ_DEBUG ) +endif() + +OPTION(PEDANTIC "Enable all compiler warnings" OFF) + +# Options (cmake -LH) +OPTION(WITH_QT5 "Use Qt 5 (disable to use Qt 4 instead)" ON) +OPTION(WITH_TESTS "Run test cases from command line" ${COPYQ_DEBUG}) +OPTION(WITH_PLUGINS "Compile plugins" ON) +# Linux-specific options +if (${CMAKE_SYSTEM_NAME} MATCHES "Linux") + set(PLUGIN_INSTALL_PREFIX "${CMAKE_INSTALL_PREFIX}/${CMAKE_SHARED_MODULE_PREFIX}/copyq/plugins" CACHE PATH "Install path for plugins") + set(ICON_INSTALL_PREFIX "${CMAKE_INSTALL_PREFIX}/share/icons/hicolor/scalable/apps" CACHE PATH "Install path for icons") + set(ICON_INSTALL_PREFIX_TEMPLATE "${CMAKE_INSTALL_PREFIX}/share/icons/hicolor/%SIZE%/apps" CACHE PATH "Install path for icons (%SIZE% is icon size)") + set(THEME_INSTALL_PREFIX "${CMAKE_INSTALL_PREFIX}/share/copyq/themes" CACHE PATH "Install path for themes") + set(DESKTOP_INSTALL_PREFIX "${CMAKE_INSTALL_PREFIX}/share/applications" CACHE PATH "Install path for \"copyq.desktop\"") + set(APPDATA_INSTALL_PREFIX "${CMAKE_INSTALL_PREFIX}/share/appdata" CACHE PATH "Install path for \"copyq.appdata.xml\"") + set(TRANSLATION_INSTALL_PREFIX "${CMAKE_INSTALL_PREFIX}/share/copyq/translations" CACHE PATH "Install path for translations") +endif() + +set(CMAKE_AUTOMOC ON) + +if (WITH_QT5) + cmake_minimum_required(VERSION 2.8.8) + find_package(Qt5Widgets) + if (NOT Qt5Widgets_FOUND) + message(FATAL_ERROR "Qt 5 is unavailable. To compile with Qt 4 use -DWITH_QT5=OFF.") + endif() + message(STATUS "Building with Qt 5.") +else() + find_package(Qt4) + if (NOT QT4_FOUND) + # Try different executable name. + set(QT_QMAKE_EXECUTABLE "qmake-qt4") + find_package(Qt4) + if (NOT QT4_FOUND) + message(FATAL_ERROR "Qt 4 is unavailable. To compile with Qt 5 use -DWITH_QT5=ON.") + endif() + endif() + message(STATUS "Building with Qt 4.") +endif() + +set(copyq_ICON_PREFIX src/images/icon) +set(copyq_ICON_NORMAL src/images/icon.svg) +set(copyq_ICON_BUSY src/images/icon-running.svg) +set(copyq_DESKTOP shared/copyq.desktop) +set(copyq_APPDATA shared/copyq.appdata.xml) + +# Be more strict while compiling debugging version +if(CMAKE_COMPILER_IS_GNUCXX OR "${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-long-long") + set(CMAKE_CXX_FLAGS_DEBUG + "${CMAKE_CXX_FLAGS_DEBUG} -Wextra -Wall -pedantic -Wfloat-equal -Woverloaded-virtual -Wundef") +endif() + +if (PEDANTIC) + if (CMAKE_COMPILER_IS_GNUCXX) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wextra -Wall \ + -Wsuggest-override \ + -Wlogical-op \ + -Wnoexcept \ + -Wstrict-null-sentinel \ + ") + else() + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Weverything \ + -Winconsistent-missing-override \ + ") + + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} \ + -Wno-c++98-compat \ + -Wno-c++98-compat-pedantic \ + -Wno-shadow-field-in-constructor \ + -Wno-weak-vtables \ + -Wno-disabled-macro-expansion \ + -fcomment-block-commands=retval \ + ") + + # Disable errors from moc-generated files. + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} \ + -Wno-undefined-reinterpret-cast \ + -Wno-missing-prototypes \ + ") + + # Disable errors from qrc-generated files. + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} \ + -Wno-exit-time-destructors \ + -Wno-global-constructors \ + ") + endif() + + set(CMAKE_CXX_FLAGS + "${CMAKE_CXX_FLAGS} -pedantic -Werror \ + -Wcast-align \ + -Wcast-qual \ + -Wctor-dtor-privacy \ + -Wdisabled-optimization \ + -Wformat=2 \ + -Winit-self \ + -Wmissing-declarations \ + -Wmissing-include-dirs \ + -Wold-style-cast \ + -Woverloaded-virtual \ + -Wredundant-decls \ + -Wstrict-overflow=4 \ + -Wundef \ + ") + + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} \ + -Wno-padded \ + -Wno-switch-enum \ + ") + + # Disable Q_OBJECT macro warnings. + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} \ + -Wno-unused-member-function \ + ") +endif() + +if(WITH_TESTS) + message(STATUS "Building with tests.") + + add_definitions( -DHAS_TESTS ) + + if (WITH_QT5) + list(APPEND copyq_Qt5_Modules Test) + else() + set(QT_USE_QTTEST TRUE) + endif() +endif() + +# Get application version. +if (EXISTS "version.txt") + file(STRINGS "version.txt" copyq_version) +endif() + +if (NOT copyq_version) + find_package(Git) + if(GIT_FOUND) + execute_process(COMMAND + "${GIT_EXECUTABLE}" describe + WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}" + RESULT_VARIABLE copyq_git_describe_result + OUTPUT_VARIABLE copyq_git_describe_output + ERROR_QUIET + OUTPUT_STRIP_TRAILING_WHITESPACE + ) + if(copyq_git_describe_result EQUAL 0) + set(copyq_version "${copyq_git_describe_output}") + endif() + endif() +endif() + +if (copyq_version) + message(STATUS "Building CopyQ version ${copyq_version}.") + add_definitions( -DCOPYQ_VERSION="${copyq_version}" ) +endif() + +if (${CMAKE_SYSTEM_NAME} MATCHES "Linux") + install(FILES ${copyq_ICON_NORMAL} DESTINATION ${ICON_INSTALL_PREFIX} RENAME copyq-normal.svg) + install(FILES ${copyq_ICON_BUSY} DESTINATION ${ICON_INSTALL_PREFIX} RENAME copyq-busy.svg) + install(FILES ${copyq_DESKTOP} DESTINATION ${DESKTOP_INSTALL_PREFIX}) + install(FILES ${copyq_APPDATA} DESTINATION ${APPDATA_INSTALL_PREFIX}) + + foreach (copyq_ICON_EXTENT 16 22 24 32 48 64 128) + set(copyq_ICON_SIZE "${copyq_ICON_EXTENT}x${copyq_ICON_EXTENT}") + string(REPLACE "%SIZE%" "${copyq_ICON_SIZE}" copyq_ICON_TARGET_PREFIX "${ICON_INSTALL_PREFIX_TEMPLATE}") + foreach (copyq_ICON_TYPE "" "-normal" "-busy") + install(FILES "${copyq_ICON_PREFIX}${copyq_ICON_TYPE}_${copyq_ICON_SIZE}.png" DESTINATION "${copyq_ICON_TARGET_PREFIX}" RENAME "copyq${copyq_ICON_TYPE}.png") + endforeach() + endforeach() + + set(copyq_THEME_INSTALL_PREFIX ${THEME_INSTALL_PREFIX}) + file(GLOB copyq_THEMES shared/themes/*.ini) + install(FILES ${copyq_THEMES} DESTINATION ${THEME_INSTALL_PREFIX}) + + add_definitions( -DCOPYQ_ICON_PREFIX="${ICON_INSTALL_PREFIX}/copyq" ) + add_definitions( -DCOPYQ_THEME_PREFIX="${THEME_INSTALL_PREFIX}" ) + add_definitions( -DCOPYQ_PLUGIN_PREFIX="${PLUGIN_INSTALL_PREFIX}" ) + add_definitions( -DCOPYQ_DESKTOP_PREFIX="${DESKTOP_INSTALL_PREFIX}" ) + add_definitions( -DCOPYQ_TRANSLATION_PREFIX="${TRANSLATION_INSTALL_PREFIX}" ) +endif() + +add_definitions( -DQT_NO_CAST_TO_ASCII ) + +add_subdirectory(src) + +if (WITH_PLUGINS) + add_subdirectory(plugins) +endif() + diff --git a/CopyQ-3.0.2/Doxyfile b/CopyQ-3.0.2/Doxyfile new file mode 100644 index 0000000..e98e4f7 --- /dev/null +++ b/CopyQ-3.0.2/Doxyfile @@ -0,0 +1,1716 @@ +# Doxyfile 1.7.4 + +# This file describes the settings to be used by the documentation system +# doxygen (www.doxygen.org) for a project. +# +# All text after a hash (#) is considered a comment and will be ignored. +# The format is: +# TAG = value [value, ...] +# For lists items can also be appended using: +# TAG += value [value, ...] +# Values that contain spaces should be placed between quotes (" "). + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- + +# This tag specifies the encoding used for all characters in the config file +# that follow. The default is UTF-8 which is also the encoding used for all +# text before the first occurrence of this tag. Doxygen uses libiconv (or the +# iconv built into libc) for the transcoding. See +# http://www.gnu.org/software/libiconv for the list of possible encodings. + +DOXYFILE_ENCODING = UTF-8 + +# The PROJECT_NAME tag is a single word (or a sequence of words surrounded +# by quotes) that should identify the project. + +PROJECT_NAME = + +# The PROJECT_NUMBER tag can be used to enter a project or revision number. +# This could be handy for archiving the generated documentation or +# if some version control system is used. + +PROJECT_NUMBER = + +# Using the PROJECT_BRIEF tag one can provide an optional one line description +# for a project that appears at the top of each page and should give viewer +# a quick idea about the purpose of the project. Keep the description short. + +PROJECT_BRIEF = + +# With the PROJECT_LOGO tag one can specify an logo or icon that is +# included in the documentation. The maximum height of the logo should not +# exceed 55 pixels and the maximum width should not exceed 200 pixels. +# Doxygen will copy the logo to the output directory. + +PROJECT_LOGO = + +# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) +# base path where the generated documentation will be put. +# If a relative path is entered, it will be relative to the location +# where doxygen was started. If left blank the current directory will be used. + +OUTPUT_DIRECTORY = + +# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create +# 4096 sub-directories (in 2 levels) under the output directory of each output +# format and will distribute the generated files over these directories. +# Enabling this option can be useful when feeding doxygen a huge amount of +# source files, where putting all generated files in the same directory would +# otherwise cause performance problems for the file system. + +CREATE_SUBDIRS = NO + +# The OUTPUT_LANGUAGE tag is used to specify the language in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all constant output in the proper language. +# The default language is English, other supported languages are: +# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, +# Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German, +# Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English +# messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian, +# Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrillic, Slovak, +# Slovene, Spanish, Swedish, Ukrainian, and Vietnamese. + +OUTPUT_LANGUAGE = English + +# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will +# include brief member descriptions after the members that are listed in +# the file and class documentation (similar to JavaDoc). +# Set to NO to disable this. + +BRIEF_MEMBER_DESC = YES + +# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend +# the brief description of a member or function before the detailed description. +# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the +# brief descriptions will be completely suppressed. + +REPEAT_BRIEF = YES + +# This tag implements a quasi-intelligent brief description abbreviator +# that is used to form the text in various listings. Each string +# in this list, if found as the leading text of the brief description, will be +# stripped from the text and the result after processing the whole list, is +# used as the annotated text. Otherwise, the brief description is used as-is. +# If left blank, the following values are used ("$name" is automatically +# replaced with the name of the entity): "The $name class" "The $name widget" +# "The $name file" "is" "provides" "specifies" "contains" +# "represents" "a" "an" "the" + +ABBREVIATE_BRIEF = + +# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then +# Doxygen will generate a detailed section even if there is only a brief +# description. + +ALWAYS_DETAILED_SEC = NO + +# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all +# inherited members of a class in the documentation of that class as if those +# members were ordinary class members. Constructors, destructors and assignment +# operators of the base classes will not be shown. + +INLINE_INHERITED_MEMB = NO + +# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full +# path before files name in the file list and in the header files. If set +# to NO the shortest path that makes the file name unique will be used. + +FULL_PATH_NAMES = YES + +# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag +# can be used to strip a user-defined part of the path. Stripping is +# only done if one of the specified strings matches the left-hand part of +# the path. The tag can be used to show relative paths in the file list. +# If left blank the directory from which doxygen is run is used as the +# path to strip. + +STRIP_FROM_PATH = + +# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of +# the path mentioned in the documentation of a class, which tells +# the reader which header file to include in order to use a class. +# If left blank only the name of the header file containing the class +# definition is used. Otherwise one should specify the include paths that +# are normally passed to the compiler using the -I flag. + +STRIP_FROM_INC_PATH = + +# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter +# (but less readable) file names. This can be useful if your file system +# doesn't support long names like on DOS, Mac, or CD-ROM. + +SHORT_NAMES = NO + +# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen +# will interpret the first line (until the first dot) of a JavaDoc-style +# comment as the brief description. If set to NO, the JavaDoc +# comments will behave just like regular Qt-style comments +# (thus requiring an explicit @brief command for a brief description.) + +JAVADOC_AUTOBRIEF = YES + +# If the QT_AUTOBRIEF tag is set to YES then Doxygen will +# interpret the first line (until the first dot) of a Qt-style +# comment as the brief description. If set to NO, the comments +# will behave just like regular Qt-style comments (thus requiring +# an explicit \brief command for a brief description.) + +QT_AUTOBRIEF = NO + +# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen +# treat a multi-line C++ special comment block (i.e. a block of //! or /// +# comments) as a brief description. This used to be the default behaviour. +# The new default is to treat a multi-line C++ comment block as a detailed +# description. Set this tag to YES if you prefer the old behaviour instead. + +MULTILINE_CPP_IS_BRIEF = NO + +# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented +# member inherits the documentation from any documented member that it +# re-implements. + +INHERIT_DOCS = YES + +# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce +# a new page for each member. If set to NO, the documentation of a member will +# be part of the file/class/namespace that contains it. + +SEPARATE_MEMBER_PAGES = NO + +# The TAB_SIZE tag can be used to set the number of spaces in a tab. +# Doxygen uses this value to replace tabs by spaces in code fragments. + +TAB_SIZE = 8 + +# This tag can be used to specify a number of aliases that acts +# as commands in the documentation. An alias has the form "name=value". +# For example adding "sideeffect=\par Side Effects:\n" will allow you to +# put the command \sideeffect (or @sideeffect) in the documentation, which +# will result in a user-defined paragraph with heading "Side Effects:". +# You can put \n's in the value part of an alias to insert newlines. + +ALIASES = + +# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C +# sources only. Doxygen will then generate output that is more tailored for C. +# For instance, some of the names that are used will be different. The list +# of all members will be omitted, etc. + +OPTIMIZE_OUTPUT_FOR_C = NO + +# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java +# sources only. Doxygen will then generate output that is more tailored for +# Java. For instance, namespaces will be presented as packages, qualified +# scopes will look different, etc. + +OPTIMIZE_OUTPUT_JAVA = NO + +# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran +# sources only. Doxygen will then generate output that is more tailored for +# Fortran. + +OPTIMIZE_FOR_FORTRAN = NO + +# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL +# sources. Doxygen will then generate output that is tailored for +# VHDL. + +OPTIMIZE_OUTPUT_VHDL = NO + +# Doxygen selects the parser to use depending on the extension of the files it +# parses. With this tag you can assign which parser to use for a given extension. +# Doxygen has a built-in mapping, but you can override or extend it using this +# tag. The format is ext=language, where ext is a file extension, and language +# is one of the parsers supported by doxygen: IDL, Java, Javascript, CSharp, C, +# C++, D, PHP, Objective-C, Python, Fortran, VHDL, C, C++. For instance to make +# doxygen treat .inc files as Fortran files (default is PHP), and .f files as C +# (default is Fortran), use: inc=Fortran f=C. Note that for custom extensions +# you also need to set FILE_PATTERNS otherwise the files are not read by doxygen. + +EXTENSION_MAPPING = + +# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want +# to include (a tag file for) the STL sources as input, then you should +# set this tag to YES in order to let doxygen match functions declarations and +# definitions whose arguments contain STL classes (e.g. func(std::string); v.s. +# func(std::string) {}). This also makes the inheritance and collaboration +# diagrams that involve STL classes more complete and accurate. + +BUILTIN_STL_SUPPORT = NO + +# If you use Microsoft's C++/CLI language, you should set this option to YES to +# enable parsing support. + +CPP_CLI_SUPPORT = NO + +# Set the SIP_SUPPORT tag to YES if your project consists of sip sources only. +# Doxygen will parse them like normal C++ but will assume all classes use public +# instead of private inheritance when no explicit protection keyword is present. + +SIP_SUPPORT = NO + +# For Microsoft's IDL there are propget and propput attributes to indicate getter +# and setter methods for a property. Setting this option to YES (the default) +# will make doxygen replace the get and set methods by a property in the +# documentation. This will only work if the methods are indeed getting or +# setting a simple type. If this is not the case, or you want to show the +# methods anyway, you should set this option to NO. + +IDL_PROPERTY_SUPPORT = YES + +# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC +# tag is set to YES, then doxygen will reuse the documentation of the first +# member in the group (if any) for the other members of the group. By default +# all members of a group must be documented explicitly. + +DISTRIBUTE_GROUP_DOC = NO + +# Set the SUBGROUPING tag to YES (the default) to allow class member groups of +# the same type (for instance a group of public functions) to be put as a +# subgroup of that type (e.g. under the Public Functions section). Set it to +# NO to prevent subgrouping. Alternatively, this can be done per class using +# the \nosubgrouping command. + +SUBGROUPING = YES + +# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and +# unions are shown inside the group in which they are included (e.g. using +# @ingroup) instead of on a separate page (for HTML and Man pages) or +# section (for LaTeX and RTF). + +INLINE_GROUPED_CLASSES = NO + +# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum +# is documented as struct, union, or enum with the name of the typedef. So +# typedef struct TypeS {} TypeT, will appear in the documentation as a struct +# with name TypeT. When disabled the typedef will appear as a member of a file, +# namespace, or class. And the struct will be named TypeS. This can typically +# be useful for C code in case the coding convention dictates that all compound +# types are typedef'ed and only the typedef is referenced, never the tag name. + +TYPEDEF_HIDES_STRUCT = NO + +# The SYMBOL_CACHE_SIZE determines the size of the internal cache use to +# determine which symbols to keep in memory and which to flush to disk. +# When the cache is full, less often used symbols will be written to disk. +# For small to medium size projects (<1000 input files) the default value is +# probably good enough. For larger projects a too small cache size can cause +# doxygen to be busy swapping symbols to and from disk most of the time +# causing a significant performance penalty. +# 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 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 + +SYMBOL_CACHE_SIZE = 0 + +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- + +# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in +# documentation are documented, even if no documentation was available. +# Private class members and static file members will be hidden unless +# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES + +EXTRACT_ALL = NO + +# If the EXTRACT_PRIVATE tag is set to YES all private members of a class +# will be included in the documentation. + +EXTRACT_PRIVATE = YES + +# If the EXTRACT_STATIC tag is set to YES all static members of a file +# will be included in the documentation. + +EXTRACT_STATIC = YES + +# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) +# defined locally in source files will be included in the documentation. +# If set to NO only classes defined in header files are included. + +EXTRACT_LOCAL_CLASSES = YES + +# This flag is only useful for Objective-C code. When set to YES local +# methods, which are defined in the implementation section but not in +# the interface are included in the documentation. +# If set to NO (the default) only methods in the interface are included. + +EXTRACT_LOCAL_METHODS = NO + +# If this flag is set to YES, the members of anonymous namespaces will be +# extracted and appear in the documentation as a namespace called +# 'anonymous_namespace{file}', where file will be replaced with the base +# name of the file that contains the anonymous namespace. By default +# anonymous namespaces are hidden. + +EXTRACT_ANON_NSPACES = NO + +# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all +# undocumented members of documented classes, files or namespaces. +# If set to NO (the default) these members will be included in the +# various overviews, but no documentation section is generated. +# This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_MEMBERS = NO + +# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all +# undocumented classes that are normally visible in the class hierarchy. +# If set to NO (the default) these classes will be included in the various +# overviews. This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_CLASSES = NO + +# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all +# friend (class|struct|union) declarations. +# If set to NO (the default) these declarations will be included in the +# documentation. + +HIDE_FRIEND_COMPOUNDS = NO + +# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any +# documentation blocks found inside the body of a function. +# If set to NO (the default) these blocks will be appended to the +# function's detailed documentation block. + +HIDE_IN_BODY_DOCS = NO + +# The INTERNAL_DOCS tag determines if documentation +# that is typed after a \internal command is included. If the tag is set +# to NO (the default) then the documentation will be excluded. +# Set it to YES to include the internal documentation. + +INTERNAL_DOCS = NO + +# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate +# file names in lower-case letters. If set to YES upper-case letters are also +# allowed. This is useful if you have classes or files whose names only differ +# in case and if your file system supports case sensitive file names. Windows +# and Mac users are advised to set this option to NO. + +CASE_SENSE_NAMES = YES + +# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen +# will show members with their full class and namespace scopes in the +# documentation. If set to YES the scope will be hidden. + +HIDE_SCOPE_NAMES = NO + +# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen +# will put a list of the files that are included by a file in the documentation +# of that file. + +SHOW_INCLUDE_FILES = YES + +# If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen +# will list include files with double quotes in the documentation +# rather than with sharp brackets. + +FORCE_LOCAL_INCLUDES = NO + +# If the INLINE_INFO tag is set to YES (the default) then a tag [inline] +# is inserted in the documentation for inline members. + +INLINE_INFO = YES + +# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen +# will sort the (detailed) documentation of file and class members +# alphabetically by member name. If set to NO the members will appear in +# declaration order. + +SORT_MEMBER_DOCS = YES + +# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the +# brief documentation of file, namespace and class members alphabetically +# by member name. If set to NO (the default) the members will appear in +# declaration order. + +SORT_BRIEF_DOCS = NO + +# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen +# will sort the (brief and detailed) documentation of class members so that +# constructors and destructors are listed first. If set to NO (the default) +# the constructors will appear in the respective orders defined by +# SORT_MEMBER_DOCS and SORT_BRIEF_DOCS. +# This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO +# and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO. + +SORT_MEMBERS_CTORS_1ST = NO + +# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the +# hierarchy of group names into alphabetical order. If set to NO (the default) +# the group names will appear in their defined order. + +SORT_GROUP_NAMES = NO + +# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be +# sorted by fully-qualified names, including namespaces. If set to +# NO (the default), the class list will be sorted only by class name, +# not including the namespace part. +# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. +# Note: This option applies only to the class list, not to the +# alphabetical list. + +SORT_BY_SCOPE_NAME = NO + +# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to +# do proper type resolution of all parameters of a function it will reject a +# match between the prototype and the implementation of a member function even +# if there is only one candidate or it is obvious which candidate to choose +# by doing a simple string match. By disabling STRICT_PROTO_MATCHING doxygen +# will still accept a match between prototype and implementation in such cases. + +STRICT_PROTO_MATCHING = NO + +# The GENERATE_TODOLIST tag can be used to enable (YES) or +# disable (NO) the todo list. This list is created by putting \todo +# commands in the documentation. + +GENERATE_TODOLIST = YES + +# The GENERATE_TESTLIST tag can be used to enable (YES) or +# disable (NO) the test list. This list is created by putting \test +# commands in the documentation. + +GENERATE_TESTLIST = YES + +# The GENERATE_BUGLIST tag can be used to enable (YES) or +# disable (NO) the bug list. This list is created by putting \bug +# commands in the documentation. + +GENERATE_BUGLIST = YES + +# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or +# disable (NO) the deprecated list. This list is created by putting +# \deprecated commands in the documentation. + +GENERATE_DEPRECATEDLIST= YES + +# The ENABLED_SECTIONS tag can be used to enable conditional +# documentation sections, marked by \if sectionname ... \endif. + +ENABLED_SECTIONS = + +# The MAX_INITIALIZER_LINES tag determines the maximum number of lines +# the initial value of a variable or macro consists of for it to appear in +# the documentation. If the initializer consists of more lines than specified +# here it will be hidden. Use a value of 0 to hide initializers completely. +# The appearance of the initializer of individual variables and macros in the +# documentation can be controlled using \showinitializer or \hideinitializer +# command in the documentation regardless of this setting. + +MAX_INITIALIZER_LINES = 30 + +# Set the SHOW_USED_FILES tag to NO to disable the list of files generated +# at the bottom of the documentation of classes and structs. If set to YES the +# list will mention the files that were used to generate the documentation. + +SHOW_USED_FILES = YES + +# If the sources in your project are distributed over multiple directories +# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy +# in the documentation. The default is NO. + +SHOW_DIRECTORIES = NO + +# Set the SHOW_FILES tag to NO to disable the generation of the Files page. +# This will remove the Files entry from the Quick Index and from the +# Folder Tree View (if specified). The default is YES. + +SHOW_FILES = YES + +# Set the SHOW_NAMESPACES tag to NO to disable the generation of the +# Namespaces page. +# This will remove the Namespaces entry from the Quick Index +# and from the Folder Tree View (if specified). The default is YES. + +SHOW_NAMESPACES = YES + +# The FILE_VERSION_FILTER tag can be used to specify a program or script that +# doxygen should invoke to get the current version for each file (typically from +# the version control system). Doxygen will invoke the program by executing (via +# popen()) the command , where is the value of +# the FILE_VERSION_FILTER tag, and is the name of an input file +# provided by doxygen. Whatever the program writes to standard output +# is used as the file version. See the manual for examples. + +FILE_VERSION_FILTER = + +# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed +# by doxygen. The layout file controls the global structure of the generated +# output files in an output format independent way. The create the layout file +# that represents doxygen's defaults, run doxygen with the -l option. +# You can optionally specify a file name after the option, if omitted +# DoxygenLayout.xml will be used as the name of the layout file. + +LAYOUT_FILE = + +#--------------------------------------------------------------------------- +# configuration options related to warning and progress messages +#--------------------------------------------------------------------------- + +# The QUIET tag can be used to turn on/off the messages that are generated +# by doxygen. Possible values are YES and NO. If left blank NO is used. + +QUIET = NO + +# The WARNINGS tag can be used to turn on/off the warning messages that are +# generated by doxygen. Possible values are YES and NO. If left blank +# NO is used. + +WARNINGS = YES + +# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings +# for undocumented members. If EXTRACT_ALL is set to YES then this flag will +# automatically be disabled. + +WARN_IF_UNDOCUMENTED = YES + +# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for +# potential errors in the documentation, such as not documenting some +# parameters in a documented function, or documenting parameters that +# don't exist or using markup commands wrongly. + +WARN_IF_DOC_ERROR = YES + +# The WARN_NO_PARAMDOC option can be enabled to get warnings for +# functions that are documented, but have no documentation for their parameters +# or return value. If set to NO (the default) doxygen will only warn about +# wrong or incomplete parameter documentation, but not about the absence of +# documentation. + +WARN_NO_PARAMDOC = NO + +# The WARN_FORMAT tag determines the format of the warning messages that +# doxygen can produce. The string should contain the $file, $line, and $text +# tags, which will be replaced by the file and line number from which the +# warning originated and the warning text. Optionally the format may contain +# $version, which will be replaced by the version of the file (if it could +# be obtained via FILE_VERSION_FILTER) + +WARN_FORMAT = "$file:$line: $text" + +# The WARN_LOGFILE tag can be used to specify a file to which warning +# and error messages should be written. If left blank the output is written +# to stderr. + +WARN_LOGFILE = + +#--------------------------------------------------------------------------- +# configuration options related to the input files +#--------------------------------------------------------------------------- + +# The INPUT tag can be used to specify the files and/or directories that contain +# documented source files. You may enter file names like "myfile.cpp" or +# directories like "/usr/src/myproject". Separate the files or directories +# with spaces. + +INPUT = src/include src + +# This tag can be used to specify the character encoding of the source files +# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is +# also the default input encoding. Doxygen uses libiconv (or the iconv built +# into libc) for the transcoding. See http://www.gnu.org/software/libiconv for +# the list of possible encodings. + +INPUT_ENCODING = UTF-8 + +# If the value of the INPUT tag contains directories, you can use the +# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank the following patterns are tested: +# *.c *.cc *.cxx *.cpp *.c++ *.d *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh +# *.hxx *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.dox *.py +# *.f90 *.f *.for *.vhd *.vhdl + +FILE_PATTERNS = *.cpp *.h + +# The RECURSIVE tag can be used to turn specify whether or not subdirectories +# should be searched for input files as well. Possible values are YES and NO. +# If left blank NO is used. + +RECURSIVE = NO + +# The EXCLUDE tag can be used to specify files and/or directories that should +# excluded from the INPUT source files. This way you can easily exclude a +# subdirectory from a directory tree whose root is specified with the INPUT tag. + +EXCLUDE = + +# The EXCLUDE_SYMLINKS tag can be used select whether or not files or +# directories that are symbolic links (a Unix file system feature) are excluded +# from the input. + +EXCLUDE_SYMLINKS = NO + +# If the value of the INPUT tag contains directories, you can use the +# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude +# certain files from those directories. Note that the wildcards are matched +# against the file with absolute path, so to exclude all test directories +# for example use the pattern */test/* + +EXCLUDE_PATTERNS = + +# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names +# (namespaces, classes, functions, etc.) that should be excluded from the +# output. The symbol name can be a fully qualified name, a word, or if the +# wildcard * is used, a substring. Examples: ANamespace, AClass, +# AClass::ANamespace, ANamespace::*Test + +EXCLUDE_SYMBOLS = + +# The EXAMPLE_PATH tag can be used to specify one or more files or +# directories that contain example code fragments that are included (see +# the \include command). + +EXAMPLE_PATH = + +# If the value of the EXAMPLE_PATH tag contains directories, you can use the +# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank all files are included. + +EXAMPLE_PATTERNS = + +# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be +# searched for input files to be used with the \include or \dontinclude +# commands irrespective of the value of the RECURSIVE tag. +# Possible values are YES and NO. If left blank NO is used. + +EXAMPLE_RECURSIVE = NO + +# The IMAGE_PATH tag can be used to specify one or more files or +# directories that contain image that are included in the documentation (see +# the \image command). + +IMAGE_PATH = + +# The INPUT_FILTER tag can be used to specify a program that doxygen should +# invoke to filter for each input file. Doxygen will invoke the filter program +# by executing (via popen()) the command , where +# is the value of the INPUT_FILTER tag, and is the name of an +# input file. Doxygen will then use the output that the filter program writes +# to standard output. +# If FILTER_PATTERNS is specified, this tag will be +# ignored. + +INPUT_FILTER = + +# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern +# basis. +# Doxygen will compare the file name with each pattern and apply the +# filter if there is a match. +# The filters are a list of the form: +# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further +# info on how filters are used. If FILTER_PATTERNS is empty or if +# non of the patterns match the file name, INPUT_FILTER is applied. + +FILTER_PATTERNS = + +# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using +# INPUT_FILTER) will be used to filter the input files when producing source +# files to browse (i.e. when SOURCE_BROWSER is set to YES). + +FILTER_SOURCE_FILES = NO + +# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file +# pattern. A pattern will override the setting for FILTER_PATTERN (if any) +# and it is also possible to disable source filtering for a specific pattern +# using *.ext= (so without naming a filter). This option only has effect when +# FILTER_SOURCE_FILES is enabled. + +FILTER_SOURCE_PATTERNS = + +#--------------------------------------------------------------------------- +# configuration options related to source browsing +#--------------------------------------------------------------------------- + +# If the SOURCE_BROWSER tag is set to YES then a list of source files will +# be generated. Documented entities will be cross-referenced with these sources. +# Note: To get rid of all source code in the generated output, make sure also +# VERBATIM_HEADERS is set to NO. + +SOURCE_BROWSER = NO + +# Setting the INLINE_SOURCES tag to YES will include the body +# of functions and classes directly in the documentation. + +INLINE_SOURCES = NO + +# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct +# doxygen to hide any special comment blocks from generated source code +# fragments. Normal C and C++ comments will always remain visible. + +STRIP_CODE_COMMENTS = YES + +# If the REFERENCED_BY_RELATION tag is set to YES +# then for each documented function all documented +# functions referencing it will be listed. + +REFERENCED_BY_RELATION = NO + +# If the REFERENCES_RELATION tag is set to YES +# then for each documented function all documented entities +# called/used by that function will be listed. + +REFERENCES_RELATION = NO + +# If the REFERENCES_LINK_SOURCE tag is set to YES (the default) +# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from +# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will +# link to the source code. +# Otherwise they will link to the documentation. + +REFERENCES_LINK_SOURCE = YES + +# If the USE_HTAGS tag is set to YES then the references to source code +# will point to the HTML generated by the htags(1) tool instead of doxygen +# built-in source browser. The htags tool is part of GNU's global source +# tagging system (see http://www.gnu.org/software/global/global.html). You +# will need version 4.8.6 or higher. + +USE_HTAGS = NO + +# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen +# will generate a verbatim copy of the header file for each class for +# which an include is specified. Set to NO to disable this. + +VERBATIM_HEADERS = YES + +#--------------------------------------------------------------------------- +# configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- + +# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index +# of all compounds will be generated. Enable this if the project +# contains a lot of classes, structs, unions or interfaces. + +ALPHABETICAL_INDEX = YES + +# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then +# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns +# in which this list will be split (can be a number in the range [1..20]) + +COLS_IN_ALPHA_INDEX = 5 + +# In case all classes in a project start with a common prefix, all +# classes will be put under the same header in the alphabetical index. +# The IGNORE_PREFIX tag can be used to specify one or more prefixes that +# should be ignored while generating the index headers. + +IGNORE_PREFIX = + +#--------------------------------------------------------------------------- +# configuration options related to the HTML output +#--------------------------------------------------------------------------- + +# If the GENERATE_HTML tag is set to YES (the default) Doxygen will +# generate HTML output. + +GENERATE_HTML = YES + +# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `html' will be used as the default path. + +HTML_OUTPUT = html + +# The HTML_FILE_EXTENSION tag can be used to specify the file extension for +# each generated HTML page (for example: .htm,.php,.asp). If it is left blank +# doxygen will generate files with .html extension. + +HTML_FILE_EXTENSION = .html + +# The HTML_HEADER tag can be used to specify a personal HTML header for +# each generated HTML page. If it is left blank doxygen will generate a +# standard header. Note that when using a custom header you are responsible +# for the proper inclusion of any scripts and style sheets that doxygen +# needs, which is dependent on the configuration options used. +# It is adviced to generate a default header using "doxygen -w html +# header.html footer.html stylesheet.css YourConfigFile" and then modify +# that header. Note that the header is subject to change so you typically +# have to redo this when upgrading to a newer version of doxygen or when changing the value of configuration settings such as GENERATE_TREEVIEW! + +HTML_HEADER = + +# The HTML_FOOTER tag can be used to specify a personal HTML footer for +# each generated HTML page. If it is left blank doxygen will generate a +# standard footer. + +HTML_FOOTER = + +# The HTML_STYLESHEET tag can be used to specify a user-defined cascading +# style sheet that is used by each HTML page. It can be used to +# fine-tune the look of the HTML output. If the tag is left blank doxygen +# will generate a default style sheet. Note that doxygen will try to copy +# the style sheet file to the HTML output directory, so don't put your own +# stylesheet in the HTML output directory as well, or it will be erased! + +HTML_STYLESHEET = + +# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or +# other source files which should be copied to the HTML output directory. Note +# that these files will be copied to the base HTML output directory. Use the +# $relpath$ marker in the HTML_HEADER and/or HTML_FOOTER files to load these +# files. In the HTML_STYLESHEET file, use the file name only. Also note that +# the files will be copied as-is; there are no commands or markers available. + +HTML_EXTRA_FILES = + +# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. +# Doxygen will adjust the colors in the stylesheet and background images +# according to this color. Hue is specified as an angle on a colorwheel, +# see http://en.wikipedia.org/wiki/Hue for more information. +# For instance the value 0 represents red, 60 is yellow, 120 is green, +# 180 is cyan, 240 is blue, 300 purple, and 360 is red again. +# The allowed range is 0 to 359. + +HTML_COLORSTYLE_HUE = 220 + +# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of +# the colors in the HTML output. For a value of 0 the output will use +# grayscales only. A value of 255 will produce the most vivid colors. + +HTML_COLORSTYLE_SAT = 100 + +# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to +# the luminance component of the colors in the HTML output. Values below +# 100 gradually make the output lighter, whereas values above 100 make +# the output darker. The value divided by 100 is the actual gamma applied, +# so 80 represents a gamma of 0.8, The value 220 represents a gamma of 2.2, +# and 100 does not change the gamma. + +HTML_COLORSTYLE_GAMMA = 80 + +# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML +# page will contain the date and time when the page was generated. Setting +# this to NO can help when comparing the output of multiple runs. + +HTML_TIMESTAMP = YES + +# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, +# files or namespaces will be aligned in HTML using tables. If set to +# NO a bullet list will be used. + +HTML_ALIGN_MEMBERS = YES + +# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML +# documentation will contain sections that can be hidden and shown after the +# page has loaded. For this to work a browser that supports +# JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox +# Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari). + +HTML_DYNAMIC_SECTIONS = NO + +# If the GENERATE_DOCSET tag is set to YES, additional index files +# will be generated that can be used as input for Apple's Xcode 3 +# integrated development environment, introduced with OSX 10.5 (Leopard). +# To create a documentation set, doxygen will generate a Makefile in the +# HTML output directory. Running make will produce the docset in that +# directory and running "make install" will install the docset in +# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find +# it at startup. +# See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html +# for more information. + +GENERATE_DOCSET = NO + +# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the +# feed. A documentation feed provides an umbrella under which multiple +# documentation sets from a single provider (such as a company or product suite) +# can be grouped. + +DOCSET_FEEDNAME = "Doxygen generated docs" + +# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that +# should uniquely identify the documentation set bundle. This should be a +# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen +# will append .docset to the name. + +DOCSET_BUNDLE_ID = org.doxygen.Project + +# When GENERATE_PUBLISHER_ID tag specifies a string that should uniquely identify +# the documentation publisher. This should be a reverse domain-name style +# string, e.g. com.mycompany.MyDocSet.documentation. + +DOCSET_PUBLISHER_ID = org.doxygen.Publisher + +# The GENERATE_PUBLISHER_NAME tag identifies the documentation publisher. + +DOCSET_PUBLISHER_NAME = Publisher + +# If the GENERATE_HTMLHELP tag is set to YES, additional index files +# will be generated that can be used as input for tools like the +# Microsoft HTML help workshop to generate a compiled HTML help file (.chm) +# of the generated HTML documentation. + +GENERATE_HTMLHELP = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can +# be used to specify the file name of the resulting .chm file. You +# can add a path in front of the file if the result should not be +# written to the html output directory. + +CHM_FILE = + +# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can +# be used to specify the location (absolute path including file name) of +# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run +# the HTML help compiler on the generated index.hhp. + +HHC_LOCATION = + +# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag +# controls if a separate .chi index file is generated (YES) or that +# it should be included in the master .chm file (NO). + +GENERATE_CHI = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING +# is used to encode HtmlHelp index (hhk), content (hhc) and project file +# content. + +CHM_INDEX_ENCODING = + +# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag +# controls whether a binary table of contents is generated (YES) or a +# normal table of contents (NO) in the .chm file. + +BINARY_TOC = NO + +# The TOC_EXPAND flag can be set to YES to add extra items for group members +# to the contents of the HTML help documentation and to the tree view. + +TOC_EXPAND = NO + +# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and +# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated +# that can be used as input for Qt's qhelpgenerator to generate a +# Qt Compressed Help (.qch) of the generated HTML documentation. + +GENERATE_QHP = NO + +# If the QHG_LOCATION tag is specified, the QCH_FILE tag can +# be used to specify the file name of the resulting .qch file. +# The path specified is relative to the HTML output folder. + +QCH_FILE = + +# The QHP_NAMESPACE tag specifies the namespace to use when generating +# Qt Help Project output. For more information please see +# http://doc.trolltech.com/qthelpproject.html#namespace + +QHP_NAMESPACE = org.doxygen.Project + +# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating +# Qt Help Project output. For more information please see +# http://doc.trolltech.com/qthelpproject.html#virtual-folders + +QHP_VIRTUAL_FOLDER = doc + +# If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to +# add. For more information please see +# http://doc.trolltech.com/qthelpproject.html#custom-filters + +QHP_CUST_FILTER_NAME = + +# The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the +# custom filter to add. For more information please see +# +# Qt Help Project / Custom Filters. + +QHP_CUST_FILTER_ATTRS = + +# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this +# project's +# filter section matches. +# +# Qt Help Project / Filter Attributes. + +QHP_SECT_FILTER_ATTRS = + +# If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can +# be used to specify the location of Qt's qhelpgenerator. +# If non-empty doxygen will try to run qhelpgenerator on the generated +# .qhp file. + +QHG_LOCATION = + +# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files +# will be generated, which together with the HTML files, form an Eclipse help +# plugin. To install this plugin and make it available under the help contents +# menu in Eclipse, the contents of the directory containing the HTML and XML +# files needs to be copied into the plugins directory of eclipse. The name of +# the directory within the plugins directory should be the same as +# the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted before +# the help appears. + +GENERATE_ECLIPSEHELP = NO + +# A unique identifier for the eclipse help plugin. When installing the plugin +# the directory name containing the HTML and XML files should also have +# this name. + +ECLIPSE_DOC_ID = org.doxygen.Project + +# The DISABLE_INDEX tag can be used to turn on/off the condensed index at +# top of each HTML page. The value NO (the default) enables the index and +# the value YES disables it. + +DISABLE_INDEX = NO + +# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values +# (range [0,1..20]) that doxygen will group on one line in the generated HTML +# documentation. Note that a value of 0 will completely suppress the enum +# values from appearing in the overview section. + +ENUM_VALUES_PER_LINE = 4 + +# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index +# structure should be generated to display hierarchical information. +# If the tag value is set to YES, a side panel will be generated +# containing a tree-like index structure (just like the one that +# is generated for HTML Help). For this to work a browser that supports +# JavaScript, DHTML, CSS and frames is required (i.e. any modern browser). +# Windows users are probably better off using the HTML help feature. + +GENERATE_TREEVIEW = NO + +# By enabling USE_INLINE_TREES, doxygen will generate the Groups, Directories, +# and Class Hierarchy pages using a tree view instead of an ordered list. + +USE_INLINE_TREES = NO + +# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be +# used to set the initial width (in pixels) of the frame in which the tree +# is shown. + +TREEVIEW_WIDTH = 250 + +# When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open +# links to external symbols imported via tag files in a separate window. + +EXT_LINKS_IN_WINDOW = NO + +# Use this tag to change the font size of Latex formulas included +# as images in the HTML documentation. The default is 10. Note that +# when you change the font size after a successful doxygen run you need +# to manually remove any form_*.png images from the HTML output directory +# to force them to be regenerated. + +FORMULA_FONTSIZE = 10 + +# Use the FORMULA_TRANPARENT tag to determine whether or not the images +# generated for formulas are transparent PNGs. Transparent PNGs are +# not supported properly for IE 6.0, but are supported on all modern browsers. +# Note that when changing this option you need to delete any form_*.png files +# in the HTML output before the changes have effect. + +FORMULA_TRANSPARENT = YES + +# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax +# (see http://www.mathjax.org) which uses client side Javascript for the +# rendering instead of using prerendered bitmaps. Use this if you do not +# have LaTeX installed or if you want to formulas look prettier in the HTML +# output. When enabled you also need to install MathJax separately and +# configure the path to it using the MATHJAX_RELPATH option. + +USE_MATHJAX = NO + +# When MathJax is enabled you need to specify the location relative to the +# HTML output directory using the MATHJAX_RELPATH option. The destination +# directory should contain the MathJax.js script. For instance, if the mathjax +# directory is located at the same level as the HTML output directory, then +# MATHJAX_RELPATH should be ../mathjax. The default value points to the +# mathjax.org site, so you can quickly see the result without installing +# MathJax, but it is strongly recommended to install a local copy of MathJax +# before deployment. + +MATHJAX_RELPATH = http://www.mathjax.org/mathjax + +# When the SEARCHENGINE tag is enabled doxygen will generate a search box +# for the HTML output. The underlying search engine uses javascript +# and DHTML and should work on any modern browser. Note that when using +# HTML help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets +# (GENERATE_DOCSET) there is already a search function so this one should +# typically be disabled. For large projects the javascript based search engine +# can be slow, then enabling SERVER_BASED_SEARCH may provide a better solution. + +SEARCHENGINE = YES + +# When the SERVER_BASED_SEARCH tag is enabled the search engine will be +# implemented using a PHP enabled web server instead of at the web client +# using Javascript. Doxygen will generate the search PHP script and index +# file to put on the web server. The advantage of the server +# based approach is that it scales better to large projects and allows +# full text search. The disadvantages are that it is more difficult to setup +# and does not have live searching capabilities. + +SERVER_BASED_SEARCH = NO + +#--------------------------------------------------------------------------- +# configuration options related to the LaTeX output +#--------------------------------------------------------------------------- + +# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will +# generate Latex output. + +GENERATE_LATEX = NO + +# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `latex' will be used as the default path. + +LATEX_OUTPUT = latex + +# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be +# invoked. If left blank `latex' will be used as the default command name. +# Note that when enabling USE_PDFLATEX this option is only used for +# generating bitmaps for formulas in the HTML output, but not in the +# Makefile that is written to the output directory. + +LATEX_CMD_NAME = latex + +# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to +# generate index for LaTeX. If left blank `makeindex' will be used as the +# default command name. + +MAKEINDEX_CMD_NAME = makeindex + +# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact +# LaTeX documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_LATEX = NO + +# The PAPER_TYPE tag can be used to set the paper type that is used +# by the printer. Possible values are: a4, letter, legal and +# executive. If left blank a4wide will be used. + +PAPER_TYPE = a4 + +# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX +# packages that should be included in the LaTeX output. + +EXTRA_PACKAGES = + +# The LATEX_HEADER tag can be used to specify a personal LaTeX header for +# the generated latex document. The header should contain everything until +# the first chapter. If it is left blank doxygen will generate a +# standard header. Notice: only use this tag if you know what you are doing! + +LATEX_HEADER = + +# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for +# the generated latex document. The footer should contain everything after +# the last chapter. If it is left blank doxygen will generate a +# standard footer. Notice: only use this tag if you know what you are doing! + +LATEX_FOOTER = + +# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated +# is prepared for conversion to pdf (using ps2pdf). The pdf file will +# contain links (just like the HTML output) instead of page references +# This makes the output suitable for online browsing using a pdf viewer. + +PDF_HYPERLINKS = YES + +# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of +# plain latex in the generated Makefile. Set this option to YES to get a +# higher quality PDF documentation. + +USE_PDFLATEX = YES + +# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. +# command to the generated LaTeX files. This will instruct LaTeX to keep +# running if errors occur, instead of asking the user for help. +# This option is also used when generating formulas in HTML. + +LATEX_BATCHMODE = NO + +# If LATEX_HIDE_INDICES is set to YES then doxygen will not +# include the index chapters (such as File Index, Compound Index, etc.) +# in the output. + +LATEX_HIDE_INDICES = NO + +# If LATEX_SOURCE_CODE is set to YES then doxygen will include +# source code with syntax highlighting in the LaTeX output. +# Note that which sources are shown also depends on other settings +# such as SOURCE_BROWSER. + +LATEX_SOURCE_CODE = NO + +#--------------------------------------------------------------------------- +# configuration options related to the RTF output +#--------------------------------------------------------------------------- + +# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output +# The RTF output is optimized for Word 97 and may not look very pretty with +# other RTF readers or editors. + +GENERATE_RTF = NO + +# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `rtf' will be used as the default path. + +RTF_OUTPUT = rtf + +# If the COMPACT_RTF tag is set to YES Doxygen generates more compact +# RTF documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_RTF = NO + +# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated +# will contain hyperlink fields. The RTF file will +# contain links (just like the HTML output) instead of page references. +# This makes the output suitable for online browsing using WORD or other +# programs which support those fields. +# Note: wordpad (write) and others do not support links. + +RTF_HYPERLINKS = NO + +# Load stylesheet definitions from file. Syntax is similar to doxygen's +# config file, i.e. a series of assignments. You only have to provide +# replacements, missing definitions are set to their default value. + +RTF_STYLESHEET_FILE = + +# Set optional variables used in the generation of an rtf document. +# Syntax is similar to doxygen's config file. + +RTF_EXTENSIONS_FILE = + +#--------------------------------------------------------------------------- +# configuration options related to the man page output +#--------------------------------------------------------------------------- + +# If the GENERATE_MAN tag is set to YES (the default) Doxygen will +# generate man pages + +GENERATE_MAN = NO + +# The MAN_OUTPUT tag is used to specify where the man pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `man' will be used as the default path. + +MAN_OUTPUT = man + +# The MAN_EXTENSION tag determines the extension that is added to +# the generated man pages (default is the subroutine's section .3) + +MAN_EXTENSION = .3 + +# If the MAN_LINKS tag is set to YES and Doxygen generates man output, +# then it will generate one additional man file for each entity +# documented in the real man page(s). These additional files +# only source the real man page, but without them the man command +# would be unable to find the correct page. The default is NO. + +MAN_LINKS = NO + +#--------------------------------------------------------------------------- +# configuration options related to the XML output +#--------------------------------------------------------------------------- + +# If the GENERATE_XML tag is set to YES Doxygen will +# generate an XML file that captures the structure of +# the code including all documentation. + +GENERATE_XML = NO + +# The XML_OUTPUT tag is used to specify where the XML pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `xml' will be used as the default path. + +XML_OUTPUT = xml + +# The XML_SCHEMA tag can be used to specify an XML schema, +# which can be used by a validating XML parser to check the +# syntax of the XML files. + +XML_SCHEMA = + +# The XML_DTD tag can be used to specify an XML DTD, +# which can be used by a validating XML parser to check the +# syntax of the XML files. + +XML_DTD = + +# If the XML_PROGRAMLISTING tag is set to YES Doxygen will +# dump the program listings (including syntax highlighting +# and cross-referencing information) to the XML output. Note that +# enabling this will significantly increase the size of the XML output. + +XML_PROGRAMLISTING = YES + +#--------------------------------------------------------------------------- +# configuration options for the AutoGen Definitions output +#--------------------------------------------------------------------------- + +# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will +# generate an AutoGen Definitions (see autogen.sf.net) file +# that captures the structure of the code including all +# documentation. Note that this feature is still experimental +# and incomplete at the moment. + +GENERATE_AUTOGEN_DEF = NO + +#--------------------------------------------------------------------------- +# configuration options related to the Perl module output +#--------------------------------------------------------------------------- + +# If the GENERATE_PERLMOD tag is set to YES Doxygen will +# generate a Perl module file that captures the structure of +# the code including all documentation. Note that this +# feature is still experimental and incomplete at the +# moment. + +GENERATE_PERLMOD = NO + +# If the PERLMOD_LATEX tag is set to YES Doxygen will generate +# the necessary Makefile rules, Perl scripts and LaTeX code to be able +# to generate PDF and DVI output from the Perl module output. + +PERLMOD_LATEX = NO + +# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be +# nicely formatted so it can be parsed by a human reader. +# This is useful +# if you want to understand what is going on. +# On the other hand, if this +# tag is set to NO the size of the Perl module output will be much smaller +# and Perl will parse it just the same. + +PERLMOD_PRETTY = YES + +# The names of the make variables in the generated doxyrules.make file +# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. +# This is useful so different doxyrules.make files included by the same +# Makefile don't overwrite each other's variables. + +PERLMOD_MAKEVAR_PREFIX = + +#--------------------------------------------------------------------------- +# Configuration options related to the preprocessor +#--------------------------------------------------------------------------- + +# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will +# evaluate all C-preprocessor directives found in the sources and include +# files. + +ENABLE_PREPROCESSING = YES + +# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro +# names in the source code. If set to NO (the default) only conditional +# compilation will be performed. Macro expansion can be done in a controlled +# way by setting EXPAND_ONLY_PREDEF to YES. + +MACRO_EXPANSION = NO + +# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES +# then the macro expansion is limited to the macros specified with the +# PREDEFINED and EXPAND_AS_DEFINED tags. + +EXPAND_ONLY_PREDEF = NO + +# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files +# pointed to by INCLUDE_PATH will be searched when a #include is found. + +SEARCH_INCLUDES = YES + +# The INCLUDE_PATH tag can be used to specify one or more directories that +# contain include files that are not input files but should be processed by +# the preprocessor. + +INCLUDE_PATH = + +# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard +# patterns (like *.h and *.hpp) to filter out the header-files in the +# directories. If left blank, the patterns specified with FILE_PATTERNS will +# be used. + +INCLUDE_FILE_PATTERNS = + +# The PREDEFINED tag can be used to specify one or more macro names that +# are defined before the preprocessor is started (similar to the -D option of +# gcc). The argument of the tag is a list of macros of the form: name +# or name=definition (no spaces). If the definition and the = are +# omitted =1 is assumed. To prevent a macro definition from being +# undefined via #undef or recursively expanded use the := operator +# instead of the = operator. + +PREDEFINED = + +# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then +# this tag can be used to specify a list of macro names that should be expanded. +# The macro definition that is found in the sources will be used. +# Use the PREDEFINED tag if you want to use a different macro definition that +# overrules the definition found in the source code. + +EXPAND_AS_DEFINED = + +# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then +# doxygen's preprocessor will remove all references to function-like macros +# that are alone on a line, have an all uppercase name, and do not end with a +# semicolon, because these will confuse the parser if not removed. + +SKIP_FUNCTION_MACROS = YES + +#--------------------------------------------------------------------------- +# Configuration::additions related to external references +#--------------------------------------------------------------------------- + +# The TAGFILES option can be used to specify one or more tagfiles. +# Optionally an initial location of the external documentation +# can be added for each tagfile. The format of a tag file without +# this location is as follows: +# +# TAGFILES = file1 file2 ... +# Adding location for the tag files is done as follows: +# +# TAGFILES = file1=loc1 "file2 = loc2" ... +# where "loc1" and "loc2" can be relative or absolute paths or +# URLs. If a location is present for each tag, the installdox tool +# does not have to be run to correct the links. +# Note that each tag file must have a unique name +# (where the name does NOT include the path) +# If a tag file is not located in the directory in which doxygen +# is run, you must also specify the path to the tagfile here. + +TAGFILES = + +# When a file name is specified after GENERATE_TAGFILE, doxygen will create +# a tag file that is based on the input files it reads. + +GENERATE_TAGFILE = + +# If the ALLEXTERNALS tag is set to YES all external classes will be listed +# in the class index. If set to NO only the inherited external classes +# will be listed. + +ALLEXTERNALS = NO + +# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed +# in the modules index. If set to NO, only the current project's groups will +# be listed. + +EXTERNAL_GROUPS = YES + +# The PERL_PATH should be the absolute path and name of the perl script +# interpreter (i.e. the result of `which perl'). + +PERL_PATH = /usr/bin/perl + +#--------------------------------------------------------------------------- +# Configuration options related to the dot tool +#--------------------------------------------------------------------------- + +# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will +# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base +# or super classes. Setting the tag to NO turns the diagrams off. Note that +# this option also works with HAVE_DOT disabled, but it is recommended to +# install and use dot, since it yields more powerful graphs. + +CLASS_DIAGRAMS = YES + +# You can define message sequence charts within doxygen comments using the \msc +# command. Doxygen will then run the mscgen tool (see +# http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the +# documentation. The MSCGEN_PATH tag allows you to specify the directory where +# the mscgen tool resides. If left empty the tool is assumed to be found in the +# default search path. + +MSCGEN_PATH = + +# If set to YES, the inheritance and collaboration graphs will hide +# inheritance and usage relations if the target is undocumented +# or is not a class. + +HIDE_UNDOC_RELATIONS = YES + +# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is +# available from the path. This tool is part of Graphviz, a graph visualization +# toolkit from AT&T and Lucent Bell Labs. The other options in this section +# have no effect if this option is set to NO (the default) + +HAVE_DOT = YES + +# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is +# allowed to run in parallel. When set to 0 (the default) doxygen will +# base this on the number of processors available in the system. You can set it +# explicitly to a value larger than 0 to get control over the balance +# between CPU load and processing speed. + +DOT_NUM_THREADS = 0 + +# By default doxygen will write a font called Helvetica to the output +# directory and reference it in all dot files that doxygen generates. +# When you want a differently looking font you can specify the font name +# using DOT_FONTNAME. You need to make sure dot is able to find the font, +# which can be done by putting it in a standard location or by setting the +# DOTFONTPATH environment variable or by setting DOT_FONTPATH to the directory +# containing the font. + +DOT_FONTNAME = Ubuntu + +# The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs. +# The default size is 10pt. + +DOT_FONTSIZE = 10 + +# By default doxygen will tell dot to use the output directory to look for the +# FreeSans.ttf font (which doxygen will put there itself). If you specify a +# different font using DOT_FONTNAME you can set the path where dot +# can find it using this tag. + +DOT_FONTPATH = + +# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect inheritance relations. Setting this tag to YES will force the +# the CLASS_DIAGRAMS tag to NO. + +CLASS_GRAPH = YES + +# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect implementation dependencies (inheritance, containment, and +# class references variables) of the class with other documented classes. + +COLLABORATION_GRAPH = YES + +# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for groups, showing the direct groups dependencies + +GROUP_GRAPHS = YES + +# If the UML_LOOK tag is set to YES doxygen will generate inheritance and +# collaboration diagrams in a style similar to the OMG's Unified Modeling +# Language. + +UML_LOOK = YES + +# If set to YES, the inheritance and collaboration graphs will show the +# relations between templates and their instances. + +TEMPLATE_RELATIONS = NO + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT +# tags are set to YES then doxygen will generate a graph for each documented +# file showing the direct and indirect include dependencies of the file with +# other documented files. + +INCLUDE_GRAPH = YES + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and +# HAVE_DOT tags are set to YES then doxygen will generate a graph for each +# documented header file showing the documented files that directly or +# indirectly include this file. + +INCLUDED_BY_GRAPH = YES + +# If the CALL_GRAPH and HAVE_DOT options are set to YES then +# doxygen will generate a call dependency graph for every global function +# or class method. Note that enabling this option will significantly increase +# the time of a run. So in most cases it will be better to enable call graphs +# for selected functions only using the \callgraph command. + +CALL_GRAPH = YES + +# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then +# doxygen will generate a caller dependency graph for every global function +# or class method. Note that enabling this option will significantly increase +# the time of a run. So in most cases it will be better to enable caller +# graphs for selected functions only using the \callergraph command. + +CALLER_GRAPH = YES + +# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen +# will generate a graphical hierarchy of all classes instead of a textual one. + +GRAPHICAL_HIERARCHY = YES + +# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES +# then doxygen will show the dependencies a directory has on other directories +# in a graphical way. The dependency relations are determined by the #include +# relations between the files in the directories. + +DIRECTORY_GRAPH = YES + +# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images +# generated by dot. Possible values are svg, png, jpg, or gif. +# If left blank png will be used. + +DOT_IMAGE_FORMAT = svg + +# The tag DOT_PATH can be used to specify the path where the dot tool can be +# found. If left blank, it is assumed the dot tool can be found in the path. + +DOT_PATH = + +# The DOTFILE_DIRS tag can be used to specify one or more directories that +# contain dot files that are included in the documentation (see the +# \dotfile command). + +DOTFILE_DIRS = + +# The MSCFILE_DIRS tag can be used to specify one or more directories that +# contain msc files that are included in the documentation (see the +# \mscfile command). + +MSCFILE_DIRS = + +# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of +# nodes that will be shown in the graph. If the number of nodes in a graph +# becomes larger than this value, doxygen will truncate the graph, which is +# visualized by representing a node as a red box. Note that doxygen if the +# number of direct children of the root node in a graph is already larger than +# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note +# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. + +DOT_GRAPH_MAX_NODES = 50 + +# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the +# graphs generated by dot. A depth value of 3 means that only nodes reachable +# from the root by following a path via at most 3 edges will be shown. Nodes +# that lay further from the root node will be omitted. Note that setting this +# option to 1 or 2 may greatly reduce the computation time needed for large +# code bases. Also note that the size of a graph can be further restricted by +# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction. + +MAX_DOT_GRAPH_DEPTH = 0 + +# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent +# background. This is disabled by default, because dot on Windows does not +# seem to support this out of the box. Warning: Depending on the platform used, +# enabling this option may lead to badly anti-aliased labels on the edges of +# a graph (i.e. they become hard to read). + +DOT_TRANSPARENT = NO + +# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output +# files in one run (i.e. multiple -o and -T options on the command line). This +# makes dot run faster, but since only newer versions of dot (>1.8.10) +# support this, this feature is disabled by default. + +DOT_MULTI_TARGETS = NO + +# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will +# generate a legend page explaining the meaning of the various boxes and +# arrows in the dot generated graphs. + +GENERATE_LEGEND = YES + +# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will +# remove the intermediate dot files that are used to generate +# the various graphs. + +DOT_CLEANUP = YES diff --git a/CopyQ-3.0.2/HACKING b/CopyQ-3.0.2/HACKING new file mode 100644 index 0000000..e9594b5 --- /dev/null +++ b/CopyQ-3.0.2/HACKING @@ -0,0 +1 @@ +https://github.com/hluk/CopyQ/wiki/Development diff --git a/CopyQ-3.0.2/INSTALL b/CopyQ-3.0.2/INSTALL new file mode 100644 index 0000000..d01cde8 --- /dev/null +++ b/CopyQ-3.0.2/INSTALL @@ -0,0 +1,30 @@ +Steps to build and install +========================== + +To build and install the application CMake is required (http://www.cmake.org/). + +On Ubuntu you'll need packages libqt4-dev, cmake, libxfixes-dev, +libxtst-dev (optional; auto-paste into some applications), +libqtwebkit-dev (optional; advanced HTML rendering). + +Build with following commands: + + cmake . + make + +To install run (sudo is required for root privileges on Linux): + + sudo make install + + +Building and Packaging for OS X +------------------------------- + +To build and install on OS X, you will need `qmake` version +5.2 or greater. Build with the following commands: + + ~/Qt/5.2.0/clang_64/bin/qmake CONFIG+=release WITH_WEBKIT=1 + make copyq.app + +This will produce a self-contained application bundle `copyq.app` +which can then be copied or moved into `/Applications`. diff --git a/CopyQ-3.0.2/LICENSE b/CopyQ-3.0.2/LICENSE new file mode 100644 index 0000000..94a9ed0 --- /dev/null +++ b/CopyQ-3.0.2/LICENSE @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/CopyQ-3.0.2/README.md b/CopyQ-3.0.2/README.md new file mode 100644 index 0000000..803014f --- /dev/null +++ b/CopyQ-3.0.2/README.md @@ -0,0 +1,210 @@ +# CopyQ + +[![Translation Status](https://hosted.weblate.org/widgets/copyq/-/svg-badge.svg)](https://hosted.weblate.org/engage/copyq/?utm_source=widget) +[![Build Status](https://travis-ci.org/hluk/CopyQ.svg?branch=master)](https://travis-ci.org/hluk/CopyQ) +[![Windows Build Status](https://ci.appveyor.com/api/projects/status/github/hluk/copyq?branch=master&svg=true)](https://ci.appveyor.com/project/hluk/copyq) +[![Coverage Status](https://coveralls.io/repos/hluk/CopyQ/badge.svg?branch=master)](https://coveralls.io/r/hluk/CopyQ?branch=master) + +CopyQ is advanced clipboard manager with editing and scripting features. +- [Downloads](https://github.com/hluk/CopyQ/releases) +- [Web Site](https://hluk.github.io/CopyQ/) +- [Wiki](https://github.com/hluk/CopyQ/wiki) +- [Mailing List](https://groups.google.com/group/copyq) +- [Bug Reports](https://github.com/hluk/CopyQ/issues) +- [Donate](https://www.bountysource.com/teams/copyq) +- [Scripting Reference](https://github.com/hluk/CopyQ/blob/master/src/scriptable/README.md) + +## Overview + +CopyQ monitors system clipboard and saves its content in customized tabs. +Saved clipboard can be later copied and pasted directly into any application. + +Items can be: + +* edited with internal editor or with preferred text editor, +* moved to other tabs, +* drag'n'dropped to applications, +* marked with tag or a note, +* passed to or changed by custom commands, +* or simply removed. + +## Features + +* Support for Linux, Windows and OS X 10.9+ +* Store text, HTML, images or any other custom formats +* Quickly browse and filter items in clipboard history +* Sort, create, edit, remove, copy/paste, drag'n'drop items in tabs +* Add notes or tags to items +* System-wide shortcuts with customizable commands +* Paste items with shortcut or from tray or main window +* Fully customizable appearance +* Advanced command-line interface and scripting +* Ignore clipboard copied from some windows or containing some text +* Support for simple Vim-like editor and shortcuts +* Many more features + +## Install and Run + +To install CopyQ, use the binary package or installer provided for your system. For system-specific information, please see below. For unlisted systems, please follow the instructions in +[INSTALL](https://github.com/hluk/CopyQ/blob/master/INSTALL) to build the +application. + +### Windows + +On Windows you can install [Chocolatey package](https://chocolatey.org/packages/copyq). + +### Ubuntu + +Install and keep CopyQ always up to date by running the following three commands from the terminal: + +```bash +$ sudo add-apt-repository ppa:hluk/copyq +$ sudo apt update +$ sudo apt install copyq +``` + +### OS X + +On OS X you can use [Homebrew](https://brew.sh/) to install the app. + +```bash +brew cask install copyq +``` + +## Using the App + +To start the application double-click the program icon or run `copyq`. + +The list with clipboard history is accessible by clicking on system tray icon +or running `copyq toggle`. + +Copying text or image to clipboard will create new item in the list. + +Selected items can be: +* edited (`F2`), +* removed (`Delete`), +* sorted (`Ctrl+Shift+S`, `Ctrl+Shift+R`), +* moved around (with mouse or `Ctrl+Up/Down`) or +* copied back to clipboard (`Enter`, `Ctrl+V`). + +All items will be restored when application is started next time. + +To exit the application select Exit from tray menu or press Ctrl-Q keys in the +application window. + +Read more: +- [Basic Usage](https://github.com/hluk/CopyQ/wiki/Basic-Usage) +- [Keyboard](https://github.com/hluk/CopyQ/wiki/Keyboard) + +### Adding Funcionality + +To create custom action that can be executed +from menu, with shortcut or when clipboard changes: +- go to Command dialog (`F6` shortcut), +- click Add button and select predefined command or create new one, +- optionally change the command details (shortcut, name), +- click OK to save the command. + +One of very useful predefined commands there is "Show/hide main window". + +Read more: +- [Writing Commands](https://github.com/hluk/CopyQ/wiki/Writing-Commands-and-Adding-Functionality) +- [CopyQ Commands Repository](https://github.com/hluk/copyq-commands) + +### Command Line + +CopyQ has powerful command line and scripting interface. + +Note: The main application must be running to be able to issue commands using +command line. + +Print help for some useful command line arguments: + + copyq --help + copyq --help add + +Insert some texts to the history: + + copyq add "first item" "second item" "third item" + +Print content of the first three items: + + copyq read 0 1 2 + copyq separator "," read 0 1 2 + +Show current clipboard content: + + copyq clipboard + copyq clipboard text/html + copyq clipboard \? # lists formats in clipboard + +Copy text to the clipboard: + + copyq copy "Some Text" + +Load file content into clipboard: + + copyq copy - < file.txt + copyq copy text/html < index.html + copyq copy image/jpeg - < image.jpg + +Create an image items: + + copyq write image/gif - < image.gif + copyq write image/svg - < image.svg + +Read more: +- [Scripting](https://github.com/hluk/CopyQ/wiki/Scripting) +- [Scripting Reference](https://github.com/hluk/CopyQ/blob/master/src/scriptable/README.md) + +## Build from Source Code + +To build the application from source code, first install the required dependencies: +- [Git](https://git-scm.com/) +- [CMake](https://cmake.org/download/) +- [Qt](https://download.qt.io/archive/qt/) +- Optionally on Linux/X11: development files and libraries for [Xtst](https://t2-project.org/packages/libxtst.html) and [Xfixes](https://www.x.org/archive/X11R7.5/doc/man/man3/Xfixes.3.html) +- Optionally [QtWebKit](https://trac.webkit.org/wiki/QtWebKit) (more advanced HTML rendering) + +### Ubuntu + +#### Install Dependencies + +```bash +sudo apt install \ + git cmake \ + qtbase5-private-dev \ + qtscript5-dev \ + qttools5-dev \ + qttools5-dev-tools \ + libqt5svg5-dev \ + libxfixes-dev \ + libxtst-dev \ + libqt5svg5 +``` + +#### Build the App + +Change install prefix if needed: + +```bash +git clone https://github.com/hluk/CopyQ.git +cd CopyQ +cmake -DCMAKE_INSTALL_PREFIX=/usr/local . +make +``` + +#### Install the App + +```bash +sudo make install +``` + +## Contributions + +You can help translate the application (click the banner below) +or help [fix issues and implement new features](https://github.com/hluk/CopyQ/issues). + +[![Translations](https://hosted.weblate.org/widgets/copyq/-/287x66-white.png)](https://hosted.weblate.org/engage/copyq/?utm_source=widget) + +See also [Development](https://github.com/hluk/CopyQ/wiki/Development). diff --git a/CopyQ-3.0.2/appveyor.yml b/CopyQ-3.0.2/appveyor.yml new file mode 100644 index 0000000..69ccef5 --- /dev/null +++ b/CopyQ-3.0.2/appveyor.yml @@ -0,0 +1,46 @@ +# Configuration file for AppVeyor CI +configuration: Release + +cache: + - build + +environment: + VCINSTALLDIR: C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\ + + matrix: + - QTDIR: C:\Qt\5.7\mingw53_32 + CMAKE_GENERATOR: MinGW Makefiles + MINGW_PATH: C:\Qt\Tools\mingw530_32 + + - QTDIR: C:\Qt\5.7\msvc2015 + CMAKE_GENERATOR: Visual Studio 14 2015 + VC_VARS_ARCH: x86 + + - QTDIR: C:\Qt\5.7\msvc2015_64 + CMAKE_GENERATOR: Visual Studio 14 2015 Win64 + VC_VARS_ARCH: amd64 + +# Parameters for default build commands (build_script is used instead). +build: + +install: +- utils\appveyor\install.bat + +before_build: +- utils\appveyor\before_build.bat + +build_script: +- utils\appveyor\build_script.bat + +after_build: +- utils\appveyor\after_build.bat + +artifacts: +- path: 'copyq*.zip' + name: CopyQ Portable + +- path: 'copyq-*-setup.exe' + name: CopyQ Setup + +matrix: + fast_finish: true # set this flag to immediately finish build once one of the jobs fails. diff --git a/CopyQ-3.0.2/common.pri b/CopyQ-3.0.2/common.pri new file mode 100644 index 0000000..2ad2720 --- /dev/null +++ b/CopyQ-3.0.2/common.pri @@ -0,0 +1,9 @@ +CONFIG += c++11 + +macx { + QMAKE_MACOSX_DEPLOYMENT_TARGET = 10.9 + QMAKE_MAC_SDK = macosx # work around QTBUG-41238 + + # Only Intel binaries are accepted so force this + CONFIG += x86 +} diff --git a/CopyQ-3.0.2/copyq b/CopyQ-3.0.2/copyq new file mode 100755 index 0000000..548b1fc Binary files /dev/null and b/CopyQ-3.0.2/copyq differ diff --git a/CopyQ-3.0.2/copyq.lang b/CopyQ-3.0.2/copyq.lang new file mode 100644 index 0000000..2d79208 --- /dev/null +++ b/CopyQ-3.0.2/copyq.lang @@ -0,0 +1,22 @@ +%lang(ar) /usr/share/copyq/locale/copyq_ar.qm +%lang(cs) /usr/share/copyq/locale/copyq_cs.qm +%lang(da) /usr/share/copyq/locale/copyq_da.qm +%lang(de) /usr/share/copyq/locale/copyq_de.qm +%lang(es) /usr/share/copyq/locale/copyq_es.qm +%lang(fr) /usr/share/copyq/locale/copyq_fr.qm +%lang(hu) /usr/share/copyq/locale/copyq_hu.qm +%lang(it) /usr/share/copyq/locale/copyq_it.qm +%lang(ja) /usr/share/copyq/locale/copyq_ja.qm +%lang(lt) /usr/share/copyq/locale/copyq_lt.qm +%lang(nb) /usr/share/copyq/locale/copyq_nb.qm +%lang(nl) /usr/share/copyq/locale/copyq_nl.qm +%lang(pl) /usr/share/copyq/locale/copyq_pl.qm +%lang(pt_BR) /usr/share/copyq/locale/copyq_pt_BR.qm +%lang(pt_PT) /usr/share/copyq/locale/copyq_pt_PT.qm +%lang(ru) /usr/share/copyq/locale/copyq_ru.qm +%lang(sk) /usr/share/copyq/locale/copyq_sk.qm +%lang(sv) /usr/share/copyq/locale/copyq_sv.qm +%lang(tr) /usr/share/copyq/locale/copyq_tr.qm +%lang(uk) /usr/share/copyq/locale/copyq_uk.qm +%lang(zh_CN) /usr/share/copyq/locale/copyq_zh_CN.qm +%lang(zh_TW) /usr/share/copyq/locale/copyq_zh_TW.qm diff --git a/CopyQ-3.0.2/copyq.pro b/CopyQ-3.0.2/copyq.pro new file mode 100644 index 0000000..c77903d --- /dev/null +++ b/CopyQ-3.0.2/copyq.pro @@ -0,0 +1,69 @@ +include("./common.pri") + +TEMPLATE = subdirs + +# generate cache file for build +cache() + +DEFINES += QT_NO_CAST_TO_ASCII +SUBDIRS += src \ + plugins +TRANSLATIONS = \ + translations/copyq_ar.ts \ + translations/copyq_cs.ts \ + translations/copyq_da.ts \ + translations/copyq_de.ts \ + translations/copyq_es.ts \ + translations/copyq_fr.ts \ + translations/copyq_hu.ts \ + translations/copyq_it.ts \ + translations/copyq_ja.ts \ + translations/copyq_lt.ts \ + translations/copyq_nb.ts \ + translations/copyq_nl.ts \ + translations/copyq_pl.ts \ + translations/copyq_pt_PT.ts \ + translations/copyq_pt_BR.ts \ + translations/copyq_ru.ts \ + translations/copyq_sk.ts \ + translations/copyq_sv.ts \ + translations/copyq_tr.ts \ + translations/copyq_uk.ts \ + translations/copyq_zh_CN.ts \ + translations/copyq_zh_TW.ts + +macx { + # Package the CopyQ plugins into the app bundle + package_plugins.commands = \ + mkdir -p copyq.app/Contents/PlugIns/copyq/ ; \ + cp plugins/*.dylib copyq.app/Contents/PlugIns/copyq/ + package_plugins.depends = sub-plugins sub-src + QMAKE_EXTRA_TARGETS += package_plugins + + # Package the Qt frameworks into the app bundle + package_frameworks.commands = \ + test -e copyq.app/Contents/Frameworks/QtCore.framework \ + || $$dirname(QMAKE_QMAKE)/macdeployqt copyq.app + package_frameworks.target = copyq.app/Contents/Frameworks/QtCore.framework + package_frameworks.depends = sub-src sub-plugins package_plugins + QMAKE_EXTRA_TARGETS += package_frameworks + + # Package the translations + package_translations.commands = \ + $$dirname(QMAKE_QMAKE)/lrelease $$_PRO_FILE_PWD_/copyq.pro && \ + mkdir -p copyq.app/Contents/Resources/translations && \ + cp $$_PRO_FILE_PWD_/translations/*.qm copyq.app/Contents/Resources/translations + QMAKE_EXTRA_TARGETS += package_translations + + # Package the themes + package_themes.commands = \ + mkdir -p copyq.app/Contents/Resources/themes && \ + cp $$_PRO_FILE_PWD_/shared/themes/*.ini copyq.app/Contents/Resources/themes + QMAKE_EXTRA_TARGETS += package_themes + + # Rename to CopyQ.app to make it look better + bundle_mac.depends = package_frameworks package_plugins package_translations package_themes + bundle_mac.target = CopyQ.app + bundle_mac.commands = mv copyq.app CopyQ.app + QMAKE_EXTRA_TARGETS += bundle_mac +} diff --git a/CopyQ-3.0.2/debian/changelog b/CopyQ-3.0.2/debian/changelog new file mode 100644 index 0000000..d6809bf --- /dev/null +++ b/CopyQ-3.0.2/debian/changelog @@ -0,0 +1,5 @@ +copyq (2.9.0~xenial) xenial; urgency=medium + + * v2.9.0 + + -- Lukas Holecek Fri, 10 Mar 2017 10:10:00 +0200 diff --git a/CopyQ-3.0.2/debian/compat b/CopyQ-3.0.2/debian/compat new file mode 100644 index 0000000..ec63514 --- /dev/null +++ b/CopyQ-3.0.2/debian/compat @@ -0,0 +1 @@ +9 diff --git a/CopyQ-3.0.2/debian/control b/CopyQ-3.0.2/debian/control new file mode 100644 index 0000000..f925c66 --- /dev/null +++ b/CopyQ-3.0.2/debian/control @@ -0,0 +1,45 @@ +Source: copyq +Section: misc +Priority: optional +Maintainer: Lukas Holecek +Build-Depends: debhelper (>= 9), cmake + ,qtbase5-private-dev + ,qtscript5-dev + ,qttools5-dev + ,qttools5-dev-tools + ,libqt5svg5-dev + ,libxfixes-dev + ,libxtst-dev +Standards-Version: 3.9.7 +Homepage: https://hluk.github.io/CopyQ/ +Vcs-Browser: https://github.com/hluk/CopyQ +Vcs-Git: https://github.com/hluk/CopyQ.git + +Package: copyq +Architecture: any +Depends: ${misc:Depends}, ${shlibs:Depends}, libqt5svg5 +Description: Advanced clipboard manager with editing and scripting features + CopyQ monitors system clipboard and saves its content in customized tabs. + Saved clipboard can be later copied and pasted directly into any application. + . + Items can be: + * edited with internal editor or with preferred text editor, + * moved to other tabs, + * drag'n'dropped to applications, + * marked with tag or a note, + * passed to or changed by custom commands, + * or simply removed. + . + Features: + * Support for Linux, Windows and OS X 10.9+ + * Store text, HTML, images or any other custom formats + * Quickly browse and filter items in clipboard history + * Sort, create, edit, remove, copy/paste, drag'n'drop items in tabs + * Add notes or tags to items + * System-wide shortcuts with customizable commands + * Paste items with shortcut or from tray or main window + * Fully customizable appearance + * Advanced command-line interface and scripting + * Ignore clipboard copied from some windows or containing some text + * Support for simple Vim-like editor and shortcuts + * Many more features diff --git a/CopyQ-3.0.2/debian/control-qt4 b/CopyQ-3.0.2/debian/control-qt4 new file mode 100644 index 0000000..91bb0dc --- /dev/null +++ b/CopyQ-3.0.2/debian/control-qt4 @@ -0,0 +1,41 @@ +Source: copyq +Section: misc +Priority: optional +Maintainer: Lukas Holecek +Build-Depends: debhelper (>= 9), cmake + , libqt4-dev + , libxfixes-dev + , libxtst-dev +Standards-Version: 3.9.7 +Homepage: https://hluk.github.io/CopyQ/ +Vcs-Browser: https://github.com/hluk/CopyQ +Vcs-Git: https://github.com/hluk/CopyQ.git + +Package: copyq +Architecture: any +Depends: ${misc:Depends}, ${shlibs:Depends}, libqt4-xml, libqt4-network, libqt4-script, libqt4-svg +Description: Advanced clipboard manager with editing and scripting features + CopyQ monitors system clipboard and saves its content in customized tabs. + Saved clipboard can be later copied and pasted directly into any application. + . + Items can be: + * edited with internal editor or with preferred text editor, + * moved to other tabs, + * drag'n'dropped to applications, + * marked with tag or a note, + * passed to or changed by custom commands, + * or simply removed. + . + Features: + * Support for Linux, Windows and OS X 10.9+ + * Store text, HTML, images or any other custom formats + * Quickly browse and filter items in clipboard history + * Sort, create, edit, remove, copy/paste, drag'n'drop items in tabs + * Add notes or tags to items + * System-wide shortcuts with customizable commands + * Paste items with shortcut or from tray or main window + * Fully customizable appearance + * Advanced command-line interface and scripting + * Ignore clipboard copied from some windows or containing some text + * Support for simple Vim-like editor and shortcuts + * Many more features diff --git a/CopyQ-3.0.2/debian/copyright b/CopyQ-3.0.2/debian/copyright new file mode 100644 index 0000000..4cd4d09 --- /dev/null +++ b/CopyQ-3.0.2/debian/copyright @@ -0,0 +1,124 @@ +Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ +Upstream-Name: CopyQ +Upstream-Contact: Lukas Holecek +Source: https://github.com/hluk/CopyQ +Files-Excluded: src/images/*.ttf + +Files: * +Copyright: 2009-2017 Lukas Holecek +License: GPL-3+ +Comment: + CopyQ is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + . + CopyQ is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + +Files: src/images/fontawesome-webfont.ttf +Copyright: Font Awesome by Dave Gandy - http://fontawesome.io +License: SIL OFL 1.1 +URL: http://scripts.sil.org/OFL + +Files: + src/gui/execmenu.* + src/gui/fancylineedit.* + src/gui/filterlineedit.* +Copyright: 2014 Digia Plc and/or its subsidiary(-ies). +License: LGPL-2.1 or LGPL-3 +Comment: + In addition, as a special exception, Digia gives you certain additional + rights. These rights are described in the Digia Qt LGPL Exception + version 1.1. + . + -------- + Digia Qt LGPL Exception version 1.1 + . + As a special exception to the GNU Lesser General Public License version + 2.1, the object code form of a "work that uses the Library" may + incorporate material from a header file that is part of the Library. You + may distribute such object code under terms of your choice, provided that + the incorporated material (i) does not exceed more than 5% of the total + size of the Library; and (ii) is limited to numerical parameters, data + structure layouts, accessors, macros, inline functions and templates. + -------- + http://doc.qt.io/qt-5/lgpl.html + +Files: qxt/* +Copyright: 2006-2011, the LibQxt project. +License: BSD-3-clause + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the LibQxt project nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + . + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +Files: debian/* +Copyright: Dmitry Smirnov +License: GPL-3+ + +License: GPL-3+ + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + . + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + . + The complete text of the GNU General Public License can be found + in "/usr/share/common-licenses/GPL-3". + +License: LGPL-2.1 + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License version 2.1 as published by the Free Software Foundation. + . + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + . + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + . + The complete text of the GNU Lesser General Public License + can be found in "/usr/share/common-licenses/LGPL-2.1". + +License: LGPL-3 + This package is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License version 3 + as published by the Free Software Foundation. + . + This package is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + . + You should have received a copy of the GNU Lesser General Public License + along with this program. If not, see + . + The complete text fo the GNU Lesser General Public License version 3 + can be found in "/usr/share/common-licenses/LGPL-3" diff --git a/CopyQ-3.0.2/debian/rules b/CopyQ-3.0.2/debian/rules new file mode 100644 index 0000000..26ccbb9 --- /dev/null +++ b/CopyQ-3.0.2/debian/rules @@ -0,0 +1,17 @@ +#!/usr/bin/make -f + +# Uncomment this to turn on verbose mode. +#export DH_VERBOSE=1 + +export DEB_BUILD_MAINT_OPTIONS = hardening=+all +export DEB_LDFLAGS_MAINT_APPEND += -Wl,--as-needed +export QT_SELECT=5 + +%: + dh $@ + +override_dh_auto_configure: + dh_auto_configure -- \ + -DCMAKE_VERBOSE_MAKEFILE=ON \ + -DCMAKE_BUILD_TYPE=Release \ + -DWITH_WEBKIT=1 diff --git a/CopyQ-3.0.2/debian/rules-qt4 b/CopyQ-3.0.2/debian/rules-qt4 new file mode 100644 index 0000000..5570df8 --- /dev/null +++ b/CopyQ-3.0.2/debian/rules-qt4 @@ -0,0 +1,17 @@ +#!/usr/bin/make -f + +# Uncomment this to turn on verbose mode. +#export DH_VERBOSE=1 + +export DEB_BUILD_MAINT_OPTIONS = hardening=+all +export DEB_LDFLAGS_MAINT_APPEND += -Wl,--as-needed + +%: + dh $@ + +override_dh_auto_configure: + dh_auto_configure -- \ + -DCMAKE_VERBOSE_MAKEFILE=ON \ + -DCMAKE_BUILD_TYPE=Release \ + -DWITH_QT5=OFF \ + -DWITH_WEBKIT=1 diff --git a/CopyQ-3.0.2/debugfiles.list b/CopyQ-3.0.2/debugfiles.list new file mode 100644 index 0000000..d63427f --- /dev/null +++ b/CopyQ-3.0.2/debugfiles.list @@ -0,0 +1,55 @@ +%dir /usr/lib/debug +%dir /usr/lib/debug/usr +%dir /usr/lib/debug/usr/bin +%dir /usr/lib/debug/usr/lib64 +%dir /usr/lib/debug/usr/lib64/copyq +%dir /usr/lib/debug/usr/lib64/copyq/plugins +%dir /usr/lib/debug/.build-id +%dir /usr/lib/debug/.build-id/39 +%dir /usr/lib/debug/.build-id/40 +%dir /usr/lib/debug/.build-id/50 +%dir /usr/lib/debug/.build-id/e9 +%dir /usr/lib/debug/.build-id/ec +%dir /usr/lib/debug/.build-id/25 +%dir /usr/lib/debug/.build-id/3c +%dir /usr/lib/debug/.build-id/92 +%dir /usr/lib/debug/.build-id/48 +%dir /usr/lib/debug/.build-id/ca +%dir /usr/lib/debug/.build-id/c9 +%dir /usr/lib/debug/.dwz +/usr/lib/debug/usr/bin/copyq.debug +/usr/lib/debug/usr/lib64/copyq/plugins/libitemdata.so.debug +/usr/lib/debug/usr/lib64/copyq/plugins/libitemencrypted.so.debug +/usr/lib/debug/usr/lib64/copyq/plugins/libitemfakevim.so.debug +/usr/lib/debug/usr/lib64/copyq/plugins/libitemimage.so.debug +/usr/lib/debug/usr/lib64/copyq/plugins/libitemnotes.so.debug +/usr/lib/debug/usr/lib64/copyq/plugins/libitempinned.so.debug +/usr/lib/debug/usr/lib64/copyq/plugins/libitemtags.so.debug +/usr/lib/debug/usr/lib64/copyq/plugins/libitemtext.so.debug +/usr/lib/debug/usr/lib64/copyq/plugins/libitemsync.so.debug +/usr/lib/debug/usr/lib64/copyq/plugins/libitemweb.so.debug +/usr/lib/debug/.build-id/39/6fb26cad0769eacd7af5a39c7fc2e9c51ea8d3 +/usr/lib/debug/.build-id/39/6fb26cad0769eacd7af5a39c7fc2e9c51ea8d3.debug +/usr/lib/debug/.build-id/40/556e992b3e12ba6de9d1882d30b7e73824ba79 +/usr/lib/debug/.build-id/40/556e992b3e12ba6de9d1882d30b7e73824ba79.debug +/usr/lib/debug/.build-id/50/851b8d260a05ae088cc78dcaa6a102464472b2 +/usr/lib/debug/.build-id/50/851b8d260a05ae088cc78dcaa6a102464472b2.debug +/usr/lib/debug/.build-id/e9/4627e58f07e29f32c365d4d028acbdd0e89921 +/usr/lib/debug/.build-id/e9/4627e58f07e29f32c365d4d028acbdd0e89921.debug +/usr/lib/debug/.build-id/ec/a114fc00cc483f6b814421f97ac456e0d12067 +/usr/lib/debug/.build-id/ec/a114fc00cc483f6b814421f97ac456e0d12067.debug +/usr/lib/debug/.build-id/25/d434b121abb4897672d69bb712ce1be3d655f9 +/usr/lib/debug/.build-id/25/d434b121abb4897672d69bb712ce1be3d655f9.debug +/usr/lib/debug/.build-id/3c/710c9050a1f474964b4a1386e82800e2c5e62d +/usr/lib/debug/.build-id/3c/710c9050a1f474964b4a1386e82800e2c5e62d.debug +/usr/lib/debug/.build-id/3c/0e7d1c8fdaa374d4ed98e50bfd758e379ae062 +/usr/lib/debug/.build-id/3c/0e7d1c8fdaa374d4ed98e50bfd758e379ae062.debug +/usr/lib/debug/.build-id/92/dc76b6c1c5f939a944e21c7a83618f5f77cc3d +/usr/lib/debug/.build-id/92/dc76b6c1c5f939a944e21c7a83618f5f77cc3d.debug +/usr/lib/debug/.build-id/48/9fe1d84c8799004869412a6e133a5e1a105b97 +/usr/lib/debug/.build-id/48/9fe1d84c8799004869412a6e133a5e1a105b97.debug +/usr/lib/debug/.build-id/ca/536be7713f249df8c76ab5378c30eed1944769 +/usr/lib/debug/.build-id/ca/536be7713f249df8c76ab5378c30eed1944769.debug +/usr/lib/debug/.build-id/c9/c11dd588b71df9b878fb201fe399e64a211f6e.debug +/usr/lib/debug/.dwz/copyq-3.0.2-1.fc27.x86_64 +/usr/src/debug/CopyQ-3.0.2 diff --git a/CopyQ-3.0.2/debuglinks.list b/CopyQ-3.0.2/debuglinks.list new file mode 100644 index 0000000..e566d45 --- /dev/null +++ b/CopyQ-3.0.2/debuglinks.list @@ -0,0 +1,23 @@ +/usr/lib/debug/.build-id/39/6fb26cad0769eacd7af5a39c7fc2e9c51ea8d3 /usr/bin/copyq +/usr/lib/debug/.build-id/39/6fb26cad0769eacd7af5a39c7fc2e9c51ea8d3.debug /usr/lib/debug/usr/bin/copyq.debug +/usr/lib/debug/.build-id/40/556e992b3e12ba6de9d1882d30b7e73824ba79 /usr/lib64/copyq/plugins/libitemdata.so +/usr/lib/debug/.build-id/40/556e992b3e12ba6de9d1882d30b7e73824ba79.debug /usr/lib/debug/usr/lib64/copyq/plugins/libitemdata.so.debug +/usr/lib/debug/.build-id/50/851b8d260a05ae088cc78dcaa6a102464472b2 /usr/lib64/copyq/plugins/libitemencrypted.so +/usr/lib/debug/.build-id/50/851b8d260a05ae088cc78dcaa6a102464472b2.debug /usr/lib/debug/usr/lib64/copyq/plugins/libitemencrypted.so.debug +/usr/lib/debug/.build-id/e9/4627e58f07e29f32c365d4d028acbdd0e89921 /usr/lib64/copyq/plugins/libitemfakevim.so +/usr/lib/debug/.build-id/e9/4627e58f07e29f32c365d4d028acbdd0e89921.debug /usr/lib/debug/usr/lib64/copyq/plugins/libitemfakevim.so.debug +/usr/lib/debug/.build-id/ec/a114fc00cc483f6b814421f97ac456e0d12067 /usr/lib64/copyq/plugins/libitemimage.so +/usr/lib/debug/.build-id/ec/a114fc00cc483f6b814421f97ac456e0d12067.debug /usr/lib/debug/usr/lib64/copyq/plugins/libitemimage.so.debug +/usr/lib/debug/.build-id/25/d434b121abb4897672d69bb712ce1be3d655f9 /usr/lib64/copyq/plugins/libitemnotes.so +/usr/lib/debug/.build-id/25/d434b121abb4897672d69bb712ce1be3d655f9.debug /usr/lib/debug/usr/lib64/copyq/plugins/libitemnotes.so.debug +/usr/lib/debug/.build-id/3c/710c9050a1f474964b4a1386e82800e2c5e62d /usr/lib64/copyq/plugins/libitempinned.so +/usr/lib/debug/.build-id/3c/710c9050a1f474964b4a1386e82800e2c5e62d.debug /usr/lib/debug/usr/lib64/copyq/plugins/libitempinned.so.debug +/usr/lib/debug/.build-id/3c/0e7d1c8fdaa374d4ed98e50bfd758e379ae062 /usr/lib64/copyq/plugins/libitemtags.so +/usr/lib/debug/.build-id/3c/0e7d1c8fdaa374d4ed98e50bfd758e379ae062.debug /usr/lib/debug/usr/lib64/copyq/plugins/libitemtags.so.debug +/usr/lib/debug/.build-id/92/dc76b6c1c5f939a944e21c7a83618f5f77cc3d /usr/lib64/copyq/plugins/libitemtext.so +/usr/lib/debug/.build-id/92/dc76b6c1c5f939a944e21c7a83618f5f77cc3d.debug /usr/lib/debug/usr/lib64/copyq/plugins/libitemtext.so.debug +/usr/lib/debug/.build-id/48/9fe1d84c8799004869412a6e133a5e1a105b97 /usr/lib64/copyq/plugins/libitemsync.so +/usr/lib/debug/.build-id/48/9fe1d84c8799004869412a6e133a5e1a105b97.debug /usr/lib/debug/usr/lib64/copyq/plugins/libitemsync.so.debug +/usr/lib/debug/.build-id/ca/536be7713f249df8c76ab5378c30eed1944769 /usr/lib64/copyq/plugins/libitemweb.so +/usr/lib/debug/.build-id/ca/536be7713f249df8c76ab5378c30eed1944769.debug /usr/lib/debug/usr/lib64/copyq/plugins/libitemweb.so.debug +/usr/lib/debug/.build-id/c9/c11dd588b71df9b878fb201fe399e64a211f6e.debug /usr/lib/debug/.dwz/copyq-3.0.2-1.fc27.x86_64 diff --git a/CopyQ-3.0.2/debugsources.list b/CopyQ-3.0.2/debugsources.list new file mode 100644 index 0000000..fa89a0c Binary files /dev/null and b/CopyQ-3.0.2/debugsources.list differ diff --git a/CopyQ-3.0.2/elfbins.list b/CopyQ-3.0.2/elfbins.list new file mode 100644 index 0000000..a7be0e3 --- /dev/null +++ b/CopyQ-3.0.2/elfbins.list @@ -0,0 +1,11 @@ +.//usr/bin/copyq +.//usr/lib64/copyq/plugins/libitemdata.so +.//usr/lib64/copyq/plugins/libitemencrypted.so +.//usr/lib64/copyq/plugins/libitemfakevim.so +.//usr/lib64/copyq/plugins/libitemimage.so +.//usr/lib64/copyq/plugins/libitemnotes.so +.//usr/lib64/copyq/plugins/libitempinned.so +.//usr/lib64/copyq/plugins/libitemtags.so +.//usr/lib64/copyq/plugins/libitemtext.so +.//usr/lib64/copyq/plugins/libitemsync.so +.//usr/lib64/copyq/plugins/libitemweb.so diff --git a/CopyQ-3.0.2/gpl.txt b/CopyQ-3.0.2/gpl.txt new file mode 100644 index 0000000..94a9ed0 --- /dev/null +++ b/CopyQ-3.0.2/gpl.txt @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/CopyQ-3.0.2/install_manifest.txt b/CopyQ-3.0.2/install_manifest.txt new file mode 100644 index 0000000..71db68d --- /dev/null +++ b/CopyQ-3.0.2/install_manifest.txt @@ -0,0 +1,66 @@ +/usr/share/icons/hicolor/scalable/apps/copyq-normal.svg +/usr/share/icons/hicolor/scalable/apps/copyq-busy.svg +/usr/share/applications/copyq.desktop +/usr/share/appdata/copyq.appdata.xml +/usr/share/icons/hicolor/16x16/apps/copyq.png +/usr/share/icons/hicolor/16x16/apps/copyq-normal.png +/usr/share/icons/hicolor/16x16/apps/copyq-busy.png +/usr/share/icons/hicolor/22x22/apps/copyq.png +/usr/share/icons/hicolor/22x22/apps/copyq-normal.png +/usr/share/icons/hicolor/22x22/apps/copyq-busy.png +/usr/share/icons/hicolor/24x24/apps/copyq.png +/usr/share/icons/hicolor/24x24/apps/copyq-normal.png +/usr/share/icons/hicolor/24x24/apps/copyq-busy.png +/usr/share/icons/hicolor/32x32/apps/copyq.png +/usr/share/icons/hicolor/32x32/apps/copyq-normal.png +/usr/share/icons/hicolor/32x32/apps/copyq-busy.png +/usr/share/icons/hicolor/48x48/apps/copyq.png +/usr/share/icons/hicolor/48x48/apps/copyq-normal.png +/usr/share/icons/hicolor/48x48/apps/copyq-busy.png +/usr/share/icons/hicolor/64x64/apps/copyq.png +/usr/share/icons/hicolor/64x64/apps/copyq-normal.png +/usr/share/icons/hicolor/64x64/apps/copyq-busy.png +/usr/share/icons/hicolor/128x128/apps/copyq.png +/usr/share/icons/hicolor/128x128/apps/copyq-normal.png +/usr/share/icons/hicolor/128x128/apps/copyq-busy.png +/usr/share/copyq/themes/dark.ini +/usr/share/copyq/themes/forest.ini +/usr/share/copyq/themes/light.ini +/usr/share/copyq/themes/paper.ini +/usr/share/copyq/themes/simple.ini +/usr/share/copyq/themes/solarized-dark.ini +/usr/share/copyq/themes/solarized-light.ini +/usr/share/copyq/themes/wine.ini +/usr/bin/copyq +/usr/share/copyq/locale/copyq_ar.qm +/usr/share/copyq/locale/copyq_cs.qm +/usr/share/copyq/locale/copyq_da.qm +/usr/share/copyq/locale/copyq_de.qm +/usr/share/copyq/locale/copyq_es.qm +/usr/share/copyq/locale/copyq_fr.qm +/usr/share/copyq/locale/copyq_hu.qm +/usr/share/copyq/locale/copyq_it.qm +/usr/share/copyq/locale/copyq_ja.qm +/usr/share/copyq/locale/copyq_lt.qm +/usr/share/copyq/locale/copyq_nb.qm +/usr/share/copyq/locale/copyq_nl.qm +/usr/share/copyq/locale/copyq_pl.qm +/usr/share/copyq/locale/copyq_pt_BR.qm +/usr/share/copyq/locale/copyq_pt_PT.qm +/usr/share/copyq/locale/copyq_ru.qm +/usr/share/copyq/locale/copyq_sk.qm +/usr/share/copyq/locale/copyq_sv.qm +/usr/share/copyq/locale/copyq_tr.qm +/usr/share/copyq/locale/copyq_uk.qm +/usr/share/copyq/locale/copyq_zh_CN.qm +/usr/share/copyq/locale/copyq_zh_TW.qm +/usr/lib64/copyq/plugins/libitemdata.so +/usr/lib64/copyq/plugins/libitemencrypted.so +/usr/lib64/copyq/plugins/libitemfakevim.so +/usr/lib64/copyq/plugins/libitemimage.so +/usr/lib64/copyq/plugins/libitemnotes.so +/usr/lib64/copyq/plugins/libitempinned.so +/usr/lib64/copyq/plugins/libitemtags.so +/usr/lib64/copyq/plugins/libitemtext.so +/usr/lib64/copyq/plugins/libitemsync.so +/usr/lib64/copyq/plugins/libitemweb.so \ No newline at end of file diff --git a/CopyQ-3.0.2/plugins/CMakeLists.txt b/CopyQ-3.0.2/plugins/CMakeLists.txt new file mode 100644 index 0000000..1790b47 --- /dev/null +++ b/CopyQ-3.0.2/plugins/CMakeLists.txt @@ -0,0 +1,65 @@ +macro (copyq_add_plugin) + set(copyq_pkg ${ARGV0}) + + file(GLOB copyq_plugin_SOURCES + ${copyq_plugin_${copyq_pkg}_SOURCES} + *.cpp + ../../src/item/itemwidget.cpp + ) + file(GLOB copyq_plugin_FORMS + ${copyq_plugin_${copyq_pkg}_FORMS} + *.ui + ) + + if (WITH_TESTS) + file(GLOB copyq_plugin_SOURCES ${copyq_plugin_SOURCES} tests/*.cpp) + endif (WITH_TESTS) + + include_directories(${CMAKE_CURRENT_BINARY_DIR} ../../src) + + if (WITH_QT5) + include_directories(${Qt5Widgets_INCLUDES}) + add_definitions(${Qt5Widgets_DEFINITIONS}) + qt5_wrap_ui(copyq_plugin_FORMS_HEADERS ${copyq_plugin_FORMS}) + qt5_add_resources(copyq_plugin_RCC ${copyq_plugin_${copyq_pkg}_RESOURCES}) + else() + include_directories(${QT_INCLUDES}) + add_definitions(${QT_DEFINITIONS}) + qt4_wrap_ui(copyq_plugin_FORMS_HEADERS ${copyq_plugin_FORMS}) + qt4_add_resources(copyq_plugin_RCC ${copyq_plugin_${copyq_pkg}_RESOURCES}) + endif() + + add_library(${copyq_pkg} MODULE + ${copyq_plugin_SOURCES} + ${copyq_plugin_FORMS_HEADERS} + ${copyq_plugin_RCC} + ) + + set_target_properties(${copyq_pkg} PROPERTIES + COMPILE_DEFINITIONS "${copyq_plugin_${copyq_pkg}_DEFINITIONS}") + + if (WITH_QT5) + qt5_use_modules(${copyq_pkg} Widgets ${copyq_Qt5_Modules} ${copyq_plugin_${copyq_pkg}_Qt5_Modules}) + else() + include(${QT_USE_FILE}) + endif() + + target_link_libraries(${copyq_pkg} ${QT_LIBRARIES} ${copyq_plugin_${copyq_pkg}_LIBRARIES}) + + if (${CMAKE_SYSTEM_NAME} MATCHES "Linux") + install(TARGETS ${copyq_pkg} DESTINATION ${PLUGIN_INSTALL_PREFIX}) + endif() +endmacro() + +set(LIBRARY_OUTPUT_PATH ${PROJECT_BINARY_DIR}/plugins) + +add_subdirectory("itemdata") +add_subdirectory("itemencrypted") +add_subdirectory("itemfakevim") +add_subdirectory("itemimage") +add_subdirectory("itemnotes") +add_subdirectory("itempinned") +add_subdirectory("itemtags") +add_subdirectory("itemtext") +add_subdirectory("itemsync") +add_subdirectory("itemweb") diff --git a/CopyQ-3.0.2/plugins/itemdata/CMakeLists.txt b/CopyQ-3.0.2/plugins/itemdata/CMakeLists.txt new file mode 100644 index 0000000..c7a70be --- /dev/null +++ b/CopyQ-3.0.2/plugins/itemdata/CMakeLists.txt @@ -0,0 +1,8 @@ +set(copyq_plugin_itemdata_SOURCES + ../../src/common/log.cpp + ../../src/common/mimetypes.cpp + ../../src/common/textdata.cpp + ) + +copyq_add_plugin(itemdata) + diff --git a/CopyQ-3.0.2/plugins/itemdata/itemdata.cpp b/CopyQ-3.0.2/plugins/itemdata/itemdata.cpp new file mode 100644 index 0000000..299a49d --- /dev/null +++ b/CopyQ-3.0.2/plugins/itemdata/itemdata.cpp @@ -0,0 +1,212 @@ +/* + Copyright (c) 2014, Lukas Holecek + + This file is part of CopyQ. + + CopyQ is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + CopyQ is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with CopyQ. If not, see . +*/ + +#include "itemdata.h" +#include "ui_itemdatasettings.h" + +#include "common/contenttype.h" +#include "common/mimetypes.h" +#include "common/textdata.h" + +#include +#include +#include +#include +#include + +namespace { + +// Limit number of characters for performance reasons. +const int defaultMaxBytes = 256; + +QString hexData(const QByteArray &data) +{ + if ( data.isEmpty() ) + return QString(); + + QString result; + QString chars; + + int i = 0; + forever { + if (i > 0) { + if ( (i % 2) == 0 ) + result.append( QString(" ") ); + if ( (i % 16) == 0 ) { + result.append(" "); + result.append(chars); + result.append( QString("\n") ); + chars.clear(); + if (i >= data.size() ) + break; + } + } + if ( (i % 16) == 0 ) { + result.append( QString("%1: ").arg(QString::number(i, 16), 4, QChar('0')) ); + } + if (i < data.size() ) { + QChar c = data[i]; + result.append( QString("%1").arg(QString::number(c.unicode(), 16), 2, QChar('0')) ); + chars.append( c.isPrint() ? escapeHtml(QString(c)) : QString(".") ); + } else { + result.append( QString(" ") ); + } + + ++i; + } + + return result; +} + +QString stringFromBytes(const QByteArray &bytes, const QString &format) +{ + QTextCodec *codec = QTextCodec::codecForName("utf-8"); + if (format == QLatin1String("text/html")) + codec = QTextCodec::codecForHtml(bytes, codec); + return codec->toUnicode(bytes); +} + +bool emptyIntersection(const QStringList &lhs, const QStringList &rhs) +{ + for (const auto &l : lhs) { + if ( rhs.contains(l) ) + return false; + } + + return true; +} + +} // namespace + +ItemData::ItemData(const QModelIndex &index, int maxBytes, QWidget *parent) + : QLabel(parent) + , ItemWidget(this) +{ + setTextInteractionFlags(Qt::TextSelectableByMouse); + setContentsMargins(4, 4, 4, 4); + setTextFormat(Qt::RichText); + + QString text; + + const QVariantMap data = index.data(contentType::data).toMap(); + for ( const auto &format : data.keys() ) { + QByteArray bytes = data[format].toByteArray(); + const int size = bytes.size(); + bool trimmed = size > maxBytes; + if (trimmed) + bytes = bytes.left(maxBytes); + + bool hasText = format.startsWith("text/") || + format.startsWith("application/x-copyq-owner-window-title"); + const QString content = hasText ? escapeHtml(stringFromBytes(bytes, format)) : hexData(bytes); + text.append( QString("

") ); + text.append( QString("%1 (%2 bytes)

%3
") + .arg(format) + .arg(size) + .arg(content) ); + text.append( QString("

") ); + + if (trimmed) + text.append( QString("

...

") ); + } + + setText(text); +} + +void ItemData::highlight(const QRegExp &, const QFont &, const QPalette &) +{ +} + +void ItemData::mousePressEvent(QMouseEvent *e) +{ + QLabel::mousePressEvent(e); + e->ignore(); +} + +void ItemData::mouseDoubleClickEvent(QMouseEvent *e) +{ + if ( e->modifiers().testFlag(Qt::ShiftModifier) ) + QLabel::mouseDoubleClickEvent(e); + else + e->ignore(); +} + +void ItemData::contextMenuEvent(QContextMenuEvent *e) +{ + e->ignore(); +} + +ItemDataLoader::ItemDataLoader() +{ +} + +ItemDataLoader::~ItemDataLoader() = default; + +ItemWidget *ItemDataLoader::create(const QModelIndex &index, QWidget *parent, bool preview) const +{ + if ( index.data(contentType::isHidden).toBool() ) + return nullptr; + + const QStringList formats = index.data(contentType::data).toMap().keys(); + if ( emptyIntersection(formats, formatsToSave()) ) + return nullptr; + + const int bytes = preview ? 4096 : m_settings.value("max_bytes", defaultMaxBytes).toInt(); + return new ItemData(index, bytes, parent); +} + +QStringList ItemDataLoader::formatsToSave() const +{ + return m_settings.contains("formats") + ? m_settings["formats"].toStringList() + : QStringList() << mimeUriList << QString("text/xml"); +} + +QVariantMap ItemDataLoader::applySettings() +{ + Q_ASSERT(ui != nullptr); + m_settings["formats"] = ui->plainTextEditFormats->toPlainText().split( QRegExp("[;,\\s]+") ); + m_settings["max_bytes"] = ui->spinBoxMaxChars->value(); + return m_settings; +} + +QWidget *ItemDataLoader::createSettingsWidget(QWidget *parent) +{ + ui.reset(new Ui::ItemDataSettings); + QWidget *w = new QWidget(parent); + ui->setupUi(w); + + const QStringList formats = formatsToSave(); + ui->plainTextEditFormats->setPlainText( formats.join(QString("\n")) ); + ui->spinBoxMaxChars->setValue( m_settings.value("max_bytes", defaultMaxBytes).toInt() ); + + connect( ui->treeWidgetFormats, SIGNAL(itemActivated(QTreeWidgetItem*,int)), + SLOT(on_treeWidgetFormats_itemActivated(QTreeWidgetItem*,int)) ); + + return w; +} + +void ItemDataLoader::on_treeWidgetFormats_itemActivated(QTreeWidgetItem *item, int column) +{ + const QString mime = item->toolTip(column); + if ( !mime.isEmpty() ) + ui->plainTextEditFormats->appendPlainText(mime); +} + +Q_EXPORT_PLUGIN2(itemdata, ItemDataLoader) diff --git a/CopyQ-3.0.2/plugins/itemdata/itemdata.h b/CopyQ-3.0.2/plugins/itemdata/itemdata.h new file mode 100644 index 0000000..6a47720 --- /dev/null +++ b/CopyQ-3.0.2/plugins/itemdata/itemdata.h @@ -0,0 +1,92 @@ +/* + Copyright (c) 2014, Lukas Holecek + + This file is part of CopyQ. + + CopyQ is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + CopyQ is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with CopyQ. If not, see . +*/ + +#ifndef ITEMDATA_H +#define ITEMDATA_H + +#include "item/itemwidget.h" +#include "gui/icons.h" + +#include + +#include + +namespace Ui { +class ItemDataSettings; +} + +class QTreeWidgetItem; + +class ItemData : public QLabel, public ItemWidget +{ + Q_OBJECT + +public: + ItemData(const QModelIndex &index, int maxBytes, QWidget *parent); + +protected: + void highlight(const QRegExp &re, const QFont &highlightFont, + const QPalette &highlightPalette) override; + + QWidget *createEditor(QWidget *) const override { return nullptr; } + + void mousePressEvent(QMouseEvent *e) override; + + void mouseDoubleClickEvent(QMouseEvent *e) override; + + void contextMenuEvent(QContextMenuEvent *e) override; +}; + +class ItemDataLoader : public QObject, public ItemLoaderInterface +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID COPYQ_PLUGIN_ITEM_LOADER_ID) + Q_INTERFACES(ItemLoaderInterface) + +public: + ItemDataLoader(); + ~ItemDataLoader(); + + ItemWidget *create(const QModelIndex &index, QWidget *parent, bool preview) const override; + + int priority() const override { return -20; } + + QString id() const override { return "itemdata"; } + QString name() const override { return tr("Data"); } + QString author() const override { return QString(); } + QString description() const override { return tr("Various data to save."); } + QVariant icon() const override { return QVariant(IconFileText); } + + QStringList formatsToSave() const override; + + QVariantMap applySettings() override; + + void loadSettings(const QVariantMap &settings) override { m_settings = settings; } + + QWidget *createSettingsWidget(QWidget *parent) override; + +private slots: + void on_treeWidgetFormats_itemActivated(QTreeWidgetItem *item, int column); + +private: + QVariantMap m_settings; + std::unique_ptr ui; +}; + +#endif // ITEMDATA_H diff --git a/CopyQ-3.0.2/plugins/itemdata/itemdata.pro b/CopyQ-3.0.2/plugins/itemdata/itemdata.pro new file mode 100644 index 0000000..4f8a0c7 --- /dev/null +++ b/CopyQ-3.0.2/plugins/itemdata/itemdata.pro @@ -0,0 +1,10 @@ +include(../plugins_common.pri) + +HEADERS += itemdata.h +SOURCES += itemdata.cpp \ + ../../src/common/log.cpp \ + ../../src/common/mimetypes.cpp \ + ../../src/common/textdata.cpp +FORMS += itemdatasettings.ui +TARGET = $$qtLibraryTarget(itemdata) + diff --git a/CopyQ-3.0.2/plugins/itemdata/itemdatasettings.ui b/CopyQ-3.0.2/plugins/itemdata/itemdatasettings.ui new file mode 100644 index 0000000..03d214d --- /dev/null +++ b/CopyQ-3.0.2/plugins/itemdata/itemdatasettings.ui @@ -0,0 +1,214 @@ + + + ItemDataSettings + + + + 0 + 0 + 430 + 321 + + + + + 0 + 1 + + + + + + + Select formats to save in history. You can add a format from examples below or type in other (one per line). + + + true + + + + + + + + + Active &Formats: + + + plainTextEditFormats + + + + + + + &Examples (double click to add to active formats): + + + true + + + treeWidgetFormats + + + + + + + QAbstractItemView::NoEditTriggers + + + false + + + + 1 + + + + + Text + + + + Unformatted simple text + + + text/plain + + + + + Formatted text, web pages + + + text/html +text/richtext + + + + + XML + + + text/xml + + + + + List of URI (e.g. copied files, URLs) + + + text/uri-list + + + + + + Images + + + + Bitmap image + + + text/bmp + + + + + Vector graphics + + + image/svg+xml +image/x-inkscape-svg-compressed + + + + + Web image formats + + + image/png +image/jpeg +image/gif + + + + + Other + + + image/epx +image/ico +image/jp2 +image/jpx +image/tiff +image/pcx +image/ppm +image/rgb +image/rgba +image/tga +image/x-targa +image/x-tga +image/xpm +image/xbm + + + + + + + + + List of clipboard mime types that will be stored in history (in given display order) + + + true + + + QPlainTextEdit::NoWrap + + + + + + + + + + + &Maximum number of characters per format to display: + + + spinBoxMaxChars + + + + + + + 4096 + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + diff --git a/CopyQ-3.0.2/plugins/itemencrypted/CMakeLists.txt b/CopyQ-3.0.2/plugins/itemencrypted/CMakeLists.txt new file mode 100644 index 0000000..f6bf0a2 --- /dev/null +++ b/CopyQ-3.0.2/plugins/itemencrypted/CMakeLists.txt @@ -0,0 +1,13 @@ +set(copyq_plugin_itemencrypted_SOURCES + ../../src/common/config.cpp + ../../src/common/log.cpp + ../../src/common/mimetypes.cpp + ../../src/common/shortcuts.cpp + ../../src/common/textdata.cpp + ../../src/gui/iconfont.cpp + ../../src/gui/iconwidget.cpp + ../../src/item/serialize.cpp + ) + +copyq_add_plugin(itemencrypted) + diff --git a/CopyQ-3.0.2/plugins/itemencrypted/itemencrypted.cpp b/CopyQ-3.0.2/plugins/itemencrypted/itemencrypted.cpp new file mode 100644 index 0000000..6375f99 --- /dev/null +++ b/CopyQ-3.0.2/plugins/itemencrypted/itemencrypted.cpp @@ -0,0 +1,891 @@ +/* + Copyright (c) 2014, Lukas Holecek + + This file is part of CopyQ. + + CopyQ is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + CopyQ is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with CopyQ. If not, see . +*/ + +#include "itemencrypted.h" +#include "ui_itemencryptedsettings.h" + +#include "common/command.h" +#include "common/config.h" +#include "common/contenttype.h" +#include "common/log.h" +#include "common/mimetypes.h" +#include "common/shortcuts.h" +#include "common/textdata.h" +#include "gui/icons.h" +#include "gui/iconwidget.h" +#include "item/serialize.h" + +#ifdef HAS_TESTS +# include "tests/itemencryptedtests.h" +#endif + +#include +#include +#include +#include +#include +#include + +namespace { + +const char mimeEncryptedData[] = "application/x-copyq-encrypted"; + +const char dataFileHeader[] = "CopyQ_encrypted_tab"; +const char dataFileHeaderV2[] = "CopyQ_encrypted_tab v2"; + +const int maxItemCount = 10000; + +struct KeyPairPaths { + KeyPairPaths() + { + const QString path = getConfigurationFilePath(QString()); + sec = QDir::toNativeSeparators(path + ".sec"); + pub = QDir::toNativeSeparators(path + ".pub"); + } + + QString sec; + QString pub; +}; + +QString gpgExecutable() +{ + return "gpg2"; +} + +QStringList getDefaultEncryptCommandArguments(const QString &publicKeyPath) +{ + return QStringList() << "--trust-model" << "always" << "--recipient" << "copyq" + << "--charset" << "utf-8" << "--display-charset" << "utf-8" << "--no-tty" + << "--no-default-keyring" << "--keyring" << publicKeyPath; +} + +void startGpgProcess(QProcess *p, const QStringList &args) +{ + KeyPairPaths keys; + p->start(gpgExecutable(), getDefaultEncryptCommandArguments(keys.pub) + args); +} + +bool verifyProcess(QProcess *p) +{ + const int exitCode = p->exitCode(); + if ( p->exitStatus() != QProcess::NormalExit ) { + log( "ItemEncrypt ERROR: Failed to run GnuPG: " + p->errorString(), LogError ); + return false; + } + + if (exitCode != 0) { + const QString errors = p->readAllStandardError(); + if ( !errors.isEmpty() ) + log( "ItemEncrypt ERROR: GnuPG stderr:\n" + errors, LogError ); + return false; + } + + return true; +} + +bool waitOrTerminate(QProcess *p) +{ + if ( p->state() != QProcess::NotRunning && !p->waitForFinished() ) { + p->terminate(); + if ( !p->waitForFinished(5000) ) + p->kill(); + return false; + } + + return true; +} + +QString importGpgKey() +{ + KeyPairPaths keys; + + QProcess p; + p.start(gpgExecutable(), getDefaultEncryptCommandArguments(keys.pub) << "--import" << keys.sec); + if ( !waitOrTerminate(&p) ) + return "Failed to import private key (process timed out)."; + + if ( !verifyProcess(&p) ) + return "Failed to import private key (see log)."; + + return QString(); +} + +QString exportGpgKey() +{ + KeyPairPaths keys; + + // Private key already created or exported. + if ( QFile::exists(keys.sec) ) + return QString(); + + QProcess p; + p.start(gpgExecutable(), getDefaultEncryptCommandArguments(keys.pub) << "--export-secret-key" << "copyq"); + if ( !waitOrTerminate(&p) ) + return "Failed to export private key (process timed out)."; + + if ( !verifyProcess(&p) ) + return "Failed to export private key (see log)."; + + QFile secKey(keys.sec); + if ( !secKey.open(QIODevice::WriteOnly) ) + return "Failed to create private key."; + + if ( !secKey.setPermissions(QFile::ReadOwner | QFile::WriteOwner) ) + return "Failed to set permissions for private key."; + + const QByteArray secKeyData = p.readAllStandardOutput(); + secKey.write(secKeyData); + secKey.close(); + + return QString(); +} + +QByteArray readGpgOutput(const QStringList &args, const QByteArray &input = QByteArray()) +{ + QProcess p; + startGpgProcess( &p, args ); + p.write(input); + p.closeWriteChannel(); + p.waitForFinished(); + verifyProcess(&p); + return p.readAllStandardOutput(); +} + +bool keysExist() +{ + return !readGpgOutput( QStringList("--list-keys") ).isEmpty(); +} + +bool decryptMimeData(QVariantMap *detinationData, const QModelIndex &index) +{ + const QVariantMap data = index.data(contentType::data).toMap(); + if ( !data.contains(mimeEncryptedData) ) + return false; + + const QByteArray encryptedBytes = data.value(mimeEncryptedData).toByteArray(); + const QByteArray bytes = readGpgOutput( QStringList() << "--decrypt", encryptedBytes ); + + return deserializeData(detinationData, bytes); +} + +void encryptMimeData(const QVariantMap &data, const QModelIndex &index, QAbstractItemModel *model) +{ + const QByteArray bytes = serializeData(data); + const QByteArray encryptedBytes = readGpgOutput( QStringList("--encrypt"), bytes ); + QVariantMap dataMap; + dataMap.insert(mimeEncryptedData, encryptedBytes); + model->setData(index, dataMap, contentType::data); +} + +void startGenerateKeysProcess(QProcess *process, bool useTransientPasswordlessKey = false) +{ + const KeyPairPaths keys; + + auto args = QStringList() << "--batch" << "--gen-key"; + + QByteArray transientOptions; + if (useTransientPasswordlessKey) { + args << "--debug-quick-random"; + transientOptions = + "\n%no-protection" + "\n%transient-key"; + } + + startGpgProcess(process, args); + process->write( "\nKey-Type: RSA" + "\nKey-Usage: encrypt" + "\nKey-Length: 2048" + "\nName-Real: copyq" + + transientOptions + + "\n%secring " + keys.sec.toUtf8() + + "\n%pubring " + keys.pub.toUtf8() + + "\n%commit" + "\n" ); + process->closeWriteChannel(); +} + +QString exportImportGpgKeys() +{ + const auto error = exportGpgKey(); + if ( !error.isEmpty() ) + return error; + + return importGpgKey(); +} + +bool isGpgInstalled() +{ + QProcess p; + startGpgProcess(&p, QStringList("--version")); + p.closeWriteChannel(); + p.waitForFinished(); + + if (p.exitStatus() != QProcess::NormalExit || p.exitCode() != 0) + return false; + + const auto versionOutput = p.readAllStandardOutput(); + return versionOutput.contains(" 2."); +} + +} // namespace + +ItemEncrypted::ItemEncrypted(QWidget *parent) + : QWidget(parent) + , ItemWidget(this) +{ + auto layout = new QVBoxLayout(this); + layout->setContentsMargins(0, 0, 0, 0); + + // Show small icon. + QWidget *iconWidget = new IconWidget(IconLock, this); + layout->addWidget(iconWidget); +} + +void ItemEncrypted::setEditorData(QWidget *editor, const QModelIndex &index) const +{ + // Decrypt before editing. + QTextEdit *textEdit = qobject_cast(editor); + if (textEdit != nullptr) { + QVariantMap data; + if ( decryptMimeData(&data, index) ) { + textEdit->setPlainText( getTextData(data, mimeText) ); + textEdit->selectAll(); + } + } +} + +void ItemEncrypted::setModelData(QWidget *editor, QAbstractItemModel *model, + const QModelIndex &index) const +{ + // Encrypt after editing. + QTextEdit *textEdit = qobject_cast(editor); + if (textEdit != nullptr) + encryptMimeData( createDataMap(mimeText, textEdit->toPlainText()), index, model ); +} + +bool ItemEncryptedSaver::saveItems(const QString &, const QAbstractItemModel &model, QIODevice *file) +{ + const auto length = model.rowCount(); + if (length == 0) + return false; // No need to encode empty tab. + + QByteArray bytes; + + { + QDataStream stream(&bytes, QIODevice::WriteOnly); + stream.setVersion(QDataStream::Qt_4_7); + + stream << static_cast(length); + + for (int i = 0; i < length && stream.status() == QDataStream::Ok; ++i) { + QModelIndex index = model.index(i, 0); + const QVariantMap dataMap = index.data(contentType::data).toMap(); + stream << dataMap; + } + } + + bytes = readGpgOutput(QStringList("--encrypt"), bytes); + if ( bytes.isEmpty() ) { + emitEncryptFailed(); + COPYQ_LOG("ItemEncrypt ERROR: Failed to read encrypted data"); + return false; + } + + QDataStream stream(file); + stream << QString(dataFileHeaderV2); + stream.writeRawData( bytes.data(), bytes.size() ); + + if ( stream.status() != QDataStream::Ok ) { + emitEncryptFailed(); + COPYQ_LOG("ItemEncrypt ERROR: Failed to write encrypted data"); + return false; + } + + return true; +} + +void ItemEncryptedSaver::emitEncryptFailed() +{ + emit error( ItemEncryptedLoader::tr("Encryption failed!") ); +} + +bool ItemEncryptedScriptable::isEncrypted() +{ + const auto args = currentArguments(); + for (const auto &arg : args) { + bool ok; + const int row = arg.toInt(&ok); + if (ok) { + const auto result = call("read", QVariantList() << "?" << row); + if ( result.toByteArray().contains(mimeEncryptedData) ) + return true; + } + } + + return false; +} + +QByteArray ItemEncryptedScriptable::encrypt() +{ + const auto args = currentArguments(); + const auto bytes = args.first().toByteArray(); + return encrypt(bytes); +} + +QByteArray ItemEncryptedScriptable::decrypt() +{ + const auto args = currentArguments(); + const auto bytes = args.first().toByteArray(); + return decrypt(bytes); +} + +void ItemEncryptedScriptable::encryptItem() +{ + QVariantMap dataMap; + const auto formats = call("dataFormats").toList(); + for (const auto &formatValue : formats) { + const auto format = formatValue.toString(); + if ( !format.startsWith(COPYQ_MIME_PREFIX) ) { + const auto data = call("data", QVariantList() << format).toByteArray(); + dataMap.insert(format, data); + } + } + + const auto bytes = call("pack", QVariantList() << dataMap).toByteArray(); + const auto encryptedBytes = encrypt(bytes); + if (encryptedBytes.isEmpty()) + return; + + call("setData", QVariantList() << mimeEncryptedData << encryptedBytes); + + for ( const auto &format : dataMap.keys() ) + call("removeData", QVariantList() << format); +} + +void ItemEncryptedScriptable::decryptItem() +{ + const auto encryptedBytes = call("data", QVariantList() << mimeEncryptedData).toByteArray(); + const auto itemData = decrypt(encryptedBytes); + if (itemData.isEmpty()) + return; + + const auto dataMap = call("unpack", QVariantList() << itemData).toMap(); + for ( const auto &format : dataMap.keys() ) + call("setData", QVariantList() << format << dataMap[format]); +} + +void ItemEncryptedScriptable::encryptItems() +{ + const auto dataValueList = call("selectedItemsData").toList(); + + QVariantList dataList; + for (const auto &itemDataValue : dataValueList) { + auto itemData = itemDataValue.toMap(); + + QVariantMap itemDataToEncrypt; + for ( const auto &format : itemData.keys() ) { + if ( !format.startsWith(COPYQ_MIME_PREFIX) ) { + itemDataToEncrypt.insert(format, itemData[format]); + itemData.remove(format); + } + } + + const auto bytes = call("pack", QVariantList() << itemDataToEncrypt).toByteArray(); + const auto encryptedBytes = encrypt(bytes); + if (encryptedBytes.isEmpty()) + return; + itemData.insert(mimeEncryptedData, encryptedBytes); + + dataList.append(itemData); + } + + call( "setSelectedItemsData", QVariantList() << QVariant(dataList) ); +} + +void ItemEncryptedScriptable::decryptItems() +{ + const auto dataValueList = call("selectedItemsData").toList(); + + QVariantList dataList; + for (const auto &itemDataValue : dataValueList) { + auto itemData = itemDataValue.toMap(); + + const auto encryptedBytes = itemData.value(mimeEncryptedData).toByteArray(); + if ( !encryptedBytes.isEmpty() ) { + itemData.remove(mimeEncryptedData); + + const auto decryptedBytes = decrypt(encryptedBytes); + if (decryptedBytes.isEmpty()) + return; + + const auto decryptedItemData = call("unpack", QVariantList() << decryptedBytes).toMap(); + for ( const auto &format : decryptedItemData.keys() ) + itemData.insert(format, decryptedItemData[format]); + } + + dataList.append(itemData); + } + + call( "setSelectedItemsData", QVariantList() << QVariant(dataList) ); +} + +void ItemEncryptedScriptable::copyEncryptedItems() +{ + const auto dataValueList = call("selectedItemsData").toList(); + QString text; + for (const auto &dataValue : dataValueList) { + if ( !text.isEmpty() ) + text.append('\n'); + + const auto data = dataValue.toMap(); + const auto itemTextValue = data.value(mimeText); + if ( itemTextValue.isValid() ) { + text.append( getTextData(itemTextValue.toByteArray()) ); + } else { + const auto encryptedBytes = data.value(mimeEncryptedData).toByteArray(); + if ( !encryptedBytes.isEmpty() ) { + const auto itemData = decrypt(encryptedBytes); + if (itemData.isEmpty()) + return; + const auto dataMap = call("unpack", QVariantList() << itemData).toMap(); + text.append( getTextData(dataMap) ); + } + } + } + + call("copy", QVariantList() << text); +} + +QString ItemEncryptedScriptable::generateTestKeys() +{ + const KeyPairPaths keys; + for ( const auto &keyFileName : {keys.sec, keys.pub} ) { + if ( QFile::exists(keyFileName) && !QFile::remove(keyFileName) ) + return QString("Failed to remove \"%1\"").arg(keys.sec); + } + + QProcess process; + startGenerateKeysProcess(&process, true); + + if ( !waitOrTerminate(&process) || !verifyProcess(&process) ) { + return QString("ItemEncrypt ERROR: %1; stderr: %2") + .arg( process.errorString() ) + .arg( QString::fromUtf8(process.readAllStandardError()) ); + } + + const auto error = exportImportGpgKeys(); + if ( !error.isEmpty() ) + return error; + + for ( const auto &keyFileName : {keys.sec, keys.pub} ) { + if ( !QFile::exists(keyFileName) ) + return QString("Failed to create \"%1\"").arg(keys.sec); + } + + return QString(); +} + +bool ItemEncryptedScriptable::isGpgInstalled() +{ + return ::isGpgInstalled(); +} + +QByteArray ItemEncryptedScriptable::encrypt(const QByteArray &bytes) +{ + const auto encryptedBytes = readGpgOutput(QStringList("--encrypt"), bytes); + if ( encryptedBytes.isEmpty() ) + eval("throw 'Failed to execute GPG!'"); + return encryptedBytes; +} + +QByteArray ItemEncryptedScriptable::decrypt(const QByteArray &bytes) +{ + const auto decryptedBytes = readGpgOutput(QStringList("--decrypt"), bytes); + if ( decryptedBytes.isEmpty() ) + eval("throw 'Failed to execute GPG!'"); + return decryptedBytes; +} + +ItemEncryptedLoader::ItemEncryptedLoader() + : ui() + , m_settings() + , m_gpgProcessStatus(GpgNotRunning) + , m_gpgProcess(nullptr) +{ +} + +ItemEncryptedLoader::~ItemEncryptedLoader() +{ + terminateGpgProcess(); +} + +ItemWidget *ItemEncryptedLoader::create(const QModelIndex &index, QWidget *parent, bool) const +{ + if ( index.data(contentType::isHidden).toBool() ) + return nullptr; + + const QVariantMap dataMap = index.data(contentType::data).toMap(); + return dataMap.contains(mimeEncryptedData) ? new ItemEncrypted(parent) : nullptr; +} + +QStringList ItemEncryptedLoader::formatsToSave() const +{ + return QStringList(mimeEncryptedData); +} + +QVariantMap ItemEncryptedLoader::applySettings() +{ + Q_ASSERT(ui != nullptr); + m_settings.insert( "encrypt_tabs", ui->plainTextEditEncryptTabs->toPlainText().split('\n') ); + return m_settings; +} + +QWidget *ItemEncryptedLoader::createSettingsWidget(QWidget *parent) +{ + ui.reset(new Ui::ItemEncryptedSettings); + QWidget *w = new QWidget(parent); + ui->setupUi(w); + + connect( ui->pushButtonAddCommands, SIGNAL(clicked()), + this, SLOT(addCommands()) ); + + ui->plainTextEditEncryptTabs->setPlainText( + m_settings.value("encrypt_tabs").toStringList().join("\n") ); + + // Check if gpg application is available. + if ( !isGpgInstalled() ) { + m_gpgProcessStatus = GpgNotInstalled; + } else { + KeyPairPaths keys; + ui->labelShareInfo->setTextFormat(Qt::RichText); + ui->labelShareInfo->setText( tr("To share encrypted items on other computer or" + " session, you'll need public and secret key files:" + "
    " + "
  • %1
  • " + "
  • %2
    (Keep this secret key in a safe place.)
  • " + "
" + ) + .arg( quoteString(keys.pub) ) + .arg( quoteString(keys.sec) ) + ); + } + + updateUi(); + + connect( ui->pushButtonPassword, SIGNAL(clicked()), + this, SLOT(setPassword()) ); + + return w; +} + +bool ItemEncryptedLoader::canLoadItems(QIODevice *file) const +{ + QDataStream stream(file); + + QString header; + stream >> header; + + return stream.status() == QDataStream::Ok + && (header == dataFileHeader || header == dataFileHeaderV2); +} + +bool ItemEncryptedLoader::canSaveItems(const QString &tabName) const +{ + for ( const auto &encryptTabName : m_settings.value("encrypt_tabs").toStringList() ) { + if ( encryptTabName.isEmpty() ) + continue; + + QString tabName1 = tabName; + + // Ignore ampersands (usually just for underlining mnemonics) if none is specified. + if ( !hasKeyHint(encryptTabName) ) + removeKeyHint(&tabName1); + + // Ignore path in tab tree if none path separator is specified. + if ( !encryptTabName.contains('/') ) { + const int i = tabName1.lastIndexOf('/'); + tabName1.remove(0, i + 1); + } + + if ( tabName1 == encryptTabName ) + return true; + } + + return false; +} + +ItemSaverPtr ItemEncryptedLoader::loadItems(const QString &, QAbstractItemModel *model, QIODevice *file, int maxItems) +{ + // This is needed to skip header. + if ( !canLoadItems(file) ) + return nullptr; + + if (m_gpgProcessStatus == GpgNotInstalled) { + emit error( tr("GnuPG must be installed to view encrypted tabs.") ); + return nullptr; + } + + importGpgKey(); + + QProcess p; + startGpgProcess( &p, QStringList("--decrypt") ); + + char encryptedBytes[4096]; + + QDataStream stream(file); + while ( !stream.atEnd() ) { + const int bytesRead = stream.readRawData(encryptedBytes, 4096); + if (bytesRead == -1) { + emitDecryptFailed(); + COPYQ_LOG("ItemEncrypted ERROR: Failed to read encrypted data"); + return nullptr; + } + p.write(encryptedBytes, bytesRead); + } + + p.closeWriteChannel(); + if ( !waitOrTerminate(&p) || !verifyProcess(&p) ) { + emitDecryptFailed(); + return nullptr; + } + + const QByteArray bytes = p.readAllStandardOutput(); + if ( bytes.isEmpty() ) { + emitDecryptFailed(); + COPYQ_LOG("ItemEncrypt ERROR: Failed to read encrypted data."); + verifyProcess(&p); + return nullptr; + } + + QDataStream stream2(bytes); + + quint64 length; + stream2 >> length; + if ( length <= 0 || stream2.status() != QDataStream::Ok ) { + emitDecryptFailed(); + COPYQ_LOG("ItemEncrypt ERROR: Failed to parse item count!"); + return nullptr; + } + length = qMin(length, static_cast(maxItems)) - static_cast(model->rowCount()); + + const auto count = length < maxItemCount ? static_cast(length) : maxItemCount; + for ( int i = 0; i < count && stream2.status() == QDataStream::Ok; ++i ) { + if ( !model->insertRow(i) ) { + emitDecryptFailed(); + COPYQ_LOG("ItemEncrypt ERROR: Failed to insert item!"); + return nullptr; + } + QVariantMap dataMap; + stream2 >> dataMap; + model->setData( model->index(i, 0), dataMap, contentType::data ); + } + + if ( stream2.status() != QDataStream::Ok ) { + emitDecryptFailed(); + COPYQ_LOG("ItemEncrypt ERROR: Failed to decrypt item!"); + return nullptr; + } + + return createSaver(); +} + +ItemSaverPtr ItemEncryptedLoader::initializeTab(const QString &, QAbstractItemModel *, int) +{ + if (m_gpgProcessStatus == GpgNotInstalled) + return nullptr; + + return createSaver(); +} + +QObject *ItemEncryptedLoader::tests(const TestInterfacePtr &test) const +{ +#ifdef HAS_TESTS + QObject *tests = new ItemEncryptedTests(test); + return tests; +#else + Q_UNUSED(test); + return nullptr; +#endif +} + +ItemScriptable *ItemEncryptedLoader::scriptableObject(QObject *parent) +{ + return new ItemEncryptedScriptable(parent); +} + +QList ItemEncryptedLoader::commands() const +{ + QList commands; + + Command c; + c.name = tr("Encrypt (needs GnuPG)"); + c.icon = QString(QChar(IconLock)); + c.input = "!OUTPUT"; + c.output = mimeEncryptedData; + c.inMenu = true; + c.cmd = "copyq: plugins.itemencrypted.encryptItems()"; + c.shortcuts.append( toPortableShortcutText(tr("Ctrl+L")) ); + commands.append(c); + + c = Command(); + c.name = tr("Decrypt"); + c.icon = QString(QChar(IconUnlock)); + c.input = mimeEncryptedData; + c.output = mimeItems; + c.inMenu = true; + c.cmd = "copyq: plugins.itemencrypted.decryptItems()"; + c.shortcuts.append( toPortableShortcutText(tr("Ctrl+L")) ); + commands.append(c); + + c = Command(); + c.name = tr("Decrypt and Copy"); + c.icon = QString(QChar(IconUnlockAlt)); + c.input = mimeEncryptedData; + c.inMenu = true; + c.cmd = "copyq: plugins.itemencrypted.copyEncryptedItems()"; + c.shortcuts.append( toPortableShortcutText(tr("Ctrl+Shift+L")) ); + commands.append(c); + + return commands; +} + +void ItemEncryptedLoader::setPassword() +{ + if (m_gpgProcessStatus == GpgGeneratingKeys) + return; + + if (m_gpgProcess != nullptr) { + terminateGpgProcess(); + return; + } + + if ( !keysExist() ) { + m_gpgProcessStatus = GpgGeneratingKeys; + m_gpgProcess = new QProcess(this); + startGenerateKeysProcess(m_gpgProcess); + } else { + // Change password. + m_gpgProcessStatus = GpgChangingPassword; + m_gpgProcess = new QProcess(this); + startGpgProcess( m_gpgProcess, QStringList() << "--edit-key" << "copyq" << "passwd" << "save"); + } + + m_gpgProcess->waitForStarted(); + if ( m_gpgProcess->state() == QProcess::NotRunning ) { + onGpgProcessFinished( m_gpgProcess->exitCode(), m_gpgProcess->exitStatus() ); + } else { + connect( m_gpgProcess, SIGNAL(finished(int,QProcess::ExitStatus)), + this, SLOT(onGpgProcessFinished(int,QProcess::ExitStatus)) ); + updateUi(); + } +} + +void ItemEncryptedLoader::terminateGpgProcess() +{ + if (m_gpgProcess == nullptr) + return; + QProcess *p = m_gpgProcess; + m_gpgProcess = nullptr; + p->terminate(); + p->waitForFinished(); + p->deleteLater(); + m_gpgProcessStatus = GpgNotRunning; + updateUi(); +} + +void ItemEncryptedLoader::onGpgProcessFinished(int exitCode, QProcess::ExitStatus exitStatus) +{ + QString error; + + if (m_gpgProcess != nullptr) { + if (ui != nullptr) { + if (exitStatus != QProcess::NormalExit) + error = m_gpgProcess->errorString(); + else if (exitCode != 0) + error = getTextData(m_gpgProcess->readAllStandardError()); + else if ( m_gpgProcess->error() != QProcess::UnknownError ) + error = m_gpgProcess->errorString(); + else if ( !keysExist() ) + error = tr("Failed to generate keys."); + } + + m_gpgProcess->deleteLater(); + m_gpgProcess = nullptr; + } + + // Export and import private key to a file in configuration. + if ( m_gpgProcessStatus == GpgGeneratingKeys && error.isEmpty() ) + error = exportImportGpgKeys(); + + if (!error.isEmpty()) + error = tr("Error: %1").arg(error); + + m_gpgProcessStatus = GpgNotRunning; + + updateUi(); + ui->labelInfo->setText( error.isEmpty() ? tr("Done") : error ); +} + +void ItemEncryptedLoader::addCommands() +{ + emit addCommands(commands()); +} + +void ItemEncryptedLoader::updateUi() +{ + if (ui == nullptr) + return; + + if (m_gpgProcessStatus == GpgNotInstalled) { + ui->labelInfo->setText("To use item encryption, install" + " GnuPG" + " application and restart CopyQ."); + ui->pushButtonPassword->hide(); + ui->pushButtonAddCommands->hide(); + ui->groupBoxEncryptTabs->hide(); + ui->groupBoxShareInfo->hide(); + } else if (m_gpgProcessStatus == GpgGeneratingKeys) { + ui->labelInfo->setText( tr("Creating new keys (this may take a few minutes)...") ); + ui->pushButtonPassword->setText( tr("Cancel") ); + } else if (m_gpgProcessStatus == GpgChangingPassword) { + ui->labelInfo->setText( tr("Setting new password...") ); + ui->pushButtonPassword->setText( tr("Cancel") ); + } else if ( !keysExist() ) { + ui->labelInfo->setText( tr("Encryption keys must be generated" + " before item encryption can be used.") ); + ui->pushButtonPassword->setText( tr("Generate New Keys...") ); + } else { + ui->pushButtonPassword->setText( tr("Change Password...") ); + } +} + +void ItemEncryptedLoader::emitDecryptFailed() +{ + emit error( tr("Decryption failed!") ); +} + +ItemSaverPtr ItemEncryptedLoader::createSaver() +{ + auto saver = std::make_shared(); + connect( saver.get(), SIGNAL(error(QString)), + this, SIGNAL(error(QString)) ); + return saver; +} + +Q_EXPORT_PLUGIN2(itemencrypted, ItemEncryptedLoader) diff --git a/CopyQ-3.0.2/plugins/itemencrypted/itemencrypted.h b/CopyQ-3.0.2/plugins/itemencrypted/itemencrypted.h new file mode 100644 index 0000000..5c3dfd0 --- /dev/null +++ b/CopyQ-3.0.2/plugins/itemencrypted/itemencrypted.h @@ -0,0 +1,165 @@ +/* + Copyright (c) 2014, Lukas Holecek + + This file is part of CopyQ. + + CopyQ is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + CopyQ is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with CopyQ. If not, see . +*/ + +#ifndef ITEMENCRYPTED_H +#define ITEMENCRYPTED_H + +#include "item/itemwidget.h" +#include "gui/icons.h" + +#include +#include + +#include + +namespace Ui { +class ItemEncryptedSettings; +} + +class QIODevice; + +class ItemEncrypted : public QWidget, public ItemWidget +{ + Q_OBJECT + +public: + explicit ItemEncrypted(QWidget *parent); + + void setEditorData(QWidget *editor, const QModelIndex &index) const override; + + void setModelData(QWidget *editor, QAbstractItemModel *model, + const QModelIndex &index) const override; +}; + +class ItemEncryptedSaver : public QObject, public ItemSaverInterface +{ + Q_OBJECT + +public: + bool saveItems(const QString &tabName, const QAbstractItemModel &model, QIODevice *file) override; + +signals: + void error(const QString &); + +private: + void emitEncryptFailed(); +}; + +class ItemEncryptedScriptable : public ItemScriptable +{ + Q_OBJECT +public: + explicit ItemEncryptedScriptable(QObject *parent) : ItemScriptable(parent) {} + +public slots: + bool isEncrypted(); + QByteArray encrypt(); + QByteArray decrypt(); + + void encryptItem(); + void decryptItem(); + + void encryptItems(); + void decryptItems(); + + void copyEncryptedItems(); + + QString generateTestKeys(); + bool isGpgInstalled(); + +private: + QByteArray encrypt(const QByteArray &bytes); + QByteArray decrypt(const QByteArray &bytes); +}; + +class ItemEncryptedLoader : public QObject, public ItemLoaderInterface +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID COPYQ_PLUGIN_ITEM_LOADER_ID) + Q_INTERFACES(ItemLoaderInterface) + +public: + ItemEncryptedLoader(); + + ~ItemEncryptedLoader(); + + ItemWidget *create(const QModelIndex &index, QWidget *parent, bool) const override; + + QString id() const override { return "itemencrypted"; } + QString name() const override { return tr("Encryption"); } + QString author() const override { return QString(); } + QString description() const override { return tr("Encrypt items and tabs."); } + QVariant icon() const override { return QVariant(IconLock); } + + QStringList formatsToSave() const override; + + QVariantMap applySettings() override; + + void loadSettings(const QVariantMap &settings) override { m_settings = settings; } + + QWidget *createSettingsWidget(QWidget *parent) override; + + bool canLoadItems(QIODevice *file) const override; + + bool canSaveItems(const QString &tabName) const override; + + ItemSaverPtr loadItems(const QString &tabName, QAbstractItemModel *model, QIODevice *file, int maxItems) override; + + ItemSaverPtr initializeTab(const QString &, QAbstractItemModel *model, int maxItems) override; + + QObject *tests(const TestInterfacePtr &test) const override; + + const QObject *signaler() const override { return this; } + + ItemScriptable *scriptableObject(QObject *parent) override; + + QList commands() const override; + +signals: + void error(const QString &); + void addCommands(const QList &commands); + +private slots: + void setPassword(); + void terminateGpgProcess(); + void onGpgProcessFinished(int exitCode, QProcess::ExitStatus exitStatus); + void addCommands(); + +private: + enum GpgProcessStatus { + GpgNotInstalled, + GpgNotRunning, + GpgGeneratingKeys, + GpgChangingPassword + }; + + void updateUi(); + + void emitDecryptFailed(); + + ItemSaverPtr createSaver(); + + std::unique_ptr ui; + QVariantMap m_settings; + + GpgProcessStatus m_gpgProcessStatus; + QProcess *m_gpgProcess; +}; + +#endif // ITEMENCRYPTED_H diff --git a/CopyQ-3.0.2/plugins/itemencrypted/itemencrypted.pro b/CopyQ-3.0.2/plugins/itemencrypted/itemencrypted.pro new file mode 100644 index 0000000..0ed1708 --- /dev/null +++ b/CopyQ-3.0.2/plugins/itemencrypted/itemencrypted.pro @@ -0,0 +1,23 @@ +include(../plugins_common.pri) + +HEADERS += itemencrypted.h \ + ../../src/gui/iconwidget.h +SOURCES += itemencrypted.cpp +SOURCES += \ + ../../src/common/config.cpp \ + ../../src/common/log.cpp \ + ../../src/common/mimetypes.cpp \ + ../../src/common/shortcuts.cpp \ + ../../src/common/textdata.cpp \ + ../../src/gui/iconfont.cpp \ + ../../src/gui/iconwidget.cpp \ + ../../src/item/serialize.cpp +FORMS += itemencryptedsettings.ui + +CONFIG(debug, debug|release) { + SOURCES += tests/itemencryptedtests.cpp + HEADERS += tests/itemencryptedtests.h +} + +TARGET = $$qtLibraryTarget(itemencrypted) + diff --git a/CopyQ-3.0.2/plugins/itemencrypted/itemencryptedsettings.ui b/CopyQ-3.0.2/plugins/itemencrypted/itemencryptedsettings.ui new file mode 100644 index 0000000..88a62cb --- /dev/null +++ b/CopyQ-3.0.2/plugins/itemencrypted/itemencryptedsettings.ui @@ -0,0 +1,160 @@ + + + ItemEncryptedSettings + + + + 0 + 0 + 324 + 367 + + + + + 0 + 1 + + + + + + + To encrypt and decrypt items add appropriate commands under Commands tab. + + + true + + + true + + + + + + + + + + true + + + + + + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + Add Actions to Menu and Toolbar + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + Sharing Encrypted Items and Tabs + + + + + + + + + true + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse + + + + + + + + + + Encrypted Tabs + + + + + + <p>Specify names of tabs (one per line) which will be automatically encrypted and decrypted.</p> +<p>Set unload tab interval in History tab to safely unload decrypted items from memory.</p> + + + true + + + + + + + + 0 + 1 + + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + diff --git a/CopyQ-3.0.2/plugins/itemencrypted/tests/itemencryptedtests.cpp b/CopyQ-3.0.2/plugins/itemencrypted/tests/itemencryptedtests.cpp new file mode 100644 index 0000000..413f0f5 --- /dev/null +++ b/CopyQ-3.0.2/plugins/itemencrypted/tests/itemencryptedtests.cpp @@ -0,0 +1,83 @@ +/* + Copyright (c) 2014, Lukas Holecek + + This file is part of CopyQ. + + CopyQ is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + CopyQ is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with CopyQ. If not, see . +*/ + +#include "itemencryptedtests.h" + +#include "tests/test_utils.h" + +ItemEncryptedTests::ItemEncryptedTests(const TestInterfacePtr &test, QObject *parent) + : QObject(parent) + , m_test(test) +{ +} + +void ItemEncryptedTests::initTestCase() +{ + if ( qgetenv("COPYQ_TESTS_SKIP_ITEMENCRYPT") == "1" ) + SKIP("Unset COPYQ_TESTS_SKIP_ITEMENCRYPT to run the tests"); + + TEST(m_test->initTestCase()); +} + +void ItemEncryptedTests::cleanupTestCase() +{ + TEST(m_test->cleanupTestCase()); +} + +void ItemEncryptedTests::init() +{ + TEST(m_test->init()); +} + +void ItemEncryptedTests::cleanup() +{ + TEST( m_test->cleanup() ); +} + +void ItemEncryptedTests::encryptDecryptData() +{ + if ( !isGpgInstalled() ) + SKIP("gpg2 is required to run the test"); + + RUN("-e" << "plugins.itemencrypted.generateTestKeys()", "\n"); + + // Test gpg errors first. + RUN("-e" << "plugins.itemencrypted.encrypt(input());print('')", ""); + + const QByteArray input("\x00\x01\x02\x03\x04", 5); + QByteArray stdoutActual; + QByteArray stderrActual; + + // Encrypted data differs. + QCOMPARE( m_test->run(Args("-e") << "plugins.itemencrypted.encrypt(input())", &stdoutActual, nullptr, input), 0 ); + QVERIFY(!stdoutActual.isEmpty()); + QVERIFY(stdoutActual != input); + + QCOMPARE( m_test->run(Args("-e") << "plugins.itemencrypted.decrypt(plugins.itemencrypted.encrypt(input()))", &stdoutActual, nullptr, input), 0 ); + QCOMPARE(stdoutActual, input); +} + +bool ItemEncryptedTests::isGpgInstalled() const +{ + QByteArray actualStdout; + const auto exitCode = m_test->run(Args("-e") << "plugins.itemencrypted.isGpgInstalled()", &actualStdout); + Q_ASSERT(exitCode == 0); + Q_ASSERT(actualStdout == "true\n" || actualStdout == "false\n"); + return actualStdout == "true\n"; +} diff --git a/CopyQ-3.0.2/plugins/itemencrypted/tests/itemencryptedtests.h b/CopyQ-3.0.2/plugins/itemencrypted/tests/itemencryptedtests.h new file mode 100644 index 0000000..f6543f9 --- /dev/null +++ b/CopyQ-3.0.2/plugins/itemencrypted/tests/itemencryptedtests.h @@ -0,0 +1,47 @@ +/* + Copyright (c) 2014, Lukas Holecek + + This file is part of CopyQ. + + CopyQ is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + CopyQ is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with CopyQ. If not, see . +*/ + +#ifndef ITEMENCRYPTEDTESTS_H +#define ITEMENCRYPTEDTESTS_H + +#include "tests/testinterface.h" + +#include + +class ItemEncryptedTests : public QObject +{ + Q_OBJECT +public: + explicit ItemEncryptedTests(const TestInterfacePtr &test, QObject *parent = nullptr); + +private slots: + void initTestCase(); + void cleanupTestCase(); + void init(); + void cleanup(); + + void encryptDecryptData(); + +private: + bool isGpgInstalled() const; + + TestInterfacePtr m_test; +}; + +#endif // ITEMENCRYPTEDTESTS_H diff --git a/CopyQ-3.0.2/plugins/itemfakevim/CMakeLists.txt b/CopyQ-3.0.2/plugins/itemfakevim/CMakeLists.txt new file mode 100644 index 0000000..2210253 --- /dev/null +++ b/CopyQ-3.0.2/plugins/itemfakevim/CMakeLists.txt @@ -0,0 +1,42 @@ +file(GLOB copyq_plugin_itemfakevim_SOURCES + ${copyq_plugin_itemfakevim_SOURCES} + fakevim/*.cpp + fakevim/utils/*.cpp + ) + +include_directories(fakevim) + +add_definitions( -DFAKEVIM_STANDALONE -DQTCREATOR_UTILS_STATIC_LIB ) +set(copyq_plugin_itemfakevim_DEFINITIONS + FAKEVIM_STANDALONE + QTCREATOR_UTILS_STATIC_LIB) + +set(copyq_plugin_itemfakevim_RESOURCES itemfakevim.qrc) + +copyq_add_plugin(itemfakevim) + +# Disable warnings for 3rd-party source code. +if (PEDANTIC) + if (CMAKE_COMPILER_IS_GNUCXX) + set(IGNORE_PEDANTIC_FLAGS "-Wno-suggest-override") + else() + set(IGNORE_PEDANTIC_FLAGS "-Wno-unused-macros") + endif() + + set_source_files_properties( + fakevim/fakevimhandler.cpp + fakevim/fakevimactions.cpp + PROPERTIES COMPILE_FLAGS + "\ + -Wno-shorten-64-to-32 \ + -Wno-sign-conversion \ + -Wno-conversion \ + -Wno-unreachable-code \ + -Wno-documentation-unknown-command \ + -Wno-shadow \ + -Wno-missing-declarations \ + -Wno-strict-overflow \ + ${IGNORE_PEDANTIC_FLAGS} \ + ") +endif() + diff --git a/CopyQ-3.0.2/plugins/itemfakevim/fakevim/LGPL_EXCEPTION.TXT b/CopyQ-3.0.2/plugins/itemfakevim/fakevim/LGPL_EXCEPTION.TXT new file mode 100644 index 0000000..add80b9 --- /dev/null +++ b/CopyQ-3.0.2/plugins/itemfakevim/fakevim/LGPL_EXCEPTION.TXT @@ -0,0 +1,22 @@ +Digia Qt LGPL Exception version 1.1 + +As an additional permission to the GNU Lesser General Public License version +2.1, the object code form of a "work that uses the Library" may incorporate +material from a header file that is part of the Library. You may distribute +such object code under terms of your choice, provided that: + (i) the header files of the Library have not been modified; and + (ii) the incorporated material is limited to numerical parameters, data + structure layouts, accessors, macros, inline functions and + templates; and + (iii) you comply with the terms of Section 6 of the GNU Lesser General + Public License version 2.1. + +Moreover, you may apply this exception to a modified version of the Library, +provided that such modification does not involve copying material from the +Library into the modified Library's header files unless such material is +limited to (i) numerical parameters; (ii) data structure layouts; +(iii) accessors; and (iv) small macros, templates and inline functions of +five lines or less in length. + +Furthermore, you are not required to apply this additional permission to a +modified version of the Library. diff --git a/CopyQ-3.0.2/plugins/itemfakevim/fakevim/LICENSE.LGPL b/CopyQ-3.0.2/plugins/itemfakevim/fakevim/LICENSE.LGPL new file mode 100644 index 0000000..602bfc9 --- /dev/null +++ b/CopyQ-3.0.2/plugins/itemfakevim/fakevim/LICENSE.LGPL @@ -0,0 +1,504 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! + + diff --git a/CopyQ-3.0.2/plugins/itemfakevim/fakevim/fakevim.png b/CopyQ-3.0.2/plugins/itemfakevim/fakevim/fakevim.png new file mode 100644 index 0000000..19bc688 Binary files /dev/null and b/CopyQ-3.0.2/plugins/itemfakevim/fakevim/fakevim.png differ diff --git a/CopyQ-3.0.2/plugins/itemfakevim/fakevim/fakevim.pri b/CopyQ-3.0.2/plugins/itemfakevim/fakevim/fakevim.pri new file mode 100644 index 0000000..ecc7127 --- /dev/null +++ b/CopyQ-3.0.2/plugins/itemfakevim/fakevim/fakevim.pri @@ -0,0 +1,14 @@ +include($$PWD/utils/utils.pri) + +DEFINES += FAKEVIM_STANDALONE + +INCLUDEPATH += $$PWD + +SOURCES += $$PWD/fakevimhandler.cpp \ + $$PWD/fakevimactions.cpp + +HEADERS += $$PWD/fakevimhandler.h \ + $$PWD/fakevimactions.h + +CONFIG += qt +QT += widgets diff --git a/CopyQ-3.0.2/plugins/itemfakevim/fakevim/fakevim.pro b/CopyQ-3.0.2/plugins/itemfakevim/fakevim/fakevim.pro new file mode 100644 index 0000000..e4d1ebc --- /dev/null +++ b/CopyQ-3.0.2/plugins/itemfakevim/fakevim/fakevim.pro @@ -0,0 +1,3 @@ +TEMPLATE = lib + +include(fakevim.pri) diff --git a/CopyQ-3.0.2/plugins/itemfakevim/fakevim/fakevimactions.cpp b/CopyQ-3.0.2/plugins/itemfakevim/fakevim/fakevimactions.cpp new file mode 100644 index 0000000..977ddc0 --- /dev/null +++ b/CopyQ-3.0.2/plugins/itemfakevim/fakevim/fakevimactions.cpp @@ -0,0 +1,236 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#include "fakevimactions.h" +#include "fakevimhandler.h" + +// Please do not add any direct dependencies to other Qt Creator code here. +// Instead emit signals and let the FakeVimPlugin channel the information to +// Qt Creator. The idea is to keep this file here in a "clean" state that +// allows easy reuse with any QTextEdit or QPlainTextEdit derived class. + + +#include + +#include +#include +#include + +#ifdef FAKEVIM_STANDALONE +using namespace FakeVim::Internal::Utils; +#else +using namespace Utils; +#endif + +/////////////////////////////////////////////////////////////////////// +// +// FakeVimSettings +// +/////////////////////////////////////////////////////////////////////// + +namespace FakeVim { +namespace Internal { + +typedef QLatin1String _; + +#ifdef FAKEVIM_STANDALONE +namespace Utils { + +SavedAction::SavedAction(QObject *parent) + : QObject(parent) +{ +} + +void SavedAction::setValue(const QVariant &value) +{ + m_value = value; +} + +QVariant SavedAction::value() const +{ + return m_value; +} + +void SavedAction::setDefaultValue(const QVariant &value) +{ + m_defaultValue = value; +} + +QVariant SavedAction::defaultValue() const +{ + return m_defaultValue; +} + +void SavedAction::setSettingsKey(const QString &key) +{ + m_settingsKey = key; +} + +QString SavedAction::settingsKey() const +{ + return m_settingsKey; +} + +} // namespace Utils +#endif // FAKEVIM_STANDALONE + +FakeVimSettings::FakeVimSettings() +{} + +FakeVimSettings::~FakeVimSettings() +{ + qDeleteAll(m_items); +} + +void FakeVimSettings::insertItem(int code, SavedAction *item, + const QString &longName, const QString &shortName) +{ + QTC_ASSERT(!m_items.contains(code), qDebug() << code; return); + m_items[code] = item; + if (!longName.isEmpty()) { + m_nameToCode[longName] = code; + m_codeToName[code] = longName; + } + if (!shortName.isEmpty()) + m_nameToCode[shortName] = code; +} + +#ifndef FAKEVIM_STANDALONE +void FakeVimSettings::readSettings(QSettings *settings) +{ + foreach (SavedAction *item, m_items) + item->readSettings(settings); +} + +void FakeVimSettings::writeSettings(QSettings *settings) +{ + foreach (SavedAction *item, m_items) + item->writeSettings(settings); +} +#endif // FAKEVIM_STANDALONE + +SavedAction *FakeVimSettings::item(int code) +{ + QTC_ASSERT(m_items.value(code, 0), qDebug() << "CODE: " << code; return 0); + return m_items.value(code, 0); +} + +SavedAction *FakeVimSettings::item(const QString &name) +{ + return m_items.value(m_nameToCode.value(name, -1), 0); +} + +QString FakeVimSettings::trySetValue(const QString &name, const QString &value) +{ + int code = m_nameToCode.value(name, -1); + if (code == -1) + return FakeVimHandler::tr("Unknown option: %1").arg(name); + if (code == ConfigTabStop || code == ConfigShiftWidth) { + if (value.toInt() <= 0) + return FakeVimHandler::tr("Argument must be positive: %1=%2") + .arg(name).arg(value); + } + SavedAction *act = item(code); + if (!act) + return FakeVimHandler::tr("Unknown option: %1").arg(name); + act->setValue(value); + return QString(); +} + +SavedAction *createAction(FakeVimSettings *instance, int code, const QVariant &value, + const QString &settingsKey = QString(), + const QString &shortKey = QString()) +{ + SavedAction *item = new SavedAction(instance); + item->setValue(value); +#ifndef FAKEVIM_STANDALONE + item->setSettingsKey(_("FakeVim"), settingsKey); + item->setDefaultValue(value); + item->setCheckable( value.canConvert() ); +#endif + instance->insertItem(code, item, settingsKey.toLower(), shortKey); + return item; +} + +FakeVimSettings *theFakeVimSettings() +{ + static FakeVimSettings *s = 0; + if (s) + return s; + + s = new FakeVimSettings; + + // Specific FakeVim settings + createAction(s, ConfigReadVimRc, false, _("ReadVimRc")); + createAction(s, ConfigVimRcPath, QString(), _("VimRcPath")); +#ifndef FAKEVIM_STANDALONE + createAction(s, ConfigUseFakeVim, false, _("UseFakeVim")); + s->item(ConfigUseFakeVim)->setText(QCoreApplication::translate("FakeVim::Internal", + "Use Vim-style Editing")); + s->item(ConfigReadVimRc)->setText(QCoreApplication::translate("FakeVim::Internal", + "Read .vimrc")); + s->item(ConfigVimRcPath)->setText(QCoreApplication::translate("FakeVim::Internal", + "Path to .vimrc")); +#endif + createAction(s, ConfigShowMarks, false, _("ShowMarks"), _("sm")); + createAction(s, ConfigPassControlKey, false, _("PassControlKey"), _("pck")); + createAction(s, ConfigPassKeys, true, _("PassKeys"), _("pk")); + + // Emulated Vim setting + createAction(s, ConfigStartOfLine, true, _("StartOfLine"), _("sol")); + createAction(s, ConfigTabStop, 8, _("TabStop"), _("ts")); + createAction(s, ConfigSmartTab, false, _("SmartTab"), _("sta")); + createAction(s, ConfigHlSearch, true, _("HlSearch"), _("hls")); + createAction(s, ConfigShiftWidth, 8, _("ShiftWidth"), _("sw")); + createAction(s, ConfigExpandTab, false, _("ExpandTab"), _("et")); + createAction(s, ConfigAutoIndent, false, _("AutoIndent"), _("ai")); + createAction(s, ConfigSmartIndent, false, _("SmartIndent"), _("si")); + createAction(s, ConfigIncSearch, true, _("IncSearch"), _("is")); + createAction(s, ConfigUseCoreSearch, false, _("UseCoreSearch"), _("ucs")); + createAction(s, ConfigSmartCase, false, _("SmartCase"), _("scs")); + createAction(s, ConfigIgnoreCase, false, _("IgnoreCase"), _("ic")); + createAction(s, ConfigWrapScan, true, _("WrapScan"), _("ws")); + createAction(s, ConfigTildeOp, false, _("TildeOp"), _("top")); + createAction(s, ConfigShowCmd, true, _("ShowCmd"), _("sc")); + createAction(s, ConfigRelativeNumber, false, _("RelativeNumber"),_("rnu")); + createAction(s, ConfigScrollOff, 0, _("ScrollOff"), _("so")); + createAction(s, ConfigBackspace, _("indent,eol,start"), _("ConfigBackspace"), _("bs")); + createAction(s, ConfigIsKeyword, _("@,48-57,_,192-255,a-z,A-Z"), _("IsKeyword"), _("isk")); + createAction(s, ConfigClipboard, QString(), _("Clipboard"), _("cb")); + + return s; +} + +SavedAction *theFakeVimSetting(int code) +{ + return theFakeVimSettings()->item(code); +} + +} // namespace Internal +} // namespace FakeVim diff --git a/CopyQ-3.0.2/plugins/itemfakevim/fakevim/fakevimactions.h b/CopyQ-3.0.2/plugins/itemfakevim/fakevim/fakevimactions.h new file mode 100644 index 0000000..513af4f --- /dev/null +++ b/CopyQ-3.0.2/plugins/itemfakevim/fakevim/fakevimactions.h @@ -0,0 +1,144 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#ifndef FAKEVIM_ACTIONS_H +#define FAKEVIM_ACTIONS_H + +#ifndef FAKEVIM_STANDALONE +# include +#endif + +#include +#include +#include +#include + +namespace FakeVim { +namespace Internal { + +#ifdef FAKEVIM_STANDALONE +namespace Utils { + +class SavedAction : public QObject +{ + Q_OBJECT + +public: + SavedAction(QObject *parent); + void setValue(const QVariant &value); + QVariant value() const; + void setDefaultValue(const QVariant &value); + QVariant defaultValue() const; + void setSettingsKey(const QString &key); + QString settingsKey() const; + + QVariant m_value; + QVariant m_defaultValue; + QString m_settingsKey; +}; + +} // namespace Utils +#endif // FAKEVIM_STANDALONE + +enum FakeVimSettingsCode +{ + ConfigUseFakeVim, + ConfigReadVimRc, + ConfigVimRcPath, + + ConfigStartOfLine, + ConfigHlSearch, + ConfigTabStop, + ConfigSmartTab, + ConfigShiftWidth, + ConfigExpandTab, + ConfigAutoIndent, + ConfigSmartIndent, + + ConfigIncSearch, + ConfigUseCoreSearch, + ConfigSmartCase, + ConfigIgnoreCase, + ConfigWrapScan, + + // command ~ behaves as g~ + ConfigTildeOp, + + // indent allow backspacing over autoindent + // eol allow backspacing over line breaks (join lines) + // start allow backspacing over the start of insert; CTRL-W and CTRL-U + // stop once at the start of insert. + ConfigBackspace, + + // @,48-57,_,192-255 + ConfigIsKeyword, + + // other actions + ConfigShowMarks, + ConfigPassControlKey, + ConfigPassKeys, + ConfigClipboard, + ConfigShowCmd, + ConfigScrollOff, + ConfigRelativeNumber +}; + +class FakeVimSettings : public QObject +{ + Q_OBJECT + +public: + FakeVimSettings(); + ~FakeVimSettings(); + void insertItem(int code, Utils::SavedAction *item, + const QString &longname = QString(), + const QString &shortname = QString()); + + Utils::SavedAction *item(int code); + Utils::SavedAction *item(const QString &name); + QString trySetValue(const QString &name, const QString &value); + +#ifndef FAKEVIM_STANDALONE + void readSettings(QSettings *settings); + void writeSettings(QSettings *settings); +#endif + +private: + QHash m_items; + QHash m_nameToCode; + QHash m_codeToName; +}; + +FakeVimSettings *theFakeVimSettings(); +Utils::SavedAction *theFakeVimSetting(int code); + +} // namespace Internal +} // namespace FakeVim + +#endif // FAKEVIM_ACTTIONS_H diff --git a/CopyQ-3.0.2/plugins/itemfakevim/fakevim/fakevimhandler.cpp b/CopyQ-3.0.2/plugins/itemfakevim/fakevim/fakevimhandler.cpp new file mode 100644 index 0000000..73b7e40 --- /dev/null +++ b/CopyQ-3.0.2/plugins/itemfakevim/fakevim/fakevimhandler.cpp @@ -0,0 +1,8671 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +// +// ATTENTION: +// +// 1 Please do not add any direct dependencies to other Qt Creator code here. +// Instead emit signals and let the FakeVimPlugin channel the information to +// Qt Creator. The idea is to keep this file here in a "clean" state that +// allows easy reuse with any QTextEdit or QPlainTextEdit derived class. +// +// 2 There are a few auto tests located in ../../../tests/auto/fakevim. +// Commands that are covered there are marked as "// tested" below. +// +// 3 Some conventions: +// +// Use 1 based line numbers and 0 based column numbers. Even though +// the 1 based line are not nice it matches vim's and QTextEdit's 'line' +// concepts. +// +// Do not pass QTextCursor etc around unless really needed. Convert +// early to line/column. +// +// A QTextCursor is always between characters, whereas vi's cursor is always +// over a character. FakeVim interprets the QTextCursor to be over the character +// to the right of the QTextCursor's position(). +// +// A current "region of interest" +// spans between anchor(), (i.e. the character below anchor()), and +// position(). The character below position() is not included +// if the last movement command was exclusive (MoveExclusive). +// + +#include "fakevimhandler.h" + +#include "fakevimactions.h" + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +//#define DEBUG_KEY 1 +#ifdef DEBUG_KEY +# define KEY_DEBUG(s) qDebug() << s +#else +# define KEY_DEBUG(s) +#endif + +//#define DEBUG_UNDO 1 +#ifdef DEBUG_UNDO +# define UNDO_DEBUG(s) qDebug() << "REV" << revision() << s +#else +# define UNDO_DEBUG(s) +#endif + +using namespace Utils; +#ifdef FAKEVIM_STANDALONE +using namespace FakeVim::Internal::Utils; +#endif + +namespace FakeVim { +namespace Internal { + +/////////////////////////////////////////////////////////////////////// +// +// FakeVimHandler +// +/////////////////////////////////////////////////////////////////////// + +#define StartOfLine QTextCursor::StartOfLine +#define EndOfLine QTextCursor::EndOfLine +#define MoveAnchor QTextCursor::MoveAnchor +#define KeepAnchor QTextCursor::KeepAnchor +#define Up QTextCursor::Up +#define Down QTextCursor::Down +#define Right QTextCursor::Right +#define Left QTextCursor::Left +#define EndOfDocument QTextCursor::End +#define StartOfDocument QTextCursor::Start +#define NextBlock QTextCursor::NextBlock + +#define ParagraphSeparator QChar::ParagraphSeparator + +#define EDITOR(s) (m_textedit ? m_textedit->s : m_plaintextedit->s) + +#define MetaModifier // Use HostOsInfo::controlModifier() instead +#define ControlModifier // Use HostOsInfo::controlModifier() instead + +typedef QLatin1String _; + +/* Clipboard MIME types used by Vim. */ +static const QString vimMimeText = _("_VIM_TEXT"); +static const QString vimMimeTextEncoded = _("_VIMENC_TEXT"); + +using namespace Qt; + +/*! A \e Mode represents one of the basic modes of operation of FakeVim. +*/ + +enum Mode +{ + InsertMode, + ReplaceMode, + CommandMode, + ExMode +}; + +enum BlockInsertMode +{ + NoneBlockInsertMode, + AppendBlockInsertMode, + AppendToEndOfLineBlockInsertMode, + InsertBlockInsertMode, + ChangeBlockInsertMode +}; + +/*! A \e SubMode is used for things that require one more data item + and are 'nested' behind a \l Mode. +*/ +enum SubMode +{ + NoSubMode, + ChangeSubMode, // Used for c + DeleteSubMode, // Used for d + FilterSubMode, // Used for ! + IndentSubMode, // Used for = + RegisterSubMode, // Used for " + ShiftLeftSubMode, // Used for < + ShiftRightSubMode, // Used for > + InvertCaseSubMode, // Used for g~ + DownCaseSubMode, // Used for gu + UpCaseSubMode, // Used for gU + WindowSubMode, // Used for Ctrl-w + YankSubMode, // Used for y + ZSubMode, // Used for z + CapitalZSubMode, // Used for Z + ReplaceSubMode, // Used for r + MacroRecordSubMode, // Used for q + MacroExecuteSubMode, // Used for @ + CtrlVSubMode // Used for Ctrl-v in insert mode +}; + +/*! A \e SubSubMode is used for things that require one more data item + and are 'nested' behind a \l SubMode. +*/ +enum SubSubMode +{ + NoSubSubMode, + FtSubSubMode, // Used for f, F, t, T. + MarkSubSubMode, // Used for m. + BackTickSubSubMode, // Used for `. + TickSubSubMode, // Used for '. + TextObjectSubSubMode, // Used for thing like iw, aW, as etc. + ZSubSubMode, // Used for zj, zk + OpenSquareSubSubMode, // Used for [{, {(, [z + CloseSquareSubSubMode, // Used for ]}, ]), ]z + SearchSubSubMode, + CtrlVUnicodeSubSubMode // Used for Ctrl-v based unicode input +}; + +enum VisualMode +{ + NoVisualMode, + VisualCharMode, + VisualLineMode, + VisualBlockMode +}; + +enum MoveType +{ + MoveExclusive, + MoveInclusive, + MoveLineWise +}; + +/*! + \enum RangeMode + + The \e RangeMode serves as a means to define how the "Range" between + the \l cursor and the \l anchor position is to be interpreted. + + \value RangeCharMode Entered by pressing \key v. The range includes + all characters between cursor and anchor. + \value RangeLineMode Entered by pressing \key V. The range includes + all lines between the line of the cursor and + the line of the anchor. + \value RangeLineModeExclusice Like \l RangeLineMode, but keeps one + newline when deleting. + \value RangeBlockMode Entered by pressing \key Ctrl-v. The range includes + all characters with line and column coordinates + between line and columns coordinates of cursor and + anchor. + \value RangeBlockAndTailMode Like \l RangeBlockMode, but also includes + all characters in the affected lines up to the end + of these lines. +*/ + +enum EventResult +{ + EventHandled, + EventUnhandled, + EventCancelled, // Event is handled but a sub mode was cancelled. + EventPassedToCore +}; + +struct CursorPosition +{ + CursorPosition() : line(-1), column(-1) {} + CursorPosition(int block, int column) : line(block), column(column) {} + explicit CursorPosition(const QTextCursor &tc) + : line(tc.block().blockNumber()), column(tc.positionInBlock()) {} + CursorPosition(const QTextDocument *document, int position) + { + QTextBlock block = document->findBlock(position); + line = block.blockNumber(); + column = position - block.position(); + } + bool isValid() const { return line >= 0 && column >= 0; } + bool operator>(const CursorPosition &other) const + { return line > other.line || column > other.column; } + bool operator==(const CursorPosition &other) const + { return line == other.line && column == other.column; } + bool operator!=(const CursorPosition &other) const { return !operator==(other); } + + int line; // Line in document (from 0, folded lines included). + int column; // Position on line. +}; + +QDebug operator<<(QDebug ts, const CursorPosition &pos) +{ + return ts << "(line: " << pos.line << ", column: " << pos.column << ")"; +} + +class Mark +{ +public: + Mark(const CursorPosition &pos = CursorPosition(), const QString &fileName = QString()) + : m_position(pos), m_fileName(fileName) {} + + bool isValid() const { return m_position.isValid(); } + + bool isLocal(const QString &localFileName) const + { + return m_fileName.isEmpty() || m_fileName == localFileName; + } + + /* Return position of mark within given document. + * If saved line number is too big, mark position is at the end of document. + * If line number is in document but column is too big, mark position is at the end of line. + */ + CursorPosition position(const QTextDocument *document) const + { + QTextBlock block = document->findBlockByNumber(m_position.line); + CursorPosition pos; + if (block.isValid()) { + pos.line = m_position.line; + pos.column = qMax(0, qMin(m_position.column, block.length() - 2)); + } else if (document->isEmpty()) { + pos.line = 0; + pos.column = 0; + } else { + pos.line = document->blockCount() - 1; + pos.column = qMax(0, document->lastBlock().length() - 2); + } + return pos; + } + + const QString &fileName() const { return m_fileName; } + +private: + CursorPosition m_position; + QString m_fileName; +}; +typedef QHash Marks; +typedef QHashIterator MarksIterator; + +struct State +{ + State() : revision(-1), position(), marks(), lastVisualMode(NoVisualMode), + lastVisualModeInverted(false) {} + State(int revision, const CursorPosition &position, const Marks &marks, + VisualMode lastVisualMode, bool lastVisualModeInverted) : revision(revision), + position(position), marks(marks), lastVisualMode(lastVisualMode), + lastVisualModeInverted(lastVisualModeInverted) {} + + bool isValid() const { return position.isValid(); } + + int revision; + CursorPosition position; + Marks marks; + VisualMode lastVisualMode; + bool lastVisualModeInverted; +}; + +struct Column +{ + Column(int p, int l) : physical(p), logical(l) {} + int physical; // Number of characters in the data. + int logical; // Column on screen. +}; + +QDebug operator<<(QDebug ts, const Column &col) +{ + return ts << "(p: " << col.physical << ", l: " << col.logical << ")"; +} + +struct Register +{ + Register() : rangemode(RangeCharMode) {} + Register(const QString &c) : contents(c), rangemode(RangeCharMode) {} + Register(const QString &c, RangeMode m) : contents(c), rangemode(m) {} + QString contents; + RangeMode rangemode; +}; + +QDebug operator<<(QDebug ts, const Register ®) +{ + return ts << reg.contents; +} + +struct SearchData +{ + SearchData() + { + forward = true; + highlightMatches = true; + } + + QString needle; + bool forward; + bool highlightMatches; +}; + +// If string begins with given prefix remove it with trailing spaces and return true. +static bool eatString(const char *prefix, QString *str) +{ + if (!str->startsWith(_(prefix))) + return false; + *str = str->mid(strlen(prefix)).trimmed(); + return true; +} + +static QRegExp vimPatternToQtPattern(QString needle, bool ignoreCaseOption, bool smartCaseOption) +{ + /* Transformations (Vim regexp -> QRegExp): + * \a -> [A-Za-z] + * \A -> [^A-Za-z] + * \h -> [A-Za-z_] + * \H -> [^A-Za-z_] + * \l -> [a-z] + * \L -> [^a-z] + * \o -> [0-7] + * \O -> [^0-7] + * \u -> [A-Z] + * \U -> [^A-Z] + * \x -> [0-9A-Fa-f] + * \X -> [^0-9A-Fa-f] + * + * \< -> \b + * \> -> \b + * [] -> \[\] + * \= -> ? + * + * (...) <-> \(...\) + * {...} <-> \{...\} + * | <-> \| + * ? <-> \? + * + <-> \+ + * \{...} -> {...} + * + * \c - set ignorecase for rest + * \C - set noignorecase for rest + */ + // FIXME: Option smartcase should be used only if search was typed by user. + bool ignorecase = ignoreCaseOption + && !(smartCaseOption && needle.contains(QRegExp(_("[A-Z]")))); + QString pattern; + pattern.reserve(2 * needle.size()); + + bool escape = false; + bool brace = false; + bool embraced = false; + bool range = false; + bool curly = false; + foreach (const QChar &c, needle) { + if (brace) { + brace = false; + if (c == QLatin1Char(']')) { + pattern.append(_("\\[\\]")); + continue; + } + pattern.append(QLatin1Char('[')); + escape = true; + embraced = true; + } + if (embraced) { + if (range) { + QChar c2 = pattern[pattern.size() - 2]; + pattern.remove(pattern.size() - 2, 2); + pattern.append(c2.toUpper() + QLatin1Char('-') + c.toUpper()); + pattern.append(c2.toLower() + QLatin1Char('-') + c.toLower()); + range = false; + } else if (escape) { + escape = false; + pattern.append(c); + } else if (c == QLatin1Char('\\')) { + escape = true; + } else if (c == QLatin1Char(']')) { + pattern.append(QLatin1Char(']')); + embraced = false; + } else if (c == QLatin1Char('-')) { + range = ignorecase && pattern[pattern.size() - 1].isLetter(); + pattern.append(QLatin1Char('-')); + } else if (c.isLetter() && ignorecase) { + pattern.append(c.toLower()).append(c.toUpper()); + } else { + pattern.append(c); + } + } else if (QString::fromLatin1("(){}+|?").indexOf(c) != -1) { + if (c == QLatin1Char('{')) { + curly = escape; + } else if (c == QLatin1Char('}') && curly) { + curly = false; + escape = true; + } + + if (escape) + escape = false; + else + pattern.append(QLatin1Char('\\')); + pattern.append(c); + } else if (escape) { + // escape expression + escape = false; + if (c == QLatin1Char('<') || c == QLatin1Char('>')) + pattern.append(_("\\b")); + else if (c == QLatin1Char('a')) + pattern.append(_("[a-zA-Z]")); + else if (c == QLatin1Char('A')) + pattern.append(_("[^a-zA-Z]")); + else if (c == QLatin1Char('h')) + pattern.append(_("[A-Za-z_]")); + else if (c == QLatin1Char('H')) + pattern.append(_("[^A-Za-z_]")); + else if (c == QLatin1Char('c') || c == QLatin1Char('C')) + ignorecase = (c == QLatin1Char('c')); + else if (c == QLatin1Char('l')) + pattern.append(_("[a-z]")); + else if (c == QLatin1Char('L')) + pattern.append(_("[^a-z]")); + else if (c == QLatin1Char('o')) + pattern.append(_("[0-7]")); + else if (c == QLatin1Char('O')) + pattern.append(_("[^0-7]")); + else if (c == QLatin1Char('u')) + pattern.append(_("[A-Z]")); + else if (c == QLatin1Char('U')) + pattern.append(_("[^A-Z]")); + else if (c == QLatin1Char('x')) + pattern.append(_("[0-9A-Fa-f]")); + else if (c == QLatin1Char('X')) + pattern.append(_("[^0-9A-Fa-f]")); + else if (c == QLatin1Char('=')) + pattern.append(_("?")); + else + pattern.append(QLatin1Char('\\') + c); + } else { + // unescaped expression + if (c == QLatin1Char('\\')) + escape = true; + else if (c == QLatin1Char('[')) + brace = true; + else if (c.isLetter() && ignorecase) + pattern.append(QLatin1Char('[') + c.toLower() + c.toUpper() + QLatin1Char(']')); + else + pattern.append(c); + } + } + if (escape) + pattern.append(QLatin1Char('\\')); + else if (brace) + pattern.append(QLatin1Char('[')); + + return QRegExp(pattern); +} + +static bool afterEndOfLine(const QTextDocument *doc, int position) +{ + return doc->characterAt(position) == ParagraphSeparator + && doc->findBlock(position).length() > 1; +} + +static void searchForward(QTextCursor *tc, QRegExp &needleExp, int *repeat) +{ + const QTextDocument *doc = tc->document(); + const int startPos = tc->position(); + + // Search from beginning of line so that matched text is the same. + tc->movePosition(StartOfLine); + + // forward to current position + *tc = doc->find(needleExp, *tc); + while (!tc->isNull() && tc->anchor() < startPos) { + if (!tc->hasSelection()) + tc->movePosition(Right); + if (tc->atBlockEnd()) + tc->movePosition(NextBlock); + *tc = doc->find(needleExp, *tc); + } + + if (tc->isNull()) + return; + + --*repeat; + + while (*repeat > 0) { + if (!tc->hasSelection()) + tc->movePosition(Right); + if (tc->atBlockEnd()) + tc->movePosition(NextBlock); + *tc = doc->find(needleExp, *tc); + if (tc->isNull()) + return; + --*repeat; + } + + if (!tc->isNull() && afterEndOfLine(doc, tc->anchor())) + tc->movePosition(Left); +} + +static void searchBackward(QTextCursor *tc, QRegExp &needleExp, int *repeat) +{ + // Search from beginning of line so that matched text is the same. + QTextBlock block = tc->block(); + QString line = block.text(); + + int i = line.indexOf(needleExp, 0); + while (i != -1 && i < tc->positionInBlock()) { + --*repeat; + i = line.indexOf(needleExp, i + qMax(1, needleExp.matchedLength())); + if (i == line.size()) + i = -1; + } + + if (i == tc->positionInBlock()) + --*repeat; + + while (*repeat > 0) { + block = block.previous(); + if (!block.isValid()) + break; + line = block.text(); + i = line.indexOf(needleExp, 0); + while (i != -1) { + --*repeat; + i = line.indexOf(needleExp, i + qMax(1, needleExp.matchedLength())); + if (i == line.size()) + i = -1; + } + } + + if (!block.isValid()) { + *tc = QTextCursor(); + return; + } + + i = line.indexOf(needleExp, 0); + while (*repeat < 0) { + i = line.indexOf(needleExp, i + qMax(1, needleExp.matchedLength())); + ++*repeat; + } + tc->setPosition(block.position() + i); + tc->setPosition(tc->position() + needleExp.matchedLength(), KeepAnchor); +} + +// Commands [[, [] +static void bracketSearchBackward(QTextCursor *tc, const QString &needleExp, int repeat) +{ + QRegExp re(needleExp); + QTextCursor tc2 = *tc; + tc2.setPosition(tc2.position() - 1); + searchBackward(&tc2, re, &repeat); + if (repeat <= 1) + tc->setPosition(tc2.isNull() ? 0 : tc2.position(), KeepAnchor); +} + +// Commands ][, ]] +// When ]] is used after an operator, then also stops below a '}' in the first column. +static void bracketSearchForward(QTextCursor *tc, const QString &needleExp, int repeat, + bool searchWithCommand) +{ + QRegExp re(searchWithCommand ? QString(_("^\\}|^\\{")) : needleExp); + QTextCursor tc2 = *tc; + tc2.setPosition(tc2.position() + 1); + searchForward(&tc2, re, &repeat); + if (repeat <= 1) { + if (tc2.isNull()) { + tc->setPosition(tc->document()->characterCount() - 1, KeepAnchor); + } else { + tc->setPosition(tc2.position() - 1, KeepAnchor); + if (searchWithCommand && tc->document()->characterAt(tc->position()).unicode() == '}') { + QTextBlock block = tc->block().next(); + if (block.isValid()) + tc->setPosition(block.position(), KeepAnchor); + } + } + } +} + +static bool substituteText(QString *text, QRegExp &pattern, const QString &replacement, + bool global) +{ + bool substituted = false; + int pos = 0; + int right = -1; + while (true) { + pos = pattern.indexIn(*text, pos, QRegExp::CaretAtZero); + if (pos == -1) + break; + + // ensure that substitution is advancing towards end of line + if (right == text->size() - pos) { + ++pos; + if (pos == text->size()) + break; + continue; + } + + right = text->size() - pos; + + substituted = true; + QString matched = text->mid(pos, pattern.cap(0).size()); + QString repl; + bool escape = false; + // insert captured texts + for (int i = 0; i < replacement.size(); ++i) { + const QChar &c = replacement[i]; + if (escape) { + escape = false; + if (c.isDigit()) { + if (c.digitValue() <= pattern.captureCount()) + repl += pattern.cap(c.digitValue()); + } else { + repl += c; + } + } else { + if (c == QLatin1Char('\\')) + escape = true; + else if (c == QLatin1Char('&')) + repl += pattern.cap(0); + else + repl += c; + } + } + text->replace(pos, matched.size(), repl); + pos += (repl.isEmpty() && matched.isEmpty()) ? 1 : repl.size(); + + if (pos >= text->size() || !global) + break; + } + + return substituted; +} + +static int findUnescaped(QChar c, const QString &line, int from) +{ + for (int i = from; i < line.size(); ++i) { + if (line.at(i) == c && (i == 0 || line.at(i - 1) != QLatin1Char('\\'))) + return i; + } + return -1; +} + +static void setClipboardData(const QString &content, RangeMode mode, + QClipboard::Mode clipboardMode) +{ + QClipboard *clipboard = QApplication::clipboard(); + char vimRangeMode = mode; + + QByteArray bytes1; + bytes1.append(vimRangeMode); + bytes1.append(content.toUtf8()); + + QByteArray bytes2; + bytes2.append(vimRangeMode); + bytes2.append("utf-8"); + bytes2.append('\0'); + bytes2.append(content.toUtf8()); + + QMimeData *data = new QMimeData; + data->setText(content); + data->setData(vimMimeText, bytes1); + data->setData(vimMimeTextEncoded, bytes2); + clipboard->setMimeData(data, clipboardMode); +} + +static QByteArray toLocalEncoding(const QString &text) +{ + return HostOsInfo::isWindowsHost() ? QString(text).replace(_("\n"), _("\r\n")).toLocal8Bit() + : text.toLocal8Bit(); +} + +static QString fromLocalEncoding(const QByteArray &data) +{ + return HostOsInfo::isWindowsHost() ? QString::fromLocal8Bit(data).replace(_("\n"), _("\r\n")) + : QString::fromLocal8Bit(data); +} + +static QString getProcessOutput(const QString &command, const QString &input) +{ + QProcess proc; + proc.start(command); + proc.waitForStarted(); + proc.write(toLocalEncoding(input)); + proc.closeWriteChannel(); + + // FIXME: Process should be interruptable by user. + // Solution is to create a QObject for each process and emit finished state. + proc.waitForFinished(); + + return fromLocalEncoding(proc.readAllStandardOutput()); +} + +static const QMap &vimKeyNames() +{ + static QMap k; + if (!k.isEmpty()) + return k; + + // FIXME: Should be value of mapleader. + k.insert(_("LEADER"), Key_Backslash); + + k.insert(_("SPACE"), Key_Space); + k.insert(_("TAB"), Key_Tab); + k.insert(_("NL"), Key_Return); + k.insert(_("NEWLINE"), Key_Return); + k.insert(_("LINEFEED"), Key_Return); + k.insert(_("LF"), Key_Return); + k.insert(_("CR"), Key_Return); + k.insert(_("RETURN"), Key_Return); + k.insert(_("ENTER"), Key_Return); + k.insert(_("BS"), Key_Backspace); + k.insert(_("BACKSPACE"), Key_Backspace); + k.insert(_("ESC"), Key_Escape); + k.insert(_("BAR"), Key_Bar); + k.insert(_("BSLASH"), Key_Backslash); + k.insert(_("DEL"), Key_Delete); + k.insert(_("DELETE"), Key_Delete); + k.insert(_("KDEL"), Key_Delete); + k.insert(_("UP"), Key_Up); + k.insert(_("DOWN"), Key_Down); + k.insert(_("LEFT"), Key_Left); + k.insert(_("RIGHT"), Key_Right); + + k.insert(_("LT"), Key_Less); + k.insert(_("GT"), Key_Greater); + + k.insert(_("F1"), Key_F1); + k.insert(_("F2"), Key_F2); + k.insert(_("F3"), Key_F3); + k.insert(_("F4"), Key_F4); + k.insert(_("F5"), Key_F5); + k.insert(_("F6"), Key_F6); + k.insert(_("F7"), Key_F7); + k.insert(_("F8"), Key_F8); + k.insert(_("F9"), Key_F9); + k.insert(_("F10"), Key_F10); + + k.insert(_("F11"), Key_F11); + k.insert(_("F12"), Key_F12); + k.insert(_("F13"), Key_F13); + k.insert(_("F14"), Key_F14); + k.insert(_("F15"), Key_F15); + k.insert(_("F16"), Key_F16); + k.insert(_("F17"), Key_F17); + k.insert(_("F18"), Key_F18); + k.insert(_("F19"), Key_F19); + k.insert(_("F20"), Key_F20); + + k.insert(_("F21"), Key_F21); + k.insert(_("F22"), Key_F22); + k.insert(_("F23"), Key_F23); + k.insert(_("F24"), Key_F24); + k.insert(_("F25"), Key_F25); + k.insert(_("F26"), Key_F26); + k.insert(_("F27"), Key_F27); + k.insert(_("F28"), Key_F28); + k.insert(_("F29"), Key_F29); + k.insert(_("F30"), Key_F30); + + k.insert(_("F31"), Key_F31); + k.insert(_("F32"), Key_F32); + k.insert(_("F33"), Key_F33); + k.insert(_("F34"), Key_F34); + k.insert(_("F35"), Key_F35); + + k.insert(_("INSERT"), Key_Insert); + k.insert(_("INS"), Key_Insert); + k.insert(_("KINSERT"), Key_Insert); + k.insert(_("HOME"), Key_Home); + k.insert(_("END"), Key_End); + k.insert(_("PAGEUP"), Key_PageUp); + k.insert(_("PAGEDOWN"), Key_PageDown); + + k.insert(_("KPLUS"), Key_Plus); + k.insert(_("KMINUS"), Key_Minus); + k.insert(_("KDIVIDE"), Key_Slash); + k.insert(_("KMULTIPLY"), Key_Asterisk); + k.insert(_("KENTER"), Key_Enter); + k.insert(_("KPOINT"), Key_Period); + + return k; +} + +static bool isOnlyControlModifier(const Qt::KeyboardModifiers &mods) +{ + return (mods ^ HostOsInfo::controlModifier()) == Qt::NoModifier; +} + + +Range::Range() + : beginPos(-1), endPos(-1), rangemode(RangeCharMode) +{} + +Range::Range(int b, int e, RangeMode m) + : beginPos(qMin(b, e)), endPos(qMax(b, e)), rangemode(m) +{} + +QString Range::toString() const +{ + return QString::fromLatin1("%1-%2 (mode: %3)").arg(beginPos).arg(endPos) + .arg(rangemode); +} + +bool Range::isValid() const +{ + return beginPos >= 0 && endPos >= 0; +} + +QDebug operator<<(QDebug ts, const Range &range) +{ + return ts << '[' << range.beginPos << ',' << range.endPos << ']'; +} + + +ExCommand::ExCommand(const QString &c, const QString &a, const Range &r) + : cmd(c), hasBang(false), args(a), range(r), count(1) +{} + +bool ExCommand::matches(const QString &min, const QString &full) const +{ + return cmd.startsWith(min) && full.startsWith(cmd); +} + +QDebug operator<<(QDebug ts, const ExCommand &cmd) +{ + return ts << cmd.cmd << ' ' << cmd.args << ' ' << cmd.range; +} + +QDebug operator<<(QDebug ts, const QList &sels) +{ + foreach (const QTextEdit::ExtraSelection &sel, sels) + ts << "SEL: " << sel.cursor.anchor() << sel.cursor.position(); + return ts; +} + +QString quoteUnprintable(const QString &ba) +{ + QString res; + for (int i = 0, n = ba.size(); i != n; ++i) { + const QChar c = ba.at(i); + const int cc = c.unicode(); + if (c.isPrint()) + res += c; + else if (cc == QLatin1Char('\n')) + res += _(""); + else + res += QString::fromLatin1("\\x%1").arg(c.unicode(), 2, 16, QLatin1Char('0')); + } + return res; +} + +static bool startsWithWhitespace(const QString &str, int col) +{ + QTC_ASSERT(str.size() >= col, return false); + for (int i = 0; i < col; ++i) { + uint u = str.at(i).unicode(); + if (u != QLatin1Char(' ') && u != QLatin1Char('\t')) + return false; + } + return true; +} + +inline QString msgMarkNotSet(const QString &text) +{ + return FakeVimHandler::tr("Mark \"%1\" not set.").arg(text); +} + +class Input +{ +public: + // Remove some extra "information" on Mac. + static Qt::KeyboardModifiers cleanModifier(Qt::KeyboardModifiers m) + { + return m & ~Qt::KeypadModifier; + } + + Input() + : m_key(0), m_xkey(0), m_modifiers(0) {} + + explicit Input(QChar x) + : m_key(x.unicode()), m_xkey(x.unicode()), m_modifiers(0), m_text(x) + { + if (x.isUpper()) + m_modifiers = Qt::ShiftModifier; + else if (x.isLower()) + m_key = x.toUpper().unicode(); + } + + Input(int k, Qt::KeyboardModifiers m, const QString &t = QString()) + : m_key(k), m_modifiers(cleanModifier(m)), m_text(t) + { + if (m_text.size() == 1) { + QChar x = m_text.at(0); + + // On Mac, QKeyEvent::text() returns non-empty strings for + // cursor keys. This breaks some of the logic later on + // relying on text() being empty for "special" keys. + // FIXME: Check the real conditions. + if (x.unicode() < ' ') + m_text.clear(); + else if (x.isLetter()) + m_key = x.toUpper().unicode(); + } + + // Set text only if input is ascii key without control modifier. + if (m_text.isEmpty() && k >= 0 && k <= 0x7f && (m & HostOsInfo::controlModifier()) == 0) { + QChar c = QChar::fromLatin1(k); + if (c.isLetter()) + m_text = QString(isShift() ? c.toUpper() : c); + else if (!isShift()) + m_text = c; + } + + // Normalize . + if (m_key == Qt::Key_Backtab) { + m_key = Qt::Key_Tab; + m_modifiers |= Qt::ShiftModifier; + } + + // m_xkey is only a cache. + m_xkey = (m_text.size() == 1 ? m_text.at(0).unicode() : m_key); + } + + bool isValid() const + { + return m_key != 0 || !m_text.isNull(); + } + + bool isDigit() const + { + return m_xkey >= '0' && m_xkey <= '9'; + } + + bool isKey(int c) const + { + return !m_modifiers && m_key == c; + } + + bool isBackspace() const + { + return m_key == Key_Backspace || isControl('h'); + } + + bool isReturn() const + { + return m_key == QLatin1Char('\n') || m_key == Key_Return || m_key == Key_Enter; + } + + bool isEscape() const + { + return isKey(Key_Escape) || isKey(27) || isControl('c') + || isControl(Key_BracketLeft); + } + + bool is(int c) const + { + return m_xkey == c && !isControl(); + } + + bool isControl() const + { + return isOnlyControlModifier(m_modifiers); + } + + bool isControl(int c) const + { + return isControl() + && (m_xkey == c || m_xkey + 32 == c || m_xkey + 64 == c || m_xkey + 96 == c); + } + + bool isShift() const + { + return m_modifiers & Qt::ShiftModifier; + } + + bool isShift(int c) const + { + return isShift() && m_xkey == c; + } + + bool operator<(const Input &a) const + { + if (m_key != a.m_key) + return m_key < a.m_key; + // Text for some mapped key cannot be determined (e.g. ) so if text is not set for + // one of compared keys ignore it. + if (!m_text.isEmpty() && !a.m_text.isEmpty() && m_text != _(" ")) + return m_text < a.m_text; + return m_modifiers < a.m_modifiers; + } + + bool operator==(const Input &a) const + { + return !(*this < a || a < *this); + } + + bool operator!=(const Input &a) const { return !operator==(a); } + + QString text() const { return m_text; } + + QChar asChar() const + { + return (m_text.size() == 1 ? m_text.at(0) : QChar()); + } + + int toInt(bool *ok, int base) const + { + const int uc = asChar().unicode(); + int res; + if ('0' <= uc && uc <= '9') + res = uc -'0'; + else if ('a' <= uc && uc <= 'z') + res = 10 + uc - 'a'; + else if ('A' <= uc && uc <= 'Z') + res = 10 + uc - 'A'; + else + res = base; + *ok = res < base; + return *ok ? res : 0; + } + + int key() const { return m_key; } + + Qt::KeyboardModifiers modifiers() const { return m_modifiers; } + + // Return raw character for macro recording or dot command. + QChar raw() const + { + if (m_key == Key_Tab) + return QLatin1Char('\t'); + if (m_key == Key_Return) + return QLatin1Char('\n'); + if (m_key == Key_Escape) + return QChar(27); + return m_xkey; + } + + QString toString() const + { + QString key = vimKeyNames().key(m_key); + bool namedKey = !key.isEmpty(); + + if (!namedKey) { + if (m_xkey == '<') + key = _(""); + else if (m_xkey == '>') + key = _(""); + else + key = QChar(m_xkey); + } + + bool shift = isShift(); + bool ctrl = isControl(); + if (shift) + key.prepend(_("S-")); + if (ctrl) + key.prepend(_("C-")); + + if (namedKey || shift || ctrl) { + key.prepend(QLatin1Char('<')); + key.append(QLatin1Char('>')); + } + + return key; + } + + QDebug dump(QDebug ts) const + { + return ts << m_key << '-' << m_modifiers << '-' + << quoteUnprintable(m_text); + } +private: + int m_key; + int m_xkey; + Qt::KeyboardModifiers m_modifiers; + QString m_text; +}; + +// mapping to (do nothing) +static const Input Nop(-1, Qt::KeyboardModifiers(-1), QString()); + +QDebug operator<<(QDebug ts, const Input &input) { return input.dump(ts); } + +class Inputs : public QVector +{ +public: + Inputs() : m_noremap(true), m_silent(false) {} + + explicit Inputs(const QString &str, bool noremap = true, bool silent = false) + : m_noremap(noremap), m_silent(silent) + { + parseFrom(str); + squeeze(); + } + + bool noremap() const { return m_noremap; } + + bool silent() const { return m_silent; } + +private: + void parseFrom(const QString &str); + + bool m_noremap; + bool m_silent; +}; + +static Input parseVimKeyName(const QString &keyName) +{ + if (keyName.length() == 1) + return Input(keyName.at(0)); + + const QStringList keys = keyName.split(QLatin1Char('-')); + const int len = keys.length(); + + if (len == 1 && keys.at(0).toUpper() == _("NOP")) + return Nop; + + Qt::KeyboardModifiers mods = NoModifier; + for (int i = 0; i < len - 1; ++i) { + const QString &key = keys[i].toUpper(); + if (key == _("S")) + mods |= Qt::ShiftModifier; + else if (key == _("C")) + mods |= HostOsInfo::controlModifier(); + else + return Input(); + } + + if (!keys.isEmpty()) { + const QString key = keys.last(); + if (key.length() == 1) { + // simple character + QChar c = key.at(0).toUpper(); + return Input(c.unicode(), mods); + } + + // find key name + QMap::ConstIterator it = vimKeyNames().constFind(key.toUpper()); + if (it != vimKeyNames().end()) + return Input(*it, mods); + } + + return Input(); +} + +void Inputs::parseFrom(const QString &str) +{ + const int n = str.size(); + for (int i = 0; i < n; ++i) { + ushort c = str.at(i).unicode(); + if (c == '<') { + int j = str.indexOf(QLatin1Char('>'), i); + Input input; + if (j != -1) { + const QString key = str.mid(i+1, j - i - 1); + if (!key.contains(QLatin1Char('<'))) + input = parseVimKeyName(key); + } + if (input.isValid()) { + append(input); + i = j; + } else { + append(Input(c)); + } + } else { + append(Input(c)); + } + } +} + +class History +{ +public: + History() : m_items(QString()), m_index(0) {} + void append(const QString &item); + const QString &move(const QStringRef &prefix, int skip); + const QString ¤t() const { return m_items[m_index]; } + const QStringList &items() const { return m_items; } + void restart() { m_index = m_items.size() - 1; } + +private: + // Last item is always empty or current search prefix. + QStringList m_items; + int m_index; +}; + +void History::append(const QString &item) +{ + if (item.isEmpty()) + return; + m_items.pop_back(); + m_items.removeAll(item); + m_items << item << QString(); + restart(); +} + +const QString &History::move(const QStringRef &prefix, int skip) +{ + if (!current().startsWith(prefix)) + restart(); + + if (m_items.last() != prefix) + m_items[m_items.size() - 1] = prefix.toString(); + + int i = m_index + skip; + if (!prefix.isEmpty()) + for (; i >= 0 && i < m_items.size() && !m_items[i].startsWith(prefix); i += skip) + ; + if (i >= 0 && i < m_items.size()) + m_index = i; + + return current(); +} + +// Command line buffer with prompt (i.e. :, / or ? characters), text contents and cursor position. +class CommandBuffer +{ +public: + CommandBuffer() : m_pos(0), m_anchor(0), m_userPos(0), m_historyAutoSave(true) {} + + void setPrompt(const QChar &prompt) { m_prompt = prompt; } + void setContents(const QString &s) { m_buffer = s; m_anchor = m_pos = s.size(); } + + void setContents(const QString &s, int pos, int anchor = -1) + { + m_buffer = s; m_pos = m_userPos = pos; m_anchor = anchor >= 0 ? anchor : pos; + } + + QStringRef userContents() const { return m_buffer.leftRef(m_userPos); } + const QChar &prompt() const { return m_prompt; } + const QString &contents() const { return m_buffer; } + bool isEmpty() const { return m_buffer.isEmpty(); } + int cursorPos() const { return m_pos; } + int anchorPos() const { return m_anchor; } + bool hasSelection() const { return m_pos != m_anchor; } + + void insertChar(QChar c) { m_buffer.insert(m_pos++, c); m_anchor = m_userPos = m_pos; } + void insertText(const QString &s) + { + m_buffer.insert(m_pos, s); m_anchor = m_userPos = m_pos = m_pos + s.size(); + } + void deleteChar() { if (m_pos) m_buffer.remove(--m_pos, 1); m_anchor = m_userPos = m_pos; } + + void moveLeft() { if (m_pos) m_userPos = --m_pos; } + void moveRight() { if (m_pos < m_buffer.size()) m_userPos = ++m_pos; } + void moveStart() { m_userPos = m_pos = 0; } + void moveEnd() { m_userPos = m_pos = m_buffer.size(); } + + void setHistoryAutoSave(bool autoSave) { m_historyAutoSave = autoSave; } + void historyDown() { setContents(m_history.move(userContents(), 1)); } + void historyUp() { setContents(m_history.move(userContents(), -1)); } + const QStringList &historyItems() const { return m_history.items(); } + void historyPush(const QString &item = QString()) + { + m_history.append(item.isNull() ? contents() : item); + } + + void clear() + { + if (m_historyAutoSave) + historyPush(); + m_buffer.clear(); + m_anchor = m_userPos = m_pos = 0; + } + + QString display() const + { + QString msg(m_prompt); + for (int i = 0; i != m_buffer.size(); ++i) { + const QChar c = m_buffer.at(i); + if (c.unicode() < 32) { + msg += QLatin1Char('^'); + msg += QChar(c.unicode() + 64); + } else { + msg += c; + } + } + return msg; + } + + void deleteSelected() + { + if (m_pos < m_anchor) { + m_buffer.remove(m_pos, m_anchor - m_pos); + m_anchor = m_pos; + } else { + m_buffer.remove(m_anchor, m_pos - m_anchor); + m_pos = m_anchor; + } + } + + bool handleInput(const Input &input) + { + if (input.isShift(Key_Left)) { + moveLeft(); + } else if (input.isShift(Key_Right)) { + moveRight(); + } else if (input.isShift(Key_Home)) { + moveStart(); + } else if (input.isShift(Key_End)) { + moveEnd(); + } else if (input.isKey(Key_Left)) { + moveLeft(); + m_anchor = m_pos; + } else if (input.isKey(Key_Right)) { + moveRight(); + m_anchor = m_pos; + } else if (input.isKey(Key_Home)) { + moveStart(); + m_anchor = m_pos; + } else if (input.isKey(Key_End)) { + moveEnd(); + m_anchor = m_pos; + } else if (input.isKey(Key_Up) || input.isKey(Key_PageUp)) { + historyUp(); + } else if (input.isKey(Key_Down) || input.isKey(Key_PageDown)) { + historyDown(); + } else if (input.isKey(Key_Delete)) { + if (hasSelection()) { + deleteSelected(); + } else { + if (m_pos < m_buffer.size()) + m_buffer.remove(m_pos, 1); + else + deleteChar(); + } + } else if (!input.text().isEmpty()) { + if (hasSelection()) + deleteSelected(); + insertText(input.text()); + } else { + return false; + } + return true; + } + +private: + QString m_buffer; + QChar m_prompt; + History m_history; + int m_pos; + int m_anchor; + int m_userPos; // last position of inserted text (for retrieving history items) + bool m_historyAutoSave; // store items to history on clear()? +}; + +// Mappings for a specific mode (trie structure) +class ModeMapping : public QMap +{ +public: + const Inputs &value() const { return m_value; } + void setValue(const Inputs &value) { m_value = value; } +private: + Inputs m_value; +}; + +// Mappings for all modes +typedef QHash Mappings; + +// Iterator for mappings +class MappingsIterator : public QVector +{ +public: + MappingsIterator(Mappings *mappings, char mode = -1, const Inputs &inputs = Inputs()) + : m_parent(mappings) + , m_lastValid(-1) + , m_mode(0) + { + reset(mode); + walk(inputs); + } + + // Reset iterator state. Keep previous mode if 0. + void reset(char mode = 0) + { + clear(); + m_lastValid = -1; + m_currentInputs.clear(); + if (mode != 0) { + m_mode = mode; + if (mode != -1) + m_modeMapping = m_parent->find(mode); + } + } + + bool isValid() const { return !empty(); } + + // Return true if mapping can be extended. + bool canExtend() const { return isValid() && !last()->empty(); } + + // Return true if this mapping can be used. + bool isComplete() const { return m_lastValid != -1; } + + // Return size of current map. + int mapLength() const { return m_lastValid + 1; } + + bool walk(const Input &input) + { + m_currentInputs.append(input); + + if (m_modeMapping == m_parent->end()) + return false; + + ModeMapping::Iterator it; + if (isValid()) { + it = last()->find(input); + if (it == last()->end()) + return false; + } else { + it = m_modeMapping->find(input); + if (it == m_modeMapping->end()) + return false; + } + + if (!it->value().isEmpty()) + m_lastValid = size(); + append(it); + + return true; + } + + bool walk(const Inputs &inputs) + { + foreach (const Input &input, inputs) { + if (!walk(input)) + return false; + } + return true; + } + + // Return current mapped value. Iterator must be valid. + const Inputs &inputs() const + { + return at(m_lastValid)->value(); + } + + void remove() + { + if (isValid()) { + if (canExtend()) { + last()->setValue(Inputs()); + } else { + if (size() > 1) { + while (last()->empty()) { + at(size() - 2)->erase(last()); + pop_back(); + if (size() == 1 || !last()->value().isEmpty()) + break; + } + if (last()->empty() && last()->value().isEmpty()) + m_modeMapping->erase(last()); + } else if (last()->empty() && !last()->value().isEmpty()) { + m_modeMapping->erase(last()); + } + } + } + } + + void setInputs(const Inputs &key, const Inputs &inputs, bool unique = false) + { + ModeMapping *current = &(*m_parent)[m_mode]; + foreach (const Input &input, key) + current = &(*current)[input]; + if (!unique || current->value().isEmpty()) + current->setValue(inputs); + } + + const Inputs ¤tInputs() const { return m_currentInputs; } + +private: + Mappings *m_parent; + Mappings::Iterator m_modeMapping; + int m_lastValid; + char m_mode; + Inputs m_currentInputs; +}; + +// state of current mapping +struct MappingState { + MappingState() + : noremap(false), silent(false), editBlock(false) {} + MappingState(bool noremap, bool silent, bool editBlock) + : noremap(noremap), silent(silent), editBlock(editBlock) {} + bool noremap; + bool silent; + bool editBlock; +}; + +class FakeVimHandler::Private : public QObject +{ + Q_OBJECT + +public: + Private(FakeVimHandler *parent, QWidget *widget); + + EventResult handleEvent(QKeyEvent *ev); + bool wantsOverride(QKeyEvent *ev); + bool parseExCommmand(QString *line, ExCommand *cmd); + bool parseLineRange(QString *line, ExCommand *cmd); + int parseLineAddress(QString *cmd); + void parseRangeCount(const QString &line, Range *range) const; + void handleCommand(const QString &cmd); // Sets m_tc + handleExCommand + void handleExCommand(const QString &cmd); + + void installEventFilter(); + void removeEventFilter(); + void passShortcuts(bool enable); + void setupWidget(); + void restoreWidget(int tabSize); + + friend class FakeVimHandler; + + void init(); + void focus(); + + // Call before any FakeVim processing (import cursor position from editor) + void enterFakeVim(); + // Call after any FakeVim processing + // (if needUpdate is true, export cursor position to editor and scroll) + void leaveFakeVim(bool needUpdate = true); + + EventResult handleKey(const Input &input); + EventResult handleDefaultKey(const Input &input); + EventResult handleCurrentMapAsDefault(); + void prependInputs(const QVector &inputs); // Handle inputs. + void prependMapping(const Inputs &inputs); // Handle inputs as mapping. + bool expandCompleteMapping(); // Return false if current mapping is not complete. + bool extendMapping(const Input &input); // Return false if no suitable mappig found. + void endMapping(); + bool canHandleMapping(); + void clearPendingInput(); + void waitForMapping(); + EventResult stopWaitForMapping(bool hasInput); + EventResult handleInsertOrReplaceMode(const Input &); + void handleInsertMode(const Input &); + void handleReplaceMode(const Input &); + void finishInsertMode(); + + EventResult handleCommandMode(const Input &); + + // return true only if input in current mode and sub-mode was correctly handled + bool handleEscape(); + bool handleNoSubMode(const Input &); + bool handleChangeDeleteSubModes(const Input &); + bool handleReplaceSubMode(const Input &); + bool handleFilterSubMode(const Input &); + bool handleRegisterSubMode(const Input &); + bool handleShiftSubMode(const Input &); + bool handleChangeCaseSubMode(const Input &); + bool handleWindowSubMode(const Input &); + bool handleYankSubMode(const Input &); + bool handleZSubMode(const Input &); + bool handleCapitalZSubMode(const Input &); + bool handleMacroRecordSubMode(const Input &); + bool handleMacroExecuteSubMode(const Input &); + + bool handleCount(const Input &); // Handle count for commands (return false if input isn't count). + bool handleMovement(const Input &); + + EventResult handleExMode(const Input &); + EventResult handleSearchSubSubMode(const Input &); + bool handleCommandSubSubMode(const Input &); + void fixSelection(); // Fix selection according to current range, move and command modes. + bool finishSearch(); + void finishMovement(const QString &dotCommandMovement = QString()); + void resetCommandMode(); + void clearCommandMode(); + QTextCursor search(const SearchData &sd, int startPos, int count, bool showMessages); + void search(const SearchData &sd, bool showMessages = true); + bool searchNext(bool forward = true); + void searchBalanced(bool forward, QChar needle, QChar other); + void highlightMatches(const QString &needle); + void stopIncrementalFind(); + void updateFind(bool isComplete); + + void resetCount(); + bool isInputCount(const Input &) const; // Return true if input can be used as count for commands. + int mvCount() const { return qMax(1, g.mvcount); } + int opCount() const { return qMax(1, g.opcount); } + int count() const { return mvCount() * opCount(); } + QTextBlock block() const { return m_cursor.block(); } + int leftDist() const { return position() - block().position(); } + int rightDist() const { return block().length() - leftDist() - (isVisualCharMode() ? 0 : 1); } + bool atBlockStart() const { return m_cursor.atBlockStart(); } + bool atBlockEnd() const { return m_cursor.atBlockEnd(); } + bool atEndOfLine() const { return atBlockEnd() && block().length() > 1; } + bool atDocumentEnd() const { return position() >= lastPositionInDocument(true); } + bool atDocumentStart() const { return m_cursor.atStart(); } + + bool atEmptyLine(const QTextCursor &tc = QTextCursor()) const; + bool atBoundary(bool end, bool simple, bool onlyWords = false, + const QTextCursor &tc = QTextCursor()) const; + bool atWordBoundary(bool end, bool simple, const QTextCursor &tc = QTextCursor()) const; + bool atWordStart(bool simple, const QTextCursor &tc = QTextCursor()) const; + bool atWordEnd(bool simple, const QTextCursor &tc = QTextCursor()) const; + bool isFirstNonBlankOnLine(int pos); + + int lastPositionInDocument(bool ignoreMode = false) const; // Returns last valid position in doc. + int firstPositionInLine(int line, bool onlyVisibleLines = true) const; // 1 based line, 0 based pos + int lastPositionInLine(int line, bool onlyVisibleLines = true) const; // 1 based line, 0 based pos + int lineForPosition(int pos) const; // 1 based line, 0 based pos + QString lineContents(int line) const; // 1 based line + QString textAt(int from, int to) const; + void setLineContents(int line, const QString &contents); // 1 based line + int blockBoundary(const QString &left, const QString &right, + bool end, int count) const; // end or start position of current code block + int lineNumber(const QTextBlock &block) const; + + QTextBlock nextLine(const QTextBlock &block) const; // following line (respects wrapped parts) + QTextBlock previousLine(const QTextBlock &block) const; // previous line (respects wrapped parts) + + int linesOnScreen() const; + int linesInDocument() const; + + // The following use all zero-based counting. + int cursorLineOnScreen() const; + int cursorLine() const; + int cursorBlockNumber() const; // "." address + int physicalCursorColumn() const; // as stored in the data + int logicalCursorColumn() const; // as visible on screen + int physicalToLogicalColumn(int physical, const QString &text) const; + int logicalToPhysicalColumn(int logical, const QString &text) const; + int windowScrollOffset() const; // return scrolloffset but max half the current window height + Column cursorColumn() const; // as visible on screen + void updateFirstVisibleLine(); + int firstVisibleLine() const; + int lastVisibleLine() const; + int lineOnTop(int count = 1) const; // [count]-th line from top reachable without scrolling + int lineOnBottom(int count = 1) const; // [count]-th line from bottom reachable without scrolling + void scrollToLine(int line); + void scrollUp(int count); + void scrollDown(int count) { scrollUp(-count); } + void updateScrollOffset(); + void alignViewportToCursor(Qt::AlignmentFlag align, int line = -1, + bool moveToNonBlank = false); + + int lineToBlockNumber(int line) const; + + void setCursorPosition(const CursorPosition &p); + void setCursorPosition(QTextCursor *tc, const CursorPosition &p); + + // Helper functions for indenting/ + bool isElectricCharacter(QChar c) const; + void indentSelectedText(QChar lastTyped = QChar()); + void indentText(const Range &range, QChar lastTyped = QChar()); + void shiftRegionLeft(int repeat = 1); + void shiftRegionRight(int repeat = 1); + + void moveToFirstNonBlankOnLine(); + void moveToFirstNonBlankOnLine(QTextCursor *tc); + void moveToFirstNonBlankOnLineVisually(); + void moveToNonBlankOnLine(QTextCursor *tc); + void moveToTargetColumn(); + void setTargetColumn(); + void moveToMatchingParanthesis(); + void moveToBoundary(bool simple, bool forward = true); + void moveToNextBoundary(bool end, int count, bool simple, bool forward); + void moveToNextBoundaryStart(int count, bool simple, bool forward = true); + void moveToNextBoundaryEnd(int count, bool simple, bool forward = true); + void moveToBoundaryStart(int count, bool simple, bool forward = true); + void moveToBoundaryEnd(int count, bool simple, bool forward = true); + void moveToNextWord(bool end, int count, bool simple, bool forward, bool emptyLines); + void moveToNextWordStart(int count, bool simple, bool forward = true, bool emptyLines = true); + void moveToNextWordEnd(int count, bool simple, bool forward = true, bool emptyLines = true); + void moveToWordStart(int count, bool simple, bool forward = true, bool emptyLines = true); + void moveToWordEnd(int count, bool simple, bool forward = true, bool emptyLines = true); + + // Convenience wrappers to reduce line noise. + void moveToStartOfLine(); + void moveToStartOfLineVisually(); + void moveToEndOfLine(); + void moveToEndOfLineVisually(); + void moveToEndOfLineVisually(QTextCursor *tc); + void moveBehindEndOfLine(); + void moveUp(int n = 1) { moveDown(-n); } + void moveDown(int n = 1); + void moveUpVisually(int n = 1) { moveDownVisually(-n); } + void moveDownVisually(int n = 1); + void movePageDown(int count = 1); + void movePageUp(int count = 1) { movePageDown(-count); } + void dump(const char *msg) const { + qDebug() << msg << "POS: " << anchor() << position() + << "EXT: " << m_oldExternalAnchor << m_oldExternalPosition + << "INT: " << m_oldInternalAnchor << m_oldInternalPosition + << "VISUAL: " << g.visualMode; + } + void moveRight(int n = 1) { + //dump("RIGHT 1"); + if (isVisualCharMode()) { + const QTextBlock currentBlock = block(); + const int max = currentBlock.position() + currentBlock.length() - 1; + const int pos = position() + n; + setPosition(qMin(pos, max)); + } else { + m_cursor.movePosition(Right, KeepAnchor, n); + } + if (atEndOfLine()) + emit q->fold(1, false); + //dump("RIGHT 2"); + } + void moveLeft(int n = 1) { + m_cursor.movePosition(Left, KeepAnchor, n); + } + void setAnchor() { + m_cursor.setPosition(position(), MoveAnchor); + } + void setAnchor(int position) { + m_cursor.setPosition(position, KeepAnchor); + } + void setPosition(int position) { + m_cursor.setPosition(position, KeepAnchor); + } + void setAnchorAndPosition(int anchor, int position) { + m_cursor.setPosition(anchor, MoveAnchor); + m_cursor.setPosition(position, KeepAnchor); + } + // Set cursor in text editor widget. + void commitCursor() { + if (isVisualBlockMode()) { + emit q->requestSetBlockSelection(m_cursor); + } else { + emit q->requestDisableBlockSelection(); + if (editor()) + EDITOR(setTextCursor(m_cursor)); + } + } + // Restore cursor from editor widget. + void pullCursor() { + if (isVisualBlockMode()) + q->requestBlockSelection(&m_cursor); + else if (editor()) + m_cursor = EDITOR(textCursor()); + } + + // Values to save when starting FakeVim processing. + int m_firstVisibleLine; + QTextCursor m_cursor; + + bool moveToPreviousParagraph(int count) { return moveToNextParagraph(-count); } + bool moveToNextParagraph(int count); + + bool handleFfTt(const QString &key, bool repeats = false); + + void enterVisualInsertMode(QChar command); + void enterReplaceMode(); + void enterInsertMode(); + void enterInsertOrReplaceMode(Mode mode); + void enterCommandMode(Mode returnToMode = CommandMode); + void enterExMode(const QString &contents = QString()); + void showMessage(MessageLevel level, const QString &msg); + void clearMessage() { showMessage(MessageInfo, QString()); } + void notImplementedYet(); + void updateMiniBuffer(); + void updateSelection(); + void updateHighlights(); + void updateCursorShape(); + QWidget *editor() const; + QTextDocument *document() const { return EDITOR(document()); } + QChar characterAtCursor() const + { return document()->characterAt(position()); } + + void joinPreviousEditBlock(); + void beginEditBlock(bool largeEditBlock = false); + void beginLargeEditBlock() { beginEditBlock(true); } + void endEditBlock(); + void breakEditBlock() { m_buffer->breakEditBlock = true; } + + Q_SLOT void onContentsChanged(int position, int charsRemoved, int charsAdded); + Q_SLOT void onUndoCommandAdded(); + + bool isInsertMode() const { return g.mode == InsertMode || g.mode == ReplaceMode; } + // Waiting for movement operator. + bool isOperatorPending() const { + return g.submode == ChangeSubMode + || g.submode == DeleteSubMode + || g.submode == FilterSubMode + || g.submode == IndentSubMode + || g.submode == ShiftLeftSubMode + || g.submode == ShiftRightSubMode + || g.submode == InvertCaseSubMode + || g.submode == DownCaseSubMode + || g.submode == UpCaseSubMode + || g.submode == YankSubMode; } + + bool isVisualMode() const { return g.visualMode != NoVisualMode; } + bool isNoVisualMode() const { return g.visualMode == NoVisualMode; } + bool isVisualCharMode() const { return g.visualMode == VisualCharMode; } + bool isVisualLineMode() const { return g.visualMode == VisualLineMode; } + bool isVisualBlockMode() const { return g.visualMode == VisualBlockMode; } + char currentModeCode() const; + void updateEditor(); + + void selectTextObject(bool simple, bool inner); + void selectWordTextObject(bool inner); + void selectWORDTextObject(bool inner); + void selectSentenceTextObject(bool inner); + void selectParagraphTextObject(bool inner); + bool changeNumberTextObject(int count); + // return true only if cursor is in a block delimited with correct characters + bool selectBlockTextObject(bool inner, char left, char right); + bool selectQuotedStringTextObject(bool inner, const QString "e); + + Q_SLOT void importSelection(); + void exportSelection(); + void commitInsertState(); + void invalidateInsertState(); + bool isInsertStateValid() const; + void clearLastInsertion(); + void ensureCursorVisible(); + void insertInInsertMode(const QString &text); + + // Macro recording + bool startRecording(const Input &input); + void record(const Input &input); + void stopRecording(); + bool executeRegister(int register); + +public: + QTextEdit *m_textedit; + QPlainTextEdit *m_plaintextedit; + bool m_wasReadOnly; // saves read-only state of document + + bool m_inFakeVim; // true if currently processing a key press or a command + + FakeVimHandler *q; + int m_oldExternalPosition; // copy from last event to check for external changes + int m_oldExternalAnchor; + int m_oldInternalPosition; // copy from last event to check for external changes + int m_oldInternalAnchor; + int m_register; + BlockInsertMode m_visualBlockInsert; + + bool m_fakeEnd; + bool m_anchorPastEnd; + bool m_positionPastEnd; // '$' & 'l' in visual mode can move past eol + + QString m_currentFileName; + + int m_findStartPosition; + + int anchor() const { return m_cursor.anchor(); } + int position() const { return m_cursor.position(); } + + struct TransformationData + { + TransformationData(const QString &s, const QVariant &d) + : from(s), extraData(d) {} + QString from; + QString to; + QVariant extraData; + }; + typedef void (Private::*Transformation)(TransformationData *td); + void transformText(const Range &range, Transformation transformation, + const QVariant &extraData = QVariant()); + + void insertText(QTextCursor &tc, const QString &text); + void insertText(const Register ®); + void removeText(const Range &range); + void removeTransform(TransformationData *td); + + void invertCase(const Range &range); + void invertCaseTransform(TransformationData *td); + + void upCase(const Range &range); + void upCaseTransform(TransformationData *td); + + void downCase(const Range &range); + void downCaseTransform(TransformationData *td); + + void replaceText(const Range &range, const QString &str); + void replaceByStringTransform(TransformationData *td); + void replaceByCharTransform(TransformationData *td); + + QString selectText(const Range &range) const; + void setCurrentRange(const Range &range); + Range currentRange() const { return Range(position(), anchor(), g.rangemode); } + + void yankText(const Range &range, int toregister); + + void pasteText(bool afterCursor); + + void joinLines(int count, bool preserveSpace = false); + + void insertNewLine(); + + bool handleInsertInEditor(const Input &input); + bool passEventToEditor(QEvent &event); // Pass event to editor widget without filtering. Returns true if event was processed. + + // undo handling + int revision() const { return document()->availableUndoSteps(); } + void undoRedo(bool undo); + void undo(); + void redo(); + void pushUndoState(bool overwrite = true); + + // extra data for '.' + void replay(const QString &text, int repeat = 1); + void setDotCommand(const QString &cmd) { g.dotCommand = cmd; } + void setDotCommand(const QString &cmd, int n) { g.dotCommand = cmd.arg(n); } + QString visualDotCommand() const; + + // visual modes + void toggleVisualMode(VisualMode visualMode); + void leaveVisualMode(); + + // marks + Mark mark(QChar code) const; + void setMark(QChar code, CursorPosition position); + // jump to valid mark return true if mark is valid and local + bool jumpToMark(QChar mark, bool backTickMode); + // update marks on undo/redo + void updateMarks(const Marks &newMarks); + CursorPosition markLessPosition() const { return mark(QLatin1Char('<')).position(document()); } + CursorPosition markGreaterPosition() const { return mark(QLatin1Char('>')).position(document()); } + + // vi style configuration + QVariant config(int code) const { return theFakeVimSetting(code)->value(); } + bool hasConfig(int code) const { return config(code).toBool(); } + bool hasConfig(int code, const char *value) const // FIXME + { return config(code).toString().contains(_(value)); } + + int m_targetColumn; // -1 if past end of line + int m_visualTargetColumn; // 'l' can move past eol in visual mode only + int m_targetColumnWrapped; // column in current part of wrapped line + + // auto-indent + QString tabExpand(int len) const; + Column indentation(const QString &line) const; + void insertAutomaticIndentation(bool goingDown, bool forceAutoIndent = false); + // number of autoindented characters + void handleStartOfLine(); + + // register handling + QString registerContents(int reg) const; + void setRegister(int reg, const QString &contents, RangeMode mode); + RangeMode registerRangeMode(int reg) const; + void getRegisterType(int reg, bool *isClipboard, bool *isSelection) const; + + void recordJump(int position = -1); + void jump(int distance); + + QList m_extraSelections; + QTextCursor m_searchCursor; + int m_searchStartPosition; + int m_searchFromScreenLine; + QString m_highlighted; // currently highlighted text + + bool handleExCommandHelper(ExCommand &cmd); // Returns success. + bool handleExPluginCommand(const ExCommand &cmd); // Handled by plugin? + bool handleExBangCommand(const ExCommand &cmd); + bool handleExYankDeleteCommand(const ExCommand &cmd); + bool handleExChangeCommand(const ExCommand &cmd); + bool handleExMoveCommand(const ExCommand &cmd); + bool handleExJoinCommand(const ExCommand &cmd); + bool handleExGotoCommand(const ExCommand &cmd); + bool handleExHistoryCommand(const ExCommand &cmd); + bool handleExRegisterCommand(const ExCommand &cmd); + bool handleExMapCommand(const ExCommand &cmd); + bool handleExNohlsearchCommand(const ExCommand &cmd); + bool handleExNormalCommand(const ExCommand &cmd); + bool handleExReadCommand(const ExCommand &cmd); + bool handleExUndoRedoCommand(const ExCommand &cmd); + bool handleExSetCommand(const ExCommand &cmd); + bool handleExShiftCommand(const ExCommand &cmd); + bool handleExSourceCommand(const ExCommand &cmd); + bool handleExSubstituteCommand(const ExCommand &cmd); + bool handleExWriteCommand(const ExCommand &cmd); + bool handleExEchoCommand(const ExCommand &cmd); + + void timerEvent(QTimerEvent *ev) override; + + void setupCharClass(); + int charClass(QChar c, bool simple) const; + signed char m_charClass[256]; + + int m_ctrlVAccumulator; + int m_ctrlVLength; + int m_ctrlVBase; + + void miniBufferTextEdited(const QString &text, int cursorPos, int anchorPos); + + // Data shared among editors with same document. + struct BufferData + { + BufferData() + : lastRevision(0) + , editBlockLevel(0) + , breakEditBlock(false) + , lastVisualMode(NoVisualMode) + , lastVisualModeInverted(false) + {} + + QStack undo; + QStack redo; + State undoState; + int lastRevision; + + int editBlockLevel; // current level of edit blocks + bool breakEditBlock; // if true, joinPreviousEditBlock() starts new edit block + + QStack jumpListUndo; + QStack jumpListRedo; + CursorPosition lastChangePosition; + + VisualMode lastVisualMode; + bool lastVisualModeInverted; + + Marks marks; + + // Insert state to get last inserted text. + struct InsertState { + int pos1; + int pos2; + int backspaces; + int deletes; + QSet spaces; + bool insertingSpaces; + QString textBeforeCursor; + bool newLineBefore; + bool newLineAfter; + } insertState; + + QString lastInsertion; + }; + + typedef QSharedPointer BufferDataPtr; + void pullOrCreateBufferData(); + BufferDataPtr m_buffer; + + // Data shared among all editors. + static struct GlobalData + { + GlobalData() + : passing(false) + , mode(CommandMode) + , submode(NoSubMode) + , subsubmode(NoSubSubMode) + , visualMode(NoVisualMode) + , mvcount(0) + , opcount(0) + , movetype(MoveInclusive) + , rangemode(RangeCharMode) + , gflag(false) + , mappings() + , currentMap(&mappings) + , inputTimer(-1) + , mapDepth(0) + , currentMessageLevel(MessageInfo) + , lastSearchForward(false) + , highlightsCleared(false) + , findPending(false) + , returnToMode(CommandMode) + , currentRegister(0) + , lastExecutedRegister(0) + { + commandBuffer.setPrompt(QLatin1Char(':')); + } + + // Current state. + bool passing; // let the core see the next event + Mode mode; + SubMode submode; + SubSubMode subsubmode; + Input subsubdata; + VisualMode visualMode; + + // [count] for current command, 0 if no [count] available + int mvcount; + int opcount; + + MoveType movetype; + RangeMode rangemode; + bool gflag; // whether current command started with 'g' + + // Extra data for ';'. + Input semicolonType; // 'f', 'F', 't', 'T' + QString semicolonKey; + + // Repetition. + QString dotCommand; + + QHash registers; + + // All mappings. + Mappings mappings; + + // Input. + QList pendingInput; + MappingsIterator currentMap; + int inputTimer; + QStack mapStates; + int mapDepth; + + // Command line buffers. + CommandBuffer commandBuffer; + CommandBuffer searchBuffer; + + // Current mini buffer message. + QString currentMessage; + MessageLevel currentMessageLevel; + QString currentCommand; + + // Search state. + QString lastSearch; // last search expression as entered by user + QString lastNeedle; // last search expression translated with vimPatternToQtPattern() + bool lastSearchForward; // last search command was '/' or '*' + bool highlightsCleared; // ':nohlsearch' command is active until next search + bool findPending; // currently searching using external tool (until editor is focused again) + + // Last substitution command. + QString lastSubstituteFlags; + QString lastSubstitutePattern; + QString lastSubstituteReplacement; + + // Global marks. + Marks marks; + + // Return to insert/replace mode after single command (). + Mode returnToMode; + + // Currently recorded macro (not recording if null string). + QString recording; + int currentRegister; + int lastExecutedRegister; + } g; +}; + +FakeVimHandler::Private::GlobalData FakeVimHandler::Private::g; + +FakeVimHandler::Private::Private(FakeVimHandler *parent, QWidget *widget) +{ + q = parent; + m_textedit = qobject_cast(widget); + m_plaintextedit = qobject_cast(widget); + + init(); + + if (editor()) { + connect(EDITOR(document()), SIGNAL(contentsChange(int,int,int)), + SLOT(onContentsChanged(int,int,int))); + connect(EDITOR(document()), SIGNAL(undoCommandAdded()), SLOT(onUndoCommandAdded())); + m_buffer->lastRevision = revision(); + } +} + +void FakeVimHandler::Private::init() +{ + m_inFakeVim = false; + m_findStartPosition = -1; + m_visualBlockInsert = NoneBlockInsertMode; + m_fakeEnd = false; + m_positionPastEnd = false; + m_anchorPastEnd = false; + m_register = '"'; + m_targetColumn = 0; + m_visualTargetColumn = 0; + m_targetColumnWrapped = 0; + m_oldInternalAnchor = -1; + m_oldInternalPosition = -1; + m_oldExternalAnchor = -1; + m_oldExternalPosition = -1; + m_searchStartPosition = 0; + m_searchFromScreenLine = 0; + m_firstVisibleLine = 0; + m_ctrlVAccumulator = 0; + m_ctrlVLength = 0; + m_ctrlVBase = 0; + + pullOrCreateBufferData(); + setupCharClass(); +} + +void FakeVimHandler::Private::focus() +{ + enterFakeVim(); + + stopIncrementalFind(); + if (!isInsertMode()) { + if (g.subsubmode == SearchSubSubMode) { + setPosition(m_searchStartPosition); + scrollToLine(m_searchFromScreenLine); + setTargetColumn(); + setAnchor(); + commitCursor(); + } else if (g.submode != NoSubMode || g.mode == ExMode) { + leaveVisualMode(); + setPosition(qMin(position(), anchor())); + setTargetColumn(); + setAnchor(); + commitCursor(); + } + + bool exitCommandLine = (g.subsubmode == SearchSubSubMode || g.mode == ExMode); + resetCommandMode(); + if (exitCommandLine) + updateMiniBuffer(); + } + updateCursorShape(); + if (g.mode != CommandMode) + updateMiniBuffer(); + updateHighlights(); + + leaveFakeVim(false); +} + +void FakeVimHandler::Private::enterFakeVim() +{ + QTC_ASSERT(!m_inFakeVim, qDebug() << "enterFakeVim() shouldn't be called recursively!"; return); + + pullOrCreateBufferData(); + + pullCursor(); + if (m_cursor.isNull()) + m_cursor = QTextCursor(document()); + + m_inFakeVim = true; + + removeEventFilter(); + + updateFirstVisibleLine(); + importSelection(); + + // Position changed externally, e.g. by code completion. + if (position() != m_oldInternalPosition) { + // record external jump to different line + if (m_oldInternalPosition != -1 && lineForPosition(m_oldInternalPosition) != lineForPosition(position())) + recordJump(m_oldInternalPosition); + setTargetColumn(); + if (atEndOfLine() && !isVisualMode() && !isInsertMode()) + moveLeft(); + } + + if (m_fakeEnd) + moveRight(); +} + +void FakeVimHandler::Private::leaveFakeVim(bool needUpdate) +{ + QTC_ASSERT(m_inFakeVim, qDebug() << "enterFakeVim() not called before leaveFakeVim()!"; return); + + // The command might have destroyed the editor. + if (m_textedit || m_plaintextedit) { + // We fake vi-style end-of-line behaviour + m_fakeEnd = atEndOfLine() && g.mode == CommandMode && !isVisualBlockMode() + && !isVisualCharMode(); + + //QTC_ASSERT(g.mode == InsertMode || g.mode == ReplaceMode + // || !atBlockEnd() || block().length() <= 1, + // qDebug() << "Cursor at EOL after key handler"); + if (m_fakeEnd) + moveLeft(); + + if (hasConfig(ConfigShowMarks)) + updateSelection(); + + exportSelection(); + updateCursorShape(); + + if (needUpdate) { + commitCursor(); + + // Move cursor line to middle of screen if it's not visible. + const int line = cursorLine(); + if (line < firstVisibleLine() || line > firstVisibleLine() + linesOnScreen()) + scrollToLine(qMax(0, line - linesOnScreen() / 2)); + else + scrollToLine(firstVisibleLine()); + updateScrollOffset(); + } + + installEventFilter(); + } + + m_inFakeVim = false; +} + +bool FakeVimHandler::Private::wantsOverride(QKeyEvent *ev) +{ + const int key = ev->key(); + const Qt::KeyboardModifiers mods = ev->modifiers(); + KEY_DEBUG("SHORTCUT OVERRIDE" << key << " PASSING: " << g.passing); + + if (key == Key_Escape) { + if (g.subsubmode == SearchSubSubMode) + return true; + // Not sure this feels good. People often hit Esc several times. + if (isNoVisualMode() + && g.mode == CommandMode + && g.submode == NoSubMode + && g.currentCommand.isEmpty() + && g.returnToMode == CommandMode) + return false; + return true; + } + + // We are interested in overriding most Ctrl key combinations. + if (isOnlyControlModifier(mods) + && !config(ConfigPassControlKey).toBool() + && ((key >= Key_A && key <= Key_Z && key != Key_K) + || key == Key_BracketLeft || key == Key_BracketRight)) { + // Ctrl-K is special as it is the Core's default notion of Locator + if (g.passing) { + KEY_DEBUG(" PASSING CTRL KEY"); + // We get called twice on the same key + //g.passing = false; + return false; + } + KEY_DEBUG(" NOT PASSING CTRL KEY"); + //updateMiniBuffer(); + return true; + } + + // Let other shortcuts trigger. + return false; +} + +EventResult FakeVimHandler::Private::handleEvent(QKeyEvent *ev) +{ + const int key = ev->key(); + const Qt::KeyboardModifiers mods = ev->modifiers(); + + if (key == Key_Shift || key == Key_Alt || key == Key_Control + || key == Key_AltGr || key == Key_Meta) + { + KEY_DEBUG("PLAIN MODIFIER"); + return EventUnhandled; + } + + if (g.passing) { + passShortcuts(false); + KEY_DEBUG("PASSING PLAIN KEY..." << ev->key() << ev->text()); + //if (input.is(',')) { // use ',,' to leave, too. + // qDebug() << "FINISHED..."; + // return EventHandled; + //} + g.passing = false; + updateMiniBuffer(); + KEY_DEBUG(" PASS TO CORE"); + return EventPassedToCore; + } + +#ifndef FAKEVIM_STANDALONE + bool inSnippetMode = false; + QMetaObject::invokeMethod(editor(), + "inSnippetMode", Q_ARG(bool *, &inSnippetMode)); + + if (inSnippetMode) + return EventPassedToCore; +#endif + + // Fake "End of line" + //m_tc = m_cursor; + + //bool hasBlock = false; + //emit q->requestHasBlockSelection(&hasBlock); + //qDebug() << "IMPORT BLOCK 2:" << hasBlock; + + //if (0 && hasBlock) { + // (pos > anc) ? --pos : --anc; + + //if ((mods & RealControlModifier) != 0) { + // if (key >= Key_A && key <= Key_Z) + // key = shift(key); // make it lower case + // key = control(key); + //} else if (key >= Key_A && key <= Key_Z && (mods & Qt::ShiftModifier) == 0) { + // key = shift(key); + //} + + //QTC_ASSERT(g.mode == InsertMode || g.mode == ReplaceMode + // || !atBlockEnd() || block().length() <= 1, + // qDebug() << "Cursor at EOL before key handler"); + + enterFakeVim(); + EventResult result = handleKey(Input(key, mods, ev->text())); + leaveFakeVim(result == EventHandled); + + return result; +} + +void FakeVimHandler::Private::installEventFilter() +{ + EDITOR(viewport()->installEventFilter(q)); + EDITOR(installEventFilter(q)); +} + +void FakeVimHandler::Private::removeEventFilter() +{ + EDITOR(viewport()->removeEventFilter(q)); + EDITOR(removeEventFilter(q)); +} + +void FakeVimHandler::Private::setupWidget() +{ + enterFakeVim(); + + resetCommandMode(); + m_wasReadOnly = EDITOR(isReadOnly()); + + updateEditor(); + importSelection(); + updateMiniBuffer(); + updateCursorShape(); + + recordJump(); + setTargetColumn(); + if (atEndOfLine() && !isVisualMode() && !isInsertMode()) + moveLeft(); + + leaveFakeVim(); +} + +void FakeVimHandler::Private::exportSelection() +{ + int pos = position(); + int anc = isVisualMode() ? anchor() : position(); + + m_oldInternalPosition = pos; + m_oldInternalAnchor = anc; + + if (isVisualMode()) { + if (g.visualMode == VisualBlockMode) { + const int col1 = anc - document()->findBlock(anc).position(); + const int col2 = pos - document()->findBlock(pos).position(); + if (col1 > col2) + ++anc; + else if (!atBlockEnd()) + ++pos; + // FIXME: After '$' command (i.e. m_visualTargetColumn == -1), end of selected lines + // should be selected. + setAnchorAndPosition(anc, pos); + commitCursor(); + } else if (g.visualMode == VisualLineMode) { + const int posLine = lineForPosition(pos); + const int ancLine = lineForPosition(anc); + if (anc < pos) { + pos = lastPositionInLine(posLine); + anc = firstPositionInLine(ancLine); + } else { + pos = firstPositionInLine(posLine); + anc = lastPositionInLine(ancLine) + 1; + } + // putting cursor on folded line will unfold the line, so move the cursor a bit + if (!document()->findBlock(pos).isVisible()) + ++pos; + setAnchorAndPosition(anc, pos); + } else if (g.visualMode == VisualCharMode) { + if (anc > pos) + ++anc; + } else { + QTC_CHECK(false); + } + + setAnchorAndPosition(anc, pos); + + setMark(QLatin1Char('<'), markLessPosition()); + setMark(QLatin1Char('>'), markGreaterPosition()); + } else { + if (g.subsubmode == SearchSubSubMode && !m_searchCursor.isNull()) + m_cursor = m_searchCursor; + else + setAnchorAndPosition(pos, pos); + } + m_oldExternalPosition = position(); + m_oldExternalAnchor = anchor(); +} + +void FakeVimHandler::Private::commitInsertState() +{ + if (!isInsertStateValid()) + return; + + QString &lastInsertion = m_buffer->lastInsertion; + BufferData::InsertState &insertState = m_buffer->insertState; + + // Get raw inserted text. + lastInsertion = textAt(insertState.pos1, insertState.pos2); + + // Escape special characters and spaces inserted by user (not by auto-indentation). + for (int i = lastInsertion.size() - 1; i >= 0; --i) { + const int pos = insertState.pos1 + i; + const ushort c = document()->characterAt(pos).unicode(); + if (c == '<') + lastInsertion.replace(i, 1, _("")); + else if ((c == ' ' || c == '\t') && insertState.spaces.contains(pos)) + lastInsertion.replace(i, 1, _(c == ' ' ? "" : "")); + } + + // Remove unnecessary backspaces. + while (insertState.backspaces > 0 && !lastInsertion.isEmpty() && lastInsertion[0].isSpace()) + --insertState.backspaces; + + // backspaces in front of inserted text + lastInsertion.prepend(QString(_("")).repeated(insertState.backspaces)); + // deletes after inserted text + lastInsertion.prepend(QString(_("")).repeated(insertState.deletes)); + + // Remove indentation. + lastInsertion.replace(QRegExp(_("(^|\n)[\\t ]+")), _("\\1")); +} + +void FakeVimHandler::Private::invalidateInsertState() +{ + m_oldInternalPosition = position(); + BufferData::InsertState &insertState = m_buffer->insertState; + insertState.pos1 = -1; + insertState.pos2 = m_oldInternalPosition; + insertState.backspaces = 0; + insertState.deletes = 0; + insertState.spaces.clear(); + insertState.insertingSpaces = false; + insertState.textBeforeCursor = textAt(document()->findBlock(m_oldInternalPosition).position(), + m_oldInternalPosition); + insertState.newLineBefore = false; + insertState.newLineAfter = false; +} + +bool FakeVimHandler::Private::isInsertStateValid() const +{ + return m_buffer->insertState.pos1 != -1; +} + +void FakeVimHandler::Private::clearLastInsertion() +{ + invalidateInsertState(); + m_buffer->lastInsertion.clear(); + m_buffer->insertState.pos1 = m_buffer->insertState.pos2; +} + +void FakeVimHandler::Private::ensureCursorVisible() +{ + int pos = position(); + int anc = isVisualMode() ? anchor() : position(); + + // fix selection so it is outside folded block + int start = qMin(pos, anc); + int end = qMax(pos, anc) + 1; + QTextBlock block = document()->findBlock(start); + QTextBlock block2 = document()->findBlock(end); + if (!block.isVisible() || !block2.isVisible()) { + // FIXME: Moving cursor left/right or unfolding block immediately after block is folded + // should restore cursor position inside block. + // Changing cursor position after folding is not Vim behavior so at least record the jump. + if (block.isValid() && !block.isVisible()) + recordJump(); + + pos = start; + while (block.isValid() && !block.isVisible()) + block = block.previous(); + if (block.isValid()) + pos = block.position() + qMin(m_targetColumn, block.length() - 2); + + if (isVisualMode()) { + anc = end; + while (block2.isValid() && !block2.isVisible()) { + anc = block2.position() + block2.length() - 2; + block2 = block2.next(); + } + } + + setAnchorAndPosition(anc, pos); + } +} + +void FakeVimHandler::Private::importSelection() +{ + if (position() == m_oldExternalPosition + && anchor() == m_oldExternalAnchor) { + // Undo drawing correction. + setAnchorAndPosition(m_oldInternalAnchor, m_oldInternalPosition); + } else { + // Import new selection. + Qt::KeyboardModifiers mods = QApplication::keyboardModifiers(); + if (m_cursor.hasSelection()) { + if (mods & HostOsInfo::controlModifier()) + g.visualMode = VisualBlockMode; + else if (mods & Qt::AltModifier) + g.visualMode = VisualBlockMode; + else if (mods & Qt::ShiftModifier) + g.visualMode = VisualLineMode; + else + g.visualMode = VisualCharMode; + m_buffer->lastVisualMode = g.visualMode; + } else { + g.visualMode = NoVisualMode; + } + } +} + +void FakeVimHandler::Private::updateEditor() +{ + const int charWidth = QFontMetrics(EDITOR(font())).width(QLatin1Char(' ')); + EDITOR(setTabStopWidth(charWidth * config(ConfigTabStop).toInt())); + setupCharClass(); +} + +void FakeVimHandler::Private::restoreWidget(int tabSize) +{ + //clearMessage(); + //updateMiniBuffer(); + //EDITOR(removeEventFilter(q)); + //EDITOR(setReadOnly(m_wasReadOnly)); + const int charWidth = QFontMetrics(EDITOR(font())).width(QLatin1Char(' ')); + EDITOR(setTabStopWidth(charWidth * tabSize)); + g.visualMode = NoVisualMode; + // Force "ordinary" cursor. + EDITOR(setOverwriteMode(false)); + updateSelection(); + updateHighlights(); +} + +EventResult FakeVimHandler::Private::handleKey(const Input &input) +{ + KEY_DEBUG("HANDLE INPUT: " << input << " MODE: " << mode); + + bool hasInput = input.isValid(); + + // Waiting on input to complete mapping? + EventResult r = stopWaitForMapping(hasInput); + + if (hasInput) { + record(input); + g.pendingInput.append(input); + } + + // Process pending input. + // Note: Pending input is global state and can be extended by: + // 1. handling a user input (though handleKey() is not called recursively), + // 2. expanding a user mapping or + // 3. executing a register. + while (!g.pendingInput.isEmpty() && r == EventHandled) { + const Input in = g.pendingInput.takeFirst(); + + // invalid input is used to pop mapping state + if (!in.isValid()) { + endMapping(); + } else { + // Handle user mapping. + if (canHandleMapping()) { + if (extendMapping(in)) { + if (!hasInput || !g.currentMap.canExtend()) + expandCompleteMapping(); + } else if (!expandCompleteMapping()) { + r = handleCurrentMapAsDefault(); + } + } else { + r = handleDefaultKey(in); + } + } + } + + if (g.currentMap.canExtend()) { + waitForMapping(); + return EventHandled; + } + + if (r != EventHandled) + clearPendingInput(); + + return r; +} + +EventResult FakeVimHandler::Private::handleDefaultKey(const Input &input) +{ + if (input == Nop) + return EventHandled; + else if (g.subsubmode == SearchSubSubMode) + return handleSearchSubSubMode(input); + else if (g.mode == CommandMode) + return handleCommandMode(input); + else if (g.mode == InsertMode || g.mode == ReplaceMode) + return handleInsertOrReplaceMode(input); + else if (g.mode == ExMode) + return handleExMode(input); + return EventUnhandled; +} + +EventResult FakeVimHandler::Private::handleCurrentMapAsDefault() +{ + // If mapping has failed take the first input from it and try default command. + const Inputs &inputs = g.currentMap.currentInputs(); + + Input in = inputs.front(); + if (inputs.size() > 1) + prependInputs(inputs.mid(1)); + g.currentMap.reset(); + + return handleDefaultKey(in); +} + +void FakeVimHandler::Private::prependInputs(const QVector &inputs) +{ + for (int i = inputs.size() - 1; i >= 0; --i) + g.pendingInput.prepend(inputs[i]); +} + +void FakeVimHandler::Private::prependMapping(const Inputs &inputs) +{ + // FIXME: Implement Vim option maxmapdepth (default value is 1000). + if (g.mapDepth >= 1000) { + const int i = qMax(0, g.pendingInput.lastIndexOf(Input())); + QList inputs = g.pendingInput.mid(i); + clearPendingInput(); + g.pendingInput.append(inputs); + showMessage(MessageError, tr("Recursive mapping")); + updateMiniBuffer(); + return; + } + + ++g.mapDepth; + g.pendingInput.prepend(Input()); + prependInputs(inputs); + g.commandBuffer.setHistoryAutoSave(false); + + // start new edit block (undo/redo) only if necessary + bool editBlock = m_buffer->editBlockLevel == 0 && !(isInsertMode() && isInsertStateValid()); + if (editBlock) + beginLargeEditBlock(); + g.mapStates << MappingState(inputs.noremap(), inputs.silent(), editBlock); +} + +bool FakeVimHandler::Private::expandCompleteMapping() +{ + if (!g.currentMap.isComplete()) + return false; + + const Inputs &inputs = g.currentMap.inputs(); + int usedInputs = g.currentMap.mapLength(); + prependInputs(g.currentMap.currentInputs().mid(usedInputs)); + prependMapping(inputs); + g.currentMap.reset(); + + return true; +} + +bool FakeVimHandler::Private::extendMapping(const Input &input) +{ + if (!g.currentMap.isValid()) + g.currentMap.reset(currentModeCode()); + return g.currentMap.walk(input); +} + +void FakeVimHandler::Private::endMapping() +{ + if (!g.currentMap.canExtend()) + --g.mapDepth; + if (g.mapStates.isEmpty()) + return; + if (g.mapStates.last().editBlock) + endEditBlock(); + g.mapStates.pop_back(); + if (g.mapStates.isEmpty()) + g.commandBuffer.setHistoryAutoSave(true); + updateMiniBuffer(); +} + +bool FakeVimHandler::Private::canHandleMapping() +{ + // Don't handle user mapping in sub-modes that cannot be followed by movement and in "noremap". + return g.subsubmode == NoSubSubMode + && g.submode != RegisterSubMode + && g.submode != WindowSubMode + && g.submode != ZSubMode + && g.submode != CapitalZSubMode + && g.submode != ReplaceSubMode + && g.submode != MacroRecordSubMode + && g.submode != MacroExecuteSubMode + && (g.mapStates.isEmpty() || !g.mapStates.last().noremap); +} + +void FakeVimHandler::Private::clearPendingInput() +{ + // Clear pending input on interrupt or bad mapping. + g.pendingInput.clear(); + g.mapStates.clear(); + g.mapDepth = 0; + + // Clear all started edit blocks. + while (m_buffer->editBlockLevel > 0) + endEditBlock(); +} + +void FakeVimHandler::Private::waitForMapping() +{ + g.currentCommand.clear(); + foreach (const Input &input, g.currentMap.currentInputs()) + g.currentCommand.append(input.toString()); + updateMiniBuffer(); + + // wait for user to press any key or trigger complete mapping after interval + g.inputTimer = startTimer(1000); +} + +EventResult FakeVimHandler::Private::stopWaitForMapping(bool hasInput) +{ + if (g.inputTimer != -1) { + killTimer(g.inputTimer); + g.inputTimer = -1; + g.currentCommand.clear(); + if (!hasInput && !expandCompleteMapping()) { + // Cannot complete mapping so handle the first input from it as default command. + return handleCurrentMapAsDefault(); + } + } + + return EventHandled; +} + +void FakeVimHandler::Private::timerEvent(QTimerEvent *ev) +{ + if (ev->timerId() == g.inputTimer) { + enterFakeVim(); + EventResult result = handleKey(Input()); + leaveFakeVim(result == EventHandled); + } +} + +void FakeVimHandler::Private::stopIncrementalFind() +{ + if (g.findPending) { + g.findPending = false; + setAnchorAndPosition(m_findStartPosition, m_cursor.selectionStart()); + finishMovement(); + setAnchor(); + } +} + +void FakeVimHandler::Private::updateFind(bool isComplete) +{ + if (!isComplete && !hasConfig(ConfigIncSearch)) + return; + + g.currentMessage.clear(); + + const QString &needle = g.searchBuffer.contents(); + if (isComplete) { + setPosition(m_searchStartPosition); + if (!needle.isEmpty()) + recordJump(); + } + + SearchData sd; + sd.needle = needle; + sd.forward = g.lastSearchForward; + sd.highlightMatches = isComplete; + search(sd, isComplete); +} + +void FakeVimHandler::Private::resetCount() +{ + g.mvcount = 0; + g.opcount = 0; +} + +bool FakeVimHandler::Private::isInputCount(const Input &input) const +{ + return input.isDigit() && (!input.is('0') || g.mvcount > 0); +} + +bool FakeVimHandler::Private::atEmptyLine(const QTextCursor &tc) const +{ + if (tc.isNull()) + return atEmptyLine(m_cursor); + return tc.block().length() == 1; +} + +bool FakeVimHandler::Private::atBoundary(bool end, bool simple, bool onlyWords, + const QTextCursor &tc) const +{ + if (tc.isNull()) + return atBoundary(end, simple, onlyWords, m_cursor); + if (atEmptyLine(tc)) + return true; + int pos = tc.position(); + QChar c1 = document()->characterAt(pos); + QChar c2 = document()->characterAt(pos + (end ? 1 : -1)); + int thisClass = charClass(c1, simple); + return (!onlyWords || thisClass != 0) + && (c2.isNull() || c2 == ParagraphSeparator || thisClass != charClass(c2, simple)); +} + +bool FakeVimHandler::Private::atWordBoundary(bool end, bool simple, const QTextCursor &tc) const +{ + return atBoundary(end, simple, true, tc); +} + +bool FakeVimHandler::Private::atWordStart(bool simple, const QTextCursor &tc) const +{ + return atWordBoundary(false, simple, tc); +} + +bool FakeVimHandler::Private::atWordEnd(bool simple, const QTextCursor &tc) const +{ + return atWordBoundary(true, simple, tc); +} + +bool FakeVimHandler::Private::isFirstNonBlankOnLine(int pos) +{ + for (int i = document()->findBlock(pos).position(); i < pos; ++i) { + if (!document()->characterAt(i).isSpace()) + return false; + } + return true; +} + +void FakeVimHandler::Private::pushUndoState(bool overwrite) +{ + if (m_buffer->editBlockLevel != 0 && m_buffer->undoState.isValid()) + return; // No need to save undo state for inner edit blocks. + + if (m_buffer->undoState.isValid() && !overwrite) + return; + + UNDO_DEBUG("PUSH UNDO"); + int pos = position(); + if (!isInsertMode()) { + if (isVisualMode() || g.submode == DeleteSubMode + || (g.submode == ChangeSubMode && g.movetype != MoveLineWise)) { + pos = qMin(pos, anchor()); + if (isVisualLineMode()) + pos = firstPositionInLine(lineForPosition(pos)); + } else if (g.movetype == MoveLineWise && hasConfig(ConfigStartOfLine)) { + QTextCursor tc = m_cursor; + if (g.submode == ShiftLeftSubMode || g.submode == ShiftRightSubMode + || g.submode == IndentSubMode) { + pos = qMin(pos, anchor()); + } + tc.setPosition(pos); + moveToFirstNonBlankOnLine(&tc); + pos = qMin(pos, tc.position()); + } + } + + m_buffer->redo.clear(); + m_buffer->lastChangePosition = CursorPosition(document(), pos); + if (isVisualMode()) { + setMark(QLatin1Char('<'), markLessPosition()); + setMark(QLatin1Char('>'), markGreaterPosition()); + } + m_buffer->undoState = State(revision(), m_buffer->lastChangePosition, m_buffer->marks, + m_buffer->lastVisualMode, m_buffer->lastVisualModeInverted); +} + +void FakeVimHandler::Private::moveDown(int n) +{ + if (n == 0) + return; + + QTextBlock block = m_cursor.block(); + const int col = position() - block.position(); + + int lines = qAbs(n); + int position = 0; + while (block.isValid()) { + position = block.position() + qMax(0, qMin(block.length() - 2, col)); + if (block.isVisible()) { + --lines; + if (lines < 0) + break; + } + block = n > 0 ? nextLine(block) : previousLine(block); + } + + setPosition(position); + moveToTargetColumn(); + updateScrollOffset(); +} + +void FakeVimHandler::Private::moveDownVisually(int n) +{ + const QTextCursor::MoveOperation moveOperation = (n > 0) ? Down : Up; + int count = qAbs(n); + int oldPos = m_cursor.position(); + + while (count > 0) { + m_cursor.movePosition(moveOperation, KeepAnchor, 1); + if (oldPos == m_cursor.position()) + break; + oldPos = m_cursor.position(); + QTextBlock block = m_cursor.block(); + if (block.isVisible()) + --count; + } + + QTextCursor tc = m_cursor; + tc.movePosition(StartOfLine); + const int minPos = tc.position(); + moveToEndOfLineVisually(&tc); + const int maxPos = tc.position(); + + if (m_targetColumn == -1) { + setPosition(maxPos); + } else { + setPosition(qMin(maxPos, minPos + m_targetColumnWrapped)); + const int targetColumn = m_targetColumnWrapped; + setTargetColumn(); + m_targetColumnWrapped = targetColumn; + } + + updateScrollOffset(); +} + +void FakeVimHandler::Private::movePageDown(int count) +{ + const int scrollOffset = windowScrollOffset(); + const int screenLines = linesOnScreen(); + const int offset = count > 0 ? scrollOffset - 2 : screenLines - scrollOffset + 2; + const int value = count * screenLines - cursorLineOnScreen() + offset; + moveDown(value); + + if (count > 0) + scrollToLine(cursorLine()); + else + scrollToLine(qMax(0, cursorLine() - screenLines + 1)); +} + +bool FakeVimHandler::Private::moveToNextParagraph(int count) +{ + const bool forward = count > 0; + int repeat = forward ? count : -count; + int pos = position(); + QTextBlock block = this->block(); + + if (block.isValid() && block.length() == 1) + ++repeat; + + for (; block.isValid(); block = forward ? block.next() : block.previous()) { + if (block.length() == 1) { + if (--repeat == 0) + break; + while (block.isValid() && block.length() == 1) + block = forward ? block.next() : block.previous(); + } + } + + if (repeat == 0) + setPosition(block.position()); + else if (repeat == 1) + setPosition(forward ? lastPositionInDocument() : 0); + else + return false; + + recordJump(pos); + setTargetColumn(); + g.movetype = MoveExclusive; + + return true; +} + +void FakeVimHandler::Private::moveToEndOfLine() +{ + // Additionally select (in visual mode) or apply current command on hidden lines following + // the current line. + bool onlyVisibleLines = isVisualMode() || g.submode != NoSubMode; + const int id = onlyVisibleLines ? lineNumber(block()) : block().blockNumber() + 1; + setPosition(lastPositionInLine(id, onlyVisibleLines)); + setTargetColumn(); +} + +void FakeVimHandler::Private::moveToEndOfLineVisually() +{ + moveToEndOfLineVisually(&m_cursor); + setTargetColumn(); +} + +void FakeVimHandler::Private::moveToEndOfLineVisually(QTextCursor *tc) +{ + // Moving to end of line ends up on following line if the line is wrapped. + tc->movePosition(StartOfLine); + const int minPos = tc->position(); + tc->movePosition(EndOfLine); + int maxPos = tc->position(); + tc->movePosition(StartOfLine); + if (minPos != tc->position()) + --maxPos; + tc->setPosition(maxPos); +} + +void FakeVimHandler::Private::moveBehindEndOfLine() +{ + emit q->fold(1, false); + int pos = qMin(block().position() + block().length() - 1, + lastPositionInDocument() + 1); + setPosition(pos); +} + +void FakeVimHandler::Private::moveToStartOfLine() +{ + setPosition(block().position()); + setTargetColumn(); +} + +void FakeVimHandler::Private::moveToStartOfLineVisually() +{ + m_cursor.movePosition(StartOfLine, KeepAnchor); + setTargetColumn(); +} + +void FakeVimHandler::Private::fixSelection() +{ + if (g.rangemode == RangeBlockMode) + return; + + if (g.movetype == MoveInclusive) { + // If position or anchor is after end of non-empty line, include line break in selection. + if (document()->characterAt(position()) == ParagraphSeparator) { + if (!atEmptyLine()) { + setPosition(position() + 1); + return; + } + } else if (document()->characterAt(anchor()) == ParagraphSeparator) { + QTextCursor tc = m_cursor; + tc.setPosition(anchor()); + if (!atEmptyLine(tc)) { + setAnchorAndPosition(anchor() + 1, position()); + return; + } + } + } + + if (g.movetype == MoveExclusive && g.subsubmode == NoSubSubMode) { + if (anchor() < position() && atBlockStart()) { + // Exclusive motion ending at the beginning of line + // becomes inclusive and end is moved to end of previous line. + g.movetype = MoveInclusive; + moveToStartOfLine(); + moveLeft(); + + // Exclusive motion ending at the beginning of line and + // starting at or before first non-blank on a line becomes linewise. + if (anchor() < block().position() && isFirstNonBlankOnLine(anchor())) + g.movetype = MoveLineWise; + } + } + + if (g.movetype == MoveLineWise) + g.rangemode = (g.submode == ChangeSubMode) + ? RangeLineModeExclusive + : RangeLineMode; + + if (g.movetype == MoveInclusive) { + if (anchor() <= position()) { + if (!atBlockEnd()) + setPosition(position() + 1); // correction + + // Omit first character in selection if it's line break on non-empty line. + int start = anchor(); + int end = position(); + if (afterEndOfLine(document(), start) && start > 0) { + start = qMin(start + 1, end); + if (g.submode == DeleteSubMode && !atDocumentEnd()) + setAnchorAndPosition(start, end + 1); + else + setAnchorAndPosition(start, end); + } + + // If more than one line is selected and all are selected completely + // movement becomes linewise. + if (start < block().position() && isFirstNonBlankOnLine(start) && atBlockEnd()) { + if (g.submode != ChangeSubMode) { + moveRight(); + if (atEmptyLine()) + moveRight(); + } + g.movetype = MoveLineWise; + } + } else if (!m_anchorPastEnd) { + setAnchorAndPosition(anchor() + 1, position()); + } + } + + if (m_positionPastEnd) { + moveBehindEndOfLine(); + moveRight(); + setAnchorAndPosition(anchor(), position()); + } + + if (m_anchorPastEnd) { + const int pos = position(); + setPosition(anchor()); + moveBehindEndOfLine(); + moveRight(); + setAnchorAndPosition(position(), pos); + } +} + +bool FakeVimHandler::Private::finishSearch() +{ + if (g.lastSearch.isEmpty() + || (!g.currentMessage.isEmpty() && g.currentMessageLevel == MessageError)) { + return false; + } + if (g.submode != NoSubMode) + setAnchorAndPosition(m_searchStartPosition, position()); + return true; +} + +void FakeVimHandler::Private::finishMovement(const QString &dotCommandMovement) +{ + //dump("FINISH MOVEMENT"); + if (g.submode == FilterSubMode) { + int beginLine = lineForPosition(anchor()); + int endLine = lineForPosition(position()); + setPosition(qMin(anchor(), position())); + enterExMode(QString::fromLatin1(".,+%1!").arg(qAbs(endLine - beginLine))); + return; + } + + if (g.submode == ChangeSubMode + || g.submode == DeleteSubMode + || g.submode == YankSubMode + || g.submode == InvertCaseSubMode + || g.submode == DownCaseSubMode + || g.submode == UpCaseSubMode) { + fixSelection(); + + if (g.submode != InvertCaseSubMode + && g.submode != DownCaseSubMode + && g.submode != UpCaseSubMode) { + yankText(currentRange(), m_register); + if (g.movetype == MoveLineWise) + setRegister(m_register, registerContents(m_register), RangeLineMode); + } + + m_positionPastEnd = m_anchorPastEnd = false; + } + + QString dotCommand; + if (g.submode == ChangeSubMode) { + pushUndoState(false); + beginEditBlock(); + removeText(currentRange()); + dotCommand = _("c"); + if (g.movetype == MoveLineWise) + insertAutomaticIndentation(true); + endEditBlock(); + setTargetColumn(); + } else if (g.submode == DeleteSubMode) { + pushUndoState(false); + beginEditBlock(); + const int pos = position(); + // Always delete something (e.g. 'dw' on an empty line deletes the line). + if (pos == anchor() && g.movetype == MoveInclusive) + removeText(Range(pos, pos + 1)); + else + removeText(currentRange()); + dotCommand = _("d"); + if (g.movetype == MoveLineWise) + handleStartOfLine(); + if (atEndOfLine()) + moveLeft(); + else + setTargetColumn(); + endEditBlock(); + } else if (g.submode == YankSubMode) { + bool isVisualModeYank = isVisualMode(); + leaveVisualMode(); + const QTextCursor tc = m_cursor; + if (g.rangemode == RangeBlockMode) { + const int pos1 = tc.block().position(); + const int pos2 = document()->findBlock(tc.anchor()).position(); + const int col = qMin(tc.position() - pos1, tc.anchor() - pos2); + setPosition(qMin(pos1, pos2) + col); + } else { + setPosition(qMin(position(), anchor())); + if (g.rangemode == RangeLineMode) { + if (isVisualModeYank) + moveToStartOfLine(); + } + } + setTargetColumn(); + } else if (g.submode == InvertCaseSubMode + || g.submode == UpCaseSubMode + || g.submode == DownCaseSubMode) { + beginEditBlock(); + if (g.submode == InvertCaseSubMode) { + invertCase(currentRange()); + dotCommand = QString::fromLatin1("g~"); + } else if (g.submode == DownCaseSubMode) { + downCase(currentRange()); + dotCommand = QString::fromLatin1("gu"); + } else if (g.submode == UpCaseSubMode) { + upCase(currentRange()); + dotCommand = QString::fromLatin1("gU"); + } + if (g.movetype == MoveLineWise) + handleStartOfLine(); + endEditBlock(); + } else if (g.submode == IndentSubMode + || g.submode == ShiftRightSubMode + || g.submode == ShiftLeftSubMode) { + recordJump(); + pushUndoState(false); + if (g.submode == IndentSubMode) { + indentSelectedText(); + dotCommand = _("="); + } else if (g.submode == ShiftRightSubMode) { + shiftRegionRight(1); + dotCommand = _(">"); + } else if (g.submode == ShiftLeftSubMode) { + shiftRegionLeft(1); + dotCommand = _("<"); + } + } + + if (!dotCommand.isEmpty() && !dotCommandMovement.isEmpty()) + setDotCommand(dotCommand + dotCommandMovement); + + // Change command continues in insert mode. + if (g.submode == ChangeSubMode) { + clearCommandMode(); + enterInsertMode(); + } else { + resetCommandMode(); + } +} + +void FakeVimHandler::Private::resetCommandMode() +{ + if (g.returnToMode == CommandMode) { + enterCommandMode(); + } else { + clearCommandMode(); + const QString lastInsertion = m_buffer->lastInsertion; + if (g.returnToMode == InsertMode) + enterInsertMode(); + else + enterReplaceMode(); + moveToTargetColumn(); + invalidateInsertState(); + m_buffer->lastInsertion = lastInsertion; + } + if (isNoVisualMode()) + setAnchor(); +} + +void FakeVimHandler::Private::clearCommandMode() +{ + g.submode = NoSubMode; + g.subsubmode = NoSubSubMode; + g.movetype = MoveInclusive; + g.gflag = false; + m_register = '"'; + g.rangemode = RangeCharMode; + g.currentCommand.clear(); + resetCount(); +} + +void FakeVimHandler::Private::updateSelection() +{ + QList selections = m_extraSelections; + if (hasConfig(ConfigShowMarks)) { + for (MarksIterator it(m_buffer->marks); it.hasNext(); ) { + it.next(); + QTextEdit::ExtraSelection sel; + sel.cursor = m_cursor; + setCursorPosition(&sel.cursor, it.value().position(document())); + sel.cursor.setPosition(sel.cursor.position(), MoveAnchor); + sel.cursor.movePosition(Right, KeepAnchor); + sel.format = m_cursor.blockCharFormat(); + sel.format.setForeground(Qt::blue); + sel.format.setBackground(Qt::green); + selections.append(sel); + } + } + //qDebug() << "SELECTION: " << selections; + emit q->selectionChanged(selections); +} + +void FakeVimHandler::Private::updateHighlights() +{ + if (hasConfig(ConfigUseCoreSearch) || !hasConfig(ConfigHlSearch) || g.highlightsCleared) { + if (m_highlighted.isEmpty()) + return; + m_highlighted.clear(); + } else if (m_highlighted != g.lastNeedle) { + m_highlighted = g.lastNeedle; + } else { + return; + } + + emit q->highlightMatches(m_highlighted); +} + +void FakeVimHandler::Private::updateMiniBuffer() +{ + if (!m_textedit && !m_plaintextedit) + return; + + QString msg; + int cursorPos = -1; + int anchorPos = -1; + MessageLevel messageLevel = MessageMode; + + if (!g.mapStates.isEmpty() && g.mapStates.last().silent && g.currentMessageLevel < MessageInfo) + g.currentMessage.clear(); + + if (g.passing) { + msg = _("PASSING"); + } else if (g.subsubmode == SearchSubSubMode) { + msg = g.searchBuffer.display(); + if (g.mapStates.isEmpty()) { + cursorPos = g.searchBuffer.cursorPos() + 1; + anchorPos = g.searchBuffer.anchorPos() + 1; + } + } else if (g.mode == ExMode) { + msg = g.commandBuffer.display(); + if (g.mapStates.isEmpty()) { + cursorPos = g.commandBuffer.cursorPos() + 1; + anchorPos = g.commandBuffer.anchorPos() + 1; + } + } else if (!g.currentMessage.isEmpty()) { + msg = g.currentMessage; + g.currentMessage.clear(); + messageLevel = g.currentMessageLevel; + } else if (!g.mapStates.isEmpty() && !g.mapStates.last().silent) { + // Do not reset previous message when after running a mapped command. + return; + } else if (g.mode == CommandMode && !g.currentCommand.isEmpty() && hasConfig(ConfigShowCmd)) { + msg = g.currentCommand; + messageLevel = MessageShowCmd; + } else if (g.mode == CommandMode && isVisualMode()) { + if (isVisualCharMode()) + msg = _("-- VISUAL --"); + else if (isVisualLineMode()) + msg = _("-- VISUAL LINE --"); + else if (isVisualBlockMode()) + msg = _("VISUAL BLOCK"); + } else if (g.mode == InsertMode) { + msg = _("-- INSERT --"); + } else if (g.mode == ReplaceMode) { + msg = _("-- REPLACE --"); + } else { + QTC_CHECK(g.mode == CommandMode && g.subsubmode != SearchSubSubMode); + if (g.returnToMode == CommandMode) + msg = _("-- COMMAND --"); + else if (g.returnToMode == InsertMode) + msg = _("-- (insert) --"); + else + msg = _("-- (replace) --"); + } + + if (!g.recording.isNull() && msg.startsWith(_("--"))) + msg.append(_("recording")); + + emit q->commandBufferChanged(msg, cursorPos, anchorPos, messageLevel, q); + + int linesInDoc = linesInDocument(); + int l = cursorLine(); + QString status; + const QString pos = QString::fromLatin1("%1,%2") + .arg(l + 1).arg(physicalCursorColumn() + 1); + // FIXME: physical "-" logical + if (linesInDoc != 0) + status = FakeVimHandler::tr("%1%2%").arg(pos, -10).arg(l * 100 / linesInDoc, 4); + else + status = FakeVimHandler::tr("%1All").arg(pos, -10); + emit q->statusDataChanged(status); +} + +void FakeVimHandler::Private::showMessage(MessageLevel level, const QString &msg) +{ + //qDebug() << "MSG: " << msg; + g.currentMessage = msg; + g.currentMessageLevel = level; +} + +void FakeVimHandler::Private::notImplementedYet() +{ + qDebug() << "Not implemented in FakeVim"; + showMessage(MessageError, FakeVimHandler::tr("Not implemented in FakeVim.")); +} + +void FakeVimHandler::Private::passShortcuts(bool enable) +{ + g.passing = enable; + updateMiniBuffer(); + if (enable) + QCoreApplication::instance()->installEventFilter(q); + else + QCoreApplication::instance()->removeEventFilter(q); +} + +bool FakeVimHandler::Private::handleCommandSubSubMode(const Input &input) +{ + //const int key = input.key; + bool handled = true; + if (g.subsubmode == FtSubSubMode) { + g.semicolonType = g.subsubdata; + g.semicolonKey = input.text(); + bool valid = handleFfTt(g.semicolonKey); + g.subsubmode = NoSubSubMode; + if (!valid) { + g.submode = NoSubMode; + resetCommandMode(); + handled = false; + } else { + finishMovement(QString::fromLatin1("%1%2%3") + .arg(count()) + .arg(g.semicolonType.text()) + .arg(g.semicolonKey)); + } + } else if (g.subsubmode == TextObjectSubSubMode) { + bool ok = true; + if (input.is('w')) + selectWordTextObject(g.subsubdata.is('i')); + else if (input.is('W')) + selectWORDTextObject(g.subsubdata.is('i')); + else if (input.is('s')) + selectSentenceTextObject(g.subsubdata.is('i')); + else if (input.is('p')) + selectParagraphTextObject(g.subsubdata.is('i')); + else if (input.is('[') || input.is(']')) + ok = selectBlockTextObject(g.subsubdata.is('i'), '[', ']'); + else if (input.is('(') || input.is(')') || input.is('b')) + ok = selectBlockTextObject(g.subsubdata.is('i'), '(', ')'); + else if (input.is('<') || input.is('>')) + ok = selectBlockTextObject(g.subsubdata.is('i'), '<', '>'); + else if (input.is('{') || input.is('}') || input.is('B')) + ok = selectBlockTextObject(g.subsubdata.is('i'), '{', '}'); + else if (input.is('"') || input.is('\'') || input.is('`')) + ok = selectQuotedStringTextObject(g.subsubdata.is('i'), input.asChar()); + else + ok = false; + g.subsubmode = NoSubSubMode; + if (ok) { + finishMovement(QString::fromLatin1("%1%2%3") + .arg(count()) + .arg(g.subsubdata.text()) + .arg(input.text())); + } else { + resetCommandMode(); + handled = false; + } + } else if (g.subsubmode == MarkSubSubMode) { + setMark(input.asChar(), CursorPosition(m_cursor)); + g.subsubmode = NoSubSubMode; + } else if (g.subsubmode == BackTickSubSubMode + || g.subsubmode == TickSubSubMode) { + if (jumpToMark(input.asChar(), g.subsubmode == BackTickSubSubMode)) { + finishMovement(); + } else { + resetCommandMode(); + handled = false; + } + g.subsubmode = NoSubSubMode; + } else if (g.subsubmode == ZSubSubMode) { + handled = false; + if (input.is('j') || input.is('k')) { + int pos = position(); + emit q->foldGoTo(input.is('j') ? count() : -count(), false); + if (pos != position()) { + handled = true; + finishMovement(QString::fromLatin1("%1z%2") + .arg(count()) + .arg(input.text())); + } + } + } else if (g.subsubmode == OpenSquareSubSubMode || g.subsubmode == CloseSquareSubSubMode) { + int pos = position(); + if (input.is('{') && g.subsubmode == OpenSquareSubSubMode) + searchBalanced(false, QLatin1Char('{'), QLatin1Char('}')); + else if (input.is('}') && g.subsubmode == CloseSquareSubSubMode) + searchBalanced(true, QLatin1Char('}'), QLatin1Char('{')); + else if (input.is('(') && g.subsubmode == OpenSquareSubSubMode) + searchBalanced(false, QLatin1Char('('), QLatin1Char(')')); + else if (input.is(')') && g.subsubmode == CloseSquareSubSubMode) + searchBalanced(true, QLatin1Char(')'), QLatin1Char('(')); + else if (input.is('[') && g.subsubmode == OpenSquareSubSubMode) + bracketSearchBackward(&m_cursor, _("^\\{"), count()); + else if (input.is('[') && g.subsubmode == CloseSquareSubSubMode) + bracketSearchForward(&m_cursor, _("^\\}"), count(), false); + else if (input.is(']') && g.subsubmode == OpenSquareSubSubMode) + bracketSearchBackward(&m_cursor, _("^\\}"), count()); + else if (input.is(']') && g.subsubmode == CloseSquareSubSubMode) + bracketSearchForward(&m_cursor, _("^\\{"), count(), g.submode != NoSubMode); + else if (input.is('z')) + emit q->foldGoTo(g.subsubmode == OpenSquareSubSubMode ? -count() : count(), true); + handled = pos != position(); + if (handled) { + if (lineForPosition(pos) != lineForPosition(position())) + recordJump(pos); + finishMovement(QString::fromLatin1("%1%2%3") + .arg(count()) + .arg(g.subsubmode == OpenSquareSubSubMode ? '[' : ']') + .arg(input.text())); + } + } else { + handled = false; + } + return handled; +} + +bool FakeVimHandler::Private::handleCount(const Input &input) +{ + if (!isInputCount(input)) + return false; + g.mvcount = g.mvcount * 10 + input.text().toInt(); + return true; +} + +bool FakeVimHandler::Private::handleMovement(const Input &input) +{ + bool handled = true; + QString movement; + int count = this->count(); + + if (handleCount(input)) { + return true; + } else if (input.is('0')) { + g.movetype = MoveExclusive; + if (g.gflag) + moveToStartOfLineVisually(); + else + moveToStartOfLine(); + count = 1; + } else if (input.is('a') || input.is('i')) { + g.subsubmode = TextObjectSubSubMode; + g.subsubdata = input; + } else if (input.is('^') || input.is('_')) { + if (g.gflag) + moveToFirstNonBlankOnLineVisually(); + else + moveToFirstNonBlankOnLine(); + g.movetype = MoveExclusive; + } else if (0 && input.is(',')) { + // FIXME: fakevim uses ',' by itself, so it is incompatible + g.subsubmode = FtSubSubMode; + // HACK: toggle 'f' <-> 'F', 't' <-> 'T' + //g.subsubdata = g.semicolonType ^ 32; + handleFfTt(g.semicolonKey, true); + g.subsubmode = NoSubSubMode; + } else if (input.is(';')) { + g.subsubmode = FtSubSubMode; + g.subsubdata = g.semicolonType; + handleFfTt(g.semicolonKey, true); + g.subsubmode = NoSubSubMode; + } else if (input.is('/') || input.is('?')) { + g.lastSearchForward = input.is('/'); + if (hasConfig(ConfigUseCoreSearch)) { + // re-use the core dialog. + g.findPending = true; + m_findStartPosition = position(); + g.movetype = MoveExclusive; + setAnchor(); // clear selection: otherwise, search is restricted to selection + emit q->findRequested(!g.lastSearchForward); + } else { + // FIXME: make core find dialog sufficiently flexible to + // produce the "default vi" behaviour too. For now, roll our own. + g.currentMessage.clear(); + g.movetype = MoveExclusive; + g.subsubmode = SearchSubSubMode; + g.searchBuffer.setPrompt(g.lastSearchForward ? QLatin1Char('/') : QLatin1Char('?')); + m_searchStartPosition = position(); + m_searchFromScreenLine = firstVisibleLine(); + m_searchCursor = QTextCursor(); + g.searchBuffer.clear(); + } + } else if (input.is('`')) { + g.subsubmode = BackTickSubSubMode; + } else if (input.is('#') || input.is('*')) { + // FIXME: That's not proper vim behaviour + QString needle; + QTextCursor tc = m_cursor; + tc.select(QTextCursor::WordUnderCursor); + needle = QRegExp::escape(tc.selection().toPlainText()); + if (!g.gflag) + needle = _("\\<") + needle + _("\\>"); + setAnchorAndPosition(tc.position(), tc.anchor()); + g.searchBuffer.historyPush(needle); + g.lastSearch = needle; + g.lastSearchForward = input.is('*'); + handled = searchNext(); + } else if (input.is('\'')) { + g.subsubmode = TickSubSubMode; + if (g.submode != NoSubMode) + g.movetype = MoveLineWise; + } else if (input.is('|')) { + moveToStartOfLine(); + moveRight(qMin(count, rightDist()) - 1); + setTargetColumn(); + } else if (input.is('}')) { + handled = moveToNextParagraph(count); + } else if (input.is('{')) { + handled = moveToPreviousParagraph(count); + } else if (input.isReturn()) { + moveToStartOfLine(); + moveDown(); + moveToFirstNonBlankOnLine(); + g.movetype = MoveLineWise; + } else if (input.is('-')) { + moveToStartOfLine(); + moveUp(count); + moveToFirstNonBlankOnLine(); + g.movetype = MoveLineWise; + } else if (input.is('+')) { + moveToStartOfLine(); + moveDown(count); + moveToFirstNonBlankOnLine(); + g.movetype = MoveLineWise; + } else if (input.isKey(Key_Home)) { + moveToStartOfLine(); + setTargetColumn(); + movement = _(""); + } else if (input.is('$') || input.isKey(Key_End)) { + if (g.gflag) { + if (count > 1) + moveDownVisually(count - 1); + moveToEndOfLineVisually(); + } else { + if (count > 1) + moveDown(count - 1); + moveToEndOfLine(); + } + g.movetype = atEmptyLine() ? MoveExclusive : MoveInclusive; + setTargetColumn(); + if (g.submode == NoSubMode) + m_targetColumn = -1; + if (isVisualMode()) + m_visualTargetColumn = -1; + movement = _("$"); + } else if (input.is('%')) { + recordJump(); + if (g.mvcount == 0) { + moveToMatchingParanthesis(); + g.movetype = MoveInclusive; + } else { + // set cursor position in percentage - formula taken from Vim help + setPosition(firstPositionInLine((count * linesInDocument() + 99) / 100)); + moveToTargetColumn(); + handleStartOfLine(); + g.movetype = MoveLineWise; + } + } else if (input.is('b') || input.isShift(Key_Left)) { + g.movetype = MoveExclusive; + moveToNextWordStart(count, false, false); + setTargetColumn(); + movement = _("b"); + } else if (input.is('B')) { + g.movetype = MoveExclusive; + moveToNextWordStart(count, true, false); + setTargetColumn(); + } else if (input.is('e') && g.gflag) { + g.movetype = MoveInclusive; + moveToNextWordEnd(count, false, false); + setTargetColumn(); + } else if (input.is('e') || input.isShift(Key_Right)) { + g.movetype = MoveInclusive; + moveToNextWordEnd(count, false, true, false); + setTargetColumn(); + movement = _("e"); + } else if (input.is('E') && g.gflag) { + g.movetype = MoveInclusive; + moveToNextWordEnd(count, true, false); + setTargetColumn(); + } else if (input.is('E')) { + g.movetype = MoveInclusive; + moveToNextWordEnd(count, true, true, false); + setTargetColumn(); + } else if (input.isControl('e')) { + // FIXME: this should use the "scroll" option, and "count" + if (cursorLineOnScreen() == 0) + moveDown(1); + scrollDown(1); + movement = _(""); + } else if (input.is('f')) { + g.subsubmode = FtSubSubMode; + g.movetype = MoveInclusive; + g.subsubdata = input; + } else if (input.is('F')) { + g.subsubmode = FtSubSubMode; + g.movetype = MoveExclusive; + g.subsubdata = input; + } else if (!g.gflag && input.is('g')) { + g.gflag = true; + return true; + } else if (input.is('g') || input.is('G')) { + QString dotCommand = QString::fromLatin1("%1G").arg(count); + recordJump(); + if (input.is('G') && g.mvcount == 0) + dotCommand = QString(QLatin1Char('G')); + int n = (input.is('g')) ? 1 : linesInDocument(); + n = g.mvcount == 0 ? n : count; + if (g.submode == NoSubMode || g.submode == ZSubMode + || g.submode == CapitalZSubMode || g.submode == RegisterSubMode) { + setPosition(firstPositionInLine(n, false)); + handleStartOfLine(); + } else { + g.movetype = MoveLineWise; + g.rangemode = RangeLineMode; + setAnchor(); + setPosition(firstPositionInLine(n, false)); + } + setTargetColumn(); + updateScrollOffset(); + } else if (input.is('h') || input.isKey(Key_Left) || input.isBackspace()) { + g.movetype = MoveExclusive; + int n = qMin(count, leftDist()); + if (m_fakeEnd && block().length() > 1) + ++n; + moveLeft(n); + setTargetColumn(); + movement = _("h"); + } else if (input.is('H')) { + const CursorPosition pos(lineToBlockNumber(lineOnTop(count)), 0); + setCursorPosition(&m_cursor, pos); + handleStartOfLine(); + } else if (input.is('j') || input.isKey(Key_Down) + || input.isControl('j') || input.isControl('n')) { + if (g.gflag) { + g.movetype = MoveExclusive; + moveDownVisually(count); + movement = _("gj"); + } else { + g.movetype = MoveLineWise; + moveDown(count); + movement = _("j"); + } + } else if (input.is('k') || input.isKey(Key_Up) || input.isControl('p')) { + if (g.gflag) { + g.movetype = MoveExclusive; + moveUpVisually(count); + movement = _("gk"); + } else { + g.movetype = MoveLineWise; + moveUp(count); + movement = _("k"); + } + } else if (input.is('l') || input.isKey(Key_Right) || input.is(' ')) { + g.movetype = MoveExclusive; + bool pastEnd = count >= rightDist() - 1; + moveRight(qMax(0, qMin(count, rightDist() - (g.submode == NoSubMode)))); + setTargetColumn(); + if (pastEnd && isVisualMode()) + m_visualTargetColumn = -1; + } else if (input.is('L')) { + const CursorPosition pos(lineToBlockNumber(lineOnBottom(count)), 0); + setCursorPosition(&m_cursor, pos); + handleStartOfLine(); + } else if (g.gflag && input.is('m')) { + const QPoint pos(EDITOR(viewport()->width()) / 2, EDITOR(cursorRect(m_cursor)).y()); + QTextCursor tc = EDITOR(cursorForPosition(pos)); + if (!tc.isNull()) { + m_cursor = tc; + setTargetColumn(); + } + } else if (input.is('M')) { + m_cursor = EDITOR(cursorForPosition(QPoint(0, EDITOR(height()) / 2))); + handleStartOfLine(); + } else if (input.is('n') || input.is('N')) { + if (hasConfig(ConfigUseCoreSearch)) { + bool forward = (input.is('n')) ? g.lastSearchForward : !g.lastSearchForward; + int pos = position(); + emit q->findNextRequested(!forward); + if (forward && pos == m_cursor.selectionStart()) { + // if cursor is already positioned at the start of a find result, this is returned + emit q->findNextRequested(false); + } + setPosition(m_cursor.selectionStart()); + } else { + handled = searchNext(input.is('n')); + } + } else if (input.is('t')) { + g.movetype = MoveInclusive; + g.subsubmode = FtSubSubMode; + g.subsubdata = input; + } else if (input.is('T')) { + g.movetype = MoveExclusive; + g.subsubmode = FtSubSubMode; + g.subsubdata = input; + } else if (input.is('w') || input.is('W')) { // tested + // Special case: "cw" and "cW" work the same as "ce" and "cE" if the + // cursor is on a non-blank - except if the cursor is on the last + // character of a word: only the current word will be changed + bool simple = input.is('W'); + if (g.submode == ChangeSubMode && !document()->characterAt(position()).isSpace()) { + moveToWordEnd(count, simple, true); + g.movetype = MoveInclusive; + } else { + moveToNextWordStart(count, simple, true); + // Command 'dw' deletes to the next word on the same line or to end of line. + if (g.submode == DeleteSubMode && count == 1) { + const QTextBlock currentBlock = document()->findBlock(anchor()); + setPosition(qMin(position(), currentBlock.position() + currentBlock.length())); + } + g.movetype = MoveExclusive; + } + setTargetColumn(); + } else if (input.is('z')) { + g.movetype = MoveLineWise; + g.subsubmode = ZSubSubMode; + } else if (input.is('[')) { + g.subsubmode = OpenSquareSubSubMode; + } else if (input.is(']')) { + g.subsubmode = CloseSquareSubSubMode; + } else if (input.isKey(Key_PageDown) || input.isControl('f')) { + movePageDown(count); + handleStartOfLine(); + movement = _("f"); + } else if (input.isKey(Key_PageUp) || input.isControl('b')) { + movePageUp(count); + handleStartOfLine(); + movement = _("b"); + } else { + handled = false; + } + + if (handled && g.subsubmode == NoSubSubMode) { + if (g.submode == NoSubMode) { + resetCommandMode(); + } else { + // finish movement for sub modes + const QString dotMovement = + (count > 1 ? QString::number(count) : QString()) + + _(g.gflag ? "g" : "") + + (movement.isNull() ? QString(input.asChar()) : movement); + finishMovement(dotMovement); + setTargetColumn(); + } + } + + return handled; +} + +EventResult FakeVimHandler::Private::handleCommandMode(const Input &input) +{ + bool handled = false; + + bool clearGflag = g.gflag; + bool clearRegister = g.submode != RegisterSubMode; + bool clearCount = g.submode != RegisterSubMode && !isInputCount(input); + + // Process input for a sub-mode. + if (input.isEscape()) { + handled = handleEscape(); + } else if (g.subsubmode != NoSubSubMode) { + handled = handleCommandSubSubMode(input); + } else if (g.submode == NoSubMode) { + handled = handleNoSubMode(input); + } else if (g.submode == ChangeSubMode || g.submode == DeleteSubMode) { + handled = handleChangeDeleteSubModes(input); + } else if (g.submode == ReplaceSubMode) { + handled = handleReplaceSubMode(input); + } else if (g.submode == FilterSubMode) { + handled = handleFilterSubMode(input); + } else if (g.submode == RegisterSubMode) { + handled = handleRegisterSubMode(input); + } else if (g.submode == WindowSubMode) { + handled = handleWindowSubMode(input); + } else if (g.submode == YankSubMode) { + handled = handleYankSubMode(input); + } else if (g.submode == ZSubMode) { + handled = handleZSubMode(input); + } else if (g.submode == CapitalZSubMode) { + handled = handleCapitalZSubMode(input); + } else if (g.submode == MacroRecordSubMode) { + handled = handleMacroRecordSubMode(input); + } else if (g.submode == MacroExecuteSubMode) { + handled = handleMacroExecuteSubMode(input); + } else if (g.submode == ShiftLeftSubMode + || g.submode == ShiftRightSubMode + || g.submode == IndentSubMode) { + handled = handleShiftSubMode(input); + } else if (g.submode == InvertCaseSubMode + || g.submode == DownCaseSubMode + || g.submode == UpCaseSubMode) { + handled = handleChangeCaseSubMode(input); + } + + if (!handled && isOperatorPending()) + handled = handleMovement(input); + + // Clear state and display incomplete command if necessary. + if (handled) { + bool noMode = + (g.mode == CommandMode && g.submode == NoSubMode && g.subsubmode == NoSubSubMode); + clearCount = clearCount && noMode && !g.gflag; + if (clearCount && clearRegister) { + resetCommandMode(); + } else { + // Use gflag only for next input. + if (clearGflag) + g.gflag = false; + // Clear [count] and [register] if its no longer needed. + if (clearCount) + resetCount(); + // Show or clear current command on minibuffer (showcmd). + if (input.isEscape() || g.mode != CommandMode || clearCount) + g.currentCommand.clear(); + else + g.currentCommand.append(input.toString()); + } + } else { + resetCommandMode(); + //qDebug() << "IGNORED IN COMMAND MODE: " << key << text + // << " VISUAL: " << g.visualMode; + + // if a key which produces text was pressed, don't mark it as unhandled + // - otherwise the text would be inserted while being in command mode + if (input.text().isEmpty()) + handled = false; + } + + updateMiniBuffer(); + + m_positionPastEnd = (m_visualTargetColumn == -1) && isVisualMode() && !atEmptyLine(); + + return handled ? EventHandled : EventCancelled; +} + +bool FakeVimHandler::Private::handleEscape() +{ + if (isVisualMode()) + leaveVisualMode(); + resetCommandMode(); + return true; +} + +bool FakeVimHandler::Private::handleNoSubMode(const Input &input) +{ + bool handled = true; + + if (input.is('&')) { + handleExCommand(g.gflag ? _("%s//~/&") : _("s")); + } else if (input.is(':')) { + enterExMode(); + } else if (input.is('!') && isNoVisualMode()) { + g.submode = FilterSubMode; + } else if (input.is('!') && isVisualMode()) { + enterExMode(QString::fromLatin1("!")); + } else if (input.is('"')) { + g.submode = RegisterSubMode; + } else if (input.is(',')) { + passShortcuts(true); + } else if (input.is('.')) { + //qDebug() << "REPEATING" << quoteUnprintable(g.dotCommand) << count() + // << input; + QString savedCommand = g.dotCommand; + g.dotCommand.clear(); + beginLargeEditBlock(); + replay(savedCommand); + endEditBlock(); + resetCommandMode(); + g.dotCommand = savedCommand; + } else if (input.is('<') || input.is('>') || input.is('=')) { + if (isNoVisualMode()) { + if (input.is('<')) + g.submode = ShiftLeftSubMode; + else if (input.is('>')) + g.submode = ShiftRightSubMode; + else + g.submode = IndentSubMode; + setAnchor(); + } else { + leaveVisualMode(); + const int lines = qAbs(lineForPosition(position()) - lineForPosition(anchor())) + 1; + const int repeat = count(); + if (input.is('<')) + shiftRegionLeft(repeat); + else if (input.is('>')) + shiftRegionRight(repeat); + else + indentSelectedText(); + const QString selectDotCommand = + (lines > 1) ? QString::fromLatin1("V%1j").arg(lines - 1): QString(); + setDotCommand(selectDotCommand + QString::fromLatin1("%1%2%2").arg(repeat).arg(input.raw())); + } + } else if ((!isVisualMode() && input.is('a')) || (isVisualMode() && input.is('A'))) { + if (isVisualMode()) { + enterVisualInsertMode(QLatin1Char('A')); + } else { + setDotCommand(_("%1a"), count()); + moveRight(qMin(rightDist(), 1)); + breakEditBlock(); + enterInsertMode(); + } + } else if (input.is('A')) { + breakEditBlock(); + moveBehindEndOfLine(); + setAnchor(); + enterInsertMode(); + setTargetColumn(); + setDotCommand(_("%1A"), count()); + } else if (input.isControl('a')) { + if (changeNumberTextObject(count())) + setDotCommand(_("%1"), count()); + } else if ((input.is('c') || input.is('d')) && isNoVisualMode()) { + setAnchor(); + g.opcount = g.mvcount; + g.mvcount = 0; + g.rangemode = RangeCharMode; + g.movetype = MoveExclusive; + g.submode = input.is('c') ? ChangeSubMode : DeleteSubMode; + } else if ((input.is('c') || input.is('C') || input.is('s') || input.is('R')) + && (isVisualCharMode() || isVisualLineMode())) { + setDotCommand(visualDotCommand() + input.asChar()); + leaveVisualMode(); + g.submode = ChangeSubMode; + finishMovement(); + } else if ((input.is('c') || input.is('s')) && isVisualBlockMode()) { + resetCount(); + enterVisualInsertMode(input.asChar()); + } else if (input.is('C')) { + setAnchor(); + moveToEndOfLine(); + g.rangemode = RangeCharMode; + g.submode = ChangeSubMode; + setDotCommand(QString(QLatin1Char('C'))); + finishMovement(); + } else if (input.isControl('c')) { + if (isNoVisualMode()) + showMessage(MessageInfo, tr("Type Alt-V, Alt-V to quit FakeVim mode.")); + else + leaveVisualMode(); + } else if ((input.is('d') || input.is('x') || input.isKey(Key_Delete)) + && isVisualMode()) { + pushUndoState(); + setDotCommand(visualDotCommand() + QLatin1Char('x')); + if (isVisualCharMode()) { + leaveVisualMode(); + g.submode = DeleteSubMode; + finishMovement(); + } else if (isVisualLineMode()) { + leaveVisualMode(); + yankText(currentRange(), m_register); + removeText(currentRange()); + handleStartOfLine(); + } else if (isVisualBlockMode()) { + leaveVisualMode(); + yankText(currentRange(), m_register); + removeText(currentRange()); + setPosition(qMin(position(), anchor())); + } + } else if (input.is('D') && isNoVisualMode()) { + pushUndoState(); + if (atEndOfLine()) + moveLeft(); + g.submode = DeleteSubMode; + g.movetype = MoveInclusive; + setAnchorAndPosition(position(), lastPositionInLine(cursorLine() + count())); + setDotCommand(QString(QLatin1Char('D'))); + finishMovement(); + setTargetColumn(); + } else if ((input.is('D') || input.is('X')) && + (isVisualCharMode() || isVisualLineMode())) { + setDotCommand(visualDotCommand() + QLatin1Char('X')); + leaveVisualMode(); + g.rangemode = RangeLineMode; + g.submode = NoSubMode; + yankText(currentRange(), m_register); + removeText(currentRange()); + moveToFirstNonBlankOnLine(); + } else if ((input.is('D') || input.is('X')) && isVisualBlockMode()) { + setDotCommand(visualDotCommand() + QLatin1Char('X')); + leaveVisualMode(); + g.rangemode = RangeBlockAndTailMode; + yankText(currentRange(), m_register); + removeText(currentRange()); + setPosition(qMin(position(), anchor())); + } else if (input.isControl('d')) { + const int scrollOffset = windowScrollOffset(); + int sline = cursorLine() < scrollOffset ? scrollOffset : cursorLineOnScreen(); + // FIXME: this should use the "scroll" option, and "count" + moveDown(linesOnScreen() / 2); + handleStartOfLine(); + scrollToLine(cursorLine() - sline); + } else if (!g.gflag && input.is('g')) { + g.gflag = true; + } else if (!isVisualMode() && (input.is('i') || input.isKey(Key_Insert))) { + setDotCommand(_("%1i"), count()); + breakEditBlock(); + enterInsertMode(); + if (atEndOfLine()) + moveLeft(); + } else if (input.is('I')) { + if (isVisualMode()) { + enterVisualInsertMode(QLatin1Char('I')); + } else { + if (g.gflag) { + setDotCommand(_("%1gI"), count()); + moveToStartOfLine(); + } else { + setDotCommand(_("%1I"), count()); + moveToFirstNonBlankOnLine(); + } + breakEditBlock(); + enterInsertMode(); + } + } else if (input.isControl('i')) { + jump(count()); + } else if (input.is('J')) { + pushUndoState(); + moveBehindEndOfLine(); + beginEditBlock(); + if (g.submode == NoSubMode) + joinLines(count(), g.gflag); + endEditBlock(); + setDotCommand(_("%1J"), count()); + } else if (input.isControl('l')) { + // screen redraw. should not be needed + } else if (!g.gflag && input.is('m')) { + g.subsubmode = MarkSubSubMode; + } else if (isVisualMode() && (input.is('o') || input.is('O'))) { + int pos = position(); + setAnchorAndPosition(pos, anchor()); + std::swap(m_positionPastEnd, m_anchorPastEnd); + setTargetColumn(); + if (m_positionPastEnd) + m_visualTargetColumn = -1; + } else if (input.is('o') || input.is('O')) { + bool insertAfter = input.is('o'); + setDotCommand(_(insertAfter ? "%1o" : "%1O"), count()); + pushUndoState(); + + // Prepend line only if on the first line and command is 'O'. + bool appendLine = true; + if (!insertAfter) { + if (block().blockNumber() == 0) + appendLine = false; + else + moveUp(); + } + const int line = lineNumber(block()); + + beginEditBlock(); + enterInsertMode(); + setPosition(appendLine ? lastPositionInLine(line) : firstPositionInLine(line)); + clearLastInsertion(); + setAnchor(); + insertNewLine(); + if (appendLine) { + m_buffer->insertState.newLineBefore = true; + } else { + moveUp(); + m_oldInternalPosition = position(); + m_buffer->insertState.pos1 = m_oldInternalPosition; + m_buffer->insertState.newLineAfter = true; + } + setTargetColumn(); + endEditBlock(); + + // Close accidentally opened block. + if (block().blockNumber() > 0) { + moveUp(); + if (line != lineNumber(block())) + emit q->fold(1, true); + moveDown(); + } + } else if (input.isControl('o')) { + jump(-count()); + } else if (input.is('p') || input.is('P') || input.isShift(Qt::Key_Insert)) { + pasteText(!input.is('P')); + setTargetColumn(); + setDotCommand(_("%1p"), count()); + finishMovement(); + } else if (input.is('q')) { + if (g.recording.isNull()) { + // Recording shouldn't work in mapping or while executing register. + handled = g.mapStates.empty(); + if (handled) + g.submode = MacroRecordSubMode; + } else { + // Stop recording. + stopRecording(); + } + } else if (input.is('r')) { + g.submode = ReplaceSubMode; + } else if (!isVisualMode() && input.is('R')) { + pushUndoState(); + breakEditBlock(); + enterReplaceMode(); + } else if (input.isControl('r')) { + int repeat = count(); + while (--repeat >= 0) + redo(); + } else if (input.is('s')) { + pushUndoState(); + leaveVisualMode(); + if (atEndOfLine()) + moveLeft(); + setAnchor(); + moveRight(qMin(count(), rightDist())); + setDotCommand(_("%1s"), count()); + g.submode = ChangeSubMode; + g.movetype = MoveExclusive; + finishMovement(); + } else if (input.is('S')) { + g.movetype = MoveLineWise; + pushUndoState(); + if (!isVisualMode()) { + const int line = cursorLine() + 1; + const int anc = firstPositionInLine(line); + const int pos = lastPositionInLine(line + count() - 1); + setAnchorAndPosition(anc, pos); + } + setDotCommand(_("%1S"), count()); + g.submode = ChangeSubMode; + finishMovement(); + } else if (g.gflag && input.is('t')) { + handleExCommand(_("tabnext")); + } else if (g.gflag && input.is('T')) { + handleExCommand(_("tabprev")); + } else if (input.isControl('t')) { + handleExCommand(_("pop")); + } else if (!g.gflag && input.is('u') && !isVisualMode()) { + int repeat = count(); + while (--repeat >= 0) + undo(); + } else if (input.isControl('u')) { + int sline = cursorLineOnScreen(); + // FIXME: this should use the "scroll" option, and "count" + moveUp(linesOnScreen() / 2); + handleStartOfLine(); + scrollToLine(cursorLine() - sline); + } else if (g.gflag && input.is('v')) { + if (m_buffer->lastVisualMode != NoVisualMode) { + CursorPosition from = markLessPosition(); + CursorPosition to = markGreaterPosition(); + toggleVisualMode(m_buffer->lastVisualMode); + setCursorPosition(m_buffer->lastVisualModeInverted ? to : from); + setAnchor(); + setCursorPosition(m_buffer->lastVisualModeInverted ? from : to); + setTargetColumn(); + } + } else if (input.is('v')) { + toggleVisualMode(VisualCharMode); + } else if (input.is('V')) { + toggleVisualMode(VisualLineMode); + } else if (input.isControl('v')) { + toggleVisualMode(VisualBlockMode); + } else if (input.isControl('w')) { + g.submode = WindowSubMode; + } else if (input.is('x') && isNoVisualMode()) { // = _("dl") + g.movetype = MoveExclusive; + g.submode = DeleteSubMode; + const int n = qMin(count(), rightDist()); + setAnchorAndPosition(position(), position() + n); + setDotCommand(_("%1x"), count()); + finishMovement(); + } else if (input.isControl('x')) { + if (changeNumberTextObject(-count())) + setDotCommand(_("%1"), count()); + } else if (input.is('X')) { + if (leftDist() > 0) { + setAnchor(); + moveLeft(qMin(count(), leftDist())); + yankText(currentRange(), m_register); + removeText(currentRange()); + } + } else if (input.is('Y') && isNoVisualMode()) { + handleYankSubMode(Input(QLatin1Char('y'))); + } else if (input.isControl('y')) { + // FIXME: this should use the "scroll" option, and "count" + if (cursorLineOnScreen() == linesOnScreen() - 1) + moveUp(1); + scrollUp(1); + } else if (input.is('y') && isNoVisualMode()) { + setAnchor(); + g.rangemode = RangeCharMode; + g.movetype = MoveExclusive; + g.submode = YankSubMode; + } else if (input.is('y') && isVisualCharMode()) { + g.rangemode = RangeCharMode; + g.movetype = MoveInclusive; + g.submode = YankSubMode; + finishMovement(); + } else if ((input.is('y') && isVisualLineMode()) + || (input.is('Y') && isVisualLineMode()) + || (input.is('Y') && isVisualCharMode())) { + g.rangemode = RangeLineMode; + g.movetype = MoveLineWise; + g.submode = YankSubMode; + finishMovement(); + } else if ((input.is('y') || input.is('Y')) && isVisualBlockMode()) { + g.rangemode = RangeBlockMode; + g.movetype = MoveInclusive; + g.submode = YankSubMode; + finishMovement(); + } else if (input.is('z')) { + g.submode = ZSubMode; + } else if (input.is('Z')) { + g.submode = CapitalZSubMode; + } else if ((input.is('~') || input.is('u') || input.is('U'))) { + g.movetype = MoveExclusive; + pushUndoState(); + if (isVisualMode()) { + setDotCommand(visualDotCommand() + QString::number(count()) + input.raw()); + if (isVisualLineMode()) + g.rangemode = RangeLineMode; + else if (isVisualBlockMode()) + g.rangemode = RangeBlockMode; + leaveVisualMode(); + if (input.is('~')) + g.submode = InvertCaseSubMode; + else if (input.is('u')) + g.submode = DownCaseSubMode; + else if (input.is('U')) + g.submode = UpCaseSubMode; + finishMovement(); + } else if (g.gflag || (input.is('~') && hasConfig(ConfigTildeOp))) { + if (atEndOfLine()) + moveLeft(); + setAnchor(); + if (input.is('~')) + g.submode = InvertCaseSubMode; + else if (input.is('u')) + g.submode = DownCaseSubMode; + else if (input.is('U')) + g.submode = UpCaseSubMode; + } else { + beginEditBlock(); + if (atEndOfLine()) + moveLeft(); + setAnchor(); + moveRight(qMin(count(), rightDist())); + if (input.is('~')) { + const int pos = position(); + invertCase(currentRange()); + setPosition(pos); + } else if (input.is('u')) { + downCase(currentRange()); + } else if (input.is('U')) { + upCase(currentRange()); + } + setDotCommand(QString::fromLatin1("%1%2").arg(count()).arg(input.raw())); + endEditBlock(); + } + } else if (input.is('@')) { + g.submode = MacroExecuteSubMode; + } else if (input.isKey(Key_Delete)) { + setAnchor(); + moveRight(qMin(1, rightDist())); + removeText(currentRange()); + if (atEndOfLine()) + moveLeft(); + } else if (input.isControl(Key_BracketRight)) { + handleExCommand(_("tag")); + } else if (handleMovement(input)) { + // movement handled + } else { + handled = false; + } + + return handled; +} + +bool FakeVimHandler::Private::handleChangeDeleteSubModes(const Input &input) +{ + bool handled = false; + + if ((g.submode == ChangeSubMode && input.is('c')) + || (g.submode == DeleteSubMode && input.is('d'))) { + g.movetype = MoveLineWise; + pushUndoState(); + const int anc = firstPositionInLine(cursorLine() + 1); + moveDown(count() - 1); + const int pos = lastPositionInLine(cursorLine() + 1); + setAnchorAndPosition(anc, pos); + if (g.submode == ChangeSubMode) + setDotCommand(_("%1cc"), count()); + else + setDotCommand(_("%1dd"), count()); + finishMovement(); + g.submode = NoSubMode; + handled = true; + } + + return handled; +} + +bool FakeVimHandler::Private::handleReplaceSubMode(const Input &input) +{ + bool handled = true; + + setDotCommand(visualDotCommand() + QLatin1Char('r') + input.asChar()); + if (isVisualMode()) { + pushUndoState(); + if (isVisualLineMode()) + g.rangemode = RangeLineMode; + else if (isVisualBlockMode()) + g.rangemode = RangeBlockMode; + else + g.rangemode = RangeCharMode; + leaveVisualMode(); + Range range = currentRange(); + if (g.rangemode == RangeCharMode) + ++range.endPos; + Transformation tr = + &FakeVimHandler::Private::replaceByCharTransform; + transformText(range, tr, input.asChar()); + } else if (count() <= rightDist()) { + pushUndoState(); + setAnchor(); + moveRight(count()); + Range range = currentRange(); + if (input.isReturn()) { + beginEditBlock(); + replaceText(range, QString()); + insertText(QString::fromLatin1("\n")); + endEditBlock(); + } else { + replaceText(range, QString(count(), input.asChar())); + moveRight(count() - 1); + } + setTargetColumn(); + setDotCommand(_("%1r") + input.text(), count()); + } else { + handled = false; + } + g.submode = NoSubMode; + finishMovement(); + + return handled; +} + +bool FakeVimHandler::Private::handleFilterSubMode(const Input &) +{ + return false; +} + +bool FakeVimHandler::Private::handleRegisterSubMode(const Input &input) +{ + bool handled = false; + + QChar reg = input.asChar(); + if (QString::fromLatin1("*+.%#:-\"").contains(reg) || reg.isLetterOrNumber()) { + m_register = reg.unicode(); + g.rangemode = RangeLineMode; + handled = true; + } + g.submode = NoSubMode; + + return handled; +} + +bool FakeVimHandler::Private::handleShiftSubMode(const Input &input) +{ + bool handled = false; + if ((g.submode == ShiftLeftSubMode && input.is('<')) + || (g.submode == ShiftRightSubMode && input.is('>')) + || (g.submode == IndentSubMode && input.is('='))) { + g.movetype = MoveLineWise; + pushUndoState(); + moveDown(count() - 1); + setDotCommand(QString::fromLatin1("%2%1%1").arg(input.asChar()), count()); + finishMovement(); + handled = true; + g.submode = NoSubMode; + } + return handled; +} + +bool FakeVimHandler::Private::handleChangeCaseSubMode(const Input &input) +{ + bool handled = false; + if ((g.submode == InvertCaseSubMode && input.is('~')) + || (g.submode == DownCaseSubMode && input.is('u')) + || (g.submode == UpCaseSubMode && input.is('U'))) { + if (!isFirstNonBlankOnLine(position())) { + moveToStartOfLine(); + moveToFirstNonBlankOnLine(); + } + setTargetColumn(); + pushUndoState(); + setAnchor(); + setPosition(lastPositionInLine(cursorLine() + count()) + 1); + finishMovement(QString::fromLatin1("%1%2").arg(count()).arg(input.raw())); + handled = true; + g.submode = NoSubMode; + } + return handled; +} + +bool FakeVimHandler::Private::handleWindowSubMode(const Input &input) +{ + if (handleCount(input)) + return true; + + leaveVisualMode(); + emit q->windowCommandRequested(input.toString(), count()); + + g.submode = NoSubMode; + return true; +} + +bool FakeVimHandler::Private::handleYankSubMode(const Input &input) +{ + bool handled = false; + if (input.is('y')) { + g.movetype = MoveLineWise; + int endPos = firstPositionInLine(lineForPosition(position()) + count() - 1); + Range range(position(), endPos, RangeLineMode); + yankText(range, m_register); + g.submode = NoSubMode; + handled = true; + } + return handled; +} + +bool FakeVimHandler::Private::handleZSubMode(const Input &input) +{ + bool handled = true; + bool foldMaybeClosed = false; + if (input.isReturn() || input.is('t') + || input.is('-') || input.is('b') + || input.is('.') || input.is('z')) { + // Cursor line to top/center/bottom of window. + Qt::AlignmentFlag align; + if (input.isReturn() || input.is('t')) + align = Qt::AlignTop; + else if (input.is('.') || input.is('z')) + align = Qt::AlignVCenter; + else + align = Qt::AlignBottom; + const bool moveToNonBlank = (input.is('.') || input.isReturn() || input.is('-')); + const int line = g.mvcount == 0 ? -1 : firstPositionInLine(count()); + alignViewportToCursor(align, line, moveToNonBlank); + } else if (input.is('o') || input.is('c')) { + // Open/close current fold. + foldMaybeClosed = input.is('c'); + emit q->fold(count(), foldMaybeClosed); + } else if (input.is('O') || input.is('C')) { + // Recursively open/close current fold. + foldMaybeClosed = input.is('C'); + emit q->fold(-1, foldMaybeClosed); + } else if (input.is('a') || input.is('A')) { + // Toggle current fold. + foldMaybeClosed = true; + emit q->foldToggle(input.is('a') ? count() : -1); + } else if (input.is('R') || input.is('M')) { + // Open/close all folds in document. + foldMaybeClosed = input.is('M'); + emit q->foldAll(foldMaybeClosed); + } else if (input.is('j') || input.is('k')) { + emit q->foldGoTo(input.is('j') ? count() : -count(), false); + } else { + handled = false; + } + if (foldMaybeClosed) + ensureCursorVisible(); + g.submode = NoSubMode; + return handled; +} + +bool FakeVimHandler::Private::handleCapitalZSubMode(const Input &input) +{ + // Recognize ZZ and ZQ as aliases for ":x" and ":q!". + bool handled = true; + if (input.is('Z')) + handleExCommand(QString(QLatin1Char('x'))); + else if (input.is('Q')) + handleExCommand(_("q!")); + else + handled = false; + g.submode = NoSubMode; + return handled; +} + +bool FakeVimHandler::Private::handleMacroRecordSubMode(const Input &input) +{ + g.submode = NoSubMode; + return startRecording(input); +} + +bool FakeVimHandler::Private::handleMacroExecuteSubMode(const Input &input) +{ + g.submode = NoSubMode; + + bool result = true; + int repeat = count(); + while (result && --repeat >= 0) + result = executeRegister(input.asChar().unicode()); + + return result; +} + +EventResult FakeVimHandler::Private::handleInsertOrReplaceMode(const Input &input) +{ + if (position() < m_buffer->insertState.pos1 || position() > m_buffer->insertState.pos2) { + commitInsertState(); + invalidateInsertState(); + } + + if (g.mode == InsertMode) + handleInsertMode(input); + else + handleReplaceMode(input); + + if (!m_textedit && !m_plaintextedit) + return EventHandled; + + if (!isInsertMode() || m_buffer->breakEditBlock + || position() < m_buffer->insertState.pos1 || position() > m_buffer->insertState.pos2) { + commitInsertState(); + invalidateInsertState(); + breakEditBlock(); + m_visualBlockInsert = NoneBlockInsertMode; + } else if (m_oldInternalPosition == position()) { + setTargetColumn(); + } + + updateMiniBuffer(); + + // We don't want fancy stuff in insert mode. + return EventHandled; +} + +void FakeVimHandler::Private::handleReplaceMode(const Input &input) +{ + if (input.isEscape()) { + commitInsertState(); + moveLeft(qMin(1, leftDist())); + enterCommandMode(); + g.dotCommand.append(m_buffer->lastInsertion + _("")); + } else if (input.isKey(Key_Left)) { + moveLeft(); + setTargetColumn(); + } else if (input.isKey(Key_Right)) { + moveRight(); + setTargetColumn(); + } else if (input.isKey(Key_Up)) { + moveUp(); + } else if (input.isKey(Key_Down)) { + moveDown(); + } else if (input.isKey(Key_Insert)) { + g.mode = InsertMode; + } else if (input.isControl('o')) { + enterCommandMode(ReplaceMode); + } else { + joinPreviousEditBlock(); + if (!atEndOfLine()) { + setAnchor(); + moveRight(); + removeText(currentRange()); + } + const QString text = input.text(); + setAnchor(); + insertText(text); + endEditBlock(); + } +} + +void FakeVimHandler::Private::finishInsertMode() +{ + bool newLineAfter = m_buffer->insertState.newLineAfter; + bool newLineBefore = m_buffer->insertState.newLineBefore; + + // Repeat insertion [count] times. + // One instance was already physically inserted while typing. + if (!m_buffer->breakEditBlock && isInsertStateValid()) { + commitInsertState(); + + QString text = m_buffer->lastInsertion; + const QString dotCommand = g.dotCommand; + const int repeat = count() - 1; + m_buffer->lastInsertion.clear(); + joinPreviousEditBlock(); + + if (newLineAfter) { + text.chop(1); + text.prepend(_("\n")); + } else if (newLineBefore) { + text.prepend(_("")); + } + + replay(text, repeat); + + if (m_visualBlockInsert != NoneBlockInsertMode && !text.contains(QLatin1Char('\n'))) { + const CursorPosition lastAnchor = markLessPosition(); + const CursorPosition lastPosition = markGreaterPosition(); + bool change = m_visualBlockInsert == ChangeBlockInsertMode; + const int insertColumn = (m_visualBlockInsert == InsertBlockInsertMode || change) + ? qMin(lastPosition.column, lastAnchor.column) + : qMax(lastPosition.column, lastAnchor.column) + 1; + + CursorPosition pos(lastAnchor.line, insertColumn); + + if (change) + pos.column = m_buffer->insertState.pos1 - document()->findBlock(m_buffer->insertState.pos1).position(); + + // Cursor position after block insert is on the first selected line, + // last selected column for 's' command, otherwise first selected column. + const int endColumn = change ? qMax(0, m_cursor.positionInBlock() - 1) + : qMin(lastPosition.column, lastAnchor.column); + + while (pos.line < lastPosition.line) { + ++pos.line; + setCursorPosition(&m_cursor, pos); + if (m_visualBlockInsert == AppendToEndOfLineBlockInsertMode) { + moveToEndOfLine(); + } else if (m_visualBlockInsert == AppendBlockInsertMode) { + // Prepend spaces if necessary. + int spaces = pos.column - m_cursor.positionInBlock(); + if (spaces > 0) { + setAnchor(); + m_cursor.insertText(QString(_(" ")).repeated(spaces)); + } + } else if (m_cursor.positionInBlock() != pos.column) { + continue; + } + replay(text, repeat + 1); + } + + setCursorPosition(CursorPosition(lastAnchor.line, endColumn)); + } else { + moveLeft(qMin(1, leftDist())); + } + + endEditBlock(); + breakEditBlock(); + + m_buffer->lastInsertion = text; + g.dotCommand = dotCommand; + } else { + moveLeft(qMin(1, leftDist())); + } + + if (newLineBefore || newLineAfter) + m_buffer->lastInsertion.remove(0, m_buffer->lastInsertion.indexOf(QLatin1Char('\n')) + 1); + g.dotCommand.append(m_buffer->lastInsertion + _("")); + + enterCommandMode(); + setTargetColumn(); +} + +void FakeVimHandler::Private::handleInsertMode(const Input &input) +{ + if (input.isEscape()) { + finishInsertMode(); + } else if (g.submode == CtrlVSubMode) { + if (g.subsubmode == NoSubSubMode) { + g.subsubmode = CtrlVUnicodeSubSubMode; + m_ctrlVAccumulator = 0; + if (input.is('x') || input.is('X')) { + // ^VXnn or ^Vxnn with 00 <= nn <= FF + // BMP Unicode codepoints ^Vunnnn with 0000 <= nnnn <= FFFF + // any Unicode codepoint ^VUnnnnnnnn with 00000000 <= nnnnnnnn <= 7FFFFFFF + // ^Vnnn with 000 <= nnn <= 255 + // ^VOnnn or ^Vonnn with 000 <= nnn <= 377 + m_ctrlVLength = 2; + m_ctrlVBase = 16; + } else if (input.is('O') || input.is('o')) { + m_ctrlVLength = 3; + m_ctrlVBase = 8; + } else if (input.is('u')) { + m_ctrlVLength = 4; + m_ctrlVBase = 16; + } else if (input.is('U')) { + m_ctrlVLength = 8; + m_ctrlVBase = 16; + } else if (input.isDigit()) { + bool ok; + m_ctrlVAccumulator = input.toInt(&ok, 10); + m_ctrlVLength = 2; + m_ctrlVBase = 10; + } else { + insertInInsertMode(input.raw()); + g.submode = NoSubMode; + g.subsubmode = NoSubSubMode; + } + } else { + bool ok; + int current = input.toInt(&ok, m_ctrlVBase); + if (ok) + m_ctrlVAccumulator = m_ctrlVAccumulator * m_ctrlVBase + current; + --m_ctrlVLength; + if (m_ctrlVLength == 0 || !ok) { + QString s; + if (QChar::requiresSurrogates(m_ctrlVAccumulator)) { + s.append(QChar(QChar::highSurrogate(m_ctrlVAccumulator))); + s.append(QChar(QChar::lowSurrogate(m_ctrlVAccumulator))); + } else { + s.append(QChar(m_ctrlVAccumulator)); + } + insertInInsertMode(s); + g.submode = NoSubMode; + g.subsubmode = NoSubSubMode; + + // Try again without Ctrl-V interpretation. + if (!ok) + handleInsertMode(input); + } + } + } else if (input.isControl('o')) { + enterCommandMode(InsertMode); + } else if (input.isControl('v')) { + g.submode = CtrlVSubMode; + g.subsubmode = NoSubSubMode; + } else if (input.isControl('w')) { + const int blockNumber = m_cursor.blockNumber(); + const int endPos = position(); + moveToNextWordStart(1, false, false); + if (blockNumber != m_cursor.blockNumber()) + moveToEndOfLine(); + const int beginPos = position(); + Range range(beginPos, endPos, RangeCharMode); + removeText(range); + } else if (input.isKey(Key_Insert)) { + g.mode = ReplaceMode; + } else if (input.isKey(Key_Left)) { + moveLeft(); + setTargetColumn(); + } else if (input.isControl(Key_Left)) { + moveToNextWordStart(1, false, false); + setTargetColumn(); + } else if (input.isKey(Key_Down)) { + g.submode = NoSubMode; + moveDown(); + } else if (input.isKey(Key_Up)) { + g.submode = NoSubMode; + moveUp(); + } else if (input.isKey(Key_Right)) { + moveRight(); + setTargetColumn(); + } else if (input.isControl(Key_Right)) { + moveToNextWordStart(1, false, true); + moveRight(); // we need one more move since we are in insert mode + setTargetColumn(); + } else if (input.isKey(Key_Home)) { + moveToStartOfLine(); + setTargetColumn(); + } else if (input.isKey(Key_End)) { + moveBehindEndOfLine(); + setTargetColumn(); + m_targetColumn = -1; + } else if (input.isReturn() || input.isControl('j') || input.isControl('m')) { + if (!input.isReturn() || !handleInsertInEditor(input)) { + joinPreviousEditBlock(); + g.submode = NoSubMode; + insertNewLine(); + endEditBlock(); + } + } else if (input.isBackspace()) { + if (!handleInsertInEditor(input)) { + joinPreviousEditBlock(); + if (!m_buffer->lastInsertion.isEmpty() + || hasConfig(ConfigBackspace, "start") + || hasConfig(ConfigBackspace, "2")) { + const int line = cursorLine() + 1; + const Column col = cursorColumn(); + QString data = lineContents(line); + const Column ind = indentation(data); + if (col.logical <= ind.logical && col.logical + && startsWithWhitespace(data, col.physical)) { + const int ts = config(ConfigTabStop).toInt(); + const int newl = col.logical - 1 - (col.logical - 1) % ts; + const QString prefix = tabExpand(newl); + setLineContents(line, prefix + data.mid(col.physical)); + moveToStartOfLine(); + moveRight(prefix.size()); + } else { + setAnchor(); + m_cursor.deletePreviousChar(); + } + } + endEditBlock(); + } + } else if (input.isKey(Key_Delete)) { + if (!handleInsertInEditor(input)) { + joinPreviousEditBlock(); + m_cursor.deleteChar(); + endEditBlock(); + } + } else if (input.isKey(Key_PageDown) || input.isControl('f')) { + movePageDown(); + } else if (input.isKey(Key_PageUp) || input.isControl('b')) { + movePageUp(); + } else if (input.isKey(Key_Tab)) { + m_buffer->insertState.insertingSpaces = true; + if (hasConfig(ConfigExpandTab)) { + const int ts = config(ConfigTabStop).toInt(); + const int col = logicalCursorColumn(); + QString str = QString(ts - col % ts, QLatin1Char(' ')); + insertText(str); + } else { + insertInInsertMode(input.raw()); + } + m_buffer->insertState.insertingSpaces = false; + } else if (input.isControl('d')) { + // remove one level of indentation from the current line + int shift = config(ConfigShiftWidth).toInt(); + int tab = config(ConfigTabStop).toInt(); + int line = cursorLine() + 1; + int pos = firstPositionInLine(line); + QString text = lineContents(line); + int amount = 0; + int i = 0; + for (; i < text.size() && amount < shift; ++i) { + if (text.at(i) == QLatin1Char(' ')) + ++amount; + else if (text.at(i) == QLatin1Char('\t')) + amount += tab; // FIXME: take position into consideration + else + break; + } + removeText(Range(pos, pos+i)); + } else if (input.isControl('p') || input.isControl('n')) { + QTextCursor tc = m_cursor; + moveToNextWordStart(1, false, false); + QString str = selectText(Range(position(), tc.position())); + m_cursor = tc; + emit q->simpleCompletionRequested(str, input.isControl('n')); + } else if (input.isShift(Qt::Key_Insert)) { + // Insert text from clipboard. + QClipboard *clipboard = QApplication::clipboard(); + const QMimeData *data = clipboard->mimeData(); + if (data && data->hasText()) + insertInInsertMode(data->text()); + } else { + m_buffer->insertState.insertingSpaces = input.isKey(Key_Space); + if (!handleInsertInEditor(input)) { + const QString toInsert = input.text(); + if (toInsert.isEmpty()) + return; + insertInInsertMode(toInsert); + } + m_buffer->insertState.insertingSpaces = false; + } +} + +void FakeVimHandler::Private::insertInInsertMode(const QString &text) +{ + joinPreviousEditBlock(); + insertText(text); + if (hasConfig(ConfigSmartIndent) && isElectricCharacter(text.at(0))) { + const QString leftText = block().text() + .left(position() - 1 - block().position()); + if (leftText.simplified().isEmpty()) { + Range range(position(), position(), g.rangemode); + indentText(range, text.at(0)); + } + } + setTargetColumn(); + endEditBlock(); + g.submode = NoSubMode; +} + +bool FakeVimHandler::Private::startRecording(const Input &input) +{ + QChar reg = input.asChar(); + if (reg == QLatin1Char('"') || reg.isLetterOrNumber()) { + g.currentRegister = reg.unicode(); + g.recording = QLatin1String(""); + return true; + } + + return false; +} + +void FakeVimHandler::Private::record(const Input &input) +{ + if ( !g.recording.isNull() ) + g.recording.append(input.toString()); +} + +void FakeVimHandler::Private::stopRecording() +{ + // Remove q from end (stop recording command). + g.recording.remove(g.recording.size() - 1, 1); + setRegister(g.currentRegister, g.recording, g.rangemode); + g.currentRegister = 0; + g.recording = QString(); +} + +bool FakeVimHandler::Private::executeRegister(int reg) +{ + QChar regChar(reg); + + // TODO: Prompt for an expression to execute if register is '='. + if (reg == '@' && g.lastExecutedRegister != 0) + reg = g.lastExecutedRegister; + else if (QString::fromLatin1("\".*+").contains(regChar) || regChar.isLetterOrNumber()) + g.lastExecutedRegister = reg; + else + return false; + + // FIXME: In Vim it's possible to interrupt recursive macro with . + // One solution may be to call QApplication::processEvents() and check if was + // used when a mapping is active. + // According to Vim, register is executed like mapping. + prependMapping(Inputs(registerContents(reg), false, false)); + + return true; +} + +EventResult FakeVimHandler::Private::handleExMode(const Input &input) +{ + if (input.isEscape()) { + g.commandBuffer.clear(); + resetCommandMode(); + g.submode = NoSubMode; + } else if (g.submode == CtrlVSubMode) { + g.commandBuffer.insertChar(input.raw()); + g.submode = NoSubMode; + } else if (input.isControl('v')) { + g.submode = CtrlVSubMode; + g.subsubmode = NoSubSubMode; + return EventHandled; + } else if (input.isBackspace()) { + if (g.commandBuffer.isEmpty()) { + leaveVisualMode(); + resetCommandMode(); + } else if (g.commandBuffer.hasSelection()) { + g.commandBuffer.deleteSelected(); + } else { + g.commandBuffer.deleteChar(); + } + } else if (input.isKey(Key_Tab)) { + // FIXME: Complete actual commands. + g.commandBuffer.historyUp(); + } else if (input.isReturn()) { + showMessage(MessageCommand, g.commandBuffer.display()); + handleExCommand(g.commandBuffer.contents()); + g.commandBuffer.clear(); + if (m_textedit || m_plaintextedit) + leaveVisualMode(); + } else if (!g.commandBuffer.handleInput(input)) { + qDebug() << "IGNORED IN EX-MODE: " << input.key() << input.text(); + return EventUnhandled; + } + updateMiniBuffer(); + return EventHandled; +} + +EventResult FakeVimHandler::Private::handleSearchSubSubMode(const Input &input) +{ + EventResult handled = EventHandled; + + if (input.isEscape()) { + g.currentMessage.clear(); + setPosition(m_searchStartPosition); + scrollToLine(m_searchFromScreenLine); + } else if (input.isBackspace()) { + if (g.searchBuffer.isEmpty()) + resetCommandMode(); + else + g.searchBuffer.deleteChar(); + } else if (input.isReturn()) { + const QString &needle = g.searchBuffer.contents(); + if (!needle.isEmpty()) + g.lastSearch = needle; + else + g.searchBuffer.setContents(g.lastSearch); + + updateFind(true); + + if (finishSearch()) { + if (g.submode != NoSubMode) + finishMovement(g.searchBuffer.prompt() + g.lastSearch + QLatin1Char('\n')); + if (g.currentMessage.isEmpty()) + showMessage(MessageCommand, g.searchBuffer.display()); + } else { + handled = EventCancelled; // Not found so cancel mapping if any. + } + } else if (input.isKey(Key_Tab)) { + g.searchBuffer.insertChar(QChar(9)); + } else if (!g.searchBuffer.handleInput(input)) { + //qDebug() << "IGNORED IN SEARCH MODE: " << input.key() << input.text(); + return EventUnhandled; + } + + if (input.isReturn() || input.isEscape()) { + g.searchBuffer.clear(); + resetCommandMode(); + updateMiniBuffer(); + } else { + updateMiniBuffer(); + updateFind(false); + } + + return handled; +} + +// This uses 0 based line counting (hidden lines included). +int FakeVimHandler::Private::parseLineAddress(QString *cmd) +{ + //qDebug() << "CMD: " << cmd; + if (cmd->isEmpty()) + return -1; + + int result = -1; + QChar c = cmd->at(0); + if (c == QLatin1Char('.')) { // current line + result = cursorBlockNumber(); + cmd->remove(0, 1); + } else if (c == QLatin1Char('$')) { // last line + result = document()->blockCount() - 1; + cmd->remove(0, 1); + } else if (c == QLatin1Char('\'')) { // mark + cmd->remove(0, 1); + if (cmd->isEmpty()) { + showMessage(MessageError, msgMarkNotSet(QString())); + return -1; + } + c = cmd->at(0); + Mark m = mark(c); + if (!m.isValid() || !m.isLocal(m_currentFileName)) { + showMessage(MessageError, msgMarkNotSet(c)); + return -1; + } + cmd->remove(0, 1); + result = m.position(document()).line; + } else if (c.isDigit()) { // line with given number + result = 0; + } else if (c == QLatin1Char('-') || c == QLatin1Char('+')) { // add or subtract from current line number + result = cursorBlockNumber(); + } else if (c == QLatin1Char('/') || c == QLatin1Char('?') + || (c == QLatin1Char('\\') && cmd->size() > 1 && QString::fromLatin1("/?&").contains(cmd->at(1)))) { + // search for expression + SearchData sd; + if (c == QLatin1Char('/') || c == QLatin1Char('?')) { + const int end = findUnescaped(c, *cmd, 1); + if (end == -1) + return -1; + sd.needle = cmd->mid(1, end - 1); + cmd->remove(0, end + 1); + } else { + c = cmd->at(1); + cmd->remove(0, 2); + sd.needle = (c == QLatin1Char('&')) ? g.lastSubstitutePattern : g.lastSearch; + } + sd.forward = (c != QLatin1Char('?')); + const QTextBlock b = block(); + const int pos = b.position() + (sd.forward ? b.length() - 1 : 0); + QTextCursor tc = search(sd, pos, 1, true); + g.lastSearch = sd.needle; + if (tc.isNull()) + return -1; + result = tc.block().blockNumber(); + } else { + return cursorBlockNumber(); + } + + // basic arithmetic ("-3+5" or "++" means "+2" etc.) + int n = 0; + bool add = true; + int i = 0; + for (; i < cmd->size(); ++i) { + c = cmd->at(i); + if (c == QLatin1Char('-') || c == QLatin1Char('+')) { + if (n != 0) + result = result + (add ? n - 1 : -(n - 1)); + add = c == QLatin1Char('+'); + result = result + (add ? 1 : -1); + n = 0; + } else if (c.isDigit()) { + n = n * 10 + c.digitValue(); + } else if (!c.isSpace()) { + break; + } + } + if (n != 0) + result = result + (add ? n - 1 : -(n - 1)); + *cmd = cmd->mid(i).trimmed(); + + return result; +} + +void FakeVimHandler::Private::setCurrentRange(const Range &range) +{ + setAnchorAndPosition(range.beginPos, range.endPos); + g.rangemode = range.rangemode; +} + +bool FakeVimHandler::Private::parseExCommmand(QString *line, ExCommand *cmd) +{ + *cmd = ExCommand(); + if (line->isEmpty()) + return false; + + // parse range first + if (!parseLineRange(line, cmd)) + return false; + + // get first command from command line + QChar close; + bool subst = false; + int i = 0; + for (; i < line->size(); ++i) { + const QChar &c = line->at(i); + if (c == QLatin1Char('\\')) { + ++i; // skip escaped character + } else if (close.isNull()) { + if (c == QLatin1Char('|')) { + // split on | + break; + } else if (c == QLatin1Char('/')) { + subst = i > 0 && (line->at(i - 1) == QLatin1Char('s')); + close = c; + } else if (c == QLatin1Char('"') || c == QLatin1Char('\'')) { + close = c; + } + } else if (c == close) { + if (subst) + subst = false; + else + close = QChar(); + } + } + + cmd->cmd = line->mid(0, i).trimmed(); + + // command arguments starts with first non-letter character + cmd->args = cmd->cmd.section(QRegExp(_("(?=[^a-zA-Z])")), 1); + if (!cmd->args.isEmpty()) { + cmd->cmd.chop(cmd->args.size()); + cmd->args = cmd->args.trimmed(); + + // '!' at the end of command + cmd->hasBang = cmd->args.startsWith(QLatin1Char('!')); + if (cmd->hasBang) + cmd->args = cmd->args.mid(1).trimmed(); + } + + // remove the first command from command line + line->remove(0, i + 1); + + return true; +} + +bool FakeVimHandler::Private::parseLineRange(QString *line, ExCommand *cmd) +{ + // remove leading colons and spaces + line->remove(QRegExp(_("^\\s*(:+\\s*)*"))); + + // special case ':!...' (use invalid range) + if (line->startsWith(QLatin1Char('!'))) { + cmd->range = Range(); + return true; + } + + // FIXME: that seems to be different for %w and %s + if (line->startsWith(QLatin1Char('%'))) + line->replace(0, 1, _("1,$")); + + int beginLine = parseLineAddress(line); + int endLine; + if (line->startsWith(QLatin1Char(','))) { + *line = line->mid(1).trimmed(); + endLine = parseLineAddress(line); + } else { + endLine = beginLine; + } + if (beginLine == -1 || endLine == -1) + return false; + + const int beginPos = firstPositionInLine(qMin(beginLine, endLine) + 1, false); + const int endPos = lastPositionInLine(qMax(beginLine, endLine) + 1, false); + cmd->range = Range(beginPos, endPos, RangeLineMode); + cmd->count = beginLine; + + return true; +} + +void FakeVimHandler::Private::parseRangeCount(const QString &line, Range *range) const +{ + bool ok; + const int count = qAbs(line.trimmed().toInt(&ok)); + if (ok) { + const int beginLine = document()->findBlock(range->endPos).blockNumber() + 1; + const int endLine = qMin(beginLine + count - 1, document()->blockCount()); + range->beginPos = firstPositionInLine(beginLine, false); + range->endPos = lastPositionInLine(endLine, false); + } +} + +// use handleExCommand for invoking commands that might move the cursor +void FakeVimHandler::Private::handleCommand(const QString &cmd) +{ + handleExCommand(cmd); +} + +bool FakeVimHandler::Private::handleExSubstituteCommand(const ExCommand &cmd) +{ + // :substitute + if (!cmd.matches(_("s"), _("substitute")) + && !(cmd.cmd.isEmpty() && !cmd.args.isEmpty() && QString::fromLatin1("&~").contains(cmd.args[0]))) { + return false; + } + + int count = 1; + QString line = cmd.args; + const int countIndex = line.lastIndexOf(QRegExp(_("\\d+$"))); + if (countIndex != -1) { + count = line.mid(countIndex).toInt(); + line = line.mid(0, countIndex).trimmed(); + } + + if (cmd.cmd.isEmpty()) { + // keep previous substitution flags on '&&' and '~&' + if (line.size() > 1 && line[1] == QLatin1Char('&')) + g.lastSubstituteFlags += line.mid(2); + else + g.lastSubstituteFlags = line.mid(1); + if (line[0] == QLatin1Char('~')) + g.lastSubstitutePattern = g.lastSearch; + } else { + if (line.isEmpty()) { + g.lastSubstituteFlags.clear(); + } else { + // we have /{pattern}/{string}/[flags] now + const QChar separator = line.at(0); + int pos1 = findUnescaped(separator, line, 1); + if (pos1 == -1) + return false; + int pos2 = findUnescaped(separator, line, pos1 + 1); + if (pos2 == -1) + pos2 = line.size(); + + g.lastSubstitutePattern = line.mid(1, pos1 - 1); + g.lastSubstituteReplacement = line.mid(pos1 + 1, pos2 - pos1 - 1); + g.lastSubstituteFlags = line.mid(pos2 + 1); + } + } + + count = qMax(1, count); + QString needle = g.lastSubstitutePattern; + + if (g.lastSubstituteFlags.contains(QLatin1Char('i'))) + needle.prepend(_("\\c")); + + QRegExp pattern = vimPatternToQtPattern(needle, hasConfig(ConfigIgnoreCase), + hasConfig(ConfigSmartCase)); + + QTextBlock lastBlock; + QTextBlock firstBlock; + const bool global = g.lastSubstituteFlags.contains(QLatin1Char('g')); + for (int a = 0; a != count; ++a) { + for (QTextBlock block = document()->findBlock(cmd.range.endPos); + block.isValid() && block.position() + block.length() > cmd.range.beginPos; + block = block.previous()) { + QString text = block.text(); + if (substituteText(&text, pattern, g.lastSubstituteReplacement, global)) { + firstBlock = block; + if (!lastBlock.isValid()) { + lastBlock = block; + beginEditBlock(); + } + QTextCursor tc = m_cursor; + const int pos = block.position(); + const int anchor = pos + block.length() - 1; + tc.setPosition(anchor); + tc.setPosition(pos, KeepAnchor); + tc.insertText(text); + } + } + } + + if (lastBlock.isValid()) { + m_buffer->undoState.position = CursorPosition(firstBlock.blockNumber(), 0); + + leaveVisualMode(); + setPosition(lastBlock.position()); + setAnchor(); + moveToFirstNonBlankOnLine(); + setTargetColumn(); + + endEditBlock(); + } + + return true; +} + +bool FakeVimHandler::Private::handleExMapCommand(const ExCommand &cmd0) // :map +{ + QByteArray modes; + enum Type { Map, Noremap, Unmap } type; + + QByteArray cmd = cmd0.cmd.toLatin1(); + + // Strange formatting. But everything else is even uglier. + if (cmd == "map") { modes = "nvo"; type = Map; } else + if (cmd == "nm" || cmd == "nmap") { modes = "n"; type = Map; } else + if (cmd == "vm" || cmd == "vmap") { modes = "v"; type = Map; } else + if (cmd == "xm" || cmd == "xmap") { modes = "x"; type = Map; } else + if (cmd == "smap") { modes = "s"; type = Map; } else + if (cmd == "omap") { modes = "o"; type = Map; } else + if (cmd == "map!") { modes = "ic"; type = Map; } else + if (cmd == "im" || cmd == "imap") { modes = "i"; type = Map; } else + if (cmd == "lm" || cmd == "lmap") { modes = "l"; type = Map; } else + if (cmd == "cm" || cmd == "cmap") { modes = "c"; type = Map; } else + + if (cmd == "no" || cmd == "noremap") { modes = "nvo"; type = Noremap; } else + if (cmd == "nn" || cmd == "nnoremap") { modes = "n"; type = Noremap; } else + if (cmd == "vn" || cmd == "vnoremap") { modes = "v"; type = Noremap; } else + if (cmd == "xn" || cmd == "xnoremap") { modes = "x"; type = Noremap; } else + if (cmd == "snor" || cmd == "snoremap") { modes = "s"; type = Noremap; } else + if (cmd == "ono" || cmd == "onoremap") { modes = "o"; type = Noremap; } else + if (cmd == "no!" || cmd == "noremap!") { modes = "ic"; type = Noremap; } else + if (cmd == "ino" || cmd == "inoremap") { modes = "i"; type = Noremap; } else + if (cmd == "ln" || cmd == "lnoremap") { modes = "l"; type = Noremap; } else + if (cmd == "cno" || cmd == "cnoremap") { modes = "c"; type = Noremap; } else + + if (cmd == "unm" || cmd == "unmap") { modes = "nvo"; type = Unmap; } else + if (cmd == "nun" || cmd == "nunmap") { modes = "n"; type = Unmap; } else + if (cmd == "vu" || cmd == "vunmap") { modes = "v"; type = Unmap; } else + if (cmd == "xu" || cmd == "xunmap") { modes = "x"; type = Unmap; } else + if (cmd == "sunm" || cmd == "sunmap") { modes = "s"; type = Unmap; } else + if (cmd == "ou" || cmd == "ounmap") { modes = "o"; type = Unmap; } else + if (cmd == "unm!" || cmd == "unmap!") { modes = "ic"; type = Unmap; } else + if (cmd == "iu" || cmd == "iunmap") { modes = "i"; type = Unmap; } else + if (cmd == "lu" || cmd == "lunmap") { modes = "l"; type = Unmap; } else + if (cmd == "cu" || cmd == "cunmap") { modes = "c"; type = Unmap; } + + else + return false; + + QString args = cmd0.args; + bool silent = false; + bool unique = false; + forever { + if (eatString("", &args)) { + silent = true; + } else if (eatString("", &args)) { + continue; + } else if (eatString("", &args)) { + continue; + } else if (eatString("", &args)) { + notImplementedYet(); + continue; + } else if (eatString("