<commit_before>/*=========================================================================

  Program:   Visualization Toolkit
  Module:    vtkCarbonRenderWindowInteractor.cxx

  Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
  All rights reserved.
  See Copyright.txt or http://www.kitware.com/Copyright.htm for details.

     This software is distributed WITHOUT ANY WARRANTY; without even
     the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
     PURPOSE.  See the above copyright notice for more information.

=========================================================================*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>

#include "vtkActor.h"
#include "vtkCarbonRenderWindow.h"
#include "vtkCarbonRenderWindowInteractor.h"
#include "vtkCommand.h"
#include "vtkInteractorStyle.h"
#include "vtkObjectFactory.h"

vtkCxxRevisionMacro(vtkCarbonRenderWindowInteractor, "1.23");
vtkStandardNewMacro(vtkCarbonRenderWindowInteractor);

void (*vtkCarbonRenderWindowInteractor::ClassExitMethod)(void *)
  = (void (*)(void *))NULL;
void *vtkCarbonRenderWindowInteractor::ClassExitMethodArg = (void *)NULL;
void (*vtkCarbonRenderWindowInteractor::ClassExitMethodArgDelete)(void *)
  = (void (*)(void *))NULL;

//--------------------------------------------------------------------------
// callback routine to handle all window-related events
// The WindowPtr of the associated window is passed in userData
static pascal OSStatus myWinEvtHndlr(EventHandlerCallRef,
                                     EventRef event, void* userData)
{
  OSStatus                         result = eventNotHandledErr;
  HIPoint                          mouseLoc;
  vtkCarbonRenderWindow            *ren;
  vtkCarbonRenderWindowInteractor  *me;
  UInt32                           eventClass = GetEventClass(event);
  UInt32                           eventKind = GetEventKind (event);

  ren = (vtkCarbonRenderWindow *)userData;

  if (NULL == ren)
    {
    return 0;
    }

  me = (vtkCarbonRenderWindowInteractor *)ren->GetInteractor();
  if (NULL == me)
    {
    return 0;
    }

  UInt32 modifierKeys;
  GetEventParameter(event, kEventParamKeyModifiers,typeUInt32, NULL,
                    sizeof(modifierKeys), NULL, &modifierKeys);
  int controlDown = (modifierKeys & controlKey);
  int shiftDown = (modifierKeys & shiftKey);

  // Even though the option key is the one with a small 'alt' label on top
  // of it, VNC (as well as some Mac users) uses the command key as 'alt'.
  // Let's use both then.
  UInt32 altKey = cmdKey | optionKey;
  int altDown = (modifierKeys & altKey);

  switch (eventClass)
    {
    case kEventClassControl:
      {
      switch (eventKind)
        {
        case kEventControlDraw:
          {
          ren->Render();
          result = noErr;
          break;
          }
        case kEventControlBoundsChanged:
          {
          if(ren->GetWindowId())
            {
            HIRect viewBounds;
            HIViewGetBounds(ren->GetWindowId(), &viewBounds);
            me->UpdateSize((int)viewBounds.size.width, (int)viewBounds.size.height);
            if (me->GetEnabled())
              {
              me->InvokeEvent(vtkCommand::ConfigureEvent,NULL);
              }
            result = noErr;
            }
          break;
          }
        }
      break;
      }

    case kEventClassKeyboard:
      {
      SInt8 charCode;
      GetEventParameter(event, kEventParamKeyMacCharCodes, typeChar,NULL,
                        sizeof(charCode),NULL,&charCode);
        switch (GetEventKind(event))
          {
          case kEventRawKeyDown:
            {
            me->SetKeyEventInformation(controlDown, shiftDown,
                                       (int)charCode,1,(char*)&charCode);
            me->SetAltKey(altDown);
            me->InvokeEvent(vtkCommand::KeyPressEvent, NULL);
            me->InvokeEvent(vtkCommand::CharEvent, NULL);
            result = noErr;
            break;
            }
          case kEventRawKeyRepeat:
            {
            me->SetKeyEventInformation(controlDown, shiftDown,
                                       (int)charCode,1,(char*)&charCode);
            me->SetAltKey(altDown);
            me->InvokeEvent(vtkCommand::KeyPressEvent, NULL);
            me->InvokeEvent(vtkCommand::CharEvent, NULL);
            result = noErr;
            break;
            }
          case kEventRawKeyUp:
            {
            me->SetKeyEventInformation(controlDown, shiftDown,
                                       (int)charCode,1,(char*)&charCode);
            me->SetAltKey(altDown);
            me->InvokeEvent(vtkCommand::KeyReleaseEvent, NULL);
            result = noErr;
            break;
            }
          }
        break;
      }

    case kEventClassMouse:
      {
      // see if the event is for this view
      HIViewRef view_for_mouse;
      HIViewRef root_window = HIViewGetRoot(ren->GetRootWindow());
      HIViewGetViewForMouseEvent(root_window, event, &view_for_mouse);
      if(view_for_mouse != ren->GetWindowId())
        return eventNotHandledErr;

      GetEventParameter(event, kEventParamMouseLocation, typeHIPoint,
                        NULL, sizeof(HIPoint), NULL, &mouseLoc);

      HIViewConvertPoint(&mouseLoc, root_window, ren->GetWindowId());

      GetEventParameter(event, kEventParamKeyModifiers,typeUInt32, NULL,
                        sizeof(modifierKeys), NULL, &modifierKeys);
      UInt16 buttonNumber;
      GetEventParameter(event, kEventParamMouseButton, typeMouseButton, NULL,
                        sizeof(buttonNumber), NULL, &buttonNumber);

      me->SetEventInformationFlipY((int)mouseLoc.x, (int)mouseLoc.y,
                                   (modifierKeys & controlKey),
                                   (modifierKeys & shiftKey));
      me->SetAltKey(modifierKeys & altKey);
      switch (GetEventKind(event))
        {
        case kEventMouseDown:
          {
          switch (buttonNumber)
            {
            case 1:
              {
              me->InvokeEvent(vtkCommand::LeftButtonPressEvent,NULL);
              break;
              }
            case 2:
              {
              me->InvokeEvent(vtkCommand::RightButtonPressEvent,NULL);
              break;
              }
            case 3:
              {
              me->InvokeEvent(vtkCommand::MiddleButtonPressEvent,NULL);
              break;
              }
            }
          result = noErr;
          break;
          }
        case kEventMouseUp:
          {
          switch (buttonNumber)
            {
            case 1:
              {
              me->InvokeEvent(vtkCommand::LeftButtonReleaseEvent,NULL);
              break;
              }
            case 2:
              {
              me->InvokeEvent(vtkCommand::RightButtonReleaseEvent,NULL);
              break;
              }
            case 3:
              {
              me->InvokeEvent(vtkCommand::MiddleButtonReleaseEvent,NULL);
              break;
              }
            }
          result = noErr;
          break;
          }
        case kEventMouseMoved:
          {
          me->InvokeEvent(vtkCommand::MouseMoveEvent, NULL);
          result = noErr;
          break;
          }
        case kEventMouseDragged:
          {
          me->InvokeEvent(vtkCommand::MouseMoveEvent, NULL);
          result = noErr;
          break;
          }
        case kEventMouseWheelMoved:
          {
          EventMouseWheelAxis axis;
          SInt32 delta;
          GetEventParameter( event, kEventParamMouseWheelAxis,
                         typeMouseWheelAxis, NULL, sizeof(axis), NULL, &axis );
          GetEventParameter( event, kEventParamMouseWheelDelta,
                          typeLongInteger, NULL, sizeof(delta), NULL, &delta );
          if ( axis == kEventMouseWheelAxisY )
            {
             if( delta > 0)
              {
              me->InvokeEvent(vtkCommand::MouseWheelForwardEvent, NULL);
              }
            else
              {
              me->InvokeEvent(vtkCommand::MouseWheelBackwardEvent, NULL);
              }
            }
          result = noErr;
          break;
          }
        default:
          {
          }
        }
      }
    }

  return result;
}

//--------------------------------------------------------------------------
// Construct object so that light follows camera motion.
vtkCarbonRenderWindowInteractor::vtkCarbonRenderWindowInteractor()
{
  this->InstallMessageProc = 1;
  this->ViewProcUPP        = NULL;
  this->WindowProcUPP      = NULL;
}

//--------------------------------------------------------------------------
vtkCarbonRenderWindowInteractor::~vtkCarbonRenderWindowInteractor()
{
  this->Enabled = 0;
}

//--------------------------------------------------------------------------
void  vtkCarbonRenderWindowInteractor::Start()
{
  // Let the compositing handle the event loop if it wants to.
  if (this->HasObserver(vtkCommand::StartEvent) && !this->HandleEventLoop)
    {
    this->InvokeEvent(vtkCommand::StartEvent,NULL);
    return;
    }

  // No need to do anything if this is a 'mapped' interactor
  if (!this->Enabled || !this->InstallMessageProc)
    {
    return;
    }
  RunApplicationEventLoop();
}

//--------------------------------------------------------------------------
// Fill in some local variables (most of this routine could probably go)
void vtkCarbonRenderWindowInteractor::Initialize()
{
  vtkCarbonRenderWindow *ren;
  int *size;

  // make sure we have a RenderWindow and camera
  if ( ! this->RenderWindow)
    {
    vtkErrorMacro(<<"No renderer defined!");
    return;
    }
  if (this->Initialized)
    {
    return;
    }
  this->Initialized = 1;
  // get the info we need from the RenderingWindow
  ren = (vtkCarbonRenderWindow *)(this->RenderWindow);

  ren->Start();
  size    = ren->GetSize();
  ren->GetPosition();
  this->Enable();
  this->Size[0] = size[0];
  this->Size[1] = size[1];
}

//--------------------------------------------------------------------------
void vtkCarbonRenderWindowInteractor::Enable()
{
  if (this->Enabled)
    {
    return;
    }

  if (this->InstallMessageProc)
    {
    // set up the event handling
    // specify which events we want to hear about
    OSStatus   err = noErr;
    EventTypeSpec view_event_list[] = {{ kEventClassControl, kEventControlDraw },
                                       { kEventClassControl, kEventControlBoundsChanged }};

    EventTypeSpec window_event_list[] ={{ kEventClassMouse, kEventMouseDown },
                                        { kEventClassMouse, kEventMouseUp },
                                        { kEventClassMouse, kEventMouseMoved },
                                        { kEventClassMouse, kEventMouseDragged },
                                        { kEventClassMouse, kEventMouseWheelMoved },
                                        { kEventClassKeyboard, kEventRawKeyDown },
                                        { kEventClassKeyboard, kEventRawKeyRepeat },
                                        { kEventClassKeyboard, kEventRawKeyUp }};

    this->WindowProcUPP = NewEventHandlerUPP(myWinEvtHndlr);
    this->ViewProcUPP = NewEventHandlerUPP(myWinEvtHndlr);
    if(!this->WindowProcUPP || !ViewProcUPP)
      err = memFullErr;

    if(!err)
      {
      vtkCarbonRenderWindow* ren = static_cast<vtkCarbonRenderWindow*>(this->RenderWindow);
      err = InstallControlEventHandler(ren->GetWindowId(), this->ViewProcUPP,
                                       GetEventTypeCount(view_event_list), view_event_list, ren, NULL);
      err = InstallWindowEventHandler(ren->GetRootWindow(), this->WindowProcUPP,
                                       GetEventTypeCount(window_event_list), window_event_list, ren, NULL);
      }
    }
  this->Enabled = 1;
  this->Modified();
}

//--------------------------------------------------------------------------
void vtkCarbonRenderWindowInteractor::Disable()
{
  if (!this->Enabled)
    {
    return;
    }
  this->Enabled = 0;
  this->Modified();
}

//--------------------------------------------------------------------------
void vtkCarbonRenderWindowInteractor::TerminateApp(void)
{
  QuitApplicationEventLoop();
}

//--------------------------------------------------------------------------
pascal void TimerAction(EventLoopTimerRef platformTimerId, void* userData)
{
  if (NULL != userData)
    {
    vtkCarbonRenderWindowInteractor *rwi =
      static_cast<vtkCarbonRenderWindowInteractor *>(userData);
    int vtkTimerId = rwi->GetVTKTimerId((int) platformTimerId);
    rwi->InvokeEvent(vtkCommand::TimerEvent, (void *) &vtkTimerId);
    }
}

//--------------------------------------------------------------------------
int vtkCarbonRenderWindowInteractor::InternalCreateTimer(
  int vtkNotUsed(timerId), int timerType, unsigned long duration)
{
  EventLoopTimerRef  platformTimerId;
  EventLoopRef       mainLoop = GetMainEventLoop();
  EventLoopTimerUPP  timerUPP = NewEventLoopTimerUPP(TimerAction);
  EventTimerInterval interval = 0;

  // Carbon's InstallEventLoopTimer can create either one-shot or repeating
  // timers... interval == 0 indicates a one-shot timer.

  if (RepeatingTimer == timerType)
    {
    interval = duration*kEventDurationMillisecond;
    }

  InstallEventLoopTimer(mainLoop,
                        duration*kEventDurationMillisecond,
                        interval,
                        timerUPP,
                        this,
                        &platformTimerId);

  return (int) platformTimerId;
}

//--------------------------------------------------------------------------
int vtkCarbonRenderWindowInteractor::InternalDestroyTimer(int platformTimerId)
{
  RemoveEventLoopTimer((EventLoopTimerRef) platformTimerId);
  return 1;
}

//--------------------------------------------------------------------------
// Specify the default function to be called when an interactor needs to exit.
// This callback is overridden by an instance ExitMethod that is defined.
void vtkCarbonRenderWindowInteractor::SetClassExitMethod(void (*f)(void *),
                                                         void *arg)
{
  if (f != vtkCarbonRenderWindowInteractor::ClassExitMethod
      || arg != vtkCarbonRenderWindowInteractor::ClassExitMethodArg)
    {
    // delete the current arg if there is a delete method
    if ((vtkCarbonRenderWindowInteractor::ClassExitMethodArg) &&
        (vtkCarbonRenderWindowInteractor::ClassExitMethodArgDelete))
      {
      (*vtkCarbonRenderWindowInteractor::ClassExitMethodArgDelete)
        (vtkCarbonRenderWindowInteractor::ClassExitMethodArg);
      }
    vtkCarbonRenderWindowInteractor::ClassExitMethod = f;
    vtkCarbonRenderWindowInteractor::ClassExitMethodArg = arg;

    // no call to this->Modified() since this is a class member function
    }
}

//--------------------------------------------------------------------------
// Set the arg delete method.  This is used to free user memory.
void
vtkCarbonRenderWindowInteractor::SetClassExitMethodArgDelete(void (*f)(void *))
{
  if (f != vtkCarbonRenderWindowInteractor::ClassExitMethodArgDelete)
    {
    vtkCarbonRenderWindowInteractor::ClassExitMethodArgDelete = f;

    // no call to this->Modified() since this is a class member function
    }
}

//--------------------------------------------------------------------------
void vtkCarbonRenderWindowInteractor::PrintSelf(ostream& os, vtkIndent indent)
{
  vtkRenderWindowInteractor::PrintSelf(os,indent);
  os << indent << "InstallMessageProc: " << this->InstallMessageProc << endl;
}

//--------------------------------------------------------------------------
void vtkCarbonRenderWindowInteractor::ExitCallback()
{
  if (this->HasObserver(vtkCommand::ExitEvent))
    {
    this->InvokeEvent(vtkCommand::ExitEvent,NULL);
    }
  else if (this->ClassExitMethod)
    {
    (*this->ClassExitMethod)(this->ClassExitMethodArg);
    }
  this->TerminateApp();
}
<commit_msg><commit_after>/*=========================================================================

  Program:   Visualization Toolkit
  Module:    vtkCarbonRenderWindowInteractor.cxx

  Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
  All rights reserved.
  See Copyright.txt or http://www.kitware.com/Copyright.htm for details.

     This software is distributed WITHOUT ANY WARRANTY; without even
     the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
     PURPOSE.  See the above copyright notice for more information.

=========================================================================*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>

#include "vtkActor.h"
#include "vtkCarbonRenderWindow.h"
#include "vtkCarbonRenderWindowInteractor.h"
#include "vtkCommand.h"
#include "vtkInteractorStyle.h"
#include "vtkObjectFactory.h"

vtkCxxRevisionMacro(vtkCarbonRenderWindowInteractor, "1.24");
vtkStandardNewMacro(vtkCarbonRenderWindowInteractor);

void (*vtkCarbonRenderWindowInteractor::ClassExitMethod)(void *)
  = (void (*)(void *))NULL;
void *vtkCarbonRenderWindowInteractor::ClassExitMethodArg = (void *)NULL;
void (*vtkCarbonRenderWindowInteractor::ClassExitMethodArgDelete)(void *)
  = (void (*)(void *))NULL;

//--------------------------------------------------------------------------
// callback routine to handle all window-related events
// The WindowPtr of the associated window is passed in userData
static pascal OSStatus myWinEvtHndlr(EventHandlerCallRef,
                                     EventRef event, void* userData)
{
  OSStatus                         result = eventNotHandledErr;
  HIPoint                          mouseLoc;
  vtkCarbonRenderWindow            *ren;
  vtkCarbonRenderWindowInteractor  *me;
  UInt32                           eventClass = GetEventClass(event);
  UInt32                           eventKind = GetEventKind (event);

  ren = (vtkCarbonRenderWindow *)userData;

  if (NULL == ren)
    {
    return 0;
    }

  me = (vtkCarbonRenderWindowInteractor *)ren->GetInteractor();
  if (NULL == me)
    {
    return 0;
    }

  UInt32 modifierKeys;
  GetEventParameter(event, kEventParamKeyModifiers,typeUInt32, NULL,
                    sizeof(modifierKeys), NULL, &modifierKeys);
  int controlDown = (modifierKeys & controlKey);
  int shiftDown = (modifierKeys & shiftKey);

  // Even though the option key is the one with a small 'alt' label on top
  // of it, VNC (as well as some Mac users) uses the command key as 'alt'.
  // Let's use both then.
  UInt32 altKey = cmdKey | optionKey;
  int altDown = (modifierKeys & altKey);

  switch (eventClass)
    {
    case kEventClassControl:
      {
      switch (eventKind)
        {
        case kEventControlDraw:
          {
          ren->Render();
          result = noErr;
          break;
          }
        case kEventControlBoundsChanged:
          {
          if(ren->GetWindowId())
            {
            HIRect viewBounds;
            HIViewGetBounds(ren->GetWindowId(), &viewBounds);
            me->UpdateSize((int)viewBounds.size.width, (int)viewBounds.size.height);
            if (me->GetEnabled())
              {
              me->InvokeEvent(vtkCommand::ConfigureEvent,NULL);
              }
            result = noErr;
            }
          break;
          }
        }
      break;
      }

    case kEventClassKeyboard:
      {
      SInt8 charCode;
      GetEventParameter(event, kEventParamKeyMacCharCodes, typeChar,NULL,
                        sizeof(charCode),NULL,&charCode);
        switch (GetEventKind(event))
          {
          case kEventRawKeyDown:
            {
            me->SetKeyEventInformation(controlDown, shiftDown,
                                       (int)charCode,1,(char*)&charCode);
            me->SetAltKey(altDown);
            me->InvokeEvent(vtkCommand::KeyPressEvent, NULL);
            me->InvokeEvent(vtkCommand::CharEvent, NULL);
            result = noErr;
            break;
            }
          case kEventRawKeyRepeat:
            {
            me->SetKeyEventInformation(controlDown, shiftDown,
                                       (int)charCode,1,(char*)&charCode);
            me->SetAltKey(altDown);
            me->InvokeEvent(vtkCommand::KeyPressEvent, NULL);
            me->InvokeEvent(vtkCommand::CharEvent, NULL);
            result = noErr;
            break;
            }
          case kEventRawKeyUp:
            {
            me->SetKeyEventInformation(controlDown, shiftDown,
                                       (int)charCode,1,(char*)&charCode);
            me->SetAltKey(altDown);
            me->InvokeEvent(vtkCommand::KeyReleaseEvent, NULL);
            result = noErr;
            break;
            }
          }
        break;
      }

    case kEventClassMouse:
      {
      // see if the event is for this view
      HIViewRef view_for_mouse;
      HIViewRef root_window = HIViewGetRoot(ren->GetRootWindow());
      HIViewGetViewForMouseEvent(root_window, event, &view_for_mouse);
      if(view_for_mouse != ren->GetWindowId())
        return eventNotHandledErr;

      GetEventParameter(event, kEventParamWindowMouseLocation, typeHIPoint,
                        NULL, sizeof(HIPoint), NULL, &mouseLoc);

      HIViewConvertPoint(&mouseLoc, root_window, ren->GetWindowId());

      GetEventParameter(event, kEventParamKeyModifiers,typeUInt32, NULL,
                        sizeof(modifierKeys), NULL, &modifierKeys);
      UInt16 buttonNumber;
      GetEventParameter(event, kEventParamMouseButton, typeMouseButton, NULL,
                        sizeof(buttonNumber), NULL, &buttonNumber);

      me->SetEventInformationFlipY((int)mouseLoc.x, (int)mouseLoc.y,
                                   (modifierKeys & controlKey),
                                   (modifierKeys & shiftKey));
      me->SetAltKey(modifierKeys & altKey);
      switch (GetEventKind(event))
        {
        case kEventMouseDown:
          {
          switch (buttonNumber)
            {
            case 1:
              {
              me->InvokeEvent(vtkCommand::LeftButtonPressEvent,NULL);
              break;
              }
            case 2:
              {
              me->InvokeEvent(vtkCommand::RightButtonPressEvent,NULL);
              break;
              }
            case 3:
              {
              me->InvokeEvent(vtkCommand::MiddleButtonPressEvent,NULL);
              break;
              }
            }
          result = noErr;
          break;
          }
        case kEventMouseUp:
          {
          switch (buttonNumber)
            {
            case 1:
              {
              me->InvokeEvent(vtkCommand::LeftButtonReleaseEvent,NULL);
              break;
              }
            case 2:
              {
              me->InvokeEvent(vtkCommand::RightButtonReleaseEvent,NULL);
              break;
              }
            case 3:
              {
              me->InvokeEvent(vtkCommand::MiddleButtonReleaseEvent,NULL);
              break;
              }
            }
          result = noErr;
          break;
          }
        case kEventMouseMoved:
          {
          me->InvokeEvent(vtkCommand::MouseMoveEvent, NULL);
          result = noErr;
          break;
          }
        case kEventMouseDragged:
          {
          me->InvokeEvent(vtkCommand::MouseMoveEvent, NULL);
          result = noErr;
          break;
          }
        case kEventMouseWheelMoved:
          {
          EventMouseWheelAxis axis;
          SInt32 delta;
          GetEventParameter( event, kEventParamMouseWheelAxis,
                         typeMouseWheelAxis, NULL, sizeof(axis), NULL, &axis );
          GetEventParameter( event, kEventParamMouseWheelDelta,
                          typeLongInteger, NULL, sizeof(delta), NULL, &delta );
          if ( axis == kEventMouseWheelAxisY )
            {
             if( delta > 0)
              {
              me->InvokeEvent(vtkCommand::MouseWheelForwardEvent, NULL);
              }
            else
              {
              me->InvokeEvent(vtkCommand::MouseWheelBackwardEvent, NULL);
              }
            }
          result = noErr;
          break;
          }
        default:
          {
          }
        }
      }
    }

  return result;
}

//--------------------------------------------------------------------------
// Construct object so that light follows camera motion.
vtkCarbonRenderWindowInteractor::vtkCarbonRenderWindowInteractor()
{
  this->InstallMessageProc = 1;
  this->ViewProcUPP        = NULL;
  this->WindowProcUPP      = NULL;
}

//--------------------------------------------------------------------------
vtkCarbonRenderWindowInteractor::~vtkCarbonRenderWindowInteractor()
{
  this->Enabled = 0;
}

//--------------------------------------------------------------------------
void  vtkCarbonRenderWindowInteractor::Start()
{
  // Let the compositing handle the event loop if it wants to.
  if (this->HasObserver(vtkCommand::StartEvent) && !this->HandleEventLoop)
    {
    this->InvokeEvent(vtkCommand::StartEvent,NULL);
    return;
    }

  // No need to do anything if this is a 'mapped' interactor
  if (!this->Enabled || !this->InstallMessageProc)
    {
    return;
    }
  RunApplicationEventLoop();
}

//--------------------------------------------------------------------------
// Fill in some local variables (most of this routine could probably go)
void vtkCarbonRenderWindowInteractor::Initialize()
{
  vtkCarbonRenderWindow *ren;
  int *size;

  // make sure we have a RenderWindow and camera
  if ( ! this->RenderWindow)
    {
    vtkErrorMacro(<<"No renderer defined!");
    return;
    }
  if (this->Initialized)
    {
    return;
    }
  this->Initialized = 1;
  // get the info we need from the RenderingWindow
  ren = (vtkCarbonRenderWindow *)(this->RenderWindow);

  ren->Start();
  size    = ren->GetSize();
  ren->GetPosition();
  this->Enable();
  this->Size[0] = size[0];
  this->Size[1] = size[1];
}

//--------------------------------------------------------------------------
void vtkCarbonRenderWindowInteractor::Enable()
{
  if (this->Enabled)
    {
    return;
    }

  if (this->InstallMessageProc)
    {
    // set up the event handling
    // specify which events we want to hear about
    OSStatus   err = noErr;
    EventTypeSpec view_event_list[] = {{ kEventClassControl, kEventControlDraw },
                                       { kEventClassControl, kEventControlBoundsChanged }};

    EventTypeSpec window_event_list[] ={{ kEventClassMouse, kEventMouseDown },
                                        { kEventClassMouse, kEventMouseUp },
                                        { kEventClassMouse, kEventMouseMoved },
                                        { kEventClassMouse, kEventMouseDragged },
                                        { kEventClassMouse, kEventMouseWheelMoved },
                                        { kEventClassKeyboard, kEventRawKeyDown },
                                        { kEventClassKeyboard, kEventRawKeyRepeat },
                                        { kEventClassKeyboard, kEventRawKeyUp }};

    this->WindowProcUPP = NewEventHandlerUPP(myWinEvtHndlr);
    this->ViewProcUPP = NewEventHandlerUPP(myWinEvtHndlr);
    if(!this->WindowProcUPP || !ViewProcUPP)
      err = memFullErr;

    if(!err)
      {
      vtkCarbonRenderWindow* ren = static_cast<vtkCarbonRenderWindow*>(this->RenderWindow);
      err = InstallControlEventHandler(ren->GetWindowId(), this->ViewProcUPP,
                                       GetEventTypeCount(view_event_list), view_event_list, ren, NULL);
      err = InstallWindowEventHandler(ren->GetRootWindow(), this->WindowProcUPP,
                                       GetEventTypeCount(window_event_list), window_event_list, ren, NULL);
      }
    }
  this->Enabled = 1;
  this->Modified();
}

//--------------------------------------------------------------------------
void vtkCarbonRenderWindowInteractor::Disable()
{
  if (!this->Enabled)
    {
    return;
    }
  this->Enabled = 0;
  this->Modified();
}

//--------------------------------------------------------------------------
void vtkCarbonRenderWindowInteractor::TerminateApp(void)
{
  QuitApplicationEventLoop();
}

//--------------------------------------------------------------------------
pascal void TimerAction(EventLoopTimerRef platformTimerId, void* userData)
{
  if (NULL != userData)
    {
    vtkCarbonRenderWindowInteractor *rwi =
      static_cast<vtkCarbonRenderWindowInteractor *>(userData);
    int vtkTimerId = rwi->GetVTKTimerId((int) platformTimerId);
    rwi->InvokeEvent(vtkCommand::TimerEvent, (void *) &vtkTimerId);
    }
}

//--------------------------------------------------------------------------
int vtkCarbonRenderWindowInteractor::InternalCreateTimer(
  int vtkNotUsed(timerId), int timerType, unsigned long duration)
{
  EventLoopTimerRef  platformTimerId;
  EventLoopRef       mainLoop = GetMainEventLoop();
  EventLoopTimerUPP  timerUPP = NewEventLoopTimerUPP(TimerAction);
  EventTimerInterval interval = 0;

  // Carbon's InstallEventLoopTimer can create either one-shot or repeating
  // timers... interval == 0 indicates a one-shot timer.

  if (RepeatingTimer == timerType)
    {
    interval = duration*kEventDurationMillisecond;
    }

  InstallEventLoopTimer(mainLoop,
                        duration*kEventDurationMillisecond,
                        interval,
                        timerUPP,
                        this,
                        &platformTimerId);

  return (int) platformTimerId;
}

//--------------------------------------------------------------------------
int vtkCarbonRenderWindowInteractor::InternalDestroyTimer(int platformTimerId)
{
  RemoveEventLoopTimer((EventLoopTimerRef) platformTimerId);
  return 1;
}

//--------------------------------------------------------------------------
// Specify the default function to be called when an interactor needs to exit.
// This callback is overridden by an instance ExitMethod that is defined.
void vtkCarbonRenderWindowInteractor::SetClassExitMethod(void (*f)(void *),
                                                         void *arg)
{
  if (f != vtkCarbonRenderWindowInteractor::ClassExitMethod
      || arg != vtkCarbonRenderWindowInteractor::ClassExitMethodArg)
    {
    // delete the current arg if there is a delete method
    if ((vtkCarbonRenderWindowInteractor::ClassExitMethodArg) &&
        (vtkCarbonRenderWindowInteractor::ClassExitMethodArgDelete))
      {
      (*vtkCarbonRenderWindowInteractor::ClassExitMethodArgDelete)
        (vtkCarbonRenderWindowInteractor::ClassExitMethodArg);
      }
    vtkCarbonRenderWindowInteractor::ClassExitMethod = f;
    vtkCarbonRenderWindowInteractor::ClassExitMethodArg = arg;

    // no call to this->Modified() since this is a class member function
    }
}

//--------------------------------------------------------------------------
// Set the arg delete method.  This is used to free user memory.
void
vtkCarbonRenderWindowInteractor::SetClassExitMethodArgDelete(void (*f)(void *))
{
  if (f != vtkCarbonRenderWindowInteractor::ClassExitMethodArgDelete)
    {
    vtkCarbonRenderWindowInteractor::ClassExitMethodArgDelete = f;

    // no call to this->Modified() since this is a class member function
    }
}

//--------------------------------------------------------------------------
void vtkCarbonRenderWindowInteractor::PrintSelf(ostream& os, vtkIndent indent)
{
  vtkRenderWindowInteractor::PrintSelf(os,indent);
  os << indent << "InstallMessageProc: " << this->InstallMessageProc << endl;
}

//--------------------------------------------------------------------------
void vtkCarbonRenderWindowInteractor::ExitCallback()
{
  if (this->HasObserver(vtkCommand::ExitEvent))
    {
    this->InvokeEvent(vtkCommand::ExitEvent,NULL);
    }
  else if (this->ClassExitMethod)
    {
    (*this->ClassExitMethod)(this->ClassExitMethodArg);
    }
  this->TerminateApp();
}
<|endoftext|>


<commit_before><commit_msg>use C++11 iteration<commit_after><|endoftext|>


<commit_before>
#include "rutil/Log.hxx"
#include "rutil/Logger.hxx"
#include "rutil/Subsystem.hxx"
#include "resip/dum/ClientAuthManager.hxx"
#include "resip/dum/ClientRegistration.hxx"
#include "resip/dum/DialogUsageManager.hxx"
#include "resip/dum/InviteSessionHandler.hxx"
#include "resip/dum/MasterProfile.hxx"
#include "resip/dum/Profile.hxx"
#include "resip/dum/UserProfile.hxx"
#include "resip/dum/RegistrationHandler.hxx"
#include "resip/dum/ClientPagerMessage.hxx"
#include "resip/dum/ServerPagerMessage.hxx"

#include "resip/dum/DialogUsageManager.hxx"
#include "resip/dum/AppDialogSet.hxx"
#include "resip/dum/AppDialog.hxx"
#include "resip/dum/RegistrationHandler.hxx"
#include "resip/dum/PagerMessageHandler.hxx"
#include "resip/stack/PlainContents.hxx"

#include <iostream>
#include <string>
#include <sstream>

#include "org_resiprocate_android_basicmessage_MessageSender.h"

using namespace std;
using namespace resip;

#define RESIPROCATE_SUBSYSTEM Subsystem::TEST

class ClientMessageHandler : public ClientPagerMessageHandler {
public:
   ClientMessageHandler()
      : finished(false),
        successful(false)
   {
   };

   virtual void onSuccess(ClientPagerMessageHandle, const SipMessage& status)
   {
      InfoLog(<<"ClientMessageHandler::onSuccess\n");
      successful = true;
      finished = true;
   }

   virtual void onFailure(ClientPagerMessageHandle, const SipMessage& status, std::auto_ptr<Contents> contents)
   {
      ErrLog(<<"ClientMessageHandler::onFailure\n");
      successful = false;
      finished = true;
   }

   bool isFinished() { return finished; };
   bool isSuccessful() { return successful; };

private:
   bool finished;
   bool successful;
};

int send_message(int argc, const char *argv[], const char* body)
{
   Log::initialize(Log::Cout, Log::Info, argv[0]);

   if( (argc < 6) || (argc > 7) ) {
      ErrLog(<< "usage: " << argv[0] << " sip:from user passwd realm sip:to [port]\n");
      return 1;
   }

   string from(argv[1]);
   string user(argv[2]);
   string passwd(argv[3]);
   string realm(argv[4]);
   string to(argv[5]);
   int port = 5060;
   if(argc == 7)
   {
      string temp(argv[6]);
      istringstream src(temp);
      src >> port;
   }

   InfoLog(<< "log: from: " << from << ", to: " << to << ", port: " << port << "\n");
   InfoLog(<< "user: " << user << ", passwd: " << passwd << ", realm: " << realm << "\n");

   SharedPtr<MasterProfile> profile(new MasterProfile);
   auto_ptr<ClientAuthManager> clientAuth(new ClientAuthManager());

   SipStack clientStack;
   DialogUsageManager clientDum(clientStack);
   clientDum.addTransport(UDP, port);
   clientDum.setMasterProfile(profile);

   clientDum.setClientAuthManager(clientAuth);
   clientDum.getMasterProfile()->setDefaultRegistrationTime(70);
   clientDum.getMasterProfile()->addSupportedMethod(MESSAGE);
   clientDum.getMasterProfile()->addSupportedMimeType(MESSAGE, Mime("text", "plain"));
   ClientMessageHandler *cmh = new ClientMessageHandler();
   clientDum.setClientPagerMessageHandler(cmh);

   NameAddr naFrom(from.c_str());
   profile->setDefaultFrom(naFrom);
   profile->setDigestCredential(realm.c_str(), user.c_str(), passwd.c_str());

   InfoLog(<< "Sending MESSAGE\n");
   NameAddr naTo(to.c_str());
   ClientPagerMessageHandle cpmh = clientDum.makePagerMessage(naTo);

   Data messageBody(body);
   auto_ptr<Contents> content(new PlainContents(messageBody));
   cpmh.get()->page(content);

   // Event loop - stack will invoke callbacks in our app
   while(!cmh->isFinished())
   {
      clientStack.process(100);
      while(clientDum.process());
   }

   if(!cmh->isSuccessful())
   {
      ErrLog(<< "Message delivery failed, aborting");
      return 1;
   }

   return 0;
}

// Note: this is very crude, it will create a new SIP stack and DUM each
// time a message needs to be sent

// A proper implementation must keep the SIP stack active as long as the
// app is running.

#ifdef __cplusplus
extern "C" {
#endif
JNIEXPORT void JNICALL Java_org_resiprocate_android_basicmessage_MessageSender_sendMessage
  (JNIEnv *env, jobject jthis, jstring recipient, jstring body)
{
   const char *_recipient = env->GetStringUTFChars(recipient, 0);
   const char *_body = env->GetStringUTFChars(body, 0);

   // These should be configured through Android preferences:

   const char *argv[8];
   argv[0] = "BasicMessage (JNI)";
   argv[1] = "sip:anonymous@example.org";
   argv[2] = "anonymous";
   argv[3] = "password";
   argv[4] = "realm";
   argv[5] = _recipient;

   // we don't use 5060 in case it clashes with some other app on the phone
   argv[6] = "5067";

   try {
   	int rc = send_message(7, argv, _body);
   } catch (exception& e)
  {
    cout << e.what() << endl;
  }
  catch(...)
  {
    cout << "some exception!" << endl;
  }

  env->ReleaseStringUTFChars(recipient, _recipient);
  env->ReleaseStringUTFChars(body, _body);
}
#ifdef __cplusplus
}
#endif
<commit_msg>Use the new AndroidLogger mechanism for logcat output<commit_after>
#include "rutil/AndroidLogger.hxx"
#include "rutil/Log.hxx"
#include "rutil/Logger.hxx"
#include "rutil/Subsystem.hxx"
#include "resip/dum/ClientAuthManager.hxx"
#include "resip/dum/ClientRegistration.hxx"
#include "resip/dum/DialogUsageManager.hxx"
#include "resip/dum/InviteSessionHandler.hxx"
#include "resip/dum/MasterProfile.hxx"
#include "resip/dum/Profile.hxx"
#include "resip/dum/UserProfile.hxx"
#include "resip/dum/RegistrationHandler.hxx"
#include "resip/dum/ClientPagerMessage.hxx"
#include "resip/dum/ServerPagerMessage.hxx"

#include "resip/dum/DialogUsageManager.hxx"
#include "resip/dum/AppDialogSet.hxx"
#include "resip/dum/AppDialog.hxx"
#include "resip/dum/RegistrationHandler.hxx"
#include "resip/dum/PagerMessageHandler.hxx"
#include "resip/stack/PlainContents.hxx"

#include <iostream>
#include <string>
#include <sstream>

#include "org_resiprocate_android_basicmessage_MessageSender.h"

using namespace std;
using namespace resip;

#define RESIPROCATE_SUBSYSTEM Subsystem::TEST

class ClientMessageHandler : public ClientPagerMessageHandler {
public:
   ClientMessageHandler()
      : finished(false),
        successful(false)
   {
   };

   virtual void onSuccess(ClientPagerMessageHandle, const SipMessage& status)
   {
      InfoLog(<<"ClientMessageHandler::onSuccess\n");
      successful = true;
      finished = true;
   }

   virtual void onFailure(ClientPagerMessageHandle, const SipMessage& status, std::auto_ptr<Contents> contents)
   {
      ErrLog(<<"ClientMessageHandler::onFailure\n");
      successful = false;
      finished = true;
   }

   bool isFinished() { return finished; };
   bool isSuccessful() { return successful; };

private:
   bool finished;
   bool successful;
};

int send_message(int argc, const char *argv[], const char* body)
{
   AndroidLogger alog;
   Log::initialize(Log::Cout, Log::Stack, argv[0], alog);

   if( (argc < 6) || (argc > 7) ) {
      ErrLog(<< "usage: " << argv[0] << " sip:from user passwd realm sip:to [port]\n");
      return 1;
   }

   string from(argv[1]);
   string user(argv[2]);
   string passwd(argv[3]);
   string realm(argv[4]);
   string to(argv[5]);
   int port = 5060;
   if(argc == 7)
   {
      string temp(argv[6]);
      istringstream src(temp);
      src >> port;
   }

   InfoLog(<< "log: from: " << from << ", to: " << to << ", port: " << port << "\n");
   InfoLog(<< "user: " << user << ", passwd: " << passwd << ", realm: " << realm << "\n");

   SharedPtr<MasterProfile> profile(new MasterProfile);
   auto_ptr<ClientAuthManager> clientAuth(new ClientAuthManager());

   SipStack clientStack;
   DialogUsageManager clientDum(clientStack);
   clientDum.addTransport(UDP, port);
   clientDum.setMasterProfile(profile);

   clientDum.setClientAuthManager(clientAuth);
   clientDum.getMasterProfile()->setDefaultRegistrationTime(70);
   clientDum.getMasterProfile()->addSupportedMethod(MESSAGE);
   clientDum.getMasterProfile()->addSupportedMimeType(MESSAGE, Mime("text", "plain"));
   ClientMessageHandler *cmh = new ClientMessageHandler();
   clientDum.setClientPagerMessageHandler(cmh);

   NameAddr naFrom(from.c_str());
   profile->setDefaultFrom(naFrom);
   profile->setDigestCredential(realm.c_str(), user.c_str(), passwd.c_str());

   InfoLog(<< "Sending MESSAGE\n");
   NameAddr naTo(to.c_str());
   ClientPagerMessageHandle cpmh = clientDum.makePagerMessage(naTo);

   Data messageBody(body);
   auto_ptr<Contents> content(new PlainContents(messageBody));
   cpmh.get()->page(content);

   // Event loop - stack will invoke callbacks in our app
   while(!cmh->isFinished())
   {
      clientStack.process(100);
      while(clientDum.process());
   }

   if(!cmh->isSuccessful())
   {
      ErrLog(<< "Message delivery failed, aborting");
      return 1;
   }

   return 0;
}

// Note: this is very crude, it will create a new SIP stack and DUM each
// time a message needs to be sent

// A proper implementation must keep the SIP stack active as long as the
// app is running.

#ifdef __cplusplus
extern "C" {
#endif
JNIEXPORT void JNICALL Java_org_resiprocate_android_basicmessage_MessageSender_sendMessage
  (JNIEnv *env, jobject jthis, jstring recipient, jstring body)
{
   const char *_recipient = env->GetStringUTFChars(recipient, 0);
   const char *_body = env->GetStringUTFChars(body, 0);

   // These should be configured through Android preferences:

   const char *argv[8];
   argv[0] = "BasicMessage (JNI)";
   argv[1] = "sip:anonymous@example.org";
   argv[2] = "anonymous";
   argv[3] = "password";
   argv[4] = "realm";
   argv[5] = _recipient;

   // we don't use 5060 in case it clashes with some other app on the phone
   argv[6] = "5067";

   try {
   	int rc = send_message(7, argv, _body);
   } catch (exception& e)
  {
    cout << e.what() << endl;
  }
  catch(...)
  {
    cout << "some exception!" << endl;
  }

  env->ReleaseStringUTFChars(recipient, _recipient);
  env->ReleaseStringUTFChars(body, _body);
}
#ifdef __cplusplus
}
#endif
<|endoftext|>


<commit_before>/**
 * This file is part of the Marble Desktop Globe.
 *
 * Copyright 2005-2007 Torsten Rahn <tackat@kde.org>"
 * Copyright 2007      Inge Wallin  <ingwa@kde.org>"
 * Copyright 2008,2009 Jens-Michael Hoffmann <jensmh@gmx.de>
 *
 * 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; see the file COPYING.LIB.  If not, write to
 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301, USA.
 */

