text:
package com.google.gapid.widgets;

import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.google.gapid.models.Analytics;
import com.google.gapid.models.Analytics.View;
import com.google.gapid.util.Events;

import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.graphics.Region;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.widgets.Canvas;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Layout;
import org.eclipse.swt.widgets.Shell;

import java.util.Arrays;
import java.util.List;
import java.util.ListIterator;
import java.util.Objects;
import java.util.Set;
import java.util.function.Function;

public class TabComposite extends Composite {
  private static final int TITLE_WIDTH = 50;
  private static final int SEP_HEIGHT = 2;
  private static final int BAR_MARGIN = 6;
  private static final int TAB_MARGIN = 10;
  private static final int FOLDER_MARGIN = 5; // needs to be odd.
  private static final int ICON_SIZE = 24;
  private static final int MIN_WIDTH = 50;
  private static final int MIN_HEIGHT = 75;

  protected final Theme theme;
  private final Group group;
  private final Events.ListenerCollection<Listener> listeners = Events.listeners(Listener.class);
  private Folder maximizedFolder = null;
  protected Hover hovered = Hover.NONE;
  private Hover mouseDown = Hover.NONE;
  protected Dragger dragger = null;

  public TabComposite(Composite parent, Theme theme, boolean horizontal) {
    super(parent, SWT.BORDER | SWT.DOUBLE_BUFFERED);
    this.theme = theme;
    this.group = horizontal ? new HorizontalGroup(1) : new VerticalGroup(1);

    setLayout(new Layout() {
      @Override
      protected Point computeSize(Composite composite, int wHint, int hHint, boolean flushCache) {
        return layoutComputeSize(wHint, hHint, flushCache);
      }

      @Override
      protected void layout(Composite composite, boolean flushCache) {
        layoutLayout();
      }

      @Override
      protected boolean flushCache(Control control) {
        return true;
      }
    });

    addListener(SWT.Paint, e -> {
      getElement().draw(e.gc, false, false);
    });

    addListener(SWT.MouseDown, e -> {
      if (e.button == 1) {
        mouseDown = hovered;
        switch (mouseDown.type) {
          case Button:
            if (maximizedFolder == null) {
              mouseDown.folder.maximized = true;
              maximizedFolder = mouseDown.folder;
            } else {
              mouseDown.folder.maximized = false;
              maximizedFolder = null;
            }
            requestLayout();
            break;
          case Tab:
            if (mouseDown.folder.updateCurrent(mouseDown.tab.control)) {
              listeners.fire().onTabShown(mouseDown.tab.info);
            }
            break;
          default:
            // Do nothing.
        }
      }
    });

    addListener(SWT.MouseMove, e -> {
      switch (mouseDown.type) {
        case Tab:
          if (dragger == null) {
            dragger = new Dragger(theme, getShell(), getDisplay().map(this, null, getClientArea()),
                theme.tabFolderPlaceholderFill(), mouseDown);
            setCursor(getDisplay().getSystemCursor(SWT.CURSOR_SIZEALL));
            mouseDown.folder.redrawBar();
          }
          dragger.shell.setLocation(getDisplay().getCursorLocation());

          getElement().redrawBar(dragger.location.x, dragger.location.y, e.x, e.y);
          dragger.location.x = e.x;
          dragger.location.y = e.y;

          Hover current = group.find(group, 0, e.x, e.y, false);
          if (current.isFolder()) {
            Location location = getLocation(current.folder, e.x, e.y);
            if (location != null) {
              dragger.overlay.show(location.highlight(current.folder));
              dragger.shell.setActive();
            } else {
              dragger.overlay.hide();
            }
          } else {
            dragger.overlay.hide();
          }
          break;
        case Separator:
          mouseDown.group.moveSeparator(mouseDown.index, e.x, e.y);
          setRedraw(false);
          try {
            layoutLayout();
          } finally {
            setRedraw(true);
          }
          update();
          break;
        default:
          updateHover(getElement().find(group, 0, e.x, e.y, false));
      }
    });

    addListener(SWT.MouseUp, e -> {
      mouseDown = Hover.NONE;
      if (dragger != null) {
        Hover src = dragger.tab;
        Hover dst = getElement().find(group, 0, e.x, e.y, true);

        dragger.close();
        setCursor(null);
        dragger = null;

        switch (dst.type) {
          case Tab:
            listeners.fire().onTabMoved(src.tab.info);
            if (dst.tab == null) {
              src.folder.removeTab(src.tab);
              dst.folder.addTab(src.tab);
              src.folder.redrawBar();
              if (src.folder != dst.folder) {
                dst.folder.redrawBar();
              }
            } else if (src.folder != dst.folder) {
              src.folder.removeTab(src.tab);
              dst.folder.addTab(src.tab, dst.tab);
              src.folder.redrawBar();
              dst.folder.redrawBar();
            } else {
              dst.folder.moveTab(src.tab, dst.tab);
              src.folder.redrawBar();
            }
            group.merge();
            dst.folder.updateCurrent(src.tab.control);
            break;
          case Folder:
            Location location = getLocation(dst.folder, e.x, e.y);
            if (location != null) {
              Folder newFolder = dst.group.newSubFolder(location, dst.index);
              src.folder.removeTab(src.tab);
              newFolder.addTab(src.tab);
              src.folder.redrawBar();
              newFolder.redrawBar();
              group.merge();
              requestLayout();
            }
            break;
          default:
            // Do nothing.
        }
      }
      updateHover(getElement().find(group, 0, e.x, e.y, false));
    });

    addListener(SWT.MouseExit, e -> updateHover(Hover.NONE));
  }

  private static Location getLocation(Folder folder, int x, int y) {
    switch (3 * (x - folder.x) / folder.w) {
      case 0: return Location.Left;
      case 1:
        switch (3 * (y - folder.y) / folder.h) {
          case 0: return Location.Top;
          case 2: return Location.Bottom;
          default: return null;
        }
      case 2: return Location.Right;
      default: return null;
    }
  }

  private void updateHover(Hover newHover) {
    if (!hovered.isTab()) {
      if (newHover.isTab()) {
        newHover.folder.redrawBar();
      }
    } else if (!newHover.isTab()) {
      hovered.folder.redrawBar();
    } else if (hovered.folder != newHover.folder) {
      hovered.folder.redrawBar();
      newHover.folder.redrawBar();
    } else {
      newHover.folder.redrawBar();
    }
    hovered = newHover;

    if (hovered.isSeparator()) {
      setCursor(getDisplay().getSystemCursor(hovered.cursor));
    } else {
      setCursor(null);
    }
  }

  public Group getRoot() {
    return group;
  }

  public boolean showTab(Object id) {
    return group.showTab(id);
  }

  public void addTabToLargestFolder(TabInfo info) {
    group.addTabToLargestFolder(info);
  }

  public boolean disposeTab(Object id) {
    if (group.disposeTab(id)) {
      group.merge();
      return true;
    }
    return false;
  }

  public void visit(Visitor visitor) {
    group.visit(visitor);
  }

