<commit_before>/*
 * AtmoExternalCaptureInput.cpp: Datasource which gets its data via a COM object call
 * or some other external method.
 *
 *
 * See the README.txt file for copyright information and how to reach the author(s).
 *
 * $Id$
 */

#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

#include "AtmoExternalCaptureInput.h"
#include "AtmoTools.h"

#if defined(_ATMO_VLC_PLUGIN_)

#ifndef INT64_C
#define INT64_C(c)  c ## LL
#endif

CAtmoExternalCaptureInput::CAtmoExternalCaptureInput(CAtmoDynData *pAtmoDynData) :
                           CAtmoInput(pAtmoDynData)
{
    vlc_cond_init( &m_WakeupCond );
    vlc_mutex_init( &m_WakeupLock );
    m_pCurrentFramePixels = NULL;
    m_pLog = pAtmoDynData->getAtmoFilter();
}

#else

CAtmoExternalCaptureInput::CAtmoExternalCaptureInput(CAtmoDynData *pAtmoDynData) :
                           CAtmoInput(pAtmoDynData)
{
    m_hWakeupEvent = CreateEvent(NULL,0,0,NULL);
    InitializeCriticalSection( &m_BufferLock );
    m_pCurrentFramePixels = NULL;
}

#endif

CAtmoExternalCaptureInput::~CAtmoExternalCaptureInput(void)
{
   /* if there is still an unprocessed bufferpicture do kill it */
#if defined(_ATMO_VLC_PLUGIN_)
   vlc_mutex_lock( &m_WakeupLock );
   free( m_pCurrentFramePixels );
   vlc_mutex_unlock( &m_WakeupLock );

   vlc_cond_destroy( &m_WakeupCond );
   vlc_mutex_destroy( &m_WakeupLock );
#else
   EnterCriticalSection( &m_BufferLock );
   free( m_pCurrentFramePixels );
   LeaveCriticalSection( &m_BufferLock );
   CloseHandle(m_hWakeupEvent);
   DeleteCriticalSection( &m_BufferLock );
#endif
}

ATMO_BOOL CAtmoExternalCaptureInput::Open()
{
    this->Run();
    return ATMO_TRUE;
}

// Closes the input-device.
// Returns true if the input-device was closed successfully.
ATMO_BOOL CAtmoExternalCaptureInput::Close(void)
{
    this->Terminate();
    return ATMO_TRUE;
}

/*
  this method will be called from another thread or possible the COM Server to feed
  new pixeldata into the calculation process it doest just the following:
  1: check if last buffer was allready processed (!m_pCurrentFramePixels)
  2. copy the bitmap info structure into the threads own one
  3. alloc memory for frame
  4. copy sourcepixeldata into own buffer...
  5. let the thread wake up and return imediately to the caller
  so that the real videoout wouldn't be stop for a too long time
*/
void CAtmoExternalCaptureInput::DeliverNewSourceDataPaket(VLC_BITMAPINFOHEADER *bmpInfoHeader,void *pixelData)
{
    /*
       normaly we should protect this area of code by critical_section or a mutex,
       but I think we can omit this here because the timing this method is called
       is really slow (in terms of the speed of a modern computer?)
       so it's nearly impossible that two frames are delivert in the same time
       the test needs and malloc needs...
    */
#if defined(_ATMO_VLC_PLUGIN_)
//    msg_Dbg( m_pLog, "DeliverNewSourceDataPaket start...");
    vlc_mutex_lock( &m_WakeupLock );
#else
    EnterCriticalSection( &m_BufferLock );
#endif
    if( !m_pCurrentFramePixels )
    {
        // Last Frame was processed... take this one...
        memcpy(&m_CurrentFrameHeader,bmpInfoHeader,bmpInfoHeader->biSize);
        int PixelDataSize = m_CurrentFrameHeader.biHeight * m_CurrentFrameHeader.biWidth;
        switch(m_CurrentFrameHeader.biBitCount) {
            case 8:  /* PixelDataSize = PixelDataSize; */ break;
            case 16: PixelDataSize = PixelDataSize * 2; break;
            case 24: PixelDataSize = PixelDataSize * 3; break;
            case 32: PixelDataSize = PixelDataSize * 4; break;
        }
        m_pCurrentFramePixels = malloc(PixelDataSize);
        memcpy(m_pCurrentFramePixels,pixelData,PixelDataSize);
    }
#if defined(_ATMO_VLC_PLUGIN_)
    vlc_cond_signal( &m_WakeupCond );
    vlc_mutex_unlock( &m_WakeupLock );
    // msg_Dbg( m_pLog, "DeliverNewSourceDataPaket done.");
#else
    SetEvent(m_hWakeupEvent);
    LeaveCriticalSection( &m_BufferLock );
#endif
}

/*
 the real thread Method which is processing the pixeldata into the hardware channel
 values - which are used by the thread AtmoLiveView...
*/
#if defined (_ATMO_VLC_PLUGIN_)

DWORD CAtmoExternalCaptureInput::Execute(void)
{
    while ((this->m_bTerminated == ATMO_FALSE)) {
          vlc_mutex_lock( &m_WakeupLock );
          vlc_cond_timedwait(&m_WakeupCond, &m_WakeupLock, mdate() + 75000 );

          /* DeliverNewSourceDataPaket delivered new work for me... get it! */
          if(m_pCurrentFramePixels)
             CalcColors(); // read picture and calculate colors
          vlc_mutex_unlock( &m_WakeupLock );
    }

    msg_Dbg( m_pLog, "DWORD CAtmoExternalCaptureInput::Execute(void) bailed out?");

    return 0;
}

#else

DWORD CAtmoExternalCaptureInput::Execute(void) {
    HANDLE handles[2];
    handles[0] = this->m_hTerminateEvent;
    handles[1] = m_hWakeupEvent;

    while (this->m_bTerminated == ATMO_FALSE) {
           DWORD event = WaitForMultipleObjects(2, &handles[0], FALSE, INFINITE);
           if(event == WAIT_OBJECT_0) {
              // Terminate Thread Event was set... say good bye...!
              break;
           }
           if(event == (WAIT_OBJECT_0+1)) {
              EnterCriticalSection( &m_BufferLock );
              if(m_pCurrentFramePixels)
                 CalcColors(); // read picture and calculate colors
              LeaveCriticalSection( &m_BufferLock );
           }
    }
    return 0;
}

#endif

void CAtmoExternalCaptureInput::CalcColors()
{
     // take data from m_CurrentFrameHeader and m_pCurrentFramePixels .. process for atmo ...
    tHSVColor HSV_Img[IMAGE_SIZE];
    tRGBColor pixelColor;
    int srcIndex,index = 0;
    memset(&HSV_Img,0,sizeof(HSV_Img));

    // msg_Dbg( m_pLog, "CalcColors start...");

    if((m_CurrentFrameHeader.biWidth == CAP_WIDTH) && (m_CurrentFrameHeader.biHeight == CAP_HEIGHT))
    {

        // HSVI = HSV Image allready in right format just copy the easiest task
        // und weiterverarbeiten lassen
#ifdef _ATMO_VLC_PLUGIN_
        if(m_CurrentFrameHeader.biCompression ==  VLC_FOURCC('H','S','V','I'))
#else
        if(m_CurrentFrameHeader.biCompression ==  MakeDword('H','S','V','I'))
#endif
        {
            memcpy( &HSV_Img, m_pCurrentFramePixels, CAP_WIDTH * CAP_HEIGHT * sizeof(tHSVColor));
        }
        else if(m_CurrentFrameHeader.biCompression == BI_RGB)
        {
            if(m_CurrentFrameHeader.biBitCount == 16)
            {
                unsigned short *buffer = (unsigned short *)m_pCurrentFramePixels;

                for(int y=0;y<CAP_HEIGHT;y++)
                {
                    srcIndex = y * CAP_WIDTH;
                    for(int x=0;x<CAP_WIDTH;x++)
                    {
                        pixelColor.b = (buffer[srcIndex] & 31) << 3;
                        pixelColor.g = ((buffer[srcIndex] >> 5) & 31) << 3;
                        pixelColor.r = ((buffer[srcIndex] >> 10) & 63) << 2;
                        srcIndex++;
                        HSV_Img[index++] = RGB2HSV(pixelColor);
                    }
                }
            }
            else if(m_CurrentFrameHeader.biBitCount == 24)
            {
                for(int y=0;y<CAP_HEIGHT;y++)
                {
                    srcIndex = y * (CAP_WIDTH*3);
                    for(int x=0;x<CAP_WIDTH;x++)
                    {
                        pixelColor.b = ((unsigned char *)m_pCurrentFramePixels)[srcIndex++];
                        pixelColor.g = ((unsigned char *)m_pCurrentFramePixels)[srcIndex++];
                        pixelColor.r = ((unsigned char *)m_pCurrentFramePixels)[srcIndex++];
                        HSV_Img[index++] = RGB2HSV(pixelColor);
                    }
                }
            }
            else if(m_CurrentFrameHeader.biBitCount == 32)
            {
                for(int y=0;y<CAP_HEIGHT;y++)
                {
                    srcIndex = y * (CAP_WIDTH*4);
                    for(int x=0;x<CAP_WIDTH;x++)
                    {
                        pixelColor.b = ((unsigned char *)m_pCurrentFramePixels)[srcIndex++];
                        pixelColor.g = ((unsigned char *)m_pCurrentFramePixels)[srcIndex++];
                        pixelColor.r = ((unsigned char *)m_pCurrentFramePixels)[srcIndex++];
                        srcIndex++;
                        HSV_Img[index++] = RGB2HSV(pixelColor);
                    }
                }
            }
        }
    }
    /*
      else {
        if the image color format wasn't recognized - the output
        will be black (memset)
        }
    */

    /* remove the source buffer */
    free( m_pCurrentFramePixels );
    m_pCurrentFramePixels = NULL;

    /*
        now convert the pixeldata into one RGB trippel for each channel,
        this is done by some very sophisticated methods and statistics ...

        the only thing I know - the pixel priority is controled by some
        gradients for each edge of the picture

        (sorry I don't know how it exactly works because the formulas
        are done by some one else...)
    */
    //msg_Dbg( m_pLog, "CalcColors ende AddPacket...");
    m_pAtmoDynData->getLivePacketQueue()->AddPacket( m_pAtmoColorCalculator->AnalyzeHSV( HSV_Img ) );
    //msg_Dbg( m_pLog, "CalcColors ende AddPacket...done.");
}

<commit_msg>atmo: remove extraneous parentheses<commit_after>/*
 * AtmoExternalCaptureInput.cpp: Datasource which gets its data via a COM object call
 * or some other external method.
 *
 *
 * See the README.txt file for copyright information and how to reach the author(s).
 *
 * $Id$
 */

#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

#include "AtmoExternalCaptureInput.h"
#include "AtmoTools.h"

#if defined(_ATMO_VLC_PLUGIN_)

#ifndef INT64_C
#define INT64_C(c)  c ## LL
#endif

CAtmoExternalCaptureInput::CAtmoExternalCaptureInput(CAtmoDynData *pAtmoDynData) :
                           CAtmoInput(pAtmoDynData)
{
    vlc_cond_init( &m_WakeupCond );
    vlc_mutex_init( &m_WakeupLock );
    m_pCurrentFramePixels = NULL;
    m_pLog = pAtmoDynData->getAtmoFilter();
}

#else

CAtmoExternalCaptureInput::CAtmoExternalCaptureInput(CAtmoDynData *pAtmoDynData) :
                           CAtmoInput(pAtmoDynData)
{
    m_hWakeupEvent = CreateEvent(NULL,0,0,NULL);
    InitializeCriticalSection( &m_BufferLock );
    m_pCurrentFramePixels = NULL;
}

#endif

CAtmoExternalCaptureInput::~CAtmoExternalCaptureInput(void)
{
   /* if there is still an unprocessed bufferpicture do kill it */
#if defined(_ATMO_VLC_PLUGIN_)
   vlc_mutex_lock( &m_WakeupLock );
   free( m_pCurrentFramePixels );
   vlc_mutex_unlock( &m_WakeupLock );

   vlc_cond_destroy( &m_WakeupCond );
   vlc_mutex_destroy( &m_WakeupLock );
#else
   EnterCriticalSection( &m_BufferLock );
   free( m_pCurrentFramePixels );
   LeaveCriticalSection( &m_BufferLock );
   CloseHandle(m_hWakeupEvent);
   DeleteCriticalSection( &m_BufferLock );
#endif
}

ATMO_BOOL CAtmoExternalCaptureInput::Open()
{
    this->Run();
    return ATMO_TRUE;
}

// Closes the input-device.
// Returns true if the input-device was closed successfully.
ATMO_BOOL CAtmoExternalCaptureInput::Close(void)
{
    this->Terminate();
    return ATMO_TRUE;
}