#include "TileLoader.h"

#include "global.h"
#include "GeoSceneLayer.h"
#include "GeoSceneTexture.h"
#include "HttpDownloadManager.h"
#include "DatasetProvider.h"
#include "TextureTile.h"
#include "MarbleDirs.h"
#include "MarbleModel.h"
#include "TileLoaderHelper.h"

#include <QtCore/QCache>
#include <QtCore/QDebug>
#include <QtCore/QHash>
#include <QtGui/QImage>

#ifdef Q_CC_MSVC
# ifndef KDEWIN_MATH_H
   long double log(int i) { return log((long double)i); }
# endif
#endif

namespace Marble
{

class TileLoaderPrivate
{
    public:
        TileLoaderPrivate()
            : m_datasetProvider( 0 ),
              m_downloadManager( 0 ),
              m_layer( 0 ),
              m_tileWidth( 0 ),
              m_tileHeight( 0 )
        {
            m_tileCache.setMaxCost( 20000 * 1024 ); // Cache size measured in bytes
        }

        DatasetProvider *m_datasetProvider;
        HttpDownloadManager *m_downloadManager;
        GeoSceneLayer *m_layer;
        QHash <TileId, TextureTile*>  m_tilesOnDisplay;
        int           m_tileWidth;
        int           m_tileHeight;
        QCache <TileId, TextureTile>  m_tileCache;
};

TileLoader::TileLoader( HttpDownloadManager *downloadManager, MarbleModel* parent)
    : d( new TileLoaderPrivate() ),
      m_parent(parent)
{
    setDownloadManager( downloadManager );
}

TileLoader::~TileLoader()
{
    flush();
    d->m_tileCache.clear();
    if ( d->m_downloadManager != 0 )
        d->m_downloadManager->disconnect( this );

    delete d;
}

void TileLoader::setDownloadManager( HttpDownloadManager *downloadManager )
{
    if ( d->m_downloadManager != 0 ) {
        d->m_downloadManager->disconnect( this );
        d->m_downloadManager = 0;
    }

    d->m_downloadManager = downloadManager;
    if ( d->m_downloadManager != 0 ) {
        connect( d->m_downloadManager, SIGNAL( downloadComplete( QString, QString ) ),
                 this,              SLOT( reloadTile( QString, QString ) ) );
    }
}

void TileLoader::setLayer( GeoSceneLayer * layer )
{
    // Initialize map theme.
    flush();
    d->m_tileCache.clear();

    if ( !layer ) {
        qDebug() << "No layer specified! (GeoSceneLayer * layer = 0)";
        return;
    }

    d->m_layer = layer;

    TileId id;
    TextureTile tile( id );

    GeoSceneTexture * texture = static_cast<GeoSceneTexture *>( d->m_layer->groundDataset() );

    tile.loadDataset( texture, 0, 0, 0 );

    // We assume that all tiles have the same size. TODO: check to be safe
    d->m_tileWidth  = tile.rawtile().width();
    d->m_tileHeight = tile.rawtile().height();
}

void TileLoader::resetTilehash()
{
    QHash<TileId, TextureTile*>::const_iterator it = d->m_tilesOnDisplay.constBegin();
    while ( it != d->m_tilesOnDisplay.constEnd() ) {
        it.value()->setUsed( false );
        ++it;
    }
}

void TileLoader::cleanupTilehash()
{
    // Make sure that tiles which haven't been used during the last
    // rendering of the map at all get removed from the tile hash.

    QHashIterator<TileId, TextureTile*> it( d->m_tilesOnDisplay );
    while ( it.hasNext() ) {
        it.next();
        if ( !it.value()->used() ) {
            // If insert call result is false then the cache is too small to store the tile
            // but the item will get deleted nevertheless and the pointer we have
            // doesn't get set to zero (so don't delete it in this case or it will crash!)
            d->m_tileCache.insert( it.key(), it.value(), it.value()->numBytes() );
            d->m_tilesOnDisplay.remove( it.key() );
        }
    }
}

void TileLoader::flush()
{
    // Remove all tiles from m_tilesOnDisplay
    QHashIterator<TileId, TextureTile*> it( d->m_tilesOnDisplay );
    while ( it.hasNext() ) {
        it.next();
        // If insert call result is false then the cache is too small to store the tile
        // but the item will get deleted nevertheless and the pointer we have
        // doesn't get set to zero (so don't delete it in this case or it will crash!)
        d->m_tileCache.insert( it.key(), it.value(), it.value()->numBytes() );
        d->m_tilesOnDisplay.remove( it.key() );
    }

    d->m_tilesOnDisplay.clear();
}

int TileLoader::tileWidth() const
{
    return d->m_tileWidth;
}

int TileLoader::tileHeight() const
{
    return d->m_tileHeight;
}

int TileLoader::globalWidth( int level ) const
{
    if ( !d->m_layer ) return 0;

    GeoSceneTexture * texture = static_cast<GeoSceneTexture *>( d->m_layer->groundDataset() );

    return d->m_tileWidth * TileLoaderHelper::levelToColumn(
                                texture->levelZeroColumns(), level );
}

int TileLoader::globalHeight( int level ) const
{
    if ( !d->m_layer ) return 0;

    GeoSceneTexture * texture = static_cast<GeoSceneTexture *>( d->m_layer->groundDataset() );

    return d->m_tileHeight * TileLoaderHelper::levelToRow(
                                texture->levelZeroRows(), level );
}

TextureTile* TileLoader::loadTile( int tilx, int tily, int tileLevel )
{
    if ( !d->m_layer ) return 0;

    TileId tileId( tileLevel, tilx, tily );

    // check if the tile is in the hash
    TextureTile * tile = d->m_tilesOnDisplay.value( tileId, 0 );
    if ( tile ) {
        tile->setUsed( true );
        return tile;
    }
    // here ends the performance critical section of this method

    // the tile was not in the hash or has been removed because of expiration
    // so check if it is in the cache
    tile = d->m_tileCache.take( tileId );

    GeoSceneTexture * texture = static_cast<GeoSceneTexture *>( d->m_layer->groundDataset() );

    if ( tile ) {
        // the tile was in the cache, but is it up to date?
        const QDateTime now = QDateTime::currentDateTime();

        if ( tile->created().secsTo( now ) < texture->expire()) {
            d->m_tilesOnDisplay[tileId] = tile;
            tile->setUsed( true );
            return tile;
        } else {
            delete tile;
            tile = 0;
        }
    }

    // tile (valid) has not been found in hash or cache, so load it from disk
    // and place it in the hash from where it will get transfered to the cache

    // qDebug() << "load Tile from Disk: " << tileId.toString();
    tile = new TextureTile( tileId );
    d->m_tilesOnDisplay[tileId] = tile;

    // FIXME: Implement asynchronous tile loading
    // d->m_datasetProvider->loadDatasets( tile );

    if ( d->m_downloadManager != 0 ) {
        connect( tile, SIGNAL( downloadTile( QUrl, QString, QString ) ),
                 d->m_downloadManager, SLOT( addJob( QUrl, QString, QString ) ) );
    }
    connect( tile, SIGNAL( tileUpdateDone() ),
             this, SIGNAL( tileUpdateAvailable() ) );

    tile->loadDataset( texture, tileLevel, tilx, tily, &( d->m_tileCache ) );
    tile->initJumpTables( false );

    // TODO should emit signal rather than directly calling paintTile
    // emit paintTile( tile, tilx, tily, tileLevel, d->m_theme, false );
    m_parent->paintTile( tile, tilx, tily, tileLevel, texture, false );

    return tile;
}

GeoSceneLayer * TileLoader::layer() const
{
    return d->m_layer;
}

quint64 TileLoader::volatileCacheLimit() const
{
    return d->m_tileCache.maxCost() / 1024;
}

QList<TileId> TileLoader::tilesOnDisplay() const
{
    QList<TileId> result;
    QHash<TileId, TextureTile*>::const_iterator pos = d->m_tilesOnDisplay.constBegin();
    QHash<TileId, TextureTile*>::const_iterator const end = d->m_tilesOnDisplay.constEnd();
    for (; pos != end; ++pos ) {
        if ( pos.value()->used() ) {
            result.append( pos.key() );
        }
    }
    return result;
}

int TileLoader::maxPartialTileLevel( GeoSceneLayer * layer )
{
    int maxtilelevel = -1;

    if ( !layer ) return maxtilelevel;

    GeoSceneTexture * texture = static_cast<GeoSceneTexture *>( layer->groundDataset() );

    if ( !texture ) return maxtilelevel;

    QString tilepath = MarbleDirs::path( TileLoaderHelper::themeStr( texture ) );
//    qDebug() << "TileLoader::maxPartialTileLevel tilepath" << tilepath;
    QStringList leveldirs = ( QDir( tilepath ) ).entryList( QDir::AllDirs | QDir::NoSymLinks | QDir::NoDotAndDotDot );

    bool ok = true;

    QStringList::const_iterator constIterator = leveldirs.constBegin();
    for (; constIterator != leveldirs.constEnd(); ++constIterator )
    {
        int value = (*constIterator).toInt( &ok, 10 );
        if ( ok && value > maxtilelevel )
            maxtilelevel = value;
    }

//    qDebug() << "Detected maximum tile level that contains data: "
//             << maxtilelevel;

    return maxtilelevel;
}

bool TileLoader::baseTilesAvailable( GeoSceneLayer * layer )
{
    if ( !layer ) return false;

    GeoSceneTexture * texture = static_cast<GeoSceneTexture *>( layer->groundDataset() );

    const int  levelZeroColumns = texture->levelZeroColumns();
    const int  levelZeroRows    = texture->levelZeroRows();

    bool noerr = true;

    // Check whether the tiles from the lowest texture level are available
    //
    for ( int column = 0; noerr && column < levelZeroColumns; ++column ) {
        for ( int row = 0; noerr && row < levelZeroRows; ++row ) {

            const QString tilepath = MarbleDirs::path( TileLoaderHelper::relativeTileFileName(
                texture, 0, column, row ));
            noerr = QFile::exists( tilepath );
        }
    }

    return noerr;
}

void TileLoader::setVolatileCacheLimit( quint64 kiloBytes )
{
    qDebug() << QString("Setting tile cache to %1 kilobytes.").arg( kiloBytes );
    d->m_tileCache.setMaxCost( kiloBytes * 1024 );
}

void TileLoader::reloadTile( const QString &idStr )
{
    if ( !d->m_layer ) return;

//    qDebug() << "TileLoader::reloadTile:" << idStr;

    const TileId id = TileId::fromString( idStr );
    if ( d->m_tilesOnDisplay.contains( id ) ) {
        int  level = id.zoomLevel();
        int  y     = id.y();
        int  x     = id.x();

        // TODO should emit signal rather than directly calling paintTile
//         emit paintTile( d->m_tilesOnDisplay[id], x, y, level, d->m_theme, true );
        GeoSceneTexture * texture = static_cast<GeoSceneTexture *>( d->m_layer->groundDataset() );

        (d->m_tilesOnDisplay[id])->loadDataset( texture, level, x, y, &( d->m_tileCache ) );
        m_parent->paintTile( d->m_tilesOnDisplay[id], x, y, level, texture, true );
//         (d->m_tilesOnDisplay[id])->reloadTile( x, y, level, d->m_theme );
    } else {
      // Remove "false" tile from cache so it doesn't get loaded anymore
      d->m_tileCache.remove( id );
      qDebug() << "No such ID:" << idStr;
    }
}

void TileLoader::reloadTile( const QString &relativeUrlString, const QString &_id )
{
    Q_UNUSED( relativeUrlString );
    // qDebug() << "Reloading Tile" << relativeUrlString << "id:" << _id;

    reloadTile( _id );
}

void TileLoader::reloadTile( const QString& serverUrlString, const QString &relativeUrlString,
                             const QString &_id )
{
    Q_UNUSED( serverUrlString );
    Q_UNUSED( relativeUrlString );
    // qDebug() << "Reloading Tile" << serverUrlString << relativeUrlString << "id:" << _id;

    reloadTile( _id );
}

void TileLoader::update()
{
    qDebug() << "TileLoader::update()";
    flush(); // trigger a reload of all tiles that are currently in use
    d->m_tileCache.clear(); // clear the tile cache in physical memory
    emit tileUpdateAvailable();
}

}

