<commit_before>/**
 * The MIT License (MIT)
 *
 * Copyright (c) 2018 Simon Sandström
 *
 * 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 "gtest/gtest.h"
#include "gmock/gmock.h"

#include "container_manager.h"
#include "player_ctrl_mock.h"
#include "world_interface.h"

using ::testing::Return;
using ::testing::ReturnRef;
using ::testing::Ref;
using ::testing::_;
using ::testing::SaveArg;

struct ItemStub : public Item
{
  ItemStub()
      : itemUniqueId_(0u),
        itemTypeId_(0),
        itemType_(),
        count_(0)
  {
  }

  ItemStub(ItemUniqueId itemUniqueId,
           ItemTypeId itemTypeId,
           ItemType itemType,
           int count)
      : itemUniqueId_(itemUniqueId),
        itemTypeId_(itemTypeId),
        itemType_(itemType),
        count_(count)
  {
  }

  ItemUniqueId getItemUniqueId() const { return itemUniqueId_; }
  ItemTypeId getItemTypeId() const { return itemTypeId_; }

  const ItemType& getItemType() const { return itemType_; }

  int getCount() const { return count_; }
  void setCount(int count) { count_ = count; }

  ItemUniqueId itemUniqueId_;
  ItemTypeId itemTypeId_;
  ItemType itemType_;
  int count_;
};

// TODO(simon): add helper functions

class ContainerManagerTest : public ::testing::Test
{
 protected:
  ContainerManagerTest()
  {
    EXPECT_CALL(player_ctrl_mock_, getPlayerId()).WillRepeatedly(Return(player_id));

    containerIds.fill(Item::INVALID_UNIQUE_ID);

    itemTypeContainer.id = 123;
    itemTypeContainer.isContainer = true;

    itemTypeNotContainer.id = 456;
    itemTypeNotContainer.isContainer = false;

    itemContainerA = ItemStub { 100u, itemTypeContainer.id, itemTypeContainer, 1 };
    itemContainerB = ItemStub { 101u, itemTypeContainer.id, itemTypeContainer, 1 };
    itemContainerC = ItemStub { 102u, itemTypeContainer.id, itemTypeContainer, 1 };

    itemNotContainerA = ItemStub { 200u, itemTypeNotContainer.id, itemTypeNotContainer, 1 };
    itemNotContainerB = ItemStub { 201u, itemTypeNotContainer.id, itemTypeNotContainer, 2 };
    itemNotContainerC = ItemStub { 202u, itemTypeNotContainer.id, itemTypeNotContainer, 3 };
  }

  ~ContainerManagerTest()
  {
    EXPECT_CALL(player_ctrl_mock_, getContainerIds()).WillOnce(ReturnRef(containerIds));
    container_manager_.playerDespawn(&player_ctrl_mock_);

    // TODO(simon): Verify that no containers have related players?
  }

  PlayerCtrlMock player_ctrl_mock_;
  ContainerManager container_manager_;
  const CreatureId player_id = 123;

  ItemType itemTypeContainer;
  ItemType itemTypeNotContainer;

  ItemStub itemContainerA;
  ItemStub itemContainerB;
  ItemStub itemContainerC;
  ItemStub itemNotContainerA;
  ItemStub itemNotContainerB;
  ItemStub itemNotContainerC;

  std::array<ItemUniqueId, 64> containerIds;
};

TEST_F(ContainerManagerTest, useContainer)
{
  // Create/open a container located in player inventory slot 0
  const auto itemContainerAPos = ItemPosition(GamePosition(0), itemContainerA.getItemTypeId());
  const auto clientContainerIdA = 1u;
  // TODO: SaveArg<1> and match with getContainer
  EXPECT_CALL(player_ctrl_mock_, hasContainerOpen(itemContainerA.getItemUniqueId())).WillOnce(Return(false));
  EXPECT_CALL(player_ctrl_mock_, onOpenContainer(clientContainerIdA, _, Ref(itemContainerA)));
  container_manager_.useContainer(&player_ctrl_mock_, itemContainerA, itemContainerAPos, clientContainerIdA);
  containerIds[clientContainerIdA] = itemContainerA.getItemUniqueId();

  // Validate the newly created container
  auto* containerA = container_manager_.getContainer(itemContainerA.getItemUniqueId());
  ASSERT_TRUE(containerA != nullptr);
  EXPECT_EQ(0, containerA->weight);
  EXPECT_EQ(itemContainerA, *containerA->item);
  EXPECT_EQ(Item::INVALID_UNIQUE_ID, containerA->parentItemUniqueId);
  EXPECT_EQ(itemContainerAPos, containerA->rootItemPosition);
  EXPECT_TRUE(containerA->items.empty());
  EXPECT_EQ(1u, containerA->relatedPlayers.size());
  EXPECT_EQ(1u,                 containerA->relatedPlayers.size());
  EXPECT_EQ(&player_ctrl_mock_, containerA->relatedPlayers.front());

  // Create/open a container located in the world
  const auto itemContainerBPos = ItemPosition(GamePosition(Position(1, 2, 3)), itemContainerB.getItemTypeId());
  const auto clientContainerIdB = 2u;
  // TODO: SaveArg<1> and match with getContainer
  EXPECT_CALL(player_ctrl_mock_, hasContainerOpen(itemContainerB.getItemUniqueId())).WillOnce(Return(false));
  EXPECT_CALL(player_ctrl_mock_, onOpenContainer(clientContainerIdB, _, Ref(itemContainerB)));
  container_manager_.useContainer(&player_ctrl_mock_, itemContainerB, itemContainerBPos, clientContainerIdB);
  containerIds[clientContainerIdB] = itemContainerB.getItemUniqueId();

  // Validate the newly created container
  auto* containerB = container_manager_.getContainer(itemContainerB.getItemUniqueId());
  ASSERT_TRUE(containerB != nullptr);
  EXPECT_EQ(0, containerB->weight);
  EXPECT_EQ(itemContainerB, *containerB->item);
  EXPECT_EQ(Item::INVALID_UNIQUE_ID, containerB->parentItemUniqueId);
  EXPECT_EQ(itemContainerBPos, containerB->rootItemPosition);
  EXPECT_TRUE(containerB->items.empty());
  EXPECT_EQ(1u, containerB->relatedPlayers.size());
  EXPECT_EQ(1u,                 containerB->relatedPlayers.size());
  EXPECT_EQ(&player_ctrl_mock_, containerB->relatedPlayers.front());

  // Make sure that both containers exist
  EXPECT_EQ(containerA, container_manager_.getContainer(itemContainerA.getItemUniqueId()));
  EXPECT_EQ(containerB, container_manager_.getContainer(itemContainerB.getItemUniqueId()));
}

TEST_F(ContainerManagerTest, useContainerWithSameId)
{
  // Create/open a container located in player inventory slot 0
  const auto itemContainerAPos = ItemPosition(GamePosition(0), itemContainerA.getItemTypeId());
  const auto clientContainerId = 1u;
  EXPECT_CALL(player_ctrl_mock_, hasContainerOpen(itemContainerA.getItemUniqueId())).WillOnce(Return(false));
  EXPECT_CALL(player_ctrl_mock_, onOpenContainer(clientContainerId, _, Ref(itemContainerA)));
  container_manager_.useContainer(&player_ctrl_mock_, itemContainerA, itemContainerAPos, clientContainerId);
  containerIds[clientContainerId] = itemContainerA.getItemUniqueId();

  // Create/open a container located in the world, with same id as previous container
  const auto itemContainerBPos = ItemPosition(GamePosition(Position(1, 2, 3)), itemContainerB.getItemTypeId());
  EXPECT_CALL(player_ctrl_mock_, hasContainerOpen(itemContainerB.getItemUniqueId())).WillOnce(Return(false));
  EXPECT_CALL(player_ctrl_mock_, onOpenContainer(clientContainerId, _, Ref(itemContainerB)));
  container_manager_.useContainer(&player_ctrl_mock_, itemContainerB, itemContainerBPos, clientContainerId);
  containerIds[clientContainerId] = itemContainerB.getItemUniqueId();
}

TEST_F(ContainerManagerTest, closeContainer)
{
  // Create/open a container located in player inventory slot 0
  const auto itemContainerAPos = ItemPosition(GamePosition(0), itemContainerA.getItemTypeId());
  const auto clientContainerIdA = 1u;
  EXPECT_CALL(player_ctrl_mock_, hasContainerOpen(itemContainerA.getItemUniqueId())).WillOnce(Return(false));
  EXPECT_CALL(player_ctrl_mock_, onOpenContainer(clientContainerIdA, _, Ref(itemContainerA)));
  container_manager_.useContainer(&player_ctrl_mock_, itemContainerA, itemContainerAPos, clientContainerIdA);
  containerIds[clientContainerIdA] = itemContainerA.getItemUniqueId();

  // Use it again to close the container
  EXPECT_CALL(player_ctrl_mock_, hasContainerOpen(itemContainerA.getItemUniqueId())).WillOnce(Return(true));
  EXPECT_CALL(player_ctrl_mock_, onCloseContainer(itemContainerA.getItemUniqueId(), false));
  container_manager_.useContainer(&player_ctrl_mock_, itemContainerA, itemContainerAPos, clientContainerIdA);

  // We need to ack by calling closeContainer
  EXPECT_CALL(player_ctrl_mock_, onCloseContainer(itemContainerA.getItemUniqueId(), true));
  container_manager_.closeContainer(&player_ctrl_mock_, itemContainerA.getItemUniqueId());
  containerIds[clientContainerIdA] = Item::INVALID_UNIQUE_ID;

  // Use the item again to open the container
  EXPECT_CALL(player_ctrl_mock_, hasContainerOpen(itemContainerA.getItemUniqueId())).WillOnce(Return(false));
  EXPECT_CALL(player_ctrl_mock_, onOpenContainer(clientContainerIdA, _, Ref(itemContainerA)));
  container_manager_.useContainer(&player_ctrl_mock_, itemContainerA, itemContainerAPos, clientContainerIdA);
  containerIds[clientContainerIdA] = itemContainerA.getItemUniqueId();

  // Close it without "using" the item
  EXPECT_CALL(player_ctrl_mock_, onCloseContainer(itemContainerA.getItemUniqueId(), true));
  container_manager_.closeContainer(&player_ctrl_mock_, itemContainerA.getItemUniqueId());
  containerIds[clientContainerIdA] = Item::INVALID_UNIQUE_ID;
}

TEST_F(ContainerManagerTest, innerContainer)
{
  // Create/open a container located in player inventory slot 0
  const auto itemContainerAPos = ItemPosition(GamePosition(0), itemContainerA.getItemTypeId());
  const auto clientContainerIdA = 1u;
  EXPECT_CALL(player_ctrl_mock_, hasContainerOpen(itemContainerA.getItemUniqueId())).WillOnce(Return(false));
  EXPECT_CALL(player_ctrl_mock_, onOpenContainer(clientContainerIdA, _, Ref(itemContainerA)));
  container_manager_.useContainer(&player_ctrl_mock_, itemContainerA, itemContainerAPos, clientContainerIdA);
  containerIds[clientContainerIdA] = itemContainerA.getItemUniqueId();

  // Add a regular item (slot 19, at the end of the container)
  EXPECT_CALL(player_ctrl_mock_, onContainerAddItem(itemContainerA.getItemUniqueId(), Ref(itemNotContainerA)));
  container_manager_.addItem(itemContainerA.getItemUniqueId(), 19, &itemNotContainerA);

  // Add another container
  EXPECT_CALL(player_ctrl_mock_, onContainerAddItem(itemContainerA.getItemUniqueId(), Ref(itemContainerB)));
  container_manager_.addItem(itemContainerA.getItemUniqueId(), 19, &itemContainerB);

  // Add a regular item
  EXPECT_CALL(player_ctrl_mock_, onContainerAddItem(itemContainerA.getItemUniqueId(), Ref(itemNotContainerB)));
  container_manager_.addItem(itemContainerA.getItemUniqueId(), 19, &itemNotContainerB);

  // The container should now contain:
  // itemContainerA:
  //   0: itemNotContainerB
  //   1: itemContainerB
  //   2: itemNotContainerA
  const auto* containerA = container_manager_.getContainer(itemContainerA.getItemUniqueId());
  ASSERT_EQ(3u, containerA->items.size());
  EXPECT_EQ(itemNotContainerB, *(containerA->items[0]));
  EXPECT_EQ(itemContainerB,    *(containerA->items[1]));
  EXPECT_EQ(itemNotContainerA, *(containerA->items[2]));

  // Now add a regular item to containerA, stackPosition 1
  // This should add the item to containerB as itemContainerB is at stackPosition 1
  // containerB is not open so there should not be any onContainerAddItem call
  // Before the call there should not exist a container for itemContainerB
  // After the call a container should exist
  EXPECT_TRUE(container_manager_.getContainer(itemContainerB.getItemUniqueId()) == nullptr);
  container_manager_.addItem(itemContainerA.getItemUniqueId(), 1, &itemNotContainerC);
  EXPECT_TRUE(container_manager_.getContainer(itemContainerB.getItemUniqueId()) != nullptr);

  // Now open the new container
  const auto clientContainerIdB = 2u;
  EXPECT_CALL(player_ctrl_mock_, hasContainerOpen(itemContainerB.getItemUniqueId())).WillOnce(Return(false));
  EXPECT_CALL(player_ctrl_mock_, onOpenContainer(clientContainerIdB, _, Ref(itemContainerB)));
  container_manager_.useContainer(&player_ctrl_mock_,
                                  itemContainerB,
                                  ItemPosition(GamePosition(itemContainerA.getItemUniqueId(), 1),
                                               itemContainerA.getItemTypeId(),
                                               0),
                                  clientContainerIdB);
  containerIds[clientContainerIdB] = itemContainerB.getItemUniqueId();

  // Current container structure should now be:
  // itemContainerA:
  //   0: itemNotContainerB
  //   1: ContainerB
  //     0: itemNotContainerC
  //   2: itemNotContainerA
  // Verify:
  {
    const auto containerA = container_manager_.getContainer(itemContainerA.getItemUniqueId());
    const auto containerB = container_manager_.getContainer(itemContainerB.getItemUniqueId());

    EXPECT_EQ(itemContainerA, *(containerA->item));
    ASSERT_EQ(3u, containerA->items.size());
    EXPECT_EQ(itemNotContainerB, *(containerA->items[0]));
    EXPECT_EQ(itemContainerB,    *(containerA->items[1]));
    EXPECT_EQ(itemNotContainerA, *(containerA->items[2]));

    EXPECT_EQ(itemContainerB, *(containerB->item));
    ASSERT_EQ(1u, containerB->items.size());
    EXPECT_EQ(itemNotContainerC, *(containerB->items[0]));

    // Verify parentContainer and rootPosition
    EXPECT_EQ(Item::INVALID_UNIQUE_ID,          containerA->parentItemUniqueId);
    EXPECT_EQ(itemContainerA.getItemUniqueId(), containerB->parentItemUniqueId);
    EXPECT_EQ(itemContainerAPos,                containerA->rootItemPosition);
    EXPECT_EQ(itemContainerAPos,                containerB->rootItemPosition);
  }
}

TEST_F(ContainerManagerTest, playerDespawn)
{
  // TODO
}

TEST_F(ContainerManagerTest, multiplePlayers)
{
  // TODO
}

TEST_F(ContainerManagerTest, moveContainer)
{

}
<commit_msg>Add helpers to ContainerManagerTest<commit_after>/**
 * The MIT License (MIT)
 *
 * Copyright (c) 2018 Simon Sandström
 *
 * 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 "gtest/gtest.h"
#include "gmock/gmock.h"

#include "container_manager.h"
#include "player_ctrl_mock.h"
#include "world_interface.h"

using ::testing::Return;
using ::testing::ReturnRef;
using ::testing::Ref;
using ::testing::_;
using ::testing::SaveArg;
using ::testing::WithArg;
using ::testing::Invoke;

struct ItemStub : public Item
{
  ItemStub()
      : itemUniqueId_(0u),
        itemTypeId_(0),
        itemType_(),
        count_(0)
  {
  }

  ItemStub(ItemUniqueId itemUniqueId,
           ItemTypeId itemTypeId,
           ItemType itemType,
           int count)
      : itemUniqueId_(itemUniqueId),
        itemTypeId_(itemTypeId),
        itemType_(itemType),
        count_(count)
  {
  }

  ItemUniqueId getItemUniqueId() const { return itemUniqueId_; }
  ItemTypeId getItemTypeId() const { return itemTypeId_; }

  const ItemType& getItemType() const { return itemType_; }

  int getCount() const { return count_; }
  void setCount(int count) { count_ = count; }

  ItemUniqueId itemUniqueId_;
  ItemTypeId itemTypeId_;
  ItemType itemType_;
  int count_;
};

// TODO(simon): add helper functions

class ContainerManagerTest : public ::testing::Test
{
 protected:
  ContainerManagerTest()
  {
    EXPECT_CALL(player_ctrl_mock_, getPlayerId()).WillRepeatedly(Return(player_id));

    containerIds.fill(Item::INVALID_UNIQUE_ID);

    itemTypeContainer.id = 123;
    itemTypeContainer.isContainer = true;

    itemTypeNotContainer.id = 456;
    itemTypeNotContainer.isContainer = false;

    itemContainerA = ItemStub { 100u, itemTypeContainer.id, itemTypeContainer, 1 };
    itemContainerB = ItemStub { 101u, itemTypeContainer.id, itemTypeContainer, 1 };
    itemContainerC = ItemStub { 102u, itemTypeContainer.id, itemTypeContainer, 1 };

    itemContainerAPos = ItemPosition(GamePosition(0), itemContainerA.getItemTypeId());
    itemContainerBPos = ItemPosition(GamePosition(Position(1, 2, 3)), itemContainerB.getItemTypeId());

    clientContainerIdA = 1;
    clientContainerIdB = 2;
    clientContainerIdC = 3;

    itemNotContainerA = ItemStub { 200u, itemTypeNotContainer.id, itemTypeNotContainer, 1 };
    itemNotContainerB = ItemStub { 201u, itemTypeNotContainer.id, itemTypeNotContainer, 2 };
    itemNotContainerC = ItemStub { 202u, itemTypeNotContainer.id, itemTypeNotContainer, 3 };
  }

  ~ContainerManagerTest()
  {
    EXPECT_CALL(player_ctrl_mock_, getContainerIds()).WillOnce(ReturnRef(containerIds));
    container_manager_.playerDespawn(&player_ctrl_mock_);

    // TODO(simon): Verify that no containers have related players?
  }

  Container* createAndOpenContainer(const ItemStub& itemContainer,
                              const ItemPosition& itemPosition,
                              int clientContainerId)
  {
    const Container* savedContainer = nullptr;
    const auto saveContainer = [&savedContainer](const Container& container)
    {
      savedContainer = &container;
    };

    EXPECT_CALL(player_ctrl_mock_, hasContainerOpen(itemContainer.getItemUniqueId())).WillOnce(Return(false));
    EXPECT_CALL(player_ctrl_mock_, onOpenContainer(clientContainerId, _, Ref(itemContainer)))
      .WillOnce(WithArg<1>(Invoke(saveContainer)));
    container_manager_.useContainer(&player_ctrl_mock_, itemContainer, itemPosition, clientContainerId);
    containerIds[clientContainerId] = itemContainer.getItemUniqueId();
    if (!savedContainer)
    {
      return nullptr;
    }
    EXPECT_EQ(savedContainer, container_manager_.getContainer(itemContainer.getItemUniqueId()));
    return container_manager_.getContainer(itemContainer.getItemUniqueId());
  }

  PlayerCtrlMock player_ctrl_mock_;
  ContainerManager container_manager_;
  const CreatureId player_id = 123;

  ItemType itemTypeContainer;
  ItemType itemTypeNotContainer;

  ItemStub itemContainerA;
  ItemStub itemContainerB;
  ItemStub itemContainerC;

  ItemPosition itemContainerAPos;
  ItemPosition itemContainerBPos;
  ItemPosition itemContainerCPos;

  int clientContainerIdA;
  int clientContainerIdB;
  int clientContainerIdC;

  ItemStub itemNotContainerA;
  ItemStub itemNotContainerB;
  ItemStub itemNotContainerC;

  std::array<ItemUniqueId, 64> containerIds;
};

TEST_F(ContainerManagerTest, useContainer)
{
  // Create, use/open and validate a new container
  auto* containerA = createAndOpenContainer(itemContainerA, itemContainerAPos, clientContainerIdA);
  ASSERT_TRUE(containerA != nullptr);
  EXPECT_EQ(0,                       containerA->weight);
  EXPECT_EQ(itemContainerA,         *containerA->item);
  EXPECT_EQ(Item::INVALID_UNIQUE_ID, containerA->parentItemUniqueId);
  EXPECT_EQ(itemContainerAPos,       containerA->rootItemPosition);
  EXPECT_TRUE(containerA->items.empty());
  EXPECT_EQ(1u,                      containerA->relatedPlayers.size());
  EXPECT_EQ(&player_ctrl_mock_,      containerA->relatedPlayers.front());

  // Create, use/open and validate a new container
  auto* containerB = createAndOpenContainer(itemContainerB, itemContainerBPos, clientContainerIdB);
  ASSERT_TRUE(containerB != nullptr);
  EXPECT_EQ(0,                       containerB->weight);
  EXPECT_EQ(itemContainerB,         *containerB->item);
  EXPECT_EQ(Item::INVALID_UNIQUE_ID, containerB->parentItemUniqueId);
  EXPECT_EQ(itemContainerBPos,       containerB->rootItemPosition);
  EXPECT_TRUE(containerB->items.empty());
  EXPECT_EQ(1u,                      containerB->relatedPlayers.size());
  EXPECT_EQ(&player_ctrl_mock_,      containerB->relatedPlayers.front());

  // Make sure that both containers still exist
  EXPECT_EQ(containerA, container_manager_.getContainer(itemContainerA.getItemUniqueId()));
  EXPECT_EQ(containerB, container_manager_.getContainer(itemContainerB.getItemUniqueId()));
}

TEST_F(ContainerManagerTest, useContainerWithSameId)
{
  // Create/open a container located in player inventory slot 0
  createAndOpenContainer(itemContainerA, itemContainerAPos, clientContainerIdA);

  // Create/open a container located in the world, with same id as previous container
  createAndOpenContainer(itemContainerB, itemContainerBPos, clientContainerIdA);
}

TEST_F(ContainerManagerTest, closeContainer)
{
  // Create/open a container located in player inventory slot 0
  createAndOpenContainer(itemContainerA, itemContainerAPos, clientContainerIdA);

  // Use it again to close the container
  EXPECT_CALL(player_ctrl_mock_, hasContainerOpen(itemContainerA.getItemUniqueId())).WillOnce(Return(true));
  EXPECT_CALL(player_ctrl_mock_, onCloseContainer(itemContainerA.getItemUniqueId(), false));
  container_manager_.useContainer(&player_ctrl_mock_, itemContainerA, itemContainerAPos, clientContainerIdA);

  // We need to ack by calling closeContainer
  EXPECT_CALL(player_ctrl_mock_, onCloseContainer(itemContainerA.getItemUniqueId(), true));
  container_manager_.closeContainer(&player_ctrl_mock_, itemContainerA.getItemUniqueId());
  containerIds[clientContainerIdA] = Item::INVALID_UNIQUE_ID;

  // Use the item again to open the container
  EXPECT_CALL(player_ctrl_mock_, hasContainerOpen(itemContainerA.getItemUniqueId())).WillOnce(Return(false));
  EXPECT_CALL(player_ctrl_mock_, onOpenContainer(clientContainerIdA, _, Ref(itemContainerA)));
  container_manager_.useContainer(&player_ctrl_mock_, itemContainerA, itemContainerAPos, clientContainerIdA);
  containerIds[clientContainerIdA] = itemContainerA.getItemUniqueId();

  // Close it without "using" the item
  EXPECT_CALL(player_ctrl_mock_, onCloseContainer(itemContainerA.getItemUniqueId(), true));
  container_manager_.closeContainer(&player_ctrl_mock_, itemContainerA.getItemUniqueId());
  containerIds[clientContainerIdA] = Item::INVALID_UNIQUE_ID;
}

TEST_F(ContainerManagerTest, innerContainer)
{
  // Create/open a container located in player inventory slot 0
  const auto* containerA = createAndOpenContainer(itemContainerA, itemContainerAPos, clientContainerIdA);

  // Add a regular item (slot 19, at the end of the container)
  EXPECT_CALL(player_ctrl_mock_, onContainerAddItem(itemContainerA.getItemUniqueId(), Ref(itemNotContainerA)));
  container_manager_.addItem(itemContainerA.getItemUniqueId(), 19, &itemNotContainerA);

  // Add another container
  EXPECT_CALL(player_ctrl_mock_, onContainerAddItem(itemContainerA.getItemUniqueId(), Ref(itemContainerB)));
  container_manager_.addItem(itemContainerA.getItemUniqueId(), 19, &itemContainerB);

  // Add a regular item
  EXPECT_CALL(player_ctrl_mock_, onContainerAddItem(itemContainerA.getItemUniqueId(), Ref(itemNotContainerB)));
  container_manager_.addItem(itemContainerA.getItemUniqueId(), 19, &itemNotContainerB);

  // The container should now contain:
  // itemContainerA:
  //   0: itemNotContainerB
  //   1: itemContainerB
  //   2: itemNotContainerA
  ASSERT_EQ(3u, containerA->items.size());
  EXPECT_EQ(itemNotContainerB, *(containerA->items[0]));
  EXPECT_EQ(itemContainerB,    *(containerA->items[1]));
  EXPECT_EQ(itemNotContainerA, *(containerA->items[2]));

  // Now add a regular item to containerA, stackPosition 1
  // This should add the item to containerB as itemContainerB is at stackPosition 1
  // containerB is not open so there should not be any onContainerAddItem call
  // Before the call there should not exist a container for itemContainerB
  // After the call a container should exist
  EXPECT_TRUE(container_manager_.getContainer(itemContainerB.getItemUniqueId()) == nullptr);
  container_manager_.addItem(itemContainerA.getItemUniqueId(), 1, &itemNotContainerC);
  EXPECT_TRUE(container_manager_.getContainer(itemContainerB.getItemUniqueId()) != nullptr);

  // Now open the new container
  EXPECT_CALL(player_ctrl_mock_, hasContainerOpen(itemContainerB.getItemUniqueId())).WillOnce(Return(false));
  EXPECT_CALL(player_ctrl_mock_, onOpenContainer(clientContainerIdB, _, Ref(itemContainerB)));
  container_manager_.useContainer(&player_ctrl_mock_,
                                  itemContainerB,
                                  ItemPosition(GamePosition(itemContainerA.getItemUniqueId(), 1),
                                               itemContainerA.getItemTypeId(),
                                               0),
                                  clientContainerIdB);
  containerIds[clientContainerIdB] = itemContainerB.getItemUniqueId();

  // Current container structure should now be:
  // itemContainerA:
  //   0: itemNotContainerB
  //   1: ContainerB
  //     0: itemNotContainerC
  //   2: itemNotContainerA
  // Verify:
  {
    const auto containerA = container_manager_.getContainer(itemContainerA.getItemUniqueId());
    const auto containerB = container_manager_.getContainer(itemContainerB.getItemUniqueId());

    EXPECT_EQ(itemContainerA, *(containerA->item));
    ASSERT_EQ(3u, containerA->items.size());
    EXPECT_EQ(itemNotContainerB, *(containerA->items[0]));
    EXPECT_EQ(itemContainerB,    *(containerA->items[1]));
    EXPECT_EQ(itemNotContainerA, *(containerA->items[2]));

    EXPECT_EQ(itemContainerB, *(containerB->item));
    ASSERT_EQ(1u, containerB->items.size());
    EXPECT_EQ(itemNotContainerC, *(containerB->items[0]));

    // Verify parentContainer and rootPosition
    EXPECT_EQ(Item::INVALID_UNIQUE_ID,          containerA->parentItemUniqueId);
    EXPECT_EQ(itemContainerA.getItemUniqueId(), containerB->parentItemUniqueId);
    EXPECT_EQ(itemContainerAPos,                containerA->rootItemPosition);
    EXPECT_EQ(itemContainerAPos,                containerB->rootItemPosition);
  }
}

TEST_F(ContainerManagerTest, playerDespawn)
{
  // TODO
}

TEST_F(ContainerManagerTest, multiplePlayers)
{
  // TODO
}

TEST_F(ContainerManagerTest, moveContainer)
{

}
<|endoftext|>


<commit_before>#include "gui_deck_selector.h"

#include <QVBoxLayout>
#include <QLabel>
#include <QPushButton>
#include <QJsonArray>

QJsonObject GuiDeckSelector::GetConfig() const {
    QJsonObject config;
    QJsonArray cards;
    for (auto &guiCardSelector: selectors) {
        cards.append(guiCardSelector->GetConfig());
    }
    QJsonObject position;
    position["start"] = positionStart->value();
    position["end"] = positionEnd->value();

    config["cards"] = cards;
    config["selector_mode"] = selectorMode->currentData().toInt();
    config["position"] = position;
    config["enabled"] = enabled->isChecked();
    return config;
}

GuiDeckSelector::GuiDeckSelector(QWidget *parent) : QWidget(parent) {
    CreateElements();
    SetSpinBoxes();
    CreateLayout();
}

GuiDeckSelector::GuiDeckSelector(const QJsonObject& config, QWidget *parent) : GuiDeckSelector(parent) {
    auto index = selectorMode->findData(config["selector_mode"].toInt());
    if (index != -1)
        selectorMode->setCurrentIndex(index);

    auto positionConfig = config["position"].toObject();
    positionStart->setValue(static_cast<int>(positionConfig["begin"].toInt()));
    positionEnd->setValue(static_cast<int>(positionConfig["end"].toInt()));

    enabled->setChecked(config["enabled"].toBool());

    auto cards = config["cards"].toArray();
    for (int i = 0; i != cards.size(); ++i) {
        auto cardConfig = cards[i].toObject();
        AddCardSelector(cardConfig);
    }
}

void GuiDeckSelector::AddCardSelector() {
    AddCardSelector(new GuiCardSelector());
}

void GuiDeckSelector::AddCardSelector(const QJsonObject& config) {
    AddCardSelector(new GuiCardSelector(config));
}

void GuiDeckSelector::AddCardSelector(GuiCardSelector* selector) {
    selectors.insert(selector);
    QObject::connect(selector, &GuiCardSelector::DeleteClicked, [this, selector](){
            if (selectors.size()==1)
                return ;
            selectors.erase(selector);
            selector->disconnect();
            selectorsLayout->removeWidget(selector);
            selector->deleteLater();
            });
    selectorsLayout->insertWidget(selectorsLayout->count() - 1, selector);
}

QSpinBox* GuiDeckSelector::CreateSpinBox(int min, int max) {
    auto box = new QSpinBox();
    box->setRange(min, max);
    box->setSingleStep(1);
    return box;
}

void GuiDeckSelector::CreateElements() {
    selectorMode = new QComboBox();
    selectorMode->addItem(tr("At least one of conditions must match"), SELECT_ONE);
    selectorMode->addItem(tr("All conditions must match"), SELECT_ALL);
    selectorMode->setCurrentIndex(0);

    enabled = new QCheckBox(tr("Is enabled"));
}

void GuiDeckSelector::SetSpinBoxes() {
    positionStart = CreateSpinBox(1, 36);
    positionEnd = CreateSpinBox(1, 36);
}

void GuiDeckSelector::CreateLayout() {
    auto mainLayout = new QVBoxLayout();
    mainLayout->addWidget(enabled);
    mainLayout->addWidget(selectorMode);
    {
        auto layout = new QHBoxLayout();
        layout->addWidget(new QLabel(tr("Position from: ")));
        layout->addWidget(positionStart);
        layout->addWidget(new QLabel(tr(" to: ")));
        layout->addWidget(positionEnd);
        mainLayout->addLayout(layout);
    }
    {
        selectorsLayout = new QHBoxLayout();
        {
            auto button = new QPushButton(tr("Add a card"));
            selectorsLayout->addWidget(button);
            QObject::connect(button, &QPushButton::clicked, [this](){AddCardSelector();});
        }
        selectorsLayout->setSizeConstraint(QLayout::SetFixedSize);
        mainLayout->addLayout(selectorsLayout);
    }
    {
        auto deleteButton = new QPushButton(tr("Remove conditions set"));
        QObject::connect(deleteButton, &QPushButton::clicked, this, &GuiDeckSelector::DeleteClicked);
        mainLayout->addWidget(deleteButton);
    }
    setLayout(mainLayout);
}

std::unique_ptr<DeckAbstractSelector> GuiDeckSelector::GetSelector() const {
    if (selectors.empty())
        throw NoCards();

    std::unique_ptr<DeckAbstractSelector> selector;
    SelectorMode mode = static_cast<SelectorMode>(selectorMode->currentData().toUInt());

    std::vector<CardSelector> cardSelectors;
    for (auto &guiCardSelector: selectors) {
        cardSelectors.push_back(std::move(guiCardSelector->GetSelector()));
    }
    std::size_t from = static_cast<std::size_t>(positionStart->value());
    std::size_t to = static_cast<std::size_t>(positionEnd->value());

    switch (mode) {
        case SELECT_ONE:
            selector.reset(new DeckOneSelector(std::move(cardSelectors), from, to));
            break;
        case SELECT_ALL:
            selector.reset(new DeckAllSelector(std::move(cardSelectors), from, to));
            break;
        default:
            throw std::logic_error("Unknown mode");
    }
    return selector;
}

<commit_msg>Bugfix (typo in parameter name)<commit_after>#include "gui_deck_selector.h"

#include <QVBoxLayout>
#include <QLabel>
#include <QPushButton>
#include <QJsonArray>

QJsonObject GuiDeckSelector::GetConfig() const {
    QJsonObject config;
    QJsonArray cards;
    for (auto &guiCardSelector: selectors) {
        cards.append(guiCardSelector->GetConfig());
    }
    QJsonObject position;
    position["start"] = positionStart->value();
    position["end"] = positionEnd->value();

    config["cards"] = cards;
    config["selector_mode"] = selectorMode->currentData().toInt();
    config["position"] = position;
    config["enabled"] = enabled->isChecked();
    return config;
}

GuiDeckSelector::GuiDeckSelector(QWidget *parent) : QWidget(parent) {
    CreateElements();
    SetSpinBoxes();
    CreateLayout();
}

GuiDeckSelector::GuiDeckSelector(const QJsonObject& config, QWidget *parent) : GuiDeckSelector(parent) {
    auto index = selectorMode->findData(config["selector_mode"].toInt());
    if (index != -1)
        selectorMode->setCurrentIndex(index);

    auto positionConfig = config["position"].toObject();
    positionStart->setValue(static_cast<int>(positionConfig["start"].toInt()));
    positionEnd->setValue(static_cast<int>(positionConfig["end"].toInt()));

    enabled->setChecked(config["enabled"].toBool());

    auto cards = config["cards"].toArray();
    for (int i = 0; i != cards.size(); ++i) {
        auto cardConfig = cards[i].toObject();
        AddCardSelector(cardConfig);
    }
}

void GuiDeckSelector::AddCardSelector() {
    AddCardSelector(new GuiCardSelector());
}

void GuiDeckSelector::AddCardSelector(const QJsonObject& config) {
    AddCardSelector(new GuiCardSelector(config));
}

void GuiDeckSelector::AddCardSelector(GuiCardSelector* selector) {
    selectors.insert(selector);
    QObject::connect(selector, &GuiCardSelector::DeleteClicked, [this, selector](){
            if (selectors.size()==1)
                return ;
            selectors.erase(selector);
            selector->disconnect();
            selectorsLayout->removeWidget(selector);
            selector->deleteLater();
            });
    selectorsLayout->insertWidget(selectorsLayout->count() - 1, selector);
}

QSpinBox* GuiDeckSelector::CreateSpinBox(int min, int max) {
    auto box = new QSpinBox();
    box->setRange(min, max);
    box->setSingleStep(1);
    return box;
}

void GuiDeckSelector::CreateElements() {
    selectorMode = new QComboBox();
    selectorMode->addItem(tr("At least one of conditions must match"), SELECT_ONE);
    selectorMode->addItem(tr("All conditions must match"), SELECT_ALL);
    selectorMode->setCurrentIndex(0);

    enabled = new QCheckBox(tr("Is enabled"));
}

void GuiDeckSelector::SetSpinBoxes() {
    positionStart = CreateSpinBox(1, 36);
    positionEnd = CreateSpinBox(1, 36);
}

void GuiDeckSelector::CreateLayout() {
    auto mainLayout = new QVBoxLayout();
    mainLayout->addWidget(enabled);
    mainLayout->addWidget(selectorMode);
    {
        auto layout = new QHBoxLayout();
        layout->addWidget(new QLabel(tr("Position from: ")));
        layout->addWidget(positionStart);
        layout->addWidget(new QLabel(tr(" to: ")));
        layout->addWidget(positionEnd);
        mainLayout->addLayout(layout);
    }
    {
        selectorsLayout = new QHBoxLayout();
        {
            auto button = new QPushButton(tr("Add a card"));
            selectorsLayout->addWidget(button);
            QObject::connect(button, &QPushButton::clicked, [this](){AddCardSelector();});
        }
        selectorsLayout->setSizeConstraint(QLayout::SetFixedSize);
        mainLayout->addLayout(selectorsLayout);
    }
    {
        auto deleteButton = new QPushButton(tr("Remove conditions set"));
        QObject::connect(deleteButton, &QPushButton::clicked, this, &GuiDeckSelector::DeleteClicked);
        mainLayout->addWidget(deleteButton);
    }
    setLayout(mainLayout);
}

std::unique_ptr<DeckAbstractSelector> GuiDeckSelector::GetSelector() const {
    if (selectors.empty())
        throw NoCards();

    std::unique_ptr<DeckAbstractSelector> selector;
    SelectorMode mode = static_cast<SelectorMode>(selectorMode->currentData().toUInt());

    std::vector<CardSelector> cardSelectors;
    for (auto &guiCardSelector: selectors) {
        cardSelectors.push_back(std::move(guiCardSelector->GetSelector()));
    }
    std::size_t from = static_cast<std::size_t>(positionStart->value());
    std::size_t to = static_cast<std::size_t>(positionEnd->value());

    switch (mode) {
        case SELECT_ONE:
            selector.reset(new DeckOneSelector(std::move(cardSelectors), from, to));
            break;
        case SELECT_ALL:
            selector.reset(new DeckAllSelector(std::move(cardSelectors), from, to));
            break;
        default:
            throw std::logic_error("Unknown mode");
    }
    return selector;
}

<|endoftext|>


<commit_before><commit_msg>Update scanner.cc<commit_after><|endoftext|>


<commit_before>#include "testing/testing.hpp"

#include "generator/osm_id.hpp"
#include "generator/restriction_collector.hpp"

#include "indexer/routing.hpp"

#include "coding/file_name_utils.hpp"

#include "platform/platform_tests_support/scoped_dir.hpp"
#include "platform/platform_tests_support/scoped_file.hpp"

#include "platform/platform.hpp"

#include "std/string.hpp"
#include "std/utility.hpp"
#include "std/vector.hpp"

using namespace platform;
using namespace platform::tests_support;

namespace routing
{
string const kRestrictionTestDir = "test-restrictions";

UNIT_TEST(RestrictionTest_ValidCase)
{
  RestrictionCollector restrictionCollector("", "");
  // Adding restrictions and feature ids to restrictionCollector in mixed order.
  restrictionCollector.AddRestriction(Restriction::Type::No, {1, 2} /* osmIds */);
  restrictionCollector.AddFeatureId(30 /* featureId */, {3} /* osmIds */);
  restrictionCollector.AddRestriction(Restriction::Type::No, {2, 3} /* osmIds */);
  restrictionCollector.AddFeatureId(10 /* featureId */, {1} /* osmIds */);
  restrictionCollector.AddFeatureId(50 /* featureId */, {5} /* osmIds */);
  restrictionCollector.AddRestriction(Restriction::Type::Only, {5, 7} /* osmIds */);
  restrictionCollector.AddFeatureId(70 /* featureId */, {7} /* osmIds */);
  restrictionCollector.AddFeatureId(20 /* featureId */, {2} /* osmIds */);

  // Composing restriction in feature id terms.
  restrictionCollector.ComposeRestrictions();
  restrictionCollector.RemoveInvalidRestrictions();

  // Checking the result.
  TEST(restrictionCollector.IsValid(), ());

  RestrictionVec const expectedRestrictions = {{Restriction::Type::No, {10, 20}},
                                               {Restriction::Type::No, {20, 30}},
                                               {Restriction::Type::Only, {50, 70}}};
  TEST_EQUAL(restrictionCollector.m_restrictions, expectedRestrictions, ());
}

