Skip to content

Commit

Permalink
Merge branch 'left-bubble'
Browse files Browse the repository at this point in the history
Conflicts:
	ChangeLog
  • Loading branch information
nickg committed Nov 6, 2010
2 parents 752b7e2 + cb7f043 commit 01ee27f
Show file tree
Hide file tree
Showing 4 changed files with 221 additions and 21 deletions.
14 changes: 14 additions & 0 deletions ChangeLog
@@ -1,3 +1,17 @@
2010-11-06 Nick Gasson <nick@nickg.me.uk>

* src/bubblegen.c (bubble_init_left): Fix rendering of
left-handed though bubbles.
* src/bubblegen.c (bubble_content_left): Align left-handed text
content correctly.

2010-10-23 Nick Gasson <nick@nickg.me.uk>

* src/bubblegen.c (bubble_init): Add a function to generate
left-handed bubbles.
* src/xcowsay.c (main): Add --left option to put bubble on
the left of the cow.

2010-10-02 Nick Gasson <nick@nickg.me.uk>

* xcowsay.6: Update bugs section: remove note about lack of word
Expand Down
179 changes: 171 additions & 8 deletions src/bubblegen.c
Expand Up @@ -61,17 +61,160 @@ typedef struct {

typedef enum { NORMAL, THOUGHT } bubble_style_t;

static void bubble_init(bubble_t *b, bubble_style_t style)
static void bubble_init_left(bubble_t *b, bubble_style_t style)
{
GdkColor black, white, bright_green;
GdkColormap *colormap;
GdkPoint tip_points[5];
GdkVisual *root_visual;

colormap = gdk_colormap_get_system();
gdk_color_black(colormap, &black);
gdk_color_white(colormap, &white);

root_visual = gdk_visual_get_system();
b->pixmap = gdk_pixmap_new(NULL, b->width, b->height, root_visual->depth);
g_assert(b->pixmap);
b->gc = gdk_gc_new(b->pixmap);
bright_green.red = 0;
bright_green.green = 65535; // Bright green is alpha
bright_green.blue = 0;
gdk_gc_set_background(b->gc, &black);
gdk_gc_set_rgb_fg_color(b->gc, &bright_green);

gdk_draw_rectangle(b->pixmap, b->gc, TRUE, 0, 0, b->width, b->height);

b->width -= BUBBLE_BORDER;
b->height -= BUBBLE_BORDER;

// Space between cow and bubble
int middle = style == NORMAL ? TIP_WIDTH : THINK_WIDTH;

// Draw the white corners
gdk_gc_set_foreground(b->gc, &white);
gdk_draw_arc(b->pixmap, b->gc, TRUE, BUBBLE_BORDER,
BUBBLE_BORDER, CORNER_DIAM, CORNER_DIAM, 90*64, 90*64);
gdk_draw_arc(b->pixmap, b->gc, TRUE, BUBBLE_BORDER,
b->height - CORNER_DIAM, CORNER_DIAM,
CORNER_DIAM, 180*64, 90*64);
gdk_draw_arc(b->pixmap, b->gc, TRUE,
b->width - CORNER_DIAM - BUBBLE_BORDER - middle,
b->height - CORNER_DIAM, CORNER_DIAM,
CORNER_DIAM, 270*64, 90*64);
gdk_draw_arc(b->pixmap, b->gc, TRUE,
b->width - CORNER_DIAM - BUBBLE_BORDER - middle,
BUBBLE_BORDER, CORNER_DIAM, CORNER_DIAM, 0*64, 90*64);

// Fill in the middle of the bubble
gdk_draw_rectangle(b->pixmap, b->gc, TRUE,
CORNER_RADIUS + BUBBLE_BORDER,
BUBBLE_BORDER,
b->width - middle - BUBBLE_BORDER - CORNER_DIAM,
b->height - BUBBLE_BORDER);
gdk_draw_rectangle(b->pixmap, b->gc, TRUE,
BUBBLE_BORDER, BUBBLE_BORDER + CORNER_RADIUS,
b->width - middle - BUBBLE_BORDER*2,
b->height - BUBBLE_BORDER - CORNER_DIAM);

if (style == NORMAL) {
// The points on the tip part
int tip_compute_offset = (b->height - BUBBLE_BORDER - CORNER_DIAM)/3;
int tip_offset[3] = { tip_compute_offset, tip_compute_offset, tip_compute_offset };
if (tip_compute_offset < MIN_TIP_HEIGHT) {
int new_offset = (b->height - BUBBLE_BORDER - CORNER_DIAM - MIN_TIP_HEIGHT)/2;
tip_offset[0] = new_offset;
tip_offset[1] = MIN_TIP_HEIGHT;
tip_offset[2] = new_offset;
}

tip_points[0].x = b->width - middle - BUBBLE_BORDER;
tip_points[0].y = BUBBLE_BORDER + CORNER_RADIUS;
tip_points[1].x = b->width - middle - BUBBLE_BORDER;
tip_points[1].y = BUBBLE_BORDER + CORNER_RADIUS + tip_offset[0];
tip_points[2].x = b->width - BUBBLE_BORDER;
tip_points[2].y = BUBBLE_BORDER + CORNER_RADIUS + tip_offset[0] + tip_offset[1]/2;
tip_points[3].x = b->width - middle - BUBBLE_BORDER;
tip_points[3].y = BUBBLE_BORDER + CORNER_RADIUS + tip_offset[0] + tip_offset[1];
tip_points[4].x = b->width - middle - BUBBLE_BORDER;
tip_points[4].y = b->height - CORNER_RADIUS;

gdk_draw_polygon(b->pixmap, b->gc, TRUE, tip_points, 5);
}
else {
// Incrementally move the top kircle down so it's within the
// bubble's border
int big_y = BIG_KIRCLE_Y;
int small_y = SMALL_KIRCLE_Y;

while (big_y + KIRCLE_TOP_MIN > b->height/2) {
big_y /= 2;
small_y /= 2;
}

// Draw two think kircles
gdk_draw_arc(b->pixmap, b->gc, TRUE,
b->width - BIG_KIRCLE_X - BIG_KIRCLE_DIAM,
b->height/2 - big_y, BIG_KIRCLE_DIAM,
BIG_KIRCLE_DIAM, 0, 360*64);

gdk_draw_arc(b->pixmap, b->gc, TRUE,
b->width - SMALL_KIRCLE_X - SMALL_KIRCLE_DIAM,
b->height/2 - small_y, SMALL_KIRCLE_DIAM,
SMALL_KIRCLE_DIAM, 0, 360*64);

gdk_gc_set_line_attributes(b->gc, 4, GDK_LINE_SOLID,
GDK_CAP_ROUND, GDK_JOIN_ROUND);
gdk_gc_set_foreground(b->gc, &black);
gdk_draw_arc(b->pixmap, b->gc, FALSE,
b->width - BIG_KIRCLE_X - BIG_KIRCLE_DIAM,
b->height/2 - big_y, BIG_KIRCLE_DIAM,
BIG_KIRCLE_DIAM, 0, 360*64);

gdk_draw_arc(b->pixmap, b->gc, FALSE,
b->width - SMALL_KIRCLE_X - SMALL_KIRCLE_DIAM,
b->height/2 - small_y, SMALL_KIRCLE_DIAM,
SMALL_KIRCLE_DIAM, 0, 360*64);
}

// Draw the black rounded corners
gdk_gc_set_line_attributes(b->gc, 4, GDK_LINE_SOLID,
GDK_CAP_ROUND, GDK_JOIN_ROUND);
gdk_gc_set_foreground(b->gc, &black);
gdk_draw_arc(b->pixmap, b->gc, FALSE, BUBBLE_BORDER,
BUBBLE_BORDER, CORNER_DIAM, CORNER_DIAM, 90*64, 90*64);
gdk_draw_arc(b->pixmap, b->gc, FALSE, BUBBLE_BORDER,
b->height - CORNER_DIAM, CORNER_DIAM,
CORNER_DIAM, 180*64, 90*64);
gdk_draw_arc(b->pixmap, b->gc, FALSE,
b->width - middle - CORNER_DIAM - BUBBLE_BORDER,
b->height - CORNER_DIAM, CORNER_DIAM,
CORNER_DIAM, 270*64, 90*64);
gdk_draw_arc(b->pixmap, b->gc, FALSE,
b->width - middle - CORNER_DIAM - BUBBLE_BORDER,
BUBBLE_BORDER, CORNER_DIAM, CORNER_DIAM, 0*64, 90*64);

// Draw the top, bottom, and left sides (easy as they're straight!)
gdk_draw_line(b->pixmap, b->gc,
BUBBLE_BORDER,
CORNER_RADIUS + BUBBLE_BORDER,
BUBBLE_BORDER, b->height - CORNER_RADIUS);
gdk_draw_line(b->pixmap, b->gc,
BUBBLE_BORDER + CORNER_RADIUS, BUBBLE_BORDER,
b->width - CORNER_RADIUS - middle, BUBBLE_BORDER);
gdk_draw_line(b->pixmap, b->gc,
BUBBLE_BORDER + CORNER_RADIUS, b->height,
b->width - CORNER_RADIUS - middle, b->height);

if (style == NORMAL)
gdk_draw_lines(b->pixmap, b->gc, tip_points, 5);
else
gdk_draw_line(b->pixmap, b->gc,
b->width - THINK_WIDTH - BUBBLE_BORDER,
CORNER_RADIUS + BUBBLE_BORDER,
b->width - THINK_WIDTH - BUBBLE_BORDER,
b->height - CORNER_RADIUS);
}

static void bubble_init_right(bubble_t *b, bubble_style_t style)
{
GdkColor black, white, bright_green;
GdkColormap *colormap;
GdkPoint tip_points[5];

colormap = gdk_colormap_get_system();
gdk_color_black(colormap, &black);
Expand Down Expand Up @@ -216,6 +359,21 @@ static void bubble_init(bubble_t *b, bubble_style_t style)
b->height - CORNER_RADIUS);
}

