~ubuntu-branches/ubuntu/saucy/gourmet/saucy

« back to all changes in this revision

Viewing changes to src/lib/importers/interactive_importer.py

  • Committer: Bazaar Package Importer
  • Author(s): Rolf Leggewie
  • Date: 2008-11-24 10:38:57 UTC
  • mto: (2.1.1 sid) (13.1.1 maverick) (1.1.6 upstream)
  • mto: This revision was merged to the branch mainline in revision 5.
  • Revision ID: james.westby@ubuntu.com-20081124103857-2jh0ohyrd8sluojp
Tags: upstream-0.14.2
ImportĀ upstreamĀ versionĀ 0.14.2

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
"""A fallback interactive importer.
2
 
 
3
 
This is handed generic text. We then guide the user through the text
4
 
(they tell us what's an instruction, what's an ingredient, etc.)
5
 
"""
6
 
 
7
 
import gtk, gtk.glade, gtk.gdk, pango
8
 
import re, os.path, sys
9
 
from gourmet import convert
10
 
from gourmet import gglobals
11
 
from gourmet import check_encodings
 
1
import gtk, pango
 
2
from gettext import gettext as _
 
3
from generic_recipe_parser import RecipeParser
 
4
import gourmet.gtk_extras.cb_extras as cb
 
5
import gourmet.gglobals as gglobals
12
6
import importer
13
 
from generic_recipe_parser import RecipeParser
 
7
import re
 
8
import gourmet.convert as convert
 
9
from gourmet.threadManager import NotThreadSafe
14
10
import imageBrowser
15
 
from gettext import gettext as _
16
 
 
17
 
# Copied from """
18
 
# SimpleGladeApp.py
19
 
# Module that provides an object oriented abstraction to pygtk and libglade.
20
 
# Copyright (C) 2004 Sandino Flores Moreno
21
 
#
22
 
import tokenize
23
 
import weakref
24
 
import inspect
25
 
 
26
 
class SimpleGladeApp:
27
 
 
28
 
    def __init__(self, path, root=None, domain=None, **kwargs):
29
 
        """
30
 
        Load a glade file specified by glade_filename, using root as
31
 
        root widget and domain as the domain for translations.
32
 
 
33
 
        If it receives extra named arguments (argname=value), then they are used
34
 
        as attributes of the instance.
35
 
 
36
 
        path:
37
 
            path to a glade filename.
38
 
            If glade_filename cannot be found, then it will be searched in the
39
 
            same directory of the program (sys.argv[0])
40
 
 
41
 
        root:
42
 
            the name of the widget that is the root of the user interface,
43
 
            usually a window or dialog (a top level widget).
44
 
            If None or ommited, the full user interface is loaded.
45
 
 
46
 
        domain:
47
 
            A domain to use for loading translations.
48
 
            If None or ommited, no translation is loaded.
49
 
 
50
 
        **kwargs:
51
 
            a dictionary representing the named extra arguments.
52
 
            It is useful to set attributes of new instances, for example:
53
 
                glade_app = SimpleGladeApp("ui.glade", foo="some value", bar="another value")
54
 
            sets two attributes (foo and bar) to glade_app.
55
 
        """        
56
 
        if os.path.isfile(path):
57
 
            self.glade_path = path
58
 
        else:
59
 
            glade_dir = os.path.dirname( sys.argv[0] )
60
 
            self.glade_path = os.path.join(glade_dir, path)
61
 
        for key, value in kwargs.items():
62
 
            try:
63
 
                setattr(self, key, weakref.proxy(value) )
64
 
            except TypeError:
65
 
                setattr(self, key, value)
66
 
        self.glade = None
67
 
        #self.install_custom_handler(self.custom_handler)
68
 
        self.glade = self.create_glade(self.glade_path, root, domain)
69
 
        if root:
70
 
            self.main_widget = self.get_widget(root)
71
 
        else:
72
 
            self.main_widget = None
73
 
        self.normalize_names()
74
 
        self.add_callbacks(self)
75
 
        self.new()
76
 
 
77
 
    def __repr__(self):
78
 
        class_name = self.__class__.__name__
79
 
        if self.main_widget:
80
 
            root = gtk.Widget.get_name(self.main_widget)
81
 
            repr = '%s(path="%s", root="%s")' % (class_name, self.glade_path, root)
82
 
        else:
83
 
            repr = '%s(path="%s")' % (class_name, self.glade_path)
84
 
        return repr
85
 
 
86
 
    def new(self):
87
 
        """
88
 
        Method called when the user interface is loaded and ready to be used.
89
 
        At this moment, the widgets are loaded and can be refered as self.widget_name
90
 
        """
91
 
        pass
92
 
 
93
 
    def add_callbacks(self, callbacks_proxy):
94
 
        """
95
 
        It uses the methods of callbacks_proxy as callbacks.
96
 
        The callbacks are specified by using:
97
 
            Properties window -> Signals tab
98
 
            in glade-2 (or any other gui designer like gazpacho).
99
 
 
100
 
        Methods of classes inheriting from SimpleGladeApp are used as
101
 
        callbacks automatically.
102
 
 
103
 
        callbacks_proxy:
104
 
            an instance with methods as code of callbacks.
105
 
            It means it has methods like on_button1_clicked, on_entry1_activate, etc.
106
 
        """        
107
 
        self.glade.signal_autoconnect(callbacks_proxy)
108
 
 
109
 
    def normalize_names(self):
110
 
        """
111
 
        It is internally used to normalize the name of the widgets.
112
 
        It means a widget named foo:vbox-dialog in glade
113
 
        is refered self.vbox_dialog in the code.
114
 
 
115
 
        It also sets a data "prefixes" with the list of
116
 
        prefixes a widget has for each widget.
117
 
        """
118
 
        for widget in self.get_widgets():
119
 
            widget_name = gtk.Widget.get_name(widget)
120
 
            prefixes_name_l = widget_name.split(":")
121
 
            prefixes = prefixes_name_l[ : -1]
122
 
            widget_api_name = prefixes_name_l[-1]
123
 
            widget_api_name = "_".join( re.findall(tokenize.Name, widget_api_name) )
124
 
            gtk.Widget.set_name(widget, widget_api_name)
125
 
            if hasattr(self, widget_api_name):
126
 
                raise AttributeError("instance %s already has an attribute %s" % (self,widget_api_name))
127
 
            else:
128
 
                setattr(self, widget_api_name, widget)
129
 
                if prefixes:
130
 
                    gtk.Widget.set_data(widget, "prefixes", prefixes)
131
 
 
132
 
    def add_prefix_actions(self, prefix_actions_proxy):
