<commit_before>// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License.  You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include <sys/mount.h>

#include <glog/logging.h>

#include <process/future.hpp>
#include <process/id.hpp>

#include <stout/foreach.hpp>
#include <stout/fs.hpp>
#include <stout/option.hpp>
#include <stout/path.hpp>
#include <stout/stringify.hpp>
#include <stout/strings.hpp>

#include <stout/os/exists.hpp>
#include <stout/os/mkdir.hpp>
#include <stout/os/stat.hpp>
#include <stout/os/touch.hpp>

#include "common/validation.hpp"

#ifdef __linux__
#include "linux/fs.hpp"
#endif // __linux__

#include "slave/containerizer/mesos/isolators/volume/sandbox_path.hpp"

using std::string;
using std::vector;

using process::ErrnoFailure;
using process::Failure;
using process::Future;
using process::Owned;

using mesos::slave::ContainerClass;
using mesos::slave::ContainerConfig;
using mesos::slave::ContainerLaunchInfo;
using mesos::slave::ContainerMountInfo;
using mesos::slave::ContainerState;
using mesos::slave::Isolator;

namespace mesos {
namespace internal {
namespace slave {

Try<Isolator*> VolumeSandboxPathIsolatorProcess::create(
    const Flags& flags)
{
  bool bindMountSupported = false;

  if (flags.launcher == "linux" &&
      strings::contains(flags.isolation, "filesystem/linux")) {
    bindMountSupported = true;
  }

  Owned<MesosIsolatorProcess> process(
      new VolumeSandboxPathIsolatorProcess(flags, bindMountSupported));

  return new MesosIsolator(process);
}

VolumeSandboxPathIsolatorProcess::VolumeSandboxPathIsolatorProcess(
    const Flags& _flags,
    bool _bindMountSupported)
  : ProcessBase(process::ID::generate("volume-sandbox-path-isolator")),
    flags(_flags),
    bindMountSupported(_bindMountSupported) {}

VolumeSandboxPathIsolatorProcess::~VolumeSandboxPathIsolatorProcess() {}

bool VolumeSandboxPathIsolatorProcess::supportsNesting()
{
  return true;
}

bool VolumeSandboxPathIsolatorProcess::supportsStandalone()
{
  return true;
}

Future<Nothing> VolumeSandboxPathIsolatorProcess::recover(
    const vector<ContainerState>& states,
    const hashset<ContainerID>& orphans)
{
  foreach (const ContainerState& state, states) {
    sandboxes[state.container_id()] = state.directory();
  }

  return Nothing();
}

Future<Option<ContainerLaunchInfo>> VolumeSandboxPathIsolatorProcess::prepare(
    const ContainerID& containerId,
    const ContainerConfig& containerConfig)
{
  // Remember the sandbox location for each container (including
  // nested). This information is important for looking up sandbox
  // locations for parent containers.
  sandboxes[containerId] = containerConfig.directory();

  if (!containerConfig.has_container_info()) {
    return None();
  }

  const ContainerInfo& containerInfo = containerConfig.container_info();

  if (containerInfo.type() != ContainerInfo::MESOS) {
    return Failure("Only support MESOS containers");
  }

  if (!bindMountSupported && containerConfig.has_rootfs()) {
    return Failure(
        "The 'linux' launcher and 'filesystem/linux' isolator must be "
        "enabled to change the rootfs and bind mount");
  }

  ContainerLaunchInfo launchInfo;

  foreach (const Volume& volume, containerInfo.volumes()) {
    // NOTE: The validation here is for backwards compatibility. For
    // example, if an old master (no validation code) is used to
    // launch a task with a volume.
    Option<Error> error = common::validation::validateVolume(volume);
    if (error.isSome()) {
      return Failure("Invalid volume: " + error->message);
    }

    Option<Volume::Source::SandboxPath> sandboxPath;

    // NOTE: This is the legacy way of specifying the Volume. The
    // 'host_path' can be relative in legacy mode, representing
    // SANDBOX_PATH volumes.
    if (volume.has_host_path() &&
        !path::absolute(volume.host_path())) {
      sandboxPath = Volume::Source::SandboxPath();
      sandboxPath->set_type(Volume::Source::SandboxPath::SELF);
      sandboxPath->set_path(volume.host_path());
    }

    if (volume.has_source() &&
        volume.source().has_type() &&
        volume.source().type() == Volume::Source::SANDBOX_PATH) {
      CHECK(volume.source().has_sandbox_path());

      if (path::absolute(volume.source().sandbox_path().path())) {
        return Failure(
            "Path '" + volume.source().sandbox_path().path() + "' "
            "in SANDBOX_PATH volume is absolute");
      }

      sandboxPath = volume.source().sandbox_path();
    }

    if (sandboxPath.isNone()) {
      continue;
    }

    if (containerConfig.has_container_class() &&
        containerConfig.container_class() == ContainerClass::DEBUG) {
      return Failure(
          "SANDBOX_PATH volume is not supported for DEBUG containers");
    }

    if (!bindMountSupported && path::absolute(volume.container_path())) {
      return Failure(
          "The 'linux' launcher and 'filesystem/linux' isolator "
          "must be enabled to support SANDBOX_PATH volume with "
          "absolute container path");
    }

    // TODO(jieyu): We need to check that source resolves under the
    // work directory because a user can potentially use a container
    // path like '../../abc'.

    if (!sandboxPath->has_type()) {
      return Failure("Unknown SANDBOX_PATH volume type");
    }

    // Prepare the source.
    string source;
    string sourceRoot; // The parent directory of 'source'.

    switch (sandboxPath->type()) {
      case Volume::Source::SandboxPath::SELF:
        // NOTE: For this case, the user can simply create a symlink
        // in its sandbox. No need for a volume.
        if (!path::absolute(volume.container_path())) {
          return Failure(
              "'container_path' is relative for "
              "SANDBOX_PATH volume SELF type");
        }

        sourceRoot = containerConfig.directory();
        source = path::join(sourceRoot, sandboxPath->path());
        break;
      case Volume::Source::SandboxPath::PARENT:
        if (!containerId.has_parent()) {
          return Failure(
              "SANDBOX_PATH volume PARENT type "
              "only works for nested container");
        }

        if (!sandboxes.contains(containerId.parent())) {
          return Failure(
              "Failed to locate the sandbox for the parent container");
        }

        sourceRoot = sandboxes[containerId.parent()];
        source = path::join(sourceRoot, sandboxPath->path());
        break;
      default:
        return Failure("Unknown SANDBOX_PATH volume type");
    }

    // NOTE: Chown should be avoided if the 'source' directory already
    // exists because it may be owned by some other user and should
    // not be mutated.
    if (!os::exists(source)) {
      Try<Nothing> mkdir = os::mkdir(source);
      if (mkdir.isError()) {
        return Failure(
            "Failed to create the directory '" + source + "' "
            "in the sandbox: " + mkdir.error());
      }

      // Get 'sourceRoot''s user and group info for the source path.
      struct stat s;

      if (::stat(sourceRoot.c_str(), &s) < 0) {
        return ErrnoFailure("Failed to stat '" + sourceRoot + "'");
      }

      LOG(INFO) << "Changing the ownership of the SANDBOX_PATH volume at '"
                << source << "' with UID " << s.st_uid << " and GID "
                << s.st_gid;

      Try<Nothing> chown = os::chown(s.st_uid, s.st_gid, source, false);
      if (chown.isError()) {
        return Failure(
            "Failed to change the ownership of the SANDBOX_PATH volume at '" +
            source + "' with UID " + stringify(s.st_uid) + " and GID " +
            stringify(s.st_gid) + ": " + chown.error());
      }
    }

    // Prepare the target.
    string target;

    if (path::absolute(volume.container_path())) {
      CHECK(bindMountSupported);

      if (containerConfig.has_rootfs()) {
        target = path::join(
            containerConfig.rootfs(),
            volume.container_path());

        if (os::stat::isdir(source)) {
          Try<Nothing> mkdir = os::mkdir(target);
          if (mkdir.isError()) {
            return Failure(
                "Failed to create the mount point at "
                "'" + target + "': " + mkdir.error());
          }
        } else {
          // The file (regular file or device file) bind mount case.
          Try<Nothing> mkdir = os::mkdir(Path(target).dirname());
          if (mkdir.isError()) {
            return Failure(
                "Failed to create directory "
                "'" + Path(target).dirname() + "' "
                "for the mount point: " + mkdir.error());
          }

          Try<Nothing> touch = os::touch(target);
          if (touch.isError()) {
            return Failure(
                "Failed to touch the mount point at "
                "'" + target + "': " + touch.error());
          }
        }
      } else {
        target = volume.container_path();

        // An absolute 'container_path' must already exist if the
        // container rootfs is the same as the host. This is because
        // we want to avoid creating mount points outside the work
        // directory in the host filesystem.
        if (!os::exists(target)) {
          return Failure(
              "Mount point '" + target + "' is an absolute path. "
              "It must exist if the container shares the host filesystem");
        }
      }

      // TODO(jieyu): We need to check that target resolves under
      // 'rootfs' because a user can potentially use a container path
      // like '/../../abc'.
    } else {
      CHECK_EQ(Volume::Source::SandboxPath::PARENT, sandboxPath->type());

      if (containerConfig.has_rootfs()) {
        target = path::join(
            containerConfig.rootfs(),
            flags.sandbox_directory,
            volume.container_path());
      } else {
        target = path::join(
            containerConfig.directory(),
            volume.container_path());
      }

      // Create the mount point if bind mount is used.
      // NOTE: We cannot create the mount point at 'target' if
      // container has rootfs defined. The bind mount of the sandbox
      // will hide what's inside 'target'. So we should always create
      // the mount point in the sandbox.
      if (bindMountSupported) {
        const string mountPoint = path::join(
            containerConfig.directory(),
            volume.container_path());

        if (os::stat::isdir(source)) {
          Try<Nothing> mkdir = os::mkdir(mountPoint);
          if (mkdir.isError()) {
            return Failure(
                "Failed to create the mount point at "
                "'" + mountPoint + "': " + mkdir.error());
          }
        } else {
          // The file (regular file or device file) bind mount case.
          Try<Nothing> mkdir = os::mkdir(Path(mountPoint).dirname());
          if (mkdir.isError()) {
            return Failure(
                "Failed to create the directory "
                "'" + Path(mountPoint).dirname() + "' "
                "for the mount point: " + mkdir.error());
          }

          Try<Nothing> touch = os::touch(mountPoint);
          if (touch.isError()) {
            return Failure(
                "Failed to touch the mount point at "
                "'" + mountPoint+ "': " + touch.error());
          }
        }
      }
    }

    if (bindMountSupported) {
#ifdef __linux__
      LOG(INFO) << "Mounting SANDBOX_PATH volume from "
                << "'" << source << "' to '" << target << "' "
                << "for container " << containerId;

      ContainerMountInfo* mount = launchInfo.add_mounts();
      mount->set_source(source);
      mount->set_target(target);
      mount->set_flags(MS_BIND | MS_REC);
#endif // __linux__
    } else {
      LOG(INFO) << "Linking SANDBOX_PATH volume from "
                << "'" << source << "' to '" << target << "' "
                << "for container " << containerId;

      Try<Nothing> symlink = ::fs::symlink(source, target);
      if (symlink.isError()) {
        return Failure(
            "Failed to symlink '" + source + "' -> '" + target + "'"
            ": " + symlink.error());
      }
    }
  }

  return launchInfo;
}

Future<Nothing> VolumeSandboxPathIsolatorProcess::cleanup(
    const ContainerID& containerId)
{
  // Remove the current container's sandbox path from `sandboxes`.
  sandboxes.erase(containerId);

  return Nothing();
}

} // namespace slave {
} // namespace internal {
} // namespace mesos {
<commit_msg>Updated `volume/sandbox_path` isolator to honor volume mode.<commit_after>// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License.  You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include <sys/mount.h>

