/gtk/eog-manage-raws

To get this branch, use:
bzr branch http://bzr.ed.am/gtk/eog-manage-raws
8 by edam
renamed project "manage raws"
1
# manage-raws.py
4 by edam
- added unkeep command
2
#
3
# Copyright (C) 2011 Tim Marston <edam@waxworlds.org>
4
#
8 by edam
renamed project "manage raws"
5
# This file is part of Manage Raws (hereafter referred to as "this
6
# program").  See http://ed.am/dev/gtk/eog-manage-raws for more
4 by edam
- added unkeep command
7
# information.
8
#
9
# This program is free software: you can redistribute it and/or modify
10
# it under the terms of the GNU General Public License as published by
11
# the Free Software Foundation, either version 3 of the License, or
12
# (at your option) any later version.
13
#
14
# This program is distributed in the hope that it will be useful,
15
# but WITHOUT ANY WARRANTY; without even the implied warranty of
16
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17
# GNU General Public License for more details.
18
#
19
# You should have received a copy of the GNU General Public License
20
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
21
22
23
# version 0.1
24
7 by edam
switch to gir Gio
25
from gi.repository import GObject, Gtk, Eog, Gio
26
import os, re
5.1.1 by edam
updated for GTK 3.0
27
8 by edam
renamed project "manage raws"
28
class ManageRawsPlugin( GObject.Object, Eog.WindowActivatable ):
5.1.1 by edam
updated for GTK 3.0
29
30
    # Override EogWindowActivatable's window property
31
    window = GObject.property( type = Eog.Window )
32
33
    # list of file extension to recognise as RAW files
1 by edam
initial comit
34
    raw_file_extensions = [ 'cr2', '3fr', 'ari', 'arw', 'srf', 'sr2', 'bay',
35
        'crw', 'cr2', 'cap', 'iiq', 'eip', 'dcs', 'dcr', 'drf', 'k25', 'kdc',
36
        'dng', 'erf', 'fff', 'mef', 'mos', 'mrw', 'nef', 'nrw', 'orf', 'ptx',
37
        'pef', 'pxn', 'r3d', 'raf', 'raw', 'rw2', 'rwl', 'rwz', 'x3f' ]
38
39
    def __init__( self ):
5.1.1 by edam
updated for GTK 3.0
40
        GObject.Object.__init__( self )
1 by edam
initial comit
41
5.1.1 by edam
updated for GTK 3.0
42
    def do_activate( self ):
1 by edam
initial comit
43
44
        # insert menu item
5.1.1 by edam
updated for GTK 3.0
45
        ui_manager = self.window.get_ui_manager()
8 by edam
renamed project "manage raws"
46
        self.action_group = Gtk.ActionGroup( 'EogManageRawsPluginActions' )
5.1.1 by edam
updated for GTK 3.0
47
        self.action_group.add_actions( [ ( 'EogPluginRunKeepRaw', None,
11 by edam
refer to raw files as files, not images
48
            _( 'Keep Raw File' ), 'K',
2 by edam
- added status bar information
49
            _( "Move the accompanying raw file to a 'raw' subdirectory" ),
11 by edam
refer to raw files as files, not images
50
            self.do_keep_raw_file ) ], self.window )
5.1.1 by edam
updated for GTK 3.0
51
        self.action_group.add_actions( [ ( 'EogPluginRunUnkeepRaw', None,
11 by edam
refer to raw files as files, not images
52
            _( 'Unkeep Raw File' ), 'U',
4 by edam
- added unkeep command
53
            _( "Move the accompanying raw file back to the image's directory" ),
11 by edam
refer to raw files as files, not images
54
            self.do_unkeep_raw_file ) ], self.window )
5.1.1 by edam
updated for GTK 3.0
55
        self.action_group.add_actions( [ ( 'EogPluginRunDeleteRaw', None,
11 by edam
refer to raw files as files, not images
56
            _( 'Delete Unkept Raw Files' ), None,
2 by edam
- added status bar information
57
            _( "Delete all raw files in the current image's directory (the unkept ones)" ),
11 by edam
refer to raw files as files, not images
58
            self.do_delete_raw_files ) ], self.window )
5.1.1 by edam
updated for GTK 3.0
59
        ui_manager.insert_action_group( self.action_group, -1 )
