├── .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 |
18 | Ignore case (e.g. searching for "fsearch
" will match "FSearch" as well)
19 | Regular expressions
20 | Wildcard support
21 | Filter support (e.g. only search for audio files)
22 | Exclude certain files and folders
23 | Fast sort by name, path, size, modification time and extension
24 |
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 | 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 | Sequence
26 | Descritpion
27 | Example
28 |
29 |
30 | Double quotes ""
31 | Everything in between ""
is treated literally (like spaces)
32 | "/home/user/my folder" *.pdf → /home/user/my folder and *.pdf
33 |
34 |
35 | Backslash \
36 | Escapes the following character (like space or "
)
37 | /home/user/my\ folder \"quote\".txt → /home/user/my folder
38 | and "quote".txt
39 |
40 |
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 | Keyword
26 | Descritpion
27 | Example
28 |
29 |
30 | case:
31 | Match case
32 | case:Test finds TestDocument.odt , but not testdocument.odt
33 |
34 |
35 | nocase:
36 | Ignore case
37 | nocase:test finds TestDocument.odt and testdocument.odt
38 |
39 |
40 | exact:
41 | Exact match
42 | exact:test finds Test and test , but not testdocument.odt
43 |
44 |
45 | file:
, files:
46 | Match files only
47 | file:test finds a file named test.odt , but won't find the folder testfolder
48 |
49 |
50 | folder:
, folders:
51 | Match folders only
52 | file:test finds a file named test.odt , but won't find the folder testfolder
53 |
54 |
55 | path:
56 | Match the full path
57 | path:home finds a folder named home , and all its children
58 |
59 |
60 | nopath:
61 | Match only the file/folder name
62 |
63 |
64 |
65 | regex:
66 |
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 |
73 | file:regex:".+\.pdf$" finds all files which have the pdf extension
74 |
75 |
76 | noregex:
77 | Disable regular expression
78 |
79 |
80 |
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 | Operator
26 | Keyword
27 | Descritpion
28 |
29 |
30 | AND
31 | space
, AND
, &&
32 | Combines two search terms. Only results that match both search terms are returned.
33 |
34 |
35 | OR
36 | OR
, ||
37 | Combines two search terms. Results that match one or both search terms are returned.
38 |
39 |
40 | NOT
41 | NOT
, !
42 | Negates the following search term.
43 |
44 |
45 | Grouping
46 | (
, )
47 | Search terms can be surrounded by parentheses to group them together.
48 |
49 |
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 | Keyword
26 | Descritpion
27 | Example
28 |
29 |
30 | *
31 | Matches zero or more characters
32 | *.pdf finds document.pdf , but also .pdf
33 |
34 |
35 | ?
36 | Matches exactly one character
37 | k?ng finds king , kong , kung , k1ng , etc.
38 |
39 |
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 |
--------------------------------------------------------------------------------