├── .editorconfig ├── .github ├── FUNDING.yml ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md └── workflows │ └── build_test.yml ├── .gitignore ├── CONTRIBUTING.md ├── LICENSE ├── NEWS ├── README.md ├── TODO.md ├── copr ├── fsearch_nightly.spec └── fsearch_release.spec ├── data ├── fsearch.1 ├── gresource.xml ├── io.github.cboxdoerfer.FSearch.desktop.in.in ├── io.github.cboxdoerfer.FSearch.metainfo.xml.in ├── io.github.cboxdoerfer.FSearch.svg ├── meson.build └── screenshots │ ├── 01-main_window_headerbar.png │ └── 02-main_window_menubar.png ├── debian ├── changelog ├── compat ├── control ├── rules └── source │ └── format ├── fsearch.doap ├── help ├── C │ ├── LINGUAS │ ├── figures │ │ └── FSearch.png │ ├── index.page │ ├── legal.xml │ ├── search_syntax.page │ ├── search_syntax_escaping.page │ ├── search_syntax_functions.page │ ├── search_syntax_modifiers.page │ ├── search_syntax_operators.page │ └── search_syntax_wildcards.page └── meson.build ├── meson.build ├── meson_options.txt ├── po ├── LINGUAS ├── Makevars ├── POTFILES.in ├── ar.po ├── ber.po ├── bg.po ├── ca.po ├── de.po ├── el.po ├── en_GB.po ├── es.po ├── eu.po ├── fi.po ├── fr.po ├── fsearch.pot ├── gl.po ├── he.po ├── hu.po ├── id.po ├── ie.po ├── it.po ├── ja.po ├── ko.po ├── lt.po ├── meson.build ├── mr.po ├── nb_NO.po ├── nl.po ├── pl.po ├── pt.po ├── pt_BR.po ├── ro.po ├── ru.po ├── sk.po ├── sv.po ├── te.po ├── tr.po ├── tzm.po ├── uk.po ├── zgh.po └── zh_CN.po ├── snap └── snapcraft.yaml └── src ├── .clang-format ├── fsearch.c ├── fsearch.h ├── fsearch_array.c ├── fsearch_array.h ├── fsearch_clipboard.c ├── fsearch_clipboard.h ├── fsearch_config.c ├── fsearch_config.h ├── fsearch_database.c ├── fsearch_database.h ├── fsearch_database_entry.c ├── fsearch_database_entry.h ├── fsearch_database_index.c ├── fsearch_database_index.h ├── fsearch_database_search.c ├── fsearch_database_search.h ├── fsearch_database_view.c ├── fsearch_database_view.h ├── fsearch_exclude_path.c ├── fsearch_exclude_path.h ├── fsearch_file_utils.c ├── fsearch_file_utils.h ├── fsearch_filter.c ├── fsearch_filter.h ├── fsearch_filter_editor.c ├── fsearch_filter_editor.h ├── fsearch_filter_editor.ui ├── fsearch_filter_manager.c ├── fsearch_filter_manager.h ├── fsearch_index.c ├── fsearch_index.h ├── fsearch_limits.h ├── fsearch_list_view.c ├── fsearch_list_view.h ├── fsearch_listview_popup.c ├── fsearch_listview_popup.h ├── fsearch_memory_pool.c ├── fsearch_memory_pool.h ├── fsearch_overlay.ui ├── fsearch_preferences.ui ├── fsearch_preferences_ui.c ├── fsearch_preferences_ui.h ├── fsearch_preferences_widgets.c ├── fsearch_preferences_widgets.h ├── fsearch_preview.c ├── fsearch_preview.h ├── fsearch_query.c ├── fsearch_query.h ├── fsearch_query_flags.h ├── fsearch_query_lexer.c ├── fsearch_query_lexer.h ├── fsearch_query_match_data.c ├── fsearch_query_match_data.h ├── fsearch_query_matchers.c ├── fsearch_query_matchers.h ├── fsearch_query_node.c ├── fsearch_query_node.h ├── fsearch_query_parser.c ├── fsearch_query_parser.h ├── fsearch_query_tree.c ├── fsearch_query_tree.h ├── fsearch_result_view.c ├── fsearch_result_view.h ├── fsearch_selection.c ├── fsearch_selection.h ├── fsearch_size_utils.c ├── fsearch_size_utils.h ├── fsearch_statusbar.c ├── fsearch_statusbar.h ├── fsearch_statusbar.ui ├── fsearch_string_utils.c ├── fsearch_string_utils.h ├── fsearch_task.c ├── fsearch_task.h ├── fsearch_task_ids.h ├── fsearch_thread_pool.c ├── fsearch_thread_pool.h ├── fsearch_time_utils.c ├── fsearch_time_utils.h ├── fsearch_ui_utils.c ├── fsearch_ui_utils.h ├── fsearch_utf.c ├── fsearch_utf.h ├── fsearch_window.c ├── fsearch_window.h ├── fsearch_window.ui ├── fsearch_window_actions.c ├── fsearch_window_actions.h ├── gresource.xml ├── main.c ├── menus.ui ├── meson.build ├── shared.css ├── strverscmp.c ├── strverscmp.h └── tests ├── meson.build ├── test_array.c ├── test_query.c ├── test_size_utils.c ├── test_string_utils.c └── test_time_utils.c /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | end_of_line = lf 6 | indent_style = space 7 | indent_size = 4 8 | insert_final_newline = false 9 | max_line_length = 120 10 | tab_width = 8 11 | trim_trailing_whitespace = true -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: cboxdoerfer # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] 4 | patreon: # Replace with a single Patreon username 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | custom: "https://www.paypal.com/donate/?hosted_button_id=TTXBUD7PMZXN2" 13 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve FSearch 4 | title: "[BUG]" 5 | labels: bug 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Desktop (please complete the following information):** 27 | - OS: [e.g. Ubuntu 20.04] 28 | - How you installed FSearch [e.g. built manually, PPA, AUR] 29 | - Version [output of `fsearch --version`] 30 | - Output of `localectl status` 31 | 32 | **Additional context** 33 | Add any other context about the problem here. 34 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project (one idea per issue) 4 | title: '' 5 | labels: feature_request 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.github/workflows/build_test.yml: -------------------------------------------------------------------------------- 1 | name: build 2 | on: 3 | push: 4 | branches: [ master ] 5 | pull_request: 6 | branches: [ master ] 7 | 8 | jobs: 9 | build: 10 | 11 | runs-on: ubuntu-20.04 12 | 13 | steps: 14 | - uses: actions/checkout@v2 15 | - name: install build dependencies 16 | run: sudo apt-get -qq update && sudo apt-get install -y build-essential meson appstream libtool itstool pkg-config intltool libicu-dev libpcre3-dev libglib2.0-dev libgtk-3-dev libxml2-utils 17 | - name: setup meson build config 18 | run: meson build 19 | - name: run meson build 20 | run: ninja -C build 21 | - name: run tests 22 | run: ninja -C build test 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Build files 2 | Makefile.in 3 | /build 4 | /build* 5 | /builddir 6 | /Makefile 7 | /data/Makefile 8 | /data/fsearch.desktop 9 | /data/fsearch.desktop.in 10 | /po/*.mo 11 | /po/*.gmo 12 | /po/Makefile 13 | /po/Makefile.in.in 14 | /po/.intltool-merge-cache 15 | /src/Makefile 16 | /autom4te.cache 17 | /autoscan.log 18 | /autoscan-*.log 19 | /aclocal.m4 20 | /compile 21 | /config.h.in 22 | /configure 23 | /configure.scan 24 | /depcomp 25 | /install-sh 26 | /missing 27 | /stamp-h1 28 | /configure 29 | /config.status 30 | /config.log 31 | .deps 32 | /.version 33 | /config.h* 34 | fsearch*-dirty 35 | 36 | # Project files 37 | tags 38 | fsearch*.tar.gz 39 | src/resources.c 40 | src/resources.h 41 | src/fsearch 42 | src/tags 43 | src/fsearch.glade~ 44 | src/*.ui~ 45 | 46 | # Valgrind 47 | callgrind.out* 48 | *.supp 49 | vgdump 50 | 51 | # VS Code 52 | *.vscode 53 | 54 | # GNOME Builder 55 | .buildconfig 56 | 57 | # AppImage 58 | AppDir 59 | appimage-build 60 | AppImageBuilder.yml 61 | 62 | # QtCreator Files 63 | *.config 64 | *.creator 65 | *.files 66 | *.includes 67 | 68 | # VIM files 69 | .vim 70 | *.swp 71 | compile_commands.json 72 | *.ycm_extra_conf* 73 | 74 | # Debug scripts 75 | /run_*.sh 76 | 77 | #idea 78 | .idea 79 | 80 | # Object files 81 | *.o 82 | *.ko 83 | *.obj 84 | *.elf 85 | 86 | # Precompiled Headers 87 | *.gch 88 | *.pch 89 | 90 | # Libraries 91 | *.lib 92 | *.a 93 | *.la 94 | *.lo 95 | 96 | # Shared objects (inc. Windows DLLs) 97 | *.dll 98 | *.so 99 | *.so.* 100 | *.dylib 101 | 102 | # Executables 103 | *.exe 104 | *.out 105 | *.app 106 | *.i*86 107 | *.x86_64 108 | *.hex 109 | 110 | # Debug files 111 | *.dSYM/ 112 | 113 | # perf files 114 | perf.data 115 | perf.data.old 116 | 117 | # Files generated by the prepare and compile process 118 | *.*~ 119 | ABOUT-NLS 120 | ABOUT-NLS~ 121 | config.guess 122 | config.rpath 123 | config.sub 124 | m4/ 125 | po/Makevars.template 126 | po/POTFILES 127 | po/Rules-quot* 128 | po/boldquot.sed 129 | po/en@boldquot.header 130 | po/en@quot.header 131 | po/insert-header.sin 132 | po/quot.sed 133 | po/remove-potcdate.sed 134 | po/remove-potcdate.sin 135 | po/stamp-po 136 | po/stamp-it 137 | /.idea_save/modules.xml 138 | /.idea_save/fsearch.iml 139 | /.idea_save/misc.xml 140 | /.idea_save/vcs.xml 141 | /.idea_save/.gitignore 142 | /data/io.github.cboxdoerfer.FSearch.metainfo.xml 143 | /debian/.debhelper/ 144 | /debian/fsearch/ 145 | /debian/fsearch-trunk/ 146 | /debian/files 147 | /config.guess.dh-orig 148 | /config.sub.dh-orig 149 | /src/icon_resources.c 150 | /src/icon_resources.h 151 | /src/ui_resources.c 152 | /src/ui_resources.h 153 | /tools/print_version.sh 154 | /data/io.github.cboxdoerfer.FSearch.desktop 155 | /data/io.github.cboxdoerfer.FSearch.desktop.in 156 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | ## Making a release 2 | 3 | - Update release notes in `NEWS` 4 | - Update version number in `meson.build` 5 | - Update version number in `data/io.github.cboxdoerfer.FSearch.appdata.xml.in` 6 | - Update release notes in `data/io.github.cboxdoerfer.FSearch.appdata.xml.in` 7 | - Update screenshots (if necessary) in `data/io.github.cboxdoerfer.FSearch.appdata.xml.in` 8 | - Update version number in `copr/fsearch_release.spec` 9 | - Update debian changelog: `dch --newrelease $version && dch --release` 10 | - Build the project and make sure tests pass: `ninja -C $builddir test` 11 | - Commit release: `git commit -a -m "Release FSearch $version"` 12 | - Add tag: `git tag $version` 13 | - Push changes and tag: `git push origin && git push origin $version` 14 | - Create release on GitHub 15 | - if major/minor release perform those steps now 16 | - Update `io.github.cboxdoerfer.FSearch.yml` in flathub repository 17 | - Make sure flatpak 18 | works: `flatpak-builder --force-clean --user --install builder-dir io.github.cboxdoerfer.FSearch.yml` 19 | - Update `PKGBUILD` in *fsearch* AUR repository: `$EDITOR PKGBUILD && makepkg --printsrcinfo > .SRCINFO` 20 | - Update OBS build files: `fsearch.dsc`, `fsearch.spec`, add `fsearch-$version.tar.gz` with stripped `debian` diretory 21 | - Commit to OBS: `osc vc && osc commit` 22 | 23 | ### Major/Minor release 24 | 25 | - Create a new branch: `git checkout -b "fsearch_$version" && git push -u origin fsearch_$version` 26 | - Point Release PPA [recipe](https://code.launchpad.net/~christian-boxdoerfer/+recipe/fsearch-stable/+edit) to new 27 | branch 28 | - Point copr [recipe](https://copr.fedorainfracloud.org/coprs/cboxdoerfer/fsearch/package/fsearch/edit) to new branch 29 | -------------------------------------------------------------------------------- /NEWS: -------------------------------------------------------------------------------- 1 | ================= 2 | Version 0.3.alpha0 3 | ================= 4 | 5 | ================= 6 | Version 0.2 7 | ================= 8 | 9 | * New search engine 10 | * Greatly improved highlighting of search terms 11 | * Custom filters 12 | * Improved sort performance 13 | * Improved scroll performance 14 | * Remember file selection after a database update 15 | * Automatically scroll the result list in rubberband selection mode 16 | * Invert selection with rubber band when **`Ctrl`** is pressed 17 | * Focus search entry when the main window gets activated 18 | * Add filter for applications (i.e. .desktop files) 19 | * Add option to launch applications directly instead of opening the .desktop files 20 | * Show application icons for .desktop files 21 | * Add .odp and .ods files to document filter 22 | * "Open with" menu now only shows applications which can handle the selected files 23 | * Improved opening of multiple files with the same type (they get now passed as a list to applications, instead of one by one) 24 | * Better support for opening files in sandboxed mode 25 | * Add menu items with links to the issue tracker, forum and donation pages 26 | * Improve support for non-Linux systems 27 | * Add option to exit FSearch when Escape is pressed instead of minimizing the window 28 | * Exclude `/proc` and `/sys` by default 29 | * Open context menu when **`Menu`** key or **`Shift + F10`** are pressed 30 | * and tons of bug fixes, translation updates and smaller improvements 31 | -------------------------------------------------------------------------------- /TODO.md: -------------------------------------------------------------------------------- 1 | | State | Task | Importance | Complexity | Difficulty | 2 | |-------|-------------------------------------------------------------------------------|------------|------------|------------| 3 | | Done | Invalidate icon cache when font size changes | High | Low | Low | 4 | | Done | Make the behavior of the `Open` action consistent with the `Open with` action | High | Low | Low | 5 | | | Display search term in window title | High | Low | Low | 6 | | | Custom file property indexing | High | Medium | Medium | 7 | | | Option to index and search for creation and access time | High | Medium | Low | 8 | | | Option to index and search for owner and permissions | High | Medium | Low | 9 | | | Option to index and search for xattrs | High | Medium | Low | 10 | | | Rework include/exclude UI | High | Medium | Low | 11 | | | File system monitoring | High | High | High | 12 | | | Option to search for run count | Medium | Low | Low | 13 | | | Add column for nesting level | Medium | Low | Low | 14 | | | Custom column order | Medium | Medium | Low | 15 | | | Pressing Enter in search entry focues and selects result | Medium | Medium | Low | 16 | | | Remember run count | Medium | Medium | Low | 17 | | | Auto column sizing | Medium | Medium | Medium | 18 | | | Custom keyboard shortcuts | Medium | Medium | Medium | 19 | | | Add CLI for searching | Medium | Medium | Low | 20 | | | Use PolicyKit to allow deletion of non-user files | Low | Medium | Medium | 21 | | | Load/save database from custom path | Low | Medium | Low | 22 | | | Content searching | Low | High | Medium | 23 | | | Option to mix files and folders in results view | Low | High | Medium | 24 | -------------------------------------------------------------------------------- /copr/fsearch_nightly.spec: -------------------------------------------------------------------------------- 1 | %global giturl https://github.com/cboxdoerfer/fsearch 2 | 3 | Name: fsearch 4 | Summary: A fast file search utility for Unix-like systems based on GTK 3 5 | Epoch: 2 6 | Version: 0.3~alpha0 7 | Release: %(date +%%Y%%m%%d)%{?dist} 8 | License: GPLv2+ 9 | URL: https://github.com/cboxdoerfer/fsearch 10 | Source0: %{giturl}/archive/master/%{name}-master.tar.gz 11 | 12 | 13 | BuildRequires: meson 14 | BuildRequires: ninja-build 15 | BuildRequires: gcc 16 | BuildRequires: gtk3-devel 17 | BuildRequires: glib2-devel 18 | BuildRequires: libappstream-glib 19 | BuildRequires: desktop-file-utils 20 | BuildRequires: itstool 21 | 22 | 23 | %description 24 | FSearch is a fast file search utility, inspired by Everything Search Engine. It's written in C and based on GTK 3. 25 | 26 | %prep 27 | %setup -q -n fsearch-master -c 28 | 29 | mv fsearch-master build 30 | 31 | %build 32 | export LDFLAGS="%{?__global_ldflags} -pthread" 33 | pushd build 34 | %meson -Dchannel=copr-nightly 35 | %meson_build -v 36 | popd 37 | 38 | %install 39 | pushd build 40 | %meson_install 41 | popd 42 | 43 | %find_lang %{name} --with-gnome 44 | 45 | desktop-file-install \ 46 | --dir=%{buildroot}%{_datadir}/applications/ \ 47 | %{buildroot}%{_datadir}/applications/io.github.cboxdoerfer.FSearch.desktop 48 | 49 | %files -f %{name}.lang 50 | %{_bindir}/fsearch 51 | %{_datadir}/applications/io.github.cboxdoerfer.FSearch.desktop 52 | %{_datadir}/icons/hicolor/scalable/apps/io.github.cboxdoerfer.FSearch.svg 53 | %{_datadir}/man/man1/fsearch.1.gz 54 | %{_datadir}/metainfo/io.github.cboxdoerfer.FSearch.metainfo.xml 55 | %{_datadir}/locale/*/*/fsearch.mo 56 | 57 | -------------------------------------------------------------------------------- /copr/fsearch_release.spec: -------------------------------------------------------------------------------- 1 | %global giturl https://github.com/cboxdoerfer/fsearch 2 | 3 | Name: fsearch 4 | Summary: A fast file search utility for Unix-like systems based on GTK 3 5 | Epoch: 1 6 | Version: 0.2 7 | Release: 1%{?dist} 8 | License: GPLv2+ 9 | URL: https://github.com/cboxdoerfer/fsearch 10 | Source0: %{giturl}/archive/%{version}/%{name}-%{version}.tar.gz 11 | 12 | 13 | BuildRequires: meson 14 | BuildRequires: ninja-build 15 | BuildRequires: gcc 16 | BuildRequires: gtk3-devel 17 | BuildRequires: glib2-devel 18 | BuildRequires: libappstream-glib 19 | BuildRequires: desktop-file-utils 20 | 21 | 22 | %description 23 | FSearch is a fast file search utility, inspired by Everything Search Engine. It's written in C and based on GTK 3. 24 | 25 | %prep 26 | %setup -q -n fsearch-%{version} -c 27 | 28 | mv fsearch-%{version} build 29 | 30 | %build 31 | export LDFLAGS="%{?__global_ldflags} -pthread" 32 | pushd build 33 | %meson 34 | %meson_build -v 35 | popd 36 | 37 | %install 38 | pushd build 39 | %meson_install 40 | 41 | desktop-file-install \ 42 | --dir=%{buildroot}%{_datadir}/applications/ \ 43 | %{buildroot}%{_datadir}/applications/io.github.cboxdoerfer.FSearch.desktop 44 | 45 | %files 46 | %{_bindir}/fsearch 47 | %{_datadir}/applications/io.github.cboxdoerfer.FSearch.desktop 48 | %{_datadir}/icons/hicolor/scalable/apps/io.github.cboxdoerfer.FSearch.svg 49 | %{_datadir}/man/man1/fsearch.1.gz 50 | %{_datadir}/metainfo/io.github.cboxdoerfer.FSearch.metainfo.xml 51 | %{_datadir}/locale/*/*/fsearch.mo 52 | 53 | -------------------------------------------------------------------------------- /data/fsearch.1: -------------------------------------------------------------------------------- 1 | .TH FSEARCH "1" "2021-07-17" 2 | . 3 | .SH NAME 4 | FSearch \- a graphical file search utility 5 | . 6 | .SH SYNOPSIS 7 | .B fsearch 8 | .RI [ OPTIONS ] 9 | . 10 | .SH DESCRIPTION 11 | .B FSearch 12 | allows you to search for files and folders by name. It uses an index to provide results almost instantly as you type. 13 | . 14 | .SH OPTIONS 15 | .SS "Help Options" 16 | .TP 17 | .BR \-h ", " \-\^\-help 18 | Show help options 19 | .TP 20 | .BR \-\^\-help\-all 21 | Show all help options 22 | .SS "Application Options" 23 | .TP 24 | .BR \-\^\-new-window 25 | Open a new application window 26 | .TP 27 | .BR \-\^\-preferences 28 | Show the application preferences 29 | .TP 30 | .BI \-s " PATTERN" "\fR,\fP \-\^\-search=" PATTERN 31 | Set the search pattern 32 | .TP 33 | .BR \-u ", " \-\^\-update-database 34 | Update the database 35 | .TP 36 | .BR \-v ", " \-\^\-version 37 | Print version information and exit 38 | .TP 39 | .BI "\-\^\-display=" DISPLAY 40 | X display to use 41 | . 42 | .SH BUGS 43 | For any bugs or feature requests, please create an issue on GitHub at https://github.com/cboxdoerfer/fsearch/issues 44 | . 45 | .SH AUTHORS 46 | Christian Boxdörfer and contributors 47 | -------------------------------------------------------------------------------- /data/gresource.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | io.github.cboxdoerfer.FSearch.svg 5 | 6 | 7 | -------------------------------------------------------------------------------- /data/io.github.cboxdoerfer.FSearch.desktop.in.in: -------------------------------------------------------------------------------- 1 | [Desktop Entry] 2 | Version=1.5 3 | Type=Application 4 | Name=FSearch 5 | Comment=A file search application focusing on performance and advanced features 6 | Icon=io.github.cboxdoerfer.FSearch 7 | TryExec=fsearch 8 | Exec=fsearch 9 | Categories=GTK;Utility; 10 | Keywords=search;fsearch;files;folders;music;video;documents;find;tool;everything; 11 | StartupNotify=true 12 | GenericName=File Search Utility 13 | PrefersNonDefaultGPU=false 14 | SingleMainWindow=false 15 | X-Purism-FormFactor=Workstation; -------------------------------------------------------------------------------- /data/io.github.cboxdoerfer.FSearch.metainfo.xml.in: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | io.github.cboxdoerfer.FSearch 5 | CC-BY-SA-4.0 6 | GPL-2.0-or-later 7 | FSearch 8 | A graphical file search application 9 | 10 |

11 | FSearch helps you to find files and folders as easy and fast as possible. 12 | Just type a few letters and search results will appear almost instantly. 13 |

14 |

15 | There are a lot of features which make searching as efficient and powerful 16 | as possible. Such as: 17 |

25 |

26 |

27 | Note: Due to Flatpak's sandboxing, FSearch can't find every file on your system. 28 |

29 |
30 | 31 | 32 | 33 | https://raw.githubusercontent.com/cboxdoerfer/fsearch/master/data/screenshots/02-main_window_menubar.png 34 | 35 | The default UI layout 36 | 37 | 38 | 39 | https://raw.githubusercontent.com/cboxdoerfer/fsearch/master/data/screenshots/01-main_window_headerbar.png 40 | 41 | A layout more closely following GNOME HIGs 42 | 43 | 44 | 45 | Utility 46 | GTK 47 | 48 | 49 | search 50 | fsearch 51 | files 52 | folders 53 | music 54 | video 55 | documents 56 | find 57 | tool 58 | everything 59 | 60 | 61 | pointing 62 | keyboard 63 | 64 | https://github.com/cboxdoerfer/fsearch 65 | https://github.com/cboxdoerfer/fsearch/issues 66 | https://github.com/sponsors/cboxdoerfer 67 | https://github.com/cboxdoerfer/fsearch/wiki 68 | https://hosted.weblate.org/projects/fsearch/ 69 | https://github.com/cboxdoerfer/fsearch 70 | https://github.com/cboxdoerfer/fsearch/blob/master/CONTRIBUTING.md 71 | https://github.com/cboxdoerfer 72 | 73 | Christian Boxdörfer 74 | christian.boxdoerfer_at_posteo.de 75 | fsearch 76 | 77 | 78 | 79 | 80 | 81 |
    82 |
  • New search engine
  • 83 |
  • Greatly improved highlighting of search terms
  • 84 |
  • Custom filters
  • 85 |
  • Improved sort performance
  • 86 |
  • Improved scroll performance
  • 87 |
  • Remember file selection after a database update
  • 88 |
  • Automatically scroll the result list in rubberband selection mode
  • 89 |
  • Invert selection with rubber band when 'Ctrl' is pressed
  • 90 |
  • Focus search entry when the main window gets activated
  • 91 |
  • Add filter for applications (i.e. .desktop files)
  • 92 |
  • Add option to launch applications directly instead of opening the .desktop files
  • 93 |
  • Show application icons for .desktop files
  • 94 |
  • Add .odp and .ods files to document filter
  • 95 |
  • 'Open with' menu now only shows applications which can handle the selected files
  • 96 |
  • Improved opening of multiple files with the same type (they get now passed as a list to applications, instead of one by one)
  • 97 |
  • Better support for opening files in sandboxed mode
  • 98 |
  • Add menu items with links to the issue tracker, forum and donation pages
  • 99 |
  • Improve support for non-Linux systems
  • 100 |
  • Add option to exit FSearch when Escape is pressed instead of minimizing the window
  • 101 |
  • Exclude '/proc' and '/sys' by default
  • 102 |
  • Open context menu when 'Menu' key or 'Shift + F10' are pressed
  • 103 |
  • and tons of bug fixes, translation updates and smaller improvements
  • 104 |
105 |
106 |
107 | 108 | 109 |

Initial release.