#include <glog/logging.h>

#include <process/future.hpp>
#include <process/id.hpp>

#include <stout/foreach.hpp>
#include <stout/fs.hpp>
#include <stout/option.hpp>
#include <stout/path.hpp>
#include <stout/stringify.hpp>
#include <stout/strings.hpp>

#include <stout/os/exists.hpp>
#include <stout/os/mkdir.hpp>
#include <stout/os/stat.hpp>
#include <stout/os/touch.hpp>

#include "common/validation.hpp"

#ifdef __linux__
#include "linux/fs.hpp"
#endif // __linux__

#include "slave/containerizer/mesos/isolators/volume/sandbox_path.hpp"

using std::string;
using std::vector;

using process::ErrnoFailure;
using process::Failure;
using process::Future;
using process::Owned;

using mesos::slave::ContainerClass;
using mesos::slave::ContainerConfig;
using mesos::slave::ContainerLaunchInfo;
using mesos::slave::ContainerMountInfo;
using mesos::slave::ContainerState;
using mesos::slave::Isolator;

namespace mesos {
namespace internal {
namespace slave {

Try<Isolator*> VolumeSandboxPathIsolatorProcess::create(
    const Flags& flags)
{
  bool bindMountSupported = false;

  if (flags.launcher == "linux" &&
      strings::contains(flags.isolation, "filesystem/linux")) {
    bindMountSupported = true;
  }

  Owned<MesosIsolatorProcess> process(
      new VolumeSandboxPathIsolatorProcess(flags, bindMountSupported));

  return new MesosIsolator(process);
}

VolumeSandboxPathIsolatorProcess::VolumeSandboxPathIsolatorProcess(
    const Flags& _flags,
    bool _bindMountSupported)
  : ProcessBase(process::ID::generate("volume-sandbox-path-isolator")),
    flags(_flags),
    bindMountSupported(_bindMountSupported) {}

VolumeSandboxPathIsolatorProcess::~VolumeSandboxPathIsolatorProcess() {}

bool VolumeSandboxPathIsolatorProcess::supportsNesting()
{
  return true;
}

bool VolumeSandboxPathIsolatorProcess::supportsStandalone()
{
  return true;
}

Future<Nothing> VolumeSandboxPathIsolatorProcess::recover(
    const vector<ContainerState>& states,
    const hashset<ContainerID>& orphans)
{
  foreach (const ContainerState& state, states) {
    sandboxes[state.container_id()] = state.directory();
  }

  return Nothing();
}

Future<Option<ContainerLaunchInfo>> VolumeSandboxPathIsolatorProcess::prepare(
    const ContainerID& containerId,
    const ContainerConfig& containerConfig)
{
  // Remember the sandbox location for each container (including
  // nested). This information is important for looking up sandbox
  // locations for parent containers.
  sandboxes[containerId] = containerConfig.directory();

  if (!containerConfig.has_container_info()) {
    return None();
  }

  const ContainerInfo& containerInfo = containerConfig.container_info();

  if (containerInfo.type() != ContainerInfo::MESOS) {
    return Failure("Only support MESOS containers");
  }

  if (!bindMountSupported && containerConfig.has_rootfs()) {
    return Failure(
        "The 'linux' launcher and 'filesystem/linux' isolator must be "
        "enabled to change the rootfs and bind mount");
  }

  ContainerLaunchInfo launchInfo;

  foreach (const Volume& volume, containerInfo.volumes()) {
    // NOTE: The validation here is for backwards compatibility. For
    // example, if an old master (no validation code) is used to
    // launch a task with a volume.
    Option<Error> error = common::validation::validateVolume(volume);
    if (error.isSome()) {
      return Failure("Invalid volume: " + error->message);
    }

    Option<Volume::Source::SandboxPath> sandboxPath;

    // NOTE: This is the legacy way of specifying the Volume. The
    // 'host_path' can be relative in legacy mode, representing
    // SANDBOX_PATH volumes.
    if (volume.has_host_path() &&
        !path::absolute(volume.host_path())) {
      sandboxPath = Volume::Source::SandboxPath();
      sandboxPath->set_type(Volume::Source::SandboxPath::SELF);
      sandboxPath->set_path(volume.host_path());
    }

    if (volume.has_source() &&
        volume.source().has_type() &&
        volume.source().type() == Volume::Source::SANDBOX_PATH) {
      CHECK(volume.source().has_sandbox_path());

      if (path::absolute(volume.source().sandbox_path().path())) {
        return Failure(
            "Path '" + volume.source().sandbox_path().path() + "' "
            "in SANDBOX_PATH volume is absolute");
      }

      sandboxPath = volume.source().sandbox_path();
    }

    if (sandboxPath.isNone()) {
      continue;
    }

    if (containerConfig.has_container_class() &&
        containerConfig.container_class() == ContainerClass::DEBUG) {
      return Failure(
          "SANDBOX_PATH volume is not supported for DEBUG containers");
    }

    if (!bindMountSupported && path::absolute(volume.container_path())) {
      return Failure(
          "The 'linux' launcher and 'filesystem/linux' isolator "
          "must be enabled to support SANDBOX_PATH volume with "
          "absolute container path");
    }

    // TODO(jieyu): We need to check that source resolves under the
    // work directory because a user can potentially use a container
    // path like '../../abc'.

    if (!sandboxPath->has_type()) {
      return Failure("Unknown SANDBOX_PATH volume type");
    }

    // Prepare the source.
    string source;
    string sourceRoot; // The parent directory of 'source'.

    switch (sandboxPath->type()) {
      case Volume::Source::SandboxPath::SELF:
        // NOTE: For this case, the user can simply create a symlink
        // in its sandbox. No need for a volume.
        if (!path::absolute(volume.container_path())) {
          return Failure(
              "'container_path' is relative for "
              "SANDBOX_PATH volume SELF type");
        }

        sourceRoot = containerConfig.directory();
        source = path::join(sourceRoot, sandboxPath->path());
        break;
      case Volume::Source::SandboxPath::PARENT:
        if (!containerId.has_parent()) {
          return Failure(
              "SANDBOX_PATH volume PARENT type "
              "only works for nested container");
        }

        if (!sandboxes.contains(containerId.parent())) {
          return Failure(
              "Failed to locate the sandbox for the parent container");
        }

        sourceRoot = sandboxes[containerId.parent()];
        source = path::join(sourceRoot, sandboxPath->path());
        break;
      default:
        return Failure("Unknown SANDBOX_PATH volume type");
    }

    // NOTE: Chown should be avoided if the 'source' directory already
    // exists because it may be owned by some other user and should
    // not be mutated.
    if (!os::exists(source)) {
      Try<Nothing> mkdir = os::mkdir(source);
      if (mkdir.isError()) {
        return Failure(
            "Failed to create the directory '" + source + "' "
            "in the sandbox: " + mkdir.error());
      }

      // Get 'sourceRoot''s user and group info for the source path.
      struct stat s;

      if (::stat(sourceRoot.c_str(), &s) < 0) {
        return ErrnoFailure("Failed to stat '" + sourceRoot + "'");
      }

      LOG(INFO) << "Changing the ownership of the SANDBOX_PATH volume at '"
                << source << "' with UID " << s.st_uid << " and GID "
                << s.st_gid;

      Try<Nothing> chown = os::chown(s.st_uid, s.st_gid, source, false);
      if (chown.isError()) {
        return Failure(
            "Failed to change the ownership of the SANDBOX_PATH volume at '" +
            source + "' with UID " + stringify(s.st_uid) + " and GID " +
            stringify(s.st_gid) + ": " + chown.error());
      }
    }

    // Prepare the target.
    string target;

    if (path::absolute(volume.container_path())) {
      CHECK(bindMountSupported);

      if (containerConfig.has_rootfs()) {
        target = path::join(
            containerConfig.rootfs(),
            volume.container_path());

        if (os::stat::isdir(source)) {
          Try<Nothing> mkdir = os::mkdir(target);
          if (mkdir.isError()) {
            return Failure(
                "Failed to create the mount point at "
                "'" + target + "': " + mkdir.error());
          }
        } else {
          // The file (regular file or device file) bind mount case.
          Try<Nothing> mkdir = os::mkdir(Path(target).dirname());
          if (mkdir.isError()) {
            return Failure(
                "Failed to create directory "
                "'" + Path(target).dirname() + "' "
                "for the mount point: " + mkdir.error());
          }

          Try<Nothing> touch = os::touch(target);
          if (touch.isError()) {
            return Failure(
                "Failed to touch the mount point at "
                "'" + target + "': " + touch.error());
          }
        }
      } else {
        target = volume.container_path();

        // An absolute 'container_path' must already exist if the
        // container rootfs is the same as the host. This is because
        // we want to avoid creating mount points outside the work
        // directory in the host filesystem.
        if (!os::exists(target)) {
          return Failure(
              "Mount point '" + target + "' is an absolute path. "
              "It must exist if the container shares the host filesystem");
        }
      }

      // TODO(jieyu): We need to check that target resolves under
      // 'rootfs' because a user can potentially use a container path
      // like '/../../abc'.
    } else {
      CHECK_EQ(Volume::Source::SandboxPath::PARENT, sandboxPath->type());

      if (containerConfig.has_rootfs()) {
        target = path::join(
            containerConfig.rootfs(),
            flags.sandbox_directory,
            volume.container_path());
      } else {
        target = path::join(
            containerConfig.directory(),
            volume.container_path());
      }

      // Create the mount point if bind mount is used.
      // NOTE: We cannot create the mount point at 'target' if
      // container has rootfs defined. The bind mount of the sandbox
      // will hide what's inside 'target'. So we should always create
      // the mount point in the sandbox.
      if (bindMountSupported) {
        const string mountPoint = path::join(
            containerConfig.directory(),
            volume.container_path());

        if (os::stat::isdir(source)) {
          Try<Nothing> mkdir = os::mkdir(mountPoint);
          if (mkdir.isError()) {
            return Failure(
                "Failed to create the mount point at "
                "'" + mountPoint + "': " + mkdir.error());
          }
        } else {
          // The file (regular file or device file) bind mount case.
          Try<Nothing> mkdir = os::mkdir(Path(mountPoint).dirname());
          if (mkdir.isError()) {
            return Failure(
                "Failed to create the directory "
                "'" + Path(mountPoint).dirname() + "' "
                "for the mount point: " + mkdir.error());
          }

          Try<Nothing> touch = os::touch(mountPoint);
          if (touch.isError()) {
            return Failure(
                "Failed to touch the mount point at "
                "'" + mountPoint+ "': " + touch.error());
          }
        }
      }
    }

    if (bindMountSupported) {
#ifdef __linux__
      LOG(INFO) << "Mounting SANDBOX_PATH volume from "
                << "'" << source << "' to '" << target << "' "
                << "for container " << containerId;

      ContainerMountInfo* mount = launchInfo.add_mounts();
      mount->set_source(source);
      mount->set_target(target);
      mount->set_flags(MS_BIND | MS_REC);

      // If the mount needs to be read-only, do a remount.
      if (volume.mode() == Volume::RO) {
        mount = launchInfo.add_mounts();
        mount->set_target(target);
        mount->set_flags(MS_BIND | MS_RDONLY | MS_REMOUNT);
      }
#endif // __linux__
    } else {
      LOG(INFO) << "Linking SANDBOX_PATH volume from "
                << "'" << source << "' to '" << target << "' "
                << "for container " << containerId;

      // NOTE: We cannot enforce read-only access given the symlink without
      // changing the source so we just log a warning here.
      if (volume.mode() == Volume::RO) {
        LOG(WARNING) << "Allowing read-write access to read-only volume '"
                     << source << "' of container " << containerId;
      }

      Try<Nothing> symlink = ::fs::symlink(source, target);
      if (symlink.isError()) {
        return Failure(
            "Failed to symlink '" + source + "' -> '" + target + "'"
            ": " + symlink.error());
      }
    }
  }

  return launchInfo;
}

Future<Nothing> VolumeSandboxPathIsolatorProcess::cleanup(
    const ContainerID& containerId)
{
  // Remove the current container's sandbox path from `sandboxes`.
  sandboxes.erase(containerId);

  return Nothing();
}

} // namespace slave {
} // namespace internal {
} // namespace mesos {
<|endoftext|>