UNIT_TEST(RestrictionTest_InvalidCase)
{
  RestrictionCollector restrictionCollector("", "");
  restrictionCollector.AddFeatureId(0 /* featureId */, {0} /* osmIds */);
  restrictionCollector.AddRestriction(Restriction::Type::No, {0, 1} /* osmIds */);
  restrictionCollector.AddFeatureId(20 /* featureId */, {2} /* osmIds */);

  restrictionCollector.ComposeRestrictions();

  TEST(!restrictionCollector.IsValid(), ());

  RestrictionVec const expectedRestrictions = {
      {Restriction::Type::No, {0, Restriction::kInvalidFeatureId}}};
  TEST_EQUAL(restrictionCollector.m_restrictions, expectedRestrictions, ());

  restrictionCollector.RemoveInvalidRestrictions();
  TEST(restrictionCollector.m_restrictions.empty(), ());
  TEST(!restrictionCollector.IsValid(), ());
}

UNIT_TEST(RestrictionTest_ParseRestrictions)
{
  string const kRestrictionName = "restrictions_in_osm_ids.csv";
  string const kRestrictionPath = my::JoinFoldersToPath(kRestrictionTestDir, kRestrictionName);
  string const kRestrictionContent = R"(No, 1, 1,
                                        Only, 0, 2,
                                        Only, 2, 3,
                                        No, 38028428, 38028428,
                                        No, 4, 5,)";

  ScopedDir const scopedDir(kRestrictionTestDir);
  ScopedFile const scopedFile(kRestrictionPath, kRestrictionContent);

  RestrictionCollector restrictionCollector("", "");

  Platform const & platform = Platform();

  TEST(restrictionCollector.ParseRestrictions(
           my::JoinFoldersToPath(platform.WritableDir(), kRestrictionPath)),
       ());
  RestrictionVec expectedRestrictions = {{Restriction::Type::No, 2},
                                         {Restriction::Type::Only, 2},
                                         {Restriction::Type::Only, 2},
                                         {Restriction::Type::No, 2},
                                         {Restriction::Type::No, 2}};
  TEST_EQUAL(restrictionCollector.m_restrictions, expectedRestrictions, ());

  vector<pair<uint64_t, RestrictionCollector::Index>> const expectedRestrictionIndex = {
      {1, {0, 0}}, {1, {0, 1}},        {0, {1, 0}},        {2, {1, 1}}, {2, {2, 0}},
      {3, {2, 1}}, {38028428, {3, 0}}, {38028428, {3, 1}}, {4, {4, 0}}, {5, {4, 1}}};
  TEST_EQUAL(restrictionCollector.m_restrictionIndex, expectedRestrictionIndex, ());
}