static void bubble_init(bubble_t *b, bubble_style_t style)
{
GdkVisual *root_visual;

root_visual = gdk_visual_get_system();
b->pixmap = gdk_pixmap_new(NULL, b->width, b->height, root_visual->depth);
g_assert(b->pixmap);
b->gc = gdk_gc_new(b->pixmap);

if (get_bool_option("left"))
bubble_init_left(b, style);
else
bubble_init_right(b, style);
}

static void bubble_size_from_content(bubble_t *b, bubble_style_t style,
int c_width, int c_height)
{
Expand All @@ -237,8 +395,13 @@ static GdkPixbuf *bubble_tidy(bubble_t *b)

static int bubble_content_left(bubble_style_t style)
{
int middle = style == NORMAL ? TIP_WIDTH : THINK_WIDTH;
return BUBBLE_BORDER + middle + CORNER_RADIUS;
if (get_bool_option("left")) {
return BUBBLE_BORDER + CORNER_RADIUS;
}
else {
const int middle = style == NORMAL ? TIP_WIDTH : THINK_WIDTH;
return BUBBLE_BORDER + middle + CORNER_RADIUS;
}
}

static int bubble_content_top()
Expand Down
42 changes: 29 additions & 13 deletions src/display_cow.c
Expand Up @@ -268,6 +268,8 @@ void display_cow(bool debug, const char *text, bool run_main, cowmode_t mode)
exit(1);
}