<commit_before>/* mbed Microcontroller Library
 * Copyright (c) 2006-2015 ARM Limited
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "mbed.h"
#include "ble/BLE.h"
#include "ble-blocktransfer/BlockTransferService.h"
#include "voytalk/Voytalk.h"

/*****************************************************************************/
/* Configuration                                                             */
/*****************************************************************************/

// set device name
const char DEVICE_NAME[] = "Testoy";

// set TX power
#ifndef CFG_BLE_TX_POWER_LEVEL
#define CFG_BLE_TX_POWER_LEVEL 0
#endif

// control debug output
#if 1
#define DEBUGOUT(...) { printf(__VA_ARGS__); }
#else
#define DEBUGOUT(...) /* nothing */
#endif // DEBUGOUT

/*****************************************************************************/

/* Voytalk short UUID */
const UUID uuid(0xFE8E);

/* Voytalk states */
typedef enum {
    FLAG_CONNECTED      = 0x01,
    FLAG_PROVISIONED    = 0x02,
} flags_t;

static volatile uint8_t state;

/*****************************************************************************/
/* Global variables used by the test app                                     */
/*****************************************************************************/

// BLE_API ble device
BLE ble;

// Transfer large blocks of data on platforms without Fragmentation-And-Recombination
BlockTransferService bts;

// Voytalk handling
VoytalkRouter router(DEVICE_NAME);

// buffer for sending and receiving data
SharedPointer<Block> writeBlock;
uint8_t readBuffer[1000];
BlockStatic readBlock(readBuffer, sizeof(readBuffer));

// wifi parameters
std::string ssid_string;
std::string key_string;

// Compatibility function
void signalReady();

/*****************************************************************************/
/* Functions for handling debug output                                       */
/*****************************************************************************/

/*
    Functions called when BLE device connects and disconnects.
*/
void whenConnected(const Gap::ConnectionCallbackParams_t* params)
{
    (void) params;
    DEBUGOUT("main: Connected: %d %d %d\r\n", params->connectionParams->minConnectionInterval,
                                              params->connectionParams->maxConnectionInterval,
                                              params->connectionParams->slaveLatency);

    // change state in main application
    state |= FLAG_CONNECTED;

    // change state inside Voytalk hub
    router.setStateMask(state);
}

void whenDisconnected(const Gap::DisconnectionCallbackParams_t*)
{
    DEBUGOUT("main: Disconnected!\r\n");
    DEBUGOUT("main: Restarting the advertising process\r\n");

    ble.gap().startAdvertising();

    // change state in main application
    state &= ~FLAG_CONNECTED;

    // change state inside Voytalk hub
    router.setStateMask(state);
}

/*****************************************************************************/
/* BlockTransfer callbacks for sending and receiving data                    */
/*****************************************************************************/

/*
    Function called when signaling client that new data is ready to be read.
*/
void blockServerSendNotification()
{
    DEBUGOUT("main: notify read updated\r\n");
    bts.updateCharacteristicValue((uint8_t*)"", 0);
}

/*
    Function called when device receives a read request over BLE.
*/
SharedPointer<Block> blockServerReadHandler(uint32_t offset)
{
    DEBUGOUT("main: block read\r\n");
    (void) offset;

    return SharedPointer<Block>(new BlockStatic(readBlock));
}

/*
    Function called when data has been written over BLE.
*/
void blockServerWriteHandler(SharedPointer<Block> block)
{
    DEBUGOUT("main: block write\r\n");

    /*
        Process received data, assuming it is CBOR encoded.
        Any output generated will be written to the readBlock.
    */
    router.processCBOR((BlockStatic*) block.get(), &readBlock);

    /*
        If the readBlock length is non-zero it means a reply has been generated.
    */
    if (readBlock.getLength() > 0)
    {
        signalReady();
    }
}

/*****************************************************************************/
/* Voytalk Wifi example                                                      */
/*****************************************************************************/

/*
    Callback function for constructing wifi intent.
*/
void wifiIntentConstruction(VTRequest& req, VTResponse& res)
{
    DEBUGOUT("main: wifi intent construction\r\n");
    /* create intent using generated endpoint and constraint set */
    VTIntent intent("com.arm.connectivity.wifi");
    intent.knownParameters("/networks");
    intent.endpoint("/wifi");

    res.write(intent);
}

void wifiIntentInvocation(VTRequest& req, VTResponse& res, VoytalkRouter::done_t done)
{
    DEBUGOUT("main: wifi invocation\r\n");

    VTIntentInvocation invocation(req.getBody());

    /////////////////////////////////////////
    // retrieve parameters
    invocation.getParameters().find("ssid").getString(ssid_string);
    invocation.getParameters().find("key").getString(key_string);

    /////////////////////////////////////////
    // create coda

    // Read ID from invocation. ID is returned in coda response.
    uint32_t invocationID = invocation.getID();

    VTCoda coda(invocationID);
    coda.success(true);
    res.write(coda);
    done(200);

    // change state in main application
    state |= FLAG_PROVISIONED;

    // change state inside Voytalk hub
    router.setStateMask(state);
}

/*****************************************************************************/
/* Reset device example                                                      */
/*****************************************************************************/

void resetIntentConstruction(VTRequest& req, VTResponse& res)
{
    DEBUGOUT("main: reset intent construction\r\n");

    /* create intent using generated endpoint and constraint set */
    VTIntent intent("com.arm.reset");
    intent.endpoint("/reset");
    res.write(intent);
}

void resetIntentInvocation(VTRequest& req, VTResponse& res, VoytalkRouter::done_t done)
{
    DEBUGOUT("main: reset invocation\r\n");

    VTIntentInvocation invocation(req.getBody());

    // print object tree
    invocation.getParameters().print();

    ssid_string = "";
    key_string = "";

    // Read ID from invocation. ID is returned in coda response.
    VTCoda coda(invocation.getID());
    coda.success(true);
    res.write(coda);
    done(200);

    // change state in main application
    state &= ~FLAG_PROVISIONED;

    // change state inside Voytalk hub
    router.setStateMask(state);
}

/*****************************************************************************/
/* Voytalk complex example                                                   */
/*****************************************************************************/

/*
    Callback functions for the example intent.
*/
void exampleIntentConstruction(VTRequest& req, VTResponse& res)
{
    DEBUGOUT("main: complex example intent construction\r\n");

    /* create intent */
    VTIntent intent("com.arm.examples.complex");
    intent.endpoint("/examples/complex");

    res.write(intent);
}

/*****************************************************************************/
/* Voytalk custom example                                                    */
/*****************************************************************************/

void customIntentConstruction(VTRequest& req, VTResponse& res)
{
    DEBUGOUT("main: custom intent construction\r\n");
    /* create intent using generated endpoint and constraint set */
    VTIntent intent("com.arm.examples.custom");
    intent.endpoint("/custom");
    intent.constraints()
        .title("Hello!")
        .description("This is the description")
        .addConstraint("test",
            VTConstraint(VTConstraint::TypeString)
                .title("Test")
                .defaultValue("default goes here")
        )
        .addConstraint("test2",
            VTConstraint(VTConstraint::TypeString)
                .title("Other test")
                .defaultValue("default goes here")
        );
    res.write(intent);
}

void printingIntentInvocation(VTRequest& req, VTResponse& res, VoytalkRouter::done_t done)
{
    DEBUGOUT("main: invocation receieved \r\n");

    VTIntentInvocation invocation(req.getBody());

    // print object tree
    invocation.getParameters().print();

    VTCoda coda(invocation.getID());
    coda.success(true);
    res.write(coda);
    done(200);
}

void networkListResource(VTRequest& req, VTResponse& res, VoytalkRouter::done_t done)
{
    DEBUGOUT("listing network resources");

    VoytalkKnownParameters parameters(res, 2);

    parameters.parameter("com.arm.connectivity.wifi", 50)
        .map(2)
            .key("ssid").value("iWifi")
            .key("key").value("supersecurepassword");

    parameters.parameter("com.arm.connectivity.wifi", 20)
        .map(2)
            .key("ssid").value("yoWifi")
            .key("key").value("securepasswordinit");

    done(200);
}

/*****************************************************************************/
/* main                                                                      */
/*****************************************************************************/