UNIT_TEST(RestrictionTest_ParseFeatureId2OsmIdsMapping)
{
  string const kFeatureIdToOsmIdsName = "feature_id_to_osm_ids.csv";
  string const kFeatureIdToOsmIdsPath =
      my::JoinFoldersToPath(kRestrictionTestDir, kFeatureIdToOsmIdsName);
  string const kFeatureIdToOsmIdsContent = R"(1, 10,
                                              2, 20,
                                              779703, 5423239545,
                                              3, 30)";

  ScopedDir const scopedDir(kRestrictionTestDir);
  ScopedFile const scopedFile(kFeatureIdToOsmIdsPath, kFeatureIdToOsmIdsContent);

  RestrictionCollector restrictionCollector("", "");

  Platform const & platform = Platform();
  restrictionCollector.ParseFeatureId2OsmIdsMapping(
      my::JoinFoldersToPath(platform.WritableDir(), kFeatureIdToOsmIdsPath));

  vector<pair<uint64_t, Restriction::FeatureId>> const expectedOsmIds2FeatureId = {
      {10, 1}, {20, 2}, {5423239545, 779703}, {30, 3}};
  vector<pair<uint64_t, Restriction::FeatureId>> const osmIds2FeatureId(
      restrictionCollector.m_osmIds2FeatureId.cbegin(),
      restrictionCollector.m_osmIds2FeatureId.cend());
  TEST_EQUAL(osmIds2FeatureId, expectedOsmIds2FeatureId, ());
}

