~rosco2/ubuntu/wily/gramps/bug-1492304

« back to all changes in this revision

Viewing changes to src/plugins/FanChart.py

  • Committer: Bazaar Package Importer
  • Author(s): James A. Treacy
  • Date: 2004-06-16 16:53:36 UTC
  • Revision ID: james.westby@ubuntu.com-20040616165336-kjzczqef4gnxrn2b
Tags: upstream-1.0.4
ImportĀ upstreamĀ versionĀ 1.0.4

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#
 
2
# Gramps - a GTK+/GNOME based genealogy program
 
3
#
 
4
# Copyright (C) 2003  Donald N. Allingham
 
5
#
 
6
# This program is free software; you can redistribute it and/or modify
 
7
# it under the terms of the GNU General Public License as published by
 
8
# the Free Software Foundation; either version 2 of the License, or
 
9
# (at your option) any later version.
 
10
#
 
11
# This program is distributed in the hope that it will be useful,
 
12
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 
13
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
14
# GNU General Public License for more details.
 
15
#
 
16
# You should have received a copy of the GNU General Public License
 
17
# along with this program; if not, write to the Free Software
 
18
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
19
#
 
20
 
 
21
#------------------------------------------------------------------------
 
22
#
 
23
# gnome/gtk
 
24
#
 
25
#------------------------------------------------------------------------
 
26
import gtk
 
27
 
 
28
#------------------------------------------------------------------------
 
29
#
 
30
# gramps modules
 
31
#
 
32
#------------------------------------------------------------------------
 
33
import GrampsCfg
 
34
import BaseDoc
 
35
import Report
 
36
import Errors
 
37
import Calendar
 
38
 
 
39
from QuestionDialog import ErrorDialog
 
40
from FontScale import string_width
 
41
from SubstKeywords import SubstKeywords
 
42
from gettext import gettext as _
 
43
 
 
44
#------------------------------------------------------------------------
 
45
#
 
46
# pt2cm - convert points to centimeters
 
47
#
 
48
#------------------------------------------------------------------------
 
49
def pt2cm(pt):
 
50
    return (float(pt)/72.0)*(254.0/100.0)
 
51
 
 
52
#------------------------------------------------------------------------
 
53
#
 
54
# FanChart
 
55
#
 
56
#------------------------------------------------------------------------
 
57
class FanChart:
 
58
 
 
59
    def __init__(self,database,person,display,doc,output,newpage=0):
 
60
        self.doc = doc
 
61
        self.doc.creator(database.getResearcher().getName())
 
62
        self.map = {}
 
63
        self.text = {}
 
64
        self.start = person
 
65
        self.output = output
 
66
        self.box_width = 0
 
67
        self.height = 0
 
68
        self.lines = 0
 
69
        self.display = display
 
70
        self.newpage = newpage
 
71
        if output:
 
72
            self.standalone = 1
 
73
            self.doc.open(output)
 
74
        else:
 
75
            self.standalone = 0
 
76
 
 
77
        g = BaseDoc.GraphicsStyle()
 
78
        g.set_paragraph_style('FC-Title')
 
79
        g.set_line_width(0)
 
80
        self.doc.add_draw_style("t",g)
 
81
 
 
82
        g = BaseDoc.GraphicsStyle()
 
83
        g.set_fill_color((255,212,210))
 
84
        g.set_paragraph_style('FC-Normal')
 
85
        self.doc.add_draw_style("FC-c1",g)
 
86
 
 
87
        g = BaseDoc.GraphicsStyle()
 
88
        g.set_fill_color((255,212,210))
 
89
        g.set_paragraph_style('FC-Normal')
 
90
        g.set_line_width(0)
 
91
        self.doc.add_draw_style("FC-c1n",g)
 
92
 
 
93
        g = BaseDoc.GraphicsStyle()
 
94
        g.set_fill_color((251,204,158))
 
95
        g.set_paragraph_style('FC-Normal')
 
96
        self.doc.add_draw_style("FC-c2",g)
 