133
 
        """
134
 
        By using a gui designer (glade-2, gazpacho, etc)
135
 
        widgets can have a prefix in theirs names
136
 
        like foo:entry1 or foo:label3
137
 
        It means entry1 and label3 has a prefix action named foo.
138
 
 
139
 
        Then, prefix_actions_proxy must have a method named prefix_foo which
140
 
        is called everytime a widget with prefix foo is found, using the found widget
141
 
        as argument.
142
 
 
143
 
        prefix_actions_proxy:
144
 
            An instance with methods as prefix actions.
145
 
            It means it has methods like prefix_foo, prefix_bar, etc.
146
 
        """        
147
 
        prefix_s = "prefix_"
148
 
        prefix_pos = len(prefix_s)
149
 
 
150
 
        is_method = lambda t : callable( t[1] )
151
 
        is_prefix_action = lambda t : t[0].startswith(prefix_s)
152
 
        drop_prefix = lambda (k,w): (k[prefix_pos:],w)
153
 
 
154
 
        members_t = inspect.getmembers(prefix_actions_proxy)
155
 
        methods_t = filter(is_method, members_t)
156
 
        prefix_actions_t = filter(is_prefix_action, methods_t)
157
 
        prefix_actions_d = dict( map(drop_prefix, prefix_actions_t) )
158
 
 
159
 
        for widget in self.get_widgets():
160
 
            prefixes = gtk.Widget.get_data(widget, "prefixes")
161
 
            if prefixes:
162
 
                for prefix in prefixes:
163
 
                    if prefix in prefix_actions_d:
164
 
                        prefix_action = prefix_actions_d[prefix]
165
 
                        prefix_action(widget)
166
 
 
167
 
    #def custom_handler(self,
168
 
    #        glade, function_name, widget_name,
169
 
    #        str1, str2, int1, int2):
170
 
    #    """
171
 
    #    Generic handler for creating custom widgets, internally used to
172
 
    #    enable custom widgets (custom widgets of glade).
173
 
    #
174
 
    #    The custom widgets have a creation function specified in design time.
175
 
    #    Those creation functions are always called with str1,str2,int1,int2 as
176
 
    #    arguments, that are values specified in design time.
177
 
    #
178
 
    #    Methods of classes inheriting from SimpleGladeApp are used as
179
 
    #    creation functions automatically.
180
 
    #
181
 
    #    If a custom widget has create_foo as creation function, then the
182
 
    #    method named create_foo is called with str1,str2,int1,int2 as arguments.
183
 
    #    """
184
 
    #    try:
185
 
    #        handler = getattr(self, function_name)
186
 
    #        return handler(str1, str2, int1, int2)
187
 
    #    except AttributeError:
188
 
    #        return None
189
 
 
190
 
    def gtk_widget_show(self, widget, *args):
191
 
        """
192
 
        Predefined callback.
193
 
        The widget is showed.
194
 
        Equivalent to widget.show()
195
 
        """
196
 
        widget.show()
197
 
 
198
 
    def gtk_widget_hide(self, widget, *args):
199
 
        """
200
 
        Predefined callback.
201
 
        The widget is hidden.
202
 
        Equivalent to widget.hide()
203
 
        """
204
 
        widget.hide()
205
 
 
206
 
    def gtk_widget_grab_focus(self, widget, *args):
207
 
        """
208
 
        Predefined callback.
209
 
        The widget grabs the focus.
210
 
        Equivalent to widget.grab_focus()
211
 
        """
212
 
        widget.grab_focus()
213
 
 
214
 
    def gtk_widget_destroy(self, widget, *args):
215
 
        """
216
 
        Predefined callback.
217
 
        The widget is destroyed.
218
 
        Equivalent to widget.destroy()
219
 
        """
220
 
        widget.destroy()
221
 
 
222
 
    def gtk_window_activate_default(self, window, *args):
223
 
        """
224
 
        Predefined callback.
225
 
        The default widget of the window is activated.
226
 
        Equivalent to window.activate_default()
227
 
        """
228
 
        widget.activate_default()
229
 
 
230
 
    def gtk_true(self, *args):
231
 
        """
232
 
        Predefined callback.
233
 
        Equivalent to return True in a callback.
234
 
        Useful for stopping propagation of signals.
235
 
        """
236
 
        return True
237
 
 
238
 
    def gtk_false(self, *args):
239
 
        """
240
 
        Predefined callback.
241
 
        Equivalent to return False in a callback.
242
 
        """
243
 
        return False
244
 
 
245
 
    def gtk_main_quit(self, *args):
246
 
        """
247
 
        Predefined callback.
248
 
        Equivalent to self.quit()
249
 
        """
250
 
        self.quit()
251
 
 
252
 
    def main(self):
253
 
        """
254
 
        Starts the main loop of processing events.
255
 
        The default implementation calls gtk.main()
256
 
 
257
 
        Useful for applications that needs a non gtk main loop.
258
 
        For example, applications based on gstreamer needs to override
259
 
        this method with gst.main()
260
 
 
261
 
        Do not directly call this method in your programs.
262
 
        Use the method run() instead.
263
 
        """
264
 
        gtk.main()
265
 
 
266
 
    def quit(self):
267
 
        """
268
 
        Quit processing events.
269
 
        The default implementation calls gtk.main_quit()
270
 
        
271
 
        Useful for applications that needs a non gtk main loop.
272
 
        For example, applications based on gstreamer needs to override
273
 
        this method with gst.main_quit()
274
 
        """
275
 
        gtk.main_quit()
276
 
 
277
 
    def run(self):
278
 
        """
279
 
        Starts the main loop of processing events checking for Control-C.
280
 
 
281
 
        The default implementation checks wheter a Control-C is pressed,
282
 
        then calls on_keyboard_interrupt().
283
 
 
284
 
        Use this method for starting programs.
285
 
        """
286
 
        try:
287
 
            self.main()
288
 
        except KeyboardInterrupt:
289
 
            self.on_keyboard_interrupt()
290
 
 
291
 
    def on_keyboard_interrupt(self):
292
 
        """
293
 
        This method is called by the default implementation of run()
294
 
        after a program is finished by pressing Control-C.
295
 
        """
296
 
        pass
297
 
 
298
 
    #def install_custom_handler(self, custom_handler):
299
 
    #    gtk.glade.set_custom_handler(custom_handler)
300
 
 
301
 
    def create_glade(self, glade_path, root, domain):
302
 
        return gtk.glade.XML(self.glade_path, root, domain)
303
 
 
304
 
    def get_widget(self, widget_name):
305
 
        return self.glade.get_widget(widget_name)
306
 
 
307
 
    def get_widgets(self):
308
 
        return self.glade.get_widget_prefix("")        
309
 
 
310
 
# End copied material
311
 
 
312
 
class ConvenientImporter (importer.importer):
 
11
import gourmet.ImageExtras as ImageExtras
 
12
# TODO
 
13
# 1. Make this interface actually import recipes...
 
14
# 2. Add drop-down menu buttons in place of red labels to make it
 
15
#    trivial to change the label of something labelled.
 
16
# 3. Remove button-hell interface and come up with something cleaner
 