/*
  this method will be called from another thread or possible the COM Server to feed
  new pixeldata into the calculation process it doest just the following:
  1: check if last buffer was allready processed (!m_pCurrentFramePixels)
  2. copy the bitmap info structure into the threads own one
  3. alloc memory for frame
  4. copy sourcepixeldata into own buffer...
  5. let the thread wake up and return imediately to the caller
  so that the real videoout wouldn't be stop for a too long time
*/
void CAtmoExternalCaptureInput::DeliverNewSourceDataPaket(VLC_BITMAPINFOHEADER *bmpInfoHeader,void *pixelData)
{
    /*
       normaly we should protect this area of code by critical_section or a mutex,
       but I think we can omit this here because the timing this method is called
       is really slow (in terms of the speed of a modern computer?)
       so it's nearly impossible that two frames are delivert in the same time
       the test needs and malloc needs...
    */
#if defined(_ATMO_VLC_PLUGIN_)
//    msg_Dbg( m_pLog, "DeliverNewSourceDataPaket start...");
    vlc_mutex_lock( &m_WakeupLock );
#else
    EnterCriticalSection( &m_BufferLock );
#endif
    if( !m_pCurrentFramePixels )
    {
        // Last Frame was processed... take this one...
        memcpy(&m_CurrentFrameHeader,bmpInfoHeader,bmpInfoHeader->biSize);
        int PixelDataSize = m_CurrentFrameHeader.biHeight * m_CurrentFrameHeader.biWidth;
        switch(m_CurrentFrameHeader.biBitCount) {
            case 8:  /* PixelDataSize = PixelDataSize; */ break;
            case 16: PixelDataSize = PixelDataSize * 2; break;
            case 24: PixelDataSize = PixelDataSize * 3; break;
            case 32: PixelDataSize = PixelDataSize * 4; break;
        }
        m_pCurrentFramePixels = malloc(PixelDataSize);
        memcpy(m_pCurrentFramePixels,pixelData,PixelDataSize);
    }
#if defined(_ATMO_VLC_PLUGIN_)
    vlc_cond_signal( &m_WakeupCond );
    vlc_mutex_unlock( &m_WakeupLock );
    // msg_Dbg( m_pLog, "DeliverNewSourceDataPaket done.");
#else
    SetEvent(m_hWakeupEvent);
    LeaveCriticalSection( &m_BufferLock );
#endif
}

/*
 the real thread Method which is processing the pixeldata into the hardware channel
 values - which are used by the thread AtmoLiveView...
*/
#if defined (_ATMO_VLC_PLUGIN_)

DWORD CAtmoExternalCaptureInput::Execute(void)
{
    while (this->m_bTerminated == ATMO_FALSE) {
          vlc_mutex_lock( &m_WakeupLock );
          vlc_cond_timedwait(&m_WakeupCond, &m_WakeupLock, mdate() + 75000 );

          /* DeliverNewSourceDataPaket delivered new work for me... get it! */
          if(m_pCurrentFramePixels)
             CalcColors(); // read picture and calculate colors
          vlc_mutex_unlock( &m_WakeupLock );
    }

    msg_Dbg( m_pLog, "DWORD CAtmoExternalCaptureInput::Execute(void) bailed out?");

    return 0;
}

#else

DWORD CAtmoExternalCaptureInput::Execute(void) {
    HANDLE handles[2];
    handles[0] = this->m_hTerminateEvent;
    handles[1] = m_hWakeupEvent;

    while (this->m_bTerminated == ATMO_FALSE) {
           DWORD event = WaitForMultipleObjects(2, &handles[0], FALSE, INFINITE);
           if(event == WAIT_OBJECT_0) {
              // Terminate Thread Event was set... say good bye...!
              break;
           }
           if(event == (WAIT_OBJECT_0+1)) {
              EnterCriticalSection( &m_BufferLock );
              if(m_pCurrentFramePixels)
                 CalcColors(); // read picture and calculate colors
              LeaveCriticalSection( &m_BufferLock );
           }
    }
    return 0;
}

#endif

void CAtmoExternalCaptureInput::CalcColors()
{
     // take data from m_CurrentFrameHeader and m_pCurrentFramePixels .. process for atmo ...
    tHSVColor HSV_Img[IMAGE_SIZE];
    tRGBColor pixelColor;
    int srcIndex,index = 0;
    memset(&HSV_Img,0,sizeof(HSV_Img));

    // msg_Dbg( m_pLog, "CalcColors start...");

    if((m_CurrentFrameHeader.biWidth == CAP_WIDTH) && (m_CurrentFrameHeader.biHeight == CAP_HEIGHT))
    {

        // HSVI = HSV Image allready in right format just copy the easiest task
        // und weiterverarbeiten lassen
#ifdef _ATMO_VLC_PLUGIN_
        if(m_CurrentFrameHeader.biCompression ==  VLC_FOURCC('H','S','V','I'))
#else
        if(m_CurrentFrameHeader.biCompression ==  MakeDword('H','S','V','I'))
#endif
        {
            memcpy( &HSV_Img, m_pCurrentFramePixels, CAP_WIDTH * CAP_HEIGHT * sizeof(tHSVColor));
        }
        else if(m_CurrentFrameHeader.biCompression == BI_RGB)
        {
            if(m_CurrentFrameHeader.biBitCount == 16)
            {
                unsigned short *buffer = (unsigned short *)m_pCurrentFramePixels;

                for(int y=0;y<CAP_HEIGHT;y++)
                {
                    srcIndex = y * CAP_WIDTH;
                    for(int x=0;x<CAP_WIDTH;x++)
                    {
                        pixelColor.b = (buffer[srcIndex] & 31) << 3;
                        pixelColor.g = ((buffer[srcIndex] >> 5) & 31) << 3;
                        pixelColor.r = ((buffer[srcIndex] >> 10) & 63) << 2;
                        srcIndex++;
                        HSV_Img[index++] = RGB2HSV(pixelColor);
                    }
                }
            }
            else if(m_CurrentFrameHeader.biBitCount == 24)
            {
                for(int y=0;y<CAP_HEIGHT;y++)
                {
                    srcIndex = y * (CAP_WIDTH*3);
                    for(int x=0;x<CAP_WIDTH;x++)
                    {
                        pixelColor.b = ((unsigned char *)m_pCurrentFramePixels)[srcIndex++];
                        pixelColor.g = ((unsigned char *)m_pCurrentFramePixels)[srcIndex++];
                        pixelColor.r = ((unsigned char *)m_pCurrentFramePixels)[srcIndex++];
                        HSV_Img[index++] = RGB2HSV(pixelColor);
                    }
                }
            }
            else if(m_CurrentFrameHeader.biBitCount == 32)
            {
                for(int y=0;y<CAP_HEIGHT;y++)
                {
                    srcIndex = y * (CAP_WIDTH*4);
                    for(int x=0;x<CAP_WIDTH;x++)
                    {
                        pixelColor.b = ((unsigned char *)m_pCurrentFramePixels)[srcIndex++];
                        pixelColor.g = ((unsigned char *)m_pCurrentFramePixels)[srcIndex++];
                        pixelColor.r = ((unsigned char *)m_pCurrentFramePixels)[srcIndex++];
                        srcIndex++;
                        HSV_Img[index++] = RGB2HSV(pixelColor);
                    }
                }
            }
        }
    }
    /*
      else {
        if the image color format wasn't recognized - the output
        will be black (memset)
        }
    */

    /* remove the source buffer */
    free( m_pCurrentFramePixels );
    m_pCurrentFramePixels = NULL;

    /*
        now convert the pixeldata into one RGB trippel for each channel,
        this is done by some very sophisticated methods and statistics ...

        the only thing I know - the pixel priority is controled by some
        gradients for each edge of the picture

        (sorry I don't know how it exactly works because the formulas
        are done by some one else...)
    */
    //msg_Dbg( m_pLog, "CalcColors ende AddPacket...");
    m_pAtmoDynData->getLivePacketQueue()->AddPacket( m_pAtmoColorCalculator->AnalyzeHSV( HSV_Img ) );
    //msg_Dbg( m_pLog, "CalcColors ende AddPacket...done.");
}

<|endoftext|>


<commit_before>/*************** <auto-copyright.pl BEGIN do not edit this line> **************
 *
 * VR Juggler is (C) Copyright 1998-2011 by Iowa State University
 *
 * Original Authors:
 *   Allen Bierbaum, Christopher Just,
 *   Patrick Hartling, Kevin Meinert,
 *   Carolina Cruz-Neira, Albert Baker
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301, USA.
 *
 *************** <auto-copyright.pl END do not edit this line> ***************/

#include <vrj/Draw/Direct3D/Config.h>

#include <gmtl/Matrix.h>
#include <gmtl/Generate.h>
#include <gmtl/MatrixOps.h>
#include <gmtl/Vec.h>
#include <gmtl/VecOps.h>
#include <gmtl/Xforms.h>
#include <gmtl/Output.h>

#include <jccl/Config/ConfigElement.h>

#include <vrj/Kernel/User.h>

#include <vrj/Draw/Direct3D/DrawManager.h>

#include <vrj/Display/DisplayManager.h>
#include <vrj/Display/Display.h>
#include <vrj/Display/Projection.h>
#include <vrj/Display/Viewport.h>
#include <vrj/Display/SimViewport.h>
#include <vrj/Display/SurfaceViewport.h>

#include <vrj/Draw/Direct3D/SimInterfaceFactory.h>
#include <vrj/Draw/Direct3D/BasicSimulator.h>

namespace vrj
{

namespace direct3d
{

VRJ_REGISTER_D3D_SIM_INTERFACE_CREATOR(BasicSimulator);

BasicSimulator::BasicSimulator()
{;}

BasicSimulator::~BasicSimulator()
{;}

/*
* Configure the basic simulator config
* @pre element is a valid configuration element.
* @post It should be configured
*/
bool BasicSimulator::config(jccl::ConfigElementPtr element)
{
   vprASSERT(element.get() != NULL);
   vprASSERT(element->getID() == std::string("default_simulator"));

   std::string camera_proxy_str = element->getProperty<std::string>("camera_pos");
   std::string wand_proxy_str = element->getProperty<std::string>("wand_pos");

   mCamera.init(camera_proxy_str);
   mWand.init(wand_proxy_str);      // Configure the wand to use

   if(!mCamera.isConnected())
   {
      vprDEBUG(vprDBG_ERROR, vprDBG_CRITICAL_LVL)
         << clrOutNORM(clrRED, "ERROR:")
         << "[vrj::direct3d::BasicSimulator::config()] Fatal Error: Camera "
         << "not found named: " << camera_proxy_str << vprDEBUG_FLUSH;
      vprASSERT(false);
   }

   // Get drawing parameters
   mDrawProjections = element->getProperty<bool>("draw_projections");
   mSurfaceColor[0] = element->getProperty<float>("surface_color", 0);
   mSurfaceColor[1] = element->getProperty<float>("surface_color", 1);
   mSurfaceColor[2] = element->getProperty<float>("surface_color", 2);

   return true;
}

/**
 * Draws this sim device using the given information about the Window it
 * will be drawing into.
 */
void BasicSimulator::draw(const float scaleFactor)
{
   drawSimulator(scaleFactor);
}

/**
 * Sets the keyboard/mouse device the simulator can use to get input from the
 * user.
 */
void BasicSimulator::setKeyboardMouse(gadget::KeyboardMouseInterface)
{
}

void BasicSimulator::updateProjectionData(const float positionScale,
                                          ProjectionPtr leftProj,
                                          ProjectionPtr rightProj)
{
   updateInternalData(positionScale);

   gmtl::Matrix44f camera_pos = getCameraPos();
   gmtl::Vec3f camera_trans = gmtl::makeTrans<gmtl::Vec3f>(camera_pos);

   // -- Calculate camera (eye) Positions -- //
   vprDEBUG(vprDBG_ALL, vprDBG_HEX_LVL)
      << "[vrj::direct3d::BasicSimulator::updateProjectionData()] Getting "
      << "camera position" << std::endl << vprDEBUG_FLUSH;
   vprDEBUG(vprDBG_ALL, vprDBG_HEX_LVL) << "CamPos:" << camera_trans
                                        << std::endl << vprDEBUG_FLUSH;

   // Compute location of left and right eyes
   float interocular_dist = mSimViewport->getUser()->getInterocularDistance();
   interocular_dist *= positionScale;               // Scale into correct units
   float eye_offset = interocular_dist / 2.0f;      // Distance to move eye

   // NOTE: Eye coord system is -z forward, x-right, y-up
   const gmtl::Point3f left_eye_pos(
      camera_pos * gmtl::Point3f(-eye_offset, 0.0f, 0.0f)
   );
   const gmtl::Point3f right_eye_pos(
      camera_pos * gmtl::Point3f(eye_offset, 0.0f, 0.0f)
   );

   leftProj->calcViewMatrix(left_eye_pos, positionScale);
   rightProj->calcViewMatrix(right_eye_pos, positionScale);
}

/**  Update internal simulator data */
void BasicSimulator::updateInternalData(float positionScale)
{
   mHeadPos = mSimViewport->getUser()->getHeadPosProxy()->getData(positionScale);
   mWandPos = mWand->getData(positionScale);

   mCameraPos = mCamera->getData(positionScale);
   gmtl::invert(mCameraPos);
}

// Draw any objects that we need to display in the scene
// from the system.  (i.e. Gloves, etc)
void BasicSimulator::drawObjects()
{
   // XXX: Not implemented
}

/**
 * Draw the projections.
 *
 * @post Draws the projections.
 *       If withApex, then it draws the frustums with different colors.
 *       If !withApex, then just draws the surfaces in all white.
 */
void BasicSimulator::drawProjections(const bool drawFrustum,
                                     const gmtl::Vec3f& surfColor,
                                     const float scaleFactor)
{
   const float ALPHA_VALUE(0.25f);

   DisplayManager* display_man =
      vrj::direct3d::DrawManager::instance()->getDisplayManager();
   display_man->updateProjections(scaleFactor);                     // Update all projections for drawing

   // XXX: Not implemented
}

/**
 * Draws a simulator using OpenGL commands.
 * @note This is called internally by the library.
 */
void BasicSimulator::drawSimulator(const float scaleFactor)
{
   // XXX: Not implemented
}

}

}
<commit_msg>Update the Direct3D Draw Manager for interface changes.<commit_after>/*************** <auto-copyright.pl BEGIN do not edit this line> **************
 *
 * VR Juggler is (C) Copyright 1998-2011 by Iowa State University
 *
 * Original Authors:
 *   Allen Bierbaum, Christopher Just,
 *   Patrick Hartling, Kevin Meinert,
 *   Carolina Cruz-Neira, Albert Baker
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301, USA.
 *
 *************** <auto-copyright.pl END do not edit this line> ***************/

