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" void speed_changed(GtkHScale *hscale, gpointer user_data);
23 extern "C" gint
canvas_configure_event(GtkWidget
*widget
, GdkEventConfigure
*event
, gpointer data
);
24 extern "C" gint
key_press_event(GtkWidget
*widget
, GdkEventKey
*event
, gpointer data
);
25 extern "C" void canvas_destroy_event(GtkWidget
*pWidget
, gpointer pUserData
);
26 //extern "C" void alphabet_combo_changed(GtkWidget *pWidget, gpointer pUserData);
27 extern "C" gboolean
canvas_focus_event(GtkWidget
*widget
, GdkEventFocus
*event
, gpointer data
);
29 // CDasherControl class definitions
30 CDasherControl::CDasherControl(GtkVBox
*pVBox
, GtkDasherControl
*pDasherControl
) {
31 m_pDasherControl
= pDasherControl
;
32 m_pVBox
= GTK_WIDGET(pVBox
);
38 // Start the dasher model
40 Start(); // FIXME - should we hold off on this until later?
42 // Create input device objects
43 // (We create the SocketInput object now even if socket input is not enabled, because
44 // we are not allowed to create it in response to a parameter update event later, because
45 // that would mean registering a new event listener during the processing of an event.
47 RegisterFactory(new CWrapperFactory(m_pEventHandler
, m_pSettingsStore
, new CDasherMouseInput(m_pEventHandler
, m_pSettingsStore
)));
48 RegisterFactory(new CWrapperFactory(m_pEventHandler
, m_pSettingsStore
, new CSocketInput(m_pEventHandler
, m_pSettingsStore
)));
50 m_pKeyboardHelper
= new CKeyboardHelper
;
51 m_pKeyboardHelper
->Grab(GetBoolParameter(BP_GLOBAL_KEYBOARD
));
55 // m_pSocketInput = (CSocketInput *)GetModule(1);
56 // m_pSocketInput->Ref();
58 m_pMouseInput
= (CDasherMouseInput
*)GetModule(0);
61 // Create a pango cache
63 m_pPangoCache
= new CPangoCache(GetStringParameter(SP_DASHER_FONT
));
65 // Don't create the screen until we've been realised.
71 void CDasherControl::SetupUI() {
72 m_pCanvas
= gtk_drawing_area_new();
73 GTK_WIDGET_SET_FLAGS(m_pCanvas
, GTK_CAN_FOCUS
);
74 gtk_widget_set_double_buffered(m_pCanvas
, false);
76 GtkWidget
*pFrame
= gtk_frame_new(NULL
);
77 gtk_frame_set_shadow_type(GTK_FRAME(pFrame
), GTK_SHADOW_IN
);
79 // m_pSpeedFrame = gtk_frame_new("Speed:");
81 // m_pSpeedHScale = gtk_hscale_new_with_range(0.1, 8.0, 0.1);
82 // gtk_scale_set_digits( GTK_SCALE(m_pSpeedHScale), 1 );
84 // gtk_container_add(GTK_CONTAINER(m_pSpeedFrame), m_pSpeedHScale);
85 gtk_container_add(GTK_CONTAINER(pFrame
), m_pCanvas
);
88 // m_pStatusBar = gtk_hbox_new(false, 2);
90 // m_pSpin = gtk_spin_button_new_with_range(0.1, 8.0, 0.1);
91 // m_pCombo = gtk_combo_box_new_text();
93 // gtk_widget_set_size_request(m_pCombo, 256, -1);
95 // m_pStatusLabel = gtk_label_new("Characters/min: --");
96 // gtk_label_set_justify(GTK_LABEL(m_pStatusLabel), GTK_JUSTIFY_RIGHT);
98 // gtk_box_pack_start(GTK_BOX(m_pStatusBar), gtk_label_new("Speed:"), 0, 0, 0);
99 // // gtk_box_pack_start(GTK_BOX(m_pStatusBar), m_pSpin, 0, 0, 0);
100 // // gtk_box_pack_start(GTK_BOX(m_pStatusBar), m_pCombo, 0, 0, 0);
101 // // gtk_box_pack_start(GTK_BOX(m_pStatusBar), m_pStatusLabel, TRUE, TRUE, 0);
103 gtk_box_pack_start(GTK_BOX(m_pVBox
), pFrame
, TRUE
, TRUE
, 0);
104 // // gtk_box_pack_start(GTK_BOX(m_pVBox), m_pSpeedFrame, FALSE, FALSE, 0);
105 // gtk_box_pack_start(GTK_BOX(m_pVBox), m_pStatusBar, FALSE, FALSE, 0);
107 gtk_widget_show_all(GTK_WIDGET(m_pVBox
));
109 // if(!GetBoolParameter(BP_SHOW_SLIDER))
110 // gtk_widget_hide(m_pStatusBar);
112 // Connect callbacks - note that we need to implement the callbacks
113 // as "C" style functions and pass this as user data so they can
116 g_signal_connect(m_pCanvas
, "button_press_event", G_CALLBACK(button_press_event
), this);
117 g_signal_connect(m_pCanvas
, "button_release_event", G_CALLBACK(button_press_event
), this);
118 g_signal_connect_after(m_pCanvas
, "realize", G_CALLBACK(realize_canvas
), this);
119 g_signal_connect(m_pCanvas
, "configure_event", G_CALLBACK(canvas_configure_event
), this);
120 g_signal_connect(m_pCanvas
, "destroy", G_CALLBACK(canvas_destroy_event
), this);
122 // We'll use the same call back for keyboard events from the canvas
123 // and slider - maybe this isn't the right thing to do long term
125 g_signal_connect(m_pCanvas
, "key-release-event", G_CALLBACK(key_release_event
), this);
126 g_signal_connect(m_pCanvas
, "key_press_event", G_CALLBACK(key_press_event
), this);
128 g_signal_connect(m_pCanvas
, "focus_in_event", G_CALLBACK(canvas_focus_event
), this);
131 void CDasherControl::SetupPaths() {
134 char *system_data_dir
;
136 home_dir
= getenv("HOME");
137 user_data_dir
= new char[strlen(home_dir
) + 10];
138 sprintf(user_data_dir
, "%s/.dasher/", home_dir
);
140 mkdir(user_data_dir
, S_IRUSR
| S_IWUSR
| S_IXUSR
| S_IRGRP
| S_IXGRP
| S_IROTH
| S_IXOTH
);
142 // PROGDATA is provided by the makefile
143 system_data_dir
= PROGDATA
"/";
145 SetStringParameter(SP_SYSTEM_LOC
, system_data_dir
);
146 SetStringParameter(SP_USER_LOC
, user_data_dir
);
149 void CDasherControl::CreateSettingsStore() {
150 m_pSettingsStore
= new CGnomeSettingsStore(m_pEventHandler
);
153 void CDasherControl::ScanAlphabetFiles(std::vector
<std::string
> &vFileList
) {
155 G_CONST_RETURN gchar
*filename
;
156 GPatternSpec
*alphabetglob
;
157 alphabetglob
= g_pattern_spec_new("alphabet*xml");
159 directory
= g_dir_open(GetStringParameter(SP_SYSTEM_LOC
).c_str(), 0, NULL
);
162 while((filename
= g_dir_read_name(directory
))) {
163 if(alphabet_filter(filename
, alphabetglob
)) {
164 vFileList
.push_back(filename
);
167 g_dir_close(directory
);
170 directory
= g_dir_open(GetStringParameter(SP_USER_LOC
).c_str(), 0, NULL
);
173 while((filename
= g_dir_read_name(directory
))) {
174 if(alphabet_filter(filename
, alphabetglob
)) {
175 vFileList
.push_back(filename
);
178 g_dir_close(directory
);
180 // FIXME - need to delete glob?
183 void CDasherControl::ScanColourFiles(std::vector
<std::string
> &vFileList
) {
185 G_CONST_RETURN gchar
*filename
;
187 GPatternSpec
*colourglob
;
188 colourglob
= g_pattern_spec_new("colour*xml");
190 directory
= g_dir_open(GetStringParameter(SP_SYSTEM_LOC
).c_str(), 0, NULL
);
193 while((filename
= g_dir_read_name(directory
))) {
194 if(colour_filter(filename
, colourglob
)) {
195 vFileList
.push_back(filename
);
198 g_dir_close(directory
);
201 directory
= g_dir_open(GetStringParameter(SP_USER_LOC
).c_str(), 0, NULL
);
204 while((filename
= g_dir_read_name(directory
))) {
205 if(colour_filter(filename
, colourglob
)) {
206 vFileList
.push_back(filename
);
209 g_dir_close(directory
);
212 // FIXME - need to delete glob?
215 CDasherControl::~CDasherControl() {
217 WriteTrainFileFull();
219 // Delete the input devices
221 if(m_pMouseInput
!= NULL
) {
222 m_pMouseInput
->Unref();
223 m_pMouseInput
= NULL
;
226 // if(m_pSocketInput != NULL) {
227 // m_pSocketInput->Unref();
228 // m_pSocketInput = NULL;
231 if(m_pPangoCache
!= NULL
) {
232 delete m_pPangoCache
;
233 m_pPangoCache
= NULL
;
236 if(m_pKeyboardHelper
) {
237 delete m_pKeyboardHelper
;
238 m_pKeyboardHelper
= 0;
242 bool CDasherControl::FocusEvent(GtkWidget
*pWidget
, GdkEventFocus
*pEvent
) {
243 if((pEvent
->type
== GDK_FOCUS_CHANGE
) && (pEvent
->in
)) {
244 GdkEventFocus
*focusEvent
= (GdkEventFocus
*) g_malloc(sizeof(GdkEventFocus
));
245 gboolean
*returnType
;
247 focusEvent
->type
= GDK_FOCUS_CHANGE
;
248 focusEvent
->window
= (GdkWindow
*) m_pDasherControl
;
249 focusEvent
->send_event
= FALSE
;
250 focusEvent
->in
= TRUE
;
252 g_signal_emit_by_name(GTK_OBJECT(m_pDasherControl
), "focus_in_event", GTK_WIDGET(m_pDasherControl
), focusEvent
, NULL
, &returnType
);
257 void CDasherControl::SetFocus() {
258 gtk_widget_grab_focus(m_pCanvas
);
261 GArray
*CDasherControl::GetAllowedValues(int iParameter
) {
263 // FIXME - this should really be implemented in DasherInterface in
264 // place of GetAlphabets and GetColours
266 GArray
*pRetVal(g_array_new(false, false, sizeof(gchar
*)));
268 std::vector
< std::string
> vList
;
269 GetPermittedValues(iParameter
, vList
);
271 for(std::vector
< std::string
>::iterator
it(vList
.begin()); it
!= vList
.end(); ++it
) {
272 // For internal glib reasons we need to make a variable and then
273 // pass - we can't use the iterator directly
274 const char *pTemp(it
->c_str());
275 char *pTempNew
= new char[strlen(pTemp
) + 1];
276 strcpy(pTempNew
, pTemp
);
277 g_array_append_val(pRetVal
, pTempNew
);
283 void CDasherControl::RealizeCanvas() {
284 // Start the timer loops as everything is set up
285 // Aim for 20 frames per second
287 g_timeout_add_full(G_PRIORITY_DEFAULT_IDLE
, 50, timer_callback
, this, NULL
);
290 int CDasherControl::CanvasConfigureEvent() {
292 if(m_pScreen
!= NULL
)
295 m_pScreen
= new CCanvas(m_pCanvas
, m_pPangoCache
);
296 ChangeScreen(m_pScreen
);
301 void CDasherControl::ExternalEventHandler(Dasher::CEvent
*pEvent
) {
302 // Pass events outside
303 if(pEvent
->m_iEventType
== 1) {
304 Dasher::CParameterNotificationEvent
* pEvt(static_cast < Dasher::CParameterNotificationEvent
* >(pEvent
));
305 HandleParameterNotification(pEvt
->m_iParameter
);
307 // TODO: Horrible - just keep events here
308 else if((pEvent
->m_iEventType
>= 2) && (pEvent
->m_iEventType
<= 8)) {
314 void CDasherControl::WriteTrainFile(const std::string
&strNewText
) {
315 if(strNewText
.length() == 0)
318 std::string
strFilename(GetStringParameter(SP_USER_LOC
) + GetStringParameter(SP_TRAIN_FILE
));
320 int fd
=open(strFilename
.c_str(),O_CREAT
|O_WRONLY
|O_APPEND
,S_IRUSR
|S_IWUSR
);
321 write(fd
,strNewText
.c_str(),strNewText
.length());
325 void CDasherControl::ExternalKeyDown(int iKeyVal
) {
326 if(m_pKeyboardHelper
) {
327 int iButtonID(m_pKeyboardHelper
->ConvertKeycode(iKeyVal
));
330 KeyDown(get_time(), iButtonID
);
334 void CDasherControl::ExternalKeyUp(int iKeyVal
) {
335 if(m_pKeyboardHelper
) {
336 int iButtonID(m_pKeyboardHelper
->ConvertKeycode(iKeyVal
));
339 KeyUp(get_time(), iButtonID
);
343 void CDasherControl::HandleParameterNotification(int iParameter
) {
347 m_pPangoCache
->ChangeFont(GetStringParameter(SP_DASHER_FONT
));
350 case BP_GLOBAL_KEYBOARD
:
351 if(m_pKeyboardHelper
)
352 m_pKeyboardHelper
->Grab(GetBoolParameter(BP_GLOBAL_KEYBOARD
));
356 // Emit a dasher_changed signal to notify the application about changes.
357 g_signal_emit_by_name(GTK_OBJECT(m_pDasherControl
), "dasher_changed", iParameter
);
361 void CDasherControl::HandleEvent(CEvent
*pEvent
) {
362 if(pEvent
->m_iEventType
== 2) {
363 CEditEvent
*pEditEvent(static_cast < CEditEvent
* >(pEvent
));
365 if(pEditEvent
->m_iEditType
== 1) {
367 g_signal_emit_by_name(GTK_OBJECT(m_pDasherControl
), "dasher_edit_insert", pEditEvent
->m_sText
.c_str());
369 else if(pEditEvent
->m_iEditType
== 2) {
371 g_signal_emit_by_name(GTK_OBJECT(m_pDasherControl
), "dasher_edit_delete", pEditEvent
->m_sText
.c_str());
373 else if(pEditEvent
->m_iEditType
== 10) {
374 g_signal_emit_by_name(GTK_OBJECT(m_pDasherControl
), "dasher_edit_convert");
376 else if(pEditEvent
->m_iEditType
== 11) {
377 g_signal_emit_by_name(GTK_OBJECT(m_pDasherControl
), "dasher_edit_protect");
380 else if(pEvent
->m_iEventType
== 3) {
381 CEditContextEvent
*pEditContextEvent(static_cast < CEditContextEvent
* >(pEvent
));
382 g_signal_emit_by_name(GTK_OBJECT(m_pDasherControl
), "dasher_context_request", pEditContextEvent
->m_iMaxLength
);
384 else if(pEvent
->m_iEventType
== 4) {
385 g_signal_emit_by_name(GTK_OBJECT(m_pDasherControl
), "dasher_start");
387 else if(pEvent
->m_iEventType
== 5) {
388 g_signal_emit_by_name(GTK_OBJECT(m_pDasherControl
), "dasher_stop");
390 else if(pEvent
->m_iEventType
== 6) {
391 CControlEvent
*pControlEvent(static_cast < CControlEvent
* >(pEvent
));
392 g_signal_emit_by_name(GTK_OBJECT(m_pDasherControl
), "dasher_control", pControlEvent
->m_iID
);
394 else if(pEvent
->m_iEventType
== 7) {
395 CLockEvent
*pLockEvent(static_cast<CLockEvent
*>(pEvent
));
396 DasherLockInfo sInfo
;
397 sInfo
.szMessage
= pLockEvent
->m_strMessage
.c_str();
398 sInfo
.bLock
= pLockEvent
->m_bLock
;
399 sInfo
.iPercent
= pLockEvent
->m_iPercent
;
401 g_signal_emit_by_name(GTK_OBJECT(m_pDasherControl
), "dasher_lock_info", &sInfo
);
403 else if(pEvent
->m_iEventType
== 8) {
404 CMessageEvent
*pMessageEvent(static_cast<CMessageEvent
*>(pEvent
));
405 DasherMessageInfo sInfo
;
406 sInfo
.szMessage
= pMessageEvent
->m_strMessage
.c_str();
407 sInfo
.iID
= pMessageEvent
->m_iID
;
408 sInfo
.iType
= pMessageEvent
->m_iType
;
410 g_signal_emit_by_name(GTK_OBJECT(m_pDasherControl
), "dasher_message", &sInfo
);
414 int CDasherControl::TimerEvent() {
417 gdk_window_get_pointer(m_pCanvas
->window
, &x
, &y
, NULL
);
418 m_pMouseInput
->SetCoordinates(x
, y
);
420 NewFrame(get_time());
422 // Update our UserLog object about the current mouse position
423 CUserLog
* pUserLog
= GetUserLogPtr();
424 if (pUserLog
!= NULL
) {
425 // We want current canvas and window coordinates so normalization
426 // is done properly with respect to the canvas.
427 GdkRectangle sWindowRect
;
428 GdkRectangle sCanvasRect
;
430 gdk_window_get_frame_extents(m_pCanvas
->window
, &sWindowRect
);
432 pUserLog
->AddWindowSize(sWindowRect
.y
,
434 sWindowRect
.y
+ sWindowRect
.height
,
435 sWindowRect
.x
+ sWindowRect
.width
);
437 if (m_pScreen
!= NULL
) {
438 if (m_pScreen
->GetCanvasSize(&sCanvasRect
))
439 pUserLog
->AddCanvasSize(sCanvasRect
.y
,
441 sCanvasRect
.y
+ sCanvasRect
.height
,
442 sCanvasRect
.x
+ sCanvasRect
.width
);
447 gdk_window_get_pointer(NULL
, &iMouseX
, &iMouseY
, NULL
);
449 pUserLog
->AddMouseLocationNormalized(iMouseX
, iMouseY
, true, GetNats());
454 // See CVS for code which used to be here
457 gboolean
CDasherControl::ButtonPressEvent(GdkEventButton
*event
) {
459 // Take the focus if we click on the canvas
461 GdkEventFocus
*focusEvent
= (GdkEventFocus
*) g_malloc(sizeof(GdkEventFocus
));
462 gboolean
*returnType
;
464 focusEvent
->type
= GDK_FOCUS_CHANGE
;
465 focusEvent
->window
= (GdkWindow
*) m_pCanvas
;
466 focusEvent
->send_event
= FALSE
;
467 focusEvent
->in
= TRUE
;
469 gtk_widget_grab_focus(GTK_WIDGET(m_pCanvas
));
470 g_signal_emit_by_name(GTK_OBJECT(m_pCanvas
), "focus_in_event", GTK_WIDGET(m_pCanvas
), focusEvent
, NULL
, &returnType
);
472 if(event
->type
== GDK_BUTTON_PRESS
)
473 KeyDown(get_time(), 100);
474 else if(event
->type
== GDK_BUTTON_RELEASE
)
475 KeyUp(get_time(), 100);
480 gint
CDasherControl::KeyReleaseEvent(GdkEventKey
*event
) {
483 if((event
->keyval
== GDK_Shift_L
) || (event
->keyval
== GDK_Shift_R
)) {
484 if(event
->state
& GDK_CONTROL_MASK
)
485 SetLongParameter(LP_BOOSTFACTOR
, 25);
487 SetLongParameter(LP_BOOSTFACTOR
, 100);
489 else if((event
->keyval
== GDK_Control_L
) || (event
->keyval
== GDK_Control_R
)) {
490 if(event
->state
& GDK_SHIFT_MASK
)
491 SetLongParameter(LP_BOOSTFACTOR
, 175);
493 SetLongParameter(LP_BOOSTFACTOR
, 100);
498 if(m_pKeyboardHelper
) {
499 int iKeyVal(m_pKeyboardHelper
->ConvertKeycode(event
->keyval
));
502 KeyUp(get_time(), iKeyVal
);
509 gint
CDasherControl::KeyPressEvent(GdkEventKey
*event
) {
510 if((event
->keyval
== GDK_Shift_L
) || (event
->keyval
== GDK_Shift_R
))
511 SetLongParameter(LP_BOOSTFACTOR
, 175);
512 else if((event
->keyval
== GDK_Control_L
) || (event
->keyval
== GDK_Control_R
))
513 SetLongParameter(LP_BOOSTFACTOR
, 25);
517 if(m_pKeyboardHelper
) {
518 int iKeyVal(m_pKeyboardHelper
->ConvertKeycode(event
->keyval
));
521 KeyDown(get_time(), iKeyVal
);
527 void CDasherControl::CanvasDestroyEvent() {
530 if(m_pScreen
!= NULL
) {
536 // Tell the logging object that a new user trial is starting.
537 void CDasherControl::UserLogNewTrial()
539 CUserLog
* pUserLog
= GetUserLogPtr();
540 if (pUserLog
!= NULL
) {
541 pUserLog
->NewTrial();
545 int CDasherControl::GetFileSize(const std::string
&strFileName
) {
546 struct stat sStatInfo
;
548 if(!stat(strFileName
.c_str(), &sStatInfo
))
549 return sStatInfo
.st_size
;
554 // FIXME - these two methods seem a bit pointless!
556 int CDasherControl::alphabet_filter(const gchar
*filename
, GPatternSpec
*alphabetglob
) {
557 return int (g_pattern_match_string(alphabetglob
, filename
));
560 int CDasherControl::colour_filter(const gchar
*filename
, GPatternSpec
*colourglob
) {
561 return int (g_pattern_match_string(colourglob
, filename
));
564 // "C" style callbacks - these are here just because it's not possible
565 // (or at least not easy) to connect a callback directly to a C++
566 // method, so we pass a pointer to th object in the user_data field
567 // and use a wrapper function. Please do not put any functional code
570 extern "C" void realize_canvas(GtkWidget
*widget
, gpointer user_data
) {
571 static_cast < CDasherControl
* >(user_data
)->RealizeCanvas();
575 extern "C" gboolean
button_press_event(GtkWidget
*widget
, GdkEventButton
*event
, gpointer data
) {
576 return static_cast < CDasherControl
* >(data
)->ButtonPressEvent(event
);
579 extern "C" gint
key_press_event(GtkWidget
*widget
, GdkEventKey
*event
, gpointer data
) {
580 return static_cast < CDasherControl
* >(data
)->KeyPressEvent(event
);
583 extern "C" gint
canvas_configure_event(GtkWidget
*widget
, GdkEventConfigure
*event
, gpointer data
) {
584 return static_cast < CDasherControl
* >(data
)->CanvasConfigureEvent();
587 extern "C" void canvas_destroy_event(GtkWidget
*pWidget
, gpointer pUserData
) {
588 static_cast<CDasherControl
*>(pUserData
)->CanvasDestroyEvent();
591 extern "C" gint
key_release_event(GtkWidget
*pWidget
, GdkEventKey
*event
, gpointer pUserData
) {
592 return static_cast<CDasherControl
*>(pUserData
)->KeyReleaseEvent(event
);
595 extern "C" gboolean
canvas_focus_event(GtkWidget
*widget
, GdkEventFocus
*event
, gpointer data
) {
596 return static_cast < CDasherControl
* >(data
)->FocusEvent(widget
, event
);