1 /* -*- c++ -*-
2 FILE: EventHandler.cpp
3 RCS REVISION: $Revision: 1.39 $
4 
5 COPYRIGHT: (c) 1999 -- 2003 Melinda Green, Don Hatch, and Jay Berkenbilt - Superliminal Software
6 
7 LICENSE: Free to use and modify for non-commercial purposes as long as the
8     following conditions are adhered to:
9     1) Obvious credit for the source of this code and the designs it embodies
10        are clearly made, and
11     2) Ports and derived versions of 4D Magic Cube programs are not distributed
12        without the express written permission of the authors.
13 
14 DESCRIPTION:
15     Implementation of the EventHandler class
16 */
17 
18 #include "EventHandler.h"
19 
20 #include <iostream>
21 #include <unistd.h>
22 #include <stdio.h>
23 
24 #include "MagicCube.h"
25 #include "Puzzlest.h"
26 #include "Polymgr.h"
27 #include "History.h"
28 #include "Macro.h"
29 #include "Machine.h"
30 #include "Widgets.h"
31 #include "MacroManager.h"
32 #include "PostScriptWriter.h"
33 
34 #include <stdint.h>
ptr2int(const void * p)35 static intptr_t ptr2int(const void* p) { return reinterpret_cast<intptr_t>(p); }
36 
37 float const EventHandler::DEF_TWIST_INCREMENT = 0.5;
38 
EventHandler(int argc,char ** argv,char const * machine_type)39 EventHandler::EventHandler(int argc, char **argv, char const* machine_type) :
40     machine(0),
41     widgets(0),
42     polymgr(0),
43     puzzle_state(0),
44     history(0),
45     macromgr(0),
46     creating_a_macro(false),
47     deleting_a_macro(false),
48     next_slicesmask(0),
49     fast_automoves(false),
50     number_of_reference_stickers_needed(0),
51     what_to_do_after_got_reference_stickers(0),
52     cur_ui_data(0),
53     quick_mode(false),
54     dragging(false),
55     nscramblechen(NSCRAMBLECHEN)
56 {
57     this->machine = Machine::createMachine(
58         this, argc, argv, this->preferences, machine_type);
59 
60     int length = this->preferences.getLength();
61 
62     this->polymgr = new PolygonManager4D(this->preferences);
63     this->puzzle_state = new PuzzleState(this->preferences, this->polymgr);
64     this->history = new History(this->preferences, this->polymgr);
65     this->macromgr = new MacroManager(this->preferences, this->polymgr);
66 
67     this->machine->init(puzzle_state);
68     this->widgets = this->machine->getWidgets();
69 
70     nscramblechen = this->preferences.getIntProperty(M4D_NSCRAMBLECHEN,
71                                                      NSCRAMBLECHEN);
72 
73     printf("Each little cube is a hypersticker.\n");
74     printf("Each %dx%dx%d cluster is a hyperface.\n", length, length, length);
75     printf("Middle button rotates a hyperface to center.\n");
76     printf("Left or right button twists a hyperface.\n");
77     printf("*** Control-left or control-right twists two layers. ***\n");
78     printf
79         ("*** Shift-left or shift-right twists the whole hypercube at once. ***\n");
80     printf
81         ("(Shift or control)-middle and dragging changes the viewing perspective.\n");
82     printf("Type 'h' for a list of key commands.\n");
83 
84     widgets->addButton(
85         Widgets::ButtonData("Undo", &EventHandler::undo_cb, (void*)0));
86     widgets->addButton(
87         Widgets::ButtonData("Redo", &EventHandler::redo_cb, (void*)0));
88     static Widgets::ButtonData twist_items[] = {
89         Widgets::ButtonData(" 1 twist", &EventHandler::scramble_cb, (void *)1),
90         Widgets::ButtonData(" 2 twists", &EventHandler::scramble_cb, (void *)2),
91         Widgets::ButtonData(" 3 twists", &EventHandler::scramble_cb, (void *)3),
92         Widgets::ButtonData(" 4 twists", &EventHandler::scramble_cb, (void *)4),
93         Widgets::ButtonData(" 5 twists", &EventHandler::scramble_cb, (void *)5),
94         Widgets::ButtonData(" 6 twists", &EventHandler::scramble_cb, (void *)6),
95         Widgets::ButtonData(" 7 twists", &EventHandler::scramble_cb, (void *)7),
96         Widgets::ButtonData(" 8 twists", &EventHandler::scramble_cb, (void *)8),
97         Widgets::ButtonData("Full Scramble",
98                             &EventHandler::scramble_cb, (void *)NSCRAMBLECHEN),
99     };
100     int         n_items = sizeof(twist_items) / sizeof(twist_items[0]);
101     assert(n_items == 9);
102     widgets->addMenuButton("Scramble...", n_items, twist_items);
103     widgets->addButton(
104         Widgets::ButtonData("Solve", &EventHandler::cheat_cb, 0));
105     widgets->addButton(
106         Widgets::ButtonData("Reset", &EventHandler::reset_cb, 0));
107     static Widgets::ButtonData new_puzzle_items[] = {
108         // widgets_button_create_data(
109         // MAXLENGTH<1 ? NULL : "1x1x1x1", new_puzzle_cb, (void *)1,
110         Widgets::ButtonData(MAXLENGTH < 2 ? 0 :
111                             Widgets::ButtonData("2x2x2x2", &EventHandler::newPuzzle_cb,
112                                                 (void *)2)),
113         Widgets::ButtonData(MAXLENGTH < 3 ? 0 :
114                             Widgets::ButtonData("3x3x3x3", &EventHandler::newPuzzle_cb,
115                                                 (void *)3)),
116         Widgets::ButtonData(MAXLENGTH < 4 ? 0 :
117                             Widgets::ButtonData("4x4x4x4", &EventHandler::newPuzzle_cb,
118                                                 (void *)4)),
119         Widgets::ButtonData(MAXLENGTH < 5 ? 0 :
120                             Widgets::ButtonData("5x5x5x5", &EventHandler::newPuzzle_cb,
121                                                 (void *)5)),
122         Widgets::ButtonData(MAXLENGTH < 6 ? 0 :
123                             Widgets::ButtonData("6x6x6x6", &EventHandler::newPuzzle_cb,
124                                                 (void *)6)),
125         Widgets::ButtonData(MAXLENGTH < 7 ? 0 :
126                             Widgets::ButtonData("7x7x7x7", &EventHandler::newPuzzle_cb,
127                                                 (void *)7)),
128     };
129     n_items = sizeof(new_puzzle_items) / sizeof(new_puzzle_items[0]);
130     widgets->addMenuButton("New Puzzle...", n_items, new_puzzle_items);
131     widgets->addButton(Widgets::ButtonData("Save", &EventHandler::save_cb, 0));
132     widgets->addButton(Widgets::ButtonData("Print", &EventHandler::print_cb, 0));
133     widgets->addButton(Widgets::ButtonData("Quit", &EventHandler::quit_cb, 0));
134 
135     readLogfile(this->preferences.getStringProperty(M4D_LOGFILE));
136 
137     void* prev_ui_data = 0;
138     for (int i = 0; i < macromgr->getNMacros(); ++i)
139     {
140         void* ui_data = widgets->addMacro(
141             macromgr->getMacroName(i), prev_ui_data);
142         macromgr->setUIData(i, ui_data);
143         prev_ui_data = ui_data;
144     }
145 
146     polymgr->getUntwistedFrame(&untwisted_frame);
147 
148     /* an expose event will happen immediately */
149     machine->addEventHandler(1 << EXPOSE, &EventHandler::expose_handler, 0);
150     machine->addEventHandler(1 << RESIZE, &EventHandler::expose_handler, 0);
151     machine->addEventHandler(1 << BUTTONDOWN, &EventHandler::buttonDown_handler, 0);
152     machine->addEventHandler(1 << KEYPRESS, &EventHandler::keyPress_handler, 0);
153     machine->addEventHandler(1 << KEYRELEASE, &EventHandler::keyRelease_handler, 0);
154     machine->addEventHandler(1 << DRAG, &EventHandler::drag_handler, 0);
155 }
156 
157 EventHandler::Callback*
createCallback(callback_fn fn,void * arg)158 EventHandler::createCallback(callback_fn fn, void* arg)
159 {
160     return new Callback(this, fn, arg);
161 }
162 
163 
164 void
run()165 EventHandler::run()
166 {
167     machine->eventLoop();
168 }
169 
~EventHandler()170 EventHandler::~EventHandler()
171 {
172     // this->widgets is under external control
173     delete this->machine;
174     delete this->polymgr;
175     delete this->puzzle_state;
176     delete this->history;
177     delete this->macromgr;
178 }
179 
180 void
readLogfile(char * filename)181 EventHandler::readLogfile(char *filename)
182 {
183     // FIX THIS: this code is duplicated between the windows and UNIX
184     // sections of the code.
185     FILE       *fp;
186     if ((fp = fopen(filename, "r")))
187     {
188         // The UNIX code doesn't currently have a notion of scramble
189         // state.  This is implemented in MagicCubeObj for the Windows
190         // version, but that object has some Windows-specific stuff in
191         // it.  For now, we just ignore scramble_state and user_twists
192         // when validating the header.
193         int         error = 0;
194         // Length must one more than field length in fscanf below
195         char        magic_number[20];
196         magic_number[19] = '\0';
197         int         file_version;
198         long int    scramble_state;
199         int         user_twists;
200         fscanf(fp, "%19s %d%ld%d", magic_number,
201                &file_version, &scramble_state, &user_twists);
202         if (!((file_version == MAGICCUBE_FILE_VERSION) &&
203               (strcmp(magic_number, MAGIC_NUMBER) == 0)))
204         {
205             fprintf(stderr, "%s does not look like a MagicCube4D log file",
206                     filename);
207             error = 1;
208         }
209         else
210         {
211             if (! this->puzzle_state->read(fp))
212             {
213                 error = 1;
214                 fprintf(stderr, "Couldn't read the state from %s!\n",
215                         filename);
216             }
217             else if (! this->history->read(fp))
218             {
219                 error = 1;
220                 fprintf(stderr, "Couldn't read the history from %s!\n",
221                         filename);
222             }
223             else if (! this->macromgr->read(fp))
224             {
225                 error = 1;
226                 fprintf(stderr, "Couldn't read the macros from %s!\n",
227                         filename);
228             }
229         }
230         fclose(fp);
231         if (error)
232         {
233             exit(2);
234         }
235     }
236 }
237 
238 /*
239  * Return 1 on success, 0 on failure
240  */
241 void
dump(FILE * fp)242 EventHandler::dump(FILE *fp)
243 {
244     // FIX THIS: this code is duplicated between the windows and UNIX
245     // sections of the code.
246 
247     // The second and third values of the header are supposed to be
248     // the scramble state and the number of twists done by the user
249     // (as opposed to the software during scrambling).  The Windows
250     // code keeps track of these things in MagicCubeObj which
251     // currently contains some Windows-specific code.  That object
252     // should be rewritten so that the non-Windows specific stuff can
253     // become common.  For now, rather than duplicating the logic
254     // here, we will just pretend that all twists are user twists and
255     // that the scramble state is NONE (which we know has the value
256     // 0).  That will almost never be exactly correct, but it will at
257     // least allow for exchange of log files between Windows and UNIX
258     // users.
259     int twists = history->countTwists();
260     fprintf(fp, "%s %d 0 %d\n", MAGIC_NUMBER, MAGICCUBE_FILE_VERSION, twists);
261     this->puzzle_state->dump(fp);
262     this->history->dump(fp);
263     this->macromgr->dump(fp);
264 }
265 
266 bool
writeLogfile(char * filename)267 EventHandler::writeLogfile(char *filename)
268 {
269     bool result = false;
270     FILE *fp;
271     if ((fp = fopen(filename, "w")))
272     {
273         dump(fp);
274         fclose(fp);
275         result = true;
276     }
277 
278     return result;
279 }
280 
281 
282 void
expose_handler(Event *,void *)283 EventHandler::expose_handler(Event*, void*)
284 {
285     this->machine->drawFrame(&untwisted_frame);
286 }
287 
288 void
showAnimation(struct stickerspec * grip,int dir,int slicesmask)289 EventHandler::showAnimation(struct stickerspec *grip, int dir, int slicesmask)
290 {
291     int         seqno, outof;
292 
293     outof = (quick_mode ? 1 : polymgr->getTwistNFrames(grip));
294     if (preferences.getBoolProperty(M4D_ALL_AT_ONCE))
295     {
296         struct frame *frames = new struct frame[outof];
297         for (seqno = 1; seqno <= outof; ++seqno)
298             polymgr->getFrame(grip, dir, slicesmask,
299                               seqno, outof, &frames[seqno - 1]);
300         for (seqno = 1; seqno <= outof; ++seqno)
301             machine->drawFrame(&frames[seqno - 1]);
302         delete [] frames;
303     }
304     else
305     {
306         struct frame frame;
307         for (seqno = 1; seqno <= outof; ++seqno)
308         {
309             polymgr->getFrame(grip, dir, slicesmask, seqno, outof, &frame);
310             machine->drawFrame(&frame);
311         }
312     }
313 }
314 
315 void
getAReferenceSticker(Event * event,void *)316 EventHandler::getAReferenceSticker(Event *event, void *)
317 {
318     struct stickerspec sticker;
319     /* FIX THIS-- change the dialog box to say how many still needed */
320     if (number_of_reference_stickers_needed > 0)
321     {
322         if (!polymgr->pick(event->x, event->y, &untwisted_frame, &sticker))
323         {
324             /* printf("missed!\n"); */
325             machine->bell();
326             return;
327         }
328         SET4(reference_stickers_needed[0], sticker.coords);
329         number_of_reference_stickers_needed--;
330         reference_stickers_needed++;
331         if (number_of_reference_stickers_needed == 0)
332             (this->*what_to_do_after_got_reference_stickers)(NULL);
333     }
334 }
335 
336 
337 void
buttonDown_handler(Event * event,void * arg)338 EventHandler::buttonDown_handler(Event *event, void *arg)
339 {
340     struct stickerspec grip;
341     int x, y;
342     int length = this->preferences.getLength();
343 
344     if (number_of_reference_stickers_needed)
345     {
346         getAReferenceSticker(event, arg);
347         return;
348     }
349 
350     if (event->button == MIDDLEBUTTON)
351         if (event->shift_is_down || event->control_is_down)
352         {
353             dragging = 1;
354             return;
355         }
356         else
357             dragging = 0;
358 
359     x = event->x;
360     y = event->y;
361     if (polymgr->pickGrip(x, y, &untwisted_frame, &grip))
362     {
363         int         slicesmask = ~0;
364         int         dir = CCW;
365         switch (event->button)
366         {
367         case LEFTBUTTON:
368         case RIGHTBUTTON:
369             if (grip.dim == 3)
370             {
371                 fprintf(stderr, "Can't twist that.\n");
372                 return;
373             }
374 
375             if (event->shift_is_down)
376                 if (event->control_is_down)
377                     slicesmask = ~(1 << (length - 1));  /* all but last */
378                 else
379                     slicesmask = ~0;    /* everything */
380             else if (event->control_is_down)
381                 slicesmask = (1 << 0) | (1 << 1);   /* two layers */
382             else if (next_slicesmask)
383                 slicesmask = next_slicesmask;   /* overridden value */
384             else
385                 slicesmask = 1; /* one layer */
386 
387             dir = ((event->button == LEFTBUTTON) ? CCW : CW);
388             break;
389 
390         case MIDDLEBUTTON:
391             if (!polymgr->facetocenterToGrip(grip.face, &grip))
392             {
393                 fprintf(stderr, "Can't rotate that to center.\n");
394                 return;
395             }
396             slicesmask = ~0;
397             dir = CCW;
398             break;
399         }
400         showAnimation(&grip, dir, slicesmask);
401         puzzle_state->applyMove(&grip, dir, slicesmask);
402         if (preferences.getBoolProperty(M4D_DRAW_NEW_STATE))
403             machine->drawFrame(&untwisted_frame);
404         history->apply(&grip, dir, slicesmask);
405         macromgr->addMove(&grip, dir, slicesmask); /* doesn't hurt */
406     }
407     else
408     {
409         /* printf("%d,%d:", x, y); */
410         /* printf("missed!\n"); */
411         machine->bell();
412     }
413 }
414 
415 /*
416  * Button callback functions...
417  */
418 void
undo_cb(void * argp)419 EventHandler::undo_cb(void* argp)
420 {
421     int arg = ptr2int(argp);
422     struct stickerspec grip;
423     int         dir;
424     int         slicesmask;
425     if (creating_a_macro)
426     {
427         /* FIX THIS */
428         printf("Sorry, can't undo while defining a macro\n");
429         return;
430     }
431     int         handled = 0;
432     if (fast_automoves && history->atMacroClose())
433     {
434         struct stickerspec sticker;
435         int         dir;
436         int         slicesmask;
437         while (history->goTowardsMark(MARK_MACRO_OPEN,
438                                       &sticker, &dir, &slicesmask) == 1)
439         {
440             handled = 1;
441             puzzle_state->applyMove(&sticker, dir, slicesmask);
442         }
443         machine->drawFrame(&untwisted_frame);
444     }
445     else if ((arg == 0) && (history->atScrambleBoundary()))
446     {
447         handled = 1;
448         printf("\aUse shift-U to undo past a scramble boundary\n");
449     }
450     if (!handled)
451     {
452         if (history->undo(&grip, &dir, &slicesmask))
453         {
454             showAnimation(&grip, dir, slicesmask);
455             puzzle_state->applyMove(&grip, dir, slicesmask);
456             if (preferences.getBoolProperty(M4D_DRAW_NEW_STATE))
457                 machine->drawFrame(&untwisted_frame);
458         }
459         else
460             printf("Nothing to undo.\n");
461     }
462 }
463 
464 void
redo_cb(void * argp)465 EventHandler::redo_cb(void* argp)
466 {
467     int arg = ptr2int(argp);
468     struct stickerspec grip;
469     int         dir;
470     int         slicesmask;
471     if (creating_a_macro)
472     {
473         /* FIX THIS */
474         printf("Sorry, can't redo while defining a macro\n");
475         return;
476     }
477     int         handled = 0;
478     if (fast_automoves && history->atMacroOpen())
479     {
480         struct stickerspec sticker;
481         int         dir;
482         int         slicesmask;
483         while (history->goTowardsMark(MARK_MACRO_CLOSE,
484                                       &sticker, &dir, &slicesmask) == 1)
485         {
486             handled = 1;
487             puzzle_state->applyMove(&sticker, dir, slicesmask);
488         }
489         machine->drawFrame(&untwisted_frame);
490     }
491     else if ((arg == 0) && (history->atScrambleBoundary()))
492     {
493         handled = 1;
494         printf("\aUse shift-R to redo past a scramble boundary\n");
495     }
496     if (!handled)
497     {
498         if (history->redo(&grip, &dir, &slicesmask))
499         {
500             showAnimation(&grip, dir, slicesmask);
501             puzzle_state->applyMove(&grip, dir, slicesmask);
502             if (preferences.getBoolProperty(M4D_DRAW_NEW_STATE))
503                 machine->drawFrame(&untwisted_frame);
504         }
505         else
506             printf("Nothing to redo.\n");
507     }
508 }
509 
510 void
scramble_cb(void * arg=NULL)511 EventHandler::scramble_cb(void *arg = NULL)
512 {
513     int n = ptr2int(arg);
514     struct stickerspec grip;
515     int i, previous_face = -1;
516     int ngrips = NFACES * 3 * 3 * 3;
517     int max_slicesmask = preferences.getLength() / 2;
518     bool full_scramble = (n == nscramblechen);
519 
520     if (full_scramble)
521     {
522         reset_cb(NULL);
523     }
524 
525     for (i = 0; i < n; ++i)
526     {
527         do
528         {
529             grip.id_within_cube = rand() % ngrips;
530             polymgr->fillStickerspecFromIdAndLength(&grip, 3);
531         }
532         while (grip.dim != 2 ||
533                i > 0 && grip.face == previous_face ||
534                i > 0 && grip.face == polymgr->oppositeFace(previous_face));
535 
536         int slicesmask = 1;
537         if (max_slicesmask > 1)
538         {
539             slicesmask += rand() % max_slicesmask;
540         }
541 
542         previous_face = grip.face;
543         puzzle_state->applyMove(&grip, CCW, slicesmask);
544         history->apply(&grip, CCW, slicesmask);
545     }
546 
547     if (full_scramble)
548     {
549         history->mark(MARK_SCRAMBLE_BOUNDARY);
550     }
551 
552     machine->drawFrame(&untwisted_frame);
553 }
554 
555 void
cheat_cb(void *)556 EventHandler::cheat_cb(void *)
557 {
558     struct stickerspec grip;
559     int         dir;
560     int         slicesmask;
561     if (creating_a_macro)
562     {
563         /* FIX THIS */
564         printf("Sorry, can't solve while defining a macro\n");
565         return;
566     }
567 
568     history->compress();
569 
570     while ((! puzzle_state->isSolved()) &&
571            history->undo(&grip, &dir, &slicesmask))
572     {
573         showAnimation(&grip, dir, slicesmask);
574         puzzle_state->applyMove(&grip, dir, slicesmask);
575         if (preferences.getBoolProperty(M4D_DRAW_NEW_STATE))
576             machine->drawFrame(&untwisted_frame);
577     }
578     history->clear();
579 }
580 
581 void
reset_cb(void *)582 EventHandler::reset_cb(void *)
583 {
584     polymgr->reset();
585     puzzle_state->reset();
586     history->reset();
587     machine->drawFrame(&untwisted_frame);
588 }
589 
590 void
save_cb(void *)591 EventHandler::save_cb(void *)
592 {
593     int i;
594     for (i = 0; i < macromgr->getNMacros(); ++i)
595     {
596         char* name = widgets->getMacroName(i);
597         if (name)
598         {
599             macromgr->setMacroName(i, name);
600         }
601     }
602 
603     if (!writeLogfile(this->preferences.getStringProperty(M4D_LOGFILE)))
604     {
605         /* FIX THIS-- need better recovery if can't write the file */
606         machine->bell();
607         fprintf(stderr, "unable to save; saving to stderr (between ---'s)\n");
608         fprintf(stderr, "---\n");
609         dump(stderr);
610         fprintf(stderr, "---\n");
611     }
612 }
613 
614 void
quit_cb(void *)615 EventHandler::quit_cb(void *)
616 {
617     if (creating_a_macro)
618         cancelAdd_cb();
619 
620     save_cb(0);
621     exit(0);
622 }
623 
624 void
print_cb(void *)625 EventHandler::print_cb(void *)
626 {
627     assert(this->puzzle_state != 0);
628     PostScriptWriter psw(this->preferences,
629                          *this->puzzle_state,
630                          untwisted_frame);
631     char filename[100];
632     sprintf(filename, "/tmp/mc4d.%d.EPS", getpid());
633     if (psw.generateOutput(filename))
634     {
635         std::cout << "wrote PostScript to " << filename << std::endl;
636     }
637 }
638 
639 void
cancelApplyMacro_cb(void *)640 EventHandler::cancelApplyMacro_cb(void *)
641 {
642     widgets->destroyDialog();
643 }
644 
645 void
applyMacroCBAfterGotRefStickers(void *)646 EventHandler::applyMacroCBAfterGotRefStickers(void *)
647 {
648     struct stickerspec grip;
649     int dir;
650     int slicesmask;
651 
652     widgets->destroyDialog();
653 
654     if (macromgr->open(macro_which, nrefs, refs, MacroManager::m_reading,
655                        macro_invert ? -1 : 1))
656     {
657         /* printf("Executing macro \"%s\"...", macro_getname(name)); */
658         fflush(stdout);
659         if (! history->atMacroOpen())
660         {
661             // FIX THIS -- Must insert this mark twice because
662             // truncate eats one of them.  If truncate doesn't eat one
663             // of them, then undoing a macro and immediately doing
664             // anything else leaves a stray m[ in the log file.
665             history->mark(MARK_MACRO_OPEN);
666         }
667         history->mark(MARK_MACRO_OPEN);
668         while (macromgr->getMove(&grip, &dir, &slicesmask))
669         {
670             if (!fast_automoves)
671             {
672                 showAnimation(&grip, dir, slicesmask);
673             }
674             puzzle_state->applyMove(&grip, dir, slicesmask);
675             if (preferences.getBoolProperty(M4D_DRAW_NEW_STATE))
676                 machine->drawFrame(&untwisted_frame);
677             history->apply(&grip, dir, slicesmask);
678             macromgr->addMove(&grip, dir, slicesmask); // allow nested macros
679         }
680         macromgr->close();
681         history->mark(MARK_MACRO_CLOSE);
682         if (fast_automoves)
683         {
684             machine->drawFrame(&untwisted_frame);
685         }
686         /* printf(" done.\n"); */
687     }
688     else
689     {
690         std::cout << "Sorry, the original reference stickers can't be rotated "
691                   << "to those. (aborted)" << std::endl;
692         /* FIX THIS--- make it an error dialog */
693         widgets->destroyDialog();
694     }
695 }
696 
697 void
applyMacroCommon(void * ui_data,bool invert)698 EventHandler::applyMacroCommon(void *ui_data, bool invert)
699 {
700     // ui_data == 0 means there's only one macro
701     macro_which = (ui_data ? this->macromgr->findWithUIData(ui_data) : 0);
702     macro_invert = invert;
703 
704     if (deleting_a_macro)
705     {
706         widgets->destroyDialog();
707         void* ui_data = macromgr->destroy(macro_which);
708         widgets->removeMacro(ui_data);
709         deleting_a_macro = false;
710         return;
711     }
712 
713     static Widgets::ButtonData button(
714         "Cancel", &EventHandler::cancelApplyMacro_cb, 0);
715     widgets->createDialog("Click on three reference stickers.\n", 1, &button);
716 
717     number_of_reference_stickers_needed = 3;
718     reference_stickers_needed = refs;
719     what_to_do_after_got_reference_stickers =
720         &EventHandler::applyMacroCBAfterGotRefStickers;
721 }
722 
723 void
applyMacro_cb(void * w)724 EventHandler::applyMacro_cb(void *w)
725 {
726     applyMacroCommon(w, 0);
727 }
728 
729 void
invertMacro_cb(void * w)730 EventHandler::invertMacro_cb(void *w)
731 {
732     applyMacroCommon(w, 1);
733 }
734 
735 void
cancelAdd_cb(void *)736 EventHandler::cancelAdd_cb(void *)
737 {
738     int which = macromgr->getNMacros() - 1;
739     widgets->destroyDialog();
740     widgets->removeMacro(cur_ui_data);
741     this->cur_ui_data = 0;
742     macromgr->close();
743     macromgr->destroy(which);
744     creating_a_macro = false;
745 }
746 
747 void
doneAdd_cb(void *)748 EventHandler::doneAdd_cb(void *)
749 {
750     widgets->destroyDialog();
751     macromgr->setUIData(macromgr->getNMacros() - 1, cur_ui_data);
752     this->cur_ui_data = 0;
753     macromgr->close();
754     creating_a_macro = false;
755 }
756 
757 void
add_cb(void *)758 EventHandler::add_cb(void *)
759 {
760     if (creating_a_macro)
761         return;
762 
763     creating_a_macro = true;
764     deleting_a_macro = false;
765 
766     void* prev_ui_data = 0;
767     if (macromgr->getNMacros() > 0)
768     {
769         prev_ui_data = macromgr->getUIData(macromgr->getNMacros() - 1);
770     }
771     macromgr->create("", nrefs, refs);
772     macromgr->open(macromgr->getNMacros() - 1, nrefs, refs,
773                    MacroManager::m_writing, 0);
774     widgets->destroyDialog();
775 
776     this->cur_ui_data = widgets->addMacro("", prev_ui_data);
777 
778     static Widgets::ButtonData button("Cancel", &EventHandler::cancelAdd_cb, 0);
779     widgets->createDialog("Click on three\nreference stickers.\n", 1, &button);
780 
781     number_of_reference_stickers_needed = 3;
782     reference_stickers_needed = refs;
783     what_to_do_after_got_reference_stickers =
784         &EventHandler::addCBAfterGotRefStickers;
785 }
786 
787 void
addCBAfterGotRefStickers(void *)788 EventHandler::addCBAfterGotRefStickers(void *)
789 {
790     widgets->destroyDialog();
791     static Widgets::ButtonData make_macro[2] = {
792         Widgets::ButtonData("Done", &EventHandler::doneAdd_cb, 0),
793         Widgets::ButtonData("Cancel", &EventHandler::cancelAdd_cb, 0),
794     };
795 
796     // FIX THIS-- it obscures the window!
797     // not if macros are on the right.  -ejb
798     widgets->createDialog
799         ("Enter the moves of the macro,\nthen click on \"Done\".", 2,
800          make_macro);
801 
802     macromgr->setMacroRefs(macromgr->getNMacros() - 1, nrefs, refs);
803 }
804 
805 void
cancelDelete_cb(void *)806 EventHandler::cancelDelete_cb(void *)
807 {
808     widgets->destroyDialog();
809     deleting_a_macro = false;
810 }
811 
812 void
delete_cb(void *)813 EventHandler::delete_cb(void *)
814 {
815     if (macromgr->getNMacros() == 0)
816     {
817         // FIX THIS-- the button shouldn't even exist
818         return;
819     }
820 
821     if (creating_a_macro)
822     {
823         std::cout << "Sorry, can't delete a macro while defining a macro"
824                   << std::endl;
825         return;
826     }
827 
828     widgets->destroyDialog();
829 
830     deleting_a_macro = true;
831     creating_a_macro = false;
832 
833     // If there's only one macro, can delete it without popping up the
834     // dialog.  Do this by pretending the apply button was hit.
835     if (macromgr->getNMacros() == 1)
836     {
837         applyMacro_cb(0);
838         return;
839     }
840 
841     static Widgets::ButtonData button(
842         "Cancel", &EventHandler::cancelDelete_cb, 0);
843     widgets->createDialog
844         ("Click on the \"Apply\" button\nof the macro you wish to delete.", 1,
845          &button);
846 }
847 
848 void
toggleFast_cb(void *)849 EventHandler::toggleFast_cb(void *)
850 {
851     fast_automoves = 1 - fast_automoves;
852     if (widgets)
853     {
854         widgets->updateFastButton(fast_automoves);
855     }
856 }
857 
858 void
newPuzzle_cb(void * arg)859 EventHandler::newPuzzle_cb(void* arg)
860 {
861     if (ptr2int(arg) == preferences.getLength())
862     {
863         reset_cb(0);
864         return;
865     }
866     preferences.setLength(ptr2int(arg));
867     int length = preferences.getLength();
868 
869     polymgr->reset(length);
870     puzzle_state->reset(length);
871     history->reset(length);
872 
873     polymgr->getUntwistedFrame(&untwisted_frame);
874 
875     if (length > 3)
876     {
877         std::cerr << "WARNING: some of the twists look like garbage (sorry)."
878                   << std::endl;
879     }
880     machine->drawFrame(&untwisted_frame);
881 }
882 
883 void
keyPress_handler(Event * event,void *)884 EventHandler::keyPress_handler(Event* event, void *)
885 {
886     static char buf[10];        /* to store some previous keystrokes */
887     static int  nchars_saved = 0;
888 
889     struct stickerspec sticker;
890     int direction;
891     int slicesmask;
892     bool forward_first = false;
893 
894     buf[nchars_saved] = event->key;
895 
896     switch (buf[0])
897     {
898     case 'h':
899     case '?':                   /* help */
900 
901 
902         std::cout
903             << "Typing any of the following keys in the window will do "
904             << "something:" << std::endl
905             << "\th or ? -- print this message" << std::endl
906             << "\tc -- clear (reset to unscrambled state)" << std::endl
907             << "\ts or C -- solve (cheat)" << std::endl
908             << "\tS -- scramble %d moves" << std::endl
909             << "\tu -- undo a move" << std::endl
910             << "\tr -- redo a move" << std::endl
911             << "\to -- toggle outlines" << std::endl
912             << "\tw -- turn background white" << std::endl
913             << "\tb -- turn background black again because the white "
914             << "hurt your eyes" << std::endl
915             << "\tf -- toggle fast automoves mode" << std::endl
916             << "\t1-9 -- act as modifier keys controlling slice "
917             << "mask for current twist" << std::endl
918             << "\t0 -- clear slice mask for next twist" << std::endl
919             << "\tshift-1-9 -- scramble by that many moves" << std::endl
920             << "\tm followed by any key -- mark a position (like in vi)"
921             << std::endl
922             << "\t' or ` followed by any key -- go to mark (like in vi)"
923             << std::endl
924             << "\tx -- twirl to the right" << std::endl
925             << "\tX -- twirl to the left" << std::endl
926             << "\ty -- tilt forward" << std::endl
927             << "\tY -- tilt backward" << std::endl
928             << "\tCTRL-s -- save" << std::endl
929             << "\tCTRL-p -- print (generate PostScript file)" << std::endl
930             << "\tCTRL-d -- dump state to stdout (for debugging)" << std::endl
931             << "\tCTRL-r -- read state from stdin (for debugging)" << std::endl
932             << "\tq -- quit" << std::endl;
933         break;
934 
935     case 'm':                   /* mark-- get another keypress */
936         if (nchars_saved == 1)
937         {
938             nchars_saved = 0;
939             char mark_ch = buf[1];
940             if ((mark_ch == MARK_MACRO_OPEN) ||
941                 (mark_ch == MARK_MACRO_CLOSE) ||
942                 (mark_ch == MARK_SCRAMBLE_BOUNDARY))
943             {
944                 printf("Can't create a mark with reserved character %c.\n",
945                        mark_ch);
946             }
947             history->mark((int)mark_ch);
948         }
949         else
950             nchars_saved = 1;   /* process it when we get another char */
951         break;
952 
953     case '\'':                  /* mark-- get another keypress */
954         forward_first = true;
955         // fall through
956     case '`':
957         if (nchars_saved == 1)
958         {
959             int nmoves_made = 0;
960             int mark = buf[1];
961             int status;
962             nchars_saved = 0;
963             while ((status =
964                     history->goTowardsMark(mark, &sticker, &direction,
965                                            &slicesmask, forward_first)) == 1)
966             {
967                 if (! fast_automoves)
968                 {
969                     showAnimation(&sticker, direction, slicesmask);
970                 }
971                 puzzle_state->applyMove(&sticker, direction, slicesmask);
972                 if (preferences.getBoolProperty(M4D_DRAW_NEW_STATE))
973                     machine->drawFrame(&untwisted_frame);
974                 ++nmoves_made;
975             }
976             if (fast_automoves)
977             {
978                 machine->drawFrame(&untwisted_frame);
979             }
980             if (status == -1)
981             {
982                 printf("No mark '%c'(%d)!\n", mark, mark);
983                 break;
984             }
985             if (nmoves_made == 0 && status == 0)
986             {                   /* already at the mark */
987                 printf("Already at mark '%c'(%d)!\n", mark, mark);
988                 break;
989             }
990         }
991         else
992             nchars_saved = 1;   /* process it when we get another char */
993         break;
994 
995     case 'u':                   /* undo */
996         undo_cb(0);
997         break;
998     case 'U':                   /* undo that goes past scramble boundary */
999         undo_cb((void*)1);
1000         break;
1001     case 'r':                   /* redo */
1002         redo_cb(0);
1003         break;
1004     case 'R':                   /* redo that goes past scramble boundary */
1005         redo_cb((void*)1);
1006         break;
1007     case 'C':                   /* cheat */
1008         cheat_cb(0);
1009         break;
1010     case 'w':                   /* make background white */
1011         machine->turnBackgroundWhite();
1012         machine->drawFrame(&untwisted_frame);
1013         break;
1014     case 'b':                   /* make background black */
1015         machine->turnBackgroundBlack();
1016         machine->drawFrame(&untwisted_frame);
1017         break;
1018     case 'o':                   /* toggle outlines */
1019         machine->toggleOutline();
1020         machine->drawFrame(&untwisted_frame);
1021         break;
1022     case 'f':
1023         toggleFast_cb(0);
1024         break;
1025     case 'c':                   /* clear (reset) */
1026         reset_cb(0);
1027         break;
1028     case 'q':                   /* quit */
1029         quit_cb(0);
1030         break;
1031     case 24:
1032         // XXX
1033         widgets->debuggingHack(macromgr);
1034         break;
1035 
1036         // 1 through 9 ors slices mask of next button event
1037     case '1':
1038     case '2':
1039     case '3':
1040     case '4':
1041     case '5':
1042     case '6':
1043     case '7':
1044     case '8':
1045     case '9':
1046         next_slicesmask |= (1 << (event->key - '1'));
1047         break;
1048 
1049         // 0 clears next_slicesmask -- should not be needed anymore,
1050         // but leave it just in case.
1051     case '0':
1052         next_slicesmask = 0;
1053         break;
1054 
1055     case '!':
1056         scramble_cb((void *)1);
1057         break;
1058 
1059     case '@':
1060         scramble_cb((void *)2);
1061         break;
1062 
1063     case '#':
1064         scramble_cb((void *)3);
1065         break;
1066 
1067     case '$':
1068         scramble_cb((void *)4);
1069         break;
1070 
1071     case '%':
1072         scramble_cb((void *)5);
1073         break;
1074 
1075     case '^':
1076         scramble_cb((void *)6);
1077         break;
1078 
1079     case '&':
1080         scramble_cb((void *)7);
1081         break;
1082 
1083     case '*':
1084         scramble_cb((void *)8);
1085         break;
1086 
1087     case '(':
1088         scramble_cb((void *)9);
1089         break;
1090 
1091     case 's':                   // "solve", which now means cheat
1092         cheat_cb(0);
1093         break;
1094 
1095     case 'S':                   // full scramble
1096         scramble_cb((void *)nscramblechen);
1097         break;
1098 
1099         /*
1100          * Simulate mouse motion since it wasn't implemented yet.
1101          * Actually maybe this is a convenient way of altering tilt and twirl.
1102          */
1103     case 'x':                   /* twirl 1 degree to the right */
1104         {
1105             real inc = preferences.getRealProperty(M4D_INC,
1106                                                    DEF_TWIST_INCREMENT);
1107             polymgr->incTwirl(DTOR(inc));
1108             polymgr->getUntwistedFrame(&untwisted_frame);
1109             machine->drawFrame(&untwisted_frame);
1110         }
1111         break;
1112     case 'X':                   /* twirl 1 degree to the left */
1113         {
1114             real inc = preferences.getRealProperty(M4D_INC,
1115                                                    DEF_TWIST_INCREMENT);
1116             polymgr->incTwirl(DTOR(-inc));
1117             polymgr->getUntwistedFrame(&untwisted_frame);
1118             machine->drawFrame(&untwisted_frame);
1119         }
1120         break;
1121     case 'y':                   /* tilt 1 degree forward */
1122         {
1123             real inc = preferences.getRealProperty(M4D_INC,
1124                                                    DEF_TWIST_INCREMENT);
1125             polymgr->incTilt(DTOR(inc));
1126             polymgr->getUntwistedFrame(&untwisted_frame);
1127             machine->drawFrame(&untwisted_frame);
1128         }
1129         break;
1130     case 'Y':                   /* tilt 1 degree backward */
1131         {
1132             real inc = preferences.getRealProperty(M4D_INC,
1133                                                    DEF_TWIST_INCREMENT);
1134             polymgr->incTilt(DTOR(-inc));
1135             polymgr->getUntwistedFrame(&untwisted_frame);
1136             machine->drawFrame(&untwisted_frame);
1137         }
1138         break;
1139 
1140     case 'S' - 'A' + 1:     /* save */
1141         save_cb(0);
1142         break;
1143 
1144     case 'P' - 'A' + 1:     /* print */
1145         print_cb(0);
1146         break;
1147 
1148     case 'D' - 'A' + 1:     /* testing state dump */
1149         printf("\n");
1150         puzzle_state->dump(stdout);
1151         break;
1152 
1153     case 'R' - 'A' + 1:     /* testing state read */
1154         printf("Enter state:\n");
1155         if (puzzle_state->read(stdin))
1156         {
1157             machine->drawFrame(&untwisted_frame);
1158             // FIX THIS--- currently no way to put the moves of a
1159             // scramble into history
1160             history->clear();
1161         }
1162         else
1163             std::cerr << "Couldn't read the state." << std::endl;
1164         break;
1165 
1166     default:
1167         printf("Got key '%c'(%d)\n", event->key, event->key);
1168     }
1169 }
1170 
1171 void
keyRelease_handler(Event * event,void *)1172 EventHandler::keyRelease_handler(Event* event, void *)
1173 {
1174     switch (event->key)
1175     {
1176         // 1 through 9 ors slices mask of next button event
1177     case '1':
1178     case '2':
1179     case '3':
1180     case '4':
1181     case '5':
1182     case '6':
1183     case '7':
1184     case '8':
1185     case '9':
1186         next_slicesmask &= ~(1 << (event->key - '1'));
1187         break;
1188 
1189     default:
1190         break;
1191     }
1192 }
1193 
1194 void
drag_handler(Event * event,void *)1195 EventHandler::drag_handler(Event *event, void *)
1196 {
1197     static real inc;
1198     if (inc == 0)
1199         inc = preferences.getRealProperty(M4D_INC, DEF_TWIST_INCREMENT);
1200     if (dragging && event->button == MIDDLEBUTTON && (event->dx || event->dy))
1201     {
1202         polymgr->incTilt(DTOR(event->dy * inc));
1203         polymgr->incTwirl(DTOR(event->dx * inc));
1204         polymgr->getUntwistedFrame(&untwisted_frame);
1205         machine->drawFrame(&untwisted_frame);
1206     }
1207 }
1208 
1209 #if 0
1210 void
1211 EventHandler::dumpSticker(struct stickerspec *sticker)
1212 {
1213     printf("\t");
1214     PRVEC4(%d, sticker->coords);
1215     printf("\t");
1216     PRINT(sticker->face);
1217     printf("\t");
1218     PRINT(sticker->dim);
1219     printf("\t");
1220     PRINT(sticker->id_within_cube);
1221     printf("\t");
1222     PRINT(sticker->id_within_face);
1223 }
1224 #endif
1225 
1226 // Local Variables:
1227 // c-basic-offset: 4
1228 // c-comment-only-line-offset: 0
1229 // c-file-offsets: ((defun-block-intro . +) (block-open . 0) (substatement-open . 0) (statement-cont . +) (statement-case-open . +4) (arglist-intro . +) (arglist-close . +) (inline-open . 0))
1230 // indent-tabs-mode: nil
1231 // End:
1232