diff --git a/.editorconfig b/.editorconfig index 0226b648e0..f090ad7738 100644 --- a/.editorconfig +++ b/.editorconfig @@ -8,8 +8,8 @@ indent_style = space trim_trailing_whitespace = true insert_final_newline = true -[*.json] -insert_final_newline = false +[*.svg] +indent_size = 1 [*.{py,json}] indent_size = 4 diff --git a/.github/CODE_OF_CONDUCT.md b/.github/CODE_OF_CONDUCT.md index 39b0bbd27f..ea0d435eac 100644 --- a/.github/CODE_OF_CONDUCT.md +++ b/.github/CODE_OF_CONDUCT.md @@ -23,13 +23,13 @@ include: Examples of unacceptable behavior by participants include: * The use of sexualized language or imagery and unwelcome sexual attention or - advances + advances * Trolling, insulting/derogatory comments, and personal or political attacks * Public or private harassment * Publishing others' private information, such as a physical or electronic - address, without explicit permission + address, without explicit permission * Other conduct which could reasonably be considered inappropriate in a - professional setting + professional setting ## Our Responsibilities @@ -67,10 +67,10 @@ members of the project's leadership. ## Attribution -This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, -available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html +This Code of Conduct is adapted from the [Contributor Covenant][homepage], +[version 1.4]. For answers to common questions about this code of conduct, see +the [FAQ]. [homepage]: https://www.contributor-covenant.org - -For answers to common questions about this code of conduct, see -https://www.contributor-covenant.org/faq +[version1.4]: https://www.contributor-covenant.org/version/1/4/code-of-conduct.html +[faq]: https://www.contributor-covenant.org/faq diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index 224b183e33..9529341f77 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -1,33 +1,34 @@ # Contributing to Numix Core + So you want to contribute to the Numix app themes? Great! Make sure that you're doing the following so that we can get your contributions merged in as soon as possible. ## General Process + All contributions, regardless of which bit of the project they're part of, must follow these steps: 1. [Fork](https://help.github.com/articles/fork-a-repo/) our GitHub repository 2. Make your changes and [push them](https://help.github.com/articles/pushing-to-a-remote/) to your fork - 1. Remember to write a proper [commit message](https://chris.beams.io/posts/git-commit/) - 2. If you're fixing an issue, [close it using keywords](https://help.github.com/articles/closing-issues-via-commit-messages/) + 1. Remember to write a proper [commit message](https://chris.beams.io/posts/git-commit/) + 2. If you're fixing an issue, [close it using keywords](https://help.github.com/articles/closing-issues-via-commit-messages/) 3. Create a new [pull request](https://help.github.com/articles/creating-a-pull-request-from-a-fork/) 4. Make any changes requested and push again -4. When the pull request is accepted you can delete your fork or [sync it](https://help.github.com/articles/syncing-a-fork/) +5. When the pull request is accepted you can delete your fork or [sync it](https://help.github.com/articles/syncing-a-fork/) Now you are ready to start again! - ## Icons + This section covers what to do when creating a new icon or changing the design for an existing icon. If you're wanting to add a symlink see the section on the data file. 1. Make your icon using [Inkscape](https://inkscape.org/) (free and open source) - 1. You must follow our [style guidelines](https://github.com/numixproject/numix-core/wiki/Guidelines) - 2. Icons must be saved as an optimised SVG with [these settings](https://github.com/numixproject/numix-core/wiki/Optimise-Options) - 3. New icon need an entry in the [data file](https://github.com/numixproject/numix-core/blob/master/.github/CONTRIBUTING.md#data-file) + 1. You must follow our [style guidelines](https://github.com/numixproject/numix-core/wiki/Guidelines) + 2. Icons must be saved as an optimized SVG with [these settings](https://github.com/numixproject/numix-core/wiki/Optimise-Options) + 3. New icon need an entry in the [data file](https://github.com/numixproject/numix-core/blob/master/.github/CONTRIBUTING.md#data-file) 2. Create a different pull request for each (unrelated) change you're submitting - 1. Pull requests must contain icons for Circle and Square at minimum - 2. Adding icons for other themes such as Shine and uTouch is optional + 1. Pull requests must contain icons for Circle and Square at minimum + 2. Adding icons for other themes such as Shine and uTouch is optional 3. Include a sample of your icon ([example](https://github.com/numixproject/numix-core/pull/1422)) to make review easier - ## Data File The `data.json` file contains all the information needed for linking the icons to the names needed by different platforms. It has the following structure: @@ -54,13 +55,13 @@ The `data.json` file contains all the information needed for linking the icons t * The `linux` part holds the names from the `Icon=*` line of `*.desktop` files * the `root` holds the primary entry * the `symlinks` holds alternative names - -If you're making changes to this file in a pull request Travis will run a validation check to make sure you haven't made any errors, but please try and check before pushing to make review as easy as possible. +If you're making changes to this file in a pull request Travis will run a validation check to make sure you haven't made any errors, but please try and check before pushing to make review as easy as possible. ## Code -The scripts used for theme building and validation are written in Python. Similarly to the data file, Travis will run a validation check using [pycodestyle](https://github.com/pycqa/pycodestyle) to make sure the coding style you've used is consistent with that used in the project. If you are working on this part of the project it's recommended that you check your changes using a pycodestyle linter before creating a pull request. +The scripts used for theme building and validation are written in Python. Similarly to the data file, Travis will run a validation check using [pycodestyle](https://github.com/pycqa/pycodestyle) to make sure the coding style you've used is consistent with that used in the project. If you are working on this part of the project it's recommended that you check your changes using a pycodestyle linter before creating a pull request. ### Indentation + Throughout this project we use 4 spaces for JSON and Python files, and generally use 1 space for SVG. If you want to diverge, do it locally (e.g. using [git filters](https://stackoverflow.com/questions/2316677/can-git-automatically-switch-between-spaces-and-tabs#2318063)). diff --git a/.github/workflow-assets/test-requirements.txt b/.github/workflow-assets/test-requirements.txt deleted file mode 100644 index c4521474e7..0000000000 --- a/.github/workflow-assets/test-requirements.txt +++ /dev/null @@ -1,6 +0,0 @@ -flake8 -pytest -cairosvg -pycodestyle -jsonschema -pygobject \ No newline at end of file diff --git a/.github/workflows/markdownlint-config.json b/.github/workflows/markdownlint-config.json new file mode 100644 index 0000000000..5eb727082c --- /dev/null +++ b/.github/workflows/markdownlint-config.json @@ -0,0 +1,4 @@ +{ + "default": true, + "line-length": false +} diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index fc28ae8a3e..132e9e7384 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -7,32 +7,55 @@ on: branches: [master] jobs: - build: + # Checks relating to the linting of files + lint: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 - - name: Set up Python 3.6 + - name: Checkout repository + uses: actions/checkout@v2 + + - name: Set up Python uses: actions/setup-python@v2 with: python-version: 3.6 - - uses: actions/cache@v2 + - name: Install dependencies + run: pip3 install flake8 + + - name: Check PEP8 compliance + run: find . -name "*.py" | xargs flake8 $1 + + - name: Check mdl compliance + uses: articulate/actions-markdownlint@v1.1.0 + with: + config: .github/workflows/markdownlint-config.json + ignore: LICENSE + + - name: Check .editorconfig compliance + uses: editorconfig-checker/action-editorconfig-checker@v1 + + # Checks relating to the database + check: + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v2 + + - name: Set up Python + uses: actions/setup-python@v2 with: - path: ~/.cache/pip - key: ${{ hashFiles('./.github/workflow-assets/test-requirements.txt') }} + python-version: 3.6 - name: Install dependencies - run: | - sudo apt-get -y update - sudo apt-get install -y python3-pip python3-gi gir1.2-gtk-3.0 libgtk-3-dev libgirepository1.0-dev - pip3 install --upgrade --upgrade-strategy eager -r ./.github/workflow-assets/test-requirements.txt + run: pip3 install jsonschema - - name: Test + - name: Check database run: | - sh ./tests/pycodestyle.sh python3 ./tests/schema.py python3 ./tests/db_entry.py python3 ./tests/icons_entry.py python3 ./tests/ordered_db.py python3 ./tests/icon_cache.py + python3 ./tests/duplicates.py diff --git a/.gitignore b/.gitignore index 4b9eee05dd..dfdc5e7b03 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,9 @@ __pycache__/* *.pyc +# Tag Directories +icons/tag.* + # Output Directories numix-circle.icns/ numix-icon-theme-circle/ diff --git a/LICENSE b/LICENSE index a737dcfed5..941fef1adf 100644 --- a/LICENSE +++ b/LICENSE @@ -1,10 +1,10 @@ GNU GENERAL PUBLIC LICENSE - Version 3, 29 June 2007 + Version 3, 29 June 2007 - Copyright (C) 2007 Free Software Foundation, Inc. - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. +Copyright (C) 2007 Free Software Foundation, Inc. +Everyone is permitted to copy and distribute verbatim copies +of this license document, but changing it is not allowed. Preamble @@ -69,7 +69,7 @@ patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. - TERMS AND CONDITIONS + TERMS AND CONDITIONS 0. Definitions. @@ -619,7 +619,7 @@ an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. - END OF TERMS AND CONDITIONS + END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs diff --git a/data.json b/data.json index df84f09640..4816048828 100644 --- a/data.json +++ b/data.json @@ -243,7 +243,8 @@ ], "linux": { "root": "aarddict" - } + }, + "tags": ["world-baseplate"] }, "abbaye": { "linux": { @@ -283,7 +284,8 @@ "abrowser": { "linux": { "root": "abrowser" - } + }, + "tags": ["world-baseplate"] }, "abuse-game": { "linux": { @@ -411,7 +413,6 @@ "root": "accessories-painting", "symlinks": [ "applications-graphics", - "azpainter", "graphics-image-editor", "kolourpaint", "org.kde.kolourpaint", @@ -463,7 +464,6 @@ "linux": { "root": "accessories-thesaurus", "symlinks": [ - "accessories-thesaurus", "clipit-trayicon", "glippy", "ptask", @@ -987,7 +987,7 @@ "root": "alleyoop" } }, - "allusion": { + "allusion": { "linux": { "root": "allusion" } @@ -1369,7 +1369,7 @@ "root": "apktool", "symlinks": [ "kali-apktool" - ] + ] } }, "apostrophe": { @@ -1442,10 +1442,8 @@ "app", "applications-accessories", "applications-other", - "cs-default-applications", "gnome-desktop-item-edit", "gnome-run", - "gnome-settings-default-applications", "preferences-desktop-launch-feedback" ] } @@ -1900,7 +1898,7 @@ "root": "atom-beta" } }, - "atom-dev": { + "atom-dev": { "linux": { "root": "atom-dev" } @@ -2108,11 +2106,11 @@ "org.openchemistry.avogadro2" ] } - }, - "avorion": { - "linux": { - "root": "steam_icon_445220" - } + }, + "avorion": { + "linux": { + "root": "steam_icon_445220" + } }, "awesomenauts": { "linux": { @@ -2161,7 +2159,7 @@ "axiom-verge": { "linux": { "root": "steam_icon_332200" - } + } }, "azardi": { "linux": { @@ -2978,7 +2976,8 @@ "tor-browser-en", "torbrowser" ] - } + }, + "tags": ["world-baseplate"] }, "btanks": { "linux": { @@ -5252,7 +5251,7 @@ "cutepeaks": { "linux": { "root": "cutepeaks" - } + } }, "cutter": { "linux": { @@ -5543,7 +5542,6 @@ "linux": { "root": "desktop", "symlinks": [ - "cs-desktop", "deepin-show-desktop", "dock", "dockbarx", @@ -6806,7 +6804,8 @@ "symlinks": [ "net.drawpile.drawpile" ] - } + }, + "tags": ["world-baseplate"] }, "drgeo": { "linux": { @@ -7138,7 +7137,8 @@ "eiskaltdcpp": { "linux": { "root": "eiskaltdcpp" - } + }, + "tags": ["world-baseplate"] }, "ekiga": { "linux": { @@ -7315,7 +7315,8 @@ "empire-total-war": { "linux": { "root": "steam_icon_10500" - } + }, + "tags": ["world-baseplate"] }, "encryptr": { "linux": { @@ -7411,7 +7412,8 @@ "symlinks": [ "com.github.cassidyjames.ephemeral" ] - } + }, + "tags": ["world-baseplate"] }, "epoptes": { "linux": { @@ -7446,7 +7448,8 @@ "eric-web": { "linux": { "root": "ericWeb" - } + }, + "tags": ["world-baseplate"] }, "espeak-gui": { "linux": { @@ -8013,7 +8016,8 @@ "firefox-icon-unbranded": { "linux": { "root": "firefox-icon-unbranded" - } + }, + "tags": ["world-baseplate"] }, "firefox-nightly": { "linux": { @@ -9772,8 +9776,7 @@ "linux": { "root": "gnome-multi-writer", "symlinks": [ - "org.gnome.MultiWriter", - "popsicle" + "org.gnome.MultiWriter" ] } }, @@ -9916,7 +9919,10 @@ }, "gnome-settings-default-applications": { "linux": { - "root": "gnome-settings-default-applications" + "root": "gnome-settings-default-applications", + "symlinks": [ + "cs-default-applications" + ] } }, "gnome-social": { @@ -10463,7 +10469,8 @@ "io.github.dfandrich.gpscorrelate", "mapollage" ] - } + }, + "tags": ["world-baseplate"] }, "gourmet": { "linux": { @@ -12114,8 +12121,6 @@ "gnome-balsa2", "imap", "internet_mail", - "kmail2", - "kmailcvt", "mail-generic", "mail_generic", "mailer", @@ -12222,7 +12227,6 @@ "root": "itunes", "symlinks": [ "atunes", - "freac", "minitunes", "tunesview" ] @@ -12307,7 +12311,8 @@ "symlinks": [ "io.jamulus.Jamulus" ] - } + }, + "tags": ["world-baseplate"] }, "jasp-desktop": { "linux": { @@ -12852,7 +12857,7 @@ ] } }, - "kdiskmark": { + "kdiskmark": { "linux": { "root": "kdiskmark" } @@ -13196,6 +13201,7 @@ "root": "kmail", "symlinks": [ "kmail2", + "kmailcvt", "org.kde.kmail", "org.kde.kmail2" ] @@ -14419,7 +14425,8 @@ "luakit": { "linux": { "root": "luakit" - } + }, + "tags": ["world-baseplate"] }, "lubuntu-software-center": { "linux": { @@ -15042,7 +15049,8 @@ "org.midori_browser.Midori", "preferences-web-browser-stylesheets" ] - } + }, + "tags": ["world-baseplate"] }, "mikrolock": { "linux": { @@ -15270,7 +15278,8 @@ "symlinks": [ "mlclient-icon-24colors-1" ] - } + }, + "tags": ["world-baseplate"] }, "mlterm": { "linux": { @@ -15456,8 +15465,7 @@ "linux": { "root": "mplayer", "symlinks": [ - "kmplayer", - "kylin-video" + "kmplayer" ] } }, @@ -15806,7 +15814,7 @@ "linux": { "root": "musictube", "symlinks": [ - "org.tordini.flavio.Minitube" + "org.tordini.flavio.Musictube" ] } }, @@ -16277,7 +16285,8 @@ "knemo", "mate-nettool" ] - } + }, + "tags": ["world-baseplate"] }, "nnn": { "linux": { @@ -16834,7 +16843,8 @@ "openofficeorg24-web", "openofficeorg3-web" ] - } + }, + "tags": ["world-baseplate"] }, "ooo-writer": { "linux": { @@ -16942,7 +16952,7 @@ "root": "openerp-client" } }, - "openkj": { + "openkj": { "linux": { "root": "openkj", "symlinks": [ @@ -17294,7 +17304,8 @@ "pac": { "linux": { "root": "pac" - } + }, + "tags": ["world-baseplate"] }, "pace": { "linux": { @@ -17453,7 +17464,8 @@ "symlinks": [ "org.kde.parley" ] - } + }, + "tags": ["world-baseplate"] }, "parnold-x.timer": { "linux": { @@ -17627,11 +17639,9 @@ "linux": { "root": "pdfmod", "symlinks": [ - "com.github.muriloventuroso.pdftricks", "flpsed", "master-pdf-editor", - "net.codeindustry.MasterPDFEditor", - "pdftricks" + "net.codeindustry.MasterPDFEditor" ] } }, @@ -17642,11 +17652,7 @@ }, "pdfshuffler": { "linux": { - "root": "pdfshuffler", - "symlinks": [ - "com.github.jeromerobert.pdfarranger", - "pdfarranger" - ] + "root": "pdfshuffler" } }, "pdfslicer": { @@ -17794,7 +17800,8 @@ "pglgui": { "linux": { "root": "pglgui" - } + }, + "tags": ["world-baseplate"] }, "pharlap": { "linux": { @@ -18216,7 +18223,8 @@ "symlinks": [ "net.poedit.Poedit" ] - } + }, + "tags": ["world-baseplate"] }, "poezio": { "linux": { @@ -19296,7 +19304,8 @@ "symlinks": [ "QMapTool" ] - } + }, + "tags": ["world-baseplate"] }, "qmidiarp": { "linux": { @@ -19345,7 +19354,8 @@ "qomp": { "linux": { "root": "qomp" - } + }, + "tags": ["world-baseplate"] }, "qopenvpn": { "linux": { @@ -19412,7 +19422,8 @@ "qrestclient": { "linux": { "root": "qrestclient" - } + }, + "tags": ["world-baseplate"] }, "qsampler": { "linux": { @@ -19709,7 +19720,8 @@ "symlinks": [ "org.qutebrowser.qutebrowser" ] - } + }, + "tags": ["world-baseplate"] }, "qutim": { "linux": { @@ -20744,7 +20756,6 @@ "linux": { "root": "screenlets", "symlinks": [ - "com.github.lainsce.notejot", "cs-desklets", "footnote" ] @@ -20964,7 +20975,7 @@ "space.fips.Fips" ] } - }, + }, "serena": { "linux": { "root": "serena", @@ -21030,7 +21041,6 @@ "linux": { "root": "setzer", "symlinks": [ - "com.github.manexim.typewriter", "org.cvfosammmm.Setzer" ] } @@ -21467,7 +21477,8 @@ "smtube": { "linux": { "root": "smtube" - } + }, + "tags": ["world-baseplate"] }, "smuxi-frontend-gnome": { "linux": { @@ -21475,7 +21486,8 @@ "symlinks": [ "smuxi" ] - } + }, + "tags": ["world-baseplate"] }, "snap-icon": { "linux": { @@ -21570,7 +21582,6 @@ "root": "softwarecenter-debian", "symlinks": [ "com.github.donadigo.eddy", - "deepin-deb-installer", "eddy" ] } @@ -23127,7 +23138,6 @@ "org.xfce.mousepad", "oxygen", "scratch-text-editor", - "textadept", "wine-notepad", "xed", "xfce-edit" @@ -24092,7 +24102,7 @@ "UltraStar-WorldParty", "WorldParty" ] - } + } }, "ultratron": { "linux": { @@ -24974,10 +24984,7 @@ }, "waveform": { "linux": { - "root": "steam_icon_204180", - "symlinks": [ - "freedv" - ] + "root": "steam_icon_204180" } }, "wayland": { @@ -25094,7 +25101,8 @@ "www-browser", "xdg-browser-launcher" ] - } + }, + "tags": ["world-baseplate"] }, "web-caret": { "linux": { @@ -26875,7 +26883,8 @@ "zeroinstall": { "linux": { "root": "zeroinstall" - } + }, + "tags": ["world-baseplate"] }, "zerotier-gui": { "linux": { diff --git a/gen.py b/gen.py index a032d94d40..f374baef39 100755 --- a/gen.py +++ b/gen.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 """ -Copyright (C) 2018 +Copyright (C) 2019 Numix Project This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License (version 3+) as published by the Free Software Foundation. You should have received @@ -18,33 +18,36 @@ from subprocess import PIPE, Popen, call try: from gi import require_version - require_version('Rsvg', '2.0') + require_version("Rsvg", "2.0") from gi.repository import Rsvg from cairo import ImageSurface, Context, FORMAT_ARGB32 use_inkscape = False except (ImportError, AttributeError, ValueError): - ink_flag = call(['which', 'inkscape'], stdout=PIPE, stderr=PIPE) - if ink_flag == 0: - use_inkscape = True - else: - exit("Can't load cariosvg nor inkscape") - + # Assigns true if Inkscape found, false otherwise + use_inkscape = not call(["which", "inkscape"], stdout=PIPE, stderr=PIPE) + if not use_inkscape: + exit("Can't load CairoSVG nor Inkscape") -parser = ArgumentParser(prog="Numix-core") -# Importing JSON +# Loading the JSON data file try: - with open('data.json') as data: + with open("data.json") as data: icons = load(data) except FileNotFoundError: exit("Please clone the whole repository and try again.") + +# Parsing build arguments +parser = ArgumentParser(prog="Numix-core") + try: - theme, themes = "", listdir("icons") - parser.add_argument("--theme", "-t", - help="Theme you want to build.", choices=themes) + # Exclude files and tag directories from the list of themes + themes = [dir for dir in listdir("icons") if "." not in dir] except FileNotFoundError: exit("No icons folder found. Please reclone and try again.") +parser.add_argument("--theme", "-t", + help="Theme you want to build.", + choices=themes) platform, platforms = "", ["android", "linux", "osx"] parser.add_argument("--platform", "-p", @@ -55,15 +58,17 @@ # User selects the theme if not args.theme: - exit("Please use --theme argument with " - "one of the following: {}".format(", ".join(themes))) + msg = "Please use --theme argument with one of the following: " + exit(msg + ", ".join(themes)) else: theme = args.theme + # Each theme may support different sizes + sizes = listdir("icons/" + theme) # User selects the platform if not args.platform: - exit("Please use --platform argument with " - "one of the following: {}".format(", ".join(platforms))) + msg = "Please use --platform argument with one of the following: " + exit(msg + ", ".join(platforms)) else: platform = args.platform @@ -76,14 +81,15 @@ def mkdir(directory): def convert_svg2png(infile, outfile, w, h): """ - Converts svg files to png using Cairosvg or Inkscape + Converts svg files to png using CairoSVG or Inkscape @file_path : String; the svg file absolute path @dest_path : String; the png file absolute path """ if use_inkscape: - cmd = Popen(["inkscape", "-z", infile, "-o", outfile, - "-w", str(w), "-h", str(h)], - stdout=PIPE, stderr=PIPE) + cmd = Popen([ + "inkscape", "-z", infile, "-o", outfile, + "-w", str(w), "-h", str(h) + ], stdout=PIPE, stderr=PIPE) cmd.communicate() else: handle = Rsvg.Handle() @@ -97,105 +103,118 @@ def convert_svg2png(infile, outfile, w, h): png_io = BytesIO() img.write_to_png(png_io) - with open(outfile, 'wb') as fout: + with open(outfile, "wb") as fout: fout.write(png_io.getvalue()) svg.close() png_io.close() img.finish() -# Only certain icon sizes may be covered -try: - sizes = listdir("icons/{0}".format(theme)) -except FileNotFoundError: - exit("The theme {0} does not exists. Please reclone" - "the repository and try again.".format(theme)) - - -# The Android Generation Stuff +# Android Generation Code if platform == "android": print("\nGenerating Android theme...") - theme_name = "com.numix.icons_{0}".format(theme) + + # Define and create theme locations + theme_name = "com.numix.icons_" + theme android_dir = theme_name + "/MainActivity22/app/src/main/res/" app_filter = android_dir + "xml/appfilter.xml" theme_dir = android_dir + "drawable-xxhdpi/" + mkdir(theme_dir) + mkdir(path.dirname(app_filter)) + # app_filter file contains Android meta data for the theme app_filter_content = '\n' app_filter_content += '\n' - mkdir(theme_dir) - mkdir(path.dirname(app_filter)) - for icon_name, icon in icons.items(): for component_info in icon.get("android", []): + # Check icon source exists before it's used source = "icons/{0}/48/{1}.svg".format(theme, format(icon_name)) + if not path.exists(source): + continue + + # Render icon to resources directory drawable_name = icon_name.replace(".", "_").replace("-", "_") - output = "{0}/{1}.png".format(theme_dir, - drawable_name) - if path.exists(source): - convert_svg2png(source, output, 192, 192) - app_filter_content += '\t\n' + output = "{0}/{1}.png".format(theme_dir, drawable_name) + convert_svg2png(source, output, 192, 192) + + # Add icons app_filter data (afd) to the app_filter file + afd = '\t\n' + app_filter_content += afd.format(component_info, drawable_name) app_filter_content += '' - with open(app_filter, 'w') as app_filter_obj: + with open(app_filter, "w") as app_filter_obj: app_filter_obj.write(app_filter_content) -# The Linux Generation Stuff +# Linux Generation Code elif platform == "linux": print("\nGenerating Linux theme...") + + # Define and create theme locations linux_dir = "numix-icon-theme-{0}/Numix-{1}".format(theme, theme.title()) for size in sizes: mkdir("{0}/{1}/apps".format(linux_dir, size)) + for icon_name, icon in icons.items(): if not icon.get("linux"): continue + for size in sizes: - root = "{0}.svg".format(icon["linux"]["root"]) + # Check icon source exists before it's used source = "icons/{0}/{1}/{2}.svg".format(theme, size, icon_name) + if not path.exists(source): + continue + + # Create root SVG icon + root = icon["linux"]["root"] + ".svg" output = "{0}/{1}/apps/".format(linux_dir, size) - if path.exists(source): - if icon["linux"].get("bfb") and (size == "48"): - output_bfb = "{0}.png".format(icon["linux"].get("bfb")) - convert_svg2png(source, output + output_bfb, 144, 144) - copy2(source, output + root) - for link in icon["linux"].get("symlinks", []): - output_symlink = "{0}{1}.svg".format(output, link) - try: - symlink(root, output_symlink) - except FileExistsError: - continue - - -# The OSX Generation Stuff + copy2(source, output + root) + + # Symlinks to root icon + for link in icon["linux"].get("symlinks", []): + output_symlink = output + link + ".svg" + try: + symlink(root, output_symlink) + except FileExistsError: + continue + + # Unity 7 BFB icons + if icon["linux"].get("bfb") and (size == "48"): + output_bfb = icon["linux"].get("bfb") + ".png" + convert_svg2png(source, output + output_bfb, 144, 144) + + +# OSX Generation Code elif platform == "osx": print("\nGenerating OSX theme...") - ink_flag = call(['which', 'png2icns'], stdout=PIPE, stderr=PIPE) - if ink_flag != 0: + + # OSX generation depends on libicns function png2icns + no_icns = call(["which", "png2icns"], stdout=PIPE, stderr=PIPE) + if no_icns: exit("You will need png2icns in order to generate OSX theme") + + # Define and create theme locations osx_dir = "numix-{0}.icns".format(theme) - osx_sub_dirs = ["icns", "pngs", "vectors"] - for sub_dir in osx_sub_dirs: + for sub_dir in ["icns", "pngs", "vectors"]: mkdir("{0}/{1}".format(osx_dir, sub_dir)) + for icon_name, icon in icons.items(): - for output_icon in icon.get("osx", [icon_name]): + for output_icon in icon.get("osx", []): + # Check icon source exists before it's used source = "icons/{0}/48/{1}.svg".format(theme, icon_name) - output_svg = "{0}/vectors/{1}.svg".format(osx_dir, output_icon) - output_png = "{0}/pngs/{1}".format(osx_dir, output_icon) - output_icns = "{0}/icns/{1}.icns".format(osx_dir, output_icon) - if path.exists(source): - copy2(source, output_svg) - sizes = [16, 32, 128, 256, 512, 1024] - filenames = [ - "{}_{}.png".format(output_png, str(x)) for x in sizes - ] - - for out_name, size in zip(filenames, sizes): - convert_svg2png(source, out_name, size, size) - - call(["png2icns"] + [output_icns] + filenames, - stdout=PIPE, stderr=PIPE) + if not path.exists(source): + continue + + # Icon output locations + svg_icon = "{0}/vectors/{1}.svg".format(osx_dir, output_icon) + png_icon = "{0}/pngs/{1}.png".format(osx_dir, output_icon) + icn_icon = "{0}/icns/{1}.icn".format(osx_dir, output_icon) + + # Create icon in SVG, PNG, and ICNS formats + copy2(source, svg_icon) + convert_svg2png(source, png_icon, 1024, 1024) + call(["png2icns", icn_icon, png_icon], stdout=PIPE, stderr=PIPE) # Clean Up diff --git a/icons/tags.py b/icons/tags.py new file mode 100755 index 0000000000..512fb0b8be --- /dev/null +++ b/icons/tags.py @@ -0,0 +1,78 @@ +#!/usr/bin/env python3 + +""" +Copyright (C) 2021 Numix Project +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License (version 3+) as +published by the Free Software Foundation. You should have received +a copy of the GNU General Public License along with this program. +If not, see . +""" + +# Sorting out modules +from argparse import ArgumentParser +from json import load +from os import listdir, makedirs, path +from shutil import copy2, rmtree + +# Used for viewing all the icon files across all themes that have a +# certain tag. Can be used either for related icons (e.g. ms-office, +# icons using the same design element (e.g. world-baseplate), etc. + + +def mkdir(directory): + """Create a directory if it doesn't exists.""" + if not path.exists(directory): + makedirs(directory) + + +# Parse arguments +parser = ArgumentParser(prog="Core Tag Viewer") +parser.add_argument("--tag", "-t", help="Tag you want to view.") +parser.add_argument("--clean", "-c", action="store_true", + help="Delete all tag outputs.") +args = parser.parse_args() + +# Cleanup first if `--clean` passed +if args.clean: + for dir in listdir("./"): + if "tag." in dir: + rmtree(dir) + # If `--clean` run alone, done + if not args.tag: + exit(0) + +# Check `--tag` used correctly +if not args.tag: + exit("Please specify a tag using --tag") +else: + tag = args.tag + +# Exclude files and tag directories from the list of themes +themes = [dir for dir in listdir("./") if "." not in dir] + +# Make folder and subfolders for tag location +for theme in themes: + if "." not in theme: + mkdir("tag.{0}/{1}".format(tag, theme)) + +# Loading the JSON data file +try: + with open("../data.json") as data: + icons = load(data) +except FileNotFoundError: + exit("Couldn't find data.json!") + +# iterate over data.json looking for keys with tag +for icon_name, icon in icons.items(): + if tag in icon.get("tags", []): + for theme in themes: + # Check icon source exists before it's used + source = "{0}/48/{1}.svg".format(theme, icon_name) + if not path.exists(source): + continue + # Copy icons for tagged keys into output directory + copy2(source, "tag.{0}/{1}".format(tag, theme)) + +# Clean Up +print("Done!") diff --git a/tests/db_entry.py b/tests/db_entry.py index b9cd5e7bf0..4c4a4145af 100644 --- a/tests/db_entry.py +++ b/tests/db_entry.py @@ -1,34 +1,37 @@ #!/usr/bin/env python3 + """ -# Copyright (C) 2016 -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License (version 3+) as -# published by the Free Software Foundation. You should have received -# a copy of the GNU General Public License along with this program. -# If not, see . +Copyright (C) 2019 Numix Project +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License (version 3+) as +published by the Free Software Foundation. You should have received +a copy of the GNU General Public License along with this program. +If not, see . """ +from json import load from os import path -import json -from utils import error +from utils import error, success, DB_FILE, ICONS_DIR, THEMES -ABS_PATH = path.dirname(path.abspath(__file__)) -DB_FILE = path.join(ABS_PATH, "../data.json") -THEMES = ["circle", "square"] -ICONS_DIR = path.join(ABS_PATH, "../icons/") +# Test which checks whether every key in data.json has an icon in +# the themes which Core supports. Success signified by exit code. -has_errors = False with open(DB_FILE, 'r') as db_obj: - data = json.load(db_obj) - for entry in data: - for theme in THEMES: - icon = path.join(ICONS_DIR, theme, "48/{}.svg".format(entry)) - if not path.exists(icon): - has_errors = True - error("The icon {} doesn't exist" - " in the theme {}".format( - entry, theme - )) -exit(int(has_errors)) + data = load(db_obj) + +has_errors = False +for entry in data: + for theme in THEMES: + icon = path.join(ICONS_DIR, theme, "48/{}.svg".format(entry)) + if path.exists(icon): + continue + + has_errors = True + error("'{}' doesn't have an icon in the {} theme".format(entry, theme)) + +if not has_errors: + success("Every database entry has an icon") + +exit(has_errors) diff --git a/tests/duplicates.py b/tests/duplicates.py new file mode 100644 index 0000000000..d0ad180513 --- /dev/null +++ b/tests/duplicates.py @@ -0,0 +1,50 @@ +#!/usr/bin/env python3 + +""" +Copyright (C) 2019 Numix Project +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License (version 3+) as +published by the Free Software Foundation. You should have received +a copy of the GNU General Public License along with this program. +If not, see . +""" + +from json import load + +from utils import error, success, DB_FILE + +# Test which checks for duplicates across all the Linux root icons +# and symlinks. Success signified by exit code. + + +with open(DB_FILE, 'r') as db_obj: + data = load(db_obj) + +linux_names = [] +for key, value in data.items(): + # At the moment we're only concerned with Linux duplicates + if not value.get("linux"): + continue + + icon = data[key]["linux"] + linux_names.append(icon["root"]) + + if data[key]["linux"].get("symlinks"): + linux_names += icon["symlinks"] + +has_linux_dupes = False + +linux_dupes = {} +for name in linux_names: + if linux_names.count(name) > 1: + linux_dupes[name] = linux_names.count(name) + +if len(linux_dupes) > 0: + has_linux_dupes = True + error("Found the following Linux icon names duplicated:") + for dupe in sorted(linux_dupes.keys()): + error("- {} ({} times)".format(dupe, linux_dupes[dupe])) +else: + success("No database duplicates") + +exit(has_linux_dupes) diff --git a/tests/icon_cache.py b/tests/icon_cache.py index 59cbea2360..c65fe40234 100644 --- a/tests/icon_cache.py +++ b/tests/icon_cache.py @@ -1,41 +1,52 @@ #!/usr/bin/env python3 + """ -# Copyright (C) 2016 -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License (version 3+) as -# published by the Free Software Foundation. You should have received -# a copy of the GNU General Public License along with this program. -# If not, see . +Copyright (C) 2019 Numix Project +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License (version 3+) as +published by the Free Software Foundation. You should have received +a copy of the GNU General Public License along with this program. +If not, see . """ -from os import path -import json -from collections import OrderedDict -from utils import error +from json import load + +from utils import error, success, DB_FILE + +# Test which checks whether the Linux icon cache would be valid for +# the given data.json file. Success signified by exit code. + + +def space_check(string): + """Checks if a string contains an empty space.""" + if " " in string: + error("'{}' contains an empty space".format(string)) + return True + else: + return False -ABS_PATH = path.dirname(path.abspath(__file__)) -DB_FILE = path.join(ABS_PATH, "../data.json") with open(DB_FILE, 'r') as db_obj: - data = json.load(db_obj, object_pairs_hook=OrderedDict) + data = load(db_obj) has_errors = False +for key, value in data.items(): + if not value.get("linux"): + continue - -def test_empty_space(icon_name): - """Test if an icon name has an empty space on it.""" - global has_errors - if " " in icon_name: + icon = data[key]["linux"] + if space_check(icon["root"]): has_errors = True - error("{} contains an empty space on it".format(icon_name)) + if not icon.get("symlinks"): + continue -for key, value in data.items(): - if value.get("linux"): - icon = data[key]["linux"] - test_empty_space(icon["root"]) - if icon.get("symlinks"): - symlinks = icon["symlinks"] - for symlink in symlinks: - test_empty_space(symlink) -exit(int(has_errors)) + symlinks = icon["symlinks"] + for symlink in symlinks: + if space_check(symlink): + has_errors = True + +if not has_errors: + success("Icon cache is valid") + +exit(has_errors) diff --git a/tests/icons_entry.py b/tests/icons_entry.py index 6f0c7b2baa..f64e3cf8fa 100644 --- a/tests/icons_entry.py +++ b/tests/icons_entry.py @@ -1,38 +1,41 @@ #!/usr/bin/env python3 + """ -# Copyright (C) 2016 -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License (version 3+) as -# published by the Free Software Foundation. You should have received -# a copy of the GNU General Public License along with this program. -# If not, see . +Copyright (C) 2019 Numix Project +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License (version 3+) as +published by the Free Software Foundation. You should have received +a copy of the GNU General Public License along with this program. +If not, see . """ + from glob import glob +from json import load from os import path -import json -from utils import error +from utils import error, success, THEMES, DB_FILE, ICONS_DIR -ABS_PATH = path.dirname(path.abspath(__file__)) -DB_FILE = path.join(ABS_PATH, "../data.json") -THEMES = ["circle", "square"] -ICONS_DIR = path.join(ABS_PATH, "../icons/") +# Test to check whether every icon in the themes which core support have +# associed entries in the data.json file Success signified by exit code. -has_errors = False with open(DB_FILE, 'r') as db_obj: - entries = json.load(db_obj).keys() + entries = load(db_obj).keys() +has_errors = False reported = [] for theme in THEMES: icons = glob(path.join(ICONS_DIR, theme, "48") + "/*.svg") for icon in icons: icon_name = path.splitext(path.basename(icon))[0] - if icon_name not in entries and icon_name not in reported: - reported.append(icon_name) - has_errors = True - error("The icon {} doesn't have any " - "entry in the database.".format( - icon_name - )) -exit(int(has_errors)) + if icon_name in entries or icon_name in reported: + continue + + reported.append(icon_name) + has_errors = True + error("'{}' doesn't have an entry in the database.".format(icon_name)) + +if not has_errors: + success("Every icon has a database entry") + +exit(has_errors) diff --git a/tests/ordered_db.py b/tests/ordered_db.py index a2a9f0ce12..e8ef7b9cd1 100644 --- a/tests/ordered_db.py +++ b/tests/ordered_db.py @@ -1,57 +1,70 @@ #!/usr/bin/env python3 + """ -# Copyright (C) 2016 -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License (version 3+) as -# published by the Free Software Foundation. You should have received -# a copy of the GNU General Public License along with this program. -# If not, see . +Copyright (C) 2019 Numix Project +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License (version 3+) as +published by the Free Software Foundation. You should have received +a copy of the GNU General Public License along with this program. +If not, see . """ -from os import path -import json from collections import OrderedDict +from json import load -from utils import sort, error - +from utils import error, success, DB_FILE -ABS_PATH = path.dirname(path.abspath(__file__)) -DB_FILE = path.realpath(path.join(ABS_PATH, "../data.json")) +# Test which checks whether the data.json file is sorted by key and +# any lists within those keys. Success signified by exit code. -with open(DB_FILE, 'r') as db_obj: - data = json.load(db_obj, object_pairs_hook=OrderedDict) +def sort_errors(list, stype, root=""): + """ + Checks if a list of 'stype' strings has sorting errors, case insensitive. + Takes as optional string 'root' as a root value for use with sublists. + """ + has_errors = False + reference = sorted(list, key=lambda item: item.lower()) -keys = list(data.keys()) -ordreded_keys = sort(keys) -has_errors = False + for i in range(len(list)): + if list[i] == reference[i]: + continue -for i in range(len(keys)): - if keys[i] != ordreded_keys[i]: - correct_position = ordreded_keys.index(keys[i]) + correct_index = reference.index(list[i]) has_errors = True - error("Database entry {} not correctly " - "ordred".format(keys[i])) - print("Should be placed at {} instead " - "of {}".format(correct_position, i)) + if root == "": + error_msg = "{} '{}' not correctly ordred" + else: + error_msg = "{} '{}' of '{}' not correctly ordred" + + error(error_msg.format(stype, list[i], root)) + print("Should be placed at {} instead of {}".format(correct_index, i)) + + return has_errors + + +with open(DB_FILE, 'r') as db_obj: + data = load(db_obj, object_pairs_hook=OrderedDict) + +has_errors = sort_errors(list(data.keys()), "Database entry") for key, value in data.items(): + if value.get("android") and sort_errors(value["android"], "Android icon"): + has_errors = True + if value.get("linux"): symlinks = value["linux"].get("symlinks") - if symlinks: - ordered_symlinks = sort(symlinks) - for i in range(len(symlinks)): - if symlinks[i] != ordered_symlinks[i]: - has_errors = True - error("Linux symlink of \"{}\" not correctly " - "ordered: {}".format(key, symlinks[i])) - if value.get("android"): - android_icons = value["android"] - ordered_and_icons = sort(android_icons) - for i in range(len(android_icons)): - if android_icons[i] != ordered_and_icons[i]: - has_errors = True - error("Android Icon \"{}\" not correctly " - "ordered".format(android_icons[i])) -exit(int(has_errors)) + if symlinks and sort_errors(symlinks, "Linux symlink", key): + has_errors = True + + if value.get("osx") and sort_errors(value["osx"], "OSX icon"): + has_errors = True + + if value.get("tags") and sort_errors(value["tags"], "Icon tag"): + has_errors = True + +if not has_errors: + success("Database is properly sorted") + +exit(has_errors) diff --git a/tests/pycodestyle.sh b/tests/pycodestyle.sh deleted file mode 100755 index 12b032c656..0000000000 --- a/tests/pycodestyle.sh +++ /dev/null @@ -1,2 +0,0 @@ -#!/usr/bin/env sh -find . -name "*.py" | xargs pycodestyle $1 diff --git a/tests/schema.json b/tests/schema.json index f8a26dd91c..cac5c110c1 100644 --- a/tests/schema.json +++ b/tests/schema.json @@ -4,6 +4,14 @@ "[a-zA-Z0-9]*$": { "type": "object", "properties": { + "android": { + "type": "array", + "items": { + "type": "string" + }, + "minItems": 1, + "uniqueItems": true + }, "linux": { "type": "object", "properties": { @@ -26,7 +34,15 @@ "root" ] }, - "android": { + "osx": { + "type": "array", + "items": { + "type": "string" + }, + "minItems": 1, + "uniqueItems": true + }, + "tags": { "type": "array", "items": { "type": "string" diff --git a/tests/schema.py b/tests/schema.py old mode 100755 new mode 100644 index 39c27bc0c4..a1bb6b0693 --- a/tests/schema.py +++ b/tests/schema.py @@ -1,23 +1,23 @@ #!/usr/bin/env python3 + """ -# Copyright (C) 2016 -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License (version 3+) as -# published by the Free Software Foundation. You should have received -# a copy of the GNU General Public License along with this program. -# If not, see . +Copyright (C) 2019 Numix Project +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License (version 3+) as +published by the Free Software Foundation. You should have received +a copy of the GNU General Public License along with this program. +If not, see . """ import json -from os import path - from jsonschema import validate, ValidationError from jsonschema.exceptions import SchemaError as SchemaError -from utils import error, success +from utils import error, success, DB_FILE, SCHEMA_FILE + +# Test which checks whether data.json matches with schema.json using +# jsonschema. Success is signified by exit code. -DB_FILE = path.join(path.dirname(path.abspath(__file__)), "../data.json") -SCHEMA_FILE = path.join(path.dirname(path.abspath(__file__)), 'schema.json') with open(SCHEMA_FILE, 'r') as schema_obj: SCHEMA = json.load(schema_obj) @@ -26,10 +26,11 @@ with open(DB_FILE, 'r') as db_obj: try: validate(json.load(db_obj), SCHEMA) - except (ValidationError, ValueError, SchemaError) as error: + except (ValidationError, ValueError, SchemaError) as thrown: has_errors = True error("Invalid database") - error("{}".format(error)) + error("{}".format(thrown)) else: success("The database is valid") -exit(int(has_errors)) + +exit(has_errors) diff --git a/tests/utils.py b/tests/utils.py index bd98f2070b..4b9b838f87 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -1,18 +1,28 @@ #!/usr/bin/env python3 + """ -# Copyright (C) 2016 -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License (version 3+) as -# published by the Free Software Foundation. You should have received -# a copy of the GNU General Public License along with this program. -# If not, see . +Copyright (C) 2019 Numix Project +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License (version 3+) as +published by the Free Software Foundation. You should have received +a copy of the GNU General Public License along with this program. +If not, see . """ +from os import path + +# Functions and variables which are used across the tests, defined +# here so as not to be duplicated. + + +# list of paths the tests need +ABS_PATH = path.dirname(path.abspath(__file__)) +DB_FILE = path.join(ABS_PATH, "../data.json") +ICONS_DIR = path.join(ABS_PATH, "../icons/") +SCHEMA_FILE = path.join(ABS_PATH, 'schema.json') -def sort(icons_list): - """Sort list case insensitive.""" - return sorted(icons_list, - key=lambda icon_name: icon_name.lower()) +# list of themes to test +THEMES = ["circle", "square"] def error(msg):