UNIT_TEST(RestrictionTest_RestrictionCollectorWholeClassTest)
{
  string const kRestrictionName = "restrictions_in_osm_ids.csv";
  string const kRestrictionPath = my::JoinFoldersToPath(kRestrictionTestDir, kRestrictionName);
  string const kRestrictionContent = R"(No, 10, 10,
                                        Only, 10, 20,
                                        Only, 30, 40,)";

  string const kFeatureIdToOsmIdsName = "feature_id_to_osm_ids.csv";
  string const kFeatureIdToOsmIdsPath =
      my::JoinFoldersToPath(kRestrictionTestDir, kFeatureIdToOsmIdsName);
  string const kFeatureIdToOsmIdsContent = R"(1, 10,
                                              2, 20,
                                              3, 30,
                                              4, 40)";

  ScopedDir scopedDir(kRestrictionTestDir);
  ScopedFile restrictionScopedFile(kRestrictionPath, kRestrictionContent);
  ScopedFile mappingScopedFile(kFeatureIdToOsmIdsPath, kFeatureIdToOsmIdsContent);

  Platform const & platform = Platform();
  RestrictionCollector restrictionCollector(
      my::JoinFoldersToPath(platform.WritableDir(), kRestrictionPath),
      my::JoinFoldersToPath(platform.WritableDir(), kFeatureIdToOsmIdsPath));
  TEST(restrictionCollector.IsValid(), ());

  RestrictionVec const & restrictions = restrictionCollector.GetRestrictions();
  TEST(is_sorted(restrictions.cbegin(), restrictions.cend()), ());

  RestrictionVec const expectedRestrictions = {{Restriction::Type::No, {1, 1}},
                                               {Restriction::Type::Only, {1, 2}},
                                               {Restriction::Type::Only, {3, 4}}};
  TEST_EQUAL(restrictions, expectedRestrictions, ());
}
}  // namespace routing
<commit_msg>Fixing ParseFeatureId2OsmIdsMapping test.<commit_after>#include "testing/testing.hpp"

#include "generator/osm_id.hpp"
#include "generator/restriction_collector.hpp"

#include "indexer/routing.hpp"

#include "coding/file_name_utils.hpp"

#include "platform/platform_tests_support/scoped_dir.hpp"
#include "platform/platform_tests_support/scoped_file.hpp"

#include "platform/platform.hpp"

#include "base/stl_helpers.hpp"

#include "std/string.hpp"
#include "std/utility.hpp"
#include "std/vector.hpp"

using namespace platform;
using namespace platform::tests_support;

