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 "common/darktable.h"
20#include "gui/actions/menu.h"
21#include "common/l10n.h"
22#include "control/control.h"
23
24
25static gboolean show_about_dialog(GtkAccelGroup *group, GObject *acceleratable, guint keyval, GdkModifierType mods, gpointer user_data)
26{
27 GtkWidget *dialog = gtk_about_dialog_new();
28 gtk_widget_set_name (dialog, "about-dialog");
29#ifdef GDK_WINDOWING_QUARTZ
31#endif
32 gtk_about_dialog_set_program_name(GTK_ABOUT_DIALOG(dialog), PACKAGE_NAME);
33 gtk_about_dialog_set_version(GTK_ABOUT_DIALOG(dialog), darktable_package_version);
34 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);
35 gtk_about_dialog_set_copyright(GTK_ABOUT_DIALOG(dialog), copyright);
36 dt_free(copyright);
37 gtk_about_dialog_set_comments(GTK_ABOUT_DIALOG(dialog),
38 _("Organize and develop images from digital cameras"));
39 gtk_about_dialog_set_website(GTK_ABOUT_DIALOG(dialog), "https://ansel.photos");
40 gtk_about_dialog_set_website_label(GTK_ABOUT_DIALOG(dialog), _("Website"));
41 char *icon = g_strdup("ansel");
42 gtk_about_dialog_set_logo_icon_name(GTK_ABOUT_DIALOG(dialog), icon);
43 dt_free(icon);
44
45 const char *str = _("all those of you that made previous releases possible");
46
47#include "tools/darktable_authors.h"
48
49 const char *final[] = {str, NULL };
50 gtk_about_dialog_add_credit_section (GTK_ABOUT_DIALOG(dialog), _("and..."), final);
51 gtk_about_dialog_set_translator_credits(GTK_ABOUT_DIALOG(dialog), _("translator-credits"));
52
53 gtk_window_set_transient_for(GTK_WINDOW(dialog), GTK_WINDOW(dt_ui_main_window(darktable.gui->ui)));
54 gtk_dialog_run(GTK_DIALOG(dialog));
55 gtk_widget_destroy(dialog);
56
57 return TRUE;
58}
59
60static gboolean open_doc_callback(GtkAccelGroup *group, GObject *acceleratable, guint keyval, GdkModifierType mods, gpointer user_data)
61{
62 // TODO: use translated URL when doc gets translated
63 gtk_show_uri_on_window(GTK_WINDOW(dt_ui_main_window(darktable.gui->ui)), "https://ansel.photos/en/doc", GDK_CURRENT_TIME, NULL);
64 return TRUE;
65}
66
67static gboolean open_chat_callback(GtkAccelGroup *group, GObject *acceleratable, guint keyval, GdkModifierType mods, gpointer user_data)
68{
69 gtk_show_uri_on_window(GTK_WINDOW(dt_ui_main_window(darktable.gui->ui)),
70 "https://app.element.io/#/room/#ansel:matrix.org", GDK_CURRENT_TIME, NULL);
71 return TRUE;
72}
73
74static gboolean open_search_callback(GtkAccelGroup *group, GObject *acceleratable, guint keyval, GdkModifierType mods, gpointer user_data)
75{
76 gtk_show_uri_on_window(GTK_WINDOW(dt_ui_main_window(darktable.gui->ui)),
77 "https://chantal.aurelienpierre.com", GDK_CURRENT_TIME, NULL);
78 return TRUE;
79}
80
81static gboolean open_forum_callback(GtkAccelGroup *group, GObject *acceleratable, guint keyval, GdkModifierType mods, gpointer user_data)
82{
83 gtk_show_uri_on_window(GTK_WINDOW(dt_ui_main_window(darktable.gui->ui)),
84 "https://community.ansel.photos", GDK_CURRENT_TIME, NULL);
85 return TRUE;
86}
87
88// TODO: this doesn't work for all widgets. the reason being that the GtkEventBox we put libs/iops into catches events.
89static char *get_help_url(GtkWidget *widget)
90{
91 while(widget)
92 {
93 // if the widget doesn't have a help url set go up the widget hierarchy to find a parent that has an url
94 gchar *help_url = g_object_get_data(G_OBJECT(widget), "dt-help-url");
95 if(help_url) return help_url;
96 widget = gtk_widget_get_parent(widget);
97 }
98
99 return NULL;
100}
101
103{
105 dt_control_change_cursor(GDK_LEFT_PTR);
106 gdk_event_handler_set((GdkEventFunc)gtk_main_do_event, NULL, NULL);
107}
108
109static void _main_do_event_help(GdkEvent *event, gpointer data)
110{
111 gboolean handled = FALSE;
112
113 switch(event->type)
114 {
115 case GDK_BUTTON_PRESS:
116 {
117 if(event->button.button == GDK_BUTTON_SECONDARY)
118 {
119 // On right-click : abort and reset
121 handled = TRUE;
122 break;
123 }
124
125 GtkWidget *event_widget = gtk_get_event_widget(event);
126 if(event_widget)
127 {
128 // Note : help url contains the fully-formed URL adapted to current language
129 gchar *help_url = get_help_url(event_widget);
130 if(help_url && *help_url)
131 {
133
135 dt_print(DT_DEBUG_CONTROL, "[context help] opening '%s'\n", help_url);
136
137 // ask the user if the website may be accessed
138 GtkWidget *dialog = gtk_message_dialog_new
139 (GTK_WINDOW(win), GTK_DIALOG_DESTROY_WITH_PARENT,
140 GTK_MESSAGE_QUESTION, GTK_BUTTONS_YES_NO,
141 _("do you want to access ansel.photos ?"));
142#ifdef GDK_WINDOWING_QUARTZ
144#endif
145
146 gtk_window_set_title(GTK_WINDOW(dialog), _("access the online usermanual?"));
147 const gint res = gtk_dialog_run(GTK_DIALOG(dialog));
148 const gboolean open = (res == GTK_RESPONSE_YES) || !(res == GTK_RESPONSE_NO);
149 gtk_widget_destroy(dialog);
150
151 if(open)
152 {
153 GError *error = NULL;
154 const gboolean uri_success = gtk_show_uri_on_window(GTK_WINDOW(win), help_url, gtk_get_current_event_time(), &error);
155
156 if(uri_success)
157 {
158 dt_control_log(_("help url opened in web browser"));
159 }
160 else
161 {
162 dt_control_log(_("error while opening help url in web browser"));
163 if (!IS_NULL_PTR(error)) // uri_success being FALSE should guarantee that
164 {
165 fprintf (stderr, "unable to read file: %s\n", error->message);
166 g_error_free(error);
167 }
168 }
169 }
170 }
171 else
172 {
173 dt_control_log(_("there is no help available for this element"));
174 }
175 }
176 handled = TRUE;
177 break;
178 }
179
180 case GDK_ENTER_NOTIFY:
181 case GDK_LEAVE_NOTIFY:
182 {
183 GtkWidget *event_widget = gtk_get_event_widget(event);
184 if(event_widget)
185 {
186 gchar *help_url = get_help_url(event_widget);
187 if(help_url)
188 {
189 // TODO: find a better way to tell the user that the hovered widget has a help link
190 dt_cursor_t cursor = event->type == GDK_ENTER_NOTIFY ? GDK_QUESTION_ARROW : GDK_X_CURSOR;
194 }
195 }
196 break;
197 }
198 default:
199 break;
200 }
201
202 if(!handled) gtk_main_do_event(event);
203}
204
205static gboolean contextual_help_callback(GtkAccelGroup *group, GObject *acceleratable, guint keyval, GdkModifierType mods, gpointer user_data)
206{
207 dt_control_change_cursor(GDK_X_CURSOR);
209 gdk_event_handler_set(_main_do_event_help, NULL, NULL);
210 return TRUE;
211}
212
213static gboolean search_accels_callback(GtkAccelGroup *group, GObject *acceleratable, guint keyval, GdkModifierType mods, gpointer user_data)
214{
217 return TRUE;
218}
219
220void append_help(GtkWidget **menus, GList **lists, const dt_menus_t index)
221{
222 add_sub_menu_entry(menus, lists, _("Online documentation"), index, NULL, open_doc_callback, NULL, NULL, NULL, 0, 0);
223 add_sub_menu_entry(menus, lists, _("Ask a question"), index, NULL, open_search_callback, NULL, NULL, NULL, 0, 0);
224 add_sub_menu_entry(menus, lists, _("Join the support chat"), index, NULL, open_chat_callback, NULL, NULL, NULL, 0, 0);
225 add_sub_menu_entry(menus, lists, _("Join the support forum"), index, NULL, open_forum_callback, NULL, NULL, NULL, 0, 0);
226 add_menu_separator(menus[index]);
227 add_sub_menu_entry(menus, lists, _("Open contextual help"), index, NULL, contextual_help_callback, NULL, NULL, NULL, 0, 0);
228 add_sub_menu_entry(menus, lists, _("Search actions..."), index, NULL, search_accels_callback, NULL, NULL,
229 NULL, GDK_KEY_p, GDK_CONTROL_MASK);
230 add_menu_separator(menus[index]);
231 add_sub_menu_entry(menus, lists, _("About"), index, NULL, show_about_dialog, NULL, NULL, NULL, 0, 0);
232}
void dt_accels_search(dt_accels_t *accels, GtkWindow *main_window, GtkWidget *anchor)
static void error(char *msg)
Definition ashift_lsd.c:202
#define TRUE
Definition ashift_lsd.c:162
#define FALSE
Definition ashift_lsd.c:158
#define PACKAGE_NAME
const char darktable_package_version[]
const char darktable_last_commit_year[]
void dt_control_forbid_change_cursor()
Definition control.c:224
void dt_control_log(const char *msg,...)
Definition control.c:761
void dt_control_allow_change_cursor()
Definition control.c:229
#define dt_control_change_cursor(cursor)
Definition control.h:116
GdkCursorType dt_cursor_t
Definition control.h:70
darktable_t darktable
Definition darktable.c:181
void dt_print(dt_debug_thread_t thread, const char *msg,...)
Definition darktable.c:1542
@ DT_DEBUG_CONTROL
Definition darktable.h:716
#define dt_free(ptr)
Definition darktable.h:456
#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 darktable.h:281
GtkWidget * dt_ui_main_window(dt_ui_t *ui)
get the main window widget
static gboolean show_about_dialog(GtkAccelGroup *group, GObject *acceleratable, guint keyval, GdkModifierType mods, gpointer user_data)
Definition help.c:25
static void _restore_default_cursor()
Definition help.c:102
void append_help(GtkWidget **menus, GList **lists, const dt_menus_t index)
Definition help.c:220
static void _main_do_event_help(GdkEvent *event, gpointer data)
Definition help.c:109
static gboolean open_chat_callback(GtkAccelGroup *group, GObject *acceleratable, guint keyval, GdkModifierType mods, gpointer user_data)
Definition help.c:67
static gboolean search_accels_callback(GtkAccelGroup *group, GObject *acceleratable, guint keyval, GdkModifierType mods, gpointer user_data)
Definition help.c:213
static gboolean open_search_callback(GtkAccelGroup *group, GObject *acceleratable, guint keyval, GdkModifierType mods, gpointer user_data)
Definition help.c:74
static gboolean open_doc_callback(GtkAccelGroup *group, GObject *acceleratable, guint keyval, GdkModifierType mods, gpointer user_data)
Definition help.c:60
static char * get_help_url(GtkWidget *widget)
Definition help.c:89
static gboolean contextual_help_callback(GtkAccelGroup *group, GObject *acceleratable, guint keyval, GdkModifierType mods, gpointer user_data)
Definition help.c:205
static gboolean open_forum_callback(GtkAccelGroup *group, GObject *acceleratable, guint keyval, GdkModifierType mods, gpointer user_data)
Definition help.c:81
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:542
void add_menu_separator(GtkWidget *menu)
Definition menu.c:598
dt_menus_t
Definition menu.h:42
void dt_osx_disallow_fullscreen(GtkWidget *widget)
Definition osx.mm:104
int main()
Definition prova.c:47
struct _GtkWidget GtkWidget
Definition splash.h:29
struct dt_gui_gtk_t * gui
Definition darktable.h:775
dt_accels_t * accels
Definition gtk.h:194
dt_ui_t * ui
Definition gtk.h:164