~umang/indicator-stickynotes/trunk

24 by Umang Varma
Added .desktop and setup.py files.
1
#!/usr/bin/python3
2
# 
154 by Umang Varma
Release 0.5.7
3
# Copyright © 2012-2016 Umang Varma <umang.me@gmail.com>
24 by Umang Varma
Added .desktop and setup.py files.
4
# 
5
# This file is part of indicator-stickynotes.
6
# 
7
# indicator-stickynotes is free software: you can redistribute it and/or
8
# modify it under the terms of the GNU General Public License as published by
9
# the Free Software Foundation, either version 3 of the License, or (at your
10
# option) any later version.
11
# 
12
# indicator-stickynotes is distributed in the hope that it will be useful, but
13
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
14
# or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
15
# more details.
16
# 
17
# You should have received a copy of the GNU General Public License along with
18
# indicator-stickynotes.  If not, see <http://www.gnu.org/licenses/>.
19
20
from stickynotes.backend import Note, NoteSet
127 by Umang Varma
Prompt backup when error reading data file
21
from stickynotes.gui import *
62 by Umang Varma
Added -d parameter for development data file
22
import stickynotes.info
36 by Umang Varma
Start using python's locale for l10n
23
from stickynotes.info import MO_DIR, LOCALE_DOMAIN
32 by Umang Varma
Save more often so changes aren't lost
24
148 by Umang Varma
gi.require_version for future-proofing
25
import gi
26
gi.require_version('Gtk', '3.0')
27
gi.require_version('GtkSource', '3.0')
28
gi.require_version('AppIndicator3', '0.1')
24 by Umang Varma
Added .desktop and setup.py files.
29
from gi.repository import Gtk, Gdk
30
from gi.repository import AppIndicator3 as appindicator
32 by Umang Varma
Save more often so changes aren't lost
31
30 by Umang Varma
Added ubuntu-mono themed icons.
32
import os.path
36 by Umang Varma
Start using python's locale for l10n
33
import locale
62 by Umang Varma
Added -d parameter for development data file
34
import argparse
36 by Umang Varma
Start using python's locale for l10n
35
from locale import gettext as _
32 by Umang Varma
Save more often so changes aren't lost
36
from functools import wraps
127 by Umang Varma
Prompt backup when error reading data file
37
from shutil import copyfile, SameFileError
32 by Umang Varma
Save more often so changes aren't lost
38
157.1.1 by Zheng-Ling Lai
Avoid duplicate process
39
import socket
40
import sys
41
32 by Umang Varma
Save more often so changes aren't lost
42
def save_required(f):
43
    """Wrapper for functions that require a save after execution"""
44
    @wraps(f)
45
    def _wrapper(self, *args, **kwargs):
46
        ret = f(self, *args, **kwargs)
47
        self.save()
48
        return ret
49
    return _wrapper
24 by Umang Varma
Added .desktop and setup.py files.
50
51
class IndicatorStickyNotes:
62 by Umang Varma
Added -d parameter for development data file
52
    def __init__(self, args = None):
53
        self.args = args
54
        # use development data file if requested
55
        isdev = args and args.d
127 by Umang Varma
Prompt backup when error reading data file
56
        self.data_file = stickynotes.info.DEBUG_SETTINGS_FILE if isdev \
57
                else stickynotes.info.SETTINGS_FILE
24 by Umang Varma
Added .desktop and setup.py files.
58
        # Initialize NoteSet
127 by Umang Varma
Prompt backup when error reading data file
59
        self.nset = NoteSet(StickyNote, self.data_file, self)
60
        try:
61
            self.nset.open()
62
        except FileNotFoundError:
63
            self.nset.load_fresh()
64
        except Exception as e:
65
            err = _("Error reading data file. Do you want to "
66
                "backup the current data?")
67
            winError = Gtk.MessageDialog(None, None, Gtk.MessageType.ERROR,
68
                    Gtk.ButtonsType.NONE, err)
69
            winError.add_buttons(Gtk.STOCK_CANCEL, Gtk.ResponseType.REJECT,
70
                    _("Backup"), Gtk.ResponseType.ACCEPT)