namespace routing
{
string const kRestrictionTestDir = "test-restrictions";

UNIT_TEST(RestrictionTest_ValidCase)
{
  RestrictionCollector restrictionCollector("", "");
  // Adding restrictions and feature ids to restrictionCollector in mixed order.
  restrictionCollector.AddRestriction(Restriction::Type::No, {1, 2} /* osmIds */);
  restrictionCollector.AddFeatureId(30 /* featureId */, {3} /* osmIds */);
  restrictionCollector.AddRestriction(Restriction::Type::No, {2, 3} /* osmIds */);
  restrictionCollector.AddFeatureId(10 /* featureId */, {1} /* osmIds */);
  restrictionCollector.AddFeatureId(50 /* featureId */, {5} /* osmIds */);
  restrictionCollector.AddRestriction(Restriction::Type::Only, {5, 7} /* osmIds */);
  restrictionCollector.AddFeatureId(70 /* featureId */, {7} /* osmIds */);
  restrictionCollector.AddFeatureId(20 /* featureId */, {2} /* osmIds */);

  // Composing restriction in feature id terms.
  restrictionCollector.ComposeRestrictions();
  restrictionCollector.RemoveInvalidRestrictions();

  // Checking the result.
  TEST(restrictionCollector.IsValid(), ());

  RestrictionVec const expectedRestrictions = {{Restriction::Type::No, {10, 20}},
                                               {Restriction::Type::No, {20, 30}},
                                               {Restriction::Type::Only, {50, 70}}};
  TEST_EQUAL(restrictionCollector.m_restrictions, expectedRestrictions, ());
}

UNIT_TEST(RestrictionTest_InvalidCase)
{
  RestrictionCollector restrictionCollector("", "");
  restrictionCollector.AddFeatureId(0 /* featureId */, {0} /* osmIds */);
  restrictionCollector.AddRestriction(Restriction::Type::No, {0, 1} /* osmIds */);
  restrictionCollector.AddFeatureId(20 /* featureId */, {2} /* osmIds */);

  restrictionCollector.ComposeRestrictions();

  TEST(!restrictionCollector.IsValid(), ());

  RestrictionVec const expectedRestrictions = {
      {Restriction::Type::No, {0, Restriction::kInvalidFeatureId}}};
  TEST_EQUAL(restrictionCollector.m_restrictions, expectedRestrictions, ());

  restrictionCollector.RemoveInvalidRestrictions();
  TEST(restrictionCollector.m_restrictions.empty(), ());
  TEST(!restrictionCollector.IsValid(), ());
}

UNIT_TEST(RestrictionTest_ParseRestrictions)
{
  string const kRestrictionName = "restrictions_in_osm_ids.csv";
  string const kRestrictionPath = my::JoinFoldersToPath(kRestrictionTestDir, kRestrictionName);
  string const kRestrictionContent = R"(No, 1, 1,
                                        Only, 0, 2,
                                        Only, 2, 3,
                                        No, 38028428, 38028428
                                        No, 4, 5,)";

  ScopedDir const scopedDir(kRestrictionTestDir);
  ScopedFile const scopedFile(kRestrictionPath, kRestrictionContent);

  RestrictionCollector restrictionCollector("", "");

  Platform const & platform = Platform();

  TEST(restrictionCollector.ParseRestrictions(
           my::JoinFoldersToPath(platform.WritableDir(), kRestrictionPath)),
       ());
  RestrictionVec expectedRestrictions = {{Restriction::Type::No, 2},
                                         {Restriction::Type::Only, 2},
                                         {Restriction::Type::Only, 2},
                                         {Restriction::Type::No, 2},
                                         {Restriction::Type::No, 2}};
  TEST_EQUAL(restrictionCollector.m_restrictions, expectedRestrictions, ());

  vector<pair<uint64_t, RestrictionCollector::Index>> const expectedRestrictionIndex = {
      {1, {0, 0}}, {1, {0, 1}},        {0, {1, 0}},        {2, {1, 1}}, {2, {2, 0}},
      {3, {2, 1}}, {38028428, {3, 0}}, {38028428, {3, 1}}, {4, {4, 0}}, {5, {4, 1}}};
  TEST_EQUAL(restrictionCollector.m_restrictionIndex, expectedRestrictionIndex, ());
}

UNIT_TEST(RestrictionTest_ParseFeatureId2OsmIdsMapping)
{
  string const kFeatureIdToOsmIdsName = "feature_id_to_osm_ids.csv";
  string const kFeatureIdToOsmIdsPath =
      my::JoinFoldersToPath(kRestrictionTestDir, kFeatureIdToOsmIdsName);
  string const kFeatureIdToOsmIdsContent = R"(1, 10,
                                              2, 20
                                              779703, 5423239545,
                                              3, 30)";

  ScopedDir const scopedDir(kRestrictionTestDir);
  ScopedFile const scopedFile(kFeatureIdToOsmIdsPath, kFeatureIdToOsmIdsContent);

  RestrictionCollector restrictionCollector("", "");

  Platform const & platform = Platform();
  restrictionCollector.ParseFeatureId2OsmIdsMapping(
      my::JoinFoldersToPath(platform.WritableDir(), kFeatureIdToOsmIdsPath));

  vector<pair<uint64_t, Restriction::FeatureId>> const expectedOsmIds2FeatureId = {
      {10, 1}, {20, 2}, {30, 3}, {5423239545, 779703}};
  vector<pair<uint64_t, Restriction::FeatureId>> osmIds2FeatureId(
      restrictionCollector.m_osmIds2FeatureId.cbegin(),
      restrictionCollector.m_osmIds2FeatureId.cend());
  sort(osmIds2FeatureId.begin(), osmIds2FeatureId.end(),
       my::LessBy(&pair<uint64_t, Restriction::FeatureId>::first));
  TEST_EQUAL(osmIds2FeatureId, expectedOsmIds2FeatureId, ());
}

UNIT_TEST(RestrictionTest_RestrictionCollectorWholeClassTest)
{
  string const kRestrictionName = "restrictions_in_osm_ids.csv";
  string const kRestrictionPath = my::JoinFoldersToPath(kRestrictionTestDir, kRestrictionName);
  string const kRestrictionContent = R"(No, 10, 10,
                                        Only, 10, 20,
                                        Only, 30, 40,)";

  string const kFeatureIdToOsmIdsName = "feature_id_to_osm_ids.csv";
  string const kFeatureIdToOsmIdsPath =
      my::JoinFoldersToPath(kRestrictionTestDir, kFeatureIdToOsmIdsName);
  string const kFeatureIdToOsmIdsContent = R"(1, 10,
                                              2, 20,
                                              3, 30,
                                              4, 40)";

  ScopedDir scopedDir(kRestrictionTestDir);
  ScopedFile restrictionScopedFile(kRestrictionPath, kRestrictionContent);
  ScopedFile mappingScopedFile(kFeatureIdToOsmIdsPath, kFeatureIdToOsmIdsContent);

  Platform const & platform = Platform();
  RestrictionCollector restrictionCollector(
      my::JoinFoldersToPath(platform.WritableDir(), kRestrictionPath),
      my::JoinFoldersToPath(platform.WritableDir(), kFeatureIdToOsmIdsPath));
  TEST(restrictionCollector.IsValid(), ());

  RestrictionVec const & restrictions = restrictionCollector.GetRestrictions();
  TEST(is_sorted(restrictions.cbegin(), restrictions.cend()), ());

  RestrictionVec const expectedRestrictions = {{Restriction::Type::No, {1, 1}},
                                               {Restriction::Type::Only, {1, 2}},
                                               {Restriction::Type::Only, {3, 4}}};
  TEST_EQUAL(restrictions, expectedRestrictions, ());
}
}  // namespace routing
<|endoftext|>


<commit_before>// @(#)root/gui:$Name:  $:$Id: TGButtonGroup.cxx,v 1.4 2000/10/22 19:28:58 rdm Exp $
// Author: Valeriy Onuchin & Fons Rademakers   16/10/2000

/*************************************************************************
 * Copyright (C) 1995-2000, Rene Brun and Fons Rademakers.               *
 * All rights reserved.                                                  *
 *                                                                       *
 * For the licensing terms see $ROOTSYS/LICENSE.                         *
 * For the list of contributors see $ROOTSYS/README/CREDITS.             *
 *************************************************************************/

//////////////////////////////////////////////////////////////////////////
//                                                                      //
// The TGButtonGroup widget organizes TGButton widgets in a group.      //
//                                                                      //
// A button group widget makes it easier to deal with groups of buttons.//
// A button in a button group is associated with a unique identifer.    //
// The button group emits a Clicked() signal with this identifier when  //
// the button is clicked. Thus, a button group is an ideal solution     //
// when you have several similar buttons and want to connect all their  //
// Clicked() signals, for example, to one slot.                         //
//                                                                      //
// An exclusive button group switches off all toggle buttons except     //
// the one that was clicked. A button group is by default non-exclusive.//
// All radio buttons that are inserted, will be mutually exclusive even //
// if the button group is non-exclusive.                                //
//                                                                      //
//                                                                      //
// There are two ways of using a button group:                          //
//                                                                      //
//    The button group is a parent widget of a number of buttons,       //
//    i.e. the button group is the parent argument in the button        //
//    constructor. The buttons are assigned identifiers 1, 2, 3 etc.    //
///   in the order they are created or you can specify button id in     //
//    the button constructor. A TGButtonGroup can display a frame and   //
//    a title because it inherits from TGGroupFrame.                    //
//                                                                      //
// Example:                                                             //
//                                                                      //
//    // vertical frame without border and title                        //
//    TGVButtonGroup *bg = new TGVButtonGroup(main_frame);              //
//                                                                      //
//    // create text button with id=1                                   //
//    TGTextButton *button1 = new TGTextButton(bg,"some text");         //
//                                                                      //
//    // create another text button with id=2                           //
//    TGTextButton *button2 = new TGTextButton(bg,"another text");      //
//                                                                      //
//    // map all buttons                                                //
//    bg->Show();                                                       //
//                                                                      //
//                                                                      //
//  ButtonGroup Signals:                                                //
//                                                                      //
//    Pressed(Int_t id)  -->  is emitted when a button in the group is  //
//                            pressed down. The id argument is the      //
//                            button's identifier.                      //
//    Released(Int_t id) -->  is emitted when a button in the group is  //
//                            released. The id argument is the button's //
//                            identifier.                               //
//    Clicked(Int_t id)  -->  is emitted when a button in the group is  //
//                            clicked. The id argument is the button's  //
//                            identifier.                               //
//                                                                      //
//                                                                      //
// The TGHButtonGroup widget organizes TGButton widgets in a group      //
// with one horizontal row. TGHButtonGroup is a convenience class that  //
// offers a thin layer on top of TGButtonGroup. It inherits from        //
// TGButtonGroup.                                                       //
//                                                                      //
// The TGVButtonGroup widget organizes TGButton widgets in a group      //
// with one vertical column. TGVButtonGroup is a convenience class that //
// offers a thin layer on top of TGButtonGroup. It inherits from        //
// TGButtonGroup.                                                       //
//                                                                      //
//////////////////////////////////////////////////////////////////////////

#include "TGButtonGroup.h"
#include "TGButton.h"
#include "TClass.h"
#include "TGLayout.h"

ClassImpQ(TGButtonGroup)
ClassImpQ(TGHButtonGroup)
ClassImpQ(TGVButtonGroup)

//______________________________________________________________________________
TGButtonGroup::TGButtonGroup(TGWindow *parent,
                             const TString &title,
                             UInt_t options,
                             GContext_t norm,
                             FontStruct_t font,
                             ULong_t back) :
   TGGroupFrame(parent, new TGString(title), options, norm, font, back)
{
   // Constructor. Layout 1 row or 1 column.

   Init();
   if (options & kVerticalFrame) {
      SetLayoutManager(new TGVerticalLayout(this));
   } else {
      SetLayoutManager(new TGHorizontalLayout(this));
   }

   fDrawBorder = !title.IsNull();
}

//______________________________________________________________________________
TGButtonGroup::TGButtonGroup(TGWindow *parent,
                             UInt_t r, UInt_t c,
                             Int_t s, Int_t h,
                             const TString &title,
                             GContext_t norm ,
                             FontStruct_t font ,
                             ULong_t back) :
   TGGroupFrame(parent, new TGString(title), 0, norm, font, back)
{
   // Constructor. Layout defined by TGMatrixLayout:
   //    r = number of rows
   //    c = number of columns
   //    s = interval between frames
   //    h = layout hints

   Init();
   fDrawBorder = !title.IsNull();
   SetLayoutManager(new TGMatrixLayout(this,r,c,s,h));
}

//______________________________________________________________________________
void TGButtonGroup::Init()
{
   // Default init.

   fMapOfButtons = new TMap();  // map of button/id pairs
   fExclGroup    = kFALSE;
   fRadioExcl    = kFALSE;
   fDrawBorder   = kTRUE;
}

//______________________________________________________________________________
TGButtonGroup::~TGButtonGroup()
{
   // Destructor, we do not delete the buttons.

   TIter next(fMapOfButtons);
   register TGButton *item = 0;

   while ((item = (TGButton*)next())) {
      item->SetGroup(0);
   }

   SafeDelete(fMapOfButtons);
}

//______________________________________________________________________________
void TGButtonGroup::SetBorderDrawn(Bool_t enable)
{
   // Makes border to be visible/invisible.

   if (enable != IsBorderDrawn()) {
      fDrawBorder = enable;
      ChangedBy("SetBorderDrawn");        // emit signal
   }
}

