~ubuntu-branches/ubuntu/wily/phatch/wily

« back to all changes in this revision

Viewing changes to .pc/wxpy3.0-compat.patch/phatch/pyWx/gui.py

  • Committer: Package Import Robot
  • Author(s): Olly Betts
  • Date: 2014-08-31 18:10:50 UTC
  • mfrom: (4.1.20 sid)
  • Revision ID: package-import@ubuntu.com-20140831181050-bsuwtc59i0whjxcw
Tags: 0.2.7.1-3.1
* Non-maintainer upload.
* Update for wxpython3.0 (Closes: #758942):
  + New patch: wxpy3.0-compat.patch

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# -*- coding: UTF-8 -*-
 
2
 
 
3
# Phatch - Photo Batch Processor
 
4
# Copyright (C) 2007-2008 www.stani.be
 
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 3 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, see http://www.gnu.org/licenses/
 
18
#
 
19
# Phatch recommends SPE (http://pythonide.stani.be) for python editing.
 
20
 
 
21
# Follows PEP8
 
22
 
 
23
#---Global import
 
24
 
 
25
#import
 
26
import new
 
27
import sys
 
28
 
 
29
#check wx
 
30
from lib.pyWx.wxcheck import ensure
 
31
try:
 
32
    wx = ensure('2.8', '2.8')
 
33
except:
 
34
    #sphinx
 
35
    import wx
 
36
 
 
37
#force wxpython2.6 for testing
 
38
#import wxversion
 
39
#wxversion.select('2.6')
 
40
#import wx
 
41
 
 
42
#check with other encoding
 
43
##wx.SetDefaultPyEncoding('ISO8859-15')
 
44
 
 
45
#standard library
 
46
import glob
 
47
import pprint
 
48
import webbrowser
 
49
import os
 
50
 
 
51
 
 
52
#---Local import
 
53
 
 
54
#gui-independent
 
55
from core import api
 
56
from core import ct
 
57
from core import config
 
58
from core import pil
 
59
from core.message import FrameReceiver
 
60
from lib import formField
 
61
from lib import notify
 
62
from lib import safe
 
63
from lib import system
 
64
from lib import listData
 
65
from lib.unicoding import exception_to_unicode
 
66
notify.init(ct.INFO['name'])
 
67
 
 
68
#gui-dependent
 
69
from lib.pyWx import droplet
 
70
from lib.pyWx import graphics
 
71
from lib.pyWx import imageFileBrowser
 
72
from lib.pyWx import imageInspector
 
73
from lib.pyWx import paint
 
74
from lib.pyWx.clipboard import copy_text
 
75
 
 
76
import images
 
77
import dialogs
 
78
import plugin
 
79
from wxGlade import frame
 
80
 
 
81
WX_ENCODING = wx.GetDefaultPyEncoding()
 
82
COMMAND_PASTE = \
 
83
    _('You can paste it as text into the properties of a new launcher.')
 
84
ERROR_INSTALL_ACTION = \
 
85
_('Sorry, you need to install the %s action for this action list.')
 
86
WARNING_LOST_VALUES = \
 
87
_('Sorry, the values of these options will be lost in %(name)s %(version)s:')
 
88
CLIPBOARD_ACTIONLIST = \
 
89
_('The droplet command for this action list was copied to the clipboard.')
 
90
CLIPBOARD_RECENT = \
 
91
_('The droplet command for recent action lists was copied to the clipboard.')
 
92
CLIPBOARD_IMAGE_INSPECTOR = \
 
93
_('The droplet command for the image inspector was copied to the clipboard.')
 
94
NO_PHOTOS = \
 
95
_('In Phatch you need to open or create an action list first.') + ' ' + \
 
96
_('As an example try out the polaroid action list from the library.') + \
 
97
'\n' + \
 
98
_('Afterwards you can drag&drop images on the Phatch window to batch them.') +\
 
99
'\n\n' + \
 
100
_('For more information see the tutorials (Help>Documentation)')
 
101
#---theme
 
102
 
 
103
 
 
104
def _theme():
 
105
    if wx.Platform != '__WXGTK__':
 
106
        set_theme('nuovext')
 
107
 
 
108
 
 
109
def set_theme(name='default'):
 
110
    if name == 'nuovext':
 
111
        from nuovext import Provider
 
112
        wx.ArtProvider.Push(Provider())
 
113
 
 
114
#---Functions
 
115
 
 
116
 
 
117
def findWindowById(id):
 
118
    return wx.GetApp().GetTopWindow().FindWindowById(id)
 
119
 
 
120
 
 
121
class DialogsMixin:
 
122
    _icon_filename = None
 
123
 
 
124
    #---dialogs
 
125
    def show_error(self, message):
 
126
        return self.show_message(message, style=wx.OK | wx.ICON_ERROR)
 
127
 
 
128
    def show_execute_dialog(self, result, settings, files=None):
 
129
        dlg = dialogs.ExecuteDialog(self, drop=files)
 
130
        if settings['overwrite_existing_images_forced']:
 
131
            dlg.overwrite_existing_images.Disable()
 
132
        if files:
 
133
            #store in settings, not result as it will be saved
 
134
            settings['paths'] = files
 
135
        dlg.import_settings(settings)
 
136
        result['cancel'] = dlg.ShowModal() == wx.ID_CANCEL
 
137
        if result['cancel']:
 
138
            dlg.Destroy()
 
139
            return
 
140
        #Retrieve settings from dialog
 
141
        dlg.export_settings(settings)
 
142
        dlg.Destroy()
 
143
 
 
144
    def show_files_message(self, result, message, title, files):
 
145
        dlg = dialogs.FilesDialog(self, message, title, files)
 
146
        x0, y0 = self.GetSize()
 
147
        x1, y1 = dlg.GetSize()
 
148
        x = max(x0, x1)
 
149
        y = max(y1, 200)
 
150
        dlg.SetSize((x, y))
 
151
        result['cancel'] = dlg.ShowModal() == wx.ID_CANCEL
 
152
 
 
153
    def show_message(self, message, title='',
 
154
            style=wx.OK | wx.ICON_EXCLAMATION):
 
155
        if self.IsShown():
 
156
            parent = self
 
157
        else:
 
158
            parent = None
 
159
        dlg = wx.MessageDialog(parent,
 
160
                message,
 
161
                '%(name)s ' % ct.INFO + title,
 
162
                style,
 
163
        )
 
164
        answer = dlg.ShowModal()
 
165
        dlg.Destroy()
 
166
        return answer
 
167
 
 
168
    def show_status(self, message, log=True):
 
169
        dlg = dialogs.StatusDialog(self)
 
170
        dlg.log.Show(log)
 
171
        dlg.SetMessage(message)
 
172
        dlg.ShowModal()
 
173
        dlg.Destroy()
 
174
 
 
175
    def show_question(self, message, style=wx.YES_NO | wx.ICON_QUESTION):
 
176
        return self.show_message(message, style=style)
 
177
 
 
178
    def show_image_tree(self, result, image_infos, widths, headers,
 
179
            ok_label='&OK', buttons=False, modal=False):
 
180
        data = listData.files_data_dict(image_infos)
 
181
        dlg = dialogs.ImageTreeDialog(data, listData.DataDict, headers,
 
182
            self, size=(600, dialogs.get_max_height(300)))
 
183
        dlg.SetColumnWidths(*widths)
 
184
        dlg.SetOkLabel(ok_label)
 
185
        dlg.ShowButtons(buttons)
 
186
        if modal or buttons:
 
187
            answer = dlg.ShowModal()
 
188
            result['answer'] = answer == wx.ID_OK
 
189
        else:
 
190
            dlg.Show()
 
191
 
 
192
    def show_report(self):
 
193
        report = wx.GetApp().report
 
194
        if report:
 
195
            self.show_image_tree({}, report,
 
196
                widths=(200, 60, 60, 60, 500),
 
197
                headers=['filename', 'width', 'height', 'mode',
 
198
                    'source'],
 
199
                buttons=False,
 
200
                modal=not isinstance(self, Frame))
 
201
        else:
 
202
            self.show_message(_('No images have been processed to report.'))
 
203
 
 
204
    def show_log(self):
 
205
        if os.path.exists(ct.USER_LOG_PATH):
 
206
            log_file = open(ct.USER_LOG_PATH)
 
207
            msg = log_file.read().strip()
 
208
            log_file.close()
 
209
            if not msg:
 
210
                msg = _('Hooray, no issues!')
 
211
        else:
 
212
            msg = _('Nothing has been logged yet.')
 
213
        self.show_scrolled_message(msg, '%s - %s' \
 
214
            % (_('Log'), ct.USER_LOG_PATH))
 
215
 
 
216
    def show_info(self, message, title=''):
 
217
        return self.show_message(message, title,
 
218
            style=wx.OK | wx.ICON_INFORMATION)
 
219
 
 
220
    def show_progress(self, title, parent_max, child_max=1, message=''):
 
221
        dlg = dialogs.ProgressDialog(self, title, parent_max, child_max,
 
222
            message)
 
223
 
 
224
    def show_progress_error(self, result, message, ignore=True):
 
225
        message += '\n\n' + api.SEE_LOG
 
226
        errorDlg = dialogs.ErrorDialog(self, message, ignore)
 
227
        answer = errorDlg.ShowModal()
 
228
        result['stop_for_errors'] = not errorDlg.future_errors.GetValue()
 
229
        errorDlg.Destroy()
 
230
        if answer == wx.ID_ABORT:
 
231
            result['answer'] = _('abort')
 
232
            self.show_log()
 
233
        elif answer == wx.ID_FORWARD:
 
234
            result['answer'] = _('skip')
 
235
        else:
 
236
            result['answer'] = _('ignore')
 
237
 
 
238
    def show_scrolled_message(self, message, title, **keyw):
 
239
        import wx.lib.dialogs
 
240
        dlg = wx.lib.dialogs.ScrolledMessageDialog(self, message, title,
 
241
            style=wx.DEFAULT_DIALOG_STYLE | wx.MAXIMIZE_BOX | wx.RESIZE_BORDER,
 
242
            **keyw)
 
243
        dlg.ShowModal()
 
244
 
 
245
    def show_notification(self, message, force=False, report=None):
 
246
        self.set_report(report)
 
247
        active = wx.GetApp().IsActive() or self.IsActive()
 
248
        if force or not active:
 
249
            notify.send(
 
250
                title=system.filename_to_title(self.filename),
 
251
                message=message,
 
252
                icon=self.get_icon_filename(),
 
253
                wxicon=graphics.bitmap(images.ICON_PHATCH_64))
 
254
        if not active:
 
255
            self.RequestUserAttention()
 
256
 
 
257
    #---settings
 
258
    def get_setting(self, name):
 
259
        return wx.GetApp().settings[name]
 
260
 
 
261
    def set_setting(self, name, value):
 
262
        wx.GetApp().settings[name] = value
 
263
 
 
264
    #---data
 
265
    def load_actionlist_data(self, filename):
 
266
        if not os.path.exists(filename):
 
267
            return
 
268
        try:
 
269
            data, warnings = api.open_actionlist(filename)
 
270
        except KeyError, details:
 
271
            self.show_error(ERROR_INSTALL_ACTION\
 
272
                % exception_to_unicode(details, WX_ENCODING))
 
273
            return
 
274
        except:
 
275
            self.show_error(api.ERROR_INCOMPATIBLE_ACTIONLIST % ct.INFO)
 
276
            return
 
277
        if data['invalid labels']:
 
278
            self.show_message('\n'.join([
 
279
            _('This action list was made by a different %(name)s version.')\
 
280
                % ct.INFO + '\n\n' + \
 
281
            WARNING_LOST_VALUES % ct.INFO + '\n',
 
282
            '\n'.join(data['invalid labels'])]))
 
283
        if warnings:
 
284
            if formField.get_safe():
 
285
                self.show_error('%s\n\n%s\n%s' % (
 
286
                    api.ERROR_UNSAFE_ACTIONLIST_INTRO, warnings,
 
287
                    api.ERROR_UNSAFE_ACTIONLIST_DISABLE_SAFE))
 
288
                return
 
289
            else:
 
290
                wx.CallAfter(self.show_error, '%s\n\n%s\n%s' % (
 
291
                    api.ERROR_UNSAFE_ACTIONLIST_INTRO, warnings,
 
292
                    api.ERROR_UNSAFE_ACTIONLIST_ACCEPT))
 
293
        return data
 
294
 
 
295
    #---notification
 
296
    def _execute(self, actionlist, **keyw):
 
297
        app = wx.GetApp()
 
298
        self.set_report([])
 
299
        api.apply_actions_to_photos(actionlist, app.settings,
 
300
            update=self._send_update_event, **keyw)
 
301
 
 
302
    def _send_update_event(self):
 
303
        update_event = imageInspector.UpdateEvent()
 
304
        for frame in wx.GetTopLevelWindows():
 
305
            if frame != self:
 
306
                wx.PostEvent(frame, update_event)
 
307
 
 
308
    def set_report(self, report):
 
309
        wx.GetApp().report = report
 
310
 
 
311
    def get_icon_filename(self):
 
312
        if self._icon_filename == None:
 
313
            self._icon_filename = os.path.join(
 
314
                self.get_setting("PHATCH_IMAGE_PATH"), 'icons',
 
315
                    '48x48', 'phatch.png')
 
316
        return self._icon_filename
 
317
 
 
318
 
 
319
class Frame(DialogsMixin, dialogs.BrowseMixin, droplet.Mixin, paint.Mixin,
 
320
        frame.Frame, FrameReceiver):
 
321
    DEFAULT_PAINT_MESSAGE = _("click '+' to add actions")
 
322
    paint_message = DEFAULT_PAINT_MESSAGE
 
323
    paint_color = images.LOGO_COLOUR
 
324
    paint_logo = images.LOGO
 
325
 
 
326
    def __init__(self, actionlist, *args, **keyw):
 
327
        frame.Frame.__init__(self, *args, **keyw)
 
328
        _theme()
 
329
        self.dlg_library = None
 
330
        self.dirty = False
 
331
        self.EnableBackgroundPainting(self.empty)
 
332
        self._menu()
 
333
        self._toolBar()
 
334
        self._plugin()
 
335
        self.on_menu_file_new()
 
336
        images.set_icon(self, 'phatch')
 
337
        self._title()
 
338
        self._description()
 
339
        self._drop()
 
340
        self._set_size()
 
341
        self._events()
 
342
        self._pubsub()
 
343
        if actionlist.endswith(ct.EXTENSION):
 
344
            self._open(actionlist)
 
345
 
 
346
    def _set_size(self):
 
347
        #make it eee pc friendly
 
348
        self._width = 400
 
349
        self._max_height = dialogs.get_max_height()
 
350
        self.SetSize((self._width, min(600, self._max_height)))
 
351
        super(Frame, self).__set_properties()
 
352
 
 
353
    def _plugin(self):
 
354
        plugin.install_frame(self)
 
355
 
 
356
    def _description(self):
 
357
        self.show_description(False)
 
358
 
 
359
    def _drop(self):
 
360
        self.SetAsFileDropTarget(self.tree, self.on_drop)
 
361
 
 
362
    def _menu(self):
 
363
        #export menu
 
364
        self.menu_file_export = \
 
365
            self.menu_file_export_actionlist_to_clipboard.GetMenu()
 
366
        #file history
 
367
        self.menu_file_recent = wx.Menu()
 
368
        self.filehistory = wx.FileHistory()
 
369
        self.filehistory.UseMenu(self.menu_file_recent)
 
370
        self._set_file_history(self.get_setting('file_history'))
 
371
        self.Bind(wx.EVT_MENU_RANGE, self.on_menu_file_history,
 
372
            id=wx.ID_FILE1, id2=wx.ID_FILE9)
 
373
        self.menu_file.InsertMenu(2, wx.ID_REFRESH,
 
374
            _("Open &Recent"),
 
375
            self.menu_file_recent, "")
 
376
 
 
377
        #library
 
378
        #actionlists = [(system.filename_to_title(a), a)
 
379
        #    for a in wx.GetApp().get_action_list_files()]
 
380
        #actionlists.sort()
 
381
        #library = self.menu_file_library = wx.Menu()
 
382
        #prefix = len(actionlists) < 10
 
383
        #self.library_files = {}
 
384
        #for index, actionlist in enumerate(actionlists):
 
385
        #    id = wx.NewId()
 
386
        #    label = actionlist[0]
 
387
        #    if prefix:
 
388
        #        label = '&%d %s' % (index + 1, label)
 
389
        #    item = library.Append(id, label)
 
390
        #    self.library_files[id] = actionlist[1]
 
391
        #    self.Bind(wx.EVT_MENU, self.on_menu_file_library, item)
 
392
        ##wx2.6 compatible
 
393
        #wx_ID_EDIT = 5030
 
394
        #self.menu_file.InsertMenu(3, wx_ID_EDIT, _("Open &Library"),
 
395
        #   library, "")
 
396
        self.library_files_dictionary = formField.files_dictionary(
 
397
                    paths=[config.PATHS["PHATCH_ACTIONLISTS_PATH"],
 
398
                    ct.USER_ACTIONLISTS_PATH],
 
399
                    extensions=['png'])
 
400
 
 
401
        #shell
 
402
        self.shell = None
 
403
        #menu_item (for enabling/disabling)
 
404
        edit = [getattr(self, attr) for attr in dir(self)
 
405
            if attr[:10] == 'menu_edit_']
 
406
        edit.remove(self.menu_edit_add)
 
407
        view = [getattr(self, attr) for attr in dir(self)
 
408
            if attr[:10] == 'menu_view_']
 
409
        self.menu_item = [
 
410
            (self.menu_file, [
 
411
                self.menu_file_new.GetId(),
 
412
                self.menu_file_save.GetId(),
 
413
                self.menu_file_save_as.GetId()]),
 
414
            (self.menu_edit, [item.GetId() for item in edit]),
 
415
            (self.menu_view, [item.GetId() for item in view]),
 
416
            (self.menu_tools, [
 
417
                self.menu_tools_execute.GetId(),
 
418
                self.menu_tools_show_report.GetId(),
 
419
                self.menu_tools_show_log.GetId()]),
 
420
            (self.menu_file_export, [
 
421
                self.menu_file_export_actionlist_to_clipboard.GetId()])]
 
422
        self.on_show_droplet(False)
 
423
        # Mac  tweaks
 
424
        if wx.Platform == "__WXMAC__":
 
425
            #todo: about doesn't seem to work: why?!
 
426
            app = wx.GetApp()
 
427
            #app.SetMacHelpMenuTitleName(_('&Help'))
 
428
            app.SetMacAboutMenuItemId(wx.ID_ABOUT)
 
429
            app.SetMacExitMenuItemId(wx.ID_EXIT)
 
430
            #app.SetMacPreferencesMenuItemId(wx.ID_PREFERENCES)
 
431
        else:
 
432
            self.menu_file.InsertSeparator(8)
 
433
 
 
434
    def _title(self):
 
435
        path, filename = os.path.split(self.filename)
 
436
        self.SetTitle(ct.FRAME_TITLE\
 
437
            % (self.dirty, os.path.splitext(filename)[0]))
 
438
        self.frame_statusbar.SetStatusText(path)
 
439
        self.SetStatusText(ct.COPYRIGHT)
 
440
 
 
441
    def is_protected_actionlist(self, filename):
 
442
        return config.PATHS["PHATCH_ACTIONLISTS_PATH"] == \
 
443
            os.path.dirname(filename)
 
444
 
 
445
    #---toolBar
 
446
    def add_tool(self, bitmap, label, tooltip, method, item=wx.ITEM_NORMAL):
 
447
        id = wx.NewId()
 
448
        bitmap = graphics.bitmap(bitmap, self.tool_bitmap_size,
 
449
                    client=wx.ART_TOOLBAR)
 
450
        args = (id, label, bitmap, wx.NullBitmap, item,
 
451
                    tooltip, "")
 
452
        tool = self.frame_toolbar.AddLabelTool(*args)
 
453
        self.Bind(wx.EVT_TOOL, method, id=id)
 
454
        return tool
 
455
 
 
456
    def _toolBar(self):
 
457
        self.tool_bitmap_size = (32, 32)
 
458
        self.frame_toolbar = wx.ToolBar(self, -1, style=wx.TB_FLAT)
 
459
        self.SetToolBar(self.frame_toolbar)
 
460
        self.frame_toolbar.SetToolBitmapSize(self.tool_bitmap_size)
 
461
        tools_item_other = [
 
462
            self.add_tool('ART_FILE_OPEN', _("Open"),
 
463
                _("Open an action list"), self.on_menu_file_open)]
 
464
        tools_item = [
 
465
            self.add_tool('ART_EXECUTABLE_FILE', _("Execute"),
 
466
                _("Execute the action"), self.on_menu_tools_execute)]
 
467
        self.frame_toolbar.AddSeparator()
 
468
        tools_item_other.extend([
 
469
            self.add_tool('ART_ADD_BOOKMARK', _("Add"),
 
470
                _("Add an action"), self.on_menu_edit_add)])
 
471
        tools_item.extend([
 
472
            self.add_tool('ART_DEL_BOOKMARK', _("Remove"),
 
473
                _("Remove the selected action"), self.on_menu_edit_remove),
 
474
            self.add_tool('ART_GO_UP', _("Up"),
 
475
                _("Move the selected action up"), self.on_menu_edit_up),
 
476
            self.add_tool('ART_GO_DOWN', _("Down"),
 
477
                _("Move the selected action down"), self.on_menu_edit_down)])
 
478
        self.frame_toolbar.AddSeparator()
 
479
        tools_item_other.extend([
 
480
            self.add_tool('ART_FIND', _("Image Inspector"),
 
481
                _("Look up exif and iptc tags"),
 
482
                self.on_menu_tools_image_inspector)])
 
483
        self.frame_toolbar.AddSeparator()
 
484
        self.toolbar_description = self.add_tool('ART_TIP', _("Description"),
 
485
            _("Show description of the action list"),
 
486
            self.on_menu_view_description, item=wx.ITEM_CHECK)
 
487
        tools_item.extend([self.toolbar_description])
 
488
        self.tools_item = [tool.GetId() for tool in tools_item]
 
489
        self.tools_all = tools_item + \
 
490
            [tool.GetId() for tool in tools_item_other]
 
491
        self._menu_toolbar_state = True
 
492
        self.frame_toolbar.Realize()
 
493
 
 
494
    def enable_actions(self, state=True):
 
495
        if state != self._menu_toolbar_state:
 
496
            for tool in self.tools_item:
 
497
                self.frame_toolbar.EnableTool(tool, state)
 
498
            for menu, items in self.menu_item:
 
499
                for item in items:
 
500
                    menu.Enable(item, state)
 
501
            self._menu_toolbar_state = state
 
502
            self.tree.Show(state)
 
503
            self.empty.Show(not state)
 
504
            self.show_description(state and self.get_setting('description'))
 
505
 
 
506
    def enable_menu(self, state=True):
 
507
        self._menu_toolbar_state = None
 
508
        menu = self.frame_menubar
 
509
        for index in range(menu.GetMenuCount()):
 
510
            menu.EnableTop(index, state)
 
511
 
 
512
    def enable_toolbar(self, state=True):
 
513
        self._menu_toolbar_state = None
 
514
        for tool in self.tools_all:
 
515
            self.frame_toolbar.EnableTool(tool, state)
 
516
 
 
517
    def show_paint_message(self, message=None):
 
518
        if message == None:
 
519
            self.paint_message = self.DEFAULT_PAINT_MESSAGE
 
520
        else:
 
521
            self.paint_message = message
 
522
        self.Refresh()
 
523
 
 
524
#---menu events
 
525
    def on_menu_file_new(self, event=None):
 
526
        if self.is_save_not_ok():
 
527
            return
 
528
        self._set_filename(ct.UNKNOWN)
 
529
        self.description.SetValue(ct.ACTION_LIST_DESCRIPTION)
 
530
        self.saved_description = ct.ACTION_LIST_DESCRIPTION
 
531
        self._new()
 
532
        self.enable_actions(False)
 
533
 
 
534
    def on_menu_file_open_library(self, event):
 
535
        if self.is_save_not_ok():
 
536
            return
 
537
        if not self.dlg_library:
 
538
            self.dlg_library = imageFileBrowser.Dialog(
 
539
                parent=self,
 
540
                files=self.library_files_dictionary,
 
541
                title=_('Library Action Lists'),
 
542
                size=(self.GetSize()[0], 370),
 
543
                icon_size=(128, 128))
 
544
            self.dlg_library.image_list.Select(0)
 
545
        if self.dlg_library.ShowModal() == wx.ID_OK:
 
546
            filename = self.dlg_library.image_path.GetValue()
 
547
            filename = self.library_files_dictionary.get(filename, filename)\
 
548
                .replace('.png', '.phatch')
 
549
            #21/9/2009 may be removed
 
550
            #save_filename = os.path.join(ct.USER_ACTIONLISTS_PATH,
 
551
            #    os.path.basename(filename))
 
552
            self._open(filename)
 
553
        self.dlg_library.Hide()
 
554
        return
 
555
 
 
556
    def on_menu_file_open(self, event):
 
557
        if self.is_save_not_ok():
 
558
            return
 
559
        dlg = wx.FileDialog(self,
 
560
            message=_('Choose an Action List File...'),
 
561
            defaultDir=os.path.dirname(self.filename),
 
562
            wildcard=ct.WILDCARD,
 
563
            style=wx.OPEN,
 
564
        )
 
565
        if dlg.ShowModal() == wx.ID_OK:
 
566
            filename = dlg.GetPath()
 
567
            self._open(filename)
 
568
        dlg.Destroy()
 
569
 
 
570
    #def on_menu_file_library(self, event):
 
571
    #    if self.is_save_not_ok():
 
572
    #        return
 
573
    #    filename = self.library_files[event.GetId()]
 
574
    #    save_filename = os.path.join(ct.USER_ACTIONLISTS_PATH,
 
575
    #        os.path.basename(filename))
 
576
    #    self._open(filename, save_filename)
 
577
    def on_menu_file_save(self, event):
 
578
        if self.filename == ct.UNKNOWN \
 
579
                or self.is_protected_actionlist(self.filename):
 
580
            return self.on_menu_file_save_as()
 
581
        else:
 
582
            self._save()
 
583
            return True
 
584
 
 
585
    def on_menu_file_save_as(self, event=None):
 
586
        if self.is_protected_actionlist(self.filename) \
 
587
                or not os.path.isfile(self.filename):
 
588
            default_dir = ct.USER_ACTIONLISTS_PATH
 
589
        else:
 
590
            default_dir = os.path.dirname(self.filename)
 
591
        dlg = wx.FileDialog(self,
 
592
            message=_('Save Action List As...'),
 
593
            defaultDir=default_dir,
 
594
            wildcard=ct.WILDCARD,
 
595
            style=wx.SAVE | wx.OVERWRITE_PROMPT,
 
596
        )
 
597
        if dlg.ShowModal() == wx.ID_OK:
 
598
            saved = True
 
599
            path = dlg.GetPath()
 
600
            if dlg.GetFilterIndex() == 0 \
 
601
                    and not os.path.splitext(path)[1]:
 
602
                path += ct.EXTENSION
 
603
                if os.path.exists(path) and self.show_question('%s %s'\
 
604
                        % (_('This file exists already.'),
 
605
                        _('Do you want to overwrite it?'))) == wx.ID_NO:
 
606
                    saved = False
 
607
            if saved:
 
608
                self._save(path)
 
609
        else:
 
610
            saved = False
 
611
        dlg.Destroy()
 
612
        return saved
 
613
 
 
614
    def on_menu_file_export_actionlist_to_clipboard(self, event):
 
615
        if self.is_save_not_ok():
 
616
            return
 
617
        copy_text(ct.COMMAND['DROP'] % self.filename)
 
618
        self.show_info(' '.join([CLIPBOARD_ACTIONLIST, COMMAND_PASTE]))
 
619
 
 
620
    def on_menu_file_export_recent_to_clipboard(self, event):
 
621
        if self.is_save_not_ok():
 
622
            return
 
623
        copy_text(ct.COMMAND['RECENT'])
 
624
        self.show_info(' '.join([CLIPBOARD_RECENT, COMMAND_PASTE]))
 
625
 
 
626
    def on_menu_file_export_inspector_to_clipboard(self, event):
 
627
        if self.is_save_not_ok():
 
628
            return
 
629
        copy_text(ct.COMMAND['INSPECTOR'])
 
630
        self.show_info(' '.join([CLIPBOARD_IMAGE_INSPECTOR,
 
631
            COMMAND_PASTE]))
 
632
 
 
633
    def on_menu_file_quit(self, event):
 
634
        self.on_close()
 
635
 
 
636
    def on_menu_file_history(self, event):
 
637
        if self.is_save_not_ok():
 
638
            return
 
639
        # get the file based on the menu ID
 
640
        filenum = event.GetId() - wx.ID_FILE1
 
641
        filename = self.filehistory.GetHistoryFile(filenum)
 
642
        self._open(filename)
 
643
 
 
644
    def on_menu_edit_add(self, event):
 
645
        settings = wx.GetApp().settings
 
646
        if not hasattr(self, 'dialog_actions'):
 
647
            self.dialog_actions = dialogs.ActionDialog(self,
 
648
                api.ACTIONS, settings['tag_actions'],
 
649
                size=(self._width, min(400, self._max_height)),
 
650
                title=_("%(name)s actions") % ct.INFO)
 
651
        if self.dialog_actions.ShowModal() == wx.ID_OK:
 
652
            self.set_dirty(True)
 
653
            label = self.dialog_actions.GetStringSelection()
 
654
            self.tree.append_form_by_label_to_selected(label)
 
655
            self.enable_actions(True)
 
656
        self.dialog_actions.Hide()
 
657
 
 
658
    def on_menu_edit_remove(self, event):
 
659
        if self.tree.remove_selected_form():
 
660
            if self.IsEmpty():
 
661
                self.enable_actions(False)
 
662
                self.set_dirty(False)
 
663
            else:
 
664
                self.set_dirty(True)
 
665
 
 
666
    def on_menu_edit_up(self, event):
 
667
        self.set_dirty(True)
 
668
        self.tree.move_form_selected_up()
 
669
 
 
670
    def on_menu_edit_down(self, event):
 
671
        self.set_dirty(True)
 
672
        self.tree.move_form_selected_down()
 
673
 
 
674
    def on_menu_edit_enable(self, event):
 
675
        self.tree.enable_selected_form(True)
 
676
 
 
677
    def on_menu_edit_disable(self, event):
 
678
        self.tree.enable_selected_form(False)
 
679
 
 
680
    def on_menu_view_droplet(self, event):
 
681
        self.show_droplet(event.IsChecked())
 
682
 
 
683
    def droplet_label_format(self, x):
 
684
        #return '123456789012345678901234567890'
 
685
        return x  # [:18]
 
686
 
 
687
    def on_menu_view_description(self, event):
 
688
        self.show_description(event.IsChecked())
 
689
 
 
690
    def on_menu_view_expand_all(self, event):
 
691
        self.tree.expand_forms()
 
692
 
 
693
    def on_menu_view_collapse_all(self, event):
 
694
        self.tree.collapse_forms()
 
695
 
 
696
    def on_menu_view_collapse_automatic(self, event):
 
697
        self.enable_collapse_automatic(event.IsChecked())
 
698
 
 
699
    def on_menu_tools_execute(self, event):
 
700
        actionlist = self.tree.export_forms()
 
701
        self._execute(actionlist)
 
702
 
 
703
    def on_menu_tools_safe(self, event):
 
704
        self.set_safe_mode(event.IsChecked())
 
705
 
 
706
    def on_menu_tools_image_inspector(self, event):
 
707
        frame = dialogs.ImageInspectorFrame(self,
 
708
            size=(470, dialogs.get_max_height(510)),
 
709
            icon=images.get_icon('inspector'))
 
710
        frame.Show()
 
711
 
 
712
    def on_menu_tools_browse_library_user(self, event):
 
713
        system.start(wx.GetApp().settings['USER_DATA_PATH'])
 
714
 
 
715
    def on_menu_tools_browse_library_phatch(self, event):
 
716
        system.start(wx.GetApp().settings['PHATCH_DATA_PATH'])
 
717
 
 
718
    def on_menu_tools_show_report(self, event):
 
719
        self.show_report()
 
720
 
 
721
    def on_menu_tools_show_log(self, event):
 
722
        self.show_log()
 
723
 
 
724
    def on_menu_tools_update_fonts(self, event):
 
725
        config.check_fonts(True)
 
726
 
 
727
    def on_menu_tools_python_shell(self, event):
 
728
        from lib.pyWx import shell
 
729
        self.tree.close_popup()
 
730
        if self.shell is None:
 
731
            title = ct.TITLE.lower()
 
732
            self.shell = shell.Frame(self,
 
733
                title=_('%(name)s Shell') % ct.INFO,
 
734
                intro='%(name)s ' % ct.INFO,
 
735
                values={
 
736
                    '%s_%s' % (title, _('application')): wx.GetApp(),
 
737
                    '%s_%s' % (title, _('frame')): self,
 
738
                    '%s_%s' % (title, _('actions')): self.tree.export_forms,
 
739
                    },
 
740
                icon=graphics.bitmap(images.ICON_PHATCH_64),
 
741
            )
 
742
        self.shell.Show(event.IsChecked())
 
743
 
 
744
    def on_menu_help_website(self, event):
 
745
        webbrowser.open('http://photobatch.stani.be')
 
746
 
 
747
    def on_menu_help_documentation(self, event):
 
748
        webbrowser.open('http://photobatch.stani.be/documentation')
 
749
 
 
750
    def on_menu_help_forum(self, event):
 
751
        webbrowser.open('http://photobatch.stani.be/forum')
 
752
 
 
753
    def on_menu_help_translate(self, event):
 
754
        webbrowser.open(
 
755
            'https://translations.launchpad.net/phatch/trunk/+pots/phatch')
 
756
 
 
757
    def on_menu_help_bug(self, event):
 
758
        webbrowser.open('https://bugs.launchpad.net/phatch')
 
759
 
 
760
    def on_menu_help_plugin(self, event):
 
761
        help_path = self.get_setting("PHATCH_DOCS_PATH")
 
762
        help_file = os.path.join(help_path, 'index.html')
 
763
        if not os.path.exists(help_file):
 
764
            #debian/ubuntu
 
765
            help_file = os.path.join(help_path, 'build', 'html',
 
766
                'index.html')
 
767
        if not os.path.exists(help_file):
 
768
            #ppa
 
769
            help_file = os.path.join(sys.prefix, 'share', 'phatch', 'docs',
 
770
                'index.html')
 
771
        webbrowser.open(help_file)
 
772
        dlg = dialogs.WritePluginDialog(self, '\n'.join([
 
773
            _('A html tutorial will open in your internet browser.'),
 
774
            '',
 
775
            _('You only need to know PIL to write a plugin for Phatch.'),
 
776
            _('Phatch will generate the user interface automatically.'),
 
777
            _('Study the action plugins in:') + ' ' + ct.PHATCH_ACTIONS_PATH,
 
778
            '',
 
779
            _('If you want to contribute a plugin for Phatch,'),
 
780
            _('please email: ') + ct.CONTACT]))
 
781
        dlg.ShowModal()
 
782
        #settings in mixin because now app bas, also config_path
 
783
 
 
784
    def on_menu_help_about(self, event):
 
785
        from lib.pyWx import about
 
786
        from data.info import all_credits
 
787
        dlg = about.Dialog(self,
 
788
            title='%(version)s' % ct.INFO,
 
789
            logo=graphics.bitmap(images.LOGO),
 
790
            description=_('PHoto bATCH Processor'),
 
791
            website=ct.INFO['url'],
 
792
            credits=all_credits(),
 
793
            license=ct.LICENSE,
 
794
        )
 
795
        dlg.ShowModal()
 
796
        dlg.Destroy()
 
797
 
 
798
    #---helper
 
799
    def _new(self):
 
800
        self.set_dirty(False)
 
801
        self.tree.delete_all_forms()
 
802
 
 
803
    def _open(self, filename):
 
804
        self._new()
 
805
        if not os.path.exists(filename):
 
806
            self.show_error(_('Sorry, "%s" is not a valid path.' % filename))
 
807
            return
 
808
        if system.file_extension(filename) in pil.IMAGE_READ_EXTENSIONS:
 
809
            self.show_error(NO_PHOTOS)
 
810
            filename = self.library_files_dictionary['Polaroid']\
 
811
                .replace('.png', '.phatch')
 
812
        data = self.load_actionlist_data(filename)
 
813
        if data:
 
814
            description = data.get('description', '')
 
815
            self.show_description(bool(description))
 
816
            if description:
 
817
                wx.FutureCall(5000, self.show_description, False)
 
818
            if not description:
 
819
                description = ct.ACTION_LIST_DESCRIPTION
 
820
            self.description.SetValue(description)
 
821
            self.saved_description = description
 
822
            if self.tree.append_forms(data['actions']):
 
823
                self.enable_actions(True)
 
824
            self._set_filename(filename)
 
825
            # add it to the history
 
826
            if not self.is_protected_actionlist(filename):
 
827
                self.filehistory.AddFileToHistory(filename)
 
828
        elif self.IsEmpty():
 
829
            self.enable_actions(False)
 
830
            self.set_dirty(False)
 
831
 
 
832
    def _save(self, filename=None):
 
833
        if filename:
 
834
            self._set_filename(filename)
 
835
        description = self.description.GetValue()
 
836
        self.saved_description = description
 
837
        if description == ct.ACTION_LIST_DESCRIPTION:
 
838
            description = ''
 
839
        data = {
 
840
            'description': description,
 
841
            'actions': self.tree.export_forms(),
 
842
        }
 
843
        api.save_actionlist(self.filename, data)
 
844
        self.set_dirty(False)
 
845
        if filename:
 
846
            # add it to the history
 
847
            self.filehistory.AddFileToHistory(filename)
 
848
 
 
849
    def _set_filename(self, filename):
 
850
        self.filename = filename
 
851
        self.set_dirty(False)
 
852
 
 
853
    #---view show/hide
 
854
    def enable_collapse_automatic(self, checked):
 
855
        self.set_setting('collapse_automatic', checked)
 
856
        self.menu_view.Check(self.menu_view_collapse_automatic.GetId(),
 
857
            checked)
 
858
        self.tree.enable_collapse_automatic(checked)
 
859
 
 
860
    def show_description(self, checked):
 
861
        self.set_setting('description', checked)
 
862
        self.description.Show(checked)
 
863
        self.menu_view.Check(self.menu_view_description.GetId(), checked)
 
864
        self.frame_toolbar.ToggleTool(self.toolbar_description.GetId(),
 
865
            checked)
 
866
        self.Layout()
 
867
 
 
868
    def show_droplet(self, checked):
 
869
        if checked:
 
870
            if api.check_actionlist(self.tree.export_forms(),
 
871
                    wx.GetApp().settings):
 
872
                self.droplet = droplet.Frame(self,
 
873
                    title=_("Drag & Drop") + ' - ' + ct.TITLE,
 
874
                    bitmap=graphics.bitmap(images.DROPLET),
 
875
                    method=self.on_drop,
 
876
                    label=self.droplet_label_format(
 
877
                        system.filename_to_title(self.filename)),
 
878
                    label_color=wx.Colour(217, 255, 186),
 
879
                    label_pos=(8, 8),
 
880
                    label_angle=0,
 
881
                    pos=self.GetPosition(),
 
882
                    auto=True,
 
883
                    OnShow=self.on_show_droplet,
 
884
                    tooltip=_(
 
885
                    "Drop any files and/or folders on this Phatch droplet\n"
 
886
                    "to batch process them.\n"
 
887
                    "Right-click or double-click to switch to normal view."))
 
888
            else:
 
889
                wx.CallAfter(self.on_show_droplet, False)
 
890
        else:
 
891
            if self.droplet:
 
892
                self.droplet.show(False)
 
893
 
 
894
    def on_show_droplet(self, bool):
 
895
        self.menu_view.Check(self.menu_view_droplet.GetId(), bool)
 
896
        if not bool:
 
897
            self.droplet = None  # don't keep a reference
 
898
 
 
899
    #---checks
 
900
    def append_save_action(self, actions):
 
901
        only_metadata = self.only_actions_with_tag(actions, 'metadata')
 
902
        message = ct.SAVE_ACTION_NEEDED + " " + \
 
903
            _("Phatch will add one for you, please check its settings.")
 
904
        if only_metadata:
 
905
            message += ' \n\n%s%s' % (\
 
906
            _('The action list only processes metadata.'),
 
907
            _('Phatch chooses the lossless "Save Tags" action.'))
 
908
        self.show_message(message)
 
909
        if only_metadata:
 
910
            self.tree.append_form_by_label_to_last('Save Tags')
 
911
        else:
 
912
            self.tree.append_form_by_label_to_last('Save')
 
913
 
 
914
    def only_actions_with_tag(self, actions, tag):
 
915
        for action in actions:
 
916
            if tag not in action.tags:
 
917
                return False
 
918
        return True
 
919
 
 
920
    #---other events
 
921
    def _events(self):
 
922
        #wxPython events
 
923
        self.Bind(wx.EVT_CLOSE, self.on_close, self)
 
924
        self.Bind(wx.EVT_SIZE, self.on_size, self)
 
925
        self.Bind(wx.EVT_MENU_HIGHLIGHT_ALL, self.on_menu_tool_enter)
 
926
        self.Bind(wx.EVT_TOOL_ENTER, self.on_menu_tool_enter)
 
927
        self.Bind(wx.EVT_TEXT, self.on_description_text, self.description)
 
928
        self.tree.Bind(wx.EVT_TREE_END_DRAG, self.on_tree_end_drag, self.tree)
 
929
        self.tree.Bind(wx.EVT_CONTEXT_MENU, self.on_context_menu, self.tree)
 
930
 
 
931
    def on_description_text(self, event):
 
932
        if not self.dirty and event.GetString() != self.saved_description:
 
933
            self.set_dirty(True)
 
934
 
 
935
    def on_drop(self, filenames, x, y):
 
936
        api.apply_actions_to_photos(self.tree.export_forms(),
 
937
            wx.GetApp().settings, paths=filenames, drop=True)
 
938
 
 
939
    def on_menu_tool_enter(self, event):
 
940
        self.tree.close_popup()
 
941
        event.Skip()
 
942
 
 
943
    def on_tree_end_drag(self, event):
 
944
        event.Skip()
 
945
        wx.CallAfter(self.set_dirty, True)
 
946
 
 
947
    def on_context_menu(self, event):
 
948
        if self.tree.is_form_selected():
 
949
            self.tree.PopupMenu(self.menu_edit)
 
950
 
 
951
    def is_save_not_ok(self):
 
952
        if self.dirty:
 
953
            answer = self.show_message(_('Save last changes to') + '\n"%s"?'\
 
954
                % self.filename,
 
955
                style=wx.YES_NO | wx.CANCEL | wx.ICON_EXCLAMATION)
 
956
            if answer == wx.ID_CANCEL:
 
957
                return True
 
958
            if answer == wx.ID_YES:
 
959
                return not self.on_menu_file_save(None)
 
960
        return False
 
961
 
 
962
    def on_close(self, event=None):
 
963
        if self.is_save_not_ok():
 
964
            return
 
965
        self.Hide()
 
966
        wx.GetApp()._saveSettings()
 
967
        #Destroy everything
 
968
        self.Destroy()
 
969
 
 
970
    def on_size(self, event):
 
971
        event.Skip()
 
972
        if self.IsEmpty():
 
973
            self.empty.Refresh()
 
974
        else:
 
975
            self.tree.resize_popup()
 
976
 
 
977
    def IsEmpty(self):
 
978
        return not self.tree.has_forms()
 
979
 
 
980
    #---file history
 
981
    def _get_file_history(self):
 
982
        result = []
 
983
        for index in range(self.filehistory.GetCount()):
 
984
            filename = self.filehistory.GetHistoryFile(index)
 
985
            if filename.strip():
 
986
                result.append(filename)
 
987
        return result
 
988
 
 
989
    def _set_file_history(self, files):
 
990
        files.reverse()
 
991
        for filename in files:
 
992
            if os.path.exists(filename):
 
993
                self.filehistory.AddFileToHistory(filename)
 
994
 
 
995
    #---settings
 
996
    def set_dirty(self, value):
 
997
        self.dirty = ('', '*')[value]
 
998
        self._title()
 
999
 
 
1000
    def set_safe_mode(self, state):
 
1001
        if state == False:
 
1002
            answer = self.show_question(
 
1003
                _('Safe mode protects you from the execution of possibly '\
 
1004
                'harmful scripts.\nAre you sure you want to disable it?'),
 
1005
                style=wx.YES_NO | wx.ICON_QUESTION | wx.NO_DEFAULT)
 
1006
            if answer != wx.ID_YES:
 
1007
                self.menu_tools.Check(self.menu_tools_safe.GetId(), True)
 
1008
                return
 
1009
        formField.set_safe(state)
 
1010
 
 
1011
    #---droplet
 
1012
    def get_droplet_folder(self):
 
1013
        folder = self.show_dir_dialog(
 
1014
            defaultPath=self.get_setting('droplet_path'),
 
1015
            message=_('Choose the folder for the droplet'))
 
1016
        if folder:
 
1017
            self.set_setting('droplet_path', folder)
 
1018
        return folder
 
1019
 
 
1020
    def menu_file_export_droplet(self, method, *args, **keyw):
 
1021
        folder = self.get_droplet_folder()
 
1022
        if folder is None:
 
1023
            return
 
1024
        try:
 
1025
            method(folder=folder, *args)
 
1026
            self.show_info(_('Phatch successfully created the droplet.'))
 
1027
        except Exception, details:
 
1028
            reason = exception_to_unicode(details, WX_ENCODING)
 
1029
            self.show_error(_('Phatch could not create the droplet: ')\
 
1030
                + '\n\n' + reason)
 
1031
 
 
1032
    def install_menu_item(self, menu, name, label, method, tooltip="",
 
1033
            style=wx.ITEM_NORMAL):
 
1034
        #item
 
1035
        item = wx.MenuItem(menu, -1, label, tooltip, style)
 
1036
        setattr(self, name, item)
 
1037
        menu.InsertItem(0, item)
 
1038
        #method
 
1039
        method_name = 'on_' + name
 
1040
        method = new.instancemethod(method, self, self.__class__)
 
1041
        setattr(self, method_name, method)
 
1042
        #bind item & method
 
1043
        self.Bind(wx.EVT_MENU, method, item)
 
1044
        #return id
 
1045
        return item.GetId()
 
1046
 
 
1047
#---Image Inspector
 
1048
 
 
1049
 
 
1050
class ImageInspectorApp(wx.App):
 
1051
 
 
1052
    def __init__(self, paths, *args, **keyw):
 
1053
        self.paths = paths
 
1054
        super(ImageInspectorApp, self).__init__(*args, **keyw)
 
1055
 
 
1056
    def OnInit(self):
 
1057
        wx.InitAllImageHandlers()
 
1058
        _theme()
 
1059
        frame = dialogs.ImageInspectorFrame(None,
 
1060
            size=dialogs.imageInspector.SIZE)
 
1061
        images.set_icon(frame, 'inspector')
 
1062
        frame.OpenImages(self.paths)
 
1063
        frame.Show()
 
1064
        self.SetTopWindow(frame)
 
1065
        return 1
 
1066
 
 
1067
 
 
1068
def inspect(paths):
 
1069
    app = ImageInspectorApp(paths, 0)
 
1070
    app.MainLoop()
 
1071
 
 
1072
#---Droplet
 
1073
 
 
1074
 
 
1075
class DropletFrame(DialogsMixin, wx.Frame, FrameReceiver):
 
1076
 
 
1077
    def __init__(self, actionlist, paths, *args, **keyw):
 
1078
        wx.Frame.__init__(self, *args, **keyw)
 
1079
        self.filename = actionlist
 
1080
        self._pubsub()
 
1081
        data = self.load_actionlist_data(actionlist)
 
1082
        if data:
 
1083
            wx.CallAfter(self.execute, data['actions'], paths=paths)
 
1084
        else:
 
1085
            sys.exit(_('Impossible to load data from action list.'))
 
1086
 
 
1087
    def execute(self, actionlist, paths):
 
1088
        self._execute(actionlist, paths=paths, drop=True)
 
1089
        self.Destroy()
 
1090
 
 
1091
 
 
1092
class DropletMixin:
 
1093
 
 
1094
    def OnInit(self):
 
1095
        wx.InitAllImageHandlers()
 
1096
        #do all application initialisation
 
1097
        self.init()
 
1098
        api.init()
 
1099
        self.report = []
 
1100
        #check for action list
 
1101
        if self.actionlist == 'recent':
 
1102
            self.actionlist = self.get_action_list(
 
1103
                self.get_action_list_files() + \
 
1104
                self.settings['file_history'])
 
1105
        if self.actionlist is None:
 
1106
            return 0
 
1107
        #create frame
 
1108
        frame = DropletFrame(self.actionlist, self.paths, None, -1, ct.TITLE)
 
1109
        frame.Hide()
 
1110
        self.SetTopWindow(frame)
 
1111
        return 1
 
1112
 
 
1113
    def get_action_list_files(self):
 
1114
        return glob.glob(os.path.join(ct.USER_ACTIONLISTS_PATH,
 
1115
                '*' + ct.EXTENSION)) +\
 
1116
            glob.glob(os.path.join(config.PHATCH_ACTIONLISTS_PATH,
 
1117
                '*' + ct.EXTENSION))
 
1118
 
 
1119
    def get_action_list(self, file_list):
 
1120
        if not file_list:
 
1121
            file_list = self.get_action_list_files()
 
1122
        d = {}
 
1123
        for f in file_list:
 
1124
            d[system.filename_to_title(f)] = f
 
1125
        actionlists = d.keys()
 
1126
        actionlists.sort()
 
1127
        dlg = wx.SingleChoiceDialog(None, _('Select action list'), ct.TITLE,
 
1128
            actionlists, wx.CHOICEDLG_STYLE)
 
1129
        if dlg.ShowModal() == wx.ID_OK:
 
1130
            actionlist = d[dlg.GetStringSelection()]
 
1131
        else:
 
1132
            actionlist = None
 
1133
        dlg.Destroy()
 
1134
        return actionlist
 
1135
 
 
1136
    def init(self):
 
1137
        pass
 
1138
 
 
1139
    def _loadSettings(self, settings):
 
1140
        self.settings = settings
 
1141
        if os.path.exists(ct.USER_SETTINGS_PATH):
 
1142
            f = open(ct.USER_SETTINGS_PATH, 'rb')
 
1143
            #exclude paths as they should not be overwritten
 
1144
            try:
 
1145
                items = safe.eval_restricted(f.read(),
 
1146
                    allowed=['True', 'False']).items()
 
1147
            except:
 
1148
                sys.stdout.write(' '.join([
 
1149
                    _('Sorry, your settings seem corrupt.'),
 
1150
                    _('Please delete "%s".' % ct.USER_SETTINGS_PATH),
 
1151
                    _('Also check if your hard disk not full.\n')]))
 
1152
                sys.exit()
 
1153
            f.close()
 
1154
            for key, value in items:
 
1155
                #FIXME: paths should not be in settings
 
1156
                if not 'PATH' in key:
 
1157
                    self.settings[key] = value
 
1158
 
 
1159
    def _saveSettings(self):
 
1160
        f = open(ct.USER_SETTINGS_PATH, 'wb')
 
1161
        settings = self.settings.copy()
 
1162
        # non permanent settings
 
1163
        for key in ('desktop', 'safe', 'no_save'):
 
1164
            if key in settings:
 
1165
                del settings[key]
 
1166
        f.write(pprint.pformat(settings))
 
1167
        f.close()
 
1168
 
 
1169
 
 
1170
class DropletApp(DropletMixin, wx.App):
 
1171
 
 
1172
    def __init__(self, actionlist, paths, settings, *args, **keyw):
 
1173
        self.actionlist = actionlist
 
1174
        self.paths = paths
 
1175
        self._loadSettings(settings)
 
1176
        super(DropletApp, self).__init__(*args, **keyw)
 
1177
 
 
1178
 
 
1179
def drop(actionlist, paths, settings):
 
1180
    app = DropletApp(actionlist, paths, settings, 0)
 
1181
    app.MainLoop()
 
1182
 
 
1183
#---Application
 
1184
 
 
1185
 
 
1186
class App(DropletMixin, wx.App):
 
1187
 
 
1188
    def __init__(self, settings, actionlist, *args, **keyw):
 
1189
        self._loadSettings(settings)
 
1190
        self.filename = actionlist
 
1191
        super(App, self).__init__(*args, **keyw)
 
1192
 
 
1193
    def OnInit(self):
 
1194
        wx.InitAllImageHandlers()
 
1195
        #frame
 
1196
        self.splash = self._splash()
 
1197
        self.splash.CentreOnScreen()
 
1198
        self.SetTopWindow(self.splash)
 
1199
        self.splash.Show()
 
1200
        self.report = []
 
1201
        wx.CallAfter(self.show_frame)
 
1202
        return 1
 
1203
 
 
1204
    def MacReopenApp(self):
 
1205
        """Called when the doc icon is clicked, and ???"""
 
1206
        #TODO: test if this is working
 
1207
        self.GetTopWindow().Raise()
 
1208
 
 
1209
    def _splash(self):
 
1210
        return droplet.Frame(None,
 
1211
            title=ct.TITLE,
 
1212
            bitmap=graphics.bitmap(images.SPLASH),
 
1213
            splash=True,
 
1214
        )
 
1215
 
 
1216
    def show_frame(self):
 
1217
        #do all application initialisation
 
1218
        self.init()
 
1219
        api.init()
 
1220
        #create frame
 
1221
        frame = Frame(self.filename, None, -1, ct.TITLE)
 
1222
        frame.menu_tools.Check(frame.menu_tools_safe.GetId(),
 
1223
            formField.get_safe())
 
1224
        frame.CentreOnScreen()
 
1225
        frame.Show()
 
1226
        self.SetTopWindow(frame)
 
1227
        frame.enable_collapse_automatic(self.settings['collapse_automatic'])
 
1228
        #delete splash
 
1229
        self.splash.Hide()
 
1230
        self.splash.Destroy()
 
1231
        del self.splash
 
1232
 
 
1233
    def init(self):
 
1234
        super(App, self).init()
 
1235
 
 
1236
    def _saveSettings(self):
 
1237
        frame = self.GetTopWindow()
 
1238
        self.settings['file_history'] = frame._get_file_history()
 
1239
        if hasattr(frame, 'dialog_actions'):
 
1240
            if frame.dialog_actions:
 
1241
                self.settings['tag_actions'] = \
 
1242
                    frame.dialog_actions.GetTagSelection()
 
1243
        super(App, self)._saveSettings()
 
1244
 
 
1245
 
 
1246
def main(settings, actionlist):
 
1247
    app = App(settings, actionlist, 0)
 
1248
    app.MainLoop()
 
1249
 
 
1250
 
 
1251
if __name__ == "__main__":
 
1252
    main()