17
#    and more attractive.
 
18
# 4. Add Undo support to editing (ugh -- this will be a PITA)
 
19
 
 
20
DEFAULT_TAGS = []
 
21
DEFAULT_TAG_LABELS = gglobals.REC_ATTR_DIC.copy()
 
22
for attr in gglobals.DEFAULT_ATTR_ORDER:
 
23
    if attr != 'link':
 
24
        DEFAULT_TAGS.append(attr)
 
25
DEFAULT_TAGS.extend(['instructions','ingredients','inggroup','ignore'])
 
26
for tag,label in [('instructions',_('Instructions')),
 
27
                  ('ingredients',_('Ingredients')),
 
28
                  ('inggroup',_('Ingredient Subgroup')),
 
29
                  ('ignore',_('Hide'))
 
30
                  ]:
 
31
    if not DEFAULT_TAG_LABELS.has_key(tag):
 
32
        DEFAULT_TAG_LABELS[tag] = label
 
33
 
 
34
class ConvenientImporter (importer.Importer):
313
35
    """Add some convenience methods to our standard importer.
314
36
    """
315
 
    started=False
316
 
 
317
 
    def autostart_rec (f):
318
 
        """A decorator to make sure our recipe has been started before
319
 
        we add something to it.
320
 
        """
321
 
        def _ (self,*args,**kwargs):
322
 
            self.set_added_to(True)
323
 
            return f(self,*args,**kwargs)
324
 
        return _
325
 
 
326
37
    def add_attribute (self, attname, txt):
327
 
        self.set_added_to(True)
328
38
        txt=txt.strip()
329
39
        if self.rec.has_key(attname):
330
40
            self.rec[attname] = self.rec[attname] + ', ' + txt
332
42
            self.rec[attname] = txt
333
43
 
334
44
    def add_text (self, attname, txt):
335
 
        self.set_added_to(True)
336
45
        if self.rec.has_key(attname):
337
46
            self.rec[attname] = self.rec[attname] + '\n' + txt
338
47
        else:
339
48
            self.rec[attname] = txt
340
49
 
341
50
    def add_ing_group (self, txt):
342
 
        self.set_added_to(True)
343
51
        self.group = txt.strip()
344
52
 
345
53
    def add_ing_from_text (self, txt):
346
 
        self.set_added_to(True)
347
54
        try:
348
55
            txt=unicode(txt.strip())
349
56
        except UnicodeDecodeError:
356
63
            amount = mm.group(convert.ING_MATCHER_AMT_GROUP)
357
64
            unit = mm.group(convert.ING_MATCHER_UNIT_GROUP)
358
65
            item = mm.group(convert.ING_MATCHER_ITEM_GROUP)
359
 
            print 'Parsed ingredient: "%s"'%txt,"into:",amount,unit,item
 
66
            #print 'Parsed ingredient: "%s"'%txt,"into:",amount,'|',unit,'|',item
360
67
            # If our unit isn't familiar and is longer than 2
361
68
            # characters, don't add it as a unit! (this lets most
362
69
            # abbreviations through)
381
88
        txt=txt.strip()
382
89
        for i in txt.split(break_at): self.add_ing_from_text(i)
383
90
 
384
 
 
385
 
# Material copied from our generic recipe importer...
386
 
 
387
 
class InteractiveImporter (SimpleGladeApp, ConvenientImporter):
388
 
 
389
 
    def __init__(self, rd, progress=None,
390
 
                 custom_parser=None):
391
 
        self.progress = progress
 
91
class InteractiveImporter (ConvenientImporter, NotThreadSafe):
 
92
 
 
93
    NEW_REC_TEXT = _('New Recipe')
 
94
 
 
95
    def __init__ (self,
 
96
                  custom_parser = None,
 
97
                  tags=DEFAULT_TAGS,
 
98
                  tag_labels=DEFAULT_TAG_LABELS,
 
99
                  modal=True):
392
100
        if custom_parser: self.parser = custom_parser
393
101
        else: self.parser = RecipeParser()
394
 
        self.parser_to_choice = {
395
 
            'ingredient':'Ingredient',
396
 
            'ingredients':'Ingredients',
397
 
            'instructions':'Instructions',
398
 
            'None':'Ignore',
399
 
            'title':'Title',
400
 
            }
401
 
        self.added_to = False
402
 
        self.attdic = gglobals.REC_ATTR_DIC 
403
 
        self.textattdic = gglobals.TEXT_ATTR_DIC
404
 
        SimpleGladeApp.__init__(self,
405
 
                                path=os.path.join(gglobals.gladebase,
406
 
                                                  "generic_importer.glade"),
407
 
                                root="window1",
408
 
                                domain="gourmet")
409
 
        ConvenientImporter.__init__(self,rd,threaded=True)
410
 
 
411
 
    #-- InteractiveImporter.new {
412
 
    def new(self):
413
 
        self.get_widget('window1').show_all()
414
 
        self.get_widget('window1').connect('delete-event',lambda *args: gtk.main_quit())
415
 
        self.textview = self.glade.get_widget('textview')
416
 
        self.textbuffer = self.textview.get_buffer()
417
 
        self.textbuffer.connect('mark-set',self.on_cursor_moved)
418
 
        #self.display = RecipeDisplay(self.glade)
419
 
        self.glade.get_widget('window1').show()
420
 
        self.populate_action_box()
421
 
    #-- InteractiveImporter.new }
422
 
 
423
 
    #-- InteractiveImporter custom methods {
424
 
    #   Write your own methods here
425
 
    def populate_action_box (self):
426
 
        """Set up our choices of actions."""
427
 
        self.action_box = self.glade.get_widget('treeview1')
428
 
        self.action_to_label = {}
429
 
        self.actions = {
430
 
            #'Instructions':self.display.add_instructions,
431
 
            #'Notes':self.display.add_notes,
432
 
            'Ingredient':lambda lab,txt: self.add_ing_from_text(txt),
433
 
            'Ingredients': lambda lab,txt: self.add_ings_from_text(txt),
434
 
            'Ingredient Subgroup':lambda lab,txt: self.add_ing_group(txt),
435
 
            }
436
 
        for attname,display_name in self.attdic.items():
437
 
            self.actions[display_name] = self.add_attribute
438
 
            self.action_to_label[display_name]=attname
439
 
        for attname,display_name in self.textattdic.items():
440
 
            self.actions[display_name] = self.add_text
441
 
            self.action_to_label[display_name]=attname
442
 
        keys = [self.attdic[a] for a in gglobals.DEFAULT_ATTR_ORDER]
443
 
        keys.extend(['Ingredient Subgroup','Ingredient','Ingredients' ])
444
 
        keys.extend([self.textattdic[a] for a in gglobals.DEFAULT_TEXT_ATTR_ORDER])
445
 
        # set up model
446
 
        mod = gtk.ListStore(str)