#include "TileLoader.moc"
<commit_msg>Cache end iterators.<commit_after>/**
 * This file is part of the Marble Desktop Globe.
 *
 * Copyright 2005-2007 Torsten Rahn <tackat@kde.org>"
 * Copyright 2007      Inge Wallin  <ingwa@kde.org>"
 * Copyright 2008,2009 Jens-Michael Hoffmann <jensmh@gmx.de>
 *
 * 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; see the file COPYING.LIB.  If not, write to
 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301, USA.
 */

#include "TileLoader.h"

#include "global.h"
#include "GeoSceneLayer.h"
#include "GeoSceneTexture.h"
#include "HttpDownloadManager.h"
#include "DatasetProvider.h"
#include "TextureTile.h"
#include "MarbleDirs.h"
#include "MarbleModel.h"
#include "TileLoaderHelper.h"

#include <QtCore/QCache>
#include <QtCore/QDebug>
#include <QtCore/QHash>
#include <QtGui/QImage>

#ifdef Q_CC_MSVC
# ifndef KDEWIN_MATH_H
   long double log(int i) { return log((long double)i); }
# endif
#endif

namespace Marble
{

class TileLoaderPrivate
{
    public:
        TileLoaderPrivate()
            : m_datasetProvider( 0 ),
              m_downloadManager( 0 ),
              m_layer( 0 ),
              m_tileWidth( 0 ),
              m_tileHeight( 0 )
        {
            m_tileCache.setMaxCost( 20000 * 1024 ); // Cache size measured in bytes
        }