  public void addListener(Listener listener) {
    listeners.addListener(listener);
  }

  public void removeListener(Listener listener) {
    listeners.removeListener(listener);
  }

  protected Point layoutComputeSize(int wHint, int hHint, boolean flushCache) {
    int w = 0, h = 0;
    for (Control child : getChildren()) {
      Point size = child.computeSize(wHint, hHint, flushCache);
      w = Math.max(size.x, w);
      h = Math.max(size.y, h);
    }
    return new Point((wHint == SWT.DEFAULT) ? w : wHint, (hHint == SWT.DEFAULT) ? h : hHint);
  }

  protected void layoutLayout() {
    Rectangle size = getClientArea();
    Set<Control> controls = Sets.newIdentityHashSet();
    controls.addAll(Arrays.asList(getChildren()));
    getElement().setBounds(controls, 0, 0, size.width, size.height);
    for (Control child : controls) {
      child.setVisible(false);
    }
  }

  private Element getElement() {
    return (maximizedFolder == null) ? group : maximizedFolder;
  }

  /**
   * Information about a single tab in a folder.
   */
  public static class TabInfo {
    public final Object id;
    public final Analytics.View view;
    public final String label;
    public final Function<Composite, Control> contentFactory;

    public TabInfo(
        Object id, View view, String label, Function<Composite, Control> contentFactory) {
      this.id = id;
      this.view = view;
      this.label = label;
      this.contentFactory = contentFactory;
    }
  }

  @SuppressWarnings("unused")
  public interface Visitor {
    public default void group(boolean horizontal, int weight) { /* do nothing */ }
    public default void endGroup() { /* do nothing */ }
    public default void folder(int weight) { /* do nothing */ }
    public default void tab(TabInfo tab) { /* do nothing */ }
    public default void endFolder() { /* do nothing */ }
  }

  private abstract class Element {
    protected int x, y, w, h;
    protected int weight = -1;

    public Element() {
    }

    public abstract boolean showTab(Object id);
    public abstract void addTabToLargestFolder(TabInfo tab);
    public abstract boolean disposeTab(Object id);

    public abstract void setBounds(Set<Control> controls, int x, int y, int w, int h);

    protected void setBounds(int x, int y, int w, int h) {
      this.x = x;
      this.y = y;
      this.w = w;
      this.h = h;
    }

    protected boolean contains(int ex, int ey) {
      return ex >= x && ex < x + w && ey >= y && ey < y + h;
    }

    protected void draw(GC gc, boolean hBorder, boolean vBorder) {
      if (hBorder) {
        gc.setForeground(theme.tabFolderLine());
        gc.drawLine(x, y - FOLDER_MARGIN / 2 - 1, x + w, y - FOLDER_MARGIN / 2 - 1);
      }
      if (vBorder) {
        gc.setForeground(theme.tabFolderLine());
        gc.drawLine(x - FOLDER_MARGIN / 2 - 1, y, x - FOLDER_MARGIN / 2 - 1, y + h);
      }

      draw(gc);
    }

    protected abstract void draw(GC gc);

    protected abstract Hover find(Group parent, int index, int mx, int my, boolean includeTrailing);
    protected abstract void redrawBar(int x1, int y1, int x2, int y2);

    protected abstract MergeState merge();

    protected abstract void visit(Visitor visitor);
  }

  public abstract class Group extends Element {
    protected final List<Element> children = Lists.newArrayList();
    private final boolean childHBorder, childVBorder;

    public Group(int weight, boolean childHBorder, boolean childVBorder) {
      this.weight = weight;
      this.childHBorder = childHBorder;
      this.childVBorder = childVBorder;
    }

    public Folder newFolder(int folderWeight) {
      Folder folder = new Folder(folderWeight);
      children.add(folder);
      return folder;
    }

    public Group newGroup(int groupWeight) {
      Group result = createGroup(groupWeight);
      children.add(result);
      return result;
    }

    @Override
    public boolean showTab(Object id) {
      for (Element child : children) {
        if (child.showTab(id)) {
          return true;
        }
      }
      return false;
    }

    @Override
    public void addTabToLargestFolder(TabInfo tab) {
      int max = 0;
      for (int i = 1; i < children.size(); i++) {
        if (children.get(i).weight > children.get(max).weight) {
          max = i;
        }
      }
      children.get(max).addTabToLargestFolder(tab);
    }

    @Override
    public boolean disposeTab(Object id) {
      for (Element child : children) {
        if (child.disposeTab(id)) {
          return true;
        }
      }
      return false;
    }

    protected abstract Group createGroup(int newWeight);

    protected abstract Folder newSubFolder(Location location, int index);

    @Override
    public void setBounds(Set<Control> controls, int x, int y, int w, int h) {
      setBounds(x, y, w, h);

      if (children.size() == 1) {
        children.get(0).setBounds(controls, x, y, w, h);
      } else {
        int weightSum = 0;
        for (Element child : children) {
          if (child.weight <= 0) {
            weightSum = -1;
            break;
          }
          weightSum += child.weight;
        }

        setChildBounds(controls, weightSum);
      }
    }

    protected abstract void setChildBounds(Set<Control> controls, int weightTotal);

    @Override
    protected void draw(GC gc) {
      for (int i = 0; i < children.size(); i++) {
        children.get(i).draw(gc, childHBorder && i > 0, childVBorder && i > 0);
      }
    }

    @Override
    protected void redrawBar(int x1, int y1, int x2, int y2) {
      boolean firstDone = x1 < 0 || y1 < 0, secondDone = x2 < 0 || y2 < 0;
      if (firstDone && secondDone) {
        return;
      }

      for (Element child : children) {
        boolean first = child.contains(x1, y1);
        boolean second = child.contains(x2, y2);
        if (first && second) {
          child.redrawBar(x1, y1, x2, y2);
          return;
        } else if (first) {
          child.redrawBar(x1, y1, -1, -1);
          if (secondDone) {
            return;
          }
          firstDone = true;
        } else if (second) {
          child.redrawBar(x2, y2, -1, -1);
          if (firstDone) {
            return;
          }
          secondDone = true;
        }
      }
    }

    @Override
    protected Hover find(Group parent, int index, int mx, int my, boolean includeTrailing) {
      for (int i = 0; i < children.size(); i++) {
        Element child = children.get(i);
        if (mx >= child.x && mx < child.x + child.w &&
            my >= child.y && my < child.y + child.h) {
          return child.find(this, i, mx, my, includeTrailing);
        }
      }
      return Hover.NONE;
    }

    protected abstract void moveSeparator(int index, int sx, int sy);