447
 
        for k in keys: mod.append([k])
448
 
        # set up treeview column...
449
 
        renderer = gtk.CellRendererText()
450
 
        tvc = gtk.TreeViewColumn('',renderer,text=0)
451
 
        self.action_box.append_column(tvc)
452
 
        self.action_box.set_model(mod)
453
 
 
454
 
    def get_current_action (self, *args):
455
 
        """Get the current default actions for our section of text."""
456
 
        sel = self.action_box.get_selection().get_selected()
457
 
        if sel:
458
 
            mod,itr = sel
459
 
            return mod.get_value(itr,0)
460
 
        else:
461
 
            return None
462
 
 
463
 
    def set_current_action (self, action):
464
 
        """Set the current default action for our section of text."""
465
 
        mod = self.action_box.get_model()
466
 
        sel = self.action_box.get_selection()
467
 
        for r in mod:
468
 
            if r[0]==action:
469
 
                sel.select_iter(r.iter)
470
 
                return
471
 
        # if we haven't returned by now, we have no action
472
 
        sel.unselect_all()
473
 
 
474
 
    def set_images (self, image_urls):
475
 
        self.images = image_urls
476
 
 
477
 
    def commit_rec (self, *args, **kwargs):
478
 
        if hasattr(self,'images') and sys.platform!='win32':
479
 
            if self.progress: self.progress(-1,_('Getting images...'))
480
 
            self.ibd=imageBrowser.ImageBrowserDialog(
481
 
                title=_("Select recipe image"),
482
 
                label=_("Select recipe image."),
483
 
                sublabel=_("Below are all the images found for the page you are importing. Select any images that are of the recipe, or don't select anything if you don't want any of these images."),
484
 
                )
485
 
            for i in self.images: self.ibd.add_image_from_uri(i)
486
 
            self.ibd.run()
487
 
            image = self.ibd.ret
488
 
            if image:
489
 
                fi=file(imageBrowser.get_image_file(image),'r')
490
 
                self.rec['image']=fi.read()
491
 
                fi.close()
492
 
        ConvenientImporter.commit_rec(self,*args,**kwargs)        
493
 
    
 
102
        self.labels_by_tag = tag_labels
 
103
        self.tags_by_label = {self.NEW_REC_TEXT:'newrec'}
 
104
        for k,v in self.labels_by_tag.items(): self.tags_by_label[v]=k
 
105
        self.tags = tags
 
106
        self.setup_window()
 
107
        self.setup_action_area()
 
108
        self.markup_marks = {}; self.markup_partners = {}
 
109
        self.anchors = []
 
110
        self.midno = 0 # an ID counter for markup marks we insert 
 
111
        self.labelled = []
 
112
        self.label_counts = {}
 
113
        self.modal = modal # If we're in an embedded gtk mainloop...
 
114
        ConvenientImporter.__init__(self)
 
115
        
 
116
    def setup_window (self):
 
117
        self.w = gtk.Window()
 
118
        self.hb = gtk.HBox()
 
119
        self.w.add(self.hb)
 
120
        self.tv = gtk.TextView()
 
121
        self.tv.set_size_request(600,400)
 
122
        self.tv.set_wrap_mode(gtk.WRAP_WORD)
 
123
        self.action_area = gtk.VBox()
 
124
        sw = gtk.ScrolledWindow(); sw.add(self.tv)
 
125
        sw.set_policy(gtk.POLICY_NEVER,gtk.POLICY_AUTOMATIC)
 
126
        self.hb.add(sw); sw.show(); self.tv.show()
 
127
        self.hb.add(self.action_area)
 
128
        self.tb = self.tv.get_buffer()
 
129
        self.setup_tags()
 
130
 
 
131
    def setup_action_area (self):
 
132
        for t in self.tags:
 
133
            tag_button = gtk.Button('_'+self.labels_by_tag[t])
 
134
            tag_button.connect('clicked',
 
135
                               self.label_callback,
 
136
                               self.labels_by_tag[t])
 
137
            self.action_area.pack_start(tag_button,expand=False,fill=False,padding=6)
 
138
            tag_button.show()
 
139
        self.action_area.add(gtk.HSeparator())
 
140
        self.remove_markup_button = gtk.Button(_('Clear _Tags'))
 
141
        self.remove_markup_button.connect('clicked',self.clear_tags)
 
142
        self.action_area.pack_start(self.remove_markup_button,expand=False,fill=False)
 
143
        self.new_recipe_button = gtk.Button(_('_New Recipe'))
 
144
        self.new_recipe_button.connect('clicked',self.new_recipe_cb)
 
145
        self.action_area.pack_start(self.new_recipe_button)
 
146
        self.action_area.pack_start(gtk.HSeparator(),expand=False,fill=False)
 
147
        self.show_text_button = gtk.Button(stock=gtk.STOCK_OK)
 
148
        self.show_text_button.connect('clicked',
 
149
                                      lambda *args: self.commit_changes())
 
150
        self.action_area.add(self.show_text_button)
 
151
        self.action_area.show_all()
 
152
 
 
153
    def setup_tags (self):
 
154
        self.markup_tag = gtk.TextTag('markup')
 
155
        self.markup_tag.set_property('editable',False)
 
156
        self.markup_tag.set_property('scale',pango.SCALE_SMALL)
 
157
        self.markup_tag.set_property('rise',15)
 
158
        self.markup_tag.set_property('foreground',
 
159
                                     '#f00'
 
160
                                     )
 
161
        self.ignore_tag = gtk.TextTag('ignore')
 
162
        self.ignore_tag.set_property('invisible',True)
 
163
        self.ignore_tag.set_property('editable',False)
 
164
        self.tb.get_tag_table().add(self.markup_tag)
 
165
        self.tb.get_tag_table().add(self.ignore_tag)        
 
166
        
 
167
    def label_callback (self, button, label):
 
168
        self.label_selection(label)
 
169
 
 
170
    def label_selection (self, label):
 
171
        cursel = self.tb.get_selection_bounds()
 
172
        if cursel:
 
173
            st,end = cursel
 
174
        else:
 
175
            # Otherwise, there's no clear sane default... we'll just
 
176
            # select the current whole line
 
177
            cur_pos = self.tb.get_insert()
 
178
            cur_pos.backward_chars(
 
179
                cur_pos.get_line_offset())
 
180
            st = cur_pos
 
181
            end = cur_pos.copy()
 
182
            end = cur_pos.forward_line()
 
183
        self.label_range(st,end,label)
 
184
 
 
185
    def insert_with_label (self, st, text, label):
 
186
        start_offset = st.get_offset()
 
187
        self.tb.insert(st,text)
 
188
        end_offset = start_offset + len(text)
 
189
        self.label_range(
 
190
            self.tb.get_iter_at_offset(start_offset),
 
191
            self.tb.get_iter_at_offset(end_offset),
 
192
            label
 
193
            )
 