149 by Umang Varma
Error title indicates which app has error (confusing on startup)
71
            winError.set_title(_("Indicator Stickynotes"))
127 by Umang Varma
Prompt backup when error reading data file
72
            resp = winError.run()
73
            winError.hide()
74
            if resp == Gtk.ResponseType.ACCEPT:
75
                self.backup_datafile()
76
            winError.destroy()
77
            self.nset.load_fresh()
78
102 by Umang Varma
Don't initialize gui just before hiding window
79
        # If all notes were visible previously, show them now
58 by Umang Varma
Remember hidden state on startup
80
        if self.nset.properties.get("all_visible", True):
81
            self.nset.showall()
24 by Umang Varma
Added .desktop and setup.py files.
82
        # Create App Indicator
83
        self.ind = appindicator.Indicator.new(
84
                "Sticky Notes", "indicator-stickynotes",
85
                appindicator.IndicatorCategory.APPLICATION_STATUS)
30 by Umang Varma
Added ubuntu-mono themed icons.
86
        # Delete/modify the following file when distributing as a package
87
        self.ind.set_icon_theme_path(os.path.abspath(os.path.join(
88
            os.path.dirname(__file__), 'Icons')))
153 by Umang Varma
Updated menu icon; fixed copying error for logos
89
        self.ind.set_icon("indicator-stickynotes-mono")
24 by Umang Varma
Added .desktop and setup.py files.
90
        self.ind.set_status(appindicator.IndicatorStatus.ACTIVE)
36 by Umang Varma
Start using python's locale for l10n
91
        self.ind.set_title(_("Sticky Notes"))
24 by Umang Varma
Added .desktop and setup.py files.
92
        # Create Menu
93
        self.menu = Gtk.Menu()
36 by Umang Varma
Start using python's locale for l10n
94
        self.mNewNote = Gtk.MenuItem(_("New Note"))
24 by Umang Varma
Added .desktop and setup.py files.
95
        self.menu.append(self.mNewNote)
96
        self.mNewNote.connect("activate", self.new_note, None)
97
        self.mNewNote.show()
98
99
        s = Gtk.SeparatorMenuItem.new()
100
        self.menu.append(s)
101
        s.show()
102
36 by Umang Varma
Start using python's locale for l10n
103
        self.mShowAll = Gtk.MenuItem(_("Show All"))
24 by Umang Varma
Added .desktop and setup.py files.
104
        self.menu.append(self.mShowAll)
105
        self.mShowAll.connect("activate", self.showall, None)
106
        self.mShowAll.show()
107
36 by Umang Varma
Start using python's locale for l10n
108
        self.mHideAll = Gtk.MenuItem(_("Hide All"))
24 by Umang Varma
Added .desktop and setup.py files.
109
        self.menu.append(self.mHideAll)
110
        self.mHideAll.connect("activate", self.hideall, None)
111
        self.mHideAll.show()
112
113
        s = Gtk.SeparatorMenuItem.new()
114
        self.menu.append(s)
115
        s.show()
116
36 by Umang Varma
Start using python's locale for l10n
117
        self.mLockAll = Gtk.MenuItem(_("Lock All"))
24 by Umang Varma
Added .desktop and setup.py files.
118
        self.menu.append(self.mLockAll)
119
        self.mLockAll.connect("activate", self.lockall, None)
120
        self.mLockAll.show()
121
36 by Umang Varma
Start using python's locale for l10n
122
        self.mUnlockAll = Gtk.MenuItem(_("Unlock All"))
24 by Umang Varma
Added .desktop and setup.py files.
123
        self.menu.append(self.mUnlockAll)
124
        self.mUnlockAll.connect("activate", self.unlockall, None)
125
        self.mUnlockAll.show()
126
127
        s = Gtk.SeparatorMenuItem.new()
128
        self.menu.append(s)
129
        s.show()
130
128 by Umang Varma
Implement import/export feature
131
        self.mExport = Gtk.MenuItem(_("Export Data"))