    @Override
    protected MergeState merge() {
      for (ListIterator<Element> it = children.listIterator(); it.hasNext(); ) {
        Element current = it.next();
        MergeState state = current.merge();
        if (state == MergeState.REMOVE) {
          it.remove();
        } else if (state == MergeState.DO_NOTHING) {
          // Do nothing.
        } else {
          if (state.replacement instanceof Folder) {
            state.replacement.weight = current.weight;
            it.set(state.replacement);
          } else {
            // The current child (C) is a group where it's only child is also a group (G). Thus,
            // C is superfluous and can be removed. However, G can not just become our child, since
            // it has the same horizontal vs. vertical layout as us, while our children must have
            // the opposite from us. This does mean, however, that G, too, is superfluous and all
            // it's children - our great-grand-children - can just become our children.
            it.remove(); // Has to be done before we add any new children.
            int totalWeight = 0;
            for (Element child : ((Group)state.replacement).children) {
              it.add(child);
              totalWeight += child.weight;
            }
            for (Element child : ((Group)state.replacement).children) {
              child.weight = (int)((child.weight * current.weight) / (double)totalWeight);
            }
          }
        }
      }

      switch (children.size()) {
        case 0: return MergeState.REMOVE;
        case 1: return MergeState.replace(children.get(0));
        default: return MergeState.DO_NOTHING;
      }
    }
  }

  private static enum Location {
    Left, Right, Top, Bottom;

    public Rectangle highlight(Folder f) {
      switch (this) {
        case Left:   return new Rectangle(f.x, f.y, f.w / 3, f.h);
        case Right:  return new Rectangle(f.x + 2 * f.w / 3, f.y, f.w / 3, f.h);
        case Top:    return new Rectangle(f.x, f.y, f.w, f.h / 3);
        case Bottom: return new Rectangle(f.x, f.y + 2 * f.h / 3, f.w, f.h / 3);
        default: throw new AssertionError();
      }
    }
  }

  private class HorizontalGroup extends Group {
    public HorizontalGroup(int weight) {
      super(weight, false, true);
    }

    @Override
    protected void setChildBounds(Set<Control> controls, int weightTotal) {
      int cw = w - (children.size() - 1) * FOLDER_MARGIN;
      if (weightTotal <= 0) {
        int fw = cw / children.size();
        int rem = cw % children.size();
        for (int i = 0, fx = x; i < children.size(); i++, rem--) {
          int add = rem > 0 ? 1 : 0;
          children.get(i).setBounds(controls, fx, y, fw + add, h);
          children.get(i).weight = fw;
          fx += fw + add + FOLDER_MARGIN;
        }
      } else {
        int diff = 0;
        if (weightTotal != cw) {
          for (Element child : children) {
            child.weight = (int)((child.weight * cw) / (double)weightTotal);
            diff += child.weight;
          }
          diff = cw - diff;
          if (diff >= children.size()) {
            for (Element child : children) {
              child.weight++;
            }
            diff -= children.size();
          }
        }
        for (int i = 0, fx = x; i < children.size(); i++, diff--) {
          int add = diff > 0 ? 1 : 0;
          int nw = children.get(i).weight + add;
          children.get(i).setBounds(controls, fx, y, nw, h);
          fx += nw + FOLDER_MARGIN;
        }
      }
    }

    @Override
    protected Group createGroup(int newWeight) {
      return new VerticalGroup(newWeight);
    }

    @Override
    protected Folder newSubFolder(Location location, int index) {
      switch (location) {
        case Left: {
          int nw = children.get(index).weight /= 2;
          Folder folder = new Folder(nw);
          children.add(index, folder);
          return folder;
        }
        case Right: {
          int nw = children.get(index).weight /= 2;
          Folder folder = new Folder(nw);
          children.add(index + 1, folder);
          return folder;
        }
        case Top: {
          Element old = children.get(index);
          Group g = createGroup(old.weight);
          children.set(index, g);
          Folder folder = g.newFolder(old.weight);
          g.children.add(old);
          return folder;
        }
        case Bottom:{
          Element old = children.get(index);
          Group g = createGroup(old.weight);
          children.set(index, g);
          g.children.add(old);
          return g.newFolder(old.weight);
        }
        default:
          throw new AssertionError();
      }
    }

    @Override
    protected Hover find(Group parent, int index, int mx, int my, boolean includeTrailing) {
      Hover result = super.find(parent, index, mx, my, includeTrailing);
      if (result == Hover.NONE && children.size() > 1) {
        Element before = children.get(0);
        for (int i = 1; i < children.size(); i++) {
          Element now = children.get(i);
          if (mx >= before.x + before.w && mx < now.x) {
            return Hover.separator(this, i, SWT.CURSOR_SIZEWE);
          }
          before = now;
        }
      }
      return result;
    }

    @Override
    protected void moveSeparator(int index, int sx, int sy) {
      Element before = children.get(index - 1);
      Element after = children.get(index);
      int newBeforeW = sx - FOLDER_MARGIN / 2 - before.x;
      int newAfterW = after.x + after.w - sx - FOLDER_MARGIN / 2 - 1;
      if (newBeforeW >= MIN_WIDTH && newAfterW >= MIN_WIDTH) {
        before.weight = newBeforeW;
        after.weight = newAfterW;
      }
    }

    @Override
    protected void visit(Visitor visitor) {
      visitor.group(true, weight);
      for (Element child : children) {
        child.visit(visitor);
      }
      visitor.endGroup();
    }
  }

  private class VerticalGroup extends Group {
    public VerticalGroup(int weight) {
      super(weight, true, false);
    }

    @Override
    protected void setChildBounds(Set<Control> controls, int weightTotal) {
      int ch = h - (children.size() - 1) * FOLDER_MARGIN;
      if (weightTotal <= 0) {
        int fh = ch / children.size();
        int rem = ch % children.size();
        for (int i = 0, fy = y; i < children.size(); i++, rem--) {
          int add = rem > 0 ? 1 : 0;
          children.get(i).setBounds(controls, x, fy, w, fh + add);
          children.get(i).weight = fh;
          fy += fh + add + FOLDER_MARGIN;
        }
      } else {
        int diff = 0;
        if (weightTotal != ch) {
          for (Element child : children) {
            child.weight = (int)((child.weight * ch) / (double)weightTotal);
            diff += child.weight;
          }
          diff = ch - diff;
          if (diff >= children.size()) {
            for (Element child : children) {
              child.weight++;
            }
            diff -= children.size();
          }
        }
        for (int i = 0, fy = y; i < children.size(); i++, diff--) {
          int add = diff > 0 ? 1 : 0;
          int nh = children.get(i).weight + add;
          children.get(i).setBounds(controls, x, fy, w, nh);
          fy += nh + FOLDER_MARGIN;
        }
      }
    }

    @Override
    protected Group createGroup(int newWeight) {
      return new HorizontalGroup(newWeight);
    }

    @Override
    protected Folder newSubFolder(Location location, int index) {
      switch (location) {
        case Left: {
          Element old = children.get(index);
          Group g = createGroup(old.weight);
          children.set(index, g);
          Folder folder = g.newFolder(old.weight);
          g.children.add(old);
          return folder;
        }
        case Right:{
          Element old = children.get(index);
          Group g = createGroup(old.weight);
          children.set(index, g);
          g.children.add(old);
          return g.newFolder(old.weight);
        }
        case Top: {
          int nw = children.get(index).weight /= 2;
          Folder folder = new Folder(nw);
          children.add(index, folder);
          return folder;
        }
        case Bottom: {
          int nw = children.get(index).weight /= 2;
          Folder folder = new Folder(nw);
          children.add(index + 1, folder);
          return folder;
        }
        default:
          throw new AssertionError();
      }
    }