194
 
 
195
    def unhide_area (self, midno):
 
196
        st,end = self.markup_marks[midno]
 
197
        self.tb.remove_tag(self.ignore_tag,
 
198
                           self.tb.get_iter_at_mark(st),
 
199
                           self.tb.get_iter_at_mark(end)
 
200
                           )
 
201
 
 
202
    def hide_range (self, st, end):
 
203
        """Hide text between start and end.
 
204
 
 
205
        Return midno that can be used to unhide the range."""
 
206
        midno = self.midno; self.midno += 1
 
207
        start_mark = gtk.TextMark('start-markup-%s'%midno,False)
 
208
        end_mark = gtk.TextMark('end-markup-%s'%midno,True)
 
209
        self.tb.apply_tag(self.ignore_tag,
 
210
                       st,end)
 
211
        self.tb.add_mark(start_mark,st)
 
212
        self.tb.add_mark(end_mark,end)
 
213
        self.markup_marks[midno] = (start_mark,end_mark)
 
214
        return midno
 
215
 
 
216
    def label_range (self, st, end, label):
 
217
        if self.tags_by_label.get(label,'')=='ignore':
 
218
            midno = self.hide_range(st,end)
 
219
            b = gtk.Button('Ignored text: Reveal hidden text')
 
220
            anchor = self.insert_widget(end,b)
 
221
            def unhide_text (*args):
 
222
                self.unhide_area(midno)
 
223
                self.remove_widget(anchor)
 
224
            b.connect('clicked',unhide_text)
 
225
            b.show()
 
226
            return
 
227
        if self.label_counts.has_key(label):
 
228
            count = self.label_counts[label]
 
229
            self.label_counts[label] += 1
 
230
        else:
 
231
            self.label_counts[label] = 1
 
232
            count = 0
 
233
        smark = gtk.TextMark(label+'-'+str(count)+'-start',True)
 
234
        emark = gtk.TextMark(label+'-'+str(count)+'-end',False)
 
235
        self.tb.add_mark(smark,st)
 
236
        self.tb.add_mark(emark,end)
 
237
        self.labelled.append((smark,emark))
 
238
        # Now we add the labels...
 
239
        start_txt = '['+label+':'
 
240
        start_id = self.insert_markup_text(st,start_txt,self.markup_tag)
 
241
        # Now move the mark back up...
 
242
        new_pos = self.tb.get_iter_at_mark(smark); new_pos.forward_chars(len(start_txt))
 
243
        self.tb.move_mark(smark,new_pos)
 
244
        # Create a "Remove me" button
 
245
        itr = self.tb.get_iter_at_mark(emark)
 
246
        #b = gtk.Button('_Remove tag'); b.show)(
 
247
        b = gtk.Button()
 
248
        i = gtk.Image(); i.set_from_stock(gtk.STOCK_REMOVE,gtk.ICON_SIZE_MENU)
 
249
        b.add(i); i.show()
 
250
        anchor = self.insert_widget(itr,b)
 
251
        # Add final bracket for end of markup
 
252
        end_bracket_itr = self.tb.get_iter_at_mark(emark)
 
253
        end_id = self.insert_markup_text(end_bracket_itr,']',self.markup_tag)
 
254
        self.markup_partners[start_id]=end_id; self.markup_partners[end_id]=start_id
 
255
        # Now back up our itr one character (it got advanced by adding
 
256
        # the right bracket and the button)
 
257
        eitr = self.tb.get_iter_at_mark(emark)
 
258
        eitr.backward_chars(2)
 
259
        self.tb.move_mark(emark,eitr)
 
260
        # Define callback to remove our text when button is clicked
 
261
        def remove_markup (*args):
 
262
            self.labelled.remove((smark,emark))
 
263
            self.remove_markup_text(start_id)
 
264
            self.remove_markup_text(end_id)
 
265
            self.remove_widget(anchor)
 
266
        b.connect('clicked',remove_markup)
 
267
 
 
268
    def new_recipe_cb (self, *args):
 
269
        # Start a new recipe at cursor
 
270
        itr = self.tb.get_iter_at_mark(self.tb.get_insert())
 
271
        self.label_range(itr,itr,self.NEW_REC_TEXT)
 
272
        
 
273
    def insert_markup_text (self, itr, text, *tags):
 
274
        """Insert markup text into the buffer. We do this in such a
 
275
        way that we can remove it easily later.
 
276
        """
 
277
        midno = self.midno; self.midno += 1
 
278
        start_mark = gtk.TextMark('start-markup-%s'%midno,False)
 
279
        end_mark = gtk.TextMark('end-markup-%s'%midno,True)
 
280
        start_offset = itr.get_offset()
 
281
        if tags:
 
282
            self.tb.insert_with_tags(itr,text,*tags)
 
283
        else:
 
284
            self.tb.insert(itr,text)
 
285
        self.tb.add_mark(start_mark,self.tb.get_iter_at_offset(start_offset))
 
286
        end_offset = start_offset + len(text)
 
287
        end_itr = self.tb.get_iter_at_offset(end_offset)
 
288
        self.tb.add_mark(end_mark,end_itr)
 
289
        self.markup_marks[midno] = (start_mark,end_mark)
 
290
        return midno
 
291
 
 
292
    def insert_widget (self, itr, widget):
 
293
        anchor = self.tb.create_child_anchor(itr)
 
294
        self.anchors.append(anchor)
 
295
        self.tv.add_child_at_anchor(widget,anchor)
 
296
        widgetstart = self.tb.get_iter_at_child_anchor(anchor)
 
297
        widgetend = widgetstart.copy(); widgetend.forward_char()
 
298
        self.tb.apply_tag(self.markup_tag,widgetstart,widgetend)
 
299
        widget.show()
 
300
        return anchor
 
301
 
 
302
    def remove_widget (self, anchor):
 
303
        anchor_iter = self.tb.get_iter_at_child_anchor(anchor)
 
304
        delete_to = anchor_iter.copy()
 
305
        delete_to.forward_char()
 
306
        self.tb.delete(anchor_iter,delete_to)
 
307
        
 
308
    def remove_markup_text (self, idno):
 
309
        smark,emark = self.markup_marks[idno]
 
310
        sitr,eitr = (self.tb.get_iter_at_mark(smark),
 
311
                     self.tb.get_iter_at_mark(emark))
 
312
        self.tb.delete(sitr,eitr)
 
313
 
 
314
    def clear_tags (self, *args):
 
315
        """Clear all markup in current selection, or whole buffer if
 
316
        there is no selection
 
317
        """
 
318
        cursel = self.tb.get_selection_bounds()
 
319
        if cursel:
 
320
            st,end = cursel
 
321
        else:
 
322
            st,end = self.tb.get_bounds()
 
323
        st_offset = st.get_offset()
 
324
        e_offset = end.get_offset()
 
325
        for idno,iters in self.markup_marks.items():
 