//______________________________________________________________________________
void TGButtonGroup::SetExclusive(Bool_t enable)
{
   // Sets the button group to be exclusive if enable is kTRUE,
   // or to be non-exclusive if enable is kFALSE.
   // An exclusive button group switches off all other toggle buttons when
   // one is switched on. This is ideal for groups of radio-buttons
   // A non-exclusive group allow many buttons to be switched on at the same
   // time. The default setting is kFALSE.

   if (enable != IsExclusive()) {
      fExclGroup = enable;
      ChangedBy("SetExclusive");  // emit signal
   }
}

//______________________________________________________________________________
void TGButtonGroup::SetRadioButtonExclusive(Bool_t enable)
{
   // If enable is kTRUE, this button group will treat radio buttons as
   // mutually exclusive, and other buttons according to IsExclusive().
   // This function is called automatically whenever a TGRadioButton
   // is inserted, so you should normally never have to call it.

   if (enable != IsRadioButtonExclusive()) {
      fRadioExcl = enable;
      ChangedBy("SetRadioButtonExclusive"); // emit signal
   }
}

//______________________________________________________________________________
void TGButtonGroup::SetButton(Int_t id, Bool_t down)
{
   // Sets the button with id to be on/down, and if this is an
   // exclusive group, all other button in the group to be off/up.

   TGButton *b = Find(id);
   if (b) b->SetDown(down);
}

//______________________________________________________________________________
Int_t TGButtonGroup::Insert(TGButton *button, Int_t id)
{
   // Inserts a button with the identifier id into the button group.
   // Returns the button identifier.
   //
   // It is not necessary to manually insert buttons that have this button
   // group as their parent widget. An exception is when you want custom
   // identifiers instead of the default 1, 2, 3 etc.
   //
   // The button is assigned the identifier id or an automatically
   // generated identifier.  It works as follows: If id > 0, this
   // identifier is assigned.  If id == -1 (default), the identifier is
   // equal to the number of buttons in the group+1.  If id is any other
   // negative integer, for instance -2, a unique identifier (negative
   // integer <= -2) is generated.
   //
   // Inserting several buttons with id = -1 assigns the identifiers 1,
   // 2, 3, etc.

   if (button->fGroup && button->fGroup != this)
      button->fGroup->Remove(button);

   if (button->fGroup == this) {
      if (id == -1)
         return GetId(button);    // the button is already in group
      else
         button->fGroup->Remove(button);  // want to set a new id
   }

   button->fGroup = this;

   static int seq_no = -2;
   int bid;

   if (id < -1)       bid = seq_no--;
   else if (id == -1) bid = GetCount()+1;
   else               bid = id;

   fMapOfButtons->Add(button, (TObject*)bid);
   AddFrame(button);

   SetRadioButtonExclusive(button->IsA()->InheritsFrom(TGRadioButton::Class()));

   Connect(button, "Pressed()" , "TGButtonGroup", this, "ReleaseButtons()");
   Connect(button, "Pressed()" , "TGButtonGroup", this, "ButtonPressed()");
   Connect(button, "Released()", "TGButtonGroup", this, "ButtonReleased()");
   Connect(button, "Clicked()" , "TGButtonGroup", this, "ButtonClicked()");

   return bid;
}

//______________________________________________________________________________
void TGButtonGroup::Remove(TGButton *button)
{
   // Removes a button from the button group.

   TGButton *item = (TGButton*) fMapOfButtons->Remove(button);
   if (item) {
      button->SetGroup(0);
      button->Disconnect(this);
   }

   RemoveFrame(button);
}

//______________________________________________________________________________
TGButton *TGButtonGroup::Find(Int_t id) const
{
   // Finds and returns a pointer to the button with the specified
   // identifier id. Returns null if the button was not found.

   TIter next(fMapOfButtons);
   register TGButton *item = 0;

   while ((item = (TGButton*)next())) {
      if ((Int_t)fMapOfButtons->GetValue(item) == id) break;   // found
   }

   return item;
}

//______________________________________________________________________________
Int_t TGButtonGroup::GetId(TGButton *button) const
{
   // Finds and returns the id of the button.
   // Returns -1 if the button is not a member of this group.

   TAssoc *a = (TAssoc*) fMapOfButtons->FindObject(button);
   if (a)
      return (Int_t) a->Value();
   else
      return -1;
}

//______________________________________________________________________________
void TGButtonGroup::ButtonPressed()
{
   // This slot is activated when one of the buttons in the group emits the
   // Pressed() signal.

#if 0
   // Is here for historical purposes and example. Now this is not needed
   // anymore since TGButton has has its own GetSender() method returning
   // the TGButton proper.

   // This is needed since gTQSender points to TQObject part of TGButton
#ifdef R__RTTI
   TGButton *btn = dynamic_cast<TGButton*>((TQObject*)gTQSender);
#else
   TQObject *oq = (TQObject*)gTQSender;
   TGButton *btn = (TGButton*)oq->IsA()->DynamicCast(TQObject::Class(), oq, kFALSE);
#endif
   if (!btn) {
      Error("ButtonPressed", "gTQSender not a TGButton");
      return;
   }
#else
      TGButton *btn = (TGButton*)gTQSender;
#endif

   TAssoc *a = (TAssoc*) fMapOfButtons->FindObject(btn);
   if (a) {
      Int_t id = (Int_t) a->Value();
      Pressed(id);
   }
}

//______________________________________________________________________________
void TGButtonGroup::ButtonReleased()
{
   // This slot is activated when one of the buttons in the group emits the
   // Released() signal.

   TGButton *btn = (TGButton*)gTQSender;

   TAssoc *a = (TAssoc*) fMapOfButtons->FindObject(btn);
   if (a) {
      Int_t id = (Int_t) a->Value();
      Released(id);
   }
}

//______________________________________________________________________________
void TGButtonGroup::ButtonClicked()
{
   // This slot is activated when one of the buttons in the group emits the
   // Clicked() signal.

   TGButton *btn = (TGButton*)gTQSender;

   TAssoc *a = (TAssoc*) fMapOfButtons->FindObject(btn);
   if (a) {
      Int_t id = (Int_t) a->Value();
      Clicked(id);
   }
}

//______________________________________________________________________________
void TGButtonGroup::ReleaseButtons()
{
   // This slot is activated when one of the buttons in the
   // exclusive group emits the Pressed() signal.

   if (!fExclGroup && !fRadioExcl) return;

   TGButton *btn = (TGButton*)gTQSender;

   if (!fExclGroup && !btn->IsA()->InheritsFrom(TGRadioButton::Class()))
      return;

   TIter next(fMapOfButtons);
   register TGButton *item = 0;

   while ((item = (TGButton*)next())) {    // loop over all buttons
      item->SetDown(btn==item);
   }
}

//______________________________________________________________________________
void TGButtonGroup::Show()
{
   // Show group of buttons.

   MapSubwindows();
   Layout();
   Resize(GetLayoutManager()->GetDefaultSize());
   MapRaised();
   fClient->NeedRedraw(this);
}

//______________________________________________________________________________
void TGButtonGroup::Hide()
{
   // Hide group of buttons.

   UnmapWindow();
}

//______________________________________________________________________________
void TGButtonGroup::SetTitle(TGString *title)
{
   // Set or change title.

   if (!title) {
      Error("SetTitle", "title cannot be 0, try \"\"");
      return;
   }

   if (strcmp(fText->GetString(), title->GetString())) {
      SetBorderDrawn(title->GetLength() ? kTRUE : kFALSE);
      TGGroupFrame::SetTitle(title);
      ChangedBy("SetTitle");
   }
}

//______________________________________________________________________________
void TGButtonGroup::SetTitle(const char *title)
{
   // Set or change title.

   if (!title) {
      Error("SetTitle", "title cannot be 0, try \"\"");
      return;
   }

   if (strcmp(fText->GetString(), title)) {
      SetBorderDrawn(title && strlen(title));
      TGGroupFrame::SetTitle(title);
      ChangedBy("SetTitle");
   }
}

//______________________________________________________________________________
void TGButtonGroup::SetLayoutHints(TGLayoutHints *l, TGButton *button)
{
   // Set layout hints.

   TGFrameElement *el;
   TIter next(fList);

   while ((el = (TGFrameElement *)next())) {
      if ((el->fFrame==(TGFrame*)button) || !button) {
         el->fLayout = l ? l : fgDefaultHints;
      }
   }
   Layout();
}

<commit_msg>Several casts to avoid compiler warnings (Philippe)<commit_after>// @(#)root/gui:$Name:  $:$Id: TGButtonGroup.cxx,v 1.5 2001/01/10 09:35:00 rdm Exp $
// Author: Valeriy Onuchin & Fons Rademakers   16/10/2000

/*************************************************************************
 * Copyright (C) 1995-2000, Rene Brun and Fons Rademakers.               *
 * All rights reserved.                                                  *
 *                                                                       *
 * For the licensing terms see $ROOTSYS/LICENSE.                         *
 * For the list of contributors see $ROOTSYS/README/CREDITS.             *
 *************************************************************************/

//////////////////////////////////////////////////////////////////////////
//                                                                      //
// The TGButtonGroup widget organizes TGButton widgets in a group.      //
//                                                                      //
// A button group widget makes it easier to deal with groups of buttons.//
// A button in a button group is associated with a unique identifer.    //
// The button group emits a Clicked() signal with this identifier when  //
// the button is clicked. Thus, a button group is an ideal solution     //
// when you have several similar buttons and want to connect all their  //
// Clicked() signals, for example, to one slot.                         //
//                                                                      //
// An exclusive button group switches off all toggle buttons except     //
// the one that was clicked. A button group is by default non-exclusive.//
// All radio buttons that are inserted, will be mutually exclusive even //
// if the button group is non-exclusive.                                //
//                                                                      //
//                                                                      //
// There are two ways of using a button group:                          //
//                                                                      //
//    The button group is a parent widget of a number of buttons,       //
//    i.e. the button group is the parent argument in the button        //
//    constructor. The buttons are assigned identifiers 1, 2, 3 etc.    //
///   in the order they are created or you can specify button id in     //
//    the button constructor. A TGButtonGroup can display a frame and   //
//    a title because it inherits from TGGroupFrame.                    //
//                                                                      //
// Example:                                                             //
//                                                                      //
//    // vertical frame without border and title                        //
//    TGVButtonGroup *bg = new TGVButtonGroup(main_frame);              //
//                                                                      //
//    // create text button with id=1                                   //
//    TGTextButton *button1 = new TGTextButton(bg,"some text");         //
//                                                                      //
//    // create another text button with id=2                           //
//    TGTextButton *button2 = new TGTextButton(bg,"another text");      //
//                                                                      //
//    // map all buttons                                                //
//    bg->Show();                                                       //
//                                                                      //
//                                                                      //
//  ButtonGroup Signals:                                                //
//                                                                      //
//    Pressed(Int_t id)  -->  is emitted when a button in the group is  //
//                            pressed down. The id argument is the      //
//                            button's identifier.                      //
//    Released(Int_t id) -->  is emitted when a button in the group is  //
//                            released. The id argument is the button's //
//                            identifier.                               //
//    Clicked(Int_t id)  -->  is emitted when a button in the group is  //
//                            clicked. The id argument is the button's  //
//                            identifier.                               //
//                                                                      //
//                                                                      //
// The TGHButtonGroup widget organizes TGButton widgets in a group      //
// with one horizontal row. TGHButtonGroup is a convenience class that  //
// offers a thin layer on top of TGButtonGroup. It inherits from        //
// TGButtonGroup.                                                       //
//                                                                      //
// The TGVButtonGroup widget organizes TGButton widgets in a group      //
// with one vertical column. TGVButtonGroup is a convenience class that //
// offers a thin layer on top of TGButtonGroup. It inherits from        //
// TGButtonGroup.                                                       //
//                                                                      //
//////////////////////////////////////////////////////////////////////////

#include "TGButtonGroup.h"
#include "TGButton.h"
#include "TClass.h"
#include "TGLayout.h"

ClassImpQ(TGButtonGroup)
ClassImpQ(TGHButtonGroup)
ClassImpQ(TGVButtonGroup)

//______________________________________________________________________________
TGButtonGroup::TGButtonGroup(TGWindow *parent,
                             const TString &title,
                             UInt_t options,
                             GContext_t norm,
                             FontStruct_t font,
                             ULong_t back) :
   TGGroupFrame(parent, new TGString(title), options, norm, font, back)
{
   // Constructor. Layout 1 row or 1 column.

   Init();
   if (options & kVerticalFrame) {
      SetLayoutManager(new TGVerticalLayout(this));
   } else {
      SetLayoutManager(new TGHorizontalLayout(this));
   }

   fDrawBorder = !title.IsNull();
}