#include <vrj/Draw/Direct3D/Config.h>

#include <gmtl/Matrix.h>
#include <gmtl/Generate.h>
#include <gmtl/MatrixOps.h>
#include <gmtl/Vec.h>
#include <gmtl/VecOps.h>
#include <gmtl/Xforms.h>
#include <gmtl/Output.h>

#include <jccl/Config/ConfigElement.h>

#include <vrj/Kernel/User.h>

#include <vrj/Draw/Direct3D/DrawManager.h>

#include <vrj/Display/DisplayManager.h>
#include <vrj/Display/Display.h>
#include <vrj/Display/Projection.h>
#include <vrj/Display/Viewport.h>
#include <vrj/Display/SimViewport.h>
#include <vrj/Display/SurfaceViewport.h>

#include <vrj/Draw/Direct3D/SimInterfaceFactory.h>
#include <vrj/Draw/Direct3D/BasicSimulator.h>

namespace vrj
{

namespace direct3d
{

VRJ_REGISTER_D3D_SIM_INTERFACE_CREATOR(BasicSimulator);

BasicSimulator::BasicSimulator()
{;}

BasicSimulator::~BasicSimulator()
{;}

/*
* Configure the basic simulator config
* @pre element is a valid configuration element.
* @post It should be configured
*/
bool BasicSimulator::config(jccl::ConfigElementPtr element)
{
   vprASSERT(element.get() != NULL);
   vprASSERT(element->getID() == std::string("default_simulator"));

   std::string camera_proxy_str = element->getProperty<std::string>("camera_pos");
   std::string wand_proxy_str = element->getProperty<std::string>("wand_pos");

   mCamera.init(camera_proxy_str);
   mWand.init(wand_proxy_str);      // Configure the wand to use

   if(!mCamera.isConnected())
   {
      vprDEBUG(vprDBG_ERROR, vprDBG_CRITICAL_LVL)
         << clrOutNORM(clrRED, "ERROR:")
         << "[vrj::direct3d::BasicSimulator::config()] Fatal Error: Camera "
         << "not found named: " << camera_proxy_str << vprDEBUG_FLUSH;
      vprASSERT(false);
   }

   // Get drawing parameters
   mDrawProjections = element->getProperty<bool>("draw_projections");
   mSurfaceColor[0] = element->getProperty<float>("surface_color", 0);
   mSurfaceColor[1] = element->getProperty<float>("surface_color", 1);
   mSurfaceColor[2] = element->getProperty<float>("surface_color", 2);

   return true;
}

/**
 * Draws this sim device using the given information about the Window it
 * will be drawing into.
 */
void BasicSimulator::draw(const float scaleFactor)
{
   drawSimulator(scaleFactor);
}

/**
 * Sets the keyboard/mouse device the simulator can use to get input from the
 * user.
 */
void BasicSimulator::setKeyboardMouse(gadget::KeyboardMouseInterface)
{
}

void BasicSimulator::updateProjectionData(const float positionScale,
                                          ProjectionPtr leftProj,
                                          ProjectionPtr rightProj)
{
   updateInternalData(positionScale);

   gmtl::Matrix44f camera_pos = getCameraPos();
   gmtl::Vec3f camera_trans = gmtl::makeTrans<gmtl::Vec3f>(camera_pos);

   // -- Calculate camera (eye) Positions -- //
   vprDEBUG(vprDBG_ALL, vprDBG_HEX_LVL)
      << "[vrj::direct3d::BasicSimulator::updateProjectionData()] Getting "
      << "camera position" << std::endl << vprDEBUG_FLUSH;
   vprDEBUG(vprDBG_ALL, vprDBG_HEX_LVL) << "CamPos:" << camera_trans
                                        << std::endl << vprDEBUG_FLUSH;

   // Compute location of left and right eyes
   float interocular_dist = mSimViewport->getUser()->getInterocularDistance();
   interocular_dist *= positionScale;               // Scale into correct units
   float eye_offset = interocular_dist / 2.0f;      // Distance to move eye

   gmtl::Matrix44f left_eye_pos, right_eye_pos;     // NOTE: Eye coord system is -z forward, x-right, y-up
   left_eye_pos = camera_pos * gmtl::makeTrans<gmtl::Matrix44f>( gmtl::Vec3f(-eye_offset, 0.0f, 0.0f) );
   right_eye_pos = camera_pos * gmtl::makeTrans<gmtl::Matrix44f>( gmtl::Vec3f(eye_offset, 0.0f, 0.0f) );

   // NOTE: Eye coord system is -z forward, x-right, y-up
   const gmtl::Point3f left_eye_pt(
      gmtl::makeTrans<gmtl::Point3f>(left_eye_pos)
   );
   const gmtl::Point3f right_eye_pt(
      gmtl::makeTrans<gmtl::Point3f>(right_eye_pos)
   );
   leftProj->calcViewMatrix(left_eye_pos, left_eye_pt, positionScale);
   rightProj->calcViewMatrix(right_eye_pos, right_eye_pt, positionScale);
}

/**  Update internal simulator data */
void BasicSimulator::updateInternalData(float positionScale)
{
   mHeadPos = mSimViewport->getUser()->getHeadPosProxy()->getData(positionScale);
   mWandPos = mWand->getData(positionScale);

   mCameraPos = mCamera->getData(positionScale);
   gmtl::invert(mCameraPos);
}

// Draw any objects that we need to display in the scene
// from the system.  (i.e. Gloves, etc)
void BasicSimulator::drawObjects()
{
   // XXX: Not implemented
}

/**
 * Draw the projections.
 *
 * @post Draws the projections.
 *       If withApex, then it draws the frustums with different colors.
 *       If !withApex, then just draws the surfaces in all white.
 */
void BasicSimulator::drawProjections(const bool drawFrustum,
                                     const gmtl::Vec3f& surfColor,
                                     const float scaleFactor)
{
   const float ALPHA_VALUE(0.25f);

   DisplayManager* display_man =
      vrj::direct3d::DrawManager::instance()->getDisplayManager();
   display_man->updateProjections(scaleFactor);                     // Update all projections for drawing

   // XXX: Not implemented
}

/**
 * Draws a simulator using OpenGL commands.
 * @note This is called internally by the library.
 */
void BasicSimulator::drawSimulator(const float scaleFactor)
{
   // XXX: Not implemented
}

}

}
<|endoftext|>


<commit_before>/*  -*- c++ -*-
    kmservertest.cpp

    This file is part of KMail, the KDE mail client.
    Copyright (c) 2001-2002 Michael Haeckel <haeckel@kde.org>
    Copyright (c) 2003 Marc Mutz <mutz@kde.org>

    KMail is free software; you can redistribute it and/or modify it
    under the terms of the GNU General Public License, version 2, as
    published by the Free Software Foundation.

    KMail is distributed in the hope that it will be useful, but
    WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

    In addition, as a special exception, the copyright holders give
    permission to link the code of this program with any edition of
    the Qt library by Trolltech AS, Norway (or with modified versions
    of Qt that use the same license as Qt), and distribute linked
    combinations including the two.  You must obey the GNU General
    Public License in all respects for all of the code used other than
    Qt.  If you modify this file, you may extend this exception to
    your version of the file, but you are not obligated to do so.  If
    you do not wish to do so, delete this exception statement from
    your version.
*/

#include <config.h>

#include "kmservertest.h"

#include <klocale.h>
#include <kmessagebox.h>
#include <kdebug.h>
#include <kurl.h>
#include <kapplication.h>
#include <kio/scheduler.h>
#include <kio/slave.h>
#include <kio/job.h>
#include <kio/global.h>

//-----------------------------------------------------------------------------
KMServerTest::KMServerTest( const QString & protocol, const QString & host, int port )
  : QObject(),
    mProtocol( protocol ), mHost( host ),
    mSSL( false ), mJob( 0 ), mSlave( 0 ), mConnectionErrorCount( 0 )
{
  KIO::Scheduler::connect(
    SIGNAL(slaveError(KIO::Slave *, int, const QString &)),
    this, SLOT(slotSlaveResult(KIO::Slave *, int, const QString &)));

  if ( port == 993 || port == 995 || port == 465 )
    port = 0;

  startOffSlave( port );
}

//-----------------------------------------------------------------------------
KMServerTest::~KMServerTest()
{
  if (mJob) mJob->kill(TRUE);
}

KIO::MetaData KMServerTest::slaveConfig() const {
  KIO::MetaData md;
  md.insert( "nologin", "on" );
  return md;
}

void KMServerTest::startOffSlave( int port ) {
  KURL url;
  url.setProtocol( mSSL ? mProtocol + 's' : mProtocol );
  url.setHost( mHost );
  if ( port )
    url.setPort( port );

  mSlave = KIO::Scheduler::getConnectedSlave( url, slaveConfig() );
  if ( !mSlave ) {
    slotSlaveResult( 0, 1 );
    return;
  }
  connect( mSlave, SIGNAL(metaData(const KIO::MetaData&)),
	   SLOT(slotMetaData(const KIO::MetaData&)) );

  QByteArray packedArgs;
  QDataStream stream( packedArgs, IO_WriteOnly );

  stream << (int) 'c';

  mJob = KIO::special( url, packedArgs, false );
  KIO::Scheduler::assignJobToSlave( mSlave, mJob );
  connect( mJob, SIGNAL(result(KIO::Job*)), SLOT(slotResult(KIO::Job*)) );
  connect( mJob, SIGNAL(infoMessage(KIO::Job*,const QString&)),
	   SLOT(slotData(KIO::Job*,const QString&)) );
}

//-----------------------------------------------------------------------------
void KMServerTest::slotData(KIO::Job *, const QString &data)
{
  if ( mSSL )
    mListSSL = QStringList::split(' ', data);
  else
    mListNormal = QStringList::split(' ', data);
}

void KMServerTest::slotMetaData( const KIO::MetaData & md ) {
  KIO::MetaData::const_iterator it = md.find( "PLAIN AUTH METHODS" );
  if ( it != md.end() ) {
    mAuthNone = it.data();
    kdDebug(5006) << "mAuthNone: " << mAuthNone << endl;
  }
  it = md.find( "TLS AUTH METHODS" );
  if ( it != md.end() ) {
    mAuthTLS = it.data();
    kdDebug(5006) << "mAuthTLS: " << mAuthTLS << endl;
  }
  it = md.find( "SSL AUTH METHODS" );
  if ( it != md.end() ) {
    mAuthSSL = it.data();
    kdDebug(5006) << "mAuthSSL: " << mAuthSSL << endl;
  }
}