97
 
 
98
        g = BaseDoc.GraphicsStyle()
 
99
        g.set_fill_color((251,204,158))
 
100
        g.set_paragraph_style('FC-Normal')
 
101
        g.set_line_width(0)
 
102
        self.doc.add_draw_style("FC-c2n",g)
 
103
 
 
104
        g = BaseDoc.GraphicsStyle()
 
105
        g.set_fill_color((255,255,111))
 
106
        g.set_paragraph_style('FC-Normal')
 
107
        self.doc.add_draw_style("FC-c3",g)
 
108
 
 
109
        g = BaseDoc.GraphicsStyle()
 
110
        g.set_fill_color((255,255,111))
 
111
        g.set_paragraph_style('FC-Normal')
 
112
        g.set_line_width(0)
 
113
        self.doc.add_draw_style("FC-c3n",g)
 
114
 
 
115
        g = BaseDoc.GraphicsStyle()
 
116
        g.set_fill_color((158,255,158))
 
117
        g.set_paragraph_style('FC-Normal')
 
118
        self.doc.add_draw_style("FC-c4",g)
 
119
 
 
120
        g = BaseDoc.GraphicsStyle()
 
121
        g.set_fill_color((158,255,158))
 
122
        g.set_paragraph_style('FC-Normal')
 
123
        g.set_line_width(0)
 
124
        self.doc.add_draw_style("FC-c4n",g)
 
125
 
 
126
        g = BaseDoc.GraphicsStyle()
 
127
        g.set_fill_color((156,205,255))
 
128
        g.set_paragraph_style('FC-Normal')
 
129
        self.doc.add_draw_style("FC-c5",g)
 
130
 
 
131
        g = BaseDoc.GraphicsStyle()
 
132
        g.set_fill_color((156,205,255))
 
133
        g.set_paragraph_style('FC-Normal')
 
134
        g.set_line_width(0)
 
135
        self.doc.add_draw_style("FC-c5n",g)
 
136
 
 
137
        self.map = [None] * 32
 
138
        self.text= {}
 
139
        self.box_width = 0
 
140
        if self.standalone:
 
141
            self.doc.init()
 
142
 
 
143
    def filter(self,person,index):
 
144
        """traverse the ancestors recursively until either the end
 
145
        of a line is found, or until we reach the maximum number of 
 
146
        generations that we want to deal with"""
 
147
        
 
148
        if person == None or index >= 32:
 
149
            return
 
150
        self.map[index-1] = person
 
151
 
 
152
        self.text[index-1] = []
 
153
 
 
154
        subst = SubstKeywords(person)
 
155
        
 
156
        for line in self.display:
 
157
            self.text[index-1].append(subst.replace(line))
 
158
 
 
159
        self.font = self.doc.style_list["FC-Normal"].get_font()
 
160
        for line in self.text[index-1]:
 
161
            self.box_width = max(self.box_width,string_width(self.font,line))
 
162
 
 
163
        self.lines = max(self.lines,len(self.text[index-1]))    
 
164
 
 
165
        family = person.getMainParents()
 
166
        if family != None:
 
167
            self.filter(family.getFather(),index*2)
 
168
            self.filter(family.getMother(),(index*2)+1)
 
169
 
 
170
    def write_report(self):
 
171
 
 
172
        self.filter(self.start,1)
 
173
 
 
174
        block_size = self.doc.get_usable_width()/14.0
 
175
 
 
176
        if self.newpage:
 
177
            self.doc.page_break()
 
178
 
 
179
        size = min(self.doc.get_usable_width(),self.doc.get_usable_height()*2.0)/2.0
 
180
        y = self.doc.get_usable_height()
 
181
        max_lines = int(size/block_size)
 
182
        center = (self.doc.get_usable_width()/2.0)
 
183
 
 
184
        self.doc.start_page()
 
185
        
 
186
        n = self.start.getPrimaryName().getRegularName()
 
187
        self.doc.center_text('t', _('Five Generation Fan Chart for %s') % n, center, 0)
 
188
 
 
189
        self.circle_5(center,y,block_size)
 
