pax_global_header00006660000000000000000000000064125420150100014500gustar00rootroot0000000000000052 comment=30470ce51c1f8d5b460b45b30e4f0ba5848f06cc j4-dmenu-desktop-r2.13/000077500000000000000000000000001254201501000147015ustar00rootroot00000000000000j4-dmenu-desktop-r2.13/.gitignore000066400000000000000000000001031254201501000166630ustar00rootroot00000000000000build/* CMakeFiles/* Makefile *~ debug/* release/* test/* tests/* j4-dmenu-desktop-r2.13/.travis.yml000066400000000000000000000001211254201501000170040ustar00rootroot00000000000000language: cpp script: - cmake . - make - ./j4-dmenu-tests compiler: - clang j4-dmenu-desktop-r2.13/CHANGELOG000066400000000000000000000030351254201501000161140ustar00rootroot00000000000000 r2.5 released 2013-11-02 + add --display-binary option r2.6 released 2013-11-28 + fixed quoting + different output formats + misc. bug fixes and improvements r2.7 released 2013-12-04 + removed i3 dependency + fixed memory leak + fixed wrong usage of getopt_long() + fixed GCC 4.7 compatibility + misc. bug fixes and improvements r2.8 released 2013-12-09 + various performance improvements + better error messages + added debugging messages + fixed mixup of malloc/new[] + fixed wrong usage of getline() r2.9 released 2014-01-25 + added test suite, travis integration + fixed $XDG_DATA_HOME being ignored + fixed wrong ordering of default search paths + support shell aliases r2.10 released 2014-04-17 + minor changes regarding quoting + change to application path if specified as per XDG spec r2.11 released 2014-04-20 + fixed unhidden desktop files being regarded as hidden r2.12 released 2014-12-14 + fixed quoting issue with localized names + fixed CMakeLists for multithreaded builds + fixed overriding files with Hidden and/or NoDisplay keys + whitespace after "=" is now ignored + implemented desktop file ID concept of the XDG specification (supersedes old "override-by-name" semantics) + added support for OnlyShowIn and NotShowIn tags + added --use-xdg-de flag to interpret $XDG_CURRENT_DESKTOP (for OnlyShowIn/NotShowIn) + temporary files are now created with mkstemp r2.13 released 2015-06-22 + GenericName fields are added to the menu, too + Added manpage j4-dmenu-desktop(1) + Menu is now sorted by names j4-dmenu-desktop-r2.13/CMakeLists.txt000066400000000000000000000037261254201501000174510ustar00rootroot00000000000000cmake_minimum_required(VERSION 2.8) project(j4-dmenu) exec_program( ${CMAKE_CXX_COMPILER} ARGS --version OUTPUT_VARIABLE _compiler_output ) string(REGEX REPLACE "(\n.*$)" "" cxx_compiler_version "${_compiler_output}") string(REGEX REPLACE "([^0-9.])|([0-9.][^0-9.])" "" cxx_compiler_version "${cxx_compiler_version}") if(CMAKE_COMPILER_IS_GNUCXX) if(${cxx_compiler_version} VERSION_LESS "4.7.0") set(CXX_OPT "-std=c++0x") else() set(CXX_OPT "-std=c++11") endif() elseif(${CMAKE_CXX_COMPILER_ID} STREQUAL "Clang") set(CXX_OPT "-std=c++11") endif() set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${CXX_OPT} ${CXX_OPT} -Wall -pedantic -Wextra -O2") set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DDEBUG") if(NOT DEFINED NO_TESTS) include(ExternalProject) ExternalProject_Add( catch PREFIX ${CMAKE_BINARY_DIR}/catch GIT_REPOSITORY https://github.com/philsquared/Catch.git TIMEOUT 10 UPDATE_COMMAND git pull CONFIGURE_COMMAND "" BUILD_COMMAND "" INSTALL_COMMAND "" LOG_DOWNLOAD ON ) # Expose required variable (CATCH_INCLUDE_DIR) to parent scope ExternalProject_Get_Property(catch source_dir) set(CATCH_INCLUDE_DIR ${source_dir}/include CACHE INTERNAL "Path to include folder for Catch") # Includes Catch in the project: #add_subdirectory(${EXT_PROJECTS_DIR}/catch) include_directories(${CATCH_INCLUDE_DIR} ${COMMON_INCLUDES}) add_definitions(-DTEST_FILES="${CMAKE_CURRENT_SOURCE_DIR}/test_files/") enable_testing(true) add_test( NAME j4-dmenu-tests COMMAND j4-dmenu-tests ) add_executable( j4-dmenu-tests src/Test.cc src/TestApplication.cc src/TestApplicationRunner.cc src/TestSearchPath.cc src/TestLocaleSuffixes.cc src/TestFileFinder.cc src/TestFormatters.cc ) add_dependencies(j4-dmenu-tests catch) endif(NOT DEFINED NO_TESTS) add_executable( j4-dmenu-desktop src/main.cc ) install ( TARGETS j4-dmenu-desktop RUNTIME DESTINATION bin ) j4-dmenu-desktop-r2.13/LICENSE000066400000000000000000000013231254201501000157050ustar00rootroot00000000000000j4-dmenu-desktop, a i3-dmenu-desktop replacement Copyright (C) 2013 enkore This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see .j4-dmenu-desktop-r2.13/README.md000066400000000000000000000044731254201501000161700ustar00rootroot00000000000000# j4-dmenu-desktop [![Build Status](http://golem.enkore.de/job/j4-dmenu-desktop-dev/badge/icon)](http://golem.enkore.de/job/j4-dmenu-desktop-dev/) j4-dmenu-desktop is a replacement for i3-dmenu-desktop. It's purpose is to find .desktop files and offer you a menu to start an application using dmenu. Since r2.7 j4-dmenu-desktop doesn't require i3wm anymore and should work just fine on about any desktop environment. You can also execute shell commands using it. ## Build requirements * Compiler with basic C++11 support (GCC 4.77 or later required, Clang works, too) * CMake Building is the usual cmake/make thingy: cmake . make sudo make install ## Distribution packages ### Archlinux The package is provided by the AUR. You can install it with an AUR helper of your choice: `j4-dmenu-desktop-git` or manually by invoking the following commands as a regular user. (to build packages from the AUR, the `base-devel` package group is assumed to be installed) wget https://aur.archlinux.org/packages/j4/j4-dmenu-desktop-git/j4-dmenu-desktop-git.tar.gz tar xf j4-dmenu-desktop-git.tar.gz cd j4-dmenu-desktop-git makepkg -si ### Gentoo The package is provided by the `gentoo-el` overlay. You can install it with the following commands as root. (you need to have `layman` installed and configured) layman -a gentoo-el echo "=x11-misc/j4-dmenu-desktop-9999 **" >> /etc/portage/package.accept_keywords emerge x11-misc/j4-dmenu-desktop ## Invocation Usage: j4-dmenu-desktop [--dmenu="dmenu -i"] [--term="i3-sensible-terminal"] j4-dmenu-desktop --help Options: --dmenu= Determines the command used to invoke dmenu Executed with your shell ($SHELL) or /bin/sh --display-binary Display binary name after each entry (off by default) --term= Sets the terminal emulator used to start terminal apps --help Display this help message ## FAQ ### Case insensitivity? Add the `-i` option to the dmenu command ### How much faster is it? % time i3-dmenu-desktop --dmenu="cat" [{"success":true}] i3-dmenu-desktop --dmenu="cat" 0.37s user 0.02s system 96% cpu 0.404 total % time ./j4-dmenu-desktop --dmenu=cat ./j4-dmenu-desktop --dmenu=cat 0.01s user 0.01s system 107% cpu 0.015 total More than 25 times faster :) j4-dmenu-desktop-r2.13/iwyu-driver000077500000000000000000000002221254201501000171110ustar00rootroot00000000000000#!/bin/sh IWYU_COMMAND=$(which include-what-you-use) COMPILER_COMMAND=/usr/bin/g++ $COMPILER_COMMAND $@ STATUS=$? $IWYU_COMMAND $@ exit $STATUS j4-dmenu-desktop-r2.13/j4-dmenu-desktop.1000066400000000000000000000031001254201501000200470ustar00rootroot00000000000000.TH J4\-DMENU\-DESKTOP 1 .SH NAME j4-dmenu-desktop \- faster replacement for i3\-dmenu\-desktop .SH DESCRIPTION j4\-dmenu\-desktop is a faster replacement for i3-dmenu-desktop. It's purpose is to find .desktop files and offer you a menu to start an application using dmenu. .SH "SYNOPSIS" .IP \fBj4\-dmenu\-desktop\fR [\fB\-\-dmenu\fR="dmenu \-i"] [\fB\-\-term\fR="i3\-sensible\-terminal"] .SH OPTIONS .IP \fB\-\-dmenu=\fR Determines the command used to invoke dmenu Executed with your shell ($SHELL) or \fI\,/bin/sh\/\fP .IP \fB\-\-use\-xdg\-de\fR Enables reading $XDG_CURRENT_DESKTOP to determine the desktop environment .IP \fB\-\-display\-binary\fR Display binary name after each entry (off by default) .IP \fB\-\-term=\fR Sets the terminal emulator used to start terminal apps .IP \fB\-\-help\fR Display this help message .SH "SEE ALSO" https://github.com/enkore/j4\-dmenu\-desktop .SH COPYRIGHT Copyright (C) 2013 enkore This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . j4-dmenu-desktop-r2.13/src/000077500000000000000000000000001254201501000154705ustar00rootroot00000000000000j4-dmenu-desktop-r2.13/src/Application.hh000066400000000000000000000161341254201501000202610ustar00rootroot00000000000000// // This file is part of j4-dmenu-desktop. // // j4-dmenu-desktop is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // j4-dmenu-desktop is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with j4-dmenu-desktop. If not, see . // #ifndef APPLICATION_DEF #define APPLICATION_DEF #include #include #include #include "Utilities.hh" #include "LocaleSuffixes.hh" namespace ApplicationHelpers { static inline constexpr uint32_t make_istring(const char *s) { return s[0] | s[1] << 8 | s[2] << 16 | s[3] << 24; } constexpr uint32_t operator "" _istr(const char *s, size_t) { return make_istring(s); } } class Application { public: explicit Application(const LocaleSuffixes &locale_suffixes, const stringlist_t *environment = 0) : locale_suffixes(locale_suffixes), environment(environment) { } // Localized name std::string name; // Generic name std::string generic_name; // Command line std::string exec; // Path std::string path; // Terminal app bool terminal = false; // file id std::string id; bool read(const char *filename, char **linep, size_t *linesz) { using namespace ApplicationHelpers; // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! // !! The code below is extremely hacky. But fast. !! // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! // // Please don't try this at home. //Whether the app should be hidden bool hidden = false; std::string fallback_name, fallback_generic_name; size_t locale_length = 0, locale_generic_length = 0; bool parse_key_values = false; ssize_t linelen; char *line; FILE *file = fopen(filename, "r"); if(!file) { char *pwd = new char[100]; int error = errno; getcwd(pwd, 100); fprintf(stderr, "%s/%s: %s\n", pwd, filename, strerror(error)); delete[] pwd; return false; } #ifdef DEBUG char *pwd = new char[100]; getcwd(pwd, 100); fprintf(stderr, "%s/%s -> ", pwd, filename); delete[] pwd; #endif id = filename + 2; // our internal filenames all start with './' std::replace(id.begin(), id.end(), '/', '-'); while((linelen = getline(linep, linesz, file)) != -1) { line = *linep; line[--linelen] = 0; // Chop off \n // Blank line or comment if(!linelen || line[0] == '#') continue; if(parse_key_values) { // Desktop Entry section ended (b/c another section starts) if(line[0] == '[') break; // Split that string in place char *key=line, *value=strchr(line, '='); if (!value) { fprintf(stderr, "%s: malformed file, ignoring\n", filename); fclose(file); return false; } (value++)[0] = 0; // Overwrite = with NUL (terminate key) //Cut spaces after the equal sign while(value[0] == ' ') ++value; switch(make_istring(key)) { case "Name"_istr: parse_localestring(key, 4, &locale_length, value, this->name, fallback_name); continue; case "GenericName"_istr: parse_localestring(key, 11, &locale_generic_length, value, this->generic_name, fallback_generic_name); continue; case "Exec"_istr: this->exec = value; break; case "Path"_istr: this->path= value; break; case "OnlyShowIn"_istr: if(environment) { stringlist_t values; split(std::string(value), ';', values); if(!have_equal_element(*environment, values)) { hidden = true; #ifdef DEBUG fprintf(stderr, "OnlyShowIn: %s -> app is hidden\n", value); #endif } } break; case "NotShowIn"_istr: if(environment) { stringlist_t values; split(std::string(value), ';', values); if(have_equal_element(*environment, values)) { hidden = true; #ifdef DEBUG fprintf(stderr, "NotShowIn: %s -> app is hidden\n", value); #endif } } break; case "Hidden"_istr: case "NoDisplay"_istr: if(value[0] == 't'){ // true #ifdef DEBUG fprintf(stderr, "NoDisplay/Hidden\n"); #endif hidden = true; } break; case "Terminal"_istr: this->terminal = make_istring(value) == "true"_istr; break; } } else if(!strcmp(line, "[Desktop Entry]")) parse_key_values = true; } if(!this->name.size()) this->name = fallback_name; #ifdef DEBUG fprintf(stderr, "%s\n", this->name.c_str()); #endif if(!this->generic_name.size()) this->generic_name = fallback_generic_name; #ifdef DEBUG fprintf(stderr, "%s\n", this->generic_name.c_str()); #endif fclose(file); if(hidden) return false; return true; } private: const LocaleSuffixes &locale_suffixes; const stringlist_t *environment; void parse_localestring(const char *key, size_t key_length, size_t *best_so_far, const char *value, std::string &field, std::string &fallback) { if(key[key_length] == '[') { // Don't ask, don't tell. const char *langcode = key + key_length + 1; // plus the [ const char *suffix; const size_t length = strlen(langcode) - 1; // minus the ] if(length < *best_so_far) { return; } int i = 0; while((suffix = this->locale_suffixes.suffixes[i++])) { if(!strncmp(suffix, langcode, length)) { #ifdef DEBUG fprintf(stderr, "[%s] ", suffix); #endif *best_so_far = length; field = value; } } } else { fallback = value; } } }; #endif j4-dmenu-desktop-r2.13/src/ApplicationRunner.hh000066400000000000000000000102701254201501000214460ustar00rootroot00000000000000// // This file is part of j4-dmenu-desktop. // // j4-dmenu-desktop is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // j4-dmenu-desktop is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with j4-dmenu-desktop. If not, see . // #ifndef APPLICATIONRUNNER_DEF #define APPLICATIONRUNNER_DEF #include #include #include #include #include "Utilities.hh" class ApplicationRunner { public: ApplicationRunner(const std::string &terminal_emulator, const Application &app, const std::string &args) : app(app), args(args), terminal_emulator(terminal_emulator) { } const std::string command() { std::string exec = this->application_command(); const std::string &name = this->app.name; std::stringstream command; puts(exec.c_str()); if(this->app.terminal) { // Execute in terminal int fd; FILE *script; char scriptname[] = "/tmp/j4-dmenu-XXXXXX"; if((fd = mkstemp(scriptname)) == -1) { std::cerr << "error creating temporary file in /tmp/: " << strerror(errno) << std::endl; return std::string(); } if((script = fdopen(fd, "w")) == 0) { std::cerr << "error creating temporary file in /tmp/: " << strerror(errno) << std::endl; return std::string(); } fprintf(script, "#!/bin/sh\n"); fprintf(script, "rm %s\n", scriptname); fprintf(script, "echo -n \"\\033]2;%s\\007\"\n", name.c_str()); fprintf(script, "echo -ne \"\\033]2;%s\\007\"\n", name.c_str()); fprintf(script, "exec %s", exec.c_str()); //closes also fd if(fclose(script) != 0) { std::cerr << "error closing temporary file " << scriptname << ": " << strerror(errno) << std::endl; std::cerr << "The file will not be deleted automatically" << std::endl; return std::string(); } //As mkstemp sets the file permissions to 0600, we need to set it to 0700 to execute the script chmod(scriptname, S_IRWXU); command << this->terminal_emulator; command << " -e \"" << scriptname << "\""; } else { command << exec; }; return command.str(); } private: static std::string quote(const std::string &s) { std::string string(s); replace(string, "\"", "\\\""); replace(string, "\\", "\\\\"); return string; } const std::string application_command() { std::string exec(this->app.exec); // Undo quoting before expanding field codes replace(exec, "\\\\(", "\\("); replace(exec, "\\\\)", "\\)"); replace(exec, "\\\\ ", "\\ "); replace(exec, "\\\\`", "\\`"); replace(exec, "\\\\$", "\\$"); replace(exec, "\\\\\"", "\\\""); replace(exec, "\\\\\\\\", "\\\\"); // Replace filename field codes with the rest of the command line. replace(exec, "%f", this->args); replace(exec, "%F", this->args); // If the program works with URLs, // we assume the user provided a URL instead of a filename. // As per the spec, there must be at most one of %f, %u, %F or %U present. replace(exec, "%u", this->args); replace(exec, "%U", this->args); // The localized name of the application replace(exec, "%c", "\"" + quote(this->app.name) + "\""); replace(exec, "%k", ""); replace(exec, "%i", ""); replace(exec, "%%", "%"); return exec; } const Application &app; std::string args; const std::string &terminal_emulator; }; #endif j4-dmenu-desktop-r2.13/src/Applications.hh000066400000000000000000000032351254201501000204420ustar00rootroot00000000000000 #ifndef APPLICATIONS_DEF #define APPLICATIONS_DEF #include #include "Application.hh" class Applications : public std::map { public: ~Applications() { for(auto app : *this) { delete app.second; } } std::pair find(const std::string &choice) { Application *app = 0; std::string args; size_t match_length = 0; // Find longest match amongst apps for(auto ¤t_app : *this) { const std::string &name = current_app.second->name; if(name.size() > match_length && startswith(choice, name)) { app = current_app.second; match_length = name.length(); } const std::string &generic_name = current_app.second->generic_name; if(generic_name.size() > match_length && startswith(choice, generic_name)) { app = current_app.second; match_length = generic_name.length(); } } if(!match_length) { // No matching app found, just execute the input in a shell const char *shell = 0; if((shell = getenv("SHELL")) == 0) shell = "/bin/sh"; fprintf(stderr, "%s -i -c '%s'\n", shell, choice.c_str()); // -i -c was tested with both bash and zsh. exit(execl(shell, shell, "-i", "-c", choice.c_str(), 0)); } // +1 b/c there must be whitespace we add back later... args = choice.substr(match_length, choice.length()-1); //args = choice; return std::make_pair(app, args); } }; #endif j4-dmenu-desktop-r2.13/src/Dmenu.hh000066400000000000000000000036231254201501000170650ustar00rootroot00000000000000 #include #include #include #include #include #include class Dmenu { public: Dmenu(const std::string &dmenu_command) : dmenu_command(dmenu_command), pid(0) { this->create(); } void write(const std::string &what) { ::write(this->outpipe[1], what.c_str(), what.size()); ::write(this->outpipe[1], "\n", 1); } void display() { // Closing the pipe produces EOF for dmenu, signalling // end of all options. dmenu shows now up on the screen // (if -f hasn't been used) close(this->outpipe[1]); } std::string read_choice() { std::string choice; std::getline(std::cin, choice); int status=0; waitpid(this->pid, &status, 0); return choice; } private: int create() { // Create the dmenu as soon as we know the command, // this speeds up things a bit if the -f flag for dmenu is // used if(pipe(this->inpipe) == -1 || pipe(this->outpipe) == -1) throw std::runtime_error("Dmenu::create(): pipe() failed"); this->pid = fork(); switch(this->pid) { case -1: throw std::runtime_error("Dmenu::create(): fork() failed"); case 0: close(this->inpipe[0]); close(this->outpipe[1]); dup2(this->inpipe[1], STDOUT_FILENO); dup2(this->outpipe[0], STDIN_FILENO); static const char *shell = 0; if((shell = getenv("SHELL")) == 0) shell = "/bin/sh"; return execl(shell, shell, "-c", this->dmenu_command.c_str(), 0); } close(this->inpipe[1]); close(this->outpipe[0]); dup2(this->inpipe[0], STDIN_FILENO); return true; } const std::string &dmenu_command; int inpipe[2]; int outpipe[2]; int pid; }; j4-dmenu-desktop-r2.13/src/FileFinder.hh000066400000000000000000000035061254201501000200240ustar00rootroot00000000000000#ifndef FILEFINDER_DEF #define FILEFINDER_DEF #include #include #include #include #include #include "Utilities.hh" class FileFinder { public: typedef std::input_iterator_tag iterator_category; FileFinder(const std::string &path, const std::string &suffix) : done(false), dir(NULL), suffix(suffix) { dirstack.push(path); } operator bool() const { return !done; } const std::string &operator*() const { return curpath; } const std::string *operator->() const { return &curpath; } FileFinder& operator++(int) { if(!dir) opendir(); if(done) return *this; dirent *entry = readdir(dir); if(!entry) { closedir(dir); dir = NULL; // If you think of this as a loop then this is a continue return (*this)++; } if(entry->d_name[0] == '.') { // Exclude ., .. and hidden files return (*this)++; } std::string fullpath(curdir + entry->d_name); if(is_directory(fullpath)) { dirstack.push(fullpath + "/"); return (*this)++; } if(endswith(fullpath, suffix)) { curpath = fullpath; return *this; } return (*this)++; } private: void opendir() { if(dirstack.empty()) { done = true; return; } curdir = dirstack.top(); dirstack.pop(); dir = ::opendir(curdir.c_str()); if(!dir) throw std::runtime_error(curdir + ": opendir() failed"); } bool done; DIR *dir; std::stack dirstack; const std::string suffix; std::string curpath; std::string curdir; }; #endif j4-dmenu-desktop-r2.13/src/Formatters.hh000066400000000000000000000024531254201501000201430ustar00rootroot00000000000000// // This file is part of j4-dmenu-desktop. // // j4-dmenu-desktop is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // j4-dmenu-desktop is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with j4-dmenu-desktop. If not, see . // #ifndef DESKTOP_DEF #define DESKTOP_DEF #include #include #include #include "Application.hh" typedef std::function application_formatter; inline std::string appformatter_default(const Application &app) { return app.name; } inline std::string appformatter_with_binary_name(const Application &app) { return app.name + " (" + split(app.exec, " ").first + ")"; } static const std::map formatters { {"standard", appformatter_default}, {"with_binary_name", appformatter_with_binary_name} }; #endif j4-dmenu-desktop-r2.13/src/LocaleSuffixes.hh000066400000000000000000000045731254201501000207360ustar00rootroot00000000000000// // This file is part of j4-dmenu-desktop. // // j4-dmenu-desktop is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // j4-dmenu-desktop is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with j4-dmenu-desktop. If not, see . // #ifndef LOCALE_DEF #define LOCALE_DEF #include #include #include #include class LocaleSuffixes { public: LocaleSuffixes() { generate(locale()); } LocaleSuffixes(const std::string &force_locale) { generate(force_locale); } ~LocaleSuffixes() { int i = 0; while(this->suffixes[i]) free(this->suffixes[i++]); delete[] this->suffixes; } char **suffixes; int count; private: std::string locale() { return setlocale(LC_MESSAGES, ""); } void generate(std::string locale) { std::set suffixset; size_t dotpos = locale.find("."); size_t atpos = locale.find("@") != std::string::npos ? locale.find("@") : locale.length(); size_t uscorepos = locale.find("_"); // Strip encoding if(dotpos < atpos) { locale = locale.substr(0, dotpos) + locale.substr(atpos, locale.length()); atpos = locale.find("@") != std::string::npos ? locale.find("@") : locale.length(); } suffixset.insert(locale); if(uscorepos < atpos && atpos != std::string::npos) { suffixset.insert(locale.substr(0, atpos)); suffixset.insert((locale.substr(0, uscorepos) + locale.substr(atpos, locale.length()))); } this->suffixes = new char*[suffixset.size()+1]; this->count = suffixset.size(); int i = 0; for(auto &suffix : suffixset) { this->suffixes[i++] = strdup(suffix.c_str()); #ifdef DEBUG fprintf(stderr, "LocaleSuffix: %s\n", this->suffixes[i-1]); #endif } this->suffixes[i++] = 0; } }; #endif j4-dmenu-desktop-r2.13/src/Main.hh000066400000000000000000000165771254201501000167150ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include #include #include "Utilities.hh" #include "Dmenu.hh" #include "Application.hh" #include "ApplicationRunner.hh" #include "Applications.hh" #include "SearchPath.hh" #include "FileFinder.hh" #include "Formatters.hh" class Main { public: Main() : dmenu_command("dmenu -i"), terminal("i3-sensible-terminal") { } int main(int argc, char **argv) { if(read_args(argc, argv)) return 0; if(use_xdg_de) { std::string env_var = get_variable("XDG_CURRENT_DESKTOP"); //XDG_CURRENT_DESKTOP can contain multiple environments separated by colons split(env_var, ':', environment); if(environment.empty()) use_xdg_de = false; } #ifdef DEBUG fprintf(stderr, "desktop environment:\n"); for(auto s: environment) fprintf(stderr, "%s\n", s.c_str()); #endif this->dmenu = new Dmenu(this->dmenu_command); collect_files(); // Sort applications by displayed name std::vector> iteration_order; iteration_order.reserve(apps.size()); for(auto &app : apps) { iteration_order.push_back({app.first, app.second}); } std::locale locale(""); std::sort(iteration_order.begin(), iteration_order.end(), [locale]( const std::pair &s1, const std::pair &s2) { return locale(s1.second->name, s2.second->name); }); // Transfer the list to dmenu for(auto &app : iteration_order) { this->dmenu->write(app.second->name); const std::string &generic_name = app.second->generic_name; if(generic_name.size() && app.second->name != generic_name) this->dmenu->write(generic_name); } this->dmenu->display(); std::string command = get_command(); delete this->dmenu; if(command.size()) { static const char *shell = 0; if((shell = getenv("SHELL")) == 0) shell = "/bin/sh"; fprintf(stderr, "%s -i -c '%s'\n", shell, command.c_str()); return execl(shell, shell, "-i", "-c", command.c_str(), 0); } return 0; } private: void print_usage(FILE* f) { fprintf(f, "j4-dmenu-desktop\n" "A faster replacement for i3-dmenu-desktop\n" "Copyright (c) 2013 Marian Beermann, GPLv3 license\n" "\nUsage:\n" "\tj4-dmenu-desktop [--dmenu=\"dmenu -i\"] [--term=\"i3-sensible-terminal\"]\n" "\tj4-dmenu-desktop --help\n" "\nOptions:\n" " --dmenu=\n" "\tDetermines the command used to invoke dmenu\n" "\tExecuted with your shell ($SHELL) or /bin/sh\n" " --use-xdg-de\n" "\tEnables reading $XDG_CURRENT_DESKTOP to determine the desktop environment\n" " --display-binary\n" "\tDisplay binary name after each entry (off by default)\n" " --term=\n" "\tSets the terminal emulator used to start terminal apps\n" " --help\n" "\tDisplay this help message\n" ); } bool read_args(int argc, char **argv) { std::string formatter = "standard"; while (true) { int option_index = 0; static struct option long_options[] = { {"dmenu", required_argument, 0, 'd'}, {"use-xdg-de", no_argument, 0, 'x'}, {"term", required_argument, 0, 't'}, {"help", no_argument, 0, 'h'}, {"display-binary", no_argument, 0, 'b'}, {0, 0, 0, 0} }; int c = getopt_long(argc, argv, "d:t:xhb", long_options, &option_index); if(c == -1) break; switch (c) { case 'd': this->dmenu_command = optarg; break; case 'x': use_xdg_de = true; break; case 't': this->terminal = optarg; break; case 'h': this->print_usage(stderr); return true; case 'b': formatter = "with_binary_name"; break; default: exit(1); } } if(!formatters.count(formatter)) { fprintf(stderr, "Formatter '%s' not found", formatter.c_str()); exit(1); } this->appformatter = formatters.at(formatter); return false; } void collect_files() { // We switch the working directory to easier get relative paths // This way desktop files that are customized in more important directories // (like $XDG_DATA_HOME/applications/) overwrite those found in system-wide // directories char original_wd[384]; getcwd(original_wd, 384); // Allocating the line buffer just once saves lots of MM calls // malloc required to avoid mixing malloc/new[] as getdelim may realloc() buf buf = static_cast(malloc(bufsz)); buf[0] = 0; for(auto &path : this->search_path) { chdir(path.c_str()); FileFinder finder("./", ".desktop"); while(finder++) { handle_file(*finder); } } free(buf); chdir(original_wd); } void handle_file(const std::string &file) { Application *dft = new Application(suffixes, use_xdg_de ? &environment : 0); bool file_read = dft->read(file.c_str(), &buf, &bufsz); dft->name = this->appformatter(*dft); if(file_read && dft->name.size()) { if(apps.count(dft->id)) { delete apps[dft->id]; } apps[dft->id] = dft; } else { if(dft->id.size()) { delete apps[dft->id]; apps.erase(dft->id); } delete dft; } parsed_files++; } std::string get_command() { std::string choice; std::string args; Application *app; printf("Read %d .desktop files, found %lu apps.\n", parsed_files, apps.size()); choice = dmenu->read_choice(); // Blocks if(!choice.size()) return ""; fprintf(stderr, "User input is: %s %s\n", choice.c_str(), args.c_str()); std::tie(app, args) = apps.find(choice); if(app->path.size()) chdir(app->path.c_str()); ApplicationRunner app_runner(terminal, *app, args); return app_runner.command(); } private: std::string dmenu_command; std::string terminal; stringlist_t environment; bool use_xdg_de = false; Dmenu *dmenu = 0; SearchPath search_path; int parsed_files = 0; Applications apps; char *buf = 0; size_t bufsz = 4096; LocaleSuffixes suffixes; application_formatter appformatter; }; j4-dmenu-desktop-r2.13/src/SearchPath.hh000066400000000000000000000042401254201501000200330ustar00rootroot00000000000000// // This file is part of j4-dmenu-desktop. // // j4-dmenu-desktop is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // j4-dmenu-desktop is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with j4-dmenu-desktop. If not, see . // #ifndef SEARCHPATH_DEF #define SEARCHPATH_DEF #include "Utilities.hh" class SearchPath { public: SearchPath() { generate(); } const stringlist_t::iterator begin() { return this->search_path.begin(); } const stringlist_t::iterator end() { return this->search_path.end(); } private: void generate() { stringlist_t sp; std::string xdg_data_home = get_variable("XDG_DATA_HOME"); if(xdg_data_home.empty()) xdg_data_home = std::string(get_variable("HOME")) + "/.local/share/"; push_var(xdg_data_home, sp); std::string xdg_data_dirs = get_variable("XDG_DATA_DIRS"); if(xdg_data_dirs.empty()) xdg_data_dirs = "/usr/share/:/usr/local/share/"; push_var(xdg_data_dirs, sp); sp.reverse(); // Fix double slashes, if any for(auto path : sp) { this->search_path.push_back(replace(path, "//", "/")); #ifdef DEBUG fprintf(stderr, "SearchPath: %s\n", this->search_path.back().c_str()); #endif } } void push_var(const std::string &string, stringlist_t &sp) { stringlist_t items; split(string, ':', items); items.reverse(); for(auto path : items) { if(!endswith(path, "/applications/")) path += "/applications/"; if(is_directory(path.c_str())) sp.push_back(path); } } stringlist_t search_path; }; #endif j4-dmenu-desktop-r2.13/src/Test.cc000066400000000000000000000001151254201501000167130ustar00rootroot00000000000000 //#include "Application.hh" #define CATCH_CONFIG_MAIN #include "catch.hpp" j4-dmenu-desktop-r2.13/src/TestApplication.cc000066400000000000000000000130231254201501000211010ustar00rootroot00000000000000 #include #include #include #include "Application.hh" #include "LocaleSuffixes.hh" #include "catch.hpp" static const std::string test_files = TEST_FILES; TEST_CASE("Application/invalid_file", "") { LocaleSuffixes ls("en_US"); Application app(ls); REQUIRE( !app.read("some-file-that-doesnt-exist", NULL, 0) ); } TEST_CASE("Application/valid/eagle", "Validates correct parsing of a simple file") { LocaleSuffixes ls("en_US"); Application app(ls); char *buffer = static_cast(malloc(4096)); size_t size = 4096; std::string path(test_files + "applications/eagle.desktop"); REQUIRE( app.read(path.c_str(), &buffer, &size) ); REQUIRE( app.name == "Eagle" ); REQUIRE( app.exec == "eagle -style plastique" ); REQUIRE( !app.terminal ); free(buffer); } TEST_CASE("Application/valid/htop", "Similar to Application/valid/eagle, just for a term app") { LocaleSuffixes ls("en_US"); Application app(ls); char *buffer = static_cast(malloc(4096)); size_t size = 4096; std::string path(test_files + "applications/htop.desktop"); REQUIRE( app.read(path.c_str(), &buffer, &size) ); REQUIRE( app.name == "Htop" ); REQUIRE( app.exec == "htop" ); REQUIRE( app.terminal ); free(buffer); } TEST_CASE("Application/valid/gimp", "Tests correct parsing of localization and stuff") { LocaleSuffixes ls("eo"); Application app(ls); char *buffer = static_cast(malloc(4096)); size_t size = 4096; std::string path(test_files + "applications/gimp.desktop"); REQUIRE( app.read(path.c_str(), &buffer, &size) ); REQUIRE( app.name == "Bildmanipulilo (GIMP = GNU Image Manipulation Program)" ); REQUIRE( app.generic_name == "Bildredaktilo" ); REQUIRE( app.exec == "gimp-2.8 %U" ); REQUIRE( !app.terminal ); free(buffer); } TEST_CASE("Application/valid/long_line", "Tests correct parsing of file with line longer than buffer") { LocaleSuffixes ls("eo"); Application app(ls); size_t size = 20; char *buffer = static_cast(malloc(20)); std::string path(test_files + "applications/gimp.desktop"); REQUIRE( app.read(path.c_str(), &buffer, &size) ); REQUIRE( app.name == "Bildmanipulilo (GIMP = GNU Image Manipulation Program)" ); REQUIRE( app.exec == "gimp-2.8 %U" ); REQUIRE( !app.terminal ); REQUIRE( size > 20 ); free(buffer); } TEST_CASE("Application/flag/hidden=false", "Regression test for issue #17, Hidden=false was read as Hidden=true") { LocaleSuffixes ls("en_US"); Application app(ls); char *buffer = static_cast(malloc(4096)); size_t size = 4096; std::string path(test_files + "applications/visible.desktop"); REQUIRE( app.read(path.c_str(), &buffer, &size) ); REQUIRE( app.name == "visibleApp" ); REQUIRE( app.exec == "visibleApp" ); free(buffer); } TEST_CASE("Application/flag/hidden=true", "Test for an issue where the name wasn't set correctly after reading a hidden file") { LocaleSuffixes ls("en_US"); Application app(ls); char *buffer = static_cast(malloc(4096)); size_t size = 4096; std::string path(test_files + "applications/hidden.desktop"); REQUIRE(!app.read(path.c_str(), &buffer, &size) ); REQUIRE( app.name == "hiddenApp" ); REQUIRE( app.exec == "hiddenApp" ); free(buffer); } TEST_CASE("Application/spaces_after_equals", "Test whether spaces after the equal sign are ignored") { LocaleSuffixes ls("en_US"); Application app(ls); char *buffer = static_cast(malloc(4096)); size_t size = 4096; std::string path(test_files + "applications/whitespaces.desktop"); REQUIRE( app.read(path.c_str(), &buffer, &size) ); //It should be "Htop", not " Htop" REQUIRE( app.name == "Htop" ); REQUIRE( app.generic_name == "Process Viewer" ); free(buffer); } TEST_CASE("Application/onlyShowIn", "Test whether the OnlyShowIn tag works") { stringlist_t env; LocaleSuffixes ls("en_US"); char *buffer = static_cast(malloc(4096)); size_t size = 4096; std::string path(test_files + "applications/onlyShowIn.desktop"); //Case 1: The app should be shown in this environment env.emplace_back("i3"); env.emplace_back("Gnome"); Application app(ls, &env); REQUIRE( app.read(path.c_str(), &buffer, &size)); REQUIRE( app.name == "Htop" ); REQUIRE( app.generic_name == "Process Viewer" ); //Case 2: The app should not be shown in this environment env.clear(); env.emplace_back("Kde"); Application app2(ls, &env); REQUIRE(!app2.read(path.c_str(), &buffer, &size)); REQUIRE( app2.name == "Htop"); REQUIRE( app2.generic_name == "Process Viewer" ); free(buffer); } TEST_CASE("Application/notShowIn", "Test whether the NotShowIn tag works") { stringlist_t env; LocaleSuffixes ls("en_US"); char *buffer = static_cast(malloc(4096)); size_t size = 4096; std::string path(test_files + "applications/notShowIn.desktop"); //Case 1: The app should be hidden env.emplace_back("i3"); env.emplace_back("Gnome"); Application app(ls, &env); REQUIRE(!app.read(path.c_str(), &buffer, &size)); REQUIRE( app.name == "Htop" ); REQUIRE( app.generic_name == "Process Viewer" ); //Case 2: The app should be shown in this environment env.clear(); env.emplace_back("Gnome"); Application app2(ls, &env); REQUIRE( app2.read(path.c_str(), &buffer, &size)); REQUIRE( app2.name == "Htop"); REQUIRE( app2.generic_name == "Process Viewer" ); free(buffer); } j4-dmenu-desktop-r2.13/src/TestApplicationRunner.cc000066400000000000000000000007331254201501000222770ustar00rootroot00000000000000#include #include "Application.hh" #include "ApplicationRunner.hh" #include "LocaleSuffixes.hh" #include "catch.hpp" TEST_CASE("ApplicationRunner/escape", "Regression test for issue #18, %c was not escaped") { LocaleSuffixes ls("en_US"); Application app(ls); app.name = "Regression Test 18"; app.exec = "1234 --caption %c"; ApplicationRunner runner("", app, ""); REQUIRE( runner.command() == "1234 --caption \"Regression Test 18\"" ); } j4-dmenu-desktop-r2.13/src/TestFileFinder.cc000066400000000000000000000010651254201501000206500ustar00rootroot00000000000000#include #include #include #include "FileFinder.hh" #include "catch.hpp" static const std::string test_files = TEST_FILES; std::vector ff2l(FileFinder &ff) { std::list l; while (ff++) l.push_back(*ff); l.sort(); // Deterministic ordering return std::vector(l.begin(), l.end());; } TEST_CASE("FileFinder/find2", "") { FileFinder ff(TEST_FILES, ".ext"); auto files = ff2l(ff); REQUIRE( files.size() == 1 ); REQUIRE( files[0] == test_files + "other.ext" ); } j4-dmenu-desktop-r2.13/src/TestFormatters.cc000066400000000000000000000017141254201501000207700ustar00rootroot00000000000000 #include #include #include #include "catch.hpp" #include "Application.hh" #include "LocaleSuffixes.hh" #include "Formatters.hh" static const std::string test_files = TEST_FILES; TEST_CASE("Formatters/standard", "") { LocaleSuffixes ls("en_US"); Application app(ls); char *buffer = static_cast(malloc(4096)); size_t size = 4096; std::string path(test_files + "applications/eagle.desktop"); REQUIRE( app.read(path.c_str(), &buffer, &size) ); REQUIRE( appformatter_default(app) == "Eagle" ); free(buffer); } TEST_CASE("Formatters/with_binary", "") { LocaleSuffixes ls("en_US"); Application app(ls); char *buffer = static_cast(malloc(4096)); size_t size = 4096; std::string path(test_files + "applications/eagle.desktop"); REQUIRE( app.read(path.c_str(), &buffer, &size) ); REQUIRE( appformatter_with_binary_name(app) == "Eagle (eagle)" ); free(buffer); } j4-dmenu-desktop-r2.13/src/TestLocaleSuffixes.cc000066400000000000000000000012141254201501000215510ustar00rootroot00000000000000#include #include "LocaleSuffixes.hh" #include "catch.hpp" TEST_CASE("LocaleSuffixes/with_encoding", "") { LocaleSuffixes ls("en_US.UTF-8"); REQUIRE( strcmp(ls.suffixes[0], "en") == 0 ); REQUIRE( strcmp(ls.suffixes[1], "en_US") == 0 ); REQUIRE( ls.count == 2 ); } TEST_CASE("LocaleSuffixes/long", "") { LocaleSuffixes ls("en_US"); REQUIRE( strcmp(ls.suffixes[0], "en") == 0 ); REQUIRE( strcmp(ls.suffixes[1], "en_US") == 0 ); REQUIRE( ls.count == 2 ); } TEST_CASE("LocaleSuffixes/short", "") { LocaleSuffixes ls("en"); REQUIRE( strcmp(ls.suffixes[0], "en") == 0 ); REQUIRE( ls.count == 1 ); } j4-dmenu-desktop-r2.13/src/TestSearchPath.cc000066400000000000000000000020061254201501000206570ustar00rootroot00000000000000 #include #include #include #include #include "SearchPath.hh" #include "catch.hpp" static const std::string test_files = TEST_FILES; TEST_CASE("SearchPath/XDG_DATA_HOME", "Check SearchPath honors XDG_DATA_HOME") { setenv("XDG_DATA_HOME", "/usr/share", 1); setenv("XDG_DATA_DIRS", " ", 1); SearchPath sp; std::vector result(sp.begin(), sp.end()); REQUIRE( result.size() == 1 ); REQUIRE( result[0] == "/usr/share/applications/" ); } TEST_CASE("SearchPath/XDG_DATA_DIRS", "Check SearchPath honors XDG_DATA_DIRS") { unsetenv("XDG_DATA_HOME"); setenv("HOME", "/home/testuser", 1); setenv("XDG_DATA_DIRS", (test_files + "usr/share/:" + test_files + "usr/local/share/").c_str(), 1); SearchPath sp; std::vector result(sp.begin(), sp.end()); REQUIRE( result.size() == 2 ); REQUIRE( result[0] == test_files + "usr/share/applications/" ); REQUIRE( result[1] == test_files + "usr/local/share/applications/" ); } j4-dmenu-desktop-r2.13/src/Utilities.hh000066400000000000000000000055541254201501000177750ustar00rootroot00000000000000// // This file is part of j4-dmenu-desktop. // // j4-dmenu-desktop is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // j4-dmenu-desktop is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with j4-dmenu-desktop. If not, see . // #ifndef UTIL_DEF #define UTIL_DEF #include #include #include #include #include #include #include #include #include #include #include #include #include typedef std::list stringlist_t; inline void split(const std::string &str, char delimiter, stringlist_t &elems) { std::stringstream ss(str); std::string item; while (std::getline(ss, item, delimiter)) elems.push_back(item); } inline bool have_equal_element(const stringlist_t &list1, const stringlist_t &list2){ for(auto e1: list1){ for(auto e2: list2){ if(e1 == e2) return true; } } return false; } inline std::pair split(const std::string &str, const std::string &delimiter) { size_t pos = 0; pos = str.find(delimiter); return std::make_pair(str.substr(0, pos), str.substr(pos+1, str.length())); } inline std::string &replace(std::string &str, const std::string &substr, const std::string &substitute) { if(substr.empty()) return str; size_t start_pos = 0; while((start_pos = str.find(substr, start_pos)) != std::string::npos) { str.replace(start_pos, substr.length(), substitute); start_pos += substitute.length(); } return str; } inline bool endswith(const std::string &str, const std::string &suffix) { if(str.length() < suffix.length()) return false; return str.compare(str.length() - suffix.length(), suffix.length(), suffix) == 0; } inline bool startswith(const std::string &str, const std::string &prefix) { if(str.length() < prefix.length()) return false; return str.compare(0, prefix.length(), prefix) == 0; } inline bool is_directory(const std::string &path) { int status; struct stat filestat; status = stat(path.c_str(), &filestat); if(status) return false; return S_ISDIR(filestat.st_mode); } inline std::string get_variable(const std::string &var) { const char *env = std::getenv(var.c_str()); if(env) { return env; } else return ""; } #endif j4-dmenu-desktop-r2.13/src/main.cc000066400000000000000000000001521254201501000167210ustar00rootroot00000000000000 #include "Main.hh" int main(int argc, char **argv) { Main main; return main.main(argc, argv); } j4-dmenu-desktop-r2.13/test_files/000077500000000000000000000000001254201501000170425ustar00rootroot00000000000000j4-dmenu-desktop-r2.13/test_files/applications/000077500000000000000000000000001254201501000215305ustar00rootroot00000000000000j4-dmenu-desktop-r2.13/test_files/applications/eagle.desktop000066400000000000000000000002471254201501000242030ustar00rootroot00000000000000[Desktop Entry] Version=1.0 Type=Application Name=Eagle TryExec=/usr/bin/eagle Exec=eagle -style plastique Icon=/opt/eagle/bin/eagleicon50.png Categories=Development; j4-dmenu-desktop-r2.13/test_files/applications/gimp.desktop000066400000000000000000000252341254201501000240650ustar00rootroot00000000000000[Desktop Entry] Version=1.0 Type=Application Name=GNU Image Manipulation Program Name[ar]=برنامج جنو لمعالجة الصور Name[ast]=Programa de manipulación d'Imaxe GNU Name[be]=GIMP — праґрама праекту GNU для працы зь відарысамі Name[bg]=Редактор на изображения (GIMP) Name[br]=Goulev da zornata skeudennoù GNU Name[ca]=Programa de retoc d'imatges de GNU Name[ca@valencia]=Programa de retoc d'imatges de GNU Name[cs]=GNU Image Manipulation Program Name[csb]=Editora òbrôzów GIMP Name[da]=GNU Image Manipulation Program Name[de]=GNU Image Manipulation Program Name[dz]=་་ཨི་མེཇི་་མ་ནུ་པུ་ལེ་ཤཱན་་པོརོ་གརམ། Name[el]=Πρόγραμμα επεξεργασίας εικόνων GNU Name[en_CA]=GNU Image Manipulation Program Name[en_GB]=GNU Image Manipulation Program Name[eo]=Bildmanipulilo (GIMP = GNU Image Manipulation Program) Name[es]=Programa de manipulación de imágenes de GNU Name[et]=GIMP - GNU pilditöötlusprogramm Name[eu]=GNUren Irudiak Manipulatzeko Programa Name[fa]=برنامهٔ روتوش تصاویر گنو Name[fi]=GIMP-kuvankäsittely Name[fr]=Éditeur d'image GIMP Name[gl]=Programa de Manipulación de Imaxes de GNU Name[gu]=GNU ચિત્ર જાળવણી કાર્યક્રમ Name[he]=תוכנת עיבוד התמונה של GNU Name[hu]=A GNU képszerkesztő program Name[is]=GNU Myndvinnsluforrit Name[it]=GNU Image Manipulation Program Name[ja]=GIMP (GNU Image Manipulation Program) Name[km]=កម្មវិធី​រៀបចំ​រូបភាពរបស់ GNU Name[kn]=GNU ಇಮೇಜ್ ಮ್ಯಾನಿಪುಲೇಶನ್ ಪ್ರೊಗ್ರಾಮ್ Name[ko]=GNU Image Manipulation Program Name[lt]=GNU paveikslėlių manipuliavimo programa Name[lv]=GNU attēlu apstrādes programma Name[mk]=ГНУ програма за манипулација со слики Name[ml]=GNU Image Manipulation Program Name[my]=GNU ရုပ်ပုံ စီမံကိုင်တွယ်ရေး ပရိုဂရမ် Name[nb]=GNU bildebehandlingsprogram Name[nds]=GNU Billbewarkenprogramm Name[ne]=जी एन यू छवि परिचालन कार्यक्रम Name[nl]=GNU Image Manipulation Program Name[nn]=GNU biletbehandlingsprogram Name[pa]=ਗਨੂ ਈਮੇਜ਼ ਮੈਨੂਪਲੇਸ਼ਨ ਪਰੋਗਰਾਮ Name[pl]=Edytor obrazów GIMP Name[pt]=Programa de Manipulação de Imagens GNU Name[pt_BR]=Programa de manipulação de imagem do GNU Name[ro]=GIMP Name[ru]=GNU Image Manipulation Program Name[si]=GNU Image Manipulation Program Name[sk]=GNU program na manipuláciu s obrázkami Name[sl]=GIMP - program GNU za obdelavo slik Name[sr]=Гнуов програм за обраду слика Name[sr@latin]=Gnuov program za obradu slika Name[sv]=GNU:s bildmanipuleringsprogram Name[ta]=க்னூ பட கையாளல் நிரல் Name[te]=గ్నూ ఇమేజ్ మానిప్యులేషన్ ప్రోగ్రామ్ Name[th]=GNU Image Manipulation Program Name[tr]=GNU Görüntü İşleme Programı Name[tt]=Sürät Eşkärtüçe GNU-Yasılım Name[uk]=GNU Image Manipulation Program Name[vi]=Chương Trình Thao Tác Ảnh GNU Name[zh_CN]=GNU 图像处理程序 Name[zh_HK]=GNU 圖片處理程式 Name[zh_TW]=GNU 圖片處理程式 GenericName=Image Editor GenericName[ar]=محرر صور GenericName[ast]=Editor d'imaxe GenericName[be]=Рэдактар відарысаў GenericName[bg]=Редактор на изображения GenericName[br]=Embanner skeudennoù GenericName[ca]=Editor d'imatges GenericName[ca@valencia]=Editor d'imatges GenericName[cs]=Editor obrázků GenericName[da]=Billedredigering GenericName[de]=Bildeditor GenericName[dz]=གཟུགས་བརྙན་ ཞུན་དག་པ། GenericName[el]=Επεξεργαστής εικόνων GenericName[en_CA]=Image Editor GenericName[en_GB]=Image Editor GenericName[eo]=Bildredaktilo GenericName[es]=Editor de imagen GenericName[et]=Pildiredaktor GenericName[eu]=Irudi-editorea GenericName[fa]=ویرایشگر تصویر GenericName[fi]=Kuvankäsittely GenericName[fr]=Éditeur d'image GenericName[gl]=Editor de imaxes GenericName[gu]=ચિત્ર સંપાદક GenericName[hu]=Képszerkesztő GenericName[id]=Penyunting Gambar GenericName[is]=Myndvinnsluforrit GenericName[it]=Modifica immagine GenericName[ja]=画像エディター GenericName[ka]=გამოსახულებების რედაქტორი GenericName[km]=កម្មវិធី​និពន្ធ​រូបភាព GenericName[kn]=ಚಿತ್ರ ಸಂಪಾದಕ GenericName[ko]=이미지 편집기 GenericName[lt]=Paveikslėlių rengyklė GenericName[lv]=Attēla redaktors GenericName[mk]=Уредник за слики GenericName[my]=ရုပ်ပုံ တည်းဖြတ်ကိရိယာ GenericName[nb]=Bildebehandler GenericName[nds]=Billbewarker GenericName[ne]=छवि सम्पादक GenericName[nl]=Afbeeldingsbewerker GenericName[nn]=Bilethandterar GenericName[pa]=ਚਿੱਤਰ ਐਡੀਟਰ GenericName[pl]=Edytor obrazów GenericName[pt]=Editor de Imagens GenericName[pt_BR]=Editor de imagens GenericName[ro]=Editor de imagini GenericName[ru]=Редактор изображений GenericName[sk]=Editor obrázkov GenericName[sl]=Urejevalnik slik GenericName[sr]=Обрада слика GenericName[sr@latin]=Obrada slika GenericName[sv]=Bildredigerare GenericName[ta]=பிம்ப திருத்தி GenericName[te]=బొమ్మ కూర్పకము GenericName[th]=เครื่องมือแก้ไขภาพ GenericName[tr]=Görüntü Düzenleyici GenericName[tt]=Sürät Tözätkeç GenericName[uk]=Редактор зображень GenericName[vi]=Bộ biên soạn ảnh GenericName[xh]=UmHleli woMfanekiso GenericName[zh_CN]=图像编辑器 GenericName[zh_HK]=圖片編輯器 GenericName[zh_TW]=圖片編輯器 Comment=Create images and edit photographs Comment[ar]=أنشئ صورا وحرّر لقطات Comment[ast]=Cree imáxenes y edite semeyes Comment[be]=Стварэньне відарысаў і праўленьне фатаздымкаў Comment[bg]=Създаване на изображения и редакция на снимки Comment[br]=Krouiñ hag embann skeudennoù pe luc'hskeudennoù Comment[ca]=Creeu imatges i editeu fotografies Comment[ca@valencia]=Creeu imatges i editeu fotografies Comment[cs]=Vytvářet obrázky a upravovat fotografie Comment[da]=Opret billeder og redigér fotografier Comment[de]=Bilder erstellen und Fotografien bearbeiten Comment[dz]=གཟུགས་བརྙན་ཚུ་ གསར་བསྐྲུན་འབད་ནི་དང་ དཔར་ཚུ་ཞུན་དག་འབད། Comment[el]=Δημιουργία εικόνων και επεξεργασία φωτογραφιών Comment[en_CA]=Create images and edit photographs Comment[en_GB]=Create images and edit photographs Comment[eo]=Krei bildojn aŭ redakti fotaĵojn Comment[es]=Cree imágenes y edite fotografías Comment[et]=Loo pilte ja redigeeri fotosid Comment[eu]=Sortu irudiak eta editatu argazkiak Comment[fi]=Luo kuvia ja muokkaa valokuvia Comment[fr]=Créer des images et modifier des photographies Comment[gl]=Crear imaxes e editar fotografías Comment[gu]=ચિત્રો બનાવો અને ફોટાઓમાં ફેરફાર કરો Comment[hu]=Képek létrehozása és fotók szerkesztése Comment[id]=Buat gambar dan sunting foto Comment[is]=Búa til myndir og breyta ljósmyndum Comment[it]=Crea immagini e modifica fotografie Comment[ja]=画像の作成と写真の編集を行います Comment[km]=បង្កើត​រូបភាព និង កែសម្រួល​រូបថត Comment[kn]=ಚಿತ್ರಗಳನ್ನು ರಚಿಸಿ ಹಾಗು ಫೋಟೋಗ್ರಾಫ್‌ಗಳನ್ನು ಸಂಪಾದಿಸಿ Comment[ko]=이미지를 만들고 사진을 편집합니다 Comment[lt]=Kurti paveikslėlius ir redaguoti fotografijas Comment[lv]=Veido attēlus vai rediģē fotogrāfijas Comment[mk]=Направи слики и уреди фотографии Comment[my]=ရုပ်ပုံများကို ဖန်တီးပြီး ဓါတ်ပုံများကို တည်းဖြတ်ပါ Comment[nb]=Lag bilder og rediger fotografier Comment[ne]=छवि सिर्जना गर्नुहोस् र फोटोग्राफ सम्पादन गर्नुहोस् Comment[nl]=Afbeeldingen of foto's aanmaken en bewerken Comment[nn]=Lag teikningar eller rediger foto Comment[pa]=ਚਿੱਤਰ ਬਣਾਓ ਅਤੇ ਤਸਵੀਰਾਂ ਸੋਧੋ Comment[pl]=Tworzenie oraz obróbka obrazów i fotografii Comment[pt]=Criar imagens e editar fotografias Comment[pt_BR]=Crie e edite imagens ou fotografias Comment[ro]=Creează imagini și editează fotografii Comment[ru]=Создание изображений и редактирование фотографий Comment[sk]=Vytvárajte a upravujte obrázky alebo fotografie Comment[sl]=Ustvari slike in uredi fotografije Comment[sr]=Направите слике и уредите фотографије Comment[sr@latin]=Napravite slike i uredite fotografije Comment[sv]=Skapa bilder och redigera fotografier Comment[ta]=பிம்பங்களை உருவாக்கவும் மற்றும் படங்களை திருத்தவும் Comment[te]=బొమ్మలను సృష్టించు మరియు చిత్రాలను సవరించు Comment[tr]=Görüntü oluşturur ve fotoğraf düzenler Comment[uk]=Створення зображень та редагування фотографій Comment[vi]=Tạo và biên soạn ảnh hay ảnh chụp Comment[zh_CN]=创建图像或编辑照片 Comment[zh_HK]=建立圖像與編輯照片 Comment[zh_TW]=建立圖像與編輯照片 Exec=gimp-2.8 %U TryExec=gimp-2.8 Icon=gimp Terminal=false Categories=Graphics;2DGraphics;RasterGraphics;GTK; X-GNOME-Bugzilla-Bugzilla=GNOME X-GNOME-Bugzilla-Product=GIMP X-GNOME-Bugzilla-Component=General X-GNOME-Bugzilla-Version=2.8.8 X-GNOME-Bugzilla-OtherBinaries=gimp-2.8 StartupNotify=true MimeType=image/bmp;image/g3fax;image/gif;image/x-fits;image/x-pcx;image/x-portable-anymap;image/x-portable-bitmap;image/x-portable-graymap;image/x-portable-pixmap;image/x-psd;image/x-sgi;image/x-tga;image/x-xbitmap;image/x-xwindowdump;image/x-xcf;image/x-compressed-xcf;image/x-gimp-gbr;image/x-gimp-pat;image/x-gimp-gih;image/tiff;image/jpeg;image/x-psp;application/postscript;image/png;image/x-icon;image/x-xpixmap;image/svg+xml;application/pdf;image/x-wmf;image/jp2;image/jpeg2000;image/jpx;image/x-xcursor; j4-dmenu-desktop-r2.13/test_files/applications/hidden.desktop000066400000000000000000000001271254201501000243560ustar00rootroot00000000000000[Desktop Entry] Version=1.0 Type=Application Name=hiddenApp Exec=hiddenApp Hidden=true j4-dmenu-desktop-r2.13/test_files/applications/htop.desktop000066400000000000000000000002611254201501000240740ustar00rootroot00000000000000[Desktop Entry] Version=1.0 Name=Htop Type=Application Comment=Show System Processes Terminal=true Exec=htop Icon=htop Categories=ConsoleOnly;System; GenericName=Process Viewer j4-dmenu-desktop-r2.13/test_files/applications/notShowIn.desktop000066400000000000000000000003021254201501000250460ustar00rootroot00000000000000[Desktop Entry] Version=1.0 Name=Htop Type=Application Comment=Show System Processes Terminal=true Exec=htop Icon=htop Categories=ConsoleOnly;System; GenericName=Process Viewer NotShowIn=i3;Kde j4-dmenu-desktop-r2.13/test_files/applications/onlyShowIn.desktop000066400000000000000000000003001254201501000252250ustar00rootroot00000000000000[Desktop Entry] Version=1.0 Name=Htop Type=Application Comment=Show System Processes Terminal=true Exec=htop Icon=htop Categories=ConsoleOnly;System; GenericName=Process Viewer OnlyShowIn=i3; j4-dmenu-desktop-r2.13/test_files/applications/visible.desktop000066400000000000000000000001321254201501000245540ustar00rootroot00000000000000[Desktop Entry] Version=1.0 Type=Application Name=visibleApp Exec=visibleApp Hidden=false j4-dmenu-desktop-r2.13/test_files/applications/whitespaces.desktop000066400000000000000000000002171254201501000254420ustar00rootroot00000000000000[Desktop Entry] Version=1.0 Name= Htop GenericName= Process Viewer Type=Application Comment=Show System Processes Terminal=true Exec=htop j4-dmenu-desktop-r2.13/test_files/file.desktop000066400000000000000000000000021254201501000213440ustar00rootroot00000000000000 j4-dmenu-desktop-r2.13/test_files/other.ext000066400000000000000000000000021254201501000206750ustar00rootroot00000000000000 j4-dmenu-desktop-r2.13/test_files/usr/000077500000000000000000000000001254201501000176535ustar00rootroot00000000000000j4-dmenu-desktop-r2.13/test_files/usr/local/000077500000000000000000000000001254201501000207455ustar00rootroot00000000000000j4-dmenu-desktop-r2.13/test_files/usr/local/share/000077500000000000000000000000001254201501000220475ustar00rootroot00000000000000j4-dmenu-desktop-r2.13/test_files/usr/local/share/applications/000077500000000000000000000000001254201501000245355ustar00rootroot00000000000000j4-dmenu-desktop-r2.13/test_files/usr/local/share/applications/dummy000066400000000000000000000000471254201501000256140ustar00rootroot00000000000000The directories are required for tests j4-dmenu-desktop-r2.13/test_files/usr/share/000077500000000000000000000000001254201501000207555ustar00rootroot00000000000000j4-dmenu-desktop-r2.13/test_files/usr/share/applications/000077500000000000000000000000001254201501000234435ustar00rootroot00000000000000j4-dmenu-desktop-r2.13/test_files/usr/share/applications/dummy000066400000000000000000000000471254201501000245220ustar00rootroot00000000000000The directories are required for tests