xcowsay.bubble = make_shape_from_pixbuf(xcowsay.bubble_pixbuf);

int total_width = shape_width(xcowsay.cow)
+ get_int_option("bubble_x")
+ xcowsay.bubble_width;
Expand Down Expand Up @@ -297,19 +299,33 @@ void display_cow(bool debug, const char *text, bool run_main, cowmode_t mode)
else if (cow_y >= area_h)
cow_y = area_h - 1;

move_shape(xcowsay.cow,
geom.x + cow_x,
geom.y + bubble_off + cow_y);
show_shape(xcowsay.cow);

xcowsay.bubble = make_shape_from_pixbuf(xcowsay.bubble_pixbuf);
int bx = shape_x(xcowsay.cow) + shape_width(xcowsay.cow)
+ get_int_option("bubble_x");
int by = shape_y(xcowsay.cow)
+ (shape_height(xcowsay.cow) - shape_height(xcowsay.bubble))/2
+ get_int_option("bubble_y");
move_shape(xcowsay.bubble, bx, by);

if (get_bool_option("left")) {
move_shape(xcowsay.cow,
geom.x + cow_x + xcowsay.bubble_width,
geom.y + bubble_off + cow_y);
show_shape(xcowsay.cow);

int bx = shape_x(xcowsay.cow) - xcowsay.bubble_width
+ get_int_option("bubble_x");
int by = shape_y(xcowsay.cow)
+ (shape_height(xcowsay.cow) - shape_height(xcowsay.bubble))/2
+ get_int_option("bubble_y");
move_shape(xcowsay.bubble, bx, by);
}
else {
move_shape(xcowsay.cow,
geom.x + cow_x,
geom.y + bubble_off + cow_y);
show_shape(xcowsay.cow);

int bx = shape_x(xcowsay.cow) + shape_width(xcowsay.cow)
+ get_int_option("bubble_x");
int by = shape_y(xcowsay.cow)
+ (shape_height(xcowsay.cow) - shape_height(xcowsay.bubble))/2
+ get_int_option("bubble_y");
move_shape(xcowsay.bubble, bx, by);
}

