1 /* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
3 * about.c: Shows the contributors to Gnumeric.
6 * Jody Goldberg <jody@gnome.org>
7 * Morten Welinder <terra@gnome.org>
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, see <https://www.gnu.org/licenses/>.
22 #include <gnumeric-config.h>
23 #include <glib/gi18n-lib.h>
29 #include <gnm-random.h>
31 #include <gnm-format.h>
32 #include <goffice/goffice.h>
35 #define ABOUT_KEY "about-dialog"
38 GNM_CORE
= 1 << 0, /* All round hacking */
39 GNM_FEATURE_HACKER
= 1 << 1, /* Implement specific feature */
40 GNM_ANALYTICS
= 1 << 2,
41 GNM_IMPORT_EXPORT
= 1 << 3,
42 GNM_SCRIPTING
= 1 << 4,
44 GNM_USABILITY
= 1 << 6,
45 GNM_DOCUMENTATION
= 1 << 7,
46 GNM_TRANSLATION
= 1 << 8,
49 GNM_PACKAGING
= 1 << 11
51 #define GNM_ABOUT_NUM_TYPES 12
52 static char const * const about_types
[GNM_ABOUT_NUM_TYPES
] = {
69 unsigned contributions
;
70 char const *details
; /* optionally NULL */
71 } const contributors
[] = {
72 { N_("Harald Ashburner"), GNM_ANALYTICS
,
73 N_("Options pricers") },
74 { N_("Sean Atkinson"), GNM_ANALYTICS
| GNM_IMPORT_EXPORT
,
75 N_("Functions and X-Base importing.") },
76 { N_("Michel Berkelaar"), GNM_ANALYTICS
,
77 N_("Simplex algorithm for Solver (LP Solve).") },
78 { N_("Jean Br\303\251fort"), GNM_CORE
| GNM_FEATURE_HACKER
,
79 N_("Core charting engine.") },
80 { N_("Grandma Chema Celorio"), GNM_FEATURE_HACKER
|GNM_USABILITY
|GNM_QA
,
81 N_("Quality Assurance and sheet copy.") },
82 { N_("Frank Chiulli"), GNM_IMPORT_EXPORT
,
83 N_("OLE2 support.") },
84 { N_("Kenneth Christiansen"), GNM_TRANSLATION
,
85 N_("Localization.") },
86 { N_("Zbigniew Chyla"), GNM_CORE
,
87 N_("Plugin system, localization.") },
88 { N_("J.H.M. Dassen (Ray)"), GNM_PACKAGING
,
89 N_("Debian packaging.") },
90 { N_("Jeroen Dirks"), GNM_ANALYTICS
,
91 N_("Simplex algorithm for Solver (LP Solve).") },
92 { N_("Tom Dyas"), GNM_FEATURE_HACKER
,
93 N_("Original plugin engine.") },
94 { N_("Kjell Eikland"), GNM_ANALYTICS
,
96 { N_("Gergo Erdi"), GNM_GUI
,
97 N_("Custom UI tools") },
98 { N_("Jody Goldberg"), GNM_CORE
, NULL
},
99 { N_("John Gotts"), GNM_PACKAGING
,
100 N_("RPM packaging") },
101 { N_("Andreas J. G\xc3\xbclzow"), GNM_CORE
|GNM_FEATURE_HACKER
|GNM_ANALYTICS
|GNM_IMPORT_EXPORT
|GNM_GUI
|GNM_USABILITY
|GNM_DOCUMENTATION
|GNM_TRANSLATION
|GNM_QA
,
102 N_("Statistics and GUI master") },
103 { N_("Jon K\xc3\xa5re Hellan"), GNM_CORE
|GNM_FEATURE_HACKER
|GNM_ANALYTICS
|GNM_IMPORT_EXPORT
|GNM_SCRIPTING
|GNM_GUI
|GNM_USABILITY
|GNM_DOCUMENTATION
|GNM_TRANSLATION
|GNM_QA
,
104 N_("UI polish and all round bug fixer") },
105 { N_("Miguel de Icaza"), GNM_CORE
, NULL
},
106 { N_("Ross Ihaka"), GNM_ANALYTICS
,
107 N_("Special functions") },
108 { N_("Jukka-Pekka Iivonen"), GNM_ANALYTICS
|GNM_GUI
|GNM_FEATURE_HACKER
,
109 N_("Solver, lots of worksheet functions, and general trailblazer") },
110 { N_("Jakub Jel\xc3\xadnek"), GNM_CORE
,
111 N_("One of the original core contributors") },
112 { N_("Chris Lahey"), GNM_FEATURE_HACKER
,
113 N_("The original value format engine and libgoffice work") },
114 { N_("Takashi Matsuda"), GNM_FEATURE_HACKER
,
115 N_("The original text plugin") },
116 { N_("Michael Meeks"), GNM_CORE
|GNM_IMPORT_EXPORT
,
117 N_("Started the MS Excel import/export engine, and 'GnmStyle'") },
118 { N_("Lutz Muller"), GNM_FEATURE_HACKER
,
119 N_("SheetObject improvement") },
120 { N_("Yukihiro Nakai"), GNM_FEATURE_HACKER
| GNM_TRANSLATION
| GNM_QA
,
121 N_("Support for non-Latin languages") },
122 { N_("Peter Notebaert"), GNM_ANALYTICS
,
124 { N_("Emmanuel Pacaud"), GNM_CORE
| GNM_FEATURE_HACKER
,
125 N_("Many plot types for charting engine.") },
126 { N_("Federico M. Quintero"), GNM_CORE
,
127 N_("canvas support") },
128 { N_("Mark Probst"), GNM_SCRIPTING
,
129 N_("Guile support") },
130 { N_("Rasca"), GNM_IMPORT_EXPORT
,
131 N_("HTML, troff, LaTeX exporters") },
132 { N_("Vincent Renardias"), GNM_IMPORT_EXPORT
|GNM_TRANSLATION
,
133 N_("original CSV support, French localization") },
134 { N_("Ariel Rios"), GNM_SCRIPTING
,
135 N_("Guile support") },
136 { N_("Jakub Steiner"), GNM_ART
,
137 N_("Icons and Images") },
138 { N_("Uwe Steinmann"), GNM_FEATURE_HACKER
|GNM_IMPORT_EXPORT
,
139 N_("Paradox Importer") },
140 { N_("Arturo Tena"), GNM_IMPORT_EXPORT
,
141 N_("Initial work on OLE2 for libgsf") },
142 { N_("Almer S. Tigelaar"), GNM_FEATURE_HACKER
|GNM_IMPORT_EXPORT
,
143 N_("Consolidation and Structured Text importer") },
144 { N_("Bruno Unna"), GNM_IMPORT_EXPORT
,
145 N_("Pieces of MS Excel import") },
146 { N_("Arief Mulya Utama"), GNM_ANALYTICS
,
147 N_("Telecommunications functions") },
148 { N_("Daniel Veillard"), GNM_IMPORT_EXPORT
,
149 N_("Initial XML support") },
150 { N_("Vladimir Vuksan"), GNM_ANALYTICS
,
151 N_("Some financial functions") },
152 { N_("Morten Welinder"), GNM_CORE
|GNM_FEATURE_HACKER
|GNM_ANALYTICS
|GNM_IMPORT_EXPORT
|GNM_SCRIPTING
|GNM_GUI
|GNM_USABILITY
|GNM_TRANSLATION
|GNM_QA
,
153 N_("All round powerhouse") },
154 { N_("Kevin Breit"), GNM_DOCUMENTATION
, NULL
},
155 { N_("Thomas Canty"), GNM_DOCUMENTATION
, NULL
},
156 { N_("Adrian Custer"), GNM_DOCUMENTATION
, NULL
},
157 { N_("Adrian Likins"), GNM_DOCUMENTATION
, NULL
},
158 { N_("Aaron Weber"), GNM_DOCUMENTATION
, NULL
},
159 { N_("Alexander Kirillov"), GNM_DOCUMENTATION
, NULL
},
162 typedef struct AboutRenderer_ AboutRenderer
;
163 typedef struct AboutState_ AboutState
;
165 struct AboutRenderer_
{
166 int start_time
, duration
;
168 gboolean (*renderer
) (AboutRenderer
*, AboutState
*);
173 gboolean fade_in
, fade_out
;
190 GtkWidget
*anim_area
;
191 GList
*active
, *waiting
;
196 /* ---------------------------------------- */
199 free_renderer (AboutRenderer
*r
)
202 g_object_unref (r
->layout
);
207 text_item_renderer (AboutRenderer
*r
, AboutState
*state
)
209 PangoLayout
*layout
= r
->layout
;
210 int age
= state
->now
- r
->start_time
;
211 double rage
= CLAMP (age
/ (double)r
->duration
, 0.0, 1.0);
212 GtkWidget
*widget
= state
->anim_area
;
213 GtkStyleContext
*ctxt
;
214 const int fade
= 500;
215 int x
, y
, width
, height
;
221 if (age
>= r
->duration
)
224 if (r
->fade_in
&& age
< fade
)
225 alpha
= age
/ (double)fade
;
226 else if (r
->fade_out
&& r
->duration
- age
< fade
)
227 alpha
= (r
->duration
- age
) / (double)fade
;
229 ctxt
= gtk_widget_get_style_context (widget
);
231 gtk_widget_get_allocation (widget
, &wa
);
232 x
= (int)(PANGO_SCALE
* wa
.width
*
233 (r
->start
.x
+ rage
* (r
->end
.x
- r
->start
.x
)));
234 y
= (int)(PANGO_SCALE
* wa
.height
*
235 (r
->start
.y
+ rage
* (r
->end
.y
- r
->start
.y
)));
237 if (r
->expansion
.count
) {
238 PangoAttrList
*attrlist
= pango_layout_get_attributes (layout
);
239 const char *p
, *text
= pango_layout_get_text (layout
);
240 PangoRectangle ink
, logical
;
242 memset (&ink
, 0, sizeof (ink
));
245 logical
.width
= (int)(rage
* r
->expansion
.rate
* r
->natural_width
/ r
->expansion
.count
);
249 const char *next
= g_utf8_next_char (p
);
250 gunichar uc
= g_utf8_get_char (p
);
251 PangoAttribute
*attr
;
253 if (uc
== UNICODE_ZERO_WIDTH_SPACE_C
) {
254 attr
= pango_attr_shape_new (&ink
, &logical
);
255 attr
->start_index
= p
- text
;
256 attr
->end_index
= next
- text
;
257 pango_attr_list_change (attrlist
, attr
);
261 pango_layout_set_attributes (layout
, attrlist
);
264 pango_layout_get_size (layout
, &width
, &height
);
269 gnm_style_context_get_color (ctxt
, GTK_STATE_FLAG_NORMAL
, &color
);
271 gdk_cairo_set_source_rgba (cr
, &color
);
272 cairo_move_to (cr
, x
/ (double)PANGO_SCALE
, y
/ (double)PANGO_SCALE
);
273 pango_cairo_show_layout (cr
, layout
);
279 set_text_motion (AboutRenderer
*r
, double sx
, double sy
, double ex
, double ey
)
289 set_text_expansion (AboutRenderer
*r
, double er
)
291 const char *text
= pango_layout_get_text (r
->layout
);
292 GString
*str
= g_string_new (NULL
);
296 r
->expansion
.rate
= er
;
297 r
->expansion
.count
= 0;
299 /* Normalize to make sure diacriticals are combined. */
300 ntext
= g_utf8_normalize (text
, -1, G_NORMALIZE_DEFAULT_COMPOSE
);
302 /* Insert inter-letter spaces we can stretch. */
303 for (p
= ntext
; *p
; p
= g_utf8_next_char (p
)) {
304 gunichar uc
= g_utf8_get_char (p
);
306 if (uc
== UNICODE_ZERO_WIDTH_SPACE_C
)
310 g_string_append_unichar (str
, UNICODE_ZERO_WIDTH_SPACE_C
);
311 r
->expansion
.count
++;
313 g_string_append_unichar (str
, uc
);
317 pango_layout_set_text (r
->layout
, str
->str
, -1);
318 g_string_free (str
, TRUE
);
321 static AboutRenderer
*
322 make_text_item (AboutState
*state
, const char *text
, int duration
)
324 AboutRenderer
*r
= g_new0 (AboutRenderer
, 1);
325 PangoAttrList
*attrlist
;
326 PangoAttribute
*attr
;
328 r
->start_time
= state
->now
;
329 r
->duration
= duration
;
330 r
->layout
= gtk_widget_create_pango_layout (state
->anim_area
, NULL
);
331 r
->renderer
= text_item_renderer
;
332 r
->fade_in
= r
->fade_out
= TRUE
;
333 set_text_motion (r
, 0.5, 0.5, 0.5, 0.5);
335 pango_layout_set_text (r
->layout
, text
, -1);
336 pango_layout_get_size (r
->layout
, &r
->natural_width
, NULL
);
338 attrlist
= pango_attr_list_new ();
339 attr
= pango_attr_weight_new (PANGO_WEIGHT_BOLD
);
340 pango_attr_list_change (attrlist
, attr
);
341 pango_layout_set_attributes (r
->layout
, attrlist
);
342 pango_attr_list_unref (attrlist
);
344 state
->now
+= duration
;
349 /* ---------------------------------------- */
352 #define TIME_SLICE 20 /* ms */
356 free_state (AboutState
*state
)
359 g_source_remove (state
->timer
);
363 g_list_free_full (state
->active
, (GDestroyNotify
)free_renderer
);
364 state
->active
= NULL
;
366 g_list_free_full (state
->waiting
, (GDestroyNotify
)free_renderer
);
367 state
->waiting
= NULL
;
373 about_dialog_timer (gpointer state_
)
375 AboutState
*state
= state_
;
377 while (state
->waiting
) {
378 AboutRenderer
*r
= state
->waiting
->data
;
379 if (r
->start_time
> state
->now
)
381 state
->active
= g_list_append (state
->active
, r
);
382 state
->waiting
= g_list_remove (state
->waiting
, r
);
386 gtk_widget_queue_draw (state
->anim_area
);
388 state
->now
+= TIME_SLICE
;
394 about_dialog_anim_draw (G_GNUC_UNUSED GtkWidget
*widget
,
402 GList
*next
= l
->next
;
403 AboutRenderer
*r
= l
->data
;
406 keep
= r
->renderer (r
, state
);
409 state
->active
= g_list_remove_link (state
->active
, l
);
417 #define APPENDR(r_) do { \
418 tail->next = g_list_prepend (NULL, (r_)); \
419 tail->next->prev = tail; \
424 create_animation (AboutState
*state
)
429 unsigned N
= G_N_ELEMENTS (contributors
);
430 unsigned *permutation
;
434 r
= make_text_item (state
, _("Gnumeric is the result of"), 3000);
435 set_text_motion (r
, 0.5, 0.9, 0.5, 0.1);
436 tail
= state
->waiting
= g_list_prepend (NULL
, r
);
438 state
->now
-= 2000; /* Overlap. */
440 r
= make_text_item (state
, _("the efforts of many people."), 3000);
441 set_text_motion (r
, 0.5, 0.9, 0.5, 0.1);
444 state
->now
-= 2000; /* Overlap. */
446 r
= make_text_item (state
, _("Your help is much appreciated!"), 3000);
447 set_text_motion (r
, 0.5, 0.9, 0.5, 0.1);
450 permutation
= g_new (unsigned, N
);
451 for (ui
= 0; ui
< N
; ui
++)
452 permutation
[ui
] = ui
;
454 for (ui
= 0; ui
< N
; ui
++) {
455 unsigned pui
= (int)(random_01 () * N
);
456 unsigned A
= permutation
[ui
];
457 permutation
[ui
] = permutation
[pui
];
458 permutation
[pui
] = A
;
461 for (ui
= 0; ui
< N
; ui
++) {
462 unsigned pui
= permutation
[ui
];
463 const char *name
= contributors
[pui
].name
;
467 state
->now
-= 1900; /* Overlap. */
469 r
= make_text_item (state
, name
, 3000);
472 set_text_motion (r
, 0.5, 0.1, 0.1, 0.9);
473 if (0) set_text_expansion (r
, 1);
476 set_text_motion (r
, 0.5, 0.1, 0.9, 0.9);
477 if (0) set_text_expansion (r
, 1);
481 set_text_motion (r
, 0.5, 0.1, 0.5, 0.9);
482 set_text_expansion (r
, 3);
490 g_free (permutation
);
494 r
= make_text_item (state
, _("We apologize if anyone was left out."),
496 set_text_motion (r
, 0.5, 0.9, 0.5, 0.1);
499 state
->now
-= 2000; /* Overlap. */
501 r
= make_text_item (state
, _("Please contact us to correct mistakes."),
503 set_text_motion (r
, 0.5, 0.9, 0.5, 0.1);
506 state
->now
-= 2000; /* Overlap. */
508 r
= make_text_item (state
, _("Report problems at http://bugzilla.gnome.org"), 3000);
509 set_text_motion (r
, 0.5, 0.9, 0.5, 0.1);
512 r
= make_text_item (state
, _("We aim to please!"), 3000);
516 state
->now
-= 100; /* Overlap. */
518 r
= make_text_item (state
, _("We aim to please!"), 1000);
520 set_text_expansion (r
, 4);
529 dialog_about (WBCGtk
*wbcg
)
535 if (gnm_dialog_raise_if_exists (wbcg
, ABOUT_KEY
))
538 state
= g_new0 (AboutState
, 1);
540 w
= g_object_new (GTK_TYPE_ABOUT_DIALOG
,
541 "title", _("About Gnumeric"),
542 "version", GNM_VERSION_FULL
,
543 "website", PACKAGE_URL
,
544 "website-label", _("Visit the Gnumeric website"),
545 "logo-icon-name", "gnumeric",
546 "copyright", _("Copyright \xc2\xa9 1998-2018"),
547 "comments", _("Free, Fast, Accurate - Pick Any Three!"),
551 g_signal_connect (w
, "response",
552 G_CALLBACK (gtk_widget_destroy
), NULL
);
553 g_signal_connect_swapped (w
, "destroy",
554 G_CALLBACK (free_state
), state
);
556 c
= gtk_dialog_get_content_area (GTK_DIALOG (w
));
557 children
= gtk_container_get_children (GTK_CONTAINER (c
));
559 if (children
&& GTK_IS_BOX (children
->data
)) {
560 GtkWidget
*vbox
= children
->data
;
564 state
->anim_area
= gtk_drawing_area_new ();
565 layout
= gtk_widget_create_pango_layout (state
->anim_area
, "x");
566 pango_layout_get_pixel_size (layout
, NULL
, &height
);
567 gtk_widget_set_size_request (state
->anim_area
, -1, 8 * height
);
568 g_object_unref (layout
);
570 g_signal_connect (state
->anim_area
, "draw",
571 G_CALLBACK (about_dialog_anim_draw
),
574 gtk_box_pack_end (GTK_BOX (vbox
), state
->anim_area
, TRUE
, TRUE
, 0);
576 create_animation (state
);
578 state
->timer
= g_timeout_add (TIME_SLICE
, about_dialog_timer
, state
);
580 g_list_free (children
);
582 gnm_keyed_dialog (wbcg
, GTK_WINDOW (w
), ABOUT_KEY
);
583 gtk_widget_show_all (w
);