├── .github └── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── .gitignore ├── AUTHORS ├── BUILD_CONFIG ├── COPYING ├── INSTALL ├── LICENSE.md ├── NOTES ├── README.md ├── _config.yml ├── archlinux └── PKGBUILD ├── debian ├── changelog ├── compat ├── control ├── copyright ├── docs ├── rules ├── source │ └── format └── timeshift.appdata.xml ├── files └── timeshift.json ├── icons ├── README ├── timeshift.png ├── timeshift.xcf ├── timeshift_2_black.xcf ├── timeshift_2_black_bold.xcf ├── timeshift_2_white.xcf ├── timeshift_2_white_bold.xcf ├── timeshift_black.png ├── timeshift_black_bold.png ├── timeshift_white.png └── timeshift_white_bold.png ├── images ├── PayPal.png ├── bitcoin_qr_code_timeshift.png ├── main_window.png ├── patreon.png ├── restore_summary.png ├── settings_btrfs.png ├── settings_filters.png ├── settings_location.png ├── settings_rsync.png ├── settings_schedule.png ├── settings_users.png ├── settings_users_btrfs.png └── settings_users_rsync.png ├── makefile ├── makepot ├── man └── timeshift.1.gz ├── merge-launchpad-translations.sh ├── po ├── timeshift-am.po ├── timeshift-ar.po ├── timeshift-az.po ├── timeshift-bg.po ├── timeshift-ca.po ├── timeshift-ca@valencia.po ├── timeshift-cs.po ├── timeshift-da.po ├── timeshift-de.po ├── timeshift-el.po ├── timeshift-en_GB.po ├── timeshift-es.po ├── timeshift-et.po ├── timeshift-eu.po ├── timeshift-fi.po ├── timeshift-fr.po ├── timeshift-he.po ├── timeshift-hi.po ├── timeshift-hr.po ├── timeshift-hu.po ├── timeshift-ia.po ├── timeshift-id.po ├── timeshift-is.po ├── timeshift-it.po ├── timeshift-ja.po ├── timeshift-ko.po ├── timeshift-lt.po ├── timeshift-nb.po ├── timeshift-ne.po ├── timeshift-nl.po ├── timeshift-pl.po ├── timeshift-pt.po ├── timeshift-pt_BR.po ├── timeshift-ro.po ├── timeshift-ru.po ├── timeshift-sk.po ├── timeshift-sr.po ├── timeshift-sv.po ├── timeshift-tr.po ├── timeshift-uk.po ├── timeshift-uz.po ├── timeshift-vi.po ├── timeshift-zh_CN.po └── timeshift-zh_TW.po ├── release └── sanity.config ├── src ├── Console │ └── AppConsole.vala ├── Core │ ├── AppExcludeEntry.vala │ ├── Main.vala │ ├── Snapshot.vala │ ├── SnapshotRepo.vala │ └── Subvolume.vala ├── Gtk │ ├── AppGtk.vala │ ├── BackupBox.vala │ ├── BackupDeviceBox.vala │ ├── BackupFinishBox.vala │ ├── BackupWindow.vala │ ├── BootOptionsBox.vala │ ├── BootOptionsWindow.vala │ ├── DeleteBox.vala │ ├── DeleteFinishBox.vala │ ├── DeleteWindow.vala │ ├── EstimateBox.vala │ ├── ExcludeAppsBox.vala │ ├── ExcludeBox.vala │ ├── ExcludeListSummaryWindow.vala │ ├── ExcludeMessageWindow.vala │ ├── FinishBox.vala │ ├── MainWindow.vala │ ├── MiscBox.vala │ ├── RestoreBox.vala │ ├── RestoreDeviceBox.vala │ ├── RestoreExcludeBox.vala │ ├── RestoreFinishBox.vala │ ├── RestoreSummaryBox.vala │ ├── RestoreWindow.vala │ ├── RsyncLogBox.vala │ ├── RsyncLogWindow.vala │ ├── ScheduleBox.vala │ ├── SettingsWindow.vala │ ├── SetupWizardWindow.vala │ ├── SnapshotBackendBox.vala │ ├── SnapshotListBox.vala │ └── UsersBox.vala ├── Utility │ ├── AppLock.vala │ ├── AsyncTask.vala │ ├── CronTab.vala │ ├── CryptTabEntry.vala │ ├── DeleteFileTask.vala │ ├── Device.vala │ ├── FileItem.vala │ ├── FsTabEntry.vala │ ├── Gtk │ │ ├── AboutWindow.vala │ │ ├── CustomMessageDialog.vala │ │ ├── DonationWindow.vala │ │ └── TerminalWindow.vala │ ├── GtkHelper.vala │ ├── IconManager.vala │ ├── LicenseText.vala │ ├── LinuxDistro.vala │ ├── MountEntry.vala │ ├── OSDNotify.vala │ ├── RsyncSpaceCheckTask.vala │ ├── RsyncTask.vala │ ├── SystemUser.vala │ ├── TeeJee.FileSystem.vala │ ├── TeeJee.Json.vala │ ├── TeeJee.Logging.vala │ ├── TeeJee.Misc.vala │ ├── TeeJee.Process.vala │ ├── TeeJee.System.vala │ └── TimeoutCounter.vala ├── makefile ├── share │ ├── icons │ │ └── hicolor │ │ │ ├── 128x128 │ │ │ └── apps │ │ │ │ └── timeshift.png │ │ │ ├── 16x16 │ │ │ └── apps │ │ │ │ └── timeshift.png │ │ │ ├── 22x22 │ │ │ └── apps │ │ │ │ └── timeshift.png │ │ │ ├── 24x24 │ │ │ └── apps │ │ │ │ └── timeshift.png │ │ │ ├── 32x32 │ │ │ └── apps │ │ │ │ └── timeshift.png │ │ │ ├── 48x48 │ │ │ └── apps │ │ │ │ └── timeshift.png │ │ │ ├── 64x64 │ │ │ └── apps │ │ │ │ └── timeshift.png │ │ │ └── 96x96 │ │ │ └── apps │ │ │ └── timeshift.png │ ├── pixmaps │ │ └── timeshift.png │ ├── polkit-1 │ │ └── actions │ │ │ └── in.teejeetech.pkexec.timeshift.policy │ └── timeshift │ │ └── images │ │ ├── clock.png │ │ ├── disk.png │ │ ├── document-open-recent-symbolic.svg │ │ ├── document-save-symbolic.svg │ │ ├── donate.png │ │ ├── drive-harddisk.svg │ │ ├── edit-delete-symbolic.svg │ │ ├── edit-delete.png │ │ ├── emblem-default-symbolic.svg │ │ ├── exclude.png │ │ ├── folder-symbolic.svg │ │ ├── gtk-file.png │ │ ├── include.png │ │ ├── item-blue.png │ │ ├── item-gray.png │ │ ├── item-green.png │ │ ├── item-red.png │ │ ├── item-yellow.png │ │ ├── list-add.png │ │ ├── list-remove.png │ │ ├── locked.png │ │ ├── media-optical.png │ │ ├── open-menu-symbolic.svg │ │ ├── preferences-system-symbolic.svg │ │ ├── timeshift-shield-high.svg │ │ ├── timeshift-shield-low.svg │ │ ├── timeshift-shield-med.svg │ │ └── unlocked.png ├── timeshift-gtk.desktop ├── timeshift-launcher └── timeshift-uninstall ├── timeshift-ru.po └── timeshift.pot /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Report an issue 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **NOTE** 11 | Timeshift is now being maintained by the Linux Mint project. Any new feature requests, issues, or pull requests should be submitted to the [Linux Mint repository](https://github.com/linuxmint/timeshift). 12 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **NOTE** 11 | Timeshift is now being maintained by the Linux Mint project. Any new feature requests, issues, or pull requests should be submitted to the Linux Mint repository. 12 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | src/timeshift 2 | src/timeshift-gtk 3 | designs/ 4 | release/source 5 | release/amd64 6 | release/i386 7 | release/armel 8 | release/armhf 9 | release/*.deb 10 | release/*.run 11 | *.geany 12 | *.mo 13 | *.c 14 | *.o 15 | .bzr 16 | .git 17 | *~ 18 | po-lp/ 19 | parts/ 20 | stage/ 21 | prime/ 22 | snap/ 23 | release/ 24 | debian/timeshift/ 25 | rpm/ 26 | debian/debhelper-build-stamp 27 | debian/files 28 | debian/timeshift.debhelper.log 29 | debian/timeshift.substvars 30 | 31 | debian/.debhelper/ 32 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | 2 | Copyright (C) 2020 Tony George (teejeetech@gmail.com) 3 | 4 | -------------------------------------------------------------------------------- /BUILD_CONFIG: -------------------------------------------------------------------------------- 1 | app_fullname="Timeshift" 2 | app_name="timeshift" 3 | pkg_name="timeshift" 4 | 5 | build_deb=0 6 | build_rpm=1 7 | build_zst=0 8 | 9 | deb_arch="amd64 i386 arm64 armhf" # amd64 i386 arm64 armhf 10 | rpm_arch="" # x86_64 i686 11 | zst_arch="" # x86_64 i686 12 | 13 | pkg_version=$(dpkg-parsechangelog --show-field Version) 14 | 15 | git_origin="git@github.com:teejee2008/timeshift.git" 16 | 17 | -------------------------------------------------------------------------------- /INSTALL: -------------------------------------------------------------------------------- 1 | # build 2 | make all 3 | 4 | # install 5 | sudo make install 6 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | 2 | GNU LESSER GENERAL PUBLIC LICENSE 3 | Version 3, 29 June 2007 4 | 5 | Copyright (C) 2007 Free Software Foundation, Inc. 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | 10 | This version of the GNU Lesser General Public License incorporates 11 | the terms and conditions of version 3 of the GNU General Public 12 | License, supplemented by the additional permissions listed below. 13 | 14 | 0. Additional Definitions. 15 | 16 | As used herein, "this License" refers to version 3 of the GNU Lesser 17 | General Public License, and the "GNU GPL" refers to version 3 of the GNU 18 | General Public License. 19 | 20 | "The Library" refers to a covered work governed by this License, 21 | other than an Application or a Combined Work as defined below. 22 | 23 | An "Application" is any work that makes use of an interface provided 24 | by the Library, but which is not otherwise based on the Library. 25 | Defining a subclass of a class defined by the Library is deemed a mode 26 | of using an interface provided by the Library. 27 | 28 | A "Combined Work" is a work produced by combining or linking an 29 | Application with the Library. The particular version of the Library 30 | with which the Combined Work was made is also called the "Linked 31 | Version". 32 | 33 | The "Minimal Corresponding Source" for a Combined Work means the 34 | Corresponding Source for the Combined Work, excluding any source code 35 | for portions of the Combined Work that, considered in isolation, are 36 | based on the Application, and not on the Linked Version. 37 | 38 | The "Corresponding Application Code" for a Combined Work means the 39 | object code and/or source code for the Application, including any data 40 | and utility programs needed for reproducing the Combined Work from the 41 | Application, but excluding the System Libraries of the Combined Work. 42 | 43 | 1. Exception to Section 3 of the GNU GPL. 44 | 45 | You may convey a covered work under sections 3 and 4 of this License 46 | without being bound by section 3 of the GNU GPL. 47 | 48 | 2. Conveying Modified Versions. 49 | 50 | If you modify a copy of the Library, and, in your modifications, a 51 | facility refers to a function or data to be supplied by an Application 52 | that uses the facility (other than as an argument passed when the 53 | facility is invoked), then you may convey a copy of the modified 54 | version: 55 | 56 | a) under this License, provided that you make a good faith effort to 57 | ensure that, in the event an Application does not supply the 58 | function or data, the facility still operates, and performs 59 | whatever part of its purpose remains meaningful, or 60 | 61 | b) under the GNU GPL, with none of the additional permissions of 62 | this License applicable to that copy. 63 | 64 | 3. Object Code Incorporating Material from Library Header Files. 65 | 66 | The object code form of an Application may incorporate material from 67 | a header file that is part of the Library. You may convey such object 68 | code under terms of your choice, provided that, if the incorporated 69 | material is not limited to numerical parameters, data structure 70 | layouts and accessors, or small macros, inline functions and templates 71 | (ten or fewer lines in length), you do both of the following: 72 | 73 | a) Give prominent notice with each copy of the object code that the 74 | Library is used in it and that the Library and its use are 75 | covered by this License. 76 | 77 | b) Accompany the object code with a copy of the GNU GPL and this license 78 | document. 79 | 80 | 4. Combined Works. 81 | 82 | You may convey a Combined Work under terms of your choice that, 83 | taken together, effectively do not restrict modification of the 84 | portions of the Library contained in the Combined Work and reverse 85 | engineering for debugging such modifications, if you also do each of 86 | the following: 87 | 88 | a) Give prominent notice with each copy of the Combined Work that 89 | the Library is used in it and that the Library and its use are 90 | covered by this License. 91 | 92 | b) Accompany the Combined Work with a copy of the GNU GPL and this license 93 | document. 94 | 95 | c) For a Combined Work that displays copyright notices during 96 | execution, include the copyright notice for the Library among 97 | these notices, as well as a reference directing the user to the 98 | copies of the GNU GPL and this license document. 99 | 100 | d) Do one of the following: 101 | 102 | 0) Convey the Minimal Corresponding Source under the terms of this 103 | License, and the Corresponding Application Code in a form 104 | suitable for, and under terms that permit, the user to 105 | recombine or relink the Application with a modified version of 106 | the Linked Version to produce a modified Combined Work, in the 107 | manner specified by section 6 of the GNU GPL for conveying 108 | Corresponding Source. 109 | 110 | 1) Use a suitable shared library mechanism for linking with the 111 | Library. A suitable mechanism is one that (a) uses at run time 112 | a copy of the Library already present on the user's computer 113 | system, and (b) will operate properly with a modified version 114 | of the Library that is interface-compatible with the Linked 115 | Version. 116 | 117 | e) Provide Installation Information, but only if you would otherwise 118 | be required to provide such information under section 6 of the 119 | GNU GPL, and only to the extent that such information is 120 | necessary to install and execute a modified version of the 121 | Combined Work produced by recombining or relinking the 122 | Application with a modified version of the Linked Version. (If 123 | you use option 4d0, the Installation Information must accompany 124 | the Minimal Corresponding Source and Corresponding Application 125 | Code. If you use option 4d1, you must provide the Installation 126 | Information in the manner specified by section 6 of the GNU GPL 127 | for conveying Corresponding Source.) 128 | 129 | 5. Combined Libraries. 130 | 131 | You may place library facilities that are a work based on the 132 | Library side by side in a single library together with other library 133 | facilities that are not Applications and are not covered by this 134 | License, and convey such a combined library under terms of your 135 | choice, if you do both of the following: 136 | 137 | a) Accompany the combined library with a copy of the same work based 138 | on the Library, uncombined with any other library facilities, 139 | conveyed under the terms of this License. 140 | 141 | b) Give prominent notice with the combined library that part of it 142 | is a work based on the Library, and explaining where to find the 143 | accompanying uncombined form of the same work. 144 | 145 | 6. Revised Versions of the GNU Lesser General Public License. 146 | 147 | The Free Software Foundation may publish revised and/or new versions 148 | of the GNU Lesser General Public License from time to time. Such new 149 | versions will be similar in spirit to the present version, but may 150 | differ in detail to address new problems or concerns. 151 | 152 | Each version is given a distinguishing version number. If the 153 | Library as you received it specifies that a certain numbered version 154 | of the GNU Lesser General Public License "or any later version" 155 | applies to it, you have the option of following the terms and 156 | conditions either of that published version or of any later version 157 | published by the Free Software Foundation. If the Library as you 158 | received it does not specify a version number of the GNU Lesser 159 | General Public License, you may choose any version of the GNU Lesser 160 | General Public License ever published by the Free Software Foundation. 161 | 162 | If the Library as you received it specifies that a proxy can decide 163 | whether future versions of the GNU Lesser General Public License shall 164 | apply, that proxy's public statement of acceptance of any version is 165 | permanent authorization for you to choose that version for the 166 | Library. 167 | -------------------------------------------------------------------------------- /NOTES: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/teejee2008/timeshift/21570e2a2df6347843a099ea2de0da02a926aafe/NOTES -------------------------------------------------------------------------------- /_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-cayman -------------------------------------------------------------------------------- /archlinux/PKGBUILD: -------------------------------------------------------------------------------- 1 | # Maintainer: Tony George teejeetech at gmail dot com 2 | 3 | set -u 4 | 5 | rm -rf *.pkg.tar* 6 | 7 | if ! [ -d src ]; then 8 | rsync -avh --delete --exclude=.git --exclude=archlinux --exclude=debian --exclude=release ../ src/ 9 | fi 10 | 11 | pkgname='timeshift' 12 | pkgver="$(cd .. && dpkg-parsechangelog --show-field Version)" 13 | pkgrel='1' 14 | pkgdesc='System restore tool for Linux' 15 | arch=('i686' 'x86_64' 'armv7h' 'aarch64') 16 | license=('gplv2') 17 | 18 | # required for build and run 19 | depends=('libgee>=0.18.0' 'libsoup' 'json-glib' 'desktop-file-utils' 'vte3' 'rsync') 20 | 21 | # required for build 22 | # dpkg is needed to parse version string 23 | makedepends=('vala' 'dpkg' 'diffutils' 'coreutils') 24 | 25 | optdepends=( 26 | 'cronie: scheduling' 27 | ) 28 | 29 | _srcdir=./ 30 | source=() 31 | 32 | prepare() { 33 | set -u 34 | cd "${_srcdir}" 35 | set +u 36 | } 37 | 38 | build() { 39 | set -u 40 | make -C "${_srcdir}" -s -j1 41 | set +u 42 | } 43 | 44 | package() { 45 | set -u 46 | make -C "${_srcdir}" -j1 DESTDIR="${pkgdir}" install 47 | set +u 48 | } 49 | 50 | set +u 51 | 52 | # (cd archlinux; makepkg ) 53 | # sudo pacman -U --noconfirm ./${pkgname}-20.08.3-1-x86_64.pkg.tar.zst 54 | 55 | -------------------------------------------------------------------------------- /debian/compat: -------------------------------------------------------------------------------- 1 | 8 2 | -------------------------------------------------------------------------------- /debian/control: -------------------------------------------------------------------------------- 1 | Source: timeshift 2 | Section: utils 3 | Priority: extra 4 | Maintainer: Tony George 5 | Build-Depends: debhelper (>= 8.0.0), autotools-dev, gzip, valac, libgtk-3-dev, libgee-0.8-dev, libjson-glib-dev, libvte-2.91-dev 6 | Standards-Version: 3.9.3 7 | Homepage: https://teejeetech.in/ 8 | #Vcs-Git: git://git.debian.org/collab-maint/hello.git 9 | #Vcs-Browser: http://git.debian.org/?p=collab-maint/hello.git;a=summary 10 | 11 | Package: timeshift 12 | Architecture: any 13 | Depends: ${shlibs:Depends}, ${misc:Depends}, rsync, btrfs-progs | btrfs-tools 14 | #Recommends: 15 | Replaces: timeshift-btrfs 16 | Description: System restore utility 17 | Timeshift is a system restore utility which takes snapshots 18 | of the system at regular intervals. These snapshots can be restored 19 | at a later date to undo system changes. Creates incremental snapshots 20 | using rsync or BTRFS snapshots using BTRFS tools. 21 | -------------------------------------------------------------------------------- /debian/copyright: -------------------------------------------------------------------------------- 1 | Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ 2 | Upstream-Name: timeshift 3 | Source: 4 | 5 | Files: * 6 | Copyright: 2013 Tony George 7 | License: GPL-2+ 8 | This package is free software; you can redistribute it and/or modify 9 | it under the terms of the GNU General Public License as published by 10 | the Free Software Foundation; either version 2 of the License, or 11 | (at your option) any later version. 12 | . 13 | This package is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU General Public License for more details. 17 | . 18 | You should have received a copy of the GNU General Public License 19 | along with this program. If not, see 20 | . 21 | On Debian systems, the complete text of the GNU General 22 | Public License version 2 can be found in "/usr/share/common-licenses/GPL-2". 23 | 24 | -------------------------------------------------------------------------------- /debian/docs: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/teejee2008/timeshift/21570e2a2df6347843a099ea2de0da02a926aafe/debian/docs -------------------------------------------------------------------------------- /debian/rules: -------------------------------------------------------------------------------- 1 | #!/usr/bin/make -f 2 | # -*- makefile -*- 3 | # Sample debian/rules that uses debhelper. 4 | # This file was originally written by Joey Hess and Craig Small. 5 | # As a special exception, when this file is copied by dh-make into a 6 | # dh-make output file, you may use that output file without restriction. 7 | # This special exception was added by Craig Small in version 0.37 of dh-make. 8 | 9 | # Uncomment this to turn on verbose mode. 10 | #export DH_VERBOSE=1 11 | 12 | override_dh_usrlocal: 13 | 14 | %: 15 | dh $@ --with autotools-dev 16 | -------------------------------------------------------------------------------- /debian/source/format: -------------------------------------------------------------------------------- 1 | 3.0 (native) 2 | -------------------------------------------------------------------------------- /debian/timeshift.appdata.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | timeshift.desktop 5 | CC-BY-SA-3.0 6 | GPL-3.0 7 | Timeshift 8 | System restore utility for Linux 9 | Nástroj pro obnovení systému pro Linux 10 | Alat obnove sustava za Linux 11 | 12 | 13 |

14 | Timeshift protects your system by taking incremental snapshots of the file system at regular intervals. 15 | These snapshots can be restored at a later date to undo all changes to the system. 16 |

17 |

18 | Timeshift is designed to protect system files and settings. 19 | It is NOT a backup tool and is not meant to protect user data. 20 | Entire contents of users' home directories are excluded by default. 21 |

22 |
23 | 24 |

25 | Timeshift chrání systém pořizováním přírůstkových zachycených stavů souborového systému v pravidelných intervalech. 26 | Tyto zachycené stavy je možné později obnovit a vzít tak zpět veškeré změny v systému. 27 |

28 |

29 | Timeshift je navržen tak, aby chránil systémové soubory a nastavení. 30 | NEJEDNÁ se o zálohovací nástroj a není určen k ochraně dat uživatelů. 31 | Veškerý obsah domovských složek uživatelů je ve výchozím stavu vynecháván. 32 |

33 |
34 | 35 |

36 | Timeshift štiti vaš sustav stvaranjem pojedinačnih snimaka datotečnog sustava u redovitim vremenskim razmacima. 37 | Te snimke sustava kasnije se mogu obnoviti kako bi se poništile sve promjene načinjene u sustavu. 38 |

39 |

40 | Timeshift je dizajniran kako bi zaštitio vaše datoteke i postavke sustava. 41 | Timeshift nije alat za sigurnosno kopiranje i namjena mu nije zaštita korisničkih podataka. 42 | Sav sadržaj osobne mape korisnika po zadanome je izuzet. 43 |

44 |
45 | 46 | 47 | 48 | 49 | https://raw.githubusercontent.com/teejee2008/timeshift/master/images/main_window.png 50 | 51 | 52 | https://raw.githubusercontent.com/teejee2008/timeshift/master/images/settings_location.png 53 | 54 | 55 | https://raw.githubusercontent.com/teejee2008/timeshift/master/images/settings_schedule.png 56 | 57 | 58 | https://raw.githubusercontent.com/teejee2008/timeshift/master/images/settings_rsync.png 59 | 60 | 61 | https://raw.githubusercontent.com/teejee2008/timeshift/master/images/settings_btrfs.png 62 | 63 | 64 | https://raw.githubusercontent.com/teejee2008/timeshift/master/images/settings_users_rsync.png 65 | 66 | 67 | https://raw.githubusercontent.com/teejee2008/timeshift/master/images/settings_filters.png 68 | 69 | 70 | https://raw.githubusercontent.com/teejee2008/timeshift/master/images/restore_summary.png 71 | 72 | 73 | 74 | https://github.com/teejee2008/timeshift 75 |
76 | 77 | -------------------------------------------------------------------------------- /files/timeshift.json: -------------------------------------------------------------------------------- 1 | { 2 | "backup_device_uuid" : "", 3 | "parent_device_uuid" : "", 4 | "do_first_run" : "true", 5 | "btrfs_mode" : "false", 6 | "include_btrfs_home" : "false", 7 | "stop_cron_emails" : "true", 8 | "schedule_monthly" : "false", 9 | "schedule_weekly" : "false", 10 | "schedule_daily" : "false", 11 | "schedule_hourly" : "false", 12 | "schedule_boot" : "false", 13 | "count_monthly" : "2", 14 | "count_weekly" : "3", 15 | "count_daily" : "5", 16 | "count_hourly" : "6", 17 | "count_boot" : "5", 18 | "snapshot_size" : "0", 19 | "snapshot_count" : "0", 20 | "exclude" : [ 21 | ], 22 | "exclude-apps" : [ 23 | ] 24 | } 25 | -------------------------------------------------------------------------------- /icons/README: -------------------------------------------------------------------------------- 1 | Font is Mathjax_Fractur 2 | -------------------------------------------------------------------------------- /icons/timeshift.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/teejee2008/timeshift/21570e2a2df6347843a099ea2de0da02a926aafe/icons/timeshift.png -------------------------------------------------------------------------------- /icons/timeshift.xcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/teejee2008/timeshift/21570e2a2df6347843a099ea2de0da02a926aafe/icons/timeshift.xcf -------------------------------------------------------------------------------- /icons/timeshift_2_black.xcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/teejee2008/timeshift/21570e2a2df6347843a099ea2de0da02a926aafe/icons/timeshift_2_black.xcf -------------------------------------------------------------------------------- /icons/timeshift_2_black_bold.xcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/teejee2008/timeshift/21570e2a2df6347843a099ea2de0da02a926aafe/icons/timeshift_2_black_bold.xcf -------------------------------------------------------------------------------- /icons/timeshift_2_white.xcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/teejee2008/timeshift/21570e2a2df6347843a099ea2de0da02a926aafe/icons/timeshift_2_white.xcf -------------------------------------------------------------------------------- /icons/timeshift_2_white_bold.xcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/teejee2008/timeshift/21570e2a2df6347843a099ea2de0da02a926aafe/icons/timeshift_2_white_bold.xcf -------------------------------------------------------------------------------- /icons/timeshift_black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/teejee2008/timeshift/21570e2a2df6347843a099ea2de0da02a926aafe/icons/timeshift_black.png -------------------------------------------------------------------------------- /icons/timeshift_black_bold.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/teejee2008/timeshift/21570e2a2df6347843a099ea2de0da02a926aafe/icons/timeshift_black_bold.png -------------------------------------------------------------------------------- /icons/timeshift_white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/teejee2008/timeshift/21570e2a2df6347843a099ea2de0da02a926aafe/icons/timeshift_white.png -------------------------------------------------------------------------------- /icons/timeshift_white_bold.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/teejee2008/timeshift/21570e2a2df6347843a099ea2de0da02a926aafe/icons/timeshift_white_bold.png -------------------------------------------------------------------------------- /images/PayPal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/teejee2008/timeshift/21570e2a2df6347843a099ea2de0da02a926aafe/images/PayPal.png -------------------------------------------------------------------------------- /images/bitcoin_qr_code_timeshift.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/teejee2008/timeshift/21570e2a2df6347843a099ea2de0da02a926aafe/images/bitcoin_qr_code_timeshift.png -------------------------------------------------------------------------------- /images/main_window.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/teejee2008/timeshift/21570e2a2df6347843a099ea2de0da02a926aafe/images/main_window.png -------------------------------------------------------------------------------- /images/patreon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/teejee2008/timeshift/21570e2a2df6347843a099ea2de0da02a926aafe/images/patreon.png -------------------------------------------------------------------------------- /images/restore_summary.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/teejee2008/timeshift/21570e2a2df6347843a099ea2de0da02a926aafe/images/restore_summary.png -------------------------------------------------------------------------------- /images/settings_btrfs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/teejee2008/timeshift/21570e2a2df6347843a099ea2de0da02a926aafe/images/settings_btrfs.png -------------------------------------------------------------------------------- /images/settings_filters.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/teejee2008/timeshift/21570e2a2df6347843a099ea2de0da02a926aafe/images/settings_filters.png -------------------------------------------------------------------------------- /images/settings_location.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/teejee2008/timeshift/21570e2a2df6347843a099ea2de0da02a926aafe/images/settings_location.png -------------------------------------------------------------------------------- /images/settings_rsync.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/teejee2008/timeshift/21570e2a2df6347843a099ea2de0da02a926aafe/images/settings_rsync.png -------------------------------------------------------------------------------- /images/settings_schedule.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/teejee2008/timeshift/21570e2a2df6347843a099ea2de0da02a926aafe/images/settings_schedule.png -------------------------------------------------------------------------------- /images/settings_users.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/teejee2008/timeshift/21570e2a2df6347843a099ea2de0da02a926aafe/images/settings_users.png -------------------------------------------------------------------------------- /images/settings_users_btrfs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/teejee2008/timeshift/21570e2a2df6347843a099ea2de0da02a926aafe/images/settings_users_btrfs.png -------------------------------------------------------------------------------- /images/settings_users_rsync.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/teejee2008/timeshift/21570e2a2df6347843a099ea2de0da02a926aafe/images/settings_users_rsync.png -------------------------------------------------------------------------------- /makefile: -------------------------------------------------------------------------------- 1 | all: 2 | cd src; make all 3 | 4 | app-gtk: 5 | cd src; make app-gtk 6 | 7 | app-console: 8 | cd src; make app-console 9 | 10 | pot: 11 | cd src; make pot 12 | 13 | manpage: 14 | cd src; make manpage 15 | 16 | dist-release: 17 | build-release 18 | 19 | dist-publish: 20 | build-update-repo-debian 21 | build-update-repo-redhat 22 | build-update-repo-archlinux 23 | 24 | dist-upload: 25 | build-upload 26 | 27 | dist-deb: 28 | build-deb amd64 29 | 30 | clean: 31 | cd src; make clean 32 | 33 | install: 34 | cd src; make install 35 | 36 | uninstall: 37 | cd src; make uninstall 38 | -------------------------------------------------------------------------------- /makepot: -------------------------------------------------------------------------------- 1 | find . -name "*.vala" > POTFILES 2 | 3 | xgettext --language=Vala --keyword=_ --output=timeshift.pot --files-from=POTFILES 4 | xgettext --output=timeshift.pot --join-existing src/share/polkit-1/actions/*.policy 5 | 6 | rm POTFILES 7 | -------------------------------------------------------------------------------- /man/timeshift.1.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/teejee2008/timeshift/21570e2a2df6347843a099ea2de0da02a926aafe/man/timeshift.1.gz -------------------------------------------------------------------------------- /merge-launchpad-translations.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | backup=`pwd` 4 | DIR="$( cd "$( dirname "$0" )" && pwd )" 5 | cd "$DIR" 6 | 7 | . ./BUILD_CONFIG 8 | 9 | languages="am ar az bg ca cs da de el en_GB es et eu fi fr he hi hr hu ia id is it ko lt nb ne nl pl pt pt_BR ro ru sk sr sv tr uk vi zh_CN" 10 | 11 | echo "" 12 | echo "==========================================================================" 13 | echo " Update PO files in po/ with downloaded translations placed in po-lp/" 14 | echo "==========================================================================" 15 | echo "" 16 | 17 | for lang in $languages; do 18 | # remove headers in po-lp/*.po so that msgcat does not create malformed headers 19 | sed -i '/^#/d' po-lp/${app_name}-$lang.po 20 | msgcat -o po/${app_name}-$lang.po po-lp/${app_name}-$lang.po po/${app_name}-$lang.po 21 | sed -i '/#-#-#-#-#/d' po/${app_name}-$lang.po 22 | sed -i '/#, fuzzy/d' po/${app_name}-$lang.po 23 | done 24 | 25 | echo "" 26 | echo "==========================================================================" 27 | echo " Update PO files in po/ with latest POT file" 28 | echo "==========================================================================" 29 | echo "" 30 | 31 | for lang in $languages; do 32 | msgmerge --update -v po/${app_name}-$lang.po ${app_name}.pot 33 | done 34 | 35 | cd "$backup" 36 | -------------------------------------------------------------------------------- /release/sanity.config: -------------------------------------------------------------------------------- 1 | app_name: Timeshift 2 | depends_debian: libgee-0.8-2 libvte-2.91-0 libjson-glib-1.0-0 rsync 3 | depends_redhat: libgee vte291 json-glib rsync psmisc 4 | depends_arch: libgee vte3 json-glib rsync 5 | depends_generic: libgee libvte json-glib rsync 6 | assume_yes: 0 7 | exec_line: timeshift-launcher 8 | -------------------------------------------------------------------------------- /src/Core/Subvolume.vala: -------------------------------------------------------------------------------- 1 | /* 2 | * Subvolume.vala 3 | * 4 | * Copyright 2012-2018 Tony George 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 2 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program; if not, write to the Free Software 18 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, 19 | * MA 02110-1301, USA. 20 | * 21 | * 22 | */ 23 | 24 | using TeeJee.Logging; 25 | using TeeJee.FileSystem; 26 | using TeeJee.JsonHelper; 27 | using TeeJee.ProcessHelper; 28 | using TeeJee.GtkHelper; 29 | using TeeJee.System; 30 | using TeeJee.Misc; 31 | 32 | public class Subvolume : GLib.Object{ 33 | 34 | public string device_uuid; 35 | public string name = ""; 36 | public string path = ""; 37 | public long id = -1; 38 | public int64 total_bytes = 0; 39 | public int64 unshared_bytes = 0; 40 | 41 | public string mount_path = ""; 42 | 43 | //parent 44 | public SnapshotRepo? repo; 45 | 46 | public Subvolume(string name, string path, string parent_dev_uuid, SnapshotRepo? parent_repo){ 47 | 48 | this.name = name; 49 | this.path = path; 50 | this.device_uuid = parent_dev_uuid; 51 | this.repo = parent_repo; 52 | 53 | if (repo != null){ 54 | this.mount_path = repo.mount_paths[name]; 55 | } 56 | } 57 | 58 | public string total_formatted{ 59 | owned get{ 60 | return format_file_size(total_bytes); 61 | } 62 | } 63 | 64 | public string unshared_formatted{ 65 | owned get{ 66 | return format_file_size(unshared_bytes); 67 | } 68 | } 69 | 70 | public Device? get_device(){ 71 | 72 | return Device.get_device_by_uuid(device_uuid); 73 | } 74 | 75 | public bool exists_on_disk{ 76 | get { 77 | return dir_exists(path); 78 | } 79 | } 80 | 81 | public bool is_system_subvolume{ 82 | get { 83 | return (repo == null); 84 | } 85 | } 86 | 87 | public static Gee.HashMap detect_subvolumes_for_system_by_path( 88 | string system_path, SnapshotRepo? repo, Gtk.Window? parent_window){ 89 | 90 | var map = new Gee.HashMap(); 91 | 92 | log_debug("Searching subvolume for system at path: %s".printf(system_path)); 93 | 94 | var fstab = FsTabEntry.read_file(path_combine(system_path, "/etc/fstab")); 95 | var crypttab = CryptTabEntry.read_file(path_combine(system_path, "/etc/crypttab")); 96 | 97 | foreach(var item in fstab){ 98 | 99 | if (!item.is_for_system_directory()){ continue; } 100 | 101 | if (item.subvolume_name().length > 0){ 102 | 103 | var dev = item.resolve_device(crypttab, parent_window); 104 | var dev_name = (dev == null) ? "" : dev.device; 105 | var dev_uuid = (dev == null) ? "" : dev.uuid; 106 | 107 | log_debug("Found subvolume: %s, on device: %s".printf(item.subvolume_name(), dev_name)); 108 | 109 | var subvol = new Subvolume(item.subvolume_name(), item.mount_point, dev_uuid, repo); 110 | map.set(subvol.name, subvol); 111 | } 112 | } 113 | 114 | return map; 115 | } 116 | 117 | public void print_info(){ 118 | 119 | log_debug("name=%s, uuid=%s, id=%ld, path=%s".printf(name, device_uuid, id, path)); 120 | } 121 | 122 | // actions ---------------------------------- 123 | 124 | public bool remove(){ 125 | 126 | if (is_system_subvolume){ 127 | if (name == "@"){ 128 | path = path_combine(App.mount_point_app + "/backup", "@"); 129 | } 130 | else if (name == "@home"){ 131 | path = path_combine(App.mount_point_app + "/backup-home", "@home"); 132 | } 133 | } 134 | 135 | string cmd = ""; 136 | string std_out, std_err, subpath; 137 | int ret_val; 138 | 139 | if (!dir_exists(path)){ return true; } // ok, item does not exist 140 | 141 | log_msg("%s: %s (Id:%ld)".printf(_("Deleting subvolume"), name, id)); 142 | 143 | string options = App.use_option_raw ? "--commit-after" : ""; 144 | 145 | subpath = path_combine(path, name); 146 | if (dir_exists(subpath)) { // there is a nested subvol to remove first 147 | cmd = "btrfs subvolume delete %s '%s'".printf(options, subpath); 148 | log_debug("Deleting nested subvolume in snapshot"); 149 | log_debug(cmd); 150 | ret_val = exec_sync(cmd, out std_out, out std_err); 151 | if (ret_val != 0){ 152 | log_error(std_err); 153 | log_error(_("Failed to delete snapshot nested subvolume") + ": '%s'".printf(path)); 154 | return false; 155 | } 156 | } 157 | 158 | cmd = "btrfs subvolume delete %s '%s'".printf(options, path); 159 | log_debug(cmd); 160 | ret_val = exec_sync(cmd, out std_out, out std_err); 161 | if (ret_val != 0){ 162 | log_error(std_err); 163 | log_error(_("Failed to delete snapshot subvolume") + ": '%s'".printf(path)); 164 | return false; 165 | } 166 | 167 | log_msg("%s: %s (Id:%ld)\n".printf(_("Deleted subvolume"), name, id)); 168 | 169 | return true; 170 | } 171 | 172 | public bool restore(){ 173 | 174 | if (is_system_subvolume) { return false; } 175 | 176 | // restore snapshot subvolume by creating new subvolume snapshots ---------------------- 177 | 178 | string src_path = path; 179 | string dst_path = path_combine(mount_path, name); 180 | 181 | if (!dir_exists(src_path)){ 182 | log_error("%s: %s".printf(_("Not Found"), src_path)); 183 | return false; 184 | } 185 | 186 | if (dir_exists(dst_path)){ 187 | log_error("%s: %s".printf(_("Subvolume exists at destination"), dst_path)); 188 | return false; 189 | } 190 | 191 | string cmd = "btrfs subvolume snapshot '%s' '%s'".printf(src_path, dst_path); 192 | log_debug(cmd); 193 | 194 | string std_out, std_err; 195 | int status = exec_sync(cmd, out std_out, out std_err); 196 | 197 | if (status != 0){ 198 | log_error(std_err); 199 | log_error(_("btrfs returned an error") + ": %d".printf(status)); 200 | log_error(_("Failed to restore system subvolume") + ": %s".printf(name)); 201 | return false; 202 | } 203 | 204 | log_msg(_("Restored system subvolume") + ": %s".printf(name)); 205 | 206 | return true; 207 | } 208 | } 209 | -------------------------------------------------------------------------------- /src/Gtk/AppGtk.vala: -------------------------------------------------------------------------------- 1 | /* 2 | * AptikGtk.vala 3 | * 4 | * Copyright 2012-2018 Tony George 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 2 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program; if not, write to the Free Software 18 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, 19 | * MA 02110-1301, USA. 20 | * 21 | * 22 | */ 23 | 24 | using GLib; 25 | using Gtk; 26 | using Gee; 27 | using Json; 28 | 29 | using TeeJee.Logging; 30 | using TeeJee.FileSystem; 31 | using TeeJee.JsonHelper; 32 | using TeeJee.ProcessHelper; 33 | using TeeJee.GtkHelper; 34 | using TeeJee.System; 35 | using TeeJee.Misc; 36 | 37 | public Main App; 38 | public const string AppName = "Timeshift"; 39 | public const string AppShortName = "timeshift"; 40 | public const string AppVersion = "22.06.6"; 41 | public const string AppAuthor = "Tony George"; 42 | public const string AppAuthorEmail = "teejeetech@gmail.com"; 43 | 44 | const string GETTEXT_PACKAGE = ""; 45 | const string LOCALE_DIR = "/usr/share/locale"; 46 | 47 | extern void exit(int exit_code); 48 | 49 | public class AppGtk : GLib.Object { 50 | 51 | public static int main (string[] args) { 52 | 53 | set_locale(); 54 | 55 | Gtk.init(ref args); 56 | 57 | GTK_INITIALIZED = true; 58 | 59 | init_tmp(AppShortName); 60 | 61 | check_if_admin(); 62 | 63 | App = new Main(args, true); 64 | parse_arguments(args); 65 | App.initialize(); 66 | start_application(); 67 | 68 | App.exit_app(); 69 | 70 | return 0; 71 | } 72 | 73 | private static void set_locale() { 74 | 75 | log_debug("setting locale..."); 76 | Intl.setlocale(GLib.LocaleCategory.MESSAGES, "timeshift"); 77 | Intl.textdomain(GETTEXT_PACKAGE); 78 | Intl.bind_textdomain_codeset(GETTEXT_PACKAGE, "utf-8"); 79 | Intl.bindtextdomain(GETTEXT_PACKAGE, LOCALE_DIR); 80 | } 81 | 82 | public static bool parse_arguments(string[] args) { 83 | 84 | //parse options 85 | for (int k = 1; k < args.length; k++) // Oth arg is app path 86 | { 87 | switch (args[k].down()) { 88 | case "--debug": 89 | LOG_DEBUG = true; 90 | break; 91 | case "--help": 92 | case "--h": 93 | case "-h": 94 | log_msg(help_message()); 95 | exit(0); 96 | return true; 97 | default: 98 | //unknown option - show help and exit 99 | log_error(_("Unknown option") + ": %s".printf(args[k])); 100 | log_msg(help_message()); 101 | App.exit_app(1); 102 | return false; 103 | } 104 | } 105 | 106 | return true; 107 | } 108 | 109 | public static string help_message() { 110 | 111 | string msg = "\n%s v%s by Tony George (%s)\n".printf(AppName, AppVersion, AppAuthorEmail); 112 | msg += "\n"; 113 | msg += _("Syntax") + ": timeshift-gtk [options]\n"; 114 | msg += "\n"; 115 | msg += _("Options") + ":\n"; 116 | msg += "\n"; 117 | msg += " --debug " + _("Print debug information") + "\n"; 118 | msg += " --h[elp] " + _("Show all options") + "\n"; 119 | msg += "\n\n"; 120 | msg += "\n%s\n".printf(_("Run 'timeshift' for the command-line version of this tool")); 121 | return msg; 122 | } 123 | 124 | public static void check_if_admin(){ 125 | 126 | if (!user_is_admin()){ 127 | 128 | var msg = _("Admin access is required to backup and restore system files.") + "\n"; 129 | msg += _("Please re-run the application as admin (using 'sudo' or 'su')"); 130 | 131 | log_error(msg); 132 | 133 | string title = _("Admin Access Required"); 134 | gtk_messagebox(title, msg, null, true); 135 | 136 | exit(1); 137 | } 138 | } 139 | 140 | public static void start_application(){ 141 | 142 | // show main window 143 | var window = new MainWindow (); 144 | window.destroy.connect(Gtk.main_quit); 145 | window.show_all(); 146 | 147 | // start event loop 148 | Gtk.main(); 149 | } 150 | } 151 | 152 | -------------------------------------------------------------------------------- /src/Gtk/BackupFinishBox.vala: -------------------------------------------------------------------------------- 1 | /* 2 | * BackupFinishBox.vala 3 | * 4 | * Copyright 2016 Tony George 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 2 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program; if not, write to the Free Software 18 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, 19 | * MA 02110-1301, USA. 20 | * 21 | * 22 | */ 23 | 24 | using Gtk; 25 | using Gee; 26 | 27 | using TeeJee.Logging; 28 | using TeeJee.FileSystem; 29 | using TeeJee.JsonHelper; 30 | using TeeJee.ProcessHelper; 31 | using TeeJee.GtkHelper; 32 | using TeeJee.System; 33 | using TeeJee.Misc; 34 | 35 | class BackupFinishBox : Gtk.Box{ 36 | private Gtk.Label lbl_header; 37 | private Gtk.Label lbl_message; 38 | private Gtk.Window parent_window; 39 | 40 | public BackupFinishBox (Gtk.Window _parent_window) { 41 | 42 | log_debug("BackupFinishBox: BackupFinishBox()"); 43 | 44 | //base(Gtk.Orientation.VERTICAL, 6); // issue with vala 45 | GLib.Object(orientation: Gtk.Orientation.VERTICAL, spacing: 6); // work-around 46 | parent_window = _parent_window; 47 | margin = 12; 48 | 49 | 50 | lbl_header = add_label_header(this, _("Completed"), true); 51 | lbl_message = add_label_scrolled(this, "", false, true, 0); 52 | 53 | log_debug("BackupFinishBox: BackupFinishBox(): exit"); 54 | } 55 | 56 | public void update_message(bool success){ 57 | 58 | var txt = ""; 59 | 60 | txt = _("Snapshot Created"); 61 | 62 | if (!success){ 63 | txt += " " + _("With Errors"); 64 | } 65 | 66 | lbl_header.label = format_text(txt, true, false, true); 67 | 68 | var msg = ""; 69 | 70 | msg += _("Close window to exit") + "\n\n"; 71 | 72 | lbl_message.label = msg; 73 | } 74 | 75 | } 76 | -------------------------------------------------------------------------------- /src/Gtk/BootOptionsWindow.vala: -------------------------------------------------------------------------------- 1 | /* 2 | * BootOptionsWindow.vala 3 | * 4 | * Copyright 2012-2018 Tony George 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 2 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program; if not, write to the Free Software 18 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, 19 | * MA 02110-1301, USA. 20 | * 21 | * 22 | */ 23 | 24 | using Gtk; 25 | using Gee; 26 | 27 | using TeeJee.Logging; 28 | using TeeJee.FileSystem; 29 | using TeeJee.JsonHelper; 30 | using TeeJee.ProcessHelper; 31 | using TeeJee.GtkHelper; 32 | using TeeJee.System; 33 | using TeeJee.Misc; 34 | 35 | class BootOptionsWindow : Gtk.Window{ 36 | 37 | private Gtk.Box vbox_main; 38 | private Gtk.ButtonBox bbox_action; 39 | private BootOptionsBox boot_options_box; 40 | 41 | private uint tmr_init; 42 | private int def_width = 450; 43 | private int def_height = 500; 44 | 45 | public BootOptionsWindow() { 46 | 47 | log_debug("BootOptionsWindow: BootOptionsWindow()"); 48 | 49 | this.title = _("Bootloader Options"); 50 | this.window_position = WindowPosition.CENTER; 51 | this.modal = true; 52 | //this.set_default_size (def_width, def_height); 53 | this.icon = IconManager.lookup("timeshift",16); 54 | 55 | this.delete_event.connect(on_delete_event); 56 | 57 | // vbox_main 58 | vbox_main = new Gtk.Box(Orientation.VERTICAL, 6); 59 | vbox_main.margin = 12; 60 | add(vbox_main); 61 | 62 | boot_options_box = new BootOptionsBox(this); 63 | boot_options_box.margin = 0; 64 | vbox_main.add(boot_options_box); 65 | 66 | create_actions(); 67 | 68 | show_all(); 69 | 70 | tmr_init = Timeout.add(100, init_delayed); 71 | 72 | log_debug("BootOptionsWindow: BootOptionsWindow(): exit"); 73 | } 74 | 75 | private bool init_delayed(){ 76 | 77 | if (tmr_init > 0){ 78 | Source.remove(tmr_init); 79 | tmr_init = 0; 80 | } 81 | 82 | return false; 83 | } 84 | 85 | private bool on_delete_event(Gdk.EventAny event){ 86 | 87 | //save_changes(); 88 | 89 | return false; // close window 90 | } 91 | 92 | private void save_changes(){ 93 | //App.cron_job_update(); 94 | } 95 | 96 | private void create_actions(){ 97 | 98 | var hbox = new Gtk.ButtonBox (Gtk.Orientation.HORIZONTAL); 99 | hbox.margin = 0; 100 | hbox.margin_top = 24; 101 | vbox_main.add(hbox); 102 | 103 | var size_group = new Gtk.SizeGroup(SizeGroupMode.HORIZONTAL); 104 | 105 | // close 106 | var btn_close = add_button(hbox, _("Close"), "", size_group, null); 107 | //hbox.set_child_packing(btn_close, false, true, 6, Gtk.PackType.END); 108 | 109 | btn_close.clicked.connect(()=>{ 110 | save_changes(); 111 | this.destroy(); 112 | }); 113 | } 114 | 115 | } 116 | 117 | 118 | 119 | -------------------------------------------------------------------------------- /src/Gtk/DeleteBox.vala: -------------------------------------------------------------------------------- 1 | /* 2 | * DeleteBox.vala 3 | * 4 | * Copyright 2012-2018 Tony George 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 2 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program; if not, write to the Free Software 18 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, 19 | * MA 02110-1301, USA. 20 | * 21 | * 22 | */ 23 | 24 | using Gtk; 25 | using Gee; 26 | 27 | #if XAPP 28 | using XApp; 29 | #endif 30 | 31 | using TeeJee.Logging; 32 | using TeeJee.FileSystem; 33 | using TeeJee.JsonHelper; 34 | using TeeJee.ProcessHelper; 35 | using TeeJee.GtkHelper; 36 | using TeeJee.System; 37 | using TeeJee.Misc; 38 | 39 | class DeleteBox : Gtk.Box{ 40 | private Gtk.Spinner spinner; 41 | public Gtk.Label lbl_msg; 42 | public Gtk.Label lbl_status; 43 | public Gtk.Label lbl_remaining; 44 | public Gtk.ProgressBar progressbar; 45 | 46 | private Gtk.Window parent_window; 47 | 48 | public DeleteBox (Gtk.Window _parent_window) { 49 | 50 | log_debug("DeleteBox: DeleteBox()"); 51 | 52 | //base(Gtk.Orientation.VERTICAL, 6); // issue with vala 53 | GLib.Object(orientation: Gtk.Orientation.VERTICAL, spacing: 6); // work-around 54 | parent_window = _parent_window; 55 | margin = 12; 56 | 57 | // header 58 | add_label_header(this, _("Deleting Snapshots..."), true); 59 | 60 | var hbox_status = new Gtk.Box(Orientation.HORIZONTAL, 6); 61 | add (hbox_status); 62 | 63 | spinner = new Gtk.Spinner(); 64 | spinner.active = true; 65 | hbox_status.add(spinner); 66 | 67 | //lbl_msg 68 | lbl_msg = add_label(hbox_status, _("Preparing...")); 69 | lbl_msg.hexpand = true; 70 | lbl_msg.ellipsize = Pango.EllipsizeMode.END; 71 | lbl_msg.max_width_chars = 45; 72 | 73 | lbl_remaining = add_label(hbox_status, ""); 74 | 75 | //progressbar 76 | progressbar = new Gtk.ProgressBar(); 77 | //progressbar.set_size_request(-1,25); 78 | //progressbar.show_text = true; 79 | //progressbar.pulse_step = 0.1; 80 | add (progressbar); 81 | 82 | //lbl_status 83 | lbl_status = add_label(this, ""); 84 | lbl_status.ellipsize = Pango.EllipsizeMode.MIDDLE; 85 | lbl_status.max_width_chars = 45; 86 | lbl_status.margin_bottom = 12; 87 | 88 | log_debug("DeleteBox: DeleteBox(): exit"); 89 | } 90 | 91 | public bool delete_snapshots(){ 92 | 93 | log_debug("DeleteBox: delete_snapshots()"); 94 | 95 | if (!App.thread_delete_running){ 96 | App.delete_begin(); 97 | } 98 | 99 | if (App.btrfs_mode){ 100 | 101 | while (App.thread_delete_running){ 102 | 103 | lbl_msg.label = App.progress_text; 104 | gtk_do_events(); 105 | sleep(200); 106 | 107 | #if XAPP 108 | XApp.set_window_progress_pulse(parent_window, true); 109 | #endif 110 | } 111 | 112 | #if XAPP 113 | XApp.set_window_progress_pulse(parent_window, false); 114 | #endif 115 | } 116 | else{ 117 | 118 | int wait_interval_millis = 100; 119 | int status_line_counter = 0; 120 | int status_line_counter_default = 1000 / wait_interval_millis; 121 | string status_line = ""; 122 | string last_status_line = ""; 123 | int remaining_counter = 10; 124 | 125 | while (App.thread_delete_running){ 126 | 127 | status_line = escape_html(App.delete_file_task.status_line); 128 | 129 | if (status_line != last_status_line){ 130 | lbl_status.label = status_line; 131 | last_status_line = status_line; 132 | status_line_counter = status_line_counter_default; 133 | } 134 | else{ 135 | status_line_counter--; 136 | if (status_line_counter < 0){ 137 | status_line_counter = status_line_counter_default; 138 | lbl_status.label = ""; 139 | } 140 | } 141 | 142 | double fraction = App.delete_file_task.progress; 143 | 144 | // time remaining 145 | remaining_counter--; 146 | 147 | if (remaining_counter == 0){ 148 | 149 | lbl_remaining.label = App.delete_file_task.stat_time_remaining + " " + _("remaining"); 150 | 151 | remaining_counter = 10; 152 | } 153 | 154 | if (fraction < 0.99){ 155 | progressbar.fraction = fraction; 156 | 157 | #if XAPP 158 | XApp.set_window_progress(parent_window, (int)(fraction * 100.0)); 159 | #endif 160 | } 161 | 162 | lbl_msg.label = App.delete_file_task.status_message; 163 | 164 | gtk_do_events(); 165 | 166 | sleep(100); 167 | } 168 | 169 | #if XAPP 170 | XApp.set_window_progress(parent_window, 0); 171 | #endif 172 | } 173 | 174 | //parent_window.destroy(); 175 | 176 | return App.thread_delete_success; 177 | } 178 | } 179 | -------------------------------------------------------------------------------- /src/Gtk/DeleteFinishBox.vala: -------------------------------------------------------------------------------- 1 | /* 2 | * DeleteFinishBox.vala 3 | * 4 | * Copyright 2012-2018 Tony George 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 2 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program; if not, write to the Free Software 18 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, 19 | * MA 02110-1301, USA. 20 | * 21 | * 22 | */ 23 | 24 | using Gtk; 25 | using Gee; 26 | 27 | using TeeJee.Logging; 28 | using TeeJee.FileSystem; 29 | using TeeJee.JsonHelper; 30 | using TeeJee.ProcessHelper; 31 | using TeeJee.GtkHelper; 32 | using TeeJee.System; 33 | using TeeJee.Misc; 34 | 35 | class DeleteFinishBox : Gtk.Box{ 36 | private Gtk.Label lbl_header; 37 | private Gtk.Label lbl_message; 38 | private Gtk.Window parent_window; 39 | 40 | public DeleteFinishBox (Gtk.Window _parent_window) { 41 | 42 | log_debug("DeleteFinishBox: DeleteFinishBox()"); 43 | 44 | //base(Gtk.Orientation.VERTICAL, 6); // issue with vala 45 | GLib.Object(orientation: Gtk.Orientation.VERTICAL, spacing: 6); // work-around 46 | parent_window = _parent_window; 47 | margin = 12; 48 | 49 | 50 | lbl_header = add_label_header(this, _("Completed"), true); 51 | lbl_message = add_label_scrolled(this, "", false, true, 0); 52 | 53 | log_debug("DeleteFinishBox: DeleteFinishBox(): exit"); 54 | } 55 | 56 | public void update_message(bool success){ 57 | 58 | var txt = ""; 59 | 60 | txt = _("Snapshot(s) Deleted"); 61 | 62 | if (!success){ 63 | txt += " " + _("With Errors"); 64 | } 65 | 66 | lbl_header.label = format_text(txt, true, false, true); 67 | 68 | var msg = ""; 69 | 70 | msg += "\n"; 71 | msg += "" + _("Close window to exit") + "\n\n"; 72 | 73 | lbl_message.label = msg; 74 | } 75 | 76 | } 77 | -------------------------------------------------------------------------------- /src/Gtk/EstimateBox.vala: -------------------------------------------------------------------------------- 1 | /* 2 | * EstimateBox.vala 3 | * 4 | * Copyright 2012-2018 Tony George 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 2 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program; if not, write to the Free Software 18 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, 19 | * MA 02110-1301, USA. 20 | * 21 | * 22 | */ 23 | 24 | using Gtk; 25 | using Gee; 26 | 27 | #if XAPP 28 | using XApp; 29 | #endif 30 | 31 | using TeeJee.Logging; 32 | using TeeJee.FileSystem; 33 | using TeeJee.JsonHelper; 34 | using TeeJee.ProcessHelper; 35 | using TeeJee.GtkHelper; 36 | using TeeJee.System; 37 | using TeeJee.Misc; 38 | 39 | class EstimateBox : Gtk.Box{ 40 | 41 | private Gtk.ProgressBar progressbar; 42 | private Gtk.Window parent_window; 43 | 44 | private bool thread_is_running = false; 45 | 46 | public EstimateBox (Gtk.Window _parent_window) { 47 | 48 | log_debug("EstimateBox: EstimateBox()"); 49 | 50 | //base(Gtk.Orientation.VERTICAL, 6); // issue with vala 51 | GLib.Object(orientation: Gtk.Orientation.VERTICAL, spacing: 6); // work-around 52 | parent_window = _parent_window; 53 | margin = 12; 54 | 55 | // header 56 | add_label_header(this, _("Estimating System Size..."), true); 57 | 58 | var hbox_status = new Gtk.Box(Orientation.HORIZONTAL, 6); 59 | add (hbox_status); 60 | 61 | var spinner = new Gtk.Spinner(); 62 | spinner.active = true; 63 | hbox_status.add(spinner); 64 | 65 | //lbl_msg 66 | var lbl_msg = add_label(hbox_status, _("Please wait...")); 67 | lbl_msg.halign = Align.START; 68 | lbl_msg.ellipsize = Pango.EllipsizeMode.END; 69 | lbl_msg.max_width_chars = 50; 70 | 71 | //progressbar 72 | progressbar = new Gtk.ProgressBar(); 73 | //progressbar.set_size_request(-1,25); 74 | //progressbar.pulse_step = 0.1; 75 | add (progressbar); 76 | 77 | log_debug("EstimateBox: EstimateBox(): exit"); 78 | } 79 | 80 | public void estimate_system_size(){ 81 | 82 | if (Main.first_snapshot_size > 0){ 83 | log_debug("EstimateBox: size > 0"); 84 | return; 85 | } 86 | 87 | progressbar.fraction = 0.0; 88 | 89 | // start the estimation if not already running 90 | if (!App.thread_estimate_running){ 91 | 92 | log_debug("EstimateBox: thread started"); 93 | 94 | try { 95 | thread_is_running = true; 96 | Thread.create (estimate_system_size_thread, true); 97 | } 98 | catch (ThreadError e) { 99 | thread_is_running = false; 100 | log_error (e.message); 101 | } 102 | } 103 | 104 | // wait for completion and increment progressbar 105 | while (thread_is_running){ 106 | 107 | if (progressbar.fraction < 98.0){ 108 | 109 | progressbar.fraction += 0.005; 110 | 111 | #if XAPP 112 | XApp.set_window_progress(parent_window, (int)(progressbar.fraction * 100.0)); 113 | #endif 114 | } 115 | 116 | gtk_do_events(); 117 | sleep(100); 118 | } 119 | 120 | #if XAPP 121 | XApp.set_window_progress(parent_window, 0); 122 | #endif 123 | } 124 | 125 | private void estimate_system_size_thread(){ 126 | 127 | App.estimate_system_size(); 128 | log_debug("EstimateBox: thread finished"); 129 | thread_is_running = false; 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /src/Gtk/ExcludeAppsBox.vala: -------------------------------------------------------------------------------- 1 | /* 2 | * ExcludeAppsBox.vala 3 | * 4 | * Copyright 2012-2018 Tony George 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 2 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program; if not, write to the Free Software 18 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, 19 | * MA 02110-1301, USA. 20 | * 21 | * 22 | */ 23 | 24 | using Gtk; 25 | using Gee; 26 | 27 | using TeeJee.Logging; 28 | using TeeJee.FileSystem; 29 | using TeeJee.JsonHelper; 30 | using TeeJee.ProcessHelper; 31 | using TeeJee.GtkHelper; 32 | using TeeJee.System; 33 | using TeeJee.Misc; 34 | 35 | class ExcludeAppsBox : Gtk.Box{ 36 | private Gtk.TreeView treeview; 37 | private Gtk.Window parent_window; 38 | 39 | public ExcludeAppsBox (Gtk.Window _parent_window) { 40 | 41 | log_debug("ExcludeAppsBox: ExcludeAppsBox()"); 42 | 43 | //base(Gtk.Orientation.VERTICAL, 6); // issue with vala 44 | GLib.Object(orientation: Gtk.Orientation.VERTICAL, spacing: 6); // work-around 45 | parent_window = _parent_window; 46 | margin = 12; 47 | 48 | var box = new Gtk.Box(Gtk.Orientation.HORIZONTAL, 6); 49 | add(box); 50 | 51 | add_label_header(box, _("Exclude Application Settings"), true); 52 | 53 | //add_label(this, _("Selected items will be excluded")); 54 | 55 | var buffer = add_label(box, ""); 56 | buffer.hexpand = true; 57 | 58 | init_exclude_summary_link(box); 59 | 60 | init_treeview(); 61 | 62 | refresh_treeview(); 63 | 64 | log_debug("ExcludeAppsBox: ExcludeAppsBox(): exit"); 65 | } 66 | 67 | private void init_treeview(){ 68 | 69 | // treeview 70 | treeview = new TreeView(); 71 | treeview.get_selection().mode = SelectionMode.MULTIPLE; 72 | treeview.headers_visible = false; 73 | treeview.rules_hint = true; 74 | treeview.reorderable = true; 75 | treeview.set_tooltip_column(2); 76 | //treeview.row_activated.connect(treeview_row_activated); 77 | 78 | // scrolled 79 | var scrolled = new ScrolledWindow(null, null); 80 | scrolled.set_shadow_type (ShadowType.ETCHED_IN); 81 | scrolled.add (treeview); 82 | scrolled.expand = true; 83 | add(scrolled); 84 | 85 | // column 86 | var col = new TreeViewColumn(); 87 | col.expand = true; 88 | treeview.append_column(col); 89 | 90 | // margin 91 | var cell_text = new CellRendererText(); 92 | cell_text.text = ""; 93 | col.pack_start (cell_text, false); 94 | 95 | // toggle 96 | var cell_toggle = new CellRendererToggle(); 97 | cell_toggle.radio = false; 98 | cell_toggle.activatable = true; 99 | col.pack_start(cell_toggle, false); 100 | 101 | col.set_cell_data_func(cell_toggle, (cell_layout, cell, model, iter)=>{ 102 | AppExcludeEntry entry; 103 | model.get (iter, 0, out entry, -1); 104 | (cell as Gtk.CellRendererToggle).active = entry.enabled; 105 | }); 106 | 107 | cell_toggle.toggled.connect ((cell_toggle, path) => { 108 | var tree_path = new Gtk.TreePath.from_string (path); 109 | Gtk.TreeIter iter; 110 | var store = (Gtk.ListStore) treeview.model; 111 | store.get_iter(out iter, tree_path); 112 | 113 | AppExcludeEntry entry; 114 | store.get(iter, 0, out entry); 115 | entry.enabled = !cell_toggle.active; 116 | 117 | store.set(iter, 1, !cell_toggle.active); 118 | }); 119 | 120 | // pattern 121 | cell_text = new CellRendererText (); 122 | col.pack_start (cell_text, false); 123 | 124 | col.set_cell_data_func(cell_text, (cell_layout, cell, model, iter)=>{ 125 | 126 | AppExcludeEntry entry; 127 | model.get (iter, 0, out entry, -1); 128 | (cell as Gtk.CellRendererText).text = entry.name; 129 | }); 130 | } 131 | 132 | private void init_exclude_summary_link(Gtk.Box box){ 133 | 134 | var size_group = new Gtk.SizeGroup(SizeGroupMode.HORIZONTAL); 135 | var button = add_button(box, _("Summary"), "", size_group, null); 136 | button.clicked.connect(()=>{ 137 | new ExcludeListSummaryWindow(true); 138 | }); 139 | } 140 | 141 | // helpers 142 | 143 | public void refresh(){ 144 | 145 | refresh_treeview(); 146 | } 147 | 148 | public void refresh_treeview(){ 149 | 150 | var model = new Gtk.ListStore(3, typeof(AppExcludeEntry), typeof(bool), typeof(string)); 151 | treeview.model = model; 152 | 153 | foreach(var entry in App.exclude_list_apps){ 154 | TreeIter iter; 155 | model.append(out iter); 156 | model.set (iter, 0, entry, -1); 157 | model.set (iter, 1, entry.enabled, -1); 158 | model.set (iter, 2, entry.tooltip_text(), -1); 159 | } 160 | } 161 | } 162 | -------------------------------------------------------------------------------- /src/Gtk/ExcludeListSummaryWindow.vala: -------------------------------------------------------------------------------- 1 | /* 2 | * ExcludeListSummaryWindow.vala 3 | * 4 | * Copyright 2012-2018 Tony George 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 2 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program; if not, write to the Free Software 18 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, 19 | * MA 02110-1301, USA. 20 | * 21 | * 22 | */ 23 | 24 | using Gtk; 25 | using Gee; 26 | 27 | using TeeJee.Logging; 28 | using TeeJee.FileSystem; 29 | using TeeJee.JsonHelper; 30 | using TeeJee.ProcessHelper; 31 | using TeeJee.GtkHelper; 32 | using TeeJee.System; 33 | using TeeJee.Misc; 34 | 35 | class ExcludeListSummaryWindow : Gtk.Window{ 36 | 37 | private Gtk.Box vbox_main; 38 | private Gtk.Label lbl_list; 39 | private Gtk.Button btn_close; 40 | private bool for_restore = false; 41 | 42 | private int def_width = 500; 43 | private int def_height = 450; 44 | 45 | public ExcludeListSummaryWindow(bool _for_restore) { 46 | 47 | log_debug("ExcludeListSummaryWindow: ExcludeListSummaryWindow()"); 48 | 49 | this.title = _("Exclude List Summary"); 50 | this.window_position = WindowPosition.CENTER; 51 | this.modal = true; 52 | this.set_default_size (def_width, def_height); 53 | this.icon = IconManager.lookup("timeshift",16); 54 | 55 | for_restore = _for_restore; 56 | 57 | // vbox_main 58 | vbox_main = new Gtk.Box(Orientation.VERTICAL, 6); 59 | vbox_main.margin = 12; 60 | add(vbox_main); 61 | 62 | add_label(vbox_main, _("Files & directories matching the patterns below will be excluded. Patterns starting with a + will include the item instead of excluding.")); 63 | 64 | lbl_list = add_label_scrolled(vbox_main, "", true, true); 65 | 66 | create_actions(); 67 | 68 | refresh(); 69 | 70 | show_all(); 71 | 72 | log_debug("ExcludeListSummaryWindow: ExcludeListSummaryWindow(): exit"); 73 | } 74 | 75 | private void create_actions(){ 76 | 77 | var hbox = new Gtk.Box(Gtk.Orientation.HORIZONTAL, 6); 78 | vbox_main.add(hbox); 79 | var size_group = new Gtk.SizeGroup(SizeGroupMode.HORIZONTAL); 80 | 81 | // close 82 | 83 | var img = new Image.from_stock("gtk-ok", Gtk.IconSize.BUTTON); 84 | btn_close = add_button(hbox, _("OK"), "", size_group, img); 85 | 86 | btn_close.clicked.connect(()=>{ 87 | this.destroy(); 88 | }); 89 | } 90 | 91 | public void refresh(){ 92 | 93 | Gee.ArrayList list; 94 | 95 | if (for_restore){ 96 | list = App.create_exclude_list_for_restore(); 97 | } 98 | else{ 99 | list = App.create_exclude_list_for_backup(); 100 | } 101 | 102 | var txt = ""; 103 | foreach(var pattern in list){ 104 | if (pattern.strip().length > 0){ 105 | txt += "%s\n".printf(pattern); 106 | } 107 | } 108 | 109 | lbl_list.label = txt; 110 | } 111 | } 112 | 113 | 114 | 115 | 116 | 117 | -------------------------------------------------------------------------------- /src/Gtk/ExcludeMessageWindow.vala: -------------------------------------------------------------------------------- 1 | /* 2 | * ExcludeMessageWindow.vala 3 | * 4 | * Copyright 2012-2018 Tony George 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 2 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program; if not, write to the Free Software 18 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, 19 | * MA 02110-1301, USA. 20 | * 21 | * 22 | */ 23 | 24 | using Gtk; 25 | using Gee; 26 | 27 | using TeeJee.Logging; 28 | using TeeJee.FileSystem; 29 | using TeeJee.JsonHelper; 30 | using TeeJee.ProcessHelper; 31 | using TeeJee.GtkHelper; 32 | using TeeJee.System; 33 | using TeeJee.Misc; 34 | 35 | public class ExcludeMessageWindow : Gtk.Dialog{ 36 | 37 | private Gtk.Box vbox_main; 38 | private Gtk.Box hbox_action; 39 | 40 | //exclude 41 | private TreeView tv_exclude; 42 | private ScrolledWindow sw_exclude; 43 | private TreeViewColumn col_exclude; 44 | private Label lbl_header_exclude; 45 | private Label lbl_exclude; 46 | private Label lbl_header_home; 47 | private Label lbl_home; 48 | 49 | //actions 50 | private Button btn_ok; 51 | 52 | public ExcludeMessageWindow () { 53 | 54 | log_debug("ExcludeMessageWindow: ExcludeMessageWindow()"); 55 | 56 | this.title = _("Excluded Directories"); 57 | this.window_position = WindowPosition.CENTER_ON_PARENT; 58 | this.set_destroy_with_parent (true); 59 | this.set_modal (true); 60 | this.set_default_size (400, 400); 61 | 62 | string msg; 63 | 64 | //vbox_main 65 | vbox_main = get_content_area (); 66 | vbox_main.margin = 6; 67 | vbox_main.spacing = 6; 68 | 69 | //lbl_header_exclude 70 | lbl_header_exclude = new Gtk.Label("" + _("Exclude List") + ":"); 71 | lbl_header_exclude.xalign = (float) 0.0; 72 | lbl_header_exclude.set_use_markup(true); 73 | vbox_main.add(lbl_header_exclude); 74 | 75 | //lbl_exclude 76 | lbl_exclude = new Gtk.Label(_("Files matching the following patterns will be excluded") + ":"); 77 | lbl_exclude.xalign = (float) 0.0; 78 | lbl_exclude.set_use_markup(true); 79 | vbox_main.add(lbl_exclude); 80 | 81 | //tv_exclude----------------------------------------------- 82 | 83 | //tv_exclude 84 | tv_exclude = new TreeView(); 85 | tv_exclude.get_selection().mode = SelectionMode.MULTIPLE; 86 | tv_exclude.headers_visible = false; 87 | tv_exclude.set_rules_hint (true); 88 | 89 | //sw_exclude 90 | sw_exclude = new ScrolledWindow(null, null); 91 | sw_exclude.set_shadow_type (ShadowType.ETCHED_IN); 92 | sw_exclude.add (tv_exclude); 93 | sw_exclude.expand = true; 94 | vbox_main.add(sw_exclude); 95 | 96 | //col_exclude 97 | col_exclude = new TreeViewColumn(); 98 | col_exclude.title = _("File Pattern"); 99 | col_exclude.expand = true; 100 | 101 | CellRendererText cell_exclude_margin = new CellRendererText (); 102 | cell_exclude_margin.text = ""; 103 | col_exclude.pack_start (cell_exclude_margin, false); 104 | 105 | CellRendererPixbuf cell_exclude_icon = new CellRendererPixbuf (); 106 | col_exclude.pack_start (cell_exclude_icon, false); 107 | col_exclude.set_attributes(cell_exclude_icon, "icon-name", 1); 108 | 109 | CellRendererText cell_exclude_text = new CellRendererText (); 110 | col_exclude.pack_start (cell_exclude_text, false); 111 | col_exclude.set_cell_data_func (cell_exclude_text, cell_exclude_text_render); 112 | cell_exclude_text.foreground = "#222222"; 113 | tv_exclude.append_column(col_exclude); 114 | 115 | //lbl_header_home 116 | lbl_header_home = new Gtk.Label("" + _("Home Directory") + ":"); 117 | lbl_header_home.xalign = (float) 0.0; 118 | lbl_header_home.set_use_markup(true); 119 | lbl_header_home.margin_top = 6; 120 | vbox_main.add(lbl_header_home); 121 | 122 | //lbl_home 123 | lbl_home = new Gtk.Label(""); 124 | lbl_home.xalign = (float) 0.0; 125 | lbl_home.set_use_markup(true); 126 | lbl_home.wrap = true; 127 | vbox_main.add(lbl_home); 128 | 129 | msg = _("Hidden files and folders are included by default since they contain user-specific configuration files.") + "\n"; 130 | msg += _("All other files and folders are excluded.") + "\n"; 131 | lbl_home.label =msg; 132 | 133 | //Actions ---------------------------------------------- 134 | 135 | //hbox_action 136 | hbox_action = (Box) get_action_area (); 137 | 138 | //btn_ok 139 | btn_ok = new Button.from_stock("gtk-ok"); 140 | hbox_action.add(btn_ok); 141 | btn_ok.clicked.connect (btn_ok_clicked); 142 | 143 | //initialize ----------------------------------------- 144 | 145 | var model = new Gtk.ListStore(2, typeof(string), typeof(string)); 146 | tv_exclude.model = model; 147 | 148 | foreach(string path in App.exclude_list_default){ 149 | tv_exclude_add_item(path); 150 | } 151 | 152 | log_debug("ExcludeMessageWindow: ExcludeMessageWindow(): exit"); 153 | } 154 | 155 | private void cell_exclude_text_render (CellLayout cell_layout, CellRenderer cell, TreeModel model, TreeIter iter){ 156 | 157 | string pattern; 158 | model.get (iter, 0, out pattern, -1); 159 | (cell as Gtk.CellRendererText).text = pattern.has_prefix("+ ") ? pattern[2:pattern.length] : pattern; 160 | } 161 | 162 | private void tv_exclude_add_item(string path){ 163 | 164 | string icon_name = null; 165 | 166 | TreeIter iter; 167 | var model = (Gtk.ListStore) tv_exclude.model; 168 | model.append(out iter); 169 | 170 | if (path.has_prefix("+ ")){ 171 | icon_name = "item-blue"; 172 | } 173 | else{ 174 | icon_name = "item-gray"; 175 | } 176 | 177 | model.set (iter, 0, path, 1, icon_name); 178 | 179 | Adjustment adj = tv_exclude.get_hadjustment(); 180 | adj.value = adj.upper; 181 | } 182 | 183 | private void btn_ok_clicked(){ 184 | 185 | this.response(Gtk.ResponseType.OK); 186 | return; 187 | } 188 | } 189 | -------------------------------------------------------------------------------- /src/Gtk/FinishBox.vala: -------------------------------------------------------------------------------- 1 | /* 2 | * FinishBox.vala 3 | * 4 | * Copyright 2012-2018 Tony George 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 2 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program; if not, write to the Free Software 18 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, 19 | * MA 02110-1301, USA. 20 | * 21 | * 22 | */ 23 | 24 | using Gtk; 25 | using Gee; 26 | 27 | using TeeJee.Logging; 28 | using TeeJee.FileSystem; 29 | using TeeJee.JsonHelper; 30 | using TeeJee.ProcessHelper; 31 | using TeeJee.GtkHelper; 32 | using TeeJee.System; 33 | using TeeJee.Misc; 34 | 35 | class FinishBox : Gtk.Box{ 36 | 37 | private Gtk.Label lbl_header; 38 | private Gtk.Label lbl_message; 39 | 40 | private Gtk.Window parent_window; 41 | private bool show_notes = true; 42 | 43 | public FinishBox (Gtk.Window _parent_window, bool _show_notes) { 44 | 45 | log_debug("FinishBox: FinishBox()"); 46 | 47 | //base(Gtk.Orientation.VERTICAL, 6); // issue with vala 48 | GLib.Object(orientation: Gtk.Orientation.VERTICAL, spacing: 6); // work-around 49 | parent_window = _parent_window; 50 | margin = 12; 51 | show_notes = _show_notes; 52 | 53 | // header 54 | if (show_notes){ 55 | lbl_header = add_label_header(this, _("Notes"), true); 56 | } 57 | else{ 58 | lbl_header = add_label_header(this, _("Setup Complete"), true); 59 | } 60 | 61 | lbl_message = add_label_scrolled(this, "", false, true, 0); 62 | 63 | refresh(); 64 | 65 | log_debug("FinishBox: FinishBox(): exit"); 66 | } 67 | 68 | public void refresh(){ 69 | 70 | var msg = ""; 71 | string bullet = "• "; 72 | 73 | if (!show_notes){ 74 | if (App.scheduled){ 75 | msg += bullet + _("Scheduled snapshots are enabled. Snapshots will be created automatically for selected levels.") + "\n\n"; 76 | } 77 | else{ 78 | msg += bullet + _("Scheduled snapshots are disabled. It's recommended to enable it.") + "\n\n"; 79 | } 80 | } 81 | 82 | msg += bullet + _("System can be rolled-back to a previous date by restoring a snapshot.") + "\n\n"; 83 | 84 | if (App.btrfs_mode){ 85 | msg += bullet + _("Restoring a snapshot will replace system subvolumes, and system subvolumes currently in use will be preserved as a new snapshot. If required, this snapshot can be restored later to 'undo' the restore.") + "\n\n"; 86 | } 87 | else{ 88 | msg += bullet + _("Restoring snapshots only replaces system files and settings. Non-hidden files and directories in user home directories will not be touched. This behaviour can be changed by adding a filter to include these files. Included files will be backed up when snapshot is created, and replaced when snapshot is restored.") + "\n\n"; 89 | } 90 | 91 | if (App.btrfs_mode){ 92 | msg += bullet + _("BTRFS snapshots are saved on the same disk from which it is created. If the system disk fails, snapshots will be lost along with the system. Save snapshots to an external non-system disk in RSYNC mode to guard against disk failures.") + "\n\n"; 93 | } 94 | else{ 95 | msg += bullet + _("Save snapshots to an external disk instead of the system disk to guard against drive failures.") + "\n\n"; 96 | 97 | msg += bullet + _("Saving snapshots to a non-system disk allows you to format and re-install the OS on the system disk without losing snapshots stored on it. You can even install another Linux distribution and later roll-back the previous distribution by restoring a snapshot.") + "\n\n"; 98 | } 99 | 100 | msg += "\n"; 101 | msg += "" + _("Close window to exit") + "\n\n"; 102 | 103 | lbl_message.label = msg; 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /src/Gtk/MiscBox.vala: -------------------------------------------------------------------------------- 1 | /* 2 | * MiscBox.vala 3 | * 4 | * Copyright 2012-2018 Tony George 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 2 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program; if not, write to the Free Software 18 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, 19 | * MA 02110-1301, USA. 20 | * 21 | * 22 | */ 23 | 24 | using Gtk; 25 | using Gee; 26 | 27 | using TeeJee.Logging; 28 | using TeeJee.FileSystem; 29 | using TeeJee.JsonHelper; 30 | using TeeJee.ProcessHelper; 31 | using TeeJee.GtkHelper; 32 | using TeeJee.System; 33 | using TeeJee.Misc; 34 | 35 | class MiscBox : Gtk.Box{ 36 | 37 | private Gtk.Window parent_window; 38 | private bool restore_mode = false; 39 | 40 | public MiscBox (Gtk.Window _parent_window, bool _restore_mode) { 41 | 42 | log_debug("MiscBox: MiscBox()"); 43 | 44 | //base(Gtk.Orientation.VERTICAL, 6); // issue with vala 45 | GLib.Object(orientation: Gtk.Orientation.VERTICAL, spacing: 6); // work-around 46 | parent_window = _parent_window; 47 | margin = 12; 48 | 49 | restore_mode = _restore_mode; 50 | 51 | var vbox = new Gtk.Box(Gtk.Orientation.VERTICAL, 6); 52 | this.add(vbox); 53 | 54 | // ------------------------ 55 | 56 | init_date_format_option(vbox); 57 | 58 | refresh(); 59 | 60 | log_debug("MiscBox: MiscBox(): exit"); 61 | } 62 | 63 | private void init_date_format_option(Gtk.Box box){ 64 | 65 | log_debug("MiscBox: init_date_format_option()"); 66 | 67 | add_label_header(box, _("Date Format"), false); 68 | 69 | var hbox = new Gtk.Box(Gtk.Orientation.HORIZONTAL, 6); 70 | box.add(hbox); 71 | 72 | var combo = new Gtk.ComboBox(); 73 | combo.hexpand = true; 74 | hbox.add(combo); 75 | 76 | var entry = new Gtk.Entry(); 77 | entry.hexpand = true; 78 | hbox.add(entry); 79 | 80 | var cell_pix = new Gtk.CellRendererPixbuf(); 81 | combo.pack_start (cell_pix, false); 82 | 83 | var cell_text = new Gtk.CellRendererText(); 84 | cell_text.xalign = (float) 0.0; 85 | combo.pack_start (cell_text, false); 86 | 87 | var now = new DateTime.local(2019, 8, 11, 20, 25, 43); 88 | 89 | combo.set_cell_data_func(cell_text, (cell_layout, cell, model, iter)=>{ 90 | 91 | string txt; 92 | model.get (iter, 0, out txt, -1); 93 | 94 | (cell as Gtk.CellRendererText).text = (txt.length == 0) ? _("Custom") : now.format(txt); 95 | }); 96 | 97 | // populate combo 98 | var model = new Gtk.ListStore(1, typeof(string)); 99 | combo.model = model; 100 | 101 | int active = -1; 102 | int index = -1; 103 | TreeIter iter; 104 | foreach(var fmt in new string[]{ 105 | "", // custom 106 | "%Y-%m-%d %H:%M:%S", // 2019-08-11 20:00:00 107 | "%Y-%m-%d %I:%M %p", // 2019-08-11 08:00 PM 108 | "%d %b %Y %I:%M %p", // 11 Aug 2019 08:00 PM 109 | "%Y %b %d, %I:%M %p", // 2019 Aug 11, 08:00 PM 110 | "%c" // Sunday, 11 August 2019 08:00:00 PM IST 111 | }){ 112 | 113 | index++; 114 | model.append(out iter); 115 | model.set(iter, 0, fmt); 116 | 117 | if (App.date_format == fmt){ 118 | active = index; 119 | } 120 | } 121 | 122 | if (active < 0){ 123 | active = 0; 124 | } 125 | 126 | combo.active = active; 127 | 128 | combo.changed.connect((path) => { 129 | 130 | TreeIter iter_active; 131 | bool selected = combo.get_active_iter(out iter_active); 132 | if (!selected){ return; } 133 | 134 | TreeIter iter_combo; 135 | var store = (Gtk.ListStore) combo.model; 136 | 137 | string txt; 138 | model.get (iter_active, 0, out txt, -1); 139 | 140 | string fmt = Main.date_format_default; 141 | if (txt.length > 0){ 142 | fmt = txt; 143 | } 144 | 145 | entry.text = fmt; 146 | 147 | entry.sensitive = (txt.length == 0); 148 | 149 | App.date_format = fmt; 150 | }); 151 | 152 | entry.text = App.date_format; 153 | 154 | entry.sensitive = (combo.active == 0); 155 | 156 | entry.focus_out_event.connect((entry1, event1) => { 157 | App.date_format = entry.text; 158 | log_debug("saved date_format: %s".printf(App.date_format)); 159 | return false; 160 | }); 161 | 162 | show_all(); 163 | 164 | log_debug("MiscBox: init_date_format_option(): exit"); 165 | } 166 | 167 | // helpers 168 | 169 | public void refresh(){ 170 | 171 | if (App.btrfs_mode){ 172 | 173 | //chk_include_btrfs_home.active = App.include_btrfs_home_for_restore; 174 | } 175 | else{ 176 | 177 | //chk_include_btrfs_home 178 | } 179 | 180 | show_all(); 181 | } 182 | } 183 | -------------------------------------------------------------------------------- /src/Gtk/RestoreExcludeBox.vala: -------------------------------------------------------------------------------- 1 | /* 2 | * RestoreExcludeBox.vala 3 | * 4 | * Copyright 2012-2018 Tony George 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 2 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program; if not, write to the Free Software 18 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, 19 | * MA 02110-1301, USA. 20 | * 21 | * 22 | */ 23 | 24 | using Gtk; 25 | using Gee; 26 | 27 | using TeeJee.Logging; 28 | using TeeJee.FileSystem; 29 | using TeeJee.JsonHelper; 30 | using TeeJee.ProcessHelper; 31 | using TeeJee.GtkHelper; 32 | using TeeJee.System; 33 | using TeeJee.Misc; 34 | 35 | class RestoreExcludeBox : Gtk.Box{ 36 | 37 | //private Gtk.CheckButton chk_web; 38 | private Gtk.CheckButton chk_other; 39 | private Gtk.CheckButton chk_web; 40 | private Gtk.CheckButton chk_torrent; 41 | 42 | private Gtk.Window parent_window; 43 | 44 | public RestoreExcludeBox (Gtk.Window _parent_window) { 45 | 46 | log_debug("RestoreExcludeBox: RestoreExcludeBox()"); 47 | 48 | //base(Gtk.Orientation.VERTICAL, 6); // issue with vala 49 | GLib.Object(orientation: Gtk.Orientation.VERTICAL, spacing: 6); // work-around 50 | parent_window = _parent_window; 51 | margin = 12; 52 | 53 | // app settings header -------------------------------- 54 | 55 | var label = add_label_header(this, _("Exclude Application Settings"), true); 56 | 57 | add_label(this, _("Select applications to exclude from restore")); 58 | 59 | // web browsers -------------------------------- 60 | 61 | var chk = add_checkbox(this, _("Web Browsers") + " (%s)".printf(_("Recommended"))); 62 | chk.active = true; 63 | chk.margin_top = 12; 64 | chk.margin_bottom = 0; 65 | chk_web = chk; 66 | 67 | chk.toggled.connect(()=>{ 68 | foreach(var name in new string[]{ 69 | "chromium", "google-chrome", "mozilla", "midori", "epiphany", 70 | "opera", "opera-stable", "opera-beta", "opera-developer" }){ 71 | if (AppExcludeEntry.app_map.has_key(name)){ 72 | AppExcludeEntry.app_map[name].enabled = chk_web.active; 73 | } 74 | } 75 | }); 76 | 77 | label = add_label( 78 | this, _("Firefox, Chromium, Chrome, Opera, Epiphany, Midori"), false, true); 79 | label.margin_top = 0; 80 | label.margin_bottom = 6; 81 | label.margin_left = 24; 82 | label.wrap = true; 83 | label.wrap_mode = Pango.WrapMode.WORD_CHAR; 84 | 85 | var tt = _("Keep configuration files for web browsers like Firefox and Chrome. If un-checked, previous configuration files will be restored from snapshot"); 86 | chk.set_tooltip_text(tt); 87 | label.set_tooltip_text(tt); 88 | 89 | // torrent clients ---------------------------- 90 | 91 | chk = add_checkbox(this, _("Bittorrent Clients") + " (%s)".printf(_("Recommended"))); 92 | chk.active = true; 93 | chk.margin_bottom = 0; 94 | chk_torrent = chk; 95 | 96 | chk.toggled.connect(()=>{ 97 | foreach(var name in new string[]{ 98 | "deluge", "transmission" }){ 99 | if (AppExcludeEntry.app_map.has_key(name)){ 100 | AppExcludeEntry.app_map[name].enabled = chk_torrent.active; 101 | } 102 | } 103 | }); 104 | 105 | label = add_label(this, _("Deluge, Transmission"), false, true); 106 | label.margin_top = 0; 107 | label.margin_bottom = 6; 108 | label.margin_left = 24; 109 | label.wrap = true; 110 | label.wrap_mode = Pango.WrapMode.WORD_CHAR; 111 | 112 | tt = _("Keep configuration files for bittorrent clients like Deluge, Transmission, etc. If un-checked, previous configuration files will be restored from snapshot."); 113 | chk.set_tooltip_text(tt); 114 | label.set_tooltip_text(tt); 115 | 116 | // all apps ---------------------------- 117 | 118 | chk = add_checkbox(this, _("Other applications (next page)")); 119 | chk_other = chk; 120 | 121 | tt = _("Show more applications to exclude on the next page"); 122 | chk.set_tooltip_text(tt); 123 | 124 | // TODO: medium: add more exclude options 125 | 126 | log_debug("RestoreExcludeBox: RestoreExcludeBox(): exit"); 127 | } 128 | 129 | public void refresh(){ 130 | 131 | chk_web.toggled(); 132 | chk_torrent.toggled(); 133 | } 134 | 135 | public bool show_all_apps(){ 136 | 137 | return chk_other.active; 138 | } 139 | } 140 | -------------------------------------------------------------------------------- /src/Gtk/RestoreFinishBox.vala: -------------------------------------------------------------------------------- 1 | /* 2 | * RestoreFinishBox.vala 3 | * 4 | * Copyright 2012-2018 Tony George 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 2 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program; if not, write to the Free Software 18 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, 19 | * MA 02110-1301, USA. 20 | * 21 | * 22 | */ 23 | 24 | using Gtk; 25 | using Gee; 26 | 27 | using TeeJee.Logging; 28 | using TeeJee.FileSystem; 29 | using TeeJee.JsonHelper; 30 | using TeeJee.ProcessHelper; 31 | using TeeJee.GtkHelper; 32 | using TeeJee.System; 33 | using TeeJee.Misc; 34 | 35 | class RestoreFinishBox : Gtk.Box{ 36 | private Gtk.Label lbl_header; 37 | private Gtk.Label lbl_message; 38 | private Gtk.Window parent_window; 39 | 40 | public RestoreFinishBox (Gtk.Window _parent_window) { 41 | 42 | log_debug("RestoreFinishBox: RestoreFinishBox()"); 43 | 44 | //base(Gtk.Orientation.VERTICAL, 6); // issue with vala 45 | GLib.Object(orientation: Gtk.Orientation.VERTICAL, spacing: 6); // work-around 46 | parent_window = _parent_window; 47 | margin = 12; 48 | 49 | 50 | lbl_header = add_label_header(this, _("Completed"), true); 51 | lbl_message = add_label_scrolled(this, "", false, true, 0); 52 | 53 | log_debug("RestoreFinishBox: RestoreFinishBox(): exit"); 54 | } 55 | 56 | public void update_message(bool success, string message_header, string message_body){ 57 | 58 | // header ----------------------------------------- 59 | 60 | var txt = ""; 61 | 62 | if (message_header.length > 0){ 63 | 64 | txt = message_header; 65 | } 66 | else{ 67 | if (App.mirror_system){ 68 | txt = _("Cloning"); 69 | } 70 | else{ 71 | txt = _("Restore"); 72 | } 73 | 74 | if (success){ 75 | txt += " " + _("Completed"); 76 | } 77 | else{ 78 | txt += " " + _("Completed With Errors"); 79 | } 80 | } 81 | 82 | lbl_header.label = format_text(txt, true, false, true); 83 | 84 | // body ------------------------------------------------------ 85 | 86 | var msg = ""; 87 | 88 | if (message_body.length > 0){ 89 | msg = message_body; 90 | } 91 | else { 92 | 93 | string bullet = "• "; 94 | 95 | if (App.btrfs_mode && App.restore_current_system){ 96 | msg += bullet + _("Restored subvolumes will become active after system is restarted.") + "\n"; 97 | msg += "\n"; 98 | msg += bullet + _("You can continue working on the current system. After restart, the current system will be visible as a new snapshot. This snapshot can be restored later if required, to 'undo' the restore.") + "\n"; 99 | } 100 | 101 | if (!App.btrfs_mode){ 102 | msg += bullet + _("If the restored system fails to boot, then boot from the Live CD/USB, install Timeshift, and try restoring another snapshot.") + "\n\n"; 103 | } 104 | 105 | msg += "\n"; 106 | msg += "" + _("Close window to exit") + "\n\n"; 107 | } 108 | 109 | lbl_message.label = msg; 110 | } 111 | 112 | } 113 | -------------------------------------------------------------------------------- /src/Gtk/RestoreSummaryBox.vala: -------------------------------------------------------------------------------- 1 | /* 2 | * RestoreSummaryBox.vala 3 | * 4 | * Copyright 2012-2018 Tony George 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 2 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program; if not, write to the Free Software 18 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, 19 | * MA 02110-1301, USA. 20 | * 21 | * 22 | */ 23 | 24 | using Gtk; 25 | using Gee; 26 | 27 | using TeeJee.Logging; 28 | using TeeJee.FileSystem; 29 | using TeeJee.JsonHelper; 30 | using TeeJee.ProcessHelper; 31 | using TeeJee.GtkHelper; 32 | using TeeJee.System; 33 | using TeeJee.Misc; 34 | 35 | class RestoreSummaryBox : Gtk.Box{ 36 | 37 | public Gtk.Label lbl_devices; 38 | public Gtk.Label lbl_reboot; 39 | public Gtk.Label lbl_disclaimer; 40 | private Gtk.Window parent_window; 41 | 42 | public RestoreSummaryBox (Gtk.Window _parent_window) { 43 | 44 | log_debug("RestoreSummaryBox: RestoreSummaryBox()"); 45 | 46 | //base(Gtk.Orientation.VERTICAL, 6); // issue with vala 47 | GLib.Object(orientation: Gtk.Orientation.VERTICAL, spacing: 6); // work-around 48 | parent_window = _parent_window; 49 | margin = 12; 50 | 51 | // devices 52 | 53 | add_label_header(this, _("Warning"), true); 54 | 55 | lbl_devices = add_label(this, "", false, false, false); 56 | 57 | // reboot warning 58 | 59 | lbl_reboot = add_label(this, "", true, false, false); 60 | lbl_reboot.margin_bottom = 6; 61 | 62 | // disclaimer 63 | 64 | add_label_header(this, _("Disclaimer"), true); 65 | 66 | lbl_disclaimer = add_label(this, "", false, false, false); 67 | 68 | 69 | log_debug("RestoreSummaryBox: RestoreSummaryBox(): exit"); 70 | } 71 | 72 | public void refresh(){ 73 | 74 | string msg_devices = ""; 75 | string msg_reboot = ""; 76 | string msg_disclaimer = ""; 77 | 78 | App.get_restore_messages( 79 | true, out msg_devices, out msg_reboot, 80 | out msg_disclaimer); 81 | 82 | lbl_devices.label = msg_devices; 83 | lbl_reboot.label = msg_reboot; 84 | lbl_disclaimer.label = msg_disclaimer; 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /src/Gtk/RsyncLogWindow.vala: -------------------------------------------------------------------------------- 1 | /* 2 | * RsyncLogWindow.vala 3 | * 4 | * Copyright 2012-2018 Tony George 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 2 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program; if not, write to the Free Software 18 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, 19 | * MA 02110-1301, USA. 20 | * 21 | * 22 | */ 23 | 24 | 25 | using Gtk; 26 | using Gee; 27 | 28 | using TeeJee.Logging; 29 | using TeeJee.FileSystem; 30 | using TeeJee.JsonHelper; 31 | using TeeJee.ProcessHelper; 32 | using TeeJee.GtkHelper; 33 | using TeeJee.System; 34 | using TeeJee.Misc; 35 | 36 | public class RsyncLogWindow : Window { 37 | 38 | //window 39 | private int def_width = 800; 40 | private int def_height = 600; 41 | 42 | private RsyncLogBox logbox; 43 | private string rsync_log_file; 44 | 45 | public RsyncLogWindow(string _rsync_log_file) { 46 | 47 | log_debug("RsyncLogWindow: RsyncLogWindow()"); 48 | 49 | this.title = _("Rsync Log Viewer"); 50 | this.window_position = Gtk.WindowPosition.CENTER_ON_PARENT; 51 | this.set_default_size(def_width, def_height); 52 | this.icon = IconManager.lookup("timeshift",16); 53 | this.resizable = true; 54 | this.modal = true; 55 | 56 | this.delete_event.connect(on_delete_event); 57 | 58 | rsync_log_file = _rsync_log_file; 59 | 60 | logbox = new RsyncLogBox(this); 61 | this.add(logbox); 62 | 63 | show_all(); 64 | 65 | logbox.open_log(rsync_log_file); 66 | 67 | log_debug("RsyncLogWindow: RsyncLogWindow(): exit"); 68 | } 69 | 70 | private bool on_delete_event(Gdk.EventAny event){ 71 | 72 | if (logbox.is_running){ 73 | return true; // keep window open 74 | } 75 | else{ 76 | this.delete_event.disconnect(on_delete_event); //disconnect this handler 77 | return false; // close window 78 | } 79 | } 80 | 81 | } 82 | -------------------------------------------------------------------------------- /src/Gtk/SettingsWindow.vala: -------------------------------------------------------------------------------- 1 | /* 2 | * SettingsWindow.vala 3 | * 4 | * Copyright 2012-2018 Tony George 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 2 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program; if not, write to the Free Software 18 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, 19 | * MA 02110-1301, USA. 20 | * 21 | * 22 | */ 23 | 24 | using Gtk; 25 | using Gee; 26 | 27 | using TeeJee.Logging; 28 | using TeeJee.FileSystem; 29 | using TeeJee.JsonHelper; 30 | using TeeJee.ProcessHelper; 31 | using TeeJee.GtkHelper; 32 | using TeeJee.System; 33 | using TeeJee.Misc; 34 | 35 | class SettingsWindow : Gtk.Window{ 36 | 37 | private Gtk.Box vbox_main; 38 | private Gtk.StackSwitcher switcher; 39 | private Gtk.Stack stack; 40 | 41 | private SnapshotBackendBox backend_box; 42 | private BackupDeviceBox backup_dev_box; 43 | private ScheduleBox schedule_box; 44 | private ExcludeBox exclude_box; 45 | private UsersBox users_box; 46 | private MiscBox misc_box; 47 | 48 | private uint tmr_init; 49 | private int def_width = 500; 50 | private int def_height = 500; 51 | 52 | public SettingsWindow() { 53 | 54 | log_debug("SettingsWindow: SettingsWindow()"); 55 | 56 | this.title = _("Settings"); 57 | this.window_position = WindowPosition.CENTER; 58 | this.modal = true; 59 | //this.set_default_size (def_width, def_height); 60 | this.icon = IconManager.lookup("timeshift",16); 61 | 62 | this.delete_event.connect(on_delete_event); 63 | 64 | vbox_main = new Gtk.Box(Orientation.VERTICAL, 0); 65 | vbox_main.set_size_request(def_width, def_height); 66 | add(vbox_main); 67 | 68 | this.resize(def_width, def_height); 69 | 70 | var hbox = new Gtk.ButtonBox (Gtk.Orientation.HORIZONTAL); 71 | hbox.set_layout (Gtk.ButtonBoxStyle.CENTER); 72 | hbox.get_style_context().add_class(Gtk.STYLE_CLASS_PRIMARY_TOOLBAR); 73 | vbox_main.add(hbox); 74 | 75 | switcher = new Gtk.StackSwitcher(); 76 | switcher.margin = 6; 77 | hbox.add (switcher); 78 | 79 | stack = new Gtk.Stack(); 80 | stack.set_transition_duration(100); 81 | stack.set_transition_type(Gtk.StackTransitionType.SLIDE_LEFT_RIGHT); 82 | vbox_main.add(stack); 83 | 84 | switcher.set_stack(stack); 85 | 86 | backend_box = new SnapshotBackendBox(this); 87 | stack.add_titled (backend_box, "type", _("Type")); 88 | 89 | backup_dev_box = new BackupDeviceBox(this); 90 | stack.add_titled (backup_dev_box, "location", _("Location")); 91 | 92 | schedule_box = new ScheduleBox(this); 93 | stack.add_titled (schedule_box, "schedule", _("Schedule")); 94 | 95 | exclude_box = new ExcludeBox(this); 96 | users_box = new UsersBox(this, exclude_box, false); 97 | exclude_box.set_users_box(users_box); 98 | 99 | misc_box = new MiscBox(this, false); 100 | 101 | stack.add_titled (users_box, "users", _("Users")); 102 | 103 | stack.add_titled (exclude_box, "filters", _("Filters")); 104 | 105 | stack.add_titled (misc_box, "misc", _("Misc")); 106 | 107 | backend_box.type_changed.connect(()=>{ 108 | exclude_box.visible = !App.btrfs_mode; 109 | backup_dev_box.refresh(); 110 | users_box.refresh(); 111 | }); 112 | 113 | stack.set_visible_child_name("type"); 114 | 115 | //var hbox = new Gtk.ButtonBox(Gtk.Orientation.HORIZONTAL); 116 | var bbox = new Gtk.ButtonBox(Gtk.Orientation.HORIZONTAL); 117 | vbox_main.add(bbox); 118 | 119 | #if GTK3_18 120 | bbox.set_layout (Gtk.ButtonBoxStyle.CENTER); 121 | #endif 122 | 123 | var btn_ok = new Button.with_label(_("OK")); 124 | btn_ok.margin = 12; 125 | btn_ok.set_size_request(100, -1); 126 | bbox.add(btn_ok); 127 | 128 | btn_ok.clicked.connect(()=>{ 129 | save_changes(); 130 | this.destroy(); 131 | }); 132 | 133 | show_all(); 134 | 135 | tmr_init = Timeout.add(100, init_delayed); 136 | 137 | log_debug("SettingsWindow: SettingsWindow(): exit"); 138 | } 139 | 140 | private bool init_delayed(){ 141 | 142 | if (tmr_init > 0){ 143 | Source.remove(tmr_init); 144 | tmr_init = 0; 145 | } 146 | 147 | backend_box.refresh(); 148 | stack.set_visible_child_name("type"); 149 | 150 | this.resize(def_width, def_height); 151 | 152 | //backup_dev_box.refresh(); //will be triggerred indirectly 153 | 154 | return false; 155 | } 156 | 157 | private bool on_delete_event(Gdk.EventAny event){ 158 | 159 | save_changes(); 160 | 161 | return false; // close window 162 | } 163 | 164 | private void save_changes(){ 165 | 166 | exclude_box.save_changes(); 167 | 168 | App.cron_job_update(); 169 | 170 | //App.check_encrypted_home(this); 171 | 172 | //App.check_encrypted_private_dirs(this); 173 | } 174 | 175 | public enum Tabs{ 176 | BACKUP_TYPE = 0, 177 | BACKUP_DEVICE = 1, 178 | SCHEDULE = 2, 179 | EXCLUDE = 3//, 180 | //NOTES = 4 181 | } 182 | } 183 | 184 | 185 | 186 | -------------------------------------------------------------------------------- /src/Gtk/SnapshotBackendBox.vala: -------------------------------------------------------------------------------- 1 | /* 2 | * SnapshotBackendBox.vala 3 | * 4 | * Copyright 2012-2018 Tony George 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 2 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program; if not, write to the Free Software 18 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, 19 | * MA 02110-1301, USA. 20 | * 21 | * 22 | */ 23 | 24 | using Gtk; 25 | using Gee; 26 | 27 | using TeeJee.Logging; 28 | using TeeJee.FileSystem; 29 | using TeeJee.JsonHelper; 30 | using TeeJee.ProcessHelper; 31 | using TeeJee.GtkHelper; 32 | using TeeJee.System; 33 | using TeeJee.Misc; 34 | 35 | class SnapshotBackendBox : Gtk.Box{ 36 | 37 | private Gtk.RadioButton opt_rsync; 38 | private Gtk.RadioButton opt_btrfs; 39 | private Gtk.Label lbl_description; 40 | private Gtk.Window parent_window; 41 | 42 | public signal void type_changed(); 43 | 44 | public SnapshotBackendBox (Gtk.Window _parent_window) { 45 | 46 | log_debug("SnapshotBackendBox: SnapshotBackendBox()"); 47 | 48 | //base(Gtk.Orientation.VERTICAL, 6); // issue with vala 49 | GLib.Object(orientation: Gtk.Orientation.VERTICAL, spacing: 6); // work-around 50 | parent_window = _parent_window; 51 | margin = 12; 52 | 53 | build_ui(); 54 | 55 | refresh(); 56 | 57 | log_debug("SnapshotBackendBox: SnapshotBackendBox(): exit"); 58 | } 59 | 60 | private void build_ui(){ 61 | 62 | add_label_header(this, _("Select Snapshot Type"), true); 63 | 64 | var vbox = new Gtk.Box(Gtk.Orientation.VERTICAL, 6); 65 | //hbox.homogeneous = true; 66 | add(vbox); 67 | 68 | add_opt_rsync(vbox); 69 | 70 | add_opt_btrfs(vbox); 71 | 72 | add_description(); 73 | } 74 | 75 | private void add_opt_rsync(Gtk.Box hbox){ 76 | 77 | var opt = new RadioButton.with_label_from_widget(null, _("RSYNC")); 78 | opt.set_tooltip_markup(_("Create snapshots using RSYNC tool and hard-links")); 79 | hbox.add (opt); 80 | opt_rsync = opt; 81 | 82 | opt_rsync.toggled.connect(()=>{ 83 | if (opt_rsync.active){ 84 | App.btrfs_mode = false; 85 | Main.first_snapshot_size = 0; 86 | init_backend(); 87 | type_changed(); 88 | update_description(); 89 | } 90 | }); 91 | } 92 | 93 | private void add_opt_btrfs(Gtk.Box hbox){ 94 | 95 | var opt = new RadioButton.with_label_from_widget(opt_rsync, _("BTRFS")); 96 | opt.set_tooltip_markup(_("Create snapshots using BTRFS")); 97 | hbox.add (opt); 98 | opt_btrfs = opt; 99 | 100 | if (!check_for_btrfs_tools()) { 101 | opt.sensitive = false; 102 | opt_rsync.active = true; 103 | } 104 | 105 | opt_btrfs.toggled.connect(()=>{ 106 | if (opt_btrfs.active){ 107 | App.btrfs_mode = true; 108 | init_backend(); 109 | type_changed(); 110 | update_description(); 111 | } 112 | }); 113 | } 114 | 115 | private bool check_for_btrfs_tools() { 116 | try { 117 | const string args[] = {"lsblk", "-o", "FSTYPE", null}; 118 | var proc = new Subprocess.newv( 119 | args, 120 | SubprocessFlags.STDOUT_PIPE | SubprocessFlags.STDERR_SILENCE 121 | ); 122 | 123 | Bytes stdout; 124 | if (proc.communicate(null, null, out stdout, null)) { 125 | string output = (string) Bytes.unref_to_data(stdout); 126 | 127 | if (output.contains("btrfs")) { 128 | return true; 129 | } 130 | } 131 | } 132 | catch (Error e) { 133 | log_error (e.message); 134 | } 135 | 136 | return false; 137 | } 138 | 139 | private void add_description(){ 140 | 141 | Gtk.Expander expander = new Gtk.Expander(_("Help")); 142 | expander.use_markup = true; 143 | expander.margin_top = 12; 144 | this.add(expander); 145 | 146 | // scrolled 147 | var scrolled = new ScrolledWindow(null, null); 148 | scrolled.set_shadow_type (ShadowType.ETCHED_IN); 149 | scrolled.margin_top = 6; 150 | //scrolled.expand = true; 151 | scrolled.set_size_request(-1,200); 152 | scrolled.hscrollbar_policy = Gtk.PolicyType.NEVER; 153 | scrolled.vscrollbar_policy = Gtk.PolicyType.AUTOMATIC; 154 | expander.add(scrolled); 155 | 156 | var lbl = new Gtk.Label(""); 157 | lbl.set_use_markup(true); 158 | lbl.xalign = (float) 0.0; 159 | lbl.yalign = (float) 0.0; 160 | lbl.wrap = true; 161 | lbl.wrap_mode = Pango.WrapMode.WORD; 162 | lbl.margin = 12; 163 | lbl.vexpand = true; 164 | scrolled.add(lbl); 165 | 166 | lbl_description = lbl; 167 | } 168 | 169 | private void update_description(){ 170 | 171 | string bullet = "• "; 172 | 173 | if (opt_btrfs.active){ 174 | string txt = "" + _("BTRFS Snapshots") + "\n\n"; 175 | 176 | txt += bullet + _("Snapshots are created using the built-in features of the BTRFS file system.") + "\n\n"; 177 | 178 | txt += bullet + _("Snapshots are created and restored instantly. Snapshot creation is an atomic transaction at the file system level.") + "\n\n"; 179 | 180 | txt += bullet + _("Snapshots are restored by replacing system subvolumes. Since files are never copied, deleted or overwritten, there is no risk of data loss. The existing system is preserved as a new snapshot after restore.") + "\n\n"; 181 | 182 | txt += bullet + _("Snapshots are perfect, byte-for-byte copies of the system. Nothing is excluded.") + "\n\n"; 183 | 184 | txt += bullet + _("Snapshots are saved on the same disk from which they are created (system disk). Storage on other disks is not supported. If system disk fails then snapshots stored on it will be lost along with the system.") + "\n\n"; 185 | 186 | txt += bullet + _("Size of BTRFS snapshots are initially zero. As system files gradually change with time, data gets written to new data blocks which take up disk space (copy-on-write). Files in the snapshot continue to point to original data blocks.") + "\n\n"; 187 | 188 | txt += bullet + _("OS must be installed on a BTRFS partition with Ubuntu-type subvolume layout (@ and @home subvolumes). Other layouts are not supported.") + "\n\n"; 189 | 190 | lbl_description.label = txt; 191 | } 192 | else{ 193 | string txt = "" + _("RSYNC Snapshots") + "\n\n"; 194 | 195 | txt += bullet + _("Snapshots are created by creating copies of system files using rsync, and hard-linking unchanged files from previous snapshot.") + "\n\n"; 196 | 197 | txt += bullet + _("All files are copied when first snapshot is created. Subsequent snapshots are incremental. Unchanged files will be hard-linked from the previous snapshot if available.") + "\n\n"; 198 | 199 | txt += bullet + _("Snapshots can be saved to any disk formatted with a Linux file system. Saving snapshots to non-system or external disk allows the system to be restored even if system disk is damaged or re-formatted.") + "\n\n"; 200 | 201 | txt += bullet + _("Files and directories can be excluded to save disk space.") + "\n\n"; 202 | 203 | lbl_description.label = txt; 204 | } 205 | } 206 | 207 | public void init_backend(){ 208 | 209 | App.try_select_default_device_for_backup(parent_window); 210 | } 211 | 212 | public void refresh(){ 213 | 214 | opt_btrfs.active = App.btrfs_mode; 215 | type_changed(); 216 | update_description(); 217 | } 218 | } 219 | -------------------------------------------------------------------------------- /src/Utility/AppLock.vala: -------------------------------------------------------------------------------- 1 | /* 2 | * AppLock.vala 3 | * 4 | * Copyright 2012-2018 Tony George 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 2 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program; if not, write to the Free Software 18 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, 19 | * MA 02110-1301, USA. 20 | * 21 | * 22 | */ 23 | 24 | using TeeJee.Logging; 25 | using TeeJee.FileSystem; 26 | using TeeJee.ProcessHelper; 27 | using TeeJee.Misc; 28 | 29 | public class AppLock : GLib.Object { 30 | public string lock_file = ""; 31 | public string lock_message = ""; 32 | 33 | public bool create(string app_name, string message){ 34 | 35 | var lock_dir = "/var/run/lock/%s".printf(app_name); 36 | dir_create(lock_dir); 37 | lock_file = path_combine(lock_dir, "lock"); 38 | 39 | try{ 40 | var file = File.new_for_path(lock_file); 41 | if (file.query_exists()) { 42 | 43 | string txt = file_read(lock_file); 44 | string process_id = txt.split(";")[0].strip(); 45 | lock_message = txt.split(";")[1].strip(); 46 | long pid = long.parse(process_id); 47 | 48 | if (process_is_running(pid)){ 49 | log_msg(_("Another instance of this application is running") 50 | + " (PID=%ld)".printf(pid)); 51 | return false; 52 | } 53 | else{ 54 | log_msg(_("[Warning] Deleted invalid lock")); 55 | file.delete(); 56 | write_lock_file(message); 57 | return true; 58 | } 59 | } 60 | else{ 61 | write_lock_file(message); 62 | return true; 63 | } 64 | } 65 | catch (Error e) { 66 | log_error (e.message); 67 | return false; 68 | } 69 | } 70 | 71 | private void write_lock_file(string message){ 72 | string current_pid = ((long) Posix.getpid()).to_string(); 73 | file_write(lock_file, "%s;%s".printf(current_pid, message)); 74 | } 75 | 76 | public void remove(){ 77 | try{ 78 | var file = File.new_for_path (lock_file); 79 | if (file.query_exists()) { 80 | file.delete(); 81 | } 82 | } 83 | catch (Error e) { 84 | log_error (e.message); 85 | } 86 | } 87 | 88 | } 89 | -------------------------------------------------------------------------------- /src/Utility/CryptTabEntry.vala: -------------------------------------------------------------------------------- 1 | /* 2 | * CryptTabEntry.vala 3 | * 4 | * Copyright 2016 Tony George 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 2 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program; if not, write to the Free Software 18 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, 19 | * MA 02110-1301, USA. 20 | * 21 | * 22 | */ 23 | 24 | 25 | using TeeJee.Logging; 26 | using TeeJee.FileSystem; 27 | using TeeJee.JsonHelper; 28 | using TeeJee.ProcessHelper; 29 | using TeeJee.GtkHelper; 30 | using TeeJee.System; 31 | using TeeJee.Misc; 32 | 33 | public class CryptTabEntry : GLib.Object{ 34 | 35 | public bool is_comment = false; 36 | public bool is_empty_line = false; 37 | 38 | // fields 39 | public string mapped_name = ""; 40 | public string device_string = ""; 41 | public string keyfile = "none"; 42 | public string options = "luks,nofail"; 43 | public string line = ""; 44 | 45 | public string device_uuid { 46 | owned get{ 47 | if (device_string.down().has_prefix("uuid=")){ 48 | return device_string.replace("\"","").replace("'","").split("=")[1]; 49 | } 50 | else{ 51 | return ""; 52 | } 53 | } 54 | set { 55 | device_string = "UUID=%s".printf(value); 56 | } 57 | } 58 | 59 | public static Gee.ArrayList read_file(string file_path){ 60 | 61 | var list = new Gee.ArrayList(); 62 | 63 | if (!file_exists(file_path)){ return list; } 64 | 65 | string text = file_read(file_path); 66 | string[] lines = text.split("\n"); 67 | foreach(string line in lines){ 68 | var entry = new CryptTabEntry(); 69 | list.add(entry); 70 | 71 | entry.is_comment = line.strip().has_prefix("#"); 72 | entry.is_empty_line = (line.strip().length == 0); 73 | 74 | if (entry.is_comment){ 75 | entry.line = line; 76 | } 77 | else if (entry.is_empty_line){ 78 | entry.line = ""; 79 | } 80 | else{ 81 | entry.line = line; 82 | 83 | string[] parts = line.replace("\t"," ").split(" "); 84 | int part_num = -1; 85 | foreach(string part in parts){ 86 | if (part.strip().length == 0) { continue; } 87 | switch (++part_num){ 88 | case 0: 89 | entry.mapped_name = part.strip(); 90 | break; 91 | case 1: 92 | entry.device_string = part.strip(); 93 | break; 94 | case 2: 95 | entry.keyfile = part.strip(); 96 | break; 97 | case 3: 98 | entry.options = part.strip(); 99 | break; 100 | } 101 | } 102 | } 103 | } 104 | 105 | return list; 106 | } 107 | 108 | public static string write_file(Gee.ArrayList entries, string file_path, bool keep_comments_and_empty_lines = true){ 109 | 110 | string text = ""; 111 | foreach(var entry in entries){ 112 | if (entry.is_comment || entry.is_empty_line){ 113 | if (keep_comments_and_empty_lines){ 114 | text += "%s\n".printf(entry.line); 115 | } 116 | } 117 | else { 118 | text += "%s\t%s\t%s\t%s\n".printf( 119 | entry.mapped_name, entry.device_string, 120 | entry.keyfile, entry.options); 121 | } 122 | } 123 | 124 | if (file_exists(file_path)){ 125 | file_delete(file_path); 126 | } 127 | 128 | file_write(file_path, text); 129 | 130 | return text; 131 | } 132 | 133 | public void append_option(string option){ 134 | 135 | if (!options.contains(option)){ 136 | options += ",%s".printf(option); 137 | } 138 | 139 | if(options.has_prefix(",")){ 140 | options = options[1:options.length]; 141 | } 142 | 143 | options = options.strip(); 144 | } 145 | 146 | public void remove_option(string option){ 147 | 148 | options = options.replace(option,"").strip(); 149 | 150 | if(options.has_prefix(",")){ 151 | options = options[1:options.length]; 152 | } 153 | 154 | if (options.has_suffix(",")){ 155 | options = options[0:options.length - 1]; 156 | } 157 | 158 | options = options.strip(); 159 | } 160 | 161 | public static CryptTabEntry? find_entry_by_uuid( 162 | Gee.ArrayList entries, string uuid){ 163 | 164 | foreach(var entry in entries){ 165 | if (entry.device_uuid == uuid){ 166 | return entry; 167 | } 168 | } 169 | 170 | return null; 171 | } 172 | } 173 | -------------------------------------------------------------------------------- /src/Utility/DeleteFileTask.vala: -------------------------------------------------------------------------------- 1 | /* 2 | * DeleteFileTask.vala 3 | * 4 | * Copyright 2012-2018 Tony George 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 2 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program; if not, write to the Free Software 18 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, 19 | * MA 02110-1301, USA. 20 | * 21 | * 22 | */ 23 | 24 | using TeeJee.Logging; 25 | using TeeJee.FileSystem; 26 | using TeeJee.JsonHelper; 27 | using TeeJee.ProcessHelper; 28 | using TeeJee.System; 29 | using TeeJee.Misc; 30 | 31 | public class DeleteFileTask : AsyncTask{ 32 | 33 | // settings 34 | public string dest_path = ""; 35 | public bool verbose = true; 36 | public bool io_nice = true; 37 | public bool use_rsync = false; 38 | 39 | //private 40 | private string source_path = ""; 41 | 42 | // regex 43 | private Gee.HashMap regex_list; 44 | 45 | // status 46 | public int64 status_line_count = 0; 47 | public int64 total_size = 0; 48 | public string status_message = ""; 49 | public string time_remaining = ""; 50 | 51 | public DeleteFileTask(){ 52 | init_regular_expressions(); 53 | } 54 | 55 | private void init_regular_expressions(){ 56 | if (regex_list != null){ 57 | return; // already initialized 58 | } 59 | 60 | regex_list = new Gee.HashMap(); 61 | 62 | try { 63 | 64 | regex_list["rsync-deleted"] = new Regex( 65 | """\*deleting[ \t]+(.*)"""); 66 | 67 | } 68 | catch (Error e) { 69 | log_error (e.message); 70 | } 71 | } 72 | 73 | public void prepare() { 74 | string script_text = build_script(); 75 | log_debug(script_text); 76 | save_bash_script_temp(script_text, script_file); 77 | 78 | log_debug("RsyncTask:prepare(): saved: %s".printf(script_file)); 79 | 80 | status_line_count = 0; 81 | total_size = 0; 82 | } 83 | 84 | private string build_script() { 85 | var cmd = ""; 86 | 87 | if (io_nice){ 88 | //cmd += "ionice -c2 -n7 "; 89 | } 90 | 91 | if (use_rsync){ 92 | 93 | cmd += "rsync -aii"; 94 | 95 | if (verbose){ 96 | cmd += " --verbose"; 97 | } 98 | else{ 99 | cmd += " --quiet"; 100 | } 101 | 102 | cmd += " --delete"; 103 | 104 | cmd += " --stats --relative"; 105 | 106 | source_path = "/tmp/%s_empty".printf(random_string()); 107 | dir_create(source_path); 108 | 109 | source_path = remove_trailing_slash(source_path); 110 | dest_path = remove_trailing_slash(dest_path); 111 | 112 | cmd += " '%s/'".printf(escape_single_quote(source_path)); 113 | cmd += " '%s/'".printf(escape_single_quote(dest_path)); 114 | } 115 | else{ 116 | cmd += "rm"; 117 | 118 | if (verbose){ 119 | cmd += " -rfv"; 120 | } 121 | else{ 122 | cmd += " -rf"; 123 | } 124 | 125 | cmd += " '%s'".printf(escape_single_quote(dest_path)); 126 | } 127 | 128 | return cmd; 129 | } 130 | 131 | // execution ---------------------------- 132 | 133 | public void execute() { 134 | 135 | status = AppStatus.RUNNING; 136 | 137 | log_debug("RsyncTask:execute()"); 138 | 139 | prepare(); 140 | 141 | begin(); 142 | 143 | if (status == AppStatus.RUNNING){ 144 | 145 | 146 | } 147 | } 148 | 149 | public override void parse_stdout_line(string out_line){ 150 | if (is_terminated) { 151 | return; 152 | } 153 | 154 | update_progress_parse_console_output(out_line); 155 | } 156 | 157 | public override void parse_stderr_line(string err_line){ 158 | if (is_terminated) { 159 | return; 160 | } 161 | 162 | update_progress_parse_console_output(err_line); 163 | } 164 | 165 | public bool update_progress_parse_console_output (string line) { 166 | if ((line == null) || (line.length == 0)) { 167 | return true; 168 | } 169 | 170 | status_line_count++; 171 | 172 | if (prg_count_total > 0){ 173 | prg_count = status_line_count; 174 | progress = (prg_count * 1.0) / prg_count_total; 175 | } 176 | 177 | MatchInfo match; 178 | if (regex_list["rsync-deleted"].match(line, 0, out match)) { 179 | 180 | //log_debug("matched: rsync-deleted:%s".printf(line)); 181 | 182 | status_line = match.fetch(1).split(" -> ")[0].strip(); 183 | } 184 | else { 185 | 186 | //log_debug("matched: else:%s".printf(line)); 187 | 188 | status_line = line.strip(); 189 | } 190 | 191 | return true; 192 | } 193 | 194 | protected override void finish_task(){ 195 | if ((status != AppStatus.CANCELLED) && (status != AppStatus.PASSWORD_REQUIRED)) { 196 | status = AppStatus.FINISHED; 197 | } 198 | } 199 | 200 | public int read_status(){ 201 | var status_file = working_dir + "/status"; 202 | var f = File.new_for_path(status_file); 203 | if (f.query_exists()){ 204 | var txt = file_read(status_file); 205 | return int.parse(txt); 206 | } 207 | return -1; 208 | } 209 | } 210 | -------------------------------------------------------------------------------- /src/Utility/FileItem.vala: -------------------------------------------------------------------------------- 1 | /* 2 | * FileItem.vala 3 | * 4 | * Copyright 2012-2018 Tony George 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 2 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program; if not, write to the Free Software 18 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, 19 | * MA 02110-1301, USA. 20 | * 21 | * 22 | */ 23 | 24 | using GLib; 25 | using Gtk; 26 | using Gee; 27 | using Json; 28 | 29 | using TeeJee.Logging; 30 | using TeeJee.FileSystem; 31 | using TeeJee.JsonHelper; 32 | using TeeJee.ProcessHelper; 33 | using TeeJee.GtkHelper; 34 | using TeeJee.System; 35 | using TeeJee.Misc; 36 | 37 | public class FileItem : GLib.Object,Gee.Comparable { 38 | 39 | public string file_name = ""; 40 | public string file_location = ""; 41 | public string file_path = ""; 42 | public string file_path_prefix = ""; 43 | public FileType file_type = FileType.REGULAR; 44 | public DateTime modified; 45 | public string permissions = ""; 46 | public string owner_user = ""; 47 | public string owner_group = ""; 48 | public string content_type = ""; 49 | public string file_status = ""; 50 | 51 | public bool is_selected = false; 52 | public bool is_symlink = false; 53 | public string symlink_target = ""; 54 | 55 | public long file_count = 0; 56 | public long dir_count = 0; 57 | private int64 _size = 0; 58 | 59 | public GLib.Icon icon; 60 | 61 | // contructors ------------------------------- 62 | 63 | public FileItem(string name) { 64 | file_name = name; 65 | } 66 | 67 | public FileItem.from_disk_path_with_basic_info(string _file_path) { 68 | file_path = _file_path; 69 | file_name = file_basename(_file_path); 70 | file_location = file_parent(_file_path); 71 | query_file_info_basic(); 72 | } 73 | 74 | public FileItem.from_path_and_type(string _file_path, FileType _file_type) { 75 | file_path = _file_path; 76 | file_name = file_basename(_file_path); 77 | file_location = file_parent(_file_path); 78 | file_type = _file_type; 79 | } 80 | 81 | // properties ------------------------------------------------- 82 | 83 | public int64 size { 84 | get{ 85 | return _size; 86 | } 87 | } 88 | 89 | // helpers ---------------------------------------------------- 90 | 91 | public int compare_to(FileItem b){ 92 | if (this.file_type != b.file_type) { 93 | if (this.file_type == FileType.DIRECTORY) { 94 | return -1; 95 | } 96 | else { 97 | return +1; 98 | } 99 | } 100 | else { 101 | //if (view.sort_column_desc) { 102 | return strcmp(this.file_name.down(), b.file_name.down()); 103 | //} 104 | //else { 105 | //return -1 * strcmp(a.file_name.down(), b.file_name.down()); 106 | //} 107 | } 108 | } 109 | 110 | // instance methods ------------------------------------------- 111 | 112 | public void query_file_info() { 113 | 114 | try { 115 | FileInfo info; 116 | File file = File.parse_name (file_path); 117 | 118 | if (file.query_exists()) { 119 | 120 | // get type without following symlinks 121 | 122 | info = file.query_info("%s,%s,%s".printf( 123 | FileAttribute.STANDARD_TYPE, 124 | FileAttribute.STANDARD_ICON, 125 | FileAttribute.STANDARD_SYMLINK_TARGET), 126 | FileQueryInfoFlags.NOFOLLOW_SYMLINKS); 127 | 128 | var item_file_type = info.get_file_type(); 129 | 130 | this.icon = info.get_icon(); 131 | 132 | if (item_file_type == FileType.SYMBOLIC_LINK) { 133 | //this.icon = GLib.Icon.new_for_string("emblem-symbolic-link"); 134 | this.is_symlink = true; 135 | this.symlink_target = info.get_symlink_target(); 136 | } 137 | else { 138 | 139 | this.is_symlink = false; 140 | this.symlink_target = ""; 141 | 142 | if (item_file_type == FileType.REGULAR){ 143 | //log_msg(file_basename(file_path) + " (gicon): " + icon.to_string()); 144 | 145 | /*var themed_icon = (GLib.ThemedIcon) icon; 146 | 147 | string txt = "-> "; 148 | foreach(var name in themed_icon.names){ 149 | txt += ", " + name; 150 | } 151 | log_msg(txt);*/ 152 | } 153 | } 154 | 155 | // get file info - follow symlinks 156 | 157 | info = file.query_info("%s,%s,%s,%s,%s,%s,%s,%s".printf( 158 | FileAttribute.STANDARD_TYPE, 159 | FileAttribute.STANDARD_SIZE, 160 | FileAttribute.STANDARD_ICON, 161 | FileAttribute.STANDARD_CONTENT_TYPE, 162 | FileAttribute.TIME_MODIFIED, 163 | FileAttribute.OWNER_USER, 164 | FileAttribute.OWNER_GROUP, 165 | FileAttribute.FILESYSTEM_FREE 166 | ), 0); 167 | 168 | if (this.is_symlink){ 169 | // get icon for the resolved file 170 | this.icon = info.get_icon(); 171 | } 172 | 173 | // file type resolved 174 | this.file_type = info.get_file_type(); 175 | 176 | // content type 177 | this.content_type = info.get_content_type(); 178 | 179 | // size 180 | if (!this.is_symlink && (this.file_type == FileType.REGULAR)) { 181 | this._size = info.get_size(); 182 | } 183 | 184 | // modified 185 | this.modified = (new DateTime.from_timeval_utc(info.get_modification_time())).to_local(); 186 | 187 | // owner_user 188 | this.owner_user = info.get_attribute_string(FileAttribute.OWNER_USER); 189 | 190 | // owner_group 191 | this.owner_group = info.get_attribute_string(FileAttribute.OWNER_GROUP); 192 | 193 | } 194 | } 195 | catch (Error e) { 196 | log_error (e.message); 197 | } 198 | } 199 | 200 | public void query_file_info_basic() { 201 | 202 | try { 203 | FileInfo info; 204 | File file = File.parse_name(file_path); 205 | 206 | if (file.query_exists()) { 207 | 208 | // get type and icon -- follow symlinks 209 | 210 | info = file.query_info("%s,%s".printf( 211 | FileAttribute.STANDARD_TYPE, 212 | FileAttribute.STANDARD_ICON 213 | ), 0); 214 | 215 | this.icon = info.get_icon(); 216 | 217 | this.file_type = info.get_file_type(); 218 | } 219 | } 220 | catch (Error e) { 221 | log_error (e.message); 222 | } 223 | } 224 | 225 | // icons ------------------------------------------------------ 226 | 227 | public Gdk.Pixbuf? get_icon(int icon_size, bool add_transparency, bool add_emblems){ 228 | 229 | Gdk.Pixbuf? pixbuf = null; 230 | 231 | if (icon != null) { 232 | pixbuf = IconManager.lookup_gicon(icon, icon_size); 233 | } 234 | 235 | if (pixbuf == null){ 236 | if (file_type == FileType.DIRECTORY) { 237 | pixbuf = IconManager.lookup("folder", icon_size, false); 238 | } 239 | else{ 240 | pixbuf = IconManager.lookup("text-x-preview", icon_size, false); 241 | } 242 | } 243 | 244 | return pixbuf; 245 | } 246 | } 247 | -------------------------------------------------------------------------------- /src/Utility/Gtk/AboutWindow.vala: -------------------------------------------------------------------------------- 1 | /* 2 | * AboutWindow.vala 3 | * 4 | * Copyright 2012-2018 Tony George 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 2 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program; if not, write to the Free Software 18 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, 19 | * MA 02110-1301, USA. 20 | * 21 | * 22 | */ 23 | 24 | using Gtk; 25 | 26 | using TeeJee.Logging; 27 | using TeeJee.FileSystem; 28 | using TeeJee.JsonHelper; 29 | using TeeJee.ProcessHelper; 30 | using TeeJee.GtkHelper; 31 | using TeeJee.System; 32 | using TeeJee.Misc; 33 | 34 | public class AboutWindow : Gtk.Window { 35 | 36 | private Gtk.Box vbox_main; 37 | private Gtk.Box vbox_lines; 38 | private Gtk.Box hbox_action; 39 | private Gtk.Window window; 40 | 41 | private Gtk.Image img_logo; 42 | private Gtk.Label lbl_program_name; 43 | private Gtk.Label lbl_version; 44 | private Gtk.Label lbl_comments; 45 | private Gtk.Label lbl_license; 46 | private Gtk.LinkButton lbtn_website; 47 | private Gtk.Label lbl_copyright; 48 | 49 | private string[] _authors; 50 | public string[] authors{ 51 | get{ 52 | return _authors; 53 | } 54 | set{ 55 | _authors = value; 56 | } 57 | } 58 | 59 | private string _comments = ""; 60 | public string comments{ 61 | get{ 62 | return _comments; 63 | } 64 | set{ 65 | _comments = value; 66 | } 67 | } 68 | 69 | private string _copyright = ""; 70 | public string copyright{ 71 | get{ 72 | return _copyright; 73 | } 74 | set{ 75 | _copyright = value; 76 | } 77 | } 78 | 79 | private Gdk.Pixbuf _logo; 80 | public Gdk.Pixbuf logo{ 81 | get{ 82 | return _logo; 83 | } 84 | set{ 85 | _logo = value; 86 | } 87 | } 88 | 89 | private string _program_name = ""; 90 | public string program_name{ 91 | get{ 92 | return _program_name; 93 | } 94 | set{ 95 | _program_name = value; 96 | } 97 | } 98 | 99 | private string[] _translators; 100 | public string[] translators{ 101 | get{ 102 | return _translators; 103 | } 104 | set{ 105 | _translators = value; 106 | } 107 | } 108 | 109 | private string[] _third_party; 110 | public string[] third_party{ 111 | get{ 112 | return _third_party; 113 | } 114 | set{ 115 | _third_party = value; 116 | } 117 | } 118 | 119 | private string _version = ""; 120 | public string version{ 121 | get{ 122 | return _version; 123 | } 124 | set{ 125 | _version = value; 126 | } 127 | } 128 | 129 | private string _website = ""; 130 | public string website{ 131 | get{ 132 | return _website; 133 | } 134 | set{ 135 | _website = value; 136 | } 137 | } 138 | 139 | private string _website_label = ""; 140 | public string website_label{ 141 | get{ 142 | return _website_label; 143 | } 144 | set{ 145 | _website_label = value; 146 | } 147 | } 148 | 149 | private string username = ""; 150 | 151 | public AboutWindow(Gtk.Window _window) { 152 | 153 | window = _window; 154 | 155 | window_position = WindowPosition.CENTER_ON_PARENT; 156 | set_destroy_with_parent (true); 157 | set_modal (true); 158 | skip_taskbar_hint = false; 159 | set_default_size(450, 400); 160 | 161 | if (get_user_id_effective() == 0){ 162 | username = get_username(); 163 | log_debug("username: %s".printf(username)); 164 | } 165 | 166 | vbox_main = new Gtk.Box(Orientation.VERTICAL, 10); 167 | vbox_main.margin = 10; 168 | this.add(vbox_main); 169 | 170 | // logo 171 | 172 | img_logo = new Gtk.Image(); 173 | img_logo.margin = 10; 174 | vbox_main.add(img_logo); 175 | 176 | // program_name 177 | 178 | lbl_program_name = new Gtk.Label(""); 179 | lbl_program_name.set_use_markup(true); 180 | vbox_main.add(lbl_program_name); 181 | 182 | // version 183 | 184 | lbl_version = new Gtk.Label(""); 185 | lbl_version.set_use_markup(true); 186 | vbox_main.add(lbl_version); 187 | 188 | // comments 189 | 190 | lbl_comments = new Gtk.Label(""); 191 | lbl_comments.set_use_markup(true); 192 | vbox_main.add(lbl_comments); 193 | 194 | // website 195 | 196 | lbtn_website = new LinkButton(""); 197 | vbox_main.add(lbtn_website); 198 | 199 | lbtn_website.activate_link.connect(()=>{ 200 | return xdg_open(lbtn_website.uri, username); 201 | }); 202 | 203 | // copyright 204 | lbl_copyright = new Gtk.Label(""); 205 | lbl_copyright.set_use_markup(true); 206 | vbox_main.add(lbl_copyright); 207 | 208 | // copyright 209 | lbl_license = new Gtk.Label(""); 210 | lbl_license.set_use_markup(true); 211 | vbox_main.add(lbl_license); 212 | 213 | // spacer 214 | var spacer = new Gtk.Label(""); 215 | spacer.vexpand = true; 216 | //vbox_main.add(spacer); 217 | 218 | add_action_buttons(); 219 | } 220 | 221 | private void add_action_buttons(){ 222 | 223 | hbox_action = add_button_box(vbox_main, Gtk.Orientation.HORIZONTAL, Gtk.ButtonBoxStyle.CENTER, 6); 224 | hbox_action.margin = 10; 225 | 226 | string url = "https://www.gnu.org/licenses/old-licenses/gpl-2.0.html"; 227 | 228 | // btn_license 229 | var btn_license = new Gtk.Button.with_label("License"); 230 | btn_license.set_tooltip_text(url); 231 | btn_license.image = IconManager.lookup_image("help-about-symbolic", 16); 232 | hbox_action.add(btn_license); 233 | 234 | btn_license.clicked.connect(()=>{ 235 | xdg_open(url, username); 236 | }); 237 | 238 | // btn_close 239 | var btn_close = new Gtk.Button.with_label(_("Close")); 240 | btn_close.image = IconManager.lookup_image("help-about-symbolic", 16); 241 | hbox_action.add(btn_close); 242 | 243 | btn_close.clicked.connect(()=>{ this.destroy(); }); 244 | } 245 | 246 | public void initialize() { 247 | 248 | title = program_name; 249 | img_logo.pixbuf = logo.scale_simple(128,128,Gdk.InterpType.HYPER); 250 | lbl_program_name.label = "%s".printf(program_name); 251 | lbl_version.label = "v%s".printf(version); 252 | lbl_comments.label = "%s".printf(comments); 253 | lbtn_website.uri = website; 254 | lbtn_website.label = website_label; 255 | lbl_copyright.label = "%s".printf(copyright); 256 | 257 | lbl_license.label = 258 | """This program comes with absolutely no warranty. 259 | See the GNU General Public License v2 or later, for details"""; 260 | 261 | if (authors.length > 0){ 262 | foreach(string name in authors){ 263 | add_line("%s\n".printf(name)); 264 | } 265 | add_line("\n"); 266 | } 267 | } 268 | 269 | private void add_line(string text, bool escape_html_chars = true){ 270 | 271 | if (text.split(":").length >= 2){ 272 | 273 | var link = new LinkButton(escape_html(text.split(":")[0])); 274 | vbox_lines.add(link); 275 | 276 | string val = text[text.index_of(":") + 1:text.length]; 277 | if (val.contains("@")){ 278 | link.uri = "mailto:" + val; 279 | } 280 | else if(val.has_prefix("http://") || val.has_prefix("https://")){ 281 | link.uri = val; 282 | } 283 | else{ 284 | link.uri = "http://" + val; 285 | } 286 | 287 | link.activate_link.connect(()=>{ 288 | return xdg_open(link.uri, username); 289 | }); 290 | } 291 | else{ 292 | var txt = text; 293 | if (escape_html_chars){ 294 | txt = escape_html(text); 295 | } 296 | 297 | var lbl = new Gtk.Label(txt); 298 | lbl.set_use_markup(true); 299 | lbl.valign = Align.START; 300 | lbl.wrap = true; 301 | lbl.wrap_mode = Pango.WrapMode.WORD; 302 | vbox_lines.add(lbl); 303 | } 304 | } 305 | } 306 | -------------------------------------------------------------------------------- /src/Utility/Gtk/CustomMessageDialog.vala: -------------------------------------------------------------------------------- 1 | /* 2 | * CustomMessageDialog.vala 3 | * 4 | * Copyright 2012-2018 Tony George 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 2 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program; if not, write to the Free Software 18 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, 19 | * MA 02110-1301, USA. 20 | * 21 | * 22 | */ 23 | 24 | 25 | using Gtk; 26 | using Gee; 27 | 28 | using TeeJee.Logging; 29 | using TeeJee.FileSystem; 30 | using TeeJee.JsonHelper; 31 | using TeeJee.ProcessHelper; 32 | using TeeJee.GtkHelper; 33 | using TeeJee.System; 34 | using TeeJee.Misc; 35 | 36 | public class CustomMessageDialog : Gtk.Dialog { 37 | 38 | private Gtk.Box vbox_main; 39 | private Gtk.Label lbl_msg; 40 | private Gtk.ScrolledWindow sw_msg; 41 | private Gtk.Button btn_ok; 42 | private Gtk.Button btn_cancel; 43 | private Gtk.Button btn_yes; 44 | private Gtk.Button btn_no; 45 | 46 | private string msg_title; 47 | private string msg_body; 48 | private Gtk.MessageType msg_type; 49 | private Gtk.ButtonsType buttons_type; 50 | 51 | public CustomMessageDialog(string _msg_title, string _msg_body, Gtk.MessageType _msg_type, Window? parent, Gtk.ButtonsType _buttons_type) { 52 | 53 | set_transient_for(parent); 54 | set_modal(true); 55 | 56 | msg_title = _msg_title; 57 | msg_body = _msg_body; 58 | msg_type = _msg_type; 59 | buttons_type = _buttons_type; 60 | 61 | init_window(); 62 | 63 | lbl_msg.expand = true; 64 | sw_msg.expand = true; 65 | sw_msg.vscrollbar_policy = PolicyType.NEVER; 66 | 67 | sw_msg.set_size_request(500, 150); // sets minimum size 68 | 69 | show_all(); 70 | 71 | if (lbl_msg.get_allocated_height() > 400){ 72 | sw_msg.vscrollbar_policy = PolicyType.AUTOMATIC; 73 | sw_msg.set_size_request(500, 400); 74 | } 75 | } 76 | 77 | public void init_window () { 78 | 79 | this.title = ""; 80 | this.window_position = WindowPosition.CENTER_ON_PARENT; 81 | this.icon = IconManager.lookup("timeshift", 16); 82 | this.resizable = false; 83 | this.deletable = false; 84 | this.skip_taskbar_hint = true; 85 | this.skip_pager_hint = true; 86 | 87 | //vbox_main 88 | vbox_main = get_content_area () as Gtk.Box; 89 | vbox_main.margin = 6; 90 | 91 | //hbox_contents 92 | var hbox_contents = new Gtk.Box(Orientation.HORIZONTAL, 6); 93 | hbox_contents.margin = 6; 94 | vbox_main.add (hbox_contents); 95 | 96 | string icon_name = "dialog-info"; 97 | 98 | switch(msg_type){ 99 | case Gtk.MessageType.INFO: 100 | icon_name = "dialog-info"; 101 | break; 102 | case Gtk.MessageType.WARNING: 103 | icon_name = "dialog-warning"; 104 | break; 105 | case Gtk.MessageType.QUESTION: 106 | icon_name = "dialog-question"; 107 | break; 108 | case Gtk.MessageType.ERROR: 109 | icon_name = "dialog-error"; 110 | break; 111 | case Gtk.MessageType.OTHER: 112 | icon_name = "dialog-info"; 113 | break; 114 | } 115 | 116 | // image ---------------- 117 | 118 | var img = new Image.from_icon_name(icon_name, Gtk.IconSize.DIALOG); 119 | hbox_contents.add(img); 120 | 121 | // label ------------------- 122 | 123 | var text = "%s\n\n%s".printf( 124 | escape_html(msg_title), 125 | msg_body); 126 | lbl_msg = new Gtk.Label(text); 127 | lbl_msg.xalign = 0.0f; 128 | lbl_msg.yalign = 0.0f; 129 | lbl_msg.max_width_chars = 70; 130 | lbl_msg.wrap = true; 131 | lbl_msg.wrap_mode = Pango.WrapMode.WORD_CHAR; 132 | lbl_msg.use_markup = true; 133 | 134 | //sw_msg 135 | sw_msg = new Gtk.ScrolledWindow(null, null); 136 | //sw_msg.set_shadow_type (ShadowType.ETCHED_IN); 137 | sw_msg.add (lbl_msg); 138 | sw_msg.hscrollbar_policy = PolicyType.NEVER; 139 | sw_msg.vscrollbar_policy = PolicyType.NEVER; 140 | //sw_msg.set_size_request(500, 400); 141 | hbox_contents.add(sw_msg); 142 | 143 | // actions ------------------------- 144 | 145 | switch(buttons_type){ 146 | case Gtk.ButtonsType.OK: 147 | btn_ok = (Gtk.Button) add_button (_("OK"), Gtk.ResponseType.OK); 148 | btn_ok.grab_focus(); 149 | break; 150 | case Gtk.ButtonsType.OK_CANCEL: 151 | btn_ok = (Gtk.Button) add_button (_("OK"), Gtk.ResponseType.OK); 152 | btn_cancel = (Gtk.Button) add_button (_("Cancel"), Gtk.ResponseType.CANCEL); 153 | btn_ok.grab_focus(); 154 | break; 155 | case Gtk.ButtonsType.YES_NO: 156 | btn_yes = (Gtk.Button) add_button (_("Yes"), Gtk.ResponseType.YES); 157 | btn_no = (Gtk.Button) add_button (_("No"), Gtk.ResponseType.NO); 158 | btn_yes.grab_focus(); 159 | break; 160 | 161 | } 162 | } 163 | } 164 | 165 | 166 | -------------------------------------------------------------------------------- /src/Utility/Gtk/DonationWindow.vala: -------------------------------------------------------------------------------- 1 | /* 2 | * DonationWindow.vala 3 | * 4 | * Copyright 2012-18 Tony George 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 2 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program; if not, write to the Free Software 18 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, 19 | * MA 02110-1301, USA. 20 | * 21 | * 22 | */ 23 | 24 | using Gtk; 25 | 26 | using TeeJee.Logging; 27 | using TeeJee.FileSystem; 28 | using TeeJee.JsonHelper; 29 | using TeeJee.ProcessHelper; 30 | using TeeJee.System; 31 | using TeeJee.Misc; 32 | using TeeJee.GtkHelper; 33 | 34 | public class DonationWindow : Gtk.Window { 35 | 36 | private Gtk.Box vbox_main; 37 | private string username = ""; 38 | private string appname = "Timeshift"; 39 | 40 | public DonationWindow(Gtk.Window window) { 41 | 42 | set_title(_("Donate")); 43 | 44 | set_transient_for(window); 45 | set_destroy_with_parent(true); 46 | 47 | window_position = WindowPosition.CENTER_ON_PARENT; 48 | 49 | set_modal(true); 50 | set_resizable(false); 51 | set_deletable(true); 52 | 53 | // vbox_main 54 | vbox_main = new Gtk.Box(Orientation.VERTICAL, 12); 55 | vbox_main.margin = 12; 56 | 57 | this.add(vbox_main); 58 | 59 | if (get_user_id_effective() == 0){ 60 | username = get_username(); 61 | } 62 | 63 | string msg = ""; 64 | 65 | // ----------------------------- 66 | 67 | add_label("%s".printf(_("Donate"))); 68 | 69 | msg = _("If you find this software useful, you can buy me a coffee by making a donation with PayPal."); 70 | 71 | add_label(msg); 72 | 73 | var hbox = add_hbox(); 74 | 75 | add_button(hbox, _("Donate"), 76 | "https://www.paypal.com/cgi-bin/webscr?business=teejeetech@gmail.com&cmd=_xclick¤cy_code=USD&item_name=%s+Donation".printf(appname)); 77 | 78 | // ----------------------------- 79 | 80 | add_label("%s".printf(_("Linux Mint Version"))); 81 | 82 | msg = _("There is a fork of Timeshift maintained by Linux Mint which is under more active development. It is recommended to switch to the Linux Mint version.\n\nThis version of Timeshift will continue to be available but will only see minor fixes and changes. Any new features, issues, or pull requests should be submitted to the Linux Mint repository."); 83 | 84 | add_label(msg); 85 | 86 | hbox = add_hbox(); 87 | 88 | add_button(hbox, _("Linux Mint GitHub"), "https://github.com/linuxmint/%s/issues".printf(appname)); 89 | 90 | // close window --------------------------------------------------------- 91 | 92 | add_label("%s".printf(_("Website"))); 93 | 94 | add_label("Visit teejeetech.com for more Linux apps."); 95 | 96 | add_label(""); 97 | 98 | hbox = add_hbox(); 99 | 100 | add_button(hbox, _("Website"), "https://teejeetech.com/"); 101 | 102 | add_button(hbox, _("Store"), "https://teejeetech.com/shop/"); 103 | 104 | var button = new Gtk.Button.with_label(_("Close")); 105 | hbox.add(button); 106 | 107 | button.clicked.connect(() => { 108 | this.destroy(); 109 | }); 110 | 111 | this.show_all(); 112 | } 113 | 114 | private Gtk.Label add_label(string msg){ 115 | 116 | var label = new Gtk.Label(msg); 117 | 118 | label.set_use_markup(true); 119 | 120 | label.wrap = true; 121 | label.wrap_mode = Pango.WrapMode.WORD; 122 | label.max_width_chars = 50; 123 | 124 | label.xalign = 0.0f; 125 | 126 | vbox_main.add(label); 127 | 128 | return label; 129 | } 130 | 131 | private Gtk.ButtonBox add_hbox(){ 132 | 133 | var hbox = new Gtk.ButtonBox(Orientation.HORIZONTAL); 134 | hbox.set_layout(Gtk.ButtonBoxStyle.CENTER); 135 | hbox.set_spacing(6); 136 | vbox_main.add(hbox); 137 | return hbox; 138 | } 139 | 140 | private void add_button(Gtk.Box box, string text, string url){ 141 | 142 | var button = new Gtk.Button.with_label(text); 143 | button.set_tooltip_text(url); 144 | box.add(button); 145 | 146 | //button.set_size_request(200,-1); 147 | 148 | button.clicked.connect(() => { 149 | xdg_open(url, username); 150 | }); 151 | } 152 | } 153 | 154 | -------------------------------------------------------------------------------- /src/Utility/Gtk/TerminalWindow.vala: -------------------------------------------------------------------------------- 1 | /* 2 | * TerminalWindow.vala 3 | * 4 | * Copyright 2012-2018 Tony George 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 2 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program; if not, write to the Free Software 18 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, 19 | * MA 02110-1301, USA. 20 | * 21 | * 22 | */ 23 | 24 | 25 | using Gtk; 26 | using Gee; 27 | 28 | using TeeJee.Logging; 29 | using TeeJee.FileSystem; 30 | using TeeJee.JsonHelper; 31 | using TeeJee.ProcessHelper; 32 | using TeeJee.GtkHelper; 33 | using TeeJee.System; 34 | using TeeJee.Misc; 35 | 36 | public class TerminalWindow : Gtk.Window { 37 | 38 | private Gtk.Box vbox_main; 39 | private Vte.Terminal term; 40 | 41 | private int def_width = 800; 42 | private int def_height = 600; 43 | 44 | private Pid child_pid; 45 | private Gtk.Window parent_win = null; 46 | public bool is_running = false; 47 | 48 | // init 49 | 50 | public TerminalWindow.with_parent(Gtk.Window? parent) { 51 | 52 | if (parent != null){ 53 | set_transient_for(parent); 54 | parent_win = parent; 55 | } 56 | 57 | set_modal(true); 58 | fullscreen(); 59 | 60 | this.delete_event.connect(()=>{ 61 | // do not allow window to close 62 | return true; 63 | }); 64 | 65 | init_window(); 66 | } 67 | 68 | public void init_window () { 69 | 70 | this.title = ""; 71 | this.icon = IconManager.lookup("timeshift",16); 72 | this.resizable = true; 73 | this.deletable = false; 74 | 75 | // vbox_main --------------- 76 | 77 | vbox_main = new Gtk.Box(Orientation.VERTICAL, 6); 78 | vbox_main.set_size_request (def_width, def_height); 79 | add (vbox_main); 80 | 81 | // terminal ---------------------- 82 | 83 | term = new Vte.Terminal(); 84 | term.expand = true; 85 | vbox_main.add(term); 86 | 87 | term.input_enabled = true; 88 | term.backspace_binding = Vte.EraseBinding.AUTO; 89 | term.cursor_blink_mode = Vte.CursorBlinkMode.SYSTEM; 90 | term.cursor_shape = Vte.CursorShape.UNDERLINE; 91 | //term.rewrap_on_resize = true; 92 | 93 | term.scroll_on_keystroke = true; 94 | term.scroll_on_output = true; 95 | 96 | // colors ----------------------------- 97 | 98 | var color = Gdk.RGBA(); 99 | color.parse("#FFFFFF"); 100 | term.set_color_foreground(color); 101 | 102 | color.parse("#404040"); 103 | term.set_color_background(color); 104 | 105 | // grab focus ---------------- 106 | 107 | term.grab_focus(); 108 | 109 | show_all(); 110 | } 111 | 112 | public void start_shell(){ 113 | 114 | string[] argv = new string[1]; 115 | argv[0] = "/bin/sh"; 116 | 117 | string[] env = Environ.get(); 118 | 119 | try{ 120 | 121 | is_running = true; 122 | 123 | term.spawn_sync( 124 | Vte.PtyFlags.DEFAULT, //pty_flags 125 | TEMP_DIR, //working_directory 126 | argv, //argv 127 | env, //env 128 | GLib.SpawnFlags.SEARCH_PATH, //spawn_flags 129 | null, //child_setup 130 | out child_pid, 131 | null 132 | ); 133 | } 134 | catch (Error e) { 135 | log_error (e.message); 136 | } 137 | } 138 | 139 | public void execute_script(string script_path, bool wait = false){ 140 | 141 | string[] argv = new string[1]; 142 | argv[0] = script_path; 143 | 144 | string[] env = Environ.get(); 145 | 146 | try{ 147 | 148 | is_running = true; 149 | 150 | term.spawn_sync( 151 | Vte.PtyFlags.DEFAULT, //pty_flags 152 | TEMP_DIR, //working_directory 153 | argv, //argv 154 | env, //env 155 | GLib.SpawnFlags.SEARCH_PATH, //spawn_flags 156 | null, //child_setup 157 | out child_pid, 158 | null 159 | ); 160 | 161 | term.watch_child(child_pid); 162 | 163 | term.child_exited.connect(script_exit); 164 | 165 | if (wait){ 166 | while (is_running){ 167 | sleep(200); 168 | gtk_do_events(); 169 | } 170 | } 171 | } 172 | catch (Error e) { 173 | log_error (e.message); 174 | } 175 | } 176 | 177 | public void script_exit(int status){ 178 | 179 | is_running = false; 180 | 181 | this.hide(); 182 | 183 | //no need to check status again 184 | 185 | //destroying parent will display main window 186 | if (parent != null){ 187 | parent_win.destroy(); 188 | } 189 | } 190 | } 191 | 192 | 193 | -------------------------------------------------------------------------------- /src/Utility/LinuxDistro.vala: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * LinuxDistro.vala 4 | * 5 | * Copyright 2012-2018 Tony George 6 | * 7 | * This program is free software; you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation; either version 2 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program; if not, write to the Free Software 19 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, 20 | * MA 02110-1301, USA. 21 | * 22 | * 23 | */ 24 | 25 | using TeeJee.Logging; 26 | using TeeJee.FileSystem; 27 | using TeeJee.ProcessHelper; 28 | 29 | public class LinuxDistro : GLib.Object{ 30 | 31 | /* Class for storing information about Linux distribution */ 32 | 33 | public string dist_id = ""; 34 | public string description = ""; 35 | public string release = ""; 36 | public string codename = ""; 37 | 38 | public LinuxDistro(){ 39 | 40 | dist_id = ""; 41 | description = ""; 42 | release = ""; 43 | codename = ""; 44 | } 45 | 46 | public string full_name(){ 47 | 48 | if (dist_id == ""){ 49 | return ""; 50 | } 51 | else{ 52 | string val = ""; 53 | val += dist_id; 54 | val += (release.length > 0) ? " " + release : ""; 55 | val += (codename.length > 0) ? " (" + codename + ")" : ""; 56 | return val; 57 | } 58 | } 59 | 60 | public static LinuxDistro get_dist_info(string root_path){ 61 | 62 | /* Returns information about the Linux distribution 63 | * installed at the given root path */ 64 | 65 | LinuxDistro info = new LinuxDistro(); 66 | 67 | string dist_file = root_path + "/etc/lsb-release"; 68 | var f = File.new_for_path(dist_file); 69 | if (f.query_exists()){ 70 | 71 | /* 72 | DISTRIB_ID=Ubuntu 73 | DISTRIB_RELEASE=13.04 74 | DISTRIB_CODENAME=raring 75 | DISTRIB_DESCRIPTION="Ubuntu 13.04" 76 | */ 77 | 78 | foreach(string line in file_read(dist_file).split("\n")){ 79 | 80 | if (line.split("=").length != 2){ continue; } 81 | 82 | string key = line.split("=")[0].strip(); 83 | string val = line.split("=")[1].strip(); 84 | 85 | if (val.has_prefix("\"")){ 86 | val = val[1:val.length]; 87 | } 88 | 89 | if (val.has_suffix("\"")){ 90 | val = val[0:val.length-1]; 91 | } 92 | 93 | switch (key){ 94 | case "DISTRIB_ID": 95 | info.dist_id = val; 96 | break; 97 | case "DISTRIB_RELEASE": 98 | info.release = val; 99 | break; 100 | case "DISTRIB_CODENAME": 101 | info.codename = val; 102 | break; 103 | case "DISTRIB_DESCRIPTION": 104 | info.description = val; 105 | break; 106 | } 107 | } 108 | } 109 | else{ 110 | 111 | dist_file = root_path + "/etc/os-release"; 112 | f = File.new_for_path(dist_file); 113 | if (f.query_exists()){ 114 | 115 | /* 116 | NAME="Ubuntu" 117 | VERSION="13.04, Raring Ringtail" 118 | ID=ubuntu 119 | ID_LIKE=debian 120 | PRETTY_NAME="Ubuntu 13.04" 121 | VERSION_ID="13.04" 122 | HOME_URL="http://www.ubuntu.com/" 123 | SUPPORT_URL="http://help.ubuntu.com/" 124 | BUG_REPORT_URL="http://bugs.launchpad.net/ubuntu/" 125 | */ 126 | 127 | foreach(string line in file_read(dist_file).split("\n")){ 128 | 129 | if (line.split("=").length != 2){ continue; } 130 | 131 | string key = line.split("=")[0].strip(); 132 | string val = line.split("=")[1].strip(); 133 | 134 | switch (key){ 135 | case "ID": 136 | info.dist_id = val; 137 | break; 138 | case "VERSION_ID": 139 | info.release = val; 140 | break; 141 | //case "DISTRIB_CODENAME": 142 | //info.codename = val; 143 | //break; 144 | case "PRETTY_NAME": 145 | info.description = val; 146 | break; 147 | } 148 | } 149 | } 150 | } 151 | 152 | return info; 153 | } 154 | 155 | public static string get_running_desktop_name(){ 156 | 157 | /* Return the names of the current Desktop environment */ 158 | 159 | int pid = -1; 160 | 161 | pid = get_pid_by_name("cinnamon"); 162 | if (pid > 0){ 163 | return "Cinnamon"; 164 | } 165 | 166 | pid = get_pid_by_name("xfdesktop"); 167 | if (pid > 0){ 168 | return "Xfce"; 169 | } 170 | 171 | pid = get_pid_by_name("lxsession"); 172 | if (pid > 0){ 173 | return "LXDE"; 174 | } 175 | 176 | pid = get_pid_by_name("gnome-shell"); 177 | if (pid > 0){ 178 | return "Gnome"; 179 | } 180 | 181 | pid = get_pid_by_name("wingpanel"); 182 | if (pid > 0){ 183 | return "Elementary"; 184 | } 185 | 186 | pid = get_pid_by_name("unity-panel-service"); 187 | if (pid > 0){ 188 | return "Unity"; 189 | } 190 | 191 | pid = get_pid_by_name("plasma-desktop"); 192 | if (pid > 0){ 193 | return "KDE"; 194 | } 195 | 196 | return "Unknown"; 197 | } 198 | 199 | public string dist_type { 200 | 201 | owned get{ 202 | 203 | if (dist_id in "fedora rhel rocky centos almalinux"){ 204 | return "redhat"; 205 | } 206 | else if (dist_id.down().contains("manjaro") || dist_id.down().contains("arch")){ 207 | return "arch"; 208 | } 209 | else if (dist_id.down().contains("ubuntu") || dist_id.down().contains("debian")){ 210 | return "debian"; 211 | } 212 | else{ 213 | return ""; 214 | } 215 | 216 | } 217 | } 218 | } 219 | 220 | 221 | -------------------------------------------------------------------------------- /src/Utility/MountEntry.vala: -------------------------------------------------------------------------------- 1 | using TeeJee.Logging; 2 | using TeeJee.FileSystem; 3 | using TeeJee.JsonHelper; 4 | using TeeJee.ProcessHelper; 5 | using TeeJee.GtkHelper; 6 | using TeeJee.System; 7 | using TeeJee.Misc; 8 | using Json; 9 | 10 | public class MountEntry : GLib.Object{ 11 | 12 | public Device device = null; 13 | public string mount_point = ""; 14 | public string mount_options = ""; 15 | 16 | public MountEntry(Device? device, string mount_point, string mount_options){ 17 | 18 | this.device = device; 19 | this.mount_point = mount_point; 20 | this.mount_options = mount_options; 21 | } 22 | 23 | public string subvolume_name(){ 24 | 25 | if (mount_options.contains("subvol=")){ 26 | 27 | string txt = mount_options.split("subvol=")[1].split(",")[0].strip(); 28 | 29 | if (txt.has_prefix("/") && (txt.split("/").length == 2)){ 30 | txt = txt.split("/")[1]; 31 | } 32 | 33 | return txt; 34 | } 35 | else{ 36 | return ""; 37 | } 38 | } 39 | 40 | public string lvm_name(){ 41 | 42 | if ((device != null) && (device.type == "lvm") && (device.mapped_name.length > 0)){ 43 | return device.mapped_name.strip(); 44 | } 45 | else{ 46 | return ""; 47 | } 48 | } 49 | 50 | public static MountEntry? find_entry_by_mount_point(Gee.ArrayList entries, string mount_path){ 51 | 52 | foreach(var entry in entries){ 53 | if (entry.mount_point == mount_path){ 54 | return entry; 55 | } 56 | } 57 | 58 | return null; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/Utility/OSDNotify.vala: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * OSDNotify.vala 4 | * 5 | * Copyright 2012-2018 Tony George 6 | * 7 | * This program is free software; you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation; either version 2 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program; if not, write to the Free Software 19 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, 20 | * MA 02110-1301, USA. 21 | * 22 | * 23 | */ 24 | 25 | using TeeJee.Logging; 26 | using TeeJee.FileSystem; 27 | using TeeJee.ProcessHelper; 28 | 29 | // dep: notify-send 30 | public class OSDNotify : GLib.Object { 31 | 32 | private static DateTime dt_last_notification = null; 33 | public const int NOTIFICATION_INTERVAL = 3; 34 | 35 | public static int notify_send ( 36 | string title, string message, int durationMillis, 37 | string urgency = "low", // low, normal, critical 38 | string dialog_type = "info" //error, info, warning 39 | ){ 40 | 41 | /* Displays notification bubble on the desktop */ 42 | 43 | int retVal = 0; 44 | 45 | switch (dialog_type){ 46 | case "error": 47 | case "info": 48 | case "warning": 49 | //ok 50 | break; 51 | default: 52 | dialog_type = "info"; 53 | break; 54 | } 55 | 56 | long seconds = 9999; 57 | 58 | if (dt_last_notification != null){ 59 | 60 | DateTime dt_end = new DateTime.now_local(); 61 | TimeSpan elapsed = dt_end.difference(dt_last_notification); 62 | seconds = (long)(elapsed * 1.0 / TimeSpan.SECOND); 63 | } 64 | 65 | if (seconds > NOTIFICATION_INTERVAL){ 66 | 67 | if (cmd_exists("notify-send")){ 68 | 69 | string desktop_entry = "timeshift-gtk"; 70 | string hint = "string:desktop-entry:%s".printf(desktop_entry); 71 | 72 | string s = "notify-send -t %d -u %s -i %s \"%s\" \"%s\" -h %s".printf( 73 | durationMillis, urgency, "gtk-dialog-" + dialog_type, title, message, hint); 74 | 75 | retVal = exec_sync (s, null, null); 76 | 77 | dt_last_notification = new DateTime.now_local(); 78 | } 79 | } 80 | 81 | return retVal; 82 | } 83 | 84 | public static bool is_supported(){ 85 | 86 | string path = get_cmd_path("notify-send"); 87 | 88 | return (path != null) && (path.length > 0); 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/Utility/RsyncSpaceCheckTask.vala: -------------------------------------------------------------------------------- 1 | /* 2 | * RsyncTask.vala 3 | * 4 | * Copyright 2012-2018 Tony George 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 2 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program; if not, write to the Free Software 18 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, 19 | * MA 02110-1301, USA. 20 | * 21 | * 22 | */ 23 | 24 | using TeeJee.Logging; 25 | using TeeJee.FileSystem; 26 | using TeeJee.JsonHelper; 27 | using TeeJee.ProcessHelper; 28 | using TeeJee.System; 29 | using TeeJee.Misc; 30 | 31 | public class RsyncSpaceCheckTask : AsyncTask{ 32 | 33 | // settings 34 | public bool delete_extra = true; 35 | public bool delete_after = false; 36 | public bool delete_excluded = false; 37 | public bool relative = false; 38 | 39 | public string exclude_from_file = ""; 40 | public string link_from_path = ""; 41 | public string source_path = ""; 42 | public string dest_path = ""; 43 | public bool verbose = true; 44 | public bool io_nice = true; 45 | public bool dry_run = false; 46 | 47 | // regex 48 | private Regex sent_bytes_regex; 49 | 50 | // status 51 | public int64 status_line_count = 0; 52 | public int64 total_size = 0; 53 | 54 | public RsyncSpaceCheckTask(){ 55 | init_regular_expressions(); 56 | } 57 | 58 | private void init_regular_expressions(){ 59 | if (sent_bytes_regex != null){ 60 | return; // already initialized 61 | } 62 | 63 | try { 64 | sent_bytes_regex = new Regex("""sent ([0-9,]+)[ \t]+bytes[ \t]+received"""); 65 | } 66 | catch (Error e) { 67 | log_error (e.message); 68 | } 69 | } 70 | 71 | public void prepare() { 72 | string script_text = build_script(); 73 | 74 | log_debug(script_text); 75 | 76 | save_bash_script_temp(script_text, script_file); 77 | log_debug("RsyncSpaceCheckTask:prepare(): saved: %s".printf(script_file)); 78 | 79 | total_size = 0; 80 | status_line_count = 0; 81 | } 82 | 83 | private string build_script() { 84 | var cmd = "export LC_ALL=C.UTF-8\n"; 85 | 86 | cmd += "rsync -aii"; 87 | 88 | cmd += " --recursive"; 89 | 90 | if (verbose){ 91 | cmd += " --verbose"; 92 | } 93 | else{ 94 | cmd += " --quiet"; 95 | } 96 | 97 | if (delete_extra){ 98 | cmd += " --delete"; 99 | } 100 | 101 | if (delete_after){ 102 | cmd += " --delete-after"; 103 | } 104 | 105 | cmd += " --force"; // allow deletion of non-empty directories 106 | 107 | cmd += " --stats"; 108 | 109 | cmd += " --sparse"; 110 | 111 | if (delete_excluded){ 112 | cmd += " --delete-excluded"; 113 | } 114 | 115 | cmd += " --dry-run"; 116 | 117 | if (link_from_path.length > 0){ 118 | if (!link_from_path.has_suffix("/")){ 119 | link_from_path = "%s/".printf(link_from_path); 120 | } 121 | 122 | cmd += " --link-dest='%s'".printf(escape_single_quote(link_from_path)); 123 | } 124 | 125 | if (exclude_from_file.length > 0){ 126 | cmd += " --exclude-from='%s'".printf(escape_single_quote(exclude_from_file)); 127 | 128 | if (delete_extra && delete_excluded){ 129 | cmd += " --delete-excluded"; 130 | } 131 | } 132 | 133 | source_path = remove_trailing_slash(source_path); 134 | 135 | dest_path = remove_trailing_slash(dest_path); 136 | 137 | cmd += " '%s/'".printf(escape_single_quote(source_path)); 138 | 139 | cmd += " '%s/'".printf(escape_single_quote(dest_path)); 140 | 141 | return cmd; 142 | } 143 | 144 | // execution ---------------------------- 145 | 146 | public void execute() { 147 | 148 | log_debug("RsyncSpaceCheckTask:execute()"); 149 | 150 | prepare(); 151 | begin(); 152 | } 153 | 154 | public override void parse_stdout_line(string out_line){ 155 | if (is_terminated) { 156 | return; 157 | } 158 | 159 | update_progress_parse_console_output(out_line); 160 | } 161 | 162 | public override void parse_stderr_line(string err_line){ 163 | if (is_terminated) { 164 | return; 165 | } 166 | 167 | update_progress_parse_console_output(err_line); 168 | } 169 | 170 | public bool update_progress_parse_console_output (string line) { 171 | if ((line == null) || (line.length == 0)) { 172 | return true; 173 | } 174 | 175 | status_line_count++; 176 | 177 | if (prg_count_total > 0){ 178 | prg_count = status_line_count; 179 | progress = (prg_count * 1.0) / prg_count_total; 180 | } 181 | 182 | MatchInfo match; 183 | 184 | if (sent_bytes_regex.match(line, 0, out match)) { 185 | total_size = int64.parse(match.fetch(1).replace(",","")); 186 | } 187 | else{ 188 | //log_debug("not-matched: %s".printf(line)); 189 | } 190 | 191 | return true; 192 | } 193 | 194 | protected override void finish_task(){ 195 | if ((status != AppStatus.CANCELLED) && (status != AppStatus.PASSWORD_REQUIRED)) { 196 | status = AppStatus.FINISHED; 197 | } 198 | } 199 | 200 | public int read_status(){ 201 | var status_file = working_dir + "/status"; 202 | var f = File.new_for_path(status_file); 203 | if (f.query_exists()){ 204 | var txt = file_read(status_file); 205 | return int.parse(txt); 206 | } 207 | return -1; 208 | } 209 | } 210 | -------------------------------------------------------------------------------- /src/Utility/SystemUser.vala: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * SystemUser.vala 4 | * 5 | * Copyright 2012-2018 Tony George 6 | * 7 | * This program is free software; you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation; either version 2 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program; if not, write to the Free Software 19 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, 20 | * MA 02110-1301, USA. 21 | * 22 | * 23 | */ 24 | 25 | using TeeJee.Logging; 26 | using TeeJee.FileSystem; 27 | using TeeJee.ProcessHelper; 28 | 29 | public class SystemUser : GLib.Object { 30 | 31 | public string name = ""; 32 | public string password = ""; 33 | public int uid = -1; 34 | public int gid = -1; 35 | public string user_info = ""; 36 | public string home_path = ""; 37 | public string shell_path = ""; 38 | 39 | public string full_name = ""; 40 | public string room_num = ""; 41 | public string phone_work = ""; 42 | public string phone_home = ""; 43 | public string other_info = ""; 44 | 45 | public bool has_encrypted_home = false; 46 | public bool has_encrypted_private_dirs = false; 47 | public Gee.ArrayList encrypted_dirs = new Gee.ArrayList(); 48 | public Gee.ArrayList encrypted_private_dirs = new Gee.ArrayList(); 49 | 50 | public bool is_selected = false; 51 | 52 | public static Gee.HashMap all_users; 53 | 54 | public SystemUser(string name){ 55 | this.name = name; 56 | } 57 | 58 | public static void query_users(){ 59 | 60 | all_users = read_users_from_file("/etc/passwd"); 61 | } 62 | 63 | public static Gee.ArrayList all_users_sorted { 64 | owned get { 65 | var list = new Gee.ArrayList(); 66 | foreach(var user in all_users.values) { 67 | list.add(user); 68 | } 69 | list.sort((a,b) => { return strcmp(a.name, b.name); }); 70 | return list; 71 | } 72 | } 73 | 74 | public static Gee.HashMap read_users_from_file(string passwd_file){ 75 | 76 | var list = new Gee.HashMap(); 77 | 78 | // read 'passwd' file --------------------------------- 79 | 80 | string txt = file_read(passwd_file); 81 | 82 | if (txt.length == 0){ 83 | return list; 84 | } 85 | 86 | foreach(string line in txt.split("\n")){ 87 | if ((line == null) || (line.length == 0)){ 88 | continue; 89 | } 90 | var user = parse_line_passwd(line); 91 | if (user != null){ 92 | list[user.name] = user; 93 | } 94 | } 95 | 96 | return list; 97 | } 98 | 99 | private static SystemUser? parse_line_passwd(string line){ 100 | 101 | if ((line == null) || (line.length == 0)){ 102 | return null; 103 | } 104 | 105 | SystemUser user = null; 106 | 107 | //teejee:x:504:504:Tony George:/home/teejee:/bin/bash 108 | string[] fields = line.split(":"); 109 | 110 | if (fields.length == 7){ 111 | user = new SystemUser(fields[0].strip()); 112 | user.password = fields[1].strip(); 113 | user.uid = int.parse(fields[2].strip()); 114 | user.gid = int.parse(fields[3].strip()); 115 | user.user_info = fields[4].strip(); 116 | user.home_path = fields[5].strip(); 117 | user.shell_path = fields[6].strip(); 118 | 119 | string[] arr = user.user_info.split(","); 120 | if (arr.length >= 1){ 121 | user.full_name = arr[0]; 122 | } 123 | if (arr.length >= 2){ 124 | user.room_num = arr[1]; 125 | } 126 | if (arr.length >= 3){ 127 | user.phone_work = arr[2]; 128 | } 129 | if (arr.length >= 4){ 130 | user.phone_home = arr[3]; 131 | } 132 | if (arr.length >= 5){ 133 | user.other_info = arr[4]; 134 | } 135 | 136 | user.check_encrypted_dirs(); 137 | } 138 | else{ 139 | log_error("'passwd' file contains a record with non-standard fields" + ": %d".printf(fields.length)); 140 | return null; 141 | } 142 | 143 | return user; 144 | } 145 | 146 | public void check_encrypted_dirs() { 147 | 148 | // check encrypted home ------------------------------ 149 | 150 | string ecryptfs_mount_file = "/home/.ecryptfs/%s/.ecryptfs/Private.mnt".printf(name); 151 | 152 | if (file_exists(ecryptfs_mount_file)){ 153 | 154 | string txt = file_read(ecryptfs_mount_file); 155 | 156 | foreach(string line in txt.split("\n")){ 157 | 158 | string path = line.strip(); 159 | 160 | if (path.length == 0){ continue; } 161 | 162 | if (path == home_path){ 163 | has_encrypted_home = true; 164 | } 165 | 166 | encrypted_dirs.add(path); 167 | } 168 | } 169 | 170 | // check encrypted Private dirs -------------------------- 171 | 172 | ecryptfs_mount_file = "%s/.ecryptfs/Private.mnt".printf(home_path); 173 | 174 | if (file_exists(ecryptfs_mount_file)){ 175 | 176 | string txt = file_read(ecryptfs_mount_file); 177 | 178 | foreach(string line in txt.split("\n")){ 179 | 180 | string path = line.strip(); 181 | 182 | if (path.length == 0){ continue; } 183 | 184 | if (path != home_path){ 185 | has_encrypted_private_dirs = true; 186 | encrypted_private_dirs.add(path); 187 | } 188 | 189 | encrypted_dirs.add(path); 190 | } 191 | } 192 | } 193 | 194 | public bool is_system{ 195 | get { 196 | return ((uid != 0) && (uid < 1000)) || (uid == 65534) || (name == "PinguyBuilder"); // 65534 - nobody 197 | } 198 | } 199 | 200 | public string group_names{ 201 | owned get { 202 | return ""; 203 | } 204 | } 205 | } 206 | 207 | -------------------------------------------------------------------------------- /src/Utility/TeeJee.Json.vala: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * TeeJee.JsonHelper.vala 4 | * 5 | * Copyright 2012-2018 Tony George 6 | * 7 | * This program is free software; you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation; either version 2 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program; if not, write to the Free Software 19 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, 20 | * MA 02110-1301, USA. 21 | * 22 | * 23 | */ 24 | 25 | using Json; 26 | 27 | namespace TeeJee.JsonHelper{ 28 | 29 | using TeeJee.Logging; 30 | 31 | /* Convenience functions for reading and writing JSON files */ 32 | 33 | public string json_get_string(Json.Object jobj, string member, string def_value){ 34 | if (jobj.has_member(member)){ 35 | return jobj.get_string_member(member); 36 | } 37 | else{ 38 | log_debug ("Member not found in JSON object: " + member); 39 | return def_value; 40 | } 41 | } 42 | 43 | public bool json_get_bool(Json.Object jobj, string member, bool def_value){ 44 | if (jobj.has_member(member)){ 45 | return bool.parse(jobj.get_string_member(member)); 46 | } 47 | else{ 48 | log_debug ("Member not found in JSON object: " + member); 49 | return def_value; 50 | } 51 | } 52 | 53 | public int json_get_int(Json.Object jobj, string member, int def_value){ 54 | if (jobj.has_member(member)){ 55 | return int.parse(jobj.get_string_member(member)); 56 | } 57 | else{ 58 | log_debug ("Member not found in JSON object: " + member); 59 | return def_value; 60 | } 61 | } 62 | 63 | public uint64 json_get_uint64(Json.Object jobj, string member, uint64 def_value){ 64 | if (jobj.has_member(member)){ 65 | return uint64.parse(jobj.get_string_member(member)); 66 | } 67 | else{ 68 | log_debug ("Member not found in JSON object: " + member); 69 | return def_value; 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/Utility/TeeJee.Logging.vala: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * TeeJee.Logging.vala 4 | * 5 | * Copyright 2012-2018 Tony George 6 | * 7 | * This program is free software; you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation; either version 2 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program; if not, write to the Free Software 19 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, 20 | * MA 02110-1301, USA. 21 | * 22 | * 23 | */ 24 | 25 | namespace TeeJee.Logging{ 26 | 27 | /* Functions for logging messages to console and log files */ 28 | 29 | using TeeJee.Misc; 30 | 31 | public DataOutputStream dos_log; 32 | public string err_log; 33 | public bool LOG_ENABLE = true; 34 | public bool LOG_TIMESTAMP = false; 35 | public bool LOG_COLORS = true; 36 | public bool LOG_DEBUG = false; 37 | public bool LOG_COMMANDS = false; 38 | 39 | public void log_msg (string message, bool highlight = false){ 40 | 41 | if (!LOG_ENABLE) { return; } 42 | 43 | string msg = ""; 44 | 45 | if (highlight && LOG_COLORS){ 46 | msg += "\033[1;38;5;34m"; 47 | } 48 | 49 | if (LOG_TIMESTAMP){ 50 | msg += "[" + timestamp(true) + "] "; 51 | } 52 | 53 | msg += message; 54 | 55 | if (highlight && LOG_COLORS){ 56 | msg += "\033[0m"; 57 | } 58 | 59 | msg += "\n"; 60 | 61 | stdout.printf (msg); 62 | stdout.flush(); 63 | 64 | try { 65 | if (dos_log != null){ 66 | dos_log.put_string ("[%s] %s\n".printf(timestamp(), message)); 67 | } 68 | } 69 | catch (Error e) { 70 | stdout.printf (e.message); 71 | } 72 | } 73 | 74 | public void log_error (string message, bool highlight = false, 75 | bool is_warning = false){ 76 | 77 | if (!LOG_ENABLE) { return; } 78 | 79 | string msg = ""; 80 | 81 | if (highlight && LOG_COLORS){ 82 | msg += "\033[1;38;5;160m"; 83 | } 84 | 85 | if (LOG_TIMESTAMP){ 86 | msg += "[" + timestamp(true) + "] "; 87 | } 88 | 89 | string prefix = (is_warning) ? "W" : "E"; 90 | 91 | msg += prefix + ": " + message; 92 | 93 | if (highlight && LOG_COLORS){ 94 | msg += "\033[0m"; 95 | } 96 | 97 | msg += "\n"; 98 | 99 | stdout.printf (msg); 100 | stdout.flush(); 101 | 102 | try { 103 | string str = "[%s] %s: %s\n".printf(timestamp(), prefix, message); 104 | 105 | if (dos_log != null){ 106 | dos_log.put_string (str); 107 | } 108 | 109 | if (err_log != null){ 110 | err_log += "%s\n".printf(message); 111 | } 112 | } 113 | catch (Error e) { 114 | stdout.printf (e.message); 115 | } 116 | } 117 | 118 | public void log_debug (string message){ 119 | if (!LOG_ENABLE) { return; } 120 | 121 | if (LOG_DEBUG){ 122 | log_msg ("D: " + message); 123 | } 124 | 125 | try { 126 | if (dos_log != null){ 127 | dos_log.put_string ("[%s] %s\n".printf(timestamp(), message)); 128 | } 129 | } 130 | catch (Error e) { 131 | stdout.printf (e.message); 132 | } 133 | } 134 | 135 | public void log_to_file (string message, bool highlight = false){ 136 | try { 137 | if (dos_log != null){ 138 | dos_log.put_string ("[%s] %s\n".printf(timestamp(), message)); 139 | } 140 | } 141 | catch (Error e) { 142 | stdout.printf (e.message); 143 | } 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /src/Utility/TeeJee.Misc.vala: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * TeeJee.Misc.vala 4 | * 5 | * Copyright 2012-2018 Tony George 6 | * 7 | * This program is free software; you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation; either version 2 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program; if not, write to the Free Software 19 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, 20 | * MA 02110-1301, USA. 21 | * 22 | * 23 | */ 24 | 25 | namespace TeeJee.Misc { 26 | 27 | /* Various utility functions */ 28 | 29 | using Gtk; 30 | using TeeJee.Logging; 31 | using TeeJee.FileSystem; 32 | using TeeJee.ProcessHelper; 33 | 34 | // localization -------------------- 35 | 36 | public void set_numeric_locale(string type){ 37 | Intl.setlocale(GLib.LocaleCategory.NUMERIC, type); 38 | Intl.setlocale(GLib.LocaleCategory.COLLATE, type); 39 | Intl.setlocale(GLib.LocaleCategory.TIME, type); 40 | } 41 | 42 | // timestamp ---------------- 43 | 44 | public string timestamp (bool show_millis = false){ 45 | 46 | /* Returns a formatted timestamp string */ 47 | 48 | // NOTE: format() does not support milliseconds 49 | 50 | DateTime now = new GLib.DateTime.now_local(); 51 | 52 | if (show_millis){ 53 | var msec = now.get_microsecond () / 1000; 54 | return "%s.%03d".printf(now.format("%H:%M:%S"), msec); 55 | } 56 | else{ 57 | return now.format ("%H:%M:%S"); 58 | } 59 | } 60 | 61 | public string timestamp_numeric(){ 62 | 63 | /* Returns a numeric timestamp string */ 64 | 65 | return "%ld".printf((long) time_t ()); 66 | } 67 | 68 | public string timestamp_for_path(){ 69 | 70 | /* Returns a formatted timestamp string */ 71 | 72 | Time t = Time.local (time_t ()); 73 | return t.format ("%Y-%m-%d_%H-%M-%S"); 74 | } 75 | 76 | // string formatting ------------------------------------------------- 77 | 78 | public string format_duration (long millis){ 79 | 80 | /* Converts time in milliseconds to format '00:00:00.0' */ 81 | 82 | double time = millis / 1000.0; // time in seconds 83 | 84 | double hr = Math.floor(time / (60.0 * 60)); 85 | time = time - (hr * 60 * 60); 86 | double min = Math.floor(time / 60.0); 87 | time = time - (min * 60); 88 | double sec = Math.floor(time); 89 | 90 | return "%02.0lf:%02.0lf:%02.0lf".printf (hr, min, sec); 91 | } 92 | 93 | public string escape_html(string html){ 94 | 95 | return GLib.Markup.escape_text(html); 96 | } 97 | 98 | public string random_string(int length = 8, string charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890"){ 99 | 100 | string random = ""; 101 | 102 | for(int i=0;i 6 | * 7 | * This program is free software; you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation; either version 2 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program; if not, write to the Free Software 19 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, 20 | * MA 02110-1301, USA. 21 | * 22 | * 23 | */ 24 | 25 | namespace TeeJee.System{ 26 | 27 | using TeeJee.ProcessHelper; 28 | using TeeJee.Logging; 29 | using TeeJee.Misc; 30 | using TeeJee.FileSystem; 31 | 32 | // user --------------------------------------------------- 33 | 34 | public bool user_is_admin(){ 35 | 36 | return (get_user_id_effective() == 0); 37 | } 38 | 39 | public int get_user_id(){ 40 | 41 | // returns actual user id of current user (even for applications executed with sudo and pkexec) 42 | 43 | string pkexec_uid = GLib.Environment.get_variable("PKEXEC_UID"); 44 | 45 | if (pkexec_uid != null){ 46 | return int.parse(pkexec_uid); 47 | } 48 | 49 | string sudo_user = GLib.Environment.get_variable("SUDO_USER"); 50 | 51 | if (sudo_user != null){ 52 | return get_user_id_from_username(sudo_user); 53 | } 54 | 55 | return get_user_id_effective(); // normal user 56 | } 57 | 58 | public int get_user_id_effective(){ 59 | 60 | // returns effective user id (0 for applications executed with sudo and pkexec) 61 | 62 | int uid = -1; 63 | string cmd = "id -u"; 64 | string std_out, std_err; 65 | exec_sync(cmd, out std_out, out std_err); 66 | if ((std_out != null) && (std_out.length > 0)){ 67 | uid = int.parse(std_out); 68 | } 69 | 70 | return uid; 71 | } 72 | 73 | public string get_username(){ 74 | 75 | // returns actual username of current user (even for applications executed with sudo and pkexec) 76 | 77 | return get_username_from_uid(get_user_id()); 78 | } 79 | 80 | public string get_username_effective(){ 81 | 82 | // returns effective user id ('root' for applications executed with sudo and pkexec) 83 | 84 | return get_username_from_uid(get_user_id_effective()); 85 | } 86 | 87 | public int get_user_id_from_username(string username){ 88 | 89 | // check local user accounts in /etc/passwd ------------------- 90 | 91 | foreach(var line in file_read("/etc/passwd").split("\n")){ 92 | 93 | var arr = line.split(":"); 94 | 95 | if ((arr.length >= 3) && (arr[0] == username)){ 96 | 97 | return int.parse(arr[2]); 98 | } 99 | } 100 | 101 | // not found -------------------- 102 | 103 | log_error("UserId not found for userName: %s".printf(username)); 104 | 105 | return -1; 106 | } 107 | 108 | public string get_username_from_uid(int user_id){ 109 | 110 | // check local user accounts in /etc/passwd ------------------- 111 | 112 | foreach(var line in file_read("/etc/passwd").split("\n")){ 113 | 114 | var arr = line.split(":"); 115 | 116 | if ((arr.length >= 3) && (arr[2] == user_id.to_string())){ 117 | 118 | return arr[0]; 119 | } 120 | } 121 | 122 | // not found -------------------- 123 | 124 | log_error("Username not found for uid: %d".printf(user_id)); 125 | 126 | return ""; 127 | } 128 | 129 | public string get_user_home(string username = get_username()){ 130 | 131 | // check local user accounts in /etc/passwd ------------------- 132 | 133 | foreach(var line in file_read("/etc/passwd").split("\n")){ 134 | 135 | var arr = line.split(":"); 136 | 137 | if ((arr.length >= 6) && (arr[0] == username)){ 138 | 139 | return arr[5]; 140 | } 141 | } 142 | 143 | // not found -------------------- 144 | 145 | log_error("Home directory not found for user: %s".printf(username)); 146 | 147 | return ""; 148 | } 149 | 150 | public string get_user_home_effective(){ 151 | return get_user_home(get_username_effective()); 152 | } 153 | 154 | // system ------------------------------------ 155 | 156 | public double get_system_uptime_seconds(){ 157 | 158 | /* Returns the system up-time in seconds */ 159 | 160 | string uptime = file_read("/proc/uptime").split(" ")[0]; 161 | double secs = double.parse(uptime); 162 | return secs; 163 | } 164 | 165 | // open ----------------------------- 166 | 167 | public bool xdg_open (string file, string user = ""){ 168 | string path = get_cmd_path ("xdg-open"); 169 | if ((path != null) && (path != "")){ 170 | string cmd = "xdg-open '%s'".printf(escape_single_quote(file)); 171 | if (user.length > 0){ 172 | cmd = "pkexec --user %s env DISPLAY=$DISPLAY XAUTHORITY=$XAUTHORITY ".printf(user) + cmd; 173 | } 174 | log_debug(cmd); 175 | int status = exec_script_async(cmd); 176 | return (status == 0); 177 | } 178 | return false; 179 | } 180 | 181 | public bool exo_open_folder (string dir_path, bool xdg_open_try_first = true){ 182 | 183 | /* Tries to open the given directory in a file manager */ 184 | 185 | /* 186 | xdg-open is a desktop-independent tool for configuring the default applications of a user. 187 | Inside a desktop environment (e.g. GNOME, KDE, Xfce), xdg-open simply passes the arguments 188 | to that desktop environment's file-opener application (gvfs-open, kde-open, exo-open, respectively). 189 | We will first try using xdg-open and then check for specific file managers if it fails. 190 | */ 191 | 192 | string path; 193 | int status; 194 | 195 | if (xdg_open_try_first){ 196 | //try using xdg-open 197 | path = get_cmd_path ("xdg-open"); 198 | if ((path != null)&&(path != "")){ 199 | string cmd = "xdg-open '%s'".printf(escape_single_quote(dir_path)); 200 | status = exec_script_async (cmd); 201 | return (status == 0); 202 | } 203 | } 204 | 205 | foreach(string app_name in 206 | new string[]{ "nemo", "nautilus", "thunar", "io.elementary.files", "pantheon-files", "marlin", "dolphin" }){ 207 | 208 | path = get_cmd_path (app_name); 209 | if ((path != null)&&(path != "")){ 210 | string cmd = "%s '%s'".printf(app_name, escape_single_quote(dir_path)); 211 | status = exec_script_async (cmd); 212 | return (status == 0); 213 | } 214 | } 215 | 216 | if (xdg_open_try_first == false){ 217 | //try using xdg-open 218 | path = get_cmd_path ("xdg-open"); 219 | if ((path != null)&&(path != "")){ 220 | string cmd = "xdg-open '%s'".printf(escape_single_quote(dir_path)); 221 | status = exec_script_async (cmd); 222 | return (status == 0); 223 | } 224 | } 225 | 226 | return false; 227 | } 228 | 229 | public bool using_efi_boot(){ 230 | 231 | /* Returns true if the system was booted in EFI mode 232 | * and false for BIOS mode */ 233 | 234 | return dir_exists("/sys/firmware/efi"); 235 | } 236 | 237 | // timers -------------------------------------------------- 238 | 239 | public GLib.Timer timer_start(){ 240 | var timer = new GLib.Timer(); 241 | timer.start(); 242 | return timer; 243 | } 244 | 245 | public ulong timer_elapsed(GLib.Timer timer, bool stop = true){ 246 | ulong microseconds; 247 | double seconds; 248 | seconds = timer.elapsed (out microseconds); 249 | if (stop){ 250 | timer.stop(); 251 | } 252 | return (ulong)((seconds * 1000 ) + (microseconds / 1000)); 253 | } 254 | 255 | public void sleep(int milliseconds){ 256 | Thread.usleep ((ulong) milliseconds * 1000); 257 | } 258 | 259 | public string timer_elapsed_string(GLib.Timer timer, bool stop = true){ 260 | ulong microseconds; 261 | double seconds; 262 | seconds = timer.elapsed (out microseconds); 263 | if (stop){ 264 | timer.stop(); 265 | } 266 | return "%.0f ms".printf((seconds * 1000 ) + microseconds/1000); 267 | } 268 | 269 | public void set_numeric_locale(string type){ 270 | Intl.setlocale(GLib.LocaleCategory.NUMERIC, type); 271 | Intl.setlocale(GLib.LocaleCategory.COLLATE, type); 272 | Intl.setlocale(GLib.LocaleCategory.TIME, type); 273 | } 274 | } 275 | -------------------------------------------------------------------------------- /src/Utility/TimeoutCounter.vala: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * TimeoutCounter.vala 4 | * 5 | * Copyright 2012-2018 Tony George 6 | * 7 | * This program is free software; you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation; either version 2 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program; if not, write to the Free Software 19 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, 20 | * MA 02110-1301, USA. 21 | * 22 | * 23 | */ 24 | 25 | 26 | using TeeJee.Logging; 27 | using TeeJee.FileSystem; 28 | using TeeJee.Misc; 29 | 30 | public class TimeoutCounter : GLib.Object { 31 | 32 | public bool active = false; 33 | public string process_to_kill = ""; 34 | public const int DEFAULT_SECONDS_TO_WAIT = 60; 35 | public int seconds_to_wait = 60; 36 | public bool exit_app = false; 37 | 38 | public void kill_process_on_timeout( 39 | string process_to_kill, int seconds_to_wait = DEFAULT_SECONDS_TO_WAIT, bool exit_app = false){ 40 | 41 | this.process_to_kill = process_to_kill; 42 | this.seconds_to_wait = seconds_to_wait; 43 | this.exit_app = exit_app; 44 | 45 | try { 46 | active = true; 47 | Thread.create (start_counter_thread, true); 48 | } 49 | catch (Error e) { 50 | log_error (e.message); 51 | } 52 | } 53 | 54 | public void exit_on_timeout(int seconds_to_wait = DEFAULT_SECONDS_TO_WAIT){ 55 | this.process_to_kill = ""; 56 | this.seconds_to_wait = seconds_to_wait; 57 | this.exit_app = true; 58 | 59 | try { 60 | active = true; 61 | Thread.create (start_counter_thread, true); 62 | } 63 | catch (Error e) { 64 | log_error (e.message); 65 | } 66 | } 67 | 68 | public void stop(){ 69 | active = false; 70 | } 71 | 72 | public void start_counter_thread(){ 73 | int secs = 0; 74 | 75 | while (active && (secs < seconds_to_wait)){ 76 | Thread.usleep((ulong) GLib.TimeSpan.MILLISECOND * 1000); 77 | secs += 1; 78 | } 79 | 80 | if (active){ 81 | active = false; 82 | stdout.printf("\n"); 83 | 84 | if (process_to_kill.length > 0){ 85 | Posix.system("killall " + process_to_kill); 86 | log_debug("[timeout] Killed process" + ": %s".printf(process_to_kill)); 87 | } 88 | 89 | if (exit_app){ 90 | log_debug("[timeout] Exit application"); 91 | exit(0); 92 | } 93 | } 94 | } 95 | } 96 | 97 | -------------------------------------------------------------------------------- /src/makefile: -------------------------------------------------------------------------------- 1 | SHELL=/bin/bash 2 | CFLAGS=--std=c99 3 | EXECUTABLES = find msgmerge msgfmt install rm mkdir cp chmod valac 4 | CHECKEXECS := $(foreach exec,$(EXECUTABLES), $(if $(shell which $(exec)),,$(error No $(exec) found, install it))) 5 | 6 | prefix=/usr 7 | sysconfdir=/etc 8 | appconfdir=$(sysconfdir)/timeshift 9 | bindir=$(prefix)/bin 10 | sharedir=$(prefix)/share 11 | polkitdir=$(sharedir)/polkit-1/actions 12 | localedir=$(sharedir)/locale 13 | launcherdir=$(sharedir)/applications 14 | mandir=$(sharedir)/man 15 | man1dir=$(mandir)/man1 16 | 17 | app_name=timeshift 18 | app_fullname=Timeshift 19 | 20 | # vte ----------------------------- 21 | 22 | vte_symbol = -D VTE_291 23 | vte_version=$(shell { (pkg-config --modversion vte-2.91 | cut -d. -f2); } ) 24 | 25 | ifeq ($(shell { test ${vte_version} -lt 52 ; echo $$? ; } ), 0) 26 | vte_symbol=$(shell echo '-D VTE_291_OLD') 27 | else 28 | vte_symbol=$(shell echo '-D VTE_291') 29 | endif 30 | 31 | # xapp ----------------------------- 32 | 33 | xapp_pkg=$(shell { ((pkg-config --exists 'xapp >= 1.0.4') >/dev/null && echo '--pkg xapp') || echo ''; } ) 34 | xapp_symbol=$(shell { ((pkg-config --exists 'xapp >= 1.0.4') >/dev/null && echo '-D XAPP') || echo ''; } ) 35 | 36 | # gtk3 ----------------------------- 37 | 38 | gtk3_version=$(shell { (pkg-config --modversion gtk+-3.0 | cut -d. -f2); } ) 39 | 40 | ifeq ($(shell { test ${gtk3_version} -gt 17 ; echo $$? ; } ), 0) 41 | gtk3_symbol=$(shell echo '-D GTK3_18') 42 | else 43 | gtk3_symbol=$(shell echo '') 44 | endif 45 | 46 | symbols=$(shell echo "${vte_symbol} ${gtk3_symbol} ${xapp_symbol}") 47 | 48 | all: app-gtk app-console 49 | 50 | app-gtk: 51 | 52 | #timeshift-gtk 53 | valac -X -D'GETTEXT_PACKAGE="${app_name}"' \ 54 | --Xcc="-lm" --Xcc="-Os" ${symbols} \ 55 | Core/*.vala Gtk/*.vala Utility/*.vala Utility/Gtk/*.vala \ 56 | -o ${app_name}-gtk \ 57 | --pkg glib-2.0 --pkg gio-unix-2.0 --pkg posix \ 58 | --pkg gee-0.8 --pkg json-glib-1.0 --pkg gio-2.0 \ 59 | --pkg gtk+-3.0 --pkg vte-2.91 $(xapp_pkg) 60 | 61 | app-console: 62 | 63 | #timeshift 64 | valac -X -D'GETTEXT_PACKAGE="${app_name}"' \ 65 | --Xcc="-lm" --Xcc="-Os" ${symbols} \ 66 | Core/*.vala Utility/*.vala Utility/Gtk/*.vala Console/*.vala \ 67 | -o ${app_name} \ 68 | --pkg glib-2.0 --pkg gio-unix-2.0 --pkg posix \ 69 | --pkg gee-0.8 --pkg json-glib-1.0 --pkg gio-2.0 \ 70 | --pkg gtk+-3.0 --pkg vte-2.91 $(xapp_pkg) 71 | 72 | manpage: 73 | ./${app_name} --help > ../man/${app_name}.1 74 | gzip -f ../man/${app_name}.1 75 | 76 | pot: 77 | 78 | # update translation template 79 | find . -iname "*.vala" | xargs xgettext \ 80 | --from-code=UTF-8 --language=C --keyword=_ \ 81 | --copyright-holder='Tony George (teejeetech@gmail.com)' \ 82 | --package-name="${app_name}" \ 83 | --package-version='18.2' \ 84 | --msgid-bugs-address='teejeetech@gmail.com' \ 85 | --escape --sort-output \ 86 | -o ../${app_name}.pot 87 | 88 | # translations 89 | for lang in am ar az bg ca cs da de el en_GB es et eu fi fr he hi hr hu ia id is it ja ko lt nb ne nl pl pt pt_BR ro ru sk sr sv tr uk vi zh_CN zh_TW; do \ 90 | msgmerge --update -v ../po/${app_name}-$$lang.po ../${app_name}.pot ; \ 91 | done 92 | 93 | clean: 94 | rm -rfv ../release/{source,i386,amd64,armel,armhf} 95 | rm -rfv ../release/*.{run,deb} 96 | rm -rfv *.c *.o *.mo 97 | rm -fv ${app_name} ${app_name}-gtk 98 | 99 | install: 100 | mkdir -p "$(DESTDIR)$(bindir)" 101 | mkdir -p "$(DESTDIR)$(sharedir)" 102 | mkdir -p "$(DESTDIR)$(mandir)" 103 | mkdir -p "$(DESTDIR)$(man1dir)" 104 | mkdir -p "$(DESTDIR)$(launcherdir)" 105 | mkdir -p "$(DESTDIR)$(polkitdir)" 106 | mkdir -p "$(DESTDIR)$(sysconfdir)" 107 | mkdir -p "$(DESTDIR)$(appconfdir)" 108 | mkdir -p "$(DESTDIR)$(sharedir)/${app_name}" 109 | mkdir -p "$(DESTDIR)$(sharedir)/icons" 110 | mkdir -p "$(DESTDIR)$(sharedir)/appdata" 111 | mkdir -p "$(DESTDIR)$(sharedir)/metainfo" 112 | mkdir -p "$(DESTDIR)$(sharedir)/pixmaps" 113 | 114 | # binary 115 | install -m 0755 ${app_name} "$(DESTDIR)$(bindir)" 116 | install -m 0755 ${app_name}-gtk "$(DESTDIR)$(bindir)" 117 | #install -m 0755 ${app_name}-uninstall "$(DESTDIR)$(bindir)" 118 | install -m 0755 ${app_name}-launcher "$(DESTDIR)$(bindir)" 119 | 120 | # shared files 121 | cp -dpr --no-preserve=ownership -t "$(DESTDIR)$(sharedir)/${app_name}" ./share/${app_name}/* 122 | find $(DESTDIR)$(sharedir)/${app_name} -type d -exec chmod 755 {} \+ 123 | find $(DESTDIR)$(sharedir)/${app_name} -type f -exec chmod 644 {} \+ 124 | 125 | # polkit 126 | install -m 0644 ./share/polkit-1/actions/in.teejeetech.pkexec.timeshift.policy "$(DESTDIR)$(polkitdir)" 127 | 128 | # launcher 129 | install -m 0755 ${app_name}-gtk.desktop "$(DESTDIR)$(launcherdir)" 130 | 131 | # config 132 | install -m 0644 ../files/${app_name}.json "$(DESTDIR)$(appconfdir)/default.json" 133 | 134 | # man page 135 | install -m 0644 ../man/${app_name}.1.gz "$(DESTDIR)$(man1dir)/${app_name}.1.gz" 136 | 137 | # app icons 138 | cp -dpr --no-preserve=ownership -t "$(DESTDIR)$(sharedir)/icons" ./share/icons/* 139 | chmod --recursive 0644 $(DESTDIR)$(sharedir)/icons/hicolor/*/apps/${app_name}.png 140 | 141 | # app icon 142 | install -m 0755 ./share/pixmaps/${app_name}.png "$(DESTDIR)$(sharedir)/pixmaps/" 143 | 144 | # appdata 145 | #install -m 0644 ../debian/${app_name}.appdata.xml "$(DESTDIR)$(sharedir)/appdata" 146 | install -m 0644 ../debian/${app_name}.appdata.xml "$(DESTDIR)$(sharedir)/metainfo" 147 | 148 | # translations 149 | for lang in am ar az bg ca cs da de el en_GB es et eu fi fr he hi hr hu ia id is it ja ko lt nb ne nl pl pt pt_BR ro ru sk sr sv tr uk vi zh_CN zh_TW; do \ 150 | mkdir -p "$(DESTDIR)$(localedir)/$$lang/LC_MESSAGES"; \ 151 | msgfmt --check --verbose -o "$(DESTDIR)$(localedir)/$$lang/LC_MESSAGES/${app_name}.mo" ../po/${app_name}-$$lang.po ; \ 152 | done 153 | 154 | uninstall: 155 | 156 | # binary 157 | rm -f "$(DESTDIR)$(bindir)/${app_name}" 158 | rm -f "$(DESTDIR)$(bindir)/${app_name}-gtk" 159 | rm -f "$(DESTDIR)$(bindir)/${app_name}-uninstall" 160 | rm -f "$(DESTDIR)$(bindir)/${app_name}-launcher" 161 | 162 | # shared files 163 | rm -rf "$(DESTDIR)$(sharedir)/${app_name}" 164 | 165 | # launcher 166 | rm -f "$(DESTDIR)$(launcherdir)/${app_name}-gtk.desktop" 167 | 168 | # polkit 169 | rm -f "$(DESTDIR)$(polkitdir)/in.teejeetech.pkexec.timeshift.policy" 170 | 171 | # man page 172 | rm -f "$(DESTDIR)$(man1dir)/${app_name}.1.gz" 173 | 174 | # app icon 175 | rm -f $(DESTDIR)$(sharedir)/icons/hicolor/*/apps/${app_name}.png 176 | 177 | # translations 178 | rm -f $(DESTDIR)$(localedir)/*/LC_MESSAGES/${app_name}.mo 179 | 180 | # appdata 181 | rm -f "$(DESTDIR)$(sharedir)/appdata/${app_name}.appdata.xml" 182 | rm -f "$(DESTDIR)$(sharedir)/metainfo/${app_name}.appdata.xml" 183 | -------------------------------------------------------------------------------- /src/share/icons/hicolor/128x128/apps/timeshift.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/teejee2008/timeshift/21570e2a2df6347843a099ea2de0da02a926aafe/src/share/icons/hicolor/128x128/apps/timeshift.png -------------------------------------------------------------------------------- /src/share/icons/hicolor/16x16/apps/timeshift.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/teejee2008/timeshift/21570e2a2df6347843a099ea2de0da02a926aafe/src/share/icons/hicolor/16x16/apps/timeshift.png -------------------------------------------------------------------------------- /src/share/icons/hicolor/22x22/apps/timeshift.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/teejee2008/timeshift/21570e2a2df6347843a099ea2de0da02a926aafe/src/share/icons/hicolor/22x22/apps/timeshift.png -------------------------------------------------------------------------------- /src/share/icons/hicolor/24x24/apps/timeshift.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/teejee2008/timeshift/21570e2a2df6347843a099ea2de0da02a926aafe/src/share/icons/hicolor/24x24/apps/timeshift.png -------------------------------------------------------------------------------- /src/share/icons/hicolor/32x32/apps/timeshift.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/teejee2008/timeshift/21570e2a2df6347843a099ea2de0da02a926aafe/src/share/icons/hicolor/32x32/apps/timeshift.png -------------------------------------------------------------------------------- /src/share/icons/hicolor/48x48/apps/timeshift.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/teejee2008/timeshift/21570e2a2df6347843a099ea2de0da02a926aafe/src/share/icons/hicolor/48x48/apps/timeshift.png -------------------------------------------------------------------------------- /src/share/icons/hicolor/64x64/apps/timeshift.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/teejee2008/timeshift/21570e2a2df6347843a099ea2de0da02a926aafe/src/share/icons/hicolor/64x64/apps/timeshift.png -------------------------------------------------------------------------------- /src/share/icons/hicolor/96x96/apps/timeshift.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/teejee2008/timeshift/21570e2a2df6347843a099ea2de0da02a926aafe/src/share/icons/hicolor/96x96/apps/timeshift.png -------------------------------------------------------------------------------- /src/share/pixmaps/timeshift.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/teejee2008/timeshift/21570e2a2df6347843a099ea2de0da02a926aafe/src/share/pixmaps/timeshift.png -------------------------------------------------------------------------------- /src/share/polkit-1/actions/in.teejeetech.pkexec.timeshift.policy: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | Tony George 7 | https://github.com/teejee2008 8 | 9 | Authentication is required to run Timeshift as Administrator 10 | Run Timeshift as Administrator 11 | timeshift 12 | 13 | auth_admin 14 | auth_admin 15 | auth_admin 16 | 17 | /usr/bin/timeshift-gtk 18 | true 19 | 20 | 21 | Authentication is required to run Timeshift as Administrator 22 | Run Timeshift as Administrator 23 | timeshift 24 | 25 | auth_admin 26 | auth_admin 27 | auth_admin 28 | 29 | /usr/bin/timeshift 30 | true 31 | 32 | 33 | -------------------------------------------------------------------------------- /src/share/timeshift/images/clock.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/teejee2008/timeshift/21570e2a2df6347843a099ea2de0da02a926aafe/src/share/timeshift/images/clock.png -------------------------------------------------------------------------------- /src/share/timeshift/images/disk.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/teejee2008/timeshift/21570e2a2df6347843a099ea2de0da02a926aafe/src/share/timeshift/images/disk.png -------------------------------------------------------------------------------- /src/share/timeshift/images/document-open-recent-symbolic.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/share/timeshift/images/document-save-symbolic.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/share/timeshift/images/donate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/teejee2008/timeshift/21570e2a2df6347843a099ea2de0da02a926aafe/src/share/timeshift/images/donate.png -------------------------------------------------------------------------------- /src/share/timeshift/images/edit-delete-symbolic.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /src/share/timeshift/images/edit-delete.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/teejee2008/timeshift/21570e2a2df6347843a099ea2de0da02a926aafe/src/share/timeshift/images/edit-delete.png -------------------------------------------------------------------------------- /src/share/timeshift/images/emblem-default-symbolic.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /src/share/timeshift/images/exclude.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/teejee2008/timeshift/21570e2a2df6347843a099ea2de0da02a926aafe/src/share/timeshift/images/exclude.png -------------------------------------------------------------------------------- /src/share/timeshift/images/folder-symbolic.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/share/timeshift/images/gtk-file.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/teejee2008/timeshift/21570e2a2df6347843a099ea2de0da02a926aafe/src/share/timeshift/images/gtk-file.png -------------------------------------------------------------------------------- /src/share/timeshift/images/include.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/teejee2008/timeshift/21570e2a2df6347843a099ea2de0da02a926aafe/src/share/timeshift/images/include.png -------------------------------------------------------------------------------- /src/share/timeshift/images/item-blue.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/teejee2008/timeshift/21570e2a2df6347843a099ea2de0da02a926aafe/src/share/timeshift/images/item-blue.png -------------------------------------------------------------------------------- /src/share/timeshift/images/item-gray.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/teejee2008/timeshift/21570e2a2df6347843a099ea2de0da02a926aafe/src/share/timeshift/images/item-gray.png -------------------------------------------------------------------------------- /src/share/timeshift/images/item-green.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/teejee2008/timeshift/21570e2a2df6347843a099ea2de0da02a926aafe/src/share/timeshift/images/item-green.png -------------------------------------------------------------------------------- /src/share/timeshift/images/item-red.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/teejee2008/timeshift/21570e2a2df6347843a099ea2de0da02a926aafe/src/share/timeshift/images/item-red.png -------------------------------------------------------------------------------- /src/share/timeshift/images/item-yellow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/teejee2008/timeshift/21570e2a2df6347843a099ea2de0da02a926aafe/src/share/timeshift/images/item-yellow.png -------------------------------------------------------------------------------- /src/share/timeshift/images/list-add.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/teejee2008/timeshift/21570e2a2df6347843a099ea2de0da02a926aafe/src/share/timeshift/images/list-add.png -------------------------------------------------------------------------------- /src/share/timeshift/images/list-remove.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/teejee2008/timeshift/21570e2a2df6347843a099ea2de0da02a926aafe/src/share/timeshift/images/list-remove.png -------------------------------------------------------------------------------- /src/share/timeshift/images/locked.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/teejee2008/timeshift/21570e2a2df6347843a099ea2de0da02a926aafe/src/share/timeshift/images/locked.png -------------------------------------------------------------------------------- /src/share/timeshift/images/media-optical.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/teejee2008/timeshift/21570e2a2df6347843a099ea2de0da02a926aafe/src/share/timeshift/images/media-optical.png -------------------------------------------------------------------------------- /src/share/timeshift/images/open-menu-symbolic.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /src/share/timeshift/images/preferences-system-symbolic.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/share/timeshift/images/timeshift-shield-high.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/share/timeshift/images/timeshift-shield-low.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/share/timeshift/images/timeshift-shield-med.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/share/timeshift/images/unlocked.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/teejee2008/timeshift/21570e2a2df6347843a099ea2de0da02a926aafe/src/share/timeshift/images/unlocked.png -------------------------------------------------------------------------------- /src/timeshift-gtk.desktop: -------------------------------------------------------------------------------- 1 | [Desktop Entry] 2 | Name=Timeshift 3 | MimeType= 4 | Exec=timeshift-launcher 5 | Type=Application 6 | GenericName=System Restore Utility 7 | GenericName[ca]=Utilitat de restauració del sistema 8 | GenericName[cs]=Nástroj pro obnovení systému 9 | GenericName[da]=Værktøj til systemgendannelse 10 | GenericName[fr]=Utilitaire de restauration système 11 | GenericName[hr]=Alat obnove sustava 12 | GenericName[ja]=システムの復元ユーティリティ 13 | GenericName[ko]=시스템 복원 유틸리티 14 | GenericName[lt]=Sistemos atkūrimo paslaugų programa 15 | GenericName[nl]=Hulpmiddel voor systeemherstel 16 | GenericName[ru]=Программа для восстановления системы 17 | Terminal=false 18 | Icon=timeshift 19 | Comment=System Restore Utility 20 | Comment[ca]=Utilitat de restauració del sistema 21 | Comment[cs]=Nástroj pro obnovení systému 22 | Comment[da]=Værktøj til systemgendannelse 23 | Comment[fr]=Utilitaire de restauration système 24 | Comment[hr]=Alat obnove sustava 25 | Comment[ja]=システムの復元ユーティリティ 26 | Comment[ko]=시스템 복원 유틸리티 27 | Comment[lt]=Sistemos atkūrimo paslaugų programa 28 | Comment[nl]=Hulpmiddel voor systeemherstel 29 | Comment[ru]=Программа для восстановления системы 30 | X-KDE-StartupNotify=false 31 | Categories=System; 32 | X-GNOME-UsesNotifications=true 33 | -------------------------------------------------------------------------------- /src/timeshift-launcher: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | app_command='timeshift-gtk' 4 | 5 | if [ "$(id -u)" -eq 0 ]; then 6 | # user is admin 7 | ${app_command} 8 | else 9 | # user is not admin 10 | if echo $- | grep "i" >/dev/null 2>&1; then 11 | # script is running in interactive mode 12 | su - -c "${app_command}" 13 | else 14 | # script is running in non-interactive mode 15 | if [ "$XDG_SESSION_TYPE" = "wayland" ] ; then 16 | xhost +SI:localuser:root 17 | pkexec ${app_command} 18 | xhost -SI:localuser:root 19 | xhost 20 | elif command -v pkexec >/dev/null 2>&1; then 21 | pkexec ${app_command} 22 | elif command -v sudo >/dev/null 2>&1; then 23 | x-terminal-emulator -e "sudo ${app_command}" 24 | elif command -v su >/dev/null 2>&1; then 25 | x-terminal-emulator -e "su - -c '${app_command}'" 26 | else 27 | x-terminal-emulator -e "echo 'Command must be run as root user: ${app_command}'" 28 | fi 29 | fi 30 | fi 31 | -------------------------------------------------------------------------------- /src/timeshift-uninstall: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | app_name='timeshift' 4 | app_fullname='Timeshift' 5 | 6 | Reset='\e[0m' 7 | Red='\e[1;31m' 8 | Green='\e[1;32m' 9 | Yellow='\e[1;33m' 10 | 11 | CHECK_COLOR_SUPPORT() { 12 | colors=`tput colors` 13 | if [ $colors -gt 1 ]; then 14 | COLORS_SUPPORTED=0 15 | else 16 | COLORS_SUPPORTED=1 17 | fi 18 | } 19 | 20 | MSG_INFO() { 21 | add_newline='' 22 | if [ "$2" == 0 ]; then 23 | add_newline='-n' 24 | fi 25 | 26 | if [ $COLORS_SUPPORTED -eq 0 ]; then 27 | echo -e ${add_newline} "[${Yellow}*${Reset}] ${Green}$1${Reset}" 28 | else 29 | echo -e ${add_newline} "[*] $1" 30 | fi 31 | } 32 | 33 | MSG_WARNING() { 34 | add_newline='' 35 | if [ "$2" == 0 ]; then 36 | add_newline='-n' 37 | fi 38 | 39 | if [ $COLORS_SUPPORTED -eq 0 ]; then 40 | echo -e ${add_newline} "[${Red}!${Reset}] ${Yellow}$1${Reset}" 41 | else 42 | echo -e ${add_newline} "[!] $1" 43 | fi 44 | } 45 | 46 | MSG_ERROR() { 47 | add_newline='' 48 | if [ "$2" == 0 ]; then 49 | add_newline='-n' 50 | fi 51 | 52 | if [ $COLORS_SUPPORTED -eq 0 ]; then 53 | echo -e ${add_newline} "[${Red}X${Reset}] ${Yellow}$1${Reset}" 54 | else 55 | echo -e ${add_newline} "[X] $1" 56 | fi 57 | } 58 | 59 | CD_PUSH() { 60 | cd_backup=`pwd` 61 | } 62 | 63 | CD_POP() { 64 | if [ ! -z "${cd_backup}" ]; then 65 | cd "${cd_backup}" 66 | fi 67 | } 68 | 69 | EXIT(){ 70 | CD_POP 71 | exit $1 72 | } 73 | 74 | WAIT_FOR_INPUT() { 75 | echo "" 76 | echo "Press any key to exit..." 77 | read dummy 78 | } 79 | 80 | GET_SCRIPT_PATH(){ 81 | SCRIPTPATH="$(cd "$(dirname "$0")" && pwd)" 82 | SCRIPTNAME=`basename $0` 83 | } 84 | 85 | RUN_AS_ADMIN() { 86 | if [ ! `id -u` -eq 0 ]; then 87 | GET_SCRIPT_PATH 88 | if command -v sudo >/dev/null 2>&1; then 89 | sudo "${SCRIPTPATH}/${SCRIPTNAME}" 90 | EXIT $? 91 | elif command -v su >/dev/null 2>&1; then 92 | su -c "${SCRIPTPATH}/${SCRIPTNAME}" 93 | EXIT $? 94 | else 95 | echo "" 96 | MSG_ERROR "** Uninstaller must be run as Admin (using 'sudo' or 'su') **" 97 | echo "" 98 | EXIT 1 99 | fi 100 | fi 101 | } 102 | 103 | CD_PUSH 104 | CHECK_COLOR_SUPPORT 105 | RUN_AS_ADMIN 106 | 107 | if [ ! -z "${app_name}" ]; then 108 | rm -f "/usr/bin/${app_name}" 109 | rm -f "/usr/bin/${app_name}-uninstall" 110 | rm -f "/usr/share/applications/${app_name}.desktop" 111 | rm -f "/usr/share/pixmaps/${app_name}.png" 112 | rm -rf "/usr/share/${app_name}" 113 | rm -rf "/usr/share/doc/${app_name}" 114 | rm -f /usr/share/locale/*/LC_MESSAGES/${app_name}.mo 115 | 116 | if [ -d /timeshift ]; then 117 | remove_backups=n 118 | MSG_INFO "Remove backups on system partition? (y/n):" "0" 119 | read remove_backups 120 | if [ "$remove_backups" == "" ]; then 121 | remove_backups=n 122 | fi 123 | 124 | if [ "$remove_backups" == "y" ]; then 125 | MSG_INFO "Freeing disk space..." 126 | echo "" 127 | rm -rf "/timeshift" 128 | fi 129 | fi 130 | fi 131 | 132 | if [ $? -eq 0 ]; then 133 | echo "${app_fullname} was uninstalled successfully." 134 | EXIT 0 135 | else 136 | echo "Uninstalled completed (some files could not be removed)" 137 | EXIT 1 138 | fi 139 | 140 | CD_POP 141 | --------------------------------------------------------------------------------