    @Override
    protected Hover find(Group parent, int index, int mx, int my, boolean includeTrailing) {
      Hover result = super.find(parent, index, mx, my, includeTrailing);
      if (result == Hover.NONE && children.size() > 1) {
        Element before = children.get(0);
        for (int i = 1; i < children.size(); i++) {
          Element now = children.get(i);
          if (my >= before.y + before.h && my < now.y) {
            return Hover.separator(this, i, SWT.CURSOR_SIZENS);
          }
          before = now;
        }
      }
      return result;
    }

    @Override
    protected void moveSeparator(int index, int sx, int sy) {
      Element before = children.get(index - 1);
      Element after = children.get(index);
      int newBeforeH = sy - FOLDER_MARGIN / 2 - before.y;
      int newAfterH = after.y + after.h - sy - FOLDER_MARGIN / 2 - 1;
      if (newBeforeH >= MIN_HEIGHT && newAfterH >= MIN_HEIGHT) {
        before.weight = newBeforeH;
        after.weight = newAfterH;
      }
    }

    @Override
    protected void visit(Visitor visitor) {
      visitor.group(false, weight);
      for (Element child : children) {
        child.visit(visitor);
      }
      visitor.endGroup();
    }
  }

  public class Folder extends Element {
    private final List<Tab> tabs = Lists.newArrayList();
    private int titleHeight;
    private Control current;
    protected boolean maximized;

    public Folder(int weight) {
      this.weight = weight;
    }

    public void newTab(TabInfo info) {
      GC gc = new GC(TabComposite.this);
      gc.setFont(theme.selectedTabTitleFont());
      Point size = gc.textExtent(info.label);
      gc.dispose();

      addTab(new Tab(info, info.contentFactory.apply(TabComposite.this), size));
    }

    @Override
    public boolean showTab(Object id) {
      for (Tab tab : tabs) {
        if (Objects.equals(tab.info.id, id)) {
          updateCurrent(tab.control);
          return true;
        }
      }
      return false;
    }

    @Override
    public void addTabToLargestFolder(TabInfo tab) {
      newTab(tab);
    }

    @Override
    public boolean disposeTab(Object id) {
      for (Tab tab : tabs) {
        if (Objects.equals(tab.info.id, id)) {
          removeTab(tab);
          tab.control.dispose();
          return true;
        }
      }
      return false;
    }

    @Override
    public void setBounds(Set<Control> controls, int x, int y, int w, int h) {
      redrawBar(); // redraw the old area

      setBounds(x, y, w, h);
      this.titleHeight = getMaxTitleHeight();

      int barH = BAR_MARGIN + titleHeight + BAR_MARGIN + SEP_HEIGHT + BAR_MARGIN;
      for (Tab tab : tabs) {
        tab.control.setBounds(x, y + barH, w, h - barH);
        tab.control.setVisible(tab.control == current);
        controls.remove(tab.control);
      }

      redrawBar(); // redraw the new area
    }

    private int getMaxTitleHeight() {
      int height = 0;
      for (Tab tab : tabs) {
        height = Math.max(height, tab.titleSize.y);
      }
      return height;
    }

    protected void addTab(Tab tab) {
      tabs.add(tab);
      if (current == null) {
        current = tab.control;
      }
    }

    protected void addTab(Tab tab, Tab before) {
      for (int i = 0; i < tabs.size(); i++) {
        if (tabs.get(i) == before) {
          tabs.add(i, tab);
          return;
        }
      }
    }

    protected void moveTab(Tab from, Tab to) {
      for (int i = 0; i < tabs.size(); i++) {
        if (tabs.get(i) == to) {
          tabs.remove(from);
          tabs.add(i, from);
          return;
        }
      }
    }

    protected void removeTab(Tab tab) {
      tabs.remove(tab);
      if (current == tab.control) {
        current = tabs.isEmpty() ? null : tabs.get(0).control;
        requestLayout();
      }
      redrawBar();
    }

    protected boolean updateCurrent(Control newCurrent) {
      if (current != newCurrent) {
        current = newCurrent;
        requestLayout();
        redrawBar();
        return true;
      }
      return false;
    }

    @Override
    protected void redrawBar(int x1, int y1, int x2, int y2) {
      int tabH = BAR_MARGIN + titleHeight + BAR_MARGIN;
      if ((x1 >= x && x1 < x + w && y1 >= y && y1 < y + tabH) ||
          (x2 >= x && x2 < x + w && y2 >= y && y2 < y + tabH)) {
        redrawBar();
      }
    }

    void redrawBar() {
      redraw(x, y, w, BAR_MARGIN + titleHeight + BAR_MARGIN + SEP_HEIGHT + BAR_MARGIN, false);
    }

    @Override
    protected void draw(GC gc) {
      int tabH = BAR_MARGIN + titleHeight + BAR_MARGIN;

      gc.setBackground(theme.tabBackgound());
      gc.fillRectangle(x, y, w, tabH + SEP_HEIGHT);

      gc.setForeground(theme.tabFolderLine());
      gc.drawLine(x, y + tabH + SEP_HEIGHT - 1, x + w, y + tabH + SEP_HEIGHT - 1);

      gc.setClipping(x, y, w - ICON_SIZE, h);

      int tabX = x;
      for (Tab tab : tabs) {
        int tabW = TAB_MARGIN + Math.max(TITLE_WIDTH, tab.titleSize.x) + TAB_MARGIN;

        if (dragger != null) {
          if (dragger.tab.tab == tab) {
            continue;
          } else if (dragger.contains(tabX, y, tabW, tabH)) {
            drawPlaceholder(gc, tabX, y, tabW);
            tabX += tabW;
          }
        }

        if (hovered != null && tab == hovered.tab) {
          gc.setBackground(theme.tabFolderHovered());
          gc.fillRectangle(tabX, y, tabW, tabH + 1);
        } else if (tab.control == current) {
          gc.setBackground(theme.tabFolderSelected());
          gc.fillRectangle(tabX, y, tabW, tabH + 1);
        }

        int dx = (tabW - tab.titleSize.x) / 2;
        gc.setForeground(theme.tabTitle());
        if (tab.control == current) {
          gc.setBackground(theme.tabFolderLineSelected());
          gc.fillRectangle(tabX, y + tabH, tabW, SEP_HEIGHT);
          gc.setFont(theme.selectedTabTitleFont());
          gc.drawText(tab.info.label, tabX + dx, y + BAR_MARGIN, SWT.DRAW_TRANSPARENT);
          gc.setFont(null);
        } else {
          gc.drawText(tab.info.label, tabX + dx, y + BAR_MARGIN, SWT.DRAW_TRANSPARENT);
        }

        tabX += tabW;
      }

      if (dragger != null &&
          dragger.location.x >= tabX && dragger.location.x < x + w &&
          dragger.location.y >= y && dragger.location.y < y + tabH) {
        int tabW = Math.max(TITLE_WIDTH, dragger.tab.tab.titleSize.x);
        drawPlaceholder(gc, tabX, y, tabW);
      }

      gc.setClipping((Rectangle)null);

      gc.drawImage(maximized ? theme.fullscreenExit() : theme.fullscreen(),
          x + w - ICON_SIZE, y + (tabH - ICON_SIZE) / 2);
    }

