1 #include "../Common/Common.h"
2 #include "../../config.h"
5 #include "DasherControl.h"
7 #include "../DasherCore/Event.h"
8 #include "../DasherCore/WrapperFactory.h"
14 #include <gdk/gdkkeysyms.h>
18 // 'Private' methods (only used in this file)
19 extern "C" gint
key_release_event(GtkWidget
*widget
, GdkEventKey
*event
, gpointer user_data
);
20 extern "C" gboolean
button_press_event(GtkWidget
*widget
, GdkEventButton
*event
, gpointer data
);
21 extern "C" void realize_canvas(GtkWidget
*widget
, gpointer user_data
);
22 extern "C" gint
canvas_configure_event(GtkWidget
*widget
, GdkEventConfigure
*event
, gpointer data
);
23 extern "C" gint
key_press_event(GtkWidget
*widget
, GdkEventKey
*event
, gpointer data
);
24 extern "C" void canvas_destroy_event(GtkWidget
*pWidget
, gpointer pUserData
);
25 extern "C" gboolean
canvas_focus_event(GtkWidget
*widget
, GdkEventFocus
*event
, gpointer data
);
26 extern "C" gint
canvas_expose_event(GtkWidget
*widget
, GdkEventExpose
*event
, gpointer data
);
28 static bool g_iTimeoutID
= 0;
30 // CDasherControl class definitions
31 CDasherControl::CDasherControl(GtkVBox
*pVBox
, GtkDasherControl
*pDasherControl
) {
35 m_pDasherControl
= pDasherControl
;
36 m_pVBox
= GTK_WIDGET(pVBox
);
40 // m_pKeyboardHelper = new CKeyboardHelper(this);
41 // m_pKeyboardHelper->Grab(GetBoolParameter(BP_GLOBAL_KEYBOARD));
44 void CDasherControl::CreateLocalFactories() {
45 RegisterFactory(new CWrapperFactory(m_pEventHandler
, m_pSettingsStore
, new CDasherMouseInput(m_pEventHandler
, m_pSettingsStore
)));
46 RegisterFactory(new CWrapperFactory(m_pEventHandler
, m_pSettingsStore
, new CSocketInput(m_pEventHandler
, m_pSettingsStore
)));
47 RegisterFactory(new CWrapperFactory(m_pEventHandler
, m_pSettingsStore
, new CDasher1DMouseInput(m_pEventHandler
, m_pSettingsStore
)));
50 RegisterFactory(new CWrapperFactory(m_pEventHandler
, m_pSettingsStore
, new CDasherJoystickInput(m_pEventHandler
, m_pSettingsStore
, this)));
51 RegisterFactory(new CWrapperFactory(m_pEventHandler
, m_pSettingsStore
, new CDasherJoystickInputDiscrete(m_pEventHandler
, m_pSettingsStore
, this)));
54 // Create locally cached copies of the mouse input objects, as we
55 // need to pass coordinates to them from the timer callback
57 m_pMouseInput
= (CDasherMouseInput
*)GetModule(0);
60 m_p1DMouseInput
= (CDasher1DMouseInput
*)GetModule(2);
61 m_p1DMouseInput
->Ref();
65 void CDasherControl::SetupUI() {
66 m_pCanvas
= gtk_drawing_area_new();
67 GTK_WIDGET_SET_FLAGS(m_pCanvas
, GTK_CAN_FOCUS
);
68 gtk_widget_set_double_buffered(m_pCanvas
, false);
70 GtkWidget
*pFrame
= gtk_frame_new(NULL
);
71 gtk_frame_set_shadow_type(GTK_FRAME(pFrame
), GTK_SHADOW_IN
);
72 gtk_container_add(GTK_CONTAINER(pFrame
), m_pCanvas
);
74 gtk_box_pack_start(GTK_BOX(m_pVBox
), pFrame
, TRUE
, TRUE
, 0);
75 gtk_widget_show_all(GTK_WIDGET(m_pVBox
));
77 // Connect callbacks - note that we need to implement the callbacks
78 // as "C" style functions and pass this as user data so they can
81 g_signal_connect(m_pCanvas
, "button_press_event", G_CALLBACK(button_press_event
), this);
82 g_signal_connect(m_pCanvas
, "button_release_event", G_CALLBACK(button_press_event
), this);
83 g_signal_connect_after(m_pCanvas
, "realize", G_CALLBACK(realize_canvas
), this);
84 g_signal_connect(m_pCanvas
, "configure_event", G_CALLBACK(canvas_configure_event
), this);
85 g_signal_connect(m_pCanvas
, "destroy", G_CALLBACK(canvas_destroy_event
), this);
87 g_signal_connect(m_pCanvas
, "key-release-event", G_CALLBACK(key_release_event
), this);
88 g_signal_connect(m_pCanvas
, "key_press_event", G_CALLBACK(key_press_event
), this);
90 g_signal_connect(m_pCanvas
, "focus_in_event", G_CALLBACK(canvas_focus_event
), this);
91 g_signal_connect(m_pCanvas
, "expose_event", G_CALLBACK(canvas_expose_event
), this);
93 // Create the Pango cache
95 // TODO: Use system defaults?
96 if(GetStringParameter(SP_DASHER_FONT
) == "")
97 SetStringParameter(SP_DASHER_FONT
, "Sans 10");
99 m_pPangoCache
= new CPangoCache(GetStringParameter(SP_DASHER_FONT
));
104 void CDasherControl::SetupPaths() {
107 char *system_data_dir
;
109 home_dir
= getenv("HOME");
110 user_data_dir
= new char[strlen(home_dir
) + 10];
111 sprintf(user_data_dir
, "%s/.dasher/", home_dir
);
113 mkdir(user_data_dir
, S_IRUSR
| S_IWUSR
| S_IXUSR
| S_IRGRP
| S_IXGRP
| S_IROTH
| S_IXOTH
);
115 // PROGDATA is provided by the makefile
116 system_data_dir
= PROGDATA
"/";
118 SetStringParameter(SP_SYSTEM_LOC
, system_data_dir
);
119 SetStringParameter(SP_USER_LOC
, user_data_dir
);
122 void CDasherControl::CreateSettingsStore() {
123 m_pSettingsStore
= new CGnomeSettingsStore(m_pEventHandler
);
126 void CDasherControl::ScanAlphabetFiles(std::vector
<std::string
> &vFileList
) {
128 G_CONST_RETURN gchar
*filename
;
129 GPatternSpec
*alphabetglob
;
130 alphabetglob
= g_pattern_spec_new("alphabet*xml");
132 directory
= g_dir_open(GetStringParameter(SP_SYSTEM_LOC
).c_str(), 0, NULL
);
135 while((filename
= g_dir_read_name(directory
))) {
136 if(g_pattern_match_string(alphabetglob
, filename
))
137 vFileList
.push_back(filename
);
139 g_dir_close(directory
);
142 directory
= g_dir_open(GetStringParameter(SP_USER_LOC
).c_str(), 0, NULL
);
145 while((filename
= g_dir_read_name(directory
))) {
146 if(g_pattern_match_string(alphabetglob
, filename
))
147 vFileList
.push_back(filename
);
149 g_dir_close(directory
);
152 g_pattern_spec_free(alphabetglob
);
155 void CDasherControl::ScanColourFiles(std::vector
<std::string
> &vFileList
) {
157 G_CONST_RETURN gchar
*filename
;
159 GPatternSpec
*colourglob
;
160 colourglob
= g_pattern_spec_new("colour*xml");
162 directory
= g_dir_open(GetStringParameter(SP_SYSTEM_LOC
).c_str(), 0, NULL
);
165 while((filename
= g_dir_read_name(directory
))) {
166 if(g_pattern_match_string(colourglob
, filename
))
167 vFileList
.push_back(filename
);
169 g_dir_close(directory
);
172 directory
= g_dir_open(GetStringParameter(SP_USER_LOC
).c_str(), 0, NULL
);
175 while((filename
= g_dir_read_name(directory
))) {
176 if(g_pattern_match_string(colourglob
, filename
))
177 vFileList
.push_back(filename
);
179 g_dir_close(directory
);
182 g_pattern_spec_free(colourglob
);
185 CDasherControl::~CDasherControl() {
187 m_pMouseInput
->Unref();
188 m_pMouseInput
= NULL
;
191 if(m_p1DMouseInput
) {
192 m_p1DMouseInput
->Unref();
193 m_p1DMouseInput
= NULL
;
197 delete m_pPangoCache
;
198 m_pPangoCache
= NULL
;
201 // if(m_pKeyboardHelper) {
202 // delete m_pKeyboardHelper;
203 // m_pKeyboardHelper = 0;
207 bool CDasherControl::FocusEvent(GtkWidget
*pWidget
, GdkEventFocus
*pEvent
) {
208 if((pEvent
->type
== GDK_FOCUS_CHANGE
) && (pEvent
->in
)) {
209 GdkEventFocus
*focusEvent
= (GdkEventFocus
*) g_malloc(sizeof(GdkEventFocus
));
210 gboolean
*returnType
;
212 focusEvent
->type
= GDK_FOCUS_CHANGE
;
213 focusEvent
->window
= (GdkWindow
*) m_pDasherControl
;
214 focusEvent
->send_event
= FALSE
;
215 focusEvent
->in
= TRUE
;
217 g_signal_emit_by_name(GTK_OBJECT(m_pDasherControl
), "focus_in_event", GTK_WIDGET(m_pDasherControl
), focusEvent
, NULL
, &returnType
);
222 void CDasherControl::SetFocus() {
223 gtk_widget_grab_focus(m_pCanvas
);
226 GArray
*CDasherControl::GetAllowedValues(int iParameter
) {
227 // Glib version of the STL based core function
229 GArray
*pRetVal(g_array_new(false, false, sizeof(gchar
*)));
231 std::vector
< std::string
> vList
;
232 GetPermittedValues(iParameter
, vList
);
234 for(std::vector
< std::string
>::iterator
it(vList
.begin()); it
!= vList
.end(); ++it
) {
235 // For internal glib reasons we need to make a variable and then
236 // pass - we can't use the iterator directly
237 const char *pTemp(it
->c_str());
238 char *pTempNew
= new char[strlen(pTemp
) + 1];
239 strcpy(pTempNew
, pTemp
);
240 g_array_append_val(pRetVal
, pTempNew
);
246 void CDasherControl::RealizeCanvas(GtkWidget
*pWidget
) {
247 // TODO: Pointless function - call directly from C callback.
251 void CDasherControl::StartTimer() {
252 // Start the timer loops as everything is set up
253 // Aim for 20 frames per second
255 if(g_iTimeoutID
== 0) {
256 g_iTimeoutID
= g_timeout_add_full(G_PRIORITY_DEFAULT_IDLE
, 50, timer_callback
, this, NULL
);
257 // TODO: Reimplement this (or at least reimplement some kind of status reporting)
258 //g_timeout_add_full(G_PRIORITY_DEFAULT_IDLE, 5000, long_timer_callback, this, NULL);
262 void CDasherControl::ShutdownTimer() {
263 // TODO: Figure out how to implement this - at the moment it's done
264 // through a return value from the timer callback, but it would be
265 // nicer to prevent any further calls as soon as the shutdown signal
266 // has been receieved.
269 int CDasherControl::CanvasConfigureEvent() {
271 if(m_pScreen
!= NULL
)
274 m_pScreen
= new CCanvas(m_pCanvas
, m_pPangoCache
);
275 ChangeScreen(m_pScreen
);
280 void CDasherControl::ExternalEventHandler(Dasher::CEvent
*pEvent
) {
281 // Convert events coming from the core to Glib signals.
283 if(pEvent
->m_iEventType
== EV_PARAM_NOTIFY
) {
284 Dasher::CParameterNotificationEvent
* pEvt(static_cast < Dasher::CParameterNotificationEvent
* >(pEvent
));
285 HandleParameterNotification(pEvt
->m_iParameter
);
286 g_signal_emit_by_name(GTK_OBJECT(m_pDasherControl
), "dasher_changed", pEvt
->m_iParameter
);
288 else if(pEvent
->m_iEventType
== EV_EDIT
) {
289 CEditEvent
*pEditEvent(static_cast < CEditEvent
* >(pEvent
));
291 if(pEditEvent
->m_iEditType
== 1) {
293 g_signal_emit_by_name(GTK_OBJECT(m_pDasherControl
), "dasher_edit_insert", pEditEvent
->m_sText
.c_str(), pEditEvent
->m_iOffset
);
295 else if(pEditEvent
->m_iEditType
== 2) {
297 g_signal_emit_by_name(GTK_OBJECT(m_pDasherControl
), "dasher_edit_delete", pEditEvent
->m_sText
.c_str(), pEditEvent
->m_iOffset
);
299 else if(pEditEvent
->m_iEditType
== 10) {
300 g_signal_emit_by_name(GTK_OBJECT(m_pDasherControl
), "dasher_edit_convert");
302 else if(pEditEvent
->m_iEditType
== 11) {
303 g_signal_emit_by_name(GTK_OBJECT(m_pDasherControl
), "dasher_edit_protect");
306 else if(pEvent
->m_iEventType
== EV_EDIT_CONTEXT
) {
307 CEditContextEvent
*pEditContextEvent(static_cast < CEditContextEvent
* >(pEvent
));
308 g_signal_emit_by_name(GTK_OBJECT(m_pDasherControl
), "dasher_context_request", pEditContextEvent
->m_iOffset
, pEditContextEvent
->m_iLength
);
310 else if(pEvent
->m_iEventType
== EV_START
) {
311 g_signal_emit_by_name(GTK_OBJECT(m_pDasherControl
), "dasher_start");
313 else if(pEvent
->m_iEventType
== EV_STOP
) {
314 g_signal_emit_by_name(GTK_OBJECT(m_pDasherControl
), "dasher_stop");
316 else if(pEvent
->m_iEventType
== EV_CONTROL
) {
317 CControlEvent
*pControlEvent(static_cast < CControlEvent
* >(pEvent
));
318 g_signal_emit_by_name(GTK_OBJECT(m_pDasherControl
), "dasher_control", pControlEvent
->m_iID
);
320 else if(pEvent
->m_iEventType
== EV_LOCK
) {
321 CLockEvent
*pLockEvent(static_cast<CLockEvent
*>(pEvent
));
322 DasherLockInfo sInfo
;
323 sInfo
.szMessage
= pLockEvent
->m_strMessage
.c_str();
324 sInfo
.bLock
= pLockEvent
->m_bLock
;
325 sInfo
.iPercent
= pLockEvent
->m_iPercent
;
327 g_signal_emit_by_name(GTK_OBJECT(m_pDasherControl
), "dasher_lock_info", &sInfo
);
329 else if(pEvent
->m_iEventType
== EV_MESSAGE
) {
330 CMessageEvent
*pMessageEvent(static_cast<CMessageEvent
*>(pEvent
));
331 DasherMessageInfo sInfo
;
332 sInfo
.szMessage
= pMessageEvent
->m_strMessage
.c_str();
333 sInfo
.iID
= pMessageEvent
->m_iID
;
334 sInfo
.iType
= pMessageEvent
->m_iType
;
336 g_signal_emit_by_name(GTK_OBJECT(m_pDasherControl
), "dasher_message", &sInfo
);
338 else if(pEvent
->m_iEventType
== EV_COMMAND
) {
339 CCommandEvent
*pCommandEvent(static_cast<CCommandEvent
*>(pEvent
));
340 g_signal_emit_by_name(GTK_OBJECT(m_pDasherControl
), "dasher_command", pCommandEvent
->m_strCommand
.c_str());
344 void CDasherControl::WriteTrainFile(const std::string
&strNewText
) {
345 if(strNewText
.length() == 0)
348 std::string
strFilename(GetStringParameter(SP_USER_LOC
) + GetStringParameter(SP_TRAIN_FILE
));
350 int fd
=open(strFilename
.c_str(),O_CREAT
|O_WRONLY
|O_APPEND
,S_IRUSR
|S_IWUSR
);
351 write(fd
,strNewText
.c_str(),strNewText
.length());
355 // TODO: Sort these methods out
356 void CDasherControl::ExternalKeyDown(int iKeyVal
) {
357 // if(m_pKeyboardHelper) {
358 // int iButtonID(m_pKeyboardHelper->ConvertKeycode(iKeyVal));
360 // if(iButtonID != -1)
361 // KeyDown(get_time(), iButtonID);
363 KeyDown(get_time(), iKeyVal
);
366 void CDasherControl::ExternalKeyUp(int iKeyVal
) {
367 // if(m_pKeyboardHelper) {
368 // int iButtonID(m_pKeyboardHelper->ConvertKeycode(iKeyVal));
370 // if(iButtonID != -1)
371 // KeyUp(get_time(), iButtonID);
373 KeyUp(get_time(), iKeyVal
);
376 void CDasherControl::HandleParameterNotification(int iParameter
) {
380 m_pPangoCache
->ChangeFont(GetStringParameter(SP_DASHER_FONT
));
384 case BP_GLOBAL_KEYBOARD
:
386 // if(m_pKeyboardHelper)
387 // m_pKeyboardHelper->Grab(GetBoolParameter(BP_GLOBAL_KEYBOARD));
392 int CDasherControl::TimerEvent() {
395 gdk_window_get_pointer(m_pCanvas
->window
, &x
, &y
, NULL
);
396 m_pMouseInput
->SetCoordinates(x
, y
);
398 gdk_window_get_pointer(gdk_get_default_root_window(), &x
, &y
, NULL
);
403 gdk_drawable_get_size(gdk_get_default_root_window(), &iRootWidth
, &iRootHeight
);
405 if(GetLongParameter(LP_YSCALE
) < 10)
406 SetLongParameter(LP_YSCALE
, 10);
408 y
= (y
- iRootHeight
/ 2);
410 m_p1DMouseInput
->SetCoordinates(y
, GetLongParameter(LP_YSCALE
));
412 NewFrame(get_time(), false);
414 // Update our UserLog object about the current mouse position
415 CUserLogBase
* pUserLog
= GetUserLogPtr();
416 if (pUserLog
!= NULL
) {
417 // We want current canvas and window coordinates so normalization
418 // is done properly with respect to the canvas.
419 GdkRectangle sWindowRect
;
420 GdkRectangle sCanvasRect
;
422 gdk_window_get_frame_extents(m_pCanvas
->window
, &sWindowRect
);
424 pUserLog
->AddWindowSize(sWindowRect
.y
,
426 sWindowRect
.y
+ sWindowRect
.height
,
427 sWindowRect
.x
+ sWindowRect
.width
);
429 if (m_pScreen
!= NULL
) {
430 if (m_pScreen
->GetCanvasSize(&sCanvasRect
))
431 pUserLog
->AddCanvasSize(sCanvasRect
.y
,
433 sCanvasRect
.y
+ sCanvasRect
.height
,
434 sCanvasRect
.x
+ sCanvasRect
.width
);
439 gdk_window_get_pointer(NULL
, &iMouseX
, &iMouseY
, NULL
);
441 pUserLog
->AddMouseLocationNormalized(iMouseX
, iMouseY
, true, GetNats());
446 // See CVS for code which used to be here
449 int CDasherControl::LongTimerEvent() {
450 // std::cout << "Framerate: " << GetFramerate() << std::endl;
451 // std::cout << "Render count: " << GetRenderCount() << std::endl;
455 gboolean
CDasherControl::ExposeEvent() {
456 NewFrame(get_time(), true);
460 gboolean
CDasherControl::ButtonPressEvent(GdkEventButton
*event
) {
462 // Take the focus if we click on the canvas
464 // GdkEventFocus *focusEvent = (GdkEventFocus *) g_malloc(sizeof(GdkEventFocus));
465 // gboolean *returnType;
467 // focusEvent->type = GDK_FOCUS_CHANGE;
468 // focusEvent->window = (GdkWindow *) m_pCanvas;
469 // focusEvent->send_event = FALSE;
470 // focusEvent->in = TRUE;
472 // gtk_widget_grab_focus(GTK_WIDGET(m_pCanvas));
473 // g_signal_emit_by_name(GTK_OBJECT(m_pCanvas), "focus_in_event", GTK_WIDGET(m_pCanvas), focusEvent, NULL, &returnType);
475 // No - don't take the focus - give it to the text area instead
477 if(event
->type
== GDK_BUTTON_PRESS
)
478 HandleClickDown(get_time(), (int)event
->x
, (int)event
->y
);
479 else if(event
->type
== GDK_BUTTON_RELEASE
)
480 HandleClickUp(get_time(), (int)event
->x
, (int)event
->y
);
485 gint
CDasherControl::KeyReleaseEvent(GdkEventKey
*event
) {
486 // TODO: This is seriously flawed - the semantics of of X11 Keyboard
487 // events mean the there's no guarantee that key up/down events will
488 // be received in pairs.
490 if((event
->keyval
== GDK_Shift_L
) || (event
->keyval
== GDK_Shift_R
)) {
491 // if(event->state & GDK_CONTROL_MASK)
492 // SetLongParameter(LP_BOOSTFACTOR, 25);
494 // SetLongParameter(LP_BOOSTFACTOR, 100);
496 else if((event
->keyval
== GDK_Control_L
) || (event
->keyval
== GDK_Control_R
)) {
497 // if(event->state & GDK_SHIFT_MASK)
498 // SetLongParameter(LP_BOOSTFACTOR, 175);
500 // SetLongParameter(LP_BOOSTFACTOR, 100);
503 // if(m_pKeyboardHelper) {
504 // int iKeyVal(m_pKeyboardHelper->ConvertKeycode(event->keyval));
507 // KeyUp(get_time(), iKeyVal);
514 gint
CDasherControl::KeyPressEvent(GdkEventKey
*event
) {
515 // if((event->keyval == GDK_Shift_L) || (event->keyval == GDK_Shift_R))
516 // SetLongParameter(LP_BOOSTFACTOR, 175);
517 // else if((event->keyval == GDK_Control_L) || (event->keyval == GDK_Control_R))
518 // SetLongParameter(LP_BOOSTFACTOR, 25);
520 // if(m_pKeyboardHelper) {
521 // int iKeyVal(m_pKeyboardHelper->ConvertKeycode(event->keyval));
524 // KeyDown(get_time(), iKeyVal);
530 void CDasherControl::CanvasDestroyEvent() {
533 if(m_pScreen
!= NULL
) {
539 // Tell the logging object that a new user trial is starting.
540 void CDasherControl::UserLogNewTrial()
542 CUserLogBase
* pUserLog
= GetUserLogPtr();
543 if (pUserLog
!= NULL
) {
544 pUserLog
->NewTrial();
548 int CDasherControl::GetFileSize(const std::string
&strFileName
) {
549 struct stat sStatInfo
;
551 if(!stat(strFileName
.c_str(), &sStatInfo
))
552 return sStatInfo
.st_size
;
557 // "C" style callbacks - these are here just because it's not possible
558 // (or at least not easy) to connect a callback directly to a C++
559 // method, so we pass a pointer to th object in the user_data field
560 // and use a wrapper function. Please do not put any functional code
563 extern "C" void realize_canvas(GtkWidget
*widget
, gpointer user_data
) {
564 static_cast < CDasherControl
* >(user_data
)->RealizeCanvas(widget
);
568 extern "C" gboolean
button_press_event(GtkWidget
*widget
, GdkEventButton
*event
, gpointer data
) {
569 return static_cast < CDasherControl
* >(data
)->ButtonPressEvent(event
);
572 extern "C" gint
key_press_event(GtkWidget
*widget
, GdkEventKey
*event
, gpointer data
) {
573 return static_cast < CDasherControl
* >(data
)->KeyPressEvent(event
);
576 extern "C" gint
canvas_configure_event(GtkWidget
*widget
, GdkEventConfigure
*event
, gpointer data
) {
577 return static_cast < CDasherControl
* >(data
)->CanvasConfigureEvent();
580 extern "C" void canvas_destroy_event(GtkWidget
*pWidget
, gpointer pUserData
) {
581 static_cast<CDasherControl
*>(pUserData
)->CanvasDestroyEvent();
584 extern "C" gint
key_release_event(GtkWidget
*pWidget
, GdkEventKey
*event
, gpointer pUserData
) {
585 return static_cast<CDasherControl
*>(pUserData
)->KeyReleaseEvent(event
);
588 extern "C" gboolean
canvas_focus_event(GtkWidget
*widget
, GdkEventFocus
*event
, gpointer data
) {
589 return static_cast < CDasherControl
* >(data
)->FocusEvent(widget
, event
);
592 extern "C" gint
canvas_expose_event(GtkWidget
*widget
, GdkEventExpose
*event
, gpointer data
) {
593 return ((CDasherControl
*)data
)->ExposeEvent();