Fixing up default values Slight tweak to external text entry
[dasher.git] / Src / Gtk2 / DasherControl.cpp
blobd7a59763bb59bf6d8e9dc377294621cf9495f9ff
1 #include "../Common/Common.h"
2 #include "../../config.h"
4 #include <iostream>
5 #include "DasherControl.h"
6 #include "Timer.h"
7 #include "../DasherCore/Event.h"
8 #include "../DasherCore/WrapperFactory.h"
10 #include <fcntl.h>
12 #include <gtk/gtk.h>
13 #include <gdk/gdk.h>
14 #include <gdk/gdkkeysyms.h>
15 #include <sys/stat.h>
16 using namespace std;
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);
34 Realize();
36 m_iComboCount = 0;
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));
53 CreateInput();
55 // m_pSocketInput = (CSocketInput *)GetModule(1);
56 // m_pSocketInput->Ref();
58 m_pMouseInput = (CDasherMouseInput *)GetModule(0);
59 m_pMouseInput->Ref();
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.
67 m_pScreen = NULL;
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
114 // call the object
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() {
132 char *home_dir;
133 char *user_data_dir;
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) {
154 GDir *directory;
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);
161 if(directory) {
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);
172 if(directory) {
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) {
184 GDir *directory;
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);
192 if(directory) {
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);
203 if(directory) {
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;
229 // }
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);
254 return true;
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);
280 return pRetVal;
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)
293 delete m_pScreen;
295 m_pScreen = new CCanvas(m_pCanvas, m_pPangoCache);
296 ChangeScreen(m_pScreen);
298 return 0;
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)) {
309 HandleEvent(pEvent);
314 void CDasherControl::WriteTrainFile(const std::string &strNewText) {
315 if(strNewText.length() == 0)
316 return;
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());
322 close(fd);
325 void CDasherControl::ExternalKeyDown(int iKeyVal) {
326 if(m_pKeyboardHelper) {
327 int iButtonID(m_pKeyboardHelper->ConvertKeycode(iKeyVal));
329 if(iButtonID != -1)
330 KeyDown(get_time(), iButtonID);
334 void CDasherControl::ExternalKeyUp(int iKeyVal) {
335 if(m_pKeyboardHelper) {
336 int iButtonID(m_pKeyboardHelper->ConvertKeycode(iKeyVal));
338 if(iButtonID != -1)
339 KeyUp(get_time(), iButtonID);
343 void CDasherControl::HandleParameterNotification(int iParameter) {
345 switch(iParameter) {
346 case SP_DASHER_FONT:
347 m_pPangoCache->ChangeFont(GetStringParameter(SP_DASHER_FONT));
348 Redraw(true);
349 break;
350 case BP_GLOBAL_KEYBOARD:
351 if(m_pKeyboardHelper)
352 m_pKeyboardHelper->Grab(GetBoolParameter(BP_GLOBAL_KEYBOARD));
353 break;
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) {
366 // Insert event
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) {
370 // Delete event
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() {
415 int x, y;
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,
433 sWindowRect.x,
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,
440 sCanvasRect.x,
441 sCanvasRect.y + sCanvasRect.height,
442 sCanvasRect.x + sCanvasRect.width);
445 int iMouseX = 0;
446 int iMouseY = 0;
447 gdk_window_get_pointer(NULL, &iMouseX, &iMouseY, NULL);
449 pUserLog->AddMouseLocationNormalized(iMouseX, iMouseY, true, GetNats());
452 return 1;
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);
477 return false;
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);
486 else
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);
492 else
493 SetLongParameter(LP_BOOSTFACTOR, 100);
495 else {
496 int iKeyVal;
498 if(m_pKeyboardHelper) {
499 int iKeyVal(m_pKeyboardHelper->ConvertKeycode(event->keyval));
501 if(iKeyVal != -1)
502 KeyUp(get_time(), iKeyVal);
506 return 0;
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);
514 else {
515 int iKeyVal;
517 if(m_pKeyboardHelper) {
518 int iKeyVal(m_pKeyboardHelper->ConvertKeycode(event->keyval));
520 if(iKeyVal != -1)
521 KeyDown(get_time(), iKeyVal);
524 return 0;
527 void CDasherControl::CanvasDestroyEvent() {
528 // Delete the screen
530 if(m_pScreen != NULL) {
531 delete m_pScreen;
532 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;
550 else
551 return 0;
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
568 // here.
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);