xcowsay.state = csLeadIn;
xcowsay.transition_timeout = get_int_option("lead_in_time");
g_timeout_add(TICK_TIMEOUT, tick, NULL);
Expand Down
7 changes: 7 additions & 0 deletions src/xcowsay.c
Expand Up @@ -73,6 +73,7 @@ static struct option long_options[] = {
{"bubble-at", required_argument, 0, 'b'},
{"at", required_argument, 0, 'a'},
{"no-wrap", no_argument, 0, 'w'},
{"left", no_argument, 0, 'l'},
{"config", required_argument, 0, 'o'},
{"debug", no_argument, &debug, 1},
{0, 0, 0, 0}
Expand Down Expand Up @@ -112,6 +113,7 @@ static void usage()
" --at=X,Y\t\t%s\n"
" --bubble-at=X,Y\t%s\n"
" --no-wrap\t\t%s\n"
" --left\t\t%s\n"
" --config=FILE\t%s\n"
" --debug\t\t%s\n\n"
"%s\n\n"
Expand All @@ -134,6 +136,7 @@ static void usage()
i18n("Force the cow to appear at screen location (X,Y)."),
i18n("Change relative position of bubble."),
i18n("Disable wrapping if text cannot fit on screen."),
i18n("Make the bubble appear to the left of cow."),
i18n("Specify alternative config file."),
i18n("Keep daemon attached to terminal."),
i18n("Default values for these options can be specified in the "
Expand Down Expand Up @@ -244,6 +247,7 @@ int main(int argc, char **argv)
add_int_option("bubble_y", 0);
add_string_option("alt_config_file", "");
add_bool_option("wrap", true);
add_bool_option("left", false);

parse_config_file();

Expand Down Expand Up @@ -305,6 +309,9 @@ int main(int argc, char **argv)
case 'w':
set_bool_option("wrap", false);
break;
case 'l':
set_bool_option("left", true);
break;
case '?':
// getopt_long already printed an error message
failure = 1;
Expand Down

0 comments on commit 01ee27f

Please sign in to comment.