Browse Source

Merge pull request #44 from eidheim/master

Some fixes, but most notably the directory window is now a properish file manager
merge-requests/365/head
Jørgen Lien Sellæg 10 years ago
parent
commit
37e5860ca9
  1. 9
      CMakeLists.txt
  2. 15
      MINGW-packages/mingw-w64-gtksourceview3/0006-hack-convert-path-back-to-unix.patch
  3. 27
      MINGW-packages/mingw-w64-gtksourceview3/LICENSE
  4. 64
      MINGW-packages/mingw-w64-gtksourceview3/PKGBUILD
  5. 27
      MINGW-packages/mingw-w64-gtksourceviewmm3/LICENSE
  6. 33
      MINGW-packages/mingw-w64-gtksourceviewmm3/PKGBUILD
  7. 38
      README.md
  8. 94
      docs/install.md
  9. 68
      src/CMakeLists.txt
  10. 20
      src/cmake.cc
  11. 3
      src/cmake/Modules/FindLibClang.cmake
  12. 8
      src/cmake/Modules/FindLibClangmm.cmake
  13. 20
      src/config.cc
  14. 248
      src/directories.cc
  15. 24
      src/directories.h
  16. 33
      src/files.h
  17. 2
      src/juci.cc
  18. 3
      src/notebook.cc
  19. 4
      src/selectiondialog.cc
  20. 2
      src/selectiondialog.h
  21. 640
      src/source.cc
  22. 53
      src/source.h
  23. 27
      src/sourcefile.cc
  24. 136
      src/terminal.cc
  25. 6
      src/terminal.h
  26. 418
      src/terminal_win.cc
  27. 4
      src/tooltips.h
  28. 167
      src/window.cc
  29. 4
      src/window.h

9
CMakeLists.txt

@ -1,13 +1,10 @@
cmake_minimum_required (VERSION 2.8.4)
set(project_name juci)
set(module juci_to_python_api)
#set(module juci_to_python_api)
#### TODO WINDOWS SUPPORT ####
set(bin_install_path "/usr/local/bin")
set(lib_install_path "/usr/local/lib/python2.7/dist-packages/")
#####
#set(lib_install_path "/usr/local/lib/python2.7/dist-packages/")
project (${project_name})
add_subdirectory("src")
add_subdirectory("src")

15
MINGW-packages/mingw-w64-gtksourceview3/0006-hack-convert-path-back-to-unix.patch