void app_start(int, char *[])
{
    /*
        Register Voytalk intents in the hub.

        First parameter is the callback function for intent generation.
        Second is the callback function for when the intent is invoked.
        Third parameter is a bitmap for grouping intents together.
    */

    // Wifi provisioning intent
    router.registerIntent(wifiIntentConstruction,
                          FLAG_CONNECTED | FLAG_PROVISIONED);

    // reset intent
    router.registerIntent(resetIntentConstruction,
                          FLAG_PROVISIONED);

    // custom intent
    router.registerIntent(customIntentConstruction,
                         FLAG_CONNECTED | FLAG_PROVISIONED);

    // example intent
    router.registerIntent(exampleIntentConstruction,
                         FLAG_CONNECTED | FLAG_PROVISIONED);

    /*
        Set the current state mask.

        Mask is AND'ed with each intent's bitmap and only intents with non-zero
        results are displayed and can be invoked.
    */
    router.setStateMask(0);

    /*
        Define some resource callbacks
    */

    router.get("/networks", networkListResource);

    router.post("/wifi", wifiIntentInvocation);
    router.post("/reset", resetIntentInvocation);
    router.post("/custom", printingIntentInvocation);
    router.post("/examples/complex", printingIntentInvocation);

    /*************************************************************************/
    /*************************************************************************/
    /* bluetooth le */
    ble.init();

    // status callback functions
    ble.gap().onConnection(whenConnected);
    ble.gap().onDisconnection(whenDisconnected);

    /* construct advertising beacon */
    ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::BREDR_NOT_SUPPORTED|GapAdvertisingData::LE_GENERAL_DISCOVERABLE);
    ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::SHORTENED_LOCAL_NAME, (const uint8_t *) DEVICE_NAME, sizeof(DEVICE_NAME) - 1);
    ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LIST_16BIT_SERVICE_IDS, uuid.getBaseUUID(), uuid.getLen());
    ble.gap().accumulateAdvertisingPayloadTxPower(CFG_BLE_TX_POWER_LEVEL);

    ble.gap().setAdvertisingType(GapAdvertisingParams::ADV_CONNECTABLE_UNDIRECTED);
    ble.gap().setAdvertisingInterval(1000); /* 1s; in multiples of 0.625ms. */

    // set TX power
    ble.gap().setTxPower(CFG_BLE_TX_POWER_LEVEL);

    // Apple uses device name instead of beacon name
    ble.gap().setDeviceName((const uint8_t*) DEVICE_NAME);

    /*************************************************************************/
    /*************************************************************************/
    // setup block transfer service

    // add service using ble device, responding to uuid, and without encryption
    bts.init(uuid, SecurityManager::SECURITY_MODE_ENCRYPTION_OPEN_LINK);

    // set callback functions for the BlockTransfer service
    bts.setWriteAuthorizationCallback(blockServerWriteHandler);
    bts.setReadAuthorizationCallback(blockServerReadHandler);

    // ble setup complete - start advertising
    ble.gap().startAdvertising();

    printf("Voytalk Test: %s %s\r\n", __DATE__, __TIME__);
}

/*****************************************************************************/
/* Compatibility                                                             */
/*****************************************************************************/

#if defined(YOTTA_MINAR_VERSION_STRING)
/*********************************************************/
/* Build for mbed OS                                     */
/*********************************************************/

void signalReady()
{
    minar::Scheduler::postCallback(blockServerSendNotification);
}

#else
/*********************************************************/
/* Build for mbed Classic                                */
/*********************************************************/

bool sendNotification = false;

void signalReady()
{
    sendNotification = true;
}

int main(void)
{
    app_start(0, NULL);

    for(;;)
    {
        // send notification outside of interrupt context
        if (sendNotification)
        {
            sendNotification = false;
            blockServerSendNotification();
        }

        ble.waitForEvent();
    }
}
#endif

<commit_msg>use middleware support<commit_after>/* mbed Microcontroller Library
 * Copyright (c) 2006-2015 ARM Limited
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "mbed.h"
#include "ble/BLE.h"
#include "ble-blocktransfer/BlockTransferService.h"
#include "voytalk/Voytalk.h"

/*****************************************************************************/
/* Configuration                                                             */
/*****************************************************************************/

// set device name
const char DEVICE_NAME[] = "Testoy";

// set TX power
#ifndef CFG_BLE_TX_POWER_LEVEL
#define CFG_BLE_TX_POWER_LEVEL 0
#endif

// control debug output
#if 1
#define DEBUGOUT(...) { printf(__VA_ARGS__); }
#else
#define DEBUGOUT(...) /* nothing */
#endif // DEBUGOUT

/*****************************************************************************/

/* Voytalk short UUID */
const UUID uuid(0xFE8E);

/* Voytalk states */
typedef enum {
    FLAG_CONNECTED      = 0x01,
    FLAG_PROVISIONED    = 0x02,
} flags_t;

static volatile uint8_t state;

/*****************************************************************************/
/* Global variables used by the test app                                     */
/*****************************************************************************/

// BLE_API ble device
BLE ble;

// Transfer large blocks of data on platforms without Fragmentation-And-Recombination
BlockTransferService bts;

// Voytalk handling
VoytalkRouter router(DEVICE_NAME);

// buffer for sending and receiving data
SharedPointer<Block> writeBlock;
uint8_t readBuffer[1000];
BlockStatic readBlock(readBuffer, sizeof(readBuffer));

// wifi parameters
std::string ssid_string;
std::string key_string;

// Compatibility function
void signalReady();

/*****************************************************************************/
/* Functions for handling debug output                                       */
/*****************************************************************************/

/*
    Functions called when BLE device connects and disconnects.
*/
void whenConnected(const Gap::ConnectionCallbackParams_t* params)
{
    (void) params;
    DEBUGOUT("main: Connected: %d %d %d\r\n", params->connectionParams->minConnectionInterval,
                                              params->connectionParams->maxConnectionInterval,
                                              params->connectionParams->slaveLatency);

    // change state in main application
    state |= FLAG_CONNECTED;

    // change state inside Voytalk hub
    router.setStateMask(state);
}

void whenDisconnected(const Gap::DisconnectionCallbackParams_t*)
{
    DEBUGOUT("main: Disconnected!\r\n");
    DEBUGOUT("main: Restarting the advertising process\r\n");

    ble.gap().startAdvertising();

    // change state in main application
    state &= ~FLAG_CONNECTED;

    // change state inside Voytalk hub
    router.setStateMask(state);
}

/*****************************************************************************/
/* BlockTransfer callbacks for sending and receiving data                    */
/*****************************************************************************/

/*
    Function called when signaling client that new data is ready to be read.
*/
void blockServerSendNotification()
{
    DEBUGOUT("main: notify read updated\r\n");
    bts.updateCharacteristicValue((uint8_t*)"", 0);
}

/*
    Function called when device receives a read request over BLE.
*/
SharedPointer<Block> blockServerReadHandler(uint32_t offset)
{
    DEBUGOUT("main: block read\r\n");
    (void) offset;

    return SharedPointer<Block>(new BlockStatic(readBlock));
}

/*
    Function called when data has been written over BLE.
*/
void blockServerWriteHandler(SharedPointer<Block> block)
{
    DEBUGOUT("main: block write\r\n");

    /*
        Process received data, assuming it is CBOR encoded.
        Any output generated will be written to the readBlock.
    */
    router.processCBOR((BlockStatic*) block.get(), &readBlock);

    /*
        If the readBlock length is non-zero it means a reply has been generated.
    */
    if (readBlock.getLength() > 0)
    {
        signalReady();
    }
}

/*****************************************************************************/
/* Voytalk Wifi example                                                      */
/*****************************************************************************/

/*
    Callback function for constructing wifi intent.
*/
void wifiIntentConstruction(VTRequest& req, VTResponse& res)
{
    DEBUGOUT("main: wifi intent construction\r\n");
    /* create intent using generated endpoint and constraint set */
    VTIntent intent("com.arm.connectivity.wifi");
    intent.knownParameters("/networks")
        .endpoint("/wifi");

    res.write(intent);
}

/*****************************************************************************/
/* Reset device example                                                      */
/*****************************************************************************/

void resetIntentConstruction(VTRequest& req, VTResponse& res)
{
    DEBUGOUT("main: reset intent construction\r\n");

    /* create intent using generated endpoint and constraint set */
    VTIntent intent("com.arm.reset");
    intent.endpoint("/reset");
    res.write(intent);
}

/*****************************************************************************/
/* Voytalk complex example                                                   */
/*****************************************************************************/

/*
    Callback functions for the example intent.
*/
void exampleIntentConstruction(VTRequest& req, VTResponse& res)
{
    DEBUGOUT("main: complex example intent construction\r\n");

    /* create intent */
    VTIntent intent("com.arm.examples.complex");
    intent.endpoint("/examples/complex");

    res.write(intent);
}

/*****************************************************************************/
/* Voytalk custom example                                                    */
/*****************************************************************************/

void customIntentConstruction(VTRequest& req, VTResponse& res)
{
    DEBUGOUT("main: custom intent construction\r\n");
    /* create intent using generated endpoint and constraint set */
    VTIntent intent("com.arm.examples.custom");
    intent.endpoint("/custom")
        .constraints()
            .title("Hello!")
            .description("This is the description")
            .addConstraint("test",
                VTConstraint(VTConstraint::TypeString)
                    .title("Test")
                    .defaultValue("default goes here")
            )
            .addConstraint("test2",
                VTConstraint(VTConstraint::TypeString)
                    .title("Other test")
                    .defaultValue("default goes here")
            );
    res.write(intent);
}

/*****************************************************************************/
/* Middleware for actually doing stuff                                       */
/*****************************************************************************/

void printInvocation(VTRequest& req, VTResponse& res, VoytalkRouter::done_t done)
{
    VTIntentInvocation invocation(req.getBody());
    invocation.getParameters().print();
}

void saveWifi(VTRequest& req, VTResponse& res, VoytalkRouter::done_t done)
{
    DEBUGOUT("main: saving wifi details\r\n");

    VTIntentInvocation invocation(req.getBody());

    invocation.getParameters().find("ssid").getString(ssid_string);
    invocation.getParameters().find("key").getString(key_string);

    // change state in main application
    state |= FLAG_PROVISIONED;

    // change state inside Voytalk hub
    router.setStateMask(state);
}

void resetDevice(VTRequest& req, VTResponse& res, VoytalkRouter::done_t done)
{
    DEBUGOUT("main: reset device\r\n");

    ssid_string = "";
    key_string = "";

    // change state in main application
    state &= ~FLAG_PROVISIONED;

    // change state inside Voytalk hub
    router.setStateMask(state);
}

void sendSuccess(VTRequest& req, VTResponse& res, VoytalkRouter::done_t done)
{
    VTIntentInvocation invocation(req.getBody());
    VTCoda coda(invocation.getID());
    coda.success(true);
    res.write(coda);
    done(200);
}

void networkList(VTRequest& req, VTResponse& res, VoytalkRouter::done_t done)
{
    DEBUGOUT("listing network resources");

    VoytalkKnownParameters parameters(res, 2);

    parameters.parameter("com.arm.connectivity.wifi", 50)
        .map(2)
            .key("ssid").value("miWifi")
            .key("key").value("supersecurepassword");

    parameters.parameter("com.arm.connectivity.wifi", 20)
        .map(2)
            .key("ssid").value("yoWifi")
            .key("key").value("securepasswordinit");

    done(200);
}

/*****************************************************************************/
/* main                                                                      */
/*****************************************************************************/