326
            lst,lend = iters
 
327
            if ((e_offset > self.tb.get_iter_at_mark(lst).get_offset() > st_offset)
 
328
                or
 
329
                (e_offset > self.tb.get_iter_at_mark(lend).get_offset() > st_offset)):
 
330
                self.remove_markup_text(idno)
 
331
                if self.markup_partners.has_key(idno):
 
332
                    self.remove_markup_text(self.markup_partners[idno])
 
333
        for lst,lend in self.labelled[:]:
 
334
            if ((e_offset > self.tb.get_iter_at_mark(lst).get_offset() > st_offset)
 
335
                or
 
336
                (e_offset > self.tb.get_iter_at_mark(lend).get_offset() > st_offset)):
 
337
                self.labelled.remove((lst,lend))
 
338
        for anchor in self.anchors[:]:
 
339
            anchor_iter = self.tb.get_iter_at_child_anchor(anchor)
 
340
            if e_offset > anchor_iter.get_offset() > st_offset:
 
341
                self.anchors.remove(anchor)
 
342
                self.remove_widget(anchor)
 
343
        
 
344
    def commit_changes (self):
 
345
        def mark_sorter (a,b):
 
346
            a = self.tb.get_iter_at_mark(a[0]).get_offset()
 
347
            b = self.tb.get_iter_at_mark(b[0]).get_offset()
 
348
            return cmp(a,b)
 
349
        self.labelled.sort(mark_sorter)
 
350
        if not self.labelled: return
 
351
        self.start_rec()
 
352
        started = False
 
353
        for smark,emark in self.labelled:
 
354
            siter = self.tb.get_iter_at_mark(smark)
 
355
            eiter = self.tb.get_iter_at_mark(emark)
 
356
            text = siter.get_text(eiter)
 
357
            name = smark.get_name()
 
358
            label = name.split('-')[0]
 
359
            print 'Processing',label,'->',text
 
360
            tag = self.tags_by_label[label]
 
361
            if tag in gglobals.TEXT_ATTR_DIC:
 
362
                self.add_text(tag,text); started=True
 
363
            elif tag in gglobals.REC_ATTR_DIC:
 
364
                if text: self.add_attribute(tag,text)
 
365
            elif tag == 'ingredient':
 
366
                if text: self.add_ing_from_text(text); started=True
 
367
            elif tag == 'ingredients':
 
368
                if text: self.add_ings_from_text(text); started=True
 
369
            elif tag == 'inggroup':
 
370
                if text: self.add_ing_group(text); started=True
 
371
            elif tag=='newrec':
 
372
                if not started: continue
 
373
                # Then we're starting a new recipe at this point...
 
374
                # Commit old recipe...
 
375
                self.commit_rec(); started=False
 
376
                # Start new one...
 
377
                self.start_rec()
 
378
            elif tag=='ignore':
 
379
                continue
 
380
            else:
 
381
                print 'UNKNOWN TAG',tag,text,label
 
382
        if started: self.commit_rec()
 
383
        print "We've added the recipes...",self.added_recs
 
384
        if hasattr(self,'images') and self.images:
 
385
            # This is ugly -- we run the dialog once per recipe. This
 
386
            # should happen rarely in current use-case (I don't know
 
387
            # of a usecase where many recipes will come from a single
 
388
            # text document / website); if in fact this becomes a
 
389
            # common usecase, we'll need to rework the UI here.
 
390
            for rec in self.added_recs:
 
391
                ibd = imageBrowser.ImageBrowserDialog(
 
392
                    title=_('Select recipe image'),
 
393
                    label=_('Select image for recipe "%s"'%rec.title or _('Untitled')),
 
394
                    sublabel=_("Below are all the images found for the page you are importing. Select any images that are of the recipe, or don't select anything if you don't want any of these images."),
 
395
                    )
 
396
                for i in self.images: ibd.add_image_from_uri(i)
 
397
                ibd.run()
 
398
                if ibd.ret:
 
399
                    ifi = file(imageBrowser.get_image_file(ibd.ret),'r')
 
400
                    image_str = ifi.read(); ifi.close()
 
401
                    image = ImageExtras.get_image_from_string(image_str)
 
402
                    # Adding image!
 
403
                    thumb = ImageExtras.resize_image(image,40,40)
 
404
                    self.rd.modify_rec(rec,{'image':ImageExtras.get_string_from_image(image),
 
405
                                            'thumb':ImageExtras.get_string_from_image(thumb),
 
406
                                            })
 
407
        if self.modal:
 
408
            self.w.hide()
 
409
            gtk.main_quit()
 
410
 
494
411
    def set_text (self, txt):
495
 
        """Set raw text."""
496
 
        txt = unicode(txt)
497
 
        if txt.strip()=='':
498
 
            raise ValueError("There is no text to set")
499
 
        self.textbuffer = gtk.TextBuffer()
500
 
        self.textview.set_buffer(self.textbuffer)
501
 
        parsed = self.parser.parse(txt,progress=self.progress)
502
 
        tagtable = self.textbuffer.get_tag_table()
503
 
        # a list of marks...
504
 
        self.sections = []
505
 
        self.section_pos = 0
506
 
        tot=len(parsed); n=0
507
 
        for line,tag in parsed:
508
 
            if self.progress:
509
 
                self.progress(float(n)/tot,
510
 
                              _('Setting up interactive importer')
511
 
                              )
512
 
                n+=1
 
412
        txt = unicode(txt) # convert to unicode for good measure
 
413
        self.set_parsed(self.parser.parse(txt))
 
414
        
 
415
 
 
416
    def set_parsed (self, parsed):
 
417
        for chunk,tag in parsed:
513
418
            if tag==None:
514
 
                self.textbuffer.insert(self.textbuffer.get_end_iter(),
515
 
                                       line)            
 
419
                self.tb.insert(self.tb.get_end_iter(),
 
420
                               chunk)
516
421
            else:
517
 
                if not tagtable.lookup(tag):
518
 
                    tagtable.add(gtk.TextTag(tag))
519
 
                smark = self.textbuffer.create_mark(None,
520
 
                                                    self.textbuffer.get_end_iter(),
521
 
                                                    True)