        DatasetProvider *m_datasetProvider;
        HttpDownloadManager *m_downloadManager;
        GeoSceneLayer *m_layer;
        QHash <TileId, TextureTile*>  m_tilesOnDisplay;
        int           m_tileWidth;
        int           m_tileHeight;
        QCache <TileId, TextureTile>  m_tileCache;
};

TileLoader::TileLoader( HttpDownloadManager *downloadManager, MarbleModel* parent)
    : d( new TileLoaderPrivate() ),
      m_parent(parent)
{
    setDownloadManager( downloadManager );
}

TileLoader::~TileLoader()
{
    flush();
    d->m_tileCache.clear();
    if ( d->m_downloadManager != 0 )
        d->m_downloadManager->disconnect( this );

    delete d;
}

void TileLoader::setDownloadManager( HttpDownloadManager *downloadManager )
{
    if ( d->m_downloadManager != 0 ) {
        d->m_downloadManager->disconnect( this );
        d->m_downloadManager = 0;
    }

    d->m_downloadManager = downloadManager;
    if ( d->m_downloadManager != 0 ) {
        connect( d->m_downloadManager, SIGNAL( downloadComplete( QString, QString ) ),
                 this,              SLOT( reloadTile( QString, QString ) ) );
    }
}

void TileLoader::setLayer( GeoSceneLayer * layer )
{
    // Initialize map theme.
    flush();
    d->m_tileCache.clear();

    if ( !layer ) {
        qDebug() << "No layer specified! (GeoSceneLayer * layer = 0)";
        return;
    }

    d->m_layer = layer;

    TileId id;
    TextureTile tile( id );

    GeoSceneTexture * texture = static_cast<GeoSceneTexture *>( d->m_layer->groundDataset() );

    tile.loadDataset( texture, 0, 0, 0 );

    // We assume that all tiles have the same size. TODO: check to be safe
    d->m_tileWidth  = tile.rawtile().width();
    d->m_tileHeight = tile.rawtile().height();
}

void TileLoader::resetTilehash()
{
    QHash<TileId, TextureTile*>::const_iterator pos = d->m_tilesOnDisplay.constBegin();
    QHash<TileId, TextureTile*>::const_iterator const end = d->m_tilesOnDisplay.constEnd();
    for (; pos != end; ++pos ) {
        pos.value()->setUsed( false );
    }
}

void TileLoader::cleanupTilehash()
{
    // Make sure that tiles which haven't been used during the last
    // rendering of the map at all get removed from the tile hash.

    QHashIterator<TileId, TextureTile*> it( d->m_tilesOnDisplay );
    while ( it.hasNext() ) {
        it.next();
        if ( !it.value()->used() ) {
            // If insert call result is false then the cache is too small to store the tile
            // but the item will get deleted nevertheless and the pointer we have
            // doesn't get set to zero (so don't delete it in this case or it will crash!)
            d->m_tileCache.insert( it.key(), it.value(), it.value()->numBytes() );
            d->m_tilesOnDisplay.remove( it.key() );
        }
    }
}

void TileLoader::flush()
{
    // Remove all tiles from m_tilesOnDisplay
    QHashIterator<TileId, TextureTile*> it( d->m_tilesOnDisplay );
    while ( it.hasNext() ) {
        it.next();
        // If insert call result is false then the cache is too small to store the tile
        // but the item will get deleted nevertheless and the pointer we have
        // doesn't get set to zero (so don't delete it in this case or it will crash!)
        d->m_tileCache.insert( it.key(), it.value(), it.value()->numBytes() );
        d->m_tilesOnDisplay.remove( it.key() );
    }

    d->m_tilesOnDisplay.clear();
}

int TileLoader::tileWidth() const
{
    return d->m_tileWidth;
}

int TileLoader::tileHeight() const
{
    return d->m_tileHeight;
}

int TileLoader::globalWidth( int level ) const
{
    if ( !d->m_layer ) return 0;

    GeoSceneTexture * texture = static_cast<GeoSceneTexture *>( d->m_layer->groundDataset() );

    return d->m_tileWidth * TileLoaderHelper::levelToColumn(
                                texture->levelZeroColumns(), level );
}

int TileLoader::globalHeight( int level ) const
{
    if ( !d->m_layer ) return 0;

    GeoSceneTexture * texture = static_cast<GeoSceneTexture *>( d->m_layer->groundDataset() );

    return d->m_tileHeight * TileLoaderHelper::levelToRow(
                                texture->levelZeroRows(), level );
}

TextureTile* TileLoader::loadTile( int tilx, int tily, int tileLevel )
{
    if ( !d->m_layer ) return 0;

    TileId tileId( tileLevel, tilx, tily );

    // check if the tile is in the hash
    TextureTile * tile = d->m_tilesOnDisplay.value( tileId, 0 );
    if ( tile ) {
        tile->setUsed( true );
        return tile;
    }
    // here ends the performance critical section of this method

    // the tile was not in the hash or has been removed because of expiration
    // so check if it is in the cache
    tile = d->m_tileCache.take( tileId );

    GeoSceneTexture * texture = static_cast<GeoSceneTexture *>( d->m_layer->groundDataset() );

    if ( tile ) {
        // the tile was in the cache, but is it up to date?
        const QDateTime now = QDateTime::currentDateTime();

        if ( tile->created().secsTo( now ) < texture->expire()) {
            d->m_tilesOnDisplay[tileId] = tile;
            tile->setUsed( true );
            return tile;
        } else {
            delete tile;
            tile = 0;
        }
    }

    // tile (valid) has not been found in hash or cache, so load it from disk
    // and place it in the hash from where it will get transfered to the cache

    // qDebug() << "load Tile from Disk: " << tileId.toString();
    tile = new TextureTile( tileId );
    d->m_tilesOnDisplay[tileId] = tile;

    // FIXME: Implement asynchronous tile loading
    // d->m_datasetProvider->loadDatasets( tile );

    if ( d->m_downloadManager != 0 ) {
        connect( tile, SIGNAL( downloadTile( QUrl, QString, QString ) ),
                 d->m_downloadManager, SLOT( addJob( QUrl, QString, QString ) ) );
    }
    connect( tile, SIGNAL( tileUpdateDone() ),
             this, SIGNAL( tileUpdateAvailable() ) );

    tile->loadDataset( texture, tileLevel, tilx, tily, &( d->m_tileCache ) );
    tile->initJumpTables( false );

    // TODO should emit signal rather than directly calling paintTile
    // emit paintTile( tile, tilx, tily, tileLevel, d->m_theme, false );
    m_parent->paintTile( tile, tilx, tily, tileLevel, texture, false );

    return tile;
}

GeoSceneLayer * TileLoader::layer() const
{
    return d->m_layer;
}

quint64 TileLoader::volatileCacheLimit() const
{
    return d->m_tileCache.maxCost() / 1024;
}

QList<TileId> TileLoader::tilesOnDisplay() const
{
    QList<TileId> result;
    QHash<TileId, TextureTile*>::const_iterator pos = d->m_tilesOnDisplay.constBegin();
    QHash<TileId, TextureTile*>::const_iterator const end = d->m_tilesOnDisplay.constEnd();
    for (; pos != end; ++pos ) {
        if ( pos.value()->used() ) {
            result.append( pos.key() );
        }
    }
    return result;
}

int TileLoader::maxPartialTileLevel( GeoSceneLayer * layer )
{
    int maxtilelevel = -1;

    if ( !layer ) return maxtilelevel;

    GeoSceneTexture * texture = static_cast<GeoSceneTexture *>( layer->groundDataset() );

    if ( !texture ) return maxtilelevel;

    QString tilepath = MarbleDirs::path( TileLoaderHelper::themeStr( texture ) );
//    qDebug() << "TileLoader::maxPartialTileLevel tilepath" << tilepath;
    QStringList leveldirs = ( QDir( tilepath ) ).entryList( QDir::AllDirs | QDir::NoSymLinks | QDir::NoDotAndDotDot );

    bool ok = true;

    QStringList::const_iterator pos = leveldirs.constBegin();
    QStringList::const_iterator const end = leveldirs.constEnd();
    for (; pos != end; ++pos ) {
        int value = (*pos).toInt( &ok, 10 );
        if ( ok && value > maxtilelevel )
            maxtilelevel = value;
    }

//    qDebug() << "Detected maximum tile level that contains data: "
//             << maxtilelevel;

    return maxtilelevel;
}

bool TileLoader::baseTilesAvailable( GeoSceneLayer * layer )
{
    if ( !layer ) return false;

    GeoSceneTexture * texture = static_cast<GeoSceneTexture *>( layer->groundDataset() );

    const int  levelZeroColumns = texture->levelZeroColumns();
    const int  levelZeroRows    = texture->levelZeroRows();

    bool noerr = true;

    // Check whether the tiles from the lowest texture level are available
    //
    for ( int column = 0; noerr && column < levelZeroColumns; ++column ) {
        for ( int row = 0; noerr && row < levelZeroRows; ++row ) {

            const QString tilepath = MarbleDirs::path( TileLoaderHelper::relativeTileFileName(
                texture, 0, column, row ));
            noerr = QFile::exists( tilepath );
        }
    }

    return noerr;
}

void TileLoader::setVolatileCacheLimit( quint64 kiloBytes )
{
    qDebug() << QString("Setting tile cache to %1 kilobytes.").arg( kiloBytes );
    d->m_tileCache.setMaxCost( kiloBytes * 1024 );
}

void TileLoader::reloadTile( const QString &idStr )
{
    if ( !d->m_layer ) return;

//    qDebug() << "TileLoader::reloadTile:" << idStr;

    const TileId id = TileId::fromString( idStr );
    if ( d->m_tilesOnDisplay.contains( id ) ) {
        int  level = id.zoomLevel();
        int  y     = id.y();
        int  x     = id.x();

        // TODO should emit signal rather than directly calling paintTile
//         emit paintTile( d->m_tilesOnDisplay[id], x, y, level, d->m_theme, true );
        GeoSceneTexture * texture = static_cast<GeoSceneTexture *>( d->m_layer->groundDataset() );

        (d->m_tilesOnDisplay[id])->loadDataset( texture, level, x, y, &( d->m_tileCache ) );
        m_parent->paintTile( d->m_tilesOnDisplay[id], x, y, level, texture, true );
//         (d->m_tilesOnDisplay[id])->reloadTile( x, y, level, d->m_theme );
    } else {
      // Remove "false" tile from cache so it doesn't get loaded anymore
      d->m_tileCache.remove( id );
      qDebug() << "No such ID:" << idStr;
    }
}

void TileLoader::reloadTile( const QString &relativeUrlString, const QString &_id )
{
    Q_UNUSED( relativeUrlString );
    // qDebug() << "Reloading Tile" << relativeUrlString << "id:" << _id;

    reloadTile( _id );
}

void TileLoader::reloadTile( const QString& serverUrlString, const QString &relativeUrlString,
                             const QString &_id )
{
    Q_UNUSED( serverUrlString );
    Q_UNUSED( relativeUrlString );
    // qDebug() << "Reloading Tile" << serverUrlString << relativeUrlString << "id:" << _id;

    reloadTile( _id );
}

void TileLoader::update()
{
    qDebug() << "TileLoader::update()";
    flush(); // trigger a reload of all tiles that are currently in use
    d->m_tileCache.clear(); // clear the tile cache in physical memory
    emit tileUpdateAvailable();
}

}

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