void app_start(int, char *[])
{
    /*
        Register Voytalk intents in the hub.

        First parameter is the callback function for intent generation.
        Second parameter is a bitmap for grouping intents together.
    */

    // Wifi provisioning intent
    router.registerIntent(wifiIntentConstruction,
                          FLAG_CONNECTED | FLAG_PROVISIONED);

    // reset intent
    router.registerIntent(resetIntentConstruction,
                          FLAG_PROVISIONED);

    // custom intent
    //router.registerIntent(customIntentConstruction,
    //                     FLAG_CONNECTED | FLAG_PROVISIONED);

    // example intent
    router.registerIntent(exampleIntentConstruction,
                         FLAG_CONNECTED | FLAG_PROVISIONED);

    /*
        Set the current state mask.

        Mask is AND'ed with each intent's bitmap and only intents with non-zero
        results are displayed and can be invoked.
    */
    router.setStateMask(0);

    /*
        Define some resource callbacks
    */

    router.get("/networks",          networkList, NULL);
    router.post("/wifi",             printInvocation, saveWifi,    sendSuccess, NULL);
    router.post("/reset",            printInvocation, resetDevice, sendSuccess, NULL);
    router.post("/custom",           printInvocation,              sendSuccess, NULL);
    router.post("/examples/complex", printInvocation,              sendSuccess, NULL);

    /*************************************************************************/
    /*************************************************************************/
    /* bluetooth le */
    ble.init();

    // status callback functions
    ble.gap().onConnection(whenConnected);
    ble.gap().onDisconnection(whenDisconnected);

    /* construct advertising beacon */
    ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::BREDR_NOT_SUPPORTED|GapAdvertisingData::LE_GENERAL_DISCOVERABLE);
    ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::SHORTENED_LOCAL_NAME, (const uint8_t *) DEVICE_NAME, sizeof(DEVICE_NAME) - 1);
    ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LIST_16BIT_SERVICE_IDS, uuid.getBaseUUID(), uuid.getLen());
    ble.gap().accumulateAdvertisingPayloadTxPower(CFG_BLE_TX_POWER_LEVEL);

    ble.gap().setAdvertisingType(GapAdvertisingParams::ADV_CONNECTABLE_UNDIRECTED);
    ble.gap().setAdvertisingInterval(1000); /* 1s; in multiples of 0.625ms. */

    // set TX power
    ble.gap().setTxPower(CFG_BLE_TX_POWER_LEVEL);

    // Apple uses device name instead of beacon name
    ble.gap().setDeviceName((const uint8_t*) DEVICE_NAME);

    /*************************************************************************/
    /*************************************************************************/
    // setup block transfer service

    // add service using ble device, responding to uuid, and without encryption
    bts.init(uuid, SecurityManager::SECURITY_MODE_ENCRYPTION_OPEN_LINK);

    // set callback functions for the BlockTransfer service
    bts.setWriteAuthorizationCallback(blockServerWriteHandler);
    bts.setReadAuthorizationCallback(blockServerReadHandler);

    // ble setup complete - start advertising
    ble.gap().startAdvertising();

    printf("Voytalk Test: %s %s\r\n", __DATE__, __TIME__);
}

/*****************************************************************************/
/* Compatibility                                                             */
/*****************************************************************************/

#if defined(YOTTA_MINAR_VERSION_STRING)
/*********************************************************/
/* Build for mbed OS                                     */
/*********************************************************/

void signalReady()
{
    minar::Scheduler::postCallback(blockServerSendNotification);
}

#else
/*********************************************************/
/* Build for mbed Classic                                */
/*********************************************************/

bool sendNotification = false;

void signalReady()
{
    sendNotification = true;
}

int main(void)
{
    app_start(0, NULL);

    for(;;)
    {
        // send notification outside of interrupt context
        if (sendNotification)
        {
            sendNotification = false;
            blockServerSendNotification();
        }

        ble.waitForEvent();
    }
}
#endif

<|endoftext|>


<commit_before>/*
The MIT License (MIT)

Copyright (c) 2016 British Broadcasting Corporation.
This software is provided by Lancaster University by arrangement with the BBC.

Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
*/

#include "MicroBit.h"
#include "MicroBitUARTService.h"

#include "EventLookup.h"

MicroBit uBit;
MicroBitUARTService *uart;
MicroBitSerial *serial;

void onGesture(MicroBitEvent e) {
  ManagedString Event = lookupGesture(uBit, e.value);
  ManagedString ShortEvent = lookupGesture(uBit, e.value, true);
  if (e.value != MICROBIT_ACCELEROMETER_EVT_NONE) {
    uBit.display.printAsync(ShortEvent);
    serial->send(Event);
  }
}

int main()
{
    // Initialise the micro:bit runtime.
  uBit.init();

  uart = new MicroBitUARTService(*uBit.ble, 32, 32);
  serial = new MicroBitSerial(USBTX, USBRX);
  uBit.messageBus.listen(MICROBIT_ID_GESTURE, MICROBIT_EVT_ANY, onGesture);

  while(1)
  {
    uBit.sleep(100);
  }

  // If main exits, there may still be other fibers running or registered Event handlers etc.
  // Simply release this fiber, which will mean we enter the scheduler. Worse case, we then
  // sit in the idle task forever, in a power efficient sleep.
  release_fiber();
}

<commit_msg>license comment<commit_after>/*
TODO - add licensing

Copyright (c) 2016 Will Lovett
*/

#include "MicroBit.h"
#include "MicroBitUARTService.h"

#include "EventLookup.h"

MicroBit uBit;
MicroBitUARTService *uart;
MicroBitSerial *serial;

void onGesture(MicroBitEvent e) {
  ManagedString Event = lookupGesture(uBit, e.value);
  ManagedString ShortEvent = lookupGesture(uBit, e.value, true);
  if (e.value != MICROBIT_ACCELEROMETER_EVT_NONE) {
    uBit.display.printAsync(ShortEvent);
    serial->send(Event);
  }
}

int main()
{
    // Initialise the micro:bit runtime.
  uBit.init();

  uart = new MicroBitUARTService(*uBit.ble, 32, 32);
  serial = new MicroBitSerial(USBTX, USBRX);
  uBit.messageBus.listen(MICROBIT_ID_GESTURE, MICROBIT_EVT_ANY, onGesture);

  while(1)
  {
    uBit.sleep(100);
  }

  // If main exits, there may still be other fibers running or registered Event handlers etc.
  // Simply release this fiber, which will mean we enter the scheduler. Worse case, we then
  // sit in the idle task forever, in a power efficient sleep.
  release_fiber();
}

<|endoftext|>


<commit_before>#include <iostream>
#include <string>
#include "TrelScript.h"

///////////////////////////////////////////////////////////
// Main function                                         //
///////////////////////////////////////////////////////////
int main()
{
	TrelScript *ts	= new TrelScript("testfile.trole");
	ts->runScript();
	delete ts;

	std::cout << "Press enter to exit.";
	std::cin.get();
	return 0;
}<commit_msg>can now load scripts from the command line<commit_after>#include <iostream>
#include <string>
#include "TrelScript.h"

///////////////////////////////////////////////////////////
// Main function                                         //
///////////////////////////////////////////////////////////
int main(int argc, char **argv)
{
	std::string filename	= "testfile.trole";
	if(argc > 0)
	{
		if(argv[1] != NULL)
			filename	= argv[1];
	}

	TrelScript *ts	= new TrelScript(filename);
	ts->runScript();
	delete ts;

	std::cout << "Press enter to exit.";
	std::cin.get();
	return 0;
}<|endoftext|>


<commit_before>#include <iostream>
#include <string>
#include "TrelScript.h"

///////////////////////////////////////////////////////////
// Main function                                         //
///////////////////////////////////////////////////////////
int main()
{
	TrelScript *ts	= new TrelScript();
	ts->runScript();

	system("pause");
	return 0;
}<commit_msg>now actually loads a file, no more memory leaks<commit_after>#include <iostream>
#include <string>
#include "TrelScript.h"

///////////////////////////////////////////////////////////
// Main function                                         //
///////////////////////////////////////////////////////////
int main()
{
	TrelScript *ts	= new TrelScript("testfile.trole");
	ts->runScript();
	delete ts;

	system("pause");
	return 0;
}<|endoftext|>


<commit_before>#include <ctrcommon/common.hpp>

#include <sstream>
#include <iomanip>
#include <stdio.h>

typedef enum {
    INSTALL,
    DELETE
} Mode;

int main(int argc, char **argv) {
    if(!platform_init()) {
        return 0;
    }

    std::vector<std::string> extensions;
    extensions.push_back("cia");

    MediaType destination = SD;
    Mode mode = INSTALL;
    bool netInstall = false;
    u64 freeSpace = fs_get_free_space(destination);
    auto onLoop = [&]() {
        bool breakLoop = false;
        if(input_is_pressed(BUTTON_L)) {
            if(destination == SD) {
                destination = NAND;
            } else {
                destination = SD;
            }

            freeSpace = fs_get_free_space(destination);
            if(mode == DELETE) {
                breakLoop = true;
            }
        }

        if(input_is_pressed(BUTTON_R)) {
            if(mode == INSTALL) {
                mode = DELETE;
            } else {
                mode = INSTALL;
            }

            breakLoop = true;
        }

        if(input_is_pressed(BUTTON_Y)) {
            netInstall = true;
            breakLoop = true;
        }

        std::stringstream stream;
        stream << "Free Space: " << freeSpace << " bytes (" << std::fixed << std::setprecision(2) << freeSpace / 1024.0f / 1024.0f << "MB)" << "\n";
        stream << "Destination: " << (destination == NAND ? "NAND" : "SD") << ", Mode: " << (mode == INSTALL ? "Install" : "Delete") << "\n";
        stream << "L - Switch Destination, R - Switch Mode" << "\n";
        stream << "Y - Receive an app over the network" << "\n";

        std::string str = stream.str();
        screen_draw_string(str, (screen_get_width() - screen_get_str_width(str)) / 2, screen_get_height() - 4 - screen_get_str_height(str), 255, 255, 255);

        return breakLoop;
    };

    auto onProgress = [&](int progress) {
        ui_display_progress(TOP_SCREEN, "Installing", "Press B to cancel.", true, progress);
        input_poll();
        return !input_is_pressed(BUTTON_B);
    };

    while(platform_is_running()) {
        std::string targetInstall;
        App targetDelete;
        bool obtained = false;
        if(mode == INSTALL) {
            obtained = ui_select_file(&targetInstall, "sdmc:", extensions, [&](bool inRoot) {
                return onLoop();
            });
        } else if(mode == DELETE) {
            obtained = ui_select_app(&targetDelete, destination, onLoop);
        }

        if(netInstall) {
            netInstall = false;

            screen_clear_buffers(BOTTOM_SCREEN, 0, 0, 0);

            RemoteFile file = ui_accept_remote_file(TOP_SCREEN);
            if(file.fd == NULL) {
                continue;
            }

            std::stringstream confirmStream;
            confirmStream << "Install the received application?" << "\n";
            confirmStream << "Size: " << file.fileSize << " bytes (" << std::fixed << std::setprecision(2) << file.fileSize / 1024.0f / 1024.0f << "MB)" << "\n";
            if(ui_prompt(TOP_SCREEN, confirmStream.str(), true)) {
                AppResult ret = app_install(destination, file.fd, file.fileSize, onProgress);
                std::stringstream resultMsg;
                if(mode == INSTALL) {
                    resultMsg << "Install ";
                } else if(mode == DELETE) {
                    resultMsg << "Delete ";
                }

                if(ret == APP_SUCCESS) {
                    resultMsg << "succeeded!";
                } else {
                    resultMsg << "failed!" << "\n";
                    resultMsg << app_get_result_string(ret) << "\n";
                }

                ui_prompt(TOP_SCREEN, resultMsg.str(), false);
            }

            fclose(file.fd);
            continue;
        }

        if(obtained) {
            std::stringstream prompt;
            if(mode == INSTALL) {
                prompt << "Install ";
            } else if(mode == DELETE) {
                prompt << "Delete ";
            }

            prompt << "the selected title?";
            if(ui_prompt(TOP_SCREEN, prompt.str(), true)) {
                AppResult ret = APP_SUCCESS;
                if(mode == INSTALL) {
                    ret = app_install_file(destination, targetInstall, onProgress);
                } else if(mode == DELETE) {
                    ui_display_message(TOP_SCREEN, "Deleting title...");
                    ret = app_delete(targetDelete);
                }

                std::stringstream resultMsg;
                if(mode == INSTALL) {
                    resultMsg << "Install ";
                } else if(mode == DELETE) {
                    resultMsg << "Delete ";
                }

                if(ret == APP_SUCCESS) {
                    resultMsg << "succeeded!";
                } else {
                    resultMsg << "failed!" << "\n";
                    resultMsg << app_get_result_string(ret) << "\n";
                }

                ui_prompt(TOP_SCREEN, resultMsg.str(), false);

                freeSpace = fs_get_free_space(destination);
            }
        }
    }

    platform_cleanup();
    return 0;
}
<commit_msg>Update for ctrcommon changes.<commit_after>#include <ctrcommon/input.hpp>
#include <ctrcommon/platform.hpp>
#include <ctrcommon/ui.hpp>