110 |
111 |
112 |
113 | io.github.cboxdoerfer.FSearch.desktop 114 | 115 | workstation 116 | 117 |
-------------------------------------------------------------------------------- /data/io.github.cboxdoerfer.FSearch.svg: -------------------------------------------------------------------------------- 1 | 2 | 12 | 14 | 15 | 17 | image/svg+xml 18 | 20 | 21 | 22 | 23 | 25 | 29 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /data/meson.build: -------------------------------------------------------------------------------- 1 | install_man('fsearch.1') 2 | 3 | install_data('io.github.cboxdoerfer.FSearch.svg', 4 | install_dir: join_paths(get_option('datadir'), 'icons', 'hicolor', 'scalable', 'apps')) 5 | 6 | 7 | desktop_file_in_config = configuration_data() 8 | desktop_file_in_config.set('app_id', app_id) 9 | desktop_file_in = configure_file( 10 | input: 'io.github.cboxdoerfer.FSearch.desktop.in.in', 11 | output: '@0@.desktop.in'.format(app_id), 12 | configuration: desktop_file_in_config, 13 | ) 14 | 15 | 16 | desktop_file = i18n.merge_file( 17 | input: desktop_file_in, 18 | output: '@0@.desktop'.format(app_id), 19 | type: 'desktop', 20 | po_dir: '../po', 21 | install: true, 22 | install_dir: join_paths(get_option('datadir'), 'applications') 23 | ) 24 | 25 | desktop_utils = find_program('desktop-file-validate', required: false) 26 | if desktop_utils.found() 27 | test('Validate desktop file', desktop_utils, 28 | args: [desktop_file] 29 | ) 30 | endif 31 | 32 | metainfo_file = i18n.merge_file( 33 | input: 'io.github.cboxdoerfer.FSearch.metainfo.xml.in', 34 | output: 'io.github.cboxdoerfer.FSearch.metainfo.xml', 35 | type: 'xml', 36 | po_dir: join_paths(meson.source_root(), 'po'), 37 | install: true, 38 | install_dir: join_paths(get_option('datadir'), 'metainfo') 39 | ) 40 | 41 | appstream_util = find_program('appstream-util', required: false) 42 | if appstream_util.found() 43 | test( 44 | 'validate-metainfo', appstream_util, 45 | args: [ 46 | 'validate-relax', '--nonet', metainfo_file.full_path() 47 | ] 48 | ) 49 | endif 50 | -------------------------------------------------------------------------------- /data/screenshots/01-main_window_headerbar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cboxdoerfer/fsearch/fda5923d75026941c1dc0349dee06cb2f9d69699/data/screenshots/01-main_window_headerbar.png -------------------------------------------------------------------------------- /data/screenshots/02-main_window_menubar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cboxdoerfer/fsearch/fda5923d75026941c1dc0349dee06cb2f9d69699/data/screenshots/02-main_window_menubar.png -------------------------------------------------------------------------------- /debian/changelog: -------------------------------------------------------------------------------- 1 | fsearch (0.2) unstable; urgency=medium 2 | 3 | * New upstream release. 4 | 5 | -- Christian Boxdörfer Mon, 15 Aug 2022 14:35:12 +0200 6 | 7 | fsearch (0.1) unstable; urgency=medium 8 | 9 | * Initial Release. 10 | 11 | -- Christian Boxdörfer Sat, 25 Sep 2021 13:16:38 +0200 12 | -------------------------------------------------------------------------------- /debian/compat: -------------------------------------------------------------------------------- 1 | 9 2 | -------------------------------------------------------------------------------- /debian/control: -------------------------------------------------------------------------------- 1 | Source: fsearch 2 | Section: utils 3 | Priority: optional 4 | Maintainer: Christian Boxdörfer 5 | Build-Depends: debhelper (>=9), 6 | build-essential, 7 | appstream, 8 | itstool, 9 | libtool, 10 | pkg-config, 11 | libicu-dev, 12 | libpcre2-dev, 13 | libglib2.0-dev, 14 | libgtk-3-dev, 15 | libxml2-utils, 16 | meson 17 | Standards-Version: 3.9.6 18 | Homepage: https://cboxdoerfer.github.io/fsearch/ 19 | 20 | Package: fsearch 21 | Architecture: any 22 | Replaces: fsearch-trunk 23 | Breaks: fsearch-trunk 24 | Depends: ${shlibs:Depends}, ${misc:Depends} 25 | Description: A fast graphical file search utility 26 | FSearch is a fast file search utility, based on GTK3. 27 | It supports regular expressions, wildcards, fast sorting, 28 | instant results with millions of files and more. 29 | -------------------------------------------------------------------------------- /debian/rules: -------------------------------------------------------------------------------- 1 | #!/usr/bin/make -f 2 | # See debhelper(7) (uncomment to enable) 3 | # output every command that modifies files on the build system. 4 | export DH_VERBOSE = 1 5 | 6 | 7 | %: 8 | dh $@ 9 | 10 | override_dh_auto_configure: 11 | dh_auto_configure -- \ 12 | -Dchannel=PPA-nightly 13 | -------------------------------------------------------------------------------- /debian/source/format: -------------------------------------------------------------------------------- 1 | 3.0 (native) 2 | -------------------------------------------------------------------------------- /fsearch.doap: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | FSearch 8 | A graphical file search application 9 | 10 | FSearch helps you to find files and folders as easy and fast as possible. 11 | Just type a few letters and search results will appear almost instantly. 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | C 29 | 30 | 31 | 32 | Christian Boxdörfer 33 | 34 | 35 | 36 | 37 | 38 | 39 | Dawid Duma (pixel96x) 40 | Dawid 41 | 42 | 43 | pixel96x 44 | Duma 45 | 46 | 47 | -------------------------------------------------------------------------------- /help/C/LINGUAS: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cboxdoerfer/fsearch/fda5923d75026941c1dc0349dee06cb2f9d69699/help/C/LINGUAS -------------------------------------------------------------------------------- /help/C/figures/FSearch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cboxdoerfer/fsearch/fda5923d75026941c1dc0349dee06cb2f9d69699/help/C/figures/FSearch.png -------------------------------------------------------------------------------- /help/C/index.page: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | Christian Boxdörfer 8 | christian.boxdoerfer@posteo.de 9 | 2022 10 | 11 | 12 | Dawid Duma (pixel96x) 13 | contact@pixel96x.com 14 | 2022 15 | https://pixel96x.com/ 16 | 17 | A graphical file search application 18 | search, fsearch, files, folders, music, video, documents, find, tool, everything 19 | 20 |

This work is licensed under a 21 | Creative Commons Attribution 4.0 International License

22 |
23 |
24 | <media type="image" its:translate="no" src="figures/FSearch.png"/>FSearch 25 |

26 | FSearch helps you to find files and folders as easy and fast as possible. 27 | Just type a few letters and search results will appear almost instantly. 28 |

29 |
-------------------------------------------------------------------------------- /help/C/legal.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 |

This work is licensed under a 5 | Creative Commons 6 | Attribution 4.0 International License.

7 |
-------------------------------------------------------------------------------- /help/C/search_syntax.page: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | Christian Boxdörfer 7 | christian.boxdoerfer@posteo.de 8 | 2022 9 | 10 | 11 | Dawid Duma (pixel96x) 12 | contact@pixel96x.com 13 | 2022 14 | https://pixel96x.com/ 15 | 16 | List of all the capabilities of the search engine. 17 | 18 |

This work is licensed under a 19 | Creative Commons Attribution 4.0 International License

20 |
21 | 22 |
23 | Search syntax 24 |

List of all the capabilities of the search engine.

25 |
-------------------------------------------------------------------------------- /help/C/search_syntax_escaping.page: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | Christian Boxdörfer 7 | christian.boxdoerfer@posteo.de 8 | 2022 9 | 10 | 11 | Dawid Duma (pixel96x) 12 | contact@pixel96x.com 13 | 2022 14 | https://pixel96x.com/ 15 | 16 | 17 |

This work is licensed under a 18 | Creative Commons Attribution 4.0 International License

19 |
20 | 21 |
22 | Escaping 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 39 | 40 |

Sequence

Descritpion

Example

Double quotes ""

Everything in between "" is treated literally (like spaces)

"/home/user/my folder" *.pdf → /home/user/my folder and *.pdf

Backslash \