132
        self.menu.append(self.mExport)
133
        self.mExport.connect("activate", self.export_datafile, None)
134
        self.mExport.show()
135
136
        self.mImport = Gtk.MenuItem(_("Import Data"))
137
        self.menu.append(self.mImport)
138
        self.mImport.connect("activate", self.import_datafile, None)
139
        self.mImport.show()
140
141
        s = Gtk.SeparatorMenuItem.new()
142
        self.menu.append(s)
143
        s.show()
144
41 by Umang Varma
Added about dialog
145
        self.mAbout = Gtk.MenuItem(_("About"))
146
        self.menu.append(self.mAbout)
147
        self.mAbout.connect("activate", self.show_about, None)
148
        self.mAbout.show()
149
47 by Umang Varma
Added settings dialog, which controls bgcolor
150
        self.mSettings = Gtk.MenuItem(_("Settings"))
151
        self.menu.append(self.mSettings)
152
        self.mSettings.connect("activate", self.show_settings, None)
153
        self.mSettings.show()
154
41 by Umang Varma
Added about dialog
155
        s = Gtk.SeparatorMenuItem.new()
156
        self.menu.append(s)
157
        s.show()
158
36 by Umang Varma
Start using python's locale for l10n
159
        self.mQuit = Gtk.MenuItem(_("Quit"))
24 by Umang Varma
Added .desktop and setup.py files.
160
        self.menu.append(self.mQuit)
161
        self.mQuit.connect("activate", Gtk.main_quit, None)
162
        self.mQuit.show()
163
        # Connect Indicator to menu
164
        self.ind.set_menu(self.menu)
165
63 by Umang Varma
Middle-click toggles Show/Hide All.
166
        # Define secondary action (middle click)
167
        self.connect_secondary_activate()
168
24 by Umang Varma
Added .desktop and setup.py files.
169
    def new_note(self, *args):
170
        self.nset.new()
171
172
    def showall(self, *args):
173
        self.nset.showall(*args)
63 by Umang Varma
Middle-click toggles Show/Hide All.
174
        self.connect_secondary_activate()
24 by Umang Varma
Added .desktop and setup.py files.
175
176
    def hideall(self, *args):
177
        self.nset.hideall()
63 by Umang Varma
Middle-click toggles Show/Hide All.
178
        self.connect_secondary_activate()
179
180
    def connect_secondary_activate(self):
181
        """Define action of secondary action (middle click) depending
182
        on visibility state of notes."""
183
        if self.nset.properties["all_visible"] == True:
184
            self.ind.set_secondary_activate_target(self.mHideAll)
185
        else:
186
            self.ind.set_secondary_activate_target(self.mShowAll)
187
24 by Umang Varma
Added .desktop and setup.py files.
188
32 by Umang Varma
Save more often so changes aren't lost
189
    @save_required
24 by Umang Varma
Added .desktop and setup.py files.
190
    def lockall(self, *args):
191
        for note in self.nset.notes:
104 by Umang Varma
Fix some issues with gui = None
192
            note.set_locked_state(True)
24 by Umang Varma
Added .desktop and setup.py files.
193
        
32 by Umang Varma
Save more often so changes aren't lost
194
    @save_required
24 by Umang Varma
Added .desktop and setup.py files.
195
    def unlockall(self, *args):
196
        for note in self.nset.notes:
104 by Umang Varma
Fix some issues with gui = None
197
            note.set_locked_state(False)
24 by Umang Varma
Added .desktop and setup.py files.
198
128 by Umang Varma
Implement import/export feature
199
    def backup_datafile(self):
200
        winChoose = Gtk.FileChooserDialog(_("Export Data"), None,
201
                Gtk.FileChooserAction.SAVE, (Gtk.STOCK_CANCEL,
202
                    Gtk.ResponseType.CANCEL, Gtk.STOCK_SAVE,
203
                    Gtk.ResponseType.ACCEPT))
204
        winChoose.set_do_overwrite_confirmation(True)
205
        response = winChoose.run()