60
        self.ui_id = ui_manager.add_ui_from_string( """
1 by edam
initial comit
61
            <ui>
62
                <menubar name="MainMenu">
63
                    <menu name="ToolsMenu" action="Tools">
64
                        <separator />
2 by edam
- added status bar information
65
                        <menuitem name="EogPluginRunKeepRaw"
1 by edam
initial comit
66
                            action="EogPluginRunKeepRaw" />
4 by edam
- added unkeep command
67
                        <menuitem name="EogPluginRunUnkeepRaw"
68
                            action="EogPluginRunUnkeepRaw" />
2 by edam
- added status bar information
69
                        <menuitem name="EogPluginRunDeleteRaw"
70
                            action="EogPluginRunDeleteRaw" />
1 by edam
initial comit
71
                        <separator />
72
                    </menu>
73
                </menubar>
74
                <popup name="ViewPopup">
75
                    <separator />
76
                    <menuitem action="EogPluginRunKeepRaw" />
77
                    <separator />
78
                </popup>
79
            </ui>
80
        """ )
81
2 by edam
- added status bar information
82
        # insert status bar
5.1.1 by edam
updated for GTK 3.0
83
        self.statusbar = Gtk.Statusbar()
84
        #self.statusbar.set_size_request( 100, 10 )
85
        statusbar = self.window.get_statusbar()
86
        statusbar.pack_end( self.statusbar, False, False, 0 );
87
        self.statusbar.show()
2 by edam
- added status bar information
88
1 by edam
initial comit
89
        # connect to selection change
5.1.1 by edam
updated for GTK 3.0
90
        thumb_view = self.window.get_thumb_view()
91
        self.on_selection_change_id = thumb_view.connect_after( \
92
            'selection_changed', self.on_selection_change, self.window )
1 by edam
initial comit
93
94
        # init ui state
5.1.1 by edam
updated for GTK 3.0
95
        self.update_status( self.window )
96
97
    def do_deactivate( self ):
98
99
        # remove menu items
100
        ui_manager = self.window.get_ui_manager()
101
        ui_manager.remove_ui( self.ui_id )
102
        self.ui_id = 0
103
        ui_manager.remove_action_group( self.action_group )
104
        self.action_group = None
105
        ui_manager.ensure_update()
1 by edam
initial comit
106
2 by edam
- added status bar information
107
        # disconnect handlers
5.1.1 by edam
updated for GTK 3.0
108
        thumb_view = self.window.get_thumb_view()
109
        thumb_view.disconnect( self.on_selection_change_id )
1 by edam
initial comit
110
2 by edam
- added status bar information
111
        # remove status bar entry
5.1.1 by edam
updated for GTK 3.0
112
        statusbar =  self.window.get_statusbar()
113
        Gtk.Container.remove( statusbar, self.statusbar )
1 by edam
initial comit
114
115
    def on_selection_change( self, view, data ):
2 by edam
- added status bar information
116
        self.update_status( data )
117
118
    def get_raw_filename_from_image( self, image ):
1 by edam
initial comit
119
        fname = image.get_file().get_path()
120
2 by edam
- added status bar information
121
        # get the base fileanme (with no extension), read for searching
1 by edam
initial comit
122
        base_fname = image.get_file().get_path()
123
        pos = base_fname.rfind( '.' )
124
        if( pos != -1 ):
2 by edam
- added status bar information
125
126
            # check if the extension of the current image makes it a raw file
127
            for ext in self.raw_file_extensions + \
128
                map( lambda x: x.upper(), self.raw_file_extensions ):
129
                if( base_fname[ pos : ] == ext ):
130
                    return False;
131
132
            # remove extension from base filename
1 by edam
initial comit
133
            base_fname = base_fname[ 0 : pos ]
134
        
135
        # check for stupidity
136
        if( base_fname[ -1 : None ] == '/' ):
2 by edam
- added status bar information
137
            return False
138
139
        # loop through valid raw file extensions, uppercase and lowercase
140
        for ext in self.raw_file_extensions + \
141
            map( lambda x: x.upper(), self.raw_file_extensions ):
142
143
            # if the raw file exists, we found it
144
            if( os.path.isfile( base_fname + '.' + ext ) ):
145
                return os.path.basename( base_fname + '.' + ext );
1 by edam
initial comit
146
11 by edam
refer to raw files as files, not images
147
        # path the raw files will be moved to
