├── .gitignore ├── .travis.yml ├── .tx └── config ├── AUTHORS.md ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── ISSUE_TEMPLATE.md ├── LICENSE ├── README.md ├── arch ├── PKGBUILD └── README.md ├── data ├── com.github.spheras.desktopfolder.appdata.xml.in ├── com.github.spheras.desktopfolder.desktop.in ├── com.github.spheras.desktopfolder.gschema.xml ├── css.gresource.xml ├── css │ └── Application.css ├── icons │ ├── 16 │ │ └── com.github.spheras.desktopfolder.svg │ ├── 48 │ │ └── com.github.spheras.desktopfolder.svg │ ├── 64 │ │ └── com.github.spheras.desktopfolder.svg │ └── 128 │ │ └── com.github.spheras.desktopfolder.svg ├── img │ ├── clip-blue.png │ ├── clip-green.png │ ├── clip-orange.png │ ├── clip-pink.png │ ├── clip-red.png │ ├── clip-yellow.png │ ├── fixo-banana.svg │ ├── fixo-black.svg │ ├── fixo-blueberry.svg │ ├── fixo-grape.svg │ ├── fixo-lime.svg │ ├── fixo-orange.svg │ ├── fixo-strawberry.svg │ ├── fixo-white.svg │ ├── hip-square.png │ ├── link.svg │ └── shadow.png ├── meson.build ├── screenshot-1.png ├── screenshot-2.png └── screenshot-3.png ├── etc ├── banner.png ├── knowhow ├── oldicon.svg ├── test-color.gif ├── test-grid.gif ├── test-item.gif ├── test-link.gif ├── test-note.gif ├── test-photo.gif └── uncrustify-elementary-vala.cfg ├── meson.build ├── meson └── post_install.py ├── meson_options.txt ├── po ├── LINGUAS ├── POTFILES.in ├── POTFILES.skip ├── README ├── bg.po ├── ca.po ├── com.github.spheras.desktopfolder.pot ├── cs.po ├── da.po ├── de_DE.po ├── en_GB.po ├── eo.po ├── es.po ├── fi.po ├── fr.po ├── fr_FR.po ├── get_merge_translations.sh ├── hu.po ├── it.po ├── ja.po ├── lt.po ├── meson.build ├── ms_MY.po ├── nl_NL.po ├── oc.po ├── pl.po ├── pt_BR.po ├── pt_PT.po ├── ro_RO.po ├── ru.po ├── tr.po ├── uk.po └── zh_CN.po ├── pre-commit ├── snap ├── requirements.txt └── snapcraft.yaml └── src ├── Application.vala ├── LanguageText.vala ├── Resources.vala ├── dialogs ├── PanelPropertiesWindow.vala └── ProgressDialog.vala ├── logic ├── DesktopManager.vala ├── FolderManager.vala ├── ItemManager.vala ├── NoteManager.vala ├── PhotoManager.vala └── folderarrangement │ ├── FolderArrangement.vala │ ├── FolderArrangementFree.vala │ ├── FolderArrangementGrid.vala │ ├── FolderArrangementManaged.vala │ ├── FolderGrid.vala │ ├── FolderSort.vala │ ├── FolderSortByName.vala │ ├── FolderSortBySize.vala │ └── FolderSortByType.vala ├── settings ├── FolderSettings.vala ├── ItemSettings.vala ├── NoteSettings.vala ├── PhotoSettings.vala └── PositionSettings.vala ├── utils ├── EditableLabel.vala ├── RenameDialog.vala ├── Util.vala ├── animation │ ├── Fx.vala │ └── GtkAnimation.vala ├── clipboard │ ├── ClipboardFile.vala │ └── ClipboardManager.vala ├── dragndrop │ ├── DndBehaviour.vala │ ├── DndHandler.vala │ ├── DndView.vala │ └── DragNDrop.vala ├── menu │ ├── MenuItemColor.vala │ └── MenuItemSeparator.vala └── shortcut │ ├── CustomShortcutSettings.vala │ └── Shortcut.vala └── widgets ├── DesktopWindow.vala ├── FolderWindow.vala ├── ItemView.vala ├── NoteWindow.vala └── PhotoWindow.vala /.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | /debug 3 | debian/build/ 4 | debian/debhelper-build-stamp 5 | debian/files 6 | debian/com.github.spheras.desktopfolder.debhelper.log 7 | debian/com.github.spheras.desktopfolder.substvars 8 | debian/com.github.spheras.desktopfolder/ 9 | .atom-dbg.cson 10 | *~ 11 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | language: node_js 4 | 5 | node_js: 6 | - 10.17.0 7 | 8 | sudo: required 9 | 10 | services: 11 | - docker 12 | 13 | addons: 14 | apt: 15 | update: true 16 | sources: 17 | - ubuntu-toolchain-r-test 18 | packages: 19 | - libstdc++-5-dev 20 | 21 | cache: 22 | directories: 23 | - /tmp/liftoff 24 | 25 | matrix: 26 | include: 27 | -env: DIST=juno 28 | 29 | install: 30 | - npm i -g @elementaryos/houston 31 | 32 | script: 33 | - houston ci 34 | -------------------------------------------------------------------------------- /.tx/config: -------------------------------------------------------------------------------- 1 | [main] 2 | host = https://www.transifex.com 3 | 4 | [desktopfolder.com-github-spheras-desktopfolder-pot] 5 | file_filter = po/.po 6 | minimum_perc = 0 7 | source_file = po/com.github.spheras.desktopfolder.pot 8 | source_lang = en_US 9 | type = PO 10 | 11 | -------------------------------------------------------------------------------- /AUTHORS.md: -------------------------------------------------------------------------------- 1 | # Desktop Folder contributors 2 | 3 | * **[José Amuedo (spheras)](https://github.com/spheras)** 4 | 5 | * **[José Ignacio Centeno (jica)](https://github.com/jica)** 6 | 7 | * **[aljelly](https://github.com/aljelly)** 8 | 9 | # Translators 10 | 11 | * **[welaq](https://github.com/welaq)** 12 | * Lithuanian translation 13 | 14 | * **[nvivant](https://github.com/nvivant)** 15 | * French translation 16 | 17 | * **[pavlepiramida](https://github.com/pavlepiramida)** 18 | * Serbian translation (Cyrillic and Latin versions) 19 | 20 | * **[camellan](https://github.com/camellan)** 21 | * Russian translation 22 | 23 | * **[ingumsky](https://github.com/ingumsky)** 24 | * Russian translation 25 | 26 | * **[andreas-xavier](https://github.com/andreas-xavier)** 27 | * Italian translation 28 | 29 | * **[vistaus](https://github.com/vistaus)** 30 | * Dutch translation 31 | 32 | # Resources 33 | 34 | * App Icon was derived from [Plank's](https://launchpad.net/plank) icon. 35 | * Paper texture made by [Tomislava Babić](https://www.behance.net/antitomi) 36 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. 6 | 7 | ## Our Standards 8 | 9 | Examples of behavior that contributes to creating a positive environment include: 10 | 11 | * Using welcoming and inclusive language 12 | * Being respectful of differing viewpoints and experiences 13 | * Gracefully accepting constructive criticism 14 | * Focusing on what is best for the community 15 | * Showing empathy towards other community members 16 | 17 | Examples of unacceptable behavior by participants include: 18 | 19 | * The use of sexualized language or imagery and unwelcome sexual attention or advances 20 | * Trolling, insulting/derogatory comments, and personal or political attacks 21 | * Public or private harassment 22 | * Publishing others' private information, such as a physical or electronic address, without explicit permission 23 | * Other conduct which could reasonably be considered inappropriate in a professional setting 24 | 25 | ## Our Responsibilities 26 | 27 | Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. 28 | 29 | Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. 30 | 31 | ## Scope 32 | 33 | This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. 34 | 35 | ## Enforcement 36 | 37 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at joseamuedo@gmail.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. 38 | 39 | Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. 40 | 41 | ## Attribution 42 | 43 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] 44 | 45 | [homepage]: http://contributor-covenant.org 46 | [version]: http://contributor-covenant.org/version/1/4/ 47 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | Please see [wiki](https://github.com/spheras/desktopfolder/wiki/Contributing) for contributing information. 4 | -------------------------------------------------------------------------------- /ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 7 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Desktop Folder 2 | Bring your desktop back to life. 3 | 4 | [![Get it on AppCenter](https://appcenter.elementary.io/badge.svg)](https://appcenter.elementary.io/com.github.spheras.desktopfolder) 5 | 6 | [![Build Status](https://travis-ci.org/spheras/desktopfolder.svg?branch=master)](https://travis-ci.org/spheras/desktopfolder) 7 | [![License: GPL v3](https://img.shields.io/badge/License-GPL%20v3-blue.svg)](http://www.gnu.org/licenses/gpl-3.0) 8 | 9 | ![Desktop Folder Banner](https://raw.githubusercontent.com/spheras/desktopfolder/master/etc/banner.png) 10 | 11 | ## Description 12 | Organize your desktop with panels that hold your things. 13 | - Access files, folders and apps from your desktop 14 | - Drop files, folders, links and .desktop launchers inside panels 15 | - Resize, position and color panels 16 | - Display photos and keep notes on your desktop 17 | - Reveal the desktop with ⌘-D 18 | 19 | ![Example1](https://raw.githubusercontent.com/spheras/desktopfolder/master/etc/test-color.gif) 20 | ![Example2](https://raw.githubusercontent.com/spheras/desktopfolder/master/etc/test-grid.gif) 21 | ![Example3](https://raw.githubusercontent.com/spheras/desktopfolder/master/etc/test-item.gif) 22 | ![Example4](https://raw.githubusercontent.com/spheras/desktopfolder/master/etc/test-link.gif) 23 | ![Example5](https://raw.githubusercontent.com/spheras/desktopfolder/master/etc/test-note.gif) 24 | ![Example6](https://raw.githubusercontent.com/spheras/desktopfolder/master/etc/test-photo.gif) 25 | 26 | ## How to Install 27 | If you use elementary OS, you can get it directly from the AppCenter. 28 | 29 | [![Get it on AppCenter](https://appcenter.elementary.io/badge.svg)](https://appcenter.elementary.io/com.github.spheras.desktopfolder) 30 | 31 | Our main target is elementary, but Desktop Folder should also work on Ubuntu and GNOME 3. You should use the latest [.deb release](https://github.com/spheras/desktopfolder/releases). To install the .deb with apt: 32 | 33 | `sudo apt install ./com.github.spheras.desktopfolder_[version]_amd64.deb` 34 | 35 | Open it like any other app after installing. Desktop Folder will launch automatically when you next log in. 36 | 37 | ## Current Status on Wayland Support 38 | 39 | Wayland (in Ubuntu since 17.10) is not fully supported yet, and we've experienced problems with adding support for it. We are working hard to fix this. You're welcome to try Desktop Folder and help us find or fix issues. 40 | 41 | ## Contributing 42 | 43 | See the [Contributing page](https://github.com/spheras/desktopfolder/wiki/Contributing) on the wiki. 44 | 45 | ## Building and Installing 46 | Important!! This version only works correctly with GTK library >=3.22 47 | 48 | You'll need the following dependencies to build: 49 | 50 | * libgee-0.8-dev 51 | * libcairo2-dev 52 | * libjson-glib-dev 53 | * libgdk-pixbuf2.0-dev 54 | * libwnck-3-dev 55 | * libgtksourceview-3.0-dev 56 | * libjson-glib-dev 57 | * meson 58 | * valac 59 | * intltool 60 | 61 | `sudo apt install meson valac libgee-0.8-dev libcairo2-dev libjson-glib-dev libgdk-pixbuf2.0-dev libwnck-3-dev libgtksourceview-3.0-dev libjson-glib-dev intltool` 62 | 63 | Run `meson build` to configure the build environment and then change to the build directory and run `ninja` to build: 64 | 65 | meson build 66 | cd build 67 | meson configure -D prefix=/usr 68 | ninja 69 | 70 | To install, use `ninja install`, then execute with `com.github.spheras.desktopfolder`: 71 | 72 | sudo ninja install 73 | com.github.spheras.desktopfolder 74 | -------------------------------------------------------------------------------- /arch/PKGBUILD: -------------------------------------------------------------------------------- 1 | # Maintainer: Gabriele Musco (change it if you want to put it on AUR) 2 | # Upstream URL: https://github.com/spheras/desktopfolder 3 | 4 | pkgname=desktopfolder-git 5 | pkgver=v1.0.5 6 | pkgrel=1 7 | pkgdesc='Bring your elementary desktop back to life' 8 | arch=('any') 9 | url='https://github.com/spheras/desktopfolder' 10 | license=('GPL3') 11 | depends=('libgee' 'cairo' 'json-glib' 'gdk-pixbuf2' 'libwnck3' 'gtksourceview3' 'gtk3>=3.14' 'python') 12 | makedepends=('git' 'meson' 'vala') 13 | provides=('desktopfolder') 14 | conflicts=('desktopfolder') 15 | source=("desktopfolder::git+https://github.com/spheras/desktopfolder") 16 | sha256sums=('SKIP') 17 | 18 | pkgver() { 19 | cd "$srcdir/desktopfolder" 20 | git describe --long --tags | sed 's/\([^-]*-g\)/r\1/;s/-/./g' 21 | } 22 | 23 | build() { 24 | cd "$srcdir/desktopfolder" 25 | rm -rf build 26 | mkdir build 27 | cd build 28 | meson --prefix /usr --buildtype release .. 29 | ninja 30 | } 31 | 32 | package() { 33 | cd "$srcdir/desktopfolder" 34 | cd build 35 | DESTDIR="$pkgdir" ninja install 36 | } 37 | -------------------------------------------------------------------------------- /arch/README.md: -------------------------------------------------------------------------------- 1 | # Build on Arch Linux 2 | 3 | - Clone this repo 4 | 5 | ```bash 6 | git clone https://github.com/spheras/desktopfolder 7 | ``` 8 | 9 | - Navigate to the `desktopfolder/arch` folder 10 | 11 | ```bash 12 | cd desktopfolder/arch 13 | ``` 14 | 15 | - Build the package (it may ask for your root password to install dependencies) 16 | 17 | ```bash 18 | makepkg -s 19 | ``` 20 | 21 | - Install the newly built package 22 | 23 | ```bash 24 | sudo pacman -U *.pkg.tar.xz 25 | ``` 26 | -------------------------------------------------------------------------------- /data/com.github.spheras.desktopfolder.appdata.xml.in: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | com.github.spheras.desktopfolder 5 | CC0-1.0 6 | GPL-3.0+ 7 | Desktop Folder 8 | Bring your desktop back to life 9 | joseamuedo@gmail.com 10 | 11 |

Organize your desktop with panels that hold your things.

12 |
    13 |
  • Access files, folders and apps from your desktop
  • 14 |
  • Drop files, folders, links and .desktop launchers inside panels
  • 15 |
  • Resize, position and color panels
  • 16 |
  • Display photos and keep notes on your desktop
  • 17 |
  • Reveal the desktop with ⌘-D
  • 18 |
19 |

Open it like any other app after installing. Desktop Folder will launch automatically when you next log in.

20 |
21 | 22 | com.github.spheras.desktopfolder 23 | 24 | 25 | 26 | Overall desktop view 27 | https://raw.githubusercontent.com/spheras/desktopfolder/master/data/screenshot-1.png 28 | 29 | 30 | Overall desktop view 31 | https://raw.githubusercontent.com/spheras/desktopfolder/master/data/screenshot-2.png 32 | 33 | 34 | Overall desktop view 35 | https://raw.githubusercontent.com/spheras/desktopfolder/master/data/screenshot-3.png 36 | 37 | 38 | DesktopFolder Developers 39 | https://github.com/spheras/desktopfolder 40 | https://github.com/spheras/desktopfolder/issues 41 | 42 | #e9f4fc 43 | #044b6c 44 | 0 45 | 46 | 47 | 48 | 49 | 50 |

1.1.3 release.

