diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json new file mode 100644 index 00000000..831d039b --- /dev/null +++ b/.config/dotnet-tools.json @@ -0,0 +1,12 @@ +{ + "version": 1, + "isRoot": true, + "tools": { + "cake.tool": { + "version": "3.0.0", + "commands": [ + "dotnet-cake" + ] + } + } +} \ No newline at end of file diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 00000000..4a0771a5 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,14 @@ +[*.{sln,resx,cake}] +indent_style = tab + +[*.{cs,sh,py,json}] +indent_style = space +indent_size = 4 + +[*.{csproj,xml,css,md,blp}] +indent_style = space +indent_size = 2 + +[*.page] +indent_style = space +indent_size = 4 diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 00000000..2c89ce9c --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,62 @@ +on: + push: + branches: [ "main", "snap" ] + pull_request: + branches: [ "main" ] + types: [ "review_requested", "ready_for_review" ] + workflow_dispatch: +name: CI +permissions: + id-token: write + contents: read +jobs: + flatpak: + name: "GNOME on Flatpak" + runs-on: ubuntu-latest + if: ${{ github.event.pull_request.user.login != 'weblate' }} + container: + image: bilelmoussaoui/flatpak-github-actions:gnome-44 + options: --privileged + strategy: + matrix: + arch: [x86_64] + fail-fast: false + steps: + - uses: actions/checkout@v3 + - uses: flatpak/flatpak-github-actions/flatpak-builder@v6.1 + with: + bundle: org.nickvision.tagger.flatpak + manifest-path: flatpak/org.nickvision.tagger.json + cache-key: flatpak-builder-${{ github.sha }} + arch: ${{ matrix.arch }} + restore-cache: true + snap: + name: "GNOME on Snap" + runs-on: ubuntu-latest + strategy: + matrix: + arch: [x86_64] + fail-fast: false + steps: + - name: Checkout Git repository + uses: actions/checkout@v3 + - name: Build Snap + uses: snapcore/action-build@v1 + id: snapcraft-build + with: + snapcraft-args: "-v" + - uses: actions/upload-artifact@v3 + with: + name: org.nickvision.tagger-snap-x86_64 + path: ${{ steps.snapcraft-build.outputs.snap }} + #- uses: diddlesnaps/snapcraft-review-action@v1 + #with: + #snap: ${{ steps.snapcraft-build.outputs.snap }} + #isClassic: 'false' + #- name: Upload and release denaro Snap + #uses: snapcore/action-publish@v1 + #env: + #SNAPCRAFT_STORE_CREDENTIALS: ${{ secrets.STORE_LOGIN }} + #with: + #snap: ${{ steps.snapcraft-build.outputs.snap }} + #release: ${{ startsWith(github.ref, 'refs/tags/') && 'candidate' || 'edge'}} diff --git a/.github/workflows/snapcraft.yml b/.github/workflows/snapcraft.yml deleted file mode 100644 index 302e8ca1..00000000 --- a/.github/workflows/snapcraft.yml +++ /dev/null @@ -1,22 +0,0 @@ -name: 🧪 Test if Tagger snap can be built on x86_64 - -on: - push: - branches: [ main ] - pull_request: - branches: [ main ] - -jobs: - build: - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v2 - - - uses: snapcore/action-build@v1 - id: snapcraft-build - - - uses: actions/upload-artifact@v3 - with: - name: tagger-snap - path: ${{ steps.snapcraft-build.outputs.snap }} diff --git a/.github/workflows/spelling.yml b/.github/workflows/spelling.yml new file mode 100644 index 00000000..50cc1697 --- /dev/null +++ b/.github/workflows/spelling.yml @@ -0,0 +1,17 @@ +on: + push: + branches: [ "main" ] + pull_request: + branches: [ "main" ] + types: [ "review_requested", "ready_for_review" ] +name: Spell Check +jobs: + codespell: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: codespell-project/actions-codespell@master + with: + check_filenames: true + skip: cargo-sources.json,.git,*.svg,*.html,*.js,*.po,*.pot,*.page + ignore_words_list: gir diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 00000000..b01af164 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,192 @@ +# Contributing to Nickvision Application + +First off, thanks for taking the time to contribute! ❤️ + +All types of contributions are encouraged and valued. See the [Table of Contents](#table-of-contents) for different ways to help and details about how this project handles them. Please make sure to read the relevant section before making your contribution. It will make it a lot easier for us maintainers and smooth out the experience for all involved. The community looks forward to your contributions. 🎉 + +> And if you like the project, but just don't have time to contribute, that's fine. There are other easy ways to support the project and show your appreciation, which we would also be very happy about: +> - Star the project +> - Tweet about it +> - Refer this project in your project's readme +> - [Sponsor](https://github.com/sponsors/nlogozzo) the lead developer + +## Table of Contents + +- [I Have a Question](#i-have-a-question) +- [I Want To Contribute](#i-want-to-contribute) + - [Reporting Bugs](#reporting-bugs) + - [Suggesting Enhancements/New Features](#suggesting-enhancements) + - [Providing translations](#providing-translations) + + - [Manually](#manually) + - [Your First Code Contribution](#your-first-code-contribution) + - [Developing on Linux](#developing-on-linux) +- [Styleguides](#styleguides) +- [Join The Project Team](#join-the-project-team) + +## I Have a Question + +Before you ask a question, it is best to search for existing [Discussions](https://github.com/NickvisionApps/Tagger/discussions) and [Issues](https://github.com/NickvisionApps/Tagger/issues) that might help you. In case you have found a suitable issue and still need clarification, you can write your question in this discussion post. It is also advisable to search the internet for answers first. + +If you then still feel the need to ask a question and need clarification, we recommend the following: + +- Open a [Discussion](https://github.com/NickvisionApps/Tagger/discussions). +- Provide as much context as you can about what you're running into. +- Provide project and platform versions (windows, gnome, etc...), depending on what seems relevant. + +We will then take care of the question as soon as possible. + +## I Want To Contribute + +> ### Legal Notice +> When contributing to this project, you must agree that you have authored 100% of the content, that you have the necessary rights to the content and that the content you contribute may be provided under the project [license](LICENSE). + +### Reporting Bugs + +#### Before Submitting a Bug Report + +A good bug report shouldn't leave others needing to chase you up for more information. Therefore, we ask you to investigate carefully, collect information and describe the issue in detail in your report. Please complete the following steps in advance to help us fix any potential bug as fast as possible. + +- Make sure that you are using the latest released version. +- Determine if your bug is really a bug and not an error on your side. If you are looking for support, you might want to check [this section](#i-have-a-question). +- To see if other users have experienced (and potentially already solved) the same issue you are having, check if there is not already a bug report existing for your bug or error in both the [Discussions](https://github.com/NickvisionApps/Tagger/discussions) and [Issues](https://github.com/NickvisionApps/Tagger/issues) sections. +- Collect information about the bug: + - Debug information provided by the application + + - GNOME: from main menu open About Application → Troubleshooting → Debugging Information, here you can copy information to clipboard or save to a file. + - Stack trace (Traceback) + - Including any error messages thrown by the application + - You may need to start the application via the terminal/console to receive an error message for a crash. + - OS, Platform and Version (Linux/Distro, Kernel Version, x64/ARM) + - Possibly your input and the output + - Can you reliably reproduce the issue? And can you also reproduce it with older versions? + +#### How Do I Submit a Good Bug Report? + +> You must never report security related issues, vulnerabilities or bugs including sensitive information to the issue tracker, or elsewhere in public. Instead sensitive bugs must be sent by email to . + +We use GitHub issues to track bugs and errors. If you run into an issue with the project: + +- Open an [Issue](https://github.com/NickvisionApps/Tagger/issues/new). (Since we can't be sure at this point whether it is a bug or not, we ask you not to talk about a bug yet and not to label the issue.) +- Explain the behavior you would expect and the actual behavior. +- Please provide as much context as possible and describe the *reproduction steps* that someone else can follow to recreate the issue on their own. This usually includes your code. For good bug reports you should isolate the problem and create a reduced test case. +- Provide the information you collected in the previous section. + +Once it's filed: + +- The project team will label the issue accordingly. +- A team member will try to reproduce the issue with your provided steps. If there are no reproduction steps or no obvious way to reproduce the issue, the team will ask you for those steps. Bugs that are not able to be reproduced will not be addressed until they are reproduced. +- If the team is able to reproduce the issue, it will be marked as a `bug` and the issue will be left to be [implemented by someone](#your-first-code-contribution). + +### Suggesting Enhancements + +This section guides you through submitting an enhancement suggestion for Nickvision Application, **including completely new features and minor improvements to existing functionality**. Following these guidelines will help maintainers and the community to understand your suggestion and find related suggestions. + +#### Before Submitting an Enhancement + +- Make sure that you are using the latest released version. +- Perform a search through [Discussions](https://github.com/NickvisionApps/Tagger/discussions) and [Issues](https://github.com/NickvisionApps/Tagger/issues) to see if the enhancement has already been suggested. If it has, add a comment to the existing issue instead of opening a new one. +- Find out whether your idea fits with the scope and aims of the project. It's up to you to make a strong case to convince the project's developers of the merits of this feature. Keep in mind that we want features that will be useful to the majority of our users and not just a small subset. + +#### How Do I Submit a Good Enhancement Suggestion? + +Enhancement suggestions are tracked as [GitHub issues](https://github.com/NickvisionApps/Tagger/issues). + +- Use a **clear and descriptive title** for the issue to identify the suggestion. +- Provide a **step-by-step description of the suggested enhancement** in as many details as possible. +- **Describe the current behavior** and **explain which behavior you expected to see instead** and why. At this point you can also tell which alternatives do not work for you. +- You may want to **include screenshots and animated GIFs** which help you demonstrate the steps or point out the part which the suggestion is related to. You can use [this tool](https://www.cockos.com/licecap/) to record GIFs on macOS and Windows, and [this tool](https://github.com/colinkeenan/silentcast) or [this tool](https://flathub.org/apps/details/io.github.seadve.Kooha) on Linux. +- **Explain why this enhancement would be useful** to most Nickvision Application users. You may also want to point out the other projects that solved it better and which could serve as inspiration. + +### Providing Translations + +Everyone is welcome to translate this app into their native or known languages, so that the application is accessible to everyone. + + +##### Manually + +To start translating the app, fork the repository and clone it locally. + +Application uses [gettext](https://www.gnu.org/software/gettext/manual/gettext.html#PO-Files) for translations. In the `NickvisionTagger.Shared/Resources/po` you will find files that can be edited in your favourite `*.po` files editor or any text editor. If you want to create a new translation, copy `application.pot` file, name the new file `.po`, where `` is the language code for your translation (usually it's 2 letters, but it also can be a locale code to differentiate between different version of the same language, for example `pt` and `pt_BR`) and edit this file. Also add the language code to `LINGUAS` file (please keep codes sorted alphabetically there). + +To check your translation file, make sure your system is in the locale of the language you are translating and run the app. You should see your translated strings! + +In case you run the app in GNOME Builder, it will force the app to run in en_US locale. To run the app in your locale without exporting and installing it, follow this steps: + +1. Build the application +2. Press Ctrl+Alt+T to open a terminal inside the application environment +3. Run the application with the following command: `LC_ALL= /app/opt/org.nickvision.tagger/NickvisionTagger.GNOME`, where `` is your system locale code (e.g. `it_IT.UTF8`). + +Once all changes to your translated file are made, commit these changes and create a pull request to the project. + +### Your First Code Contribution + +#### Structure + +Application is built using .NET 7 and C#. With these technologies, Application is built for GNOME (Linux). +The solution is setup into 2 projects: + - NickvisionTagger.Shared + - NickvisionTagger.GNOME + +The whole solution utilizes the [MVC](https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller) pattern for separating data and UI views. + +##### NickvisionTagger.Shared + +This project contains all of the code used by all platforms of the app. +- Models => The data driven objects of the application (i.e. Configuration, Database, etc...) +- Controllers => The objects used by UI views to receive and manipulate data from the Models +- Helpers => Useful objects and functions such as the Gettext functions for receiving translated strings throughout the app +- Resources => Strings, icons, and fonts used by the app + +##### NickvisionTagger.GNOME + +This project contains all of the code used for the GNOME platform version of the app, including flathub manifest and desktop files. +Powered by the C# bindings for GTK4/Libadwaita: [gir.core](https://github.com/gircore/gir.core) +- Views => The views (pages, windows, dialogs) of the app that connect to the shared controllers +- Controls => Generic controls for the app + - These controls should not be connected to a controller and should be able to be ported to any other application +- Helpers => Useful objects that are specific for GNOME platform version of the app +- Blueprints => UI files written in [Blueprint markup language](https://jwestman.pages.gitlab.gnome.org/blueprint-compiler/) + +#### Developing on Linux + +##### GNOME + +Recommended IDEs: +- GNOME Builder 43 and up. +- VS Code with [flatpak extension](https://github.com/bilelmoussaoui/flatpak-vscode). + +You may also make your changes via any code editor and use [`flatpak-builder`](https://docs.flatpak.org/en/latest/flatpak-builder.html) to run the application locally through flatpak. + +You may also build the app manually without using flatpak. List of dependencies: +- dotnet >=7.0 +- GTK >=4.10 +- libadwaita >=1.2 +- [cake](https://cakebuild.net/) (build only) + - `dotnet tool install --global Cake.Tool` or `dotnet tool restore` (in repository root folder) +- blueprint-compiler, GTK and libadwaita development files (build only) +- glib-compile-resources (build only) + +Use one of the commands to build the app: + +| Command | Result | +|---|---| +| `dotnet cake --target=Run --ui=gnome` (in repo root folder) or `dotnet run` (in project subfolder) | Builds the application and runs it. Application will not get installed, which might result in some missing icons and lack of desktop integration. | +| `dotnet cake --target=Publish --prefix=PREFIX --ui=gnome` | Builds the application in `_nickbuild` directory, preparing it to be installed in a provided prefix (examples of a valid prefix: `/usr`, `/app`). If `--self-contained` is added, the application will not need dotnet-runtime to run. +| `dotnet cake --target=Install --destdir=DESTDIR` | Copies files to the `DESTDIR`. `--destdir` is optional, by default files are copied to root (`/`). This command should be used after `Publish`. | + +## Styleguides + +Application follows [Microsoft's C# Coding Conventions](https://learn.microsoft.com/en-us/dotnet/csharp/fundamentals/coding-style/coding-conventions). + +See [Microsoft's C# Identifier Names](https://learn.microsoft.com/en-us/dotnet/csharp/fundamentals/coding-style/identifier-names) as well. + +## Join The Project Team + +Join our room + +## Attribution +This guide is based on the **contributing-gen**. [Make your own](https://github.com/bttger/contributing-gen)! diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000..4f59b640 --- /dev/null +++ b/LICENSE @@ -0,0 +1,7 @@ +Copyright (c) 2021-2023 Nicholas Logozzo + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/NickvisionTagger.GNOME/.fleet/run.json b/NickvisionTagger.GNOME/.fleet/run.json new file mode 100644 index 00000000..dabdccfd --- /dev/null +++ b/NickvisionTagger.GNOME/.fleet/run.json @@ -0,0 +1,12 @@ +{ + "configurations": [ + { + "type": "dotnet", + "name": "Tagger GNOME", + "workingDir": "$PROJECT_DIR$/bin/Debug/net7.0", + "projectPath": "$PROJECT_DIR$/NickvisionTagger.GNOME.csproj", + "args": [], + }, + + ] +} diff --git a/NickvisionTagger.GNOME/Blueprints/preferences_dialog.blp b/NickvisionTagger.GNOME/Blueprints/preferences_dialog.blp new file mode 100644 index 00000000..f992f803 --- /dev/null +++ b/NickvisionTagger.GNOME/Blueprints/preferences_dialog.blp @@ -0,0 +1,30 @@ +using Gtk 4.0; +using Adw 1; + +Adw.PreferencesWindow _root { + default-width: 600; + default-height: 400; + modal: true; + destroy-with-parent: false; + hide-on-close: true; + title: _("Preferences"); + + Adw.PreferencesPage { + Adw.PreferencesGroup { + title: _("User Interface"); + description: _("Customize the application's user interface."); + + Adw.ComboRow _themeRow { + title: _("Theme"); + model: Gtk.StringList { + strings [C_("Theme", "Light"), C_("Theme", "Dark"), C_("Theme", "System")] + }; + + [prefix] + Gtk.Image { + icon-name: "dark-mode-symbolic"; + } + } + } + } +} \ No newline at end of file diff --git a/NickvisionTagger.GNOME/Blueprints/shortcuts_dialog.blp b/NickvisionTagger.GNOME/Blueprints/shortcuts_dialog.blp new file mode 100644 index 00000000..fab0d149 --- /dev/null +++ b/NickvisionTagger.GNOME/Blueprints/shortcuts_dialog.blp @@ -0,0 +1,51 @@ +using Gtk 4.0; +using Adw 1; + +Gtk.ShortcutsWindow _shortcuts { + default-width: 600; + default-height: 500; + modal: true; + resizable: true; + destroy-with-parent: false; + hide-on-close: true; + + Gtk.ShortcutsSection { + Gtk.ShortcutsGroup { + title: C_("Shortcut", "Folder"); + + Gtk.ShortcutsShortcut { + title: _("Open Folder"); + accelerator: "o"; + } + + Gtk.ShortcutsShortcut { + title: _("Close Folder"); + accelerator: "w"; + } + } + + Gtk.ShortcutsGroup { + title: C_("Shortcut", "Application"); + + Gtk.ShortcutsShortcut { + title: _("Preferences"); + accelerator: "comma"; + } + + Gtk.ShortcutsShortcut { + title: _("Keyboard Shortcuts"); + accelerator: "question"; + } + + Gtk.ShortcutsShortcut _aboutShortcut { + title: _("About Tagger"); + accelerator: "F1"; + } + + Gtk.ShortcutsShortcut { + title: _("Quit"); + accelerator: "q"; + } + } + } +} \ No newline at end of file diff --git a/NickvisionTagger.GNOME/Blueprints/window.blp b/NickvisionTagger.GNOME/Blueprints/window.blp new file mode 100644 index 00000000..c6818275 --- /dev/null +++ b/NickvisionTagger.GNOME/Blueprints/window.blp @@ -0,0 +1,86 @@ +using Gtk 4.0; +using Adw 1; + +menu mainMenu { + item(_("Preferences"), "win.preferences") + item(_("Keyboard Shortcuts"), "win.keyboardShortcuts") + item(_("About Tagger"), "win.about") +} + +Adw.ApplicationWindow _root { + default-width: 800; + default-height: 600; + + Gtk.Box { + orientation: vertical; + + Adw.HeaderBar _headerBar { + title-widget: Adw.WindowTitle _title {}; + + [start] + Gtk.Button _openFolderButton { + tooltip-text: _("Open Folder (Ctrl+O)"); + visible: false; + action-name: "win.openFolder"; + + Adw.ButtonContent { + label: _("Open"); + icon-name: "folder-open-symbolic"; + } + } + + [start] + Gtk.Button _closeFolderButton { + icon-name: "window-close-symbolic"; + tooltip-text: _("Close Folder (Ctrl+W)"); + action-name: "win.closeFolder"; + visible: false; + } + + [end] + Gtk.MenuButton { + direction: none; + menu-model: mainMenu; + tooltip-text: _("Main Menu"); + primary: true; + } + + styles ["flat"] + } + + Adw.ToastOverlay _toastOverlay { + hexpand: true; + vexpand: true; + + Adw.ViewStack _viewStack { + Adw.ViewStackPage { + name: "NoFolder"; + child: Adw.StatusPage _greeting { + icon-name: "org.nickvision.tagger"; + description: _("Open a folder (or drag one into the app) to get started."); + child: Gtk.Button { + label: _("Open"); + halign: center; + action-name: "win.openFolder"; + styles ["pill", "suggested-action"] + }; + + styles ["icon-dropshadow"] + }; + } + + Adw.ViewStackPage { + name: "Folder"; + child: Gtk.Box { + orientation: vertical; + + Gtk.Label _filesLabel { + hexpand: true; + vexpand: true; + } + }; + } + } + } + } +} diff --git a/NickvisionTagger.GNOME/Controls/MessageDialog.cs b/NickvisionTagger.GNOME/Controls/MessageDialog.cs new file mode 100644 index 00000000..eaf8c01f --- /dev/null +++ b/NickvisionTagger.GNOME/Controls/MessageDialog.cs @@ -0,0 +1,107 @@ +namespace NickvisionTagger.GNOME.Controls; + +/// +/// Responses for the MessageDialog +/// +public enum MessageDialogResponse +{ + Suggested, + Destructive, + Cancel +} + +/// +/// A dialog for showing a message +/// +public partial class MessageDialog +{ + private readonly Adw.MessageDialog _dialog; + + public MessageDialogResponse Response { get; private set; } + + /// + /// Whether or not the dialog is visible + /// + public bool Visible => _dialog.GetVisible(); + + /// + /// Constructs a MessageDialog + /// + /// Gtk.Window + /// The name of the icon of the dialog + /// The title of the dialog + /// The message of the dialog + /// The text of the cancel button + /// The text of the destructive button + /// The text of the suggested button + public MessageDialog(Gtk.Window parentWindow, string iconName, string title, string message, string? cancelText, string? destructiveText = null, string? suggestedText = null) + { + _dialog = Adw.MessageDialog.New(parentWindow, title, message); + _dialog.SetHideOnClose(true); + _dialog.SetIconName(iconName); + Response = MessageDialogResponse.Cancel; + if (!string.IsNullOrEmpty(cancelText)) + { + _dialog.AddResponse("cancel", cancelText); + _dialog.SetDefaultResponse("cancel"); + _dialog.SetCloseResponse("cancel"); + } + if (!string.IsNullOrEmpty(destructiveText)) + { + _dialog.AddResponse("destructive", destructiveText); + _dialog.SetResponseAppearance("destructive", Adw.ResponseAppearance.Destructive); + } + if (!string.IsNullOrEmpty(suggestedText)) + { + _dialog.AddResponse("suggested", suggestedText); + _dialog.SetResponseAppearance("suggested", Adw.ResponseAppearance.Suggested); + } + _dialog.OnResponse += (sender, e) => SetResponse(e.Response); + } + + public event GObject.SignalHandler OnResponse + { + add + { + _dialog.OnResponse += value; + } + remove + { + _dialog.OnResponse -= value; + } + } + + /// + /// Presents the dialog + /// + public void Present() => _dialog.Present(); + + /// + /// Destroys the dialog + /// + public void Destroy() => _dialog.Destroy(); + + /// + /// Resets the destructive response appearance to default + /// + public void UnsetDestructiveApperance() => _dialog.SetResponseAppearance("destructive", Adw.ResponseAppearance.Default); + + /// + /// Resets the suggested response appearance to default + /// + public void UnsetSuggestedApperance() => _dialog.SetResponseAppearance("suggested", Adw.ResponseAppearance.Default); + + /// + /// Sets the response of the dialog as a MessageDialogResponse + /// + /// The string response of the dialog + private void SetResponse(string response) + { + Response = response switch + { + "suggested" => MessageDialogResponse.Suggested, + "destructive" => MessageDialogResponse.Destructive, + _ => MessageDialogResponse.Cancel + }; + } +} \ No newline at end of file diff --git a/NickvisionTagger.GNOME/Helpers/Builder.cs b/NickvisionTagger.GNOME/Helpers/Builder.cs new file mode 100644 index 00000000..c3bf5057 --- /dev/null +++ b/NickvisionTagger.GNOME/Helpers/Builder.cs @@ -0,0 +1,41 @@ +using System.IO; +using System.Reflection; +using System.Xml; +using static NickvisionTagger.Shared.Helpers.Gettext; + +namespace NickvisionTagger.GNOME.Helpers; + +public class Builder +{ + /// + /// Creates a Gtk.Builder from an embedded resource and replaces all translatable strings with the localized version + /// + /// The name of the embedded resource + /// Gtk.Builder + public static Gtk.Builder FromFile(string name) + { + using var stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(name); + using var reader = new StreamReader(stream!); + var uiContents = reader.ReadToEnd(); + var xml = new XmlDocument(); + xml.LoadXml(uiContents); + var elements = xml.GetElementsByTagName("*"); + foreach (XmlElement element in elements) + { + if (element.HasAttribute("translatable")) + { + element.RemoveAttribute("translatable"); + if (element.HasAttribute("context")) + { + var context = element.GetAttribute("context"); + element.InnerText = _p(context, element.InnerText); + } + else + { + element.InnerText = _(element.InnerText); + } + } + } + return Gtk.Builder.NewFromString(xml.OuterXml, -1); + } +} \ No newline at end of file diff --git a/NickvisionTagger.GNOME/NickvisionTagger.GNOME.csproj b/NickvisionTagger.GNOME/NickvisionTagger.GNOME.csproj new file mode 100644 index 00000000..90b417be --- /dev/null +++ b/NickvisionTagger.GNOME/NickvisionTagger.GNOME.csproj @@ -0,0 +1,47 @@ + + + + Exe + net7.0 + enable + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/NickvisionTagger.GNOME/Program.cs b/NickvisionTagger.GNOME/Program.cs new file mode 100644 index 00000000..8befe5af --- /dev/null +++ b/NickvisionTagger.GNOME/Program.cs @@ -0,0 +1,120 @@ +using NickvisionTagger.GNOME.Views; +using NickvisionTagger.Shared.Controllers; +using NickvisionTagger.Shared.Models; +using System; +using System.Collections.Generic; +using System.IO; +using System.Reflection; +using System.Runtime.InteropServices; +using static NickvisionTagger.Shared.Helpers.Gettext; + +namespace NickvisionTagger.GNOME; + +/// +/// The Program +/// +public partial class Program +{ + [LibraryImport("libadwaita-1.so.0", StringMarshalling = StringMarshalling.Utf8)] + private static partial nuint gtk_file_chooser_cell_get_type(); + + [LibraryImport("libadwaita-1.so.0", StringMarshalling = StringMarshalling.Utf8)] + private static partial nint g_resource_load(string path); + + [LibraryImport("libadwaita-1.so.0", StringMarshalling = StringMarshalling.Utf8)] + private static partial void g_resources_register(nint file); + + private readonly Adw.Application _application; + private MainWindow? _mainWindow; + private MainWindowController _mainWindowController; + + /// + /// Main method + /// + /// string[] + /// Return code from Adw.Application.Run() + public static int Main(string[] args) => new Program().Run(args); + + /// + /// Constructs a Program + /// + public Program() + { + gtk_file_chooser_cell_get_type(); + _application = Adw.Application.New("org.nickvision.tagger", Gio.ApplicationFlags.FlagsNone); + _mainWindow = null; + _mainWindowController = new MainWindowController(); + _mainWindowController.AppInfo.ID = "org.nickvision.tagger"; + _mainWindowController.AppInfo.Name = "Nickvision Tagger"; + _mainWindowController.AppInfo.ShortName = _("Tagger"); + _mainWindowController.AppInfo.Description = $"{_("Manage your music library with ease")}."; + _mainWindowController.AppInfo.Version = "2023.7.0-next"; + _mainWindowController.AppInfo.Changelog = "
  • Initial Release