    private void drawPlaceholder(GC gc, int px, int py, int pw) {
      gc.setBackground(theme.tabFolderPlaceholderFill());
      gc.setForeground(theme.tabFolderPlaceholderStroke());
      gc.fillRectangle(px, py, pw, 2 * BAR_MARGIN + titleHeight + 1);
      gc.drawRectangle(px, py, pw, 2 * BAR_MARGIN + titleHeight + 1);
    }

    @Override
    protected Hover find(Group parent, int index, int mx, int my, boolean includeTrailing) {
      int tabH = BAR_MARGIN + titleHeight + BAR_MARGIN;
      if (mx < x || mx >= x + w || my < y || my >= y + h) {
        return Hover.NONE;
      } else if (my >= y + tabH) {
        return Hover.folder(parent, index, this);
      }

      if (mx >= x + w - ICON_SIZE) {
        return Hover.button(this);
      }

      int tabX = x;
      for (Tab tab : tabs) {
        int tabW = TAB_MARGIN + Math.max(TITLE_WIDTH, tab.titleSize.x) + TAB_MARGIN;
        if (mx >= tabX && mx < tabX + tabW) {
          return Hover.tab(this, tab);
        }
        tabX += tabW;
      }
      return includeTrailing ? Hover.tab(this, null) : Hover.NONE;
    }

    @Override
    protected MergeState merge() {
      return tabs.isEmpty() ? MergeState.REMOVE : MergeState.DO_NOTHING;
    }

    @Override
    protected void visit(Visitor visitor) {
      visitor.folder(weight);
      for (Tab tab : tabs) {
        visitor.tab(tab.info);
      }
      visitor.endFolder();
    }
  }

  public static interface Listener extends Events.Listener {
    public void onTabShown(TabInfo tab);
    public void onTabMoved(TabInfo tab);
  }

  private static class Tab {
    public final TabInfo info;
    public final Control control;
    public final Point titleSize;

    public Tab(TabInfo info, Control control, Point titleSize) {
      this.info = info;
      this.control = control;
      this.titleSize = titleSize;
    }
  }

  private static class Hover {
    public static final Hover NONE = new Hover(Type.None, null, 0, 0, null, null);

    public final Type type;
    public final Group group;
    public final int index;
    public final int cursor;
    public final Folder folder;
    public final Tab tab;

    private Hover(Type type, Group group, int index, int cursor, Folder folder, Tab tab) {
      this.type = type;
      this.group = group;
      this.index = index;
      this.cursor = cursor;
      this.folder = folder;
      this.tab = tab;
    }

    public static Hover separator(Group group, int index, int cursor) {
      return new Hover(Type.Separator, group, index, cursor, null, null);
    }

    public static Hover button(Folder folder) {
      return new Hover(Type.Button, null, 0, 0, folder, null);
    }

    public static Hover folder(Group parent, int index, Folder folder) {
      return new Hover(Type.Folder, parent, index, 0, folder, null);
    }

    public static Hover tab(Folder folder, Tab tab) {
      return new Hover(Type.Tab, null, 0, 0, folder, tab);
    }

    public boolean isSeparator() {
      return type == Type.Separator;
    }

    public boolean isFolder() {
      return type == Type.Folder;
    }

    public boolean isTab() {
      return type == Type.Tab;
    }

    public static enum Type {
      None, Separator, Button, Folder, Tab;
    }
  }

  private static class MergeState {
    public static final MergeState DO_NOTHING = new MergeState(null);
    public static final MergeState REMOVE = new MergeState(null);

    public final Element replacement;

    private MergeState(Element replacement) {
      this.replacement = replacement;
    }

    public static MergeState replace(Element replacement) {
      return new MergeState(replacement);
    }
  }

  private static class Overlay {
    private final Shell shell;
    private Region region = null;

    public Overlay(Shell parent, Rectangle bounds, Color bg) {
      this.shell = new Shell(parent, SWT.NO_TRIM | SWT.MODELESS | SWT.NO_FOCUS | SWT.ON_TOP);

      shell.setBounds(bounds);
      shell.setEnabled(false);
      shell.setAlpha(128);
      shell.setBackground(bg);
    }

    public void hide() {
      shell.setVisible(false);
    }

    public void show(Rectangle highlight) {
      if (region != null) {
        region.dispose();
      }

      region = new Region();
      region.add(highlight);
      shell.setRegion(region);
      shell.setEnabled(false);
      shell.setVisible(true);
    }

    public void close() {
      shell.dispose();
      if (region != null) {
        region.dispose();
      }
    }
  }

  private static class Dragger {
    public final Overlay overlay;
    public final Shell shell;
    public final Hover tab;
    public final Point location = new Point(-1, -1);

    public Dragger(Theme theme, Shell parent, Rectangle bounds, Color bg, Hover tab) {
      this.overlay = new Overlay(parent, bounds, bg);
      this.shell = new Shell(parent, SWT.NO_TRIM | SWT.MODELESS | SWT.NO_FOCUS | SWT.ON_TOP);
      this.tab = tab;

      shell.setLayout(new FillLayout());
      shell.setSize(tab.tab.titleSize.x + 2 * TAB_MARGIN, tab.tab.titleSize.y + 2 * BAR_MARGIN);
      shell.setEnabled(false);

      Canvas canvas = new Canvas(shell, SWT.NONE);
      canvas.addListener(SWT.Paint, e -> {
        e.gc.setFont(theme.selectedTabTitleFont());
        e.gc.drawText(tab.tab.info.label, TAB_MARGIN, BAR_MARGIN, SWT.DRAW_TRANSPARENT);
      });

      shell.setVisible(true);
    }

    public boolean contains(int x, int y, int w, int h) {
      return location.x >= 0 && location.y >= 0 &&
          x <= location.x && x + w > location.x &&
          y <= location.y && y + h > location.y;
    }

    public void close() {
      overlay.close();
      shell.dispose();
    }
  }
}