190
        self.circle_4(center,y,block_size)
 
191
        self.circle_3(center,y,block_size)
 
192
        self.circle_2(center,y,block_size)
 
193
        self.circle_1(center,y,block_size)
 
194
 
 
195
        self.doc.end_page()
 
196
        if self.standalone:
 
197
            self.doc.close()
 
198
 
 
199
    def get_info(self,person):
 
200
        pn = person.getPrimaryName()
 
201
        b = person.getBirth().getDateObj().getYear()
 
202
        d = person.getDeath().getDateObj().getYear()
 
203
        if b == Calendar.UNDEF:
 
204
            b = ""
 
205
        if d == Calendar.UNDEF:
 
206
            d = ""
 
207
 
 
208
        if b or d:
 
209
            val = "%s - %s" % (str(b),str(d))
 
210
        else:
 
211
            val = ""
 
212
            
 
213
        return [ pn.getFirstName(), pn.getSurname(), val ]
 
214
 
 
215
    def circle_1(self,center,y,size):
 
216
        (xc,yc) = self.doc.draw_wedge("FC-c1", center, y, size, 180, 360)
 
217
        self.doc.rotate_text("FC-c1n", self.get_info(self.map[0]), xc, yc ,0)
 
218
 
 
219
    def circle_2(self,center,y,size):
 
220
        (xc,yc) = self.doc.draw_wedge("FC-c2", center, y, size*2, 180, 270, size)
 
221
        if self.map[1]:
 
222
            self.doc.rotate_text("FC-c2n", self.get_info(self.map[1]), xc, yc, -45)
 
223
            
 
224
        (xc,yc) = self.doc.draw_wedge("FC-c2", center, y, size*2, 270, 360, size)
 
225
        if self.map[2]:
 
226
            self.doc.rotate_text("FC-c2n", self.get_info(self.map[2]), xc,yc ,45)
 
227
 
 
228
    def circle_3(self,center,y,size):
 
229
        delta = 45
 
230
        sangle = -67.5
 
231
        for index in range(3,7):
 
232
            start = 180+(index-3)*45
 
233
            stop = start+45
 
234
            (xc,yc) = self.doc.draw_wedge("FC-c3", center, y, size*3, start, stop, size*2)
 
235
            if self.map[index]:
 
236
                self.doc.rotate_text("FC-c3n", self.get_info(self.map[index]),
 
237
                                     xc,yc ,sangle)
 
238
            sangle += 45
 
239
 
 
240
    def circle_4(self,center,y,size):
 
241
        delta = 22.5
 
242
        sangle = -78.75 + 90
 
243
        for i in range(0,8):
 
244
            start_angle = 180 + (i * delta)
 
245
            end_angle = 180 + ((i+1) * delta)
 
246
            (xc,yc) = self.doc.draw_wedge("FC-c4", center, y, size*5, start_angle,
 
247
                                          end_angle, size*3)
 
248
            if i == 4:
 
249
                sangle += 180
 
250
            if self.map[i+7]:
 
251
                self.doc.rotate_text("FC-c4n", self.get_info(self.map[i+7]),
 
252
                                     xc,yc ,sangle)
 
253
            sangle += 22.5
 
254
 
 
255
    def circle_5(self,center,y,size):
 
256
        delta = 11.25
 
257
        sangle = -84.625 + 90
 
258
        for i in range(0,16):
 
259
            start_angle = 180 + (i * delta)
 
260
            end_angle = 180 + ((i+1) * delta)
 
261
            (xc,yc) = self.doc.draw_wedge("FC-c5", center, y, size*7, start_angle,
 
262
                                          end_angle, size*5)
 
263
            if i == 8:
 
264
                sangle += 180
 
265
            if self.map[i+15]:
 
266
                self.doc.rotate_text("FC-c5n", self.get_info(self.map[i+15]),
 
267
                                     xc,yc ,sangle)
 
268
            sangle += 11.25
 
269
 
 
270
#------------------------------------------------------------------------
 
271
#
 
272
 
