gaim/gaim

i don't think we want these...
gaim-doodle
2005-07-28, Gary Kramlich
a75ee5623db1
i don't think we want these...
/*
* gaim
*
* Gaim is the legal property of its developers, whose names are too numerous
* to list here. Please refer to the COPYRIGHT file distributed with this
* source distribution.
*
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
// INCLUDES ============================================================================================
#include "gtkwhiteboard.h"
// GLOBALS =============================================================================================
//GList *buttonList = NULL;
int LastX; // Tracks last position of the mouse when drawing
int LastY;
int MotionCount; // Tracks how many brush motions made
int BrushState = BRUSH_STATE_UP;
static GaimWhiteboardUiOps ui_ops =
{
gaim_gtk_whiteboard_create,
gaim_gtk_whiteboard_destroy,
gaim_gtk_whiteboard_draw_brush_point,
gaim_gtk_whiteboard_draw_brush_line,
gaim_gtk_whiteboard_clear
};
// FUNCTIONS ============================================================================================
GaimWhiteboardUiOps *gaim_gtk_whiteboard_get_ui_ops( void )
{
return( &ui_ops );
}
// ------------------------------------------------------------------------------------------------------
void gaim_gtk_whiteboard_create( GaimWhiteboard *wb )
{
//int i;
//g_print( "gaim_gtk_whiteboard_create()\n" );
GtkWidget *window;
GtkWidget *drawing_area;
GtkWidget *hbox_palette;
GtkWidget *vbox_palette_above_canvas_and_controls;
GtkWidget *hbox_canvas_and_controls;
GtkWidget *vbox_controls;
// --------------------------
// |[][][][palette[][][][][]|
// |------------------------|
// | canvas | con |
// | | trol|
// | | s |
// | | |
// | | |
// --------------------------
GtkWidget *clear_button;
GtkWidget *save_button;
//GtkWidget *color_box[PALETTE_NUM_OF_COLORS];
GaimGtkWhiteboard *gtkwb = g_new0( GaimGtkWhiteboard, 1 );
gtkwb->wb = wb;
wb->ui_data = gtkwb;
// Protocol specific ui_ops?
gtkwb->width = 368;
gtkwb->height = 256;
window = gtk_window_new( GTK_WINDOW_TOPLEVEL );
gtkwb->window = window;
gtk_widget_set_name( window, wb->who );
gtk_window_set_title( ( GtkWindow* )( window ), wb->who ); // TODO Try and use alias first
gtk_window_set_resizable( ( GtkWindow* )( window ), FALSE );
g_signal_connect( G_OBJECT( window ), "destroy",
G_CALLBACK( gaim_gtk_whiteboard_exit ), ( gpointer )( gtkwb ) );
// Create vertical box to place palette above the canvas and controls
vbox_palette_above_canvas_and_controls = gtk_vbox_new( FALSE, 0 );
gtk_container_add( GTK_CONTAINER( window ), vbox_palette_above_canvas_and_controls );
gtk_widget_show( vbox_palette_above_canvas_and_controls );
// Create horizontal box for the palette and all its entries
hbox_palette = gtk_hbox_new( FALSE, 0 );
gtk_container_add( GTK_CONTAINER( vbox_palette_above_canvas_and_controls ), hbox_palette );
gtk_widget_show( hbox_palette );
// Create horizontal box to seperate the canvas from the controls
hbox_canvas_and_controls = gtk_hbox_new( FALSE, 0 );
gtk_container_add( GTK_CONTAINER( vbox_palette_above_canvas_and_controls ), hbox_canvas_and_controls );
gtk_widget_show( hbox_canvas_and_controls );
/*
for( i = 0; i < PALETTE_NUM_OF_COLORS; i++ )
{
color_box[i] = gtk_label_new( NULL );
gtk_widget_set_size_request( color_box[i], gtkwb->width / PALETTE_NUM_OF_COLORS ,32 );
gtk_container_add( GTK_CONTAINER( hbox_palette ), color_box[i] );
gtk_widget_show( color_box[i] );
}
*/
// Create the drawing area
drawing_area = gtk_drawing_area_new();
gtkwb->drawing_area = drawing_area;
// NOTE Protocol specific needs UI_OP?
gtk_widget_set_size_request( GTK_WIDGET( drawing_area ), gtkwb->width, gtkwb->height );
gtk_box_pack_start( GTK_BOX( hbox_canvas_and_controls ), drawing_area, TRUE, TRUE, 8 );
gtk_widget_show( drawing_area );
// Signals used to handle backing pixmap
g_signal_connect( G_OBJECT( drawing_area ), "expose_event",
G_CALLBACK( gaim_gtk_whiteboard_expose_event ), ( gpointer )( gtkwb ) );
g_signal_connect( G_OBJECT( drawing_area ), "configure_event",
G_CALLBACK( gaim_gtk_whiteboard_configure_event ), ( gpointer )( gtkwb ) );
// Event signals
g_signal_connect( G_OBJECT( drawing_area ), "button_press_event",
G_CALLBACK( gaim_gtk_whiteboard_brush_down ), ( gpointer )( gtkwb ) );
g_signal_connect( G_OBJECT( drawing_area ), "motion_notify_event",
G_CALLBACK( gaim_gtk_whiteboard_brush_motion ), ( gpointer )( gtkwb ) );
g_signal_connect( G_OBJECT( drawing_area ), "button_release_event",
G_CALLBACK( gaim_gtk_whiteboard_brush_up ), ( gpointer )( gtkwb ) );
gtk_widget_set_events( drawing_area, GDK_EXPOSURE_MASK |
GDK_LEAVE_NOTIFY_MASK |
GDK_BUTTON_PRESS_MASK |
GDK_POINTER_MOTION_MASK |
GDK_BUTTON_RELEASE_MASK |
GDK_POINTER_MOTION_HINT_MASK );
// Create vertical box to contain the controls
vbox_controls = gtk_vbox_new( FALSE, 0 );
gtk_container_add( GTK_CONTAINER( hbox_canvas_and_controls ), vbox_controls );
gtk_widget_show( vbox_controls );
// Add a clear button
clear_button = gtk_button_new_with_label( "Clear" );
gtk_widget_set_size_request( clear_button, 96 ,32 );
gtk_box_pack_start( GTK_BOX( vbox_controls ), clear_button, FALSE, FALSE, 0 );
gtk_widget_show( clear_button );
g_signal_connect( G_OBJECT( clear_button ), "clicked",
G_CALLBACK( gaim_gtk_whiteboard_button_clear_press ), ( gpointer )( gtkwb ) );
// Add a save button
save_button = gtk_button_new_with_label( "Save" );
gtk_widget_set_size_request( save_button, 96 ,32 );
gtk_box_pack_start( GTK_BOX( vbox_controls ), save_button, FALSE, FALSE, 8 );
gtk_widget_show( save_button );
// Make all this (window) visible
gtk_widget_show( window );
gaim_gtk_whiteboard_set_canvas_as_icon( gtkwb );
// TODO Specific protocol/whiteboard assignment here? Needs a UI Op?
// Set default brush size and color
//ds->brush_size = DOODLE_BRUSH_MEDIUM;
//ds->brush_color = 0; // black
}
// ------------------------------------------------------------------------------------------------------
void gaim_gtk_whiteboard_destroy( GaimWhiteboard *wb )
{
//g_print( "gaim_gtk_whiteboard_destroy()\n" );
GaimGtkWhiteboard *gtkwb = wb->ui_data;
// TODO Ask if user wants to save picture before the session is closed
// Clear graphical memory
if( gtkwb->pixmap )
g_object_unref( gtkwb->pixmap );
gaim_whiteboard_destroy( gtkwb->wb );
if( gtkwb )
g_free( gtkwb );
}
// ------------------------------------------------------------------------------------------------------
void gaim_gtk_whiteboard_exit( GtkWidget *widget, gpointer data )
{
//g_print( "gaim_gtk_whiteboard_exit()\n" );
GaimGtkWhiteboard *gtkwb = ( GaimGtkWhiteboard* )( data );
gaim_gtk_whiteboard_destroy( gtkwb->wb );
}
// ------------------------------------------------------------------------------------------------------
/*
// Whiteboard start button on conversation window (ooo boy... figure this one out)
void gaim_gtkwhiteboard_button_start_press( GtkButton *button, gpointer data )
{
GaimConversation *conv = data;
GaimAccount *account = gaim_conversation_get_account( conv );
GaimConnection *gc = gaim_account_get_connection( account );
char *to = ( char* )( gaim_conversation_get_name( conv ) );
// Only handle this if local client requested Doodle session (else local client would have sent one)
GaimWhiteboard *wb = gaim_whiteboard_get( account, from );
// Write a local message to this conversation showing that
// a request for a Doodle session has been made
gaim_conv_im_write( GAIM_CONV_IM( conv ), "", _("Sent Doodle request."),
GAIM_MESSAGE_NICK | GAIM_MESSAGE_RECV, time( NULL ) );
yahoo_doodle_command_send_request( gc, to );
yahoo_doodle_command_send_ready( gc, to );
// Insert this 'session' in the list. At this point, it's only a requested session.
wb = gaim_whiteboard_create( account, from, DOODLE_STATE_REQUESTING );
}
*/
// ------------------------------------------------------------------------------------------------------
gboolean gaim_gtk_whiteboard_configure_event( GtkWidget *widget, GdkEventConfigure *event, gpointer data )
{
//g_print( "gaim_gtk_whiteboard_configure_event | %s\n", ds->who );
GaimGtkWhiteboard *gtkwb = ( GaimGtkWhiteboard* )( data );
GdkPixmap *pixmap = gtkwb->pixmap;
if( pixmap )
g_object_unref( pixmap );
pixmap = gdk_pixmap_new( widget->window,
widget->allocation.width,
widget->allocation.height,
-1 );
gtkwb->pixmap = pixmap;
gdk_draw_rectangle( pixmap,
widget->style->white_gc,
TRUE,
0, 0,
widget->allocation.width,
widget->allocation.height );
return( TRUE );
}
// ------------------------------------------------------------------------------------------------------
gboolean gaim_gtk_whiteboard_expose_event( GtkWidget *widget, GdkEventExpose *event, gpointer data )
{
//g_print( "gaim_gtk_whiteboard_expose_event | %s\n", ds->who );
GaimGtkWhiteboard *gtkwb = ( GaimGtkWhiteboard* )( data );
GdkPixmap *pixmap = gtkwb->pixmap;
gdk_draw_drawable( widget->window,
widget->style->fg_gc[GTK_WIDGET_STATE( widget )],
pixmap,
event->area.x, event->area.y,
event->area.x, event->area.y,
event->area.width, event->area.height );
return( FALSE );
}
// ------------------------------------------------------------------------------------------------------
gboolean gaim_gtk_whiteboard_brush_down( GtkWidget *widget, GdkEventButton *event, gpointer data )
{
//g_print( "BRUSH_DOWN | %s\n", ds->who );
GaimGtkWhiteboard *gtkwb = ( GaimGtkWhiteboard* )( data );
GdkPixmap *pixmap = gtkwb->pixmap;
/* GList *d_list = gtkwb->wb->draw_list;
int *brush_color = NULL;
int *brush_size = NULL;
int *x0 = NULL;
int *y0 = NULL;
*/
if( BrushState != BRUSH_STATE_UP )
{
// Potential double-click DOWN to DOWN?
g_print( "***Bad brush state transition %d to DOWN\n", BrushState );
BrushState = BRUSH_STATE_DOWN;
//return( FALSE );
}
BrushState = BRUSH_STATE_DOWN;
if( event->button == 1 && pixmap != NULL )
{
/* // Check if draw_list has contents; if so, clear it
if( d_list )
gaim_whiteboard_draw_list_destroy( d_list );
brush_color = g_new0( int, 1 );
brush_size = g_new0( int, 1 );
x0 = g_new0( int, 1 );
y0 = g_new0( int, 1 );
// *brush_color = gtkwb->brush_color;
// *brush_size = gtkwb->brush_size;
*x0 = event->x;
*y0 = event->y;
*/
// Set tracking variables
LastX = event->x;//LastX = *x0;
LastY = event->y;//LastY = *y0;
MotionCount = 0;
/*
d_list = g_list_append( d_list, ( gpointer )( brush_color ) );
d_list = g_list_append( d_list, ( gpointer )( brush_size ) );
d_list = g_list_append( d_list, ( gpointer )( x0 ) );
d_list = g_list_append( d_list, ( gpointer )( y0 ) );
*/
gaim_gtk_whiteboard_draw_brush_point( gtkwb->wb,
event->x, event->y,
0,5 );//gtkwb->brush_color, gtkwb->brush_size ); NOTE temp const prot uiop
}
return( TRUE );
}
// ------------------------------------------------------------------------------------------------------
gboolean gaim_gtk_whiteboard_brush_motion( GtkWidget *widget, GdkEventMotion *event, gpointer data )
{
//g_print( "BRUSH_MOTION | %s\n", ds->who );
int x;
int y;
// int *dx;
// int *dy;
GdkModifierType state;
GaimGtkWhiteboard *gtkwb = ( GaimGtkWhiteboard* )( data );
GdkPixmap *pixmap = gtkwb->pixmap;
// GList *d_list = gtkwb->wb->draw_list;
if( event->is_hint )
gdk_window_get_pointer( event->window, &x, &y, &state );
else
{
x = event->x;
y = event->y;
state = event->state;
}
if( state & GDK_BUTTON1_MASK && pixmap != NULL )
{
if( ( BrushState != BRUSH_STATE_DOWN ) && ( BrushState != BRUSH_STATE_MOTION ) )
{
g_print( "***Bad brush state transition %d to MOTION\n", BrushState );
BrushState = BRUSH_STATE_MOTION;
return( FALSE );
}
BrushState = BRUSH_STATE_MOTION;
/*
dx = g_new0( int, 1 );
dy = g_new0( int, 1 );
*dx = x - LastX;
*dy = y - LastY;
*/
MotionCount++;
/*
// NOTE 100 is a temporary constant for how many deltas/motions in a stroke (needs UI Ops?)
if( MotionCount == 100 )
{
d_list = g_list_append( d_list, ( gpointer )( dx ) );
d_list = g_list_append( d_list, ( gpointer )( dy ) );
//NOTE Protocol specific here too... UI Ops?
//char* message = goodle_doodle_session_build_draw_string( d_list );
//yahoo_doodle_command_send_draw( ds->gc, ds->who, message );
// The brush stroke is finished, clear the list for another one
gaim_whiteboard_draw_list_destroy( d_list );
int *brush_color = g_new0( int, 1 );
int *brush_size = g_new0( int, 1 );
int *x0 = g_new0( int, 1 );
int *y0 = g_new0( int, 1 );
// *brush_color = gtkwb->brush_color;
// *brush_size = gtkwb->brush_size;
*x0 = LastX;
*y0 = LastY;
// Reset motion tracking
MotionCount = 0;
d_list = g_list_append( d_list, ( gpointer )( brush_color ) );
d_list = g_list_append( d_list, ( gpointer )( brush_size ) );
d_list = g_list_append( d_list, ( gpointer )( x0 ) );
d_list = g_list_append( d_list, ( gpointer )( y0 ) );
}
//else
{
dx = g_new0( int, 1 );
dy = g_new0( int, 1 );
*dx = x - LastX;
*dy = y - LastY;
d_list = g_list_append( d_list, ( gpointer )( dx ) );
d_list = g_list_append( d_list, ( gpointer )( dy ) );
}
*/
gaim_gtk_whiteboard_draw_brush_line( gtkwb->wb,
LastX, LastY,
x, y,
0, 5 );//gtkwb->brush_color, gtkwb->brush_size ); temp const proto ui ops?
// Set tracking variables
LastX = x;
LastY = y;
}
return( TRUE );
}
// ------------------------------------------------------------------------------------------------------
gboolean gaim_gtk_whiteboard_brush_up( GtkWidget *widget, GdkEventButton *event, gpointer data )
{
//g_print( "BRUSH_UP | %s\n", ds->who );
GaimGtkWhiteboard *gtkwb = ( GaimGtkWhiteboard* )( data );
GdkPixmap *pixmap = gtkwb->pixmap;
// GList *d_list = gtkwb->wb->draw_list;
if( ( BrushState != BRUSH_STATE_DOWN ) && ( BrushState != BRUSH_STATE_MOTION ) )
{
g_print( "***Bad brush state transition %d to UP\n", BrushState );
BrushState = BRUSH_STATE_UP;
return( FALSE );
}
BrushState = BRUSH_STATE_UP;
if( event->button == 1 && pixmap != NULL )
{
/* // If the brush was never moved, express two sets of two deltas
// That's a 'point,' but not for Yahoo!
//if( ( event->x == LastX ) && ( event->y == LastY ) )
if( MotionCount == 0 )
{
int index;
for( index = 0; index < 2; index++ )
{
int *x0 = NULL;
int *y0 = NULL;
x0 = g_new0( int, 1 );
y0 = g_new0( int, 1 );
d_list = g_list_append( d_list, ( gpointer )( x0 ) );
d_list = g_list_append( d_list, ( gpointer )( y0 ) );
}
}
//else
// MotionCount = 0;
//char* message = goodle_doodle_session_build_draw_string( drawList );
//yahoo_doodle_command_send_draw( ds->gc, ds->who, message );
*/
gaim_gtk_whiteboard_set_canvas_as_icon( gtkwb );
// The brush stroke is finished, clear the list for another one
// gaim_whiteboard_draw_list_destroy( d_list );
}
return( TRUE );
}
// ------------------------------------------------------------------------------------------------------
// void gaim_gtk_whiteboard_draw_brush_point( GtkWidget *widget, GaimGtkWhiteboard *gtkwb,
// int x, int y, int color, int size )
void gaim_gtk_whiteboard_draw_brush_point( GaimWhiteboard *wb, int x, int y, int color, int size )
{
//g_print( "goodle_doodle_session_draw_brush | %s\n", ds->who );
GaimGtkWhiteboard *gtkwb = wb->ui_data;
GtkWidget *widget = gtkwb->drawing_area;
GdkPixmap *pixmap = gtkwb->pixmap;
GdkRectangle update_rect;
update_rect.x = x - size / 2;
update_rect.y = y - size / 2;
update_rect.width = size;
update_rect.height = size;
// Interpret and convert color
GdkGC *gfx_con = gdk_gc_new( pixmap );
GdkColor col;
gaim_gtk_whiteboard_rgb24_to_rgb48( color, &col );
gdk_gc_set_rgb_fg_color( gfx_con, &col );
//gdk_gc_set_rgb_bg_color( gfx_con, &col );
// NOTE 5 is a size constant for now... this is because of how poorly the gdk_draw_arc draws small circles
if( size < 5 )
{
// Draw a rectangle/square
gdk_draw_rectangle( pixmap,
gfx_con,
TRUE,
update_rect.x, update_rect.y,
update_rect.width, update_rect.height );
}
else
{
// Draw a circle
gdk_draw_arc( pixmap,
gfx_con,
TRUE,
update_rect.x, update_rect.y,
update_rect.width, update_rect.height,
0, FULL_CIRCLE_DEGREES );
}
gtk_widget_queue_draw_area( widget,
update_rect.x, update_rect.y,
update_rect.width, update_rect.height );
gdk_gc_unref( gfx_con );
}
// ------------------------------------------------------------------------------------------------------
// Uses Bresenham's algorithm (as provided by Wikipedia)
// void gaim_gtk_whiteboard_draw_brush_line( GtkWidget *widget, GaimGtkWhiteboard *gtkwb,
// int x0, int y0, int x1, int y1, int color, int size )
void gaim_gtk_whiteboard_draw_brush_line( GaimWhiteboard *wb, int x0, int y0, int x1, int y1, int color, int size )
{
int temp;
int xstep;
int ystep;
gboolean steep = abs( y1 - y0 ) > abs( x1 - x0 );
if( steep )
{
temp = x0; x0 = y0; y0 = temp;
temp = x1; x1 = y1; y1 = temp;
}
int dx = abs( x1 - x0 );
int dy = abs( y1 - y0 );
int error = 0;
int derror = dy;
int x = x0;
int y = y0;
if( x0 < x1 )
xstep = 1;
else
xstep = -1;
if( y0 < y1 )
ystep = 1;
else
ystep = -1;
if( steep )
gaim_gtk_whiteboard_draw_brush_point( wb, y, x, color, size );
else
gaim_gtk_whiteboard_draw_brush_point( wb, x, y, color, size );
while( x != x1 )
{
x = x + xstep;
error = error + derror;
if( ( error * 2 ) >= dx )
{
y = y + ystep;
error = error - dx;
}
if( steep )
gaim_gtk_whiteboard_draw_brush_point( wb, y, x, color, size );
else
gaim_gtk_whiteboard_draw_brush_point( wb, x, y, color, size );
}
}
// ------------------------------------------------------------------------------------------------------
void gaim_gtk_whiteboard_clear( GaimWhiteboard *wb )
{
GaimGtkWhiteboard *gtkwb = wb->ui_data;
GdkPixmap *pixmap = gtkwb->pixmap;
GtkWidget *drawing_area = gtkwb->drawing_area;
gdk_draw_rectangle( pixmap,
drawing_area->style->white_gc,
TRUE,
0, 0,
drawing_area->allocation.width, drawing_area->allocation.height );
gtk_widget_queue_draw_area( drawing_area,
0, 0,
drawing_area->allocation.width, drawing_area->allocation.height );
}
// ------------------------------------------------------------------------------------------------------
void gaim_gtk_whiteboard_button_clear_press( GtkWidget *widget, gpointer data )
{
GaimGtkWhiteboard *gtkwb = ( GaimGtkWhiteboard* )( data );
// Proto specific? UI Ops?
//goodle_send_command_clear( ds->gc, ds->who );
gaim_gtk_whiteboard_clear( gtkwb->wb );
gaim_gtk_whiteboard_set_canvas_as_icon( gtkwb );
}
// ------------------------------------------------------------------------------------------------------
void gaim_gtk_whiteboard_set_canvas_as_icon( GaimGtkWhiteboard *gtkwb )
{
GdkPixbuf *pixbuf;
// Makes an icon from the whiteboard's canvas 'image'
pixbuf = gdk_pixbuf_get_from_drawable( NULL,
( GdkDrawable* )( gtkwb->pixmap ),
gdk_drawable_get_colormap( gtkwb->pixmap ),
0, 0,
0, 0,
gtkwb->width, gtkwb->height ); //Constants for protocol specific UI_Ops?
gtk_window_set_icon( ( GtkWindow* )( gtkwb->window ), pixbuf );
}
// ------------------------------------------------------------------------------------------------------
void gaim_gtk_whiteboard_rgb24_to_rgb48( int color_rgb, GdkColor *color )
{
color->red = ( color_rgb >> 8 ) | 0xFF;
color->green = ( color_rgb & 0xFF00 ) | 0xFF;
color->blue = ( ( color_rgb & 0xFF ) << 8 ) | 0xFF;
}
// ------------------------------------------------------------------------------------------------------