dialog.c 8.42 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
/*****************************************************************************
 * dialog.c: User dialog functions
 *****************************************************************************
 * Copyright © 2009 Rémi Denis-Courmont
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (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
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU 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.
 *****************************************************************************/

/**
 * \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 117 118 119 120
    vlc_object_t *provider = dialog_GetProvider (obj);
    if (provider == NULL)
    {
        msg_Err (obj, "%s", title);
        msg_GenericVa (obj, VLC_MSG_ERR, MODULE_STRING, fmt, ap);
        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 131 132 133
        free (text);
    }
    vlc_object_release (provider);
}

#undef dialog_Login
/**
 * Requests a username and password through the user interface.
134
 * @param obj the VLC object requesting credential information
135 136 137
 * @param username a pointer to the specified username [OUT]
 * @param password a pointer to the specified password [OUT]
 * @param title title for the dialog
138
 * @param text format string for the message in the dialog
139 140 141 142 143 144
 * @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, char **username, char **password,
145
                   const char *title, const char *fmt, ...)
146 147 148 149 150 151 152 153 154
{
    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)
155 156
        return;

157 158 159 160 161 162 163 164 165 166 167
    char *text;
    va_list ap;

    va_start (ap, fmt);
    if (vasprintf (&text, fmt, ap) != -1)
    {
        dialog_login_t dialog = { title, text, username, password, };
        var_SetAddress (provider, "dialog-login", &dialog);
        free (text);
    }
    va_end (ap);
168
    vlc_object_release (provider);
169
}
170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198


#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
 * @param text dialog box text
 * @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.
 */
int dialog_Question (vlc_object_t *obj, const char *title, const char *text,
                     const char *yes, const char *no, const char *cancel)
{
    if (obj->i_flags & OBJECT_FLAGS_NOINTERACT)
        return 0;

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

    dialog_question_t dialog = { title, text, yes, no, cancel, 0, };
    var_SetAddress (provider, "dialog-question", &dialog);
    vlc_object_release (provider);
    return dialog.answer;
}
199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 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

#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);
}

248 249
void dialog_ProgressSet (dialog_progress_bar_t *dialog, const char *text,
                         float value)
250 251 252
{
    assert (dialog);

253
    dialog->pf_update (dialog->p_sys, text, value);
254 255 256 257 258 259 260 261 262
}

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

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

263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282
#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;
}