//______________________________________________________________________________
TGButtonGroup::TGButtonGroup(TGWindow *parent,
                             UInt_t r, UInt_t c,
                             Int_t s, Int_t h,
                             const TString &title,
                             GContext_t norm ,
                             FontStruct_t font ,
                             ULong_t back) :
   TGGroupFrame(parent, new TGString(title), 0, norm, font, back)
{
   // Constructor. Layout defined by TGMatrixLayout:
   //    r = number of rows
   //    c = number of columns
   //    s = interval between frames
   //    h = layout hints

   Init();
   fDrawBorder = !title.IsNull();
   SetLayoutManager(new TGMatrixLayout(this,r,c,s,h));
}

//______________________________________________________________________________
void TGButtonGroup::Init()
{
   // Default init.

   fMapOfButtons = new TMap();  // map of button/id pairs
   fExclGroup    = kFALSE;
   fRadioExcl    = kFALSE;
   fDrawBorder   = kTRUE;
}

//______________________________________________________________________________
TGButtonGroup::~TGButtonGroup()
{
   // Destructor, we do not delete the buttons.

   TIter next(fMapOfButtons);
   register TGButton *item = 0;

   while ((item = (TGButton*)next())) {
      item->SetGroup(0);
   }

   SafeDelete(fMapOfButtons);
}

//______________________________________________________________________________
void TGButtonGroup::SetBorderDrawn(Bool_t enable)
{
   // Makes border to be visible/invisible.

   if (enable != IsBorderDrawn()) {
      fDrawBorder = enable;
      ChangedBy("SetBorderDrawn");        // emit signal
   }
}

//______________________________________________________________________________
void TGButtonGroup::SetExclusive(Bool_t enable)
{
   // Sets the button group to be exclusive if enable is kTRUE,
   // or to be non-exclusive if enable is kFALSE.
   // An exclusive button group switches off all other toggle buttons when
   // one is switched on. This is ideal for groups of radio-buttons
   // A non-exclusive group allow many buttons to be switched on at the same
   // time. The default setting is kFALSE.

   if (enable != IsExclusive()) {
      fExclGroup = enable;
      ChangedBy("SetExclusive");  // emit signal
   }
}

//______________________________________________________________________________
void TGButtonGroup::SetRadioButtonExclusive(Bool_t enable)
{
   // If enable is kTRUE, this button group will treat radio buttons as
   // mutually exclusive, and other buttons according to IsExclusive().
   // This function is called automatically whenever a TGRadioButton
   // is inserted, so you should normally never have to call it.

   if (enable != IsRadioButtonExclusive()) {
      fRadioExcl = enable;
      ChangedBy("SetRadioButtonExclusive"); // emit signal
   }
}

//______________________________________________________________________________
void TGButtonGroup::SetButton(Int_t id, Bool_t down)
{
   // Sets the button with id to be on/down, and if this is an
   // exclusive group, all other button in the group to be off/up.

   TGButton *b = Find(id);
   if (b) b->SetDown(down);
}

//______________________________________________________________________________
Int_t TGButtonGroup::Insert(TGButton *button, Int_t id)
{
   // Inserts a button with the identifier id into the button group.
   // Returns the button identifier.
   //
   // It is not necessary to manually insert buttons that have this button
   // group as their parent widget. An exception is when you want custom
   // identifiers instead of the default 1, 2, 3 etc.
   //
   // The button is assigned the identifier id or an automatically
   // generated identifier.  It works as follows: If id > 0, this
   // identifier is assigned.  If id == -1 (default), the identifier is
   // equal to the number of buttons in the group+1.  If id is any other
   // negative integer, for instance -2, a unique identifier (negative
   // integer <= -2) is generated.
   //
   // Inserting several buttons with id = -1 assigns the identifiers 1,
   // 2, 3, etc.

   if (button->fGroup && button->fGroup != this)
      button->fGroup->Remove(button);

   if (button->fGroup == this) {
      if (id == -1)
         return GetId(button);    // the button is already in group
      else
         button->fGroup->Remove(button);  // want to set a new id
   }

   button->fGroup = this;

   static int seq_no = -2;
   int bid;

   if (id < -1)       bid = seq_no--;
   else if (id == -1) bid = GetCount()+1;
   else               bid = id;

   fMapOfButtons->Add(button, (TObject*)bid);
   AddFrame(button);

   SetRadioButtonExclusive(button->IsA()->InheritsFrom(TGRadioButton::Class()));

   Connect(button, "Pressed()" , "TGButtonGroup", this, "ReleaseButtons()");
   Connect(button, "Pressed()" , "TGButtonGroup", this, "ButtonPressed()");
   Connect(button, "Released()", "TGButtonGroup", this, "ButtonReleased()");
   Connect(button, "Clicked()" , "TGButtonGroup", this, "ButtonClicked()");

   return bid;
}

//______________________________________________________________________________
void TGButtonGroup::Remove(TGButton *button)
{
   // Removes a button from the button group.

   TGButton *item = (TGButton*) fMapOfButtons->Remove(button);
   if (item) {
      button->SetGroup(0);
      button->Disconnect(this);
   }

   RemoveFrame(button);
}

//______________________________________________________________________________
TGButton *TGButtonGroup::Find(Int_t id) const
{
   // Finds and returns a pointer to the button with the specified
   // identifier id. Returns null if the button was not found.

   TIter next(fMapOfButtons);
   register TGButton *item = 0;

   while ((item = (TGButton*)next())) {
      if ((Int_t)(long)fMapOfButtons->GetValue(item) == id) break;   // found
   }

   return item;
}

//______________________________________________________________________________
Int_t TGButtonGroup::GetId(TGButton *button) const
{
   // Finds and returns the id of the button.
   // Returns -1 if the button is not a member of this group.

   TAssoc *a = (TAssoc*) fMapOfButtons->FindObject(button);
   if (a)
      return (Int_t)(long) a->Value();
   else
      return -1;
}

//______________________________________________________________________________
void TGButtonGroup::ButtonPressed()
{
   // This slot is activated when one of the buttons in the group emits the
   // Pressed() signal.

#if 0
   // Is here for historical purposes and example. Now this is not needed
   // anymore since TGButton has has its own GetSender() method returning
   // the TGButton proper.

   // This is needed since gTQSender points to TQObject part of TGButton
#ifdef R__RTTI
   TGButton *btn = dynamic_cast<TGButton*>((TQObject*)gTQSender);
#else
   TQObject *oq = (TQObject*)gTQSender;
   TGButton *btn = (TGButton*)oq->IsA()->DynamicCast(TQObject::Class(), oq, kFALSE);
#endif
   if (!btn) {
      Error("ButtonPressed", "gTQSender not a TGButton");
      return;
   }
#else
      TGButton *btn = (TGButton*)gTQSender;
#endif

   TAssoc *a = (TAssoc*) fMapOfButtons->FindObject(btn);
   if (a) {
      Int_t id = (Int_t)(long) a->Value();
      Pressed(id);
   }
}

//______________________________________________________________________________
void TGButtonGroup::ButtonReleased()
{
   // This slot is activated when one of the buttons in the group emits the
   // Released() signal.

   TGButton *btn = (TGButton*)gTQSender;

   TAssoc *a = (TAssoc*) fMapOfButtons->FindObject(btn);
   if (a) {
      Int_t id = (Int_t)(long) a->Value();
      Released(id);
   }
}

//______________________________________________________________________________
void TGButtonGroup::ButtonClicked()
{
   // This slot is activated when one of the buttons in the group emits the
   // Clicked() signal.

   TGButton *btn = (TGButton*)gTQSender;

   TAssoc *a = (TAssoc*) fMapOfButtons->FindObject(btn);
   if (a) {
      Int_t id = (Int_t)(long) a->Value();
      Clicked(id);
   }
}

//______________________________________________________________________________
void TGButtonGroup::ReleaseButtons()
{
   // This slot is activated when one of the buttons in the
   // exclusive group emits the Pressed() signal.

   if (!fExclGroup && !fRadioExcl) return;

   TGButton *btn = (TGButton*)gTQSender;

   if (!fExclGroup && !btn->IsA()->InheritsFrom(TGRadioButton::Class()))
      return;

   TIter next(fMapOfButtons);
   register TGButton *item = 0;

   while ((item = (TGButton*)next())) {    // loop over all buttons
      item->SetDown(btn==item);
   }
}

//______________________________________________________________________________
void TGButtonGroup::Show()
{
   // Show group of buttons.

   MapSubwindows();
   Layout();
   Resize(GetLayoutManager()->GetDefaultSize());
   MapRaised();
   fClient->NeedRedraw(this);
}

//______________________________________________________________________________
void TGButtonGroup::Hide()
{
   // Hide group of buttons.

   UnmapWindow();
}

//______________________________________________________________________________
void TGButtonGroup::SetTitle(TGString *title)
{
   // Set or change title.

   if (!title) {
      Error("SetTitle", "title cannot be 0, try \"\"");
      return;
   }

   if (strcmp(fText->GetString(), title->GetString())) {
      SetBorderDrawn(title->GetLength() ? kTRUE : kFALSE);
      TGGroupFrame::SetTitle(title);
      ChangedBy("SetTitle");
   }
}

//______________________________________________________________________________
void TGButtonGroup::SetTitle(const char *title)
{
   // Set or change title.

   if (!title) {
      Error("SetTitle", "title cannot be 0, try \"\"");
      return;
   }

   if (strcmp(fText->GetString(), title)) {
      SetBorderDrawn(title && strlen(title));
      TGGroupFrame::SetTitle(title);
      ChangedBy("SetTitle");
   }
}

//______________________________________________________________________________
void TGButtonGroup::SetLayoutHints(TGLayoutHints *l, TGButton *button)
{
   // Set layout hints.

   TGFrameElement *el;
   TIter next(fList);

   while ((el = (TGFrameElement *)next())) {
      if ((el->fFrame==(TGFrame*)button) || !button) {
         el->fLayout = l ? l : fgDefaultHints;
      }
   }
   Layout();
}

<|endoftext|>