273
#
 
274
#------------------------------------------------------------------------
 
275
def _make_default_style(default_style):
 
276
    """Make the default output style for the Fan Chart report."""
 
277
    f = BaseDoc.FontStyle()
 
278
    f.set_size(8)
 
279
    f.set_type_face(BaseDoc.FONT_SANS_SERIF)
 
280
    p = BaseDoc.ParagraphStyle()
 
281
    p.set_font(f)
 
282
    p.set_alignment(BaseDoc.PARA_ALIGN_CENTER)
 
283
    p.set_description(_('The basic style used for the text display.'))
 
284
    default_style.add_style("FC-Normal",p)
 
285
 
 
286
    f = BaseDoc.FontStyle()
 
287
    f.set_size(20)
 
288
    f.set_bold(1)
 
289
    f.set_type_face(BaseDoc.FONT_SANS_SERIF)
 
290
    p = BaseDoc.ParagraphStyle()
 
291
    p.set_font(f)
 
292
    p.set_alignment(BaseDoc.PARA_ALIGN_CENTER)
 
293
    p.set_description(_('The style used for the title.'))
 
294
    default_style.add_style("FC-Title",p)
 
295
 
 
296
#------------------------------------------------------------------------
 
297
#
 
298
# FanChartDialog 
 
299
#
 
300
#------------------------------------------------------------------------
 
301
class FanChartDialog(Report.DrawReportDialog):
 
302
 
 
303
    report_options = {}
 
304
    
 
305
    def __init__(self,database,person):
 
306
        Report.DrawReportDialog.__init__(self,database,person,self.report_options)
 
307
 
 
308
    def get_title(self):
 
309
        """The window title for this dialog"""
 
310
        return "%s - %s - GRAMPS" % (_("Fan Chart"),_("Graphical Reports"))
 
311
 
 
312
    def get_header(self, name):
 
313
        """The header line at the top of the dialog contents."""
 
314
        return _("Fan Chart for %s") % name
 
315
 
 
316
    def get_target_browser_title(self):
 
317
        """The title of the window created when the 'browse' button is
 
318
        clicked in the 'Save As' frame."""
 
319
        return _("Save Fan Chart")
 
320
 
 
321
    def get_stylesheet_savefile(self):
 
322
        """Where to save user defined styles for this report."""
 
323
        return _style_file
 
324
    
 
325
    def get_report_generations(self):
 
326
        """Default to 10 generations, no page breaks."""
 
327
        return (0, 0)
 
328
 
 
329
    def make_default_style(self):
 
330
        _make_default_style(self.default_style)
 
331
 
 
332
    def make_report(self):
 
333
        """Create the object that will produce the Fan Chart.
 
334
        All user dialog has already been handled and the output file
 
335
        opened."""
 
336
 
 
337
        try:
 
338
            MyReport = FanChart(self.db, self.person,
 
339
                        "%n", self.doc, self.target_path)
 
340
            MyReport.write_report()
 
341
        except Errors.FilterError, msg:
 
342
            (m1,m2) = msg.messages()
 
343
            ErrorDialog(m1,m2)
 
344
        except IOError, msg:
 
345
            ErrorDialog(_("Could not create %s" % self.target_path),msg)
 
346
        except:
 
347
            import DisplayTrace
 
348
            DisplayTrace.DisplayTrace()
 
349
 
 
350
#------------------------------------------------------------------------
 
351
#
 
352
# Entry point of the report. Takes the database and the active person
 
353
# as its arguments.
 
354
#
 
355
#------------------------------------------------------------------------
 
356
def report(database,person):
 
357
    FanChartDialog(database,person)
 
358
 
 
359
#------------------------------------------------------------------------
 
360
#
 
361
# Set up sane defaults for the book_item
 
362
#
 
363
#------------------------------------------------------------------------
 
364
_style_file = "fan_chart.xml"
 
365
_style_name = "default" 
 
366
 
 
367
_person_id = ""
 
368
_options = ( _person_id, )
 
369
 
 
370
#------------------------------------------------------------------------
 