#include <sstream>
#include <iomanip>
#include <stdio.h>

typedef enum {
    INSTALL,
    DELETE
} Mode;

int main(int argc, char **argv) {
    if(!platform_init()) {
        return 0;
    }

    std::vector<std::string> extensions;
    extensions.push_back("cia");

    MediaType destination = SD;
    Mode mode = INSTALL;
    bool netInstall = false;
    u64 freeSpace = fs_get_free_space(destination);
    auto onLoop = [&]() {
        bool breakLoop = false;
        if(input_is_pressed(BUTTON_L)) {
            if(destination == SD) {
                destination = NAND;
            } else {
                destination = SD;
            }

            freeSpace = fs_get_free_space(destination);
            if(mode == DELETE) {
                breakLoop = true;
            }
        }

        if(input_is_pressed(BUTTON_R)) {
            if(mode == INSTALL) {
                mode = DELETE;
            } else {
                mode = INSTALL;
            }

            breakLoop = true;
        }

        if(input_is_pressed(BUTTON_Y)) {
            netInstall = true;
            breakLoop = true;
        }

        std::stringstream stream;
        stream << "Free Space: " << freeSpace << " bytes (" << std::fixed << std::setprecision(2) << freeSpace / 1024.0f / 1024.0f << "MB)" << "\n";
        stream << "Destination: " << (destination == NAND ? "NAND" : "SD") << ", Mode: " << (mode == INSTALL ? "Install" : "Delete") << "\n";
        stream << "L - Switch Destination, R - Switch Mode" << "\n";
        stream << "Y - Receive an app over the network" << "\n";

        std::string str = stream.str();
        screen_draw_string(str, (screen_get_width() - screen_get_str_width(str)) / 2, screen_get_height() - 4 - screen_get_str_height(str), 255, 255, 255);

        return breakLoop;
    };

    auto onProgress = [&](int progress) {
        ui_display_progress(TOP_SCREEN, "Installing", "Press B to cancel.", true, progress);
        input_poll();
        return !input_is_pressed(BUTTON_B);
    };

    while(platform_is_running()) {
        std::string targetInstall;
        App targetDelete;
        bool obtained = false;
        if(mode == INSTALL) {
            obtained = ui_select_file(&targetInstall, "sdmc:", extensions, [&](bool inRoot) {
                return onLoop();
            });
        } else if(mode == DELETE) {
            obtained = ui_select_app(&targetDelete, destination, onLoop);
        }

        if(netInstall) {
            netInstall = false;

            screen_clear_buffers(BOTTOM_SCREEN, 0, 0, 0);

            RemoteFile file = ui_accept_remote_file(TOP_SCREEN);
            if(file.fd == NULL) {
                continue;
            }

            std::stringstream confirmStream;
            confirmStream << "Install the received application?" << "\n";
            confirmStream << "Size: " << file.fileSize << " bytes (" << std::fixed << std::setprecision(2) << file.fileSize / 1024.0f / 1024.0f << "MB)" << "\n";
            if(ui_prompt(TOP_SCREEN, confirmStream.str(), true)) {
                AppResult ret = app_install(destination, file.fd, file.fileSize, onProgress);
                std::stringstream resultMsg;
                if(mode == INSTALL) {
                    resultMsg << "Install ";
                } else if(mode == DELETE) {
                    resultMsg << "Delete ";
                }

                if(ret == APP_SUCCESS) {
                    resultMsg << "succeeded!";
                } else {
                    resultMsg << "failed!" << "\n";
                    resultMsg << app_get_result_string(ret) << "\n";
                }

                ui_prompt(TOP_SCREEN, resultMsg.str(), false);
            }

            fclose(file.fd);
            continue;
        }

        if(obtained) {
            std::stringstream prompt;
            if(mode == INSTALL) {
                prompt << "Install ";
            } else if(mode == DELETE) {
                prompt << "Delete ";
            }

            prompt << "the selected title?";
            if(ui_prompt(TOP_SCREEN, prompt.str(), true)) {
                AppResult ret = APP_SUCCESS;
                if(mode == INSTALL) {
                    ret = app_install_file(destination, targetInstall, onProgress);
                } else if(mode == DELETE) {
                    ui_display_message(TOP_SCREEN, "Deleting title...");
                    ret = app_delete(targetDelete);
                }

                std::stringstream resultMsg;
                if(mode == INSTALL) {
                    resultMsg << "Install ";
                } else if(mode == DELETE) {
                    resultMsg << "Delete ";
                }

                if(ret == APP_SUCCESS) {
                    resultMsg << "succeeded!";
                } else {
                    resultMsg << "failed!" << "\n";
                    resultMsg << app_get_result_string(ret) << "\n";
                }

                ui_prompt(TOP_SCREEN, resultMsg.str(), false);

                freeSpace = fs_get_free_space(destination);
            }
        }
    }

    platform_cleanup();
    return 0;
}
<|endoftext|>


<commit_before>#ifdef PIXELBOOST_PLATFORM_ANDROID

#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <ifaddrs.h>

#include "pixelboost/network/networkHelpers.h"

namespace pb
{

namespace NetworkHelpers
{

std::string GetWifiAddress()
{
    return "";
}

}

}

#endif<commit_msg>Fix Android compile error<commit_after>#ifdef PIXELBOOST_PLATFORM_ANDROID

#include "pixelboost/network/networkHelpers.h"

namespace pb
{

namespace NetworkHelpers
{

std::string GetWifiAddress()
{
    return "";
}

}

}

#endif<|endoftext|>


<commit_before>/***************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies).
**
** Contact:  Qt Software Information (qt-info@nokia.com)
**
**
** Non-Open Source Usage
**
** Licensees may use this file in accordance with the Qt Beta Version
** License Agreement, Agreement version 2.2 provided with the Software or,
** alternatively, in accordance with the terms contained in a written
** agreement between you and Nokia.
**
** GNU General Public License Usage
**
** Alternatively, this file may be used under the terms of the GNU General
** Public License versions 2.0 or 3.0 as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL included in the packaging
** of this file.  Please review the following information to ensure GNU
** General Public Licensing requirements will be met:
**
** http://www.fsf.org/licensing/licenses/info/GPLv2.html and
** http://www.gnu.org/copyleft/gpl.html.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights. These rights are described in the Nokia Qt GPL Exception
** version 1.2, included in the file GPL_EXCEPTION.txt in this package.
**
***************************************************************************/

#include "stackededitorgroup.h"
#include "editormanager.h"
#include "coreimpl.h"

#include <utils/qtcassert.h>

#include <QtCore/QDebug>
#include <QtCore/QDir>
#include <QtCore/QFileInfo>
#include <QtCore/QMimeData>

#include <QtGui/QApplication>
#include <QtGui/QComboBox>
#include <QtGui/QHBoxLayout>
#include <QtGui/QLabel>
#include <QtGui/QMouseEvent>
#include <QtGui/QPainter>
#include <QtGui/QStackedWidget>
#include <QtGui/QStyle>
#include <QtGui/QStyleOption>
#include <QtGui/QToolBar>
#include <QtGui/QToolButton>
#ifdef Q_WS_MAC
#include <qmacstyle_mac.h>
#endif

Q_DECLARE_METATYPE(Core::IEditor *)

using namespace Core;
using namespace Core::Internal;