<commit_before>/*Copyright 2010 George Karagoulis

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 "file_manager.h"
#include "stringhelpers.h"
#include "encryption.h"
#include "exception.h"
#include <QDesktopServices>
#include <QFile>
#include <QXmlStreamWriter>
#include <QXmlStreamReader>
#include <QVariant>
#include <QMutex>
#include <QReadWriteLock>
#include <QSqlDatabase>
#include <QSqlQuery>
#include <QSqlError>
#include <QSqlResult>
#include <QMap>
#include <QMessageBox>
#include <QByteArray>
#include <iostream>
using namespace GUtil;
using namespace GUtil::QtUtil;

// A class to keep track of our mutexes
class mutex_record_t
{
public:
    mutex_record_t()
    {
        count = 1;
        mut = new QReadWriteLock();
    }
    ~mutex_record_t(){ delete mut;}

    QReadWriteLock *mut;
    int count;
};

QMap<QString, mutex_record_t *> mutexes;
QReadWriteLock mutex_lock;

File_Manager::File_Manager(const QString &id, bool primary)
{
    my_id = id;
    file_location = get_file_loc(id);

    mutex_lock.lockForWrite();
    if(mutexes.contains(id))
    {
        mutexes.value(id)->mut->lockForWrite();

        mutexes[id]->count++;

        mutexes.value(id)->mut->unlock();
    }
    else
    {
        mutexes.insert(id, new mutex_record_t());
    }
    mutex_lock.unlock();

    if(primary)
    {
        QSqlDatabase::addDatabase("QSQLITE");
        reset();
    }
}

File_Manager::~File_Manager()
{

}

int File_Manager::addFile(const QString &data)
{
    mutex_lock.lockForRead();
    mutexes.value(my_id)->mut->lockForWrite();

    QSqlDatabase dbase;
    get_database(dbase);
    if(!dbase.open())
    {
        mutexes.value(my_id)->mut->unlock();
        mutex_lock.unlock();
        throw GUtil::Exception("Cannot open database");
    }

    int ret = get_free_file_id(dbase);

    QSqlQuery q("INSERT INTO files (id, data) VALUES (:id, :data)", dbase);
    q.bindValue(":id", ret);
    q.bindValue(":data", data, QSql::Binary);
    if(!q.exec())
    {
        QString err = q.lastError().text();
        dbase.close();
        mutexes.value(my_id)->mut->unlock();
        mutex_lock.unlock();
        throw GUtil::Exception(err.toStdString());
    }

    dbase.close();
    mutexes.value(my_id)->mut->unlock();
    mutex_lock.unlock();

    return ret;
}

void File_Manager::removeFile(int id)
{
    mutex_lock.lockForRead();
    mutexes.value(my_id)->mut->lockForWrite();

    QSqlDatabase dbase;
    get_database(dbase);
    if(!dbase.open())
    {
        mutexes.value(my_id)->mut->unlock();
        mutex_lock.unlock();
        throw GUtil::Exception("Cannot open database");
    }

    // Remove each item one by one
    QSqlQuery q("DELETE FROM files WHERE id=:id");
    q.bindValue(":id", id);
    if(!q.exec() || (q.numRowsAffected() != 1))
    {
        QString err = q.lastError().text();
        dbase.close();
        mutexes.value(my_id)->mut->unlock();
        mutex_lock.unlock();
        throw GUtil::Exception(err.toStdString());
    }

    dbase.close();
    mutexes.value(my_id)->mut->unlock();
    mutex_lock.unlock();
}

QString File_Manager::getFile(int id)
{
    mutex_lock.lockForRead();
    mutexes.value(my_id)->mut->lockForRead();

    QSqlDatabase dbase;
    get_database(dbase);
    if(!dbase.open())
    {
        mutexes.value(my_id)->mut->unlock();
        mutex_lock.unlock();
        throw GUtil::Exception("Could not open database");
    }

    QSqlQuery q("SELECT data FROM files WHERE id=:id", dbase);
    q.bindValue(":id", id);
    q.exec();

    if(q.first())
    {
        QByteArray ba = q.value(0).toByteArray();
        mutexes.value(my_id)->mut->unlock();
        mutex_lock.unlock();
        return QString::fromStdString(string(ba.constData(), ba.length()));
    }

    dbase.close();
    mutexes.value(my_id)->mut->unlock();
    mutex_lock.unlock();
    throw GUtil::Exception("File not found");
}

// Clear all files
void File_Manager::reset()
{
    mutex_lock.lockForRead();
    mutexes.value(my_id)->mut->lockForWrite();

    QSqlDatabase dbase;
    get_database(dbase);
    if(!dbase.open())
    {
        mutexes.value(my_id)->mut->unlock();
        mutex_lock.unlock();
        throw GUtil::Exception("Cannot open database");
    }

    QSqlQuery q(dbase);
    q.exec("DROP TABLE \"files\"");
    q.exec("CREATE TABLE \"files\" ("
               "\"id\" INTEGER NOT NULL,"
               "\"data\" BLOB NOT NULL)");
    q.exec("CREATE INDEX idx ON files(id)");
    dbase.close();

    mutexes.value(my_id)->mut->unlock();
    mutex_lock.unlock();
}

void File_Manager::get_database(QSqlDatabase &dbase) const
{
    dbase = QSqlDatabase::database();
    dbase.setDatabaseName(file_location);
}

QString File_Manager::get_file_loc(const QString &id)
{
    return QDesktopServices::storageLocation(QDesktopServices::TempLocation)
            + QString("/%1.tempfile").arg(id);
}

QList<int> File_Manager::idList()
{
    mutex_lock.lockForRead();
    mutexes.value(my_id)->mut->lockForRead();

    QList<int> ret;
    QSqlDatabase dbase;
    get_database(dbase);
    if(!dbase.open())
    {
        mutexes.value(my_id)->mut->unlock();
        mutex_lock.unlock();
        throw GUtil::Exception("Cannot open database");
    }

    QSqlQuery q("SELECT id FROM files");
    if(q.exec())
    {
        while(q.next())
        {
            ret.append(q.value(0).toInt());
        }
    }

    dbase.close();
    mutexes.value(my_id)->mut->unlock();
    mutex_lock.unlock();
    return ret;
}

bool File_Manager::hasFile(int id)
{
    mutex_lock.lockForRead();
    mutexes.value(my_id)->mut->lockForRead();

    QSqlDatabase dbase;
    get_database(dbase);
    if(!dbase.open())
    {
        mutexes.value(my_id)->mut->unlock();
        mutex_lock.unlock();
        throw GUtil::Exception("Cannot open database");
    }

    QSqlQuery q("SELECT id FROM files WHERE id=:id");
    q.bindValue(":id", id);
    q.exec();
    bool ret = q.first();

    mutexes.value(my_id)->mut->unlock();
    mutex_lock.unlock();
    return ret;
}

int File_Manager::get_free_file_id(QSqlDatabase &dbase)
{
    // You must already have a lock on the database before using this function!

    int max_id = 0;
    QSqlQuery q("SELECT COUNT(id),MAX(id) FROM files", dbase);
    q.exec();
    if(q.first())
    {
        if(q.value(0).toInt() > 0)
            max_id = q.value(1).toInt() + 1;
    }

    return max_id;
}
<commit_msg>Fixed an error condition<commit_after>/*Copyright 2010 George Karagoulis

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 "file_manager.h"
#include "stringhelpers.h"
#include "encryption.h"
#include "exception.h"
#include <QDesktopServices>
#include <QFile>
#include <QXmlStreamWriter>
#include <QXmlStreamReader>
#include <QVariant>
#include <QMutex>
#include <QReadWriteLock>
#include <QSqlDatabase>
#include <QSqlQuery>
#include <QSqlError>
#include <QSqlResult>
#include <QMap>
#include <QMessageBox>
#include <QByteArray>
#include <iostream>
using namespace GUtil;
using namespace GUtil::QtUtil;

// A class to keep track of our mutexes
class mutex_record_t
{
public:
    mutex_record_t()
    {
        count = 1;
        mut = new QReadWriteLock();
    }
    ~mutex_record_t(){ delete mut;}

    QReadWriteLock *mut;
    int count;
};

QMap<QString, mutex_record_t *> mutexes;
QReadWriteLock mutex_lock;

File_Manager::File_Manager(const QString &id, bool primary)
{
    my_id = id;
    file_location = get_file_loc(id);

    mutex_lock.lockForWrite();
    if(mutexes.contains(id))
    {
        mutexes.value(id)->mut->lockForWrite();

        mutexes[id]->count++;

        mutexes.value(id)->mut->unlock();
    }
    else
    {
        mutexes.insert(id, new mutex_record_t());
    }
    mutex_lock.unlock();

    if(primary)
    {
        QSqlDatabase::addDatabase("QSQLITE");
        reset();
    }
}

File_Manager::~File_Manager()
{

}

int File_Manager::addFile(const QString &data)
{
    mutex_lock.lockForRead();
    mutexes.value(my_id)->mut->lockForWrite();

    QSqlDatabase dbase;
    get_database(dbase);
    if(!dbase.open())
    {
        mutexes.value(my_id)->mut->unlock();
        mutex_lock.unlock();
        throw GUtil::Exception("Cannot open database");
    }

    int ret = get_free_file_id(dbase);

    QSqlQuery q("INSERT INTO files (id, data) VALUES (:id, :data)", dbase);
    q.bindValue(":id", ret);
    q.bindValue(":data", data, QSql::Binary);
    if(!q.exec())
    {
        QString err = q.lastError().text();
        dbase.close();
        mutexes.value(my_id)->mut->unlock();
        mutex_lock.unlock();
        throw GUtil::Exception(err.toStdString());
    }

    dbase.close();
    mutexes.value(my_id)->mut->unlock();
    mutex_lock.unlock();

    return ret;
}

void File_Manager::removeFile(int id)
{
    mutex_lock.lockForRead();
    mutexes.value(my_id)->mut->lockForWrite();

    QSqlDatabase dbase;
    get_database(dbase);
    if(!dbase.open())
    {
        mutexes.value(my_id)->mut->unlock();
        mutex_lock.unlock();
        throw GUtil::Exception("Cannot open database");
    }

    // Remove each item one by one
    QSqlQuery q("DELETE FROM files WHERE id=:id");
    q.bindValue(":id", id);
    if(!q.exec() || (q.numRowsAffected() != 1))
    {
        QString err = q.lastError().text();
        dbase.close();
        mutexes.value(my_id)->mut->unlock();
        mutex_lock.unlock();
        throw GUtil::Exception(err.toStdString());
    }

    dbase.close();
    mutexes.value(my_id)->mut->unlock();
    mutex_lock.unlock();
}

QString File_Manager::getFile(int id)
{
    mutex_lock.lockForRead();
    mutexes.value(my_id)->mut->lockForRead();

    QSqlDatabase dbase;
    get_database(dbase);
    if(!dbase.open())
    {
        mutexes.value(my_id)->mut->unlock();
        mutex_lock.unlock();
        throw GUtil::Exception("Could not open database");
    }

    QSqlQuery q("SELECT data, COUNT(data) FROM files WHERE id=:id", dbase);
    q.bindValue(":id", id);
    q.exec();

    if(q.first() && (q.value(1).toInt() == 1))
    {
        QByteArray ba = q.value(0).toByteArray();
        mutexes.value(my_id)->mut->unlock();
        mutex_lock.unlock();
        return QString::fromStdString(string(ba.constData(), ba.length()));
    }

    dbase.close();
    mutexes.value(my_id)->mut->unlock();
    mutex_lock.unlock();
    throw GUtil::Exception("File not found");
}

// Clear all files
void File_Manager::reset()
{
    mutex_lock.lockForRead();
    mutexes.value(my_id)->mut->lockForWrite();

    QSqlDatabase dbase;
    get_database(dbase);
    if(!dbase.open())
    {
        mutexes.value(my_id)->mut->unlock();
        mutex_lock.unlock();
        throw GUtil::Exception("Cannot open database");
    }

    QSqlQuery q(dbase);
    q.exec("DROP TABLE \"files\"");
    q.exec("CREATE TABLE \"files\" ("
               "\"id\" INTEGER NOT NULL,"
               "\"data\" BLOB NOT NULL)");
    q.exec("CREATE INDEX idx ON files(id)");
    dbase.close();

    mutexes.value(my_id)->mut->unlock();
    mutex_lock.unlock();
}

void File_Manager::get_database(QSqlDatabase &dbase) const
{
    dbase = QSqlDatabase::database();
    dbase.setDatabaseName(file_location);
}

QString File_Manager::get_file_loc(const QString &id)
{
    return QDesktopServices::storageLocation(QDesktopServices::TempLocation)
            + QString("/%1.tempfile").arg(id);
}

QList<int> File_Manager::idList()
{
    mutex_lock.lockForRead();
    mutexes.value(my_id)->mut->lockForRead();

    QList<int> ret;
    QSqlDatabase dbase;
    get_database(dbase);
    if(!dbase.open())
    {
        mutexes.value(my_id)->mut->unlock();
        mutex_lock.unlock();
        throw GUtil::Exception("Cannot open database");
    }

    QSqlQuery q("SELECT id FROM files");
    if(q.exec())
    {
        while(q.next())
        {
            ret.append(q.value(0).toInt());
        }
    }

    dbase.close();
    mutexes.value(my_id)->mut->unlock();
    mutex_lock.unlock();
    return ret;
}

bool File_Manager::hasFile(int id)
{
    mutex_lock.lockForRead();
    mutexes.value(my_id)->mut->lockForRead();

    QSqlDatabase dbase;
    get_database(dbase);
    if(!dbase.open())
    {
        mutexes.value(my_id)->mut->unlock();
        mutex_lock.unlock();
        throw GUtil::Exception("Cannot open database");
    }

    QSqlQuery q("SELECT id FROM files WHERE id=:id");
    q.bindValue(":id", id);
    q.exec();
    bool ret = q.first();

    mutexes.value(my_id)->mut->unlock();
    mutex_lock.unlock();
    return ret;
}

int File_Manager::get_free_file_id(QSqlDatabase &dbase)
{
    // You must already have a lock on the database before using this function!

    int max_id = 0;
    QSqlQuery q("SELECT COUNT(id),MAX(id) FROM files", dbase);
    q.exec();
    if(q.first())
    {
        if(q.value(0).toInt() > 0)
            max_id = q.value(1).toInt() + 1;
    }

    return max_id;
}
<|endoftext|>