1 by edam
initial comit
148
        raw_path = os.path.dirname( fname ) + '/raw';
149
2 by edam
- added status bar information
150
        # loop through valid raw file extensions, uppercase and lowercase
1 by edam
initial comit
151
        for ext in self.raw_file_extensions + \
2 by edam
- added status bar information
152
            map( lambda x: x.upper(), self.raw_file_extensions ):
153
154
            # if the raw file exists, we found it
155
            if( os.path.isfile( os.path.dirname( fname ) + '/raw/' + \
156
                os.path.basename( base_fname + '.' + ext ) ) ):
157
                return os.path.basename( base_fname + '.' + ext );
158
159
        # not found
160
        return False;
161
162
    def update_status( self, window ):
163
        thumb_view = window.get_thumb_view()
164
165
        # do we have just the one selected image? we can't handle multiple
166
        # images because EogThumbView.get_selected_images() doesn't work.
5.1.1 by edam
updated for GTK 3.0
167
        self.action_group.set_sensitive( \
2 by edam
- added status bar information
168
            thumb_view.get_n_selected() == 1 )
169
170
        # update the status bar
171
        if( thumb_view.get_n_selected() > 0 ):
172
            image = thumb_view.get_first_selected_image()
173
            raw_fname = self.get_raw_filename_from_image( image )
174
            path = os.path.dirname( image.get_file().get_path() )
5.1.1 by edam
updated for GTK 3.0
175
            self.statusbar.pop( 0 )
2 by edam
- added status bar information
176
            if( raw_fname is False ):
5.1.1 by edam
updated for GTK 3.0
177
                self.statusbar.push( 0, "  " + _( 'raw: -' ) )
2 by edam
- added status bar information
178
            elif( os.path.isfile( path + '/' + raw_fname ) ):
5.1.1 by edam
updated for GTK 3.0
179
                self.statusbar.push( 0, "  " + _( 'raw: unkept' ) )
2 by edam
- added status bar information
180
            else:
5.1.1 by edam
updated for GTK 3.0
181
                self.statusbar.push( 0, "  " + _( 'raw: keep' ) )
2 by edam
- added status bar information
182
11 by edam
refer to raw files as files, not images
183
    def do_keep_raw_file( self, action, window ):
184
185
        # do we have just the one selected image? we can't handle multiple
186
        # images because EogThumbView.get_selected_images() doesn't work.
187
        thumb_view = window.get_thumb_view()
188
        if( thumb_view.get_n_selected() == 1 ):
189
190
            # get the image
191
            image = thumb_view.get_first_selected_image()
192
            if( image != None ):
193
                self.keep_raw_file( image )
194
195
            # update ui
196
            self.update_status( window )
197
198
    def do_unkeep_raw_file( self, action, window ):
199
200
        # do we have just the one selected image? we can't handle multiple
201
        # images because EogThumbView.get_selected_images() doesn't work.
202
        thumb_view = window.get_thumb_view()
203
        if( thumb_view.get_n_selected() == 1 ):
204
205
            # get the image
206
            image = thumb_view.get_first_selected_image()
207
            if( image != None ):
208
                self.unkeep_raw_file( image )
209
210
            # update ui
211
            self.update_status( window )
212
213
    def keep_raw_file( self, image ):
2 by edam
- added status bar information
214
215
        raw_fname = self.get_raw_filename_from_image( image )
216
        if( raw_fname is False ):
5.1.1 by edam
updated for GTK 3.0
217
            dialog = Gtk.MessageDialog( None, Gtk.DialogFlags.MODAL,
218
                Gtk.MessageType.INFO, Gtk.ButtonsType.CLOSE,
2 by edam
- added status bar information
219
                _( 'Raw file not found!' ) )
220
            dialog.format_secondary_text( _( "This image doesn't appear to " +
221
                "have an accompanying raw file." ) )
1 by edam
initial comit
222
            dialog.run()
223
            dialog.destroy()
224
            return
2 by edam
- added status bar information
225
226
        # does raw file exist?
227
        path = os.path.dirname( image.get_file().get_path() )
228
        if( os.path.isfile( path + '/' + raw_fname ) ):
229
230
            # create the raw directory, if it doesn't exist
231
            if( not os.path.isdir( path + '/raw' ) ):
232
                os.mkdir( path + '/raw' );