<commit_before>// Copyright 2010 Jens-Michael Hoffmann <jmho@c-xx.com>
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 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
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library. If not, see <http://www.gnu.org/licenses/>.

#include "TileLoader.h"

#include <QtCore/QDateTime>
#include <QtCore/QFileInfo>
#include <QtCore/QMetaType>
#include <QtGui/QImage>

#include "GeoSceneTexture.h"
#include "HttpDownloadManager.h"
#include "MarbleDebug.h"
#include "MarbleDirs.h"
#include "TileLoaderHelper.h"

Q_DECLARE_METATYPE( Marble::DownloadUsage )

namespace Marble
{

TileLoader::TileLoader( HttpDownloadManager * const downloadManager )
{
    qRegisterMetaType<DownloadUsage>( "DownloadUsage" );
    connect( this, SIGNAL( downloadTile( QUrl, QString, QString, DownloadUsage )),
             downloadManager, SLOT( addJob( QUrl, QString, QString, DownloadUsage )));
    connect( downloadManager, SIGNAL( downloadComplete( QByteArray, QString )),
             SLOT( updateTile( QByteArray, QString )));
}

// If the tile is locally available:
//     - if not expired: create TextureTile, set state to "uptodate", return it => done
//     - if expired: create TextureTile, state is set to Expired by default, trigger dl,

QImage TileLoader::loadTile( GeoSceneTexture const *textureLayer, TileId const & tileId, DownloadUsage const usage )
{
    QString const fileName = tileFileName( textureLayer, tileId );
    TileStatus status = tileStatus( textureLayer, tileId );
    if ( status != Missing ) {
        // check if an update should be triggered

        if ( status == Available ) {
            mDebug() << Q_FUNC_INFO << tileId << "StateUptodate";
        } else {
            Q_ASSERT( status == Expired );
            mDebug() << Q_FUNC_INFO << tileId << "StateExpired";
            triggerDownload( textureLayer, tileId, usage );
        }

        QImage const image( fileName );
        if ( !image.isNull() ) {
            // file is there, so create and return a tile object in any case
            return image;
        }
    }

    // tile was not locally available => trigger download and look for tiles in other levels
    // for scaling
    QImage replacementTile = scaledLowerLevelTile( textureLayer, tileId );
    Q_ASSERT( !replacementTile.isNull() );

    triggerDownload( textureLayer, tileId, usage );

    return replacementTile;
}

// This method triggers a download of the given tile (without checking
// expiration). It is called by upper layer (StackedTileLoader) when the tile
// that should be reloaded is currently loaded in memory.
//
// post condition
//     - download is triggered
void TileLoader::downloadTile( GeoSceneTexture const *textureLayer, TileId const &tileId, DownloadUsage const usage )
{
    triggerDownload( textureLayer, tileId, usage );
}

int TileLoader::maximumTileLevel( GeoSceneTexture const & texture )
{
    // if maximum tile level is configured in the DGML files,
    // then use it, otherwise use old detection code.
    if ( texture.maximumTileLevel() >= 0 ) {
        return texture.maximumTileLevel();
    }

    int maximumTileLevel = -1;
    const QFileInfo themeStr( texture.themeStr() );
    const QString tilepath = themeStr.isAbsolute() ? themeStr.absoluteFilePath() : MarbleDirs::path( texture.themeStr() );
    //    mDebug() << "StackedTileLoader::maxPartialTileLevel tilepath" << tilepath;
    QStringList leveldirs = QDir( tilepath ).entryList( QDir::AllDirs | QDir::NoSymLinks
                                                        | QDir::NoDotAndDotDot );

    QStringList::const_iterator it = leveldirs.constBegin();
    QStringList::const_iterator const end = leveldirs.constEnd();
    for (; it != end; ++it ) {
        bool ok = true;
        const int value = (*it).toInt( &ok, 10 );

        if ( ok && value > maximumTileLevel )
            maximumTileLevel = value;
    }

    //    mDebug() << "Detected maximum tile level that contains data: "
    //             << maxtilelevel;
    return maximumTileLevel + 1;
}

bool TileLoader::baseTilesAvailable( GeoSceneTexture const & texture )
{
    const int  levelZeroColumns = texture.levelZeroColumns();
    const int  levelZeroRows    = texture.levelZeroRows();

    bool result = true;

    // Check whether the tiles from the lowest texture level are available
    //
    for ( int column = 0; result && column < levelZeroColumns; ++column ) {
        for ( int row = 0; result && row < levelZeroRows; ++row ) {
            const TileId id( texture.sourceDir(), 0, column, row );
            const QString tilepath = tileFileName( &texture, id );
            result &= QFile::exists( tilepath );
        }
    }

    return result;
}

TileLoader::TileStatus TileLoader::tileStatus( GeoSceneTexture const *textureLayer, const TileId &tileId )
{
    QString const fileName = tileFileName( textureLayer, tileId );
    QFileInfo fileInfo( fileName );
    if ( !fileInfo.exists() ) {
        return Missing;
    }

    const QDateTime lastModified = fileInfo.lastModified();
    const int expireSecs = textureLayer->expire();
    const bool isExpired = lastModified.secsTo( QDateTime::currentDateTime() ) >= expireSecs;
    return isExpired ? Expired : Available;
}

void TileLoader::updateTile( QByteArray const & data, QString const & tileId )
{
    TileId const id = TileId::fromString( tileId );

    QImage const tileImage = QImage::fromData( data );
    if ( tileImage.isNull() )
        return;

    emit tileCompleted( id, tileImage );
}

QString TileLoader::tileFileName( GeoSceneTexture const * textureLayer, TileId const & tileId )
{
    QString const fileName = textureLayer->relativeTileFileName( tileId );
    QFileInfo const dirInfo( fileName );
    return dirInfo.isAbsolute() ? fileName : MarbleDirs::path( fileName );
}

void TileLoader::triggerDownload( GeoSceneTexture const *textureLayer, TileId const &id, DownloadUsage const usage )
{
    QUrl const sourceUrl = textureLayer->downloadUrl( id );
    QString const destFileName = textureLayer->relativeTileFileName( id );
    emit downloadTile( sourceUrl, destFileName, id.toString(), usage );
}

QImage TileLoader::scaledLowerLevelTile( GeoSceneTexture const *textureLayer, TileId const & id ) const
{
    mDebug() << Q_FUNC_INFO << id;

    for ( int level = qMax<int>( 0, id.zoomLevel() - 1 ); level >= 0; --level ) {
        int const deltaLevel = id.zoomLevel() - level;
        TileId const replacementTileId( id.mapThemeIdHash(), level,
                                        id.x() >> deltaLevel, id.y() >> deltaLevel );
        QString const fileName = tileFileName( textureLayer, replacementTileId );
        mDebug() << "TileLoader::scaledLowerLevelTile" << "trying" << fileName;
        QImage toScale( fileName );

        if ( level == 0 && toScale.isNull() ) {
            mDebug() << "No level zero tile installed in map theme dir. Falling back to a transparent image for now.";
            QSize tileSize = textureLayer->tileSize();
            Q_ASSERT( !tileSize.isEmpty() ); // assured by textureLayer
            toScale = QImage( tileSize, QImage::Format_ARGB32_Premultiplied );
            toScale.fill( qRgba( 0, 0, 0, 0 ) );
        }

        if ( !toScale.isNull() ) {
            // which rect to scale?
            int const restTileX = id.x() % ( 1 << deltaLevel );
            int const restTileY = id.y() % ( 1 << deltaLevel );
            int const partWidth = toScale.width() >> deltaLevel;
            int const partHeight = toScale.height() >> deltaLevel;
            int const startX = restTileX * partWidth;
            int const startY = restTileY * partHeight;
            mDebug() << "QImage::copy:" << startX << startY << partWidth << partHeight;
            QImage const part = toScale.copy( startX, startY, partWidth, partHeight );
            mDebug() << "QImage::scaled:" << toScale.size();
            return part.scaled( toScale.size() );
        }
    }

    Q_ASSERT_X( false, "scaled image", "level zero image missing" ); // not reached
    return QImage();
}

}