"; + _mainWindowController.AppInfo.GitHubRepo = new Uri("https://github.com/NickvisionApps/Tagger"); + _mainWindowController.AppInfo.IssueTracker = new Uri("https://github.com/NickvisionApps/Tagger/issues/new"); + _mainWindowController.AppInfo.SupportUrl = new Uri("https://github.com/NickvisionApps/Tagger/discussions"); + _application.OnActivate += OnActivate; + if (File.Exists(Path.GetFullPath(System.IO.Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)) + "/org.nickvision.tagger.gresource")) + { + //Load file from program directory, required for `dotnet run` + g_resources_register(g_resource_load(Path.GetFullPath(System.IO.Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)) + "/org.nickvision.tagger.gresource")); + } + else + { + var prefixes = new List { + Directory.GetParent(Directory.GetParent(Path.GetFullPath(System.IO.Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location))).FullName).FullName, + Directory.GetParent(Path.GetFullPath(System.IO.Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location))).FullName, + "/usr" + }; + foreach (var prefix in prefixes) + { + if (File.Exists(prefix + "/share/org.nickvision.tagger/org.nickvision.tagger.gresource")) + { + g_resources_register(g_resource_load(Path.GetFullPath(prefix + "/share/org.nickvision.tagger/org.nickvision.tagger.gresource"))); + break; + } + } + } + } + + /// + /// Runs the program + /// + /// Return code from Adw.Application.Run() + public int Run(string[] args) + { + try + { + var argv = new string[args.Length + 1]; + argv[0] = $"{_mainWindowController.AppInfo.Name}.GNOME"; + args.CopyTo(argv, 1); + return _application.Run(args.Length + 1, argv); + } + catch (Exception ex) + { + Console.WriteLine(ex.Message); + Console.WriteLine($"\n\n{ex.StackTrace}"); + return -1; + } + } + + /// + /// Occurs when the application is activated + /// + /// Gio.Application + /// EventArgs + private void OnActivate(Gio.Application sedner, EventArgs e) + { + //Set Adw Theme + _application.StyleManager!.ColorScheme = _mainWindowController.Theme switch + { + Theme.System => Adw.ColorScheme.PreferLight, + Theme.Light => Adw.ColorScheme.ForceLight, + Theme.Dark => Adw.ColorScheme.ForceDark, + _ => Adw.ColorScheme.PreferLight + }; + //Main Window + _mainWindow = new MainWindow(_mainWindowController, _application); + _mainWindow.Start(); + } +} diff --git a/NickvisionTagger.GNOME/Resources/dark-mode-symbolic.svg b/NickvisionTagger.GNOME/Resources/dark-mode-symbolic.svg new file mode 100644 index 00000000..94eee23e --- /dev/null +++ b/NickvisionTagger.GNOME/Resources/dark-mode-symbolic.svg @@ -0,0 +1,2 @@ + + diff --git a/document-modified-symbolic.svg b/NickvisionTagger.GNOME/Resources/document-modified-symbolic.svg similarity index 100% rename from document-modified-symbolic.svg rename to NickvisionTagger.GNOME/Resources/document-modified-symbolic.svg diff --git a/NickvisionTagger.GNOME/Resources/org.nickvision.tagger.gresource.xml b/NickvisionTagger.GNOME/Resources/org.nickvision.tagger.gresource.xml new file mode 100644 index 00000000..e67865df --- /dev/null +++ b/NickvisionTagger.GNOME/Resources/org.nickvision.tagger.gresource.xml @@ -0,0 +1,7 @@ + + + + dark-mode-symbolic.svg + document-modified-symbolic.svg + + diff --git a/NickvisionTagger.GNOME/Screenshots/Dark.png b/NickvisionTagger.GNOME/Screenshots/Dark.png new file mode 100644 index 00000000..60260b27 Binary files /dev/null and b/NickvisionTagger.GNOME/Screenshots/Dark.png differ diff --git a/NickvisionTagger.GNOME/Screenshots/Light.png b/NickvisionTagger.GNOME/Screenshots/Light.png new file mode 100644 index 00000000..3c8bee74 Binary files /dev/null and b/NickvisionTagger.GNOME/Screenshots/Light.png differ diff --git a/NickvisionTagger.GNOME/Views/MainWindow.cs b/NickvisionTagger.GNOME/Views/MainWindow.cs new file mode 100644 index 00000000..411dafe1 --- /dev/null +++ b/NickvisionTagger.GNOME/Views/MainWindow.cs @@ -0,0 +1,357 @@ +using NickvisionTagger.GNOME.Controls; +using NickvisionTagger.GNOME.Helpers; +using NickvisionTagger.Shared.Controllers; +using NickvisionTagger.Shared.Events; +using System; +using System.Diagnostics; +using System.Globalization; +using System.IO; +using System.Runtime.InteropServices; +using System.Text; +using static NickvisionTagger.Shared.Helpers.Gettext; + +namespace NickvisionTagger.GNOME.Views; + +/// +/// The MainWindow for the application +/// +public partial class MainWindow : Adw.ApplicationWindow +{ + private delegate void GAsyncReadyCallback(nint source, nint res, nint user_data); + + [LibraryImport("libadwaita-1.so.0", StringMarshalling = StringMarshalling.Utf8)] + private static partial string g_file_get_path(nint file); + + [LibraryImport("libadwaita-1.so.0", StringMarshalling = StringMarshalling.Utf8)] + private static partial nint gtk_file_dialog_new(); + + [LibraryImport("libadwaita-1.so.0", StringMarshalling = StringMarshalling.Utf8)] + private static partial void gtk_file_dialog_set_title(nint dialog, string title); + + [LibraryImport("libadwaita-1.so.0", StringMarshalling = StringMarshalling.Utf8)] + private static partial void gtk_file_dialog_select_folder(nint dialog, nint parent, nint cancellable, GAsyncReadyCallback callback, nint user_data); + + [LibraryImport("libadwaita-1.so.0", StringMarshalling = StringMarshalling.Utf8)] + private static partial nint gtk_file_dialog_select_folder_finish(nint dialog, nint result, nint error); + + [LibraryImport("libadwaita-1.so.0", StringMarshalling = StringMarshalling.Utf8)] + private static partial nint g_file_new_for_path(string path); + + [LibraryImport("libadwaita-1.so.0", StringMarshalling = StringMarshalling.Utf8)] + private static partial nint g_file_icon_new(nint gfile); + + [LibraryImport("libadwaita-1.so.0", StringMarshalling = StringMarshalling.Utf8)] + private static partial void g_notification_set_icon(nint notification, nint icon); + + private readonly MainWindowController _controller; + private readonly Adw.Application _application; + private GAsyncReadyCallback? _saveCallback; + + [Gtk.Connect] private readonly Adw.HeaderBar _headerBar; + [Gtk.Connect] private readonly Adw.WindowTitle _title; + [Gtk.Connect] private readonly Gtk.Button _openFolderButton; + [Gtk.Connect] private readonly Gtk.Button _closeFolderButton; + [Gtk.Connect] private readonly Adw.ToastOverlay _toastOverlay; + [Gtk.Connect] private readonly Adw.ViewStack _viewStack; + [Gtk.Connect] private readonly Adw.StatusPage _greeting; + [Gtk.Connect] private readonly Gtk.Label _filesLabel; + private readonly Gtk.DropTarget _dropTarget; + + private MainWindow(Gtk.Builder builder, MainWindowController controller, Adw.Application application) : base(builder.GetPointer("_root"), false) + { + //Window Settings + _controller = controller; + _application = application; + _saveCallback = null; + SetDefaultSize(800, 600); + SetTitle(_controller.AppInfo.ShortName); + SetIconName(_controller.AppInfo.ID); + if (_controller.IsDevVersion) + { + AddCssClass("devel"); + } + //Build UI + builder.Connect(this); + _title.SetTitle(_controller.AppInfo.ShortName); + _greeting.SetTitle(_controller.Greeting); + //Register Events + _controller.NotificationSent += NotificationSent; + _controller.ShellNotificationSent += ShellNotificationSent; + _controller.FolderChanged += FolderChanged; + //Open Folder Action + var actOpenFolder = Gio.SimpleAction.New("openFolder", null); + actOpenFolder.OnActivate += OpenFolder; + AddAction(actOpenFolder); + application.SetAccelsForAction("win.openFolder", new string[] { "O" }); + //Close Folder Action + var actCloseFolder = Gio.SimpleAction.New("closeFolder", null); + actCloseFolder.OnActivate += CloseFolder; + AddAction(actCloseFolder); + application.SetAccelsForAction("win.closeFolder", new string[] { "W" }); + //Preferences Action + var actPreferences = Gio.SimpleAction.New("preferences", null); + actPreferences.OnActivate += Preferences; + AddAction(actPreferences); + application.SetAccelsForAction("win.preferences", new string[] { "comma" }); + //Keyboard Shortcuts Action + var actKeyboardShortcuts = Gio.SimpleAction.New("keyboardShortcuts", null); + actKeyboardShortcuts.OnActivate += KeyboardShortcuts; + AddAction(actKeyboardShortcuts); + application.SetAccelsForAction("win.keyboardShortcuts", new string[] { "question" }); + //Quit Action + var actQuit = Gio.SimpleAction.New("quit", null); + actQuit.OnActivate += Quit; + AddAction(actQuit); + application.SetAccelsForAction("win.quit", new string[] { "q" }); + //About Action + var actAbout = Gio.SimpleAction.New("about", null); + actAbout.OnActivate += About; + AddAction(actAbout); + application.SetAccelsForAction("win.about", new string[] { "F1" }); + //Drop Target + _dropTarget = Gtk.DropTarget.New(Gio.FileHelper.GetGType(), Gdk.DragAction.Copy); + _dropTarget.OnDrop += OnDrop; + AddController(_dropTarget); + } + + /// + /// Constructs a MainWindow + /// + /// The MainWindowController + /// The Adw.Application + public MainWindow(MainWindowController controller, Adw.Application application) : this(Builder.FromFile("window.ui"), controller, application) + { + } + + /// + /// Starts the MainWindow + /// + public void Start() + { + _application.AddWindow(this); + Present(); + } + + /// + /// Occurs when a notification is sent from the controller + /// + /// object? + /// NotificationSentEventArgs + private void NotificationSent(object? sender, NotificationSentEventArgs e) + { + var toast = Adw.Toast.New(e.Message); + if (e.Action == "close") + { + toast.SetButtonLabel(_("Close")); + toast.OnButtonClicked += (sender, ex) => _controller.CloseFolder(); + } + _toastOverlay.AddToast(toast); + } + + /// + /// Occurs when a shell notification is sent from the controller + /// + /// object? + /// ShellNotificationSentEventArgs + private void ShellNotificationSent(object? sender, ShellNotificationSentEventArgs? e) + { + var notification = Gio.Notification.New(e.Title); + notification.SetBody(e.Message); + notification.SetPriority(e.Severity switch + { + NotificationSeverity.Success => Gio.NotificationPriority.High, + NotificationSeverity.Warning => Gio.NotificationPriority.Urgent, + NotificationSeverity.Error => Gio.NotificationPriority.Urgent, + _ => Gio.NotificationPriority.Normal + }); + if (string.IsNullOrEmpty(Environment.GetEnvironmentVariable("SNAP"))) + { + notification.SetIcon(Gio.ThemedIcon.New($"{_controller.AppInfo.ID}-symbolic")); + } + else + { + var iconHandle = g_file_icon_new(g_file_new_for_path($"{Environment.GetEnvironmentVariable("SNAP")}/usr/share/icons/hicolor/symbolic/apps/{_controller.AppInfo.ID}-symbolic.svg")); + g_notification_set_icon(notification.Handle, iconHandle); + } + _application.SendNotification(_controller.AppInfo.ID, notification); + } + + /// + /// Occurs when something is dropped onto the window + /// + /// Gtk.DropTarget + /// Gtk.DropTarget.DropSignalArgs + private bool OnDrop(Gtk.DropTarget sender, Gtk.DropTarget.DropSignalArgs e) + { + var obj = e.Value.GetObject(); + if (obj != null) + { + var path = g_file_get_path(obj.Handle); + if (Directory.Exists(path)) + { + _controller.OpenFolder(path); + return true; + } + } + return false; + } + + /// + /// Occurs when the folder is changed + /// + /// object? + /// EventArgs + private void FolderChanged(object? sender, EventArgs e) + { + _title.SetSubtitle(_controller.IsFolderOpened ? _controller.FolderPath : ""); + _openFolderButton.SetVisible(_controller.IsFolderOpened); + _closeFolderButton.SetVisible(_controller.IsFolderOpened); + _viewStack.SetVisibleChildName(_controller.IsFolderOpened ? "Folder" : "NoFolder"); + if(_controller.IsFolderOpened) + { + _headerBar.RemoveCssClass("flat"); + } + else + { + _headerBar.AddCssClass("flat"); + } + } + + /// + /// Occurs when the open folder action is triggered + /// + /// Gio.SimpleAction + /// EventArgs + private void OpenFolder(Gio.SimpleAction sender, EventArgs e) + { + var folderDialog = gtk_file_dialog_new(); + gtk_file_dialog_set_title(folderDialog, _("Open Folder")); + _saveCallback = (source, res, data) => + { + var fileHandle = gtk_file_dialog_select_folder_finish(folderDialog, res, IntPtr.Zero); + if (fileHandle != IntPtr.Zero) + { + var path = g_file_get_path(fileHandle); + _controller.OpenFolder(path); + _filesLabel.SetLabel(_n("There is {0} file in the folder.", "There are {0} files in the folder.", Directory.GetFiles(path, "*", SearchOption.TopDirectoryOnly).Length)); + } + }; + gtk_file_dialog_select_folder(folderDialog, Handle, IntPtr.Zero, _saveCallback, IntPtr.Zero); + } + + /// + /// Occurs when the close folder action is triggered + /// + /// Gio.SimpleAction + /// EventArgs + private void CloseFolder(Gio.SimpleAction sender, EventArgs e) + { + var dialog = new MessageDialog(this, _controller.AppInfo.ID, _("Close Folder?"), _("Are you sure you want to close the folder?"), _("Cancel"), _("Close")); + dialog.Present(); + dialog.OnResponse += (sender, e) => + { + if (dialog.Response == MessageDialogResponse.Destructive) + { + _controller.CloseFolder(); + } + dialog.Destroy(); + }; + } + + /// + /// Occurs when the preferences action is triggered + /// + /// Gio.SimpleAction + /// EventArgs + private void Preferences(Gio.SimpleAction sender, EventArgs e) + { + var preferencesDialog = new PreferencesDialog(_controller.CreatePreferencesViewController(), _application, this); + preferencesDialog.Present(); + } + + /// + /// Occurs when the keyboard shortcuts action is triggered + /// + /// Gio.SimpleAction + /// EventArgs + private void KeyboardShortcuts(Gio.SimpleAction sender, EventArgs e) + { + var builder = Builder.FromFile("shortcuts_dialog.ui"); + var shortcutsWindow = (Gtk.ShortcutsWindow)builder.GetObject("_shortcuts"); + shortcutsWindow.SetTransientFor(this); + shortcutsWindow.SetIconName(_controller.AppInfo.ID); + shortcutsWindow.Present(); + } + + /// + /// Occurs when quit action is triggered + /// + /// Gio.SimpleAction + /// EventArgs + private void Quit(Gio.SimpleAction sender, EventArgs e) => _application.Quit(); + + /// + /// Occurs when the about action is triggered + /// + /// Gio.SimpleAction + /// EventArgs + private void About(Gio.SimpleAction sender, EventArgs e) + { + var debugInfo = new StringBuilder(); + debugInfo.AppendLine(_controller.AppInfo.ID); + debugInfo.AppendLine(_controller.AppInfo.Version); + debugInfo.AppendLine($"GTK {Gtk.Functions.GetMajorVersion()}.{Gtk.Functions.GetMinorVersion()}.{Gtk.Functions.GetMicroVersion()}"); + debugInfo.AppendLine($"libadwaita {Adw.Functions.GetMajorVersion()}.{Adw.Functions.GetMinorVersion()}.{Adw.Functions.GetMicroVersion()}"); + if (File.Exists("/.flatpak-info")) + { + debugInfo.AppendLine("Flatpak"); + } + else if (!string.IsNullOrEmpty(Environment.GetEnvironmentVariable("SNAP"))) + { + debugInfo.AppendLine("Snap"); + } + debugInfo.AppendLine(CultureInfo.CurrentCulture.ToString()); + var localeProcess = new Process + { + StartInfo = new ProcessStartInfo + { + FileName = "locale", + UseShellExecute = false, + RedirectStandardOutput = true + } + }; + try + { + localeProcess.Start(); + var localeString = localeProcess.StandardOutput.ReadToEnd().Trim(); + localeProcess.WaitForExit(); + debugInfo.AppendLine(localeString); + } + catch + { + debugInfo.AppendLine("Unknown locale"); + } + var dialog = Adw.AboutWindow.New(); + dialog.SetTransientFor(this); + dialog.SetIconName(_controller.AppInfo.ID); + dialog.SetApplicationName(_controller.AppInfo.ShortName); + dialog.SetApplicationIcon(_controller.AppInfo.ID + (_controller.AppInfo.GetIsDevelVersion() ? "-devel" : "")); + dialog.SetVersion(_controller.AppInfo.Version); + dialog.SetDebugInfo(debugInfo.ToString()); + dialog.SetComments(_controller.AppInfo.Description); + dialog.SetDeveloperName("Nickvision"); + dialog.SetLicenseType(Gtk.License.MitX11); + dialog.SetCopyright("© Nickvision 2021-2023"); + dialog.SetWebsite("https://nickvision.org/"); + dialog.SetIssueUrl(_controller.AppInfo.IssueTracker.ToString()); + dialog.SetSupportUrl(_controller.AppInfo.SupportUrl.ToString()); + dialog.AddLink(_("GitHub Repo"), _controller.AppInfo.GitHubRepo.ToString()); + dialog.AddLink(_("Matrix Chat"), "https://matrix.to/#/#nickvision:matrix.org"); + dialog.SetDevelopers(string.Format(_("Nicholas Logozzo {0}\nContributors on GitHub ❤️ {1}"), "https://github.com/nlogozzo", "https://github.com/NickvisionApps/Tagger/graphs/contributors").Split("\n")); + dialog.SetDesigners(string.Format(_("Nicholas Logozzo {0}\nFyodor Sobolev {1}"), "https://github.com/nlogozzo", "https://github.com/fsobolev").Split("\n")); + dialog.SetArtists(string.Format(_("Nicholas Logozzo {0}"), "https://github.com/nlogozzo").Split("\n")); + dialog.SetTranslatorCredits(_("translator-credits")); + dialog.SetReleaseNotes(_controller.AppInfo.Changelog); + dialog.Present(); + } +} diff --git a/NickvisionTagger.GNOME/Views/PreferencesDialog.cs b/NickvisionTagger.GNOME/Views/PreferencesDialog.cs new file mode 100644 index 00000000..834a224e --- /dev/null +++ b/NickvisionTagger.GNOME/Views/PreferencesDialog.cs @@ -0,0 +1,74 @@ +using NickvisionTagger.GNOME.Helpers; +using NickvisionTagger.Shared.Controllers; +using NickvisionTagger.Shared.Models; +using System; + +namespace NickvisionTagger.GNOME.Views; + +/// +/// The PreferencesDialog for the application +/// +public partial class PreferencesDialog : Adw.PreferencesWindow +{ + private readonly PreferencesViewController _controller; + private readonly Adw.Application _application; + + [Gtk.Connect] private readonly Adw.ComboRow _themeRow; + + private PreferencesDialog(Gtk.Builder builder, PreferencesViewController controller, Adw.Application application, Gtk.Window parent) : base(builder.GetPointer("_root"), false) + { + //Window Settings + _controller = controller; + _application = application; + SetTransientFor(parent); + SetIconName(_controller.AppInfo.ID); + //Build UI + builder.Connect(this); + _themeRow.OnNotify += (sender, e) => + { + if (e.Pspec.GetName() == "selected-item") + { + OnThemeChanged(); + } + }; + _themeRow.SetSelected((uint)_controller.Theme); + //Layout + OnHide += Hide; + } + + /// + /// Constructs a PreferencesDialog + /// + /// PreferencesViewController + /// Adw.Application + /// Gtk.Window + public PreferencesDialog(PreferencesViewController controller, Adw.Application application, Gtk.Window parent) : this(Builder.FromFile("preferences_dialog.ui"), controller, application, parent) + { + } + + /// + /// Occurs when the dialog is hidden + /// + /// Gtk.Widget + /// EventArgs + private void Hide(Gtk.Widget sender, EventArgs e) + { + _controller.SaveConfiguration(); + Destroy(); + } + + /// + /// Occurs when the theme selection is changed + /// + private void OnThemeChanged() + { + _controller.Theme = (Theme)_themeRow.GetSelected(); + _application.StyleManager!.ColorScheme = _controller.Theme switch + { + Theme.System => Adw.ColorScheme.PreferLight, + Theme.Light => Adw.ColorScheme.ForceLight, + Theme.Dark => Adw.ColorScheme.ForceDark, + _ => Adw.ColorScheme.PreferLight + }; + } +} diff --git a/NickvisionTagger.GNOME/nuget-sources.json b/NickvisionTagger.GNOME/nuget-sources.json new file mode 100644 index 00000000..41aa4267 --- /dev/null +++ b/NickvisionTagger.GNOME/nuget-sources.json @@ -0,0 +1,149 @@ +[ + { + "type": "file", + "url": "https://api.nuget.org/v3-flatcontainer/gettext.net/1.8.7/gettext.net.1.8.7.nupkg", + "sha512": "f998a8437b52b406141c65f0d132ddc5dd04b85635b5b410f508b851ad7a5108b623ea615aa58a0dec2825e6e0420cd2f9f6f2689646aa71afafb172303dceaa", + "dest": "nuget-sources", + "dest-filename": "gettext.net.1.8.7.nupkg" + }, + { + "type": "file", + "url": "https://api.nuget.org/v3-flatcontainer/gircore.adw-1/0.3.0/gircore.adw-1.0.3.0.nupkg", + "sha512": "62383207dc5cd8547de6655197b16ac43b05083692478d317a7fc9d0cb1f6545f065da590bbd4868cbd8734e47275b5b8b68c0d942af52fb3264d06b16e08b33", + "dest": "nuget-sources", + "dest-filename": "gircore.adw-1.0.3.0.nupkg" + }, + { + "type": "file", + "url": "https://api.nuget.org/v3-flatcontainer/gircore.cairo-1.0/0.3.0/gircore.cairo-1.0.0.3.0.nupkg", + "sha512": "5ee2ef7020a5f579c5c680a1941f64a1287447108655dd8812e5db80158a346fcbab8206a7bfa4a28507ec40f16c138fec03f8ef26d85d97c40e44828c446804", + "dest": "nuget-sources", + "dest-filename": "gircore.cairo-1.0.0.3.0.nupkg" + }, + { + "type": "file", + "url": "https://api.nuget.org/v3-flatcontainer/gircore.freetype2-2.0/0.3.0/gircore.freetype2-2.0.0.3.0.nupkg", + "sha512": "06bfe54165a5b4b11631db0ac54a9b8919b7537ffb83faf991da384b0d86f8ff585778ec5d6cd37dc03c1d2002ab8155889da4d077b2fa5a20593cfb363fcc49", + "dest": "nuget-sources", + "dest-filename": "gircore.freetype2-2.0.0.3.0.nupkg" + }, + { + "type": "file", + "url": "https://api.nuget.org/v3-flatcontainer/gircore.gdk-4.0/0.3.0/gircore.gdk-4.0.0.3.0.nupkg", + "sha512": "77d56ec4eb5aaf4bb01f7f0c1aa3ab640e30b040711f59f723e828ef9d08801a08c9a5f161bebc794c12831551e42343c3cc64b17add469c67e9fa3cd863139e", + "dest": "nuget-sources", + "dest-filename": "gircore.gdk-4.0.0.3.0.nupkg" + }, + { + "type": "file", + "url": "https://api.nuget.org/v3-flatcontainer/gircore.gdkpixbuf-2.0/0.3.0/gircore.gdkpixbuf-2.0.0.3.0.nupkg", + "sha512": "9d4b13d75106db7f30c08b7e705e1a899a2518d343ccf1e7183a892bd5856794c40f321fdeb08db516438c3c92c4087fe37c789382eacca0cd4e5b79ec2590a9", + "dest": "nuget-sources", + "dest-filename": "gircore.gdkpixbuf-2.0.0.3.0.nupkg" + }, + { + "type": "file", + "url": "https://api.nuget.org/v3-flatcontainer/gircore.gio-2.0/0.3.0/gircore.gio-2.0.0.3.0.nupkg", + "sha512": "19a2d351371a67bfc03a2e0a503edc985afb9085be14f726c5c710b027ccb99ec31ac572e57ec77d48401e7877d844e7b269661dbb6f92bd7d0c035c532ae269", + "dest": "nuget-sources", + "dest-filename": "gircore.gio-2.0.0.3.0.nupkg" + }, + { + "type": "file", + "url": "https://api.nuget.org/v3-flatcontainer/gircore.glib-2.0/0.3.0/gircore.glib-2.0.0.3.0.nupkg", + "sha512": "1987a080d7c0bd7ecbb193f00e6669391460fdece4c0ea0eb03628ae184b9ed8401e7c981e948b3b58a124a6836d2188ccd375a0eba24cc7a0599e8430e9b7ce", + "dest": "nuget-sources", + "dest-filename": "gircore.glib-2.0.0.3.0.nupkg" + }, + { + "type": "file", + "url": "https://api.nuget.org/v3-flatcontainer/gircore.gobject-2.0/0.3.0/gircore.gobject-2.0.0.3.0.nupkg", + "sha512": "b464239ab153c35c112273f785c126c24485c2d9fe49016c7698aa3c81b8ff450382b834b427251ccab4370cfac78c8cc76d1c8e78cf81180193ebed4fa55ae1", + "dest": "nuget-sources", + "dest-filename": "gircore.gobject-2.0.0.3.0.nupkg" + }, + { + "type": "file", + "url": "https://api.nuget.org/v3-flatcontainer/gircore.graphene-1.0/0.3.0/gircore.graphene-1.0.0.3.0.nupkg", + "sha512": "bb06d6b049a508d85b7a130a3f2184a0759b53aac8226baabf6826ebe0142ce2b6eb3d9d20adb6521bcf346fc55a26d070d3a304c4ad74bae78b09eb109f6d55", + "dest": "nuget-sources", + "dest-filename": "gircore.graphene-1.0.0.3.0.nupkg" + }, + { + "type": "file", + "url": "https://api.nuget.org/v3-flatcontainer/gircore.gsk-4.0/0.3.0/gircore.gsk-4.0.0.3.0.nupkg", + "sha512": "bea7a578e40e96494b64fa732488bd7eb21254b106ab3edea232916c6a423703932ccbe49428b3ac9872e77a80f4a29a47a3efff89ba185ce070610f9bae6e2f", + "dest": "nuget-sources", + "dest-filename": "gircore.gsk-4.0.0.3.0.nupkg" + }, + { + "type": "file", + "url": "https://api.nuget.org/v3-flatcontainer/gircore.gtk-4.0/0.3.0/gircore.gtk-4.0.0.3.0.nupkg", + "sha512": "86849fdda5e99a0610304e991ecfb25ddadbcff65ac4b84d82a7b4f11e857c7eccb6e416cb17d357727ea084c57052af347b6e1c9165fb5e81c751327e6f09a3", + "dest": "nuget-sources", + "dest-filename": "gircore.gtk-4.0.0.3.0.nupkg" + }, + { + "type": "file", + "url": "https://api.nuget.org/v3-flatcontainer/gircore.harfbuzz-0.0/0.3.0/gircore.harfbuzz-0.0.0.3.0.nupkg", + "sha512": "e71031d461f400b2306947e54746c23805dbc8dc7c4ebde3f72ee4fdf6508907f939a9dfcdb0b571edd15b95193fdfd5191e97fce0c1960a975f5d4d0e3d4e01", + "dest": "nuget-sources", + "dest-filename": "gircore.harfbuzz-0.0.0.3.0.nupkg" + }, + { + "type": "file", + "url": "https://api.nuget.org/v3-flatcontainer/gircore.pango-1.0/0.3.0/gircore.pango-1.0.0.3.0.nupkg", + "sha512": "a332d73342d7a656883024db2f0f110f800eba17b1572a32aa9e5eac679478e55d98010d352f9d82d96acfced1319c145b151bebf660e1d29ccaa72afded7546", + "dest": "nuget-sources", + "dest-filename": "gircore.pango-1.0.0.3.0.nupkg" + }, + { + "type": "file", + "url": "https://api.nuget.org/v3-flatcontainer/microsoft.aspnetcore.app.runtime.linux-arm/7.0.7/microsoft.aspnetcore.app.runtime.linux-arm.7.0.7.nupkg", + "sha512": "ee82766060640a38ce636154de61b8762185c1fd44651a2a3b713e463d38170815d654a31bb522ec1ed1d00a51998a9da3a08b9ea67a36958fbdfe6c63c6c05c", + "dest": "nuget-sources", + "dest-filename": "microsoft.aspnetcore.app.runtime.linux-arm.7.0.7.nupkg" + }, + { + "type": "file", + "url": "https://api.nuget.org/v3-flatcontainer/microsoft.aspnetcore.app.runtime.linux-arm64/7.0.7/microsoft.aspnetcore.app.runtime.linux-arm64.7.0.7.nupkg", + "sha512": "8bc0bc895d8cc2f20f83bfb6b63cdce4375a8285211cbf33b728aecfc28f38809c40437b80d462cbca3665fbcf037e2ff07e938cfd17ae96e28e8c528ab911b5", + "dest": "nuget-sources", + "dest-filename": "microsoft.aspnetcore.app.runtime.linux-arm64.7.0.7.nupkg" + }, + { + "type": "file", + "url": "https://api.nuget.org/v3-flatcontainer/microsoft.aspnetcore.app.runtime.linux-x64/7.0.7/microsoft.aspnetcore.app.runtime.linux-x64.7.0.7.nupkg", + "sha512": "61fe2bd5f4dd149e4f0fb31d138fc3eceb21c02aebc63ece75a8b2332f98844b717f81db4b06ac642b4404e163bf8553eedaf4206f4824cffbd25bd690dbbd7c", + "dest": "nuget-sources", + "dest-filename": "microsoft.aspnetcore.app.runtime.linux-x64.7.0.7.nupkg" + }, + { + "type": "file", + "url": "https://api.nuget.org/v3-flatcontainer/microsoft.netcore.app.runtime.linux-arm/7.0.7/microsoft.netcore.app.runtime.linux-arm.7.0.7.nupkg", + "sha512": "f371aeb1a193877bec1828f3d902ea46bfeb52e4983b1b5b53dee9ae4da677f43849ca927825e41201d98a1be971909b668061ffd86b5d04f7bcf49d8654e960", + "dest": "nuget-sources", + "dest-filename": "microsoft.netcore.app.runtime.linux-arm.7.0.7.nupkg" + }, + { + "type": "file", + "url": "https://api.nuget.org/v3-flatcontainer/microsoft.netcore.app.runtime.linux-arm64/7.0.7/microsoft.netcore.app.runtime.linux-arm64.7.0.7.nupkg", + "sha512": "573e2485f0cde94c86bc761fb416b98d680bff7f50addfb774ca25b86ba71cb2b20628e6d6ae550abfe69edfa0a0679d2802be2d4a8d1b0a0f0e2eefffba5cc9", + "dest": "nuget-sources", + "dest-filename": "microsoft.netcore.app.runtime.linux-arm64.7.0.7.nupkg" + }, + { + "type": "file", + "url": "https://api.nuget.org/v3-flatcontainer/microsoft.netcore.app.runtime.linux-x64/7.0.7/microsoft.netcore.app.runtime.linux-x64.7.0.7.nupkg", + "sha512": "115ae211fe40d7ae392bdf250975a66cd309fb0c6dc2880fa9bf8029b8ed12fbf4fe4d18f61f85563c0705664e9bca5c31fd1657663a7b59e4076afeb4e80ee7", + "dest": "nuget-sources", + "dest-filename": "microsoft.netcore.app.runtime.linux-x64.7.0.7.nupkg" + }, + { + "type": "file", + "url": "https://api.nuget.org/v3-flatcontainer/microsoft.netcore.targets/5.0.0/microsoft.netcore.targets.5.0.0.nupkg", + "sha512": "8581e6dc902340efe7c92c5c9751296616045bed8fdc7d5e2d936bf90c603b94e72d2ea1aad7e2e567218d0ce389de393189a1cb65fb20e99c5ad0cfe1fa4c1b", + "dest": "nuget-sources", + "dest-filename": "microsoft.netcore.targets.5.0.0.nupkg" + } +] \ No newline at end of file diff --git a/NickvisionTagger.Shared/Controllers/MainWindowController.cs b/NickvisionTagger.Shared/Controllers/MainWindowController.cs new file mode 100644 index 00000000..aa7f8c19 --- /dev/null +++ b/NickvisionTagger.Shared/Controllers/MainWindowController.cs @@ -0,0 +1,107 @@ +using NickvisionTagger.Shared.Events; +using NickvisionTagger.Shared.Models; +using System; +using System.IO; +using static NickvisionTagger.Shared.Helpers.Gettext; + +namespace NickvisionTagger.Shared.Controllers; + +/// +/// A controller for a MainWindow +/// +public class MainWindowController +{ + /// + /// The path of the folder opened + /// + public string FolderPath { get; private set; } + + /// + /// Gets the AppInfo object + /// + public AppInfo AppInfo => AppInfo.Current; + /// + /// Whether or not the version is a development version or not + /// + public bool IsDevVersion => AppInfo.Current.Version.IndexOf('-') != -1; + /// + /// The preferred theme of the application + /// + public Theme Theme => Configuration.Current.Theme; + /// + /// Whether or not the folder is opened + /// + public bool IsFolderOpened => FolderPath != "No Folder Opened"; + + /// + /// Occurs when a notification is sent + /// + public event EventHandler? NotificationSent; + /// + /// Occurs when a shell notification is sent + /// + public event EventHandler? ShellNotificationSent; + /// + /// Occurs when a folder is opened or closed + /// + public event EventHandler? FolderChanged; + + /// + /// Constructs a MainWindowController + /// + public MainWindowController() + { + FolderPath = "No Folder Opened"; + } + + /// + /// The string for greeting on the home page + /// + public string Greeting + { + get + { + return DateTime.Now.Hour switch + { + >= 0 and < 6 => _p("Night", "Good Morning!"), + < 12 => _p("Morning", "Good Morning!"), + < 18 => _("Good Afternoon!"), + < 24 => _("Good Evening!"), + _ => _("Good Day!") + }; + } + } + + /// + /// Creates a new PreferencesViewController + /// + /// The PreferencesViewController + public PreferencesViewController CreatePreferencesViewController() => new PreferencesViewController(); + + /// + /// Opens a folder + /// + /// The path of the folder to open + /// True if folder opened, else false + public bool OpenFolder(string folderPath) + { + if (Directory.Exists(folderPath)) + { + FolderPath = folderPath; + NotificationSent?.Invoke(this, new NotificationSentEventArgs(_("Folder Opened: {0}", FolderPath), NotificationSeverity.Success, "close")); + FolderChanged?.Invoke(this, EventArgs.Empty); + return true; + } + return false; + } + + /// + /// Closes the folder + /// + public void CloseFolder() + { + FolderPath = "No Folder Opened"; + NotificationSent?.Invoke(this, new NotificationSentEventArgs(_("Folder closed."), NotificationSeverity.Warning)); + FolderChanged?.Invoke(this, EventArgs.Empty); + } +} diff --git a/NickvisionTagger.Shared/Controllers/PreferencesViewController.cs b/NickvisionTagger.Shared/Controllers/PreferencesViewController.cs new file mode 100644 index 00000000..c000ebcc --- /dev/null +++ b/NickvisionTagger.Shared/Controllers/PreferencesViewController.cs @@ -0,0 +1,37 @@ +using NickvisionTagger.Shared.Models; + +namespace NickvisionTagger.Shared.Controllers; + +/// +/// A controller for a PreferencesView +/// +public class PreferencesViewController +{ + /// + /// Gets the AppInfo object + /// + public AppInfo AppInfo => AppInfo.Current; + + /// + /// Constructs a PreferencesViewController + /// + internal PreferencesViewController() + { + + } + + /// + /// The preferred theme of the application + /// + public Theme Theme + { + get => Configuration.Current.Theme; + + set => Configuration.Current.Theme = value; + } + + /// + /// Saves the configuration to disk + /// + public void SaveConfiguration() => Configuration.Current.Save(); +} diff --git a/NickvisionTagger.Shared/Events/NotificationSentEventArgs.cs b/NickvisionTagger.Shared/Events/NotificationSentEventArgs.cs new file mode 100644 index 00000000..bce9c5a6 --- /dev/null +++ b/NickvisionTagger.Shared/Events/NotificationSentEventArgs.cs @@ -0,0 +1,41 @@ +using System; + +namespace NickvisionTagger.Shared.Events; + +/// +/// Event args for when a notification is sent +/// +public class NotificationSentEventArgs : EventArgs +{ + /// + /// The message of the notification + /// + public string Message { get; set; } + /// + /// The severity of the notification + /// + public NotificationSeverity Severity { get; set; } + /// + /// Additional action for notification + /// + public string Action { get; set; } + /// + /// Parameter for additional action + /// + public string ActionParam { get; set; } + + /// + /// Constructs a NotificationSentEventArgs + /// + /// The message of the notification + /// The severity of the notification + /// Additional action for notification + /// Parameter for additional action + public NotificationSentEventArgs(string message, NotificationSeverity severity, string action = "", string actionParam = "") + { + Message = message; + Severity = severity; + Action = action; + ActionParam = actionParam; + } +} diff --git a/NickvisionTagger.Shared/Events/NotificationSeverity.cs b/NickvisionTagger.Shared/Events/NotificationSeverity.cs new file mode 100644 index 00000000..8a7e9629 --- /dev/null +++ b/NickvisionTagger.Shared/Events/NotificationSeverity.cs @@ -0,0 +1,12 @@ +namespace NickvisionTagger.Shared.Events; + +/// +/// Severities for a notification +/// +public enum NotificationSeverity +{ + Informational = 0, + Success, + Warning, + Error +} diff --git a/NickvisionTagger.Shared/Events/ShellNotificationSentEventArgs.cs b/NickvisionTagger.Shared/Events/ShellNotificationSentEventArgs.cs new file mode 100644 index 00000000..3d3dc543 --- /dev/null +++ b/NickvisionTagger.Shared/Events/ShellNotificationSentEventArgs.cs @@ -0,0 +1,23 @@ +using NickvisionTagger.Shared.Events; + +/// +/// Event args for when a notification is sent +/// +public class ShellNotificationSentEventArgs : NotificationSentEventArgs +{ + /// + /// The title of the notification + /// + public string Title { get; set; } + + /// + /// Constructs a ShellNotificationSentEventArgs + /// + /// The title of the notification + /// The message of the notification + /// The severity of the notification + public ShellNotificationSentEventArgs(string title, string message, NotificationSeverity severity) : base(message, severity) + { + Title = title; + } +} diff --git a/NickvisionTagger.Shared/Helpers/Gettext.cs b/NickvisionTagger.Shared/Helpers/Gettext.cs new file mode 100644 index 00000000..05f63834 --- /dev/null +++ b/NickvisionTagger.Shared/Helpers/Gettext.cs @@ -0,0 +1,50 @@ +using GetText; +using System.IO; +using System.Reflection; + +namespace NickvisionTagger.Shared.Helpers; + +public static class Gettext +{ + private static readonly ICatalog _catalog = new Catalog("application", Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)); + + public static string _(string text) + { + return _catalog.GetString(text); + } + + public static string _(string text, params object[] args) + { + return _catalog.GetString(text, args); + } + + public static string _n(string text, string pluralText, long n) + { + return _catalog.GetPluralString(text, pluralText, n); + } + + public static string _n(string text, string pluralText, long n, params object[] args) + { + return _catalog.GetPluralString(text, pluralText, n, args); + } + + public static string _p(string context, string text) + { + return _catalog.GetParticularString(context, text); + } + + public static string _p(string context, string text, params object[] args) + { + return _catalog.GetParticularString(context, text, args); + } + + public static string _pn(string context, string text, string pluralText, long n) + { + return _catalog.GetParticularPluralString(context, text, pluralText, n); + } + + public static string _pn(string context, string text, string pluralText, long n, params object[] args) + { + return _catalog.GetParticularPluralString(context, text, pluralText, n, args); + } +} \ No newline at end of file diff --git a/NickvisionTagger.Shared/Models/AppInfo.cs b/NickvisionTagger.Shared/Models/AppInfo.cs new file mode 100644 index 00000000..4c09ba12 --- /dev/null +++ b/NickvisionTagger.Shared/Models/AppInfo.cs @@ -0,0 +1,73 @@ +using System; + +namespace NickvisionTagger.Shared.Models; + +/// +/// A model for the information about the application +/// +public class AppInfo +{ + /// + /// Gets the singleton object + /// + internal static AppInfo Current { get; } = new(); + + /// + /// The id of the application + /// + public string ID { get; set; } + /// + /// The name of the application + /// + public string Name { get; set; } + /// + /// The short name of the application + /// + public string ShortName { get; set; } + /// + /// The description of the application + /// + public string Description { get; set; } + /// + /// The running version of the application + /// + public string Version { get; set; } + /// + /// The changelog for the running version of the application + /// + public string Changelog { get; set; } + /// + /// The GitHub repo for the application + /// + public Uri GitHubRepo { get; set; } + /// + /// The issue tracker url for the application + /// + public Uri IssueTracker { get; set; } + /// + /// The support url for the application + /// + public Uri SupportUrl { get; set; } + + /// + /// Constructs an AppInfo + /// + internal AppInfo() + { + ID = ""; + Name = ""; + Description = ""; + ShortName = ""; + Version = "0.0.0"; + Changelog = ""; + GitHubRepo = new Uri("about:blank"); + IssueTracker = new Uri("about:blank"); + SupportUrl = new Uri("about:blank"); + } + + /// + /// Gets whether or not the application version is a development version or not + /// + /// True for development version, else false + public bool GetIsDevelVersion() => Version.Contains("-"); +} diff --git a/NickvisionTagger.Shared/Models/Configuration.cs b/NickvisionTagger.Shared/Models/Configuration.cs new file mode 100644 index 00000000..0a360166 --- /dev/null +++ b/NickvisionTagger.Shared/Models/Configuration.cs @@ -0,0 +1,68 @@ +using System; +using System.IO; +using System.Text.Json; + +namespace NickvisionTagger.Shared.Models; + +/// +/// A model for the configuration of the application +/// +public class Configuration +{ + public static readonly string ConfigDir = $"{Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData)}{Path.DirectorySeparatorChar}Nickvision{Path.DirectorySeparatorChar}{AppInfo.Current.Name}"; + private static readonly string ConfigPath = $"{ConfigDir}{Path.DirectorySeparatorChar}config.json"; + private static Configuration? _instance; + + /// + /// The preferred theme for the application + /// + public Theme Theme { get; set; } + + /// + /// Occurs when the configuration is saved to disk + /// + public event EventHandler? Saved; + + /// + /// Constructs a Configuration + /// + public Configuration() + { + if (!Directory.Exists(ConfigDir)) + { + Directory.CreateDirectory(ConfigDir); + } + Theme = Theme.System; + } + + /// + /// Gets the singleton object + /// + internal static Configuration Current + { + get + { + if (_instance == null) + { + try + { + _instance = JsonSerializer.Deserialize(File.ReadAllText(ConfigPath)) ?? new Configuration(); + } + catch + { + _instance = new Configuration(); + } + } + return _instance; + } + } + + /// + /// Saves the configuration to disk + /// + public void Save() + { + File.WriteAllText(ConfigPath, JsonSerializer.Serialize(this)); + Saved?.Invoke(this, EventArgs.Empty); + } +} diff --git a/NickvisionTagger.Shared/Models/Theme.cs b/NickvisionTagger.Shared/Models/Theme.cs new file mode 100644 index 00000000..47a7251e --- /dev/null +++ b/NickvisionTagger.Shared/Models/Theme.cs @@ -0,0 +1,11 @@ +namespace NickvisionTagger.Shared.Models; + +/// +/// Application themes +/// +public enum Theme +{ + Light = 0, + Dark, + System +} diff --git a/NickvisionTagger.Shared/NickvisionTagger.Shared.csproj b/NickvisionTagger.Shared/NickvisionTagger.Shared.csproj new file mode 100644 index 00000000..d2a86480 --- /dev/null +++ b/NickvisionTagger.Shared/NickvisionTagger.Shared.csproj @@ -0,0 +1,27 @@ + + + + net7.0 + enable + + + + + + + + + + + + + + + + + + + + + + diff --git a/org.nickvision.tagger-devel.svg b/NickvisionTagger.Shared/Resources/org.nickvision.tagger-devel.svg similarity index 100% rename from org.nickvision.tagger-devel.svg rename to NickvisionTagger.Shared/Resources/org.nickvision.tagger-devel.svg diff --git a/org.nickvision.tagger-symbolic.svg b/NickvisionTagger.Shared/Resources/org.nickvision.tagger-symbolic.svg similarity index 100% rename from org.nickvision.tagger-symbolic.svg rename to NickvisionTagger.Shared/Resources/org.nickvision.tagger-symbolic.svg diff --git a/NickvisionTagger.Shared/Resources/org.nickvision.tagger.png b/NickvisionTagger.Shared/Resources/org.nickvision.tagger.png new file mode 100644 index 00000000..e2f992d0 Binary files /dev/null and b/NickvisionTagger.Shared/Resources/org.nickvision.tagger.png differ diff --git a/org.nickvision.tagger.svg b/NickvisionTagger.Shared/Resources/org.nickvision.tagger.svg similarity index 100% rename from org.nickvision.tagger.svg rename to NickvisionTagger.Shared/Resources/org.nickvision.tagger.svg diff --git a/NickvisionTagger.Shared/Resources/po/LINGUAS b/NickvisionTagger.Shared/Resources/po/LINGUAS new file mode 100644 index 00000000..e69de29b diff --git a/NickvisionTagger.Shared/org.nickvision.tagger.desktop.in b/NickvisionTagger.Shared/org.nickvision.tagger.desktop.in new file mode 100644 index 00000000..b99ab917 --- /dev/null +++ b/NickvisionTagger.Shared/org.nickvision.tagger.desktop.in @@ -0,0 +1,12 @@ +[Desktop Entry] +Version=1.0 +Name=Tagger +Comment=Manage your music library with ease +Exec=@EXEC@ +Icon=org.nickvision.tagger +Terminal=false +Type=Application +Categories=AudioVideo; +Keywords=Tag;Music;Editor;Library; +X-GNOME-UsesNotifications=true +StartupNotify=true \ No newline at end of file diff --git a/NickvisionTagger.Shared/org.nickvision.tagger.in b/NickvisionTagger.Shared/org.nickvision.tagger.in new file mode 100644 index 00000000..1d4e7d21 --- /dev/null +++ b/NickvisionTagger.Shared/org.nickvision.tagger.in @@ -0,0 +1,2 @@ +#!/bin/sh +exec @EXEC@ $@ \ No newline at end of file diff --git a/NickvisionTagger.Shared/org.nickvision.tagger.metainfo.xml.in b/NickvisionTagger.Shared/org.nickvision.tagger.metainfo.xml.in new file mode 100644 index 00000000..d910b1b2 --- /dev/null +++ b/NickvisionTagger.Shared/org.nickvision.tagger.metainfo.xml.in @@ -0,0 +1,40 @@ + + + org.nickvision.tagger + CC0-1.0 + MIT + Tagger + Nickvision + Manage your music library with ease + +