//-----------------------------------------------------------------------------
void KMServerTest::slotResult(KIO::Job *job)
{
  slotSlaveResult(mSlave, job->error());
}

//-----------------------------------------------------------------------------
void KMServerTest::slotSlaveResult(KIO::Slave *aSlave, int error,
  const QString &errorText)
{
  if (aSlave != mSlave) return;
  if (error != KIO::ERR_SLAVE_DIED && mSlave)
  {
    // disconnect slave after every connect
    KIO::Scheduler::disconnectSlave(mSlave);
    mSlave = 0;
  }
  if ( error == KIO::ERR_COULD_NOT_CONNECT )
  {
    // if one of the two connection tests fails we ignore the error
    // if both fail the host is probably not correct so we display the error
    if ( mConnectionErrorCount == 0 )
    {
      error = 0;
    }
    ++mConnectionErrorCount;
  }
  if ( error )
  {
    mJob = 0;
    KMessageBox::error( kapp->activeWindow(),
        KIO::buildErrorString( error, errorText ),
        i18n("Error") );
    emit capabilities( mListNormal, mListSSL );
    emit capabilities( mListNormal, mListSSL, mAuthNone, mAuthSSL, mAuthTLS );
    return;
  }
  if (!mSSL) {
    mSSL = true;
    mListNormal.append("NORMAL-CONNECTION");
    startOffSlave();
  } else {
    //mListSSL.append("SSL");
    mJob = 0;

    emit capabilities( mListNormal, mListSSL );
    emit capabilities( mListNormal, mListSSL, mAuthNone, mAuthSSL, mAuthTLS );
  }
}

#include "kmservertest.moc"
<commit_msg>The dummy entry is needed because the server might respond with an empty string to the CAPA command although SSL (without additional capabilities) works. Since we check for the stringlist being empty to find out whether SSL is supported the stringlist must only be empty if SSL is not supported.<commit_after>/*  -*- c++ -*-
    kmservertest.cpp

    This file is part of KMail, the KDE mail client.
    Copyright (c) 2001-2002 Michael Haeckel <haeckel@kde.org>
    Copyright (c) 2003 Marc Mutz <mutz@kde.org>

    KMail is free software; you can redistribute it and/or modify it
    under the terms of the GNU General Public License, version 2, as
    published by the Free Software Foundation.

    KMail is distributed in the hope that it will be useful, but
    WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

    In addition, as a special exception, the copyright holders give
    permission to link the code of this program with any edition of
    the Qt library by Trolltech AS, Norway (or with modified versions
    of Qt that use the same license as Qt), and distribute linked
    combinations including the two.  You must obey the GNU General
    Public License in all respects for all of the code used other than
    Qt.  If you modify this file, you may extend this exception to
    your version of the file, but you are not obligated to do so.  If
    you do not wish to do so, delete this exception statement from
    your version.
*/

#include <config.h>

#include "kmservertest.h"

#include <klocale.h>
#include <kmessagebox.h>
#include <kdebug.h>
#include <kurl.h>
#include <kapplication.h>
#include <kio/scheduler.h>
#include <kio/slave.h>
#include <kio/job.h>
#include <kio/global.h>

//-----------------------------------------------------------------------------
KMServerTest::KMServerTest( const QString & protocol, const QString & host, int port )
  : QObject(),
    mProtocol( protocol ), mHost( host ),
    mSSL( false ), mJob( 0 ), mSlave( 0 ), mConnectionErrorCount( 0 )
{
  KIO::Scheduler::connect(
    SIGNAL(slaveError(KIO::Slave *, int, const QString &)),
    this, SLOT(slotSlaveResult(KIO::Slave *, int, const QString &)));

  if ( port == 993 || port == 995 || port == 465 )
    port = 0;

  startOffSlave( port );
}

//-----------------------------------------------------------------------------
KMServerTest::~KMServerTest()
{
  if (mJob) mJob->kill(TRUE);
}

KIO::MetaData KMServerTest::slaveConfig() const {
  KIO::MetaData md;
  md.insert( "nologin", "on" );
  return md;
}

void KMServerTest::startOffSlave( int port ) {
  KURL url;
  url.setProtocol( mSSL ? mProtocol + 's' : mProtocol );
  url.setHost( mHost );
  if ( port )
    url.setPort( port );

  mSlave = KIO::Scheduler::getConnectedSlave( url, slaveConfig() );
  if ( !mSlave ) {
    slotSlaveResult( 0, 1 );
    return;
  }
  connect( mSlave, SIGNAL(metaData(const KIO::MetaData&)),
	   SLOT(slotMetaData(const KIO::MetaData&)) );

  QByteArray packedArgs;
  QDataStream stream( packedArgs, IO_WriteOnly );

  stream << (int) 'c';

  mJob = KIO::special( url, packedArgs, false );
  KIO::Scheduler::assignJobToSlave( mSlave, mJob );
  connect( mJob, SIGNAL(result(KIO::Job*)), SLOT(slotResult(KIO::Job*)) );
  connect( mJob, SIGNAL(infoMessage(KIO::Job*,const QString&)),
	   SLOT(slotData(KIO::Job*,const QString&)) );
}

//-----------------------------------------------------------------------------
void KMServerTest::slotData(KIO::Job *, const QString &data)
{
  if ( mSSL )
    mListSSL = QStringList::split(' ', data);
  else
    mListNormal = QStringList::split(' ', data);
}

void KMServerTest::slotMetaData( const KIO::MetaData & md ) {
  KIO::MetaData::const_iterator it = md.find( "PLAIN AUTH METHODS" );
  if ( it != md.end() ) {
    mAuthNone = it.data();
    kdDebug(5006) << "mAuthNone: " << mAuthNone << endl;
  }
  it = md.find( "TLS AUTH METHODS" );
  if ( it != md.end() ) {
    mAuthTLS = it.data();
    kdDebug(5006) << "mAuthTLS: " << mAuthTLS << endl;
  }
  it = md.find( "SSL AUTH METHODS" );
  if ( it != md.end() ) {
    mAuthSSL = it.data();
    kdDebug(5006) << "mAuthSSL: " << mAuthSSL << endl;
  }
}

//-----------------------------------------------------------------------------
void KMServerTest::slotResult(KIO::Job *job)
{
  slotSlaveResult(mSlave, job->error());
}

//-----------------------------------------------------------------------------
void KMServerTest::slotSlaveResult(KIO::Slave *aSlave, int error,
  const QString &errorText)
{
  if (aSlave != mSlave) return;
  if ( mSSL && error == 0 ) {
    // add a dummy entry to the list of SSL capabilities so that the receiver
    // of the capabilities signal can use mListSSL.isEmpty() in order to find
    // out whether SSL is supported
    mListSSL.append("SSL");
  }

  if (error != KIO::ERR_SLAVE_DIED && mSlave)
  {
    // disconnect slave after every connect
    KIO::Scheduler::disconnectSlave(mSlave);
    mSlave = 0;
  }
  if ( error == KIO::ERR_COULD_NOT_CONNECT )
  {
    // if one of the two connection tests fails we ignore the error
    // if both fail the host is probably not correct so we display the error
    if ( mConnectionErrorCount == 0 )
    {
      error = 0;
    }
    ++mConnectionErrorCount;
  }
  if ( error )
  {
    mJob = 0;
    KMessageBox::error( kapp->activeWindow(),
        KIO::buildErrorString( error, errorText ),
        i18n("Error") );
    emit capabilities( mListNormal, mListSSL );
    emit capabilities( mListNormal, mListSSL, mAuthNone, mAuthSSL, mAuthTLS );
    return;
  }
  if (!mSSL) {
    mSSL = true;
    mListNormal.append("NORMAL-CONNECTION");
    startOffSlave();
  } else {
    mJob = 0;

    emit capabilities( mListNormal, mListSSL );
    emit capabilities( mListNormal, mListSSL, mAuthNone, mAuthSSL, mAuthTLS );
  }
}

#include "kmservertest.moc"
<|endoftext|>


<commit_before>/***************************************************************************
                          kmsystemtray.cpp  -  description
                             -------------------
    begin                : Fri Aug 31 22:38:44 EDT 2001
    copyright            : (C) 2001 by Ryan Breen
    email                : ryan@porivo.com
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 ***************************************************************************/

#ifdef HAVE_CONFIG_H
  #include <config.h>
#endif

#include <kapp.h>
#include <kpopupmenu.h>
#include <kdebug.h>
#include <kiconloader.h>
#include <kwin.h>

#include <qevent.h>
#include <qfontmetrics.h>
#include <qlistview.h>
#include <qstring.h>
#include <qtooltip.h>
#include <qlabel.h>
#include <qwidgetlist.h>

#include <kglobalsettings.h>
#include <kiconeffect.h>
#include <qpainter.h>

#include <math.h>

#include "kaction.h"
#include "kmmainwidget.h"
#include "kmsystemtray.h"
#include "kmfolder.h"
#include "kmfoldertree.h"
#include "kmkernel.h"
#include "kmfoldermgr.h"
#include "kmfolderimap.h"
#include "kmmainwidget.h"

/**
 * Construct a KSystemTray icon to be displayed when new mail
 * has arrived in a non-system folder.  The KMSystemTray listens
 * for updateNewMessageNotification events from each non-system
 * KMFolder and maintains a store of all folders with unread
 * messages.
 *
 * The KMSystemTray also provides a popup menu listing each folder
 * with its count of unread messages, allowing the user to jump
 * to the first unread message in each folder.
 */
KMSystemTray::KMSystemTray(QWidget *parent, const char *name) : KSystemTray(parent, name),
mNewMessagePopupId(-1), mPopupMenu(0)
{
  setAlignment( AlignCenter );
  kdDebug(5006) << "Initting systray" << endl;

  mDefaultIcon = SmallIcon( "kmail" );
  mTransparentIcon = SmallIcon( "kmail" );
  KIconEffect::semiTransparent( mTransparentIcon );

  setPixmap(mDefaultIcon);
  mParentVisible = true;
  mMode = OnNewMail;

  /** Initiate connections between folders and this object */
  foldersChanged();

  connect( kernel->folderMgr(), SIGNAL(changed()), SLOT(foldersChanged()));
  connect( kernel->imapFolderMgr(), SIGNAL(changed()), SLOT(foldersChanged()));
  connect( kernel->searchFolderMgr(), SIGNAL(changed()), SLOT(foldersChanged()));
}

void KMSystemTray::buildPopupMenu()
{
  // Delete any previously created popup menu
  delete mPopupMenu;
  mPopupMenu = 0;

  mPopupMenu = new KPopupMenu();
  if (!getKMMainWidget())
    return;

  mPopupMenu->insertTitle(*(this->pixmap()), "KMail");
  getKMMainWidget()->action("check_mail")->plug(mPopupMenu);
  getKMMainWidget()->action("check_mail_in")->plug(mPopupMenu);
  mPopupMenu->insertSeparator();
  getKMMainWidget()->action("new_message")->plug(mPopupMenu);
  getKMMainWidget()->action("kmail_configure_kmail")->plug(mPopupMenu);
  mPopupMenu->insertSeparator();
  getKMMainWidget()->action("file_quit")->plug(mPopupMenu);
}

KMSystemTray::~KMSystemTray()
{
  delete mPopupMenu;
  mPopupMenu = 0;
}

void KMSystemTray::setMode(int newMode)
{
  if(newMode == mMode) return;

  kdDebug(5006) << "Setting systray mMode to " << newMode << endl;
  mMode = newMode;

  if(mMode == AlwaysOn)
  {
    kdDebug(5006) << "Initting alwayson mMode" << endl;

    if(isHidden()) show();
  } else
  {
    if(mCount == 0)
    {
      hide();
    }
  }
}

/**
 * Update the count of unread messages.  If there are unread messages,
 * overlay the count on top of a transparent version of the KMail icon.
 * If there is no unread mail, restore the normal KMail icon.
 */
void KMSystemTray::updateCount()
{
  if(mCount != 0)
  {

    int oldPixmapWidth = pixmap()->size().width();
    int oldPixmapHeight = pixmap()->size().height();

    /** Scale the font size down with each increase in the number
     * of digits in the count, to a minimum point size of 6 */
    int numDigits = ((int) log10((double) mCount) + 1);
    QFont countFont = KGlobalSettings::generalFont();
    countFont.setBold(true);

    int countFontSize = countFont.pointSize();
    while(countFontSize > 5)
    {
      QFontMetrics qfm( countFont );
      int fontWidth = qfm.width( QChar( '0' ) );
      if((fontWidth * numDigits) > oldPixmapWidth)
      {
        --countFontSize;
        countFont.setPointSize(countFontSize);
      } else break;
    }

    QPixmap bg(oldPixmapWidth, oldPixmapHeight);
    bg.fill(this, 0, 0);

    /** Overlay count on transparent icon */
    QPainter p(&bg);
    p.drawPixmap(0, 0, mTransparentIcon);
    p.setFont(countFont);
    p.setPen(Qt::blue);
    p.drawText(pixmap()->rect(), Qt::AlignCenter, QString::number(mCount));

    setPixmap(bg);
  } else
  {
    setPixmap(mDefaultIcon);
  }
}

