awesome/widgets/workspacelist.c

286 lines
8.2 KiB
C

/*
* workspacelist.c - workspace list widget
*
* Copyright © 2008 Julien Danjou <julien@danjou.info>
* Copyright © 2007 Aldo Cortesi <aldo@nullcube.com>
*
* 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.
*
*/
#include "client.h"
#include "widget.h"
#include "workspace.h"
#include "lua.h"
#include "event.h"
#include "common/markup.h"
#include "common/configopts.h"
extern awesome_t globalconf;
typedef struct workspacelist_drawn_area_t workspacelist_drawn_area_t;
struct workspacelist_drawn_area_t
{
void *object;
area_t *area;
workspacelist_drawn_area_t *next, *prev;
};
DO_SLIST(workspacelist_drawn_area_t, workspacelist_drawn_area, p_delete);
typedef struct
{
char *text_normal, *text_focus, *text_urgent;
bool show_empty;
workspacelist_drawn_area_t *drawn_area;
} workspacelist_data_t;
static char *
workspace_markup_parse(workspace_t *t, const char *str, ssize_t len)
{
const char *elements[] = { "title", NULL };
char *title_esc = g_markup_escape_text(t->name, -1);
const char *elements_sub[] = { title_esc , NULL };
markup_parser_data_t *p;
char *ret;
p = markup_parser_data_new(elements, elements_sub, countof(elements));
if(markup_parse(p, str, len))
{
ret = p->text;
p->text = NULL;
}
else
ret = a_strdup(str);
markup_parser_data_delete(&p);
p_delete(&title_esc);
return ret;
}
/** Check if at least one client is on the workspace.
* \param ws The workspace.
* \return True or false.
*/
static bool
workspace_isoccupied(workspace_t *ws)
{
client_t *c;
for(c = globalconf.clients; c; c = c->next)
if(workspace_client_get(c) == ws)
return true;
return false;
}
static bool
workspace_isurgent(workspace_t *ws)
{
client_t *c;
for(c = globalconf.clients; c; c = c->next)
if(c->isurgent && workspace_client_get(c) == ws)
return true;
return false;
}
static char *
workspacelist_text_get(workspace_t *ws, screen_t *screen, workspacelist_data_t *data)
{
if(screen->workspace == ws)
return data->text_focus;
if(workspace_isurgent(ws))
return data->text_urgent;
return data->text_normal;
}
static int
workspacelist_draw(draw_context_t *ctx, int screen,
widget_node_t *w,
int offset,
int used __attribute__ ((unused)),
void *object)
{
workspace_t *ws;
workspacelist_data_t *data = w->widget->data;
int i = 0, prev_width = 0;
area_t *area, rectangle = { 0, 0, 0, 0, NULL, NULL };
char **text = NULL;
workspacelist_drawn_area_t *tda;
w->area.width = w->area.y = 0;
/* Lookup for our workspacelist_drawn_area.
* This will be used to store area where we draw ws list for each object. */
for(tda = data->drawn_area; tda && tda->object != object; tda = tda->next);
/* Oh, we did not find a drawn area for our object. First time? */
if(!tda)
{
/** \todo delete this when the widget is removed from the object */
tda = p_new(workspacelist_drawn_area_t, 1);
tda->object = object;
workspacelist_drawn_area_list_push(&data->drawn_area, tda);
}
area_list_wipe(&tda->area);
/* First compute text and widget width */
for(ws = globalconf.workspaces; ws; ws = ws->next, i++)
{
p_realloc(&text, i + 1);
area = p_new(area_t, 1);
text[i] = workspacelist_text_get(ws, &globalconf.screens[screen], data);
text[i] = workspace_markup_parse(ws, text[i], a_strlen(text[i]));
*area = draw_text_extents(ctx->connection, ctx->phys_screen,
globalconf.font, text[i]);
if (data->show_empty || workspace_isoccupied(ws))
w->area.width += area->width;
area_list_append(&tda->area, area);
}
/* Now that we have widget width we can compute widget x coordinate */
w->area.x = widget_calculate_offset(ctx->width, w->area.width,
offset, w->widget->align);
for(area = tda->area, ws = globalconf.workspaces, i = 0;
ws && area;
ws = ws->next, area = area->next, i++)
{
if (!data->show_empty && !workspace_isoccupied(ws))
continue;
area->x = w->area.x + prev_width;
prev_width += area->width;
draw_text(ctx, globalconf.font, *area, text[i]);
p_delete(&text[i]);
if(workspace_isoccupied(ws))
{
rectangle.width = rectangle.height = (globalconf.font->height + 2) / 3;
rectangle.x = area->x;
rectangle.y = area->y;
draw_rectangle(ctx, rectangle, 1.0, false, ctx->fg);
}
}
p_delete(&text);
w->area.height = ctx->height;
return w->area.width;
}
/** Handle button click on tasklist.
* \param w The widget node.
* \param ev The button press event.
* \param screen The screen where the click was.
* \param object The object we're onto.
* \param type The type object.
*/
static void
workspacelist_button_press(widget_node_t *w,
xcb_button_press_event_t *ev,
int screen __attribute__ ((unused)),
void *object,
awesome_type_t type)
{
button_t *b;
workspacelist_data_t *data = w->widget->data;
workspacelist_drawn_area_t *tda;
area_t *area;
workspace_t *ws;
/* Find the good drawn area list */
for(tda = data->drawn_area; tda && tda->object != object; tda = tda->next);
area = tda->area;
for(b = w->widget->buttons; b; b = b->next)
if(ev->detail == b->button && CLEANMASK(ev->state) == b->mod && b->fct)
for(ws = globalconf.workspaces; ws && area; ws = ws->next, area = area->next)
if(ev->event_x >= AREA_LEFT(*area)
&& ev->event_x < AREA_RIGHT(*area)
&& (data->show_empty || workspace_isoccupied(ws)) )
{
luaA_pushpointer(object, type);
luaA_workspace_userdata_new(ws);
luaA_dofunction(globalconf.L, b->fct, 2);
return;
}
}
static widget_tell_status_t
workspacelist_tell(widget_t *widget, const char *property, const char *new_value)
{
workspacelist_data_t *d = widget->data;
if(!a_strcmp(property, "text_normal"))
{
p_delete(&d->text_normal);
d->text_normal = a_strdup(new_value);
}
else if(!a_strcmp(property, "text_focus"))
{
p_delete(&d->text_focus);
d->text_focus = a_strdup(new_value);
}
else if(!a_strcmp(property, "text_urgent"))
{
p_delete(&d->text_urgent);
d->text_urgent = a_strdup(new_value);
}
else if(!a_strcmp(property, "show_empty"))
d->show_empty = a_strtobool(new_value);
else
return WIDGET_ERROR;
return WIDGET_NOERROR;
}
widget_t *
workspacelist_new(alignment_t align)
{
widget_t *w;
workspacelist_data_t *d;
w = p_new(widget_t, 1);
widget_common_new(w);
w->align = align;
w->draw = workspacelist_draw;
w->button_press = workspacelist_button_press;
w->tell = workspacelist_tell;
w->data = d = p_new(workspacelist_data_t, 1);
d->text_normal = a_strdup(" <text align=\"center\"/><title/> ");
d->text_focus = a_strdup(" <text align=\"center\"/><title/> ");
d->text_urgent = a_strdup(" <text align=\"center\"/><title/> ");
d->show_empty = true;
/* Set cache property */
w->cache_flags = WIDGET_CACHE_WORKSPACES | WIDGET_CACHE_CLIENTS;
return w;
}
// vim: filetype=c:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=80