Ansel 0.0
A darktable fork - bloat + design vision
Loading...
Searching...
No Matches
help.c
Go to the documentation of this file.
1/*
2 This file is part of the Ansel project.
3 Copyright (C) 2023, 2025-2026 Aurélien PIERRE.
4 Copyright (C) 2023 Luca Zulberti.
5
6 Ansel is free software: you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
10
11 Ansel 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 Ansel. If not, see <http://www.gnu.org/licenses/>.
18*/
19#include "config.h" // DT_BUILD_CHANNEL, darktable_last_commit_year
20#include "common/updates.h"
21#include <string.h>
22#include "system/mem_alloc.h"
23#include "gui/application.h"
25#include "gui/actions/menu.h"
26#ifdef __APPLE__
27#include "osx/osx.h" // dt_osx_disallow_fullscreen(), used under GDK_WINDOWING_QUARTZ below
28#endif
30#include "control/control.h"
31
32
33static gboolean show_about_dialog(GtkAccelGroup *group, GObject *acceleratable, guint keyval, GdkModifierType mods, gpointer user_data)
34{
35 GtkWidget *dialog = gtk_about_dialog_new();
36 gtk_widget_set_name (dialog, "about-dialog");
37#ifdef GDK_WINDOWING_QUARTZ
39#endif
40 gtk_about_dialog_set_program_name(GTK_ABOUT_DIALOG(dialog), PACKAGE_NAME);
41 gtk_about_dialog_set_version(GTK_ABOUT_DIALOG(dialog), darktable_package_version);
42 char *copyright = g_strdup_printf(_("Copyright \302\251 darktable authors 2009-2022\nCopyright \302\251 Aur\303\251lien Pierre 2022-%s"), darktable_last_commit_year);
43 gtk_about_dialog_set_copyright(GTK_ABOUT_DIALOG(dialog), copyright);
44 dt_free(copyright);
45 gtk_about_dialog_set_comments(GTK_ABOUT_DIALOG(dialog),
46 _("Organize and develop images from digital cameras"));
47 gtk_about_dialog_set_website(GTK_ABOUT_DIALOG(dialog), "https://ansel.photos");
48 gtk_about_dialog_set_website_label(GTK_ABOUT_DIALOG(dialog), _("Website"));
49 char *icon = g_strdup("ansel");
50 gtk_about_dialog_set_logo_icon_name(GTK_ABOUT_DIALOG(dialog), icon);
51 dt_free(icon);
52
53 const char *str = _("all those of you that made previous releases possible");
54
55#include "tools/darktable_authors.h"
56
57 const char *final[] = {str, NULL };
58 gtk_about_dialog_add_credit_section (GTK_ABOUT_DIALOG(dialog), _("and..."), final);
59 gtk_about_dialog_set_translator_credits(GTK_ABOUT_DIALOG(dialog), _("translator-credits"));
60
61 gtk_window_set_transient_for(GTK_WINDOW(dialog), GTK_WINDOW(dt_gui_main_window()));
62 gtk_dialog_run(GTK_DIALOG(dialog));
63 gtk_widget_destroy(dialog);
64
65 return TRUE;
66}
67
68// Nightly builds only: opens the download for this installation's format when the
69// daily check found a newer build, and the download page otherwise -- so the entry
70// is useful even before the check has run, or when it is switched off.
71static gboolean update_nightly_callback(GtkAccelGroup *group, GObject *acceleratable, guint keyval, GdkModifierType mods, gpointer user_data)
72{
73 const char *url = dt_updates_get_download_url();
74 if(!url) url = "https://ansel.photos/en/download/";
75 gtk_show_uri_on_window(GTK_WINDOW(dt_gui_main_window()), url, GDK_CURRENT_TIME, NULL);
76 return TRUE;
77}
78
79static gboolean open_doc_callback(GtkAccelGroup *group, GObject *acceleratable, guint keyval, GdkModifierType mods, gpointer user_data)
80{
81 // TODO: use translated URL when doc gets translated
82 gtk_show_uri_on_window(GTK_WINDOW(dt_gui_main_window()), "https://ansel.photos/en/doc", GDK_CURRENT_TIME, NULL);
83 return TRUE;
84}
85
86static gboolean open_chat_callback(GtkAccelGroup *group, GObject *acceleratable, guint keyval, GdkModifierType mods, gpointer user_data)
87{
88 gtk_show_uri_on_window(GTK_WINDOW(dt_gui_main_window()),
89 "https://app.element.io/#/room/#ansel:matrix.org", GDK_CURRENT_TIME, NULL);
90 return TRUE;
91}
92
93static gboolean open_search_callback(GtkAccelGroup *group, GObject *acceleratable, guint keyval, GdkModifierType mods, gpointer user_data)
94{
95 gtk_show_uri_on_window(GTK_WINDOW(dt_gui_main_window()),
96 "https://chantal.aurelienpierre.com", GDK_CURRENT_TIME, NULL);
97 return TRUE;
98}
99
100static gboolean open_forum_callback(GtkAccelGroup *group, GObject *acceleratable, guint keyval, GdkModifierType mods, gpointer user_data)
101{
102 gtk_show_uri_on_window(GTK_WINDOW(dt_gui_main_window()),
103 "https://community.ansel.photos", GDK_CURRENT_TIME, NULL);
104 return TRUE;
105}
106
107// TODO: this doesn't work for all widgets. the reason being that the GtkEventBox we put libs/iops into catches events.
108static char *get_help_url(GtkWidget *widget)
109{
110 while(widget)
111 {
112 // if the widget doesn't have a help url set go up the widget hierarchy to find a parent that has an url
113 gchar *help_url = g_object_get_data(G_OBJECT(widget), "dt-help-url");
114 if(help_url) return help_url;
115 widget = gtk_widget_get_parent(widget);
116 }
117
118 return NULL;
119}
120
122{
124 dt_control_change_cursor(GDK_LEFT_PTR);
125 gdk_event_handler_set((GdkEventFunc)gtk_main_do_event, NULL, NULL);
126}
127
128static void _main_do_event_help(GdkEvent *event, gpointer data)
129{
130 gboolean handled = FALSE;
131
132 switch(event->type)
133 {
134 case GDK_BUTTON_PRESS:
135 {
136 if(event->button.button == GDK_BUTTON_SECONDARY)
137 {
138 // On right-click : abort and reset
140 handled = TRUE;
141 break;
142 }
143
144 GtkWidget *event_widget = gtk_get_event_widget(event);
145 if(event_widget)
146 {
147 // Note : help url contains the fully-formed URL adapted to current language
148 gchar *help_url = get_help_url(event_widget);
149 if(help_url && *help_url)
150 {
152
154 dt_print(DT_DEBUG_CONTROL, "[context help] opening '%s'\n", help_url);
155
156 // ask the user if the website may be accessed
157 GtkWidget *dialog = gtk_message_dialog_new
158 (GTK_WINDOW(win), GTK_DIALOG_DESTROY_WITH_PARENT,
159 GTK_MESSAGE_QUESTION, GTK_BUTTONS_YES_NO,
160 _("do you want to access ansel.photos ?"));
161#ifdef GDK_WINDOWING_QUARTZ
163#endif
164
165 gtk_window_set_title(GTK_WINDOW(dialog), _("access the online usermanual?"));
166 const gint res = gtk_dialog_run(GTK_DIALOG(dialog));
167 const gboolean open = (res == GTK_RESPONSE_YES) || !(res == GTK_RESPONSE_NO);
168 gtk_widget_destroy(dialog);
169
170 if(open)
171 {
172 GError *error = NULL;
173 const gboolean uri_success = gtk_show_uri_on_window(GTK_WINDOW(win), help_url, gtk_get_current_event_time(), &error);
174
175 if(uri_success)
176 {
177 dt_control_log(_("help url opened in web browser"));
178 }
179 else
180 {
181 dt_control_log(_("error while opening help url in web browser"));
182 if (!IS_NULL_PTR(error)) // uri_success being FALSE should guarantee that
183 {
184 fprintf (stderr, "unable to read file: %s\n", error->message);
185 g_error_free(error);
186 }
187 }
188 }
189 }
190 else
191 {
192 dt_control_log(_("there is no help available for this element"));
193 }
194 }
195 handled = TRUE;
196 break;
197 }
198
199 case GDK_ENTER_NOTIFY:
200 case GDK_LEAVE_NOTIFY:
201 {
202 GtkWidget *event_widget = gtk_get_event_widget(event);
203 if(event_widget)
204 {
205 gchar *help_url = get_help_url(event_widget);
206 if(help_url)
207 {
208 // TODO: find a better way to tell the user that the hovered widget has a help link
209 dt_cursor_t cursor = event->type == GDK_ENTER_NOTIFY ? GDK_QUESTION_ARROW : GDK_X_CURSOR;
213 }
214 }
215 break;
216 }
217 default:
218 break;
219 }
220
221 if(!handled) gtk_main_do_event(event);
222}
223
224static gboolean contextual_help_callback(GtkAccelGroup *group, GObject *acceleratable, guint keyval, GdkModifierType mods, gpointer user_data)
225{
226 dt_control_change_cursor(GDK_X_CURSOR);
228 gdk_event_handler_set(_main_do_event_help, NULL, NULL);
229 return TRUE;
230}
231
232static gboolean search_accels_callback(GtkAccelGroup *group, GObject *acceleratable, guint keyval, GdkModifierType mods, gpointer user_data)
233{
236 return TRUE;
237}
238
239static gboolean supervisor_callback(GtkAccelGroup *group, GObject *acceleratable, guint keyval, GdkModifierType mods, gpointer user_data)
240{
242 return TRUE;
243}
244
245void append_help(GtkWidget **menus, GList **lists, const dt_menus_t index)
246{
247 add_sub_menu_entry(menus, lists, _("Online documentation"), index, NULL, open_doc_callback, NULL, NULL, NULL, 0, 0);
248 if(strcmp(DT_BUILD_CHANNEL, "nightly") == 0)
249 add_sub_menu_entry(menus, lists, _("Update to the latest nightly build"), index, NULL, update_nightly_callback, NULL, NULL, NULL, 0, 0);
250 add_sub_menu_entry(menus, lists, _("Ask a question"), index, NULL, open_search_callback, NULL, NULL, NULL, 0, 0);
251 add_sub_menu_entry(menus, lists, _("Join the support chat"), index, NULL, open_chat_callback, NULL, NULL, NULL, 0, 0);
252 add_sub_menu_entry(menus, lists, _("Join the support forum"), index, NULL, open_forum_callback, NULL, NULL, NULL, 0, 0);
253 add_menu_separator(menus[index]);
254 add_sub_menu_entry(menus, lists, _("Open contextual help"), index, NULL, contextual_help_callback, NULL, NULL, NULL, 0, 0);
255 add_sub_menu_entry(menus, lists, _("Search actions..."), index, NULL, search_accels_callback, NULL, NULL,
256 NULL, GDK_KEY_p, DT_PRIMARY_MASK);
257 add_menu_separator(menus[index]);
258 add_sub_menu_entry(menus, lists, _("Event supervisor..."), index, NULL, supervisor_callback, NULL, NULL, NULL, 0, 0);
259 add_menu_separator(menus[index]);
260 add_sub_menu_entry(menus, lists, _("About"), index, NULL, show_about_dialog, NULL, NULL, NULL, 0, 0);
261}
void dt_accels_search(dt_accels_t *accels, GtkWindow *main_window, GtkWidget *anchor)
Handle default and user-set shortcuts (accelerators)
#define DT_PRIMARY_MASK
GtkWidget * dt_gui_main_window(void)
struct dt_accels_t * dt_gui_get_accels(void)
static void error(char *msg)
Definition ashift_lsd.c:202
#define TRUE
Definition ashift_lsd.c:162
#define FALSE
Definition ashift_lsd.c:158
struct _GtkWidget GtkWidget
GtkWidget, opaque, spelled exactly as GTK spells it.
Definition colorspaces.h:98
#define PACKAGE_NAME
const char darktable_package_version[]
#define DT_BUILD_CHANNEL
const char darktable_last_commit_year[]
void dt_control_forbid_change_cursor()
Definition control.c:233
void dt_control_log(const char *msg,...)
Definition control.c:824
void dt_control_allow_change_cursor()
Definition control.c:238
#define dt_control_change_cursor(cursor)
Definition control.h:121
GdkCursorType dt_cursor_t
Definition control.h:71
const int res
Definition dtpthread.h:351
static gboolean show_about_dialog(GtkAccelGroup *group, GObject *acceleratable, guint keyval, GdkModifierType mods, gpointer user_data)
Definition help.c:33
static gboolean supervisor_callback(GtkAccelGroup *group, GObject *acceleratable, guint keyval, GdkModifierType mods, gpointer user_data)
Definition help.c:239
static void _restore_default_cursor()
Definition help.c:121
static gboolean update_nightly_callback(GtkAccelGroup *group, GObject *acceleratable, guint keyval, GdkModifierType mods, gpointer user_data)
Definition help.c:71
void append_help(GtkWidget **menus, GList **lists, const dt_menus_t index)
Definition help.c:245
static void _main_do_event_help(GdkEvent *event, gpointer data)
Definition help.c:128
static gboolean open_chat_callback(GtkAccelGroup *group, GObject *acceleratable, guint keyval, GdkModifierType mods, gpointer user_data)
Definition help.c:86
static gboolean search_accels_callback(GtkAccelGroup *group, GObject *acceleratable, guint keyval, GdkModifierType mods, gpointer user_data)
Definition help.c:232
static gboolean open_search_callback(GtkAccelGroup *group, GObject *acceleratable, guint keyval, GdkModifierType mods, gpointer user_data)
Definition help.c:93
static gboolean open_doc_callback(GtkAccelGroup *group, GObject *acceleratable, guint keyval, GdkModifierType mods, gpointer user_data)
Definition help.c:79
static char * get_help_url(GtkWidget *widget)
Definition help.c:108
static gboolean contextual_help_callback(GtkAccelGroup *group, GObject *acceleratable, guint keyval, GdkModifierType mods, gpointer user_data)
Definition help.c:224
static gboolean open_forum_callback(GtkAccelGroup *group, GObject *acceleratable, guint keyval, GdkModifierType mods, gpointer user_data)
Definition help.c:100
@ DT_DEBUG_CONTROL
Definition logging.h:52
void dt_print(dt_debug_thread_t thread, const char *msg,...) __attribute__((format(printf
Print to stdout when thread is enabled, prefixed with seconds since startup.
#define IS_NULL_PTR(p)
C is way too permissive with !=, == and if(var) checks, which can mean too many things depending on w...
Definition macros.h:96
#define dt_free(ptr)
g_free() ptr and set it to NULL, skipping both if it is already NULL.
Definition mem_alloc.h:171
void add_sub_menu_entry(GtkWidget **menus, GList **lists, const gchar *label, const dt_menus_t index, void *data, gboolean(*action_callback)(GtkAccelGroup *group, GObject *acceleratable, guint keyval, GdkModifierType mods, gpointer user_data), gboolean(*checked_callback)(GtkWidget *widget), gboolean(*active_callback)(GtkWidget *widget), gboolean(*sensitive_callback)(GtkWidget *widget), guint key_val, GdkModifierType mods)
Definition menu.c:565
void add_menu_separator(GtkWidget *menu)
Definition menu.c:621
dt_menus_t
Definition menu.h:42
void dt_osx_disallow_fullscreen(GtkWidget *widget)
Definition osx.mm:105
int main()
Definition prova.c:47
void dt_gui_supervisor_window_show(void)
const char * dt_updates_get_download_url(void)
Definition updates.c:63
The nightly update check.