/**
 * Refreshes the list of folders we are monitoring.  This is called on
 * startup and is also connected to the 'changed' signal on the KMFolderMgr.
 */
void KMSystemTray::foldersChanged()
{
  /**
   * Hide and remove all unread mappings to cover the case where the only
   * unread message was in a folder that was just removed.
   */
  mFoldersWithUnread.clear();
  mCount = 0;

  if(mMode == OnNewMail)
  {
    hide();
  }

  /** Disconnect all previous connections */
  disconnect(this, SLOT(updateNewMessageNotification(KMFolder *)));

  QStringList folderNames;
  QValueList<QGuardedPtr<KMFolder> > folderList;
  kernel->folderMgr()->createFolderList(&folderNames, &folderList);
  kernel->imapFolderMgr()->createFolderList(&folderNames, &folderList);
  kernel->searchFolderMgr()->createFolderList(&folderNames, &folderList);

  QStringList::iterator strIt = folderNames.begin();

  for(QValueList<QGuardedPtr<KMFolder> >::iterator it = folderList.begin();
     it != folderList.end() && strIt != folderNames.end(); ++it, ++strIt)
  {
    KMFolder * currentFolder = *it;
    QString currentName = *strIt;

    if((!currentFolder->isSystemFolder() || (currentFolder->name().lower() == "inbox")) ||
       (currentFolder->protocol() == "imap"))
    {
      /** If this is a new folder, start listening for messages */
      connect(currentFolder, SIGNAL(numUnreadMsgsChanged(KMFolder *)),
              this, SLOT(updateNewMessageNotification(KMFolder *)));

      /** Check all new folders to see if we started with any new messages */
      updateNewMessageNotification(currentFolder);
    }
  }
}

/**
 * On left mouse click, switch focus to the first KMMainWidget.  On right
 * click, bring up a list of all folders with a count of unread messages.
 */
void KMSystemTray::mousePressEvent(QMouseEvent *e)
{
  // switch to kmail on left mouse button
  if( e->button() == LeftButton )
  {
    if( mParentVisible )
      hideKMail();
    else
      showKMail();
  }

  // open popup menu on right mouse button
  if( e->button() == RightButton )
  {
    mPopupFolders.clear();
    mPopupFolders.resize(mFoldersWithUnread.count());

    // Rebuild popup menu at click time to minimize race condition if
    // the base KMainWidget is closed.
    buildPopupMenu();

    if(mNewMessagePopupId != -1)
    {
      mPopupMenu->removeItem(mNewMessagePopupId);
    }

    if(mFoldersWithUnread.count() > 0)
    {
      KPopupMenu *newMessagesPopup = new KPopupMenu();

      QMap<QGuardedPtr<KMFolder>, int>::Iterator it = mFoldersWithUnread.begin();
      for(uint i=0; it != mFoldersWithUnread.end(); ++i)
      {
        kdDebug(5006) << "Adding folder" << endl;
        if(i > mPopupFolders.size()) mPopupFolders.resize(i * 2);
        mPopupFolders.insert(i, it.key());
        QString item = prettyName(it.key()) + "(" + QString::number(it.data()) + ")";
        newMessagesPopup->insertItem(item, this, SLOT(selectedAccount(int)), 0, i);
        ++it;
      }

      mNewMessagePopupId = mPopupMenu->insertItem(i18n("New messages in..."),
                                                  newMessagesPopup, mNewMessagePopupId, 3);

      kdDebug(5006) << "Folders added" << endl;
    }

    mPopupMenu->popup(e->globalPos());
  }

}

/**
 * Return the name of the folder in which the mail is deposited, prepended
 * with the account name if the folder is IMAP.
 */
QString KMSystemTray::prettyName(KMFolder * fldr)
{
  QString rvalue = fldr->label();
  if(fldr->protocol() == "imap")
  {
    KMFolderImap * imap = dynamic_cast<KMFolderImap*> (fldr);
    assert(imap);

    if((imap->account() != 0) &&
       (imap->account()->name() != 0) )
    {
      kdDebug(5006) << "IMAP folder, prepend label with type" << endl;
      rvalue = imap->account()->name() + "->" + rvalue;
    }
  }

  kdDebug(5006) << "Got label " << rvalue << endl;

  return rvalue;
}

/**
 * Shows and raises the first KMMainWidget and
 * switches to the appropriate virtual desktop.
 */
void KMSystemTray::showKMail()
{
  if (!getKMMainWidget())
    return;
  QWidget *mainWin = getKMMainWidget()->topLevelWidget();
  assert(mainWin);
  if(mainWin)
  {
    /** Force window to grab focus */
    mainWin->show();
    mainWin->raise();
    mParentVisible = true;
    /** Switch to appropriate desktop */
    int desk = KWin::info(mainWin->winId()).desktop;
    KWin::setCurrentDesktop(desk);
  }
}

void KMSystemTray::hideKMail()
{
  if (!getKMMainWidget())
    return;
  QWidget *mainWin = getKMMainWidget()->topLevelWidget();
  assert(mainWin);
  if(mainWin)
  {
    mainWin->hide();
    mParentVisible = false;
  }
}

/**
 * Grab a pointer to the first KMMainWidget.
 */
KMMainWidget * KMSystemTray::getKMMainWidget()
{
  QWidgetList *l = kapp->topLevelWidgets();
  QWidgetListIt it( *l );
  QWidget *wid;

  while ( (wid = it.current()) != 0 ) {
    ++it;
    QObjectList *l2 = wid->topLevelWidget()->queryList("KMMainWidget");
    if (l2->first())
      return dynamic_cast<KMMainWidget *>(l2->first());
  }
  return 0;
}

/**
 * Called on startup of the KMSystemTray and when the numUnreadMsgsChanged signal
 * is emitted for one of the watched folders.  Shows the system tray icon if there
 * are new messages and the icon was hidden, or hides the system tray icon if there
 * are no more new messages.
 */
void KMSystemTray::updateNewMessageNotification(KMFolder * fldr)
{

  if(!fldr)
  {
    kdDebug(5006) << "Null folder, can't mess with that" << endl;
    return;
  }

  /** The number of unread messages in that folder */
  int unread = fldr->countUnread();

  QMap<QGuardedPtr<KMFolder>, int>::Iterator it =
  mFoldersWithUnread.find(fldr);
  bool unmapped = (it == mFoldersWithUnread.end());

  /** If the folder is not mapped yet, increment count by numUnread
      in folder */
  if(unmapped) mCount += unread;
  /* Otherwise, get the difference between the numUnread in the folder and
   * our last known version, and adjust mCount with that difference */
  else
  {
    int diff = unread - it.data();
    mCount += diff;
  }

  if(unread > 0)
  {
    /** Add folder to our internal store, or update unread count if already mapped */
    mFoldersWithUnread.insert(fldr, unread);
    kdDebug(5006) << "There are now " << mFoldersWithUnread.count() << " folders with unread" << endl;
  }

  /**
   * Look for folder in the list of folders already represented.  If there are
   * unread messages and the system tray icon is hidden, show it.  If there are
   * no unread messages, remove the folder from the mapping.
   */
  if(unmapped)
  {
    /** Spurious notification, ignore */
    if(unread == 0) return;

    /** Make sure the icon will be displayed */
    if(mMode == OnNewMail)
    {
      if(isHidden()) show();
    }

  } else
  {

    if(unread == 0)
    {
      kdDebug(5006) << "Removing folder " << fldr->name() << endl;

      /** Remove the folder from the internal store */
      mFoldersWithUnread.remove(fldr);

      /** if this was the last folder in the dictionary, hide the systemtray icon */
      if(mFoldersWithUnread.count() == 0)
      {
        mPopupFolders.clear();
        disconnect(this, SLOT(selectedAccount(int)));

        mCount = 0;

        if(mMode == OnNewMail)
          hide();
      }
    }
  }

  updateCount();

  /** Update tooltip to reflect count of unread messages */
  QToolTip::add(this, i18n("There is 1 unread message.",
                           "There are %n unread messages.",
                           mCount));
}

/**
 * Called when user selects a folder name from the popup menu.  Shows
 * the first KMMainWin in the memberlist and jumps to the first unread
 * message in the selected folder.
 */
void KMSystemTray::selectedAccount(int id)
{
  showKMail();

  KMMainWidget * mainWidget = getKMMainWidget();
  assert(mainWidget);

  /** Select folder */
  KMFolder * fldr = mPopupFolders.at(id);
  if(!fldr) return;
  KMFolderTree * ft = mainWidget->folderTree();
  if(!ft) return;
  QListViewItem * fldrIdx = ft->indexOfFolder(fldr);
  if(!fldrIdx) return;

  ft->setCurrentItem(fldrIdx);
  ft->selectCurrentFolder();
  mainWidget->folderSelectedUnread(fldr);
}

#include "kmsystemtray.moc"
<commit_msg>*slap*<commit_after>/***************************************************************************
                          kmsystemtray.cpp  -  description
                             -------------------
    begin                : Fri Aug 31 22:38:44 EDT 2001
    copyright            : (C) 2001 by Ryan Breen
    email                : ryan@porivo.com
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 ***************************************************************************/

#ifdef HAVE_CONFIG_H
  #include <config.h>
#endif

#include <kapp.h>
#include <kpopupmenu.h>
#include <kdebug.h>
#include <kiconloader.h>
#include <kwin.h>

#include <qevent.h>
#include <qfontmetrics.h>
#include <qlistview.h>
#include <qstring.h>
#include <qtooltip.h>
#include <qlabel.h>
#include <qwidgetlist.h>

#include <kglobalsettings.h>
#include <kiconeffect.h>
#include <qpainter.h>

#include <math.h>

#include "kaction.h"
#include "kmmainwidget.h"
#include "kmsystemtray.h"
#include "kmfolder.h"
#include "kmfoldertree.h"
#include "kmkernel.h"
#include "kmfoldermgr.h"
#include "kmfolderimap.h"
#include "kmmainwidget.h"

/**
 * Construct a KSystemTray icon to be displayed when new mail
 * has arrived in a non-system folder.  The KMSystemTray listens
 * for updateNewMessageNotification events from each non-system
 * KMFolder and maintains a store of all folders with unread
 * messages.
 *
 * The KMSystemTray also provides a popup menu listing each folder
 * with its count of unread messages, allowing the user to jump
 * to the first unread message in each folder.
 */
KMSystemTray::KMSystemTray(QWidget *parent, const char *name) : KSystemTray(parent, name),
mNewMessagePopupId(-1), mPopupMenu(0)
{
  setAlignment( AlignCenter );
  kdDebug(5006) << "Initting systray" << endl;

  mDefaultIcon = SmallIcon( "kmail" );
  mTransparentIcon = SmallIcon( "kmail" );
  KIconEffect::semiTransparent( mTransparentIcon );

  setPixmap(mDefaultIcon);
  mParentVisible = true;
  mMode = OnNewMail;

  /** Initiate connections between folders and this object */
  foldersChanged();

  connect( kernel->folderMgr(), SIGNAL(changed()), SLOT(foldersChanged()));
  connect( kernel->imapFolderMgr(), SIGNAL(changed()), SLOT(foldersChanged()));
  connect( kernel->searchFolderMgr(), SIGNAL(changed()), SLOT(foldersChanged()));
}

void KMSystemTray::buildPopupMenu()
{
  // Delete any previously created popup menu
  delete mPopupMenu;
  mPopupMenu = 0;

  mPopupMenu = new KPopupMenu();
  if (!getKMMainWidget())
    return;

  mPopupMenu->insertTitle(*(this->pixmap()), "KMail");
  getKMMainWidget()->action("check_mail")->plug(mPopupMenu);
  getKMMainWidget()->action("check_mail_in")->plug(mPopupMenu);
  mPopupMenu->insertSeparator();
  getKMMainWidget()->action("new_message")->plug(mPopupMenu);
  getKMMainWidget()->action("kmail_configure_kmail")->plug(mPopupMenu);
  mPopupMenu->insertSeparator();
  if (getKMMainWidget()->action("file_quit"))
    getKMMainWidget()->action("file_quit")->plug(mPopupMenu);
}

KMSystemTray::~KMSystemTray()
{
  delete mPopupMenu;
  mPopupMenu = 0;
}