206
        backupfile = None
207
        if response == Gtk.ResponseType.ACCEPT:
208
            backupfile =  winChoose.get_filename()
209
        winChoose.destroy()
210
        if backupfile:
211
            try:
212
                copyfile(os.path.expanduser(self.data_file), backupfile)
213
            except SameFileError:
214
                err = _("Please choose a different "
215
                    "destination for the backup file.")
216
                winError = Gtk.MessageDialog(None, None,
217
                        Gtk.MessageType.ERROR, Gtk.ButtonsType.CLOSE, err)
218
                winError.run()
219
                winError.destroy()
220
                self.backup_datafile()
221
222
    def export_datafile(self, *args):
223
        self.backup_datafile()
224
225
    def import_datafile(self, *args):
226
        winChoose = Gtk.FileChooserDialog(_("Import Data"), None,
227
                Gtk.FileChooserAction.OPEN, (Gtk.STOCK_CANCEL,
228
                    Gtk.ResponseType.CANCEL, Gtk.STOCK_OPEN,
229
                    Gtk.ResponseType.ACCEPT))
230
        response = winChoose.run()
231
        backupfile = None
232
        if response == Gtk.ResponseType.ACCEPT:
233
            backupfile =  winChoose.get_filename()
234
        winChoose.destroy()
235
        if backupfile:
236
            try:
237
                with open(backupfile, encoding="utf-8") as fsock:
238
                    self.nset.merge(fsock.read())
239
            except Exception as e:
240
                err = _("Error importing data.")
241
                winError = Gtk.MessageDialog(None, None,
242
                        Gtk.MessageType.ERROR, Gtk.ButtonsType.CLOSE, err)
243
                winError.run()
244
                winError.destroy()
245
41 by Umang Varma
Added about dialog
246
    def show_about(self, *args):
247
        show_about_dialog()
248
47 by Umang Varma
Added settings dialog, which controls bgcolor
249
    def show_settings(self, *args):
250
        wSettings = SettingsDialog(self.nset)
251
24 by Umang Varma
Added .desktop and setup.py files.
252
    def save(self):
253
        self.nset.save()
254
255
def main():
157.1.1 by Zheng-Ling Lai
Avoid duplicate process
256
    # Avoid duplicate process
257
    # From https://stackoverflow.com/questions/788411/check-to-see-if-python-script-is-running
258
    procLock = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM)
259
    try:
260
        procLock.bind('\0' + 'indicator-stickynotes')
261
    except socket.error:
262
        print('Indicator stickynotes already running.')
263
        sys.exit()
264
36 by Umang Varma
Start using python's locale for l10n
265
    try:
266
        locale.setlocale(locale.LC_ALL, '')
267
    except:
268
        locale.setlocale(locale.LC_ALL, 'C')
269
    # If we're running from /usr, then .mo files are not in MO_DIR.
270
    if os.path.abspath(__file__)[:4] == '/usr':
271
        # Fallback to default
272
        locale_dir = None
273
    else:
39 by Umang Varma
Find locale_dir correctly
274
        locale_dir = os.path.join(os.path.dirname(__file__), MO_DIR)
36 by Umang Varma
Start using python's locale for l10n
275
    locale.bindtextdomain(LOCALE_DOMAIN, locale_dir)
276
    locale.textdomain(LOCALE_DOMAIN)
62 by Umang Varma
Added -d parameter for development data file
277
77 by Umang Varma
Updated pot file and some strings
278
    parser = argparse.ArgumentParser(description=_("Sticky Notes"))
62 by Umang Varma
Added -d parameter for development data file
279
    parser.add_argument("-d", action='store_true', help="use the development"
280
            " data file")
281
    args = parser.parse_args()
282
283
    indicator = IndicatorStickyNotes(args)
46 by Umang Varma
Notes can now change color.
284
    # Load global css for the first time.
285
    load_global_css()
24 by Umang Varma
Added .desktop and setup.py files.
286
    Gtk.main()
287
    indicator.save()
288
289
if __name__ == "__main__":
290
    main()