233
234
            # move the raw file in to the raw directory
235
            os.rename( path + '/' + raw_fname, path + '/raw/' + raw_fname )
236
11 by edam
refer to raw files as files, not images
237
    def unkeep_raw_file( self, image ):
4 by edam
- added unkeep command
238
239
        raw_fname = self.get_raw_filename_from_image( image )
240
        if( raw_fname is False ):
5.1.1 by edam
updated for GTK 3.0
241
            dialog = Gtk.MessageDialog( None, Gtk.DialogFlags.MODAL,
242
                Gtk.MessageType.INFO, Gtk.ButtonsType.CLOSE,
4 by edam
- added unkeep command
243
                _( 'Raw file not found!' ) )
244
            dialog.format_secondary_text( _( "This image doesn't appear to " +
245
                "have an accompanying raw file." ) )
246
            dialog.run()
247
            dialog.destroy()
248
            return
249
250
        # does raw file exist?
251
        path = os.path.dirname( image.get_file().get_path() )
252
        if( os.path.isfile( path + '/raw/' + raw_fname ) ):
253
254
            # move the raw file back to the parent directory
255
            os.rename( path + '/raw/' + raw_fname, path + '/' + raw_fname )
256
257
            # if the raw directory is empty, remove it
258
            if( len( os.listdir( path + '/raw' ) ) == 0 ):
259
                os.rmdir( path + '/raw' )
260
11 by edam
refer to raw files as files, not images
261
    def do_delete_raw_files( self, action, window ):
2 by edam
- added status bar information
262
263
        # get path to first selected image, if there is one
5.1.1 by edam
updated for GTK 3.0
264
        thumb_view = window.get_thumb_view()
2 by edam
- added status bar information
265
        if( thumb_view.get_n_selected() == 1 ):
266
            image = thumb_view.get_first_selected_image()
267
            path = os.path.dirname( image.get_file().get_path() )
268
269
            # build a regex that will match raw filenames
270
            regex = re.compile(
271
                '.*\.(' + '|'.join( self.raw_file_extensions ) + ')', re.I )
272
273
            # itterate through files in the dir
274
            files = set()
275
            for file in os.listdir( path ):
276
                if( regex.match( file ) ):
277
                    files.add( file )
278
279
            # do we have any files to delete?
280
            if( len( files ) > 0 ):
281
282
                # ask the user if they are sure
5.1.1 by edam
updated for GTK 3.0
283
                dialog = Gtk.MessageDialog( None, Gtk.DialogFlags.MODAL,
284
                    Gtk.MessageType.QUESTION, Gtk.ButtonsType.YES_NO,
2 by edam
- added status bar information
285
                    _( 'Delete Raw Files?' ) )
286
                if( len( files ) == 1 ):
287
                    subject = _( ' remaining raw file' )
288
                else:
289
                    subject = _( ' remaining raw files' )
290
                dialog.format_secondary_text(
291
                    _( 'You are about to delete the ' ) +
292
                    str( len( files ) ) + subject +
293
                    _( " that you haven't specifically asked to keep from " +
294
                        "the directory that the current image is in.\n\n" +
295
                        "Do you want to continue?" ) )
296
                result = dialog.run()
297
                dialog.destroy()
5.1.1 by edam
updated for GTK 3.0
298
                if( result != Gtk.ResponseType.YES ):
2 by edam
- added status bar information
299
                    return
300
301
                # delete the files
302
                for file in files:
7 by edam
switch to gir Gio
303
                    Gio.file_parse_name( path + '/' + file ).trash( None )
4 by edam
- added unkeep command
304
                    #os.unlink( path + '/' + file )
2 by edam
- added status bar information
305
5.1.1 by edam
updated for GTK 3.0
306
                self.update_status( window )
2 by edam
- added status bar information
307
        
308
            # else, tell the user there aren't any raw files!
309
            else:
5.1.1 by edam
updated for GTK 3.0
310
                dialog = Gtk.MessageDialog( None, Gtk.DialogFlags.MODAL,
311
                    Gtk.MessageType.INFO, Gtk.ButtonsType.CLOSE,
2 by edam
- added status bar information
312
                    _( 'No raw files to delete!' ) )
313
                dialog.format_secondary_text(
314
                    _( 'There are no raw files to delete!' ) )
315
                dialog.run()
316
                dialog.destroy()