@ -0,0 +1,15 @@
--- gtksourceview-3.13.90/configure.ac.orig 2014-08-22 00:42:28.532000000 +0400
+++ gtksourceview-3.13.90/configure.ac 2014-08-22 00:43:00.621200000 +0400
@@ -133,6 +133,12 @@
AC_MSG_RESULT([$GLADE_CATALOG_DIR])
AC_SUBST(GLADE_CATALOG_DIR)])
+case "$host" in
+ *-*-mingw*)
+ GLADE_CATALOG_DIR=`cygpath -u $GLADE_CATALOG_DIR`
+ ;;
+esac
+
# i18N stuff
IT_PROG_INTLTOOL([0.40])
AS_IF([test "$USE_NLS" = "yes"],

27
MINGW-packages/mingw-w64-gtksourceview3/LICENSE

@ -0,0 +1,27 @@
Copyright (c) 2013, Алексей
All rights reserved.
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 {organization} 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 THE COPYRIGHT HOLDER OR CONTRIBUTORS 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.

64
MINGW-packages/mingw-w64-gtksourceview3/PKGBUILD

@ -0,0 +1,64 @@
# Maintainer: Alexey Pavlov <alexpux@gmail.com>
_realname=gtksourceview
pkgname="${MINGW_PACKAGE_PREFIX}-${_realname}3"
pkgver=3.12.0
pkgrel=2
pkgdesc="A text widget adding syntax highlighting and more to GNOME (mingw-w64)"
arch=('any')
url="http://www.gnome.org"
license=("LGPL")
makedepends=("${MINGW_PACKAGE_PREFIX}-gcc"
"${MINGW_PACKAGE_PREFIX}-pkg-config"
"${MINGW_PACKAGE_PREFIX}-gobject-introspection"
"${MINGW_PACKAGE_PREFIX}-glade"
"${MINGW_PACKAGE_PREFIX}-vala"
"intltool"
"gtk-doc")
depends=("${MINGW_PACKAGE_PREFIX}-gtk3"
"${MINGW_PACKAGE_PREFIX}-libxml2")
options=(!libtool strip staticlibs)
source=("http://ftp.gnome.org/pub/gnome/sources/gtksourceview/${pkgver%.*}/gtksourceview-${pkgver}.tar.xz"
0006-hack-convert-path-back-to-unix.patch)
md5sums=('8850fc0aee4893668ede37a30ef05e85'
'324c9e3bb2e4fa2a4977653ce6ed6ef9')
prepare() {
cd ${_realname}-${pkgver}
patch -p1 -i ${srcdir}/0006-hack-convert-path-back-to-unix.patch
autoreconf -fi
}
build() {
mkdir -p "${srcdir}/build-${MINGW_CHOST}"
cd "${srcdir}/build-${MINGW_CHOST}"
mkdir -p docs/reference/html
cp -rf ../${_realname}-${pkgver}/docs/reference/html/* docs/reference/html
DATADIRNAME=share \
../${_realname}-${pkgver}/configure \
--prefix=${MINGW_PREFIX} \
--build=${MINGW_CHOST} \
--host=${MINGW_CHOST} \
--enable-shared \
--disable-static \
--enable-glade-catalog
LC_ALL=C make
}
package() {
cd "${srcdir}/build-${MINGW_CHOST}"
make -j1 DESTDIR="${pkgdir}" install
for ff in ${pkgdir}/${MINGW_PREFIX}/bin/libgtksourceview*.dll; do
local _reallib=$(basename $ff)
_reallib=${_reallib%.dll}
_reallib=${_reallib#lib}
sed -e "s|library=\"gtksourceview-3.0\"|library=\"${_reallib}\"|g" -i ${pkgdir}/${MINGW_PREFIX}/share/glade/catalogs/gtksourceview.xml
done
install -Dm644 "${srcdir}/${_realname}-${pkgver}/COPYING" "${pkgdir}${MINGW_PREFIX}/share/licenses/${_realname}/COPYING"
}

27
MINGW-packages/mingw-w64-gtksourceviewmm3/LICENSE

@ -0,0 +1,27 @@
Copyright (c) 2013, Алексей
All rights reserved.
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 {organization} 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 THE COPYRIGHT HOLDER OR CONTRIBUTORS 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.

33
MINGW-packages/mingw-w64-gtksourceviewmm3/PKGBUILD

@ -0,0 +1,33 @@
_realname=gtksourceviewmm3
pkgname="${MINGW_PACKAGE_PREFIX}-${_realname}"
pkgver=3.12.0
pkgrel=1
pkgdesc="C++ bindings for gtksourceview3 (mingw-w64)"
arch=('any')
url="https://developer.gnome.org/gtksourceviewmm/"
license=("LGPL")
makedepends=("patch" "${MINGW_PACKAGE_PREFIX}-gcc" "${MINGW_PACKAGE_PREFIX}-pkg-config")
depends=("${MINGW_PACKAGE_PREFIX}-gtksourceview3=${pkgver}")
options=('staticlibs' 'strip')
source=("https://download.gnome.org/sources/gtksourceviewmm/${pkgver%.*}/gtksourceviewmm-${pkgver}.tar.xz")
sha256sums=("73939031bcc60e6ad31a315ec84b132deba15e5732de16e75fe424a619267ace")
build() {
mkdir -p "${srcdir}/build-${MINGW_CHOST}"
cd "${srcdir}/build-${MINGW_CHOST}"
../gtksourceviewmm-${pkgver}/configure \
CXXFLAGS=-std=c++11 \
--prefix=${MINGW_PREFIX} \
--build=${MINGW_CHOST} \
--host=${MINGW_CHOST} \
--enable-shared \
--enable-static \
--disable-documentation
make
}
package() {
cd "${srcdir}/build-${MINGW_CHOST}"
make DESTDIR="${pkgdir}" install
find "${pkgdir}${MINGW_PREFIX}" -name '*.def' -o -name '*.exp' | xargs -rtl1 rm
}

38
README.md

@ -1,21 +1,23 @@
# juCi++
###### a lightweight C++-IDE with support for C++11 and C++14.
## About
A lot of IDEs struggle with good C++11/14 support. Therefore we
created juCi++. juCi++ is based of the compiler and will *always*
support new versions of C++.
Current IDEs struggle with C++ support due to the complexity of
the programming language. juCI++, however, is designed especially
towards libclang with speed in mind.
## Features
* Syntax highlighing (even C++11/14)
* Superfast autocomletion (even external libraries)
* Accurate refactoring across files
* Basic editor functionallity
* Fast and responsive
* Syntax highlighing (even C++11/14, and more than 100 other file types)
* C++ warnings and errors on the fly
* Fast C++ autocomletion (even external libraries)
* Tooltips showing type information and doxygen documentation
* Refactoring across files
* Highlighting of similar types
* write your own plugins in python (limited atm)
* Spell checking depending on file context
* Basic editor functionallity
* Write your own plugins in python (disabled at the moment)
## Dependencies ##
Please install these dependencies on your system.
* libboost-python-dev
* libboost-filesystem-dev
* libboost-log-dev
* libboost-test-dev
@ -23,20 +25,10 @@ Please install these dependencies on your system.
* libboost-system-dev
* libgtkmm-3.0-dev
* libgtksourceview2.0-dev
* libgtksourceviewmm-3.0-dev
* libpython-dev
* libgtksourceviewmm-3.0-dev
* libaspell-dev
* libclang-dev
* [libclangmm](http://github.com/cppit/libclangmm/)
* cmake
* make
* clang or gcc (compiler)
## Installation ##
Quickstart:
```sh
$ cmake .
$ make
$ sudo make install
```
See [installation guide](http://github.com/cppit/jucipp/blob/master/docs/install.md) for more details.
See [installation guide](http://github.com/cppit/jucipp/blob/master/docs/install.md).

94
docs/install.md

@ -1,22 +1,90 @@
# juCi++
## Installation guide ##
Before installation, please install libclangmm see [installation guide](http://github.com/cppit/libclangmm/blob/master/docs/install.md) for installation.
## Debian
First dependencies:
Before installation, please install libclangmm, see [installation guide](http://github.com/cppit/libclangmm/blob/master/docs/install.md).
## Debian/Ubuntu
```sh
$ sudo apt-get install libboost-python-dev libboost-filesystem-dev libboost-log-dev libboost-test-dev
libboost-thread-dev libboost-system-dev libgtkmm-3.0-dev libgtksourceview2.0-dev libgtksourceviewmm-3.0-dev
libpython-dev libclang-dev make cmake gcc g++
sudo apt-get install pkg-config libboost-system-dev libboost-thread-dev libboost-filesystem-dev libboost-log-dev libgtkmm-3.0-dev libgtksourceviewmm-3.0-dev aspell-en libaspell-dev
```
Install the project:
```sh
$ git clone http://github.com/cppit/jucipp.git juci
$ cd juci
$ cmake .
$ make
$ sudo make install
git clone http://github.com/cppit/jucipp.git
cd jucipp
cmake .
make
sudo make install
```
## OS X with Homebrew (http://brew.sh/)
```sh
brew install pkg-config boost gtkmm3 gtksourceviewmm3 aspell
```
```sh
git clone https://github.com/cppit/jucipp.git
cd jucipp
cmake .
make
make install
```
##Windows with MSYS2 (https://msys2.github.io/)
Install dependencies(replace [arch] with i686 or x86_64 depending on your MSYS2 install):
```sh
pacman -S patch autoconf automake-wrapper mingw-w64-[arch]-gtkmm3 mingw-w64-[arch]-boost mingw-w64-[arch]-aspell mingw-w64-[arch]-aspell-en
```
Get juCi++ source:
```sh
git clone https://github.com/cppit/jucipp.git
cd jucipp
```
Compiling and installing gtksourceview3 and gtksourceviewmm3:
```sh
cd MINGW-packages/mingw-w64-gtksourceview3/
makepkg-mingw -sLf
pacman -U mingw-w64-[arch]-gtksourceview3-3.12.0-2-any.pkg.tar.xz
cd ../mingw-w64-gtksourceviewmm3/
makepkg-mingw -sLf
pacman -U mingw-w64-[arch]-gtksourceviewmm3-3.12.0-1-any.pkg.tar.xz
cd ../../
```
Compile and install juCi++ source:
```sh
cmake -G"MSYS Makefiles" -DCMAKE_INSTALL_PREFIX=/mingw[32 or 64] .
make
make install
```
<!--
## Windows with Cygwin (https://www.cygwin.com/)
**Make sure the PATH environment variable does not include paths to non-Cygwin cmake, make and g++.**
Select and install the following packages from the Cygwin-installer:
```
pkg-config libboost-devel libgtkmm3.0-devel libgtksourceviewmm3.0-devel xinit
```
Then run the following in the Cygwin Terminal:
```sh
git clone https://github.com/cppit/jucipp.git
cd jucipp
cmake .
make
make install
```
Note that we are currently working on a Windows-version without the need of an X-server.
-->
## Run
```sh
$ juci
juci
```
<!--
#### Windows
```sh
startxwin /usr/local/bin/juci
```
-->

68
src/CMakeLists.txt

@ -7,6 +7,7 @@ if(APPLE)
set(CMAKE_MACOSX_RPATH 1)
set(ENV{PKG_CONFIG_PATH} "$ENV{PKG_CONFIG_PATH}:/usr/local/lib/pkgconfig:/opt/X11/lib/pkgconfig")
endif()
INCLUDE(FindPkgConfig)
set(validation true)
@ -21,7 +22,7 @@ function(install_help APPLE UNIX WINDOWS)
endif(APPLE)
endif(UNIX)
if(WINDOWS)
message("choco install ${WINDOWS}")
#message("choco install ${WINDOWS}") #Removed this for the time being
endif(WINDOWS)
endfunction(install_help)
@ -38,10 +39,16 @@ validate(${LCL_FOUND} "clangmm" "clangmm" "clangmm")
find_package(LibClang)
validate(${LIBCLANG_FOUND} "clang" "libclang-dev" "llvm")
find_package(PythonLibs 2.7)
validate(${PYTHONLIBS_FOUND} "python" "libpython-dev" "python")
#TODO: till clang is fixed on MSYS2 ((lib)clang.dll.a is missing):
if(MSYS)
set(LIBCLANG_LIBRARIES "${CMAKE_INSTALL_PREFIX}/bin/clang.dll")
endif()
#find_package(PythonLibs 2.7)
#validate(${PYTHONLIBS_FOUND} "python" "libpython-dev" "python")
find_package(Boost 1.55 COMPONENTS python thread log system filesystem REQUIRED)
#find_package(Boost 1.55 COMPONENTS python thread log system filesystem REQUIRED)
find_package(Boost 1.55 COMPONENTS thread log system filesystem REQUIRED)
validate(${Boost_FOUND} "boost" "libboost-all-dev" "boost")
pkg_check_modules(GTKMM gtkmm-3.0) # The name GTKMM is set here for the variables abouve
@ -50,9 +57,9 @@ validate(${GTKMM_FOUND} "gtkmm" "libgtkmm-dev" "gtkmm")
pkg_check_modules(GTKSVMM gtksourceviewmm-3.0)
validate(${GTKSVMM_FOUND} "gtksvmm" "libgtksvmm-dev" "gtkmmsv")
if(${validation})
add_executable(${project_name}
juci.h
find_package(ASPELL REQUIRED)
set(source_files juci.h
juci.cc
menu.h
menu.cc
@ -66,8 +73,8 @@ if(${validation})
sourcefile.cc
window.cc
window.h
api.h
api.cc
# api.h
# api.cc
notebook.cc
notebook.h
entrybox.h
@ -75,7 +82,6 @@ if(${validation})
directories.h
directories.cc
terminal.h
terminal.cc
tooltips.h
tooltips.cc
singletons.h
@ -83,31 +89,42 @@ if(${validation})
cmake.h
cmake.cc)
add_library(${module} SHARED
api
api_ext)
if(MSYS)
list(APPEND source_files terminal_win.cc)
else()
list(APPEND source_files terminal.cc)
endif()
if(${validation})
add_executable(${project_name} ${source_files})
# add_library(${module} SHARED
# api
# api_ext)
include_directories(
${Boost_INCLUDE_DIRS}
${PYTHON_INCLUDE_DIRS}
# ${PYTHON_INCLUDE_DIRS}
${GTKMM_INCLUDE_DIRS}
${GTKSVMM_INCLUDE_DIRS}
${LCL_INCLUDE_DIRS}
${LIBCLANG_INCLUDE_DIRS})
${LIBCLANG_INCLUDE_DIRS}
${ASPELL_INCLUDE_DIR})
link_directories(
${GTKMM_LIBRARY_DIRS}
${GTKSVMM_LIBRARY_DIRS}
${Boost_LIBRARY_DIRS}
${PYTHON_INCLUDE_DIRS}
# ${PYTHON_INCLUDE_DIRS}
${LCL_LIBRARY_DIRS}
${LIBCLANG_LIBRARY_DIRS})
set_target_properties(${module}
PROPERTIES PREFIX ""
LIBRARY_OUTPUT_DIRECTORY "${CMAKE_SOURCE_DIR}/lib/")
# set_target_properties(${module}
# PROPERTIES PREFIX ""
# LIBRARY_OUTPUT_DIRECTORY "${CMAKE_SOURCE_DIR}/lib/")
target_link_libraries(${module} ${PYTHON_LIBRARIES} ${Boost_LIBRARIES})
# target_link_libraries(${module} ${PYTHON_LIBRARIES} ${Boost_LIBRARIES})
# target_link_libraries(${module} ${Boost_LIBRARIES})
target_link_libraries(${project_name}
${LIBCLANG_LIBRARIES}
@ -115,11 +132,14 @@ if(${validation})
${GTKMM_LIBRARIES}
${GTKSVMM_LIBRARIES}
${Boost_LIBRARIES}
${PYTHON_LIBRARIES})
${ASPELL_LIBRARIES}
# ${PYTHON_LIBRARIES}
)
install(TARGETS ${project_name} ${module}
RUNTIME DESTINATION ${bin_install_path}
LIBRARY DESTINATION ${lib_install_path}
# install(TARGETS ${project_name} ${module}
install(TARGETS ${project_name}
RUNTIME DESTINATION bin
# LIBRARY DESTINATION ${lib_install_path}
)
endif(${validation})

20
src/cmake.cc

@ -46,9 +46,25 @@ CMake::CMake(const boost::filesystem::path &path) {
bool CMake::create_compile_commands(const boost::filesystem::path &path) {
Singleton::terminal()->print("Creating "+path.string()+"/compile_commands.json\n");
//TODO: Windows...
if(Singleton::terminal()->execute("cmake . -DCMAKE_EXPORT_COMPILE_COMMANDS=ON", path)==EXIT_SUCCESS)
if(Singleton::terminal()->execute(Singleton::Config::terminal()->cmake_command+" . -DCMAKE_EXPORT_COMPILE_COMMANDS=ON", path)==EXIT_SUCCESS) {
#ifdef _WIN32 //Temporary fix to MSYS2's libclang
auto compile_commands_path=path;
compile_commands_path+="/compile_commands.json";
auto compile_commands_file=juci::filesystem::read(compile_commands_path);
size_t pos=0;
while((pos=compile_commands_file.find("-I/", pos))!=std::string::npos) {
if(pos+3<compile_commands_file.size()) {
std::string drive;
drive+=compile_commands_file[pos+3];
compile_commands_file.replace(pos, 4, "-I"+drive+":");
}
else
break;
}
juci::filesystem::write(compile_commands_path, compile_commands_file);
#endif
return true;
}
return false;
}

3
src/cmake/Modules/FindLibClang.cmake

@ -14,7 +14,8 @@
# Known LLVM release numbers.
# most recent versions come first
set(LIBCLANG_KNOWN_LLVM_VERSIONS 3.6.1
set(LIBCLANG_KNOWN_LLVM_VERSIONS 3.6.2
3.6.1
3.6
3.5.1
3.5.0 #Arch Linux

8
src/cmake/Modules/FindLibClangmm.cmake

@ -5,12 +5,10 @@
find_package(PkgConfig)
find_path(LCL_INCLUDE_DIR clangmm.h
HINTS "/usr/local/include/libclangmm/"
)
PATH_SUFFIXES libclangmm
)
find_library(LCL_LIBRARY NAMES clangmm
HINTS "/usr/local/lib/"
)
find_library(LCL_LIBRARY NAMES clangmm)
set(LCL_LIBRARIES ${LCL_LIBRARY} )
set(LCL_INCLUDE_DIRS ${LCL_INCLUDE_DIR} )

20
src/config.cc

@ -14,8 +14,9 @@ MainConfig::MainConfig() {
Singleton::Config::window()->theme_name=cfg.get<std::string>("gtk_theme.name");
Singleton::Config::window()->theme_variant=cfg.get<std::string>("gtk_theme.variant");
Singleton::Config::terminal()->make_command=cfg.get<std::string>("project.make_command");
Singleton::Config::terminal()->cmake_command=cfg.get<std::string>("project.cmake_command");
}
void MainConfig::find_or_create_config_files() {
@ -45,13 +46,16 @@ void MainConfig::GenerateSource() {
auto source_cfg = Singleton::Config::source();
auto source_json = cfg.get_child("source");
source_cfg->tab_size = source_json.get<unsigned>("tab_size");
source_cfg->tab_char = source_json.get<char>("tab_char");
for(unsigned c=0;c<source_cfg->tab_size;c++)
source_cfg->tab+=source_cfg->tab_char;
source_cfg->highlight_current_line = source_json.get_value<bool>("highlight_current_line");
source_cfg->show_line_numbers = source_json.get_value<bool>("show_line_numbers");
source_cfg->spellcheck_language = source_json.get<std::string>("spellcheck_language");
source_cfg->default_tab_char = source_json.get<char>("default_tab_char");
source_cfg->default_tab_size = source_json.get<unsigned>("default_tab_size");
source_cfg->auto_tab_char_and_size = source_json.get<bool>("auto_tab_char_and_size");
source_cfg->wrap_lines = source_json.get<bool>("wrap_lines");
source_cfg->highlight_current_line = source_json.get<bool>("highlight_current_line");
source_cfg->show_line_numbers = source_json.get<bool>("show_line_numbers");
for (auto &i : source_json.get_child("clang_types"))
source_cfg->clang_types[i.first] = i.second.get_value<std::string>();

248
src/directories.cc

@ -3,7 +3,7 @@
#include "logging.h"
#include "singletons.h"
#include <algorithm>
#include "boost/algorithm/string.hpp"
#include <unordered_set>
#include <iostream> //TODO: remove
using namespace std; //TODO: remove
@ -12,78 +12,169 @@ namespace sigc {
SIGC_FUNCTORS_DEDUCE_RESULT_TYPE_WITH_DECLTYPE
}
Directories::Directories() {
Directories::Directories() : stop_update_thread(false) {
DEBUG("adding treeview to scrolledwindow");
add(tree_view);
set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
tree_store = Gtk::TreeStore::create(column_record);
tree_view.set_model(tree_store);
tree_view.append_column("", column_record.name);
tree_store->set_sort_column(0, Gtk::SortType::SORT_ASCENDING);
tree_store->set_sort_column(column_record.id, Gtk::SortType::SORT_ASCENDING);
tree_view.set_enable_search(true); //TODO: why does this not work in OS X?
tree_view.set_search_column(column_record.name);
tree_view.signal_row_activated().connect([this](const Gtk::TreeModel::Path& path, Gtk::TreeViewColumn* column){
INFO("Directory navigation");
auto iter = tree_store->get_iter(path);
if (iter) {
Gtk::TreeModel::Row row = *iter;
std::string upath = Glib::ustring(row[column_record.path]);
boost::filesystem::path fs_path(upath);
if (boost::filesystem::is_directory(fs_path)) {
tree_view.row_expanded(path) ? tree_view.collapse_row(path) : tree_view.expand_row(path, false);
} else {
std::stringstream sstm;
sstm << row[column_record.path];
if(on_row_activated)
on_row_activated(sstm.str());
auto path_str=iter->get_value(column_record.path);
if(path_str!="") {
if (boost::filesystem::is_directory(boost::filesystem::path(path_str))) {
tree_view.row_expanded(path) ? tree_view.collapse_row(path) : tree_view.expand_row(path, false);
} else {
if(on_row_activated)
on_row_activated(path_str);
}
}
}
});
tree_view.signal_test_expand_row().connect([this](const Gtk::TreeModel::iterator& iter, const Gtk::TreeModel::Path& path){
if(iter->children().begin()->get_value(column_record.path)=="") {
update_mutex.lock();
add_path(iter->get_value(column_record.path), *iter);
update_mutex.unlock();
}
return false;
});
tree_view.signal_row_collapsed().connect([this](const Gtk::TreeModel::iterator& iter, const Gtk::TreeModel::Path& path){
update_mutex.lock();
last_write_times.erase(iter->get_value(column_record.path));
update_mutex.unlock();
auto children=iter->children();
if(children) {
while(children) {
tree_store->erase(children.begin());
}
auto child=tree_store->append(iter->children());
child->set_value(column_record.name, std::string("(empty)"));
}
});
update_dispatcher.connect([this](){
update_mutex.lock();
for(auto &path: update_paths) {
if(last_write_times.count(path)>0)
add_path(path, last_write_times.at(path).first);
}
update_paths.clear();
update_mutex.unlock();
});
update_thread=std::thread([this](){
while(!stop_update_thread) {
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
update_mutex.lock();
if(update_paths.size()==0) {
for(auto it=last_write_times.begin();it!=last_write_times.end();) {
try {
if(boost::filesystem::exists(it->first)) { //Added for older boost versions (no exception thrown)
if(it->second.second<boost::filesystem::last_write_time(it->first)) {
update_paths.emplace_back(it->first);
}
it++;
}
else
it=last_write_times.erase(it);
}
catch(const std::exception &e) {
it=last_write_times.erase(it);
}
}
if(update_paths.size()>0)
update_dispatcher();
}
update_mutex.unlock();
}
});
}
void Directories::open_folder(const boost::filesystem::path& dir_path) {
auto new_path=dir_path;
INFO("Open folder");
if(new_path=="") {
if(current_path=="")
return;
new_path=current_path;
}
Directories::~Directories() {
stop_update_thread=true;
update_thread.join();
}
void Directories::open(const boost::filesystem::path& dir_path) {
if(dir_path=="")
return;
std::vector<Gtk::TreeModel::Path> expanded_paths;
if(current_path==new_path) {
tree_view.map_expanded_rows([&expanded_paths](Gtk::TreeView* tree_view, const Gtk::TreeModel::Path& path){
expanded_paths.emplace_back(path);
});
}
INFO("Open folder");
tree_store->clear();
if(dir_path!="")
cmake=std::unique_ptr<CMake>(new CMake(new_path));
update_mutex.lock();
last_write_times.clear();
update_paths.clear();
update_mutex.unlock();
cmake=std::unique_ptr<CMake>(new CMake(dir_path));
auto project=cmake->get_functions_parameters("project");
if(project.size()>0 && project[0].second.size()>0)
tree_view.get_column(0)->set_title(project[0].second[0]);
else
tree_view.get_column(0)->set_title("");
add_paths(new_path, Gtk::TreeModel::Row(), 0);
for(auto &path: expanded_paths)
tree_view.expand_row(path, false);
update_mutex.lock();
add_path(dir_path, Gtk::TreeModel::Row());
update_mutex.unlock();
current_path=new_path;
current_path=dir_path;
if(selected_path!="")
select_path(selected_path);
DEBUG("Folder opened");
}
void Directories::select_path(const boost::filesystem::path &path) {
void Directories::update() {
update_mutex.lock();
for(auto &last_write_time: last_write_times) {
add_path(last_write_time.first, last_write_time.second.first);
}
update_mutex.unlock();
}
void Directories::select(const boost::filesystem::path &path) {
if(current_path=="")
return;
if(path.string().substr(0, current_path.string().size())!=current_path.string())
return;
std::list<boost::filesystem::path> paths;
boost::filesystem::path parent_path;
if(boost::filesystem::is_directory(path))
parent_path=path;
else
parent_path=path.parent_path();
paths.emplace_front(parent_path);
while(parent_path!=current_path) {
parent_path=parent_path.parent_path();
paths.emplace_front(parent_path);
}
for(auto &a_path: paths) {
tree_store->foreach_iter([this, &a_path](const Gtk::TreeModel::iterator& iter){
if(iter->get_value(column_record.path)==a_path.string()) {
update_mutex.lock();
add_path(a_path, *iter);
update_mutex.unlock();
return true;
}
return false;
});
}
tree_store->foreach_iter([this, &path](const Gtk::TreeModel::iterator& iter){
if(iter->get_value(column_record.path)==path.string()) {
auto tree_path=Gtk::TreePath(iter);
tree_view.expand_to_path(tree_path);
tree_view.set_cursor(tree_path);
selected_path=path;
return true;
}
return false;
@ -106,37 +197,58 @@ bool Directories::ignored(std::string path) {
return false;
}
void Directories::add_paths(const boost::filesystem::path& dir_path, const Gtk::TreeModel::Row &parent, unsigned row_id) {
boost::filesystem::directory_iterator end_itr;
Gtk::TreeModel::Row child;
Gtk::TreeModel::Row row;
DEBUG("");
// Fill the treeview
for(boost::filesystem::directory_iterator itr(dir_path);itr != end_itr;++itr) {
if (!ignored(itr->path().filename().string())) {
if (boost::filesystem::is_directory(itr->status())) {
if (boost::filesystem::canonical(itr->path()) > boost::filesystem::canonical(dir_path)) { // is child
child = *(tree_store->append(parent.children()));
std::string col_id("a"+itr->path().filename().string());
child[column_record.id] = col_id;
child[column_record.name] = itr->path().filename().string();
child[column_record.path] = itr->path().string();
add_paths(itr->path(), child, row_id);
} else {
row = *(tree_store->append());
std::string col_id("a"+itr->path().filename().string());
row[column_record.path] = itr->path().string();
row[column_record.id] = col_id;
row[column_record.name] = itr->path().filename().string();
add_paths(itr->path(), parent, row_id);
void Directories::add_path(const boost::filesystem::path& dir_path, const Gtk::TreeModel::Row &parent) {
last_write_times[dir_path.string()]={parent, boost::filesystem::last_write_time(dir_path)};
std::unique_ptr<Gtk::TreeNodeChildren> children; //Gtk::TreeNodeChildren is missing default constructor...
if(parent)
children=std::unique_ptr<Gtk::TreeNodeChildren>(new Gtk::TreeNodeChildren(parent.children()));
else
children=std::unique_ptr<Gtk::TreeNodeChildren>(new Gtk::TreeNodeChildren(tree_store->children()));
if(*children) {
if(children->begin()->get_value(column_record.path)=="")
tree_store->erase(children->begin());
}
std::unordered_set<std::string> not_deleted;
boost::filesystem::directory_iterator end_it;
for(boost::filesystem::directory_iterator it(dir_path);it!=end_it;it++) {
auto filename=it->path().filename().string();
if (!ignored(filename)) {
bool already_added=false;
if(*children) {
for(auto &child: *children) {
if(child.get_value(column_record.name)==filename) {
not_deleted.emplace(filename);
already_added=true;
break;
}
}
} else { // is a file
child = *(tree_store->append(parent.children()));
std::string col_id("b"+itr->path().filename().string());
child[column_record.id] = col_id;
child[column_record.name] = itr->path().filename().string();
child[column_record.path] = itr->path().string();
}
if(!already_added) {
auto child = tree_store->append(*children);
not_deleted.emplace(filename);
child->set_value(column_record.name, filename);
child->set_value(column_record.path, it->path().string());
if (boost::filesystem::is_directory(*it)) {
child->set_value(column_record.id, "a"+filename);
auto grandchild=tree_store->append(child->children());
grandchild->set_value(column_record.name, std::string("(empty)"));
}
else
child->set_value(column_record.id, "b"+filename);
}
}
}
if(*children) {
for(auto it=children->begin();it!=children->end();) {
if(not_deleted.count(it->get_value(column_record.name))==0) {
it=tree_store->erase(it);
}
else
it++;
}
}
if(!*children) {
auto child=tree_store->append(*children);
child->set_value(column_record.name, std::string("(empty)"));
}
}

24
src/directories.h

@ -6,6 +6,9 @@
#include <string>
#include "boost/filesystem.hpp"
#include "cmake.h"
#include <thread>
#include <mutex>
#include <atomic>
class Directories : public Gtk::ScrolledWindow {
public:
@ -22,26 +25,33 @@ public:
add(name);
add(path);
}
Gtk::TreeModelColumn<Glib::ustring> id;
Gtk::TreeModelColumn<Glib::ustring> name;
Gtk::TreeModelColumn<Glib::ustring> path;
Gtk::TreeModelColumn<std::string> id;
Gtk::TreeModelColumn<std::string> name;
Gtk::TreeModelColumn<std::string> path;
};
Directories();
void open_folder(const boost::filesystem::path& dir_path="");
void select_path(const boost::filesystem::path &path);
~Directories();
void open(const boost::filesystem::path& dir_path="");
void update();
void select(const boost::filesystem::path &path);
std::function<void(const std::string &file)> on_row_activated;
std::unique_ptr<CMake> cmake;
boost::filesystem::path current_path;
private:
void add_paths(const boost::filesystem::path& dir_path, const Gtk::TreeModel::Row &row, unsigned depth);
void add_path(const boost::filesystem::path& dir_path, const Gtk::TreeModel::Row &row);
bool ignored(std::string path);
Gtk::TreeView tree_view;
Glib::RefPtr<Gtk::TreeStore> tree_store;
ColumnRecord column_record;
boost::filesystem::path selected_path;
std::unordered_map<std::string, std::pair<Gtk::TreeModel::Row, std::time_t> > last_write_times;
std::mutex update_mutex;
std::thread update_thread;
std::atomic<bool> stop_update_thread;
Glib::Dispatcher update_dispatcher;
std::vector<std::string> update_paths;
};
#endif // JUCI_DIRECTORIES_H_

33
src/files.h

@ -7,7 +7,17 @@ const std::string configjson =
" },\n"
" \"source\": {\n"
" \"style\": \"juci-light\", //Use \"\" for default style, and for instance juci-dark together with dark gtk_theme variant. Styles from normal gtksourceview install: classic, cobalt, kate, oblivion, solarized-dark, solarized-light, tango\n"
" \"font\": \"Monospace\", //Use \"\" for default font, and for instance \"Monospace 12\" to also set size.\n"
#ifdef __APPLE__
" \"font\": \"Menlo 11\", "
#else
#ifdef _WIN32
" \"font\": \"Consolas\", "
#else
" \"font\": \"Monospace\", "
#endif
#endif
"//Use \"\" for default font, and for instance \"Monospace 12\" to also set size.\n"
" \"spellcheck_language\": \"en_US\", //Use \"\" to set language from your locale settings\n"
" \"clang_types\": {\n"
" \"8\": \"def:function\",\n"
" \"21\": \"def:function\",\n"
@ -23,14 +33,17 @@ const std::string configjson =
" \"702\": \"def:statement\",\n"
" \"705\": \"def:comment\"\n"
" },\n"
" \"tab_size\": 2,\n"
" \"tab_char\": \" \", //Use \"\\t\" for regular tab\n"
" \"auto_tab_char_and_size\": true, //Use false to always use default tab char and size\n"
" \"default_tab_char\": \" \", //Use \"\\t\" for regular tab\n"
" \"default_tab_size\": 2,\n"
" \"wrap_lines\": false,\n"
" \"highlight_current_line\": true,\n"
" \"show_line_numbers\": true\n"
" },\n"
" \"keybindings\": {\n"
" \"new_file\": \"<primary>n\",\n"
" \"open_folder\": \"<primary><alt>o\",\n"
" \"new_folder\": \"<primary><shift>n\",\n"
" \"open_folder\": \"<primary><shift>o\",\n"
" \"open_file\": \"<primary>o\",\n"
" \"save\": \"<primary>s\",\n"
" \"save_as\": \"<primary><shift>s\",\n"
@ -55,18 +68,17 @@ const std::string configjson =
" \"force_kill_last_running\": \"<primary><shift>Escape\"\n"
" },\n"
" \"project\": {\n"
#ifdef _WIN32
" \"cmake_command\": \"cmake -G\\\"MSYS Makefiles\\\"\",\n"
#else
" \"cmake_command\": \"cmake\",\n"
#endif
" \"make_command\": \"make\"\n"
" },\n"
" \"directoryfilter\": {\n"
" \"ignore\": [\n"
" \"cmake\",\n"
" \"#\",\n"
" \"~\",\n"
" \".idea\",\n"
" \".so\"\n"
" ],\n"
" \"exceptions\": [\n"
" \"cmakelists.txt\"\n"
" ]\n"
" }\n"
"}\n";
@ -76,6 +88,7 @@ const std::string menuxml =
" <menubar name=\"MenuBar\">\n"
" <menu action=\"FileMenu\">\n"
" <menuitem action=\"FileNewFile\"/>\n"
" <menuitem action=\"FileNewFolder\"/>\n"
" <menu action=\"FileNewProject\">\n"
" <menuitem action=\"FileNewProjectCpp\"/>\n"
" </menu>\n"

2
src/juci.cc

@ -45,7 +45,7 @@ void app::on_activate() {
bool first_directory=true;
for(auto &directory: directories) {
if(first_directory) {
window->directories.open_folder(directory);
window->directories.open(directory);
first_directory=false;
}
else {

3
src/notebook.cc

@ -67,7 +67,7 @@ void Notebook::open(const boost::filesystem::path &file_path) {
else
Singleton::terminal()->print("Error: could not find project path for "+file_path.string()+"\n");
}
source_views.emplace_back(new Source::ClangView(file_path, project_path));
source_views.emplace_back(new Source::ClangView(file_path, project_path, language));
}
else
source_views.emplace_back(new Source::GenericView(file_path, language));
@ -120,7 +120,6 @@ bool Notebook::save(int page) {
//TODO: recreate cmake even without directories open?
if(view->file_path.filename()=="CMakeLists.txt") {
if(directories.cmake && directories.cmake->project_path!="" && view->file_path.string().substr(0, directories.cmake->project_path.string().size())==directories.cmake->project_path.string() && CMake::create_compile_commands(directories.cmake->project_path)) {
directories.open_folder();
for(auto source_view: source_views) {
if(auto source_clang_view=dynamic_cast<Source::ClangView*>(source_view)) {
if(directories.cmake->project_path.string()==source_clang_view->project_path) {

4
src/selectiondialog.cc

@ -64,6 +64,7 @@ void SelectionDialogBase::add_row(const std::string& row, const std::string& too
}
void SelectionDialogBase::show() {
shown=true;
move();
window->show_all();
}
@ -72,8 +73,9 @@ void SelectionDialogBase::hide() {
window->hide();
if(tooltips)
tooltips->hide();
if(on_hide)
if(on_hide && shown)
on_hide();
shown=false;
}
void SelectionDialogBase::update_tooltips() {

2
src/selectiondialog.h

@ -31,6 +31,8 @@ protected:
std::unique_ptr<Tooltips> tooltips;
std::unordered_map<std::string, std::string> tooltip_texts;
std::string last_row;
private:
bool shown=false;
};
class SelectionDialog : public SelectionDialogBase {

640
src/source.cc

@ -4,12 +4,11 @@
#include <boost/timer/timer.hpp>
#include "logging.h"
#include <algorithm>
#include <regex>
#include "singletons.h"
#include <gtksourceview/gtksource.h>
#include <boost/lexical_cast.hpp>
#include <iostream>
#include <iostream> //TODO: remove
using namespace std; //TODO: remove
namespace sigc {
@ -37,6 +36,8 @@ Glib::RefPtr<Gsv::Language> Source::guess_language(const boost::filesystem::path
//////////////
//// View ////
//////////////
AspellConfig* Source::View::spellcheck_config=NULL;
Source::View::View(const boost::filesystem::path &file_path): file_path(file_path) {
set_smart_home_end(Gsv::SMART_HOME_END_BEFORE);
get_source_buffer()->begin_not_undoable_action();
@ -71,10 +72,202 @@ Source::View::View(const boost::filesystem::path &file_path): file_path(file_pat
Singleton::terminal()->print("Error: Could not find gtksourceview style: "+Singleton::Config::source()->style+'\n');
}
if(Singleton::Config::source()->wrap_lines)
set_wrap_mode(Gtk::WrapMode::WRAP_CHAR);
property_highlight_current_line() = Singleton::Config::source()->highlight_current_line;
property_show_line_numbers() = Singleton::Config::source()->show_line_numbers;
if(Singleton::Config::source()->font.size()>0)
override_font(Pango::FontDescription(Singleton::Config::source()->font));
//Create tags for diagnostic warnings and errors:
auto scheme = get_source_buffer()->get_style_scheme();
auto tag_table=get_buffer()->get_tag_table();
auto style=scheme->get_style("def:warning");
auto diagnostic_tag=get_source_buffer()->create_tag("def:warning");
auto diagnostic_tag_underline=get_source_buffer()->create_tag("def:warning_underline");
if(style && (style->property_foreground_set() || style->property_background_set())) {
Glib::ustring warning_property;
if(style->property_foreground_set()) {
warning_property=style->property_foreground().get_value();
diagnostic_tag->property_foreground() = warning_property;
}
else if(style->property_background_set())
warning_property=style->property_background().get_value();
diagnostic_tag_underline->property_underline()=Pango::Underline::UNDERLINE_ERROR;
auto tag_class=G_OBJECT_GET_CLASS(diagnostic_tag_underline->gobj()); //For older GTK+ 3 versions:
auto param_spec=g_object_class_find_property(tag_class, "underline-rgba");
if(param_spec!=NULL) {
diagnostic_tag_underline->set_property("underline-rgba", Gdk::RGBA(warning_property));
}
}
style=scheme->get_style("def:error");
diagnostic_tag=get_source_buffer()->create_tag("def:error");
diagnostic_tag_underline=get_source_buffer()->create_tag("def:error_underline");
if(style && (style->property_foreground_set() || style->property_background_set())) {
Glib::ustring error_property;
if(style->property_foreground_set()) {
error_property=style->property_foreground().get_value();
diagnostic_tag->property_foreground() = error_property;
}
else if(style->property_background_set())
error_property=style->property_background().get_value();
diagnostic_tag_underline->property_underline()=Pango::Underline::UNDERLINE_ERROR;
auto tag_class=G_OBJECT_GET_CLASS(diagnostic_tag_underline->gobj()); //For older GTK+ 3 versions:
auto param_spec=g_object_class_find_property(tag_class, "underline-rgba");
if(param_spec!=NULL) {
diagnostic_tag_underline->set_property("underline-rgba", Gdk::RGBA(error_property));
}
}
//TODO: clear tag_class and param_spec?
//Add tooltip foreground and background
style = scheme->get_style("def:note");
auto note_tag=get_source_buffer()->create_tag("def:note_background");
if(style->property_background_set()) {
note_tag->property_background()=style->property_background();
}
note_tag=get_source_buffer()->create_tag("def:note");
if(style->property_foreground_set()) {
note_tag->property_foreground()=style->property_foreground();
}
tab_char=Singleton::Config::source()->default_tab_char;
tab_size=Singleton::Config::source()->default_tab_size;
if(Singleton::Config::source()->auto_tab_char_and_size) {
auto tab_char_and_size=find_tab_char_and_size();
if(tab_char_and_size.first!=0) {
if(tab_char!=tab_char_and_size.first || tab_size!=tab_char_and_size.second) {
std::string tab_str;
if(tab_char_and_size.first==' ')
tab_str="<space>";
else
tab_str="<tab>";
Singleton::terminal()->print("Tab char and size for file "+file_path.string()+" set to: "+tab_str+", "+boost::lexical_cast<std::string>(tab_char_and_size.second)+".\n");
}
tab_char=tab_char_and_size.first;
tab_size=tab_char_and_size.second;
}
}
for(unsigned c=0;c<tab_size;c++)
tab+=tab_char;
tabs_regex=std::regex(std::string("^(")+tab_char+"*)(.*)$");
if(spellcheck_config==NULL) {
spellcheck_config=new_aspell_config();
if(Singleton::Config::source()->spellcheck_language.size()>0)
aspell_config_replace(spellcheck_config, "lang", Singleton::Config::source()->spellcheck_language.c_str());
}
spellcheck_possible_err=new_aspell_speller(spellcheck_config);
spellcheck_checker=NULL;
if (aspell_error_number(spellcheck_possible_err) != 0)
std::cerr << "Spell check error: " << aspell_error_message(spellcheck_possible_err) << std::endl;
else {
spellcheck_checker = to_aspell_speller(spellcheck_possible_err);
auto tag=get_buffer()->create_tag("spellcheck_error");
tag->property_underline()=Pango::Underline::UNDERLINE_ERROR;
get_buffer()->signal_changed().connect([this](){
auto iter=get_buffer()->get_insert()->get_iter();
if(iter.backward_char()) {
auto context_iter=iter;
if(context_iter.backward_char()) {
if((spellcheck_all && !get_source_buffer()->iter_has_context_class(context_iter, "no-spell-check")) || get_source_buffer()->iter_has_context_class(context_iter, "comment") || get_source_buffer()->iter_has_context_class(context_iter, "string")) {
if(*iter==32 || *iter==45) { //Might have used space or - to split two words
auto first=iter;
auto second=iter;
if(first.backward_char() && second.forward_char()) {
get_buffer()->remove_tag_by_name("spellcheck_error", first, second);
spellcheck(first);
spellcheck(second);
}
}
else
spellcheck(iter);
}
}
}
});
}
set_tooltip_events();
}
void Source::View::set_tooltip_events() {
signal_motion_notify_event().connect([this](GdkEventMotion* event) {
if(on_motion_last_x!=event->x || on_motion_last_y!=event->y) {
delayed_tooltips_connection.disconnect();
if(event->state==0) {
gdouble x=event->x;
gdouble y=event->y;
delayed_tooltips_connection=Glib::signal_timeout().connect([this, x, y]() {
Tooltips::init();
Gdk::Rectangle rectangle(x, y, 1, 1);
if(source_readable) {
type_tooltips.show(rectangle);
diagnostic_tooltips.show(rectangle);
}
spellcheck_tooltips.show(rectangle);
return false;
}, 100);
}
type_tooltips.hide();
diagnostic_tooltips.hide();
spellcheck_tooltips.hide();
}
on_motion_last_x=event->x;
on_motion_last_y=event->y;
return false;
});
get_buffer()->signal_mark_set().connect([this](const Gtk::TextBuffer::iterator& iterator, const Glib::RefPtr<Gtk::TextBuffer::Mark>& mark) {
if(get_buffer()->get_has_selection() && mark->get_name()=="selection_bound")
delayed_tooltips_connection.disconnect();
if(mark->get_name()=="insert") {
delayed_tooltips_connection.disconnect();
delayed_tooltips_connection=Glib::signal_timeout().connect([this]() {
Tooltips::init();
Gdk::Rectangle rectangle;
get_iter_location(get_buffer()->get_insert()->get_iter(), rectangle);
int location_window_x, location_window_y;
buffer_to_window_coords(Gtk::TextWindowType::TEXT_WINDOW_TEXT, rectangle.get_x(), rectangle.get_y(), location_window_x, location_window_y);
rectangle.set_x(location_window_x-2);
rectangle.set_y(location_window_y);
rectangle.set_width(4);
if(source_readable) {
type_tooltips.show(rectangle);
diagnostic_tooltips.show(rectangle);
}
spellcheck_tooltips.show(rectangle);
return false;
}, 500);
type_tooltips.hide();
diagnostic_tooltips.hide();
spellcheck_tooltips.hide();
}
});
signal_scroll_event().connect([this](GdkEventScroll* event) {
delayed_tooltips_connection.disconnect();
type_tooltips.hide();
diagnostic_tooltips.hide();
spellcheck_tooltips.hide();
return false;
});
signal_focus_out_event().connect([this](GdkEventFocus* event) {
delayed_tooltips_connection.disconnect();
type_tooltips.hide();
diagnostic_tooltips.hide();
spellcheck_tooltips.hide();
return false;
});
}
void Source::View::search_occurrences_updated(GtkWidget* widget, GParamSpec* property, gpointer data) {
@ -86,6 +279,8 @@ void Source::View::search_occurrences_updated(GtkWidget* widget, GParamSpec* pro
Source::View::~View() {
g_clear_object(&search_context);
g_clear_object(&search_settings);
delete_aspell_speller(spellcheck_checker);
}
void Source::View::search_highlight(const std::string &text, bool case_sensitive, bool regex) {
@ -151,11 +346,10 @@ void Source::View::replace_all(const std::string &replacement) {
void Source::View::paste() {
Gtk::Clipboard::get()->request_text([this](const Glib::ustring& text){
const std::regex spaces_regex(std::string("^(")+Singleton::Config::source()->tab_char+"*)(.*)$");
auto line=get_line_before_insert();
std::smatch sm;
std::string prefix_tabs;
if(!get_buffer()->get_has_selection() && std::regex_match(line, sm, spaces_regex) && sm[2].str().size()==0) {
if(!get_buffer()->get_has_selection() && std::regex_match(line, sm, tabs_regex) && sm[2].str().size()==0) {
prefix_tabs=sm[1].str();
Glib::ustring::size_type start_line=0;
@ -177,7 +371,7 @@ void Source::View::paste() {
std::string line=text.substr(start_line, end_line-start_line);
size_t tabs=0;
for(auto chr: line) {
if(chr==Singleton::Config::source()->tab_char)
if(chr==tab_char)
tabs++;
else
break;
@ -260,21 +454,19 @@ string Source::View::get_line_before_insert() {
//Basic indentation
bool Source::View::on_key_press_event(GdkEventKey* key) {
get_source_buffer()->begin_user_action();
auto config=Singleton::Config::source();
const std::regex spaces_regex(std::string("^(")+config->tab_char+"*).*$");
//Indent as in next or previous line
if(key->keyval==GDK_KEY_Return && key->state==0 && !get_buffer()->get_has_selection()) {
auto insert_it=get_buffer()->get_insert()->get_iter();
int line_nr=insert_it.get_line();
auto line=get_line_before_insert();
std::smatch sm;
if(std::regex_match(line, sm, spaces_regex)) {
if(std::regex_match(line, sm, tabs_regex)) {
if((line_nr+1)<get_buffer()->get_line_count()) {
string next_line=get_line(line_nr+1);
auto line_end_iter=get_buffer()->get_iter_at_line(line_nr+1);
line_end_iter--;
std::smatch sm2;
if(insert_it==line_end_iter && std::regex_match(next_line, sm2, spaces_regex)) {
if(insert_it==line_end_iter && std::regex_match(next_line, sm2, tabs_regex)) {
if(sm2[1].str().size()>sm[1].str().size()) {
get_source_buffer()->insert_at_cursor("\n"+sm2[1].str());
scroll_to(get_source_buffer()->get_insert());
@ -297,7 +489,7 @@ bool Source::View::on_key_press_event(GdkEventKey* key) {
int line_end=selection_end.get_line();
for(int line=line_start;line<=line_end;line++) {
Gtk::TextIter line_it = get_source_buffer()->get_iter_at_line(line);
get_source_buffer()->insert(line_it, config->tab);
get_source_buffer()->insert(line_it, tab);
}
get_source_buffer()->end_user_action();
return true;
@ -309,11 +501,11 @@ bool Source::View::on_key_press_event(GdkEventKey* key) {
int line_start=selection_start.get_line();
int line_end=selection_end.get_line();
unsigned indent_left_steps=config->tab_size;
unsigned indent_left_steps=tab_size;
for(int line_nr=line_start;line_nr<=line_end;line_nr++) {
string line=get_line(line_nr);
std::smatch sm;
if(std::regex_match(line, sm, spaces_regex) && sm[1].str().size()>0) {
if(std::regex_match(line, sm, tabs_regex) && sm[1].str().size()>0) {
indent_left_steps=std::min(indent_left_steps, (unsigned)sm[1].str().size());
}
else {
@ -339,12 +531,12 @@ bool Source::View::on_key_press_event(GdkEventKey* key) {
int line_nr=insert_it.get_line();
auto line=get_line_before_insert();
std::smatch sm;
if(std::regex_match(line, sm, spaces_regex) && sm[1].str().size()==line.size()) {
if(std::regex_match(line, sm, tabs_regex) && sm[1].str().size()==line.size()) {
if((line_nr-1)>=0) {
string previous_line=get_line(line_nr-1);
std::smatch sm2;
if(std::regex_match(previous_line, sm2, spaces_regex)) {
if(line.size()==sm2[1].str().size() || line.size()==sm2[1].str().size()+config->tab_size || line.size()==sm2[1].str().size()-config->tab_size) {
if(std::regex_match(previous_line, sm2, tabs_regex)) {
if(line.size()==sm2[1].str().size() || line.size()==sm2[1].str().size()+tab_size || line.size()==sm2[1].str().size()-tab_size) {
auto previous_line_end_it=insert_it;
for(unsigned c=0;c<line.size();c++)
previous_line_end_it--;
@ -355,9 +547,9 @@ bool Source::View::on_key_press_event(GdkEventKey* key) {
}
}
}
if(line.size()>=config->tab_size) {
if(line.size()>=tab_size) {
auto insert_minus_tab_it=insert_it;
for(unsigned c=0;c<config->tab_size;c++)
for(unsigned c=0;c<tab_size;c++)
insert_minus_tab_it--;
get_source_buffer()->erase(insert_minus_tab_it, insert_it);
get_source_buffer()->end_user_action();
@ -371,6 +563,134 @@ bool Source::View::on_key_press_event(GdkEventKey* key) {
return stop;
}
bool Source::View::on_button_press_event(GdkEventButton *event) {
if(event->type==GDK_2BUTTON_PRESS) {
Gtk::TextIter start, end;
if(get_buffer()->get_selection_bounds(start, end)) {
auto iter=start;
while((*iter>=48 && *iter<=57) || (*iter>=65 && *iter<=90) || (*iter>=97 && *iter<=122) || *iter==95) {
start=iter;
if(!iter.backward_char())
break;
}
while((*end>=48 && *end<=57) || (*end>=65 && *end<=90) || (*end>=97 && *end<=122) || *end==95) {
if(!end.forward_char())
break;
}
get_buffer()->select_range(start, end);
return true;
}
}
return Gsv::View::on_button_press_event(event);
}
std::pair<char, unsigned> Source::View::find_tab_char_and_size() {
const std::regex indent_regex("^([ \t]+).*$");
auto size=get_buffer()->get_line_count();
std::unordered_map<char, size_t> tab_chars;
std::unordered_map<unsigned, size_t> tab_sizes;
unsigned last_tab_size=0;
for(int c=0;c<size;c++) {
auto line=get_line(c);
std::smatch sm;
if(std::regex_match(line, sm, indent_regex)) {
auto str=sm[1].str();
long tab_diff=abs(static_cast<long>(str.size()-last_tab_size));
if(tab_diff>0) {
unsigned tab_diff_unsigned=static_cast<unsigned>(tab_diff);
auto it_size=tab_sizes.find(tab_diff_unsigned);
if(it_size!=tab_sizes.end())
it_size->second++;
else
tab_sizes[tab_diff_unsigned]=1;
}
last_tab_size=str.size();
if(str.size()>0) {
auto it_char=tab_chars.find(str[0]);
if(it_char!=tab_chars.end())
it_char->second++;
else
tab_chars[str[0]]=1;
}
}
}
char found_tab_char=0;
size_t occurences=0;
for(auto &tab_char: tab_chars) {
if(tab_char.second>occurences) {
found_tab_char=tab_char.first;
occurences=tab_char.second;
}
}
unsigned found_tab_size=0;
occurences=0;
for(auto &tab_size: tab_sizes) {
if(tab_size.second>occurences) {
found_tab_size=tab_size.first;
occurences=tab_size.second;
}
}
return {found_tab_char, found_tab_size};
}
void Source::View::spellcheck(Gtk::TextIter iter) {
auto start=iter;
auto end=iter;
while((*iter>=48 && *iter<=57) || (*iter>=65 && *iter<=90) || (*iter>=97 && *iter<=122) || *iter==39 || *iter>=128) {
start=iter;
if(!iter.backward_char())
break;
}
while((*end>=48 && *end<=57) || (*end>=65 && *end<=90) || (*end>=97 && *end<=122) || *iter==39 || *end>=128) {
if(!end.forward_char())
break;
}
for(auto it=spellcheck_tooltips.begin();it!=spellcheck_tooltips.end();it++) {
if(it->start_mark->get_iter()==start || it->start_mark->get_iter()==it->end_mark->get_iter())
it=spellcheck_tooltips.erase(it);
}
auto word=get_buffer()->get_text(start, end);
std::vector<Glib::ustring> words;
if(word.size()>0) {
auto correct = aspell_speller_check(spellcheck_checker, word.data(), word.bytes());
if(correct==0) {
get_buffer()->apply_tag_by_name("spellcheck_error", start, end);
const AspellWordList *suggestions = aspell_speller_suggest(spellcheck_checker, word.data(), word.bytes());
AspellStringEnumeration *elements = aspell_word_list_elements(suggestions);
auto words=std::make_shared<std::string>();
const char *word;
while ((word = aspell_string_enumeration_next(elements))!= NULL) {
if(words->size()==0) {
*words="Suggestions:\n";
(*words)+=word;
}
else
(*words)+=std::string(", ")+word;
}
delete_aspell_string_enumeration(elements);
if(words->size()>0) {
auto create_tooltip_buffer=[this, words]() {
auto tooltip_buffer=Gtk::TextBuffer::create(get_buffer()->get_tag_table());
tooltip_buffer->insert_with_tag(tooltip_buffer->get_insert()->get_iter(), *words, "def:note");
return tooltip_buffer;
};
spellcheck_tooltips.emplace_back(create_tooltip_buffer, *this, get_buffer()->create_mark(start), get_buffer()->create_mark(end));
}
}
else
get_buffer()->remove_tag_by_name("spellcheck_error", start, end);
}
}
/////////////////////
//// GenericView ////
/////////////////////
@ -379,6 +699,7 @@ Source::GenericView::GenericView(const boost::filesystem::path &file_path, Glib:
get_source_buffer()->set_language(language);
Singleton::terminal()->print("Language for file "+file_path.string()+" set to "+language->get_name()+".\n");
}
spellcheck_all=true;
auto completion=get_completion();
auto completion_words=Gsv::CompletionWords::create("", Glib::RefPtr<Gdk::Pixbuf>());
completion_words->register_provider(get_buffer());
@ -417,59 +738,7 @@ Source::View(file_path), project_path(project_path) {
DEBUG("Style " + item.second + " not found in " + scheme->get_name());
}
}
INFO("Tagtable filled");
//Create tags for diagnostic warnings and errors:
auto style=scheme->get_style("def:warning");
auto diagnostic_tag=get_source_buffer()->create_tag("def:warning");
auto diagnostic_tag_underline=get_source_buffer()->create_tag("def:warning_underline");
if(style && (style->property_foreground_set() || style->property_background_set())) {
Glib::ustring warning_property;
if(style->property_foreground_set()) {
warning_property=style->property_foreground().get_value();
diagnostic_tag->property_foreground() = warning_property;
}
else if(style->property_background_set())
warning_property=style->property_background().get_value();
diagnostic_tag_underline->property_underline()=Pango::Underline::UNDERLINE_ERROR;
auto tag_class=G_OBJECT_GET_CLASS(diagnostic_tag_underline->gobj()); //For older GTK+ 3 versions:
auto param_spec=g_object_class_find_property(tag_class, "underline-rgba");
if(param_spec!=NULL) {
diagnostic_tag_underline->set_property("underline-rgba", Gdk::RGBA(warning_property));
}
}
style=scheme->get_style("def:error");
diagnostic_tag=get_source_buffer()->create_tag("def:error");
diagnostic_tag_underline=get_source_buffer()->create_tag("def:error_underline");
if(style && (style->property_foreground_set() || style->property_background_set())) {
Glib::ustring error_property;
if(style->property_foreground_set()) {
error_property=style->property_foreground().get_value();
diagnostic_tag->property_foreground() = error_property;
}
else if(style->property_background_set())
error_property=style->property_background().get_value();
diagnostic_tag_underline->property_underline()=Pango::Underline::UNDERLINE_ERROR;
auto tag_class=G_OBJECT_GET_CLASS(diagnostic_tag_underline->gobj()); //For older GTK+ 3 versions:
auto param_spec=g_object_class_find_property(tag_class, "underline-rgba");
if(param_spec!=NULL) {
diagnostic_tag_underline->set_property("underline-rgba", Gdk::RGBA(error_property));
}
}
//TODO: clear tag_class and param_spec?
//Add tooltip foreground and background
style = scheme->get_style("def:note");
auto note_tag=get_source_buffer()->create_tag("def:note_background");
if(style->property_background_set()) {
note_tag->property_background()=style->property_background();
}
note_tag=get_source_buffer()->create_tag("def:note");
if(style->property_foreground_set()) {
note_tag->property_foreground()=style->property_foreground();
}
INFO("Tagtable filled");
parsing_in_progress=Singleton::terminal()->print_in_progress("Parsing "+file_path.string());
//GTK-calls must happen in main thread, so the parse_thread
@ -489,7 +758,7 @@ Source::View(file_path), project_path(project_path) {
update_syntax();
update_diagnostics();
update_types();
clang_readable=true;
source_readable=true;
set_status("");
parsing_mutex.unlock();
INFO("Syntax updated");
@ -506,16 +775,20 @@ Source::View(file_path), project_path(project_path) {
start_reparse();
type_tooltips.hide();
diagnostic_tooltips.hide();
spellcheck_tooltips.hide();
});
get_buffer()->signal_mark_set().connect(sigc::mem_fun(*this, &Source::ClangViewParse::on_mark_set), false);
bracket_regex=std::regex(std::string("^(")+tab_char+"*).*\\{ *$");
no_bracket_statement_regex=std::regex(std::string("^(")+tab_char+"*)(if|for|else if|catch|while) *\\(.*[^;}] *$");
no_bracket_no_para_statement_regex=std::regex(std::string("^(")+tab_char+"*)(else|try|do) *$");
}
void Source::ClangViewParse::init_parse() {
type_tooltips.hide();
diagnostic_tooltips.hide();
spellcheck_tooltips.hide();
get_buffer()->remove_all_tags(get_buffer()->begin(), get_buffer()->end());
clang_readable=false;
source_readable=false;
parse_thread_go=true;
parse_thread_mapped=false;
parse_thread_stop=false;
@ -579,10 +852,10 @@ std::map<std::string, std::string> Source::ClangViewParse::get_buffer_map() cons
void Source::ClangViewParse::start_reparse() {
parse_thread_mapped=false;
clang_readable=false;
source_readable=false;
delayed_reparse_connection.disconnect();
delayed_reparse_connection=Glib::signal_timeout().connect([this]() {
clang_readable=false;
source_readable=false;
parse_thread_go=true;
set_status("parsing...");
return false;
@ -608,6 +881,39 @@ std::vector<std::string> Source::ClangViewParse::get_compilation_commands() {
}
if(file_path.extension()==".h") //TODO: temporary fix for .h-files (parse as c++)
arguments.emplace_back("-xc++");
#ifdef _WIN32 //Temporary fix to MSYS2's libclang
arguments.emplace_back("-IC:/msys32/mingw32/lib/gcc/i686-w64-mingw32/5.2.0/include");
arguments.emplace_back("-IC:/msys32/mingw32//include");
arguments.emplace_back("-IC:/msys32/mingw32/lib/gcc/i686-w64-mingw32/5.2.0/include-fixed");
arguments.emplace_back("-IC:/msys32/mingw32/i686-w64-mingw32/include");
arguments.emplace_back("-IC:/msys32/mingw32/include/c++/5.2.0");
arguments.emplace_back("-IC:/msys32/mingw32/include/c++/5.2.0/i686-w64-mingw32");
arguments.emplace_back("-IC:/msys32/mingw32/include/c++/5.2.0/backward");
arguments.emplace_back("-IC:/msys32/mingw64/lib/gcc/i686-w64-mingw32/5.2.0/include");
arguments.emplace_back("-IC:/msys32/mingw64//include");
arguments.emplace_back("-IC:/msys32/mingw64/lib/gcc/i686-w64-mingw32/5.2.0/include-fixed");
arguments.emplace_back("-IC:/msys32/mingw64/i686-w64-mingw32/include");
arguments.emplace_back("-IC:/msys32/mingw64/include/c++/5.2.0");
arguments.emplace_back("-IC:/msys32/mingw64/include/c++/5.2.0/i686-w64-mingw32");
arguments.emplace_back("-IC:/msys32/mingw64/include/c++/5.2.0/backward");
arguments.emplace_back("-IC:/msys64/mingw32/lib/gcc/i686-w64-mingw32/5.2.0/include");
arguments.emplace_back("-IC:/msys64/mingw32//include");
arguments.emplace_back("-IC:/msys64/mingw32/lib/gcc/i686-w64-mingw32/5.2.0/include-fixed");
arguments.emplace_back("-IC:/msys64/mingw32/i686-w64-mingw32/include");
arguments.emplace_back("-IC:/msys64/mingw32/include/c++/5.2.0");
arguments.emplace_back("-IC:/msys64/mingw32/include/c++/5.2.0/i686-w64-mingw32");
arguments.emplace_back("-IC:/msys64/mingw32/include/c++/5.2.0/backward");
arguments.emplace_back("-IC:/msys64/mingw64/lib/gcc/i686-w64-mingw32/5.2.0/include");
arguments.emplace_back("-IC:/msys64/mingw64//include");
arguments.emplace_back("-IC:/msys64/mingw64/lib/gcc/i686-w64-mingw32/5.2.0/include-fixed");
arguments.emplace_back("-IC:/msys64/mingw64/i686-w64-mingw32/include");
arguments.emplace_back("-IC:/msys64/mingw64/include/c++/5.2.0");
arguments.emplace_back("-IC:/msys64/mingw64/include/c++/5.2.0/i686-w64-mingw32");
arguments.emplace_back("-IC:/msys64/mingw64/include/c++/5.2.0/backward");
#endif
return arguments;
}
@ -658,8 +964,24 @@ void Source::ClangViewParse::update_diagnostics() {
auto diagnostics=clang_tu->get_diagnostics();
for(auto &diagnostic: diagnostics) {
if(diagnostic.path==file_path.string()) {
auto start=get_buffer()->get_iter_at_line_index(diagnostic.offsets.first.line-1, diagnostic.offsets.first.index-1);
auto end=get_buffer()->get_iter_at_line_index(diagnostic.offsets.second.line-1, diagnostic.offsets.second.index-1);
auto start_line=get_line(diagnostic.offsets.first.line-1); //index is sometimes off the line
auto start_line_index=diagnostic.offsets.first.index-1;
if(start_line_index>=start_line.size()) {
if(start_line.size()==0)
start_line_index=0;
else
start_line_index=start_line.size()-1;
}
auto end_line=get_line(diagnostic.offsets.second.line-1); //index is sometimes off the line
auto end_line_index=diagnostic.offsets.second.index-1;
if(end_line_index>=end_line.size()) {
if(end_line.size()==0)
end_line_index=0;
else
end_line_index=end_line.size()-1;
}
auto start=get_buffer()->get_iter_at_line_index(diagnostic.offsets.first.line-1, start_line_index);
auto end=get_buffer()->get_iter_at_line_index(diagnostic.offsets.second.line-1, end_line_index);
std::string diagnostic_tag_name;
if(diagnostic.severity<=CXDiagnostic_Warning)
diagnostic_tag_name="def:warning";
@ -702,62 +1024,6 @@ void Source::ClangViewParse::update_types() {
}
}
bool Source::ClangViewParse::on_motion_notify_event(GdkEventMotion* event) {
delayed_tooltips_connection.disconnect();
if(clang_readable && event->state==0) {
Gdk::Rectangle rectangle(event->x, event->y, 1, 1);
Tooltips::init();
type_tooltips.show(rectangle);
diagnostic_tooltips.show(rectangle);
}
else {
type_tooltips.hide();
diagnostic_tooltips.hide();
}
return Source::View::on_motion_notify_event(event);
}
void Source::ClangViewParse::on_mark_set(const Gtk::TextBuffer::iterator& iterator, const Glib::RefPtr<Gtk::TextBuffer::Mark>& mark) {
if(get_buffer()->get_has_selection() && mark->get_name()=="selection_bound")
delayed_tooltips_connection.disconnect();
if(mark->get_name()=="insert") {
delayed_tooltips_connection.disconnect();
delayed_tooltips_connection=Glib::signal_timeout().connect([this]() {
if(clang_readable) {
Gdk::Rectangle rectangle;
get_iter_location(get_buffer()->get_insert()->get_iter(), rectangle);
int location_window_x, location_window_y;
buffer_to_window_coords(Gtk::TextWindowType::TEXT_WINDOW_TEXT, rectangle.get_x(), rectangle.get_y(), location_window_x, location_window_y);
rectangle.set_x(location_window_x-2);
rectangle.set_y(location_window_y);
rectangle.set_width(4);
Tooltips::init();
type_tooltips.show(rectangle);
diagnostic_tooltips.show(rectangle);
}
return false;
}, 500);
type_tooltips.hide();
diagnostic_tooltips.hide();
}
}
bool Source::ClangViewParse::on_focus_out_event(GdkEventFocus* event) {
delayed_tooltips_connection.disconnect();
type_tooltips.hide();
diagnostic_tooltips.hide();
return Source::View::on_focus_out_event(event);
}
bool Source::ClangViewParse::on_scroll_event(GdkEventScroll* event) {
delayed_tooltips_connection.disconnect();
type_tooltips.hide();
diagnostic_tooltips.hide();
return Source::View::on_scroll_event(event);
}
//Clang indentation
//TODO: replace indentation methods with a better implementation or maybe use libclang
bool Source::ClangViewParse::on_key_press_event(GdkEventKey* key) {
@ -765,11 +1031,6 @@ bool Source::ClangViewParse::on_key_press_event(GdkEventKey* key) {
return Source::View::on_key_press_event(key);
}
get_source_buffer()->begin_user_action();
auto config=Singleton::Config::source();
const std::regex bracket_regex(std::string("^(")+config->tab_char+"*).*\\{ *$");
const std::regex no_bracket_statement_regex(std::string("^(")+config->tab_char+"*)(if|for|else if|catch|while) *\\(.*[^;}] *$");
const std::regex no_bracket_no_para_statement_regex(std::string("^(")+config->tab_char+"*)(else|try|do) *$");
const std::regex spaces_regex(std::string("^(")+config->tab_char+"*).*$");
//Indent depending on if/else/etc and brackets
if(key->keyval==GDK_KEY_Return && key->state==0) {
@ -780,18 +1041,30 @@ bool Source::ClangViewParse::on_key_press_event(GdkEventKey* key) {
if((line_nr+1)<get_source_buffer()->get_line_count()) {
string next_line=get_line(line_nr+1);
std::smatch sm2;
if(std::regex_match(next_line, sm2, spaces_regex)) {
if(sm2[1].str()==sm[1].str()+config->tab) {
get_source_buffer()->insert_at_cursor("\n"+sm[1].str()+config->tab);
if(std::regex_match(next_line, sm2, tabs_regex)) {
if(sm2[1].str()==sm[1].str()+tab) {
get_source_buffer()->insert_at_cursor("\n"+sm[1].str()+tab);
scroll_to(get_source_buffer()->get_insert());
get_source_buffer()->end_user_action();
return true;
}
}
if(next_line!=sm[1].str()+"}") {
get_source_buffer()->insert_at_cursor("\n"+sm[1].str()+config->tab+"\n"+sm[1].str()+"}");
auto next_char=*get_buffer()->get_insert()->get_iter();
auto next_line_with_end_bracket=sm[1].str()+"}";
if(next_char!='}' && next_line.substr(0, next_line_with_end_bracket.size())!=next_line_with_end_bracket) {
get_source_buffer()->insert_at_cursor("\n"+sm[1].str()+tab+"\n"+sm[1].str()+"}");
auto insert_it = get_source_buffer()->get_insert()->get_iter();
for(size_t c=0;c<config->tab_size+sm[1].str().size();c++)
for(size_t c=0;c<sm[1].str().size()+2;c++)
insert_it--;
scroll_to(get_source_buffer()->get_insert());
get_source_buffer()->place_cursor(insert_it);
get_source_buffer()->end_user_action();
return true;
}
else if(next_char=='}') {
get_source_buffer()->insert_at_cursor("\n"+sm[1].str()+tab+"\n"+sm[1].str());
auto insert_it = get_source_buffer()->get_insert()->get_iter();
for(size_t c=0;c<sm[1].str().size()+1;c++)
insert_it--;
scroll_to(get_source_buffer()->get_insert());
get_source_buffer()->place_cursor(insert_it);
@ -799,7 +1072,7 @@ bool Source::ClangViewParse::on_key_press_event(GdkEventKey* key) {
return true;
}
else {
get_source_buffer()->insert_at_cursor("\n"+sm[1].str()+config->tab);
get_source_buffer()->insert_at_cursor("\n"+sm[1].str()+tab);
scroll_to(get_source_buffer()->get_insert());
get_source_buffer()->end_user_action();
return true;
@ -807,21 +1080,21 @@ bool Source::ClangViewParse::on_key_press_event(GdkEventKey* key) {
}
}
else if(std::regex_match(line, sm, no_bracket_statement_regex)) {
get_source_buffer()->insert_at_cursor("\n"+sm[1].str()+config->tab);
get_source_buffer()->insert_at_cursor("\n"+sm[1].str()+tab);
scroll_to(get_source_buffer()->get_insert());
get_source_buffer()->end_user_action();
return true;
}
else if(std::regex_match(line, sm, no_bracket_no_para_statement_regex)) {
get_source_buffer()->insert_at_cursor("\n"+sm[1].str()+config->tab);
get_source_buffer()->insert_at_cursor("\n"+sm[1].str()+tab);
scroll_to(get_source_buffer()->get_insert());
get_source_buffer()->end_user_action();
return true;
}
else if(std::regex_match(line, sm, spaces_regex)) {
else if(std::regex_match(line, sm, tabs_regex)) {
std::smatch sm2;
size_t line_nr=get_source_buffer()->get_insert()->get_iter().get_line();
if(line_nr>0 && sm[1].str().size()>=config->tab_size) {
if(line_nr>0 && sm[1].str().size()>=tab_size) {
string previous_line=get_line(line_nr-1);
if(!std::regex_match(previous_line, sm2, bracket_regex)) {
if(std::regex_match(previous_line, sm2, no_bracket_statement_regex)) {
@ -843,23 +1116,25 @@ bool Source::ClangViewParse::on_key_press_event(GdkEventKey* key) {
//Indent left when writing } on a new line
else if(key->keyval==GDK_KEY_braceright) {
string line=get_line_before_insert();
if(line.size()>=config->tab_size) {
if(line.size()>=tab_size) {
for(auto c: line) {
if(c!=config->tab_char) {
if(c!=tab_char) {
get_source_buffer()->insert_at_cursor("}");
get_source_buffer()->end_user_action();
return Source::View::on_key_press_event(key);
return true;
}
}
Gtk::TextIter insert_it = get_source_buffer()->get_insert()->get_iter();
Gtk::TextIter line_it = get_source_buffer()->get_iter_at_line(insert_it.get_line());
Gtk::TextIter line_plus_it=line_it;
for(unsigned c=0;c<config->tab_size;c++)
for(unsigned c=0;c<tab_size;c++)
line_plus_it++;
get_source_buffer()->erase(line_it, line_plus_it);
}
get_source_buffer()->insert_at_cursor("}");
get_source_buffer()->end_user_action();
return Source::View::on_key_press_event(key);
return true;
}
get_source_buffer()->end_user_action();
@ -898,6 +1173,14 @@ Source::ClangViewParse(file_path, project_path), autocomplete_cancel_starting(fa
return false;
}, false);
signal_focus_out_event().connect([this](GdkEventFocus* event) {
autocomplete_cancel_starting=true;
if(completion_dialog_shown) {
completion_dialog->hide();
}
return false;
});
}
bool Source::ClangViewAutocomplete::on_key_press_event(GdkEventKey *key) {
@ -909,15 +1192,6 @@ bool Source::ClangViewAutocomplete::on_key_press_event(GdkEventKey *key) {
return ClangViewParse::on_key_press_event(key);
}
bool Source::ClangViewAutocomplete::on_focus_out_event(GdkEventFocus* event) {
autocomplete_cancel_starting=true;
if(completion_dialog_shown) {
completion_dialog->hide();
}
return Source::ClangViewParse::on_focus_out_event(event);
}
void Source::ClangViewAutocomplete::start_autocomplete() {
if(!has_focus())
return;
@ -1030,16 +1304,17 @@ void Source::ClangViewAutocomplete::autocomplete() {
});
std::shared_ptr<std::map<std::string, std::string> > buffer_map=std::make_shared<std::map<std::string, std::string> >();
auto& buffer=(*buffer_map)[this->file_path.string()];
buffer=get_buffer()->get_text(get_buffer()->begin(), get_buffer()->get_insert()->get_iter());
auto iter = get_source_buffer()->get_insert()->get_iter();
auto ustr=get_buffer()->get_text();
auto iter=get_buffer()->get_insert()->get_iter();
auto line_nr=iter.get_line()+1;
auto column_nr=iter.get_line_offset()+1;
while((buffer.back()>='a' && buffer.back()<='z') || (buffer.back()>='A' && buffer.back()<='Z') || (buffer.back()>='0' && buffer.back()<='9') || buffer.back()=='_') {
buffer.pop_back();
auto pos=iter.get_offset()-1;
while(pos>=0 && ((ustr[pos]>='a' && ustr[pos]<='z') || (ustr[pos]>='A' && ustr[pos]<='Z') || (ustr[pos]>='0' && ustr[pos]<='9') || ustr[pos]=='_')) {
ustr.replace(pos, 1, " ");
column_nr--;
pos--;
}
buffer+="\n";
(*buffer_map)[this->file_path.string()]=std::move(ustr); //TODO: does this work?
set_status("autocomplete...");
if(autocomplete_thread.joinable())
autocomplete_thread.join();
@ -1102,7 +1377,7 @@ Source::ClangViewAutocomplete(file_path, project_path) {
});
get_token=[this]() -> std::string {
if(clang_readable) {
if(source_readable) {
auto iter=get_buffer()->get_insert()->get_iter();
auto line=(unsigned)iter.get_line();
auto index=(unsigned)iter.get_line_index();
@ -1120,7 +1395,7 @@ Source::ClangViewAutocomplete(file_path, project_path) {
};
get_token_name=[this]() -> std::string {
if(clang_readable) {
if(source_readable) {
auto iter=get_buffer()->get_insert()->get_iter();
auto line=(unsigned)iter.get_line();
auto index=(unsigned)iter.get_line_index();
@ -1136,7 +1411,7 @@ Source::ClangViewAutocomplete(file_path, project_path) {
};
tag_similar_tokens=[this](const std::string &usr){
if(clang_readable) {
if(source_readable) {
if(usr.size()>0 && last_similar_tokens_tagged!=usr) {
get_buffer()->remove_tag(similar_tokens_tag, get_buffer()->begin(), get_buffer()->end());
auto offsets=clang_tokens->get_similar_token_offsets(usr);
@ -1154,7 +1429,7 @@ Source::ClangViewAutocomplete(file_path, project_path) {
rename_similar_tokens=[this](const std::string &usr, const std::string &text) {
size_t number=0;
if(clang_readable) {
if(source_readable) {
auto offsets=clang_tokens->get_similar_token_offsets(usr);
std::vector<std::pair<Glib::RefPtr<Gtk::TextMark>, Glib::RefPtr<Gtk::TextMark> > > marks;
for(auto &offset: offsets) {
@ -1177,14 +1452,18 @@ Source::ClangViewAutocomplete(file_path, project_path) {
get_buffer()->signal_mark_set().connect([this](const Gtk::TextBuffer::iterator& iterator, const Glib::RefPtr<Gtk::TextBuffer::Mark>& mark){
if(mark->get_name()=="insert") {
auto usr=get_token();
tag_similar_tokens(usr);
delayed_tag_similar_tokens_connection.disconnect();
delayed_tag_similar_tokens_connection=Glib::signal_timeout().connect([this]() {
auto usr=get_token();
tag_similar_tokens(usr);
return false;
}, 100);
}
});
get_declaration_location=[this](){
std::pair<std::string, clang::Offset> location;
if(clang_readable) {
if(source_readable) {
auto iter=get_buffer()->get_insert()->get_iter();
auto line=(unsigned)iter.get_line();
auto index=(unsigned)iter.get_line_index();
@ -1205,7 +1484,7 @@ Source::ClangViewAutocomplete(file_path, project_path) {
};
goto_method=[this](){
if(clang_readable) {
if(source_readable) {
selection_dialog=std::unique_ptr<SelectionDialog>(new SelectionDialog(*this, get_buffer()->create_mark(get_buffer()->get_insert()->get_iter())));
auto rows=std::make_shared<std::unordered_map<std::string, clang::Offset> >();
auto methods=clang_tokens->get_cxx_methods();
@ -1227,7 +1506,12 @@ Source::ClangViewAutocomplete(file_path, project_path) {
};
}
Source::ClangView::ClangView(const boost::filesystem::path &file_path, const boost::filesystem::path& project_path): ClangViewRefactor(file_path, project_path) {
Source::ClangView::ClangView(const boost::filesystem::path &file_path, const boost::filesystem::path& project_path, Glib::RefPtr<Gsv::Language> language): ClangViewRefactor(file_path, project_path) {
if(language) {
get_source_buffer()->set_highlight_syntax(false);
get_source_buffer()->set_language(language);
}
do_delete_object.connect([this](){
if(delete_thread.joinable())
delete_thread.join();

53
src/source.h

@ -14,6 +14,8 @@
#include "tooltips.h"
#include "selectiondialog.h"
#include <set>
#include <regex>
#include <aspell.h>
namespace Source {
Glib::RefPtr<Gsv::Language> guess_language(const boost::filesystem::path &file_path);
@ -22,9 +24,11 @@ namespace Source {
public:
std::string style;
std::string font;
unsigned tab_size;
char tab_char;
std::string tab;
std::string spellcheck_language;
bool auto_tab_char_and_size;
char default_tab_char;
unsigned default_tab_size;
bool wrap_lines;
bool highlight_current_line;
bool show_line_numbers;
std::unordered_map<std::string, std::string> clang_types;
@ -73,16 +77,39 @@ namespace Source {
std::function<void(View* view, const std::string &status)> on_update_status;
std::string status;
protected:
bool source_readable;
Tooltips diagnostic_tooltips;
Tooltips type_tooltips;
Tooltips spellcheck_tooltips;
gdouble on_motion_last_x;
gdouble on_motion_last_y;
sigc::connection delayed_tooltips_connection;
void set_tooltip_events();
void set_status(const std::string &status);
std::string get_line(size_t line_number);
std::string get_line_before_insert();
bool on_key_press_event(GdkEventKey* key);
bool on_button_press_event(GdkEventButton *event);
std::pair<char, unsigned> find_tab_char_and_size();
unsigned tab_size;
char tab_char;
std::string tab;
std::regex tabs_regex;
bool spellcheck_all=false;
private:
GtkSourceSearchContext *search_context;
GtkSourceSearchSettings *search_settings;
static void search_occurrences_updated(GtkWidget* widget, GParamSpec* property, gpointer data);
static AspellConfig* spellcheck_config;
AspellCanHaveError *spellcheck_possible_err;
AspellSpeller *spellcheck_checker;
void spellcheck(Gtk::TextIter iter);
}; // class View
class GenericView : public View {
@ -94,21 +121,22 @@ namespace Source {
public:
ClangViewParse(const boost::filesystem::path &file_path, const boost::filesystem::path& project_path);
boost::filesystem::path project_path;
void start_reparse();
protected:
void init_parse();
void start_reparse();
bool on_key_press_event(GdkEventKey* key);
bool on_focus_out_event(GdkEventFocus* event);
std::unique_ptr<clang::TranslationUnit> clang_tu;
std::mutex parsing_mutex;
std::unique_ptr<clang::Tokens> clang_tokens;
bool clang_readable;
sigc::connection delayed_reparse_connection;
sigc::connection delayed_tooltips_connection;
std::shared_ptr<Terminal::InProgress> parsing_in_progress;
std::thread parse_thread;
std::atomic<bool> parse_thread_stop;
std::regex bracket_regex;
std::regex no_bracket_statement_regex;
std::regex no_bracket_no_para_statement_regex;
private:
std::map<std::string, std::string> get_buffer_map() const;
// inits the syntax highligthing on file open
@ -118,12 +146,7 @@ namespace Source {
std::set<std::string> last_syntax_tags;
void update_diagnostics();
void update_types();
Tooltips diagnostic_tooltips;
Tooltips type_tooltips;
bool on_motion_notify_event(GdkEventMotion* event);
void on_mark_set(const Gtk::TextBuffer::iterator& iterator, const Glib::RefPtr<Gtk::TextBuffer::Mark>& mark);
bool on_scroll_event(GdkEventScroll* event);
static clang::Index clang_index;
std::vector<std::string> get_compilation_commands();
@ -140,7 +163,6 @@ namespace Source {
ClangViewAutocomplete(const boost::filesystem::path &file_path, const boost::filesystem::path& project_path);
protected:
bool on_key_press_event(GdkEventKey* key);
bool on_focus_out_event(GdkEventFocus* event);
std::thread autocomplete_thread;
private:
void start_autocomplete();
@ -163,13 +185,14 @@ namespace Source {
private:
Glib::RefPtr<Gtk::TextTag> similar_tokens_tag;
std::string last_similar_tokens_tagged;
sigc::connection delayed_tag_similar_tokens_connection;
std::unique_ptr<SelectionDialog> selection_dialog;
bool renaming=false;
};
class ClangView : public ClangViewRefactor {
public:
ClangView(const boost::filesystem::path &file_path, const boost::filesystem::path& project_path);
ClangView(const boost::filesystem::path &file_path, const boost::filesystem::path& project_path, Glib::RefPtr<Gsv::Language> language);
void async_delete();
bool restart_parse();
private:

27
src/sourcefile.cc

@ -7,7 +7,7 @@ const size_t buffer_size=131072;
//Only use on small files
std::string juci::filesystem::read(const std::string &path) {
std::stringstream ss;
std::ifstream input(path);
std::ifstream input(path, std::ofstream::binary);
if(input) {
ss << input.rdbuf();
input.close();
@ -16,17 +16,22 @@ std::string juci::filesystem::read(const std::string &path) {
}
bool juci::filesystem::read(const std::string &path, Glib::RefPtr<Gtk::TextBuffer> text_buffer) {
std::ifstream input(path);
std::ifstream input(path, std::ofstream::binary);
if(input) {
std::vector<char> buffer(buffer_size);
size_t read_length;
std::string buffer_str;
while((read_length=input.read(&buffer[0], buffer_size).gcount())>0) {
auto ustr=Glib::ustring(std::string(&buffer[0], read_length));
if(ustr.validate())
text_buffer->insert_at_cursor(ustr);
else {
input.close();
return false;
buffer_str+=std::string(&buffer[0], read_length);
if(buffer_str.back()>=0) {
auto ustr=Glib::ustring(buffer_str);
buffer_str="";
if(ustr.validate())
text_buffer->insert_at_cursor(ustr);
else {
input.close();
return false;
}
}
}
input.close();
@ -38,7 +43,7 @@ bool juci::filesystem::read(const std::string &path, Glib::RefPtr<Gtk::TextBuffe
//Only use on small files
std::vector<std::string> juci::filesystem::read_lines(const std::string &path) {
std::vector<std::string> res;
std::ifstream input(path);
std::ifstream input(path, std::ofstream::binary);
if (input) {
do { res.emplace_back(); } while(getline(input, res.back()));
}
@ -48,7 +53,7 @@ std::vector<std::string> juci::filesystem::read_lines(const std::string &path) {
//Only use on small files
bool juci::filesystem::write(const std::string &path, const std::string &new_content) {
std::ofstream output(path);
std::ofstream output(path, std::ofstream::binary);
if(output)
output << new_content;
else
@ -58,7 +63,7 @@ bool juci::filesystem::write(const std::string &path, const std::string &new_con
}
bool juci::filesystem::write(const std::string &path, Glib::RefPtr<Gtk::TextBuffer> buffer) {
std::ofstream output(path);
std::ofstream output(path, std::ofstream::binary);
if(output) {
auto start_iter=buffer->begin();
auto end_iter=start_iter;

136
src/terminal.cc

@ -8,57 +8,74 @@
#include <iostream> //TODO: remove
using namespace std; //TODO: remove
//TODO: Windows...
//A working implementation of popen3, with all pipes getting closed properly.
//TODO: Eidheim is going to publish this one on his github, along with example uses
pid_t popen3(const char *command, int &stdin, int &stdout, int &stderr) {
pid_t popen3(const std::string &command, const std::string &path, int *stdin_fd, int *stdout_fd, int *stderr_fd) {
pid_t pid;
int p_stdin[2], p_stdout[2], p_stderr[2];
int stdin_p[2], stdout_p[2], stderr_p[2];
if (pipe(p_stdin) != 0 || pipe(p_stdout) != 0 || pipe(p_stderr) != 0) {
close(p_stdin[0]);
close(p_stdout[0]);
close(p_stderr[0]);
close(p_stdin[1]);
close(p_stdout[1]);
close(p_stderr[1]);
if(stdin_fd!=nullptr && pipe(stdin_p)!=0) {
close(stdin_p[0]);
close(stdin_p[1]);
return -1;
}
if(stdout_fd!=nullptr && pipe(stdout_p)!=0) {
if(stdin_fd!=nullptr) close(stdin_p[0]);
if(stdin_fd!=nullptr) close(stdin_p[1]);
close(stdout_p[0]);
close(stdout_p[1]);
return -1;
}
if(stderr_fd!=nullptr && pipe(stderr_p)!=0) {
if(stdin_fd!=nullptr) close(stdin_p[0]);
if(stdin_fd!=nullptr) close(stdin_p[1]);
if(stdout_fd!=nullptr) close(stdout_p[0]);
if(stdout_fd!=nullptr) close(stdout_p[1]);
close(stderr_p[0]);
close(stderr_p[1]);
return -1;
}
pid = fork();
if (pid < 0) {
close(p_stdin[0]);
close(p_stdout[0]);
close(p_stderr[0]);
close(p_stdin[1]);
close(p_stdout[1]);
close(p_stderr[1]);
if(stdin_fd!=nullptr) close(stdin_p[0]);
if(stdin_fd!=nullptr) close(stdin_p[1]);
if(stdout_fd!=nullptr) close(stdout_p[0]);
if(stdout_fd!=nullptr) close(stdout_p[1]);
if(stderr_fd!=nullptr) close(stderr_p[0]);
if(stderr_fd!=nullptr) close(stderr_p[1]);
return pid;
}
else if (pid == 0) {
close(p_stdin[1]);
close(p_stdout[0]);
close(p_stderr[0]);
dup2(p_stdin[0], 0);
dup2(p_stdout[1], 1);
dup2(p_stderr[1], 2);
if(stdin_fd!=nullptr) close(stdin_p[1]);
if(stdout_fd!=nullptr) close(stdout_p[0]);
if(stderr_fd!=nullptr) close(stderr_p[0]);
if(stdin_fd!=nullptr) dup2(stdin_p[0], 0);
if(stdout_fd!=nullptr) dup2(stdout_p[1], 1);
if(stderr_fd!=nullptr) dup2(stderr_p[1], 2);
setpgid(0, 0);
//TODO: See here on how to emulate tty for colors: http://stackoverflow.com/questions/1401002/trick-an-application-into-thinking-its-stdin-is-interactive-not-a-pipe
//TODO: One solution is: echo "command;exit"|script -q /dev/null
execl("/bin/sh", "sh", "-c", command, NULL);
std::string cd_path_and_command;
if(path!="") {
cd_path_and_command="cd \""+path+"\" && "+command;
}
else
cd_path_and_command=command;
execl("/bin/sh", "sh", "-c", cd_path_and_command.c_str(), NULL);
perror("execl");
exit(EXIT_FAILURE);
}
close(p_stdin[0]);
close(p_stdout[1]);
close(p_stderr[1]);
if(stdin_fd!=nullptr) close(stdin_p[0]);
if(stdout_fd!=nullptr) close(stdout_p[1]);
if(stderr_fd!=nullptr) close(stderr_p[1]);
stdin = p_stdin[1];
stdout = p_stdout[0];
stderr = p_stderr[0];
if(stdin_fd!=nullptr) *stdin_fd = stdin_p[1];
if(stdout_fd!=nullptr) *stdout_fd = stdout_p[0];
if(stderr_fd!=nullptr) *stderr_fd = stderr_p[0];
return pid;
}
@ -137,26 +154,18 @@ Terminal::Terminal() {
}
int Terminal::execute(const std::string &command, const boost::filesystem::path &path) {
std::string cd_path_and_command;
if(path!="") {
//TODO: Windows...
cd_path_and_command="cd \""+path.string()+"\" && "+command;
}
else
cd_path_and_command=command;
int stdin, stdout, stderr;
auto pid=popen3(cd_path_and_command.c_str(), stdin, stdout, stderr);
int stdin_fd, stdout_fd, stderr_fd;
auto pid=popen3(command, path.string(), &stdin_fd, &stdout_fd, &stderr_fd);
if (pid<=0) {
async_print("Error: Failed to run command: " + command + "\n");
return -1;
}
else {
std::thread stderr_thread([this, stderr](){
std::thread stderr_thread([this, stderr_fd](){
char buffer[1024];
ssize_t n;
while ((n=read(stderr, buffer, 1024)) > 0) {
while ((n=read(stderr_fd, buffer, 1024)) > 0) {
std::string message;
for(ssize_t c=0;c<n;c++)
message+=buffer[c];
@ -164,11 +173,11 @@ int Terminal::execute(const std::string &command, const boost::filesystem::path
}
});
stderr_thread.detach();
std::thread stdout_thread([this, stdout](){
std::thread stdout_thread([this, stdout_fd](){
char buffer[1024];
ssize_t n;
INFO("read");
while ((n=read(stdout, buffer, 1024)) > 0) {
while ((n=read(stdout_fd, buffer, 1024)) > 0) {
std::string message;
for(ssize_t c=0;c<n;c++)
message+=buffer[c];
@ -179,9 +188,9 @@ int Terminal::execute(const std::string &command, const boost::filesystem::path
int exit_code;
waitpid(pid, &exit_code, 0);
close(stdin);
close(stdout);
close(stderr);
close(stdin_fd);
close(stdout_fd);
close(stderr_fd);
return exit_code;
}
@ -189,20 +198,11 @@ int Terminal::execute(const std::string &command, const boost::filesystem::path
void Terminal::async_execute(const std::string &command, const boost::filesystem::path &path, std::function<void(int exit_code)> callback) {
std::thread async_execute_thread([this, command, path, callback](){
std::string cd_path_and_command;
if(path!="") {
//TODO: Windows...
cd_path_and_command="cd \""+path.string()+"\" && "+command;
}
else
cd_path_and_command=command;
int stdin, stdout, stderr;
int stdin_fd, stdout_fd, stderr_fd;
async_executes_mutex.lock();
stdin_buffer.clear();
auto pid=popen3(cd_path_and_command.c_str(), stdin, stdout, stderr);
async_executes.emplace_back(pid, stdin);
auto pid=popen3(command, path.string(), &stdin_fd, &stdout_fd, &stderr_fd);
async_executes.emplace_back(pid, stdin_fd);
async_executes_mutex.unlock();
if (pid<=0) {
@ -211,27 +211,27 @@ void Terminal::async_execute(const std::string &command, const boost::filesystem
callback(-1);
}
else {
std::thread stderr_thread([this, stderr](){
std::thread stderr_thread([this, stderr_fd](){
char buffer[1024];
ssize_t n;
while ((n=read(stderr, buffer, 1024)) > 0) {
while ((n=read(stderr_fd, buffer, 1024)) > 0) {
std::string message;
for(ssize_t c=0;c<n;c++)
message+=buffer[c];
async_print(message, true);
}
}
});
stderr_thread.detach();
std::thread stdout_thread([this, stdout](){
std::thread stdout_thread([this, stdout_fd](){
char buffer[1024];
ssize_t n;
INFO("read");
while ((n=read(stdout, buffer, 1024)) > 0) {
while ((n=read(stdout_fd, buffer, 1024)) > 0) {
std::string message;
for(ssize_t c=0;c<n;c++)
message+=buffer[c];
async_print(message);
}
}
});
stdout_thread.detach();
@ -245,9 +245,9 @@ void Terminal::async_execute(const std::string &command, const boost::filesystem
}
}
stdin_buffer.clear();
close(stdin);
close(stdout);
close(stderr);
close(stdin_fd);
close(stdout_fd);
close(stderr_fd);
async_executes_mutex.unlock();
if(callback)

6
src/terminal.h

@ -13,6 +13,7 @@ class Terminal : public Gtk::TextView {
public:
class Config {
public:
std::string cmake_command;
std::string make_command;
};
@ -53,8 +54,11 @@ private:
Glib::RefPtr<Gtk::TextTag> bold_tag;
std::mutex async_executes_mutex;
#ifdef _WIN32
std::list<std::pair<void*, void*> > async_executes;
#else
std::list<std::pair<pid_t, int> > async_executes;
#endif
std::string stdin_buffer;
};

418
src/terminal_win.cc

@ -0,0 +1,418 @@
#include "terminal.h"
#include <iostream>
#include "logging.h"
#include "singletons.h"
#include <unistd.h>
#include <windows.h>
#include <iostream> //TODO: remove
using namespace std; //TODO: remove
#define BUFSIZE 1024
HANDLE popen3(const std::string &command, const std::string &path, HANDLE *stdin_h, HANDLE *stdout_h, HANDLE *stderr_h) {
HANDLE g_hChildStd_IN_Rd = NULL;
HANDLE g_hChildStd_IN_Wr = NULL;
HANDLE g_hChildStd_OUT_Rd = NULL;
HANDLE g_hChildStd_OUT_Wr = NULL;
HANDLE g_hChildStd_ERR_Rd = NULL;
HANDLE g_hChildStd_ERR_Wr = NULL;
SECURITY_ATTRIBUTES saAttr;
saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
saAttr.bInheritHandle = TRUE;
saAttr.lpSecurityDescriptor = NULL;
if (!CreatePipe(&g_hChildStd_IN_Rd, &g_hChildStd_IN_Wr, &saAttr, 0))
return NULL;
if(!SetHandleInformation(g_hChildStd_IN_Wr, HANDLE_FLAG_INHERIT, 0)) {
CloseHandle(g_hChildStd_IN_Rd);
CloseHandle(g_hChildStd_IN_Wr);
return NULL;
}
if (!CreatePipe(&g_hChildStd_OUT_Rd, &g_hChildStd_OUT_Wr, &saAttr, 0)) {
CloseHandle(g_hChildStd_IN_Rd);
CloseHandle(g_hChildStd_IN_Wr);
return NULL;
}
if(!SetHandleInformation(g_hChildStd_OUT_Rd, HANDLE_FLAG_INHERIT, 0)) {
CloseHandle(g_hChildStd_IN_Rd);
CloseHandle(g_hChildStd_IN_Wr);
CloseHandle(g_hChildStd_OUT_Rd);
CloseHandle(g_hChildStd_OUT_Wr);
return NULL;
}
if (!CreatePipe(&g_hChildStd_ERR_Rd, &g_hChildStd_ERR_Wr, &saAttr, 0)) {
CloseHandle(g_hChildStd_IN_Rd);
CloseHandle(g_hChildStd_IN_Wr);
CloseHandle(g_hChildStd_OUT_Rd);
CloseHandle(g_hChildStd_OUT_Wr);
return NULL;
}
if(!SetHandleInformation(g_hChildStd_ERR_Rd, HANDLE_FLAG_INHERIT, 0)) {
CloseHandle(g_hChildStd_IN_Rd);
CloseHandle(g_hChildStd_IN_Wr);
CloseHandle(g_hChildStd_OUT_Rd);
CloseHandle(g_hChildStd_OUT_Wr);
CloseHandle(g_hChildStd_ERR_Rd);
CloseHandle(g_hChildStd_ERR_Wr);
return NULL;
}
PROCESS_INFORMATION process_info;
STARTUPINFO siStartInfo;
ZeroMemory(&process_info, sizeof(PROCESS_INFORMATION));
ZeroMemory(&siStartInfo, sizeof(STARTUPINFO));
siStartInfo.cb = sizeof(STARTUPINFO);
siStartInfo.hStdError = g_hChildStd_ERR_Wr;
siStartInfo.hStdOutput = g_hChildStd_OUT_Wr;
siStartInfo.hStdInput = g_hChildStd_IN_Rd;
siStartInfo.dwFlags |= STARTF_USESTDHANDLES;
char* path_ptr;
if(path=="")
path_ptr=NULL;
else {
path_ptr=new char[path.size()+1];
std::strcpy(path_ptr, path.c_str());
}
char* command_cstr=new char[command.size()+1];
std::strcpy(command_cstr, command.c_str());
BOOL bSuccess = CreateProcess(NULL,
command_cstr, // command line
NULL, // process security attributes
NULL, // primary thread security attributes
TRUE, // handles are inherited
0, // creation flags
NULL, // use parent's environment
path_ptr, // use parent's current directory
&siStartInfo, // STARTUPINFO pointer
&process_info); // receives PROCESS_INFORMATION
if(!bSuccess) {
CloseHandle(process_info.hProcess);
CloseHandle(process_info.hThread);
CloseHandle(g_hChildStd_IN_Rd);
CloseHandle(g_hChildStd_OUT_Wr);
CloseHandle(g_hChildStd_ERR_Wr);
return NULL;
}
else {
// Close handles to the child process and its primary thread.
// Some applications might keep these handles to monitor the status
// of the child process, for example.
CloseHandle(process_info.hThread);
CloseHandle(g_hChildStd_IN_Rd);
CloseHandle(g_hChildStd_OUT_Wr);
CloseHandle(g_hChildStd_ERR_Wr);
}
*stdin_h=g_hChildStd_IN_Wr;
*stdout_h=g_hChildStd_OUT_Rd;
*stderr_h=g_hChildStd_ERR_Rd;
return process_info.hProcess;
}
Terminal::InProgress::InProgress(const std::string& start_msg): stop(false) {
waiting_print.connect([this](){
Singleton::terminal()->async_print(line_nr-1, ".");
});
start(start_msg);
}
Terminal::InProgress::~InProgress() {
stop=true;
if(wait_thread.joinable())
wait_thread.join();
}
void Terminal::InProgress::start(const std::string& msg) {
line_nr=Singleton::terminal()->print(msg+"...\n");
wait_thread=std::thread([this](){
size_t c=0;
while(!stop) {
if(c%100==0)
waiting_print();
std::this_thread::sleep_for(std::chrono::milliseconds(10));
c++;
}
});
}
void Terminal::InProgress::done(const std::string& msg) {
if(!stop) {
stop=true;
Singleton::terminal()->async_print(line_nr-1, msg);
}
}
void Terminal::InProgress::cancel(const std::string& msg) {
if(!stop) {
stop=true;
Singleton::terminal()->async_print(line_nr-1, msg);
}
}
Terminal::Terminal() {
bold_tag=get_buffer()->create_tag();
bold_tag->property_weight()=PANGO_WEIGHT_BOLD;
signal_size_allocate().connect([this](Gtk::Allocation& allocation){
auto iter=get_buffer()->end();
if(iter.backward_char()) {
auto mark=get_buffer()->create_mark(iter);
scroll_to(mark, 0.0, 1.0, 1.0);
get_buffer()->delete_mark(mark);
}
});
async_print_dispatcher.connect([this](){
async_print_strings_mutex.lock();
if(async_print_strings.size()>0) {
for(auto &string_bold: async_print_strings)
print(string_bold.first, string_bold.second);
async_print_strings.clear();
}
async_print_strings_mutex.unlock();
});
async_print_on_line_dispatcher.connect([this](){
async_print_on_line_strings_mutex.lock();
if(async_print_on_line_strings.size()>0) {
for(auto &line_string: async_print_on_line_strings)
print(line_string.first, line_string.second);
async_print_on_line_strings.clear();
}
async_print_on_line_strings_mutex.unlock();
});
}
//Based on the example at https://msdn.microsoft.com/en-us/library/windows/desktop/ms682499(v=vs.85).aspx
int Terminal::execute(const std::string &command, const boost::filesystem::path &path) {
HANDLE stdin_h, stdout_h, stderr_h;
auto process=popen3(command, path.string(), &stdin_h, &stdout_h, &stderr_h);
if(process==NULL) {
async_print("Error: Failed to run command: " + command + "\n");
return -1;
}
std::thread stderr_thread([this, stderr_h](){
DWORD n;
CHAR buffer[BUFSIZE];
for (;;) {
BOOL bSuccess = ReadFile(stderr_h, buffer, BUFSIZE, &n, NULL);
if(!bSuccess || n == 0)
break;
std::string message;
for(DWORD c=0;c<n;c++)
message+=buffer[c];
async_print(message, true);
}
});
stderr_thread.detach();
std::thread stdout_thread([this, stdout_h](){
DWORD n;
CHAR buffer[BUFSIZE];
for (;;) {
BOOL bSuccess = ReadFile(stdout_h, buffer, BUFSIZE, &n, NULL);
if(!bSuccess || n == 0)
break;
std::string message;
for(DWORD c=0;c<n;c++)
message+=buffer[c];
async_print(message);
}
});
stdout_thread.detach();
unsigned long exit_code;
WaitForSingleObject(process, INFINITE);
GetExitCodeProcess(process, &exit_code);
CloseHandle(process);
CloseHandle(stdin_h);
CloseHandle(stdout_h);
CloseHandle(stderr_h);
return exit_code;
}
void Terminal::async_execute(const std::string &command, const boost::filesystem::path &path, std::function<void(int exit_code)> callback) {
std::thread async_execute_thread([this, command, path, callback](){
HANDLE stdin_h, stdout_h, stderr_h;
async_executes_mutex.lock();
stdin_buffer.clear();
auto process=popen3(command, path.string(), &stdin_h, &stdout_h, &stderr_h);
if(process==NULL) {
async_executes_mutex.unlock();
async_print("Error: Failed to run command: " + command + "\n");
if(callback)
callback(-1);
return;
}
async_executes.emplace_back(process, stdin_h);
async_executes_mutex.unlock();
std::thread stderr_thread([this, stderr_h](){
DWORD n;
CHAR buffer[BUFSIZE];
for (;;) {
BOOL bSuccess = ReadFile(stderr_h, buffer, BUFSIZE, &n, NULL);
if(!bSuccess || n == 0)
break;
std::string message;
for(DWORD c=0;c<n;c++)
message+=buffer[c];
async_print(message, true);
}
});
stderr_thread.detach();
std::thread stdout_thread([this, stdout_h](){
DWORD n;
CHAR buffer[BUFSIZE];
for (;;) {
BOOL bSuccess = ReadFile(stdout_h, buffer, BUFSIZE, &n, NULL);
if(!bSuccess || n == 0)
break;
std::string message;
for(DWORD c=0;c<n;c++)
message+=buffer[c];
async_print(message);
}
});
stdout_thread.detach();
unsigned long exit_code;
WaitForSingleObject(process, INFINITE);
GetExitCodeProcess(process, &exit_code);
async_executes_mutex.lock();
for(auto it=async_executes.begin();it!=async_executes.end();it++) {
if(it->first==process) {
async_executes.erase(it);
break;
}
}
stdin_buffer.clear();
CloseHandle(process);
CloseHandle(stdin_h);
CloseHandle(stdout_h);
CloseHandle(stderr_h);
async_executes_mutex.unlock();
if(callback)
callback(exit_code);
});
async_execute_thread.detach();
}
void Terminal::kill_last_async_execute(bool force) {
async_executes_mutex.lock();
if(async_executes.size()>0) {
TerminateProcess(async_executes.back().first, 2);
}
async_executes_mutex.unlock();
}
void Terminal::kill_async_executes(bool force) {
async_executes_mutex.lock();
for(auto &async_execute: async_executes) {
TerminateProcess(async_execute.first, 2);
}
async_executes_mutex.unlock();
}
int Terminal::print(const std::string &message, bool bold){
INFO("Terminal: PrintMessage");
if(bold)
get_buffer()->insert_with_tag(get_buffer()->end(), message, bold_tag);
else
get_buffer()->insert(get_buffer()->end(), message);
auto iter=get_buffer()->end();
if(iter.backward_char()) {
auto mark=get_buffer()->create_mark(iter);
scroll_to(mark, 0.0, 1.0, 1.0);
get_buffer()->delete_mark(mark);
}
return get_buffer()->end().get_line();
}
void Terminal::print(int line_nr, const std::string &message){
INFO("Terminal: PrintMessage at line " << line_nr);
auto iter=get_buffer()->get_iter_at_line(line_nr);
while(!iter.ends_line())
iter++;
get_buffer()->insert(iter, message);
}
std::shared_ptr<Terminal::InProgress> Terminal::print_in_progress(std::string start_msg) {
std::shared_ptr<Terminal::InProgress> in_progress=std::shared_ptr<Terminal::InProgress>(new Terminal::InProgress(start_msg));
return in_progress;
}
void Terminal::async_print(const std::string &message, bool bold) {
async_print_strings_mutex.lock();
bool dispatch=true;
if(async_print_strings.size()>0)
dispatch=false;
async_print_strings.emplace_back(message, bold);
async_print_strings_mutex.unlock();
if(dispatch)
async_print_dispatcher();
}
void Terminal::async_print(int line_nr, const std::string &message) {
async_print_on_line_strings_mutex.lock();
bool dispatch=true;
if(async_print_on_line_strings.size()>0)
dispatch=false;
async_print_on_line_strings.emplace_back(line_nr, message);
async_print_on_line_strings_mutex.unlock();
if(dispatch)
async_print_on_line_dispatcher();
}
bool Terminal::on_key_press_event(GdkEventKey *event) {
async_executes_mutex.lock();
if(async_executes.size()>0) {
get_buffer()->place_cursor(get_buffer()->end());
auto unicode=gdk_keyval_to_unicode(event->keyval);
char chr=(char)unicode;
if(unicode>=32 && unicode<=126) {
stdin_buffer+=chr;
get_buffer()->insert_at_cursor(stdin_buffer.substr(stdin_buffer.size()-1));
scroll_to(get_buffer()->get_insert());
}
else if(event->keyval==GDK_KEY_BackSpace) {
if(stdin_buffer.size()>0 && get_buffer()->get_char_count()>0) {
auto iter=get_buffer()->end();
iter--;
stdin_buffer.pop_back();
get_buffer()->erase(iter, get_buffer()->end());
scroll_to(get_buffer()->get_insert());
}
}
else if(event->keyval==GDK_KEY_Return) {
stdin_buffer+='\n';
DWORD written;
WriteFile(async_executes.back().second, stdin_buffer.c_str(), stdin_buffer.size(), &written, NULL);
//TODO: is this line needed?
get_buffer()->insert_at_cursor(stdin_buffer.substr(stdin_buffer.size()-1));
scroll_to(get_buffer()->get_insert());
stdin_buffer.clear();
}
}
async_executes_mutex.unlock();
return true;
}

4
src/tooltips.h

@ -14,13 +14,13 @@ public:
Gdk::Rectangle activation_rectangle;
std::unique_ptr<Gtk::Window> window;
Glib::RefPtr<Gtk::TextBuffer::Mark> start_mark;
Glib::RefPtr<Gtk::TextBuffer::Mark> end_mark;
private:
void wrap_lines(Glib::RefPtr<Gtk::TextBuffer> text_buffer);
std::function<Glib::RefPtr<Gtk::TextBuffer>()> create_tooltip_buffer;
std::unique_ptr<Gtk::TextView> tooltip_widget;
Glib::RefPtr<Gtk::TextBuffer::Mark> start_mark;
Glib::RefPtr<Gtk::TextBuffer::Mark> end_mark;
Gtk::TextView& text_view;
int tooltip_width, tooltip_height;
};

167
src/window.cc

@ -3,7 +3,7 @@
#include "singletons.h"
#include "sourcefile.h"
#include "config.h"
#include "api.h"
//#include "api.h"
#include <boost/lexical_cast.hpp>
#include <iostream> //TODO: remove
@ -30,7 +30,7 @@ Window::Window() : box(Gtk::ORIENTATION_VERTICAL), notebook(directories), compil
add(box);
generate_keybindings();
PluginApi(&this->notebook, &this->menu);
//PluginApi(&this->notebook, &this->menu);
create_menu();
box.pack_start(menu.get_widget(), Gtk::PACK_SHRINK);
@ -91,7 +91,10 @@ Window::Window() : box(Gtk::ORIENTATION_VERTICAL), notebook(directories), compil
if(auto menu_item=dynamic_cast<Gtk::MenuItem*>(menu.ui_manager->get_widget("/MenuBar/SourceMenu/SourceRename")))
menu_item->set_sensitive((bool)notebook.get_current_view()->rename_similar_tokens);
directories.select_path(notebook.get_current_view()->file_path);
directories.select(notebook.get_current_view()->file_path);
if(auto source_view=dynamic_cast<Source::ClangView*>(notebook.get_current_view()))
source_view->start_reparse();
Singleton::status()->set_text(notebook.get_current_view()->status);
}
@ -99,10 +102,6 @@ Window::Window() : box(Gtk::ORIENTATION_VERTICAL), notebook(directories), compil
notebook.signal_page_removed().connect([this](Gtk::Widget* page, guint page_num) {
entry_box.hide();
});
compile_success.connect([this](){
directories.open_folder();
});
INFO("Window created");
} // Window constructor
@ -112,20 +111,23 @@ void Window::create_menu() {
menu.action_group->add(Gtk::Action::create("FileQuit", "Quit juCi++"), Gtk::AccelKey(menu.key_map["quit"]), [this]() {
hide();
});
menu.action_group->add(Gtk::Action::create("FileNewFile", "New file"), Gtk::AccelKey(menu.key_map["new_file"]), [this]() {
new_file_entry();
menu.action_group->add(Gtk::Action::create("FileNewFile", "New File"), Gtk::AccelKey(menu.key_map["new_file"]), [this]() {
new_file_dialog();
});
menu.action_group->add(Gtk::Action::create("FileNewFolder", "New Folder"), Gtk::AccelKey(menu.key_map["new_folder"]), [this]() {
new_folder_dialog();
});
menu.action_group->add(Gtk::Action::create("FileNewProject", "New Project"));
menu.action_group->add(Gtk::Action::create("FileNewProjectCpp", "C++"), [this]() {
new_cpp_project_dialog();
});
menu.action_group->add(Gtk::Action::create("FileOpenFile", "Open file"), Gtk::AccelKey(menu.key_map["open_file"]), [this]() {
menu.action_group->add(Gtk::Action::create("FileOpenFile", "Open File"), Gtk::AccelKey(menu.key_map["open_file"]), [this]() {
open_file_dialog();
});
menu.action_group->add(Gtk::Action::create("FileOpenFolder", "Open folder"), Gtk::AccelKey(menu.key_map["open_folder"]), [this]() {
menu.action_group->add(Gtk::Action::create("FileOpenFolder", "Open Folder"), Gtk::AccelKey(menu.key_map["open_folder"]), [this]() {
open_folder_dialog();
});
menu.action_group->add(Gtk::Action::create("FileSaveAs", "Save as"), Gtk::AccelKey(menu.key_map["save_as"]), [this]() {
menu.action_group->add(Gtk::Action::create("FileSaveAs", "Save As"), Gtk::AccelKey(menu.key_map["save_as"]), [this]() {
save_file_dialog();
});
@ -163,6 +165,7 @@ void Window::create_menu() {
auto undo_manager = notebook.get_current_view()->get_source_buffer()->get_undo_manager();
if (undo_manager->can_undo()) {
undo_manager->undo();
notebook.get_current_view()->scroll_to(notebook.get_current_view()->get_buffer()->get_insert());
}
}
INFO("Done undo");
@ -173,22 +176,23 @@ void Window::create_menu() {
auto undo_manager = notebook.get_current_view()->get_source_buffer()->get_undo_manager();
if(undo_manager->can_redo()) {
undo_manager->redo();
notebook.get_current_view()->scroll_to(notebook.get_current_view()->get_buffer()->get_insert());
}
}
INFO("Done Redo");
});
menu.action_group->add(Gtk::Action::create("SourceGotoLine", "Go to line"), Gtk::AccelKey(menu.key_map["source_goto_line"]), [this]() {
menu.action_group->add(Gtk::Action::create("SourceGotoLine", "Go to Line"), Gtk::AccelKey(menu.key_map["source_goto_line"]), [this]() {
goto_line_entry();
});
menu.action_group->add(Gtk::Action::create("SourceCenterCursor", "Center cursor"), Gtk::AccelKey(menu.key_map["source_center_cursor"]), [this]() {
menu.action_group->add(Gtk::Action::create("SourceCenterCursor", "Center Cursor"), Gtk::AccelKey(menu.key_map["source_center_cursor"]), [this]() {
if(notebook.get_current_page()!=-1) {
while(gtk_events_pending())
gtk_main_iteration();
notebook.get_current_view()->scroll_to(notebook.get_current_view()->get_buffer()->get_insert(), 0.0, 1.0, 0.5);
}
});
menu.action_group->add(Gtk::Action::create("SourceGotoDeclaration", "Go to declaration"), Gtk::AccelKey(menu.key_map["source_goto_declaration"]), [this]() {
menu.action_group->add(Gtk::Action::create("SourceGotoDeclaration", "Go to Declaration"), Gtk::AccelKey(menu.key_map["source_goto_declaration"]), [this]() {
if(notebook.get_current_page()!=-1) {
if(notebook.get_current_view()->get_declaration_location) {
auto location=notebook.get_current_view()->get_declaration_location();
@ -202,7 +206,7 @@ void Window::create_menu() {
}
}
});
menu.action_group->add(Gtk::Action::create("SourceGotoMethod", "Go to method"), Gtk::AccelKey(menu.key_map["source_goto_method"]), [this]() {
menu.action_group->add(Gtk::Action::create("SourceGotoMethod", "Go to Method"), Gtk::AccelKey(menu.key_map["source_goto_method"]), [this]() {
if(notebook.get_current_page()!=-1) {
if(notebook.get_current_view()->goto_method) {
notebook.get_current_view()->goto_method();
@ -213,11 +217,10 @@ void Window::create_menu() {
rename_token_entry();
});
menu.action_group->add(Gtk::Action::create("ProjectCompileAndRun", "Compile And Run"), Gtk::AccelKey(menu.key_map["compile_and_run"]), [this]() {
menu.action_group->add(Gtk::Action::create("ProjectCompileAndRun", "Compile and Run"), Gtk::AccelKey(menu.key_map["compile_and_run"]), [this]() {
if(notebook.get_current_page()==-1 || compiling)
return;
CMake cmake(notebook.get_current_view()->file_path);
directories.open_folder();
auto executables = cmake.get_functions_parameters("add_executable");
boost::filesystem::path executable_path;
if(executables.size()>0 && executables[0].second.size()>0) {
@ -228,13 +231,10 @@ void Window::create_menu() {
if(executable_path!="") {
compiling=true;
Singleton::terminal()->print("Compiling and running "+executable_path.string()+"\n");
//TODO: Windows...
auto project_path=cmake.project_path;
Singleton::terminal()->async_execute(Singleton::Config::terminal()->make_command, cmake.project_path, [this, executable_path, project_path](int exit_code){
compiling=false;
if(exit_code==EXIT_SUCCESS) {
compile_success();
//TODO: Windows...
auto executable_path_spaces_fixed=executable_path.string();
char last_char=0;
for(size_t c=0;c<executable_path_spaces_fixed.size();c++) {
@ -261,15 +261,11 @@ void Window::create_menu() {
if(notebook.get_current_page()==-1 || compiling)
return;
CMake cmake(notebook.get_current_view()->file_path);
directories.open_folder();
if(cmake.project_path!="") {
compiling=true;
Singleton::terminal()->print("Compiling project "+cmake.project_path.string()+"\n");
//TODO: Windows...
Singleton::terminal()->async_execute(Singleton::Config::terminal()->make_command, cmake.project_path, [this](int exit_code){
compiling=false;
if(exit_code==EXIT_SUCCESS)
compile_success();
});
}
});
@ -298,7 +294,7 @@ void Window::create_menu() {
Singleton::terminal()->kill_last_async_execute(true);
});
menu.action_group->add(Gtk::Action::create("WindowCloseTab", "Close tab"), Gtk::AccelKey(menu.key_map["close_tab"]), [this]() {
menu.action_group->add(Gtk::Action::create("WindowCloseTab", "Close Tab"), Gtk::AccelKey(menu.key_map["close_tab"]), [this]() {
notebook.close_current_page();
});
add_accel_group(menu.ui_manager->get_accel_group());
@ -355,35 +351,62 @@ void Window::hide() {
Gtk::Window::hide();
}
void Window::new_file_entry() {
entry_box.clear();
entry_box.entries.emplace_back("untitled", [this](const std::string& content){
std::string filename=content;
if(filename!="") {
if(directories.current_path!="" && !boost::filesystem::path(filename).is_absolute())
filename=directories.current_path.string()+"/"+filename;
boost::filesystem::path p(filename);
if(boost::filesystem::exists(p)) {
Singleton::terminal()->print("Error: "+p.string()+" already exists.\n");
void Window::new_file_dialog() {
Gtk::FileChooserDialog dialog("Please create a new file", Gtk::FILE_CHOOSER_ACTION_SAVE);
if(directories.current_path!="")
gtk_file_chooser_set_current_folder((GtkFileChooser*)dialog.gobj(), directories.current_path.string().c_str());
else
gtk_file_chooser_set_current_folder((GtkFileChooser*)dialog.gobj(), boost::filesystem::current_path().string().c_str());
dialog.set_transient_for(*this);
dialog.set_position(Gtk::WindowPosition::WIN_POS_CENTER_ALWAYS);
dialog.add_button("Cancel", Gtk::RESPONSE_CANCEL);
dialog.add_button("Save", Gtk::RESPONSE_OK);
int result = dialog.run();
if(result==Gtk::RESPONSE_OK) {
boost::filesystem::path path = dialog.get_filename();
if(path!="") {
if(boost::filesystem::exists(path)) {
Singleton::terminal()->print("Error: "+path.string()+" already exists.\n");
}
else {
if(juci::filesystem::write(p)) {
if(juci::filesystem::write(path)) {
if(directories.current_path!="")
directories.open_folder();
notebook.open(boost::filesystem::canonical(p).string());
Singleton::terminal()->print("New file "+p.string()+" created.\n");
directories.update();
notebook.open(path.string());
Singleton::terminal()->print("New file "+path.string()+" created.\n");
}
else
Singleton::terminal()->print("Error: could not create new file "+p.string()+".\n");
Singleton::terminal()->print("Error: could not create new file "+path.string()+".\n");
}
}
entry_box.hide();
});
auto entry_it=entry_box.entries.begin();
entry_box.buttons.emplace_back("Create file", [this, entry_it](){
entry_it->activate();
});
entry_box.show();
}
}
void Window::new_folder_dialog() {
auto time_now=std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
Gtk::FileChooserDialog dialog("Please create a new folder", Gtk::FILE_CHOOSER_ACTION_CREATE_FOLDER);
if(directories.current_path!="")
gtk_file_chooser_set_current_folder((GtkFileChooser*)dialog.gobj(), directories.current_path.string().c_str());
else
gtk_file_chooser_set_current_folder((GtkFileChooser*)dialog.gobj(), boost::filesystem::current_path().string().c_str());
dialog.set_transient_for(*this);
dialog.set_position(Gtk::WindowPosition::WIN_POS_CENTER_ALWAYS);
dialog.add_button("Cancel", Gtk::RESPONSE_CANCEL);
dialog.add_button("Create", Gtk::RESPONSE_OK);
int result = dialog.run();
if(result==Gtk::RESPONSE_OK) {
boost::filesystem::path path=dialog.get_filename();
if(boost::filesystem::last_write_time(path)>=time_now) {
if(directories.current_path!="")
directories.update();
Singleton::terminal()->print("New folder "+path.string()+" created.\n");
}
else
Singleton::terminal()->print("Error: "+path.string()+" already exists.\n");
directories.select(path);
}
}
void Window::new_cpp_project_dialog() {
@ -393,9 +416,9 @@ void Window::new_cpp_project_dialog() {
else
gtk_file_chooser_set_current_folder((GtkFileChooser*)dialog.gobj(), boost::filesystem::current_path().string().c_str());
dialog.set_transient_for(*this);
dialog.add_button("_Cancel", Gtk::RESPONSE_CANCEL);
dialog.add_button("Select", Gtk::RESPONSE_OK);
dialog.set_position(Gtk::WindowPosition::WIN_POS_CENTER_ALWAYS);
dialog.add_button("Cancel", Gtk::RESPONSE_CANCEL);
dialog.add_button("Create", Gtk::RESPONSE_OK);
int result = dialog.run();
if(result==Gtk::RESPONSE_OK) {
@ -420,7 +443,7 @@ void Window::new_cpp_project_dialog() {
std::string cmakelists="cmake_minimum_required(VERSION 2.8)\n\nproject("+project_name+")\n\nset(CMAKE_CXX_FLAGS \"${CMAKE_CXX_FLAGS} -std=c++1y -Wall\")\n\nadd_executable("+project_name+" main.cpp)\n";
std::string cpp_main="#include <iostream>\n\nusing namespace std;\n\nint main() {\n cout << \"Hello World!\" << endl;\n\n return 0;\n}\n";
if(juci::filesystem::write(cmakelists_path, cmakelists) && juci::filesystem::write(cpp_main_path, cpp_main)) {
directories.open_folder(project_path);
directories.open(project_path);
notebook.open(cpp_main_path);
Singleton::terminal()->print("C++ project "+project_name+" created.\n");
}
@ -436,15 +459,15 @@ void Window::open_folder_dialog() {
else
gtk_file_chooser_set_current_folder((GtkFileChooser*)dialog.gobj(), boost::filesystem::current_path().string().c_str());
dialog.set_transient_for(*this);
//Add response buttons the the dialog:
dialog.add_button("_Cancel", Gtk::RESPONSE_CANCEL);
dialog.add_button("Select", Gtk::RESPONSE_OK);
dialog.set_position(Gtk::WindowPosition::WIN_POS_CENTER_ALWAYS);
dialog.add_button("Cancel", Gtk::RESPONSE_CANCEL);
dialog.add_button("Open", Gtk::RESPONSE_OK);
int result = dialog.run();
if(result==Gtk::RESPONSE_OK) {
std::string project_path=dialog.get_filename();
directories.open_folder(project_path);
directories.open(project_path);
}
}
@ -456,28 +479,8 @@ void Window::open_file_dialog() {
gtk_file_chooser_set_current_folder((GtkFileChooser*)dialog.gobj(), boost::filesystem::current_path().string().c_str());
dialog.set_transient_for(*this);
dialog.set_position(Gtk::WindowPosition::WIN_POS_CENTER_ALWAYS);
//Add response buttons the the dialog:
dialog.add_button("_Cancel", Gtk::RESPONSE_CANCEL);
dialog.add_button("_Open", Gtk::RESPONSE_OK);
//Add filters, so that only certain file types can be selected:
Glib::RefPtr<Gtk::FileFilter> filter_text = Gtk::FileFilter::create();
filter_text->set_name("Text files");
filter_text->add_mime_type("text/plain");
dialog.add_filter(filter_text);
Glib::RefPtr<Gtk::FileFilter> filter_cpp = Gtk::FileFilter::create();
filter_cpp->set_name("C/C++ files");
filter_cpp->add_mime_type("text/x-c");
filter_cpp->add_mime_type("text/x-c++");
filter_cpp->add_mime_type("text/x-c-header");
dialog.add_filter(filter_cpp);
Glib::RefPtr<Gtk::FileFilter> filter_any = Gtk::FileFilter::create();
filter_any->set_name("Any files");
filter_any->add_pattern("*");
dialog.add_filter(filter_any);
dialog.add_button("Cancel", Gtk::RESPONSE_CANCEL);
dialog.add_button("Open", Gtk::RESPONSE_OK);
int result = dialog.run();
@ -492,10 +495,10 @@ void Window::save_file_dialog() {
return;
INFO("Save file dialog");
Gtk::FileChooserDialog dialog(*this, "Please choose a file", Gtk::FILE_CHOOSER_ACTION_SAVE);
gtk_file_chooser_set_filename((GtkFileChooser*)dialog.gobj(), notebook.get_current_view()->file_path.c_str());
gtk_file_chooser_set_filename((GtkFileChooser*)dialog.gobj(), notebook.get_current_view()->file_path.string().c_str());
dialog.set_position(Gtk::WindowPosition::WIN_POS_CENTER_ALWAYS);
dialog.add_button("_Cancel", Gtk::RESPONSE_CANCEL);
dialog.add_button("_Save", Gtk::RESPONSE_OK);
dialog.add_button("Cancel", Gtk::RESPONSE_CANCEL);
dialog.add_button("Save", Gtk::RESPONSE_OK);
int result = dialog.run();
if(result==Gtk::RESPONSE_OK) {
@ -506,7 +509,7 @@ void Window::save_file_dialog() {
file << notebook.get_current_view()->get_buffer()->get_text();
file.close();
if(directories.current_path!="")
directories.open_folder();
directories.update();
notebook.open(path);
Singleton::terminal()->print("File saved to: " + notebook.get_current_view()->file_path.string()+"\n");
}

4
src/window.h

@ -36,11 +36,11 @@ private:
EntryBox entry_box;
Menu menu;
std::atomic<bool> compiling;
Glib::Dispatcher compile_success;
void create_menu();
void hide();
void new_file_entry();
void new_file_dialog();
void new_folder_dialog();
void new_cpp_project_dialog();
void open_folder_dialog();
void open_file_dialog();

Loading…
Cancel
Save