StackedEditorGroup::StackedEditorGroup(QWidget *parent) :
    EditorGroup(parent),
    m_toplevel(new QWidget),
    m_toolBar(new QWidget),
    m_container(new QStackedWidget(this)),
    m_editorList(new QComboBox),
    m_closeButton(new QToolButton),
    m_lockButton(new QToolButton),
    m_defaultToolBar(new QToolBar(this)),
    m_infoWidget(new QFrame(this)),
    m_editorForInfoWidget(0)
{
    QVBoxLayout *tl = new QVBoxLayout(m_toplevel);
    tl->setSpacing(0);
    tl->setMargin(0);
    {
        m_editorList->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
        m_editorList->setMinimumContentsLength(20);
        m_proxyModel.setSourceModel(model());
        m_proxyModel.sort(0);
        m_editorList->setModel(&m_proxyModel);
        m_editorList->setMaxVisibleItems(40);

        QToolBar *editorListToolBar = new QToolBar;

        editorListToolBar->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Ignored);
        editorListToolBar->addWidget(m_editorList);

        m_defaultToolBar->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
        m_activeToolBar = m_defaultToolBar;

        QHBoxLayout *toolBarLayout = new QHBoxLayout;
        toolBarLayout->setMargin(0);
        toolBarLayout->setSpacing(0);
        toolBarLayout->addWidget(m_defaultToolBar);
        m_toolBar->setLayout(toolBarLayout);

        m_lockButton->setAutoRaise(true);
        m_lockButton->setProperty("type", QLatin1String("dockbutton"));

        m_closeButton->setAutoRaise(true);
        m_closeButton->setIcon(QIcon(":/qworkbench/images/closebutton.png"));
        m_closeButton->setProperty("type", QLatin1String("dockbutton"));

        QToolBar *rightToolBar = new QToolBar;
        rightToolBar->setLayoutDirection(Qt::RightToLeft);
        rightToolBar->addWidget(m_closeButton);
        rightToolBar->addWidget(m_lockButton);

        QHBoxLayout *toplayout = new QHBoxLayout;
        toplayout->setSpacing(0);
        toplayout->setMargin(0);
        toplayout->addWidget(editorListToolBar);
        toplayout->addWidget(m_toolBar, 1); // Custom toolbar stretches
        toplayout->addWidget(rightToolBar);

        QWidget *top = new QWidget;
        QVBoxLayout *vlayout = new QVBoxLayout(top);
        vlayout->setSpacing(0);
        vlayout->setMargin(0);
        vlayout->addLayout(toplayout);
        tl->addWidget(top);

        connect(m_editorList, SIGNAL(currentIndexChanged(int)), this, SLOT(listSelectionChanged(int)));
        connect(m_lockButton, SIGNAL(clicked()), this, SLOT(makeEditorWritable()));
        connect(m_closeButton, SIGNAL(clicked()), this, SLOT(sendCloseRequest()));
    }
    {
        m_infoWidget->setFrameStyle(QFrame::Panel | QFrame::Raised);
        m_infoWidget->setLineWidth(1);
        m_infoWidget->setForegroundRole(QPalette::ToolTipText);
        m_infoWidget->setBackgroundRole(QPalette::ToolTipBase);
        m_infoWidget->setAutoFillBackground(true);

        QHBoxLayout *hbox = new QHBoxLayout(m_infoWidget);
        hbox->setMargin(2);
        m_infoWidgetLabel = new QLabel("Placeholder");
        m_infoWidgetLabel->setForegroundRole(QPalette::ToolTipText);
        hbox->addWidget(m_infoWidgetLabel);
        hbox->addStretch(1);

        m_infoWidgetButton = new QToolButton;
        m_infoWidgetButton->setText(tr("Placeholder"));
        hbox->addWidget(m_infoWidgetButton);

        QToolButton *closeButton = new QToolButton;
        closeButton->setAutoRaise(true);
        closeButton->setIcon(QIcon(":/qworkbench/images/clear.png"));
        closeButton->setToolTip(tr("Close"));
        connect(closeButton, SIGNAL(clicked()), m_infoWidget, SLOT(hide()));

        hbox->addWidget(closeButton);

        tl->addWidget(m_infoWidget);
    }
    tl->addWidget(m_container);

    QHBoxLayout *l = new QHBoxLayout;
    l->setSpacing(0);
    l->setMargin(0);
    l->addWidget(m_toplevel);
    setLayout(l);

    m_toplevel->setVisible(false);
}

void StackedEditorGroup::showEditorInfoBar(const QString &kind,
                                           const QString &infoText,
                                           const QString &buttonText,
                                           QObject *object, const char *member)
{
    m_infoWidgetKind = kind;
    m_infoWidgetLabel->setText(infoText);
    m_infoWidgetButton->setText(buttonText);
    m_infoWidgetButton->disconnect();
    if (object && member)
        connect(m_infoWidgetButton, SIGNAL(clicked()), object, member);
    m_infoWidget->setVisible(true);
    m_editorForInfoWidget = currentEditor();
}

void StackedEditorGroup::hideEditorInfoBar(const QString &kind)
{
    if (kind == m_infoWidgetKind)
        m_infoWidget->setVisible(false);
}

StackedEditorGroup::~StackedEditorGroup()
{
}

void StackedEditorGroup::focusInEvent(QFocusEvent *e)
{
    if (m_container->count() > 0) {
        setEditorFocus(m_container->currentIndex());
    } else {
        EditorGroup::focusInEvent(e);
    }
}

void StackedEditorGroup::setEditorFocus(int index)
{
    QWidget *w = m_container->widget(index);
    w->setFocus();
}

void StackedEditorGroup::addEditor(IEditor *editor)
{
    insertEditor(editorCount(), editor);
}

void StackedEditorGroup::insertEditor(int index, IEditor *editor)
{
    EditorGroup::insertEditor(index, editor);
    if (m_container->indexOf(editor->widget()) != -1)
        return;

    m_container->insertWidget(index, editor->widget());
    m_widgetEditorMap.insert(editor->widget(), editor);

    QToolBar *toolBar = editor->toolBar();
    if (toolBar)
        m_toolBar->layout()->addWidget(toolBar);
    connect(editor, SIGNAL(changed()), this, SLOT(updateEditorStatus()));

    updateEditorStatus(editor);
    updateToolBar(editor);

    emit editorAdded(editor);
}

void StackedEditorGroup::sendCloseRequest()
{
    emit closeRequested(currentEditor());
}

void StackedEditorGroup::removeEditor(IEditor *editor)
{
    QTC_ASSERT(editor, return);
    EditorGroup::removeEditor(editor);
    const int index = m_container->indexOf(editor->widget());
    if (index != -1) {
        m_container->removeWidget(editor->widget());
        m_widgetEditorMap.remove(editor->widget());
        editor->widget()->setParent(0);
        disconnect(editor, SIGNAL(changed()), this, SLOT(updateEditorStatus()));
        QToolBar *toolBar = editor->toolBar();
        if (toolBar != 0) {
            if (m_activeToolBar == toolBar) {
                m_activeToolBar = m_defaultToolBar;
                m_activeToolBar->setVisible(true);
            }
            m_toolBar->layout()->removeWidget(toolBar);
            toolBar->setVisible(false);
            toolBar->setParent(0);
        }
        if (m_container->count() == 0) {
            m_toplevel->setVisible(false);
            setFocus();
        }
        emit editorRemoved(editor);
    }
}

IEditor *StackedEditorGroup::currentEditor() const
{
    if (m_container->count() > 0)
        return m_widgetEditorMap.value(m_container->currentWidget());
    return 0;
}

void StackedEditorGroup::setCurrentEditor(IEditor *editor)
{
    if (!editor || m_container->count() <= 0
        || m_container->indexOf(editor->widget()) == -1)
        return;
    m_toplevel->setVisible(true);
    const int idx = m_container->indexOf(editor->widget());
    QTC_ASSERT(idx >= 0, return);
    if (m_container->currentIndex() != idx) {
        m_container->setCurrentIndex(idx);

        const bool block = m_editorList->blockSignals(true);
        m_editorList->setCurrentIndex(indexOf(editor));
        m_editorList->blockSignals(block);

        updateEditorStatus(editor);
        updateToolBar(editor);
    }
    setEditorFocus(idx);
    if (editor != m_editorForInfoWidget) {
        m_infoWidget->hide();
        m_editorForInfoWidget = 0;
    }
}

void StackedEditorGroup::updateEditorStatus(IEditor *editor)
{
    if (!editor)
        editor = qobject_cast<IEditor *>(sender());
    QTC_ASSERT(editor, return);

    static const QIcon lockedIcon(QLatin1String(":/qworkbench/images/locked.png"));
    static const QIcon unlockedIcon(QLatin1String(":/qworkbench/images/unlocked.png"));

    if (editor->file()->isReadOnly()) {
        m_lockButton->setIcon(lockedIcon);
        m_lockButton->setEnabled(!editor->file()->fileName().isEmpty());
        m_lockButton->setToolTip(tr("Make writable"));
    } else {
        m_lockButton->setIcon(unlockedIcon);
        m_lockButton->setEnabled(false);
        m_lockButton->setToolTip(tr("File is writable"));
    }
    if (currentEditor() == editor)
        m_editorList->setToolTip(model()->data(model()->indexOf(editor), Qt::ToolTipRole).toString());
    model()->emitDataChanged(editor);
}

void StackedEditorGroup::updateToolBar(IEditor *editor)
{
    QToolBar *toolBar = editor->toolBar();
    if (!toolBar)
        toolBar = m_defaultToolBar;
    if (m_activeToolBar == toolBar)
        return;
    m_activeToolBar->setVisible(false);
    toolBar->setVisible(true);
    m_activeToolBar = toolBar;
}

int StackedEditorGroup::editorCount() const
{
    return model()->editors().count();
}

QList<IEditor *> StackedEditorGroup::editors() const
{
    QAbstractItemModel *model = m_editorList->model();
    QList<IEditor*> output;
    int rows = model->rowCount();
    for (int i = 0; i < rows; ++i)
        output.append(model->data(model->index(i, 0), Qt::UserRole).value<IEditor*>());
    return output;
}

QList<IEditor *> StackedEditorGroup::editorsInNaturalOrder() const
{
    return model()->editors();
}

void StackedEditorGroup::makeEditorWritable()
{
    CoreImpl::instance()->editorManager()->makeEditorWritable(currentEditor());
}

void StackedEditorGroup::listSelectionChanged(int index)
{
    QAbstractItemModel *model = m_editorList->model();
    setCurrentEditor(model->data(model->index(index, 0), Qt::UserRole).value<IEditor*>());
}

int StackedEditorGroup::indexOf(IEditor *editor)
{
    QAbstractItemModel *model = m_editorList->model();
    int rows = model->rowCount();
    for (int i = 0; i < rows; ++i) {
        if (editor == model->data(model->index(i, 0), Qt::UserRole).value<IEditor*>())
            return i;
    }
    QTC_ASSERT(false, /**/);
    return 0;
}
<commit_msg>fix show on split<commit_after>/***************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies).
**
** Contact:  Qt Software Information (qt-info@nokia.com)
**
**
** Non-Open Source Usage
**
** Licensees may use this file in accordance with the Qt Beta Version
** License Agreement, Agreement version 2.2 provided with the Software or,
** alternatively, in accordance with the terms contained in a written
** agreement between you and Nokia.
**
** GNU General Public License Usage
**
** Alternatively, this file may be used under the terms of the GNU General
** Public License versions 2.0 or 3.0 as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL included in the packaging
** of this file.  Please review the following information to ensure GNU
** General Public Licensing requirements will be met:
**
** http://www.fsf.org/licensing/licenses/info/GPLv2.html and
** http://www.gnu.org/copyleft/gpl.html.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights. These rights are described in the Nokia Qt GPL Exception
** version 1.2, included in the file GPL_EXCEPTION.txt in this package.
**
***************************************************************************/

#include "stackededitorgroup.h"
#include "editormanager.h"
#include "coreimpl.h"

#include <utils/qtcassert.h>

#include <QtCore/QDebug>
#include <QtCore/QDir>
#include <QtCore/QFileInfo>
#include <QtCore/QMimeData>

#include <QtGui/QApplication>
#include <QtGui/QComboBox>
#include <QtGui/QHBoxLayout>
#include <QtGui/QLabel>
#include <QtGui/QMouseEvent>
#include <QtGui/QPainter>
#include <QtGui/QStackedWidget>
#include <QtGui/QStyle>
#include <QtGui/QStyleOption>
#include <QtGui/QToolBar>
#include <QtGui/QToolButton>
#ifdef Q_WS_MAC
#include <qmacstyle_mac.h>
#endif