void KMSystemTray::setMode(int newMode)
{
  if(newMode == mMode) return;

  kdDebug(5006) << "Setting systray mMode to " << newMode << endl;
  mMode = newMode;

  if(mMode == AlwaysOn)
  {
    kdDebug(5006) << "Initting alwayson mMode" << endl;

    if(isHidden()) show();
  } else
  {
    if(mCount == 0)
    {
      hide();
    }
  }
}

/**
 * Update the count of unread messages.  If there are unread messages,
 * overlay the count on top of a transparent version of the KMail icon.
 * If there is no unread mail, restore the normal KMail icon.
 */
void KMSystemTray::updateCount()
{
  if(mCount != 0)
  {

    int oldPixmapWidth = pixmap()->size().width();
    int oldPixmapHeight = pixmap()->size().height();

    /** Scale the font size down with each increase in the number
     * of digits in the count, to a minimum point size of 6 */
    int numDigits = ((int) log10((double) mCount) + 1);
    QFont countFont = KGlobalSettings::generalFont();
    countFont.setBold(true);

    int countFontSize = countFont.pointSize();
    while(countFontSize > 5)
    {
      QFontMetrics qfm( countFont );
      int fontWidth = qfm.width( QChar( '0' ) );
      if((fontWidth * numDigits) > oldPixmapWidth)
      {
        --countFontSize;
        countFont.setPointSize(countFontSize);
      } else break;
    }

    QPixmap bg(oldPixmapWidth, oldPixmapHeight);
    bg.fill(this, 0, 0);

    /** Overlay count on transparent icon */
    QPainter p(&bg);
    p.drawPixmap(0, 0, mTransparentIcon);
    p.setFont(countFont);
    p.setPen(Qt::blue);
    p.drawText(pixmap()->rect(), Qt::AlignCenter, QString::number(mCount));

    setPixmap(bg);
  } else
  {
    setPixmap(mDefaultIcon);
  }
}

/**
 * Refreshes the list of folders we are monitoring.  This is called on
 * startup and is also connected to the 'changed' signal on the KMFolderMgr.
 */
void KMSystemTray::foldersChanged()
{
  /**
   * Hide and remove all unread mappings to cover the case where the only
   * unread message was in a folder that was just removed.
   */
  mFoldersWithUnread.clear();
  mCount = 0;

  if(mMode == OnNewMail)
  {
    hide();
  }

  /** Disconnect all previous connections */
  disconnect(this, SLOT(updateNewMessageNotification(KMFolder *)));

  QStringList folderNames;
  QValueList<QGuardedPtr<KMFolder> > folderList;
  kernel->folderMgr()->createFolderList(&folderNames, &folderList);
  kernel->imapFolderMgr()->createFolderList(&folderNames, &folderList);
  kernel->searchFolderMgr()->createFolderList(&folderNames, &folderList);

  QStringList::iterator strIt = folderNames.begin();

  for(QValueList<QGuardedPtr<KMFolder> >::iterator it = folderList.begin();
     it != folderList.end() && strIt != folderNames.end(); ++it, ++strIt)
  {
    KMFolder * currentFolder = *it;
    QString currentName = *strIt;

    if((!currentFolder->isSystemFolder() || (currentFolder->name().lower() == "inbox")) ||
       (currentFolder->protocol() == "imap"))
    {
      /** If this is a new folder, start listening for messages */
      connect(currentFolder, SIGNAL(numUnreadMsgsChanged(KMFolder *)),
              this, SLOT(updateNewMessageNotification(KMFolder *)));

      /** Check all new folders to see if we started with any new messages */
      updateNewMessageNotification(currentFolder);
    }
  }
}

/**
 * On left mouse click, switch focus to the first KMMainWidget.  On right
 * click, bring up a list of all folders with a count of unread messages.
 */
void KMSystemTray::mousePressEvent(QMouseEvent *e)
{
  // switch to kmail on left mouse button
  if( e->button() == LeftButton )
  {
    if( mParentVisible )
      hideKMail();
    else
      showKMail();
  }

  // open popup menu on right mouse button
  if( e->button() == RightButton )
  {
    mPopupFolders.clear();
    mPopupFolders.resize(mFoldersWithUnread.count());

    // Rebuild popup menu at click time to minimize race condition if
    // the base KMainWidget is closed.
    buildPopupMenu();

    if(mNewMessagePopupId != -1)
    {
      mPopupMenu->removeItem(mNewMessagePopupId);
    }

    if(mFoldersWithUnread.count() > 0)
    {
      KPopupMenu *newMessagesPopup = new KPopupMenu();

      QMap<QGuardedPtr<KMFolder>, int>::Iterator it = mFoldersWithUnread.begin();
      for(uint i=0; it != mFoldersWithUnread.end(); ++i)
      {
        kdDebug(5006) << "Adding folder" << endl;
        if(i > mPopupFolders.size()) mPopupFolders.resize(i * 2);
        mPopupFolders.insert(i, it.key());
        QString item = prettyName(it.key()) + "(" + QString::number(it.data()) + ")";
        newMessagesPopup->insertItem(item, this, SLOT(selectedAccount(int)), 0, i);
        ++it;
      }

      mNewMessagePopupId = mPopupMenu->insertItem(i18n("New messages in..."),
                                                  newMessagesPopup, mNewMessagePopupId, 3);

      kdDebug(5006) << "Folders added" << endl;
    }

    mPopupMenu->popup(e->globalPos());
  }

}

/**
 * Return the name of the folder in which the mail is deposited, prepended
 * with the account name if the folder is IMAP.
 */
QString KMSystemTray::prettyName(KMFolder * fldr)
{
  QString rvalue = fldr->label();
  if(fldr->protocol() == "imap")
  {
    KMFolderImap * imap = dynamic_cast<KMFolderImap*> (fldr);
    assert(imap);

    if((imap->account() != 0) &&
       (imap->account()->name() != 0) )
    {
      kdDebug(5006) << "IMAP folder, prepend label with type" << endl;
      rvalue = imap->account()->name() + "->" + rvalue;
    }
  }

  kdDebug(5006) << "Got label " << rvalue << endl;

  return rvalue;
}

/**
 * Shows and raises the first KMMainWidget and
 * switches to the appropriate virtual desktop.
 */
void KMSystemTray::showKMail()
{
  if (!getKMMainWidget())
    return;
  QWidget *mainWin = getKMMainWidget()->topLevelWidget();
  assert(mainWin);
  if(mainWin)
  {
    /** Force window to grab focus */
    mainWin->show();
    mainWin->raise();
    mParentVisible = true;
    /** Switch to appropriate desktop */
    int desk = KWin::info(mainWin->winId()).desktop;
    KWin::setCurrentDesktop(desk);
  }
}

void KMSystemTray::hideKMail()
{
  if (!getKMMainWidget())
    return;
  QWidget *mainWin = getKMMainWidget()->topLevelWidget();
  assert(mainWin);
  if(mainWin)
  {
    mainWin->hide();
    mParentVisible = false;
  }
}

/**
 * Grab a pointer to the first KMMainWidget.
 */
KMMainWidget * KMSystemTray::getKMMainWidget()
{
  QWidgetList *l = kapp->topLevelWidgets();
  QWidgetListIt it( *l );
  QWidget *wid;

  while ( (wid = it.current()) != 0 ) {
    ++it;
    QObjectList *l2 = wid->topLevelWidget()->queryList("KMMainWidget");
    if (l2->first())
      return dynamic_cast<KMMainWidget *>(l2->first());
  }
  return 0;
}

/**
 * Called on startup of the KMSystemTray and when the numUnreadMsgsChanged signal
 * is emitted for one of the watched folders.  Shows the system tray icon if there
 * are new messages and the icon was hidden, or hides the system tray icon if there
 * are no more new messages.
 */
void KMSystemTray::updateNewMessageNotification(KMFolder * fldr)
{

  if(!fldr)
  {
    kdDebug(5006) << "Null folder, can't mess with that" << endl;
    return;
  }

  /** The number of unread messages in that folder */
  int unread = fldr->countUnread();

  QMap<QGuardedPtr<KMFolder>, int>::Iterator it =
  mFoldersWithUnread.find(fldr);
  bool unmapped = (it == mFoldersWithUnread.end());

  /** If the folder is not mapped yet, increment count by numUnread
      in folder */
  if(unmapped) mCount += unread;
  /* Otherwise, get the difference between the numUnread in the folder and
   * our last known version, and adjust mCount with that difference */
  else
  {
    int diff = unread - it.data();
    mCount += diff;
  }

  if(unread > 0)
  {
    /** Add folder to our internal store, or update unread count if already mapped */
    mFoldersWithUnread.insert(fldr, unread);
    kdDebug(5006) << "There are now " << mFoldersWithUnread.count() << " folders with unread" << endl;
  }

  /**
   * Look for folder in the list of folders already represented.  If there are
   * unread messages and the system tray icon is hidden, show it.  If there are
   * no unread messages, remove the folder from the mapping.
   */
  if(unmapped)
  {
    /** Spurious notification, ignore */
    if(unread == 0) return;

    /** Make sure the icon will be displayed */
    if(mMode == OnNewMail)
    {
      if(isHidden()) show();
    }

  } else
  {

    if(unread == 0)
    {
      kdDebug(5006) << "Removing folder " << fldr->name() << endl;

      /** Remove the folder from the internal store */
      mFoldersWithUnread.remove(fldr);

      /** if this was the last folder in the dictionary, hide the systemtray icon */
      if(mFoldersWithUnread.count() == 0)
      {
        mPopupFolders.clear();
        disconnect(this, SLOT(selectedAccount(int)));

        mCount = 0;

        if(mMode == OnNewMail)
          hide();
      }
    }
  }

  updateCount();

  /** Update tooltip to reflect count of unread messages */
  QToolTip::add(this, i18n("There is 1 unread message.",
                           "There are %n unread messages.",
                           mCount));
}

/**
 * Called when user selects a folder name from the popup menu.  Shows
 * the first KMMainWin in the memberlist and jumps to the first unread
 * message in the selected folder.
 */
void KMSystemTray::selectedAccount(int id)
{
  showKMail();

  KMMainWidget * mainWidget = getKMMainWidget();
  assert(mainWidget);

  /** Select folder */
  KMFolder * fldr = mPopupFolders.at(id);
  if(!fldr) return;
  KMFolderTree * ft = mainWidget->folderTree();
  if(!ft) return;
  QListViewItem * fldrIdx = ft->indexOfFolder(fldr);
  if(!fldrIdx) return;

  ft->setCurrentItem(fldrIdx);
  ft->selectCurrentFolder();
  mainWidget->folderSelectedUnread(fldr);
}

#include "kmsystemtray.moc"
<|endoftext|>


<commit_before>#ifndef MATH_HPP
#define MATH_HPP

#include <algorithm>
#include <cmath>
#include <math.h>

#define PI 3.14159265358979323846f
#define TAU 6.28318530717958647692f
#define KAPPA90 0.5522847493f

// KAPPA90 = (4/3 * (sqrt(2) - 1))
// https://de.wikipedia.org/wiki/Datei:Circle-cbezier-method2.svg

#define OE_FLOAT_INFINITY std::numeric_limits<float>::infinity()

#define ARRAY_COUNT(arr) (sizeof(arr) / sizeof(0[arr]))

namespace OrbitEngine { namespace Math {

	inline double deg2rad(double deg)
	{
		return deg * PI / 180.f;
	}

	inline double rad2deg(double rad)
	{
		return rad * 180.f / PI;
	}

	inline unsigned int nextPowerOfTwo(unsigned int x)
	{
		x--;
		x |= x >> 1;
		x |= x >> 2;
		x |= x >> 4;
		x |= x >> 8;
		x |= x >> 16;
		x++;
		return x;
	}

	inline bool isPowerOfTwo(unsigned int x) {
		return x == 0 || (x & (x - 1)) == 0;
	}

	template<typename T>
	inline T approximatelyEqual(T a, T b, T epsilon)
	{
		return std::abs(a - b) <= ((std::abs(a) < std::abs(b) ? std::abs(b) : std::abs(a)) * epsilon);
	}

	template<typename T>
	inline T lerp(T v0, T v1, float t) {
		return v0 + (v1 - v0) * t;
	}

	template<typename T>
	inline T clamp(T input, T min_, T max_) {
		return (std::max)(min_, (std::min)(input, max_));
	}

	template<typename T>
	inline T sign(T input)
	{
		return input >= (T)0 ? (T)1 : (T)-1;
	};

	template<typename A, typename B>
	A castMinMax(B in)
	{
		// only integer types

		A retVal = 0;

		if (in > 0)
			retVal = static_cast<A>(in & std::numeric_limits<A>::max());
		else if (in < 0)
			retVal = static_cast<A>(in | std::numeric_limits<A>::min());

		return retVal;
	}
} }