If your lotion does not remain white when you rub it into your skin then you have used too much color. If product significantly thins you have used too much fragrance.
Water, Cetearyl Alcohol, Glyceryl Stearate, Polysorbate-60, Stearyl Alcohol, Sunflower Oil, Allantoin, Propylene Glycol, Isopropyl Palmitate, Petrolatum, Diazolidinyl Urea, Methylparaben, Propylparaben, BHT, Carbomer, Benzophenone-4, Stearic Acid.
Packaging in Jars: Measure base and place in a mixing container. Add 1% cosmetic fragrance oil or 1/3% essential oil. Add cosmetic color as desired. Mix well. Place in jars, apply top and label.
Packaging in Bottles: Measure base and place in Pyrex cup. Heat in microwave on very low, intermittent heat. Once liquid, add 1% cosmetic fragrance oil or 1/3% essential oil. Add cosmetic color as desired. Mix well. Pour into bottles. Allow to cool to room temperature before applying top. Label.
COMMENTS: We started a new small business and used this lotion base, it has been very great and our customers love the feel of the lotion.


text:
'BAD BOYS FOR LIFE' REVIEW: WORTH THE WAIT
Directors: Adil El Arbi, Bilall Fallah
Writers: Chris Bremner, Peter Craig, Joe Carnahan
Stars: Will Smith, Alexander Ludwig, Vanessa Hudgens, Martin Lawrence, Joe Pantoliano
Running Time: 2h 3m / Classification: Rated R
The third installment of the Bad Boys franchise just hit theaters worldwide and let's just say, it's been a long time coming!
The last time audiences followed the adventures of officers/ best friends Mike Lowrey (Will Smith) and Marcus Burnett (Martin Lawrence) was in 2003 when Bad Boys II was released. Michael Bay directed that sequel which grossed $273 million worldwide.
Bad Boys For Life follows the duo as they reunite once again when a mysterious assassin (played by Jacob Scipio) murders multiple people involved in an old police case. Lowrey is also gunned down but he survives. After six months of hospital recovery, the officer is eager to find the man who put him there. Burnett is not on board at first. He vowed to leave his violent ways behind if his best friend pulls through. But when their beloved police captain takes a hit, uncle Mike and now pop pop Marcus (his daughter gives birth to a boy in an earlier scene, and Reggie is the father!) team up one last time to put end to the wave of homicides and to take down whoever is responsible.
The film co-stars Vanessa Hudgens, Alexander Ludwig, Charles Melton, Paola Núñez, Kate del Castillo, Nicky Jam, Bianca Bethune and Joe Pantoliano.
The chemistry between Smith and Lawrence is undeniable and carries a lot of the movie. Their sense of humor and special bond translates well through their characters. All of it feels authentic. The jokes are not overexaggerated or forced, just enough to make you enjoy them. You will laugh at them trolling each other and will be touched by their I-always-got-your-back-type friendship.
The action sequences in this latest installment are more lucid than in its predecessors. Slashed throats, bullets penetrating torsos, point-blank shots to the face – directors Adil El Arbi and Bilall Fallah (who are best known for directing the TV series Snowfall) did not hold back. Demolished buildings, burning helicopters, shoot outs, explosions, high-speed car chases – action junkies will be satisfied.
At some point in the movie, the Bad Boys team up with a younger, elite team called AMMO (which stands for Advanced Miami Metro Operations). The tech-savvy youngsters are clearly juxtaposed to Lowrey and Burnett who are used to handling things the old way. Their constant clashing finds its resolve when the agents face the mastermind behind the series of killings. Her name is Isabel Aretas (played by Kate del Castillo) and she has a bone to pick with Lowrey. We learn that many years ago, even before the Bad Boys, Lowrey was on an undercover mission that resulted in him putting the "only woman he ever loved" (Isabel) in jail. And… here we are introduced to the plot twist (we will keep it a secret for now), which felt a bit forced but definitely a surprise. Its placement is kind of a necessary step to unveil the hidden secrets, evoke an emotional response on Lowrey's part, and, ultimately, conclude the storyline (specifically, for Bad Boys For Life, not the entire franchise). The after-credit scene suggests there might be a Bad Boys 4 coming.
Bad Boys For Life is entertaining and action-packed. Its been 17 years but it's like they never left! If you are a fan of Bad Boys and Bad Boys II, then you will enjoy this third (but apparently not final) installment.
Related Topics:Alexander LudwigBad BoysBad Boys For LifeCharles MeltonHollywood MelaninJoe PantolianoKate del CastilloMartin LawrenceNicky JamPaola NúñezVanessa HudgensWill Smith
NICO SANTOS (SUPERSTORE, NBC)
"CHARM CITY KINGS": NEW TRAILER RELEASED
'Bel-Air': Premiere Date For Peacock's Reimagining of 'The Fresh Prince of Bel Air' Announced

redpajama_set_name: RedPajamaCommonCrawl


text:
Q: Using openOCD to extract ETB traces on Cortex-M (STM32F4) STM32F4 discovery (Cortex-M4) has ETB, a buffer storing instruction traces. How can I use OpenOCD and on-chip st-link debugger to pull out the traces from ETB?
I am little confused between SWO/SWD. What should I be using? Also, do I need any additional hardware for extracting traces?
Thank you

A: I'm afraid I never heard of
STM32F4
including an Embedded Trace Buffer (ETB) in the implemented subset of the
ARM core
and its
CoreSight features.
I think this is because ETB is an optional feature, and ST has decided not to configure/implement this ETB option in its STM32F4 controllers and the ARM core they embed.
I looked up the programming/reference manuals and datasheet of an upper-level representative of STM32F4xx family, and I didn't find anything about ETB, which seems to confirm this assumption.

Now, ETB is not the only option if one wants to stream trace data out of one's MCU:

*

*STM32F4 controllers all have an Instruction Trace Macrocell (ITM), which can alternatively deliver a software-defined char output stream or snapshots of data values or the program counter that are collected either at your breakpoints or just periodically, with assistance by the Data Watchpoint/Trace (DWT) unit.
You can use the ITM

*

*to instrument the application with character output (printf())

*to profile your application

*to inspect certain properties/state flows of your software by tracing program breakpoints or data watchpoints

The ITM is usable through the SWO pin, using any adapter hardware like any version of ST-Link, j-Link, uLink-* etc.
It is a proper trace interface since it works without stopping the CPU at breakpoints, so examination won't break your system's real-time properties.

*Many STM32F4 controllers (AFAIK, those with >= 100 pins) include an Embedded Trace macrocell (ETM), which is able to trace program counter (PC) and data of every CPU cycle, so you can use this one to trace the entire control flow (and data flow) of your controller, also without stopping it at any breakpoint.
The humongous amount of data to be traced (make sure you have a free USB3 port...) can only be delivered in a useful way through the synchronous port interface around the GPIOE group (alternate functions TRACECLK+TRACED0/1/2/3 => 5 pins in total), which is connected to the Trace Port Interface Unit (TPIU) next to the ETM.
In order to use this technology, you need the more expensive variants of debug adapters like j-Trace, uLink-Pro or Lauterbach. The cheapest ETM-capable adapter I'm aware of (haven't used it yet, though) is QTrace by PDQlogic starting around £379. The others are available for about 1-4 k£/k€/k$.

The way your question sounds tells me that you probably just started programming STM32's. Therefore I recommend you to get a development board with an embedded ST-Link inside. This is the cheapest solution to get (SWD debug running first, and then) SWO trace running. Atollic blog has a nice intro how to do that quickly.