522
 
                self.textbuffer.insert_with_tags_by_name(
523
 
                    self.textbuffer.get_end_iter(),
524
 
                    line,
525
 
                    tag
 
422
                self.insert_with_label(
 
423
                    self.tb.get_end_iter(),
 
424
                    chunk,
 
425
                    self.labels_by_tag.get(tag,tag)
526
426
                    )
527
 
                emark = self.textbuffer.create_mark(None,
528
 
                                                    self.textbuffer.get_end_iter(),
529
 
                                                    True)
530
 
                self.sections.append((smark,emark))
531
 
        self.goto_section(0)
532
 
        self.on_new_recipe()
533
 
 
534
 
    def goto_next_section (self):
535
 
        """Goto our next section"""
536
 
        cur_sec = self.get_section_containing_mark()
537
 
        end_bound =self.textbuffer.get_iter_at_mark(
538
 
            self.sections[cur_sec][1]
539
 
            ).get_offset()
540
 
        cur_pos = self.textbuffer.get_iter_at_mark(
541
 
            self.textbuffer.get_insert()
542
 
            ).get_offset()
543
 
        if cur_pos < end_bound:
544
 
            self.goto_section(cur_sec,direction=1)
545
 
        else:
546
 
            self.goto_section(cur_sec+1,direction=1)
547
 
 
548
 
    def goto_prev_section (self):
549
 
        """Goto our previous section"""
550
 
        cur_sec = self.get_section_containing_mark()
551
 
        start_bound =self.textbuffer.get_iter_at_mark(
552
 
            self.sections[cur_sec][0]
553
 
            ).get_offset()
554
 
        cur_sel = self.textbuffer.get_selection_bounds()
555
 
        if cur_sel:
556
 
            cur_pos = (cur_sel[0].get_offset()<cur_sel[1].get_offset() and 
557
 
                       cur_sel[0].get_offset() or
558
 
                       cur_sel[1].get_offset()
559
 
                       )
560
 
        else:
561
 
            cur_pos = self.textbuffer.get_iter_at_mark(
562
 
                self.textbuffer.get_insert()
563
 
                ).get_offset()
564
 
        if cur_pos > start_bound:
565
 
            self.goto_section(cur_sec,direction=-1)
566
 
        else:
567
 
            self.goto_section(cur_sec-1,direction=-1)
568
 
 
569
 
    def section_contains_mark (self, section, mark=None):
570
 
        if not mark: mark = self.textbuffer.get_insert()
571
 
        if type(section)==int: section = self.sections[section]
572
 
        st,end = section
573
 
        siter = self.textbuffer.get_iter_at_mark(st)
574
 
        eiter = self.textbuffer.get_iter_at_mark(end)
575
 
        citer = self.textbuffer.get_iter_at_mark(mark)
576
 
        return siter.get_offset() < citer.get_offset() < eiter.get_offset()
577
 
 
578
 
    def get_section_containing_mark (self):
579
 
        """Get the current position of our cursor relative to our marks"""
580
 
        itr = self.textbuffer.get_iter_at_mark(self.textbuffer.get_insert())
581
 
        cur_offset = itr.get_offset()
582
 
        for n,mks in enumerate(self.sections):
583
 
            start = self.textbuffer.get_iter_at_mark(mks[0])
584
 
            end = self.textbuffer.get_iter_at_mark(mks[1])
585
 
            if start.get_offset() <= cur_offset < end.get_offset():
586
 
                return n
587
 
            elif cur_offset < start.get_offset():
588
 
                return n - 1
589
 
        return len(self.sections)-1
590
 
 
591
 
    def goto_section (self, n, direction=1):
592
 
        """Move to section numbered n
593
 
 
594
 
        If direction is positive, adjust forward if necessary.
595
 
        If direction is negative, adjust backward if necessary.
596
 
        """
597
 
        if n >= len(self.sections): n = len(self.sections)-1
598
 
        elif n < 0: n = 0
599
 
        self.curmark = n
600
 
        if len(self.sections)-1 <= n:
601
 
            print "There is no section ",n
602
 
            return 
603
 
        s,e=self.sections[n]
604
 
        start_itr=self.textbuffer.get_iter_at_mark(s)
605
 
        end_itr = self.textbuffer.get_iter_at_mark(e)
606
 
        # Check where our current section is
607
 
        cur_sel = self.textbuffer.get_selection_bounds()
608
 
        if cur_sel:
609
 
            soffset,eoffset = start_itr.get_offset(),end_itr.get_offset()
610
 
            cur_start,cur_end = cur_sel
611
 
            if cur_start.get_offset() < soffset and cur_end.get_offset() > eoffset:
612
 
                if direction>0: self.goto_section(n+1)
613
 
                else: self.goto_section(n-1)
614
 
            if direction > 0 and soffset < cur_end.get_offset() < eoffset:
615
 
                start_itr = cur_end
616
 
                import sys
617
 
                if re.match('\s',start_itr.get_char()):
618
 
                    start_itr.forward_find_char(lambda c,user_data: re.match('\S',c) and True,
619
 
                                                limit=end_itr)
620
 
            elif direction < 0 and soffset < cur_start.get_offset() < eoffset:
621
 
                end_itr = cur_start
622
 
                if re.match('\s',start_itr.get_char()):
623
 
                    start_itr.backward_find_char(lambda c,user_data: re.match('\S',c) and True,
624
 
                                                 limit=end_itr)
625
 
        #if end_itr.get_offset()==start_itr.get_offset(): print 'Funny, end == start'
626
 
        self.textbuffer.select_range(end_itr,
627
 
                                     start_itr
628
 
                                     )
629
 
        self.textview.scroll_to_iter(start_itr,0.3)
630
 
        self.on_cursor_moved(self.textview)
 
427
    
 
428
    def do_run (self):
 
429
        self.w.show_all()
 
430
        if self.modal:
 
431
            self.w.connect('delete-event',gtk.main_quit)
 
432
            gtk.main()
 
433
        else:
 
434
            self.w.connect('delete-event',lambda *args: self.w.hide())
631
435
        
 
436
    
632
437
            
633
 
    #-- InteractiveImporter custom methods }
634
 
 
635
 
    #-- InteractiveImporter.on_open {
636
 
    def on_open(self, widget, *args):
637
 
        fname = dialog_extras.select_file('Open recipe',
638
 
                                         filters=[['Plain Text',['text/plain'],'*.txt']])
639
 
        if fname:
640
 
            ofi = file(fname,'r')
641
 
            self.set_text(ofi.read())
642
 
            ofi.close()            
643
 
    #-- InteractiveImporter.on_open }
644
 
 
645
 
    #-- InteractiveImporter.on_open_url {
646
 
    def on_open_url(self, widget, *args):
647
 
        # A quick hack to try web import... eventually we'll want to
648
 
        # use urllib to do something crossplatform and reasonable if
649
 
        # we want this (and of course we can just borrow code from
650
 
        # Gourmet which already does this right)
651
 
        url = dialog_extras.getEntry(label='Enter address of webpage with a recipe on it.',
652
 
                                     entryLabel='URL:',
653
 
                                     entryTip="""URLs start with http://""")
654
 
        if url.find('//')<0: url = 'http://'+url
655
 
        ifi = os.popen('w3m -T text/html -dump %(url)s'%locals())
656
 
        self.set_text(ifi.read())
657
 
    #-- InteractiveImporter.on_open_url }
658
 
 
659
 
    #-- InteractiveImporter.on_save {
660
 
    def on_save(self, widget, *args):
661
 
        self.commit_rec()
662
 
 
663
 
    #-- InteractiveImporter.on_quit {
664
 
    def on_quit(self, widget, *args):
665
 
        self.commit_rec()
666
 
        self.glade.get_widget('window1').hide()
667
 
        if self.progress: self.progress(1,_('Import complete!'))
668
 
        gtk.main_quit()
669
 
        
670
 
    #-- InteractiveImporter.on_quit }
671
 
 
672
 
    def set_added_to (self, bool):
673
 
        """Set a switch that we have been added to, or not."""
674
 
        self.added_to = bool
675
 
        if bool: self.glade.get_widget('NewRecipeButton').set_sensitive(bool)
676
 
 
677
 
    def on_new_recipe (self, *args):
678
 
        # If we already have a recipe
679
 
        if self.added_to:
680
 
            self.commit_rec()
681
 
        self.start_rec()
682
 
        self.set_added_to(False)
683
 
 
684
 
    #-- InteractiveImporter.on_cursor_moved {
685
 
    def on_cursor_moved (self, widget, *args):
686
 
        cursor = self.textbuffer.get_insert()
687
 
        itr = self.textbuffer.get_iter_at_mark(cursor)
688
 
        tags = itr.get_tags()
689
 
        while not tags:
690
 
            itr = self.textbuffer.get_iter_at_offset(
691
 
                itr.get_offset()-1
692
 
                )
693
 
            tags = itr.get_tags()
694
 
        action = None
695
 
        for t in tags:
696
 
            if self.parser_to_choice.has_key(t.get_property('name')):
697
 
                action = self.parser_to_choice[t.get_property('name')]
698
 
            elif self.attdic.has_key(t.get_property('name')):
699
 
                action = self.attdic[t.get_property('name')]
700
 
            elif self.textattdic.has_key(t.get_property('name')):
701
 
                action = self.textattdic[t.get_property('name')]
702
 
        self.set_current_action(action)
703
 
        
704
 
    #-- InteractiveImporter.on_cursor_moved }
705
 
 
706
 
    #-- InteractiveImporter.on_ingredientEventBox_drag_drop {
707
 
    def on_ingredientEventBox_drag_drop(self, widget, *args):
708
 
        pass
709
 
    #-- InteractiveImporter.on_ingredientEventBox_drag_drop }
710
 
 
711
 
    #-- InteractiveImporter.on_back {
712
 
    def on_back(self, widget, *args):
713
 
        self.goto_prev_section()
714
 
    #-- InteractiveImporter.on_back }
715
 
 
716
 
    #-- InteractiveImporter.on_forward {
717
 
    def on_forward(self, widget, *args):
718
 
        self.goto_next_section()
719
 
    #-- InteractiveImporter.on_forward }
720
 
 
721
 
    #-- InteractiveImporter.on_apply {
722
 
    def on_apply(self, widget, *args):
723
 
        selection = self.textbuffer.get_text(
724
 
            *self.textbuffer.get_selection_bounds()
725
 
            )
726
 
        active_txt = self.get_current_action()
727
 
        if not hasattr(self,'inserted_tag'):
728
 
            self.inserted_tag = gtk.TextTag('inserted')
729
 
            self.inserted_tag.set_property('editable',False)
730
 
            self.inserted_tag.set_property('style',pango.STYLE_ITALIC)
731
 
        if not self.textbuffer.get_tag_table().lookup('inserted'):
732
 
            self.textbuffer.get_tag_table().add(self.inserted_tag)
733
 
        self.textbuffer.apply_tag(self.inserted_tag,*self.textbuffer.get_selection_bounds())
734
 
        if not hasattr(self,'markup_tag'):
735
 
            self.markup_tag = gtk.TextTag('markup')
736
 
            self.markup_tag.set_property('editable',False)
737
 
            self.markup_tag.set_property('scale',pango.SCALE_SMALL)
738
 
            self.markup_tag.set_property('rise',15)
739
 
            self.markup_tag.set_property('foreground',
740
 
                                         '#f00'
741
 
                                         )
742
 
        if not self.textbuffer.get_tag_table().lookup('markup'):
743
 
            self.textbuffer.get_tag_table().add(self.markup_tag)
744
 
        st,end = self.textbuffer.get_selection_bounds()
745
 
        self.textbuffer.insert_with_tags(st,'['+active_txt+':',self.markup_tag)
746
 
        st,end = self.textbuffer.get_selection_bounds()
747
 
        while end.starts_line() and end.backward_char():
748
 
            end = self.textbuffer.get_iter_at_offset(end.get_offset()-1)
749
 
 
750
 
        # Here's how we would add a "Remove" button -- but this really
751
 
        # requires rethinking the whole process...
752
 
        #
753
 
        #anchor = self.textbuffer.create_child_anchor(end)
754
 
        #button = gtk.Button('_Remove tag'); button.show()
755
 
        #self.textview.add_child_at_anchor(button,anchor)
756
 
        
757
 
        self.textbuffer.insert_with_tags(end,']',self.markup_tag)
758
 
        action = self.actions[active_txt]
759
 
        if self.action_to_label.has_key(active_txt):
760
 
            active_txt = self.action_to_label[active_txt]
761
 
        action(active_txt,selection)
762
 
        self.on_forward(None)
763
 
    #-- InteractiveImporter.on_apply }
764
 
 
765
 
class InteractiveTextImporter (InteractiveImporter):
766
 
    def __init__ (self, filename, rd, progress=None, source=None, threaded=False,custom_parser=None):
767
 
        InteractiveImporter.__init__(self,rd,progress=progress,custom_parser=custom_parser)
768
 
        ofi = file(filename,'r')
769
 
        self.set_text(
770
 
            '\n'.join(check_encodings.get_file(ofi)) # this is mildly idiotic
771
 
            )
772
 
        #self.set_text(ofi.read())
773
 
        ofi.close()
774
 
 
775
 
    def __repr__ (self): return "<InteractiveTextImporter>"
776
 
 
777
 
def main():
778
 
    from gourmet import recipeManager
779
 
    rd = recipeManager.RecipeManager(file='/tmp/foo.db')
780
 
    window1 = InteractiveImporter(rd)
781
 
    window1.set_text(
 
438
if __name__ == '__main__':
 
439
    ii = InteractiveImporter()
 
440
    ii.w.connect('delete-event',gtk.main_quit)
 
441
    ii.w.show_all()
 
442
    if True:
 
443
        ii.images = ['http://grecipe-manager.sourceforge.net/CardView.png']
 
444
        ii.set_text(
782
445
        u"""
783
446
Quick Pesto Dinner
784
447
 
803
466
Chop up tomatoes roughly.
804
467
 
805
468
Toss spaghetti in pesto and tomatoes.
 
469
 
 
470
Ignore: this
806
471
""")
807
 
    window1.run()        
808
 
    return window1
809
 
 
810
 
if __name__ == '__main__':
811
 
    ii = main()
 
472
    gtk.main()
 
473