371
#
 
372
# Book Item Options dialog
 
373
#
 
374
#------------------------------------------------------------------------
 
375
class FanChartBareDialog(Report.BareReportDialog):
 
376
 
 
377
    def __init__(self,database,person,opt,stl):
 
378
 
 
379
        self.options = opt
 
380
        self.db = database
 
381
        if self.options[0]:
 
382
            self.person = self.db.getPerson(self.options[0])
 
383
        else:
 
384
            self.person = person
 
385
        self.style_name = stl
 
386
 
 
387
        Report.BareReportDialog.__init__(self,database,self.person)
 
388
        self.new_person = None
 
389
        self.window.run()
 
390
 
 
391
    #------------------------------------------------------------------------
 
392
    #
 
393
    # Customization hooks
 
394
    #
 
395
    #------------------------------------------------------------------------
 
396
    def get_title(self):
 
397
        """The window title for this dialog"""
 
398
        return "%s - GRAMPS Book" % (_("Fan Chart"))
 
399
 
 
400
    def get_header(self, name):
 
401
        """The header line at the top of the dialog contents"""
 
402
        return _("Fan Chart for GRAMPS Book") 
 
403
 
 
404
    def get_stylesheet_savefile(self):
 
405
        """Where to save styles for this report."""
 
406
        return _style_file
 
407
    
 
408
    def get_report_generations(self):
 
409
        """No generations, no page breaks."""
 
410
        return (0, 0)
 
411
    
 
412
    def make_default_style(self):
 
413
        _make_default_style(self.default_style)
 
414
 
 
415
    def on_cancel(self, obj):
 
416
        pass
 
417
 
 
418
    def on_ok_clicked(self, obj):
 
419
        """The user is satisfied with the dialog choices. Parse all options
 
420
        and close the window."""
 
421
 
 
422
        # Preparation
 
423
        self.parse_style_frame()
 
424
        
 
425
        if self.new_person:
 
426
            self.person = self.new_person
 
427
        self.options = ( self.person.getId(), )
 
428
        self.style_name = self.selected_style.get_name()
 
429
 
 
430
#------------------------------------------------------------------------
 
431
#
 
432
# Function to write Book Item 
 
433
#
 
434
#------------------------------------------------------------------------
 
435
def write_book_item(database,person,doc,options,newpage=0):
 
436
    """Write the Fan Chart using options set.
 
437
    All user dialog has already been handled and the output file opened."""
 
438
    try:
 
439
        if options[0]:
 
440
            person = database.getPerson(options[0])
 
441
        return FanChart(database, person, 
 
442
                                   "%n", doc, None, newpage )
 
443
    except Errors.ReportError, msg:
 
444
        (m1,m2) = msg.messages()
 
445
        ErrorDialog(m1,m2)
 
446
    except Errors.FilterError, msg:
 
447
        (m1,m2) = msg.messages()
 
448
        ErrorDialog(m1,m2)
 
449
    except:
 
450
        import DisplayTrace
 
451
        DisplayTrace.DisplayTrace()
 
452
 
 
453
#------------------------------------------------------------------------
 
454
#
 
455
# Register the report with the plugin system. If this is not done, then
 
456
# GRAMPS will not know that the report exists.
 
457
#
 
458
#------------------------------------------------------------------------
 
459
from Plugins import register_report, register_book_item
 
460
 
 
461
register_report(
 
462
    report,
 
463
    _("Fan Chart"),
 
464
    category=_("Graphical Reports"),
 
465
    status=(_("Alpha")),
 
466
    description=_("Produces a five generation fan chart")
 
467
    )
 
468
 
 
469
# (name,category,options_dialog,write_book_item,options,style_name,style_file,make_default_style)
 
470
register_book_item( 
 
471
    _("Fan Chart"), 
 
472
    _("Graphics"),
 
473
    FanChartBareDialog,
 
474
    write_book_item,
 
475
    _options,
 
476
    _style_name,
 
477
    _style_file,
 
478
    _make_default_style
 
479
    )