redpajama_set_name: RedPajamaStackExchange


10:14 PM - Rivergoer mentioned boraz in post Terrible in snow.
03:04 PM - Rivergoer mentioned boraz in post anybody get chevy vr goggles in the mail?
08:54 PM - MP81 mentioned boraz in post What did you do to your Cruze today?
04:18 PM - boraz mentioned Robby in post DPF behavior I have observed.
boraz - so what happened?
07:42 PM - boraz mentioned diesel in post Broken exhaust leading to Particulate Sensor and NOx2 codes?
.... Maybe @Tomko or @boraz can help.
09:36 PM - Ma v e n quoted boraz in post Hey GM...I might need this in my life. With a 2.0T please.


I've been taking photographs since the days of the Kodak Instamatic, and have had complete Nikon 35mm systems, Pentax medium format camera and lenses, Hasselblad equipment, and then digital equipment. The Canon equipment I use now is the best I have ever used, but it is the experience of taking and composing photographs for decades that is key.
I love composing, whether it is composing a new garden design or composing a compelling photograph. The prime consideration is finding that angle, finding the light and finding the approach to the project that fits the subject. On top of that, I want to make it fun while we're at it.
I title this section "About Us". I'm the experienced man pictured on this page who holds the camera when we meet, but it is not just "About Me". The reality is that when you and I work together, it does become "Us." The subject and photographer work in unison and we get wonderful results.
I hold a business license, a psychologist license, a pesticide license, a driver's license, a nursery license, a contractor license, and most importantly, the license to have fun and explore possibilities and make your portrait session special. Thank goodness there is not a state agency for that!


text:
Makoto Moroi (jap. , Moroi Makoto, * 17. Dezember 1930 in der Präfektur Tokio; † 2. September 2013) war ein japanischer Komponist.

Werdegang
Der Sohn des Komponisten Saburō Moroi studierte Komposition bei Tomojirō Ikenouchi. Er unterrichtete Gregorianik am Musikinstitut von Hiroshima und Komposition am Konservatorium von Tokio.

Er komponierte eine Sinfonie, ein Klavierkonzert, eine konzertante Suite für Violine und Orchester, drei Kammerkantaten, fünf "Orchesterkompositionen", Werke für elektronische Instrumente und Vokalwerke mit unterschiedlicher Instrumentalbegleitung. Seine Kompositionen stehen in der Tradition von Anton Webern.