#endif<commit_msg>Math doc<commit_after>#ifndef MATH_HPP
#define MATH_HPP

#include <algorithm>
#include <cmath>
#include <math.h>

/// PI value
#define PI 3.14159265358979323846f
/// TAU value (2 * PI)
#define TAU 6.28318530717958647692f
/// @brief KAPPA90 = (4/3 * (sqrt(2) - 1))
/// @see https://de.wikipedia.org/wiki/Datei:Circle-cbezier-method2.svg
#define KAPPA90 0.5522847493f

/// Max float value
#define OE_FLOAT_INFINITY std::numeric_limits<float>::infinity()

/// Count the number of elements in a C array
#define ARRAY_COUNT(arr) (sizeof(arr) / sizeof(0[arr]))

namespace OrbitEngine { namespace Math {

	/// Convert degrees to radians
	inline double deg2rad(double deg)
	{
		return deg * PI / 180.f;
	}

	/// Convert radians to degrees
	inline double rad2deg(double rad)
	{
		return rad * 180.f / PI;
	}

	/// Get the next power of two
	inline unsigned int nextPowerOfTwo(unsigned int x)
	{
		x--;
		x |= x >> 1;
		x |= x >> 2;
		x |= x >> 4;
		x |= x >> 8;
		x |= x >> 16;
		x++;
		return x;
	}

	/// Check if a number is a power of two
	inline bool isPowerOfTwo(unsigned int x) {
		return x == 0 || (x & (x - 1)) == 0;
	}

	/// Check if two values are 'equal' by at most \p epsilon
	template<typename T>
	inline T approximatelyEqual(T a, T b, T epsilon)
	{
		return std::abs(a - b) <= ((std::abs(a) < std::abs(b) ? std::abs(b) : std::abs(a)) * epsilon);
	}

	/// Linearly interpolates two elements
	template<typename T>
	inline T lerp(T v0, T v1, float t) {
		return v0 + (v1 - v0) * t;
	}

	/// Constrain a value between two others
	template<typename T>
	inline T clamp(T input, T min_, T max_) {
		return (std::max)(min_, (std::min)(input, max_));
	}

	/// Extract the sign of the parameter
	template<typename T>
	inline T sign(T input)
	{
		return input >= (T)0 ? (T)1 : (T)-1;
	};

	template<typename A, typename B>
	A castMinMax(B in)
	{
		// only integer types

		A retVal = 0;

		if (in > 0)
			retVal = static_cast<A>(in & std::numeric_limits<A>::max());
		else if (in < 0)
			retVal = static_cast<A>(in | std::numeric_limits<A>::min());

		return retVal;
	}
} }

#endif<|endoftext|>


<commit_before>
#include "../Flare.h"
#include "FlareFleet.h"
#include "FlareCompany.h"
#include "FlareGame.h"
#include "../Player/FlarePlayerController.h"
#include "FlareSimulatedSector.h"

#define LOCTEXT_NAMESPACE "FlareFleet"

/*----------------------------------------------------
	Constructor
----------------------------------------------------*/

UFlareFleet::UFlareFleet(const FObjectInitializer& ObjectInitializer)
	: Super(ObjectInitializer)
{
}

void UFlareFleet::Load(const FFlareFleetSave& Data)
{
	FleetCompany = Cast<UFlareCompany>(GetOuter());
	Game = FleetCompany->GetGame();
	FleetData = Data;
	IsShipListLoaded = false;
}

FFlareFleetSave* UFlareFleet::Save()
{
	return &FleetData;
}

/*----------------------------------------------------
	Gameplay
----------------------------------------------------*/

FText UFlareFleet::GetName()
{
	if (GetShips().Num() > 0)
	{
		return FText::FromString(GetShips()[0]->GetImmatriculation().ToString());// TODO Clean with GetFleetName
	}
	else
	{
		return GetFleetName();
	}
}

FText  UFlareFleet::GetFleetName() const
{
	if(Game->GetPC()->GetPlayerFleet() == this)
	{
		return LOCTEXT("PlayerFleetName", "Player Fleet");
	}

	return FleetData.Name;
}

bool UFlareFleet::IsTraveling() const
{
	return CurrentTravel != NULL;
}

bool UFlareFleet::IsTrading() const
{
	return GetTradingShipCount() > 0;
}

bool UFlareFleet::CanTravel()
{
	if (IsTraveling() && !GetCurrentTravel()->CanChangeDestination())
	{
		return false;
	}

	if (GetImmobilizedShipCount() == FleetShips.Num())
	{
		// All ship are immobilized
		return false;
	}

	return true;
}

bool UFlareFleet::CanTravel(FText& OutInfo)
{
	if (IsTraveling() && !GetCurrentTravel()->CanChangeDestination())
	{
		OutInfo = LOCTEXT("TravellingFormat", "Can't change destination");
		return false;
	}

	if (GetImmobilizedShipCount() == FleetShips.Num())
	{
		OutInfo = LOCTEXT("Travelling", "Some ships are trading or stranded");
		return false;
	}

	return true;
}

uint32 UFlareFleet::GetImmobilizedShipCount()
{
	uint32 ImmobilizedShip = 0;

	for (int ShipIndex = 0; ShipIndex < FleetShips.Num(); ShipIndex++)
	{
		if (!FleetShips[ShipIndex]->CanTravel())
		{
			ImmobilizedShip++;
		}
	}
	return ImmobilizedShip;
}

uint32 UFlareFleet::GetTradingShipCount() const
{
	uint32 TradingShip = 0;

	for (int ShipIndex = 0; ShipIndex < FleetShips.Num(); ShipIndex++)
	{
		if (FleetShips[ShipIndex]->IsTrading())
		{
			TradingShip++;
		}
	}
	return TradingShip;
}

uint32 UFlareFleet::GetShipCount() const
{
	return FleetShips.Num();
}

uint32 UFlareFleet::GetMaxShipCount()
{
	return 20;
}

FText UFlareFleet::GetStatusInfo() const
{
	if (IsTraveling())
	{
		int64 RemainingDuration = CurrentTravel->GetRemainingTravelDuration();
		return FText::Format(LOCTEXT("TravelTextFormat", "Travelling to {0} ({1} left)"),
			CurrentTravel->GetDestinationSector()->GetSectorName(),
			FText::FromString(*UFlareGameTools::FormatDate(RemainingDuration, 1))); //FString needed here
	}
	else if (IsTrading())
	{
		if(GetTradingShipCount() == GetShipCount())
		{
			return FText::Format(LOCTEXT("FleetTrading", "Trading in {0}"), GetCurrentSector()->GetSectorName());
		}
		else
		{
			return FText::Format(LOCTEXT("FleetPartialTrading", "{0} of {1} ships are trading in {2}"), FText::AsNumber(GetTradingShipCount()), FText::AsNumber(GetShipCount()), GetCurrentSector()->GetSectorName());
		}
	}
	else
	{
		return FText::Format(LOCTEXT("FleetIdle", "Idle in {0}"), GetCurrentSector()->GetSectorName());
	}

	return FText();
}

int32 UFlareFleet::GetFleetCapacity() const
{
	int32 FreeCargoSpace = 0;

	for (int ShipIndex = 0; ShipIndex < FleetShips.Num(); ShipIndex++)
	{
		FreeCargoSpace += FleetShips[ShipIndex]->GetCargoBay()->GetCapacity();
	}
	return FreeCargoSpace;
}

int32 UFlareFleet::GetFleetFreeCargoSpace() const
{
	int32 FreeCargoSpace = 0;

	for (int ShipIndex = 0; ShipIndex < FleetShips.Num(); ShipIndex++)
	{
		FreeCargoSpace += FleetShips[ShipIndex]->GetCargoBay()->GetFreeCargoSpace();
	}
	return FreeCargoSpace;
}

void UFlareFleet::RemoveImmobilizedShips()
{
	TArray<UFlareSimulatedSpacecraft*> ShipToRemove;

	for (int ShipIndex = 0; ShipIndex < FleetShips.Num(); ShipIndex++)
	{
		if (!FleetShips[ShipIndex]->CanTravel())
		{
			ShipToRemove.Add(FleetShips[ShipIndex]);
		}
	}

	for (int ShipIndex = 0; ShipIndex < ShipToRemove.Num(); ShipIndex++)
	{
		RemoveShip(ShipToRemove[ShipIndex]);
	}
}

void UFlareFleet::AddShip(UFlareSimulatedSpacecraft* Ship)
{
	if (IsTraveling())
	{
		FLOGV("Fleet Disband fail: '%s' is travelling", *GetFleetName().ToString());
		return;
	}

	if (GetCurrentSector() != Ship->GetCurrentSector())
	{
		FLOGV("Fleet Merge fail: '%s' is the sector '%s' but '%s' is the sector '%s'",
			  *GetFleetName().ToString(),
			  *GetCurrentSector()->GetSectorName().ToString(),
			  *Ship->GetImmatriculation().ToString(),
			  *Ship->GetCurrentSector()->GetSectorName().ToString());
		return;
	}

	UFlareFleet* OldFleet = Ship->GetCurrentFleet();
	if (OldFleet)
	{
		OldFleet->RemoveShip(Ship);
	}

	FleetData.ShipImmatriculations.Add(Ship->GetImmatriculation());
	FleetShips.AddUnique(Ship);
	Ship->SetCurrentFleet(this);
}

void UFlareFleet::RemoveShip(UFlareSimulatedSpacecraft* Ship, bool destroyed)
{
	if (IsTraveling())
	{
		FLOGV("Fleet RemoveShip fail: '%s' is travelling", *GetFleetName().ToString());
		return;
	}

	FleetData.ShipImmatriculations.Remove(Ship->GetImmatriculation());
	FleetShips.Remove(Ship);
	Ship->SetCurrentFleet(NULL);

	if (!destroyed)
	{
		Ship->GetCompany()->CreateAutomaticFleet(Ship);
	}

	if(FleetShips.Num() == 0)
	{
		Disband();
	}
}

/** Remove all ship from the fleet and delete it. Not possible during travel */
void UFlareFleet::Disband()
{
	if (IsTraveling())
	{
		FLOGV("Fleet Disband fail: '%s' is travelling", *GetFleetName().ToString());
		return;
	}

	for (int ShipIndex = 0; ShipIndex < FleetShips.Num(); ShipIndex++)
	{
		FleetShips[ShipIndex]->SetCurrentFleet(NULL);
	}

	if (GetCurrentTradeRoute())
	{
		GetCurrentTradeRoute()->RemoveFleet(this);
	}
	GetCurrentSector()->DisbandFleet(this);
	FleetCompany->RemoveFleet(this);
}

bool UFlareFleet::CanMerge(UFlareFleet* Fleet, FText& OutInfo)
{
	if (GetShipCount() + Fleet->GetShipCount() > GetMaxShipCount())
	{
		OutInfo = LOCTEXT("MergeFleetMaxShips", "Can't add, max ships reached");
		return false;
	}

	if (IsTraveling())
	{
		OutInfo = LOCTEXT("MergeFleetTravel", "Can't add during travel");
		return false;
	}

	if (Fleet->IsTraveling())
	{
		OutInfo = LOCTEXT("MergeOtherFleetTravel", "Can't add travelling ships");
		return false;
	}

	if (GetCurrentSector() != Fleet->GetCurrentSector())
	{
		OutInfo = LOCTEXT("MergeFleetDifferenSector", "Can't add from a different sector");
		return false;
	}

	return true;
}

void UFlareFleet::Merge(UFlareFleet* Fleet)
{
	FText Unused;
	if (!CanMerge(Fleet, Unused))
	{
		FLOGV("Fleet Merge fail: '%s' is not mergeable", *Fleet->GetFleetName().ToString());
		return;
	}

	TArray<UFlareSimulatedSpacecraft*> Ships = Fleet->GetShips();
	Fleet->Disband();
	for (int ShipIndex = 0; ShipIndex < Ships.Num(); ShipIndex++)
	{
		AddShip(Ships[ShipIndex]);
	}
}

void UFlareFleet::SetCurrentSector(UFlareSimulatedSector* Sector)
{
	CurrentSector = Sector;
	if(!Sector->IsTravelSector())
	{
		CurrentTravel = NULL;
	}
	InitShipList();
}

void UFlareFleet::SetCurrentTravel(UFlareTravel* Travel)
{
	CurrentSector = Travel->GetTravelSector();
	CurrentTravel = Travel;
	InitShipList();
	for (int ShipIndex = 0; ShipIndex < FleetShips.Num(); ShipIndex++)
	{
		FleetShips[ShipIndex]->SetSpawnMode(EFlareSpawnMode::Travel);
	}
}