Q_DECLARE_METATYPE(Core::IEditor *)

using namespace Core;
using namespace Core::Internal;

StackedEditorGroup::StackedEditorGroup(QWidget *parent) :
    EditorGroup(parent),
    m_toplevel(new QWidget),
    m_toolBar(new QWidget),
    m_container(new QStackedWidget(this)),
    m_editorList(new QComboBox),
    m_closeButton(new QToolButton),
    m_lockButton(new QToolButton),
    m_defaultToolBar(new QToolBar(this)),
    m_infoWidget(new QFrame(this)),
    m_editorForInfoWidget(0)
{
    QVBoxLayout *tl = new QVBoxLayout(m_toplevel);
    tl->setSpacing(0);
    tl->setMargin(0);
    {
        m_editorList->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
        m_editorList->setMinimumContentsLength(20);
        m_proxyModel.setSourceModel(model());
        m_proxyModel.sort(0);
        m_editorList->setModel(&m_proxyModel);
        m_editorList->setMaxVisibleItems(40);

        QToolBar *editorListToolBar = new QToolBar;

        editorListToolBar->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Ignored);
        editorListToolBar->addWidget(m_editorList);

        m_defaultToolBar->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
        m_activeToolBar = m_defaultToolBar;

        QHBoxLayout *toolBarLayout = new QHBoxLayout;
        toolBarLayout->setMargin(0);
        toolBarLayout->setSpacing(0);
        toolBarLayout->addWidget(m_defaultToolBar);
        m_toolBar->setLayout(toolBarLayout);

        m_lockButton->setAutoRaise(true);
        m_lockButton->setProperty("type", QLatin1String("dockbutton"));

        m_closeButton->setAutoRaise(true);
        m_closeButton->setIcon(QIcon(":/qworkbench/images/closebutton.png"));
        m_closeButton->setProperty("type", QLatin1String("dockbutton"));

        QToolBar *rightToolBar = new QToolBar;
        rightToolBar->setLayoutDirection(Qt::RightToLeft);
        rightToolBar->addWidget(m_closeButton);
        rightToolBar->addWidget(m_lockButton);

        QHBoxLayout *toplayout = new QHBoxLayout;
        toplayout->setSpacing(0);
        toplayout->setMargin(0);
        toplayout->addWidget(editorListToolBar);
        toplayout->addWidget(m_toolBar, 1); // Custom toolbar stretches
        toplayout->addWidget(rightToolBar);

        QWidget *top = new QWidget;
        QVBoxLayout *vlayout = new QVBoxLayout(top);
        vlayout->setSpacing(0);
        vlayout->setMargin(0);
        vlayout->addLayout(toplayout);
        tl->addWidget(top);

        connect(m_editorList, SIGNAL(currentIndexChanged(int)), this, SLOT(listSelectionChanged(int)));
        connect(m_lockButton, SIGNAL(clicked()), this, SLOT(makeEditorWritable()));
        connect(m_closeButton, SIGNAL(clicked()), this, SLOT(sendCloseRequest()));
    }
    {
        m_infoWidget->setFrameStyle(QFrame::Panel | QFrame::Raised);
        m_infoWidget->setLineWidth(1);
        m_infoWidget->setForegroundRole(QPalette::ToolTipText);
        m_infoWidget->setBackgroundRole(QPalette::ToolTipBase);
        m_infoWidget->setAutoFillBackground(true);

        QHBoxLayout *hbox = new QHBoxLayout(m_infoWidget);
        hbox->setMargin(2);
        m_infoWidgetLabel = new QLabel("Placeholder");
        m_infoWidgetLabel->setForegroundRole(QPalette::ToolTipText);
        hbox->addWidget(m_infoWidgetLabel);
        hbox->addStretch(1);

        m_infoWidgetButton = new QToolButton;
        m_infoWidgetButton->setText(tr("Placeholder"));
        hbox->addWidget(m_infoWidgetButton);

        QToolButton *closeButton = new QToolButton;
        closeButton->setAutoRaise(true);
        closeButton->setIcon(QIcon(":/qworkbench/images/clear.png"));
        closeButton->setToolTip(tr("Close"));
        connect(closeButton, SIGNAL(clicked()), m_infoWidget, SLOT(hide()));

        hbox->addWidget(closeButton);

        m_infoWidget->setVisible(false);
        tl->addWidget(m_infoWidget);
    }
    tl->addWidget(m_container);

    QHBoxLayout *l = new QHBoxLayout;
    l->setSpacing(0);
    l->setMargin(0);
    l->addWidget(m_toplevel);
    setLayout(l);
}

void StackedEditorGroup::showEditorInfoBar(const QString &kind,
                                           const QString &infoText,
                                           const QString &buttonText,
                                           QObject *object, const char *member)
{
    m_infoWidgetKind = kind;
    m_infoWidgetLabel->setText(infoText);
    m_infoWidgetButton->setText(buttonText);
    m_infoWidgetButton->disconnect();
    if (object && member)
        connect(m_infoWidgetButton, SIGNAL(clicked()), object, member);
    m_infoWidget->setVisible(true);
    m_editorForInfoWidget = currentEditor();
}

void StackedEditorGroup::hideEditorInfoBar(const QString &kind)
{
    if (kind == m_infoWidgetKind)
        m_infoWidget->setVisible(false);
}

StackedEditorGroup::~StackedEditorGroup()
{
}

void StackedEditorGroup::focusInEvent(QFocusEvent *e)
{
    if (m_container->count() > 0) {
        setEditorFocus(m_container->currentIndex());
    } else {
        EditorGroup::focusInEvent(e);
    }
}

void StackedEditorGroup::setEditorFocus(int index)
{
    QWidget *w = m_container->widget(index);
    w->setFocus();
}

void StackedEditorGroup::addEditor(IEditor *editor)
{
    insertEditor(editorCount(), editor);
}

void StackedEditorGroup::insertEditor(int index, IEditor *editor)
{
    EditorGroup::insertEditor(index, editor);
    if (m_container->indexOf(editor->widget()) != -1)
        return;

    m_container->insertWidget(index, editor->widget());
    m_widgetEditorMap.insert(editor->widget(), editor);

    QToolBar *toolBar = editor->toolBar();
    if (toolBar)
        m_toolBar->layout()->addWidget(toolBar);
    connect(editor, SIGNAL(changed()), this, SLOT(updateEditorStatus()));

    updateEditorStatus(editor);
    updateToolBar(editor);

    emit editorAdded(editor);
}

void StackedEditorGroup::sendCloseRequest()
{
    emit closeRequested(currentEditor());
}

void StackedEditorGroup::removeEditor(IEditor *editor)
{
    QTC_ASSERT(editor, return);
    EditorGroup::removeEditor(editor);
    const int index = m_container->indexOf(editor->widget());
    if (index != -1) {
        m_container->removeWidget(editor->widget());
        m_widgetEditorMap.remove(editor->widget());
        editor->widget()->setParent(0);
        disconnect(editor, SIGNAL(changed()), this, SLOT(updateEditorStatus()));
        QToolBar *toolBar = editor->toolBar();
        if (toolBar != 0) {
            if (m_activeToolBar == toolBar) {
                m_activeToolBar = m_defaultToolBar;
                m_activeToolBar->setVisible(true);
            }
            m_toolBar->layout()->removeWidget(toolBar);
            toolBar->setVisible(false);
            toolBar->setParent(0);
        }
        emit editorRemoved(editor);
    }
}

IEditor *StackedEditorGroup::currentEditor() const
{
    if (m_container->count() > 0)
        return m_widgetEditorMap.value(m_container->currentWidget());
    return 0;
}

void StackedEditorGroup::setCurrentEditor(IEditor *editor)
{
    if (!editor || m_container->count() <= 0
        || m_container->indexOf(editor->widget()) == -1)
        return;
    const int idx = m_container->indexOf(editor->widget());
    QTC_ASSERT(idx >= 0, return);
    if (m_container->currentIndex() != idx) {
        m_container->setCurrentIndex(idx);

        const bool block = m_editorList->blockSignals(true);
        m_editorList->setCurrentIndex(indexOf(editor));
        m_editorList->blockSignals(block);

        updateEditorStatus(editor);
        updateToolBar(editor);
    }
    setEditorFocus(idx);
    if (editor != m_editorForInfoWidget) {
        m_infoWidget->hide();
        m_editorForInfoWidget = 0;
    }
}

void StackedEditorGroup::updateEditorStatus(IEditor *editor)
{
    if (!editor)
        editor = qobject_cast<IEditor *>(sender());
    QTC_ASSERT(editor, return);

    static const QIcon lockedIcon(QLatin1String(":/qworkbench/images/locked.png"));
    static const QIcon unlockedIcon(QLatin1String(":/qworkbench/images/unlocked.png"));

    if (editor->file()->isReadOnly()) {
        m_lockButton->setIcon(lockedIcon);
        m_lockButton->setEnabled(!editor->file()->fileName().isEmpty());
        m_lockButton->setToolTip(tr("Make writable"));
    } else {
        m_lockButton->setIcon(unlockedIcon);
        m_lockButton->setEnabled(false);
        m_lockButton->setToolTip(tr("File is writable"));
    }
    if (currentEditor() == editor)
        m_editorList->setToolTip(model()->data(model()->indexOf(editor), Qt::ToolTipRole).toString());
    model()->emitDataChanged(editor);
}

void StackedEditorGroup::updateToolBar(IEditor *editor)
{
    QToolBar *toolBar = editor->toolBar();
    if (!toolBar)
        toolBar = m_defaultToolBar;
    if (m_activeToolBar == toolBar)
        return;
    m_activeToolBar->setVisible(false);
    toolBar->setVisible(true);
    m_activeToolBar = toolBar;
}

int StackedEditorGroup::editorCount() const
{
    return model()->editors().count();
}

QList<IEditor *> StackedEditorGroup::editors() const
{
    QAbstractItemModel *model = m_editorList->model();
    QList<IEditor*> output;
    int rows = model->rowCount();
    for (int i = 0; i < rows; ++i)
        output.append(model->data(model->index(i, 0), Qt::UserRole).value<IEditor*>());
    return output;
}

QList<IEditor *> StackedEditorGroup::editorsInNaturalOrder() const
{
    return model()->editors();
}

void StackedEditorGroup::makeEditorWritable()
{
    CoreImpl::instance()->editorManager()->makeEditorWritable(currentEditor());
}

void StackedEditorGroup::listSelectionChanged(int index)
{
    QAbstractItemModel *model = m_editorList->model();
    setCurrentEditor(model->data(model->index(index, 0), Qt::UserRole).value<IEditor*>());
}

int StackedEditorGroup::indexOf(IEditor *editor)
{
    QAbstractItemModel *model = m_editorList->model();
    int rows = model->rowCount();
    for (int i = 0; i < rows; ++i) {
        if (editor == model->data(model->index(i, 0), Qt::UserRole).value<IEditor*>())
            return i;
    }
    QTC_ASSERT(false, /**/);
    return 0;
}
<|endoftext|>