dialog.c 9.09 KB
Newer Older
1 2 3 4 5
/*****************************************************************************
 * dialog.c: User dialog functions
 *****************************************************************************
 * Copyright © 2009 Rémi Denis-Courmont
 *
Jean-Baptiste Kempf's avatar
LGPL  
Jean-Baptiste Kempf committed
6 7 8
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 2.1 of the License, or
9 10 11 12
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Jean-Baptiste Kempf's avatar
LGPL  
Jean-Baptiste Kempf committed
13 14
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Lesser General Public License for more details.
15
 *
Jean-Baptiste Kempf's avatar
LGPL  
Jean-Baptiste Kempf committed
16 17 18
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
 *****************************************************************************/

/**
 * \file dialog.c
 * User dialogs core
 */

#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

#include <stdarg.h>

#include <vlc_common.h>
#include <vlc_dialog.h>
34
#include <vlc_extensions.h>
35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95
#include <assert.h>
#include "libvlc.h"

static vlc_mutex_t provider_lock = VLC_STATIC_MUTEX;

#undef dialog_Register
/**
 * Registers an object as the dialog provider.
 * It is assumed that the appropriate variable callbacks are already
 * registered.
 */
int dialog_Register (vlc_object_t *obj)
{
    libvlc_priv_t *priv = libvlc_priv (obj->p_libvlc);
    int ret = VLC_EGENERIC;

    vlc_mutex_lock (&provider_lock);
    if (priv->p_dialog_provider == NULL)
    {   /* Since the object is responsible for unregistering itself before
         * it terminates, at reference is not needed. */
        priv->p_dialog_provider = obj;
        ret = VLC_SUCCESS;
    }
    vlc_mutex_unlock (&provider_lock);
    return ret;
}

#undef dialog_Unregister
/**
 * Unregisters the dialog provider.
 * Note that unless you have unregistered the callbacks already, the provider
 * might still be in use by other threads. Also, you need to cancel all
 * pending dialogs yourself.
 */
int dialog_Unregister (vlc_object_t *obj)
{
    libvlc_priv_t *priv = libvlc_priv (obj->p_libvlc);
    int ret = VLC_EGENERIC;

    vlc_mutex_lock (&provider_lock);
    if (priv->p_dialog_provider == obj)
    {
        priv->p_dialog_provider = NULL;
        ret = VLC_SUCCESS;
    }
    vlc_mutex_unlock (&provider_lock);
    return ret;
}

static vlc_object_t *dialog_GetProvider (vlc_object_t *obj)
{
    libvlc_priv_t *priv = libvlc_priv (obj->p_libvlc);
    vlc_object_t *provider;

    vlc_mutex_lock (&provider_lock);
    if ((provider = priv->p_dialog_provider) != NULL)
        vlc_object_hold (provider);
    vlc_mutex_unlock (&provider_lock);
    return provider;
}

96 97 98 99 100 101 102 103 104
/**
 * Sends an error message through the user interface (if any).
 * @param obj the VLC object emitting the error
 * @param modal whether to wait for user to acknowledge the error
 *              before returning control to the caller
 * @param title title of the error dialog
 * @param fmt format string for the error message
 * @param ap parameters list for the formatted error message
 */
105 106
void dialog_VFatal (vlc_object_t *obj, bool modal, const char *title,
                    const char *fmt, va_list ap)
107 108 109
{
    char *text;

110 111 112
    if (obj->i_flags & OBJECT_FLAGS_NOINTERACT)
        return;

113 114 115 116
    vlc_object_t *provider = dialog_GetProvider (obj);
    if (provider == NULL)
    {
        msg_Err (obj, "%s", title);
117
        msg_GenericVa (obj, VLC_MSG_ERR, fmt, ap);
118 119 120
        return;
    }

121 122
    if (vasprintf (&text, fmt, ap) != -1)
    {
123 124 125
        dialog_fatal_t dialog = { title, text, };
        var_SetAddress (provider,
                        modal ? "dialog-critical" : "dialog-error", &dialog);
126 127 128 129 130
        free (text);
    }
    vlc_object_release (provider);
}

131 132 133 134
#undef dialog_vaLogin
void dialog_vaLogin (vlc_object_t *obj, const char *default_username,
                   char **username, char **password, bool *store,
                   const char *title, const char *fmt, va_list ap)
135 136 137 138 139 140 141 142 143
{
    assert ((username != NULL) && (password != NULL));

    *username = *password = NULL;
    if (obj->i_flags & OBJECT_FLAGS_NOINTERACT)
        return;

    vlc_object_t *provider = dialog_GetProvider (obj);
    if (provider == NULL)
144 145
        return;

146 147 148 149
    char *text;

    if (vasprintf (&text, fmt, ap) != -1)
    {
150 151
        dialog_login_t dialog = { title, text, default_username, username,
                                  password, store };
152 153 154
        var_SetAddress (provider, "dialog-login", &dialog);
        free (text);
    }
155
    vlc_object_release (provider);
156
}
157