void UFlareFleet::InitShipList()
{
	if (!IsShipListLoaded)
	{
		IsShipListLoaded = true;
		FleetShips.Empty();
		for (int ShipIndex = 0; ShipIndex < FleetData.ShipImmatriculations.Num(); ShipIndex++)
		{
			UFlareSimulatedSpacecraft* Ship = FleetCompany->FindSpacecraft(FleetData.ShipImmatriculations[ShipIndex]);
			if (!Ship)
			{
				FLOGV("WARNING: Fail to find ship with id %s in company %s for fleet %s (%d ships)",
						*FleetData.ShipImmatriculations[ShipIndex].ToString(),
						*FleetCompany->GetCompanyName().ToString(),
						*GetFleetName().ToString(),
						FleetData.ShipImmatriculations.Num());
				continue;
			}
			Ship->SetCurrentFleet(this);
			FleetShips.Add(Ship);
		}
	}
}

/*----------------------------------------------------
	Getters
----------------------------------------------------*/

TArray<UFlareSimulatedSpacecraft*>& UFlareFleet::GetShips()
{
	InitShipList();

	return FleetShips;
}

#undef LOCTEXT_NAMESPACE
<commit_msg>#527 Forgid travel if player ship is stranded<commit_after>
#include "../Flare.h"
#include "FlareFleet.h"
#include "FlareCompany.h"
#include "FlareGame.h"
#include "../Player/FlarePlayerController.h"
#include "FlareSimulatedSector.h"

#define LOCTEXT_NAMESPACE "FlareFleet"

/*----------------------------------------------------
	Constructor
----------------------------------------------------*/

UFlareFleet::UFlareFleet(const FObjectInitializer& ObjectInitializer)
	: Super(ObjectInitializer)
{
}

void UFlareFleet::Load(const FFlareFleetSave& Data)
{
	FleetCompany = Cast<UFlareCompany>(GetOuter());
	Game = FleetCompany->GetGame();
	FleetData = Data;
	IsShipListLoaded = false;
}

FFlareFleetSave* UFlareFleet::Save()
{
	return &FleetData;
}

/*----------------------------------------------------
	Gameplay
----------------------------------------------------*/

FText UFlareFleet::GetName()
{
	if (GetShips().Num() > 0)
	{
		return FText::FromString(GetShips()[0]->GetImmatriculation().ToString());// TODO Clean with GetFleetName
	}
	else
	{
		return GetFleetName();
	}
}

FText  UFlareFleet::GetFleetName() const
{
	if(Game->GetPC()->GetPlayerFleet() == this)
	{
		return LOCTEXT("PlayerFleetName", "Player Fleet");
	}

	return FleetData.Name;
}

bool UFlareFleet::IsTraveling() const
{
	return CurrentTravel != NULL;
}

bool UFlareFleet::IsTrading() const
{
	return GetTradingShipCount() > 0;
}

bool UFlareFleet::CanTravel()
{
	if (IsTraveling() && !GetCurrentTravel()->CanChangeDestination())
	{
		return false;
	}

	if (GetImmobilizedShipCount() == FleetShips.Num())
	{
		// All ship are immobilized
		return false;
	}

	if(Game->GetPC()->GetPlayerFleet() == this && Game->GetPC()->GetPlayerShip()->GetDamageSystem()->IsStranded())
	{
		// The player ship is stranded
		return false;
	}

	return true;
}

bool UFlareFleet::CanTravel(FText& OutInfo)
{
	if (IsTraveling() && !GetCurrentTravel()->CanChangeDestination())
	{
		OutInfo = LOCTEXT("TravellingFormat", "Can't change destination");
		return false;
	}

	if (GetImmobilizedShipCount() == FleetShips.Num())
	{
		OutInfo = LOCTEXT("Travelling", "Some ships are trading or stranded");
		return false;
	}

	if(Game->GetPC()->GetPlayerFleet() == this && Game->GetPC()->GetPlayerShip()->GetDamageSystem()->IsStranded())
	{
		OutInfo = LOCTEXT("PlayerShipStranded", "The ship you are piloting is stranded");
		return false;
	}

	return true;
}

uint32 UFlareFleet::GetImmobilizedShipCount()
{
	uint32 ImmobilizedShip = 0;

	for (int ShipIndex = 0; ShipIndex < FleetShips.Num(); ShipIndex++)
	{
		if (!FleetShips[ShipIndex]->CanTravel())
		{
			ImmobilizedShip++;
		}
	}
	return ImmobilizedShip;
}

uint32 UFlareFleet::GetTradingShipCount() const
{
	uint32 TradingShip = 0;

	for (int ShipIndex = 0; ShipIndex < FleetShips.Num(); ShipIndex++)
	{
		if (FleetShips[ShipIndex]->IsTrading())
		{
			TradingShip++;
		}
	}
	return TradingShip;
}

uint32 UFlareFleet::GetShipCount() const
{
	return FleetShips.Num();
}

uint32 UFlareFleet::GetMaxShipCount()
{
	return 20;
}

FText UFlareFleet::GetStatusInfo() const
{
	if (IsTraveling())
	{
		int64 RemainingDuration = CurrentTravel->GetRemainingTravelDuration();
		return FText::Format(LOCTEXT("TravelTextFormat", "Travelling to {0} ({1} left)"),
			CurrentTravel->GetDestinationSector()->GetSectorName(),
			FText::FromString(*UFlareGameTools::FormatDate(RemainingDuration, 1))); //FString needed here
	}
	else if (IsTrading())
	{
		if(GetTradingShipCount() == GetShipCount())
		{
			return FText::Format(LOCTEXT("FleetTrading", "Trading in {0}"), GetCurrentSector()->GetSectorName());
		}
		else
		{
			return FText::Format(LOCTEXT("FleetPartialTrading", "{0} of {1} ships are trading in {2}"), FText::AsNumber(GetTradingShipCount()), FText::AsNumber(GetShipCount()), GetCurrentSector()->GetSectorName());
		}
	}
	else
	{
		return FText::Format(LOCTEXT("FleetIdle", "Idle in {0}"), GetCurrentSector()->GetSectorName());
	}

	return FText();
}

int32 UFlareFleet::GetFleetCapacity() const
{
	int32 FreeCargoSpace = 0;

	for (int ShipIndex = 0; ShipIndex < FleetShips.Num(); ShipIndex++)
	{
		FreeCargoSpace += FleetShips[ShipIndex]->GetCargoBay()->GetCapacity();
	}
	return FreeCargoSpace;
}

int32 UFlareFleet::GetFleetFreeCargoSpace() const
{
	int32 FreeCargoSpace = 0;

	for (int ShipIndex = 0; ShipIndex < FleetShips.Num(); ShipIndex++)
	{
		FreeCargoSpace += FleetShips[ShipIndex]->GetCargoBay()->GetFreeCargoSpace();
	}
	return FreeCargoSpace;
}

void UFlareFleet::RemoveImmobilizedShips()
{
	TArray<UFlareSimulatedSpacecraft*> ShipToRemove;

	for (int ShipIndex = 0; ShipIndex < FleetShips.Num(); ShipIndex++)
	{
		if (!FleetShips[ShipIndex]->CanTravel())
		{
			ShipToRemove.Add(FleetShips[ShipIndex]);
		}
	}

	for (int ShipIndex = 0; ShipIndex < ShipToRemove.Num(); ShipIndex++)
	{
		RemoveShip(ShipToRemove[ShipIndex]);
	}
}

void UFlareFleet::AddShip(UFlareSimulatedSpacecraft* Ship)
{
	if (IsTraveling())
	{
		FLOGV("Fleet Disband fail: '%s' is travelling", *GetFleetName().ToString());
		return;
	}

	if (GetCurrentSector() != Ship->GetCurrentSector())
	{
		FLOGV("Fleet Merge fail: '%s' is the sector '%s' but '%s' is the sector '%s'",
			  *GetFleetName().ToString(),
			  *GetCurrentSector()->GetSectorName().ToString(),
			  *Ship->GetImmatriculation().ToString(),
			  *Ship->GetCurrentSector()->GetSectorName().ToString());
		return;
	}

	UFlareFleet* OldFleet = Ship->GetCurrentFleet();
	if (OldFleet)
	{
		OldFleet->RemoveShip(Ship);
	}

	FleetData.ShipImmatriculations.Add(Ship->GetImmatriculation());
	FleetShips.AddUnique(Ship);
	Ship->SetCurrentFleet(this);
}

void UFlareFleet::RemoveShip(UFlareSimulatedSpacecraft* Ship, bool destroyed)
{
	if (IsTraveling())
	{
		FLOGV("Fleet RemoveShip fail: '%s' is travelling", *GetFleetName().ToString());
		return;
	}

	FleetData.ShipImmatriculations.Remove(Ship->GetImmatriculation());
	FleetShips.Remove(Ship);
	Ship->SetCurrentFleet(NULL);

	if (!destroyed)
	{
		Ship->GetCompany()->CreateAutomaticFleet(Ship);
	}

	if(FleetShips.Num() == 0)
	{
		Disband();
	}
}

/** Remove all ship from the fleet and delete it. Not possible during travel */
void UFlareFleet::Disband()
{
	if (IsTraveling())
	{
		FLOGV("Fleet Disband fail: '%s' is travelling", *GetFleetName().ToString());
		return;
	}

	for (int ShipIndex = 0; ShipIndex < FleetShips.Num(); ShipIndex++)
	{
		FleetShips[ShipIndex]->SetCurrentFleet(NULL);
	}

	if (GetCurrentTradeRoute())
	{
		GetCurrentTradeRoute()->RemoveFleet(this);
	}
	GetCurrentSector()->DisbandFleet(this);
	FleetCompany->RemoveFleet(this);
}

bool UFlareFleet::CanMerge(UFlareFleet* Fleet, FText& OutInfo)
{
	if (GetShipCount() + Fleet->GetShipCount() > GetMaxShipCount())
	{
		OutInfo = LOCTEXT("MergeFleetMaxShips", "Can't add, max ships reached");
		return false;
	}

	if (IsTraveling())
	{
		OutInfo = LOCTEXT("MergeFleetTravel", "Can't add during travel");
		return false;
	}

	if (Fleet->IsTraveling())
	{
		OutInfo = LOCTEXT("MergeOtherFleetTravel", "Can't add travelling ships");
		return false;
	}

	if (GetCurrentSector() != Fleet->GetCurrentSector())
	{
		OutInfo = LOCTEXT("MergeFleetDifferenSector", "Can't add from a different sector");
		return false;
	}

	return true;
}

void UFlareFleet::Merge(UFlareFleet* Fleet)
{
	FText Unused;
	if (!CanMerge(Fleet, Unused))
	{
		FLOGV("Fleet Merge fail: '%s' is not mergeable", *Fleet->GetFleetName().ToString());
		return;
	}

	TArray<UFlareSimulatedSpacecraft*> Ships = Fleet->GetShips();
	Fleet->Disband();
	for (int ShipIndex = 0; ShipIndex < Ships.Num(); ShipIndex++)
	{
		AddShip(Ships[ShipIndex]);
	}
}

void UFlareFleet::SetCurrentSector(UFlareSimulatedSector* Sector)
{
	CurrentSector = Sector;
	if(!Sector->IsTravelSector())
	{
		CurrentTravel = NULL;
	}
	InitShipList();
}

void UFlareFleet::SetCurrentTravel(UFlareTravel* Travel)
{
	CurrentSector = Travel->GetTravelSector();
	CurrentTravel = Travel;
	InitShipList();
	for (int ShipIndex = 0; ShipIndex < FleetShips.Num(); ShipIndex++)
	{
		FleetShips[ShipIndex]->SetSpawnMode(EFlareSpawnMode::Travel);
	}
}

void UFlareFleet::InitShipList()
{
	if (!IsShipListLoaded)
	{
		IsShipListLoaded = true;
		FleetShips.Empty();
		for (int ShipIndex = 0; ShipIndex < FleetData.ShipImmatriculations.Num(); ShipIndex++)
		{
			UFlareSimulatedSpacecraft* Ship = FleetCompany->FindSpacecraft(FleetData.ShipImmatriculations[ShipIndex]);
			if (!Ship)
			{
				FLOGV("WARNING: Fail to find ship with id %s in company %s for fleet %s (%d ships)",
						*FleetData.ShipImmatriculations[ShipIndex].ToString(),
						*FleetCompany->GetCompanyName().ToString(),
						*GetFleetName().ToString(),
						FleetData.ShipImmatriculations.Num());
				continue;
			}
			Ship->SetCurrentFleet(this);
			FleetShips.Add(Ship);
		}
	}
}

/*----------------------------------------------------
	Getters
----------------------------------------------------*/

TArray<UFlareSimulatedSpacecraft*>& UFlareFleet::GetShips()
{
	InitShipList();

	return FleetShips;
}

#undef LOCTEXT_NAMESPACE
<|endoftext|>