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