51 |
    52 |
  • fix: removed ubuntu installations post scripts
  • 53 |
  • fix: houston ci node version
  • 54 |
  • fix: transparent icons on changing desktops
  • 55 |
  • fix: fast double click (issue #293)
  • 56 |
  • fix: black backdrop on Fedora (issue #297)
  • 57 |
  • Translations Added and Updated
  • 58 |
59 |
60 |
61 | 62 | 63 | 64 |

1.1.2 release.

65 |
    66 |
  • Reorganised Menu
  • 67 |
  • Double click on photos to open
  • 68 |
  • Translations Added and Updated
  • 69 |
  • Minor bugfixing
  • 70 |
71 |
72 |
73 | 74 | 75 | 76 |

1.1.0 release.

77 |
    78 |
  • Free, Grid and Automatic arrangements
  • 79 |
  • Drag & Drop without Ctrl Key and autodetection
  • 80 |
  • Transifex translations integration, new translations added
  • 81 |
  • File Synchronization Threads
  • 82 |
  • Animations Fx
  • 83 |
  • Hide/Show Desktop Panel and Icons
  • 84 |
  • Vertical/Horizontal Icons Arrangement
  • 85 |
  • Folder Panels with Scrollbars
  • 86 |
  • Folder/File Info Dialog
  • 87 |
  • Other minor functionality added
  • 88 |
  • Minor bugfixing
  • 89 |
90 |
91 |
92 | 93 | 94 | 95 | 96 |

1.0.10 release.

97 |
    98 |
  • Change Icon for Files, and SVG selections
  • 99 |
  • Auto alignment for initial files
  • 100 |
  • Ubuntu Budgie compatible
  • 101 |
  • fully update to Gtk+3.22
  • 102 |
  • Minor bugfixes and stabilization
  • 103 |
104 |
105 |
106 | 107 | 108 | 109 |

Release: Juno publication.

110 |
    111 |
  • Minor bugfix release
  • 112 |
  • Juno publication release
  • 113 |
114 |
115 |
116 | 117 | 118 | 119 |

Release: Updated for Elementary Juno and other minor features.

120 |
    121 |
  • Change Wallpaper from Desktop right click
  • 122 |
  • Dutch Translation by @Vistaus
  • 123 |
  • Improved Russian Translation by @ingumsky
  • 124 |
  • Updated project for GTK+ 3.22 and Elementary Juno
  • 125 |
  • Minor Bugfixes are included for this release
  • 126 |
127 |
128 |
129 | 130 | 131 | 132 |

Release: Italian and minor improvements.

133 |
    134 |
  • Italian Translation thanks to andreas-xavier
  • 135 |
  • Saving settings performance improvements
  • 136 |
  • Files *~ are ignored now
  • 137 |
138 |
139 |
140 | 141 | 142 | 143 |

1.0.5 release with great changes!

144 |
    145 |
  • Desktop Background & Rename in Place
  • 146 |
  • Different Notes Status (top, back, normal)
  • 147 |
  • Buttons to Headers (remove)
  • 148 |
  • Link Panels to folders
  • 149 |
  • Photo Scale aspect ratio
  • 150 |
  • Custom Colors for Panel and Notes
  • 151 |
  • Menu Redesign And Panel Settings Window
  • 152 |
  • Restore Windows when Super+D is pressed again
  • 153 |
  • Resolution Strategy to prevent widgets going off-screen
  • 154 |
  • Use .desktopnote/.desktopphoto rather than .dfn/.dfp
  • 155 |
  • Don't allow renaming panels to invalid folder names
  • 156 |
  • Serbian translation by @pavlepiramida
  • 157 |
  • Russian translation by @camellan
  • 158 |
  • Arch building by @GabMus
  • 159 |
  • A lot of Minor Bugfixing
  • 160 |
161 |
162 |
163 | 164 | 165 | 166 |

1.0.2 release.

167 |
    168 |
  • Drag and Drop to Move/Copy/Link depending on key pressed (none/control/alt|shift)
  • 169 |
  • Text Shadow and Bold configuration
  • 170 |
  • Deleting Last Panel bugfixing
  • 171 |
  • Move panels from body
  • 172 |
  • DESKTOP window types (workspace movement improvement and others)
  • 173 |
  • Drag and Drop folders (recursive copy)
  • 174 |
  • Undo/Redo and others over Text Notes & Menu Redesign
  • 175 |
  • Lithuanian and French translations
  • 176 |
  • Minor Bugfixing included with this release
  • 177 |
178 |
179 |
180 |
181 | 182 | 183 | none 184 | none 185 | none 186 | none 187 | none 188 | none 189 | none 190 | none 191 | none 192 | none 193 | none 194 | none 195 | none 196 | none 197 | none 198 | none 199 | none 200 | none 201 | none 202 | none 203 | none 204 | intense 205 | none 206 | none 207 | none 208 | none 209 | none 210 | 211 |
212 | -------------------------------------------------------------------------------- /data/com.github.spheras.desktopfolder.desktop.in: -------------------------------------------------------------------------------- 1 | [Desktop Entry] 2 | Name=Desktop Folder 3 | Comment=Organize your desktop with panels, notes and photos 4 | Exec=@exe_var@ 5 | Icon=com.github.spheras.desktopfolder 6 | Keywords=desktop;folder;file;organize; 7 | Terminal=false 8 | Type=Application 9 | NoDisplay=false 10 | X-GNOME-Autostart-enabled=true 11 | X-GNOME-Autostart-phase=Running 12 | AutostartCondition=GSettings com.github.spheras.desktopfolder show-desktopfolder 13 | X-GNOME-AutoRestart=true 14 | StartupNotify=false 15 | Categories=GTK;Utility; 16 | -------------------------------------------------------------------------------- /data/com.github.spheras.desktopfolder.gschema.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 17 | 18 | true 19 | Icons on Desktop Panel 20 | Whether to show icons on Desktop Panel or not 21 | 22 | 23 | true 24 | Desktop Panel over Desktop 25 | Desktop Transparent Panel to allow desktop interaction 26 | 27 | 28 | 'STORE' 29 | Resolution Change Strategy 30 | How the widgets should be adapted to different screen resolutions 31 | 32 | 33 | 'GRID' 34 | Default Panel Arrangement 35 | Define whats the default arrangement strategy for panels 36 | 37 | 38 | 10 39 | Default Arrangement Padding 40 | The Default Arrangement Padding Between Items 41 | 42 | 43 | true 44 | Have DesktopFolder handle the desktop 45 | If set false, DesktopFolder will exit gracefully on startup or when running 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /data/css.gresource.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | css/Application.css 5 | 6 | img/hip-square.png 7 | img/clip-blue.png 8 | img/clip-green.png 9 | img/clip-orange.png 10 | img/clip-pink.png 11 | img/clip-red.png 12 | img/clip-yellow.png 13 | img/link.svg 14 | 15 | img/fixo-blueberry.svg 16 | img/fixo-black.svg 17 | img/fixo-lime.svg 18 | img/fixo-grape.svg 19 | img/fixo-white.svg 20 | img/fixo-banana.svg 21 | img/fixo-strawberry.svg 22 | img/fixo-orange.svg 23 | img/shadow.png 24 | 25 | 26 | -------------------------------------------------------------------------------- /data/icons/16/com.github.spheras.desktopfolder.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 44 | 47 | 48 | 50 | 59 | 61 | 65 | 69 | 70 | 79 | 81 | 85 | 89 | 90 | 99 | 101 | 105 | 109 | 110 | 119 | 121 | 125 | 129 | 133 | 137 | 138 | 140 | 144 | 148 | 152 | 156 | 157 | 167 | 176 | 183 | 190 | 191 | 193 | 194 | 196 | image/svg+xml 197 | 199 | 200 | 201 | 202 | 203 | 212 | 219 | 228 | 234 | 243 | 252 | 261 | 270 | 279 | 288 | 289 | -------------------------------------------------------------------------------- /data/img/clip-blue.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spheras/desktopfolder/c45d456b81546c702a792d2946e642509fccbee3/data/img/clip-blue.png -------------------------------------------------------------------------------- /data/img/clip-green.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spheras/desktopfolder/c45d456b81546c702a792d2946e642509fccbee3/data/img/clip-green.png -------------------------------------------------------------------------------- /data/img/clip-orange.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spheras/desktopfolder/c45d456b81546c702a792d2946e642509fccbee3/data/img/clip-orange.png -------------------------------------------------------------------------------- /data/img/clip-pink.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spheras/desktopfolder/c45d456b81546c702a792d2946e642509fccbee3/data/img/clip-pink.png -------------------------------------------------------------------------------- /data/img/clip-red.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spheras/desktopfolder/c45d456b81546c702a792d2946e642509fccbee3/data/img/clip-red.png -------------------------------------------------------------------------------- /data/img/clip-yellow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spheras/desktopfolder/c45d456b81546c702a792d2946e642509fccbee3/data/img/clip-yellow.png -------------------------------------------------------------------------------- /data/img/fixo-banana.svg: -------------------------------------------------------------------------------- 1 | 2 | 14 | 16 | 18 | 19 | 21 | image/svg+xml 22 | 24 | 25 | 26 | 27 | 28 | 31 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /data/img/fixo-black.svg: -------------------------------------------------------------------------------- 1 | 2 | 14 | 16 | 18 | 19 | 21 | image/svg+xml 22 | 24 | 25 | 26 | 27 | 28 | 31 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /data/img/fixo-blueberry.svg: -------------------------------------------------------------------------------- 1 | 2 | 14 | 16 | 18 | 19 | 21 | image/svg+xml 22 | 24 | 25 | 26 | 27 | 28 | 31 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /data/img/fixo-grape.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 20 | 22 | 48 | 51 | 52 | 54 | 55 | 57 | image/svg+xml 58 | 60 | 61 | 62 | 63 | 64 | 69 | 75 | 76 | 77 | -------------------------------------------------------------------------------- /data/img/fixo-lime.svg: -------------------------------------------------------------------------------- 1 | 2 | 14 | 16 | 18 | 19 | 21 | image/svg+xml 22 | 24 | 25 | 26 | 27 | 28 | 31 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /data/img/fixo-orange.svg: -------------------------------------------------------------------------------- 1 | 2 | 14 | 16 | 18 | 19 | 21 | image/svg+xml 22 | 24 | 25 | 26 | 27 | 28 | 31 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /data/img/fixo-strawberry.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 20 | 22 | 48 | 51 | 52 | 54 | 55 | 57 | image/svg+xml 58 | 60 | 61 | 62 | 63 | 64 | 69 | 75 | 76 | 77 | -------------------------------------------------------------------------------- /data/img/fixo-white.svg: -------------------------------------------------------------------------------- 1 | 2 | 18 | 39 | 41 | 43 | 44 | 46 | image/svg+xml 47 | 49 | 50 | 51 | 52 | 53 | 57 | 61 | 62 | 63 | -------------------------------------------------------------------------------- /data/img/hip-square.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spheras/desktopfolder/c45d456b81546c702a792d2946e642509fccbee3/data/img/hip-square.png -------------------------------------------------------------------------------- /data/img/link.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 21 | 39 | 41 | 42 | 44 | image/svg+xml 45 | 47 | 48 | 49 | 50 | 51 | 56 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /data/img/shadow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spheras/desktopfolder/c45d456b81546c702a792d2946e642509fccbee3/data/img/shadow.png -------------------------------------------------------------------------------- /data/meson.build: -------------------------------------------------------------------------------- 1 | icon_sizes = ['16', '48', '64', '128'] 2 | 3 | foreach i : icon_sizes 4 | install_data( 5 | join_paths('icons', i, meson.project_name() + '.svg'), 6 | install_dir: join_paths(get_option('datadir'), 'icons', 'hicolor', i + 'x' + i, 'apps') 7 | ) 8 | endforeach 9 | 10 | install_data( 11 | meson.project_name() + '.gschema.xml', 12 | install_dir: join_paths(get_option('datadir'), 'glib-2.0', 'schemas') 13 | ) 14 | 15 | podir = join_paths(meson.source_root(), 'po') 16 | intltool = find_program('intltool-merge') 17 | 18 | executable_name = exename # from root meson_options.txt 19 | 20 | desktop_conf = configuration_data() 21 | desktop_conf.set('exe_var', executable_name) 22 | 23 | desktop_exe = configure_file( input : meson.project_name() + '.desktop.in', 24 | output : 'desktopfolder_with_executable_name.desktop.in', 25 | configuration : desktop_conf) 26 | 27 | i18n.merge_file ( 28 | 'appdata', 29 | input: meson.project_name() + '.appdata.xml.in', 30 | output: meson.project_name() + '.appdata.xml', 31 | install: true, 32 | install_dir: join_paths(get_option('datadir'), 'metainfo'), 33 | po_dir: join_paths(meson.source_root (), 'po') 34 | ) 35 | 36 | i18n.merge_file ( 37 | 'desktop', 38 | input: desktop_exe, 39 | output: meson.project_name() + '.desktop', 40 | install: true, 41 | install_dir: join_paths(get_option('datadir'), 'applications'), 42 | po_dir: join_paths(meson.source_root (), 'po'), 43 | type: 'desktop' 44 | ) 45 | 46 | i18n.merge_file ( 47 | 'desktop', 48 | input: desktop_exe, 49 | output: meson.project_name() + '-autostart.desktop', 50 | install: true, 51 | install_dir: '/etc/xdg/autostart', 52 | po_dir: join_paths(meson.source_root (), 'po'), 53 | type: 'desktop' 54 | ) -------------------------------------------------------------------------------- /data/screenshot-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spheras/desktopfolder/c45d456b81546c702a792d2946e642509fccbee3/data/screenshot-1.png -------------------------------------------------------------------------------- /data/screenshot-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spheras/desktopfolder/c45d456b81546c702a792d2946e642509fccbee3/data/screenshot-2.png -------------------------------------------------------------------------------- /data/screenshot-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spheras/desktopfolder/c45d456b81546c702a792d2946e642509fccbee3/data/screenshot-3.png -------------------------------------------------------------------------------- /etc/banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spheras/desktopfolder/c45d456b81546c702a792d2946e642509fccbee3/etc/banner.png -------------------------------------------------------------------------------- /etc/knowhow: -------------------------------------------------------------------------------- 1 | First time build 2 | ················· 3 | $ meson build 4 | $ cd build 5 | $ ninja 6 | 7 | Build, install and run the app with debug messages in one line 8 | .............................................................. 9 | $ sudo ninja install && env G_MESSAGES_DEBUG=all com.github.spheras.desktopfolder 10 | 11 | 12 | Use debugger suite 13 | .................. 14 | Nemiver is GNOME's GUI debugger. It can be really useful for locating the source of crashes and bugs. 15 | $ sudo apt install nemiver 16 | 17 | After installing use this instead of the one liner above: 18 | $ sudo ninja install && env G_MESSAGES_DEBUG=all nemiver com.github.spheras.desktopfolder 19 | 20 | 21 | Install uncrustify 22 | ·················· 23 | Don't use sudo apt install uncrustify! 24 | 25 | Run this one-liner to install the latest release of uncrustify: 26 | $ cd ~/Downloads && wget https://github.com/uncrustify/uncrustify/archive/uncrustify-0.65.tar.gz && tar -xf uncrustify-0.65.tar.gz && cd uncrustify-uncrustify-0.65/cmake/ && cmake .. && sudo make install 27 | 28 | If you want to run it step by step instead: 29 | $ cd ~/Downloads 30 | $ wget https://github.com/uncrustify/uncrustify/archive/uncrustify-0.65.tar.gz 31 | $ tar -xf uncrustify-0.65.tar.gz 32 | $ cd uncrustify-uncrustify-0.65/cmake/ 33 | $ cmake .. 34 | $ sudo make install 35 | 36 | This means everyone is using the same version, so the output is consistent. 37 | 38 | 39 | Use uncrustify 40 | ·············· 41 | Go to src/ and run uncrustify like this: 42 | $ uncrustify --replace --no-backup -c ../etc/uncrustify-elementary-vala.cfg */*.vala *.vala 43 | 44 | Make sure you don't end comment lines with a \ (backslash), otherwise 45 | uncrustify turns the next line into a comment as well: 46 | 47 | // This prints a \ 48 | my_function (); 49 | 50 | Gets turned into: 51 | 52 | // This prints a 53 | // my_function (); 54 | 55 | It's okay if you put any character after it though: 56 | 57 | // This prints a \ character. 58 | // (or) This prints a \. 59 | my_function (); 60 | 61 | 62 | Show debug messages 63 | ··················· 64 | $ export G_MESSAGES_DEBUG=all 65 | 66 | 67 | Generate pot file 68 | ················· 69 | $ ninja com.github.spheras.desktopfolder-pot 70 | 71 | 72 | Debian build 73 | ············ 74 | sudo apt install devscripts 75 | 76 | From root directory: 77 | $ debuild -uc -us 78 | 79 | (Hint) To execute rules: 80 | $ ./debian/rules install 81 | 82 | 83 | GTK Introspector 84 | ················ 85 | $ gsettings set org.gtk.Settings.Debug enable-inspector-keybinding true 86 | 87 | After that, press Ctrl+Shift+D or Ctrl+Shift+I on the window you want to debug. 88 | 89 | 90 | Snap creation 91 | ············· 92 | $ sudo apt install snapcraft 93 | 94 | $ sudo docker pull snapcore/snapcraft 95 | $ sudo docker run -v $PWD:$PWD -w $PWD snapcore/snapcraft snapcraft 96 | 97 | Relase a new version 98 | ···················· 99 | https://wiki.debian.org/Creating%20signed%20GitHub%20releases 100 | 1. tag the code (change version): $ git tag -s v1.1.2 101 | 2. push the tag created: $ git push --tags 102 | 3. then publish a new release into github 103 | 4. download the .gz generated at the release 104 | 5. sign the tarball (change version): $ gpg --armor --detach-sign desktopfolder-1.1.2.tar.gz 105 | 6. it will create a desktopfolder-1.1.2.tar.gz.asc file 106 | 7. Edit your release again and attach the detached signature desktopfolder-1.1.2.tar.gz.asc as binary to the release. 107 | 8. Compile with debian at the root: $ debuild -uc -us 108 | 9. Attach also the .dsc .tar.xz and .deb generated 109 | -------------------------------------------------------------------------------- /etc/oldicon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 20 | 22 | 32 | 35 | 39 | 43 | 44 | 54 | 57 | 61 | 65 | 66 | 67 | 85 | 87 | 88 | 90 | image/svg+xml 91 | 93 | 94 | 95 | 96 | 97 | 102 | 109 | 115 | 118 | 123 | 132 | 141 | 150 | 159 | 168 | 169 | 170 | 171 | -------------------------------------------------------------------------------- /etc/test-color.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spheras/desktopfolder/c45d456b81546c702a792d2946e642509fccbee3/etc/test-color.gif -------------------------------------------------------------------------------- /etc/test-grid.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spheras/desktopfolder/c45d456b81546c702a792d2946e642509fccbee3/etc/test-grid.gif -------------------------------------------------------------------------------- /etc/test-item.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spheras/desktopfolder/c45d456b81546c702a792d2946e642509fccbee3/etc/test-item.gif -------------------------------------------------------------------------------- /etc/test-link.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spheras/desktopfolder/c45d456b81546c702a792d2946e642509fccbee3/etc/test-link.gif -------------------------------------------------------------------------------- /etc/test-note.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spheras/desktopfolder/c45d456b81546c702a792d2946e642509fccbee3/etc/test-note.gif -------------------------------------------------------------------------------- /etc/test-photo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spheras/desktopfolder/c45d456b81546c702a792d2946e642509fccbee3/etc/test-photo.gif -------------------------------------------------------------------------------- /meson.build: -------------------------------------------------------------------------------- 1 | project('com.github.spheras.desktopfolder', ['vala', 'c'], 2 | version: '1.1.3' 3 | ) 4 | 5 | gnome = import('gnome') 6 | i18n = import('i18n') 7 | 8 | asresources = gnome.compile_resources( 9 | 'as-resources', 'data/css.gresource.xml', 10 | source_dir: 'data', 11 | c_name: 'as' 12 | ) 13 | 14 | add_project_arguments(['--vapidir', join_paths(meson.current_source_dir(), 'vapi')], language: 'vala') 15 | 16 | conf = configuration_data() 17 | conf.set_quoted('GETTEXT_PACKAGE', meson.project_name()) 18 | configure_file(output: 'config.h', configuration: conf) 19 | config_h_dir = include_directories('.') 20 | 21 | c_args = [ 22 | '-include', 'config.h', 23 | '-DWNCK_I_KNOW_THIS_IS_UNSTABLE' 24 | ] 25 | 26 | #We need to add the math library if required 27 | cc = meson.get_compiler('c') 28 | m_dep = cc.find_library('m', required : false) 29 | 30 | #Source files 31 | src = [ 32 | 'src/LanguageText.vala', 33 | 'src/Resources.vala', 34 | 'src/Application.vala', 35 | 'src/utils/Util.vala', 36 | 'src/utils/shortcut/Shortcut.vala', 37 | 'src/utils/shortcut/CustomShortcutSettings.vala', 38 | 'src/utils/menu/MenuItemColor.vala', 39 | 'src/utils/menu/MenuItemSeparator.vala', 40 | 'src/utils/clipboard/ClipboardFile.vala', 41 | 'src/utils/clipboard/ClipboardManager.vala', 42 | 'src/utils/dragndrop/DragNDrop.vala', 43 | 'src/utils/dragndrop/DndHandler.vala', 44 | 'src/utils/dragndrop/DndView.vala', 45 | 'src/utils/dragndrop/DndBehaviour.vala', 46 | 'src/utils/RenameDialog.vala', 47 | 'src/utils/animation/GtkAnimation.vala', 48 | 'src/utils/animation/Fx.vala', 49 | 'src/utils/EditableLabel.vala', 50 | 'src/widgets/DesktopWindow.vala', 51 | 'src/widgets/FolderWindow.vala', 52 | 'src/widgets/NoteWindow.vala', 53 | 'src/widgets/ItemView.vala', 54 | 'src/widgets/PhotoWindow.vala', 55 | 'src/settings/PositionSettings.vala', 56 | 'src/settings/FolderSettings.vala', 57 | 'src/settings/ItemSettings.vala', 58 | 'src/settings/NoteSettings.vala', 59 | 'src/settings/PhotoSettings.vala', 60 | 'src/logic/DesktopManager.vala', 61 | 'src/logic/FolderManager.vala', 62 | 'src/logic/ItemManager.vala', 63 | 'src/logic/NoteManager.vala', 64 | 'src/logic/PhotoManager.vala', 65 | 'src/dialogs/ProgressDialog.vala', 66 | 'src/dialogs/PanelPropertiesWindow.vala', 67 | 'src/logic/folderarrangement/FolderArrangement.vala', 68 | 'src/logic/folderarrangement/FolderArrangementFree.vala', 69 | 'src/logic/folderarrangement/FolderArrangementGrid.vala', 70 | 'src/logic/folderarrangement/FolderArrangementManaged.vala', 71 | 'src/logic/folderarrangement/FolderSort.vala', 72 | 'src/logic/folderarrangement/FolderSortByName.vala', 73 | 'src/logic/folderarrangement/FolderSortBySize.vala', 74 | 'src/logic/folderarrangement/FolderSortByType.vala', 75 | 'src/logic/folderarrangement/FolderGrid.vala' 76 | ] 77 | 78 | exename = get_option('exename') 79 | 80 | executable( 81 | exename, 82 | src, 83 | asresources, 84 | c_args: c_args, 85 | dependencies: [ 86 | dependency('gio-unix-2.0'), 87 | dependency('gee-0.8'), 88 | dependency('glib-2.0'), 89 | dependency('gobject-2.0'), 90 | dependency('gtk+-3.0',version:'>=3.22'), 91 | dependency('cairo'), 92 | dependency('json-glib-1.0'), 93 | dependency('gdk-pixbuf-2.0'), 94 | dependency('libwnck-3.0'), 95 | dependency('gtksourceview-3.0'), 96 | m_dep 97 | ], 98 | install : true 99 | ) 100 | 101 | meson.add_install_script('meson/post_install.py') 102 | 103 | subdir('data') 104 | subdir('po') 105 | -------------------------------------------------------------------------------- /meson/post_install.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import os 4 | import subprocess 5 | 6 | schemadir = os.path.join(os.environ['MESON_INSTALL_PREFIX'], 'share', 'glib-2.0', 'schemas') 7 | 8 | if not os.environ.get('DESTDIR'): 9 | print('Compiling gsettings schemas...') 10 | subprocess.call(['glib-compile-schemas', schemadir]) 11 | -------------------------------------------------------------------------------- /meson_options.txt: -------------------------------------------------------------------------------- 1 | option('exename', type : 'string', value : 'com.github.spheras.desktopfolder', description : 'executable name') 2 | -------------------------------------------------------------------------------- /po/LINGUAS: -------------------------------------------------------------------------------- 1 | bg 2 | ca 3 | cs 4 | da 5 | de_DE 6 | en_GB 7 | eo 8 | es 9 | fi 10 | fr 11 | fr_FR 12 | hu 13 | it 14 | ja 15 | lt 16 | ms_MY 17 | nl_NL 18 | oc 19 | pl 20 | pt_BR 21 | pt_PT 22 | ro_RO 23 | ru 24 | tr 25 | uk 26 | zh_CN 27 | -------------------------------------------------------------------------------- /po/POTFILES.in: -------------------------------------------------------------------------------- 1 | ./src/Application.vala 2 | ./src/logic/PhotoManager.vala 3 | ./src/logic/DesktopManager.vala 4 | ./src/logic/FolderManager.vala 5 | ./src/logic/ItemManager.vala 6 | ./src/logic/folderarrangement/FolderGrid.vala 7 | ./src/logic/folderarrangement/FolderArrangementManaged.vala 8 | ./src/logic/folderarrangement/FolderSortByName.vala 9 | ./src/logic/folderarrangement/FolderSort.vala 10 | ./src/logic/folderarrangement/FolderSortByType.vala 11 | ./src/logic/folderarrangement/FolderArrangementFree.vala 12 | ./src/logic/folderarrangement/FolderArrangement.vala 13 | ./src/logic/folderarrangement/FolderSortBySize.vala 14 | ./src/logic/folderarrangement/FolderArrangementGrid.vala 15 | ./src/logic/NoteManager.vala 16 | ./src/LanguageText.vala 17 | ./src/widgets/PhotoWindow.vala 18 | ./src/widgets/ItemView.vala 19 | ./src/widgets/DesktopWindow.vala 20 | ./src/widgets/FolderWindow.vala 21 | ./src/widgets/NoteWindow.vala 22 | ./src/dialogs/PanelPropertiesWindow.vala 23 | ./src/dialogs/ProgressDialog.vala 24 | ./src/utils/EditableLabel.vala 25 | ./src/utils/Util.vala 26 | ./src/utils/shortcut/CustomShortcutSettings.vala 27 | ./src/utils/shortcut/Shortcut.vala 28 | ./src/utils/dragndrop/DndBehaviour.vala 29 | ./src/utils/dragndrop/DndHandler.vala 30 | ./src/utils/dragndrop/DragNDrop.vala 31 | ./src/utils/dragndrop/DndView.vala 32 | ./src/utils/RenameDialog.vala 33 | ./src/utils/clipboard/ClipboardManager.vala 34 | ./src/utils/clipboard/ClipboardFile.vala 35 | ./src/utils/animation/Fx.vala 36 | ./src/utils/animation/GtkAnimation.vala 37 | ./src/utils/menu/MenuItemColor.vala 38 | ./src/utils/menu/MenuItemSeparator.vala 39 | ./src/settings/NoteSettings.vala 40 | ./src/settings/PositionSettings.vala 41 | ./src/settings/ItemSettings.vala 42 | ./src/settings/PhotoSettings.vala 43 | ./src/settings/FolderSettings.vala 44 | ./src/Resources.vala 45 | data/com.github.spheras.desktopfolder.appdata.xml.in 46 | data/com.github.spheras.desktopfolder.desktop.in 47 | -------------------------------------------------------------------------------- /po/POTFILES.skip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spheras/desktopfolder/c45d456b81546c702a792d2946e642509fccbee3/po/POTFILES.skip -------------------------------------------------------------------------------- /po/README: -------------------------------------------------------------------------------- 1 | # Translations handling 2 | 3 | All translations are managed via transifex https://www.transifex.com/ubuntu-budgie/desktopfolder 4 | 5 | To pull from transifex: 6 | 7 | git clone https://github.com/spheras/desktopfolder 8 | cd desktopfolder 9 | ./po/get_merge_translations.sh 10 | 11 | To update the pot file: 12 | 13 | git clone https://github.com/spheras/desktopfolder 14 | cd desktopfolder 15 | mkdir build 16 | meson --prefix 17 | ninja com.github.spheras.desktopfolder-pot 18 | 19 | Edit the file po/com.github.spheras.desktopfolder.pot 20 | Delete the following: 21 | 22 | #: data/com.github.spheras.desktopfolder.desktop.in:6 23 | msgid "com.github.spheras.desktopfolder" 24 | msgstr "" 25 | 26 | #: data/com.github.spheras.desktopfolder.desktop.in:7 27 | msgid "desktop;folder;file;organize;" 28 | msgstr "" 29 | -------------------------------------------------------------------------------- /po/get_merge_translations.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | tx pull -a -f --minimum-perc=46 3 | 4 | cd po 5 | rm LINGUAS 6 | 7 | for i in *.po ; do 8 | echo `echo $i|sed 's/.po$//'` >> LINGUAS 9 | done 10 | 11 | sed -i 's/CHARSET/UTF-8/g' *.po 12 | 13 | #intltool-merge --desktop-style . ../data/com.github.spheras.desktopfolder.desktop.in ../data/com.github.spheras.desktopfolder.desktop 14 | -------------------------------------------------------------------------------- /po/meson.build: -------------------------------------------------------------------------------- 1 | i18n.gettext(meson.project_name(), preset: 'glib') 2 | -------------------------------------------------------------------------------- /pre-commit: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Runs uncrustify to format code 4 | # Adapted from elementary icon's pre-commit hook: https://github.com/elementary/icons/blob/master/pre-commit 5 | 6 | CDIR=$(git rev-parse --show-toplevel) 7 | 8 | echo "Running Uncrustify..." 9 | 10 | git diff --cached --name-status --diff-filter=ACMR | while read STATUS FILE; do 11 | if [[ "$FILE" =~ ^.+(vala)$ ]]; then 12 | uncrustify --replace --no-backup -c $CDIR/etc/uncrustify-elementary-vala.cfg $CDIR/$FILE 13 | fi 14 | done 15 | 16 | git add . 17 | exit 0 18 | -------------------------------------------------------------------------------- /snap/requirements.txt: -------------------------------------------------------------------------------- 1 | argparse==1.2.2 2 | -------------------------------------------------------------------------------- /snap/snapcraft.yaml: -------------------------------------------------------------------------------- 1 | name: desktopfolder # you probably want to 'snapcraft register ' 2 | version: '1.0.10' # just for humans, typically '1.2+git' or '1.3.2' 3 | summary: Bring your desktop back to life # 79 char long summary 4 | description: | 5 | This is my-snap's description. You have a paragraph or two to tell the 6 | most important story about your snap. Keep it under 100 words though, 7 | we live in tweetspace and your description wants to look good in the snap 8 | store. 9 | icon: data/icons/128/com.github.spheras.desktopfolder.svg 10 | 11 | base: core18 12 | 13 | grade: stable # must be 'stable' to release into candidate/stable channels 14 | confinement: devmode # use 'strict' once you have the right plugs and slots 15 | 16 | plugs: 17 | gnome-3-28-1804: 18 | interface: content 19 | target: gnome-platform 20 | default-provider: gnome-3-28-1804:gnome-3-28-1804 21 | content: gnome-3-28-1804 22 | gtk-3-themes: 23 | interface: content 24 | target: $SNAP/data-dir/themes 25 | default-provider: gtk-common-themes:gtk-3-themes 26 | icon-themes: 27 | interface: content 28 | target: $SNAP/data-dir/icons 29 | default-provider: gtk-common-themes:icon-themes 30 | apps: 31 | desktopfolder: 32 | command: desktop-launch com.github.spheras.desktopfolder 33 | plugs: 34 | - x11 35 | - network 36 | - home 37 | - gsettings 38 | - opengl 39 | - desktop 40 | - desktop-legacy 41 | - unity7 42 | - gnome-3-28-1804 43 | desktop: usr/share/applications/com.github.spheras.desktopfolder.desktop 44 | environment: 45 | GSETTINGS_SCHEMA_DIR: $SNAP/share/glib-2.0/schemas 46 | 47 | parts: 48 | desktopfolder: 49 | source: . 50 | plugin: meson 51 | meson-parameters: [--prefix=/usr] 52 | after: [desktop-gnome-platform] 53 | organize: 54 | snap/desktopfolder/current/usr: usr 55 | #install: | 56 | # mkdir -p $SNAPCRAFT_PART_INSTALL/meta/gui/ 57 | # cp snapbuild/data/simple-scan.desktop $SNAPCRAFT_PART_INSTALL/meta/gui/ 58 | build-packages: 59 | - valac 60 | - libgtk-3-dev 61 | - libgranite-dev 62 | - libgee-0.8-dev 63 | - libcairo2-dev 64 | - libjson-glib-dev 65 | - libgdk-pixbuf2.0-dev 66 | - libwnck-3-dev 67 | - libglib2.0-dev 68 | - libgtksourceview-3.0-dev 69 | stage-packages: 70 | - gnome-settings-daemon-schemas 71 | - gnome-themes-extra 72 | - adwaita-icon-theme -------------------------------------------------------------------------------- /src/Resources.vala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015-2017 elementary LLC (http://launchpad.net/elementary) 3 | * Copyright (c) 2017-2019 José Amuedo (https://github.com/spheras) 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | namespace DesktopFolder { 20 | public const string APP_ID = "com.github.spheras.desktopfolder"; 21 | public const string APP_DESKTOP = APP_ID + ".desktop"; 22 | public const string APP_TITLE = "Desktop-Folder"; 23 | public const string APP_NAME = "desktopfolder"; 24 | public const string VERSION = "1.1.3"; 25 | /** Name of the Folder Settings File */ 26 | public const string FOLDER_SETTINGS_FILE = ".desktopfolder"; 27 | public const string OLD_NOTE_EXTENSION = "dfn"; 28 | public const string OLD_PHOTO_EXTENSION = "dfp"; 29 | public const string NEW_NOTE_EXTENSION = "desktopnote"; 30 | public const string NEW_PHOTO_EXTENSION = "desktopphoto"; 31 | /** param to show the desktop */ 32 | public const string PARAM_SHOW_DESKTOP = "show-desktop"; 33 | public const string PARAM_SHOW_VERSION = "version"; 34 | public const int ICON_SIZE = 48; 35 | public const int ICON_DEFAULT_WIDTH = 90; 36 | // Decoration margin for windows, see application.css 37 | public const int WINDOW_DECORATION_MARGIN = 10; 38 | 39 | public const string PANEL_BLACKLIST_FILE = ".nopanel"; 40 | public const int HEADERBAR_HEIGHT = 34; 41 | 42 | public const int SETTINGS_VERSION = 4; 43 | } 44 | -------------------------------------------------------------------------------- /src/dialogs/ProgressDialog.vala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017-2019 José Amuedo (https://github.com/spheras) 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | public class DesktopFolder.ProgressDialog : Gtk.Dialog { 19 | private Gtk.Widget status; 20 | private Gtk.Widget progress_bar; 21 | private Gtk.Box window_vbox; 22 | private uint timeout_id = 0; 23 | private GLib.Cancellable cancellable; 24 | 25 | /** 26 | * @constructor 27 | * @param FolderManager manager the manager of this window 28 | */ 29 | public ProgressDialog (string title, Gtk.Window ? parent) { 30 | this.resizable = false; 31 | this.deletable = false; 32 | this.title = title; 33 | 34 | this.icon_name = "system-file-manager"; 35 | this.window_vbox = new Gtk.Box (Gtk.Orientation.VERTICAL, 5); 36 | this.get_content_area ().set_border_width (10); 37 | this.get_content_area ().add (window_vbox); 38 | this.window_vbox.show (); 39 | 40 | this.status = new Gtk.Label (title); 41 | (this.status as Gtk.Label).set_size_request (500, -1); 42 | (this.status as Gtk.Label).set_max_width_chars (50); 43 | (this.status as Gtk.Label).set_ellipsize (Pango.EllipsizeMode.MIDDLE); 44 | (this.status as Gtk.Label).set_line_wrap (true); 45 | (this.status as Gtk.Label).set_line_wrap_mode (Pango.WrapMode.WORD_CHAR); 46 | (this.status as Gtk.Misc).set_alignment ((float) 0.0, (float) 0.5); 47 | window_vbox.pack_start (status, true, false, 0); 48 | 49 | var window_hbox = new Gtk.Box (Gtk.Orientation.HORIZONTAL, 2); 50 | this.progress_bar = new Gtk.ProgressBar (); 51 | window_hbox.pack_start (this.progress_bar, true, true, 0); 52 | var button = new Gtk.Button.from_icon_name ("process-stop-symbolic", Gtk.IconSize.BUTTON); 53 | button.get_style_context ().add_class ("flat"); 54 | button.clicked.connect (() => { 55 | button.sensitive = false; 56 | this.cancellable.cancel (); 57 | this.stop (); 58 | }); 59 | window_hbox.pack_start (button, false, false, 0); 60 | this.window_vbox.pack_start (window_hbox); 61 | 62 | this.set_transient_for (parent); 63 | this.show_all (); 64 | } 65 | 66 | /** 67 | * @name start 68 | * @description start the progress dialog 69 | */ 70 | public GLib.Cancellable start () { 71 | this.cancellable = new GLib.Cancellable (); 72 | this.cancellable.cancelled.connect (() => { 73 | this.stop (); 74 | }); 75 | 76 | this.show (); 77 | this.timeout_id = GLib.Timeout.add (100, () => { 78 | // this.prueba.set_text("prueba %d".printf(this.index)); 79 | (this.progress_bar as Gtk.ProgressBar).pulse (); 80 | (this.status as Gtk.Label).set_text (this.text); 81 | return true; 82 | }); 83 | 84 | return this.cancellable; 85 | } 86 | 87 | /** 88 | * @name stop 89 | * @description stop the progres bar 90 | */ 91 | public void stop () { 92 | if (this.timeout_id > 0) { 93 | GLib.Source.remove (this.timeout_id); 94 | this.timeout_id = 0; 95 | } 96 | this.close (); 97 | } 98 | 99 | string text = ""; 100 | 101 | /** 102 | * @name show_action 103 | * @description show the action in the progress dialog 104 | * @param {string} action the action to be showed 105 | */ 106 | public void show_action (string action) { 107 | text = action; 108 | debug (action); 109 | } 110 | 111 | } 112 | -------------------------------------------------------------------------------- /src/logic/DesktopManager.vala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017-2019 José Amuedo (https://github.com/spheras) 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | /** 19 | * @class 20 | * Desktop Manager 21 | */ 22 | public class DesktopFolder.DesktopManager : DesktopFolder.FolderManager { 23 | 24 | /** 25 | * @constructor 26 | * @param DesktopFolderApp application the application owner of this window 27 | * @param string folder_name the name of the folder 28 | */ 29 | public DesktopManager (DesktopFolderApp application) { 30 | // first, let's check the folder 31 | var directory = File.new_for_path (DesktopFolderApp.get_app_folder ()); // + "/" + DesktopFolder.DesktopWindow.DESKTOP_FAKE_FOLDER_NAME); 32 | if (!directory.query_exists ()) { 33 | DirUtils.create (directory.get_path (), 0755); 34 | } 35 | 36 | base (application, ""); 37 | 38 | // we cannot be moved 39 | this.is_moveable = false; 40 | this.get_view ().set_type_hint (Gdk.WindowTypeHint.DOCK); 41 | this.get_view ().change_body_color (0); 42 | 43 | Gdk.Screen screen = Gdk.Screen.get_default (); 44 | this.on_screen_size_changed (screen); 45 | 46 | this.view.show (); 47 | } 48 | 49 | /** 50 | * @overrided 51 | */ 52 | public override int get_parent_default_arrangement_orientation_setting () { 53 | return FolderSettings.ARRANGEMENT_ORIENTATION_VERTICAL; 54 | } 55 | 56 | /** 57 | * @name create_view 58 | * @description create the view associated with this manager 59 | * @overrided 60 | */ 61 | protected override void create_view () { 62 | this.view = new DesktopFolder.DesktopWindow (this); 63 | } 64 | 65 | /** 66 | * @name show_items 67 | * @description shows the items 68 | */ 69 | public override void show_items () { 70 | // debug (@"show_items $(this.get_application ().get_desktop_visibility ())"); 71 | if (this.get_application ().get_desktop_visibility ()) { 72 | // debug ("showing items"); 73 | base.show_items (); 74 | base.view.refresh (); 75 | } 76 | } 77 | 78 | /** 79 | * @name hide_items 80 | * @description hides the items 81 | */ 82 | public override void hide_items () { 83 | debug (@"hide_items $(this.get_application ().get_desktop_visibility ())"); 84 | debug ("hiding items"); 85 | base.hide_items (); 86 | } 87 | 88 | /** 89 | * @name show_view 90 | * @description show the items on the desktop 91 | * @override 92 | */ 93 | public override void show_view () { 94 | this.show_items (); 95 | } 96 | 97 | /** 98 | * @name hide_view 99 | * @description hide the items on the desktop 100 | * @override 101 | */ 102 | public override void hide_view () { 103 | this.hide_items (); 104 | } 105 | 106 | /** 107 | * @name on_screen_size_changed 108 | * @description detecting screen size changes 109 | */ 110 | public override void on_screen_size_changed (Gdk.Screen screen) { 111 | if (screen == null) { 112 | screen = Gdk.Screen.get_default (); 113 | } 114 | 115 | Gdk.Rectangle boundingbox = DesktopFolder.Util.get_desktop_bounding_box (); 116 | // debug ("bounding box result: %d,%d -- %d,%d", boundingbox.x, boundingbox.y, boundingbox.width, boundingbox.height); 117 | 118 | int[] borders = this.get_dock_borders (); 119 | 120 | this.get_view ().move (borders[0], borders[2]); // (-12, -10); 121 | int w = boundingbox.width - borders[0]; // deprecated -> screen.get_width (); // + 25; 122 | int h = boundingbox.height - borders[2]; // deprecated -> screen.get_height (); // + 25; 123 | this.get_view ().resize (w, h); 124 | this.get_view ().set_default_size (w, h); 125 | this.get_view ().height_request = h; 126 | this.get_view ().width_request = w; 127 | 128 | debug ("DESKTOP SIZE CHANGED! (%d,%d) (%d,%d)", 0, 0, w, h); 129 | } 130 | 131 | /** 132 | * @name get_dock_borders 133 | * @description return an array with the space needed for system docks, i.e. plank 134 | * @return {int[]} the array with space needed [left, right, top, bottom] 135 | */ 136 | public int[] get_dock_borders () { 137 | unowned Wnck.Screen screen = Wnck.Screen.get_default (); 138 | while (Gtk.events_pending ()) { 139 | Gtk.main_iteration (); 140 | } 141 | unowned List windows = screen.get_windows (); 142 | Gee.List docks = new Gee.ArrayList (); 143 | int[] borders = new int[4]; 144 | foreach (Wnck.Window w in windows) { 145 | Wnck.Application window_app = w.get_application (); 146 | string app_name = window_app.get_name (); 147 | if (w.get_window_type () == Wnck.WindowType.DOCK && app_name.index_of ("desktopfolder") < 0) { 148 | var name = w.get_name (); 149 | debug ("Dock window found: %s", name); 150 | docks.add (w); 151 | string[] strut = this.get_strut (w.get_xid ()); 152 | if (strut != null) { 153 | // we get the greatest border 154 | for (int i = 0; i < 4; i++) { 155 | int istrut = int.parse (strut[i]); 156 | if (borders[i] < istrut) { 157 | borders[i] = istrut; 158 | } 159 | } 160 | } 161 | } 162 | } 163 | 164 | return borders; 165 | } 166 | 167 | /** 168 | * @name ge_strut 169 | * @description get the strut- values from xprop, on dock type windows. 170 | * This function is inspired in the function for Ubuntu Budgie Window Shuffler 171 | * It is based on the xprop command, check it by using: xprop -id {xid of the window} 172 | * https://github.com/UbuntuBudgie/window-shuffler/blob/c2df9934fd823f50a2409effccd8349654bf7b5e/shuffler_geo.py#L62 173 | * @param {ulong} xid the x id of the window 174 | * @return {string[]} the strut info (or null). An array of 4 positions [left,right,top,bottom] 175 | */ 176 | private string[] ? get_strut (ulong xid) { 177 | // get the strut- values from xprop, on dock type windows. Since Plank is 178 | // an exception, the function indicates if the dock is a plank instance. 179 | string output; 180 | string cmd = "xprop -id %d".printf ((int) xid); 181 | const string STRUT_CARDINAL = "_NET_WM_STRUT(CARDINAL) = "; 182 | GLib.Process.spawn_command_line_sync (cmd, out output); 183 | string[] all_lines = output.split ("\n"); 184 | foreach (string l in all_lines) { 185 | // debug("XPROP: %s",l); 186 | if (l.index_of (STRUT_CARDINAL, 0) >= 0) { 187 | return l.split ("=")[1].split (","); 188 | } 189 | } 190 | 191 | return null; 192 | } 193 | 194 | /** 195 | * @name skip_file 196 | * @description we must skip the widget setting files 197 | * @override 198 | */ 199 | protected override bool skip_file (File file) { 200 | string basename = file.get_basename (); 201 | 202 | if (FileUtils.test (file.get_path (), FileTest.IS_DIR)) { 203 | // is a panel? 204 | string flagfilepath = file.get_path () + "/.desktopfolder"; 205 | // debug("is a panel? %s",flagfilepath); 206 | File flagfile = File.new_for_commandline_arg (flagfilepath); 207 | return flagfile.query_exists (); 208 | } else { 209 | if (basename.has_suffix (DesktopFolder.OLD_NOTE_EXTENSION) || basename.has_suffix (DesktopFolder.OLD_PHOTO_EXTENSION) 210 | || basename.has_suffix (DesktopFolder.NEW_NOTE_EXTENSION) || basename.has_suffix (DesktopFolder.NEW_PHOTO_EXTENSION)) { 211 | return true; 212 | } 213 | } 214 | 215 | return base.skip_file (file); 216 | } 217 | 218 | /** 219 | * @overrided 220 | * we must create a .nopanel inside to avoid creating a panel from this folder 221 | */ 222 | protected override void create_new_folder_inside (string folder_path) { 223 | File nopanel = File.new_for_path (folder_path + "/.nopanel"); 224 | try { 225 | if (!nopanel.query_exists ()) { 226 | nopanel.create (FileCreateFlags.NONE); 227 | } 228 | } catch (Error e) { 229 | stderr.printf ("Error: %s\n", e.message); 230 | Util.show_error_dialog ("Error", e.message); 231 | } 232 | } 233 | 234 | } 235 | -------------------------------------------------------------------------------- /src/logic/NoteManager.vala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017-2019 José Amuedo (https://github.com/spheras) 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | public errordomain NoteManagerIOError { 19 | FILE_EXISTS, 20 | MOVE_ERROR 21 | } 22 | 23 | /** 24 | * @class 25 | * Item Manager that represents an Icon of a File or Folder 26 | */ 27 | public class DesktopFolder.NoteManager : Object { 28 | /** parent application */ 29 | private DesktopFolderApp application; 30 | /** the File object associated with the item */ 31 | private File file; 32 | /** the view associated with this manager */ 33 | private NoteWindow view; 34 | /** name of the note */ 35 | private string note_name = null; 36 | /** Note Settings of this note */ 37 | private NoteSettings settings = null; 38 | /** flag to get the validity of the note */ 39 | private bool flag_valid = true; 40 | /** 41 | * @constructor 42 | * @param DesktopFolderApp application the application of this note 43 | * @param string note_name the name of the note 44 | * @param File file the GLib File object for the file associated with this item 45 | */ 46 | public NoteManager (DesktopFolderApp application, string note_name, File file) { 47 | this.note_name = note_name; 48 | this.file = file; 49 | 50 | // Let's load the settings of the folder (if exist or a new one) 51 | if (!this.load_note_settings ()) { 52 | // removing the settings file 53 | if (this.file.query_exists ()) { 54 | try { 55 | this.file.trash (); 56 | } catch (Error error) { 57 | stderr.printf ("Error: %s\n", error.message); 58 | Util.show_error_dialog ("Error", error.message); 59 | } 60 | } 61 | this.flag_valid = false; 62 | } else { 63 | // First we create a Note Window above the desktop 64 | this.application = application; 65 | this.view = new NoteWindow (this); 66 | this.application.add_window (this.view); 67 | this.view.show (); 68 | } 69 | } 70 | 71 | /** 72 | * @name on_screen_size_changed 73 | * @description detecting screen size changes 74 | */ 75 | public void on_screen_size_changed (Gdk.Screen screen) { 76 | this.settings.calculate_current_position (); 77 | this.view.reload_settings (); 78 | } 79 | 80 | /** 81 | * @name is_valid 82 | * @description return the validity of the note widget 83 | * @return {bool} true->yes, it is valid 84 | */ 85 | public bool is_valid () { 86 | return this.flag_valid; 87 | } 88 | 89 | /** 90 | * @name load_note_settings 91 | * @description load the settings of this note. 92 | * The note/settings file contains all the info needed to create the note position, size, etc.. and the text itself 93 | */ 94 | private bool load_note_settings () { 95 | // let's search the folder settings file 96 | var abs_path = this.get_absolute_path (); 97 | debug ("loading note settings...%s", abs_path); 98 | if (!this.file.query_exists ()) { 99 | warning ("note file does not exist!"); 100 | return false; 101 | } else { 102 | NoteSettings existent = NoteSettings.read_settings (this.file, this.get_note_name ()); 103 | if (existent == null) { 104 | // something bad occurred, we must delete this note widget 105 | return false; 106 | } else { 107 | this.settings = existent; 108 | } 109 | } 110 | return true; 111 | } 112 | 113 | /** 114 | * @name reopen 115 | * @description close the current view and reopen it again 116 | * (mainly to reposition the window on top or back, and most important, the icon over the status bar) 117 | */ 118 | public void reopen () { 119 | this.view.save_current_position_and_size (); 120 | this.get_settings ().save (); 121 | 122 | // closing 123 | this.application.remove_window (this.view); 124 | this.view.close (); 125 | // reopening 126 | this.view = new NoteWindow (this); 127 | this.application.add_window (this.view); 128 | this.view.show (); 129 | } 130 | 131 | /** 132 | * @name on_text_change 133 | * @description the text change event was produced 134 | * @param {string} text the new text of the note 135 | */ 136 | public void on_text_change (string text) { 137 | this.settings.text = text; 138 | this.settings.save (); 139 | } 140 | 141 | /** 142 | * @name get_settings 143 | * @description return the settings of this note 144 | * @return FolderSettings the settings of this note 145 | */ 146 | public NoteSettings get_settings () { 147 | return this.settings; 148 | } 149 | 150 | /** 151 | * @name get_note_name 152 | * @description return the note name 153 | * @return string the note name 154 | */ 155 | public string get_note_name () { 156 | return this.note_name; 157 | } 158 | 159 | /** 160 | * @name get_application 161 | * @description return the desktop folder application 162 | * @return DesktopFolderApp 163 | */ 164 | public DesktopFolderApp get_application () { 165 | return this.application; 166 | } 167 | 168 | /** 169 | * @name get_view 170 | * @description return the view of this manager 171 | * @return ItemView 172 | */ 173 | public NoteWindow get_view () { 174 | return this.view; 175 | } 176 | 177 | /** 178 | * @name show_view 179 | * @description show the folder 180 | */ 181 | public void show_view () { 182 | // setting opacity to stop the folder window flashing at startup 183 | this.view.opacity = 1; 184 | this.view.show_all (); 185 | this.view.fade_in (); 186 | } 187 | 188 | /** 189 | * @name hide_view 190 | * @description hide the folder 191 | */ 192 | public void hide_view () { 193 | this.view.fade_out (); 194 | Timeout.add (160, () => { 195 | // ditto 196 | this.view.opacity = 0; 197 | this.view.hide (); 198 | return false; 199 | }); 200 | } 201 | 202 | /** 203 | * @name get_file 204 | * @description return the Glib.File associated 205 | * @return File the file associated 206 | */ 207 | public GLib.File get_file () { 208 | return this.file; 209 | } 210 | 211 | /** 212 | * @name get_absolute_path 213 | * @description return the absolute path to this item 214 | * @return string the absolute path 215 | */ 216 | public string get_absolute_path () { 217 | return this.get_file ().get_path (); 218 | } 219 | 220 | /** 221 | * @name close 222 | * @description close the item manager and its view 223 | */ 224 | public void close () { 225 | this.view.close (); 226 | } 227 | 228 | /** 229 | * @name trash 230 | * @description trash the file or folder associated 231 | */ 232 | public void trash () { 233 | try { 234 | File file = File.new_for_path (this.get_absolute_path ()); 235 | file.trash (); 236 | } catch (Error e) { 237 | stderr.printf ("Error: %s\n", e.message); 238 | Util.show_error_dialog ("Error", e.message); 239 | } 240 | } 241 | 242 | /** 243 | * @name set_new_shape 244 | * @description set a new shape (position and size) of the view 245 | */ 246 | public void set_new_shape (int x, int y, int width, int height) { 247 | this.settings.x = x; 248 | this.settings.y = y; 249 | this.settings.w = width; 250 | this.settings.h = height; 251 | this.settings.save (); 252 | } 253 | 254 | /** 255 | * @name save_head_color 256 | * @description save a head color to the settings file 257 | * @param color string the color for the head to be saved 258 | */ 259 | public void save_head_color (string color) { 260 | this.settings.fgcolor = color; 261 | this.settings.save (); 262 | } 263 | 264 | /** 265 | * @name save_body_color 266 | * @description save a body color to the settings file 267 | * @param color string the color for the body to be saved 268 | */ 269 | public void save_body_color (string color) { 270 | this.settings.bgcolor = color; 271 | this.settings.save (); 272 | } 273 | 274 | /** 275 | * @name rename 276 | * @description renaming myself 277 | * @param string name the new name 278 | * @return bool true->everything is ok, false->something failed, rollback 279 | */ 280 | public bool rename (string new_name) { 281 | string sanitized_name = DesktopFolder.Util.sanitize_name (new_name); 282 | if (!DesktopFolder.Util.check_name (sanitized_name)) { 283 | DesktopFolder.Util.show_invalid_name_error_dialog (this.view, new_name); 284 | return false; 285 | } 286 | string old_name = this.note_name; 287 | string old_path = this.get_absolute_path (); 288 | var old_file = this.file; 289 | this.note_name = sanitized_name; 290 | string new_path = DesktopFolderApp.get_app_folder () + "/" + sanitized_name + "." + DesktopFolder.NEW_NOTE_EXTENSION; 291 | var new_file = File.new_for_path (new_path); 292 | try { 293 | if (new_file.query_exists ()) { 294 | DesktopFolder.Util.show_file_exists_error_dialog (this.view, sanitized_name, _("Note"), null); 295 | throw new NoteManagerIOError.FILE_EXISTS ("File already exists"); 296 | } 297 | NoteSettings note_settings = this.get_settings (); 298 | note_settings.name = sanitized_name; 299 | 300 | FileUtils.rename (old_path, new_path); 301 | this.file = File.new_for_path (new_path); 302 | if (this.file.query_exists ()) { 303 | note_settings.save_to_file (this.file); 304 | return true; 305 | } else { 306 | throw new NoteManagerIOError.MOVE_ERROR ("Failed to rename note"); 307 | } 308 | 309 | } catch (Error error) { 310 | warning (error.message); 311 | // Revert changes 312 | this.note_name = old_name; 313 | NoteSettings note_settings = this.get_settings (); 314 | note_settings.name = old_name; 315 | this.file = old_file; 316 | note_settings.save (); 317 | return false; 318 | } 319 | } 320 | 321 | } 322 | -------------------------------------------------------------------------------- /src/logic/PhotoManager.vala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017-2019 José Amuedo (https://github.com/spheras) 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | /** 19 | * @class 20 | * Item Manager that represents an Icon of a File or Folder 21 | */ 22 | public class DesktopFolder.PhotoManager : Object { 23 | /** parent application */ 24 | private DesktopFolderApp application; 25 | /** the File object associated with the item */ 26 | private File file; 27 | /** the view associated with this manager */ 28 | private PhotoWindow view; 29 | /** name of the photo */ 30 | private string photo_name = null; 31 | /** photo Settings of this photo */ 32 | private PhotoSettings settings = null; 33 | /** flag to get the validity of the photo */ 34 | private bool flag_valid = true; 35 | 36 | /** 37 | * @constructor 38 | * @param DesktopFolderApp application the application of this photo 39 | * @param string photo_name the name of the photo 40 | * @param File file the GLib File object for the file associated with this item 41 | */ 42 | public PhotoManager (DesktopFolderApp application, string photo_name, File file) { 43 | this.photo_name = photo_name; 44 | this.file = file; 45 | 46 | // Let's load the settings of the folder (if exist or a new one) 47 | if (!this.load_photo_settings ()) { 48 | // removing the settings file 49 | if (this.file.query_exists ()) { 50 | try { 51 | this.file.trash (); 52 | } catch (Error e) { 53 | } 54 | } 55 | this.flag_valid = false; 56 | } else { 57 | // First we create a photo Window above the desktop 58 | this.application = application; 59 | this.view = new PhotoWindow (this); 60 | this.application.add_window (this.view); 61 | this.view.show (); 62 | 63 | // trying to put it in front of the rest 64 | this.view.set_keep_below (false); 65 | this.view.set_keep_above (true); 66 | this.view.present (); 67 | this.view.set_keep_above (false); 68 | this.view.set_keep_below (true); 69 | // --------------------------------------- 70 | } 71 | } 72 | 73 | /** 74 | * @name on_screen_size_changed 75 | * @description detecting screen size changes 76 | */ 77 | public void on_screen_size_changed (Gdk.Screen screen) { 78 | this.settings.calculate_current_position (); 79 | this.view.reload_settings (); 80 | } 81 | 82 | /** 83 | * @name is_valid 84 | * @description return the validity of the photo widget 85 | * @return {bool} true->yes, it is valid 86 | */ 87 | public bool is_valid () { 88 | return this.flag_valid; 89 | } 90 | 91 | /** 92 | * @name load_photo_settings 93 | * @description load the settings of this photo. 94 | * The photo/settings file contains all the info needed to create the photo position, size, etc.. and the text itself 95 | * @return {bool} true->everything was ok, false->something was bad 96 | */ 97 | private bool load_photo_settings () { 98 | // let's search the folder settings file 99 | var abs_path = this.get_absolute_path (); 100 | debug ("loading photo settings...%s", abs_path); 101 | if (!this.file.query_exists ()) { 102 | warning ("photo file does not exist!"); 103 | return false; 104 | } else { 105 | PhotoSettings existent = PhotoSettings.read_settings (this.file, this.get_photo_name ()); 106 | if (existent == null) { 107 | // something bad occurred, we must delete this photo widget 108 | return false; 109 | } else { 110 | this.settings = existent; 111 | } 112 | } 113 | return true; 114 | } 115 | 116 | /** 117 | * @name get_settings 118 | * @description return the settings of this photo 119 | * @return PhotoSettings the settings of this photo 120 | */ 121 | public PhotoSettings get_settings () { 122 | return this.settings; 123 | } 124 | 125 | /** 126 | * @name get_photo_name 127 | * @description return the photo name 128 | * @return string the photo name 129 | */ 130 | public string get_photo_name () { 131 | return this.photo_name; 132 | } 133 | 134 | /** 135 | * @name get_application 136 | * @description return the desktop folder application 137 | * @return DesktopFolderApp 138 | */ 139 | public DesktopFolderApp get_application () { 140 | return this.application; 141 | } 142 | 143 | /** 144 | * @name get_view 145 | * @description return the view of this manager 146 | * @return PhotoWindow 147 | */ 148 | public PhotoWindow get_view () { 149 | return this.view; 150 | } 151 | 152 | /** 153 | * @name show_view 154 | * @description show the folder 155 | */ 156 | public void show_view () { 157 | // setting opacity to stop the folder window flashing at startup 158 | this.view.opacity = 1; 159 | this.view.show_all (); 160 | this.view.fade_in (); 161 | } 162 | 163 | /** 164 | * @name hide_view 165 | * @description hide the folder 166 | */ 167 | public void hide_view () { 168 | this.view.fade_out (); 169 | Timeout.add (160, () => { 170 | // ditto 171 | this.view.opacity = 0; 172 | this.view.hide (); 173 | return false; 174 | }); 175 | } 176 | 177 | /** 178 | * @name get_file 179 | * @description return the Glib.File associated 180 | * @return File the file associated 181 | */ 182 | public GLib.File get_file () { 183 | return this.file; 184 | } 185 | 186 | /** 187 | * @name get_absolute_path 188 | * @description return the absolute path to this item 189 | * @return string the absolute path 190 | */ 191 | public string get_absolute_path () { 192 | return this.get_file ().get_path (); 193 | } 194 | 195 | /** 196 | * @name close 197 | * @description close the item manager and its view 198 | */ 199 | public void close () { 200 | this.view.close (); 201 | } 202 | 203 | /** 204 | * @name reopen 205 | * @description close the current view and reopen it again 206 | */ 207 | public void reopen () { 208 | this.view.save_current_position_and_size (); 209 | this.get_settings ().save (); 210 | 211 | // closing 212 | this.application.remove_window (this.view); 213 | this.view.close (); 214 | // reopening 215 | this.view = new PhotoWindow (this); 216 | this.application.add_window (this.view); 217 | this.view.show (); 218 | } 219 | 220 | /** 221 | * @name open 222 | * @description open the photo in the default viewer 223 | */ 224 | public void open () { 225 | try { 226 | var command = "xdg-open \"" + get_settings ().photo_path + "\""; 227 | var appinfo = AppInfo.create_from_commandline (command, null, AppInfoCreateFlags.SUPPORTS_URIS); 228 | appinfo.launch_uris (null, null); 229 | } catch (Error e) { 230 | stderr.printf ("Error: %s\n", e.message); 231 | Util.show_error_dialog ("Error", e.message); 232 | } 233 | } 234 | 235 | /** 236 | * @name delete 237 | * @description delete the file associated 238 | */ 239 | public void delete () { 240 | try { 241 | File file = File.new_for_path (this.get_absolute_path ()); 242 | file.delete (); 243 | } catch (Error e) { 244 | stderr.printf ("Error: %s\n", e.message); 245 | Util.show_error_dialog ("Error", e.message); 246 | } 247 | } 248 | 249 | /** 250 | * @name set_new_shape 251 | * @description set a new shape (position and size) of the view 252 | */ 253 | public void set_new_shape (int x, int y, int width, int height) { 254 | this.settings.x = x; 255 | this.settings.y = y; 256 | this.settings.w = width; 257 | this.settings.h = height; 258 | this.settings.save (); 259 | } 260 | 261 | /** 262 | * @name rename 263 | * @description renaming myself 264 | * @param string name the new name 265 | * @return bool true->everything is ok, false->something failed, rollback 266 | */ 267 | public bool rename (string new_name) { 268 | if (new_name.length <= 0) { 269 | return false; 270 | } 271 | string old_name = this.photo_name; 272 | string old_path = this.get_absolute_path (); 273 | this.photo_name = new_name; 274 | string new_path = DesktopFolderApp.get_app_folder () + "/" + new_name + "." + DesktopFolder.NEW_PHOTO_EXTENSION; 275 | 276 | try { 277 | PhotoSettings is = this.get_settings (); 278 | is.name = new_name; 279 | 280 | FileUtils.rename (old_path, new_path); 281 | this.file = File.new_for_path (new_path); 282 | is.save_to_file (this.file); 283 | 284 | return true; 285 | } catch (Error e) { 286 | // we can't rename, undoing 287 | this.photo_name = old_name; 288 | PhotoSettings is = this.get_settings (); 289 | is.name = old_name; 290 | is.save (); 291 | 292 | // showing the error 293 | stderr.printf ("Error: %s\n", e.message); 294 | Util.show_error_dialog ("Error", e.message); 295 | 296 | return false; 297 | } 298 | } 299 | 300 | } 301 | -------------------------------------------------------------------------------- /src/logic/folderarrangement/FolderArrangement.vala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017-2019 José Amuedo (https://github.com/spheras) 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | /** 19 | * @class 20 | * Folder Arragement Interface 21 | */ 22 | public interface DesktopFolder.FolderArrangement : Object { 23 | public static int DEFAULT_PADDING = 10; 24 | public static int DEFAULT_EXTERNAL_MARGIN = 10; 25 | public static int ARRANGEMENT_TYPE_FREE = 1; 26 | public static int ARRANGEMENT_TYPE_GRID = 2; 27 | public static int ARRANGEMENT_TYPE_MANAGED = 3; 28 | 29 | /** 30 | * @name have_margin 31 | * @description indicates whether the arrangment have internal margins at the panel 32 | * to put items inside the panel 33 | * @return bool true->yes we have margins, false otherwise 34 | */ 35 | public abstract bool have_margin (); 36 | 37 | /** 38 | * @name can_drag 39 | * @description return whether the items dan be dragged or not 40 | * @return bool true->yes they can be dragged, false otherwise 41 | */ 42 | public abstract bool can_drag (); 43 | 44 | /** 45 | * @name get_sensitivity 46 | * @description Get the value of sensitivity, used to calculate the alignment of the items 47 | */ 48 | public abstract int get_sensitivity (); 49 | 50 | /** 51 | * @name can_organize 52 | * @description return whether the arrangement allow the reorganization of items, asked manually 53 | * @return bool true->yes, it is allowed, false otherwise 54 | */ 55 | public abstract bool can_organize (); 56 | 57 | /** 58 | * @name force_organization 59 | * @description indicates whether the arrangement must force the organization when the panel is resized 60 | * @return bool true->yes, force, othwerise false 61 | */ 62 | public abstract bool force_organization (); 63 | 64 | /** 65 | * @name start_drag 66 | * @description a itemview has started being dragged into the arrangement 67 | * @param {ItemView} view the view that is being dragged 68 | */ 69 | public abstract void start_drag (ItemView view); 70 | 71 | /** 72 | * @name motion_drag 73 | * @description a itemview is being moved (the started was notified previously) 74 | * @param {int} x the x new position 75 | * @param {int} y the y new position 76 | */ 77 | public abstract void motion_drag (int x, int y); 78 | 79 | /** 80 | * @name end_drag 81 | * @description the item drag was finished 82 | */ 83 | public abstract void end_drag (); 84 | 85 | 86 | 87 | /** 88 | * Factory method to obtain an arragement type 89 | * @see ARRAGEMENT_TYPE_FREE, ARRAGEMENT_TYPE_GRID, ARRAGEMENT_TYPE_MANAGED constants 90 | * @param int type the factory type 91 | * @return FolderArrangement the concrete folder arragement for that type, null if none valid 92 | */ 93 | public static FolderArrangement ? factory (int type) { 94 | if (type == FolderArrangement.ARRANGEMENT_TYPE_FREE) { 95 | return new FolderArrangementFree (); 96 | } else if (type == FolderArrangement.ARRANGEMENT_TYPE_GRID) { 97 | return new FolderArrangementGrid (); 98 | } else if (type == FolderArrangement.ARRANGEMENT_TYPE_MANAGED) { 99 | return new FolderArrangementManaged (); 100 | } 101 | return null; 102 | } 103 | 104 | /** 105 | * @name organize_items 106 | * @desription organize the list of items in a panel sorted 107 | * @param parent_window FolderWindow the parent panel of the items 108 | * @param items List the reference for the list of items to organize. Items are positioned to foce the new organization 109 | * @param sort_by_type int the sort type @see FolderSort constants 110 | * @param asc bool to indicate ascendent sort or descent (true->ascendent) 111 | */ 112 | public static void organize_items (FolderWindow parent_window, ref List items, int sort_by_type, bool asc, bool vertically) { 113 | Organize.Thread fot = new Organize.Thread (parent_window, ref items, sort_by_type, asc, vertically); 114 | fot.start (); 115 | } 116 | 117 | } 118 | 119 | /** 120 | * Class to represent a desired movement of an item to a different positions after a reorganization 121 | */ 122 | private class DesktopFolder.Organize.ItemMove { 123 | /** the view to move */ 124 | public ItemView view { get; set; } 125 | /** the new point x,y to put the item view*/ 126 | public Gdk.Point point { get; set; } 127 | 128 | public ItemMove (ItemView view, Gdk.Point point) { 129 | this.view = view; 130 | this.point = point; 131 | } 132 | } 133 | 134 | /** 135 | * Thread to organize items following a grid 136 | */ 137 | private class DesktopFolder.Organize.Thread { 138 | private FolderWindow parent_window; 139 | private List items; 140 | private int sort_by_type; 141 | private bool asc; 142 | private bool vertically; 143 | 144 | public Thread (FolderWindow parent_window, ref List items, int sort_by_type, bool asc, bool vertically) { 145 | this.parent_window = parent_window; 146 | 147 | // HELP! how to copy a ref variable in vala!? 148 | this.items = new List (); 149 | items.foreach ((i) => { 150 | this.items.append (i); 151 | }) ; 152 | // ****************************************** 153 | 154 | this.asc = asc; 155 | this.sort_by_type = sort_by_type; 156 | this.vertically = vertically; 157 | } 158 | 159 | /** 160 | * @name start 161 | * @description start the thread 162 | */ 163 | public void start () { 164 | parent_window.show_loading (); 165 | try { 166 | new GLib.Thread .try ("sync_thread", this.organize); 167 | } catch (Error e) { 168 | stderr.printf ("Error: %s\n", e.message); 169 | Util.show_error_dialog ("Error", e.message); 170 | } 171 | } 172 | 173 | /** 174 | * @name organize 175 | * @description organize algorithm 176 | */ 177 | private bool organize () { 178 | debug ("ORGANIZE THREAD -> INIT"); 179 | FolderSort folder_sort = FolderSort.factory (sort_by_type); 180 | folder_sort.sort (ref items, asc); 181 | 182 | // window width 183 | int width = parent_window.get_manager ().get_settings ().w; 184 | int height = parent_window.get_manager ().get_settings ().h; 185 | // left margin to start the grid 186 | int left_margin = FolderArrangement.DEFAULT_EXTERNAL_MARGIN; 187 | 188 | // cursors pixel 189 | int cursor_x = left_margin; 190 | int cursor_y = 0; 191 | int padding = parent_window.get_manager ().get_settings ().arrangement_padding; 192 | Gee.HashMap managed_items = parent_window.get_manager ().get_settings ().get_items_parsed (); 193 | Gee.List item_moves = new Gee.ArrayList (); 194 | 195 | for (int i = 0; i < items.length (); i++) { 196 | ItemManager item = items.nth_data (i); 197 | 198 | // moving in the view to the correct position 199 | // parent_window.move_item (item.get_view (), cursor_x, cursor_y); 200 | Gdk.Point px = Gdk.Point (); 201 | px.x = cursor_x; 202 | px.y = cursor_y; 203 | 204 | // add the movement to the pending movements to be executed in the gtk thread 205 | item_moves.add (new Organize.ItemMove (item.get_view (), px)); 206 | 207 | // saving settings for the new position 208 | ItemSettings is = managed_items[item.get_file_name ()]; 209 | 210 | is.x = cursor_x; 211 | is.y = cursor_y; 212 | 213 | // moving the cursor horizontally 214 | if (vertically) { 215 | cursor_y = cursor_y + DesktopFolder.ICON_DEFAULT_WIDTH + padding; 216 | if (cursor_y + DesktopFolder.ICON_DEFAULT_WIDTH > height) { 217 | // we need to move to the next rows 218 | cursor_x = cursor_x + DesktopFolder.ICON_DEFAULT_WIDTH + padding; 219 | cursor_y = 0; 220 | } 221 | } else { 222 | cursor_x = cursor_x + DesktopFolder.ICON_DEFAULT_WIDTH + padding; 223 | if (cursor_x + DesktopFolder.ICON_DEFAULT_WIDTH + left_margin > width) { 224 | // we need to move to the next rows 225 | cursor_x = left_margin; 226 | cursor_y = cursor_y + DesktopFolder.ICON_DEFAULT_WIDTH + padding; 227 | } 228 | } 229 | } 230 | 231 | 232 | // only last to put the items in their positions, this is something can only be done in the gtk main thread 233 | GLib.Idle.add_full (GLib.Priority.LOW, () => { 234 | const int MAX = 5; 235 | int cursor = 0; 236 | 237 | while (cursor < MAX && item_moves.size > 0) { 238 | Organize.ItemMove move = item_moves.remove_at (0); 239 | UtilGtkAnimation.animate_move (move.view, move.point, 500, UtilFx.AnimationMode.EASE_IN_BACK); 240 | cursor++; 241 | } 242 | 243 | if (item_moves.size > 0) { 244 | // next iteration in the main gtk thread 245 | return true; 246 | } else { 247 | parent_window.get_manager ().get_settings ().serialize_array (managed_items.values.to_array ()); 248 | parent_window.get_manager ().get_settings ().save (); 249 | 250 | this.parent_window.hide_loading (); 251 | debug ("ORGANIZE THREAD -> END"); 252 | return false; 253 | } 254 | }); 255 | 256 | return true; 257 | } 258 | 259 | } 260 | 261 | 262 | private class GridRow { 263 | 264 | } 265 | -------------------------------------------------------------------------------- /src/logic/folderarrangement/FolderArrangementFree.vala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017-2019 José Amuedo (https://github.com/spheras) 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | /** 19 | * @class 20 | * Free Arragement for Panels. User can put icons wherever 21 | */ 22 | public class DesktopFolder.FolderArrangementFree : Object, FolderArrangement { 23 | public const int SENSITIVITY_WITHOUT_GRID = 1; 24 | 25 | public bool have_margin () { 26 | return false; 27 | } 28 | 29 | public bool can_drag () { 30 | return true; 31 | } 32 | 33 | public int get_sensitivity () { 34 | return SENSITIVITY_WITHOUT_GRID; 35 | } 36 | 37 | public bool can_organize () { 38 | return true; 39 | } 40 | 41 | public bool force_organization () { 42 | return false; 43 | } 44 | 45 | public void start_drag (ItemView view) { 46 | // nothing 47 | } 48 | 49 | public void motion_drag (int x, int y) { 50 | // nothing 51 | } 52 | 53 | public void end_drag () { 54 | // nothing 55 | } 56 | 57 | } 58 | -------------------------------------------------------------------------------- /src/logic/folderarrangement/FolderArrangementGrid.vala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017-2019 José Amuedo (https://github.com/spheras) 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | /** 19 | * @class 20 | * Grid Arragement - User can move icons wherever following a grid 21 | */ 22 | public class DesktopFolder.FolderArrangementGrid : Object, FolderArrangement { 23 | protected const int SENSITIVITY_WITH_GRID = DesktopFolder.ICON_DEFAULT_WIDTH; 24 | 25 | public bool have_margin () { 26 | return true; 27 | } 28 | 29 | public bool can_drag () { 30 | return true; 31 | } 32 | 33 | /** 34 | * @name get_sensitivity 35 | * @description Get the value of sensitivity, used to calculate the alignment of the items 36 | */ 37 | public int get_sensitivity () { 38 | return SENSITIVITY_WITH_GRID; 39 | } 40 | 41 | public bool can_organize () { 42 | return true; 43 | } 44 | 45 | public bool force_organization () { 46 | return false; 47 | } 48 | 49 | /** 50 | * @name on_toggle_align_to_grid 51 | * @description the toggle align to grid event. The align to grid property must change 52 | */ 53 | public void on_toggle_align_to_grid () { 54 | /* 55 | if (this.get_sensitivity () == SENSITIVITY_WITH_GRID) { 56 | this.set_sensitivity (SENSITIVITY_WITHOUT_GRID); 57 | this.manager.get_settings ().align_to_grid = false; 58 | } else { 59 | this.set_sensitivity (SENSITIVITY_WITH_GRID); 60 | this.manager.get_settings ().align_to_grid = true; 61 | } 62 | this.manager.get_settings ().save (); 63 | this.clear_all (); 64 | this.manager.sync_files (0, 0); 65 | */ 66 | } 67 | 68 | // util grid map during te item dragging 69 | private FolderGrid drag_grid; 70 | 71 | private ItemView drag_item; 72 | private Gdk.Point ? init_item_cell; 73 | private Gdk.Point ? init_item_px; 74 | 75 | private ItemSettings drag_conflict_item; 76 | private Gdk.Point ? drag_conflict_cell; 77 | private Gdk.Point ? drag_conflict_px; 78 | private int drag_padding; 79 | 80 | public Gdk.Point ? get_init_item_cell () { 81 | return this.init_item_cell; 82 | } 83 | 84 | public bool is_dragging () { 85 | return this.drag_item != null; 86 | } 87 | 88 | public void start_drag (ItemView view) { 89 | // debug ("start_drag - start"); 90 | this.drag_item = view; 91 | this.init_item_px = Gdk.Point (); 92 | ItemSettings view_settings = view.get_manager ().get_settings (); 93 | init_item_px.x = view_settings.x; 94 | init_item_px.y = view_settings.y; 95 | this.drag_padding = view.get_manager ().get_folder ().get_settings ().arrangement_padding; 96 | this.drag_grid = FolderGrid.build_grid_structure (view.get_manager ().get_folder ().get_view (), this.drag_padding, view_settings); 97 | this.init_item_cell = this.drag_grid.get_item_cell_position (view_settings, 98 | (a, b) => { 99 | return a.name == b.name; 100 | }); 101 | // debug ("start_drag - end"); 102 | } 103 | 104 | public void motion_drag (int x, int y) { 105 | // we get the cell at pixel x,y 106 | Gdk.Point cell_at_xy = this.drag_grid.get_cell (SENSITIVITY_WITH_GRID, this.drag_padding, x, y); 107 | // debug ("motion_drag - cell(%d,%d)", cell_at_xy.x, cell_at_xy.y); 108 | 109 | // the drag item settings 110 | ItemSettings drag_item_settings = this.drag_item.get_manager ().get_settings (); 111 | 112 | // checking if we had an old conflict to restore the conflicting item 113 | if (this.drag_conflict_item != null && 114 | (cell_at_xy.x != this.drag_conflict_cell.x || 115 | cell_at_xy.y != this.drag_conflict_cell.y)) { 116 | // restoring conflict item to its original position 117 | this.drag_grid.replace (drag_item_settings, this.drag_conflict_cell, this.drag_conflict_item, this.init_item_cell); 118 | ItemManager item_conflict_manager = this.drag_item.get_manager ().get_folder ().get_item_by_filename (this.drag_conflict_item.name); 119 | 120 | if (!item_conflict_manager.is_selected ()) { 121 | UtilGtkAnimation.animate_move (item_conflict_manager.get_view (), this.drag_conflict_px, 1000); 122 | } 123 | 124 | 125 | this.drag_conflict_item.x = this.drag_conflict_px.x; 126 | this.drag_conflict_item.y = this.drag_conflict_px.y; 127 | 128 | this.drag_conflict_item = null; 129 | this.drag_conflict_cell = null; 130 | this.drag_conflict_px = null; 131 | } else if (this.drag_conflict_item != null) { 132 | // debug ("conflict (%d,%d)", this.drag_conflict_cell.x, this.drag_conflict_cell.y); 133 | } 134 | 135 | // and now the item at that cell in the grid 136 | ItemSettings item_at_pos = this.drag_grid.get_item_at_cell (cell_at_xy); 137 | 138 | if (item_at_pos != null && item_at_pos.name != drag_item_settings.name) { 139 | // anotate the conflict 140 | this.drag_conflict_item = item_at_pos; 141 | this.drag_conflict_cell = cell_at_xy; 142 | ItemManager item_at_pos_manager = this.drag_item.get_manager ().get_folder ().get_item_by_filename (item_at_pos.name); 143 | 144 | // interchanging positions in the grid and physically 145 | this.drag_grid.replace (drag_item_settings, this.init_item_cell, item_at_pos, cell_at_xy); 146 | this.drag_conflict_px = Gdk.Point (); 147 | this.drag_conflict_px.x = item_at_pos.x; 148 | this.drag_conflict_px.y = item_at_pos.y; 149 | item_at_pos.x = init_item_px.x; 150 | item_at_pos.y = init_item_px.y; 151 | 152 | if (!item_at_pos_manager.is_selected ()) { 153 | // this.drag_item.get_manager ().get_folder ().get_view ().move_item (item_at_pos_manager.get_view (), init_item_px.x, init_item_px.y); 154 | UtilGtkAnimation.animate_move (item_at_pos_manager.get_view (), init_item_px, 500, UtilFx.AnimationMode.EASE_IN_BACK); 155 | } 156 | 157 | } 158 | 159 | // if no, then restore the item in conflict to the original position 160 | } 161 | 162 | public void end_drag () { 163 | // debug ("end_drag - start"); 164 | if (this.drag_conflict_item != null) { 165 | // debug ("end_drag - saving conflict"); 166 | ItemManager drag_conflict_manager = this.drag_item.get_manager ().get_folder ().get_item_by_filename (this.drag_conflict_item.name); 167 | drag_conflict_manager.get_folder ().get_settings ().set_item (this.drag_conflict_item); 168 | drag_conflict_manager.get_folder ().get_settings ().save (); 169 | 170 | } 171 | 172 | this.drag_grid = null; 173 | this.drag_item = null; 174 | this.init_item_cell = null; 175 | this.init_item_px = null; 176 | this.drag_conflict_item = null; 177 | this.drag_conflict_cell = null; 178 | this.drag_conflict_px = null; 179 | // debug("end_drag - end"); 180 | } 181 | 182 | } 183 | -------------------------------------------------------------------------------- /src/logic/folderarrangement/FolderArrangementManaged.vala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017-2019 José Amuedo (https://github.com/spheras) 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | /** 19 | * @class 20 | * Managed Arragement for Panels - User can't move icons.. all of them 21 | * are managed by the sytem 22 | */ 23 | public class DesktopFolder.FolderArrangementManaged : Object, FolderArrangement { 24 | 25 | public bool have_margin () { 26 | return true; 27 | } 28 | 29 | public bool can_drag () { 30 | return false; 31 | } 32 | 33 | public int get_sensitivity () { 34 | return -1; 35 | } 36 | 37 | public bool can_organize () { 38 | return false; 39 | } 40 | 41 | public bool force_organization () { 42 | return true; 43 | } 44 | 45 | public void start_drag (ItemView view) { 46 | // nothing 47 | } 48 | 49 | public void motion_drag (int x, int y) { 50 | // nothing 51 | } 52 | 53 | public void end_drag () { 54 | // nothing 55 | } 56 | 57 | } 58 | -------------------------------------------------------------------------------- /src/logic/folderarrangement/FolderSort.vala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017-2019 José Amuedo (https://github.com/spheras) 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | /** 19 | * @class 20 | * Folder Arragement Interface 21 | */ 22 | public interface DesktopFolder.FolderSort : Object { 23 | public static int SORT_BY_NAME = 0; 24 | public static int SORT_BY_SIZE = 1; 25 | public static int SORT_BY_TYPE = 2; 26 | 27 | /** 28 | * @name sort 29 | * @description sort the list of items by the criteria of the concrete sort 30 | * @param List items the reference to the list of items 31 | * @param bool asc if the order is ascendent or not 32 | */ 33 | public abstract void sort (ref List items, bool asc); 34 | 35 | /** 36 | * Factory method to obtain an sort type 37 | * @see SORT_BY_NAME, SORT_BY_SIZE, SORT_BY_TYPE constants 38 | * @param int type the factory type 39 | * @return FolderSort the concrete folder sort for that type, null if none valid 40 | */ 41 | public static FolderSort ? factory (int type) { 42 | if (type == FolderSort.SORT_BY_NAME) { 43 | return new FolderSortByName (); 44 | } else if (type == FolderSort.SORT_BY_SIZE) { 45 | return new FolderSortBySize (); 46 | } else if (type == FolderSort.SORT_BY_TYPE) { 47 | return new FolderSortByType (); 48 | } 49 | return null; 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /src/logic/folderarrangement/FolderSortByName.vala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017-2019 José Amuedo (https://github.com/spheras) 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | /** 19 | * @class 20 | * Sort By File Name 21 | */ 22 | public class DesktopFolder.FolderSortByName : Object, FolderSort { 23 | 24 | public void sort (ref List items, bool asc) { 25 | items.sort_with_data ((a, b) => { 26 | if (a.is_folder () != b.is_folder ()) { 27 | if (a.is_folder ()) { 28 | return -1; 29 | } else { 30 | return 1; 31 | } 32 | } else { 33 | string afn = a.get_file_name (); 34 | string bfn = b.get_file_name (); 35 | return (asc) ? afn.ascii_casecmp (bfn) : bfn.ascii_casecmp (afn); 36 | } 37 | }); 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /src/logic/folderarrangement/FolderSortBySize.vala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017-2019 José Amuedo (https://github.com/spheras) 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | /** 19 | * @class 20 | * Sort By File Size 21 | */ 22 | public class DesktopFolder.FolderSortBySize : Object, FolderSort { 23 | 24 | public void sort (ref List items, bool asc) { 25 | items.sort_with_data ((a, b) => { 26 | if (a.is_folder () != b.is_folder ()) { 27 | if (a.is_folder ()) { 28 | return -1; 29 | } else { 30 | return 1; 31 | } 32 | } else { 33 | File af = a.get_file (); 34 | File bf = b.get_file (); 35 | try { 36 | FileInfo afi = af.query_info ("standard::size", FileQueryInfoFlags.NONE); 37 | FileInfo bfi = bf.query_info ("standard::size", FileQueryInfoFlags.NONE); 38 | 39 | return (asc) ? (int) (afi.get_size () - bfi.get_size ()) : (int) (bfi.get_size () - afi.get_size ()); 40 | } catch (Error e) { 41 | // error! ?? 42 | stderr.printf ("Error: %s\n", e.message); 43 | DesktopFolder.Util.show_error_dialog ("Error", e.message); 44 | return -1; 45 | } 46 | } 47 | }); 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /src/logic/folderarrangement/FolderSortByType.vala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017-2019 José Amuedo (https://github.com/spheras) 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | /** 19 | * @class 20 | * Sort By File Type 21 | */ 22 | public class DesktopFolder.FolderSortByType : Object, FolderSort { 23 | 24 | public void sort (ref List items, bool asc) { 25 | items.sort_with_data ((a, b) => { 26 | if (a.is_folder () != b.is_folder ()) { 27 | if (a.is_folder ()) { 28 | return -1; 29 | } else { 30 | return 1; 31 | } 32 | } else { 33 | File af = a.get_file (); 34 | File bf = b.get_file (); 35 | 36 | try { 37 | var afti = af.query_info ("standard::content-type", FileQueryInfoFlags.NONE); 38 | var bfti = bf.query_info ("standard::content-type", FileQueryInfoFlags.NONE); 39 | string act = afti.get_content_type (); 40 | string bct = bfti.get_content_type (); 41 | 42 | return (asc) ? strcmp (act, bct) : strcmp (bct, act); 43 | 44 | } catch (Error e) { 45 | // error! ?? 46 | stderr.printf ("Error: %s\n", e.message); 47 | DesktopFolder.Util.show_error_dialog ("Error", e.message); 48 | return -1; 49 | } 50 | } 51 | }); 52 | } 53 | 54 | } 55 | -------------------------------------------------------------------------------- /src/settings/ItemSettings.vala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017-2019 José Amuedo (https://github.com/spheras) 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | /** 19 | * @class 20 | * Item Settings 21 | */ 22 | public class DesktopFolder.ItemSettings : Object { 23 | /** the name of the item*/ 24 | public string name { get; set; } 25 | /** x position */ 26 | public int x { get; set; } 27 | /** y position */ 28 | public int y { get; set; } 29 | /** the icon for this file */ 30 | public string icon { get; set; } 31 | 32 | /** 33 | * @constructor 34 | */ 35 | public ItemSettings () { 36 | this.name = "helloWorld.txt"; 37 | this.x = 5; 38 | this.x = 5; 39 | this.icon = ""; 40 | } 41 | 42 | /** 43 | * @name to_string 44 | * @description convert to string the item 45 | */ 46 | public string to_string () { 47 | return "%s;%d;%d;%s".printf (this.name, this.x, this.y, this.icon); 48 | } 49 | 50 | /** 51 | * @name parse 52 | * @description parse an string to create an ItemSettings object 53 | * @param data string the data to be parsed 54 | * @return ItemSettings the ItemSettings created 55 | */ 56 | public static ItemSettings parse (string data) { 57 | ItemSettings result = new ItemSettings (); 58 | string[] split = data.split (";"); 59 | result.name = split[0]; 60 | result.x = int.parse (split[1]); 61 | result.y = int.parse (split[2]); 62 | if (split.length > 3) { 63 | result.icon = split[3]; 64 | } 65 | return result; 66 | } 67 | 68 | } 69 | -------------------------------------------------------------------------------- /src/settings/NoteSettings.vala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017-2019 José Amuedo (https://github.com/spheras) 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | public class DesktopFolder.NoteSettings : PositionSettings { 19 | private string _name; 20 | public string name { 21 | get { 22 | return _name; 23 | } 24 | set { 25 | if (_name != value) { 26 | _name = value; flagChanged = true; 27 | } 28 | } 29 | } 30 | private string _bgcolor; 31 | public string bgcolor { 32 | get { 33 | return _bgcolor; 34 | } 35 | set { 36 | if (_bgcolor != value) { 37 | _bgcolor = value; flagChanged = true; 38 | } 39 | } 40 | } 41 | 42 | private string _fgcolor; 43 | public string fgcolor { 44 | get { 45 | return _fgcolor; 46 | } 47 | set { 48 | if (_fgcolor != value) { 49 | _fgcolor = value; flagChanged = true; 50 | } 51 | } 52 | } 53 | private int _clipcolor; 54 | public int clipcolor { 55 | get { 56 | return _clipcolor; 57 | } 58 | set { 59 | if (_clipcolor != value) { 60 | _clipcolor = value; flagChanged = true; 61 | } 62 | } 63 | } 64 | private string _texture; 65 | public string texture { 66 | get { 67 | return _texture; 68 | } 69 | set { 70 | if (_texture != value) { 71 | _texture = value; flagChanged = true; 72 | } 73 | } 74 | } 75 | private string _text; 76 | public string text { 77 | get { 78 | return _text; 79 | } 80 | set { 81 | if (_text != value) { 82 | _text = value; flagChanged = true; 83 | } 84 | } 85 | } 86 | private bool _on_top; 87 | public bool on_top { 88 | get { 89 | return _on_top; 90 | } 91 | set { 92 | if (_on_top != value) { 93 | _on_top = value; flagChanged = true; 94 | } 95 | } 96 | } 97 | private bool _on_back; 98 | public bool on_back { 99 | get { 100 | return _on_back; 101 | } 102 | set { 103 | if (_on_back != value) { 104 | _on_back = value; flagChanged = true; 105 | } 106 | } 107 | } 108 | 109 | // util value to know the settings versions 110 | private int _version; 111 | public int version { 112 | get { 113 | return _version; 114 | } 115 | set { 116 | if (_version != value) { 117 | _version = value; flagChanged = true; 118 | } 119 | } 120 | } 121 | 122 | private bool _edit_label_on_creation; 123 | public bool edit_label_on_creation { 124 | get { 125 | return _edit_label_on_creation; 126 | } 127 | set { 128 | if (_edit_label_on_creation != value) { 129 | _edit_label_on_creation = value; flagChanged = true; 130 | } 131 | } 132 | } 133 | 134 | private File file; 135 | 136 | public NoteSettings (string name) { 137 | this.x = 110; 138 | this.y = 110; 139 | this.w = 300; 140 | this.h = 300; 141 | this.bgcolor = "df_yellow"; 142 | this.fgcolor = "df_dark"; 143 | this.texture = ""; 144 | this.clipcolor = Random.int_range (1, 6); 145 | this.name = name; 146 | this.text = "Lorem Ipsum"; 147 | this.on_top = false; 148 | this.on_back = true; 149 | this.version = DesktopFolder.SETTINGS_VERSION; 150 | } 151 | 152 | /** 153 | * @name save 154 | * @description persist the changes to the filesystem. The File is the same as the saved initially. 155 | */ 156 | public void save () { 157 | this.save_to_file (this.file); 158 | } 159 | 160 | /** 161 | * @name save_to_file 162 | * @description persist the changes to the filesystem. The file used is passed to the function, and saved for following saves. 163 | * @param file File the file to be saved 164 | */ 165 | public void save_to_file (File file) { 166 | if (!flagChanged) { 167 | return; 168 | } 169 | 170 | flagChanged = false; 171 | this.file = file; 172 | 173 | store_resolution_position (); 174 | 175 | // string data = Json.gobject_to_data (this, null); 176 | Json.Node root = Json.gobject_serialize (this); 177 | 178 | // To string: (see gobject_to_data) 179 | Json.Generator generator = new Json.Generator (); 180 | generator.set_root (root); 181 | string data = generator.to_data (null); 182 | 183 | try { 184 | // an output file in the current working directory 185 | if (file.query_exists ()) { 186 | file.delete (); 187 | } 188 | 189 | // creating a file and a DataOutputStream to the file 190 | /* 191 | Use BufferedOutputStream to increase write speed: 192 | var dos = new DataOutputStream (new BufferedOutputStream.sized (file.create (FileCreateFlags.REPLACE_DESTINATION), 65536)); 193 | */ 194 | var dos = new DataOutputStream (file.create (FileCreateFlags.REPLACE_DESTINATION)); 195 | // writing a short string to the stream 196 | dos.put_string (data); 197 | 198 | } catch (Error e) { 199 | stderr.printf ("%s\n", e.message); 200 | } 201 | } 202 | 203 | /** 204 | * @name read_settings 205 | * @description read the settings from a file to create a Note Settings object 206 | * @param file File the file where the settings are persisted 207 | * @param name string the name of the note 208 | * @return NoteSettings the NoteSettings created 209 | */ 210 | public static NoteSettings read_settings (File file, string name) { 211 | NoteSettings result = _read_settings (file, name); 212 | if (result == null) { 213 | // some error occurred, lets delete the settings and create again 214 | try { 215 | file.trash (); 216 | } catch (Error e) { 217 | } 218 | NoteSettings new_settings = new NoteSettings (name); 219 | new_settings.save_to_file (file); 220 | return _read_settings (file, name); 221 | } 222 | return result; 223 | } 224 | 225 | public static NoteSettings _read_settings (File file, string name) { 226 | try { 227 | string content = ""; 228 | var dis = new DataInputStream (file.read ()); 229 | string line; 230 | // Read lines until end of file (null) is reached 231 | while ((line = dis.read_line (null)) != null) { 232 | content = content + line; 233 | } 234 | NoteSettings existent = Json.gobject_from_data (typeof (NoteSettings), content) as NoteSettings; 235 | existent.file = file; 236 | existent.name = name; 237 | 238 | // regression for classes, now must have a df_ prefix 239 | if (existent.bgcolor.length > 0 && !existent.bgcolor.has_prefix ("df_")) { 240 | if (!existent.bgcolor.has_prefix ("rgb")) { 241 | existent.bgcolor = "df_" + existent.bgcolor; 242 | existent.texture = "square_paper"; 243 | } 244 | } 245 | if (existent.fgcolor.length > 0 && !existent.fgcolor.has_prefix ("df_")) { 246 | // backward compability 247 | existent.fgcolor = "df_" + existent.fgcolor; 248 | existent.texture = "square_paper"; 249 | } 250 | 251 | // regression for on top and back 252 | if (existent.version == 0) { 253 | existent.version = DesktopFolder.SETTINGS_VERSION; 254 | existent.on_top = false; 255 | existent.on_back = true; 256 | } 257 | 258 | // the properties have not changed, just loaded 259 | existent.flagChanged = false; 260 | 261 | return existent; 262 | } catch (Error e) { 263 | stderr.printf ("Error: %s\n", e.message); 264 | return null as NoteSettings; 265 | } 266 | } 267 | 268 | } 269 | -------------------------------------------------------------------------------- /src/settings/PhotoSettings.vala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017-2019 José Amuedo (https://github.com/spheras) 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | public class DesktopFolder.PhotoSettings : PositionSettings { 19 | private int _original_width = 0; 20 | public int original_width { 21 | get { 22 | return _original_width; 23 | } 24 | set { 25 | if (_original_width != value) { 26 | _original_width = value; flagChanged = true; 27 | } 28 | } 29 | } 30 | private int _original_height = 0; 31 | public int original_height { 32 | get { 33 | return _original_height; 34 | } 35 | set { 36 | if (_original_height != value) { 37 | _original_height = value; flagChanged = true; 38 | } 39 | } 40 | } 41 | private int _fixocolor = 0; 42 | public int fixocolor { 43 | get { 44 | return _fixocolor; 45 | } 46 | set { 47 | if (_fixocolor != value) { 48 | _fixocolor = value; flagChanged = true; 49 | } 50 | } 51 | } 52 | private string _name; 53 | public string name { 54 | get { 55 | return _name; 56 | } 57 | set { 58 | if (_name != value) { 59 | _name = value; flagChanged = true; 60 | } 61 | } 62 | } 63 | private string _photo_path; 64 | public string photo_path { 65 | get { 66 | return _photo_path; 67 | } 68 | set { 69 | if (_photo_path != value) { 70 | _photo_path = value; flagChanged = true; 71 | } 72 | } 73 | } 74 | 75 | // util value to know the settings versions 76 | private int _version; 77 | public int version { 78 | get { 79 | return _version; 80 | } 81 | set { 82 | if (_version != value) { 83 | _version = value; flagChanged = true; 84 | } 85 | } 86 | } 87 | 88 | private File file; 89 | 90 | public PhotoSettings (string photo_path, Gdk.Window window) { 91 | this.x = 110; 92 | this.y = 110; 93 | this.photo_path = photo_path; 94 | var file = File.new_for_path (photo_path); 95 | this.name = file.get_basename (); 96 | this.fixocolor = Random.int_range (1, 6); 97 | this.version = DesktopFolder.SETTINGS_VERSION; 98 | 99 | try { 100 | // we calculate an aproximated image size 101 | var pixbuf = new Gdk.Pixbuf.from_file (photo_path); 102 | this.w = pixbuf.get_width (); 103 | this.h = pixbuf.get_height (); 104 | this.original_width = w; 105 | this.original_height = h; 106 | 107 | // max a 30% of the screen 108 | Gdk.Screen screen = Gdk.Screen.get_default (); 109 | int MAX = (screen.get_display ().get_monitor_at_window (window).get_geometry ().width * 30) / 100; 110 | 111 | if (this.w > MAX) { 112 | int newWidth = MAX; 113 | int newHeight = (this.h * newWidth) / this.w; 114 | this.w = newWidth; 115 | this.h = newHeight; 116 | } else if (this.h > MAX) { 117 | int newHeight = MAX; 118 | int newWidth = (this.w * newHeight) / this.h; 119 | this.w = newWidth; 120 | this.h = newHeight; 121 | } 122 | } catch (Error e) { 123 | // error! ?? 124 | stderr.printf ("Error: %s\n", e.message); 125 | } 126 | } 127 | 128 | /** 129 | * @name save 130 | * @description persist the changes to the filesystem. The File is the same as the saved initially. 131 | */ 132 | public void save () { 133 | this.save_to_file (this.file); 134 | } 135 | 136 | /** 137 | * @name save_to_file 138 | * @description persist the changes to the filesystem. The file used is passed to the function, and saved for following saves. 139 | * @param file File the file to be saved 140 | */ 141 | public void save_to_file (File file) { 142 | if (!flagChanged) { 143 | return; 144 | } 145 | 146 | flagChanged = false; 147 | 148 | this.file = file; 149 | 150 | store_resolution_position (); 151 | 152 | // string data = Json.gobject_to_data (this, null); 153 | Json.Node root = Json.gobject_serialize (this); 154 | 155 | // To string: (see gobject_to_data) 156 | Json.Generator generator = new Json.Generator (); 157 | generator.set_root (root); 158 | string data = generator.to_data (null); 159 | 160 | try { 161 | // an output file in the current working directory 162 | if (file.query_exists ()) { 163 | file.delete (); 164 | } 165 | 166 | // creating a file and a DataOutputStream to the file 167 | /* 168 | Use BufferedOutputStream to increase write speed: 169 | var dos = new DataOutputStream (new BufferedOutputStream.sized (file.create (FileCreateFlags.REPLACE_DESTINATION), 65536)); 170 | */ 171 | var dos = new DataOutputStream (file.create (FileCreateFlags.REPLACE_DESTINATION)); 172 | // writing a short string to the stream 173 | dos.put_string (data); 174 | 175 | } catch (Error e) { 176 | stderr.printf ("%s\n", e.message); 177 | } 178 | } 179 | 180 | /** 181 | * @name read_settings 182 | * @description read the settings from a file to create a Photo Settings object 183 | * @param file File the file where the settings are persisted 184 | * @param name string the name of the photo 185 | * @return PhotoSettings the PhotoSettings created 186 | */ 187 | public static PhotoSettings read_settings (File file, string name) { 188 | PhotoSettings result = _read_settings (file, name); 189 | if (result != null) { 190 | // now lets check the existence of the photo linked and if it is valid 191 | var photo_file = File.new_for_path (result.photo_path); 192 | if (photo_file.query_exists ()) { 193 | try { 194 | new Gdk.Pixbuf.from_file (result.photo_path); 195 | } catch (Error error) { 196 | return null as PhotoSettings; 197 | } 198 | } else { 199 | return null as PhotoSettings; 200 | } 201 | } 202 | return result; 203 | } 204 | 205 | public static PhotoSettings _read_settings (File file, string name) { 206 | try { 207 | string content = ""; 208 | var dis = new DataInputStream (file.read ()); 209 | string line; 210 | // Read lines until end of file (null) is reached 211 | while ((line = dis.read_line (null)) != null) { 212 | content = content + line; 213 | } 214 | PhotoSettings existent = Json.gobject_from_data (typeof (PhotoSettings), content) as PhotoSettings; 215 | existent.file = file; 216 | existent.name = name; 217 | 218 | // old versions don't calculate the original width/height 219 | if (existent.original_width <= 0 || existent.original_height <= 0) { 220 | // we calculate an aproximated image size 221 | var pixbuf = new Gdk.Pixbuf.from_file (existent.photo_path); 222 | existent.original_width = pixbuf.get_width (); 223 | existent.original_height = pixbuf.get_height (); 224 | } 225 | 226 | // regression for on top and back 227 | if (existent.version == 0) { 228 | existent.version = DesktopFolder.SETTINGS_VERSION; 229 | } 230 | 231 | // the properties have not changed, just loaded 232 | existent.flagChanged = false; 233 | 234 | return existent; 235 | } catch (Error e) { 236 | stderr.printf ("Error: %s\n", e.message); 237 | return null as PhotoSettings; 238 | } 239 | } 240 | 241 | } 242 | -------------------------------------------------------------------------------- /src/utils/EditableLabel.vala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017 Lains 3 | * Copyright (c) 2017-2019 José Amuedo (https://github.com/spheras) 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | * 18 | * Co-authored by: Corentin Noël 19 | * 20 | */ 21 | 22 | public class DesktopFolder.EditableLabel : Gtk.EventBox { 23 | /** 24 | * @name changed 25 | * @description Signal when the label has been changed 26 | */ 27 | public signal void changed (string new_title); 28 | 29 | /** 30 | * @name show_popup 31 | * @description signal to allow third parties to show a popup when the right button has been pressed 32 | * @param {Gdk.EventButton} event the event button produced for right click 33 | * @return {bool} true-> the event has been captured and processed, false otherwise 34 | */ 35 | public signal bool show_popup (Gdk.EventButton event); 36 | 37 | /** 38 | * @name on_start_editing 39 | * @description Signal when the label has changed to entry 40 | * @deprecated 41 | */ 42 | public signal void on_start_editing (); 43 | 44 | /** 45 | * @name on_stop_editing 46 | * @description Signal when the enyty has changed to label 47 | * @deprecated 48 | */ 49 | public signal void on_stop_editing (); 50 | 51 | public Gtk.Label title_label { private set; public get; } 52 | private Gtk.Entry title_entry; 53 | private Gtk.Stack stack; 54 | 55 | public string text { 56 | get { 57 | return title_label.label; 58 | } 59 | set { 60 | title_label.label = value; 61 | } 62 | } 63 | 64 | public bool editing { 65 | private set { 66 | if (value) { 67 | // debug("set editing true"); 68 | title_entry.text = title_label.label; 69 | 70 | // trying to get the same size as label 71 | this.update_entry_width (); 72 | 73 | stack.set_visible_child (title_entry); 74 | title_entry.grab_focus (); 75 | } else { 76 | // debug("set editing false"); 77 | title_entry.text = title_entry.text.strip (); 78 | if (title_entry.text.strip () != "" && title_label.label != title_entry.text) { 79 | // title_label.label = title_entry.text; 80 | changed (title_entry.text); 81 | } 82 | stack.set_visible_child (title_label); 83 | title_entry.width_request = -1; 84 | } 85 | } 86 | public get { 87 | return stack.get_visible_child () == title_entry; 88 | } 89 | } 90 | 91 | public EditableLabel (string ? title_name) { 92 | 93 | title_label = new Gtk.Label (title_name); 94 | title_label.hexpand = true; 95 | /* 96 | title_label.valign = Gtk.Align.CENTER; 97 | title_label.halign = Gtk.Align.FILL; 98 | */ 99 | 100 | title_label.get_style_context ().add_class ("df_label"); 101 | title_label.wrap_mode = Pango.WrapMode.WORD_CHAR; 102 | title_label.set_line_wrap (true); 103 | title_label.set_justify (Gtk.Justification.CENTER); 104 | title_label.set_ellipsize (Pango.EllipsizeMode.END); 105 | title_label.set_lines (1); 106 | title_label.valign = Gtk.Align.CENTER; 107 | title_label.halign = Gtk.Align.FILL; 108 | 109 | title_entry = new Gtk.Entry (); 110 | title_entry.halign = Gtk.Align.CENTER; 111 | title_entry.valign = Gtk.Align.FILL; 112 | title_entry.expand = true; 113 | // title_entry.set_style (title_label.get_style ()); 114 | // Minimum entry with 115 | title_entry.set_width_chars (1); 116 | 117 | stack = new Gtk.Stack (); 118 | stack.transition_type = Gtk.StackTransitionType.CROSSFADE; 119 | stack.add (title_label); 120 | stack.add (title_entry); 121 | add (stack); 122 | 123 | // Clicked on the title 124 | // This event should be managed only by this.title_label 125 | this.button_press_event.connect ((event) => { 126 | if (event.type == Gdk.EventType.@2BUTTON_PRESS) { 127 | this.start_editing (); 128 | return true; 129 | } 130 | if (event.type == Gdk.EventType.BUTTON_PRESS && 131 | (event.button == Gdk.BUTTON_SECONDARY)) { 132 | return this.show_popup (event); 133 | } 134 | 135 | return false; 136 | }); 137 | 138 | // If press intro while editting 139 | this.title_entry.activate.connect (() => { 140 | // debug ("title_entry.activate.connect"); 141 | this.stop_editing (); 142 | }); 143 | 144 | // focus lost while editing 145 | this.title_entry.focus_out_event.connect ((event) => { 146 | // debug ("title_entry.focus_out_event.connect"); 147 | this.stop_editing (); 148 | return false; 149 | }); 150 | 151 | // keyboard shortcuts 152 | this.key_release_event.connect (this.on_key); 153 | this.key_press_event.connect (this.on_key); 154 | } 155 | 156 | /** 157 | * @name set_margin 158 | * @description set the left and right margin for the label and entry widgets 159 | * @param {int} margin the margin to apply 160 | */ 161 | public void set_margin (int margin) { 162 | this.title_label.margin_start = margin; 163 | this.title_label.margin_end = margin; 164 | this.title_entry.margin_start = margin; 165 | this.title_entry.margin_end = margin; 166 | } 167 | 168 | /** 169 | * @name on_key 170 | * @description the key event captured for the window 171 | * @param EventKey event the event produced 172 | * @return bool @see the on_key signal 173 | */ 174 | private bool on_key (Gdk.EventKey event) { 175 | // debug ("EditableLabel on_key, event: %s", event.type == Gdk.EventType.KEY_RELEASE ? "KEY_RELEASE" : event.type == Gdk.EventType.KEY_PRESS ? "KEY_PRESS" : "OTRO"); 176 | int key = (int) event.keyval; 177 | // debug ("EditableLabel event key %d", key); 178 | 179 | var mods = event.state & Gtk.accelerator_get_default_mod_mask (); 180 | bool control_pressed = ((mods & Gdk.ModifierType.CONTROL_MASK) != 0); 181 | 182 | const int ESCAPE_KEY = 65307; 183 | 184 | if (control_pressed) { 185 | if (key == 'z' || key == 'Z') { 186 | this.undo_changes (); 187 | } 188 | } else if (key == ESCAPE_KEY) { 189 | // debug("undo changes"); 190 | this.undo_changes (); 191 | this.stop_editing (); 192 | } else { 193 | this.update_entry_width (); 194 | } 195 | 196 | return true; 197 | } 198 | 199 | /** 200 | * Updates Entry width to fit its content 201 | */ 202 | private void update_entry_width () { 203 | int width, height; 204 | const int margin = 10; 205 | title_entry.get_layout ().get_size (out width, out height); 206 | width = (width / Pango.SCALE) + margin; 207 | 208 | Gtk.Allocation label_allocation; 209 | title_label.get_allocation (out label_allocation); 210 | 211 | if (width > label_allocation.width) { 212 | title_entry.width_request = label_allocation.width; 213 | } else { 214 | title_entry.width_request = width; 215 | } 216 | } 217 | 218 | /** 219 | * @name start_editing 220 | * @description Actions to be performed to start editing 221 | */ 222 | public void start_editing () { 223 | editing = true; 224 | on_start_editing (); 225 | } 226 | 227 | /** 228 | * @name stop_editing 229 | * @description Actions to be performed to stop editing 230 | */ 231 | public void stop_editing () { 232 | if (editing == true) { 233 | // debug("stop editing"); 234 | editing = false; 235 | on_stop_editing (); 236 | } 237 | } 238 | 239 | /** 240 | * @name undo_changes 241 | * @description Actions to be performed to undo chenges when editing 242 | */ 243 | private void undo_changes () { 244 | title_entry.text = title_label.label; 245 | } 246 | 247 | /** 248 | * @name set_lines 249 | * @description Sets the number of lines of the Gtk.label component 250 | * @see Gkt.label 251 | */ 252 | public void set_lines (int n) { 253 | this.title_label.set_lines (n); 254 | } 255 | 256 | /** 257 | * @name set_lines 258 | * @description Sets the mode used to ellipsize (add an ellipsis: "...") to 259 | * the text if there is not enough space to render the entire string. 260 | * @see Gkt.label 261 | */ 262 | public void set_ellipsize (Pango.EllipsizeMode mode) { 263 | this.title_label.set_ellipsize (mode); 264 | } 265 | 266 | } 267 | -------------------------------------------------------------------------------- /src/utils/RenameDialog.vala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017-2019 José Amuedo (https://github.com/spheras) 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | public class RenameDialog : Gtk.Dialog { 19 | private Gtk.Entry entry; 20 | 21 | public signal void on_rename (string new_name); 22 | public signal void on_cancel (); 23 | 24 | public RenameDialog (Gtk.Window parent, string title, string label_message, string entry_text) { 25 | if (parent != null) { 26 | this.set_transient_for (parent); 27 | } 28 | this.title = title; 29 | this.border_width = 5; 30 | set_default_size (350, 100); 31 | this.get_style_context ().add_class ("df_dialog"); 32 | this.set_decorated (true); 33 | this.window_position = Gtk.WindowPosition.MOUSE; 34 | 35 | create_widgets (label_message, entry_text); 36 | connect_signals (); 37 | } 38 | 39 | private void create_widgets (string label_message, string entry_text) { 40 | 41 | var description = new Gtk.Label (label_message); 42 | description.halign = Gtk.Align.START; 43 | this.entry = new Gtk.Entry (); 44 | this.entry.activate.connect (() => { 45 | this.response (Gtk.ResponseType.OK); 46 | }); 47 | this.entry.set_text (entry_text); 48 | 49 | // Layout widgets 50 | Gtk.Box hbox = new Gtk.Box (Gtk.Orientation.VERTICAL, 5); 51 | hbox.pack_start (description, false, true, 0); 52 | hbox.pack_start (entry, true, true, 0); 53 | 54 | var grid = new Gtk.Grid (); 55 | grid.margin = 10; 56 | grid.attach (hbox, 0, 0, 1, 1); 57 | 58 | Gtk.Box content = this.get_content_area () as Gtk.Box; 59 | content.pack_start (grid, false, true, 0); 60 | content.spacing = 10; 61 | 62 | // Add buttons to button area at the bottom 63 | add_button (DesktopFolder.Lang.DIALOG_OK, Gtk.ResponseType.OK); 64 | add_button (DesktopFolder.Lang.DIALOG_CANCEL, Gtk.ResponseType.CANCEL); 65 | } 66 | 67 | private void connect_signals () { 68 | this.response.connect (on_response); 69 | } 70 | 71 | private void on_response (Gtk.Dialog source, int response_id) { 72 | switch (response_id) { 73 | case Gtk.ResponseType.OK: 74 | this.on_rename (this.entry.get_text ()); 75 | this.destroy (); 76 | break; 77 | case Gtk.ResponseType.CANCEL: 78 | this.on_cancel (); 79 | this.destroy (); 80 | break; 81 | } 82 | } 83 | 84 | } 85 | -------------------------------------------------------------------------------- /src/utils/animation/GtkAnimation.vala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017-2019 José Amuedo (https://github.com/spheras) 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | namespace DesktopFolder.UtilGtkAnimation { 19 | /** 20 | * @name AnimateFn 21 | * @description Animate Function to be executed en each frame 22 | * @param {double} ellapsed the time ellapsed since the start of the animation 23 | */ 24 | protected delegate void AnimateFn (double ellapsed); 25 | 26 | public void animate_move (Gtk.Widget widget, Gdk.Point to, double duration, UtilFx.AnimationMode mode = UtilFx.AnimationMode.EASE_OUT_BACK) { 27 | WidgetAnimationMove animation = new WidgetAnimationMove (widget, to, duration, mode); 28 | animation.start (); 29 | } 30 | 31 | /** 32 | * @class 33 | * @name WidgetAnimation 34 | * @description util abstract class to animate widgets, each implementation should implement the animation itself 35 | */ 36 | public abstract class WidgetAnimation { 37 | /** the time when the animation was started, in ms */ 38 | protected double _start_time; 39 | /** the duration of the animation, ms */ 40 | protected double _duration; 41 | /** the widget to be moved */ 42 | protected Gtk.Widget _widget; 43 | /** the animation function to be executed in each frame */ 44 | protected unowned AnimateFn _animation_fn; 45 | /** the uid reference for the animation */ 46 | private uint _animation_ref; 47 | 48 | /** 49 | * @constructor 50 | * @param {Gtk.Widget} widget the widget to animate 51 | * @param {double} duration the duration of the animation 52 | * @param {AnimateFn} animation_fn the animation function 53 | */ 54 | protected WidgetAnimation (Gtk.Widget widget, double duration, AnimateFn animation_fn) { 55 | this._widget = widget; 56 | this._animation_fn = animation_fn; 57 | this._duration = duration; 58 | } 59 | 60 | /** 61 | * @name get_pending_animations 62 | * @description return the pending animations for this animation 63 | * @return {List} the list of widget animations that are pending 64 | */ 65 | protected abstract unowned Gee.List get_pending_animations (); 66 | 67 | /** 68 | * @name find_pending_animations 69 | * @description util method to find a pending animation for the widget 70 | * @return {WidgetAnimation} the Widget animation found or null 71 | */ 72 | private WidgetAnimation ? find_pending_animations () { 73 | unowned Gee.List pending_animations = this.get_pending_animations (); 74 | for (var i = 0; i < pending_animations.size; i++) { 75 | WidgetAnimation anim = pending_animations.@get (i); 76 | if (anim.get_widget () != null && anim.get_widget () == this._widget) { 77 | return anim; 78 | } 79 | } 80 | return null; 81 | } 82 | 83 | /** 84 | * @name finish_pending_animations 85 | * @description finish all pending animations for the widget 86 | */ 87 | private void finish_pending_animations () { 88 | WidgetAnimation found = this.find_pending_animations (); 89 | if (found != null) { 90 | found.stop (); 91 | } 92 | unowned Gee.List pending_animations = this.get_pending_animations (); 93 | pending_animations.remove (found); 94 | } 95 | 96 | /** 97 | * @name stop 98 | * @description stop the current animation, simulating the last frame 99 | */ 100 | public void stop () { 101 | // removing the pending threads 102 | try { 103 | Source.remove (this._animation_ref); 104 | } catch (Error err) { 105 | 106 | } 107 | // executing the last frame 108 | // this._animation_fn (this._duration); 109 | } 110 | 111 | /** 112 | * @name start 113 | * @description start the animation 114 | */ 115 | public void start () { 116 | // first we finish all the pending animations for the widget 117 | this.finish_pending_animations (); 118 | 119 | // adding a new pending animation 120 | unowned Gee.List pending_animations = this.get_pending_animations (); 121 | pending_animations.add (this); 122 | 123 | // and starting a new one 124 | this._start_time = (double) GLib.get_real_time () / 1000; 125 | this._animation_ref = GLib.Timeout.add (40, () => { 126 | double current = (double) GLib.get_real_time () / 1000; 127 | double ellapsed = current - this._start_time; 128 | this._animation_fn (ellapsed); 129 | if (ellapsed < this._duration) { 130 | return true; 131 | } else { 132 | WidgetAnimation found = this.find_pending_animations (); 133 | pending_animations.remove (found); 134 | return false; 135 | } 136 | }); 137 | } 138 | 139 | /** 140 | * @Getter 141 | */ 142 | public Gtk.Widget ? get_widget () { 143 | return this._widget; 144 | } 145 | 146 | /** 147 | * @Getter 148 | */ 149 | public double get_duration () { 150 | return this._duration; 151 | } 152 | 153 | } 154 | 155 | /** 156 | * @name WidgetAnimationMove 157 | * @description util class to move a Gtk.Widget from its current place to a different place 158 | */ 159 | public class WidgetAnimationMove : WidgetAnimation { 160 | /** list of pending move animations */ 161 | private static Gee.List move_animations = null; 162 | 163 | static construct { 164 | WidgetAnimationMove.move_animations = new Gee.ArrayList (); 165 | } 166 | 167 | /** 168 | * @override 169 | */ 170 | protected override unowned Gee.List get_pending_animations () { 171 | // WidgetAnimationMove.move_animations.append(this); 172 | return WidgetAnimationMove.move_animations; 173 | } 174 | 175 | /** 176 | * @constructor 177 | * @param {Gtk.Widget} widget the widget to animate 178 | * @param {Gdk.Point} to the point x,y where the widget will be placed at the end of the animation 179 | * @param {double} duration the duration of the animation 180 | * @param {UtilFx.AnimationMode} mode (optional) the animation to perform @see UtilFx.AnimationMode, by default EASE_OUT_BACK 181 | */ 182 | public WidgetAnimationMove (Gtk.Widget ? widget, Gdk.Point ? to, double ? duration, UtilFx.AnimationMode mode = UtilFx.AnimationMode.EASE_OUT_BACK) { 183 | // calculating the distances and positions to animate 184 | Gtk.Fixed parent = (Gtk.Fixed)widget.get_parent (); 185 | Gtk.Allocation widget_alloc; 186 | widget.get_allocation (out widget_alloc); 187 | int x = widget_alloc.x; 188 | int y = widget_alloc.y; 189 | int x_dist = to.x - x; 190 | int y_dist = to.y - y; 191 | int x_dist_curr = 0; 192 | int y_dist_curr = 0; 193 | 194 | // the animation function 195 | AnimateFn anim_fn = (ellapsed) => { 196 | if (ellapsed < duration) { 197 | double ease = UtilFx.easing_for_mode (mode, ellapsed, duration); 198 | x_dist_curr = (int) (x_dist * ease); 199 | y_dist_curr = (int) (y_dist * ease); 200 | parent.move (widget, x + x_dist_curr, y + y_dist_curr); 201 | } else { 202 | // last frame 203 | parent.move (widget, to.x, to.y); 204 | } 205 | }; 206 | 207 | base (widget, duration, (ell) => { 208 | // trick to allow access to widget! 209 | // help: why widget is null inside this closure when called from the Glib.Timeout.add?? 210 | anim_fn (ell); 211 | }); 212 | } 213 | 214 | } 215 | 216 | } 217 | -------------------------------------------------------------------------------- /src/utils/clipboard/ClipboardFile.vala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017-2019 José Amuedo (https://github.com/spheras) 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | namespace DesktopFolder.Clipboard { 19 | /** 20 | * @interface ClipboardFile 21 | * @description this interface represents a file that can be managed by the ClipboardManager 22 | */ 23 | public interface ClipboardFile : DragnDrop.DndView { 24 | /** 25 | * @name delete 26 | * @description this signal should be launched when the file is being deleted 27 | */ 28 | public signal void on_delete (); 29 | 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/utils/dragndrop/DndView.vala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017-2019 José Amuedo (https://github.com/spheras) 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | namespace DesktopFolder.DragnDrop { 19 | 20 | public interface DndView : Object { 21 | /** 22 | * @name get_widget 23 | * @description return the widget associated with this view 24 | * @return Widget the widget 25 | */ 26 | public abstract Gtk.Widget get_widget (); 27 | 28 | /** 29 | * @name get_application_window 30 | * @description return the application window of this view, needed for drag operations 31 | * @return ApplicationWindow 32 | */ 33 | public abstract Gtk.ApplicationWindow get_application_window (); 34 | 35 | /** 36 | * @name get_file 37 | * @description return the file asociated with this view 38 | * @return File the file 39 | */ 40 | public abstract GLib.File get_file (); 41 | 42 | /** 43 | * @name get_image 44 | * @description return the image to be shown when dragging 45 | * @return {Gtk.Image} the image to be rendered 46 | */ 47 | public abstract Gtk.Image get_image (); 48 | 49 | /** 50 | * @name get_file_at 51 | * @name get the file at the position x, y 52 | * @return File 53 | */ 54 | public abstract GLib.File get_file_at (int x, int y); 55 | 56 | /** 57 | * @name is_folder 58 | * @description check whether the view represents a folder or a file 59 | * @return bool true->this view represents a folder 60 | */ 61 | public abstract bool is_folder (); 62 | 63 | /** 64 | * @name is_writable 65 | * @description indicates if the file linked by this view is writable or not 66 | * @return bool 67 | */ 68 | public abstract bool is_writable (); 69 | 70 | /** 71 | * @name get_target_location 72 | * @description return the target File that represents this view 73 | * @return File the file target of this view 74 | */ 75 | public abstract GLib.File get_target_location (); 76 | 77 | /** 78 | * @name is_recent_uri_scheme 79 | * @description check whether the File is a recent uri scheme? 80 | * @return bool 81 | */ 82 | public abstract bool is_recent_uri_scheme (); 83 | 84 | /** 85 | * @name get_display_target_uri 86 | * @description return the target uri of this view 87 | * @return string the target uri 88 | */ 89 | public abstract string get_display_target_uri (); 90 | 91 | /** 92 | * @name on_drag_end 93 | * @description drag finished event 94 | */ 95 | public abstract void on_drag_end (); 96 | 97 | /** 98 | * @name on_drag_motion 99 | * @description the mouse is over the dnd view 100 | */ 101 | public abstract void on_drag_motion (); 102 | 103 | /** 104 | * @name on_drag_leave 105 | * @description the mouse leave the view 106 | */ 107 | public abstract void on_drag_leave (); 108 | 109 | /** 110 | * @name get_all_selected_views 111 | * @description return all the selected views at this moment 112 | * @return DndView[] the list of DndViews selected 113 | */ 114 | public abstract DndView[] get_all_selected_views (); 115 | 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /src/utils/menu/MenuItemColor.vala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017-2019 José Amuedo (https://github.com/spheras) 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | private class DesktopFolder.MenuItemColor : Gtk.MenuItem { 19 | private new bool has_focus; 20 | private int height; 21 | public signal void color_changed (int ncolor); 22 | public signal void custom_changed (string custom); 23 | 24 | private string[] tags_colors; 25 | private string custom; 26 | private const int XPAD = 17; 27 | 28 | public MenuItemColor (string[] tags_colors, string ? custom) { 29 | this.tags_colors = tags_colors; 30 | this.custom = custom; 31 | set_size_request (160, 20); 32 | height = 20; 33 | 34 | button_press_event.connect (button_pressed_cb); 35 | draw.connect (on_draw); 36 | 37 | select.connect (() => { 38 | has_focus = true; 39 | }); 40 | 41 | deselect.connect (() => { 42 | has_focus = false; 43 | }); 44 | } 45 | 46 | private bool button_pressed_cb (Gdk.EventButton event) { 47 | determine_button_pressed_event (event); 48 | return true; 49 | } 50 | 51 | private void determine_button_pressed_event (Gdk.EventButton event) { 52 | int i; 53 | int btnw = 10; 54 | int btnh = 10; 55 | int y0 = (height - btnh) / 2; 56 | int x0 = btnw + 5; 57 | int xpad = XPAD; 58 | 59 | if (event.y >= y0 && event.y <= y0 + btnh) { 60 | for (i = 1; i <= this.tags_colors.length + 1; i++) { 61 | if (event.x >= xpad + x0 * i && event.x <= xpad + x0 * i + btnw) { 62 | if (i > this.tags_colors.length) { 63 | Gtk.ColorSelectionDialog dialog = new Gtk.ColorSelectionDialog (DesktopFolder.Lang.MENU_COLOR_DIALOG_TITLE); 64 | dialog.set_transient_for ((Gtk.Window)this.get_toplevel()); 65 | dialog.get_color_selection ().set_has_opacity_control (true); 66 | Gdk.RGBA _rgba = Gdk.RGBA (); 67 | _rgba.parse (this.custom); 68 | dialog.get_color_selection ().set_current_rgba (_rgba); 69 | dialog.get_color_selection ().set_previous_rgba (_rgba); 70 | if (dialog.run () == Gtk.ResponseType.OK) { 71 | unowned Gtk.ColorSelection widget = dialog.get_color_selection (); 72 | string rgba = widget.current_rgba.to_string (); 73 | custom_changed (rgba); 74 | // uint alpha = widget.current_alpha; 75 | // stdout.puts ("Selection\n"); 76 | // stdout.printf (" %s\n", rgba); 77 | // stdout.printf (" %u\n", alpha); 78 | } 79 | dialog.close (); 80 | } else { 81 | color_changed (i - 1); 82 | } 83 | break; 84 | } 85 | } 86 | } 87 | } 88 | 89 | protected bool on_draw (Cairo.Context cr) { 90 | int i; 91 | int btnw = 10; 92 | int btnh = 10; 93 | int y0 = (height - btnh) / 2; 94 | int x0 = btnw + 5; 95 | int xpad = XPAD; 96 | 97 | for (i = 1; i <= this.tags_colors.length + 1; i++) { 98 | if (i == 1) { 99 | DrawCross (cr, xpad + x0 * i, y0 + 1, btnw - 2, btnh - 2); 100 | } else if (i > this.tags_colors.length) { 101 | if (this.custom != null) { 102 | Gdk.RGBA rgba = Gdk.RGBA (); 103 | rgba.parse (this.custom); 104 | rgba.alpha = 1; 105 | string custom_without_alpha = rgba.to_string (); 106 | DrawRoundedRectangle (cr, xpad + x0 * i, y0, btnw, btnh, "stroke", custom_without_alpha); 107 | DrawRoundedRectangle (cr, xpad + x0 * i, y0, btnw, btnh, "fill", custom_without_alpha); 108 | DrawGradientOverlay (cr, xpad + x0 * i, y0, btnw, btnh); 109 | DrawInterrogation (cr, xpad + x0 * i, y0 + 1, btnw - 2, btnh - 2); 110 | } 111 | } else { 112 | DrawRoundedRectangle (cr, xpad + x0 * i, y0, btnw, btnh, "stroke", this.tags_colors[i - 1]); 113 | DrawRoundedRectangle (cr, xpad + x0 * i, y0, btnw, btnh, "fill", this.tags_colors[i - 1]); 114 | DrawGradientOverlay (cr, xpad + x0 * i, y0, btnw, btnh); 115 | } 116 | } 117 | 118 | return true; 119 | } 120 | 121 | private void DrawCross (Cairo.Context cr, int x, int y, int w, int h) { 122 | cr.new_path (); 123 | cr.set_line_width (2.0); 124 | cr.move_to (x, y); 125 | cr.rel_line_to (w, h); 126 | cr.move_to (x, y + h); 127 | cr.rel_line_to (w, -h); 128 | cr.set_source_rgba (0, 0, 0, 0.6); 129 | cr.stroke (); 130 | 131 | cr.close_path (); 132 | } 133 | 134 | /** 135 | * @name DrawInterrogation 136 | * @description draw an interrogation character over the back custom color 137 | * @param {Cairo.Context} cr the context to use 138 | * @param {int} the X position 139 | * @param {int} the Y position 140 | * @param {int} the Width of the rectangle 141 | * @param {int} the Height of the rectangle 142 | */ 143 | private void DrawInterrogation (Cairo.Context cr, int x, int y, int w, int h) { 144 | string utf8 = "?"; 145 | 146 | cr.set_source_rgba (1, 1, 1, 1); 147 | cr.select_font_face ("Dialog", Cairo.FontSlant.NORMAL, Cairo.FontWeight.BOLD); 148 | cr.set_font_size (12.0); 149 | 150 | Cairo.TextExtents extents; 151 | cr.text_extents (utf8, out extents); 152 | double sx = x + (w / 2) - (extents.width / 2 + extents.x_bearing); 153 | double sy = y + (y / 2) - (extents.height / 2 + extents.y_bearing); 154 | 155 | cr.move_to (sx, sy); 156 | cr.show_text (utf8); 157 | } 158 | 159 | /* 160 | * Create a rounded rectangle using the Bezier curve. 161 | * Adapted from http://cairographics.org/cookbook/roundedrectangles/ 162 | */ 163 | private void DrawRoundedRectangle (Cairo.Context cr, int x, int y, int w, int h, string style, string color) { 164 | int radius_x = 2; 165 | int radius_y = 2; 166 | double ARC_TO_BEZIER = 0.55228475; 167 | 168 | if (radius_x > w - radius_x) 169 | radius_x = w / 2; 170 | 171 | if (radius_y > h - radius_y) 172 | radius_y = h / 2; 173 | 174 | /* approximate (quite close) the arc using a bezier curve */ 175 | double ca = ARC_TO_BEZIER * radius_x; 176 | double cb = ARC_TO_BEZIER * radius_y; 177 | 178 | cr.new_path (); 179 | cr.set_line_width (0.7); 180 | cr.set_tolerance (0.1); 181 | cr.move_to (x + radius_x, y); 182 | cr.rel_line_to (w - 2 * radius_x, 0.0); 183 | cr.rel_curve_to (ca, 0.0, radius_x, cb, radius_x, radius_y); 184 | cr.rel_line_to (0, h - 2 * radius_y); 185 | cr.rel_curve_to (0.0, cb, ca - radius_x, radius_y, -radius_x, radius_y); 186 | cr.rel_line_to (-w + 2 * radius_x, 0); 187 | cr.rel_curve_to (-ca, 0, -radius_x, -cb, -radius_x, -radius_y); 188 | cr.rel_line_to (0, -h + 2 * radius_y); 189 | cr.rel_curve_to (0.0, -cb, radius_x - ca, -radius_y, radius_x, -radius_y); 190 | 191 | switch (style) { 192 | default: 193 | case "fill": 194 | Gdk.RGBA rgba = Gdk.RGBA (); 195 | rgba.parse (color); 196 | Gdk.cairo_set_source_rgba (cr, rgba); 197 | cr.fill (); 198 | break; 199 | case "stroke": 200 | cr.set_source_rgba (0, 0, 0, 0.5); 201 | cr.stroke (); 202 | break; 203 | } 204 | 205 | cr.close_path (); 206 | } 207 | 208 | /* 209 | * Draw the overlaying gradient 210 | */ 211 | private void DrawGradientOverlay (Cairo.Context cr, int x, int y, int w, int h) { 212 | var radial = new Cairo.Pattern.radial (w, h, 1, 0.0, 0.0, 0.0); 213 | radial.add_color_stop_rgba (0, 0.3, 0.3, 0.3, 0.0); 214 | radial.add_color_stop_rgba (1, 0.0, 0.0, 0.0, 0.5); 215 | 216 | cr.set_source (radial); 217 | cr.rectangle (x, y, w, h); 218 | cr.fill (); 219 | } 220 | 221 | } 222 | -------------------------------------------------------------------------------- /src/utils/menu/MenuItemSeparator.vala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017-2019 José Amuedo (https://github.com/spheras) 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | private class DesktopFolder.MenuItemSeparator : Gtk.MenuItem { 19 | public MenuItemSeparator () { 20 | draw.connect (on_draw); 21 | } 22 | 23 | protected bool on_draw (Cairo.Context cr) { 24 | int padding = 10; 25 | Gtk.Allocation allocation; 26 | this.get_allocation (out allocation); 27 | int middle = allocation.height / 2; 28 | cr.new_path (); 29 | cr.set_line_width (1); 30 | // +0.5 to avoid fuzzy lines? 31 | // http://stevehanov.ca/blog/index.php?id=28 32 | cr.move_to (padding + 0.5, middle + 0.5); 33 | cr.rel_line_to (allocation.width - padding * 2, 0); 34 | cr.set_source_rgba (0, 0, 0, 0.2); 35 | cr.stroke (); 36 | 37 | cr.close_path (); 38 | 39 | return true; 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /src/utils/shortcut/CustomShortcutSettings.vala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017-2019 José Amuedo (https://github.com/spheras) 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | class Pantheon.Keyboard.Shortcuts.CustomShortcutSettings : Object { 19 | 20 | const string SCHEMA = "org.gnome.settings-daemon.plugins.media-keys"; 21 | const string KEY = "custom-keybinding"; 22 | 23 | const string RELOCATABLE_SCHEMA_PATH_TEMLPATE = "/org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/custom%d/"; 24 | 25 | const int MAX_SHORTCUTS = 100; 26 | 27 | static GLib.Settings settings; 28 | 29 | public static bool available = false; 30 | 31 | public struct CustomShortcut { 32 | string shortcut; 33 | string command; 34 | string relocatable_schema; 35 | } 36 | 37 | public static void init () { 38 | var schema_source = GLib.SettingsSchemaSource.get_default (); 39 | 40 | var schema = schema_source.lookup (SCHEMA, true); 41 | 42 | if (schema == null) { 43 | warning ("Schema \"%s\" is not installed on your system.", SCHEMA); 44 | return; 45 | } 46 | 47 | settings = new GLib.Settings.full (schema, null, null); 48 | available = true; 49 | } 50 | 51 | static string[] get_relocatable_schemas () { 52 | return settings.get_strv (KEY + "s"); 53 | } 54 | 55 | static string get_relocatable_schema_path (int i) { 56 | return RELOCATABLE_SCHEMA_PATH_TEMLPATE.printf (i); 57 | } 58 | 59 | static GLib.Settings ? get_relocatable_schema_settings (string relocatable_schema) { 60 | return new GLib.Settings.with_path (SCHEMA + "." + KEY, relocatable_schema); 61 | } 62 | 63 | public static string ? create_shortcut () requires (available) { 64 | for (int i = 0; i < MAX_SHORTCUTS; i++) { 65 | var new_relocatable_schema = get_relocatable_schema_path (i); 66 | 67 | if (relocatable_schema_is_used (new_relocatable_schema) == false) { 68 | reset_relocatable_schema (new_relocatable_schema); 69 | add_relocatable_schema (new_relocatable_schema); 70 | return new_relocatable_schema; 71 | } 72 | } 73 | 74 | return (string) null; 75 | } 76 | 77 | static bool relocatable_schema_is_used (string new_relocatable_schema) { 78 | var relocatable_schemas = get_relocatable_schemas (); 79 | 80 | foreach (var relocatable_schema in relocatable_schemas) 81 | if (relocatable_schema == new_relocatable_schema) 82 | return true; 83 | 84 | return false; 85 | } 86 | 87 | static void add_relocatable_schema (string new_relocatable_schema) { 88 | var relocatable_schemas = get_relocatable_schemas (); 89 | relocatable_schemas += new_relocatable_schema; 90 | settings.set_strv (KEY + "s", relocatable_schemas); 91 | apply_settings (settings); 92 | } 93 | 94 | static void reset_relocatable_schema (string relocatable_schema) { 95 | var relocatable_settings = get_relocatable_schema_settings (relocatable_schema); 96 | relocatable_settings.reset ("name"); 97 | relocatable_settings.reset ("command"); 98 | relocatable_settings.reset ("binding"); 99 | apply_settings (relocatable_settings); 100 | } 101 | 102 | /* NEVER USED 103 | public static void remove_shortcut (string relocatable_schema) 104 | requires (available) { 105 | 106 | string []relocatable_schemas = {}; 107 | 108 | foreach (var schema in get_relocatable_schemas ()) 109 | if (schema != relocatable_schema) 110 | relocatable_schemas += schema; 111 | 112 | reset_relocatable_schema (relocatable_schema); 113 | settings.set_strv (KEY + "s", relocatable_schemas); 114 | apply_settings (settings); 115 | } 116 | */ 117 | 118 | public static bool edit_shortcut (string relocatable_schema, string shortcut) 119 | requires (available) { 120 | 121 | var relocatable_settings = get_relocatable_schema_settings (relocatable_schema); 122 | relocatable_settings.set_string ("binding", shortcut); 123 | apply_settings (relocatable_settings); 124 | return true; 125 | } 126 | 127 | public static bool edit_command (string relocatable_schema, string command) 128 | requires (available) { 129 | 130 | var relocatable_settings = get_relocatable_schema_settings (relocatable_schema); 131 | relocatable_settings.set_string ("command", command); 132 | relocatable_settings.set_string ("name", command); 133 | apply_settings (relocatable_settings); 134 | return true; 135 | } 136 | 137 | public static GLib.List list_custom_shortcuts () 138 | requires (available) { 139 | 140 | var list = new GLib.List (); 141 | foreach (var relocatable_schema in get_relocatable_schemas ()) 142 | list.append (create_custom_shortcut_object (relocatable_schema)); 143 | return list; 144 | } 145 | 146 | static CustomShortcut ? create_custom_shortcut_object (string relocatable_schema) { 147 | var relocatable_settings = get_relocatable_schema_settings (relocatable_schema); 148 | 149 | return { 150 | relocatable_settings.get_string ("binding"), 151 | relocatable_settings.get_string ("command"), 152 | relocatable_schema 153 | }; 154 | } 155 | 156 | public static bool shortcut_conflicts (Shortcut new_shortcut, out string command, 157 | out string relocatable_schema) { 158 | var custom_shortcuts = list_custom_shortcuts (); 159 | command = ""; 160 | relocatable_schema = ""; 161 | 162 | foreach (var custom_shortcut in custom_shortcuts) { 163 | // debug("checking:%s",custom_shortcut.shortcut); 164 | var shortcut = new Shortcut.parse (custom_shortcut.shortcut); 165 | if (shortcut.is_equal (new_shortcut)) { 166 | command = custom_shortcut.command; 167 | relocatable_schema = custom_shortcut.relocatable_schema; 168 | return true; 169 | } 170 | } 171 | 172 | return false; 173 | } 174 | 175 | private static void apply_settings (GLib.Settings asettings) { 176 | asettings.apply (); 177 | GLib.Settings.sync (); 178 | } 179 | 180 | } 181 | -------------------------------------------------------------------------------- /src/utils/shortcut/Shortcut.vala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017-2019 José Amuedo (https://github.com/spheras) 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | namespace Pantheon.Keyboard.Shortcuts { 19 | // stores a shortcut, converts to gsettings format and readable format 20 | // and checks for validity 21 | class Shortcut : GLib.Object { 22 | public Gdk.ModifierType modifiers; 23 | public uint accel_key; 24 | 25 | /* NEVER USED 26 | string SEPARATOR = " · "; 27 | */ 28 | 29 | // constructors 30 | public Shortcut (uint key = 0, Gdk.ModifierType mod = (Gdk.ModifierType) 0) { 31 | accel_key = key; 32 | modifiers = mod; 33 | } 34 | 35 | public Shortcut.parse (string ? str) 36 | { 37 | if (str == null) { 38 | accel_key = 0; 39 | modifiers = (Gdk.ModifierType) 0; 40 | return; 41 | } 42 | Gtk.accelerator_parse (str, out accel_key, out modifiers); 43 | } 44 | 45 | // converters 46 | public string to_gsettings () { 47 | if (!valid ()) 48 | return ""; 49 | return Gtk.accelerator_name (accel_key, modifiers); 50 | } 51 | 52 | /* NEVER USED 53 | public string to_readable () 54 | { 55 | if (!valid()) 56 | return _("Disabled"); 57 | 58 | string tmp = ""; 59 | 60 | if ((modifiers & Gdk.ModifierType.SHIFT_MASK) > 0) 61 | tmp += "⇧" + SEPARATOR; 62 | if ((modifiers & Gdk.ModifierType.SUPER_MASK) > 0) 63 | tmp += "⌘" + SEPARATOR; 64 | if ((modifiers & Gdk.ModifierType.CONTROL_MASK) > 0) 65 | tmp += _("Ctrl") + SEPARATOR; 66 | if ((modifiers & Gdk.ModifierType.MOD1_MASK) > 0) 67 | tmp += "⎇" + SEPARATOR; 68 | if ((modifiers & Gdk.ModifierType.MOD2_MASK) > 0) 69 | tmp += "Mod2" + SEPARATOR; 70 | if ((modifiers & Gdk.ModifierType.MOD3_MASK) > 0) 71 | tmp += "Mod3" + SEPARATOR; 72 | if ((modifiers & Gdk.ModifierType.MOD4_MASK) > 0) 73 | tmp += "Mod4" + SEPARATOR; 74 | 75 | switch (accel_key) { 76 | 77 | case Gdk.Key.Tab: tmp += "↹"; break; 78 | case Gdk.Key.Up: tmp += "↑"; break; 79 | case Gdk.Key.Down: tmp += "↓"; break; 80 | case Gdk.Key.Left: tmp += "←"; break; 81 | case Gdk.Key.Right: tmp += "→"; break; 82 | default: 83 | tmp += Gtk.accelerator_get_label (accel_key, 0); 84 | break; 85 | } 86 | 87 | return tmp; 88 | } 89 | */ 90 | 91 | public bool is_equal (Shortcut shortcut) { 92 | // debug("is equal? %d, %d : %d, %d",shortcut.modifiers, modifiers, (int)shortcut.accel_key, (int) accel_key); 93 | if (shortcut.modifiers == modifiers) { 94 | if (shortcut.accel_key == accel_key) { 95 | return true; 96 | } 97 | } 98 | return false; 99 | } 100 | 101 | // validator 102 | public bool valid () { 103 | if (accel_key == 0 || (modifiers == (Gdk.ModifierType) 0 && accel_key != Gdk.Key.Print)) 104 | return false; 105 | 106 | if (modifiers == Gdk.ModifierType.SHIFT_MASK) { 107 | if ((accel_key >= Gdk.Key.a && accel_key <= Gdk.Key.z) 108 | || (accel_key >= Gdk.Key.A && accel_key <= Gdk.Key.Z) 109 | || (accel_key >= Gdk.Key.@0 && accel_key <= Gdk.Key.@9) 110 | || (accel_key >= Gdk.Key.kana_fullstop && accel_key <= Gdk.Key.semivoicedsound) 111 | || (accel_key >= Gdk.Key.Arabic_comma && accel_key <= Gdk.Key.Arabic_sukun) 112 | || (accel_key >= Gdk.Key.Serbian_dje && accel_key <= Gdk.Key.Cyrillic_HARDSIGN) 113 | || (accel_key >= Gdk.Key.Greek_ALPHAaccent && accel_key <= Gdk.Key.Greek_omega) 114 | || (accel_key >= Gdk.Key.hebrew_doublelowline && accel_key <= Gdk.Key.hebrew_taf) 115 | || (accel_key >= Gdk.Key.Thai_kokai && accel_key <= Gdk.Key.Thai_lekkao) 116 | || (accel_key >= Gdk.Key.Hangul && accel_key <= Gdk.Key.Hangul_Special) 117 | || (accel_key >= Gdk.Key.Hangul_Kiyeog && accel_key <= Gdk.Key.Hangul_J_YeorinHieuh) 118 | || (accel_key == Gdk.Key.Home) 119 | || (accel_key == Gdk.Key.Left) 120 | || (accel_key == Gdk.Key.Up) 121 | || (accel_key == Gdk.Key.Right) 122 | || (accel_key == Gdk.Key.Down) 123 | || (accel_key == Gdk.Key.Page_Up) 124 | || (accel_key == Gdk.Key.Page_Down) 125 | || (accel_key == Gdk.Key.End) 126 | || (accel_key == Gdk.Key.Tab) 127 | || (accel_key == Gdk.Key.KP_Enter) 128 | || (accel_key == Gdk.Key.Return)) { 129 | return false; 130 | } 131 | } 132 | 133 | return true; 134 | } 135 | 136 | } 137 | } 138 | --------------------------------------------------------------------------------