<commit_before><?php

namespace ws\loewe\Woody\Event;

use \ws\loewe\Utils\Http\HttpGetRequest;
use \ws\loewe\Woody\Components\Controls\Actionable;
use \ws\loewe\Woody\Components\Controls\Control;

/**
 * This class is responsible for creating events from the raw winbinder event data. Furhtermore, it dispatched the
 * created events to listeneres registered at the respective source of the event.
 */
class EventFactory {
  /**
   * the control that just had the focus before
   *
   * @var Control
   */
  private static $previousFocusedControl = null;

  /**
   * This method creates a collection of events based in the event info object that is given as input.
   *
   * @param EventInfo $eventInfo the event info object containing the raw event information
   * @return \Traversable
   */
  public static function createEvent(EventInfo $eventInfo) {
    $events = new \ArrayObject();

    // window close button is not a real control, so handle it here - close window
    if($eventInfo->isWindowCloseEvent()) {
      $events[] = self::createWindowClosedEvent($eventInfo);
    }

    // events being triggered when pressing ENTER or ESC in controls
    else if($eventInfo->isOkEvent() || $eventInfo->isCancelEvent()) {
      // ignore for now
    }

    // timeout of timers are handled here, too - the callback is executed by calling Timer::run
    else if($eventInfo->isTimerEvent()) {
      $events[] = self::createTimeoutEvent($eventInfo);
    }

    else if($eventInfo->isFocusEvent()) {
      $events[] = self::createFocusEvent($eventInfo);
    }

    else if($eventInfo->isMouseEvent()) {
      $events[] = self::createMouseEvent($eventInfo);
    }

    else if($eventInfo->isKeyEvent()) {
      $events[] = self::createKeyEvent($eventInfo);
    }

    // create and dispatch WindowEvents, e.g. when resizing a window
    else if($eventInfo->isWindowEvent()) {
      if($eventInfo->isWindowResizeEvent()) {
        $events[] = self::createWindowResizeEvent($eventInfo);
      }
    }

    if($eventInfo->isControlEvent()) {
      // only handle non-focus-events of controls (FocusEvent was handled above and does not trigger Actions)
      if(!$eventInfo->isFocusEvent()) {
        $events[] = self::createActionEvent($eventInfo);
      }
    }

    // app crashes when an ActionEvent was created and $event was returned here !?!?!?!
    return $events;
  }

  private static function createFocusEvent($eventInfo) {
    $event = new FocusEvent($eventInfo, self::$previousFocusedControl);

    self::$previousFocusedControl = $event->getSource();

    return $event;
  }

  private static function createMouseEvent($eventInfo) {
    $event = new MouseEvent($eventInfo);

    return $event;
  }

  private static function createKeyEvent($eventInfo) {
    return new KeyEvent($eventInfo);
  }

  // no other events are raised when, e.g., clicking on a button, or selecting from a listbox, therefore, AcionEvents
  // are needed
  // TODO: for EditBox, an ActionEvent would be FocusLost + content changed = onChangeEvent
  private static function createActionEvent($eventInfo) {
    // trigger action events for all Actionables, as well as when the propertyID actually is a HttpGetRequest
    // the later are special events, namely those raised by HTMLControls when they receive new data
    // @see HtmlControlServer::processClient
    $event = new ActionEvent($eventInfo);
    if($event->getSource() instanceof Actionable || $event->property instanceof HttpGetRequest) {
      return $event;
    }

    return null;
  }

  private static function createWindowResizeEvent($eventInfo) {
    $event = new WindowResizeEvent($eventInfo);

    // update the dimension of the Woody object here (the actual component is already resized)
    if($event->getSource() !== null)
      $event->getSource()->resizeTo($event->getNewDimension());

    return $event;
  }

  private static function createWindowClosedEvent($eventInfo) {
    return new WindowCloseEvent($eventInfo);
  }

  private static function createTimeoutEvent($eventInfo) {
    return new TimeoutEvent($eventInfo);
  }
}<commit_msg>added workaround for the fact, that table control emit too many events (due to bugs in winbinder library)<commit_after><?php

namespace ws\loewe\Woody\Event;

use ArrayObject;
use Traversable;
use ws\loewe\Utils\Http\HttpGetRequest;
use ws\loewe\Woody\Components\Controls\Actionable;
use ws\loewe\Woody\Components\Controls\Control;
use ws\loewe\Woody\Components\Controls\Table;

/**
 * This class is responsible for creating events from the raw winbinder event data. Furhtermore, it dispatched the
 * created events to listeneres registered at the respective source of the event.
 */
class EventFactory {
  /**
   * the control that just had the focus before
   *
   * @var Control
   */
  private static $previousFocusedControl = null;

  /**
   * a mapping from control id of a table control to the index of the currently selected column
   * in that table control, needed to filter superfluous events generated by the winbinder library
   *
   * @var array
   */
  private static $selectedTableColumns = array();