Escapes the following character (like space or ")

/home/user/my\ folder \"quote\".txt → /home/user/my folder 38 | and "quote".txt

41 |
-------------------------------------------------------------------------------- /help/C/search_syntax_modifiers.page: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | Christian Boxdörfer 7 | christian.boxdoerfer@posteo.de 8 | 2022 9 | 10 | 11 | Dawid Duma (pixel96x) 12 | contact@pixel96x.com 13 | 2022 14 | https://pixel96x.com/ 15 | 16 | 17 |

This work is licensed under a 18 | Creative Commons Attribution 4.0 International License

19 |
20 | 21 |
22 | Modifiers 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 |

Keyword

Descritpion

Example

case:

Match case

case:Test finds TestDocument.odt, but not testdocument.odt

nocase:

Ignore case

nocase:test finds TestDocument.odt and testdocument.odt

exact:

Exact match

exact:test finds Test and test, but not testdocument.odt

file:, files:

Match files only

file:test finds a file named test.odt, but won't find the folder testfolder

folder:, folders:

Match folders only

file:test finds a file named test.odt, but won't find the folder testfolder

path:

Match the full path

path:home finds a folder named home, and all its children

nopath:

Match only the file/folder name

regex:

67 |

Enable regular expression

68 |

Note: All reserved characters used by the FSearch search syntax 69 | (e.g. (, ), \) need to be 70 | escaped properly when they're used in the regular expression. 71 | This can be achieved by surrounding the whole expression with double quotes ".

72 |

file:regex:".+\.pdf$" finds all files which have the pdf extension

noregex:

Disable regular expression

81 |
-------------------------------------------------------------------------------- /help/C/search_syntax_operators.page: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | Christian Boxdörfer 7 | christian.boxdoerfer@posteo.de 8 | 2022 9 | 10 | 11 | Dawid Duma (pixel96x) 12 | contact@pixel96x.com 13 | 2022 14 | https://pixel96x.com/ 15 | 16 | 17 |

This work is licensed under a 18 | Creative Commons Attribution 4.0 International License

19 |
20 | 21 |
22 | Operators 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 |

Operator

Keyword

Descritpion

AND

space, AND, &&

Combines two search terms. Only results that match both search terms are returned.

OR

OR, ||

Combines two search terms. Results that match one or both search terms are returned.

NOT

NOT, !

Negates the following search term.

Grouping

(, )

Search terms can be surrounded by parentheses to group them together.

50 |
-------------------------------------------------------------------------------- /help/C/search_syntax_wildcards.page: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | Christian Boxdörfer 7 | christian.boxdoerfer@posteo.de 8 | 2022 9 | 10 | 11 | Dawid Duma (pixel96x) 12 | contact@pixel96x.com 13 | 2022 14 | https://pixel96x.com/ 15 | 16 | 17 |

This work is licensed under a 18 | Creative Commons Attribution 4.0 International License

19 |
20 | 21 |
22 | Wildcards 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 |

Keyword

Descritpion

Example

*

Matches zero or more characters

*.pdf finds document.pdf, but also .pdf

?

Matches exactly one character

k?ng finds king, kong, kung, k1ng, etc.

40 |
-------------------------------------------------------------------------------- /help/meson.build: -------------------------------------------------------------------------------- 1 | help_files = [ 2 | 'index.page', 3 | 'search_syntax.page', 4 | 'search_syntax_escaping.page', 5 | 'search_syntax_functions.page', 6 | 'search_syntax_modifiers.page', 7 | 'search_syntax_operators.page', 8 | 'search_syntax_wildcards.page', 9 | 'legal.xml' 10 | ] 11 | 12 | help_media = [ 13 | 'figures/FSearch.png' 14 | ] 15 | 16 | gnome.yelp( 17 | meson.project_name(), 18 | media: help_media, 19 | sources: help_files 20 | ) -------------------------------------------------------------------------------- /meson.build: -------------------------------------------------------------------------------- 1 | project('fsearch', 'c', 2 | version: '0.3.alpha0', 3 | meson_version: '>= 0.45.0', 4 | default_options: ['c_std=gnu11', 5 | 'channel=other', 6 | ], 7 | ) 8 | app_id = 'io.github.cboxdoerfer.FSearch' 9 | 10 | cc = meson.get_compiler('c') 11 | 12 | gnome = import('gnome') 13 | i18n = import('i18n') 14 | 15 | have_malloc_trim = meson.get_compiler('c').has_function('malloc_trim') 16 | 17 | config_h = configuration_data() 18 | config_h.set('HAVE_MALLOC_TRIM', have_malloc_trim) 19 | config_h.set_quoted('APP_ID', app_id) 20 | config_h.set_quoted('PACKAGE_VERSION', meson.project_version()) 21 | config_h.set_quoted('VERSION', meson.project_version()) 22 | config_h.set_quoted('GETTEXT_PACKAGE', 'fsearch') 23 | config_h.set_quoted('LOCALEDIR', join_paths(get_option('prefix'), get_option('localedir'))) 24 | config_h.set_quoted('PACKAGE_WEBSITE', 'https://github.com/cboxdoerfer/fsearch') 25 | config_h.set_quoted('PACKAGE_ICON_NAME', app_id) 26 | config_h.set_quoted('PACKAGE_NAME', 'FSearch') 27 | config_h.set_quoted('BUILD_CHANNEL', get_option('channel')) 28 | 29 | # ensure off_t is 64bit 30 | add_project_arguments('-DHAVE_CONFIG_H', '-D_FILE_OFFSET_BITS=64', '-D_GNU_SOURCE', language : 'c') 31 | 32 | fsearch_include_dirs = include_directories( 33 | '.', 34 | ) 35 | 36 | subdir('data') 37 | subdir('help') 38 | subdir('src') 39 | subdir('po') 40 | 41 | configure_file( 42 | output: 'config.h', 43 | configuration: config_h, 44 | ) 45 | 46 | -------------------------------------------------------------------------------- /meson_options.txt: -------------------------------------------------------------------------------- 1 | option('channel', 2 | type: 'combo', 3 | choices: [ 'other', 'AUR-stable', 'AUR-devel', 'copr-stable', 'copr-nightly', 'PPA-stable', 'PPA-nightly', 'snap-stable', 'snap-nightly', 'flathub-stable', 'flathub-nightly', 'OBS-deb-stable', 'OBS-rpm-stable' ], 4 | description: 'The distribution channel for FSearch', 5 | ) 6 | -------------------------------------------------------------------------------- /po/LINGUAS: -------------------------------------------------------------------------------- 1 | # Keep list sorted alphabetically 2 | 3 | ar 4 | ber 5 | bg 6 | ca 7 | de 8 | el 9 | en_GB 10 | es 11 | eu 12 | fi 13 | fr 14 | gl 15 | he 16 | hu 17 | id 18 | ie 19 | it 20 | ja 21 | ko 22 | lt 23 | mr 24 | nb_NO 25 | nl 26 | pl 27 | pt 28 | pt_BR 29 | ro 30 | ru 31 | sk 32 | sv 33 | te 34 | tr 35 | tzm 36 | uk 37 | zgh 38 | zh_CN 39 | -------------------------------------------------------------------------------- /po/Makevars: -------------------------------------------------------------------------------- 1 | # Makefile variables for PO directory in any package using GNU gettext. 2 | 3 | # Usually the message domain is the same as the package name. 4 | DOMAIN = $(PACKAGE) 5 | 6 | # These two variables depend on the location of this directory. 7 | subdir = po 8 | top_builddir = .. 9 | 10 | # These options get passed to xgettext. 11 | XGETTEXT_OPTIONS = --keyword=_ --keyword=N_ --from-code=UTF-8 12 | 13 | # This is the copyright holder that gets inserted into the header of the 14 | # $(DOMAIN).pot file. Set this to the copyright holder of the surrounding 15 | # package. (Note that the msgstr strings, extracted from the package's 16 | # sources, belong to the copyright holder of the package.) Translators are 17 | # expected to transfer the copyright for their translations to this person 18 | # or entity, or to disclaim their copyright. The empty string stands for 19 | # the public domain; in this case the translators are expected to disclaim 20 | # their copyright. 21 | COPYRIGHT_HOLDER = Christian Boxdörfer 22 | 23 | # This tells whether or not to prepend "GNU " prefix to the package 24 | # name that gets inserted into the header of the $(DOMAIN).pot file. 25 | # Possible values are "yes", "no", or empty. If it is empty, try to 26 | # detect it automatically by scanning the files in $(top_srcdir) for 27 | # "GNU packagename" string. 28 | PACKAGE_GNU = 29 | 30 | # This is the email address or URL to which the translators shall report 31 | # bugs in the untranslated strings: 32 | # - Strings which are not entire sentences, see the maintainer guidelines 33 | # in the GNU gettext documentation, section 'Preparing Strings'. 34 | # - Strings which use unclear terms or require additional context to be 35 | # understood. 36 | # - Strings which make invalid assumptions about notation of date, time or 37 | # money. 38 | # - Pluralisation problems. 39 | # - Incorrect English spelling. 40 | # - Incorrect formatting. 41 | # It can be your email address, or a mailing list address where translators 42 | # can write to without being subscribed, or the URL of a web page through 43 | # which the translators can contact you. 44 | MSGID_BUGS_ADDRESS = 45 | 46 | # This is the list of locale categories, beyond LC_MESSAGES, for which the 47 | # message catalogs shall be used. It is usually empty. 48 | EXTRA_LOCALE_CATEGORIES = 49 | 50 | # This tells whether the $(DOMAIN).pot file contains messages with an 'msgctxt' 51 | # context. Possible values are "yes" and "no". Set this to yes if the 52 | # package uses functions taking also a message context, like pgettext(), or 53 | # if in $(XGETTEXT_OPTIONS) you define keywords with a context argument. 54 | USE_MSGCTXT = no 55 | 56 | # These options get passed to msgmerge. 57 | # Useful options are in particular: 58 | # --previous to keep previous msgids of translated messages, 59 | # --quiet to reduce the verbosity. 60 | MSGMERGE_OPTIONS = 61 | 62 | # These options get passed to msginit. 63 | # If you want to disable line wrapping when writing PO files, add 64 | # --no-wrap to MSGMERGE_OPTIONS, XGETTEXT_OPTIONS, and 65 | # MSGINIT_OPTIONS. 66 | MSGINIT_OPTIONS = 67 | 68 | # This tells whether or not to regenerate a PO file when $(DOMAIN).pot 69 | # has changed. Possible values are "yes" and "no". Set this to no if 70 | # the POT file is checked in the repository and the version control 71 | # program ignores timestamps. 72 | PO_DEPENDS_ON_POT = yes 73 | 74 | # This tells whether or not to forcibly update $(DOMAIN).pot and 75 | # regenerate PO files on "make dist". Possible values are "yes" and 76 | # "no". Set this to no if the POT file and PO files are maintained 77 | # externally. 78 | DIST_DEPENDS_ON_UPDATE_PO = yes 79 | -------------------------------------------------------------------------------- /po/POTFILES.in: -------------------------------------------------------------------------------- 1 | data/io.github.cboxdoerfer.FSearch.desktop.in.in 2 | data/io.github.cboxdoerfer.FSearch.metainfo.xml.in 3 | src/fsearch.c 4 | src/fsearch_database.c 5 | src/fsearch_file_utils.c 6 | src/fsearch_filter.c 7 | src/fsearch_filter_editor.c 8 | src/fsearch_filter_editor.ui 9 | src/fsearch_listview_popup.c 10 | src/fsearch_overlay.ui 11 | src/fsearch_preferences.ui 12 | src/fsearch_preferences_ui.c 13 | src/fsearch_preferences_widgets.c 14 | src/fsearch_statusbar.c 15 | src/fsearch_statusbar.ui 16 | src/fsearch_window.c 17 | src/fsearch_window.ui 18 | src/fsearch_window_actions.c 19 | src/main.c 20 | src/menus.ui 21 | -------------------------------------------------------------------------------- /po/meson.build: -------------------------------------------------------------------------------- 1 | i18n.gettext('fsearch', preset: 'glib') 2 | -------------------------------------------------------------------------------- /snap/snapcraft.yaml: -------------------------------------------------------------------------------- 1 | name: fsearch 2 | version: '0.2alpha' 3 | grade: devel 4 | adopt-info: fsearch 5 | base: core18 6 | confinement: strict 7 | license: GPL-2.0+ 8 | compression: lzo 9 | 10 | apps: 11 | fsearch: 12 | command: usr/bin/fsearch 13 | extensions: [ gnome-3-28 ] 14 | plugs: 15 | - home 16 | - removable-media 17 | - udisks2 18 | slots: 19 | - dbus-daemon 20 | common-id: io.github.cboxdoerfer.FSearch 21 | 22 | parts: 23 | fsearch: 24 | plugin: meson 25 | source: https://github.com/cboxdoerfer/fsearch.git 26 | meson-parameters: 27 | - -Dchannel=snap-nightly 28 | - --buildtype=release 29 | - --prefix=/snap/fsearch/current/usr 30 | build-packages: 31 | - gettext 32 | - libglib2.0-dev 33 | - libicu-dev 34 | - libpcre2-dev 35 | stage-packages: 36 | - libicu60 37 | - libpcre2-8-0 38 | organize: 39 | snap/fsearch/current/usr: usr 40 | parse-info: [ usr/share/metainfo/io.github.cboxdoerfer.FSearch.metainfo.xml ] 41 | 42 | slots: 43 | dbus-daemon: 44 | interface: dbus 45 | bus: session 46 | name: io.github.cboxdoerfer.FSearch -------------------------------------------------------------------------------- /src/.clang-format: -------------------------------------------------------------------------------- 1 | BasedOnStyle: LLVM 2 | 3 | Language: Cpp 4 | IndentWidth: 4 5 | ColumnLimit: 120 6 | AlwaysBreakAfterReturnType: All 7 | AlignAfterOpenBracket: Align 8 | AlignOperands: AlignAfterOperator 9 | AllowAllArgumentsOnNextLine: false 10 | AllowAllParametersOfDeclarationOnNextLine: false 11 | AllowShortBlocksOnASingleLine: Never 12 | AllowShortIfStatementsOnASingleLine: false 13 | BinPackArguments: false 14 | BinPackParameters: false 15 | BreakBeforeBinaryOperators: NonAssignment 16 | BreakBeforeBraces: Custom 17 | BraceWrapping: 18 | BeforeElse: true 19 | 20 | # Penalties 21 | PenaltyBreakAssignment: 50 22 | PenaltyBreakBeforeFirstCallParameter: 40 23 | PenaltyBreakComment: 10 24 | PenaltyBreakFirstLessLess: 0 25 | PenaltyBreakString: 10 26 | PenaltyExcessCharacter: 10 27 | PenaltyReturnTypeOnItsOwnLine: 30 -------------------------------------------------------------------------------- /src/fsearch.h: -------------------------------------------------------------------------------- 1 | /* 2 | FSearch - A fast file search utility 3 | Copyright © 2020 Christian Boxdörfer 4 | 5 | This program is free software; you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation; either version 2 of the License, or 8 | (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program; if not, see . 17 | */ 18 | 19 | #pragma once 20 | 21 | #include "fsearch_config.h" 22 | #include "fsearch_database.h" 23 | #include "fsearch_thread_pool.h" 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | G_BEGIN_DECLS 30 | 31 | #define FSEARCH_APPLICATION_TYPE (fsearch_application_get_type()) 32 | #define FSEARCH_APPLICATION_DEFAULT (FSEARCH_APPLICATION(g_application_get_default())) 33 | 34 | G_DECLARE_FINAL_TYPE(FsearchApplication, fsearch_application, FSEARCH, APPLICATION, GtkApplication) 35 | 36 | FsearchApplication * 37 | fsearch_application_new(void); 38 | 39 | G_END_DECLS 40 | 41 | typedef enum { 42 | FSEARCH_DATABASE_STATE_SCANNING, 43 | FSEARCH_DATABASE_STATE_LOADING, 44 | FSEARCH_DATABASE_STATE_IDLE, 45 | NUM_FSEARCH_DATABASE_STATES 46 | } FsearchDatabaseState; 47 | 48 | FsearchDatabaseState 49 | fsearch_application_get_db_state(FsearchApplication *fsearch); 50 | 51 | uint32_t 52 | fsearch_application_get_num_db_entries(FsearchApplication *fsearch); 53 | 54 | FsearchDatabase * 55 | fsearch_application_get_db(FsearchApplication *fsearch); 56 | 57 | FsearchConfig * 58 | fsearch_application_get_config(FsearchApplication *fsearch); 59 | 60 | void 61 | fsearch_application_state_lock(FsearchApplication *fsearch); 62 | 63 | void 64 | fsearch_application_state_unlock(FsearchApplication *fsearch); 65 | 66 | char * 67 | fsearch_application_get_database_file_path(void); 68 | 69 | char * 70 | fsearch_application_get_database_dir(void); 71 | 72 | gboolean 73 | fsearch_application_has_file_manager_on_bus(FsearchApplication *fsearch); 74 | -------------------------------------------------------------------------------- /src/fsearch_array.h: -------------------------------------------------------------------------------- 1 | /* 2 | FSearch - A fast file search utility 3 | Copyright © 2020 Christian Boxdörfer 4 | 5 | This program is free software; you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation; either version 2 of the License, or 8 | (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program; if not, see . 17 | */ 18 | 19 | #pragma once 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | typedef struct DynamicArray DynamicArray; 27 | 28 | typedef int32_t (*DynamicArrayCompareFunc)(void *a, void *b); 29 | typedef int32_t (*DynamicArrayCompareDataFunc)(void *a, void *b, void *data); 30 | 31 | bool 32 | darray_binary_search_with_data(DynamicArray *array, 33 | void *item, 34 | DynamicArrayCompareDataFunc comp_func, 35 | void *data, 36 | uint32_t *matched_index); 37 | 38 | void 39 | darray_sort_multi_threaded(DynamicArray *array, 40 | DynamicArrayCompareDataFunc comp_func, 41 | GCancellable *cancellable, 42 | void *data); 43 | 44 | void 45 | darray_sort(DynamicArray *array, DynamicArrayCompareDataFunc comp_func, GCancellable *cancellable, void *data); 46 | 47 | uint32_t 48 | darray_get_size(DynamicArray *array); 49 | 50 | uint32_t 51 | darray_get_num_items(DynamicArray *array); 52 | 53 | void * 54 | darray_get_item(DynamicArray *array, uint32_t idx); 55 | 56 | void * 57 | darray_get_item_next(DynamicArray *array, 58 | void *item, 59 | DynamicArrayCompareDataFunc compare_func, 60 | void *data, 61 | uint32_t *next_idx); 62 | 63 | bool 64 | darray_get_item_idx(DynamicArray *array, void *item, DynamicArrayCompareDataFunc compare_func, void *data, uint32_t *index); 65 | 66 | void 67 | darray_add_items(DynamicArray *array, void **items, uint32_t num_items); 68 | 69 | void 70 | darray_add_item(DynamicArray *array, void *data); 71 | 72 | DynamicArray * 73 | darray_new(size_t num_items); 74 | 75 | void 76 | darray_unref(DynamicArray *array); 77 | 78 | DynamicArray * 79 | darray_ref(DynamicArray *array); 80 | 81 | DynamicArray * 82 | darray_copy(DynamicArray *array); 83 | -------------------------------------------------------------------------------- /src/fsearch_clipboard.c: -------------------------------------------------------------------------------- 1 | /* 2 | FSearch - A fast file search utility 3 | Copyright © 2020 Christian Boxdörfer 4 | 5 | This program is free software; you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation; either version 2 of the License, or 8 | (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program; if not, see . 17 | */ 18 | 19 | #define G_LOG_DOMAIN "fsearch-clipboard" 20 | 21 | #include "fsearch_clipboard.h" 22 | #include 23 | #include 24 | #include 25 | 26 | static GdkDragAction clipboard_action = GDK_ACTION_DEFAULT; 27 | static GList *clipboard_file_list = NULL; 28 | 29 | enum { URI_LIST = 1, NAUTILUS_WORKAROUND, GNOME_COPIED_FILES, KDE_CUT_SELECTION, N_CLIPBOARD_TARGETS }; 30 | 31 | static GtkTargetEntry targets[] = {{"text/uri-list", 0, URI_LIST}, 32 | {"text/plain;charset=utf-8", 0, NAUTILUS_WORKAROUND}, 33 | {"application/x-kde-cutselection", 0, KDE_CUT_SELECTION}, 34 | {"x-special/gnome-copied-files", 0, GNOME_COPIED_FILES}}; 35 | 36 | static void 37 | clipboard_clean_data(GtkClipboard *clipboard, gpointer user_data) { 38 | /* g_debug("clean clipboard!"); */ 39 | if (clipboard_file_list) { 40 | g_list_free_full(g_steal_pointer(&clipboard_file_list), (GDestroyNotify)g_free); 41 | } 42 | clipboard_action = GDK_ACTION_DEFAULT; 43 | } 44 | 45 | static void 46 | clipboard_get_data(GtkClipboard *clipboard, GtkSelectionData *selection_data, guint info, gpointer user_data) { 47 | if (!clipboard_file_list) { 48 | return; 49 | } 50 | 51 | if (info == KDE_CUT_SELECTION) { 52 | // Tell KDE that the selection data should be cut 53 | g_debug("[get_data] KDE_CUT_SELECTION"); 54 | if (clipboard_action == GDK_ACTION_MOVE) 55 | gtk_selection_data_set(selection_data, gtk_selection_data_get_target(selection_data), 8, (guchar *)"1", 2); 56 | return; 57 | } 58 | 59 | g_autoptr(GString) list = g_string_sized_new(8192); 60 | 61 | if (info == GNOME_COPIED_FILES) { 62 | g_debug("[get_data] GNOME_COPIED_FILES"); 63 | const gchar *action = clipboard_action == GDK_ACTION_MOVE ? "cut\n" : "copy\n"; 64 | g_string_append(list, action); 65 | } 66 | else if (info == URI_LIST) { 67 | g_debug("[get_data] URI_LIST"); 68 | } 69 | else if (info == NAUTILUS_WORKAROUND) { 70 | g_debug("[get_data] NAUTILUS_WORKAROUND"); 71 | g_string_append(list, "x-special/nautilus-clipboard\n"); 72 | const gchar *action = clipboard_action == GDK_ACTION_MOVE ? "cut\n" : "copy\n"; 73 | g_string_append(list, action); 74 | } 75 | else { 76 | g_debug("[get_data] unknown format: %d", info); 77 | return; 78 | } 79 | 80 | for (GList *l = clipboard_file_list; l; l = l->next) { 81 | g_autofree gchar *file_name = g_filename_to_uri((char *)l->data, NULL, NULL); 82 | g_string_append(list, file_name); 83 | 84 | if (l->next != NULL) { 85 | if (info == URI_LIST) { 86 | g_string_append(list, "\r\n"); 87 | } 88 | else { 89 | g_string_append_c(list, '\n'); 90 | } 91 | } 92 | } 93 | if (info == NAUTILUS_WORKAROUND) { 94 | g_string_append_c(list, '\n'); 95 | } 96 | 97 | gtk_selection_data_set(selection_data, 98 | gtk_selection_data_get_target(selection_data), 99 | 8, 100 | (guchar *)list->str, 101 | (gint)list->len + 1); 102 | } 103 | 104 | void 105 | clipboard_copy_file_list(GList *file_list, bool copy) { 106 | GtkClipboard *clip = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD); 107 | gtk_clipboard_set_with_data(clip, targets, G_N_ELEMENTS(targets), clipboard_get_data, clipboard_clean_data, NULL); 108 | 109 | clipboard_file_list = file_list; 110 | clipboard_action = copy ? GDK_ACTION_COPY : GDK_ACTION_MOVE; 111 | } 112 | -------------------------------------------------------------------------------- /src/fsearch_clipboard.h: -------------------------------------------------------------------------------- 1 | /* 2 | FSearch - A fast file search utility 3 | Copyright © 2020 Christian Boxdörfer 4 | 5 | This program is free software; you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation; either version 2 of the License, or 8 | (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program; if not, see . 17 | */ 18 | 19 | #pragma once 20 | 21 | #include 22 | #include 23 | 24 | void 25 | clipboard_copy_file_list(GList *file_list, bool copy); 26 | -------------------------------------------------------------------------------- /src/fsearch_config.h: -------------------------------------------------------------------------------- 1 | /* 2 | FSearch - A fast file search utility 3 | Copyright © 2020 Christian Boxdörfer 4 | 5 | This program is free software; you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation; either version 2 of the License, or 8 | (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program; if not, see . 17 | */ 18 | 19 | #pragma once 20 | 21 | #include 22 | #include 23 | #include 24 | 25 | #include "fsearch_filter_manager.h" 26 | 27 | typedef struct _FsearchConfig FsearchConfig; 28 | 29 | typedef enum FsearchConfigActionAfterOpen { 30 | ACTION_AFTER_OPEN_NOTHING = 0, 31 | ACTION_AFTER_OPEN_MINIMIZE, 32 | ACTION_AFTER_OPEN_CLOSE, 33 | N_ACTIONS_AFTER_OPEN, 34 | } FsearchConfigActionAfterOpen; 35 | 36 | typedef struct { 37 | bool database_config_changed; 38 | bool listview_config_changed; 39 | bool search_config_changed; 40 | } FsearchConfigCompareResult; 41 | 42 | struct _FsearchConfig { 43 | // Search 44 | bool hide_results_on_empty_search; 45 | bool search_in_path; 46 | bool enable_regex; 47 | bool match_case; 48 | bool auto_search_in_path; 49 | bool auto_match_case; 50 | bool search_as_you_type; 51 | bool show_base_2_units; 52 | 53 | // Applications 54 | char *folder_open_cmd; 55 | 56 | // Window 57 | bool restore_window_size; 58 | int32_t window_width; 59 | int32_t window_height; 60 | 61 | // Interface 62 | bool highlight_search_terms; 63 | bool single_click_open; 64 | bool launch_desktop_files; 65 | bool enable_dark_theme; 66 | bool enable_list_tooltips; 67 | bool restore_column_config; 68 | bool restore_sort_order; 69 | bool double_click_path; 70 | FsearchConfigActionAfterOpen action_after_file_open; 71 | bool action_after_file_open_keyboard; 72 | bool action_after_file_open_mouse; 73 | bool exit_on_escape; 74 | bool show_indexing_status; 75 | 76 | // Warning Dialogs 77 | bool show_dialog_failed_opening; 78 | 79 | // View menu 80 | bool show_menubar; 81 | bool show_statusbar; 82 | bool show_filter; 83 | bool show_search_button; 84 | 85 | // Columns 86 | bool show_listview_icons; 87 | bool show_path_column; 88 | bool show_type_column; 89 | bool show_extension_column; 90 | bool show_size_column; 91 | bool show_modified_column; 92 | 93 | char *sort_by; 94 | bool sort_ascending; 95 | 96 | uint32_t name_column_width; 97 | uint32_t path_column_width; 98 | uint32_t type_column_width; 99 | uint32_t extension_column_width; 100 | uint32_t size_column_width; 101 | uint32_t modified_column_width; 102 | 103 | uint32_t name_column_pos; 104 | uint32_t path_column_pos; 105 | uint32_t type_column_pos; 106 | uint32_t size_column_pos; 107 | uint32_t modified_column_pos; 108 | 109 | // database 110 | bool update_database_on_launch; 111 | bool update_database_every; 112 | uint32_t update_database_every_hours; 113 | uint32_t update_database_every_minutes; 114 | 115 | bool exclude_hidden_items; 116 | bool follow_symlinks; 117 | 118 | FsearchFilterManager *filters; 119 | GList *indexes; 120 | GList *exclude_locations; 121 | char **exclude_files; 122 | }; 123 | 124 | bool 125 | config_make_dir(void); 126 | 127 | bool 128 | config_load(FsearchConfig *config); 129 | 130 | bool 131 | config_load_default(FsearchConfig *config); 132 | 133 | bool 134 | config_save(FsearchConfig *config); 135 | 136 | void 137 | config_build_dir(char *path, size_t len); 138 | 139 | FsearchConfigCompareResult 140 | config_cmp(FsearchConfig *c1, FsearchConfig *c2); 141 | 142 | FsearchConfig * 143 | config_copy(FsearchConfig *config); 144 | 145 | void 146 | config_free(FsearchConfig *config); 147 | -------------------------------------------------------------------------------- /src/fsearch_database.h: -------------------------------------------------------------------------------- 1 | /* 2 | FSearch - A fast file search utility 3 | Copyright © 2020 Christian Boxdörfer 4 | 5 | This program is free software; you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation; either version 2 of the License, or 8 | (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program; if not, see . 17 | */ 18 | 19 | #pragma once 20 | 21 | #include "fsearch_array.h" 22 | #include "fsearch_database_index.h" 23 | #include "fsearch_thread_pool.h" 24 | 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | typedef struct FsearchDatabase FsearchDatabase; 31 | 32 | bool 33 | db_register_view(FsearchDatabase *db, gpointer view); 34 | 35 | bool 36 | db_unregister_view(FsearchDatabase *db, gpointer view); 37 | 38 | bool 39 | db_load(FsearchDatabase *db, const char *path, void (*status_cb)(const char *)); 40 | 41 | bool 42 | db_scan(FsearchDatabase *db, GCancellable *cancellable, void (*status_cb)(const char *)); 43 | 44 | FsearchDatabase * 45 | db_ref(FsearchDatabase *db); 46 | 47 | void 48 | db_unref(FsearchDatabase *db); 49 | 50 | FsearchDatabase * 51 | db_new(GList *includes, GList *excludes, char **exclude_files, bool exclude_hidden); 52 | 53 | bool 54 | db_save(FsearchDatabase *db, const char *path); 55 | 56 | time_t 57 | db_get_timestamp(FsearchDatabase *db); 58 | 59 | uint32_t 60 | db_get_num_files(FsearchDatabase *db); 61 | 62 | uint32_t 63 | db_get_num_folders(FsearchDatabase *db); 64 | 65 | uint32_t 66 | db_get_num_entries(FsearchDatabase *db); 67 | 68 | void 69 | db_unlock(FsearchDatabase *db); 70 | 71 | void 72 | db_lock(FsearchDatabase *db); 73 | 74 | bool 75 | db_try_lock(FsearchDatabase *db); 76 | 77 | DynamicArray * 78 | db_get_folders_copy(FsearchDatabase *db); 79 | 80 | DynamicArray * 81 | db_get_files_copy(FsearchDatabase *db); 82 | 83 | DynamicArray * 84 | db_get_folders(FsearchDatabase *db); 85 | 86 | DynamicArray * 87 | db_get_files(FsearchDatabase *db); 88 | 89 | FsearchThreadPool * 90 | db_get_thread_pool(FsearchDatabase *db); 91 | 92 | bool 93 | db_has_entries_sorted_by_type(FsearchDatabase *db, FsearchDatabaseIndexType sort_type); 94 | 95 | bool 96 | db_get_entries_sorted(FsearchDatabase *db, 97 | FsearchDatabaseIndexType requested_sort_type, 98 | FsearchDatabaseIndexType *returned_sort_type, 99 | DynamicArray **folders, 100 | DynamicArray **files); 101 | 102 | DynamicArray * 103 | db_get_folders_sorted_copy(FsearchDatabase *db, FsearchDatabaseIndexType sort_type); 104 | 105 | DynamicArray * 106 | db_get_files_sorted_copy(FsearchDatabase *db, FsearchDatabaseIndexType sort_type); 107 | 108 | DynamicArray * 109 | db_get_folders_sorted(FsearchDatabase *db, FsearchDatabaseIndexType sort_type); 110 | 111 | DynamicArray * 112 | db_get_files_sorted(FsearchDatabase *db, FsearchDatabaseIndexType sort_type); 113 | -------------------------------------------------------------------------------- /src/fsearch_database_entry.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | typedef enum { 8 | DATABASE_ENTRY_TYPE_NONE, 9 | DATABASE_ENTRY_TYPE_FOLDER, 10 | DATABASE_ENTRY_TYPE_FILE, 11 | NUM_DATABASE_ENTRY_TYPES, 12 | } FsearchDatabaseEntryType; 13 | 14 | typedef struct FsearchDatabaseEntry FsearchDatabaseEntry; 15 | typedef struct FsearchDatabaseEntryFile FsearchDatabaseEntryFile; 16 | typedef struct FsearchDatabaseEntryFolder FsearchDatabaseEntryFolder; 17 | 18 | typedef struct FsearchDatabaseEntryCompareContext { 19 | GHashTable *file_type_table; 20 | GHashTable *entry_to_file_type_table; 21 | } FsearchDatabaseEntryCompareContext; 22 | 23 | bool 24 | db_entry_is_folder(FsearchDatabaseEntry *entry); 25 | 26 | bool 27 | db_entry_is_file(FsearchDatabaseEntry *entry); 28 | 29 | size_t 30 | db_entry_get_sizeof_folder_entry(); 31 | 32 | size_t 33 | db_entry_get_sizeof_file_entry(); 34 | 35 | uint32_t 36 | db_entry_folder_get_num_children(FsearchDatabaseEntryFolder *entry); 37 | 38 | uint32_t 39 | db_entry_folder_get_num_files(FsearchDatabaseEntryFolder *entry); 40 | 41 | uint32_t 42 | db_entry_folder_get_num_folders(FsearchDatabaseEntryFolder *entry); 43 | 44 | void 45 | db_entry_set_idx(FsearchDatabaseEntry *entry, uint32_t idx); 46 | 47 | void 48 | db_entry_set_mtime(FsearchDatabaseEntry *entry, time_t mtime); 49 | 50 | void 51 | db_entry_set_size(FsearchDatabaseEntry *entry, off_t size); 52 | 53 | void 54 | db_entry_set_mark(FsearchDatabaseEntry *entry, uint8_t mark); 55 | 56 | void 57 | db_entry_set_name(FsearchDatabaseEntry *entry, const char *name); 58 | 59 | void 60 | db_entry_set_parent(FsearchDatabaseEntry *entry, FsearchDatabaseEntryFolder *parent); 61 | 62 | void 63 | db_entry_set_type(FsearchDatabaseEntry *entry, FsearchDatabaseEntryType type); 64 | 65 | void 66 | db_entry_update_parent_size(FsearchDatabaseEntry *entry); 67 | 68 | uint8_t 69 | db_entry_get_mark(FsearchDatabaseEntry *entry); 70 | 71 | uint32_t 72 | db_entry_get_idx(FsearchDatabaseEntry *entry); 73 | 74 | uint32_t 75 | db_entry_get_depth(FsearchDatabaseEntry *entry); 76 | 77 | GString * 78 | db_entry_get_path(FsearchDatabaseEntry *entry); 79 | 80 | GString * 81 | db_entry_get_path_full(FsearchDatabaseEntry *entry); 82 | 83 | void 84 | db_entry_append_path(FsearchDatabaseEntry *entry, GString *str); 85 | 86 | void 87 | db_entry_append_full_path(FsearchDatabaseEntry *entry, GString *str); 88 | 89 | time_t 90 | db_entry_get_mtime(FsearchDatabaseEntry *entry); 91 | 92 | off_t 93 | db_entry_get_size(FsearchDatabaseEntry *entry); 94 | 95 | const char * 96 | db_entry_get_extension(FsearchDatabaseEntry *entry); 97 | 98 | GString * 99 | db_entry_get_name_for_display(FsearchDatabaseEntry *entry); 100 | 101 | const char * 102 | db_entry_get_name_raw_for_display(FsearchDatabaseEntry *entry); 103 | 104 | const char * 105 | db_entry_get_name_raw(FsearchDatabaseEntry *entry); 106 | 107 | FsearchDatabaseEntryFolder * 108 | db_entry_get_parent(FsearchDatabaseEntry *entry); 109 | 110 | FsearchDatabaseEntryType 111 | db_entry_get_type(FsearchDatabaseEntry *entry); 112 | 113 | void 114 | db_entry_append_content_type(FsearchDatabaseEntry *entry, GString *str); 115 | 116 | void 117 | db_entry_destroy(FsearchDatabaseEntry *entry); 118 | 119 | int 120 | db_entry_compare_entries_by_extension(FsearchDatabaseEntry **a, FsearchDatabaseEntry **b); 121 | 122 | int 123 | db_entry_compare_entries_by_size(FsearchDatabaseEntry **a, FsearchDatabaseEntry **b); 124 | 125 | int 126 | db_entry_compare_entries_by_type(FsearchDatabaseEntry **a, FsearchDatabaseEntry **b, gpointer data); 127 | 128 | int 129 | db_entry_compare_entries_by_modification_time(FsearchDatabaseEntry **a, FsearchDatabaseEntry **b); 130 | 131 | int 132 | db_entry_compare_entries_by_position(FsearchDatabaseEntry **a, FsearchDatabaseEntry **b); 133 | 134 | int 135 | db_entry_compare_entries_by_path(FsearchDatabaseEntry **a, FsearchDatabaseEntry **b); 136 | 137 | int 138 | db_entry_compare_entries_by_name(FsearchDatabaseEntry **a, FsearchDatabaseEntry **b); 139 | -------------------------------------------------------------------------------- /src/fsearch_database_index.c: -------------------------------------------------------------------------------- 1 | #include "fsearch_database_index.h" 2 | -------------------------------------------------------------------------------- /src/fsearch_database_index.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define DATABASE_INDEX_TYPE_NAME_STRING "Name" 4 | #define DATABASE_INDEX_TYPE_PATH_STRING "Path" 5 | #define DATABASE_INDEX_TYPE_SIZE_STRING "Size" 6 | #define DATABASE_INDEX_TYPE_MODIFICATION_TIME_STRING "Date Modified" 7 | #define DATABASE_INDEX_TYPE_FILETYPE_STRING "Type" 8 | #define DATABASE_INDEX_TYPE_EXTENSION_STRING "Extension" 9 | 10 | typedef enum { 11 | DATABASE_INDEX_FLAG_NAME = 1 << 0, 12 | DATABASE_INDEX_FLAG_PATH = 1 << 1, 13 | DATABASE_INDEX_FLAG_SIZE = 1 << 2, 14 | DATABASE_INDEX_FLAG_MODIFICATION_TIME = 1 << 3, 15 | DATABASE_INDEX_FLAG_ACCESS_TIME = 1 << 4, 16 | DATABASE_INDEX_FLAG_CREATION_TIME = 1 << 5, 17 | DATABASE_INDEX_FLAG_STATUS_CHANGE_TIME = 1 << 6, 18 | } FsearchDatabaseIndexFlags; 19 | 20 | typedef enum { 21 | DATABASE_INDEX_TYPE_NAME, 22 | DATABASE_INDEX_TYPE_PATH, 23 | DATABASE_INDEX_TYPE_SIZE, 24 | DATABASE_INDEX_TYPE_MODIFICATION_TIME, 25 | DATABASE_INDEX_TYPE_ACCESS_TIME, 26 | DATABASE_INDEX_TYPE_CREATION_TIME, 27 | DATABASE_INDEX_TYPE_STATUS_CHANGE_TIME, 28 | DATABASE_INDEX_TYPE_FILETYPE, 29 | DATABASE_INDEX_TYPE_EXTENSION, 30 | NUM_DATABASE_INDEX_TYPES, 31 | } FsearchDatabaseIndexType; 32 | -------------------------------------------------------------------------------- /src/fsearch_database_search.h: -------------------------------------------------------------------------------- 1 | /* 2 | FSearch - A fast file search utility 3 | Copyright © 2020 Christian Boxdörfer 4 | 5 | This program is free software; you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation; either version 2 of the License, or 8 | (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program; if not, see . 17 | */ 18 | 19 | #pragma once 20 | 21 | #include "fsearch_array.h" 22 | #include "fsearch_database_entry.h" 23 | #include "fsearch_filter.h" 24 | #include "fsearch_query.h" 25 | 26 | #include 27 | 28 | typedef struct DatabaseSearchResult { 29 | DynamicArray *folders; 30 | DynamicArray *files; 31 | FsearchDatabaseIndexType sort_type; 32 | } DatabaseSearchResult; 33 | 34 | DatabaseSearchResult * 35 | db_search_empty(DynamicArray *folders, DynamicArray *files, FsearchDatabaseIndexType sort_type); 36 | 37 | DatabaseSearchResult * 38 | db_search(FsearchQuery *q, 39 | FsearchThreadPool *pool, 40 | DynamicArray *folders, 41 | DynamicArray *files, 42 | FsearchDatabaseIndexType sort_type, 43 | GCancellable *cancellable); 44 | -------------------------------------------------------------------------------- /src/fsearch_database_view.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "fsearch_database.h" 4 | #include "fsearch_database_entry.h" 5 | #include "fsearch_filter_manager.h" 6 | #include "fsearch_query.h" 7 | #include "fsearch_query_flags.h" 8 | 9 | typedef enum { 10 | DATABASE_VIEW_NOTIFY_CONTENT_CHANGED, 11 | DATABASE_VIEW_NOTIFY_SELECTION_CHANGED, 12 | DATABASE_VIEW_NOTIFY_SEARCH_STARTED, 13 | DATABASE_VIEW_NOTIFY_SEARCH_FINISHED, 14 | DATABASE_VIEW_NOTIFY_SORT_STARTED, 15 | DATABASE_VIEW_NOTIFY_SORT_FINISHED, 16 | } FsearchDatabaseViewNotify; 17 | 18 | typedef struct FsearchDatabaseView FsearchDatabaseView; 19 | 20 | typedef void (*FsearchDatabaseViewNotifyFunc)(FsearchDatabaseView *view, FsearchDatabaseViewNotify id, gpointer user_data); 21 | 22 | void 23 | db_view_unref(FsearchDatabaseView *view); 24 | 25 | FsearchDatabaseView * 26 | db_view_ref(FsearchDatabaseView *view); 27 | 28 | FsearchDatabaseView * 29 | db_view_new(const char *query_text, 30 | FsearchQueryFlags flags, 31 | FsearchFilter *filter, 32 | FsearchFilterManager *filters, 33 | FsearchDatabaseIndexType sort_order, 34 | GtkSortType sort_type, 35 | FsearchDatabaseViewNotifyFunc notify_func, 36 | gpointer notify_func_data); 37 | 38 | void 39 | db_view_cancel_current_task(FsearchDatabaseView *view); 40 | 41 | void 42 | db_view_set_thread_pool(FsearchDatabaseView *view, FsearchThreadPool *pool); 43 | 44 | void 45 | db_view_set_filter(FsearchDatabaseView *view, FsearchFilter *filter); 46 | 47 | void 48 | db_view_set_filters(FsearchDatabaseView *view, FsearchFilterManager *filters); 49 | 50 | void 51 | db_view_set_query_flags(FsearchDatabaseView *view, FsearchQueryFlags query_flags); 52 | 53 | void 54 | db_view_set_query_text(FsearchDatabaseView *view, const char *query_text); 55 | 56 | void 57 | db_view_set_sort_order(FsearchDatabaseView *view, FsearchDatabaseIndexType sort_order, GtkSortType sort_type); 58 | 59 | // NOTE: Getters are not thread save, they need to be wrapped with db_view_lock/db_view_unlock 60 | uint32_t 61 | db_view_get_num_folders(FsearchDatabaseView *view); 62 | 63 | uint32_t 64 | db_view_get_num_files(FsearchDatabaseView *view); 65 | 66 | uint32_t 67 | db_view_get_num_entries(FsearchDatabaseView *view); 68 | 69 | GtkSortType 70 | db_view_get_sort_type(FsearchDatabaseView *view); 71 | 72 | FsearchDatabaseIndexType 73 | db_view_get_sort_order(FsearchDatabaseView *view); 74 | 75 | GString * 76 | db_view_entry_get_path_for_idx(FsearchDatabaseView *view, uint32_t idx); 77 | 78 | GString * 79 | db_view_entry_get_path_full_for_idx(FsearchDatabaseView *view, uint32_t idx); 80 | 81 | void 82 | db_view_entry_append_path_for_idx(FsearchDatabaseView *view, uint32_t idx, GString *str); 83 | 84 | time_t 85 | db_view_entry_get_mtime_for_idx(FsearchDatabaseView *view, uint32_t idx); 86 | 87 | off_t 88 | db_view_entry_get_size_for_idx(FsearchDatabaseView *view, uint32_t idx); 89 | 90 | char * 91 | db_view_entry_get_extension_for_idx(FsearchDatabaseView *view, uint32_t idx); 92 | 93 | GString * 94 | db_view_entry_get_name_for_idx(FsearchDatabaseView *view, uint32_t idx); 95 | 96 | FsearchDatabaseEntry * 97 | db_view_entry_get_for_idx(FsearchDatabaseView *view, uint32_t idx); 98 | 99 | GString * 100 | db_view_entry_get_name_raw_for_idx(FsearchDatabaseView *view, uint32_t idx); 101 | 102 | int32_t 103 | db_view_entry_get_parent_for_idx(FsearchDatabaseView *view, uint32_t idx); 104 | 105 | FsearchDatabaseEntryType 106 | db_view_entry_get_type_for_idx(FsearchDatabaseView *view, uint32_t idx); 107 | 108 | void 109 | db_view_register_database(FsearchDatabaseView *view, FsearchDatabase *db); 110 | 111 | void 112 | db_view_unregister_database(FsearchDatabaseView *view); 113 | 114 | FsearchQueryFlags 115 | db_view_get_query_flags(FsearchDatabaseView *view); 116 | 117 | FsearchQuery * 118 | db_view_get_query(FsearchDatabaseView *view); 119 | 120 | // NOTE: Selection handlers are thread safe 121 | void 122 | db_view_select_toggle(FsearchDatabaseView *view, uint32_t idx); 123 | 124 | void 125 | db_view_select(FsearchDatabaseView *view, uint32_t idx); 126 | 127 | bool 128 | db_view_is_selected(FsearchDatabaseView *view, uint32_t idx); 129 | 130 | void 131 | db_view_select_range(FsearchDatabaseView *view, uint32_t start_idx, uint32_t end_idx); 132 | 133 | void 134 | db_view_toggle_range(FsearchDatabaseView *view, uint32_t start_idx, uint32_t end_idx); 135 | 136 | void 137 | db_view_select_all(FsearchDatabaseView *view); 138 | 139 | void 140 | db_view_unselect_all(FsearchDatabaseView *view); 141 | 142 | void 143 | db_view_invert_selection(FsearchDatabaseView *view); 144 | 145 | uint32_t 146 | db_view_get_num_selected(FsearchDatabaseView *view); 147 | 148 | void 149 | db_view_selection_for_each(FsearchDatabaseView *view, GHFunc func, gpointer user_data); 150 | 151 | void 152 | db_view_unlock(FsearchDatabaseView *view); 153 | 154 | void 155 | db_view_lock(FsearchDatabaseView *view); 156 | -------------------------------------------------------------------------------- /src/fsearch_exclude_path.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "fsearch_exclude_path.h" 7 | 8 | FsearchExcludePath * 9 | fsearch_exclude_path_new(const char *path, bool enabled) { 10 | FsearchExcludePath *fs_path = calloc(1, sizeof(FsearchExcludePath)); 11 | g_assert(fs_path); 12 | 13 | if (path) { 14 | fs_path->path = strdup(path); 15 | } 16 | fs_path->enabled = enabled; 17 | 18 | return fs_path; 19 | } 20 | 21 | FsearchExcludePath * 22 | fsearch_exclude_path_copy(FsearchExcludePath *src) { 23 | if (!src) { 24 | return NULL; 25 | } 26 | return fsearch_exclude_path_new(src->path, src->enabled); 27 | } 28 | 29 | void 30 | fsearch_exclude_path_free(FsearchExcludePath *fs_path) { 31 | if (!fs_path) { 32 | return; 33 | } 34 | 35 | g_clear_pointer(&fs_path->path, free); 36 | g_clear_pointer(&fs_path, free); 37 | } 38 | -------------------------------------------------------------------------------- /src/fsearch_exclude_path.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | typedef struct _FsearchExcludePath { 8 | char *path; 9 | bool enabled; 10 | } FsearchExcludePath; 11 | 12 | FsearchExcludePath * 13 | fsearch_exclude_path_new(const char *path, bool enabled); 14 | 15 | FsearchExcludePath * 16 | fsearch_exclude_path_copy(FsearchExcludePath *src); 17 | 18 | void 19 | fsearch_exclude_path_free(FsearchExcludePath *fs_path); 20 | 21 | -------------------------------------------------------------------------------- /src/fsearch_file_utils.h: -------------------------------------------------------------------------------- 1 | /* 2 | FSearch - A fast file search utility 3 | Copyright © 2020 Christian Boxdörfer 4 | 5 | This program is free software; you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation; either version 2 of the License, or 8 | (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program; if not, see . 17 | */ 18 | 19 | #pragma once 20 | 21 | #include 22 | #include 23 | 24 | typedef void (*FsearchFileUtilsOpenCallback)(gboolean result, const char *error_message, gpointer user_data); 25 | 26 | void 27 | fsearch_file_utils_init_data_dir_path(char *path, size_t len); 28 | 29 | bool 30 | fsearch_file_utils_create_dir(const char *path); 31 | 32 | bool 33 | fsearch_file_utils_trash(const char *path, GString *error_messages); 34 | 35 | bool 36 | fsearch_file_utils_remove(const char *path, GString *error_messages); 37 | 38 | void 39 | fsearch_file_utils_open_path_list(GList *paths, 40 | bool launch_desktop_files, 41 | GAppLaunchContext *app_launch_context, 42 | FsearchFileUtilsOpenCallback callback, 43 | gpointer callback_data); 44 | 45 | bool 46 | fsearch_file_utils_open_path_list_with_command(GList *paths, const char *cmd, GString *error_message); 47 | 48 | gchar * 49 | fsearch_file_utils_get_file_type(const gchar *name, gboolean is_dir); 50 | 51 | gchar * 52 | fsearch_file_utils_get_file_type_non_localized(const char *name, gboolean is_dir); 53 | 54 | GIcon * 55 | fsearch_file_utils_get_icon_for_path(const char *path); 56 | 57 | GIcon * 58 | fsearch_file_utils_guess_icon(const char *name, const char *path, bool is_dir); 59 | 60 | char * 61 | fsearch_file_utils_get_size_formatted(off_t size, bool show_base_2_units); 62 | 63 | bool 64 | fsearch_file_utils_is_desktop_file(const char *path); 65 | 66 | GIcon * 67 | fsearch_file_utils_get_desktop_file_icon(const char *path); 68 | 69 | char * 70 | fsearch_file_utils_get_content_type(const char *path, GError **error); 71 | 72 | GIcon * 73 | fsearch_file_utils_get_thumbnail_icon(const char *path); 74 | -------------------------------------------------------------------------------- /src/fsearch_filter.c: -------------------------------------------------------------------------------- 1 | #include "fsearch_filter.h" 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | FsearchFilter * 8 | fsearch_filter_new(const char *name, const char *macro, const char *query, FsearchQueryFlags flags) { 9 | g_assert(name); 10 | 11 | FsearchFilter *filter = calloc(1, sizeof(FsearchFilter)); 12 | g_assert(filter); 13 | 14 | filter->name = strdup(name); 15 | filter->macro = strdup(macro ? macro : ""); 16 | filter->query = strdup(query ? query : ""); 17 | filter->flags = flags; 18 | filter->ref_count = 1; 19 | return filter; 20 | } 21 | 22 | bool 23 | fsearch_filter_cmp(FsearchFilter *filter_1, FsearchFilter *filter_2) { 24 | g_assert(filter_1); 25 | g_assert(filter_2); 26 | 27 | if (strcmp(filter_1->name, filter_2->name) != 0) { 28 | return false; 29 | } 30 | if (strcmp(filter_1->macro, filter_2->macro) != 0) { 31 | return false; 32 | } 33 | if (strcmp(filter_1->query, filter_2->query) != 0) { 34 | return false; 35 | } 36 | if (filter_1->flags != filter_2->flags) { 37 | return false; 38 | } 39 | return true; 40 | } 41 | 42 | FsearchFilter * 43 | fsearch_filter_copy(FsearchFilter *filter) { 44 | if (!filter) { 45 | return NULL; 46 | } 47 | return fsearch_filter_new(filter->name, filter->macro, filter->query, filter->flags); 48 | } 49 | 50 | static void 51 | fsearch_filter_free(FsearchFilter *filter) { 52 | g_clear_pointer(&filter->name, free); 53 | g_clear_pointer(&filter->macro, free); 54 | g_clear_pointer(&filter->query, free); 55 | g_clear_pointer(&filter, free); 56 | } 57 | 58 | FsearchFilter * 59 | fsearch_filter_ref(FsearchFilter *filter) { 60 | if (!filter || g_atomic_int_get(&filter->ref_count) <= 0) { 61 | return NULL; 62 | } 63 | g_atomic_int_inc(&filter->ref_count); 64 | return filter; 65 | } 66 | 67 | void 68 | fsearch_filter_unref(FsearchFilter *filter) { 69 | if (!filter || g_atomic_int_get(&filter->ref_count) <= 0) { 70 | return; 71 | } 72 | if (g_atomic_int_dec_and_test(&filter->ref_count)) { 73 | g_clear_pointer(&filter, fsearch_filter_free); 74 | } 75 | } 76 | 77 | static const char *file_filter = "file:"; 78 | static const char *folder_filter = "folder:"; 79 | static const char *application_filter = "ext:desktop;DESKTOP"; 80 | static const char *document_filter = "ext:c;chm;cpp;csv;cxx;doc;docm;docx;dot;dotm;dotx;h;hpp;htm;html;hxx;ini;java;" 81 | "lua;mht;mhtml;ods;odt;odp;pdf;potx;potm;ppam;ppsm;ppsx;pps;ppt;pptm;pptx;rtf;" 82 | "sldm;sldx;thmx;txt;vsd;vsdx;wpd;wps;wri;xlam;xls;xlsb;xlsm;xlsx;xltm;xltx;xml;C;" 83 | "CHM;" 84 | "CPP;CSV;CXX;DOC;DOCM;DOCX;DOT;DOTM;DOTX;H;HPP;HTM;HTML;HXX;INI;JAVA;LUA;MHT;" 85 | "MHTML;ODS;ODT;ODP;PDF;POTX;POTM;PPAM;PPSM;PPSX;PPS;PPT;PPTM;PPTX;RTF;SLDM;SLDX;" 86 | "THMX;TXT;VSD;VSDX;WPD;WPS;WRI;XLAM;XLS;XLSB;XLSM;XLSX;XLTM;XLTX;XML"; 87 | static const char *audio_filter = "ext:aac;ac3;aif;aifc;aiff;au;cda;dts;fla;flac;it;m1a;m2a;m3u;m4a;mid;midi;mka;mod;" 88 | "mp2;mp3;mpa;ogg;opus;ra;rmi;spc;rmi;snd;umx;voc;wav;wma;xm;AAC;AC3;AIF;AIFC;AIFF;AU;" 89 | "CDA;DTS;FLA;FLAC;IT;M1A;M2A;M3U;M4A;MID;MIDI;MKA;MOD;MP2;MP3;MPA;OGG;OPUS;RA;RMI;" 90 | "SPC;RMI;SND;UMX;VOC;WAV;WMA;XM"; 91 | static const char *image_filter = "ext:ani;bmp;gif;ico;jpe;jpeg;jpg;pcx;png;psd;tga;tif;tiff;webp;svg;avif;nef;dds;exr;" 92 | "hdr;wmf;" 93 | "ANI;BMP;GIF;ICO;JPE;JPEG;JPG;PCX;PNG;PSD;TGA;TIF;TIFF;WEBP;WMF;SVG;AVIF;NEF;DDS;EXR;" 94 | "HDR;WMF;"; 95 | static const char *video_filter = "ext:3g2;3gp;3gp2;3gpp;amr;amv;asf;avi;bdmv;bik;d2v;divx;drc;dsa;dsm;dss;dsv;evo;f4v;" 96 | "flc;fli;flic;flv;hdmov;ifo;ivf;m1v;m2p;m2t;m2ts;m2v;m4b;m4p;m4v;mkv;mp2v;mp4;mp4v;" 97 | "mpe;mpeg;mpg;mpls;mpv2;mpv4;mov;mts;ogm;ogv;pss;pva;qt;ram;ratdvd;rm;rmm;rmvb;roq;" 98 | "rpm;smil;smk;swf;tp;tpr;ts;vob;vp6;webm;wm;wmp;wmv;3G2;3GP;3GP2;3GPP;AMR;AMV;ASF;" 99 | "AVI;BDMV;BIK;D2V;DIVX;DRC;DSA;DSM;DSS;DSV;EVO;F4V;FLC;FLI;FLIC;FLV;HDMOV;IFO;IVF;" 100 | "M1V;M2P;M2T;M2TS;M2V;M4B;M4P;M4V;MKV;MP2V;MP4;MP4V;MPE;MPEG;MPG;MPLS;MPV2;MPV4;MOV;" 101 | "MTS;OGM;OGV;PSS;PVA;QT;RAM;RATDVD;RM;RMM;RMVB;ROQ;RPM;SMIL;SMK;SWF;TP;TPR;TS;VOB;" 102 | "VP6;WEBM;WM;WMP;WMV"; 103 | static const char *archive_filter = "ext:7z;ace;arj;bz2;cab;gz;gzip;jar;r00;r01;r02;r03;r04;r05;r06;r07;r08;r09;r10;" 104 | "r11;r12;r13;r14;r15;r16;r17;r18;r19;r20;r21;r22;r23;r24;r25;r26;r27;r28;r29;rar;" 105 | "tar;tgz;z;zip;7Z;ACE;ARJ;BZ2;CAB;GZ;GZIP;JAR;R00;R01;R02;R03;R04;R05;R06;R07;R08;" 106 | "R09;R10;R11;R12;R13;R14;R15;R16;R17;R18;R19;R20;R21;R22;R23;R24;R25;R26;R27;R28;" 107 | "R29;RAR;TAR;TGZ;Z;ZIP"; 108 | 109 | GList * 110 | fsearch_filter_get_default() { 111 | GList *filters = NULL; 112 | filters = g_list_append(filters, fsearch_filter_new(_("All"), NULL, NULL, 0)); 113 | filters = g_list_append(filters, fsearch_filter_new(_("Folders"), NULL, folder_filter, 0)); 114 | filters = g_list_append(filters, fsearch_filter_new(_("Files"), NULL, file_filter, 0)); 115 | filters = g_list_append(filters, 116 | fsearch_filter_new(_("Applications"), "app", application_filter, QUERY_FLAG_MATCH_CASE)); 117 | filters = g_list_append(filters, fsearch_filter_new(_("Archives"), "archive", archive_filter, QUERY_FLAG_MATCH_CASE)); 118 | filters = g_list_append(filters, fsearch_filter_new(_("Audio"), "audio", audio_filter, QUERY_FLAG_MATCH_CASE)); 119 | filters = g_list_append(filters, fsearch_filter_new(_("Documents"), "doc", document_filter, QUERY_FLAG_MATCH_CASE)); 120 | filters = g_list_append(filters, fsearch_filter_new(_("Pictures"), "pic", image_filter, QUERY_FLAG_MATCH_CASE)); 121 | filters = g_list_append(filters, fsearch_filter_new(_("Videos"), "video", video_filter, QUERY_FLAG_MATCH_CASE)); 122 | 123 | return filters; 124 | } -------------------------------------------------------------------------------- /src/fsearch_filter.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include "fsearch_query_flags.h" 7 | 8 | typedef struct FsearchFilter { 9 | char *name; 10 | char *macro; 11 | char *query; 12 | FsearchQueryFlags flags; 13 | 14 | volatile int ref_count; 15 | } FsearchFilter; 16 | 17 | FsearchFilter * 18 | fsearch_filter_new(const char *name, const char *macro, const char *query, FsearchQueryFlags flags); 19 | 20 | FsearchFilter * 21 | fsearch_filter_ref(FsearchFilter *filter); 22 | 23 | bool 24 | fsearch_filter_cmp(FsearchFilter *filter_1, FsearchFilter *filter_2); 25 | 26 | FsearchFilter * 27 | fsearch_filter_copy(FsearchFilter *filter); 28 | 29 | void 30 | fsearch_filter_unref(FsearchFilter *filter); 31 | 32 | GList * 33 | fsearch_filter_get_default(); 34 | -------------------------------------------------------------------------------- /src/fsearch_filter_editor.c: -------------------------------------------------------------------------------- 1 | #include "fsearch_filter_editor.h" 2 | 3 | #include 4 | #include 5 | 6 | #include "fsearch_string_utils.h" 7 | 8 | struct FsearchFilterEditor { 9 | FsearchFilter *filter; 10 | GtkBuilder *builder; 11 | GtkWidget *dialog; 12 | GtkWidget *ok_button; 13 | GtkEntry *name_entry; 14 | GtkEntry *macro_entry; 15 | GtkTextBuffer *query_text_buffer; 16 | GtkToggleButton *search_in_path; 17 | GtkToggleButton *enable_regex; 18 | GtkToggleButton *match_case; 19 | FsearchFilterEditorResponse *callback; 20 | gpointer data; 21 | }; 22 | 23 | static void 24 | fsearch_filter_editor_free(FsearchFilterEditor *editor) { 25 | g_clear_object(&editor->builder); 26 | g_clear_pointer(&editor->filter, fsearch_filter_unref); 27 | g_clear_pointer(&editor->dialog, gtk_widget_destroy); 28 | g_clear_pointer(&editor, free); 29 | } 30 | 31 | static void 32 | on_editor_ui_response(GtkDialog *dialog, GtkResponseType response, gpointer user_data) { 33 | FsearchFilterEditor *editor = user_data; 34 | 35 | char *name = NULL; 36 | char *query = NULL; 37 | char *macro = NULL; 38 | FsearchQueryFlags flags = 0; 39 | 40 | const char *name_str = gtk_entry_get_text(editor->name_entry); 41 | if (response == GTK_RESPONSE_OK && !fsearch_string_is_empty(name_str)) { 42 | name = g_strdup(name_str); 43 | 44 | macro = g_strdup(gtk_entry_get_text(editor->macro_entry)); 45 | GtkTextIter start, end; 46 | gtk_text_buffer_get_bounds(editor->query_text_buffer, &start, &end); 47 | query = gtk_text_buffer_get_text(editor->query_text_buffer, &start, &end, FALSE); 48 | const gboolean match_case = gtk_toggle_button_get_active(editor->match_case); 49 | const gboolean enable_regex = gtk_toggle_button_get_active(editor->enable_regex); 50 | const gboolean search_in_path = gtk_toggle_button_get_active(editor->search_in_path); 51 | if (match_case) { 52 | flags |= QUERY_FLAG_MATCH_CASE; 53 | } 54 | if (enable_regex) { 55 | flags |= QUERY_FLAG_REGEX; 56 | } 57 | if (search_in_path) { 58 | flags |= QUERY_FLAG_SEARCH_IN_PATH; 59 | } 60 | } 61 | 62 | if (editor->callback) { 63 | editor->callback(editor->filter, name, macro, query, flags, editor->data); 64 | } 65 | 66 | fsearch_filter_editor_free(editor); 67 | } 68 | 69 | static void 70 | on_macro_entry_changed(GtkEntry *entry, gpointer user_data) { 71 | FsearchFilterEditor *editor = user_data; 72 | if (!editor->ok_button) { 73 | return; 74 | } 75 | const char *macro_text = gtk_entry_get_text(entry); 76 | if (macro_text && strchr(macro_text, ':')) { 77 | gtk_widget_set_sensitive(editor->ok_button, FALSE); 78 | g_object_set(entry, "secondary-icon-name", "dialog-warning-symbolic", NULL); 79 | g_object_set(entry, "secondary-icon-tooltip-text", _("Macro names must not contain `:` characters."), NULL); 80 | } 81 | else if (!gtk_widget_get_sensitive(editor->ok_button)) { 82 | gtk_widget_set_sensitive(editor->ok_button, TRUE); 83 | g_object_set(entry, "secondary-icon-name", NULL, NULL); 84 | } 85 | } 86 | 87 | void 88 | fsearch_filter_editor_run(const char *title, 89 | GtkWindow *parent_window, 90 | FsearchFilter *filter, 91 | FsearchFilterEditorResponse callback, 92 | gpointer data) { 93 | FsearchFilterEditor *editor = calloc(1, sizeof(FsearchFilterEditor)); 94 | g_assert(editor); 95 | 96 | editor->filter = filter; 97 | editor->callback = callback; 98 | editor->data = data; 99 | 100 | editor->builder = gtk_builder_new_from_resource("/io/github/cboxdoerfer/fsearch/ui/fsearch_filter_editor.ui"); 101 | 102 | editor->dialog = GTK_WIDGET(gtk_builder_get_object(editor->builder, "FsearchFilterEditorWindow")); 103 | gtk_window_set_transient_for(GTK_WINDOW(editor->dialog), parent_window); 104 | gtk_dialog_add_button(GTK_DIALOG(editor->dialog), _("_Cancel"), GTK_RESPONSE_CANCEL); 105 | editor->ok_button = gtk_dialog_add_button(GTK_DIALOG(editor->dialog), _("_OK"), GTK_RESPONSE_OK); 106 | g_signal_connect(editor->dialog, "response", G_CALLBACK(on_editor_ui_response), editor); 107 | 108 | editor->search_in_path = GTK_TOGGLE_BUTTON(gtk_builder_get_object(editor->builder, "filter_search_in_path")); 109 | editor->enable_regex = GTK_TOGGLE_BUTTON(gtk_builder_get_object(editor->builder, "filter_regex")); 110 | editor->match_case = GTK_TOGGLE_BUTTON(gtk_builder_get_object(editor->builder, "filter_match_case")); 111 | editor->name_entry = GTK_ENTRY(gtk_builder_get_object(editor->builder, "filter_name")); 112 | editor->macro_entry = GTK_ENTRY(gtk_builder_get_object(editor->builder, "filter_macro")); 113 | g_signal_connect(editor->macro_entry, "changed", G_CALLBACK(on_macro_entry_changed), editor); 114 | editor->query_text_buffer = GTK_TEXT_BUFFER(gtk_builder_get_object(editor->builder, "filter_query_buffer")); 115 | 116 | if (title) { 117 | gtk_window_set_title(GTK_WINDOW(editor->dialog), title); 118 | } 119 | 120 | if (filter) { 121 | gtk_entry_set_text(editor->name_entry, filter->name); 122 | gtk_entry_set_text(editor->macro_entry, filter->macro); 123 | gtk_text_buffer_set_text(editor->query_text_buffer, filter->query, -1); 124 | gtk_toggle_button_set_active(editor->search_in_path, filter->flags & QUERY_FLAG_SEARCH_IN_PATH ? TRUE : FALSE); 125 | gtk_toggle_button_set_active(editor->match_case, filter->flags & QUERY_FLAG_MATCH_CASE ? TRUE : FALSE); 126 | gtk_toggle_button_set_active(editor->enable_regex, filter->flags & QUERY_FLAG_REGEX ? TRUE : FALSE); 127 | } 128 | gtk_widget_show(editor->dialog); 129 | } 130 | -------------------------------------------------------------------------------- /src/fsearch_filter_editor.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "fsearch_filter.h" 6 | 7 | typedef struct FsearchFilterEditor FsearchFilterEditor; 8 | typedef void(FsearchFilterEditorResponse)(FsearchFilter *, char *, char *, char *, FsearchQueryFlags, gpointer); 9 | 10 | void 11 | fsearch_filter_editor_run(const char *title, 12 | GtkWindow *parent_window, 13 | FsearchFilter *filter, 14 | FsearchFilterEditorResponse callback, 15 | gpointer data); 16 | -------------------------------------------------------------------------------- /src/fsearch_filter_manager.c: -------------------------------------------------------------------------------- 1 | #include "fsearch_filter_manager.h" 2 | 3 | #include 4 | #include 5 | 6 | struct FsearchFilterManager { 7 | GList *filters; 8 | }; 9 | 10 | void 11 | fsearch_filter_manager_free(FsearchFilterManager *manager) { 12 | if (!manager) { 13 | return; 14 | } 15 | g_list_free_full(g_steal_pointer(&manager->filters), (GDestroyNotify)fsearch_filter_unref); 16 | g_clear_pointer(&manager, free); 17 | } 18 | 19 | FsearchFilterManager * 20 | fsearch_filter_manager_new(void) { 21 | FsearchFilterManager *manager = calloc(1, sizeof(FsearchFilterManager)); 22 | g_assert(manager); 23 | 24 | manager->filters = NULL; 25 | return manager; 26 | } 27 | 28 | FsearchFilterManager * 29 | fsearch_filter_manager_new_with_defaults(void) { 30 | FsearchFilterManager *manager = fsearch_filter_manager_new(); 31 | 32 | manager->filters = fsearch_filter_get_default(); 33 | return manager; 34 | } 35 | 36 | FsearchFilterManager * 37 | fsearch_filter_manager_copy(FsearchFilterManager *manager) { 38 | FsearchFilterManager *copy = fsearch_filter_manager_new(); 39 | 40 | for (GList *l = manager->filters; l != NULL; l = l->next) { 41 | FsearchFilter *filter = l->data; 42 | copy->filters = g_list_append(copy->filters, fsearch_filter_copy(filter)); 43 | } 44 | return copy; 45 | } 46 | 47 | static gboolean 48 | filter_exists(GList *filters, FsearchFilter *filter, const char *name) { 49 | for (GList *f = filters; f != NULL; f = f->next) { 50 | FsearchFilter *ff = f->data; 51 | if (!ff || ff == filter) { 52 | continue; 53 | } 54 | if (!strcmp(ff->name, name)) { 55 | return TRUE; 56 | } 57 | } 58 | return FALSE; 59 | } 60 | 61 | static void 62 | update_filter_to_unique_name(GList *filters, FsearchFilter *filter) { 63 | uint32_t filter_name_copy = 1; 64 | 65 | char *filter_name = g_strdup(filter->name); 66 | char *new_name = g_strdup(filter->name); 67 | 68 | while (filter_exists(filters, filter, new_name)) { 69 | g_clear_pointer(&new_name, g_free); 70 | new_name = g_strdup_printf("%s (%d)", filter_name, filter_name_copy); 71 | filter_name_copy++; 72 | } 73 | g_clear_pointer(&filter->name, g_free); 74 | g_clear_pointer(&filter_name, g_free); 75 | filter->name = new_name; 76 | } 77 | 78 | void 79 | fsearch_filter_manager_append_filter(FsearchFilterManager *manager, FsearchFilter *filter) { 80 | update_filter_to_unique_name(manager->filters, filter); 81 | manager->filters = g_list_append(manager->filters, fsearch_filter_ref(filter)); 82 | } 83 | 84 | void 85 | fsearch_filter_manager_reorder(FsearchFilterManager *manager, gint *new_order, size_t new_order_len) { 86 | if (!new_order) { 87 | return; 88 | } 89 | GList *reordered_filters = NULL; 90 | for (uint32_t i = 0; i < new_order_len; ++i) { 91 | const gint old_pos = new_order[i]; 92 | GList *f = g_list_nth(manager->filters, old_pos); 93 | reordered_filters = g_list_append(reordered_filters, f->data); 94 | } 95 | g_list_free(manager->filters); 96 | manager->filters = reordered_filters; 97 | } 98 | 99 | void 100 | fsearch_filter_manager_remove(FsearchFilterManager *manager, FsearchFilter *filter) { 101 | if (!filter) { 102 | return; 103 | } 104 | manager->filters = g_list_remove(manager->filters, filter); 105 | g_clear_pointer(&filter, fsearch_filter_unref); 106 | } 107 | 108 | void 109 | fsearch_filter_manager_edit(FsearchFilterManager *manager, 110 | FsearchFilter *filter, 111 | const char *name, 112 | const char *macro, 113 | const char *query, 114 | FsearchQueryFlags flags) { 115 | if (!name) { 116 | return; 117 | } 118 | g_clear_pointer(&filter->name, g_free); 119 | g_clear_pointer(&filter->query, g_free); 120 | filter->name = g_strdup(name); 121 | filter->query = g_strdup(query ? query : ""); 122 | filter->macro = g_strdup(macro ? macro : ""); 123 | filter->flags = flags; 124 | update_filter_to_unique_name(manager->filters, filter); 125 | } 126 | 127 | FsearchFilter * 128 | fsearch_filter_manager_get_filter_for_name(FsearchFilterManager *manager, const char *name) { 129 | g_assert(name); 130 | 131 | for (GList *l = manager->filters; l != NULL; l = l->next) { 132 | FsearchFilter *filter = l->data; 133 | g_assert(filter); 134 | if (!strcmp(filter->name, name)) { 135 | return fsearch_filter_ref(filter); 136 | } 137 | } 138 | return NULL; 139 | } 140 | 141 | guint 142 | fsearch_filter_manager_get_num_filters(FsearchFilterManager *manager) { 143 | return g_list_length(manager->filters); 144 | } 145 | 146 | FsearchFilter * 147 | fsearch_filter_manager_get_filter(FsearchFilterManager *manager, guint idx) { 148 | if (idx >= g_list_length(manager->filters)) { 149 | return NULL; 150 | } 151 | GList *l = g_list_nth(manager->filters, idx); 152 | FsearchFilter *filter = l->data; 153 | return filter ? fsearch_filter_ref(filter) : NULL; 154 | } 155 | 156 | bool 157 | fsearch_filter_manager_cmp(FsearchFilterManager *manager_1, FsearchFilterManager *manager_2) { 158 | g_assert(manager_1); 159 | g_assert(manager_2); 160 | 161 | guint len1 = g_list_length(manager_1->filters); 162 | guint len2 = g_list_length(manager_2->filters); 163 | if (len1 != len2) { 164 | return false; 165 | } 166 | 167 | GList *l1 = manager_1->filters; 168 | GList *l2 = manager_2->filters; 169 | while (l1 && l2) { 170 | FsearchFilter *f1 = l1->data; 171 | FsearchFilter *f2 = l2->data; 172 | if (!fsearch_filter_cmp(f1, f2)) { 173 | return false; 174 | } 175 | l1 = l1->next; 176 | l2 = l2->next; 177 | } 178 | return true; 179 | } 180 | -------------------------------------------------------------------------------- /src/fsearch_filter_manager.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include "fsearch_filter.h" 7 | 8 | typedef struct FsearchFilterManager FsearchFilterManager; 9 | 10 | void 11 | fsearch_filter_manager_free(FsearchFilterManager *manager); 12 | 13 | FsearchFilterManager * 14 | fsearch_filter_manager_new(void); 15 | 16 | FsearchFilterManager * 17 | fsearch_filter_manager_new_with_defaults(void); 18 | 19 | FsearchFilter * 20 | fsearch_filter_manager_get_filter_for_name(FsearchFilterManager *manager, const char *name); 21 | 22 | FsearchFilterManager * 23 | fsearch_filter_manager_copy(FsearchFilterManager *manager); 24 | 25 | guint 26 | fsearch_filter_manager_get_num_filters(FsearchFilterManager *manager); 27 | 28 | FsearchFilter * 29 | fsearch_filter_manager_get_filter(FsearchFilterManager *manager, guint idx); 30 | 31 | void 32 | fsearch_filter_manager_append_filter(FsearchFilterManager *manager, FsearchFilter *filter); 33 | 34 | void 35 | fsearch_filter_manager_reorder(FsearchFilterManager *manager, gint *new_order, size_t new_order_len); 36 | 37 | void 38 | fsearch_filter_manager_remove(FsearchFilterManager *manager, FsearchFilter *filter); 39 | 40 | void 41 | fsearch_filter_manager_edit(FsearchFilterManager *manager, 42 | FsearchFilter *filter, 43 | const char *name, 44 | const char *macro, 45 | const char *query, 46 | FsearchQueryFlags flags); 47 | 48 | bool 49 | fsearch_filter_manager_cmp(FsearchFilterManager *manager_1, FsearchFilterManager *manager_2); 50 | -------------------------------------------------------------------------------- /src/fsearch_index.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "fsearch_index.h" 7 | 8 | FsearchIndex * 9 | fsearch_index_new(FsearchIndexType type, 10 | const char *path, 11 | bool search_in, 12 | bool update, 13 | bool one_filesystem, 14 | time_t last_updated) { 15 | FsearchIndex *index = calloc(1, sizeof(FsearchIndex)); 16 | g_assert(index); 17 | 18 | index->type = type; 19 | index->path = path ? strdup(path) : strdup(""); 20 | index->enabled = search_in; 21 | index->update = update; 22 | index->one_filesystem = one_filesystem; 23 | index->last_updated = last_updated; 24 | 25 | return index; 26 | } 27 | 28 | FsearchIndex * 29 | fsearch_index_copy(FsearchIndex *index) { 30 | if (!index) { 31 | return NULL; 32 | } 33 | return fsearch_index_new(index->type, 34 | index->path, 35 | index->enabled, 36 | index->update, 37 | index->one_filesystem, 38 | index->last_updated); 39 | } 40 | 41 | void 42 | fsearch_index_free(FsearchIndex *index) { 43 | if (!index) { 44 | return; 45 | } 46 | 47 | g_clear_pointer(&index->path, free); 48 | g_clear_pointer(&index, free); 49 | } 50 | -------------------------------------------------------------------------------- /src/fsearch_index.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | typedef enum { 8 | FSEARCH_INDEX_FOLDER_TYPE, 9 | NUM_FSEARCH_INDEX_TYPES, 10 | } FsearchIndexType; 11 | 12 | typedef struct _FsearchIndex { 13 | FsearchIndexType type; 14 | 15 | char *path; 16 | bool enabled; 17 | bool update; 18 | bool one_filesystem; 19 | 20 | time_t last_updated; 21 | } FsearchIndex; 22 | 23 | FsearchIndex * 24 | fsearch_index_new(FsearchIndexType type, 25 | const char *path, 26 | bool search_in, 27 | bool update, 28 | bool one_filesystem, 29 | time_t last_updated); 30 | 31 | FsearchIndex * 32 | fsearch_index_copy(FsearchIndex *index); 33 | 34 | void 35 | fsearch_index_free(FsearchIndex *index); 36 | -------------------------------------------------------------------------------- /src/fsearch_limits.h: -------------------------------------------------------------------------------- 1 | /* 2 | FSearch - A fast file search utility 3 | Copyright © 2020 Christian Boxdörfer 4 | 5 | This program is free software; you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation; either version 2 of the License, or 8 | (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program; if not, see . 17 | */ 18 | 19 | #pragma once 20 | 21 | #include 22 | 23 | #define FSEARCH_THREAD_LIMIT 32 24 | 25 | #ifndef PATH_MAX 26 | #define PATH_MAX 4096 /* max # of characters in a path name */ 27 | #endif 28 | -------------------------------------------------------------------------------- /src/fsearch_list_view.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #define ROW_PADDING_Y 4 7 | #define ROW_PADDING_X 3 8 | 9 | G_BEGIN_DECLS 10 | 11 | #define FSEARCH_TYPE_LIST_VIEW (fsearch_list_view_get_type()) 12 | 13 | G_DECLARE_FINAL_TYPE(FsearchListView, fsearch_list_view, FSEARCH, LIST_VIEW, GtkContainer) 14 | 15 | G_END_DECLS 16 | 17 | typedef struct { 18 | GtkWidget *button; 19 | GtkWidget *arrow; 20 | GtkWidget *emblem; 21 | FsearchListView *view; 22 | int type; 23 | char *name; 24 | gint width; 25 | gint effective_width; 26 | gboolean expand; 27 | gboolean visible; 28 | PangoAlignment alignment; 29 | PangoEllipsizeMode ellipsize_mode; 30 | 31 | GdkWindow *window; 32 | 33 | volatile gint ref_count; 34 | } FsearchListViewColumn; 35 | 36 | typedef char *(*FsearchListViewQueryTooltipFunc)(PangoLayout *layout, 37 | uint32_t row_height, 38 | uint32_t row, 39 | FsearchListViewColumn *col, 40 | gpointer user_data); 41 | 42 | typedef void (*FsearchListViewDrawRowFunc)(cairo_t *cr, 43 | GdkWindow *bin_window, 44 | PangoLayout *layout, 45 | GtkStyleContext *context, 46 | GList *columns, 47 | cairo_rectangle_int_t *rect, 48 | uint32_t row, 49 | gboolean row_selected, 50 | gboolean row_focused, 51 | gboolean row_hovered, 52 | gboolean right_to_left_text, 53 | gpointer user_data); 54 | 55 | typedef void (*FsearchListViewSortFunc)(int sort_order, GtkSortType sort_type, gpointer user_data); 56 | 57 | // selection handlers 58 | typedef gboolean (*FsearchListViewIsSelectedFunc)(int row_idx, gpointer user_data); 59 | typedef void (*FsearchListViewSelectFunc)(int row_idx, gpointer user_data); 60 | typedef void (*FsearchListViewSelectToggleFunc)(int row_idx, gpointer user_data); 61 | typedef void (*FsearchListViewSelectRangeFunc)(int start_idx, int end_idx, gpointer user_data); 62 | typedef void (*FsearchListViewUnselectAllFunc)(gpointer user_data); 63 | typedef guint (*FsearchListViewNumSelectedFunc)(gpointer user_data); 64 | 65 | FsearchListViewColumn * 66 | fsearch_list_view_column_ref(FsearchListViewColumn *col); 67 | 68 | void 69 | fsearch_list_view_column_unref(FsearchListViewColumn *col); 70 | 71 | FsearchListViewColumn * 72 | fsearch_list_view_column_new(int type, 73 | char *name, 74 | PangoAlignment alignment, 75 | PangoEllipsizeMode ellipsize_mode, 76 | gboolean visible, 77 | gboolean expand, 78 | uint32_t width); 79 | 80 | FsearchListView * 81 | fsearch_list_view_new(GtkAdjustment *hadjustment, GtkAdjustment *vadjustment); 82 | 83 | void 84 | fsearch_list_view_set_selection_handlers(FsearchListView *view, 85 | FsearchListViewIsSelectedFunc is_selected_func, 86 | FsearchListViewSelectFunc select_func, 87 | FsearchListViewSelectToggleFunc select_toggle_func, 88 | FsearchListViewSelectRangeFunc select_range_func, 89 | FsearchListViewSelectRangeFunc toggle_range_func, 90 | FsearchListViewUnselectAllFunc unselect_func, 91 | FsearchListViewNumSelectedFunc num_selected_func, 92 | gpointer user_data); 93 | 94 | void 95 | fsearch_list_view_column_set_visible(FsearchListView *view, FsearchListViewColumn *col, gboolean visible); 96 | 97 | void 98 | fsearch_list_view_column_set_tooltip(FsearchListViewColumn *col, const char *tooltip); 99 | 100 | void 101 | fsearch_list_view_column_set_emblem(FsearchListViewColumn *col, const char *emblem_name, gboolean visible); 102 | 103 | void 104 | fsearch_list_view_remove_column(FsearchListView *view, FsearchListViewColumn *col); 105 | 106 | void 107 | fsearch_list_view_append_column(FsearchListView *view, FsearchListViewColumn *col); 108 | 109 | FsearchListViewColumn * 110 | fsearch_list_view_get_first_column_for_type(FsearchListView *view, int type); 111 | 112 | void 113 | fsearch_list_view_set_config(FsearchListView *view, uint32_t num_rows, int sort_order, GtkSortType sort_type); 114 | 115 | gint 116 | fsearch_list_view_get_cursor(FsearchListView *view); 117 | 118 | void 119 | fsearch_list_view_set_cursor(FsearchListView *view, int row_idx); 120 | 121 | void 122 | fsearch_list_view_set_single_click_activate(FsearchListView *view, gboolean value); 123 | 124 | int 125 | fsearch_list_view_get_sort_order(FsearchListView *view); 126 | 127 | GtkSortType 128 | fsearch_list_view_get_sort_type(FsearchListView *view); 129 | 130 | void 131 | fsearch_list_view_set_sort_func(FsearchListView *view, FsearchListViewSortFunc func, gpointer sort_func_data); 132 | 133 | void 134 | fsearch_list_view_set_query_tooltip_func(FsearchListView *view, 135 | FsearchListViewQueryTooltipFunc func, 136 | gpointer func_data); 137 | 138 | void 139 | fsearch_list_view_set_draw_row_func(FsearchListView *view, FsearchListViewDrawRowFunc func, gpointer func_data); 140 | -------------------------------------------------------------------------------- /src/fsearch_listview_popup.h: -------------------------------------------------------------------------------- 1 | /* 2 | FSearch - A fast file search utility 3 | Copyright © 2020 Christian Boxdörfer 4 | 5 | This program is free software; you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation; either version 2 of the License, or 8 | (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program; if not, see . 17 | */ 18 | 19 | #pragma once 20 | 21 | #ifdef HAVE_CONFIG_H 22 | #include 23 | #endif 24 | 25 | #include "fsearch_database_view.h" 26 | 27 | #include 28 | 29 | gboolean 30 | listview_popup_menu(GtkWidget *widget, FsearchDatabaseView *db_view); 31 | -------------------------------------------------------------------------------- /src/fsearch_memory_pool.c: -------------------------------------------------------------------------------- 1 | #include "fsearch_memory_pool.h" 2 | 3 | #include 4 | #include 5 | 6 | typedef struct FsearchMemoryPoolFreed { 7 | struct FsearchMemoryPoolFreed *next; 8 | } FsearchMemoryPoolFreed; 9 | 10 | typedef struct { 11 | uint32_t num_used; 12 | uint32_t capacity; 13 | void *items; 14 | } FsearchMemoryPoolBlock; 15 | 16 | struct FsearchMemoryPool { 17 | GList *blocks; 18 | FsearchMemoryPoolFreed *freed_items; 19 | uint32_t block_size; 20 | size_t item_size; 21 | GDestroyNotify item_free_func; 22 | }; 23 | 24 | void 25 | fsearch_memory_pool_new_block(FsearchMemoryPool *pool) { 26 | FsearchMemoryPoolBlock *block = calloc(1, sizeof(FsearchMemoryPoolBlock)); 27 | g_assert(block); 28 | 29 | block->items = calloc(pool->block_size + 1, pool->item_size); 30 | g_assert(block->items); 31 | 32 | block->num_used = 0; 33 | block->capacity = pool->block_size; 34 | pool->blocks = g_list_prepend(pool->blocks, block); 35 | } 36 | 37 | FsearchMemoryPool * 38 | fsearch_memory_pool_new(uint32_t block_size, size_t item_size, GDestroyNotify item_free_func) { 39 | FsearchMemoryPool *pool = calloc(1, sizeof(FsearchMemoryPool)); 40 | g_assert(pool); 41 | pool->item_free_func = item_free_func; 42 | pool->block_size = block_size; 43 | pool->item_size = MAX(item_size, sizeof(FsearchMemoryPoolFreed)); 44 | fsearch_memory_pool_new_block(pool); 45 | 46 | return pool; 47 | } 48 | 49 | static void 50 | fsearch_memory_pool_free_block(FsearchMemoryPool *pool, FsearchMemoryPoolBlock *block) { 51 | if (pool->item_free_func) { 52 | for (int i = 0; i < block->num_used; i++) { 53 | void *data = block->items + i * pool->item_size; 54 | if (!data) { 55 | break; 56 | } 57 | pool->item_free_func(data); 58 | } 59 | } 60 | g_clear_pointer(&block->items, free); 61 | g_clear_pointer(&block, free); 62 | } 63 | 64 | void 65 | fsearch_memory_pool_free_pool(FsearchMemoryPool *pool) { 66 | if (!pool) { 67 | return; 68 | } 69 | for (GList *b = pool->blocks; b != NULL; b = b->next) { 70 | FsearchMemoryPoolBlock *block = b->data; 71 | g_assert(block); 72 | fsearch_memory_pool_free_block(pool, g_steal_pointer(&block)); 73 | } 74 | pool->freed_items = NULL; 75 | 76 | g_clear_pointer(&pool->blocks, g_list_free); 77 | g_clear_pointer(&pool, free); 78 | } 79 | 80 | bool 81 | fsearch_memory_pool_is_block_full(FsearchMemoryPool *pool) { 82 | FsearchMemoryPoolBlock *block = pool->blocks->data; 83 | g_assert(block); 84 | if (block->num_used < block->capacity) { 85 | return false; 86 | } 87 | return true; 88 | } 89 | 90 | void 91 | fsearch_memory_pool_free(FsearchMemoryPool *pool, void *item, bool item_clear) { 92 | if (!pool || !item) { 93 | return; 94 | } 95 | if (item_clear && pool->item_free_func) { 96 | pool->item_free_func(item); 97 | } 98 | FsearchMemoryPoolFreed *freed_items = pool->freed_items; 99 | pool->freed_items = item; 100 | pool->freed_items->next = freed_items; 101 | } 102 | 103 | void * 104 | fsearch_memory_pool_malloc(FsearchMemoryPool *pool) { 105 | if (!pool) { 106 | return NULL; 107 | } 108 | if (pool->freed_items) { 109 | void *freed_head = pool->freed_items; 110 | pool->freed_items = pool->freed_items->next; 111 | return freed_head; 112 | } 113 | 114 | if (!pool->blocks || fsearch_memory_pool_is_block_full(pool)) { 115 | fsearch_memory_pool_new_block(pool); 116 | } 117 | FsearchMemoryPoolBlock *block = pool->blocks->data; 118 | g_assert(block); 119 | 120 | return block->items + block->num_used++ * pool->item_size; 121 | } 122 | -------------------------------------------------------------------------------- /src/fsearch_memory_pool.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | typedef struct FsearchMemoryPool FsearchMemoryPool; 9 | 10 | FsearchMemoryPool * 11 | fsearch_memory_pool_new(uint32_t block_size, size_t item_size, GDestroyNotify item_free_func); 12 | 13 | void 14 | fsearch_memory_pool_free(FsearchMemoryPool *pool, void *item, bool item_clear); 15 | 16 | void 17 | fsearch_memory_pool_free_pool(FsearchMemoryPool *pool); 18 | 19 | void * 20 | fsearch_memory_pool_malloc(FsearchMemoryPool *pool); 21 | -------------------------------------------------------------------------------- /src/fsearch_preferences_ui.h: -------------------------------------------------------------------------------- 1 | /* 2 | FSearch - A fast file search utility 3 | Copyright © 2020 Christian Boxdörfer 4 | 5 | This program is free software; you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation; either version 2 of the License, or 8 | (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program; if not, see . 17 | */ 18 | 19 | #pragma once 20 | 21 | #include "fsearch_config.h" 22 | #include 23 | 24 | typedef enum FsearchPreferencesPage { 25 | PREF_PAGE_GENERAL = 0, 26 | PREF_PAGE_SEARCH, 27 | PREF_PAGE_DATABASE, 28 | N_PREF_PAGES, 29 | 30 | } FsearchPreferencesPage; 31 | 32 | void 33 | preferences_ui_launch(FsearchConfig *config, 34 | GtkWindow *window, 35 | FsearchPreferencesPage page, 36 | void (*finsihed_cb)(FsearchConfig *)); 37 | -------------------------------------------------------------------------------- /src/fsearch_preferences_widgets.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "fsearch_filter.h" 6 | #include "fsearch_preferences_ui.h" 7 | 8 | GList * 9 | pref_index_treeview_data_get(GtkTreeView *view); 10 | 11 | GList * 12 | pref_exclude_treeview_data_get(GtkTreeView *view); 13 | 14 | void 15 | pref_treeview_row_remove(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer userdata); 16 | 17 | void 18 | pref_filter_treeview_update(GtkTreeModel *filter_model, FsearchFilterManager *filters); 19 | 20 | void 21 | pref_filter_treeview_row_add(GtkTreeModel *index_model, FsearchFilter *filter); 22 | 23 | void 24 | pref_index_treeview_row_add(GtkTreeModel *index_model, const char *path); 25 | 26 | void 27 | pref_exclude_treeview_row_add(GtkTreeModel *model, const char *path); 28 | 29 | GtkTreeModel * 30 | pref_index_treeview_init(GtkTreeView *view, GList *locations); 31 | 32 | GtkTreeModel * 33 | pref_exclude_treeview_init(GtkTreeView *view, GList *locations); 34 | 35 | GtkTreeModel * 36 | pref_filter_treeview_init(GtkTreeView *view, FsearchFilterManager *filters); 37 | -------------------------------------------------------------------------------- /src/fsearch_preview.c: -------------------------------------------------------------------------------- 1 | /* 2 | FSearch - A fast file search utility 3 | Copyright © 2020 Christian Boxdörfer 4 | 5 | This program is free software; you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation; either version 2 of the License, or 8 | (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program; if not, see . 17 | */ 18 | 19 | #include "fsearch_preview.h" 20 | #include "fsearch.h" 21 | 22 | #include 23 | 24 | #define PREVIEWER_DBUS_NAME "org.gnome.NautilusPreviewer" 25 | #define PREVIEWER_DBUS_IFACE "org.gnome.NautilusPreviewer" 26 | #define PREVIEWER_DBUS_PATH "/org/gnome/NautilusPreviewer" 27 | 28 | static void 29 | preview_show_file_ready_cb(GObject *source, GAsyncResult *res, gpointer user_data) { 30 | g_autoptr(GError) error = NULL; 31 | g_dbus_connection_call_finish(G_DBUS_CONNECTION(source), res, &error); 32 | 33 | if (error != NULL) { 34 | g_debug("Unable to call ShowFile on NautilusPreviewer: %s", error->message); 35 | } 36 | } 37 | 38 | static void 39 | preview_close_ready_cb(GObject *source, GAsyncResult *res, gpointer user_data) { 40 | g_autoptr(GError) error = NULL; 41 | g_dbus_connection_call_finish(G_DBUS_CONNECTION(source), res, &error); 42 | 43 | if (error != NULL) { 44 | g_debug("Unable to call Close on NautilusPreviewer: %s", error->message); 45 | } 46 | } 47 | 48 | void 49 | fsearch_preview_call_show_file(const gchar *uri, guint xid, gboolean close_if_already_visible) { 50 | GDBusConnection *connection = g_application_get_dbus_connection(G_APPLICATION(FSEARCH_APPLICATION_DEFAULT)); 51 | GVariant *variant = g_variant_new("(sib)", uri, xid, close_if_already_visible); 52 | 53 | g_dbus_connection_call(connection, 54 | PREVIEWER_DBUS_NAME, 55 | PREVIEWER_DBUS_PATH, 56 | PREVIEWER_DBUS_IFACE, 57 | "ShowFile", 58 | variant, 59 | NULL, 60 | G_DBUS_CALL_FLAGS_NONE, 61 | -1, 62 | NULL, 63 | preview_show_file_ready_cb, 64 | NULL); 65 | } 66 | 67 | void 68 | fsearch_preview_call_close(void) { 69 | GDBusConnection *connection = g_application_get_dbus_connection(G_APPLICATION(FSEARCH_APPLICATION_DEFAULT)); 70 | 71 | g_dbus_connection_call(connection, 72 | PREVIEWER_DBUS_NAME, 73 | PREVIEWER_DBUS_PATH, 74 | PREVIEWER_DBUS_IFACE, 75 | "Close", 76 | NULL, 77 | NULL, 78 | G_DBUS_CALL_FLAGS_NO_AUTO_START, 79 | -1, 80 | NULL, 81 | preview_close_ready_cb, 82 | NULL); 83 | } 84 | -------------------------------------------------------------------------------- /src/fsearch_preview.h: -------------------------------------------------------------------------------- 1 | /* 2 | FSearch - A fast file search utility 3 | Copyright © 2020 Christian Boxdörfer 4 | 5 | This program is free software; you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation; either version 2 of the License, or 8 | (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program; if not, see . 17 | */ 18 | 19 | #pragma once 20 | 21 | #include 22 | 23 | void 24 | fsearch_preview_call_show_file(const gchar *uri, guint xid, gboolean close_if_already_visible); 25 | 26 | void 27 | fsearch_preview_call_close(void); -------------------------------------------------------------------------------- /src/fsearch_query.h: -------------------------------------------------------------------------------- 1 | /* 2 | FSearch - A fast file search utility 3 | Copyright © 2020 Christian Boxdörfer 4 | 5 | This program is free software; you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation; either version 2 of the License, or 8 | (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program; if not, see . 17 | */ 18 | 19 | #pragma once 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | #include "fsearch_array.h" 27 | #include "fsearch_filter_manager.h" 28 | #include "fsearch_list_view.h" 29 | #include "fsearch_query_flags.h" 30 | #include "fsearch_query_match_data.h" 31 | #include "fsearch_query_node.h" 32 | #include "fsearch_query_tree.h" 33 | #include "fsearch_thread_pool.h" 34 | 35 | typedef struct FsearchQuery { 36 | char *search_term; 37 | 38 | FsearchFilter *filter; 39 | FsearchFilterManager *filters; 40 | 41 | GNode *query_tree; 42 | GNode *filter_tree; 43 | 44 | char *query_id; 45 | 46 | FsearchQueryFlags flags; 47 | 48 | bool triggers_auto_match_case; 49 | bool triggers_auto_match_path; 50 | bool wants_single_threaded_search; 51 | 52 | volatile int ref_count; 53 | } FsearchQuery; 54 | 55 | FsearchQuery * 56 | fsearch_query_new(const char *search_term, 57 | FsearchFilter *filter, 58 | FsearchFilterManager *filters, 59 | FsearchQueryFlags flags, 60 | const char *query_id); 61 | 62 | FsearchQuery * 63 | fsearch_query_ref(FsearchQuery *query); 64 | 65 | void 66 | fsearch_query_unref(FsearchQuery *query); 67 | 68 | bool 69 | fsearch_query_matches_everything(FsearchQuery *query); 70 | 71 | bool 72 | fsearch_query_match(FsearchQuery *queyr, FsearchQueryMatchData *match_data); 73 | 74 | bool 75 | fsearch_query_highlight(FsearchQuery *query, FsearchQueryMatchData *match_data); 76 | -------------------------------------------------------------------------------- /src/fsearch_query_flags.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | typedef enum FsearchQueryFlags { 6 | QUERY_FLAG_MATCH_CASE = 1 << 0, 7 | QUERY_FLAG_AUTO_MATCH_CASE = 1 << 1, 8 | QUERY_FLAG_REGEX = 1 << 2, 9 | QUERY_FLAG_SEARCH_IN_PATH = 1 << 3, 10 | QUERY_FLAG_AUTO_SEARCH_IN_PATH = 1 << 4, 11 | QUERY_FLAG_FILES_ONLY = 1 << 5, 12 | QUERY_FLAG_FOLDERS_ONLY = 1 << 6, 13 | QUERY_FLAG_EXACT_MATCH = 1 << 7, 14 | } FsearchQueryFlags; 15 | -------------------------------------------------------------------------------- /src/fsearch_query_lexer.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "fsearch_query_lexer.h" 6 | 7 | struct FsearchQueryLexer { 8 | GString *input; 9 | GQueue *char_stack; 10 | 11 | uint32_t input_pos; 12 | }; 13 | 14 | static const char *reserved_chars = ":=<>()"; 15 | 16 | static char 17 | get_next_input_char(FsearchQueryLexer *lexer) { 18 | if (lexer->input && lexer->input_pos < lexer->input->len) { 19 | return lexer->input->str[lexer->input_pos++]; 20 | } 21 | return '\0'; 22 | } 23 | 24 | static char 25 | top_char(GQueue *stack) { 26 | return (char)GPOINTER_TO_UINT(g_queue_peek_tail(stack)); 27 | } 28 | 29 | static char 30 | pop_char(GQueue *stack) { 31 | return (char)(GPOINTER_TO_UINT(g_queue_pop_tail(stack))); 32 | } 33 | 34 | static void 35 | push_char(GQueue *stack, char c) { 36 | g_queue_push_tail(stack, GUINT_TO_POINTER((guint)c)); 37 | } 38 | 39 | static char 40 | get_next_char(FsearchQueryLexer *lexer) { 41 | GQueue *stack = lexer->char_stack; 42 | if (!g_queue_is_empty(stack)) { 43 | return pop_char(stack); 44 | } 45 | return get_next_input_char(lexer); 46 | } 47 | 48 | static void 49 | give_back_char(FsearchQueryLexer *lexer, char c) { 50 | push_char(lexer->char_stack, c); 51 | } 52 | 53 | // parse_string() assumes that the first double quote was already read 54 | static void 55 | parse_quoted_string(FsearchQueryLexer *lexer, GString *string) { 56 | char c = '\0'; 57 | while ((c = get_next_char(lexer))) { 58 | if (c == '"') { 59 | return; 60 | } 61 | else { 62 | g_string_append_c(string, c); 63 | } 64 | } 65 | } 66 | 67 | FsearchQueryToken 68 | fsearch_query_lexer_get_next_token(FsearchQueryLexer *lexer, GString **result) { 69 | FsearchQueryToken token = FSEARCH_QUERY_TOKEN_NONE; 70 | 71 | char c = '\0'; 72 | 73 | /* Skip white space. */ 74 | while ((c = get_next_char(lexer)) && g_ascii_isspace(c)) { 75 | continue; 76 | } 77 | 78 | // field-term relations, and ranges 79 | switch (c) { 80 | case '\0': 81 | return FSEARCH_QUERY_TOKEN_EOS; 82 | case '=': 83 | return FSEARCH_QUERY_TOKEN_EQUAL; 84 | case ':': 85 | return FSEARCH_QUERY_TOKEN_CONTAINS; 86 | case '<': { 87 | char c1 = get_next_char(lexer); 88 | if (c1 == '=') { 89 | return FSEARCH_QUERY_TOKEN_SMALLER_EQ; 90 | } 91 | else { 92 | give_back_char(lexer, c1); 93 | return FSEARCH_QUERY_TOKEN_SMALLER; 94 | } 95 | } 96 | case '>': { 97 | char c1 = get_next_char(lexer); 98 | if (c1 == '=') { 99 | return FSEARCH_QUERY_TOKEN_GREATER_EQ; 100 | } 101 | else { 102 | give_back_char(lexer, c1); 103 | return FSEARCH_QUERY_TOKEN_GREATER; 104 | } 105 | } 106 | case '!': 107 | return FSEARCH_QUERY_TOKEN_NOT; 108 | case '(': 109 | return FSEARCH_QUERY_TOKEN_BRACKET_OPEN; 110 | case ')': 111 | return FSEARCH_QUERY_TOKEN_BRACKET_CLOSE; 112 | } 113 | 114 | give_back_char(lexer, c); 115 | 116 | // Other chars start a term or field name or reserved word 117 | g_autoptr(GString) token_value = g_string_sized_new(1024); 118 | 119 | while ((c = get_next_char(lexer))) { 120 | if (g_ascii_isspace(c)) { 121 | // word broken by whitespace 122 | break; 123 | } 124 | else if (c == '"') { 125 | parse_quoted_string(lexer, token_value); 126 | } 127 | else if (c == '\\') { 128 | // escape: get next char 129 | c = get_next_char(lexer); 130 | g_string_append_c(token_value, c); 131 | } 132 | else if (strchr(reserved_chars, c)) { 133 | if (c == ':') { 134 | // field: detected 135 | c = get_next_char(lexer); 136 | if (g_ascii_isspace(c) || c == '\0') { 137 | token = FSEARCH_QUERY_TOKEN_FIELD_EMPTY; 138 | } 139 | else { 140 | give_back_char(lexer, c); 141 | token = FSEARCH_QUERY_TOKEN_FIELD; 142 | } 143 | goto out; 144 | } 145 | // word broken by reserved character 146 | give_back_char(lexer, c); 147 | break; 148 | } 149 | else { 150 | g_string_append_c(token_value, c); 151 | } 152 | } 153 | 154 | if (!strcmp(token_value->str, "NOT")) { 155 | return FSEARCH_QUERY_TOKEN_NOT; 156 | } 157 | if (!strcmp(token_value->str, "AND") || !strcmp(token_value->str, "&&")) { 158 | return FSEARCH_QUERY_TOKEN_AND; 159 | } 160 | else if (!strcmp(token_value->str, "OR") || !strcmp(token_value->str, "||")) { 161 | return FSEARCH_QUERY_TOKEN_OR; 162 | } 163 | 164 | token = FSEARCH_QUERY_TOKEN_WORD; 165 | 166 | out: 167 | if (result) { 168 | *result = g_steal_pointer(&token_value); 169 | } 170 | return token; 171 | } 172 | 173 | FsearchQueryToken 174 | fsearch_query_lexer_peek_next_token(FsearchQueryLexer *lexer, GString **result) { 175 | // remember old lexing state 176 | size_t old_input_pos = lexer->input_pos; 177 | GQueue *old_char_stack = g_queue_copy(lexer->char_stack); 178 | 179 | FsearchQueryToken res = fsearch_query_lexer_get_next_token(lexer, result); 180 | 181 | // restore old lexing state 182 | lexer->input_pos = old_input_pos; 183 | g_clear_pointer(&lexer->char_stack, g_queue_free); 184 | lexer->char_stack = old_char_stack; 185 | return res; 186 | } 187 | 188 | FsearchQueryLexer * 189 | fsearch_query_lexer_new(const char *input) { 190 | g_assert(input); 191 | 192 | FsearchQueryLexer *lexer = calloc(1, sizeof(FsearchQueryLexer)); 193 | g_assert(lexer); 194 | 195 | lexer->input = g_string_new(input); 196 | lexer->input_pos = 0; 197 | 198 | lexer->char_stack = g_queue_new(); 199 | return lexer; 200 | } 201 | 202 | void 203 | fsearch_query_lexer_free(FsearchQueryLexer *lexer) { 204 | if (lexer == NULL) { 205 | return; 206 | } 207 | g_string_free(g_steal_pointer(&lexer->input), TRUE); 208 | g_clear_pointer(&lexer->char_stack, g_queue_free); 209 | g_clear_pointer(&lexer, free); 210 | } 211 | -------------------------------------------------------------------------------- /src/fsearch_query_lexer.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | typedef enum FsearchQueryToken { 6 | FSEARCH_QUERY_TOKEN_NONE, 7 | FSEARCH_QUERY_TOKEN_EOS, 8 | FSEARCH_QUERY_TOKEN_WORD, 9 | FSEARCH_QUERY_TOKEN_FIELD, 10 | FSEARCH_QUERY_TOKEN_FIELD_EMPTY, 11 | FSEARCH_QUERY_TOKEN_AND, 12 | FSEARCH_QUERY_TOKEN_OR, 13 | FSEARCH_QUERY_TOKEN_NOT, 14 | FSEARCH_QUERY_TOKEN_CONTAINS, 15 | FSEARCH_QUERY_TOKEN_GREATER_EQ, 16 | FSEARCH_QUERY_TOKEN_GREATER, 17 | FSEARCH_QUERY_TOKEN_SMALLER_EQ, 18 | FSEARCH_QUERY_TOKEN_SMALLER, 19 | FSEARCH_QUERY_TOKEN_EQUAL, 20 | FSEARCH_QUERY_TOKEN_BRACKET_OPEN, 21 | FSEARCH_QUERY_TOKEN_BRACKET_CLOSE, 22 | NUM_FSEARCH_QUERY_TOKENS, 23 | } FsearchQueryToken; 24 | 25 | typedef struct FsearchQueryLexer FsearchQueryLexer; 26 | 27 | FsearchQueryLexer * 28 | fsearch_query_lexer_new(const char *input); 29 | 30 | void 31 | fsearch_query_lexer_free(FsearchQueryLexer *lexer); 32 | 33 | FsearchQueryToken 34 | fsearch_query_lexer_peek_next_token(FsearchQueryLexer *lexer, GString **result); 35 | 36 | FsearchQueryToken 37 | fsearch_query_lexer_get_next_token(FsearchQueryLexer *lexer, GString **result); 38 | -------------------------------------------------------------------------------- /src/fsearch_query_match_data.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "fsearch_database_entry.h" 4 | #include "fsearch_database_index.h" 5 | #include "fsearch_utf.h" 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | typedef struct FsearchQueryMatchData FsearchQueryMatchData; 12 | 13 | FsearchQueryMatchData * 14 | fsearch_query_match_data_new(void); 15 | 16 | void 17 | fsearch_query_match_data_free(FsearchQueryMatchData *match_data); 18 | 19 | void 20 | fsearch_query_match_data_set_entry(FsearchQueryMatchData *match_data, FsearchDatabaseEntry *entry); 21 | 22 | void 23 | fsearch_query_match_data_add_highlight(FsearchQueryMatchData *match_data, 24 | PangoAttribute *attribute, 25 | FsearchDatabaseIndexType idx); 26 | 27 | PangoAttrList * 28 | fsearch_query_match_get_highlight(FsearchQueryMatchData *match_data, FsearchDatabaseIndexType idx); 29 | 30 | void 31 | fsearch_query_match_data_set_thread_id(FsearchQueryMatchData *match_data, int32_t thread_id); 32 | 33 | int32_t 34 | fsearch_query_match_data_get_thread_id(FsearchQueryMatchData *match_data); 35 | 36 | void 37 | fsearch_query_match_data_set_result(FsearchQueryMatchData *match_data, bool result); 38 | 39 | bool 40 | fsearch_query_match_data_get_result(FsearchQueryMatchData *match_data); 41 | 42 | const char * 43 | fsearch_query_match_data_get_name_str(FsearchQueryMatchData *match_data); 44 | 45 | const char * 46 | fsearch_query_match_data_get_parent_path_str(FsearchQueryMatchData *match_data); 47 | 48 | const char * 49 | fsearch_query_match_data_get_path_str(FsearchQueryMatchData *match_data); 50 | 51 | const char * 52 | fsearch_query_match_data_get_content_type_str(FsearchQueryMatchData *match_data); 53 | 54 | FsearchUtfBuilder * 55 | fsearch_query_match_data_get_utf_parent_path_builder(FsearchQueryMatchData *match_data); 56 | 57 | FsearchUtfBuilder * 58 | fsearch_query_match_data_get_utf_path_builder(FsearchQueryMatchData *match_data); 59 | 60 | FsearchUtfBuilder * 61 | fsearch_query_match_data_get_utf_name_builder(FsearchQueryMatchData *match_data); 62 | 63 | FsearchDatabaseEntry * 64 | fsearch_query_match_data_get_entry(FsearchQueryMatchData *match_data); 65 | -------------------------------------------------------------------------------- /src/fsearch_query_matchers.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "fsearch_query_match_data.h" 4 | #include "fsearch_query_node.h" 5 | 6 | #include 7 | #include 8 | 9 | uint32_t 10 | fsearch_query_matcher_false(FsearchQueryNode *node, FsearchQueryMatchData *match_data); 11 | 12 | uint32_t 13 | fsearch_query_matcher_true(FsearchQueryNode *node, FsearchQueryMatchData *match_data); 14 | 15 | uint32_t 16 | fsearch_query_matcher_extension(FsearchQueryNode *node, FsearchQueryMatchData *match_data); 17 | 18 | uint32_t 19 | fsearch_query_matcher_date_modified(FsearchQueryNode *node, FsearchQueryMatchData *match_data); 20 | 21 | uint32_t 22 | fsearch_query_matcher_depth(FsearchQueryNode *node, FsearchQueryMatchData *match_data); 23 | 24 | uint32_t 25 | fsearch_query_matcher_childcount(FsearchQueryNode *node, FsearchQueryMatchData *match_data); 26 | 27 | uint32_t 28 | fsearch_query_matcher_childfilecount(FsearchQueryNode *node, FsearchQueryMatchData *match_data); 29 | 30 | uint32_t 31 | fsearch_query_matcher_childfoldercount(FsearchQueryNode *node, FsearchQueryMatchData *match_data); 32 | 33 | uint32_t 34 | fsearch_query_matcher_size(FsearchQueryNode *node, FsearchQueryMatchData *match_data); 35 | 36 | uint32_t 37 | fsearch_query_matcher_regex(FsearchQueryNode *node, FsearchQueryMatchData *match_data); 38 | 39 | uint32_t 40 | fsearch_query_matcher_utf_strcasecmp(FsearchQueryNode *node, FsearchQueryMatchData *match_data); 41 | 42 | uint32_t 43 | fsearch_query_matcher_utf_strcasestr(FsearchQueryNode *node, FsearchQueryMatchData *match_data); 44 | 45 | uint32_t 46 | fsearch_query_matcher_strstr(FsearchQueryNode *node, FsearchQueryMatchData *match_data); 47 | 48 | uint32_t 49 | fsearch_query_matcher_strcasestr(FsearchQueryNode *node, FsearchQueryMatchData *match_data); 50 | 51 | uint32_t 52 | fsearch_query_matcher_strcmp(FsearchQueryNode *node, FsearchQueryMatchData *match_data); 53 | 54 | uint32_t 55 | fsearch_query_matcher_strcasecmp(FsearchQueryNode *node, FsearchQueryMatchData *match_data); 56 | 57 | uint32_t 58 | fsearch_query_matcher_highlight_none(FsearchQueryNode *node, FsearchQueryMatchData *match_data); 59 | 60 | uint32_t 61 | fsearch_query_matcher_highlight_extension(FsearchQueryNode *node, FsearchQueryMatchData *match_data); 62 | 63 | uint32_t 64 | fsearch_query_matcher_highlight_size(FsearchQueryNode *node, FsearchQueryMatchData *match_data); 65 | 66 | uint32_t 67 | fsearch_query_matcher_highlight_regex(FsearchQueryNode *node, FsearchQueryMatchData *match_data); 68 | 69 | uint32_t 70 | fsearch_query_matcher_highlight_ascii(FsearchQueryNode *node, FsearchQueryMatchData *match_data); 71 | -------------------------------------------------------------------------------- /src/fsearch_query_node.h: -------------------------------------------------------------------------------- 1 | 2 | #pragma once 3 | 4 | #define PCRE2_CODE_UNIT_WIDTH 8 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include "fsearch_database_index.h" 14 | #include "fsearch_filter_manager.h" 15 | #include "fsearch_query_flags.h" 16 | #include "fsearch_query_match_data.h" 17 | #include "fsearch_utf.h" 18 | 19 | typedef struct FsearchQueryNode FsearchQueryNode; 20 | typedef uint32_t(FsearchQueryNodeMatchFunc)(FsearchQueryNode *, FsearchQueryMatchData *); 21 | typedef void *(FsearchQueryNodeHaystackFunc)(FsearchQueryMatchData *); 22 | 23 | typedef enum FsearchQueryNodeType { 24 | FSEARCH_QUERY_NODE_TYPE_OPERATOR, 25 | FSEARCH_QUERY_NODE_TYPE_QUERY, 26 | NUM_FSEARCH_QUERY_NODE_TYPES, 27 | } FsearchQueryNodeType; 28 | 29 | typedef enum FsearchQueryNodeComparison { 30 | FSEARCH_QUERY_NODE_COMPARISON_EQUAL, 31 | FSEARCH_QUERY_NODE_COMPARISON_GREATER, 32 | FSEARCH_QUERY_NODE_COMPARISON_GREATER_EQ, 33 | FSEARCH_QUERY_NODE_COMPARISON_SMALLER, 34 | FSEARCH_QUERY_NODE_COMPARISON_SMALLER_EQ, 35 | FSEARCH_QUERY_NODE_COMPARISON_RANGE, 36 | NUM_FSEARCH_QUERY_NODE_COMPARISONS, 37 | } FsearchQueryNodeComparison; 38 | 39 | typedef enum FsearchQueryNodeOperator { 40 | FSEARCH_QUERY_NODE_OPERATOR_AND, 41 | FSEARCH_QUERY_NODE_OPERATOR_OR, 42 | FSEARCH_QUERY_NODE_OPERATOR_NOT, 43 | NUM_FSEARCH_QUERY_NODE_OPERATORS, 44 | } FsearchQueryNodeOperator; 45 | 46 | struct FsearchQueryNode { 47 | FsearchQueryNodeType type; 48 | GString *description; 49 | 50 | FsearchQueryNodeOperator operator; 51 | 52 | char *needle; 53 | size_t needle_len; 54 | 55 | GPtrArray *search_term_list; 56 | 57 | int64_t num_start; 58 | int64_t num_end; 59 | FsearchQueryNodeComparison comparison_type; 60 | 61 | FsearchQueryNodeMatchFunc *search_func; 62 | FsearchQueryNodeMatchFunc *highlight_func; 63 | FsearchQueryNodeHaystackFunc *haystack_func; 64 | 65 | FsearchUtfBuilder *needle_builder; 66 | 67 | // Using the pcre2_code with multiple threads is safe. 68 | // However, pcre2_match_data can't be shared across threads. 69 | // So to avoid frequent calls to pcre2_match_data_create_from_pattern during the matching process, 70 | // we simply generate an array which holds a unique instance for each thread per regex node. 71 | pcre2_code *regex; 72 | GPtrArray *regex_match_data_for_threads; 73 | bool regex_jit_available; 74 | 75 | FsearchQueryFlags flags; 76 | 77 | bool triggers_auto_match_case; 78 | bool triggers_auto_match_path; 79 | bool wants_single_threaded_search; 80 | }; 81 | 82 | void 83 | fsearch_query_node_free(FsearchQueryNode *node); 84 | 85 | FsearchQueryNode * 86 | fsearch_query_node_new_date_modified(FsearchQueryFlags flags, 87 | int64_t dm_start, 88 | int64_t dm_end, 89 | FsearchQueryNodeComparison comp_type); 90 | 91 | FsearchQueryNode * 92 | fsearch_query_node_new_size(FsearchQueryFlags flags, 93 | int64_t size_start, 94 | int64_t size_end, 95 | FsearchQueryNodeComparison comp_type); 96 | 97 | FsearchQueryNode * 98 | fsearch_query_node_new_depth(FsearchQueryFlags flags, 99 | int64_t child_folder_count_start, 100 | int64_t child_folder_count_end, 101 | FsearchQueryNodeComparison comp_type); 102 | 103 | FsearchQueryNode * 104 | fsearch_query_node_new_childcount(FsearchQueryFlags flags, 105 | int64_t child_folder_count_start, 106 | int64_t child_folder_count_end, 107 | FsearchQueryNodeComparison comp_type); 108 | 109 | FsearchQueryNode * 110 | fsearch_query_node_new_childfoldercount(FsearchQueryFlags flags, 111 | int64_t child_folder_count_start, 112 | int64_t child_folder_count_end, 113 | FsearchQueryNodeComparison comp_type); 114 | 115 | FsearchQueryNode * 116 | fsearch_query_node_new_childfilecount(FsearchQueryFlags flags, 117 | int64_t child_file_count_start, 118 | int64_t child_file_count_end, 119 | FsearchQueryNodeComparison comp_type); 120 | 121 | FsearchQueryNode *fsearch_query_node_new_operator(FsearchQueryNodeOperator operator); 122 | 123 | FsearchQueryNode * 124 | fsearch_query_node_new_match_nothing(void); 125 | 126 | FsearchQueryNode * 127 | fsearch_query_node_new_match_everything(FsearchQueryFlags flags); 128 | 129 | FsearchQueryNode * 130 | fsearch_query_node_new_regex(const char *search_term, FsearchQueryFlags flags); 131 | 132 | FsearchQueryNode * 133 | fsearch_query_node_new_wildcard(const char *search_term, FsearchQueryFlags flags); 134 | 135 | FsearchQueryNode * 136 | fsearch_query_node_new_parent(const char *search_term, FsearchQueryFlags flags); 137 | 138 | FsearchQueryNode * 139 | fsearch_query_node_new_extension(const char *search_term, FsearchQueryFlags flags); 140 | 141 | FsearchQueryNode * 142 | fsearch_query_node_new_contenttype(const char *search_term, FsearchQueryFlags flags); 143 | 144 | FsearchQueryNode * 145 | fsearch_query_node_new(const char *search_term, FsearchQueryFlags flags); 146 | -------------------------------------------------------------------------------- /src/fsearch_query_parser.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "fsearch_query_flags.h" 4 | #include "fsearch_query_lexer.h" 5 | 6 | #include 7 | #include 8 | 9 | typedef struct FsearchQueryParseContext { 10 | FsearchQueryLexer *lexer; 11 | GPtrArray *macro_filters; 12 | GQueue *operator_stack; 13 | GQueue *macro_stack; 14 | FsearchQueryToken last_token; 15 | } FsearchQueryParseContext; 16 | 17 | GList * 18 | fsearch_query_parser_parse_expression(FsearchQueryParseContext *parse_ctx, bool in_open_bracket, FsearchQueryFlags flags); 19 | -------------------------------------------------------------------------------- /src/fsearch_query_tree.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "fsearch_filter_manager.h" 4 | 5 | #include 6 | 7 | bool 8 | fsearch_query_node_tree_triggers_auto_match_path(GNode *tree); 9 | 10 | bool 11 | fsearch_query_node_tree_triggers_auto_match_case(GNode *tree); 12 | 13 | bool 14 | fsearch_query_node_tree_wants_single_threaded_search(GNode *tree); 15 | 16 | GNode * 17 | fsearch_query_node_tree_new(const char *search_term, FsearchFilterManager *filters, FsearchQueryFlags flags); 18 | 19 | void 20 | fsearch_query_node_tree_free(GNode *node); 21 | -------------------------------------------------------------------------------- /src/fsearch_result_view.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "fsearch_database_view.h" 4 | #include "fsearch_list_view.h" 5 | 6 | typedef struct { 7 | FsearchDatabaseView *database_view; 8 | FsearchListView *list_view; 9 | 10 | GHashTable *row_cache; 11 | GHashTable *pixbuf_cache; 12 | GHashTable *app_gicon_cache; 13 | 14 | // remember the row height from the last draw call 15 | // when it changes we need to reset the icon cache 16 | int32_t row_height; 17 | 18 | FsearchDatabaseIndexType sort_order; 19 | GtkSortType sort_type; 20 | } FsearchResultView; 21 | 22 | FsearchResultView * 23 | fsearch_result_view_new(void); 24 | 25 | void 26 | fsearch_result_view_free(FsearchResultView *result_view); 27 | 28 | void 29 | fsearch_result_view_row_cache_reset(FsearchResultView *result_view); 30 | 31 | char * 32 | fsearch_result_view_query_tooltip(FsearchDatabaseView *view, 33 | uint32_t row, 34 | FsearchListViewColumn *col, 35 | PangoLayout *layout, 36 | uint32_t row_height); 37 | 38 | void 39 | fsearch_result_view_draw_row(FsearchResultView *view, 40 | cairo_t *cr, 41 | GdkWindow *bin_window, 42 | PangoLayout *layout, 43 | GtkStyleContext *context, 44 | GList *columns, 45 | cairo_rectangle_int_t *rect, 46 | uint32_t row, 47 | gboolean row_selected, 48 | gboolean row_focused, 49 | gboolean row_hovered, 50 | gboolean right_to_left_text); 51 | -------------------------------------------------------------------------------- /src/fsearch_selection.c: -------------------------------------------------------------------------------- 1 | #define G_LOG_DOMAIN "fsearch-selection" 2 | 3 | #include "fsearch_selection.h" 4 | 5 | void 6 | fsearch_selection_free(GHashTable *selection) { 7 | g_assert(selection); 8 | g_clear_pointer(&selection, g_hash_table_destroy); 9 | } 10 | 11 | GHashTable * 12 | fsearch_selection_new(void) { 13 | return g_hash_table_new(g_direct_hash, g_direct_equal); 14 | } 15 | 16 | void 17 | fsearch_selection_select_toggle(GHashTable *selection, gpointer item) { 18 | g_assert(selection); 19 | g_assert(item); 20 | 21 | if (g_hash_table_steal(selection, item)) { 22 | return; 23 | } 24 | g_hash_table_add(selection, item); 25 | } 26 | 27 | void 28 | fsearch_selection_select(GHashTable *selection, gpointer item) { 29 | g_assert(selection); 30 | g_assert(item); 31 | 32 | g_hash_table_add(selection, item); 33 | } 34 | 35 | bool 36 | fsearch_selection_is_selected(GHashTable *selection, gpointer item) { 37 | g_assert(selection); 38 | g_assert(item); 39 | 40 | return g_hash_table_contains(selection, item); 41 | } 42 | 43 | void 44 | fsearch_selection_select_all(GHashTable *selection, DynamicArray *items) { 45 | g_assert(selection); 46 | g_assert(items); 47 | 48 | const uint32_t num_items = darray_get_num_items(items); 49 | 50 | for (uint32_t i = 0; i < num_items; i++) { 51 | void *item = darray_get_item(items, i); 52 | if (!item) { 53 | g_debug("[select_all] item is NULL"); 54 | } 55 | g_hash_table_add(selection, item); 56 | } 57 | } 58 | 59 | void 60 | fsearch_selection_unselect_all(GHashTable *selection) { 61 | g_assert(selection); 62 | g_hash_table_remove_all(selection); 63 | } 64 | 65 | void 66 | fsearch_selection_invert(GHashTable *selection, DynamicArray *items) { 67 | g_assert(selection); 68 | g_assert(items); 69 | 70 | const uint32_t num_items = darray_get_num_items(items); 71 | 72 | for (uint32_t i = 0; i < num_items; i++) { 73 | void *item = darray_get_item(items, i); 74 | if (!item) { 75 | g_debug("[select_all] item is NULL"); 76 | } 77 | if (g_hash_table_steal(selection, item)) { 78 | continue; 79 | } 80 | g_hash_table_add(selection, item); 81 | } 82 | } 83 | 84 | uint32_t 85 | fsearch_selection_get_num_selected(GHashTable *selection) { 86 | g_assert(selection); 87 | return g_hash_table_size(selection); 88 | } 89 | void 90 | fsearch_selection_for_each(GHashTable *selection, GHFunc func, gpointer user_data) { 91 | g_assert(selection); 92 | g_hash_table_foreach(selection, func, user_data); 93 | } 94 | -------------------------------------------------------------------------------- /src/fsearch_selection.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "fsearch_array.h" 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | void 10 | fsearch_selection_free(GHashTable *selection); 11 | 12 | GHashTable * 13 | fsearch_selection_new(void); 14 | 15 | void 16 | fsearch_selection_select_toggle(GHashTable *selection, gpointer item); 17 | 18 | void 19 | fsearch_selection_select(GHashTable *selection, gpointer item); 20 | 21 | bool 22 | fsearch_selection_is_selected(GHashTable *selection, gpointer item); 23 | 24 | void 25 | fsearch_selection_select_all(GHashTable *selection, DynamicArray *items); 26 | 27 | void 28 | fsearch_selection_unselect_all(GHashTable *selection); 29 | 30 | void 31 | fsearch_selection_invert(GHashTable *selection, DynamicArray *items); 32 | 33 | uint32_t 34 | fsearch_selection_get_num_selected(GHashTable *selection); 35 | 36 | void 37 | fsearch_selection_for_each(GHashTable *selection, GHFunc func, gpointer user_data); 38 | -------------------------------------------------------------------------------- /src/fsearch_size_utils.c: -------------------------------------------------------------------------------- 1 | #include "fsearch_size_utils.h" 2 | 3 | #include 4 | #include 5 | 6 | bool 7 | fsearch_size_parse(const char *str, int64_t *size_out, int64_t *size_end_out) { 8 | g_assert(str); 9 | 10 | char *size_suffix = NULL; 11 | int64_t size = strtoll(str, &size_suffix, 10); 12 | if (size_suffix == str) { 13 | return false; 14 | } 15 | 16 | int64_t plus = 0; 17 | if (size_suffix && *size_suffix != '\0') { 18 | switch (*size_suffix) { 19 | case 'k': 20 | case 'K': 21 | size *= 1000; 22 | plus = 1000 - 50 - 1; 23 | break; 24 | case 'm': 25 | case 'M': 26 | size *= 1000 * 1000; 27 | plus = 1000 * (1000 - 50) - 1; 28 | break; 29 | case 'g': 30 | case 'G': 31 | size *= 1000 * 1000 * 1000; 32 | plus = 1000 * 1000 * (1000 - 50) - 1; 33 | break; 34 | case 't': 35 | case 'T': 36 | size *= (int64_t)1000 * 1000 * 1000 * 1000; 37 | plus = (int64_t)1000 * 1000 * 1000 * (1000 - 50) - 1; 38 | break; 39 | default: 40 | goto out; 41 | } 42 | size_suffix++; 43 | 44 | switch (*size_suffix) { 45 | case 'b': 46 | case 'B': 47 | size_suffix++; 48 | break; 49 | default: 50 | goto out; 51 | } 52 | } 53 | out: 54 | if (size_suffix && size_suffix[0] != '\0') { 55 | return false; 56 | } 57 | if (size_end_out) { 58 | *size_end_out = size + plus; 59 | } 60 | if (size_out) { 61 | *size_out = size; 62 | } 63 | return true; 64 | } 65 | -------------------------------------------------------------------------------- /src/fsearch_size_utils.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | bool 7 | fsearch_size_parse(const char *str, int64_t *size_out, int64_t *size_end_out); 8 | -------------------------------------------------------------------------------- /src/fsearch_statusbar.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | G_BEGIN_DECLS 8 | 9 | #define FSEARCH_STATUSBAR_TYPE (fsearch_statusbar_get_type()) 10 | 11 | G_DECLARE_FINAL_TYPE(FsearchStatusbar, fsearch_statusbar, FSEARCH, STATUSBAR, GtkRevealer) 12 | 13 | FsearchStatusbar * 14 | fsearch_statusbar_new(void); 15 | 16 | G_END_DECLS 17 | 18 | typedef enum { 19 | FSEARCH_STATUSBAR_REVEALER_MATCH_CASE, 20 | FSEARCH_STATUSBAR_REVEALER_SMART_MATCH_CASE, 21 | FSEARCH_STATUSBAR_REVEALER_SEARCH_IN_PATH, 22 | FSEARCH_STATUSBAR_REVEALER_SMART_SEARCH_IN_PATH, 23 | FSEARCH_STATUSBAR_REVEALER_REGEX, 24 | NUM_FSEARCH_STATUSBAR_REVEALERS, 25 | } FsearchStatusbarRevealer; 26 | 27 | typedef enum { 28 | FSEARCH_STATUSBAR_DATABASE_STATE_IDLE, 29 | FSEARCH_STATUSBAR_DATABASE_STATE_LOADING, 30 | FSEARCH_STATUSBAR_DATABASE_STATE_SCANNING, 31 | NUM_FSEARCH_STATUSBAR_DATABASE_STATES, 32 | } FsearchStatusbarState; 33 | 34 | void 35 | fsearch_statusbar_set_query_text(FsearchStatusbar *sb, const char *text); 36 | 37 | void 38 | fsearch_statusbar_set_num_search_results(FsearchStatusbar *sb, uint32_t num_results); 39 | 40 | void 41 | fsearch_statusbar_set_query_status_delayed(FsearchStatusbar *sb); 42 | 43 | void 44 | fsearch_statusbar_set_sort_status_delayed(FsearchStatusbar *sb); 45 | 46 | void 47 | fsearch_statusbar_set_revealer_visibility(FsearchStatusbar *sb, FsearchStatusbarRevealer revealer, gboolean visible); 48 | 49 | void 50 | fsearch_statusbar_set_filter(FsearchStatusbar *sb, const char *filter_name); 51 | 52 | void 53 | fsearch_statusbar_set_database_state(FsearchStatusbar *sb, 54 | FsearchStatusbarState state, 55 | uint32_t num_files, 56 | uint32_t num_folders); 57 | 58 | void 59 | fsearch_statusbar_set_database_index_text(FsearchStatusbar *sb, const char *text); 60 | 61 | void 62 | fsearch_statusbar_set_selection(FsearchStatusbar *sb, 63 | uint32_t num_files_selected, 64 | uint32_t num_folders_selected, 65 | uint32_t num_files, 66 | uint32_t num_folders); 67 | -------------------------------------------------------------------------------- /src/fsearch_string_utils.c: -------------------------------------------------------------------------------- 1 | /* 2 | FSearch - A fast file search utility 3 | Copyright © 2020 Christian Boxdörfer 4 | 5 | This program is free software; you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation; either version 2 of the License, or 8 | (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program; if not, see . 17 | */ 18 | 19 | #define G_LOG_DOMAIN "fsearch-string-utils" 20 | 21 | #include "fsearch_string_utils.h" 22 | #include 23 | #include 24 | #include 25 | 26 | bool 27 | fsearch_string_is_empty(const char *str) { 28 | // query is considered empty if: 29 | // - fist character is null terminator 30 | // - or it has only space characters 31 | g_assert(str); 32 | while (*str != '\0') { 33 | if (!isspace(*str)) { 34 | return false; 35 | } 36 | str++; 37 | } 38 | return true; 39 | } 40 | 41 | bool 42 | fsearch_string_is_ascii_icase(const char *str) { 43 | g_assert(str); 44 | const gssize str_len = (gssize)strlen(str); 45 | if (str_len == 0) { 46 | return true; 47 | } 48 | g_autofree char *down = g_utf8_strdown(str, str_len); 49 | g_autofree char *up = g_utf8_strup(str, str_len); 50 | 51 | if (g_str_is_ascii(down) && g_str_is_ascii(up)) { 52 | return true; 53 | } 54 | else { 55 | g_debug("[non_ascii_string] \"%s\" (down: \"%s\", up: \"%s\")", str, down, up); 56 | return false; 57 | } 58 | } 59 | 60 | bool 61 | fsearch_string_utf8_has_upper(const char *str) { 62 | g_assert(str); 63 | char *p = (char *)str; 64 | if (!g_utf8_validate(p, -1, NULL)) { 65 | return false; 66 | } 67 | while (p && *p != '\0') { 68 | gunichar c = g_utf8_get_char(p); 69 | if (g_unichar_isupper(c)) { 70 | return true; 71 | } 72 | p = g_utf8_next_char(p); 73 | } 74 | return false; 75 | } 76 | 77 | bool 78 | fsearch_string_has_upper(const char *str) { 79 | g_assert(str); 80 | const char *ptr = str; 81 | while (*ptr != '\0') { 82 | if (isupper(*ptr)) { 83 | return true; 84 | } 85 | ptr++; 86 | } 87 | return false; 88 | } 89 | 90 | const char * 91 | fsearch_string_get_extension(const char *str) { 92 | const char *ext = strrchr(str, '.'); 93 | if (!ext || ext == str || ext[1] == '\0') { 94 | // filename has no dot 95 | // OR filename starts with dot (i.e. hidden file) 96 | // OR filename ends with dot 97 | return ""; 98 | } 99 | return ext + 1; 100 | } 101 | 102 | bool 103 | fsearch_string_has_wildcards(const char *str) { 104 | if (strchr(str, '*') || strchr(str, '?')) { 105 | return true; 106 | } 107 | return false; 108 | } 109 | 110 | char * 111 | fsearch_string_convert_wildcard_to_regex_expression(const char *str) { 112 | g_assert(str); 113 | 114 | GString *regex_epxression = g_string_sized_new(strlen(str)); 115 | g_string_append_c(regex_epxression, '^'); 116 | const char *s = str; 117 | while (*s != '\0') { 118 | switch (*s) { 119 | case '.': 120 | case '^': 121 | case '$': 122 | case '+': 123 | case '(': 124 | case ')': 125 | case '[': 126 | case ']': 127 | case '{': 128 | case '}': 129 | case '\\': 130 | case '|': 131 | g_string_append_c(regex_epxression, '\\'); 132 | g_string_append_c(regex_epxression, *s); 133 | break; 134 | case '*': 135 | g_string_append_c(regex_epxression, '.'); 136 | g_string_append_c(regex_epxression, '*'); 137 | break; 138 | case '?': 139 | g_string_append_c(regex_epxression, '.'); 140 | break; 141 | default: 142 | g_string_append_c(regex_epxression, *s); 143 | break; 144 | } 145 | s++; 146 | } 147 | g_string_append_c(regex_epxression, '$'); 148 | 149 | return g_string_free(regex_epxression, FALSE); 150 | } 151 | 152 | bool 153 | fsearch_string_starts_with_interval(char *str, char **end_ptr) { 154 | g_assert(str); 155 | g_assert(end_ptr); 156 | if (g_str_has_prefix(str, "..")) { 157 | *end_ptr = str + 2; 158 | return true; 159 | } 160 | else if (g_str_has_prefix(str, "-")) { 161 | *end_ptr = str + 1; 162 | return true; 163 | } 164 | *end_ptr = str; 165 | return false; 166 | } 167 | -------------------------------------------------------------------------------- /src/fsearch_string_utils.h: -------------------------------------------------------------------------------- 1 | /* 2 | FSearch - A fast file search utility 3 | Copyright © 2020 Christian Boxdörfer 4 | 5 | This program is free software; you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation; either version 2 of the License, or 8 | (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program; if not, see . 17 | */ 18 | 19 | #pragma once 20 | #include 21 | #include 22 | 23 | bool 24 | fsearch_string_is_empty(const char *str); 25 | 26 | bool 27 | fsearch_string_has_upper(const char *str); 28 | 29 | const char * 30 | fsearch_string_get_extension(const char *str); 31 | 32 | bool 33 | fsearch_string_utf8_has_upper(const char *str); 34 | 35 | // Detect if str is pure ascii characters in both its lower and upper case form. 36 | bool 37 | fsearch_string_is_ascii_icase(const char *str); 38 | 39 | bool 40 | fsearch_string_has_wildcards(const char *str); 41 | 42 | // Converts a wildcard expression to a regular expression, i.e. 43 | // `*` becomes `.*` 44 | // `?` becomes `.` 45 | // and properly escapes other valid regular expression tokens 46 | char * 47 | fsearch_string_convert_wildcard_to_regex_expression(const char *str); 48 | 49 | // Detect if str starts with a interval identifier (i.e. `..` or `-`). 50 | // At success end_ptr will point to the first character after the interval. 51 | // If no interval was detected end_ptr will point to str. 52 | bool 53 | fsearch_string_starts_with_interval(char *str, char **end_ptr); 54 | -------------------------------------------------------------------------------- /src/fsearch_task.c: -------------------------------------------------------------------------------- 1 | #define G_LOG_DOMAIN "fsearch-task" 2 | 3 | #include "fsearch_task.h" 4 | 5 | #include 6 | #include 7 | 8 | typedef struct FsearchTask { 9 | enum { 10 | FSEARCH_TASK_TYPE_QUIT, 11 | FSEARCH_TASK_TYPE_NORMAL, 12 | } type; 13 | int id; 14 | GCancellable *task_cancellable; 15 | FsearchTaskFunc task_func; 16 | FsearchTaskFinishedFunc task_finished_func; 17 | FsearchTaskCancelledFunc task_cancelled_func; 18 | gpointer data; 19 | } FsearchTask; 20 | 21 | struct FsearchTaskQueue { 22 | GAsyncQueue *queue; 23 | GThread *queue_thread; 24 | FsearchTask *current_task; 25 | GMutex current_task_lock; 26 | }; 27 | 28 | static void 29 | fsearch_task_free(FsearchTask *task) { 30 | g_clear_object(&task->task_cancellable); 31 | g_clear_pointer(&task, free); 32 | } 33 | 34 | static FsearchTask * 35 | fsearch_task_new(int id, 36 | FsearchTaskFunc task_func, 37 | FsearchTaskFinishedFunc task_finished_func, 38 | FsearchTaskCancelledFunc task_cancelled_func, 39 | gpointer data) { 40 | FsearchTask *task = calloc(1, sizeof(FsearchTask)); 41 | g_assert(task); 42 | task->type = FSEARCH_TASK_TYPE_NORMAL; 43 | task->task_cancellable = g_cancellable_new(); 44 | task->task_func = task_func; 45 | task->task_finished_func = task_finished_func; 46 | task->task_cancelled_func = task_cancelled_func; 47 | task->data = data; 48 | task->id = id; 49 | 50 | return task; 51 | } 52 | 53 | static gpointer 54 | fsearch_task_queue_thread(FsearchTaskQueue *queue) { 55 | while (true) { 56 | FsearchTask *task = g_async_queue_pop(queue->queue); 57 | if (!task) { 58 | continue; 59 | } 60 | if (task->type == FSEARCH_TASK_TYPE_QUIT) { 61 | // quit task queue thread 62 | g_debug("[queue_thread] quit"); 63 | g_clear_pointer(&task, fsearch_task_free); 64 | break; 65 | } 66 | g_mutex_lock(&queue->current_task_lock); 67 | queue->current_task = task; 68 | g_mutex_unlock(&queue->current_task_lock); 69 | 70 | g_cancellable_reset(task->task_cancellable); 71 | gpointer result = task->task_func(task->data, task->task_cancellable); 72 | 73 | g_mutex_lock(&queue->current_task_lock); 74 | queue->current_task = NULL; 75 | g_mutex_unlock(&queue->current_task_lock); 76 | 77 | g_cancellable_reset(task->task_cancellable); 78 | task->task_finished_func(result, task->data); 79 | 80 | g_clear_pointer(&task, fsearch_task_free); 81 | } 82 | return NULL; 83 | } 84 | 85 | void 86 | fsearch_task_queue_cancel_current(FsearchTaskQueue *queue) { 87 | g_mutex_lock(&queue->current_task_lock); 88 | if (queue->current_task) { 89 | g_cancellable_cancel(queue->current_task->task_cancellable); 90 | } 91 | g_mutex_unlock(&queue->current_task_lock); 92 | } 93 | 94 | static void 95 | fsearch_task_queue_clear(FsearchTaskQueue *queue, FsearchTaskQueueClearPolicy clear_policy, int id) { 96 | if (clear_policy == FSEARCH_TASK_CLEAR_NONE) { 97 | return; 98 | } 99 | 100 | GQueue *task_queue = g_queue_new(); 101 | 102 | g_async_queue_lock(queue->queue); 103 | while (true) { 104 | // clear all queued tasks 105 | FsearchTask *task = g_async_queue_try_pop_unlocked(queue->queue); 106 | if (!task) { 107 | break; 108 | } 109 | if (clear_policy == FSEARCH_TASK_CLEAR_SAME_ID && task->id != id) { 110 | // remember tasks which need to be inserted back into the async queue later 111 | g_queue_push_tail(task_queue, g_steal_pointer(&task)); 112 | continue; 113 | } 114 | 115 | if (task->task_cancelled_func) { 116 | task->task_cancelled_func(task->data); 117 | } 118 | g_clear_pointer(&task, fsearch_task_free); 119 | } 120 | 121 | // insert all the tasks back into the async queue, which still need to be processed 122 | while (true) { 123 | FsearchTask *task = g_queue_pop_head(task_queue); 124 | if (task) { 125 | g_async_queue_push_unlocked(queue->queue, g_steal_pointer(&task)); 126 | } 127 | else { 128 | break; 129 | } 130 | } 131 | 132 | g_async_queue_unlock(queue->queue); 133 | 134 | g_clear_pointer(&task_queue, g_queue_free); 135 | } 136 | 137 | void 138 | fsearch_task_queue_free(FsearchTaskQueue *queue) { 139 | g_assert(queue); 140 | 141 | fsearch_task_queue_clear(queue, FSEARCH_TASK_CLEAR_ALL, -1); 142 | 143 | fsearch_task_queue_cancel_current(queue); 144 | 145 | FsearchTask *task = calloc(1, sizeof(FsearchTask)); 146 | g_assert(task); 147 | task->type = FSEARCH_TASK_TYPE_QUIT; 148 | g_async_queue_push(queue->queue, g_steal_pointer(&task)); 149 | 150 | g_thread_join(g_steal_pointer(&queue->queue_thread)); 151 | 152 | g_mutex_clear(&queue->current_task_lock); 153 | 154 | g_clear_pointer(&queue->queue, g_async_queue_unref); 155 | g_clear_pointer(&queue, g_free); 156 | } 157 | 158 | FsearchTaskQueue * 159 | fsearch_task_queue_new(const char *name) { 160 | FsearchTaskQueue *queue = calloc(1, sizeof(FsearchTaskQueue)); 161 | g_assert(queue); 162 | 163 | queue->queue = g_async_queue_new(); 164 | queue->queue_thread = g_thread_new(name, (GThreadFunc)fsearch_task_queue_thread, queue); 165 | 166 | g_mutex_init(&queue->current_task_lock); 167 | 168 | return queue; 169 | } 170 | 171 | void 172 | fsearch_task_queue(FsearchTaskQueue *queue, 173 | gint id, 174 | FsearchTaskFunc task_func, 175 | FsearchTaskFinishedFunc task_finished_func, 176 | FsearchTaskCancelledFunc task_cancelled_func, 177 | FsearchTaskQueueClearPolicy clear_policy, 178 | gpointer data) { 179 | FsearchTask *task = fsearch_task_new(id, task_func, task_finished_func, task_cancelled_func, data); 180 | if (clear_policy != FSEARCH_TASK_CLEAR_NONE) { 181 | fsearch_task_queue_clear(queue, clear_policy, task->id); 182 | g_mutex_lock(&queue->current_task_lock); 183 | if (queue->current_task) { 184 | if (clear_policy != FSEARCH_TASK_CLEAR_SAME_ID || queue->current_task->id == task->id) { 185 | g_cancellable_cancel(queue->current_task->task_cancellable); 186 | } 187 | } 188 | g_mutex_unlock(&queue->current_task_lock); 189 | } 190 | g_async_queue_push(queue->queue, g_steal_pointer(&task)); 191 | } 192 | 193 | -------------------------------------------------------------------------------- /src/fsearch_task.h: -------------------------------------------------------------------------------- 1 | 2 | #pragma once 3 | 4 | #include 5 | #include 6 | 7 | typedef struct FsearchTaskQueue FsearchTaskQueue; 8 | 9 | typedef gpointer (*FsearchTaskFunc)(gpointer data, GCancellable *cancellable); 10 | typedef void (*FsearchTaskCancelledFunc)(gpointer data); 11 | typedef void (*FsearchTaskFinishedFunc)(gpointer result, gpointer data); 12 | 13 | typedef enum { 14 | FSEARCH_TASK_CLEAR_NONE = -1, 15 | FSEARCH_TASK_CLEAR_SAME_ID, 16 | FSEARCH_TASK_CLEAR_ALL, 17 | } FsearchTaskQueueClearPolicy; 18 | 19 | void 20 | fsearch_task_queue_free(FsearchTaskQueue *queue); 21 | 22 | FsearchTaskQueue * 23 | fsearch_task_queue_new(const char *name); 24 | 25 | void 26 | fsearch_task_queue(FsearchTaskQueue *queue, 27 | gint id, 28 | FsearchTaskFunc task_func, 29 | FsearchTaskFinishedFunc task_finished_func, 30 | FsearchTaskCancelledFunc task_cancelled_func, 31 | FsearchTaskQueueClearPolicy clear_policy, 32 | gpointer data); 33 | 34 | void 35 | fsearch_task_queue_cancel_current(FsearchTaskQueue *queue); 36 | -------------------------------------------------------------------------------- /src/fsearch_task_ids.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | typedef enum FsearchTaskId { 4 | FSEARCH_TASK_ID_SEARCH, 5 | FSEARCH_TASK_ID_SORT, 6 | } FsearchTaskId; -------------------------------------------------------------------------------- /src/fsearch_thread_pool.h: -------------------------------------------------------------------------------- 1 | /* 2 | FSearch - A fast file search utility 3 | Copyright © 2020 Christian Boxdörfer 4 | 5 | This program is free software; you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation; either version 2 of the License, or 8 | (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program; if not, see . 17 | */ 18 | 19 | #pragma once 20 | 21 | #include 22 | #include 23 | #include 24 | 25 | typedef struct FsearchThreadPool FsearchThreadPool; 26 | typedef void (*FsearchThreadPoolFunc)(void *data); 27 | ; 28 | 29 | typedef enum FsearchThreadStatus { THREAD_IDLE, THREAD_BUSY, THREAD_FINISHED } FsearchThreadStatus; 30 | 31 | FsearchThreadPool * 32 | fsearch_thread_pool_init(void); 33 | 34 | void 35 | fsearch_thread_pool_free(FsearchThreadPool *pool); 36 | 37 | GList * 38 | fsearch_thread_pool_get_threads(FsearchThreadPool *pool); 39 | 40 | uint32_t 41 | fsearch_thread_pool_get_num_threads(FsearchThreadPool *pool); 42 | 43 | bool 44 | fsearch_thread_pool_push_data(FsearchThreadPool *pool, 45 | GList *thread, 46 | FsearchThreadPoolFunc thread_func, 47 | gpointer thread_data); 48 | 49 | bool 50 | fsearch_thread_pool_wait_for_thread(FsearchThreadPool *pool, GList *thread); 51 | 52 | bool 53 | fsearch_thread_pool_task_is_busy(FsearchThreadPool *pool, GList *thread); 54 | 55 | bool 56 | fsearch_thread_pool_task_is_idle(FsearchThreadPool *pool, GList *thread); 57 | 58 | gpointer 59 | fsearch_thread_pool_get_data(FsearchThreadPool *pool, GList *thread); 60 | 61 | bool 62 | fsearch_thread_pool_set_task_finished(FsearchThreadPool *pool, GList *thread); 63 | -------------------------------------------------------------------------------- /src/fsearch_time_utils.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | bool 7 | fsearch_date_time_parse_interval(const char *str, time_t *time_start_out, time_t *time_end_out); -------------------------------------------------------------------------------- /src/fsearch_ui_utils.c: -------------------------------------------------------------------------------- 1 | /* 2 | FSearch - A fast file search utility 3 | Copyright © 2020 Christian Boxdörfer 4 | 5 | This program is free software; you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation; either version 2 of the License, or 8 | (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program; if not, see . 17 | */ 18 | 19 | #include "fsearch_ui_utils.h" 20 | 21 | void 22 | ui_utils_run_gtk_dialog_async(GtkWidget *parent, 23 | GtkMessageType type, 24 | GtkButtonsType buttons, 25 | const gchar *primary_text, 26 | const gchar *sec_text, 27 | GCallback response_cb, 28 | gpointer response_cb_data) { 29 | g_return_if_fail(primary_text); 30 | 31 | GtkWidget *dialog = 32 | gtk_message_dialog_new(GTK_WINDOW(parent), GTK_DIALOG_DESTROY_WITH_PARENT, type, buttons, primary_text, NULL); 33 | 34 | if (sec_text) { 35 | gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(dialog), sec_text, NULL); 36 | } 37 | 38 | gtk_window_set_modal(GTK_WINDOW(dialog), TRUE); 39 | gtk_window_set_title(GTK_WINDOW(dialog), ""); 40 | 41 | g_signal_connect(dialog, "response", G_CALLBACK(response_cb), response_cb_data); 42 | gtk_widget_show(dialog); 43 | } 44 | 45 | gint 46 | ui_utils_run_gtk_dialog(GtkWidget *parent, 47 | GtkMessageType type, 48 | GtkButtonsType buttons, 49 | const gchar *primary_text, 50 | const gchar *sec_text) { 51 | if (!parent || !primary_text) { 52 | return GTK_RESPONSE_CANCEL; 53 | } 54 | 55 | GtkWidget *dialog = 56 | gtk_message_dialog_new(GTK_WINDOW(parent), GTK_DIALOG_DESTROY_WITH_PARENT, type, buttons, primary_text, NULL); 57 | 58 | if (sec_text) { 59 | gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(dialog), sec_text, NULL); 60 | } 61 | 62 | gtk_window_set_title(GTK_WINDOW(dialog), ""); 63 | 64 | gint response = gtk_dialog_run(GTK_DIALOG(dialog)); 65 | 66 | g_clear_pointer(&dialog, gtk_widget_destroy); 67 | 68 | return response; 69 | } 70 | -------------------------------------------------------------------------------- /src/fsearch_ui_utils.h: -------------------------------------------------------------------------------- 1 | /* 2 | FSearch - A fast file search utility 3 | Copyright © 2020 Christian Boxdörfer 4 | 5 | This program is free software; you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation; either version 2 of the License, or 8 | (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program; if not, see . 17 | */ 18 | 19 | #pragma once 20 | 21 | #include 22 | 23 | void 24 | ui_utils_run_gtk_dialog_async(GtkWidget *parent, 25 | GtkMessageType type, 26 | GtkButtonsType buttons, 27 | const gchar *primary_text, 28 | const gchar *sec_text, 29 | GCallback response_cb, 30 | gpointer response_cb_data); 31 | 32 | gint 33 | ui_utils_run_gtk_dialog(GtkWidget *parent, 34 | GtkMessageType type, 35 | GtkButtonsType buttons, 36 | const gchar *primary_text, 37 | const gchar *sec_text); 38 | -------------------------------------------------------------------------------- /src/fsearch_utf.c: -------------------------------------------------------------------------------- 1 | #include "fsearch_utf.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | 10 | void 11 | fsearch_utf_builder_init(FsearchUtfBuilder *builder, int32_t num_characters) { 12 | g_return_if_fail(builder); 13 | 14 | builder->initialized = true; 15 | 16 | builder->fold_options = U_FOLD_CASE_DEFAULT; 17 | const char *current_locale = setlocale(LC_CTYPE, NULL); 18 | if (current_locale && (!strncmp(current_locale, "tr", 2) || !strncmp(current_locale, "az", 2))) { 19 | // Use special case mapping for Turkic languages 20 | builder->fold_options = U_FOLD_CASE_EXCLUDE_SPECIAL_I; 21 | } 22 | 23 | UErrorCode status = U_ZERO_ERROR; 24 | builder->case_map = ucasemap_open(current_locale, builder->fold_options, &status); 25 | g_assert(U_SUCCESS(status)); 26 | 27 | builder->normalizer = unorm2_getNFDInstance(&status); 28 | g_assert(U_SUCCESS(status)); 29 | 30 | builder->string_utf8_is_folded = false; 31 | builder->string_is_folded_and_normalized = false; 32 | builder->num_characters = num_characters; 33 | builder->string_utf8_folded = calloc(builder->num_characters, sizeof(char)); 34 | builder->string_utf8_folded_len = 0; 35 | builder->string_folded = calloc(builder->num_characters, sizeof(UChar)); 36 | builder->string_folded_len = 0; 37 | builder->string_normalized_folded = calloc(builder->num_characters, sizeof(UChar)); 38 | builder->string_normalized_folded_len = 0; 39 | } 40 | 41 | void 42 | fsearch_utf_builder_clear(FsearchUtfBuilder *builder) { 43 | if (!builder) { 44 | return; 45 | } 46 | builder->initialized = false; 47 | g_clear_pointer(&builder->case_map, ucasemap_close); 48 | g_clear_pointer(&builder->string, free); 49 | g_clear_pointer(&builder->string_utf8_folded, free); 50 | g_clear_pointer(&builder->string_folded, free); 51 | g_clear_pointer(&builder->string_normalized_folded, free); 52 | } 53 | 54 | bool 55 | fsearch_utf_fold_case_utf8(UCaseMap *case_map, FsearchUtfBuilder *builder, const char *string) { 56 | if (!builder || !builder->initialized) { 57 | return false; 58 | } 59 | 60 | UErrorCode status = U_ZERO_ERROR; 61 | 62 | // first perform case folding (this can be done while our string is still in UTF8 form) 63 | builder->string_utf8_folded_len = 64 | ucasemap_utf8FoldCase(case_map, builder->string_utf8_folded, builder->num_characters, string, -1, &status); 65 | if (G_UNLIKELY(U_FAILURE(status))) { 66 | return false; 67 | } 68 | builder->string_utf8_is_folded = true; 69 | builder->string_is_folded_and_normalized = false; 70 | 71 | return true; 72 | } 73 | 74 | bool 75 | fsearch_utf_builder_normalize_and_fold_case(FsearchUtfBuilder *builder, 76 | const char *string) { 77 | g_assert(builder); 78 | if (!builder->initialized) { 79 | goto fail; 80 | } 81 | 82 | UErrorCode status = U_ZERO_ERROR; 83 | 84 | builder->string = g_strdup(string); 85 | // first perform case folding (this can be done while our string is still in UTF8 form) 86 | builder->string_utf8_folded_len = 87 | ucasemap_utf8FoldCase(builder->case_map, builder->string_utf8_folded, builder->num_characters, string, -1, &status); 88 | if (G_UNLIKELY(U_FAILURE(status))) { 89 | goto fail; 90 | } 91 | builder->string_utf8_is_folded = true; 92 | 93 | // then convert folded UTF8 string to UTF16 for normalizer 94 | u_strFromUTF8(builder->string_folded, 95 | builder->num_characters, 96 | &builder->string_folded_len, 97 | builder->string_utf8_folded, 98 | builder->string_utf8_folded_len, 99 | &status); 100 | if (G_UNLIKELY(U_FAILURE(status))) { 101 | goto fail; 102 | } 103 | 104 | // check how much of the string needs to be normalized (if anything at all) 105 | const int32_t span_end = 106 | unorm2_spanQuickCheckYes(builder->normalizer, builder->string_folded, builder->string_folded_len, &status); 107 | if (G_UNLIKELY(U_FAILURE(status))) { 108 | goto fail; 109 | } 110 | 111 | if (G_LIKELY(span_end == builder->string_folded_len)) { 112 | // the string is already normalized 113 | // this should be the most common case and fortunately is the quickest (simple memcpy) 114 | memcpy(builder->string_normalized_folded, builder->string_folded, span_end * sizeof(UChar)); 115 | builder->string_normalized_folded_len = builder->string_folded_len; 116 | } 117 | else if (span_end < builder->string_folded_len) { 118 | // the string isn't fully normalized 119 | // normalize everything after string_folded + span_end 120 | u_strncpy(builder->string_normalized_folded, builder->string_folded, span_end); 121 | builder->string_normalized_folded_len = unorm2_normalizeSecondAndAppend(builder->normalizer, 122 | builder->string_normalized_folded, 123 | span_end, 124 | builder->num_characters, 125 | builder->string_folded + span_end, 126 | builder->string_folded_len - span_end, 127 | &status); 128 | if (G_UNLIKELY(U_FAILURE(status))) { 129 | goto fail; 130 | } 131 | } 132 | else { 133 | // span_end is reported to be after string_folded_len, there's likely a bug in our code 134 | g_assert_not_reached(); 135 | } 136 | 137 | builder->string_is_folded_and_normalized = true; 138 | return true; 139 | 140 | fail: 141 | builder->string_utf8_folded_len = 0; 142 | builder->string_folded_len = 0; 143 | builder->string_normalized_folded_len = 0; 144 | builder->string_is_folded_and_normalized = false; 145 | builder->string_utf8_is_folded = false; 146 | return false; 147 | } 148 | -------------------------------------------------------------------------------- /src/fsearch_utf.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | typedef struct FsearchUtfBuilder { 9 | UCaseMap *case_map; 10 | const UNormalizer2 *normalizer; 11 | 12 | char *string; 13 | char *string_utf8_folded; 14 | UChar *string_folded; 15 | UChar *string_normalized_folded; 16 | 17 | int32_t string_folded_len; 18 | int32_t string_normalized_folded_len; 19 | int32_t string_utf8_folded_len; 20 | 21 | uint32_t fold_options; 22 | 23 | int32_t num_characters; 24 | bool initialized; 25 | bool string_is_folded_and_normalized; 26 | bool string_utf8_is_folded; 27 | } FsearchUtfBuilder; 28 | 29 | void 30 | fsearch_utf_builder_init(FsearchUtfBuilder *builder, int32_t num_characters); 31 | 32 | void 33 | fsearch_utf_builder_clear(FsearchUtfBuilder *builder); 34 | 35 | bool 36 | fsearch_utf_fold_case_utf8(UCaseMap *case_map, FsearchUtfBuilder *builder, const char *string); 37 | 38 | bool 39 | fsearch_utf_builder_normalize_and_fold_case(FsearchUtfBuilder *builder, 40 | const char *string); 41 | -------------------------------------------------------------------------------- /src/fsearch_window.h: -------------------------------------------------------------------------------- 1 | /* 2 | FSearch - A fast file search utility 3 | Copyright © 2020 Christian Boxdörfer 4 | 5 | This program is free software; you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation; either version 2 of the License, or 8 | (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program; if not, see . 17 | */ 18 | 19 | #pragma once 20 | 21 | #include 22 | #include 23 | #include 24 | 25 | #include "fsearch.h" 26 | #include "fsearch_database.h" 27 | #include "fsearch_list_view.h" 28 | #include "fsearch_query.h" 29 | #include "fsearch_statusbar.h" 30 | 31 | G_BEGIN_DECLS 32 | 33 | #define FSEARCH_APPLICATION_WINDOW_TYPE (fsearch_application_window_get_type()) 34 | 35 | G_DECLARE_FINAL_TYPE(FsearchApplicationWindow, fsearch_application_window, FSEARCH, APPLICATION_WINDOW, GtkApplicationWindow) 36 | 37 | FsearchApplicationWindow * 38 | fsearch_application_window_new(FsearchApplication *app); 39 | 40 | void 41 | fsearch_application_window_prepare_shutdown(gpointer self); 42 | 43 | FsearchListView * 44 | fsearch_application_window_get_listview(FsearchApplicationWindow *self); 45 | 46 | void 47 | fsearch_application_window_update_listview_config(FsearchApplicationWindow *self); 48 | 49 | void 50 | fsearch_application_window_apply_statusbar_revealer_config(FsearchApplicationWindow *win); 51 | 52 | void 53 | fsearch_application_window_focus_search_entry(FsearchApplicationWindow *win); 54 | 55 | GtkEntry * 56 | fsearch_application_window_get_search_entry(FsearchApplicationWindow *self); 57 | 58 | FsearchStatusbar * 59 | fsearch_application_window_get_statusbar(FsearchApplicationWindow *self); 60 | 61 | void 62 | fsearch_application_window_update_query_flags(FsearchApplicationWindow *self); 63 | 64 | void 65 | fsearch_application_window_remove_model(FsearchApplicationWindow *self); 66 | 67 | void 68 | fsearch_application_window_set_database_index_progress(FsearchApplicationWindow *self, const char *text); 69 | 70 | uint32_t 71 | fsearch_application_window_get_num_results(FsearchApplicationWindow *self); 72 | 73 | gint 74 | fsearch_application_window_get_active_filter(FsearchApplicationWindow *self); 75 | 76 | void 77 | fsearch_application_window_set_active_filter(FsearchApplicationWindow *self, guint active_filter); 78 | 79 | void 80 | fsearch_application_window_apply_search_revealer_config(FsearchApplicationWindow *win); 81 | 82 | void 83 | fsearch_application_window_added(FsearchApplicationWindow *win, FsearchApplication *app); 84 | 85 | void 86 | fsearch_application_window_removed(FsearchApplicationWindow *win, FsearchApplication *app); 87 | 88 | void 89 | fsearch_application_window_cancel_current_task(FsearchApplicationWindow *win); 90 | 91 | void 92 | fsearch_application_window_invert_selection(FsearchApplicationWindow *self); 93 | 94 | void 95 | fsearch_application_window_unselect_all(FsearchApplicationWindow *self); 96 | 97 | void 98 | fsearch_application_window_select_all(FsearchApplicationWindow *self); 99 | 100 | uint32_t 101 | fsearch_application_window_get_num_selected(FsearchApplicationWindow *self); 102 | 103 | void 104 | fsearch_application_window_selection_for_each(FsearchApplicationWindow *self, GHFunc func, gpointer user_data); 105 | 106 | void 107 | fsearch_application_window_toggle_app_menu(FsearchApplicationWindow *self); 108 | G_END_DECLS 109 | -------------------------------------------------------------------------------- /src/fsearch_window_actions.h: -------------------------------------------------------------------------------- 1 | /* 2 | FSearch - A fast file search utility 3 | Copyright © 2020 Christian Boxdörfer 4 | 5 | This program is free software; you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation; either version 2 of the License, or 8 | (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program; if not, see . 17 | */ 18 | 19 | #pragma once 20 | 21 | #include "fsearch_window.h" 22 | 23 | void 24 | fsearch_window_actions_init(FsearchApplicationWindow *self); 25 | 26 | void 27 | fsearch_window_actions_update(FsearchApplicationWindow *self); 28 | 29 | void 30 | fsearch_window_action_open_generic(FsearchApplicationWindow *win, bool open_parent_folder, bool triggered_with_mouse); 31 | -------------------------------------------------------------------------------- /src/gresource.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | fsearch_filter_editor.ui 5 | fsearch_overlay.ui 6 | fsearch_preferences.ui 7 | fsearch_statusbar.ui 8 | fsearch_window.ui 9 | menus.ui 10 | shared.css 11 | 12 | 13 | -------------------------------------------------------------------------------- /src/main.c: -------------------------------------------------------------------------------- 1 | /* 2 | FSearch - A fast file search utility 3 | Copyright © 2020 Christian Boxdörfer 4 | 5 | This program is free software; you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation; either version 2 of the License, or 8 | (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program; if not, see . 17 | */ 18 | 19 | #ifdef HAVE_CONFIG_H 20 | #include 21 | #endif 22 | 23 | #include "fsearch.h" 24 | #include 25 | #include 26 | #include 27 | 28 | int 29 | main(int argc, char *argv[]) { 30 | setlocale(LC_ALL, ""); 31 | bindtextdomain(GETTEXT_PACKAGE, LOCALEDIR); 32 | bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8"); 33 | textdomain(GETTEXT_PACKAGE); 34 | 35 | g_set_application_name(_("FSearch")); 36 | g_set_prgname("io.github.cboxdoerfer.FSearch"); 37 | 38 | return g_application_run(G_APPLICATION(fsearch_application_new()), argc, argv); 39 | } 40 | -------------------------------------------------------------------------------- /src/meson.build: -------------------------------------------------------------------------------- 1 | resources = gnome.compile_resources('ui_resources', 2 | 'gresource.xml', 3 | source_dir: '.', 4 | c_name: 'fsearch_ui', 5 | ) 6 | 7 | resources += gnome.compile_resources('icon_resources', 8 | join_paths(meson.source_root(), 'data', 'gresource.xml'), 9 | source_dir: join_paths(meson.source_root(), 'data'), 10 | c_name: 'fsearch_icons', 11 | ) 12 | 13 | libfsearch_sources = [ 14 | resources, 15 | 'fsearch.c', 16 | 'fsearch_array.c', 17 | 'fsearch_clipboard.c', 18 | 'fsearch_config.c', 19 | 'fsearch_database.c', 20 | 'fsearch_database_entry.c', 21 | 'fsearch_database_index.c', 22 | 'fsearch_database_search.c', 23 | 'fsearch_database_view.c', 24 | 'fsearch_exclude_path.c', 25 | 'fsearch_file_utils.c', 26 | 'fsearch_filter.c', 27 | 'fsearch_filter_editor.c', 28 | 'fsearch_filter_manager.c', 29 | 'fsearch_index.c', 30 | 'fsearch_list_view.c', 31 | 'fsearch_listview_popup.c', 32 | 'fsearch_memory_pool.c', 33 | 'fsearch_preferences_ui.c', 34 | 'fsearch_preferences_widgets.c', 35 | 'fsearch_query.c', 36 | 'fsearch_query_match_data.c', 37 | 'fsearch_query_matchers.c', 38 | 'fsearch_query_node.c', 39 | 'fsearch_query_lexer.c', 40 | 'fsearch_query_parser.c', 41 | 'fsearch_query_tree.c', 42 | 'fsearch_result_view.c', 43 | 'fsearch_selection.c', 44 | 'fsearch_size_utils.c', 45 | 'fsearch_statusbar.c', 46 | 'fsearch_string_utils.c', 47 | 'fsearch_task.c', 48 | 'fsearch_thread_pool.c', 49 | 'fsearch_time_utils.c', 50 | 'fsearch_ui_utils.c', 51 | 'fsearch_utf.c', 52 | 'fsearch_window.c', 53 | 'fsearch_window_actions.c', 54 | 'fsearch_preview.c', 55 | ] 56 | 57 | if build_machine.system()=='darwin' 58 | libfsearch_sources+=['strverscmp.c'] 59 | endif 60 | 61 | fsearch_deps = [ 62 | cc.find_library('m', required: true), 63 | dependency('gio-unix-2.0', version: '>= 2.50'), 64 | dependency('gtk+-3.0', version: '>= 3.18'), 65 | dependency('libpcre2-8', version: '>= 10.21'), 66 | dependency('icu-uc', version: '>= 3.8'), 67 | ] 68 | 69 | libfsearch = static_library( 70 | 'fsearch', 71 | libfsearch_sources, 72 | dependencies: fsearch_deps, 73 | include_directories: fsearch_include_dirs, 74 | ) 75 | 76 | libfsearch_include_dirs = include_directories('.') 77 | 78 | libfsearch_dep = declare_dependency( 79 | link_with: libfsearch, 80 | include_directories: [ 81 | fsearch_include_dirs, 82 | libfsearch_include_dirs 83 | ], 84 | dependencies: fsearch_deps, 85 | sources: resources 86 | ) 87 | 88 | fsearch = executable('fsearch', 'main.c', 89 | include_directories: fsearch_include_dirs, 90 | dependencies: libfsearch_dep, 91 | install: true, 92 | ) 93 | 94 | subdir('tests') 95 | -------------------------------------------------------------------------------- /src/shared.css: -------------------------------------------------------------------------------- 1 | .fsearch-statusbar-cancel-update-button { 2 | padding: 0px; 3 | min-height: 20px; 4 | min-width: 20px; 5 | 6 | border-radius: 0px; 7 | border-bottom-width: 0px; 8 | border-top-width: 0px; 9 | } 10 | 11 | .filter_combobox box.linked button:dir(ltr) { 12 | border-bottom-left-radius: 0; 13 | border-top-left-radius: 0; 14 | border-left-width: 0; 15 | } 16 | 17 | .filter_combobox box.linked button:dir(rtl) { 18 | border-bottom-right-radius: 0; 19 | border-top-right-radius: 0; 20 | border-right-width: 0; 21 | } 22 | 23 | .filter_centered box.linked button { 24 | border-radius: 0; 25 | } 26 | 27 | .search_button:dir(ltr) { 28 | border-bottom-left-radius: 0; 29 | border-top-left-radius: 0; 30 | border-left-width: 0; 31 | } 32 | 33 | .search_button:dir(rtl) { 34 | border-bottom-right-radius: 0; 35 | border-top-right-radius: 0; 36 | border-right-width: 0; 37 | } 38 | 39 | .search_entry_has_neighbours:dir(ltr) { 40 | border-bottom-right-radius: 0; 41 | border-top-right-radius: 0; 42 | } 43 | 44 | .search_entry_has_neighbours:dir(rtl) { 45 | border-bottom-left-radius: 0; 46 | border-top-left-radius: 0; 47 | } 48 | 49 | .fsearch-statusbar-button { 50 | border-radius: 0px; 51 | border-top: none; 52 | border-bottom: none; 53 | } 54 | 55 | .results_frame { 56 | border-left: none; 57 | border-right: none; 58 | } 59 | 60 | .results_frame_last { 61 | border-bottom: none; 62 | } 63 | 64 | .results_frame_csd_mode { 65 | border-top: none; 66 | } 67 | 68 | .results_frame treeview button { 69 | border-radius: 0; 70 | border-top: 0; 71 | border-right: 0; 72 | margin-left: 0; 73 | margin-right: 0; 74 | } 75 | 76 | .results_frame treeview button:first-child:dir(rtl) { 77 | border-right: 0; 78 | } 79 | 80 | .results_frame treeview button:first-child:dir(ltr) { 81 | border-left: 0; 82 | } 83 | 84 | .path_entry { 85 | border-top-right-radius: 0; 86 | border-bottom-right-radius: 0; 87 | } 88 | 89 | .path_add_button { 90 | border-left: none; 91 | border-top-left-radius: 0; 92 | border-bottom-left-radius: 0; 93 | } 94 | -------------------------------------------------------------------------------- /src/strverscmp.c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cboxdoerfer/fsearch/fda5923d75026941c1dc0349dee06cb2f9d69699/src/strverscmp.c -------------------------------------------------------------------------------- /src/strverscmp.h: -------------------------------------------------------------------------------- 1 | int strverscmp (const char *, const char *); -------------------------------------------------------------------------------- /src/tests/meson.build: -------------------------------------------------------------------------------- 1 | test_array = executable('test_array', 'test_array.c', dependencies: libfsearch_dep) 2 | test_query = executable('test_query', 'test_query.c', dependencies: libfsearch_dep) 3 | test_size_utils = executable('test_size_utils', 'test_size_utils.c', dependencies: libfsearch_dep) 4 | test_string_utils = executable('test_string_utils', 'test_string_utils.c', dependencies: libfsearch_dep) 5 | test_time_utils = executable('test_time_utils', 'test_time_utils.c', dependencies: libfsearch_dep) 6 | 7 | test('test_array', 8 | test_array, 9 | env: [ 10 | 'G_TEST_SRCDIR=@0@'.format(meson.current_source_dir()), 11 | 'G_TEST_BUILDDIR=@0@'.format(meson.current_build_dir()), 12 | ], 13 | ) 14 | test('test_query', 15 | test_query, 16 | env: [ 17 | 'G_TEST_SRCDIR=@0@'.format(meson.current_source_dir()), 18 | 'G_TEST_BUILDDIR=@0@'.format(meson.current_build_dir()), 19 | ], 20 | ) 21 | test('test_size_utils', 22 | test_size_utils, 23 | env: [ 24 | 'G_TEST_SRCDIR=@0@'.format(meson.current_source_dir()), 25 | 'G_TEST_BUILDDIR=@0@'.format(meson.current_build_dir()), 26 | ], 27 | ) 28 | test('test_string_utils', 29 | test_string_utils, 30 | env: [ 31 | 'G_TEST_SRCDIR=@0@'.format(meson.current_source_dir()), 32 | 'G_TEST_BUILDDIR=@0@'.format(meson.current_build_dir()), 33 | ], 34 | ) 35 | test('test_time_utils', 36 | test_time_utils, 37 | env: [ 38 | 'G_TEST_SRCDIR=@0@'.format(meson.current_source_dir()), 39 | 'G_TEST_BUILDDIR=@0@'.format(meson.current_build_dir()), 40 | ], 41 | ) 42 | -------------------------------------------------------------------------------- /src/tests/test_array.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include 6 | 7 | typedef struct Version { 8 | int major; 9 | int minor; 10 | } Version; 11 | 12 | static int32_t 13 | sort_int_descending(void **a, void **b, void *data) { 14 | int32_t ia = GPOINTER_TO_INT(*a); 15 | int32_t ib = GPOINTER_TO_INT(*b); 16 | return ib - ia; 17 | } 18 | 19 | static int32_t 20 | sort_int_ascending(void **a, void **b, void *data) { 21 | return -1 * sort_int_descending(a, b, data); 22 | } 23 | 24 | static void 25 | test_main(void) { 26 | DynamicArray *array = darray_new(10); 27 | g_assert_true(darray_get_size(array) == 10); 28 | 29 | const int32_t upper_limit = 128; 30 | for (int32_t i = 0; i < upper_limit; ++i) { 31 | darray_add_item(array, GINT_TO_POINTER(i)); 32 | } 33 | for (int32_t i = 0; i < upper_limit; ++i) { 34 | int32_t j = GPOINTER_TO_INT(darray_get_item(array, i)); 35 | g_assert_true(i == j); 36 | } 37 | g_assert_true(darray_get_num_items(array) == upper_limit); 38 | 39 | darray_sort(array, (DynamicArrayCompareDataFunc)sort_int_descending, NULL, NULL); 40 | for (int32_t i = 0; i < upper_limit; ++i) { 41 | int32_t j = GPOINTER_TO_INT(darray_get_item(array, i)); 42 | int32_t expected_val = upper_limit - i - 1; 43 | if (expected_val != j) { 44 | g_print("[sort] Expect %d at index %d. Result: %d\n", expected_val, i, j); 45 | } 46 | g_assert_true(expected_val == j); 47 | uint32_t matched_idx = 0; 48 | if (darray_binary_search_with_data(array, 49 | GINT_TO_POINTER(i), 50 | (DynamicArrayCompareDataFunc)sort_int_descending, 51 | NULL, 52 | &matched_idx)) { 53 | if (matched_idx != expected_val) { 54 | g_print("[bin_search] Expect %d to be at idx %d\n", i, expected_val); 55 | } 56 | g_assert_true(matched_idx == expected_val); 57 | } 58 | else { 59 | g_print("[bin_search] Didn't find %d!\n", i); 60 | g_assert_not_reached(); 61 | } 62 | } 63 | 64 | darray_sort_multi_threaded(array, (DynamicArrayCompareDataFunc)sort_int_ascending, NULL, NULL); 65 | for (int32_t i = 0; i < upper_limit; ++i) { 66 | int32_t j = GPOINTER_TO_INT(darray_get_item(array, i)); 67 | g_print("%d:%d\n", i, j); 68 | } 69 | for (int32_t i = 0; i < upper_limit; ++i) { 70 | int32_t j = GPOINTER_TO_INT(darray_get_item(array, i)); 71 | int32_t expected_val = i; 72 | if (expected_val != j) { 73 | g_print("[threaded_sort] Expect %d at index %d. Result: %d\n", expected_val, i, j); 74 | } 75 | g_assert_true(expected_val == j); 76 | uint32_t matched_idx = 0; 77 | if (darray_binary_search_with_data(array, 78 | GINT_TO_POINTER(i), 79 | (DynamicArrayCompareDataFunc)sort_int_ascending, 80 | NULL, 81 | &matched_idx)) { 82 | if (expected_val != matched_idx) { 83 | g_print("[bin_search] Expect %d to be at idx %d\n", i, expected_val); 84 | } 85 | g_assert_true(matched_idx == expected_val); 86 | } 87 | else { 88 | g_print("[bin_search] Didn't find %d!\n", i); 89 | g_assert_not_reached(); 90 | } 91 | } 92 | 93 | for (uint32_t i = 0; i < upper_limit - 1; ++i) { 94 | int32_t i1 = GPOINTER_TO_INT(darray_get_item(array, i)); 95 | g_assert_true(i1 == i); 96 | uint32_t i2_idx = 0; 97 | int32_t i2 = GPOINTER_TO_INT( 98 | darray_get_item_next(array, GINT_TO_POINTER(i1), (DynamicArrayCompareDataFunc)sort_int_ascending, NULL, &i2_idx)); 99 | g_assert_true(i2 == i1 + 1); 100 | g_assert_true(i2_idx == i1 + 1); 101 | } 102 | 103 | g_clear_pointer(&array, darray_unref); 104 | } 105 | 106 | static void 107 | same_elements(void) { 108 | DynamicArray *array = darray_new(10); 109 | 110 | uint32_t element = 42; 111 | for (int32_t i = 0; i < 10; ++i) { 112 | darray_add_item(array, GINT_TO_POINTER(element)); 113 | } 114 | 115 | for (uint32_t i = 0; i < element * 2; ++i) { 116 | if (i == element) { 117 | continue; 118 | } 119 | uint32_t matched_idx = 0; 120 | if (darray_binary_search_with_data(array, 121 | GINT_TO_POINTER(i), 122 | (DynamicArrayCompareDataFunc)sort_int_ascending, 123 | NULL, 124 | &matched_idx)) { 125 | g_assert_not_reached(); 126 | } 127 | } 128 | 129 | g_clear_pointer(&array, darray_unref); 130 | } 131 | 132 | static int32_t 133 | sort_version(void **a, void **b, void *data) { 134 | Version *v1 = *a; 135 | Version *v2 = *b; 136 | return v1->major - v2->major; 137 | } 138 | 139 | static void 140 | test_single_and_multi_threaded_sort(DynamicArray *array, DynamicArrayCompareDataFunc comp_func) { 141 | DynamicArray *a1 = darray_copy(array); 142 | DynamicArray *a2 = darray_copy(array); 143 | darray_sort(a1, (DynamicArrayCompareDataFunc)sort_version, NULL, NULL); 144 | darray_sort_multi_threaded(a2, (DynamicArrayCompareDataFunc)sort_version, NULL, NULL); 145 | for (uint32_t i = 0; i < darray_get_num_items(a1); ++i) { 146 | Version *v1 = darray_get_item(a1, i); 147 | Version *v2 = darray_get_item(a2, i); 148 | g_print("%d.%d / %d.%d\n", v1->major, v1->minor, v2->major, v2->minor); 149 | g_assert(v1 == v2); 150 | } 151 | } 152 | 153 | static void 154 | test_sort(void) { 155 | Version versions[] = { 156 | {3, 0}, 157 | {4, 1}, 158 | {4, 3}, 159 | {1, 5}, 160 | {1, 4}, 161 | {2, 6}, 162 | {0, 7}, 163 | {2, 8}, 164 | {1, 9}, 165 | {0, 9}, 166 | {0, 9}, 167 | {0, 9}, 168 | {4, 2}, 169 | {0, 9}, 170 | {0, 9}, 171 | {0, 9}, 172 | }; 173 | 174 | DynamicArray *array = darray_new(10); 175 | for (uint32_t i = 0; i < G_N_ELEMENTS(versions); i++) { 176 | darray_add_item(array, &versions[i]); 177 | } 178 | test_single_and_multi_threaded_sort(array, (DynamicArrayCompareDataFunc)sort_version); 179 | } 180 | 181 | static void 182 | test_search(void) { 183 | same_elements(); 184 | } 185 | 186 | int 187 | main(int argc, char *argv[]) { 188 | g_test_init(&argc, &argv, NULL); 189 | g_test_add_func("/FSearch/array/main", test_main); 190 | g_test_add_func("/FSearch/array/sort", test_sort); 191 | g_test_add_func("/FSearch/array/search", test_search); 192 | return g_test_run(); 193 | } 194 | -------------------------------------------------------------------------------- /src/tests/test_size_utils.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | static void 6 | test_parse_size(void) { 7 | typedef struct { 8 | const char *string; 9 | gboolean expected_success; 10 | int64_t expected_size; 11 | int64_t expected_plus; 12 | } FsearchTestSizeParseContext; 13 | 14 | // size factors 15 | const int64_t fkb = 1000; 16 | const int64_t fmb = 1000 * fkb; 17 | const int64_t fgb = 1000 * fmb; 18 | const int64_t ftb = 1000 * fgb; 19 | // expected plus 20 | const int64_t pkb = 1000 - 50 - 1; 21 | const int64_t pmb = fkb * (1000 - 50) - 1; 22 | const int64_t pgb = fmb * (1000 - 50) - 1; 23 | const int64_t ptb = fgb * (1000 - 50) - 1; 24 | 25 | FsearchTestSizeParseContext file_names[] = { 26 | {"abc", FALSE, 0, 0}, 27 | {"mb", FALSE, 0, 0}, 28 | {"0m", TRUE, 0, pmb}, 29 | {"100", TRUE, 100, 0}, 30 | {"100abc", FALSE, 100, 0}, 31 | {"100k", TRUE, 100 * fkb, pkb}, 32 | {"100K", TRUE, 100 * fkb, pkb}, 33 | {"12mb", TRUE, 12 * fmb, pmb}, 34 | {"12Mb", TRUE, 12 * fmb, pmb}, 35 | {"12mB", TRUE, 12 * fmb, pmb}, 36 | {"123MB", TRUE, 123 * fmb, pmb}, 37 | {"1234GB", TRUE, 1234 * fgb, pgb}, 38 | {"12345TB", TRUE, 12345 * ftb, ptb}, 39 | }; 40 | 41 | for (gint i = 0; i < G_N_ELEMENTS(file_names); ++i) { 42 | FsearchTestSizeParseContext *ctx = &file_names[i]; 43 | int64_t size_start = 0; 44 | int64_t size_end = 0; 45 | 46 | gboolean res = fsearch_size_parse(ctx->string, &size_start, &size_end); 47 | g_assert_true(res == ctx->expected_success); 48 | if (res == TRUE) { 49 | g_assert_cmpint(size_start, ==, ctx->expected_size); 50 | g_assert_cmpint(size_end, ==, ctx->expected_size + ctx->expected_plus); 51 | } 52 | } 53 | } 54 | 55 | int 56 | main(int argc, char *argv[]) { 57 | g_test_init(&argc, &argv, NULL); 58 | g_test_add_func("/FSearch/size_utils/parse_size", test_parse_size); 59 | return g_test_run(); 60 | } 61 | -------------------------------------------------------------------------------- /src/tests/test_time_utils.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | static void 6 | test_parse_time_interval(void) { 7 | typedef struct { 8 | const char *string; 9 | gboolean expected_success; 10 | time_t expected_time_start; 11 | time_t expected_time_end; 12 | } FsearchTestTimeIntervalParseContext; 13 | 14 | FsearchTestTimeIntervalParseContext strings[] = { 15 | {"2000abc", FALSE, -1, -1}, 16 | {"abc2000", FALSE, -1, -1}, 17 | {"abc", FALSE, -1, -1}, 18 | 19 | {"today", TRUE, -1, -1}, 20 | {"yesterday", TRUE, -1, -1}, 21 | {"thishour", TRUE, -1, -1}, 22 | {"pastyear", TRUE, -1, -1}, 23 | {"past4year", FALSE, -1, -1}, 24 | {"pastyears", FALSE, -1, -1}, 25 | {"past3years", TRUE, -1, -1}, 26 | {"lastweek", TRUE, -1, -1}, 27 | {"last2weeks", TRUE, -1, -1}, 28 | {"lasttwoweeks", TRUE, -1, -1}, 29 | {"lastweeks", FALSE, -1, -1}, 30 | {"inthelastday", TRUE, -1, -1}, 31 | {"4months", TRUE, -1, -1}, 32 | {"4month", FALSE, -1, -1}, 33 | {"3min", TRUE, -1, -1}, 34 | {"3minutes", TRUE, -1, -1}, 35 | 36 | {"2022", TRUE, -1, -1}, 37 | {"22", TRUE, -1, -1}, 38 | {"2022-01", TRUE, -1, -1}, 39 | {"22-01", TRUE, -1, -1}, 40 | {"22-1", TRUE, -1, -1}, 41 | {"22-1-1", TRUE, -1, -1}, 42 | {"22-1-1 12:00:00", TRUE, -1, -1}, 43 | {"2022-01-01 12:00:00", TRUE, -1, -1}, 44 | {"2022-01-01 12:00", TRUE, -1, -1}, 45 | {"2022-01-01 12", TRUE, -1, -1}, 46 | {"2022-01 12:00:00", FALSE, -1, -1}, 47 | {"1960", FALSE, -1, -1}, 48 | }; 49 | 50 | for (gint i = 0; i < G_N_ELEMENTS(strings); ++i) { 51 | FsearchTestTimeIntervalParseContext *ctx = &strings[i]; 52 | time_t time_start = 0; 53 | time_t time_end = 0; 54 | 55 | gboolean res = fsearch_date_time_parse_interval(ctx->string, &time_start, &time_end); 56 | g_assert_true(res == ctx->expected_success); 57 | if (res == TRUE && ctx->expected_time_start != -1) { 58 | g_assert_cmpint(time_start, ==, ctx->expected_time_start); 59 | g_assert_cmpint(time_end, ==, ctx->expected_time_end); 60 | } 61 | } 62 | } 63 | 64 | int 65 | main(int argc, char *argv[]) { 66 | g_test_init(&argc, &argv, NULL); 67 | g_test_add_func("/FSearch/time_utils/parse_time_interval", test_parse_time_interval); 68 | return g_test_run(); 69 | } 70 | --------------------------------------------------------------------------------