#include "TileLoader.moc"
<commit_msg>More detailed debug info when a base tile is missing.<commit_after>// Copyright 2010 Jens-Michael Hoffmann <jmho@c-xx.com>
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 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
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library. If not, see <http://www.gnu.org/licenses/>.

#include "TileLoader.h"

#include <QtCore/QDateTime>
#include <QtCore/QFileInfo>
#include <QtCore/QMetaType>
#include <QtGui/QImage>

#include "GeoSceneTexture.h"
#include "HttpDownloadManager.h"
#include "MarbleDebug.h"
#include "MarbleDirs.h"
#include "TileLoaderHelper.h"

Q_DECLARE_METATYPE( Marble::DownloadUsage )

namespace Marble
{

TileLoader::TileLoader( HttpDownloadManager * const downloadManager )
{
    qRegisterMetaType<DownloadUsage>( "DownloadUsage" );
    connect( this, SIGNAL( downloadTile( QUrl, QString, QString, DownloadUsage )),
             downloadManager, SLOT( addJob( QUrl, QString, QString, DownloadUsage )));
    connect( downloadManager, SIGNAL( downloadComplete( QByteArray, QString )),
             SLOT( updateTile( QByteArray, QString )));
}

// If the tile is locally available:
//     - if not expired: create TextureTile, set state to "uptodate", return it => done
//     - if expired: create TextureTile, state is set to Expired by default, trigger dl,

QImage TileLoader::loadTile( GeoSceneTexture const *textureLayer, TileId const & tileId, DownloadUsage const usage )
{
    QString const fileName = tileFileName( textureLayer, tileId );
    TileStatus status = tileStatus( textureLayer, tileId );
    if ( status != Missing ) {
        // check if an update should be triggered

        if ( status == Available ) {
            mDebug() << Q_FUNC_INFO << tileId << "StateUptodate";
        } else {
            Q_ASSERT( status == Expired );
            mDebug() << Q_FUNC_INFO << tileId << "StateExpired";
            triggerDownload( textureLayer, tileId, usage );
        }

        QImage const image( fileName );
        if ( !image.isNull() ) {
            // file is there, so create and return a tile object in any case
            return image;
        }
    }

    // tile was not locally available => trigger download and look for tiles in other levels
    // for scaling
    QImage replacementTile = scaledLowerLevelTile( textureLayer, tileId );
    Q_ASSERT( !replacementTile.isNull() );

    triggerDownload( textureLayer, tileId, usage );

    return replacementTile;
}

// This method triggers a download of the given tile (without checking
// expiration). It is called by upper layer (StackedTileLoader) when the tile
// that should be reloaded is currently loaded in memory.
//
// post condition
//     - download is triggered
void TileLoader::downloadTile( GeoSceneTexture const *textureLayer, TileId const &tileId, DownloadUsage const usage )
{
    triggerDownload( textureLayer, tileId, usage );
}

int TileLoader::maximumTileLevel( GeoSceneTexture const & texture )
{
    // if maximum tile level is configured in the DGML files,
    // then use it, otherwise use old detection code.
    if ( texture.maximumTileLevel() >= 0 ) {
        return texture.maximumTileLevel();
    }

    int maximumTileLevel = -1;
    const QFileInfo themeStr( texture.themeStr() );
    const QString tilepath = themeStr.isAbsolute() ? themeStr.absoluteFilePath() : MarbleDirs::path( texture.themeStr() );
    //    mDebug() << "StackedTileLoader::maxPartialTileLevel tilepath" << tilepath;
    QStringList leveldirs = QDir( tilepath ).entryList( QDir::AllDirs | QDir::NoSymLinks
                                                        | QDir::NoDotAndDotDot );

    QStringList::const_iterator it = leveldirs.constBegin();
    QStringList::const_iterator const end = leveldirs.constEnd();
    for (; it != end; ++it ) {
        bool ok = true;
        const int value = (*it).toInt( &ok, 10 );

        if ( ok && value > maximumTileLevel )
            maximumTileLevel = value;
    }

    //    mDebug() << "Detected maximum tile level that contains data: "
    //             << maxtilelevel;
    return maximumTileLevel + 1;
}

bool TileLoader::baseTilesAvailable( GeoSceneTexture const & texture )
{
    const int  levelZeroColumns = texture.levelZeroColumns();
    const int  levelZeroRows    = texture.levelZeroRows();

    bool result = true;

    // Check whether the tiles from the lowest texture level are available
    //
    for ( int column = 0; result && column < levelZeroColumns; ++column ) {
        for ( int row = 0; result && row < levelZeroRows; ++row ) {
            const TileId id( texture.sourceDir(), 0, column, row );
            const QString tilepath = tileFileName( &texture, id );
            result &= QFile::exists( tilepath );
            if (!result) {
                mDebug() << "Base tile " << texture.relativeTileFileName( id ) << " is missing for source dir " << texture.sourceDir();
            }
        }
    }

    return result;
}

TileLoader::TileStatus TileLoader::tileStatus( GeoSceneTexture const *textureLayer, const TileId &tileId )
{
    QString const fileName = tileFileName( textureLayer, tileId );
    QFileInfo fileInfo( fileName );
    if ( !fileInfo.exists() ) {
        return Missing;
    }

    const QDateTime lastModified = fileInfo.lastModified();
    const int expireSecs = textureLayer->expire();
    const bool isExpired = lastModified.secsTo( QDateTime::currentDateTime() ) >= expireSecs;
    return isExpired ? Expired : Available;
}

void TileLoader::updateTile( QByteArray const & data, QString const & tileId )
{
    TileId const id = TileId::fromString( tileId );

    QImage const tileImage = QImage::fromData( data );
    if ( tileImage.isNull() )
        return;

    emit tileCompleted( id, tileImage );
}

QString TileLoader::tileFileName( GeoSceneTexture const * textureLayer, TileId const & tileId )
{
    QString const fileName = textureLayer->relativeTileFileName( tileId );
    QFileInfo const dirInfo( fileName );
    return dirInfo.isAbsolute() ? fileName : MarbleDirs::path( fileName );
}

void TileLoader::triggerDownload( GeoSceneTexture const *textureLayer, TileId const &id, DownloadUsage const usage )
{
    QUrl const sourceUrl = textureLayer->downloadUrl( id );
    QString const destFileName = textureLayer->relativeTileFileName( id );
    emit downloadTile( sourceUrl, destFileName, id.toString(), usage );
}

QImage TileLoader::scaledLowerLevelTile( GeoSceneTexture const *textureLayer, TileId const & id ) const
{
    mDebug() << Q_FUNC_INFO << id;

    for ( int level = qMax<int>( 0, id.zoomLevel() - 1 ); level >= 0; --level ) {
        int const deltaLevel = id.zoomLevel() - level;
        TileId const replacementTileId( id.mapThemeIdHash(), level,
                                        id.x() >> deltaLevel, id.y() >> deltaLevel );
        QString const fileName = tileFileName( textureLayer, replacementTileId );
        mDebug() << "TileLoader::scaledLowerLevelTile" << "trying" << fileName;
        QImage toScale( fileName );

        if ( level == 0 && toScale.isNull() ) {
            mDebug() << "No level zero tile installed in map theme dir. Falling back to a transparent image for now.";
            QSize tileSize = textureLayer->tileSize();
            Q_ASSERT( !tileSize.isEmpty() ); // assured by textureLayer
            toScale = QImage( tileSize, QImage::Format_ARGB32_Premultiplied );
            toScale.fill( qRgba( 0, 0, 0, 0 ) );
        }

        if ( !toScale.isNull() ) {
            // which rect to scale?
            int const restTileX = id.x() % ( 1 << deltaLevel );
            int const restTileY = id.y() % ( 1 << deltaLevel );
            int const partWidth = toScale.width() >> deltaLevel;
            int const partHeight = toScale.height() >> deltaLevel;
            int const startX = restTileX * partWidth;
            int const startY = restTileY * partHeight;
            mDebug() << "QImage::copy:" << startX << startY << partWidth << partHeight;
            QImage const part = toScale.copy( startX, startY, partWidth, partHeight );
            mDebug() << "QImage::scaled:" << toScale.size();
            return part.scaled( toScale.size() );
        }
    }

    Q_ASSERT_X( false, "scaled image", "level zero image missing" ); // not reached
    return QImage();
}

}

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