  /**
   * This method creates a collection of events based in the event info object that is given as input.
   *
   * @param EventInfo $eventInfo the event info object containing the raw event information
   * @return Traversable
   */
  public static function createEvent(EventInfo $eventInfo) {
    $events = new ArrayObject();

    // window close button is not a real control, so handle it here - close window
    if($eventInfo->isWindowCloseEvent()) {
      $events[] = self::createWindowClosedEvent($eventInfo);
    }

    // events being triggered when pressing ENTER or ESC in controls
    else if($eventInfo->isOkEvent() || $eventInfo->isCancelEvent()) {
      // ignore for now
    }

    // timeout of timers are handled here, too - the callback is executed by calling Timer::run
    else if($eventInfo->isTimerEvent()) {
      $events[] = self::createTimeoutEvent($eventInfo);
    }

    else if($eventInfo->isFocusEvent()) {
      $events[] = self::createFocusEvent($eventInfo);
    }

    else if($eventInfo->isMouseEvent()) {
      $events[] = self::createMouseEvent($eventInfo);
    }

    else if($eventInfo->isKeyEvent()) {
      $events[] = self::createKeyEvent($eventInfo);
    }

    // create and dispatch WindowEvents, e.g. when resizing a window
    else if($eventInfo->isWindowEvent()) {
      if($eventInfo->isWindowResizeEvent()) {
        $events[] = self::createWindowResizeEvent($eventInfo);
      }
    }

    if(!($eventInfo->source instanceof Table) && self::$tableSelectionEventBuffer instanceof ActionEvent) {
      $events[] = self::$tableSelectionEventBuffer;
      self::$tableSelectionEventBuffer = new NoOpEvent($eventInfo);
    }

    if($eventInfo->isControlEvent()) {
      // only handle non-focus-events of controls (FocusEvent was handled above and does not trigger Actions)
      if(!$eventInfo->isFocusEvent()) {
        $events[] = self::createActionEvent($eventInfo);
      }
    }

    // app crashes when an ActionEvent was created and $event was returned here !?!?!?!
    return $events;
  }

  private static function createFocusEvent($eventInfo) {
    $event = new FocusEvent($eventInfo, self::$previousFocusedControl);

    self::$previousFocusedControl = $event->getSource();

    return $event;
  }

  private static function createMouseEvent($eventInfo) {
    $event = new MouseEvent($eventInfo);

    return $event;
  }

  private static function createKeyEvent($eventInfo) {
    return new KeyEvent($eventInfo);
  }

  private static $tableSelectionEventBuffer = null;


  // TODO: for EditBox, an ActionEvent would be FocusLost + content changed = onChangeEvent
  private static function createActionEvent($eventInfo) {
    $event = new ActionEvent($eventInfo);

    // swallow superfluous events created by the winbinder library
    if($event->getSource() instanceof Table) {
      self::$tableSelectionEventBuffer = $event;
      return new NoOpEvent($eventInfo);
    }

    // only Actionables and HttpGetRequests can trigger action events, the rest just emits NoOpEvents
    if(!(($event->getSource() instanceof Actionable) || ($event->property instanceof HttpGetRequest))) {
      return new NoOpEvent($eventInfo);
    }

    return $event;
  }

  private static function createWindowResizeEvent($eventInfo) {
    $event = new WindowResizeEvent($eventInfo);

    // update the dimension of the Woody object here (the actual component is already resized)
    if($event->getSource() !== null) {
      $event->getSource()->resizeTo($event->getNewDimension());
    }

    return $event;
  }

  private static function createWindowClosedEvent($eventInfo) {
    return new WindowCloseEvent($eventInfo);
  }

  private static function createTimeoutEvent($eventInfo) {
    return new TimeoutEvent($eventInfo);
  }

  private static function isRedundantTableSelectionEvent(Table $table) {
    if(!isset(self::$selectedTableColumns[$table->getControlID()])) {
      self::$selectedTableColumns[$table->getControlID()] = -1;
    }
    echo(PHP_EOL.(date('H:i:s')).' received potential list view event [self::$selectedTableColumns['.$table->getControlID().'] = '.self::$selectedTableColumns[$table->getControlID()].']');

    if($table->getSelectedIndex()->count() === 0) {
      echo("... an empty-selection event");
    }

    else {
      echo(".. an selection event with indizes ".(implode(", ", $table->getSelectedIndex()->getArrayCopy())));
    }


    if($table->getSelectedIndex()->count() === 0) {
      var_dump("\nskipping (empty selection) event of list view");
      self::$selectedTableColumns[$table->getControlID()] = -1;
      return true;
    }
    if(self::$selectedTableColumns[$table->getControlID()] == $table->getSelectedIndex()[0]) {
      var_dump("\nskipping (redundant) event of list view");
      return true;
    }

    self::$selectedTableColumns[$table->getControlID()] = $table->getSelectedIndex()[0];
    echo(PHP_EOL.(date('H:i:s')).' returning list view event, setting self::$selectedTableColumns['.$table->getControlID().'] = '.self::$selectedTableColumns[$table->getControlID()]);
    return false;
  }
}
{'fraction_non_alphanumeric': 0.12055626285378514, 'fraction_numerical': 0.0005876016061110567, 'mean_word_length': 3.760839160839161, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 58, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2002314', 'n_tokens_mistral': 2839, 'n_tokens_neox': 2785, 'n_words': 822}