288 lines
7.8 KiB
C
288 lines
7.8 KiB
C
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
|
|
/*
|
|
* mx-utils.c: General utility functions used in Moblin
|
|
*
|
|
* Copyright 2009, 2010 Intel Corporation
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify it
|
|
* under the terms and conditions of the GNU Lesser General Public License,
|
|
* version 2.1, as published by the Free Software Foundation.
|
|
*
|
|
* This program is distributed in the hope it will be useful, but WITHOUT ANY
|
|
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
|
* FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
|
|
* more details.
|
|
*
|
|
* 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 St - Fifth Floor, Boston, MA 02110-1301 USA.
|
|
*
|
|
* Authors: Emmanuele Bassi <ebassi@linux.intel.com>
|
|
* Rob Bradford <rob@linux.intel.com>
|
|
* Neil Roberts <neil@linux.intel.com>
|
|
*/
|
|
|
|
/**
|
|
* SECTION:mx-utils
|
|
* @short_description: General utility functions useful for GUIs
|
|
*
|
|
* Utilities useful for creating user interfaces.
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include "config.h"
|
|
#endif
|
|
|
|
#include <glib/gi18n-lib.h>
|
|
|
|
#include "mx-utils.h"
|
|
#include "mx-private.h"
|
|
|
|
/**
|
|
* mx_set_locale:
|
|
*
|
|
* Initializes internationalization support for Mx. If MxApplication is
|
|
* used, this is called automatically. Otherwise it has to be called
|
|
* together with clutter_init() before using Mx.
|
|
*/
|
|
void
|
|
mx_set_locale ()
|
|
{
|
|
/* initialise i81n language bindings for mx */
|
|
bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
|
|
bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
|
|
}
|
|
|
|
|
|
/* This code is based on a similar function in Tweet */
|
|
|
|
/**
|
|
* mx_utils_format_time:
|
|
* @time_: a time value
|
|
*
|
|
* Generates a string describing the time given in @time_ using
|
|
* colloquial language suitable for display to the user. Examples of
|
|
* what might be returned are "A few minutes ago" or "Yesterday".
|
|
*
|
|
* Returns: a string. Free with g_free().
|
|
*/
|
|
gchar *
|
|
mx_utils_format_time (GTimeVal *time_)
|
|
{
|
|
GTimeVal now;
|
|
struct tm tm_mtime;
|
|
gchar *retval = NULL;
|
|
GDate d1, d2;
|
|
gint secs_diff, mins_diff, hours_diff, days_diff, months_diff, years_diff;
|
|
|
|
g_return_val_if_fail (time_->tv_usec >= 0 &&
|
|
time_->tv_usec < G_USEC_PER_SEC, NULL);
|
|
|
|
g_get_current_time (&now);
|
|
|
|
#ifdef HAVE_LOCALTIME_R
|
|
localtime_r ((time_t *) &(time_->tv_sec), &tm_mtime);
|
|
#else
|
|
{
|
|
struct tm *ptm = localtime ((time_t *) &(time_->tv_sec));
|
|
|
|
if (!ptm)
|
|
{
|
|
g_warning ("ptm != NULL failed");
|
|
return NULL;
|
|
}
|
|
else
|
|
memcpy ((void *) &tm_mtime, (void *) ptm, sizeof (struct tm));
|
|
}
|
|
#endif /* HAVE_LOCALTIME_R */
|
|
|
|
secs_diff = now.tv_sec - time_->tv_sec;
|
|
if (secs_diff < 60)
|
|
return g_strdup (_("Less than a minute ago"));
|
|
|
|
mins_diff = secs_diff / 60;
|
|
if (mins_diff < 60)
|
|
return g_strdup (_("A few minutes ago"));
|
|
|
|
hours_diff = mins_diff / 60;
|
|
if (hours_diff < 3)
|
|
return g_strdup (_("A couple of hours ago"));
|
|
|
|
g_date_set_time_t (&d1, now.tv_sec);
|
|
g_date_set_time_t (&d2, time_->tv_sec);
|
|
days_diff = g_date_get_julian (&d1) - g_date_get_julian (&d2);
|
|
|
|
if (days_diff == 0)
|
|
return g_strdup (_("Earlier today"));
|
|
|
|
if (days_diff == 1)
|
|
return g_strdup (_("Yesterday"));
|
|
|
|
if (days_diff < 7)
|
|
{
|
|
const gchar *format = NULL;
|
|
gchar *locale_format = NULL;
|
|
gchar buf[256];
|
|
|
|
format = _("On %A"); /* day of the week */
|
|
locale_format = g_locale_from_utf8 (format, -1, NULL, NULL, NULL);
|
|
|
|
if (strftime (buf, sizeof (buf), locale_format, &tm_mtime) != 0)
|
|
retval = g_locale_to_utf8 (buf, -1, NULL, NULL, NULL);
|
|
else
|
|
retval = g_strdup (_("Unknown"));
|
|
|
|
g_free (locale_format);
|
|
return retval;
|
|
}
|
|
|
|
if (days_diff < 14)
|
|
return g_strdup (_("Last week"));
|
|
|
|
if (days_diff < 21)
|
|
return g_strdup (_("A couple of weeks ago"));
|
|
|
|
months_diff = g_date_get_month (&d1) - g_date_get_month (&d2);
|
|
years_diff = g_date_get_year (&d1) - g_date_get_year (&d2);
|
|
|
|
if (years_diff == 0 && months_diff == 0)
|
|
return g_strdup (_("This month"));
|
|
|
|
if ((years_diff == 0 && months_diff == 1) ||
|
|
(years_diff == 1 && months_diff == -11)) /* Now Jan., last used in Dec. */
|
|
return g_strdup (_("Last month"));
|
|
|
|
if (years_diff == 0)
|
|
return g_strdup (_("This year"));
|
|
|
|
if (years_diff == 1)
|
|
return g_strdup (_("Last year"));
|
|
|
|
return g_strdup (_("Ages ago"));
|
|
}
|
|
|
|
/* Utility function to modify a child allocation box with respect to the
|
|
* x/y-fill child properties. Expects childbox to contain the available
|
|
* allocation space.
|
|
*/
|
|
void
|
|
mx_allocate_align_fill (ClutterActor *child,
|
|
ClutterActorBox *childbox,
|
|
MxAlign x_alignment,
|
|
MxAlign y_alignment,
|
|
gboolean x_fill,
|
|
gboolean y_fill)
|
|
{
|
|
gfloat natural_width, natural_height;
|
|
gfloat min_width, min_height;
|
|
gfloat child_width, child_height;
|
|
gfloat available_width, available_height;
|
|
ClutterRequestMode request;
|
|
ClutterActorBox allocation = { 0, };
|
|
gdouble x_align, y_align;
|
|
|
|
if (x_alignment == MX_ALIGN_START)
|
|
x_align = 0.0;
|
|
else if (x_alignment == MX_ALIGN_MIDDLE)
|
|
x_align = 0.5;
|
|
else
|
|
x_align = 1.0;
|
|
|
|
if (y_alignment == MX_ALIGN_START)
|
|
y_align = 0.0;
|
|
else if (y_alignment == MX_ALIGN_MIDDLE)
|
|
y_align = 0.5;
|
|
else
|
|
y_align = 1.0;
|
|
|
|
available_width = childbox->x2 - childbox->x1;
|
|
available_height = childbox->y2 - childbox->y1;
|
|
|
|
if (available_width < 0)
|
|
available_width = 0;
|
|
|
|
if (available_height < 0)
|
|
available_height = 0;
|
|
|
|
if (x_fill)
|
|
{
|
|
allocation.x1 = childbox->x1;
|
|
allocation.x2 = (int)(allocation.x1 + available_width);
|
|
}
|
|
|
|
if (y_fill)
|
|
{
|
|
allocation.y1 = childbox->y1;
|
|
allocation.y2 = (int)(allocation.y1 + available_height);
|
|
}
|
|
|
|
/* if we are filling horizontally and vertically then we're done */
|
|
if (x_fill && y_fill)
|
|
{
|
|
*childbox = allocation;
|
|
return;
|
|
}
|
|
|
|
request = clutter_actor_get_request_mode (child);
|
|
child_width = child_height = 0.0f;
|
|
|
|
if (request == CLUTTER_REQUEST_HEIGHT_FOR_WIDTH)
|
|
{
|
|
clutter_actor_get_preferred_width (child, available_height,
|
|
&min_width,
|
|
&natural_width);
|
|
|
|
child_width = CLAMP (natural_width, min_width, available_width);
|
|
|
|
if (!y_fill)
|
|
{
|
|
clutter_actor_get_preferred_height (child, child_width,
|
|
&min_height,
|
|
&natural_height);
|
|
|
|
child_height = CLAMP (natural_height, min_height, available_height);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
clutter_actor_get_preferred_height (child, available_width,
|
|
&min_height,
|
|
&natural_height);
|
|
|
|
child_height = CLAMP (natural_height, min_height, available_height);
|
|
|
|
if (!x_fill)
|
|
{
|
|
clutter_actor_get_preferred_width (child, child_height,
|
|
&min_width,
|
|
&natural_width);
|
|
|
|
child_width = CLAMP (natural_width, min_width, available_width);
|
|
}
|
|
}
|
|
|
|
if (!x_fill)
|
|
{
|
|
allocation.x1 = childbox->x1 + (int)((available_width - child_width) * x_align);
|
|
allocation.x2 = allocation.x1 + (int) child_width;
|
|
}
|
|
|
|
if (!y_fill)
|
|
{
|
|
allocation.y1 = childbox->y1 + (int)((available_height - child_height) * y_align);
|
|
allocation.y2 = allocation.y1 + (int) child_height;
|
|
}
|
|
|
|
*childbox = allocation;
|
|
}
|
|
|
|
|
|
void
|
|
mx_actor_box_clamp_to_pixels (ClutterActorBox *box)
|
|
{
|
|
box->x1 = (int) box->x1;
|
|
box->y1 = (int) box->y1;
|
|
box->x2 = (int) box->x2;
|
|
box->y2 = (int) box->y2;
|
|
}
|