1#
2# Gramps - a GTK+/GNOME based genealogy program
3#
4# Copyright (C) 2013       John Ralls <jralls@ceridwen.us>
5# Copyright (C) 2013-2017  Paul Franklin
6#
7# This program is free software; you can redistribute it and/or modify
8# it under the terms of the GNU General Public License as published by
9# the Free Software Foundation; either version 2 of the License, or
10# (at your option) any later version.
11#
12# This program is distributed in the hope that it will be useful,
13# but WITHOUT ANY WARRANTY; without even the implied warranty of
14# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15# GNU General Public License for more details.
16#
17# You should have received a copy of the GNU General Public License
18# along with this program; if not, write to the Free Software
19# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20#
21
22"""
23Commonly used report options. Call the function, don't copy the code!
24"""
25
26#-------------------------------------------------------------------------
27#
28# Gramps modules
29#
30#-------------------------------------------------------------------------
31from ...config import config
32from ...datehandler import get_date_formats, LANG_TO_DISPLAY, main_locale
33from ...display.name import displayer as global_name_display
34from ...lib.date import Today
35from ...display.place import displayer as _pd
36from ..menu import EnumeratedListOption, BooleanOption, NumberOption
37from ...proxy import PrivateProxyDb, LivingProxyDb
38from ...utils.grampslocale import GrampsLocale
39from ...const import GRAMPS_LOCALE as glocale
40_ = glocale.translation.sgettext
41
42# _T_ is a gramps-defined keyword -- see po/update_po.py and po/genpot.sh
43def _T_(value): # enable deferred translations (see Python docs 22.1.3.4)
44    return value
45
46#-------------------------------------------------------------------------
47#
48# StandardReportOptions
49#
50#-------------------------------------------------------------------------
51
52def add_localization_option(menu, category):
53    """
54    Insert an option for localizing the report into a different locale
55    from the UI locale.
56    """
57
58    trans = EnumeratedListOption(_("Translation"),
59                                 glocale.DEFAULT_TRANSLATION_STR)
60    trans.add_item(glocale.DEFAULT_TRANSLATION_STR, _("Default"))
61    languages = glocale.get_language_dict()
62    for language in sorted(languages, key=glocale.sort_key):
63        trans.add_item(languages[language], language)
64    trans.set_help(_("The translation to be used for the report."))
65    menu.add_option(category, "trans", trans)
66    return trans
67
68def add_name_format_option(menu, category):
69    """
70    Insert an option for changing the report's name format to a
71    report-specific format instead of the user's Edit=>Preferences choice
72    """
73    name_format = EnumeratedListOption(_("Name format"), 0)
74    name_format.add_item(0, _("Default"))
75    format_list = global_name_display.get_name_format()
76    for number, name, format_string, whether_active in format_list:
77        name_format.add_item(number, name)
78    name_format.set_help(_("Select the format to display names"))
79    current_format = config.get('preferences.name-format')
80    # if this report hasn't ever been run, start with user's current setting
81    name_format.set_value(current_format)
82    # if the report has been run, this line will get the user's old setting
83    menu.add_option(category, "name_format", name_format)
84    return name_format
85
86def run_name_format_option(report, menu):
87    """
88    Run the option for changing the report's name format to a
89    report-specific format instead of the user's Edit=>Preferences choice
90    """
91    current_format = config.get('preferences.name-format')
92    name_format = menu.get_option_by_name("name_format").get_value()
93    if name_format != current_format:
94        report._name_display.set_default_format(name_format)
95    return name_format
96
97def add_private_data_option(menu, category, default=True):
98    """
99    Insert an option for deciding whether the information in the
100    database marked "private" shall be included in the report
101
102    Since historically, before this option, the entire database was
103    used, including private information, the default for this option
104    has been set to be True, to include such private information.
105    """
106    incl_private = BooleanOption(_("Include data marked private"), default)
107    incl_private.set_help(_("Whether to include private data"))
108    menu.add_option(category, "incl_private", incl_private)
109
110def run_private_data_option(report, menu):
111    """
112    Run the option for deciding whether the information in the
113    database marked "private" shall be included in the report
114    """
115    include_private_data = menu.get_option_by_name('incl_private').get_value()
116    if not include_private_data:
117        report.database = PrivateProxyDb(report.database)
118
119def add_living_people_option(menu, category,
120                             mode=LivingProxyDb.MODE_INCLUDE_ALL,
121                             after_death_years=0,
122                             process_names=True):
123    """
124    Insert an option for deciding how the information in the
125    database for living people shall be handled by the report
126
127    Because historically, before this option, the entire database was
128    used, including all information on all living people, the default
129    mode for this option has been set to include all such information
130
131    The value of the "living_people" option is the same as the value
132    of the "mode" argument in the call to the LivingProxyDb proxy DB
133
134    :param menu: The menu the options should be added to.
135    :type menu: :class:`.Menu`
136    :param category: A label that describes the category that the option
137        belongs to.
138        Example: "Report Options"
139    :type category: string
140    :param mode:
141        The method for handling living people.
142        LivingProxyDb.MODE_EXCLUDE_ALL will remove living people altogether.
143        LivingProxyDb.MODE_INCLUDE_LAST_NAME_ONLY will remove all
144            information and change their given name to "[Living]" or whatever
145            has been set in Preferences -> Text -> Private given name.
146        LivingProxyDb.MODE_REPLACE_COMPLETE_NAME will remove all
147            information and change their given name and surname to
148            "[Living]" or whatever has been set in Preferences -> Text
149            for Private surname and Private given name.
150        LivingProxyDb.MODE_INCLUDE_FULL_NAME_ONLY will remove all
151            information but leave the entire name intact.
152        LivingProxyDb.MODE_INCLUDE_ALL will not invoke LivingProxyDb at all.
153    :type mode: int
154    :param after_death_years:
155        The number of years after a person's death to
156        still consider them as living.
157    :type after_death_years: int
158    :return: nothing
159    :param process_names: whether to offer name-oriented option choices
160    :type process_names: Boolean
161    """
162
163    def living_people_changed():
164        """
165        Handle a change in the living_people option
166        """
167        if living_people.get_value() == LivingProxyDb.MODE_INCLUDE_ALL:
168            years_past_death.set_available(False)
169        else:
170            years_past_death.set_available(True)
171
172    living_people = EnumeratedListOption(_("Living People"), mode)
173    items = [(LivingProxyDb.MODE_INCLUDE_ALL,
174              _T_("'living people'|Included, and all data"))]
175    if process_names:
176        items += [
177            (LivingProxyDb.MODE_INCLUDE_FULL_NAME_ONLY,
178             _T_("'living people'|Full names, but data removed")),
179            (LivingProxyDb.MODE_INCLUDE_LAST_NAME_ONLY,
180             _T_("'living people'|Given names replaced, and data removed")),
181            (LivingProxyDb.MODE_REPLACE_COMPLETE_NAME,
182             _T_("'living people'|Complete names replaced, and data removed"))]
183    items += [(LivingProxyDb.MODE_EXCLUDE_ALL,
184               _T_("'living people'|Not included"))]
185    living_people.set_items(items, xml_items=True) # for deferred translation
186    living_people.set_help(_("How to handle living people"))
187    menu.add_option(category, "living_people", living_people)
188    living_people.connect('value-changed', living_people_changed)
189    years_past_death = NumberOption(_("Years from death to consider living"),
190                                      after_death_years, 0, 100)
191    years_past_death.set_help(
192        _("Whether to restrict data on recently-dead people"))
193    menu.add_option(category, "years_past_death", years_past_death)
194    living_people_changed()
195
196def run_living_people_option(report, menu, llocale=glocale):
197    """
198    Run the option for deciding how the information in the
199    database for living people shall be handled by the report
200
201    If llocale is passed in (a :class:`.GrampsLocale`), then (insofar as
202    possible) the translated values will be returned instead.
203
204    :param llocale: allow deferred translation of "[Living]"
205    :type llocale: a :class:`.GrampsLocale` instance
206    """
207    option = menu.get_option_by_name('living_people')
208    living_value = option.get_value()
209    years_past_death = menu.get_option_by_name('years_past_death').get_value()
210    if living_value != LivingProxyDb.MODE_INCLUDE_ALL:
211        report.database = LivingProxyDb(report.database, living_value,
212                                        years_after_death=years_past_death,
213                                        llocale=llocale)
214    return option
215
216def add_date_format_option(menu, category, localization_option):
217    """
218    Insert an option for changing the report's date format to a
219    report-specific format instead of the user's Edit=>Preferences choice
220
221    :param localization_option: allow realtime translation of date formats
222    :type localization_option: a :class:`.EnumeratedListOption` instance
223    """
224
225    def on_trans_value_changed():
226        """
227        Handle a change in the localization option (inside the date-format one)
228        """
229        lang = localization_option.get_value()
230        if lang == GrampsLocale.DEFAULT_TRANSLATION_STR: # the UI language
231            vlocale = glocale
232        elif lang in LANG_TO_DISPLAY: # a displayer exists
233            vlocale = LANG_TO_DISPLAY[lang]._locale # locale is already loaded
234        else: # no displayer
235            vlocale = GrampsLocale(lang=main_locale[lang])
236        ldd = vlocale.date_displayer
237        target_format_list = get_date_formats(vlocale) # get localized formats
238        # trans_text is a defined keyword (see po/update_po.py, po/genpot.sh)
239        trans_text = vlocale.translation.sgettext
240        if global_date_format < len(target_format_list):
241            ldd.set_format(global_date_format)
242            example = vlocale.get_date(today)
243            target_option_list = [
244                (0, "%s (%s) (%s)" % (trans_text("Default"),
245                                      target_format_list[global_date_format],
246                                      example))]
247        else:
248            target_option_list = [(0, trans_text("Default"))]
249        for fmt in target_format_list:
250            index = target_format_list.index(fmt) + 1 # option default = 0
251            ldd.set_format(index - 1)
252            example = vlocale.get_date(today)
253            target_option_list += [(index, fmt + ' (%s)' % example)]
254        date_option.set_items(target_option_list)
255
256    today = Today()
257    date_option = EnumeratedListOption(_("Date format"), 0)
258    global_date_format = config.get('preferences.date-format')
259    on_trans_value_changed()
260    date_option.set_help(_("The format and language for dates, with examples"))
261    # if this report hasn't ever been run, start with user's current setting
262    date_option.set_value(global_date_format + 1)
263    # if the report has been run, this line will get the user's old setting
264    menu.add_option(category, 'date_format', date_option)
265    localization_option.connect('value-changed', on_trans_value_changed)
266
267def run_date_format_option(report, menu):
268    """
269    Run the option for changing the report's date format to a
270    report-specific format instead of the user's Edit=>Preferences choice
271    """
272    def warning(value):
273        """
274        Convenience function for the error message.
275
276        The 'value' (starting at '0') is the index in the option choices list
277        (as opposed to the index (starting at '0') in the target format list,
278        which is shorter by one, so corresponding offsets are made).
279        """
280        target_format_choices = date_option.get_items() # with numbers
281        report._user.warn(
282            _("Ignoring unknown option: %s") % value,
283            _("Valid values: ") + str(target_format_choices)
284            + '\n\n' +
285            _("Using options string: %s") % str(target_format_list[0])
286            ) # the previous line's "0" is because ISO is always the fallback
287
288    target_format_list = get_date_formats(report._locale)
289    date_option = menu.get_option_by_name('date_format')
290    date_opt_value = date_option.get_value()
291    if date_opt_value == 0: # "default"
292        format_to_be = config.get('preferences.date-format') # the UI choice
293    elif date_opt_value <= len(target_format_list):
294        format_to_be = date_opt_value - 1
295    else:
296        warning(date_opt_value)
297        format_to_be = 0 # ISO always exists
298    if format_to_be + 1 > len(target_format_list):
299        warning(format_to_be + 1)
300        format_to_be = 0 # ISO always exists
301    report._ldd.set_format(format_to_be)
302
303def add_gramps_id_option(menu, category, ownline=False):
304    """
305    Insert an option for deciding whether to include Gramps IDs
306    in the report
307
308    Since for some reports it makes sense to possibly have the ID on its
309    own line (e.g. Graphviz reports), that possibility is included, but
310    since for most reports it won't make sense the default is False
311
312    :param menu: The menu the options should be added to.
313    :type menu: :class:`.Menu`
314    :param category: A label that describes the category that the option
315        belongs to, e.g. "Report Options"
316    :type category: string
317    :param ownline: whether the option offers to have the ID on its own line
318    :type ownline: Boolean
319    """
320
321    include_id = EnumeratedListOption(_('Gramps ID'), 0)
322    include_id.add_item(0, _('Do not include'))
323    if ownline:
324        include_id.add_item(1, _('Share an existing line'))
325        include_id.add_item(2, _('On a line of its own'))
326        include_id.set_help(_('Whether (and where) to include Gramps IDs'))
327    else:
328        include_id.add_item(1, _('Include'))
329        include_id.set_help(_("Whether to include Gramps IDs"))
330    menu.add_option(category, 'inc_id', include_id)
331
332def add_place_format_option(menu, category):
333    """
334    Insert an option for changing the report's place format to a
335    report-specific format instead of the user's Edit=>Preferences choice
336    """
337    place_format = EnumeratedListOption(_("Place format"), -1)
338    place_format.add_item(-1, _("Default"))
339    for number, fmt in enumerate(_pd.get_formats()):
340        place_format.add_item(number, fmt.name)
341    place_format.set_help(_("Select the format to display places"))
342    menu.add_option(category, "place_format", place_format)
343    return place_format
344