— Edit tags and album art of multiple files, even across subfolders, all at once

+

— Support for multiple music file types (mp3, ogg, flac, wma, and wav)

+

— Convert filenames to tags and tags to filenames with ease

+

— Lookup file information using MusicBrainz

+
+ org.nickvision.tagger.desktop + + + https://raw.githubusercontent.com/NickvisionApps/Tagger/main/@PROJECT@/Screenshots/Light.png + + + https://raw.githubusercontent.com/NickvisionApps/Tagger/main/@PROJECT@/Screenshots/Dark.png + + + https://github.com/NickvisionApps/Tagger + https://github.com/NickvisionApps/Tagger/issues + https://github.com/sponsors/nlogozzo + https://github.com/NickvisionApps/Tagger/blob/master/CONTRIBUTING.md#providing-translations + Nickvision + + org.nickvision.tagger + + + + +

- Initial Release

+
+
+
+ +
diff --git a/Tagger.doap b/Tagger.doap new file mode 100644 index 00000000..017f3825 --- /dev/null +++ b/Tagger.doap @@ -0,0 +1,22 @@ + + + Tagger + Manage your music library with ease + + + + + C# + + + + Nick Logozzo + + nlogozzo + + + diff --git a/build.cake b/build.cake new file mode 100644 index 00000000..de4f81f9 --- /dev/null +++ b/build.cake @@ -0,0 +1,296 @@ +#addin nuget:?package=Cake.FileHelpers&version=6.1.3 + +const string appId = "org.nickvision.tagger"; +const string projectName = "NickvisionTagger"; +const string shortName = "tagger"; + +var sep = System.IO.Path.DirectorySeparatorChar; + +var target = Argument("target", "Run"); +var configuration = Argument("configuration", "Debug"); +var ui = Argument("ui", EnvironmentVariable("NICK_UI", "")); +var projectSuffix = ""; + +////////////////////////////////////////////////////////////////////// +// TASKS +////////////////////////////////////////////////////////////////////// + +Task("Clean") + .WithCriteria(c => HasArgument("rebuild")) + .Does(() => +{ + CleanDirectory($".{sep}{projectName}.{projectSuffix}{sep}bin{sep}{configuration}"); +}); + +Task("Build") + .IsDependentOn("Clean") + .Does(() => +{ + DotNetBuild($".{sep}{projectName}.{projectSuffix}{sep}{projectName}.{projectSuffix}.csproj", new DotNetBuildSettings + { + Configuration = configuration + }); +}); + +Task("Run") + .IsDependentOn("Build") + .Does(() => +{ + DotNetRun($".{sep}{projectName}.{projectSuffix}{sep}{projectName}.{projectSuffix}.csproj", new DotNetRunSettings + { + Configuration = configuration, + NoBuild = true + }); +}); + +Task("Publish") + .Does(() => +{ + var selfContained = Argument("self-contained", false) || HasArgument("self-contained") || HasArgument("sc"); + var outDir = EnvironmentVariable("NICK_BUILDDIR", "_nickbuild"); + CleanDirectory(outDir); + var prefix = Argument("prefix", "/usr"); + var libDir = string.IsNullOrEmpty(prefix) ? "lib" : $"{prefix}{sep}lib"; + var publishDir = $"{outDir}{libDir}{sep}{appId}"; + var exitCode = 0; + DotNetPublish($".{sep}{projectName}.{projectSuffix}{sep}{projectName}.{projectSuffix}.csproj", new DotNetPublishSettings + { + Configuration = "Release", + SelfContained = selfContained, + OutputDirectory = publishDir, + Sources = Argument("sources", "").Split(" "), + HandleExitCode = code => { + exitCode = code; + return false; + } + }); + if (exitCode != 0) + { + throw new Exception($"Publishing failed with exit code {exitCode}."); + } + + FinishPublishLinux(outDir, prefix, libDir, selfContained); + + if (projectSuffix == "GNOME") + { + PostPublishGNOME(outDir, prefix, libDir); + } +}); + +Task("Install") + .Does(() => +{ + var buildDir = EnvironmentVariable("NICK_BUILDDIR", "_nickbuild"); + var destDir = Argument("destdir", "/"); + CopyDirectory(buildDir, destDir); +}); + +Task("FlatpakSourcesGen") + .Does(() => +{ + StartProcess("flatpak-dotnet-generator.py", new ProcessSettings { + Arguments = $"{projectName}.{projectSuffix}{sep}nuget-sources.json {projectName}.{projectSuffix}{sep}{projectName}.{projectSuffix}.csproj" + }); +}); + +Task("GeneratePot") + .Does(() => +{ + StartProcess("GetText.Extractor", new ProcessSettings { + Arguments = $"-s ./{projectName}.GNOME -s ./{projectName}.Shared -as \"_\" -ad \"_p\" -ap \"_n\" -adp \"_pn\" -t ./{projectName}.Shared/Resources/po/{shortName}.pot" + }); + StartProcess("sh", new ProcessSettings { + Arguments = $"-c \"xgettext --from-code=UTF-8 --add-comments --keyword=_ --keyword=C_:1c,2 -o ./{projectName}.Shared/Resources/po/{shortName}.pot -j ./{projectName}.GNOME/Blueprints/*.blp\"" + }); + StartProcess("xgettext", new ProcessSettings { + Arguments = $"-o ./{projectName}.Shared/Resources/po/{shortName}.pot -j ./{projectName}.Shared/{appId}.desktop.in" + }); + StartProcess("xgettext", new ProcessSettings { + Arguments = $"-o ./{projectName}.Shared/Resources/po/{shortName}.pot -j ./{projectName}.Shared/{appId}.metainfo.xml.in" + }); +}); + +Task("UpdatePo") + .Does(() => +{ + foreach (var lang in FileReadLines($"./{projectName}.Shared/Resources/po/LINGUAS")) + { + StartProcess("msgmerge", new ProcessSettings { + Arguments = $"-U ./{projectName}.Shared/Resources/po/{lang}.po ./{projectName}.Shared/Resources/po/{shortName}.pot" + }); + } +}); + +Task("DocsGeneratePot") + .Does(() => +{ + StartProcess("sh", new ProcessSettings { + Arguments = $"-c \"itstool -o po{sep}{shortName}.pot yelp{sep}C{sep}*.page\"", + WorkingDirectory = $"{projectName}.Shared{sep}Docs" + }); +}); + +Task("DocsUpdatePo") + .Does(() => +{ + foreach (var lang in FileReadLines($".{sep}{projectName}.Shared{sep}Docs{sep}po{sep}LINGUAS")) + { + StartProcess("msgmerge", new ProcessSettings { + Arguments = $"-U .{sep}{projectName}.Shared{sep}Docs{sep}po{sep}{lang}.po .{sep}{projectName}.Shared{sep}Docs{sep}po{sep}{shortName}.pot" + }); + } +}); + +Task("DocsUpdateYelp") + .Does(() => +{ + foreach (var lang in FileReadLines($".{sep}{projectName}.Shared{sep}Docs{sep}po{sep}LINGUAS")) + { + StartProcess("msgfmt", new ProcessSettings { + Arguments = $"{lang}.po -o {lang}.mo", + WorkingDirectory = $"{projectName}.Shared{sep}Docs{sep}po" + }); + CreateDirectory($".{sep}{projectName}.Shared{sep}Docs{sep}yelp{sep}{lang}"); + StartProcess("sh", new ProcessSettings { + Arguments = $"-c \"itstool -m po{sep}{lang}.mo -o yelp{sep}{lang}{sep} yelp{sep}C{sep}*.page\"", + WorkingDirectory = $"{projectName}.Shared{sep}Docs" + }); + DeleteFile($"{projectName}.Shared{sep}Docs{sep}po{sep}{lang}.mo"); + CreateDirectory($".{sep}{projectName}.Shared{sep}Docs{sep}yelp{sep}{lang}{sep}figures"); + CopyDirectory($".{sep}{projectName}.Shared{sep}Docs{sep}yelp{sep}C{sep}figures", $".{sep}{projectName}.Shared{sep}Docs{sep}yelp{sep}{lang}{sep}figures"); + } +}); + +Task("DocsUpdateHtml") + .Does(() => +{ + //C for english + CreateDirectory($"{projectName}.Shared{sep}Docs{sep}html{sep}C"); + CreateDirectory($"{projectName}.Shared{sep}Docs{sep}html{sep}C{sep}figures"); + CopyDirectory($"{projectName}.Shared{sep}Docs{sep}yelp{sep}C{sep}figures", $"{projectName}.Shared{sep}Docs{sep}html{sep}C{sep}figures"); + StartProcess("yelp-build", new ProcessSettings { + Arguments = $"html -o html{sep}C{sep} yelp{sep}C{sep}", + WorkingDirectory = $"{projectName}.Shared{sep}Docs" + }); + //Other langs + foreach (var lang in FileReadLines($".{sep}{projectName}.Shared{sep}Docs{sep}po{sep}LINGUAS")) + { + CreateDirectory($"{projectName}.Shared{sep}Docs{sep}html{sep}{lang}"); + StartProcess("yelp-build", new ProcessSettings { + Arguments = $"html -o html{sep}{lang}{sep} yelp{sep}{lang}{sep}", + WorkingDirectory = $"{projectName}.Shared{sep}Docs" + }); + DeleteDirectory($"{projectName}.Shared{sep}Docs{sep}html{sep}{lang}{sep}figures", new DeleteDirectorySettings { + Force = true, + Recursive = true + }); + DeleteFiles($"{projectName}.Shared{sep}Docs{sep}html{sep}{lang}{sep}*.css"); + DeleteFiles($"{projectName}.Shared{sep}Docs{sep}html{sep}{lang}{sep}*.js"); + ReplaceRegexInFiles($"{projectName}.Shared{sep}Docs{sep}html{sep}{lang}{sep}*.html", "href=\".*.css\"", "href=\"../C/C.css\""); + ReplaceRegexInFiles($"{projectName}.Shared{sep}Docs{sep}html{sep}{lang}{sep}*.html", "src=\"highlight.pack.js\"", "src=\"../C/highlight.pack.js\""); + ReplaceRegexInFiles($"{projectName}.Shared{sep}Docs{sep}html{sep}{lang}{sep}*.html", "src=\"yelp.js\"", "src=\"../C/yelp.js\""); + ReplaceRegexInFiles($"{projectName}.Shared{sep}Docs{sep}html{sep}{lang}{sep}*.html", $"src=\"figures/{shortName}.png\"", $"src=\"../C/figures/{shortName}.png\""); + } +}); + +////////////////////////////////////////////////////////////////////// +// FUNCTIONS +////////////////////////////////////////////////////////////////////// + +private void FinishPublishLinux(string outDir, string prefix, string libDir, bool selfContained) +{ + var binDir = string.IsNullOrEmpty(prefix) ? $"{outDir}/bin" : $"{outDir}{prefix}/bin"; + CreateDirectory(binDir); + CopyFileToDirectory($"./{projectName}.Shared/{appId}.in", binDir); + ReplaceTextInFiles($"{binDir}/{appId}.in", "@EXEC@", selfContained ? $"{libDir}/{appId}/{projectName}.{projectSuffix}" : $"dotnet {libDir}/{appId}/{projectName}.{projectSuffix}.dll"); + MoveFile($"{binDir}/{appId}.in", $"{binDir}/{appId}"); + StartProcess("chmod", new ProcessSettings{ + Arguments = $"+x {binDir}/{appId}" + }); + + var shareDir = string.IsNullOrEmpty(prefix) ? $"{outDir}/share" : $"{outDir}{prefix}/share"; + + var iconsScalableDir = $"{shareDir}{sep}icons{sep}hicolor{sep}scalable{sep}apps"; + CreateDirectory(iconsScalableDir); + CopyFileToDirectory($".{sep}{projectName}.Shared{sep}Resources{sep}{appId}.svg", iconsScalableDir); + CopyFileToDirectory($".{sep}{projectName}.Shared{sep}Resources{sep}{appId}-devel.svg", iconsScalableDir); + var iconsSymbolicDir = $"{shareDir}{sep}icons{sep}hicolor{sep}symbolic{sep}apps"; + CreateDirectory(iconsSymbolicDir); + CopyFileToDirectory($".{sep}{projectName}.Shared{sep}Resources{sep}{appId}-symbolic.svg", iconsSymbolicDir); + + var desktopDir = $"{shareDir}/applications"; + CreateDirectory(desktopDir); + CopyFileToDirectory($"./{projectName}.Shared/{appId}.desktop.in", desktopDir); + ReplaceTextInFiles($"{desktopDir}/{appId}.desktop.in", "@EXEC@", $"{prefix}/bin/{appId}"); + StartProcess("msgfmt", new ProcessSettings { + Arguments = $"--desktop --template={desktopDir}/{appId}.desktop.in -o {desktopDir}/{appId}.desktop -d ./{projectName}.Shared/Resources/po/" + }); + DeleteFile($"{desktopDir}/{appId}.desktop.in"); + + var metainfoDir = $"{shareDir}/metainfo"; + CreateDirectory(metainfoDir); + CopyFileToDirectory($"./{projectName}.Shared/{appId}.metainfo.xml.in", metainfoDir); + ReplaceTextInFiles($"{metainfoDir}/{appId}.metainfo.xml.in", "@PROJECT@", $"{projectName}.{projectSuffix}"); + StartProcess("msgfmt", new ProcessSettings { + Arguments = $"--xml --template={metainfoDir}/{appId}.metainfo.xml.in -o {metainfoDir}/{appId}.metainfo.xml -d ./{projectName}.Shared/Resources/po/" + }); + DeleteFile($"{metainfoDir}/{appId}.metainfo.xml.in"); +} + +private void PostPublishGNOME(string outDir, string prefix, string libDir) +{ + var shareDir = string.IsNullOrEmpty(prefix) ? $"{outDir}{sep}share" : $"{outDir}{prefix}{sep}share"; + + CreateDirectory($"{shareDir}{sep}{appId}"); + MoveFileToDirectory($"{outDir}{libDir}{sep}{appId}{sep}{appId}.gresource", $"{shareDir}{sep}{appId}"); + + if (FileExists($".{sep}{projectName}.GNOME{sep}{appId}.service.in")) + { + var servicesDir = $"{shareDir}{sep}dbus-1{sep}services"; + CreateDirectory(servicesDir); + CopyFileToDirectory($".{sep}{projectName}.GNOME{sep}{appId}.service.in", servicesDir); + ReplaceTextInFiles($"{servicesDir}{sep}{appId}.service.in", "@PREFIX@", $"{prefix}"); + MoveFile($"{servicesDir}{sep}{appId}.service.in", $"{servicesDir}{sep}{appId}.service"); + FileAppendLines($"{shareDir}{sep}applications{sep}{appId}.desktop" , new string[] { "DBusActivatable=true" }); + } + + if (DirectoryExists($"{projectName}.Shared{sep}Docs")) + { + var docsDir = $"{shareDir}{sep}help"; + //C for english + CreateDirectory($"{docsDir}{sep}C{sep}{shortName}"); + CopyDirectory($"{projectName}.Shared{sep}Docs{sep}yelp{sep}C", $"{docsDir}{sep}C{sep}{shortName}"); + //Other langs + foreach (var lang in FileReadLines($"{projectName}.Shared{sep}Docs{sep}po{sep}LINGUAS")) + { + CreateDirectory($"{docsDir}{sep}{lang}{sep}{shortName}"); + CopyDirectory($"{projectName}.Shared{sep}Docs{sep}yelp{sep}{lang}", $"{docsDir}{sep}{lang}{sep}{shortName}"); + } + } +} + +////////////////////////////////////////////////////////////////////// +// EXECUTION +////////////////////////////////////////////////////////////////////// + +if (target == "Install" || target == "GeneratePot" || target == "UpdatePo") +{ + RunTarget(target); + return; +} + +if (string.IsNullOrEmpty(ui)) +{ + throw new Exception("UI is not set. Use --ui option or NICK_UI environment variable."); +} +projectSuffix = ui.ToLower() switch +{ + "gnome" => "GNOME", + _ => "" +}; +if (string.IsNullOrEmpty(projectSuffix)) +{ + throw new Exception("Unknown UI. Possible values: gnome."); +} + +RunTarget(target); diff --git a/flatpak/NuGet.config b/flatpak/NuGet.config new file mode 100644 index 00000000..d5e20337 --- /dev/null +++ b/flatpak/NuGet.config @@ -0,0 +1,6 @@ + + + + + + diff --git a/flatpak/build.sh b/flatpak/build.sh new file mode 100644 index 00000000..f774fc7e --- /dev/null +++ b/flatpak/build.sh @@ -0,0 +1,21 @@ +#!/bin/sh + +if [ -z $1 ] +then + echo "Usage: $0 UI" + exit 1 +fi + +if [ -d "nuget-sources" ] +then + mv flatpak/NuGet.config ./ + mv cake-sources/cake.tool.3.0.0.nupkg flatpak/cake_repo/cake.tool/3.0.0/ + mv cake-sources/cake.filehelpers.6.1.3.nupkg flatpak/cake_repo/cake.filehelpers/6.1.3/ + dotnet tool restore + dotnet cake --target=Publish --prefix=/app --ui=$1 --self-contained --sources=nuget-sources +else + dotnet tool restore + dotnet cake --target=Publish --prefix=/app --ui=$1 --self-contained +fi + +dotnet cake --target=Install diff --git a/flatpak/cake-sources.json b/flatpak/cake-sources.json new file mode 100644 index 00000000..62109858 --- /dev/null +++ b/flatpak/cake-sources.json @@ -0,0 +1,16 @@ +[ + { + "type": "file", + "url": "https://api.nuget.org/v3-flatcontainer/cake.tool/3.0.0/cake.tool.3.0.0.nupkg", + "sha512": "5de1d48c64c8cd3ede8a5e34a566ebc237f4b58c4fe99ccff9a7a65db33be4da5d6957a4f4438574eca1f43e8ec40b75dccfe4939e0e053294933d0eff703281", + "dest": "cake-sources", + "dest-filename": "cake.tool.3.0.0.nupkg" + }, + { + "type": "file", + "url": "https://api.nuget.org/v3-flatcontainer/cake.filehelpers/6.1.3/cake.filehelpers.6.1.3.nupkg", + "sha512": "3045743d86914cea38168063c3a2002429a5ed439a191dd85a9fd755ce4e67835e1592cf0f6098a9dfcd9370ab43f8d588d5dceb71c4d8ebc8f0e8f0c7d1dd96", + "dest": "cake-sources", + "dest-filename": "cake.filehelpers.6.1.3.nupkg" + } +] \ No newline at end of file diff --git a/flatpak/cake_repo/cake.filehelpers/6.1.3/.nupkg.metadata b/flatpak/cake_repo/cake.filehelpers/6.1.3/.nupkg.metadata new file mode 100644 index 00000000..065a3ecc --- /dev/null +++ b/flatpak/cake_repo/cake.filehelpers/6.1.3/.nupkg.metadata @@ -0,0 +1,5 @@ +{ + "version": 2, + "contentHash": "KHQyol6oqxt++5K0WzsVXqIo34P2Igodceg4hQnv1QUkYkGcDUEb75208QLyCu5X0hWAfibT6NHTX2Fa18eKGA==", + "source": null +} \ No newline at end of file diff --git a/flatpak/cake_repo/cake.filehelpers/6.1.3/cake.filehelpers.6.1.3.nupkg.sha512 b/flatpak/cake_repo/cake.filehelpers/6.1.3/cake.filehelpers.6.1.3.nupkg.sha512 new file mode 100644 index 00000000..561faf0a --- /dev/null +++ b/flatpak/cake_repo/cake.filehelpers/6.1.3/cake.filehelpers.6.1.3.nupkg.sha512 @@ -0,0 +1 @@ +MEV0PYaRTOo4FoBjw6IAJCml7UOaGR3YWp/XVc5OZ4NeFZLPD2CYqd/Nk3CrQ/jViNXc63HE2OvI8Ojwx9Hdlg== \ No newline at end of file diff --git a/flatpak/cake_repo/cake.filehelpers/6.1.3/cake.filehelpers.nuspec b/flatpak/cake_repo/cake.filehelpers/6.1.3/cake.filehelpers.nuspec new file mode 100644 index 00000000..68230e42 --- /dev/null +++ b/flatpak/cake_repo/cake.filehelpers/6.1.3/cake.filehelpers.nuspec @@ -0,0 +1,22 @@ + + + + Cake.FileHelpers + 6.1.3 + Cake.FileHelpers + Redth + Apache-2.0 + https://licenses.nuget.org/Apache-2.0 + icon.png + https://github.com/cake-contrib/Cake.FileHelpers + https://cdn.jsdelivr.net/gh/cake-contrib/graphics/png/addin/cake-contrib-addin-medium.png + Cake Build addon to provide Aliases for common File operations (Reading, Writing, Replacing Text). + Copyright 2017-2023 - Cake Contributions + cake script build cake-addin cake-build addin + + + + + + + \ No newline at end of file diff --git a/flatpak/cake_repo/cake.tool/3.0.0/.nupkg.metadata b/flatpak/cake_repo/cake.tool/3.0.0/.nupkg.metadata new file mode 100644 index 00000000..e9a7aa64 --- /dev/null +++ b/flatpak/cake_repo/cake.tool/3.0.0/.nupkg.metadata @@ -0,0 +1,5 @@ +{ + "version": 2, + "contentHash": "196AuVQvj9E2vEEJtKY1N0+LN9VX9/T+UgRhkEQAZZcmZW3GahIyhrZTrC3PbVX4fCpdauP4eNY0yAw2wG38mg==", + "source": null +} \ No newline at end of file diff --git a/flatpak/cake_repo/cake.tool/3.0.0/cake.tool.3.0.0.nupkg.sha512 b/flatpak/cake_repo/cake.tool/3.0.0/cake.tool.3.0.0.nupkg.sha512 new file mode 100644 index 00000000..4dc70526 --- /dev/null +++ b/flatpak/cake_repo/cake.tool/3.0.0/cake.tool.3.0.0.nupkg.sha512 @@ -0,0 +1 @@ +XeHUjGTIzT7eil40pWbrwjf0tYxP6ZzP+aemXbM75NpdaVek9EOFdOyh9D6OxAt13M/kk54OBTKUkz0O/3AygQ== \ No newline at end of file diff --git a/flatpak/cake_repo/cake.tool/3.0.0/cake.tool.nuspec b/flatpak/cake_repo/cake.tool/3.0.0/cake.tool.nuspec new file mode 100644 index 00000000..249719ea --- /dev/null +++ b/flatpak/cake_repo/cake.tool/3.0.0/cake.tool.nuspec @@ -0,0 +1,59 @@ + + + + Cake.Tool + 3.0.0 + Patrik Svensson, Mattias Karlsson, Gary Ewan Park, Alistair Chapman, Martin Björkström, Dave Glick, Pascal Berger, Jérémie Desautels, Enrico Campidoglio, C. Augusto Proiete, Nils Andresen, and contributors + MIT + https://licenses.nuget.org/MIT + cake-medium.png + NuGet.org.md + https://cakebuild.net/ + The Cake .NET Tool. + 4046 Add typed data context CakeTaskBuilder.Finally overload. +4000 Remove obsolete CakeEngine Setup/Teardown events. +3997 Remove obsolete Logger property from DotNetTestSettings. +3991 Remove obsolete DotNetCore aliases. +3972 Remove .NET Core 3.1 TFM. +3969 Update Spectre.Console to 0.45.0. +3949 Remove .NET 5 TFM. +3946 Access to ITaskSetupContext in Frosting. +3867 Rename WindowsFact to WindowsFactAttribute to follow best practices. +3722 DotNetCore -> DotNet Rename Missed Objects. +4047 Add typed CakeTaskBuilder / TaskOf() to easier work with typed data context. +4028 Add GitLab CI SetEnvironmentVariable Command. +4019 Add support for TeamCity build status message. +4018 Add Support For TeamCity Statistics. +4011 Add GitHub Actions SetStepSummary Command. +4009 Add GitHub Actions SetOutputParameter Command. +3950 Add .NET 7 Support. +3328 Frosting: Support criteria description. +2863 Add support for GitHub Action workflow commands. +2470 Call multiple tasks from CLI and pass them to RunTarget. +1146 Add OutputDirectory property for Chocolatey Aliases. +4060 Update Microsoft.NETCore.Platforms to 7.0.0. +4059 Update System.Reflection.Metadata to 7.0.0. +4058 Update System.Collections.Immutable to 7.0.0. +4057 Update Microsoft.Extensions.DependencyInjection to 7.0.0. +4055 Add column to summary to include skip reason. +4052 Update Basic.Reference.Assemblies.* to 1.4.1. +4050 Overhaul Chocolatey Cake aliases. +4044 Overload missing for IsDependeeOf accepting a CakeTaskBuilder object. +4038 Add missing MSBuildSettings to DotNetRunSettings. +4036 Add missing MSBuildSettings to DotNetTestSettings. +4032 Add additional settings for ILMerge. +4020 Update Basic.Reference.Assemblies.* to 1.4.0. +4016 Update Microsoft.CodeAnalysis.CSharp.Scripting to 4.4.0-4.final. +4006 Add missing GitVersion command line options. +3124 EscapedBranchName is not supported by GitVersion.CommandLine. +4023 Update supported versions of Cake. +4034 DotNetMSBuildSettings ArgumentCustomization is not called with all DotNet* aliases. +4030 GitLab CI runner tags are not split correctly. + Copyright (c) .NET Foundation and contributors + Cake Script Build + + + + + + \ No newline at end of file diff --git a/flatpak/org.nickvision.tagger.json b/flatpak/org.nickvision.tagger.json new file mode 100644 index 00000000..cf1023fe --- /dev/null +++ b/flatpak/org.nickvision.tagger.json @@ -0,0 +1,59 @@ +{ + "app-id" : "org.nickvision.tagger", + "runtime" : "org.gnome.Platform", + "runtime-version" : "44", + "sdk" : "org.gnome.Sdk", + "sdk-extensions": [ + "org.freedesktop.Sdk.Extension.dotnet7" + ], + "build-options": { + "append-path": "/usr/lib/sdk/dotnet7/bin", + "append-ld-library-path": "/usr/lib/sdk/dotnet7/lib", + "env": { + "PKG_CONFIG_PATH": "/app/lib/pkgconfig:/app/share/pkgconfig:/usr/lib/pkgconfig:/usr/share/pkgconfig:/usr/lib/sdk/dotnet7/lib/pkgconfig" + } + }, + "command" : "/app/bin/org.nickvision.tagger", + "finish-args":[ + "--socket=fallback-x11", + "--socket=wayland", + "--device=dri", + "--share=ipc", + "--filesystem=home" + ], + "cleanup":[ + "/include", + "/lib/pkgconfig", + "*.a" + ], + "modules" : [ + { + "name": "blueprint-compiler", + "buildsystem": "meson", + "sources": [ + { + "type": "git", + "url": "https://gitlab.gnome.org/jwestman/blueprint-compiler", + "tag": "v0.6.0", + "commit": "9adcab2d225fd6435edc85c72a0b67e33880e00b" + } + ] + }, + { + "name": "org.nickvision.tagger", + "buildsystem": "simple", + "build-commands": [ + "chmod +x ./flatpak/build.sh", + "./flatpak/build.sh gnome" + ], + "sources": [ + { + "type": "dir", + "path": ".." + }, + "cake-sources.json", + "../NickvisionTagger.GNOME/nuget-sources.json" + ] + } + ] +} diff --git a/tools/Addins/Cake.FileHelpers.6.1.3/.signature.p7s b/tools/Addins/Cake.FileHelpers.6.1.3/.signature.p7s new file mode 100755 index 00000000..a9c237de Binary files /dev/null and b/tools/Addins/Cake.FileHelpers.6.1.3/.signature.p7s differ diff --git a/tools/Addins/Cake.FileHelpers.6.1.3/Cake.FileHelpers.nuspec b/tools/Addins/Cake.FileHelpers.6.1.3/Cake.FileHelpers.nuspec new file mode 100755 index 00000000..68230e42 --- /dev/null +++ b/tools/Addins/Cake.FileHelpers.6.1.3/Cake.FileHelpers.nuspec @@ -0,0 +1,22 @@ + + + + Cake.FileHelpers + 6.1.3 + Cake.FileHelpers + Redth + Apache-2.0 + https://licenses.nuget.org/Apache-2.0 + icon.png + https://github.com/cake-contrib/Cake.FileHelpers + https://cdn.jsdelivr.net/gh/cake-contrib/graphics/png/addin/cake-contrib-addin-medium.png + Cake Build addon to provide Aliases for common File operations (Reading, Writing, Replacing Text). + Copyright 2017-2023 - Cake Contributions + cake script build cake-addin cake-build addin + + + + + + + \ No newline at end of file diff --git a/tools/Addins/Cake.FileHelpers.6.1.3/icon.png b/tools/Addins/Cake.FileHelpers.6.1.3/icon.png new file mode 100755 index 00000000..84c2a823 Binary files /dev/null and b/tools/Addins/Cake.FileHelpers.6.1.3/icon.png differ diff --git a/tools/Addins/Cake.FileHelpers.6.1.3/lib/net6.0/Cake.FileHelpers.xml b/tools/Addins/Cake.FileHelpers.6.1.3/lib/net6.0/Cake.FileHelpers.xml new file mode 100755 index 00000000..c17b9d09 --- /dev/null +++ b/tools/Addins/Cake.FileHelpers.6.1.3/lib/net6.0/Cake.FileHelpers.xml @@ -0,0 +1,379 @@ + + + + Cake.FileHelpers + + + + + File helper aliases. + A set of aliases for Cake Build to help with simple File operations such as Reading, Writing and Replacing text. + + + + + Reads all text from a file + + The file's text. + The context. + The file to read. + + + + Reads all text from a file + + The file's text. + The context. + The file to read. + The encoding to use for the file. + + + + Reads all lines from a file + + The file's text lines. + The context. + The file to read. + + + + Reads all lines from a file + + The file's text lines. + The context. + The file to read. + The encoding to use for the file. + + + + Writes all text to a file + + The context. + The file to write to. + The text to write. + + + + Writes all text to a file + + The context. + The file to write to. + The encoding to use for the file. + The text to write. + + + + Writes all text lines to a file + + The context. + The file to write to. + The text lines to write. + + + + Writes all text lines to a file + + The context. + The file to write to. + The encoding to use for the file. + The text lines to write. + + + + Appends all text to a file + + The context. + The file to append text to. + The text to append. + + + + Appends all text to a file + + The context. + The file to append text to. + The encoding to use for the file. + The text to append. + + + + Appends all text lines to a file + + The context. + The file to append text to. + The text lines to append. + + + + Appends all text lines to a file + + The context. + The file to append text to. + The encoding to use for the file. + The text lines to append. + + + + Sets the last write time of a file to the current time + + The context. + The file to touch + + + + Replaces the text in files matched by the given globber pattern + + The files that had text replaced in them. + The context. + The globber pattern to match files to replace text in. + The text to find. + The replacement text. + + + + Replaces the text in files matched by the given globber pattern + + The files that had text replaced in them. + The context. + The globber pattern to match files to replace text in. + The encoding to use for the file. + The text to find. + The replacement text. + + + + Replaces the regex pattern in files matched by the given globber pattern. + + The files that had text replaced in them. + The context. + The globber pattern to match files to replace text in. + The regular expression to find. + The replacement text. + + + + Replaces the regex pattern in files matched by the given globber pattern. + + The files that had text replaced in them. + The context. + The globber pattern to match files to replace text in. + The encoding to use for the file. + The regular expression to find. + The replacement text. + + + + Replaces the regex pattern in files matched by the given globber pattern. + + The files that had text replaced in them. + The context. + The globber pattern to match files to replace text in. + The regular expression to find. + The replacement text. + The regular expression options to use. + + + + Replaces the regex pattern in files matched by the given globber pattern. + + The files that had text replaced in them. + The context. + The globber pattern to match files to replace text in. + The encoding to use for the file. + The regular expression to find. + The replacement text. + The regular expression options to use. + + + + Finds files with regular expression pattern in files matching the given globber pattern. + + The files which match the regular expression and globber pattern. + The context. + The globber pattern to match files to replace text in. + The regular expression to find. + + + + Finds files with regular expression pattern in files matching the given globber pattern. + + The files which match the regular expression and globber pattern. + The context. + The globber pattern to match files to replace text in. + The encoding to use for the file. + The regular expression to find. + + + + Finds files with regular expression pattern in files matching the given globber pattern. + + The files which match the regular expression and globber pattern. + The context. + The globber pattern to match files to replace text in. + The regular expression to find. + The regular expression options to use. + + + + Finds files with regular expression pattern in files matching the given globber pattern. + + The files which match the regular expression and globber pattern. + The context. + The globber pattern to match files to replace text in. + The encoding to use for the file. + The regular expression to find. + The regular expression options to use. + + + + Finds files with the given text in files matching the given globber pattern. + + The files which match the regular expression and globber pattern. + The context. + The globber pattern to match files to find text in. + The substring to find. + + + + Finds files with the given text in files matching the given globber pattern. + + The files which match the regular expression and globber pattern. + The context. + The globber pattern to match files to replace text in. + The encoding to use for the file. + The regular expression to find. + + + + Finds files with the given text in files matching the given collection of files. + + The files which match the regular expession in the files. + The context. + The files to find text in. + The substring to find. + + + + Finds files with the given text in files matching the given collection of files. + + The files which match the regular expession in the files. + The context. + The files to find text in. + The encoding to use for the file. + The substring to find. + + + + Finds the regex matches in a text file. + + The regex matches in file. + Context. + The text file. + The regex pattern to search for. + The regex options. + + + + Finds the regex matches in a text file. + + The regex matches in file. + Context. + The text file. + The encoding to use for the file. + The regex pattern to search for. + The regex options. + + + + Finds the first regex match in a textfile. + + The first regex match in the file. + The context. + The file. + The regex pattern to search for. + The regex options. + + + + Finds the first regex match in a textfile. + + The first regex match in the file. + The context. + The file. + The encoding to use for the file. + The regex pattern to search for. + The regex options. + + + + Finds regex matches in a file and returns all match groups. + + The matches with their groups. + The context. + The file. + The regex pattern to search for. + The regex options. + + + + Finds regex matches in a file and returns all match groups. + + The matches with their groups. + The context. + The file. + The encoding to use for the file. + The regex pattern to search for. + The regex options. + + + + Finds the first regex match in a file and returns all match groups. + + The match groups. + The context. + The file. + The regex pattern to search for. + The regex options. + + + + Finds the first regex match in a file and returns all match groups. + + The match groups. + The context. + The file. + The encoding to use for the file. + The regex pattern to search for. + The regex options. + + + + Finds the first regex match in a file and returns a specific match group. + + The matches with their groups. + The context. + The file. + The regex pattern to search for. + The specific match group. + The regex options. + + + + Finds the first regex match in a file and returns a specific match group. + + The matches with their groups. + The context. + The file. + The encoding to use for the file. + The regex pattern to search for. + The specific match group. + The regex options. + + + + This namespace contain types representing data used for common file operations, + such as reading, writing and replacing text. + + + + diff --git a/tools/Addins/Cake.FileHelpers.6.1.3/lib/net7.0/Cake.FileHelpers.xml b/tools/Addins/Cake.FileHelpers.6.1.3/lib/net7.0/Cake.FileHelpers.xml new file mode 100755 index 00000000..c17b9d09 --- /dev/null +++ b/tools/Addins/Cake.FileHelpers.6.1.3/lib/net7.0/Cake.FileHelpers.xml @@ -0,0 +1,379 @@ + + + + Cake.FileHelpers + + + + + File helper aliases. + A set of aliases for Cake Build to help with simple File operations such as Reading, Writing and Replacing text. + + + + + Reads all text from a file + + The file's text. + The context. + The file to read. + + + + Reads all text from a file + + The file's text. + The context. + The file to read. + The encoding to use for the file. + + + + Reads all lines from a file + + The file's text lines. + The context. + The file to read. + + + + Reads all lines from a file + + The file's text lines. + The context. + The file to read. + The encoding to use for the file. + + + + Writes all text to a file + + The context. + The file to write to. + The text to write. + + + + Writes all text to a file + + The context. + The file to write to. + The encoding to use for the file. + The text to write. + + + + Writes all text lines to a file + + The context. + The file to write to. + The text lines to write. + + + + Writes all text lines to a file + + The context. + The file to write to. + The encoding to use for the file. + The text lines to write. + + + + Appends all text to a file + + The context. + The file to append text to. + The text to append. + + + + Appends all text to a file + + The context. + The file to append text to. + The encoding to use for the file. + The text to append. + + + + Appends all text lines to a file + + The context. + The file to append text to. + The text lines to append. + + + + Appends all text lines to a file + + The context. + The file to append text to. + The encoding to use for the file. + The text lines to append. + + + + Sets the last write time of a file to the current time + + The context. + The file to touch + + + + Replaces the text in files matched by the given globber pattern + + The files that had text replaced in them. + The context. + The globber pattern to match files to replace text in. + The text to find. + The replacement text. + + + + Replaces the text in files matched by the given globber pattern + + The files that had text replaced in them. + The context. + The globber pattern to match files to replace text in. + The encoding to use for the file. + The text to find. + The replacement text. + + + + Replaces the regex pattern in files matched by the given globber pattern. + + The files that had text replaced in them. + The context. + The globber pattern to match files to replace text in. + The regular expression to find. + The replacement text. + + + + Replaces the regex pattern in files matched by the given globber pattern. + + The files that had text replaced in them. + The context. + The globber pattern to match files to replace text in. + The encoding to use for the file. + The regular expression to find. + The replacement text. + + + + Replaces the regex pattern in files matched by the given globber pattern. + + The files that had text replaced in them. + The context. + The globber pattern to match files to replace text in. + The regular expression to find. + The replacement text. + The regular expression options to use. + + + + Replaces the regex pattern in files matched by the given globber pattern. + + The files that had text replaced in them. + The context. + The globber pattern to match files to replace text in. + The encoding to use for the file. + The regular expression to find. + The replacement text. + The regular expression options to use. + + + + Finds files with regular expression pattern in files matching the given globber pattern. + + The files which match the regular expression and globber pattern. + The context. + The globber pattern to match files to replace text in. + The regular expression to find. + + + + Finds files with regular expression pattern in files matching the given globber pattern. + + The files which match the regular expression and globber pattern. + The context. + The globber pattern to match files to replace text in. + The encoding to use for the file. + The regular expression to find. + + + + Finds files with regular expression pattern in files matching the given globber pattern. + + The files which match the regular expression and globber pattern. + The context. + The globber pattern to match files to replace text in. + The regular expression to find. + The regular expression options to use. + + + + Finds files with regular expression pattern in files matching the given globber pattern. + + The files which match the regular expression and globber pattern. + The context. + The globber pattern to match files to replace text in. + The encoding to use for the file. + The regular expression to find. + The regular expression options to use. + + + + Finds files with the given text in files matching the given globber pattern. + + The files which match the regular expression and globber pattern. + The context. + The globber pattern to match files to find text in. + The substring to find. + + + + Finds files with the given text in files matching the given globber pattern. + + The files which match the regular expression and globber pattern. + The context. + The globber pattern to match files to replace text in. + The encoding to use for the file. + The regular expression to find. + + + + Finds files with the given text in files matching the given collection of files. + + The files which match the regular expession in the files. + The context. + The files to find text in. + The substring to find. + + + + Finds files with the given text in files matching the given collection of files. + + The files which match the regular expession in the files. + The context. + The files to find text in. + The encoding to use for the file. + The substring to find. + + + + Finds the regex matches in a text file. + + The regex matches in file. + Context. + The text file. + The regex pattern to search for. + The regex options. + + + + Finds the regex matches in a text file. + + The regex matches in file. + Context. + The text file. + The encoding to use for the file. + The regex pattern to search for. + The regex options. + + + + Finds the first regex match in a textfile. + + The first regex match in the file. + The context. + The file. + The regex pattern to search for. + The regex options. + + + + Finds the first regex match in a textfile. + + The first regex match in the file. + The context. + The file. + The encoding to use for the file. + The regex pattern to search for. + The regex options. + + + + Finds regex matches in a file and returns all match groups. + + The matches with their groups. + The context. + The file. + The regex pattern to search for. + The regex options. + + + + Finds regex matches in a file and returns all match groups. + + The matches with their groups. + The context. + The file. + The encoding to use for the file. + The regex pattern to search for. + The regex options. + + + + Finds the first regex match in a file and returns all match groups. + + The match groups. + The context. + The file. + The regex pattern to search for. + The regex options. + + + + Finds the first regex match in a file and returns all match groups. + + The match groups. + The context. + The file. + The encoding to use for the file. + The regex pattern to search for. + The regex options. + + + + Finds the first regex match in a file and returns a specific match group. + + The matches with their groups. + The context. + The file. + The regex pattern to search for. + The specific match group. + The regex options. + + + + Finds the first regex match in a file and returns a specific match group. + + The matches with their groups. + The context. + The file. + The encoding to use for the file. + The regex pattern to search for. + The specific match group. + The regex options. + + + + This namespace contain types representing data used for common file operations, + such as reading, writing and replacing text. + + + +