158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180
#undef dialog_Login
/**
 * Requests a username and password through the user interface.
 * @param obj the VLC object requesting credential information
 * @param username a pointer to the specified username [OUT]
 * @param password a pointer to the specified password [OUT]
 * @param title title for the dialog
 * @param fmt format string for the message in the dialog
 * @return Nothing. If a user name resp. a password was specified,
 * it will be returned as a heap-allocated character array
 * into the username resp password pointer. Those must be freed with free().
 * Otherwise *username resp *password will be NULL.
 */
void dialog_Login (vlc_object_t *obj, const char *default_username,
                   char **username, char **password,
                   bool *store, const char *title, const char *fmt, ...)
{
    va_list ap;
    va_start (ap, fmt);
    dialog_vaLogin (obj, default_username, username, password, store,
                    title, fmt, ap);
    va_end (ap);
}
181 182 183 184 185 186

#undef dialog_Question
/**
 * Asks a total (Yes/No/Cancel) question through the user interface.
 * @param obj VLC object emitting the question
 * @param title dialog box title
187
 * @param fmt format string for the dialog box text
188 189 190 191 192 193
 * @param yes first choice/button text
 * @param no second choice/button text
 * @param cancel third answer/button text, or NULL if no third option
 * @return 0 if the user could not answer the question (e.g. there is no UI),
 * 1, 2 resp. 3 if the user pressed the first, second resp. third button.
 */
194 195
int dialog_Question (vlc_object_t *obj, const char *title, const char *fmt,
                     const char *yes, const char *no, const char *cancel, ...)
196 197 198 199 200 201 202 203
{
    if (obj->i_flags & OBJECT_FLAGS_NOINTERACT)
        return 0;

    vlc_object_t *provider = dialog_GetProvider (obj);
    if (provider == NULL)
        return 0;

204 205 206 207 208 209 210 211 212 213
    char *text;
    va_list ap;
    int answer = 0;

    va_start (ap, cancel);
    if (vasprintf (&text, fmt, ap) != -1)
    {
        dialog_question_t dialog = { title, text, yes, no, cancel, 0, };
        var_SetAddress (provider, "dialog-question", &dialog);
        answer = dialog.answer;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
214
        free (text);
215 216
    }
    va_end (ap);
217
    vlc_object_release (provider);
218
    return answer;
219
}
220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268

#undef dialog_ProgressCreate
/**
 * Creates a progress bar dialog.
 */
dialog_progress_bar_t *
dialog_ProgressCreate (vlc_object_t *obj, const char *title,
                       const char *message, const char *cancel)
{
    if (obj->i_flags & OBJECT_FLAGS_NOINTERACT)
        return NULL;

    vlc_object_t *provider = dialog_GetProvider (obj);
    if (provider == NULL)
        return NULL;

    dialog_progress_bar_t *dialog = malloc (sizeof (*dialog));
    if (dialog != NULL)
    {
        dialog->title = title;
        dialog->message = message;
        dialog->cancel = cancel;
        var_SetAddress (provider, "dialog-progress-bar", dialog);
#ifndef NDEBUG
        dialog->title = dialog->message = dialog->cancel = NULL;
#endif
        assert (dialog->pf_update);
        assert (dialog->pf_check);
        assert (dialog->pf_destroy);
    }

    /* FIXME: This could conceivably crash if the dialog provider is destroyed
     * before the dialog user. Holding the provider does not help, as it only
     * protects object variable operations. For instance, it does not prevent
     * unloading of the interface plugin. In the short term, the only solution
     * is to not use progress dialog after deinitialization of the interfaces.
     */
    vlc_object_release (provider);
    return dialog;
}

void dialog_ProgressDestroy (dialog_progress_bar_t *dialog)
{
    assert (dialog);

    dialog->pf_destroy (dialog->p_sys);
    free (dialog);
}

269 270
void dialog_ProgressSet (dialog_progress_bar_t *dialog, const char *text,
                         float value)
271 272 273
{
    assert (dialog);

274
    dialog->pf_update (dialog->p_sys, text, value);
275 276 277 278 279 280 281 282 283
}

bool dialog_ProgressCancelled (dialog_progress_bar_t *dialog)
{
    assert (dialog);

    return dialog->pf_check (dialog->p_sys);
}

284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303
#undef dialog_ExtensionUpdate
int dialog_ExtensionUpdate (vlc_object_t *obj, extension_dialog_t *dialog)
{
    assert (obj);
    assert (dialog);

    vlc_object_t *dp = dialog_GetProvider(obj);
    if (!dp)
    {
        msg_Warn (obj, "Dialog provider is not set, can't update dialog '%s'",
                  dialog->psz_title);
        return VLC_EGENERIC;
    }

    // Signaling the dialog provider
    int ret = var_SetAddress (dp, "dialog-extension", dialog);

    vlc_object_release (dp);
    return ret;
}