Werke
 1951 Musica da camera n. 3 für Flöte, Oboe, Klarinette, Fagott, Horn und Bratsche
 1952 Komposition Nr. 1 für Orchester
 1953 Partita für Flöte allein
 1956 α und β für Klavier
 1956 Klavierstück 1956
 1956 Variations sur 7 (mit Toshiro Mayuzumi)
 1957 Développements raréfiants für Sopran und Ensemble
 1958 Transfiguration (Metamorphosis) (Elektronische Musik)
 1958 Ordre für Violoncello und Klavier
 1959 Cantata da camera (Kammerkantate) n. 1 für Chor und Kammerensemble
 1959 Cantata da camera n. 2 für Chor und Kammerensemble
 1962 Suite concertante für Violine und Orchester
 1962 Varieté (Elektronische Musik)
 1963 Cinq épigrammes (Fünf Epigramme) für Flöte, Klarinette, Vibraphon, Celesta, Harfe, Violine und Cello
 1964 Chikurai: Five Pieces for Shakuhachi (Fünf Sätze)
 1964 Five Dialogues (Fünf Dialoge) für zwei Shakuhachi
 1966 Konzert für Klavier und Orchester
 1966 Sonatine für Klavier
 1968 Symphonie
 1969 Five Metamorphic Strata für Shakuhachi, Sangen, Koto und Jushichigen
 1970 Waga Izumo für Sopran, Bariton, Chor, Orchester und elektronische Instrumente
 1971 3 Concerto Movements für Shakuhachi, Schlagzeug und Streicher
 1975 Bouquets de sonatine für Klavier
 1975 Duo concertate "Chikurai" für Shakuhachi und 17-gen (oder 20-gen)
 1977 Phantasie und Fuge für Orgel über JoHAnn SEBAStiAn Bach
 1979 Lamenta '79 für Shakuhachi, Klavier und Streicher
 1992 A Contradiction within a Contradiction: Contradiction IV
 2002 Hommage à JSB (Partita '02) für Flöte allein

Literatur

Weblinks
 Makoto MOROI

Einzelnachweise

Komponist (Japan)
Komponist klassischer Musik (20. Jahrhundert)
Japaner
Geboren 1930
Gestorben 2013
Mann

redpajama_set_name: RedPajamaWikipedia


Andy Burton, Founder and Director, Cloud Industry Forum will discuss the pitfalls and opportunities as markets expand and competition intensifies. He will also share what he thinks customers should demand from their vendor relationships.
Further Information and to Register for Your Complimentary Place (Use Code CIFVIP).


I have posted this one three times since 2009 and I never tire of it … the images, the fabulous memories, and the unmatched wonder of a child's world all grown up.
Enjoy if you will my first Christmas re-run.
Today I give you my homage to the magnificent edifice of Rockefeller Plaza and my cherished memories of the Radio City Music Hall.
As a child, I cannot remember a Christmas when we did not go to The City, walk around Rockefeller Plaza to see the tree, and the show at the Music Hall.
I cherish those moments spent with my mother, Mary Fois, and her two friends, Josephine Chiappe and Beatrice Napoli. These were my mom's cohorts, confederates, her closest and dearest friends, and they were, among the six women who became our chaperones, the greatest fun to play with.
The Radio City Music Hall was to my mother the holy grail of events. Not wind, nor storm nor dead of night could keep her from her appointed mission, to herd eight to ten children with her two girlfriends as point and rear guards to the Christmas and Easter shows at the Music Hall.
We gathered at dawn with blankets, pillows, coffee, hot chocolate, and buttered rolls, and took the long subway ride from Brooklyn to Rockefeller Center. Soon the early hour and the motion of the trains, lulled us to sleep, resting our heads on each other's shoulders or their laps.
With a round of hands clapping, we woke at our destination, yawned and rubbed our eyes … we were almost there.
Walking towards Rockefeller Center was always a delight. We were no longer sleepy or hungry and turned slowly in a circle to capture all the sights and sounds around us.
Rockefeller Center is an art deco marvel consisting of nineteen commercial buildings covering eleven acres in midtown Manhattan from Forty-Ninth Street to Fifty-Second Street, from Fifth Avenue to Seventh Avenue, with smaller buildings in a rectangle.
On the first floor of these buildings are exclusive shops, their windows lit up and decorated for the holiday season. The tall building in the middle, Thirty Rockefeller Plaza, or as it is called, 30 Rock, looms over a golden sculpture of Prometheus which sits below the giant tree as a symbol of opulence for tourists and native New Yorker's to enjoy.
At Christmas the tree-lined pathways of the arcade are decked out in their holiday finest and lead to the giant tree in the middle and along the pathway from Fifth Avenue, the row of Herald Angels.
Hundreds of excited children and their parents mulled in the enormous vestibule of the theater enjoying the spectacle of it all.
The huge triple wide staircase lined with thick, rich carpeting, the sparkle of the chandeliers, the vendors selling their wares, the beautiful color program guides the parents purchased for each child, ushers dressed in formal wear and the giant Wurlitzer organ playing holiday tunes.
The majestic stage is encased in a dome in shades of gold liken to a sunset, a golden curtain across the back. The rows of seats curve upwards from the bottom of the stage … over five thousand soft, wide seats that push back for comfort.
The three women rushed to the middle to give each of the children, even the shortest, a grand view of the show. Carefully the mothers took hats, gloves and coats and began folding and stuffing clothes in their large bags. For an added measure they put pillows and coats under the shortest children and stashed the rest under the seats.
The Christmas show at the Radio City Music Hall is an extravaganza of incredible proportions with live animals onstage and the Rockettes in synchronized, syncopation, organ music and a movie.
We sat in one long row with the three mothers positioned at the beginning, the middle and the end. It was hard not to admire their organization, their stamina and the den-mother patience with each trip to the bathroom, spitting up, the constant and persistent flow of questions, giggles, interruptions and tantrums of eight children aged six to twelve.
As the show began to unfold, we became uncharacteristically quiet and still. Not wanting to miss one second as the stage moved up and down in three parts, revealing Christmas scenes like real ice skating, the Nativity with Joseph pulling Mother Mary on a real donkey, and a giant tree, rising from below to the squeals of the audience.
In the darkened theater we sat mesmerized by the sounds, the lights and the best of all, the Rockettes as they slowly began their final routine; arms and legs in perfect unity, kicking one, two and three … one, two and three … kicking and circling … kicking and fanning the length of the magnificent stage … adults and children, babies and old women fascinated by the perfection in their dance.
There were three more trips to the bathroom with the youngest children as the Wurlitzer was winding up intermission. When we heard the announcer warn the audience to come back to their seats, each child got the second half of their chocolate bar. Then the final delight, a full length movie.
My fondest memory, the year White Christmas premiered at the Music Hall.
Thanks to Peter Allen, a wonderful Broadway entertainer and many of his friends and patrons of the arts, the Music Hall was saved from the wrecking ball, saved from the same fate as the old Metropolitan Opera House, and preserved for generations of adults and children to enjoy.
In Loving Memory, to Mary, Josephine and Beatrice.
My trio of angels. Together again.
person who remains in your heart?
What a wonderful description of your younger years at Christmas time. I love the picture of the stage and the seats. It must have been awesome to experience as a little kid. We have an old movie theater that has had many different lives since I was young. It went from a beautiful art deco movie theater to a gymnasium to being many others I can't recall then someone purchased it and renovated it to its original state. I now can go back inside and see how beautiful it still is and recall sitting in the balcony with my girlfriends at 15 years old, watching movies and giggling and eating candy. My kids get to experience what I once did. Kinda cool.
Ah, Patti. That is more than super cool. To be in your hometown long enough to see something be reborn and have the joy of sharing it with your kids … how great is that?
I look forward to these every year, especially this post! Thank you again for the virtual tour of New York!
Oh Florence, these are wonderful pictures and make me want to visit NYC during the holidays, although I am sooo not a cold weather person. I love the shows and have dreamed of seeing the Rockettes. Happy holidays!
Actually, Vicki. NYC is downstate and our fall and Christmas seasons are usually very mild. I would often make my yearly trek to the tree and the store windows with nothing more than a blazer.
One of my favorites. It reminds me of the wonderful day trips my family took from Connecticut to NYC at the holidays when I was small.. Always to shop at B. Altman, FAO Schwartz, and Macy's. Then to lunch at Rockefeller Center to watch the skaters…then the show with the Rockettes! The highlight of the Christmas season for me!
Well, Anne … here we go again. It's such a joy that we can share similar memories of The City. I am a bit saddened. B.Altman and FAO Schwartz are both gone. The City we once roamed has changed. But we all owe a debt of gratitude to Peter Allen and Jackie Onasis for preserving some of our greatest sights. Him, the Music Hall and she, Grand Central Station.
What a wonderful post . . . my 15yo daughter got to visit NYC for the first time last month & I showed her your blog before she left 🙂 My one special angel was my grandmother on my mom's side. Sadly, she passed away at 62 from cancer, but I guess God needed her home for something.
How exciting, Jamie. Exciting and delightful that your 15yo got to experience NYC up close and personal. She'll be able to tell her friends … it's better in person than in the movies.
Sorry that you lost your angel but glad you have her during your childhood. That's when we need our grandmothers the most.


When you place an order on www.magictruffles.com we ask you to provide personal data. In this section we specify what data we ask and for what purpose we use it.
The products on www.magictruffles.com are not sold to persons under 18 years old. We ask you to provide your date of birth to determine whether you can purchase our products.
Optionally, you can also give us permission to use your email address for marketing purposes. You give us permission for this by ticking the appropriate box during the ordering process. Thereby you give us permission to send you a newsletter on a regular basis, as well as permission to send you notifications about discounts and other promotions. After you have registered for this service, you can unsubscribe from these messages at any time. You do this by clicking "unsubscribe" in one of the emails you no longer wish to receive.
To place an order on www.magictruffles.com you can create a personal account. We store the data provided by you for as long as you continue to actively use your account. Accounts that have not been used for more than 26 months, which means that no order has been placed during a continuous period of 26 months, are considered inactive accounts. Inactive accounts are deleted. If your account has been deleted and you want to place a new order, you can create a new account, if desired with the same personal data. You have the right to have your account removed from our database. You can send an email to info@magictruffles.com to request the deletion of your account. Your account will then be removed from our database as soon as possible, but in any case within 72 hours.
You have the right to access, modify and delete your personal data. By logging in to your personal account you can view your personal details and modify them if necessary. If you wish to have your account deleted from our database you can send an e-mail with this request to info@magictruffles.com. Your account will then be removed from our system as soon as possible, but in any case within 72 hours.
You have the right to submit a complaint to the Dutch Data Protection Authority when you have a complaint about the way we process or have processed your personal data. You can do this via the website of the Dutch Data Protection Authority. Of course we would prefer it if you could first contact us in case of complaints. To get in touch, please send an email to info@magictruffles.com.
To place an order on our website it is necessary to agree to our terms and conditions. To do so, you can tick the appropriate box during the ordering process. Your agreement to our terms and conditions also means that you agree with our privacy statement, which describes how we process your personal data. You can change your consent to the processing of your personal data at any time. To do so, please send an e-mail to info@magictruffles.com. Please note that we need permission to use your personal data to be able to process your order.
Being able to guarantee your privacy is a priority to us. If you have any questions about how we process your personal data, please contact us at info@magictruffles.com.