<commit_before>// Implementation of functions declared in FLUID designer

#include <iostream>
#include <fstream>
#include <thread>
#include <chrono>

#include <FL/Fl.H>

#include "configuration.h"
#include "gnss_transceiver.h"
#include "ui.h"

void Ui::apply_settings()
{
  Configuration conf{transceiver_->config()};
  try {
    conf.device_id = std::stoul(text_device_id->value());
    conf.gnss_port = text_gnss_port->value();
    conf.use_msg_rmc = check_rmc->value();
    conf.use_msg_gga = check_gga->value();
    conf.use_msg_gsv = check_gsv->value();
    conf.use_msg_other = check_other->value();
    conf.trans_ip = text_trans_ip->value();
    conf.trans_port = std::stoul(text_trans_port->value());
    conf.recv_ip = text_recv_ip->value();
    conf.recv_port = std::stoul(text_recv_port->value());
    transceiver_->set_config(conf);
  }
  catch (const std::invalid_argument& e) {
    std::cerr << "Error applying settings: " << e.what()
              << "\nSettings were not applied." << std::endl;
  }
  catch (const std::out_of_range& e) {
    std::cerr << "Error applying settings: " << e.what()
              << "\nSettings were not applied." << std::endl;
  }
}

void Ui::load_settings()
{
  auto& conf = transceiver_->config();
  text_device_id->value(std::to_string(conf.device_id).c_str());
  text_gnss_port->value(conf.gnss_port.c_str());
  check_rmc->value(conf.use_msg_rmc);
  check_gga->value(conf.use_msg_gga);
  check_gsv->value(conf.use_msg_gsv);
  check_other->value(conf.use_msg_other);
  text_trans_ip->value(conf.trans_ip.c_str());
  text_trans_port->value(std::to_string(conf.trans_port).c_str());
  text_recv_ip->value(conf.recv_ip.c_str());
  text_recv_port->value(std::to_string(conf.recv_port).c_str());
}

void Ui::button_start_clicked()
{
  if (button_start->value()) {
    // Make sure transceiver isn't running
    if (transceiver_->is_active()) {
      std::cerr << "Transceiver already running!" << std::endl;
      button_start->value(1);
    }
    else {
      std::string filename;
      if (choice_mode->value() > 0) {
        auto t = std::chrono::high_resolution_clock::now().time_since_epoch();
        filename = std::string("logs/log_") + std::to_string(t.count()) + ".csv";
      }
      switch (choice_mode->value()) {
        case 0: transceiver_->start_gnss_transmitter(); break;
        case 1: transceiver_->start_gnss_receiver(filename); break;
        case 2: transceiver_->start_gnss_logger(filename); break;
      }
      button_start->label("Stop");
      last_count_ = 0;
      Fl::add_timeout(1.0, &Ui::update_status_callback, this);
    }
  }
  else {
    transceiver_->stop();
    button_start->label("Start");
    Fl::remove_timeout(&Ui::update_status_callback, this);
  }
}

void Ui::button_apply_clicked()
{
  apply_settings();
}

void Ui::button_sync_time_clicked()
{
  // Rough time sync via NTP
  std::cout << "Syncing time, please wait..." << std::endl;
  std::thread t{[]{ system("sudo service ntp stop && sudo ntpd -gq && sudo service ntp start"); }};
  t.detach();
}

void Ui::update_status_callback(void* p)
{
  auto* ui = reinterpret_cast<Ui*>(p);
  ui->update_status();
  Fl::repeat_timeout(1.0, &Ui::update_status_callback, ui);
}

void Ui::update_status()
{
  // Rate (intented to be in hz) only works when UI is updated each second
  auto count = transceiver_->activity_count();
  auto rate = count - last_count_;
  last_count_ = count;

  text_rate->value(std::to_string(rate).c_str());
  text_total->value(std::to_string(count).c_str());
}
<commit_msg>Added check to prevent time sync while running<commit_after>// Implementation of functions declared in FLUID designer

#include <iostream>
#include <fstream>
#include <thread>
#include <chrono>

#include <FL/Fl.H>

#include "configuration.h"
#include "gnss_transceiver.h"
#include "ui.h"

void Ui::apply_settings()
{
  Configuration conf{transceiver_->config()};
  try {
    conf.device_id = std::stoul(text_device_id->value());
    conf.gnss_port = text_gnss_port->value();
    conf.use_msg_rmc = check_rmc->value();
    conf.use_msg_gga = check_gga->value();
    conf.use_msg_gsv = check_gsv->value();
    conf.use_msg_other = check_other->value();
    conf.trans_ip = text_trans_ip->value();
    conf.trans_port = std::stoul(text_trans_port->value());
    conf.recv_ip = text_recv_ip->value();
    conf.recv_port = std::stoul(text_recv_port->value());
    transceiver_->set_config(conf);
  }
  catch (const std::invalid_argument& e) {
    std::cerr << "Error applying settings: " << e.what()
              << "\nSettings were not applied." << std::endl;
  }
  catch (const std::out_of_range& e) {
    std::cerr << "Error applying settings: " << e.what()
              << "\nSettings were not applied." << std::endl;
  }
}

void Ui::load_settings()
{
  auto& conf = transceiver_->config();
  text_device_id->value(std::to_string(conf.device_id).c_str());
  text_gnss_port->value(conf.gnss_port.c_str());
  check_rmc->value(conf.use_msg_rmc);
  check_gga->value(conf.use_msg_gga);
  check_gsv->value(conf.use_msg_gsv);
  check_other->value(conf.use_msg_other);
  text_trans_ip->value(conf.trans_ip.c_str());
  text_trans_port->value(std::to_string(conf.trans_port).c_str());
  text_recv_ip->value(conf.recv_ip.c_str());
  text_recv_port->value(std::to_string(conf.recv_port).c_str());
}

void Ui::button_start_clicked()
{
  if (button_start->value()) {
    // Make sure transceiver isn't running
    if (transceiver_->is_active()) {
      std::cerr << "Transceiver already running!" << std::endl;
      button_start->value(1);
    }
    else {
      std::string filename;
      if (choice_mode->value() > 0) {
        auto t = std::chrono::high_resolution_clock::now().time_since_epoch();
        filename = std::string("logs/log_") + std::to_string(t.count()) + ".csv";
      }
      switch (choice_mode->value()) {
        case 0: transceiver_->start_gnss_transmitter(); break;
        case 1: transceiver_->start_gnss_receiver(filename); break;
        case 2: transceiver_->start_gnss_logger(filename); break;
      }
      button_start->label("Stop");
      last_count_ = 0;
      Fl::add_timeout(1.0, &Ui::update_status_callback, this);
    }
  }
  else {
    transceiver_->stop();
    button_start->label("Start");
    Fl::remove_timeout(&Ui::update_status_callback, this);
  }
}

void Ui::button_apply_clicked()
{
  apply_settings();
}

void Ui::button_sync_time_clicked()
{
  // This is just for convenience and not intented as a proper time sync method
  if (!transceiver_->is_active()) {
    // Rough time sync via NTP
    std::cout << "Syncing time, please wait..." << std::endl;
    std::thread t{[]{ system("sudo service ntp stop && sudo ntpd -gq && sudo service ntp start"); }};
    t.detach();
  }
  else std::cerr << "Can't sync time while transceiver is running!" << std::endl;
}

void Ui::update_status_callback(void* p)
{
  auto* ui = reinterpret_cast<Ui*>(p);
  ui->update_status();
  Fl::repeat_timeout(1.0, &Ui::update_status_callback, ui);
}

void Ui::update_status()
{
  // Rate (intented to be in hz) only works when UI is updated each second
  auto count = transceiver_->activity_count();
  auto rate = count - last_count_;
  last_count_ = count;

  text_rate->value(std::to_string(rate).c_str());
  text_total->value(std::to_string(count).c_str());
}
<|endoftext|>


<commit_before><commit_msg>Fix tools/string.hxx remnants<commit_after><|endoftext|>


<commit_before><commit_msg>logging<commit_after><|endoftext|>


<commit_before>/*************************************************************************
 *
 *  OpenOffice.org - a multi-platform office productivity suite
 *
 *  $RCSfile: formula.hxx,v $
 *
 *  $Revision: 1.4 $
 *
 *  last change: $Author: rt $ $Date: 2005-09-08 21:26:26 $
 *
 *  The Contents of this file are made available subject to
 *  the terms of GNU Lesser General Public License Version 2.1.
 *
 *
 *    GNU Lesser General Public License Version 2.1
 *    =============================================
 *    Copyright 2005 by Sun Microsystems, Inc.
 *    901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *    This library is free software; you can redistribute it and/or
 *    modify it under the terms of the GNU Lesser General Public
 *    License version 2.1, as published by the Free Software Foundation.
 *
 *    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, write to the Free Software
 *    Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *    MA  02111-1307  USA
 *
 ************************************************************************/

#ifndef SC_FORMULA_HXX
#define SC_FORMULA_HXX

#ifndef SC_ANYREFDG_HXX
#include "anyrefdg.hxx"
#endif

#ifndef SC_FUNCUTL_HXX
#include "funcutl.hxx"
#endif

#ifndef SC_SCGLOB_HXX
#include "global.hxx"       // ScAddress
#endif

#ifndef _STDCTRL_HXX //autogen
#include <svtools/stdctrl.hxx>
#endif
#ifndef _LSTBOX_HXX //autogen
#include <vcl/lstbox.hxx>
#endif

#ifndef _SV_GROUP_HXX //autogen
#include <vcl/group.hxx>
#endif

#ifndef _SVEDIT_HXX //autogen
#include <svtools/svmedit.hxx>
#endif

#ifndef _SV_TABPAGE_HXX //autogen
#include <vcl/tabpage.hxx>
#endif

#ifndef _SVSTDARR_STRINGS

#define _SVSTDARR_STRINGS
#include <svtools/svstdarr.hxx>

#endif

#ifndef _SV_TABCTRL_HXX //autogen
#include <vcl/tabctrl.hxx>
#endif

#ifndef SC_PARAWIN_HXX
#include "parawin.hxx"
#endif

#ifndef _SVTREEBOX_HXX //autogen
#include <svtools/svtreebx.hxx>
#endif

#ifndef SC_COMPILER_HXX
#include "compiler.hxx"
#endif

#ifndef SC_CELL_HXX
#include "cell.hxx"
#endif

#ifndef SC_FUNCPAGE_HXX
#include "funcpage.hxx"
#endif

#ifndef SC_STRUCTPG_HXX
#include "structpg.hxx"
#endif

class ScViewData;
class ScDocument;
class ScFuncDesc;
class ScInputHandler;
class ScDocShell;

//============================================================================

enum ScFormulaDlgMode { SC_FORMDLG_FORMULA, SC_FORMDLG_ARGS, SC_FORMDLG_EDIT };

//============================================================================

typedef ScTabViewShell* PtrTabViewShell;

//============================================================================

//============================================================================

class ScFormulaDlg : public ScAnyRefDlg
{
public:
                    ScFormulaDlg( SfxBindings* pB, SfxChildWindow* pCW,
                                    Window* pParent, ScViewData* pViewData );
                    ~ScFormulaDlg();

    virtual void    SetReference( const ScRange& rRef, ScDocument* pRefDoc );
    virtual BOOL    IsRefInputMode() const;
    virtual BOOL    IsDocAllowed(SfxObjectShell* pDocSh) const;
    virtual void    SetActive();
    virtual BOOL    Close();

private:

    TabControl      aTabCtrl;
    GroupBox        aGbEdit;        //! MUST be placed before aScParaWin for initializing
    ScParaWin       aScParaWin;
    FixedText       aFtHeadLine;
    FixedInfo       aFtFuncName;
    FixedInfo       aFtFuncDesc;

    FixedText       aFtEditName;
    //FixedInfo     aFtEditDesc;

    FixedText       aFtResult;
    ValWnd          aWndResult;

    FixedText       aFtFormula;
    ScEditBox       aMEFormula;

    CheckBox        aBtnMatrix;
    HelpButton      aBtnHelp;
    CancelButton    aBtnCancel;

    PushButton      aBtnBackward;
    PushButton      aBtnForward;
    OKButton        aBtnEnd;

    ScRefEdit       aEdRef;
    ScRefButton     aRefBtn;

    FixedText       aFtFormResult;
    ValWnd          aWndFormResult;

    ScRefEdit*      pTheRefEdit;
    ScRefButton*    pTheRefButton;
    ScFuncPage*     pScFuncPage;
    ScStructPage*   pScStructPage;
    ScFormulaCell*  pCell;
    ScCompiler*     pComp;
    ScTokenArray*   pScTokA;
    String          aOldFormula;
    BOOL            bStructUpdate;
    MultiLineEdit*  pMEdit;
    BOOL            bUserMatrixFlag;
    Timer           aTimer;

    const String    aTitle1;
    const String    aTitle2;
    const String    aTxtEnd;
    const String    aTxtOk;     // hinter aBtnEnd

    ULONG           nOldHelp;
    ULONG           nOldUnique;
    ULONG           nActivWinId;
    BOOL            bIsShutDown;

    Font            aFntBold;
    Font            aFntLight;
    USHORT          nEdFocus;
//    Selection       theCurSel;
    BOOL            bEditFlag;
    const ScFuncDesc*   pFuncDesc;
    USHORT          nArgs;
    String**        pArgArr;
    Selection       aFuncSel;

    static ScDocument*  pDoc;
    static ScAddress    aCursorPos;

protected:

    virtual long    PreNotify( NotifyEvent& rNEvt );
    virtual void    RefInputStart( ScRefEdit* pEdit, ScRefButton* pButton = NULL );
    virtual void    RefInputDone( BOOL bForced = FALSE );
    ULONG           FindFocusWin(Window *pWin);
    void            SetFocusWin(Window *pWin,ULONG nUniqueId);
    String          RepairFormula(const String& aFormula);
    void            SaveLRUEntry(const ScFuncDesc*  pFuncDesc);
    void            HighlightFunctionParas(const String& aFormula);

private:

    BOOL            IsInputHdl(ScInputHandler* pHdl);
    ScInputHandler* GetNextInputHandler(ScDocShell* pDocShell,PtrTabViewShell* ppViewSh=NULL);

    void            MakeTree(SvLBoxEntry* pParent,ScToken* pScToken,long Count,
                                    ScTokenArray* pScTokA,ScCompiler*   pComp);

    void            ClearAllParas();
    void            DeleteArgs();
    void            FillDialog(BOOL nFlag=TRUE);
    void            EditNextFunc( BOOL bForward, xub_StrLen nFStart=NOT_FOUND );
    void            EditThisFunc(xub_StrLen nFStart);
    void            EditFuncParas(xub_StrLen nEditPos);

    void            UpdateArgInput( USHORT nOffset, USHORT nInput );
    BOOL            CalcValue( const String& rStrExp, String& rStrResult );
    BOOL            CalcStruct( const String& rStrExp);

    void            UpdateValues();
    void            SaveArg( USHORT nEd );
    void            UpdateSelection();
    void            DoEnter( BOOL bOk );
    void            UpdateFunctionDesc();
    void            ResizeArgArr( const ScFuncDesc* pNewFunc );
    void            FillListboxes();
    void            FillControls();

    xub_StrLen      GetFunctionPos(xub_StrLen nPos);
    void            UpdateTokenArray( const String& rStrExp);

    ScRefEdit*      GetCurrRefEdit();

    DECL_LINK( ScrollHdl, ScParaWin* );
    DECL_LINK( ModifyHdl, ScParaWin* );
    DECL_LINK( FxHdl, ScParaWin* );

    DECL_LINK( MatrixHdl, CheckBox *);
    DECL_LINK( FormulaHdl, MultiLineEdit* );
    DECL_LINK( FormulaCursorHdl, ScEditBox*);
    DECL_LINK( BtnHdl, PushButton* );
    DECL_LINK( GetEdFocusHdl, ArgInput* );
    DECL_LINK( GetFxFocusHdl, ArgInput* );
    DECL_LINK( DblClkHdl, ScFuncPage* );
    DECL_LINK( FuncSelHdl, ScFuncPage*);
    DECL_LINK( StructSelHdl, ScStructPage * );
    DECL_LINK( UpdateFocusHdl, Timer*);
};

#endif // SC_CRNRDLG_HXX

<commit_msg>INTEGRATION: CWS changefileheader (1.4.700); FILE MERGED 2008/04/01 15:30:53 thb 1.4.700.3: #i85898# Stripping all external header guards 2008/04/01 12:36:45 thb 1.4.700.2: #i85898# Stripping all external header guards 2008/03/31 17:15:41 rt 1.4.700.1: #i87441# Change license header to LPGL v3.<commit_after>/*************************************************************************
 *
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * Copyright 2008 by Sun Microsystems, Inc.
 *
 * OpenOffice.org - a multi-platform office productivity suite
 *
 * $RCSfile: formula.hxx,v $
 * $Revision: 1.5 $
 *
 * This file is part of OpenOffice.org.
 *
 * OpenOffice.org is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License version 3
 * only, as published by the Free Software Foundation.
 *
 * OpenOffice.org 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 version 3 for more details
 * (a copy is included in the LICENSE file that accompanied this code).
 *
 * You should have received a copy of the GNU Lesser General Public License
 * version 3 along with OpenOffice.org.  If not, see
 * <http://www.openoffice.org/license.html>
 * for a copy of the LGPLv3 License.
 *
 ************************************************************************/

#ifndef SC_FORMULA_HXX
#define SC_FORMULA_HXX

#include "anyrefdg.hxx"
#include "funcutl.hxx"
#include "global.hxx"       // ScAddress
#include <svtools/stdctrl.hxx>
#ifndef _LSTBOX_HXX //autogen
#include <vcl/lstbox.hxx>
#endif
#include <vcl/group.hxx>
#include <svtools/svmedit.hxx>
#include <vcl/tabpage.hxx>

#ifndef _SVSTDARR_STRINGS

#define _SVSTDARR_STRINGS
#include <svtools/svstdarr.hxx>

#endif
#include <vcl/tabctrl.hxx>
#include "parawin.hxx"
#include <svtools/svtreebx.hxx>
#include "compiler.hxx"
#include "cell.hxx"
#include "funcpage.hxx"
#include "structpg.hxx"

class ScViewData;
class ScDocument;
class ScFuncDesc;
class ScInputHandler;
class ScDocShell;

//============================================================================

enum ScFormulaDlgMode { SC_FORMDLG_FORMULA, SC_FORMDLG_ARGS, SC_FORMDLG_EDIT };

//============================================================================

typedef ScTabViewShell* PtrTabViewShell;

//============================================================================

//============================================================================

class ScFormulaDlg : public ScAnyRefDlg
{
public:
                    ScFormulaDlg( SfxBindings* pB, SfxChildWindow* pCW,
                                    Window* pParent, ScViewData* pViewData );
                    ~ScFormulaDlg();

    virtual void    SetReference( const ScRange& rRef, ScDocument* pRefDoc );
    virtual BOOL    IsRefInputMode() const;
    virtual BOOL    IsDocAllowed(SfxObjectShell* pDocSh) const;
    virtual void    SetActive();
    virtual BOOL    Close();

private:

    TabControl      aTabCtrl;
    GroupBox        aGbEdit;        //! MUST be placed before aScParaWin for initializing
    ScParaWin       aScParaWin;
    FixedText       aFtHeadLine;
    FixedInfo       aFtFuncName;
    FixedInfo       aFtFuncDesc;

    FixedText       aFtEditName;
    //FixedInfo     aFtEditDesc;

    FixedText       aFtResult;
    ValWnd          aWndResult;

    FixedText       aFtFormula;
    ScEditBox       aMEFormula;

    CheckBox        aBtnMatrix;
    HelpButton      aBtnHelp;
    CancelButton    aBtnCancel;

    PushButton      aBtnBackward;
    PushButton      aBtnForward;
    OKButton        aBtnEnd;

    ScRefEdit       aEdRef;
    ScRefButton     aRefBtn;

    FixedText       aFtFormResult;
    ValWnd          aWndFormResult;

    ScRefEdit*      pTheRefEdit;
    ScRefButton*    pTheRefButton;
    ScFuncPage*     pScFuncPage;
    ScStructPage*   pScStructPage;
    ScFormulaCell*  pCell;
    ScCompiler*     pComp;
    ScTokenArray*   pScTokA;
    String          aOldFormula;
    BOOL            bStructUpdate;
    MultiLineEdit*  pMEdit;
    BOOL            bUserMatrixFlag;
    Timer           aTimer;

    const String    aTitle1;
    const String    aTitle2;
    const String    aTxtEnd;
    const String    aTxtOk;     // hinter aBtnEnd

    ULONG           nOldHelp;
    ULONG           nOldUnique;
    ULONG           nActivWinId;
    BOOL            bIsShutDown;

    Font            aFntBold;
    Font            aFntLight;
    USHORT          nEdFocus;
//    Selection       theCurSel;
    BOOL            bEditFlag;
    const ScFuncDesc*   pFuncDesc;
    USHORT          nArgs;
    String**        pArgArr;
    Selection       aFuncSel;

    static ScDocument*  pDoc;
    static ScAddress    aCursorPos;

protected:

    virtual long    PreNotify( NotifyEvent& rNEvt );
    virtual void    RefInputStart( ScRefEdit* pEdit, ScRefButton* pButton = NULL );
    virtual void    RefInputDone( BOOL bForced = FALSE );
    ULONG           FindFocusWin(Window *pWin);
    void            SetFocusWin(Window *pWin,ULONG nUniqueId);
    String          RepairFormula(const String& aFormula);
    void            SaveLRUEntry(const ScFuncDesc*  pFuncDesc);
    void            HighlightFunctionParas(const String& aFormula);

private:

    BOOL            IsInputHdl(ScInputHandler* pHdl);
    ScInputHandler* GetNextInputHandler(ScDocShell* pDocShell,PtrTabViewShell* ppViewSh=NULL);

    void            MakeTree(SvLBoxEntry* pParent,ScToken* pScToken,long Count,
                                    ScTokenArray* pScTokA,ScCompiler*   pComp);

    void            ClearAllParas();
    void            DeleteArgs();
    void            FillDialog(BOOL nFlag=TRUE);
    void            EditNextFunc( BOOL bForward, xub_StrLen nFStart=NOT_FOUND );
    void            EditThisFunc(xub_StrLen nFStart);
    void            EditFuncParas(xub_StrLen nEditPos);

    void            UpdateArgInput( USHORT nOffset, USHORT nInput );
    BOOL            CalcValue( const String& rStrExp, String& rStrResult );
    BOOL            CalcStruct( const String& rStrExp);

    void            UpdateValues();
    void            SaveArg( USHORT nEd );
    void            UpdateSelection();
    void            DoEnter( BOOL bOk );
    void            UpdateFunctionDesc();
    void            ResizeArgArr( const ScFuncDesc* pNewFunc );
    void            FillListboxes();
    void            FillControls();

    xub_StrLen      GetFunctionPos(xub_StrLen nPos);
    void            UpdateTokenArray( const String& rStrExp);

    ScRefEdit*      GetCurrRefEdit();

    DECL_LINK( ScrollHdl, ScParaWin* );
    DECL_LINK( ModifyHdl, ScParaWin* );
    DECL_LINK( FxHdl, ScParaWin* );

    DECL_LINK( MatrixHdl, CheckBox *);
    DECL_LINK( FormulaHdl, MultiLineEdit* );
    DECL_LINK( FormulaCursorHdl, ScEditBox*);
    DECL_LINK( BtnHdl, PushButton* );
    DECL_LINK( GetEdFocusHdl, ArgInput* );
    DECL_LINK( GetFxFocusHdl, ArgInput* );
    DECL_LINK( DblClkHdl, ScFuncPage* );
    DECL_LINK( FuncSelHdl, ScFuncPage*);
    DECL_LINK( StructSelHdl, ScStructPage * );
    DECL_LINK( UpdateFocusHdl, Timer*);
};

#endif // SC_CRNRDLG_HXX

<|endoftext|>


<commit_before>
#include "serialport.h"
#include <list>
#include "win/disphelper.h"

#include "win/stdafx.h"
#include "win/enumser.h"

#ifdef WIN32

std::list<int> g_closingHandles;
int bufferSize;
void ErrorCodeToString(const char* prefix, int errorCode, char *errorStr) {
  switch(errorCode) {
  case ERROR_FILE_NOT_FOUND:
    sprintf(errorStr, "%s: File not found", prefix);
    break;
  case ERROR_INVALID_HANDLE:
    sprintf(errorStr, "%s: Invalid handle", prefix);
    break;
  case ERROR_ACCESS_DENIED:
    sprintf(errorStr, "%s: Access denied", prefix);
    break;
  case ERROR_OPERATION_ABORTED:
    sprintf(errorStr, "%s: operation aborted", prefix);
    break;
  default:
    sprintf(errorStr, "%s: Unknown error code %d", prefix, errorCode);
    break;
  }
}

void EIO_Open(uv_work_t* req) {
  OpenBaton* data = static_cast<OpenBaton*>(req->data);

  HANDLE file = CreateFile(
    data->path,
    GENERIC_READ | GENERIC_WRITE,
    0,
    NULL,
    OPEN_EXISTING,
    FILE_FLAG_OVERLAPPED,
    NULL);
  if (file == INVALID_HANDLE_VALUE) {
    DWORD errorCode = GetLastError();
    char temp[100];
    sprintf(temp, "Opening %s", data->path);
    ErrorCodeToString(temp, errorCode, data->errorString);
    return;
  }

  bufferSize = data->bufferSize;

  DCB dcb = { 0 };
  dcb.DCBlength = sizeof(DCB);
  if(!BuildCommDCB("9600,n,8,1", &dcb)) {
    ErrorCodeToString("BuildCommDCB", GetLastError(), data->errorString);
    return;
  }

  dcb.fBinary = true;
  dcb.BaudRate = data->baudRate;
  dcb.ByteSize = data->dataBits;
  switch(data->parity) {
  case SERIALPORT_PARITY_NONE:
    dcb.Parity = NOPARITY;
    break;
  case SERIALPORT_PARITY_MARK:
    dcb.Parity = MARKPARITY;
    break;
  case SERIALPORT_PARITY_EVEN:
    dcb.Parity = EVENPARITY;
    break;
  case SERIALPORT_PARITY_ODD:
    dcb.Parity = ODDPARITY;
    break;
  case SERIALPORT_PARITY_SPACE:
    dcb.Parity = SPACEPARITY;
    break;
  }
  switch(data->stopBits) {
  case SERIALPORT_STOPBITS_ONE:
    dcb.StopBits = ONESTOPBIT;
    break;
  case SERIALPORT_STOPBITS_ONE_FIVE:
    dcb.StopBits = ONE5STOPBITS;
    break;
  case SERIALPORT_STOPBITS_TWO:
    dcb.StopBits = TWOSTOPBITS;
    break;
  }

  if(!SetCommState(file, &dcb)) {
    ErrorCodeToString("SetCommState", GetLastError(), data->errorString);
    return;
  }

  // Set the com port read/write timeouts
  DWORD serialBitsPerByte = 8/*std data bits*/ + 1/*start bit*/;
  serialBitsPerByte += (data->parity   == SERIALPORT_PARITY_NONE ) ? 0 : 1;
  serialBitsPerByte += (data->stopBits == SERIALPORT_STOPBITS_ONE) ? 1 : 2;
  DWORD msPerByte = (data->baudRate > 0) ?
                    ((1000 * serialBitsPerByte + data->baudRate - 1) / data->baudRate) :
                    1;
  if (msPerByte < 1) {
    msPerByte = 1;
  }
  COMMTIMEOUTS commTimeouts = {0};
  commTimeouts.ReadIntervalTimeout = msPerByte; // Minimize chance of concatenating of separate serial port packets on read
  commTimeouts.ReadTotalTimeoutMultiplier  = 0; // Do not allow big read timeout when big read buffer used
  commTimeouts.ReadTotalTimeoutConstant    = 1000; // Total read timeout (period of read loop)
  commTimeouts.WriteTotalTimeoutConstant   = 1000; // Const part of write timeout
  commTimeouts.WriteTotalTimeoutMultiplier = msPerByte; // Variable part of write timeout (per byte)
  if(!SetCommTimeouts(file, &commTimeouts)) {
    ErrorCodeToString("SetCommTimeouts", GetLastError(), data->errorString);
    return;
  }

  // Remove garbage data in RX/TX queues
  PurgeComm(file, PURGE_RXCLEAR);
  PurgeComm(file, PURGE_TXCLEAR);

  data->result = (int)file;
}

struct WatchPortBaton {
public:
  HANDLE fd;
  DWORD bytesRead;
  char buffer[100];
  char errorString[1000];
  DWORD errorCode;
  bool disconnected;
  v8::Persistent<v8::Value> dataCallback;
  v8::Persistent<v8::Value> errorCallback;
  v8::Persistent<v8::Value> disconnectedCallback;
};

void EIO_WatchPort(uv_work_t* req) {
  WatchPortBaton* data = static_cast<WatchPortBaton*>(req->data);
  data->bytesRead = 0;
  data->disconnected = false;

  // Event used by GetOverlappedResult(..., TRUE) to wait for incoming data or timeout
  // Event MUST be used if program has several simultaneous asynchronous operations
  // on the same handle (i.e. ReadFile and WriteFile)
  HANDLE hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);

  while(true) {
    OVERLAPPED ov = {0};
    ov.hEvent = hEvent;

    // Start read operation - synchrounous or asynchronous
    DWORD bytesReadSync = 0;
    if(!ReadFile((HANDLE)data->fd, data->buffer, bufferSize, &bytesReadSync, &ov)) {
      data->errorCode = GetLastError();
      if(data->errorCode != ERROR_IO_PENDING) {
        // Read operation error
        if(data->errorCode == ERROR_OPERATION_ABORTED) {
          data->disconnected = true;
        }
        else {
          ErrorCodeToString("Reading from COM port (ReadFile)", data->errorCode, data->errorString);
        }
        break;
      }

      // Read operation is asynchronous and is pending
      // We MUST wait for operation completion before deallocation of OVERLAPPED struct
      // or read data buffer

      // Wait for async read operation completion or timeout
      DWORD bytesReadAsync = 0;
      if(!GetOverlappedResult((HANDLE)data->fd, &ov, &bytesReadAsync, TRUE)) {
        // Read operation error
        data->errorCode = GetLastError();
        if(data->errorCode == ERROR_OPERATION_ABORTED) {
          data->disconnected = true;
        }
        else {
          ErrorCodeToString("Reading from COM port (GetOverlappedResult)", data->errorCode, data->errorString);
        }
        break;
      }
      else {
        // Read operation completed asynchronously
        data->bytesRead = bytesReadAsync;
      }
    }
    else {
      // Read operation completed synchronously
      data->bytesRead = bytesReadSync;
    }

    // Return data received if any
    if(data->bytesRead > 0) {
      break;
    }
  }

  CloseHandle(hEvent);
}

bool IsClosingHandle(int fd) {
  for(std::list<int>::iterator it=g_closingHandles.begin(); it!=g_closingHandles.end(); it++) {
    if(fd == *it) {
      g_closingHandles.remove(fd);
      return true;
    }
  }
  return false;
}

void EIO_AfterWatchPort(uv_work_t* req) {
  WatchPortBaton* data = static_cast<WatchPortBaton*>(req->data);
  if(data->disconnected) {
    v8::Handle<v8::Value> argv[1];
    v8::Function::Cast(*data->disconnectedCallback)->Call(v8::Context::GetCurrent()->Global(), 0, argv);
    goto cleanup;
  }

  if(data->bytesRead > 0) {
    v8::Handle<v8::Value> argv[1];
    argv[0] = node::Buffer::New(data->buffer, data->bytesRead)->handle_;
    v8::Function::Cast(*data->dataCallback)->Call(v8::Context::GetCurrent()->Global(), 1, argv);
  } else if(data->errorCode > 0) {
    if(data->errorCode == ERROR_INVALID_HANDLE && IsClosingHandle((int)data->fd)) {
      goto cleanup;
    } else {
      v8::Handle<v8::Value> argv[1];
      argv[0] = v8::Exception::Error(v8::String::New(data->errorString));
      v8::Function::Cast(*data->errorCallback)->Call(v8::Context::GetCurrent()->Global(), 1, argv);
      Sleep(100); // prevent the errors from occurring too fast
    }
  }
  AfterOpenSuccess((int)data->fd, data->dataCallback, data->disconnectedCallback, data->errorCallback);

cleanup:
  data->dataCallback.Dispose();
  data->errorCallback.Dispose();
  delete data;
  delete req;
}

void AfterOpenSuccess(int fd, v8::Handle<v8::Value> dataCallback, v8::Handle<v8::Value> disconnectedCallback, v8::Handle<v8::Value> errorCallback) {
  WatchPortBaton* baton = new WatchPortBaton();
  memset(baton, 0, sizeof(WatchPortBaton));
  baton->fd = (HANDLE)fd;
  baton->dataCallback = v8::Persistent<v8::Value>::New(dataCallback);
  baton->errorCallback = v8::Persistent<v8::Value>::New(errorCallback);
  baton->disconnectedCallback = v8::Persistent<v8::Value>::New(disconnectedCallback);

  uv_work_t* req = new uv_work_t();
  req->data = baton;

  uv_queue_work(uv_default_loop(), req, EIO_WatchPort, EIO_AfterWatchPort);
}

void EIO_Write(uv_work_t* req) {
  QueuedWrite* queuedWrite = static_cast<QueuedWrite*>(req->data);
  WriteBaton* data = static_cast<WriteBaton*>(queuedWrite->baton);
  data->result = 0;

  OVERLAPPED ov = {0};
  // Event used by GetOverlappedResult(..., TRUE) to wait for outgoing data or timeout
  // Event MUST be used if program has several simultaneous asynchronous operations
  // on the same handle (i.e. ReadFile and WriteFile)
  ov.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);

  // Start write operation - synchrounous or asynchronous
  DWORD bytesWrittenSync = 0;
  if(!WriteFile((HANDLE)data->fd, data->bufferData, data->bufferLength, &bytesWrittenSync, &ov)) {
    DWORD lastError = GetLastError();
    if(lastError != ERROR_IO_PENDING) {
      // Write operation error
      ErrorCodeToString("Writing to COM port (WriteFile)", lastError, data->errorString);
    }
    else {
      // Write operation is asynchronous and is pending
      // We MUST wait for operation completion before deallocation of OVERLAPPED struct
      // or write data buffer

      // Wait for async write operation completion or timeout
      DWORD bytesWrittenAsync = 0;
      if(!GetOverlappedResult((HANDLE)data->fd, &ov, &bytesWrittenAsync, TRUE)) {
        // Write operation error
        DWORD lastError = GetLastError();
        ErrorCodeToString("Writing to COM port (GetOverlappedResult)", lastError, data->errorString);
      }
      else {
        // Write operation completed asynchronously
        data->result = bytesWrittenAsync;
      }
    }
  }
  else {
    // Write operation completed synchronously
    data->result = bytesWrittenSync;
  }

  CloseHandle(ov.hEvent);
}

void EIO_Close(uv_work_t* req) {
  CloseBaton* data = static_cast<CloseBaton*>(req->data);

  g_closingHandles.push_back(data->fd);
  if(!CloseHandle((HANDLE)data->fd)) {
    ErrorCodeToString("closing connection", GetLastError(), data->errorString);
    return;
  }
}

/*
 * listComPorts.c -- list COM ports
 *
 * http://github.com/todbot/usbSearch/
 *
 * 2012, Tod E. Kurt, http://todbot.com/blog/
 *
 *
 * Uses DispHealper : http://disphelper.sourceforge.net/
 *
 * Notable VIDs & PIDs combos:
 * VID 0403 - FTDI
 *
 * VID 0403 / PID 6001 - Arduino Diecimila
 *
 */
void EIO_List(uv_work_t* req) {
  ListBaton* data = static_cast<ListBaton*>(req->data);

  {
    DISPATCH_OBJ(wmiSvc);
    DISPATCH_OBJ(colDevices);

    dhInitialize(TRUE);
    dhToggleExceptions(TRUE);

    dhGetObject(L"winmgmts:{impersonationLevel=impersonate}!\\\\.\\root\\cimv2", NULL, &wmiSvc);
    dhGetValue(L"%o", &colDevices, wmiSvc, L".ExecQuery(%S)", L"Select * from Win32_PnPEntity");

    int port_count = 0;
    FOR_EACH(objDevice, colDevices, NULL) {
      char* name = NULL;
      char* pnpid = NULL;
      char* manu = NULL;
      char* match;

      dhGetValue(L"%s", &name,  objDevice, L".Name");
      dhGetValue(L"%s", &pnpid, objDevice, L".PnPDeviceID");

      if( (match = strstr( name, "(COM" )) != NULL ) { // look for "(COM23)"
        // 'Manufacturuer' can be null, so only get it if we need it
        dhGetValue(L"%s", &manu, objDevice,  L".Manufacturer");
        port_count++;
        char* comname = strtok( match, "()");
        ListResultItem* resultItem = new ListResultItem();
        resultItem->comName = comname;
        resultItem->manufacturer = manu;
        resultItem->pnpId = pnpid;
        data->results.push_back(resultItem);
        dhFreeString(manu);
      }

      dhFreeString(name);
      dhFreeString(pnpid);
    } NEXT(objDevice);

    SAFE_RELEASE(colDevices);
    SAFE_RELEASE(wmiSvc);

    dhUninitialize(TRUE);
  }

  std::vector<UINT> ports;
  if (CEnumerateSerial::UsingQueryDosDevice(ports))
  {
    for (size_t i = 0; i < ports.size(); i++)
    {
      char comname[64] = { 0 };
      sprintf(comname, "COM%u", ports[i]);
      bool bFound = false;
      for (std::list<ListResultItem*>::iterator ri = data->results.begin(); ri != data->results.end(); ri++)
      {
        if (stricmp((*ri)->comName.c_str(), comname) == 0)
        {
          bFound = true;
          break;
        }
      }
      if (!bFound)
      {
        ListResultItem* resultItem = new ListResultItem();
        resultItem->comName = comname;
        resultItem->manufacturer = "";
        resultItem->pnpId = "";
        data->results.push_back(resultItem);
      }
    }
  }
}

void EIO_Flush(uv_work_t* req) {
  FlushBaton* data = static_cast<FlushBaton*>(req->data);

  if(!FlushFileBuffers((HANDLE)data->fd)) {
    ErrorCodeToString("flushing connection", GetLastError(), data->errorString);
    return;
  }
}

#endif
<commit_msg>increase buffer size<commit_after>
#include "serialport.h"
#include <list>
#include "win/disphelper.h"

#include "win/stdafx.h"
#include "win/enumser.h"

#ifdef WIN32

#define MAX_BUFFER_SIZE 1000

std::list<int> g_closingHandles;
int bufferSize;
void ErrorCodeToString(const char* prefix, int errorCode, char *errorStr) {
  switch(errorCode) {
  case ERROR_FILE_NOT_FOUND:
    sprintf(errorStr, "%s: File not found", prefix);
    break;
  case ERROR_INVALID_HANDLE:
    sprintf(errorStr, "%s: Invalid handle", prefix);
    break;
  case ERROR_ACCESS_DENIED:
    sprintf(errorStr, "%s: Access denied", prefix);
    break;
  case ERROR_OPERATION_ABORTED:
    sprintf(errorStr, "%s: operation aborted", prefix);
    break;
  default:
    sprintf(errorStr, "%s: Unknown error code %d", prefix, errorCode);
    break;
  }
}

void EIO_Open(uv_work_t* req) {
  OpenBaton* data = static_cast<OpenBaton*>(req->data);

  HANDLE file = CreateFile(
    data->path,
    GENERIC_READ | GENERIC_WRITE,
    0,
    NULL,
    OPEN_EXISTING,
    FILE_FLAG_OVERLAPPED,
    NULL);
  if (file == INVALID_HANDLE_VALUE) {
    DWORD errorCode = GetLastError();
    char temp[100];
    sprintf(temp, "Opening %s", data->path);
    ErrorCodeToString(temp, errorCode, data->errorString);
    return;
  }

  bufferSize = data->bufferSize;
  if(bufferSize > MAX_BUFFER_SIZE) {
    bufferSize = MAX_BUFFER_SIZE;
  }

  DCB dcb = { 0 };
  dcb.DCBlength = sizeof(DCB);
  if(!BuildCommDCB("9600,n,8,1", &dcb)) {
    ErrorCodeToString("BuildCommDCB", GetLastError(), data->errorString);
    return;
  }

  dcb.fBinary = true;
  dcb.BaudRate = data->baudRate;
  dcb.ByteSize = data->dataBits;
  switch(data->parity) {
  case SERIALPORT_PARITY_NONE:
    dcb.Parity = NOPARITY;
    break;
  case SERIALPORT_PARITY_MARK:
    dcb.Parity = MARKPARITY;
    break;
  case SERIALPORT_PARITY_EVEN:
    dcb.Parity = EVENPARITY;
    break;
  case SERIALPORT_PARITY_ODD:
    dcb.Parity = ODDPARITY;
    break;
  case SERIALPORT_PARITY_SPACE:
    dcb.Parity = SPACEPARITY;
    break;
  }
  switch(data->stopBits) {
  case SERIALPORT_STOPBITS_ONE:
    dcb.StopBits = ONESTOPBIT;
    break;
  case SERIALPORT_STOPBITS_ONE_FIVE:
    dcb.StopBits = ONE5STOPBITS;
    break;
  case SERIALPORT_STOPBITS_TWO:
    dcb.StopBits = TWOSTOPBITS;
    break;
  }

  if(!SetCommState(file, &dcb)) {
    ErrorCodeToString("SetCommState", GetLastError(), data->errorString);
    return;
  }

  // Set the com port read/write timeouts
  DWORD serialBitsPerByte = 8/*std data bits*/ + 1/*start bit*/;
  serialBitsPerByte += (data->parity   == SERIALPORT_PARITY_NONE ) ? 0 : 1;
  serialBitsPerByte += (data->stopBits == SERIALPORT_STOPBITS_ONE) ? 1 : 2;
  DWORD msPerByte = (data->baudRate > 0) ?
                    ((1000 * serialBitsPerByte + data->baudRate - 1) / data->baudRate) :
                    1;
  if (msPerByte < 1) {
    msPerByte = 1;
  }
  COMMTIMEOUTS commTimeouts = {0};
  commTimeouts.ReadIntervalTimeout = msPerByte; // Minimize chance of concatenating of separate serial port packets on read
  commTimeouts.ReadTotalTimeoutMultiplier  = 0; // Do not allow big read timeout when big read buffer used
  commTimeouts.ReadTotalTimeoutConstant    = 1000; // Total read timeout (period of read loop)
  commTimeouts.WriteTotalTimeoutConstant   = 1000; // Const part of write timeout
  commTimeouts.WriteTotalTimeoutMultiplier = msPerByte; // Variable part of write timeout (per byte)
  if(!SetCommTimeouts(file, &commTimeouts)) {
    ErrorCodeToString("SetCommTimeouts", GetLastError(), data->errorString);
    return;
  }

  // Remove garbage data in RX/TX queues
  PurgeComm(file, PURGE_RXCLEAR);
  PurgeComm(file, PURGE_TXCLEAR);

  data->result = (int)file;
}

struct WatchPortBaton {
public:
  HANDLE fd;
  DWORD bytesRead;
  char buffer[MAX_BUFFER_SIZE];
  char errorString[1000];
  DWORD errorCode;
  bool disconnected;
  v8::Persistent<v8::Value> dataCallback;
  v8::Persistent<v8::Value> errorCallback;
  v8::Persistent<v8::Value> disconnectedCallback;
};

void EIO_WatchPort(uv_work_t* req) {
  WatchPortBaton* data = static_cast<WatchPortBaton*>(req->data);
  data->bytesRead = 0;
  data->disconnected = false;

  // Event used by GetOverlappedResult(..., TRUE) to wait for incoming data or timeout
  // Event MUST be used if program has several simultaneous asynchronous operations
  // on the same handle (i.e. ReadFile and WriteFile)
  HANDLE hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);

  while(true) {
    OVERLAPPED ov = {0};
    ov.hEvent = hEvent;

    // Start read operation - synchrounous or asynchronous
    DWORD bytesReadSync = 0;
    if(!ReadFile((HANDLE)data->fd, data->buffer, bufferSize, &bytesReadSync, &ov)) {
      data->errorCode = GetLastError();
      if(data->errorCode != ERROR_IO_PENDING) {
        // Read operation error
        if(data->errorCode == ERROR_OPERATION_ABORTED) {
          data->disconnected = true;
        }
        else {
          ErrorCodeToString("Reading from COM port (ReadFile)", data->errorCode, data->errorString);
        }
        break;
      }

      // Read operation is asynchronous and is pending
      // We MUST wait for operation completion before deallocation of OVERLAPPED struct
      // or read data buffer

      // Wait for async read operation completion or timeout
      DWORD bytesReadAsync = 0;
      if(!GetOverlappedResult((HANDLE)data->fd, &ov, &bytesReadAsync, TRUE)) {
        // Read operation error
        data->errorCode = GetLastError();
        if(data->errorCode == ERROR_OPERATION_ABORTED) {
          data->disconnected = true;
        }
        else {
          ErrorCodeToString("Reading from COM port (GetOverlappedResult)", data->errorCode, data->errorString);
        }
        break;
      }
      else {
        // Read operation completed asynchronously
        data->bytesRead = bytesReadAsync;
      }
    }
    else {
      // Read operation completed synchronously
      data->bytesRead = bytesReadSync;
    }

    // Return data received if any
    if(data->bytesRead > 0) {
      break;
    }
  }

  CloseHandle(hEvent);
}

bool IsClosingHandle(int fd) {
  for(std::list<int>::iterator it=g_closingHandles.begin(); it!=g_closingHandles.end(); it++) {
    if(fd == *it) {
      g_closingHandles.remove(fd);
      return true;
    }
  }
  return false;
}

void EIO_AfterWatchPort(uv_work_t* req) {
  WatchPortBaton* data = static_cast<WatchPortBaton*>(req->data);
  if(data->disconnected) {
    v8::Handle<v8::Value> argv[1];
    v8::Function::Cast(*data->disconnectedCallback)->Call(v8::Context::GetCurrent()->Global(), 0, argv);
    goto cleanup;
  }

  if(data->bytesRead > 0) {
    v8::Handle<v8::Value> argv[1];
    argv[0] = node::Buffer::New(data->buffer, data->bytesRead)->handle_;
    v8::Function::Cast(*data->dataCallback)->Call(v8::Context::GetCurrent()->Global(), 1, argv);
  } else if(data->errorCode > 0) {
    if(data->errorCode == ERROR_INVALID_HANDLE && IsClosingHandle((int)data->fd)) {
      goto cleanup;
    } else {
      v8::Handle<v8::Value> argv[1];
      argv[0] = v8::Exception::Error(v8::String::New(data->errorString));
      v8::Function::Cast(*data->errorCallback)->Call(v8::Context::GetCurrent()->Global(), 1, argv);
      Sleep(100); // prevent the errors from occurring too fast
    }
  }
  AfterOpenSuccess((int)data->fd, data->dataCallback, data->disconnectedCallback, data->errorCallback);

cleanup:
  data->dataCallback.Dispose();
  data->errorCallback.Dispose();
  delete data;
  delete req;
}

void AfterOpenSuccess(int fd, v8::Handle<v8::Value> dataCallback, v8::Handle<v8::Value> disconnectedCallback, v8::Handle<v8::Value> errorCallback) {
  WatchPortBaton* baton = new WatchPortBaton();
  memset(baton, 0, sizeof(WatchPortBaton));
  baton->fd = (HANDLE)fd;
  baton->dataCallback = v8::Persistent<v8::Value>::New(dataCallback);
  baton->errorCallback = v8::Persistent<v8::Value>::New(errorCallback);
  baton->disconnectedCallback = v8::Persistent<v8::Value>::New(disconnectedCallback);

  uv_work_t* req = new uv_work_t();
  req->data = baton;

  uv_queue_work(uv_default_loop(), req, EIO_WatchPort, EIO_AfterWatchPort);
}

void EIO_Write(uv_work_t* req) {
  QueuedWrite* queuedWrite = static_cast<QueuedWrite*>(req->data);
  WriteBaton* data = static_cast<WriteBaton*>(queuedWrite->baton);
  data->result = 0;

  OVERLAPPED ov = {0};
  // Event used by GetOverlappedResult(..., TRUE) to wait for outgoing data or timeout
  // Event MUST be used if program has several simultaneous asynchronous operations
  // on the same handle (i.e. ReadFile and WriteFile)
  ov.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);

  // Start write operation - synchrounous or asynchronous
  DWORD bytesWrittenSync = 0;
  if(!WriteFile((HANDLE)data->fd, data->bufferData, data->bufferLength, &bytesWrittenSync, &ov)) {
    DWORD lastError = GetLastError();
    if(lastError != ERROR_IO_PENDING) {
      // Write operation error
      ErrorCodeToString("Writing to COM port (WriteFile)", lastError, data->errorString);
    }
    else {
      // Write operation is asynchronous and is pending
      // We MUST wait for operation completion before deallocation of OVERLAPPED struct
      // or write data buffer

      // Wait for async write operation completion or timeout
      DWORD bytesWrittenAsync = 0;
      if(!GetOverlappedResult((HANDLE)data->fd, &ov, &bytesWrittenAsync, TRUE)) {
        // Write operation error
        DWORD lastError = GetLastError();
        ErrorCodeToString("Writing to COM port (GetOverlappedResult)", lastError, data->errorString);
      }
      else {
        // Write operation completed asynchronously
        data->result = bytesWrittenAsync;
      }
    }
  }
  else {
    // Write operation completed synchronously
    data->result = bytesWrittenSync;
  }

  CloseHandle(ov.hEvent);
}

void EIO_Close(uv_work_t* req) {
  CloseBaton* data = static_cast<CloseBaton*>(req->data);

  g_closingHandles.push_back(data->fd);
  if(!CloseHandle((HANDLE)data->fd)) {
    ErrorCodeToString("closing connection", GetLastError(), data->errorString);
    return;
  }
}

/*
 * listComPorts.c -- list COM ports
 *
 * http://github.com/todbot/usbSearch/
 *
 * 2012, Tod E. Kurt, http://todbot.com/blog/
 *
 *
 * Uses DispHealper : http://disphelper.sourceforge.net/
 *
 * Notable VIDs & PIDs combos:
 * VID 0403 - FTDI
 *
 * VID 0403 / PID 6001 - Arduino Diecimila
 *
 */
void EIO_List(uv_work_t* req) {
  ListBaton* data = static_cast<ListBaton*>(req->data);

  {
    DISPATCH_OBJ(wmiSvc);
    DISPATCH_OBJ(colDevices);

    dhInitialize(TRUE);
    dhToggleExceptions(TRUE);

    dhGetObject(L"winmgmts:{impersonationLevel=impersonate}!\\\\.\\root\\cimv2", NULL, &wmiSvc);
    dhGetValue(L"%o", &colDevices, wmiSvc, L".ExecQuery(%S)", L"Select * from Win32_PnPEntity");

    int port_count = 0;
    FOR_EACH(objDevice, colDevices, NULL) {
      char* name = NULL;
      char* pnpid = NULL;
      char* manu = NULL;
      char* match;

      dhGetValue(L"%s", &name,  objDevice, L".Name");
      dhGetValue(L"%s", &pnpid, objDevice, L".PnPDeviceID");

      if( (match = strstr( name, "(COM" )) != NULL ) { // look for "(COM23)"
        // 'Manufacturuer' can be null, so only get it if we need it
        dhGetValue(L"%s", &manu, objDevice,  L".Manufacturer");
        port_count++;
        char* comname = strtok( match, "()");
        ListResultItem* resultItem = new ListResultItem();
        resultItem->comName = comname;
        resultItem->manufacturer = manu;
        resultItem->pnpId = pnpid;
        data->results.push_back(resultItem);
        dhFreeString(manu);
      }

      dhFreeString(name);
      dhFreeString(pnpid);
    } NEXT(objDevice);

    SAFE_RELEASE(colDevices);
    SAFE_RELEASE(wmiSvc);

    dhUninitialize(TRUE);
  }

  std::vector<UINT> ports;
  if (CEnumerateSerial::UsingQueryDosDevice(ports))
  {
    for (size_t i = 0; i < ports.size(); i++)
    {
      char comname[64] = { 0 };
      sprintf(comname, "COM%u", ports[i]);
      bool bFound = false;
      for (std::list<ListResultItem*>::iterator ri = data->results.begin(); ri != data->results.end(); ri++)
      {
        if (stricmp((*ri)->comName.c_str(), comname) == 0)
        {
          bFound = true;
          break;
        }
      }
      if (!bFound)
      {
        ListResultItem* resultItem = new ListResultItem();
        resultItem->comName = comname;
        resultItem->manufacturer = "";
        resultItem->pnpId = "";
        data->results.push_back(resultItem);
      }
    }
  }
}

void EIO_Flush(uv_work_t* req) {
  FlushBaton* data = static_cast<FlushBaton*>(req->data);

  if(!FlushFileBuffers((HANDLE)data->fd)) {
    ErrorCodeToString("flushing connection", GetLastError(), data->errorString);
    return;
  }
}

#endif
<|endoftext|>


<commit_before>/*******************************************************************************
 * c7a/common/stats_timer.hpp
 *
 * Distributed under the Boost Software License, Version 1.0.
 * (See accompanying file LICENSE_1_0.txt or copy at
 * http://www.boost.org/LICENSE_1_0.txt)
 *
 * Part of Project c7a.
 *
 * Copyright (C) 2014 Thomas Keh <thomas.keh@student.kit.edu>
 * Copyright (C) 2015 Timo Bingmann <tb@panthema.net>
 *
 * This file has no license. Only Chuck Norris can compile it.
 ******************************************************************************/

#pragma once
#ifndef C7A_COMMON_STATS_TIMER_HEADER
#define C7A_COMMON_STATS_TIMER_HEADER

#include <cassert>
#include <chrono>
#include <memory>
#include <ostream>

namespace c7a {
namespace common {

/*!
 * This class provides a statistical stop watch timer that can easily be
 * deactivated using a boolean template switch.
 *
 * It uses std::chrono to get the current time when start() is called. Then,
 * after some processing, the function stop() functions can be called, or
 * seconds() and other accessors can be called directly.
 */
template <bool Active = true>
class StatsTimer
{ };

using TimerPtr = std::shared_ptr<StatsTimer<true> >;
template <>
class StatsTimer<true>
{
public:
    typedef std::chrono::steady_clock steady_clock;
    typedef std::chrono::steady_clock::time_point time_point;

    typedef std::chrono::microseconds duration;

protected:
    //! boolean whether the timer is currently running
    bool running_;

    //! total accumulated time in microseconds.
    duration accumulated_;

    //! last start time of the stop watch
    time_point last_start_;

public:
    //! Initialize and optionally immediately start the timer
    explicit StatsTimer(bool start_immediately = false)
        : running_(false),
          accumulated_() {
        if (start_immediately) Start();
    }

    //! Whether the timer is real
    bool Real() const { return true; }

    //! start timer
    void Start() {
        assert(!running_);
        running_ = true;
        last_start_ = steady_clock::now();
    }

    //! stop timer
    void Stop() {
        assert(running_);
        running_ = false;
        accumulated_ += std::chrono::duration_cast<duration>(
            steady_clock::now() - last_start_);
    }

    //! return accumulated time
    void Reset() {
        accumulated_ = duration(0);
        last_start_ = steady_clock::now();
    }

    //! return currently accumulated time
    duration Accumulated() const {
        duration d = accumulated_;

        if (running_)
            d += std::chrono::duration_cast<duration>(
                steady_clock::now() - last_start_);

        return d;
    }

    //! return currently accumulated time in microseconds
    std::chrono::microseconds::rep Microseconds() const {
        return std::chrono::duration_cast<std::chrono::microseconds>(
            Accumulated()).count();
    }

    //! return currently accumulated time in milliseconds
    std::chrono::milliseconds::rep Milliseconds() const {
        return std::chrono::duration_cast<std::chrono::milliseconds>(
            Accumulated()).count();
    }

    //! return currently accumulated time in seconds
    std::chrono::seconds::rep Seconds() const {
        return std::chrono::duration_cast<std::chrono::seconds>(
            Accumulated()).count();
    }

    //! accumulate elapsed time from another timer
    StatsTimer& operator += (const StatsTimer& tm) {
        accumulated_ += tm.accumulated_;
        return *this;
    }

    //! direct <<-operator for ostream. Can be used for printing with std::cout.
    friend std::ostream& operator << (std::ostream& os, const StatsTimer& t) {
        return os << t.Microseconds() << "us";
    }
};

template <>
class StatsTimer<false>
{
public:
    typedef std::chrono::steady_clock steady_clock;
    typedef std::chrono::steady_clock::time_point time_point;

    typedef std::chrono::microseconds duration;

public:
    //! Initialize and optionally immediately start the timer
    explicit StatsTimer(bool = false)
    { }

    //! Whether the timer is real
    bool Real() const { return false; }

    //! start timer
    void Start()
    { }

    //! stop timer
    void Stop()
    { }

    //! return accumulated time
    void Reset()
    { }

    //! return currently accumulated time
    duration Accumulated() const {
        return duration();
    }

    //! return currently accumulated time in microseconds
    std::chrono::microseconds::rep Microseconds() const {
        return 0;
    }

    //! return currently accumulated time in milliseconds
    std::chrono::milliseconds::rep Milliseconds() const {
        return 0;
    }

    //! return currently accumulated time in milliseconds
    std::chrono::seconds::rep Seconds() const {
        return 0;
    }

    //! accumulate elapsed time from another timer
    StatsTimer& operator += (const StatsTimer&) {
        return *this;
    }

    //! direct <<-operator for ostream. Can be used for printing with std::cout.
    friend std::ostream& operator << (std::ostream& os, const StatsTimer&) {
        return os << "<invalid>" << "ms";
    }
};

} // namespace common
} // namespace c7a

#endif // !C7A_COMMON_STATS_TIMER_HEADER

/******************************************************************************/
<commit_msg>StatsTimer::operator<< prints only Micrsecond value without 'us'. This is better for SqlPlotTools<commit_after>/*******************************************************************************
 * c7a/common/stats_timer.hpp
 *
 * Distributed under the Boost Software License, Version 1.0.
 * (See accompanying file LICENSE_1_0.txt or copy at
 * http://www.boost.org/LICENSE_1_0.txt)
 *
 * Part of Project c7a.
 *
 * Copyright (C) 2014 Thomas Keh <thomas.keh@student.kit.edu>
 * Copyright (C) 2015 Timo Bingmann <tb@panthema.net>
 *
 * This file has no license. Only Chuck Norris can compile it.
 ******************************************************************************/

#pragma once
#ifndef C7A_COMMON_STATS_TIMER_HEADER
#define C7A_COMMON_STATS_TIMER_HEADER

#include <cassert>
#include <chrono>
#include <memory>
#include <ostream>

namespace c7a {
namespace common {

/*!
 * This class provides a statistical stop watch timer that can easily be
 * deactivated using a boolean template switch.
 *
 * It uses std::chrono to get the current time when start() is called. Then,
 * after some processing, the function stop() functions can be called, or
 * seconds() and other accessors can be called directly.
 */
template <bool Active = true>
class StatsTimer
{ };

using TimerPtr = std::shared_ptr<StatsTimer<true> >;
template <>
class StatsTimer<true>
{
public:
    typedef std::chrono::steady_clock steady_clock;
    typedef std::chrono::steady_clock::time_point time_point;

    typedef std::chrono::microseconds duration;

protected:
    //! boolean whether the timer is currently running
    bool running_;

    //! total accumulated time in microseconds.
    duration accumulated_;

    //! last start time of the stop watch
    time_point last_start_;

public:
    //! Initialize and optionally immediately start the timer
    explicit StatsTimer(bool start_immediately = false)
        : running_(false),
          accumulated_() {
        if (start_immediately) Start();
    }

    //! Whether the timer is real
    bool Real() const { return true; }

    //! start timer
    void Start() {
        assert(!running_);
        running_ = true;
        last_start_ = steady_clock::now();
    }

    //! stop timer
    void Stop() {
        assert(running_);
        running_ = false;
        accumulated_ += std::chrono::duration_cast<duration>(
            steady_clock::now() - last_start_);
    }

    //! return accumulated time
    void Reset() {
        accumulated_ = duration(0);
        last_start_ = steady_clock::now();
    }

    //! return currently accumulated time
    duration Accumulated() const {
        duration d = accumulated_;

        if (running_)
            d += std::chrono::duration_cast<duration>(
                steady_clock::now() - last_start_);

        return d;
    }

    //! return currently accumulated time in microseconds
    std::chrono::microseconds::rep Microseconds() const {
        return std::chrono::duration_cast<std::chrono::microseconds>(
            Accumulated()).count();
    }

    //! return currently accumulated time in milliseconds
    std::chrono::milliseconds::rep Milliseconds() const {
        return std::chrono::duration_cast<std::chrono::milliseconds>(
            Accumulated()).count();
    }

    //! return currently accumulated time in seconds
    std::chrono::seconds::rep Seconds() const {
        return std::chrono::duration_cast<std::chrono::seconds>(
            Accumulated()).count();
    }

    //! accumulate elapsed time from another timer
    StatsTimer& operator += (const StatsTimer& tm) {
        accumulated_ += tm.accumulated_;
        return *this;
    }

    //! direct <<-operator for ostream. Can be used for printing with std::cout.
    friend std::ostream& operator << (std::ostream& os, const StatsTimer& t) {
        return os << t.Microseconds();
    }
};

template <>
class StatsTimer<false>
{
public:
    typedef std::chrono::steady_clock steady_clock;
    typedef std::chrono::steady_clock::time_point time_point;

    typedef std::chrono::microseconds duration;

public:
    //! Initialize and optionally immediately start the timer
    explicit StatsTimer(bool = false)
    { }

    //! Whether the timer is real
    bool Real() const { return false; }

    //! start timer
    void Start()
    { }

    //! stop timer
    void Stop()
    { }

    //! return accumulated time
    void Reset()
    { }

    //! return currently accumulated time
    duration Accumulated() const {
        return duration();
    }

    //! return currently accumulated time in microseconds
    std::chrono::microseconds::rep Microseconds() const {
        return 0;
    }

    //! return currently accumulated time in milliseconds
    std::chrono::milliseconds::rep Milliseconds() const {
        return 0;
    }

    //! return currently accumulated time in milliseconds
    std::chrono::seconds::rep Seconds() const {
        return 0;
    }

    //! accumulate elapsed time from another timer
    StatsTimer& operator += (const StatsTimer&) {
        return *this;
    }

    //! direct <<-operator for ostream. Can be used for printing with std::cout.
    friend std::ostream& operator << (std::ostream& os, const StatsTimer&) {
        return os << "<invalid>";
    }
};

} // namespace common
} // namespace c7a

#endif // !C7A_COMMON_STATS_TIMER_HEADER

/******************************************************************************/
<|endoftext|>


<commit_before>/*
    This file is part of Akregator.

    Copyright (C) 2004 Teemu Rytilahti <tpr@d5k.net>

    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.

    This program 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.

    As a special exception, permission is given to link this program
    with any edition of Qt, and distribute the resulting executable,
    without including the source code for Qt in the source distribution.
*/

#include <kaction.h>
#include <kapplication.h>
#include <kfiledialog.h>
#include <khtmlview.h>
#include <kiconloader.h>
#include <klocale.h>
#include <kmessagebox.h>
#include <kpopupmenu.h>
#include <kprocess.h>
#include <krun.h>
#include <kshell.h>
#include <kurl.h>
#include <kparts/browserextension.h>

#include <qaccel.h>
#include <qclipboard.h>
#include <qpaintdevicemetrics.h>

#include "viewer.h"
#include "akregator_run.h"
#include "akregatorconfig.h"

using namespace Akregator;

Viewer::Viewer(QWidget *parent, const char *name)
    : KHTMLPart(parent, name), m_url(0)
{
    setZoomFactor(100);
    setJScriptEnabled(true);
    setJavaEnabled(true);
    setMetaRefreshEnabled(true);
    setPluginsEnabled(true);
    setDNDEnabled(true);
    setAutoloadImages(true);
    setStatusMessagesEnabled(true);

    // change the cursor when loading stuff...
    connect( this, SIGNAL(started(KIO::Job *)),
             this, SLOT(slotStarted(KIO::Job *)));
    connect( this, SIGNAL(completed()),
             this, SLOT(slotCompleted()));

    connect( browserExtension(), SIGNAL(openURLRequestDelayed(const KURL&, const KParts::URLArgs&)), this, SLOT(slotOpenURLRequest(const KURL&, const KParts::URLArgs& )) );

    connect( browserExtension(),

SIGNAL(popupMenu (KXMLGUIClient*, const QPoint&, const KURL&, const
    KParts::URLArgs&, KParts::BrowserExtension::PopupFlags, mode_t)), this, SLOT(slotPopupMenu(KXMLGUIClient*, const QPoint&, const KURL&, const
    KParts::URLArgs&, KParts::BrowserExtension::PopupFlags, mode_t)));

    KStdAction::print(this, SLOT(slotPrint()), actionCollection(), "viewer_print");
    KStdAction::copy(this, SLOT(slotCopy()), actionCollection(), "viewer_copy");

    new KAction( i18n("&Increase Font Sizes"), "viewmag+", "Ctrl+Plus", this, SLOT(slotZoomIn()), actionCollection(), "incFontSizes" );
    new KAction( i18n("&Decrease Font Sizes"), "viewmag-", "Ctrl+Minus", this, SLOT(slotZoomOut()), actionCollection(), "decFontSizes" );

    connect(this, SIGNAL(selectionChanged()), this, SLOT(slotSelectionChanged()));

    new KAction(i18n("Copy &Link Address"), "", 0,
                                 this, SLOT(slotCopyLinkAddress()),
                                 actionCollection(), "copylinkaddress");
    new KAction(i18n("&Save Link As..."), "", 0,
                                 this, SLOT(slotSaveLinkAs()),
                                 actionCollection(), "savelinkas");
}

Viewer::~Viewer()
{}

bool Viewer::openURL(const KURL &url)
{
    new Akregator::BrowserRun(this, (QWidget*)parent(), this, url, browserExtension()->urlArgs());
    emit started(0);
    return true;
}

bool Viewer::closeURL()
{
    emit browserExtension()->loadingProgress(-1);
    emit canceled(QString::null);
    return KHTMLPart::closeURL();
}

int Viewer::pointsToPixel(int pointSize) const
{
    const QPaintDeviceMetrics metrics(view());
    return ( pointSize * metrics.logicalDpiY() + 36 ) / 72 ;
}

void Viewer::displayInExternalBrowser(const KURL &url, const QString &mimetype)
{
   if (!url.isValid()) return;
   if (Settings::externalBrowserUseKdeDefault())
   {
       if (mimetype.isEmpty())
           kapp->invokeBrowser(url.url(), "0");
       else
           KRun::runURL(url, mimetype, false, false);
   }
   else
   {
       QString cmd = Settings::externalBrowserCustomCommand();
       QString urlStr = url.url();
       cmd.replace(QRegExp("%u"), urlStr);
       KProcess *proc = new KProcess;
       QStringList cmdAndArgs = KShell::splitArgs(cmd);
       *proc << cmdAndArgs;
       proc->start(KProcess::DontCare);
       delete proc;
   }
}

void Viewer::slotOpenURLRequest(const KURL& url, const KParts::URLArgs& args)
{
    m_url = url;
    browserExtension()->setURLArgs(args);
    if (args.frameName == "_blank") // apparently this indicates that the MMB was pressed...
    {
        switch (Settings::mMBBehaviour())
        {
            case Settings::EnumMMBBehaviour::OpenInExternalBrowser:
                slotOpenLinkInBrowser();
                break;
            case Settings::EnumMMBBehaviour::OpenInBackground:
                slotOpenLinkInBackgroundTab();
                break;
            default:
                slotOpenLinkInForegroundTab();
                break;
        }
    }
    else // LMB:
    {
        switch (Settings::lMBBehaviour())
        {
            case Settings::EnumLMBBehaviour::OpenInExternalBrowser:
                slotOpenLinkInBrowser();
                break;
            case Settings::EnumLMBBehaviour::OpenInBackground:
                slotOpenLinkInBackgroundTab();
                break;
            default:
                slotOpenLinkInForegroundTab();
                break;
        }
    }
}

void Viewer::slotPopupMenu(KXMLGUIClient*, const QPoint& p, const KURL& kurl, const KParts::URLArgs&, KParts::BrowserExtension::PopupFlags kpf, mode_t)
{
   const bool isLink = (kpf & KParts::BrowserExtension::ShowNavigationItems) == 0;

   QString url = kurl.url();

   if (!isLink)
        return;
   m_url = url;
   KPopupMenu popup;

   if (isLink)
   {
        popup.insertItem(SmallIcon("tab_new"), i18n("Open Link in New &Tab"), this, SLOT(slotOpenLinkInForegroundTab()));
        popup.insertItem(SmallIcon("window_new"), i18n("Open Link in External &Browser"), this, SLOT(slotOpenLinkInBrowser()));
        popup.insertSeparator();
        action("savelinkas")->plug(&popup);
        action("copylinkaddress")->plug(&popup);
   }
   else
   {
       action("viewer_copy")->plug(&popup);
       popup.insertSeparator();
       action("viewer_print")->plug(&popup);
       KAction *ac = action("setEncoding");
       if (ac)
            ac->plug(&popup);
   }
   popup.exec(p);
}

// taken from KDevelop
void Viewer::slotCopy()
{
    QString text = selectedText();
    text.replace( QChar( 0xa0 ), ' ' );
    QClipboard *cb = QApplication::clipboard();
    disconnect( cb, SIGNAL( selectionChanged() ), this, SLOT( slotClearSelection() ) );
    cb->setText(text);
    connect( cb, SIGNAL( selectionChanged() ), this, SLOT( slotClearSelection() ) );
}

void Viewer::slotCopyLinkAddress()
{
   if(m_url.isEmpty()) return;
   QClipboard *cb = QApplication::clipboard();
   cb->setText(m_url.prettyURL(), QClipboard::Clipboard);
   cb->setText(m_url.prettyURL(), QClipboard::Selection);
}

void Viewer::slotSelectionChanged()
{
    action("viewer_copy")->setEnabled(!selectedText().isEmpty());
}

void Viewer::slotOpenLinkInternal()
{
   openURL(m_url);
}

void Viewer::slotOpenLinkInForegroundTab()
{
    emit urlClicked(m_url, false);
}

void Viewer::slotOpenLinkInBackgroundTab()
{
    emit urlClicked(m_url, true);
}

void Viewer::slotOpenLinkInBrowser()
{
    displayInExternalBrowser(m_url, QString::null);
}

void Viewer::slotSaveLinkAs()
{
    KURL tmp( m_url );

    if ( tmp.fileName(false).isEmpty() )
        tmp.setFileName( "index.html" );
    KParts::BrowserRun::simpleSave(tmp, tmp.fileName());
}

void Viewer::slotStarted(KIO::Job *)
{
   widget()->setCursor( waitCursor );
}

void Viewer::slotCompleted()
{
   widget()->unsetCursor();
}

void Viewer::slotScrollUp()
{
    view()->scrollBy(0,-10);
}

void Viewer::slotScrollDown()
{
    view()->scrollBy(0,10);
}

void Viewer::slotZoomIn()
{
    int zf = zoomFactor();
    if (zf < 100)
    {
        zf = zf - (zf % 20) + 20;
        setZoomFactor(zf);
    }
    else
    {
        zf = zf - (zf % 50) + 50;
        setZoomFactor(zf < 300 ? zf : 300);
    }
}

void Viewer::slotZoomOut()
{
    int zf = zoomFactor();
    if (zf <= 100)
    {
        zf = zf - (zf % 20) - 20;
        setZoomFactor(zf > 20 ? zf : 20);
    }
    else
    {
        zf = zf - (zf % 50) - 50;
        setZoomFactor(zf);
    }
}

void Viewer::slotSetZoomFactor(int percent)
{
    setZoomFactor(percent);
}

// some code taken from KDevelop (lib/widgets/kdevhtmlpart.cpp)
void Viewer::slotPrint( )
{
    view()->print();
}

void Viewer::setSafeMode()
{
    setJScriptEnabled(false);
    setJavaEnabled(false);
    setMetaRefreshEnabled(false);
    setPluginsEnabled(false);
    setDNDEnabled(true);
    setAutoloadImages(true);
    setStatusMessagesEnabled(false);
}

#include "viewer.moc"

// vim: set et ts=4 sts=4 sw=4:
<commit_msg>show context menus correctly for selections and !isLink case<commit_after>/*
    This file is part of Akregator.

    Copyright (C) 2004 Teemu Rytilahti <tpr@d5k.net>

    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.

    This program 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.

    As a special exception, permission is given to link this program
    with any edition of Qt, and distribute the resulting executable,
    without including the source code for Qt in the source distribution.
*/

#include <kaction.h>
#include <kapplication.h>
#include <kfiledialog.h>
#include <khtmlview.h>
#include <kiconloader.h>
#include <klocale.h>
#include <kmessagebox.h>
#include <kpopupmenu.h>
#include <kprocess.h>
#include <krun.h>
#include <kshell.h>
#include <kurl.h>
#include <kparts/browserextension.h>

#include <qaccel.h>
#include <qclipboard.h>
#include <qpaintdevicemetrics.h>

#include "viewer.h"
#include "akregator_run.h"
#include "akregatorconfig.h"

using namespace Akregator;

Viewer::Viewer(QWidget *parent, const char *name)
    : KHTMLPart(parent, name), m_url(0)
{
    setZoomFactor(100);
    setJScriptEnabled(true);
    setJavaEnabled(true);
    setMetaRefreshEnabled(true);
    setPluginsEnabled(true);
    setDNDEnabled(true);
    setAutoloadImages(true);
    setStatusMessagesEnabled(true);

    // change the cursor when loading stuff...
    connect( this, SIGNAL(started(KIO::Job *)),
             this, SLOT(slotStarted(KIO::Job *)));
    connect( this, SIGNAL(completed()),
             this, SLOT(slotCompleted()));

    connect( browserExtension(), SIGNAL(openURLRequestDelayed(const KURL&, const KParts::URLArgs&)), this, SLOT(slotOpenURLRequest(const KURL&, const KParts::URLArgs& )) );

    connect( browserExtension(),

SIGNAL(popupMenu (KXMLGUIClient*, const QPoint&, const KURL&, const
    KParts::URLArgs&, KParts::BrowserExtension::PopupFlags, mode_t)), this, SLOT(slotPopupMenu(KXMLGUIClient*, const QPoint&, const KURL&, const
    KParts::URLArgs&, KParts::BrowserExtension::PopupFlags, mode_t)));

    KStdAction::print(this, SLOT(slotPrint()), actionCollection(), "viewer_print");
    KStdAction::copy(this, SLOT(slotCopy()), actionCollection(), "viewer_copy");

    new KAction( i18n("&Increase Font Sizes"), "viewmag+", "Ctrl+Plus", this, SLOT(slotZoomIn()), actionCollection(), "incFontSizes" );
    new KAction( i18n("&Decrease Font Sizes"), "viewmag-", "Ctrl+Minus", this, SLOT(slotZoomOut()), actionCollection(), "decFontSizes" );

    connect(this, SIGNAL(selectionChanged()), this, SLOT(slotSelectionChanged()));

    new KAction(i18n("Copy &Link Address"), "", 0,
                                 this, SLOT(slotCopyLinkAddress()),
                                 actionCollection(), "copylinkaddress");
    new KAction(i18n("&Save Link As..."), "", 0,
                                 this, SLOT(slotSaveLinkAs()),
                                 actionCollection(), "savelinkas");
}

Viewer::~Viewer()
{}

bool Viewer::openURL(const KURL &url)
{
    new Akregator::BrowserRun(this, (QWidget*)parent(), this, url, browserExtension()->urlArgs());
    emit started(0);
    return true;
}

bool Viewer::closeURL()
{
    emit browserExtension()->loadingProgress(-1);
    emit canceled(QString::null);
    return KHTMLPart::closeURL();
}

int Viewer::pointsToPixel(int pointSize) const
{
    const QPaintDeviceMetrics metrics(view());
    return ( pointSize * metrics.logicalDpiY() + 36 ) / 72 ;
}

void Viewer::displayInExternalBrowser(const KURL &url, const QString &mimetype)
{
   if (!url.isValid()) return;
   if (Settings::externalBrowserUseKdeDefault())
   {
       if (mimetype.isEmpty())
           kapp->invokeBrowser(url.url(), "0");
       else
           KRun::runURL(url, mimetype, false, false);
   }
   else
   {
       QString cmd = Settings::externalBrowserCustomCommand();
       QString urlStr = url.url();
       cmd.replace(QRegExp("%u"), urlStr);
       KProcess *proc = new KProcess;
       QStringList cmdAndArgs = KShell::splitArgs(cmd);
       *proc << cmdAndArgs;
       proc->start(KProcess::DontCare);
       delete proc;
   }
}

void Viewer::slotOpenURLRequest(const KURL& url, const KParts::URLArgs& args)
{
    m_url = url;
    browserExtension()->setURLArgs(args);
    if (args.frameName == "_blank") // apparently this indicates that the MMB was pressed...
    {
        switch (Settings::mMBBehaviour())
        {
            case Settings::EnumMMBBehaviour::OpenInExternalBrowser:
                slotOpenLinkInBrowser();
                break;
            case Settings::EnumMMBBehaviour::OpenInBackground:
                slotOpenLinkInBackgroundTab();
                break;
            default:
                slotOpenLinkInForegroundTab();
                break;
        }
    }
    else // LMB:
    {
        switch (Settings::lMBBehaviour())
        {
            case Settings::EnumLMBBehaviour::OpenInExternalBrowser:
                slotOpenLinkInBrowser();
                break;
            case Settings::EnumLMBBehaviour::OpenInBackground:
                slotOpenLinkInBackgroundTab();
                break;
            default:
                slotOpenLinkInForegroundTab();
                break;
        }
    }
}

void Viewer::slotPopupMenu(KXMLGUIClient*, const QPoint& p, const KURL& kurl, const KParts::URLArgs&, KParts::BrowserExtension::PopupFlags kpf, mode_t)
{
   const bool isLink = (kpf & KParts::BrowserExtension::ShowNavigationItems) == 0;
   const bool isSelection = (kpf & KParts::BrowserExtension::ShowTextSelectionItems) != 0;

   QString url = kurl.url();

   m_url = url;
   KPopupMenu popup;

   if (isLink && !isSelection)
   {
        popup.insertItem(SmallIcon("tab_new"), i18n("Open Link in New &Tab"), this, SLOT(slotOpenLinkInForegroundTab()));
        popup.insertItem(SmallIcon("window_new"), i18n("Open Link in External &Browser"), this, SLOT(slotOpenLinkInBrowser()));
        popup.insertSeparator();
        action("savelinkas")->plug(&popup);
        action("copylinkaddress")->plug(&popup);
   }
   else
   {
       if (isSelection)
       {
            action("viewer_copy")->plug(&popup);
            popup.insertSeparator();
       }
       action("viewer_print")->plug(&popup);
       KAction *ac = action("setEncoding");
       if (ac)
            ac->plug(&popup);
   }
   popup.exec(p);
}

// taken from KDevelop
void Viewer::slotCopy()
{
    QString text = selectedText();
    text.replace( QChar( 0xa0 ), ' ' );
    QClipboard *cb = QApplication::clipboard();
    disconnect( cb, SIGNAL( selectionChanged() ), this, SLOT( slotClearSelection() ) );
    cb->setText(text);
    connect( cb, SIGNAL( selectionChanged() ), this, SLOT( slotClearSelection() ) );
}

void Viewer::slotCopyLinkAddress()
{
   if(m_url.isEmpty()) return;
   QClipboard *cb = QApplication::clipboard();
   cb->setText(m_url.prettyURL(), QClipboard::Clipboard);
   cb->setText(m_url.prettyURL(), QClipboard::Selection);
}

void Viewer::slotSelectionChanged()
{
    action("viewer_copy")->setEnabled(!selectedText().isEmpty());
}

void Viewer::slotOpenLinkInternal()
{
   openURL(m_url);
}

void Viewer::slotOpenLinkInForegroundTab()
{
    emit urlClicked(m_url, false);
}

void Viewer::slotOpenLinkInBackgroundTab()
{
    emit urlClicked(m_url, true);
}

void Viewer::slotOpenLinkInBrowser()
{
    displayInExternalBrowser(m_url, QString::null);
}

void Viewer::slotSaveLinkAs()
{
    KURL tmp( m_url );

    if ( tmp.fileName(false).isEmpty() )
        tmp.setFileName( "index.html" );
    KParts::BrowserRun::simpleSave(tmp, tmp.fileName());
}

void Viewer::slotStarted(KIO::Job *)
{
   widget()->setCursor( waitCursor );
}

void Viewer::slotCompleted()
{
   widget()->unsetCursor();
}

void Viewer::slotScrollUp()
{
    view()->scrollBy(0,-10);
}

void Viewer::slotScrollDown()
{
    view()->scrollBy(0,10);
}

void Viewer::slotZoomIn()
{
    int zf = zoomFactor();
    if (zf < 100)
    {
        zf = zf - (zf % 20) + 20;
        setZoomFactor(zf);
    }
    else
    {
        zf = zf - (zf % 50) + 50;
        setZoomFactor(zf < 300 ? zf : 300);
    }
}

void Viewer::slotZoomOut()
{
    int zf = zoomFactor();
    if (zf <= 100)
    {
        zf = zf - (zf % 20) - 20;
        setZoomFactor(zf > 20 ? zf : 20);
    }
    else
    {
        zf = zf - (zf % 50) - 50;
        setZoomFactor(zf);
    }
}

void Viewer::slotSetZoomFactor(int percent)
{
    setZoomFactor(percent);
}

// some code taken from KDevelop (lib/widgets/kdevhtmlpart.cpp)
void Viewer::slotPrint( )
{
    view()->print();
}

void Viewer::setSafeMode()
{
    setJScriptEnabled(false);
    setJavaEnabled(false);
    setMetaRefreshEnabled(false);
    setPluginsEnabled(false);
    setDNDEnabled(true);
    setAutoloadImages(true);
    setStatusMessagesEnabled(false);
}

#include "viewer.moc"

// vim: set et ts=4 sts=4 sw=4:
<|endoftext|>


<commit_before>/*
**  Copyright (C) 2012 Aldebaran Robotics
**  See COPYING for the license
*/

// Disable "'this': used in base member initializer list"
#ifdef _MSC_VER
# pragma warning( push )
# pragma warning(disable: 4355)
#endif

#include "sessionservice.hpp"
#include "servicedirectoryclient.hpp"
#include "objectregistrar.hpp"
#include "remoteobject_p.hpp"

qiLogCategory("qimessaging.sessionservice");

namespace qi {

  inline void sessionServiceWaitBarrier(Session_Service* ptr) {
    ptr->_destructionBarrier.setValue(0);
  }
  Session_Service::Session_Service(TransportSocketCache *socketCache, ServiceDirectoryClient *sdClient, ObjectRegistrar *server)
    : _socketCache(socketCache)
    , _sdClient(sdClient)
    , _server(server)
    , _self(this, sessionServiceWaitBarrier) // create a shared_ptr so that shared_from_this works
  {
    _linkServiceRemoved = _sdClient->serviceRemoved.connect(boost::bind<void>(&Session_Service::onServiceRemoved, this, _1, _2));
  }

  Session_Service::~Session_Service()
  {
    _sdClient->serviceRemoved.disconnect(_linkServiceRemoved);
    _self.reset(); // now existing weak_ptrs cannot from this cannot be locked
    _destructionBarrier.future().wait();
    close();
  }

  void Session_Service::onServiceRemoved(const unsigned int &index, const std::string &service) {
    qiLogVerbose() << "Remote Service Removed:" << service << " #" << index;
    removeService(service);
  }

  void Session_Service::removeService(const std::string &service) {
    {
      boost::mutex::scoped_lock sl(_remoteObjectsMutex);
      RemoteObjectMap::iterator it = _remoteObjects.find(service);
      if (it != _remoteObjects.end()) {
        qiLogVerbose() << "Session: Removing cached RemoteObject " << service;
        _remoteObjects.erase(it);
      }
    }
  }

  void Session_Service::close() {
    //cleanup all RemoteObject
    //they are not valid anymore after this function
    {
      boost::mutex::scoped_lock sl(_remoteObjectsMutex);
      RemoteObjectMap::iterator it = _remoteObjects.begin();
      for (; it != _remoteObjects.end(); ++it) {
        reinterpret_cast<RemoteObject*>(it->second->value)->close();
      }
      _remoteObjects.clear();
    }
  }

  ServiceRequest *Session_Service::serviceRequest(long requestId)
  {
    boost::recursive_mutex::scoped_lock sl(_requestsMutex);
    {
      std::map<int, ServiceRequest*>::iterator it;

      it = _requests.find(requestId);
      if (it == _requests.end()) {
        qiLogVerbose() << "qi.session.service(): No matching request for id(" << requestId << ").";
        return 0;
      }
      return it->second;
    }
  }

  static void deleteLater(qi::RemoteObject *remote) {
    delete remote;
  }

  void Session_Service::removeRequest(long requestId)
  {
    boost::recursive_mutex::scoped_lock sl(_requestsMutex);
    qi::RemoteObject *remote = 0;
    ServiceRequest   *sr     = 0;
    {
      std::map<int, ServiceRequest*>::iterator it;

      it = _requests.find(requestId);
      if (it == _requests.end()) {
        qiLogVerbose() << "qi.session.service(): No matching request for id(" << requestId << ").";
        return;
      }
      if (it->second) {
        remote = it->second->remoteObject;
        it->second->remoteObject = 0;
      }
      sr = it->second;
      it->second = 0;
      _requests.erase(it);
    }
    //we do not call delete on RemoteObject, because remoteObject->close disconnect onMessagePending,
    //which is the signal we are coming from.  (when called from onRemoteObjectComplete)
    //delete later as a workaround.
    qi::getDefaultNetworkEventLoop()->post(boost::bind(&deleteLater, remote));
    delete sr;
  }

  void Session_Service::onTransportSocketResult(qi::Future<TransportSocketPtr> value, long requestId) {
    qiLogDebug() << "Got transport socket for service";
    boost::recursive_mutex::scoped_lock sl(_requestsMutex);
    ServiceRequest *sr = serviceRequest(requestId);
    if (!sr)
      return;

    if (value.hasError()) {
      sr->promise.setError(value.error());
      removeRequest(requestId);
      return;
    }

    sr->remoteObject = new qi::RemoteObject(sr->serviceId, value.value());

    //ask the remoteObject to fetch the metaObject
    qi::Future<void> fut = sr->remoteObject->fetchMetaObject();
    fut.connect(boost::bind<void>(&Session_Service::onRemoteObjectComplete, this, _1, requestId));
  }

  void Session_Service::onRemoteObjectComplete(qi::Future<void> future, long requestId) {
    qiLogDebug() << "Got metaobject";
    boost::recursive_mutex::scoped_lock l(_requestsMutex);
    ServiceRequest *sr = serviceRequest(requestId);
    if (!sr)
      return;

    if (future.hasError()) {
      sr->promise.setError(future.error());
      removeRequest(requestId);
      return;
    }

    {
      boost::mutex::scoped_lock sl(_remoteObjectsMutex);
      RemoteObjectMap::iterator it = _remoteObjects.find(sr->name);
      if (it != _remoteObjects.end()) {
        //another object have been registered before us, return it
        //the new socket will be closed when the request is deleted
        qiLogVerbose() << "A request for the service " << sr->name << " have been discarded, "
                                        << "the remoteobject on the service was already available.";
        sr->promise.setValue(it->second);
      } else {

        AnyObject o = makeDynamicAnyObject(sr->remoteObject);
        //register the remote object in the cache
        _remoteObjects[sr->name] = o;
        sr->promise.setValue(o);
        sr->remoteObject = 0;
      }
    }

    removeRequest(requestId);
  }

  inline void onServiceInfoResultIfExists(Session_Service* s, qi::Future<qi::ServiceInfo> f,
    long requestId, std::string protocol, boost::weak_ptr<Session_Service> self)
  {
    boost::shared_ptr<Session_Service> sself = self.lock();
    if (sself)
      sself->onServiceInfoResult(f, requestId, protocol);
  }

  // We received a ServiceInfo, and want to establish a connection
  void Session_Service::onServiceInfoResult(qi::Future<qi::ServiceInfo> result, long requestId, std::string protocol) {
    qiLogDebug() << "Got serviceinfo message";
    boost::recursive_mutex::scoped_lock sl(_requestsMutex);
    ServiceRequest *sr = serviceRequest(requestId);
    if (!sr)
      return;
    if (result.hasError()) {
      sr->promise.setError(result.error());
      removeRequest(requestId);
      return;
    }
    const qi::ServiceInfo &si = result.value();
    sr->serviceId = si.serviceId();
    //empty serviceInfo
    if (!si.endpoints().size()) {
      std::stringstream ss;
      ss << "No endpoints returned for service:" << sr->name << " (id:" << sr->serviceId << ")";
      qiLogVerbose() << ss.str();
      sr->promise.setError(ss.str());
      removeRequest(requestId);
      return;
    }

    if (protocol != "")
    {
      std::vector<qi::Url>::const_iterator it = si.endpoints().begin();

      for (;
           it != si.endpoints().end() && it->protocol() != protocol;
           it++)
      {
        continue;
      }

      if (it == si.endpoints().end())
      {
        std::stringstream ss;
        ss << "No " << protocol << " endpoint available for service:" << sr->name << " (id:" << sr->serviceId << ")";
        qiLogVerbose("session.service") << ss.str();
        sr->promise.setError(ss.str());
      }
    }

    qiLogDebug() << "Requesting socket from cache";
    qi::Future<qi::TransportSocketPtr> fut = _socketCache->socket(si, protocol);
    fut.connect(boost::bind<void>(&Session_Service::onTransportSocketResult, this, _1, requestId));
  }

  qi::Future<qi::AnyObject> Session_Service::service(const std::string &service,
                                                     const std::string &protocol)
  {
    qi::Future<qi::AnyObject> result;
    if (protocol == "" || protocol == "local") {
      //qiLogError() << "service is not implemented for local service, it always return a remote service";
      //look for local object registered in the server
      qi::AnyObject go = _server->registeredServiceObject(service);
      if (go)
        return qi::Future<qi::AnyObject>(go);
      if (protocol == "local") {
        qi::Promise<qi::AnyObject> prom;
        prom.setError(std::string("No local object found for ") + service);
        return prom.future();
      }
    }

    //look for already registered remote objects
    {
      boost::mutex::scoped_lock sl(_remoteObjectsMutex);
      RemoteObjectMap::iterator it = _remoteObjects.find(service);
      if (it != _remoteObjects.end()) {
        return qi::Future<qi::AnyObject>(it->second);
      }
    }

    {
      boost::recursive_mutex::scoped_lock l(_requestsMutex);
      std::map<int, ServiceRequest*>::const_iterator it;
      for (it = _requests.begin(); it != _requests.end(); ++it)
      {
        if (it->second->name == service)
        {
          return it->second->promise.future();
        }
      }
    }

    qi::Future<qi::ServiceInfo> fut = _sdClient->service(service);
    ServiceRequest *rq = new ServiceRequest(service);
    long requestId = ++_requestsIndex;

    {
      boost::recursive_mutex::scoped_lock l(_requestsMutex);
      _requests[requestId] = rq;
    }
    result = rq->promise.future();
    //rq is not valid anymore after addCallbacks, because it could have been handled and cleaned
    fut.connect(boost::bind<void>(&onServiceInfoResultIfExists, this, _1, requestId, protocol, boost::weak_ptr<Session_Service>(_self)));
    return result;
  }
}

#ifdef _MSC_VER
# pragma warning( pop )
#endif
<commit_msg>sessionservice: fix a deadlock<commit_after>/*
**  Copyright (C) 2012 Aldebaran Robotics
**  See COPYING for the license
*/

// Disable "'this': used in base member initializer list"
#ifdef _MSC_VER
# pragma warning( push )
# pragma warning(disable: 4355)
#endif

#include "sessionservice.hpp"
#include "servicedirectoryclient.hpp"
#include "objectregistrar.hpp"
#include "remoteobject_p.hpp"

qiLogCategory("qimessaging.sessionservice");

namespace qi {

  inline void sessionServiceWaitBarrier(Session_Service* ptr) {
    ptr->_destructionBarrier.setValue(0);
  }
  Session_Service::Session_Service(TransportSocketCache *socketCache, ServiceDirectoryClient *sdClient, ObjectRegistrar *server)
    : _socketCache(socketCache)
    , _sdClient(sdClient)
    , _server(server)
    , _self(this, sessionServiceWaitBarrier) // create a shared_ptr so that shared_from_this works
  {
    _linkServiceRemoved = _sdClient->serviceRemoved.connect(boost::bind<void>(&Session_Service::onServiceRemoved, this, _1, _2));
  }

  Session_Service::~Session_Service()
  {
    _sdClient->serviceRemoved.disconnect(_linkServiceRemoved);
    _self.reset(); // now existing weak_ptrs cannot from this cannot be locked
    _destructionBarrier.future().wait();
    close();
  }

  void Session_Service::onServiceRemoved(const unsigned int &index, const std::string &service) {
    qiLogVerbose() << "Remote Service Removed:" << service << " #" << index;
    removeService(service);
  }

  void Session_Service::removeService(const std::string &service) {
    {
      boost::mutex::scoped_lock sl(_remoteObjectsMutex);
      RemoteObjectMap::iterator it = _remoteObjects.find(service);
      if (it != _remoteObjects.end()) {
        qiLogVerbose() << "Session: Removing cached RemoteObject " << service;
        _remoteObjects.erase(it);
      }
    }
  }

  void Session_Service::close() {
    //cleanup all RemoteObject
    //they are not valid anymore after this function
    {
      boost::mutex::scoped_lock sl(_remoteObjectsMutex);
      RemoteObjectMap::iterator it = _remoteObjects.begin();
      for (; it != _remoteObjects.end(); ++it) {
        reinterpret_cast<RemoteObject*>(it->second->value)->close();
      }
      _remoteObjects.clear();
    }
  }

  ServiceRequest *Session_Service::serviceRequest(long requestId)
  {
    boost::recursive_mutex::scoped_lock sl(_requestsMutex);
    {
      std::map<int, ServiceRequest*>::iterator it;

      it = _requests.find(requestId);
      if (it == _requests.end()) {
        qiLogVerbose() << "qi.session.service(): No matching request for id(" << requestId << ").";
        return 0;
      }
      return it->second;
    }
  }

  static void deleteLater(qi::RemoteObject *remote) {
    delete remote;
  }

  void Session_Service::removeRequest(long requestId)
  {
    boost::recursive_mutex::scoped_lock sl(_requestsMutex);
    qi::RemoteObject *remote = 0;
    ServiceRequest   *sr     = 0;
    {
      std::map<int, ServiceRequest*>::iterator it;

      it = _requests.find(requestId);
      if (it == _requests.end()) {
        qiLogVerbose() << "qi.session.service(): No matching request for id(" << requestId << ").";
        return;
      }
      if (it->second) {
        remote = it->second->remoteObject;
        it->second->remoteObject = 0;
      }
      sr = it->second;
      it->second = 0;
      _requests.erase(it);
    }
    //we do not call delete on RemoteObject, because remoteObject->close disconnect onMessagePending,
    //which is the signal we are coming from.  (when called from onRemoteObjectComplete)
    //delete later as a workaround.
    qi::getDefaultNetworkEventLoop()->post(boost::bind(&deleteLater, remote));
    delete sr;
  }

  void Session_Service::onTransportSocketResult(qi::Future<TransportSocketPtr> value, long requestId) {
    qiLogDebug() << "Got transport socket for service";
    boost::recursive_mutex::scoped_lock sl(_requestsMutex);
    ServiceRequest *sr = serviceRequest(requestId);
    if (!sr)
      return;

    if (value.hasError()) {
      sr->promise.setError(value.error());
      removeRequest(requestId);
      return;
    }

    sr->remoteObject = new qi::RemoteObject(sr->serviceId, value.value());

    //ask the remoteObject to fetch the metaObject
    qi::Future<void> fut = sr->remoteObject->fetchMetaObject();
    fut.connect(boost::bind<void>(&Session_Service::onRemoteObjectComplete, this, _1, requestId));
  }

  void Session_Service::onRemoteObjectComplete(qi::Future<void> future, long requestId) {
    qiLogDebug() << "Got metaobject";
    boost::recursive_mutex::scoped_lock l(_requestsMutex);
    ServiceRequest *sr = serviceRequest(requestId);
    if (!sr)
      return;

    if (future.hasError()) {
      sr->promise.setError(future.error());
      removeRequest(requestId);
      return;
    }

    {
      boost::mutex::scoped_lock sl(_remoteObjectsMutex);
      RemoteObjectMap::iterator it = _remoteObjects.find(sr->name);
      if (it != _remoteObjects.end()) {
        //another object have been registered before us, return it
        //the new socket will be closed when the request is deleted
        qiLogVerbose() << "A request for the service " << sr->name << " have been discarded, "
                                        << "the remoteobject on the service was already available.";
        sr->promise.setValue(it->second);
      } else {

        AnyObject o = makeDynamicAnyObject(sr->remoteObject);
        //register the remote object in the cache
        _remoteObjects[sr->name] = o;
        sr->promise.setValue(o);
        sr->remoteObject = 0;
      }
    }

    removeRequest(requestId);
  }

  inline void onServiceInfoResultIfExists(Session_Service* s, qi::Future<qi::ServiceInfo> f,
    long requestId, std::string protocol, boost::weak_ptr<Session_Service> self)
  {
    boost::shared_ptr<Session_Service> sself = self.lock();
    if (sself)
      sself->onServiceInfoResult(f, requestId, protocol);
  }

  // We received a ServiceInfo, and want to establish a connection
  void Session_Service::onServiceInfoResult(qi::Future<qi::ServiceInfo> result, long requestId, std::string protocol) {
    qiLogDebug() << "Got serviceinfo message";
    {
      boost::recursive_mutex::scoped_lock sl(_requestsMutex);
      ServiceRequest *sr = serviceRequest(requestId);
      if (!sr)
        return;
      if (result.hasError()) {
        sr->promise.setError(result.error());
        removeRequest(requestId);
        return;
      }
      const qi::ServiceInfo &si = result.value();
      sr->serviceId = si.serviceId();
      //empty serviceInfo
      if (!si.endpoints().size()) {
        std::stringstream ss;
        ss << "No endpoints returned for service:" << sr->name << " (id:" << sr->serviceId << ")";
        qiLogVerbose() << ss.str();
        sr->promise.setError(ss.str());
        removeRequest(requestId);
        return;
      }

      if (protocol != "")
      {
        std::vector<qi::Url>::const_iterator it = si.endpoints().begin();

        for (;
             it != si.endpoints().end() && it->protocol() != protocol;
             it++)
        {
          continue;
        }

        if (it == si.endpoints().end())
        {
          std::stringstream ss;
          ss << "No " << protocol << " endpoint available for service:" << sr->name << " (id:" << sr->serviceId << ")";
          qiLogVerbose("session.service") << ss.str();
          sr->promise.setError(ss.str());
        }
      }
    }
    qiLogDebug() << "Requesting socket from cache";
    qi::Future<qi::TransportSocketPtr> fut = _socketCache->socket(result.value(), protocol);
    fut.connect(boost::bind<void>(&Session_Service::onTransportSocketResult, this, _1, requestId));
  }

  qi::Future<qi::AnyObject> Session_Service::service(const std::string &service,
                                                     const std::string &protocol)
  {
    qi::Future<qi::AnyObject> result;
    if (protocol == "" || protocol == "local") {
      //qiLogError() << "service is not implemented for local service, it always return a remote service";
      //look for local object registered in the server
      qi::AnyObject go = _server->registeredServiceObject(service);
      if (go)
        return qi::Future<qi::AnyObject>(go);
      if (protocol == "local") {
        qi::Promise<qi::AnyObject> prom;
        prom.setError(std::string("No local object found for ") + service);
        return prom.future();
      }
    }

    //look for already registered remote objects
    {
      boost::mutex::scoped_lock sl(_remoteObjectsMutex);
      RemoteObjectMap::iterator it = _remoteObjects.find(service);
      if (it != _remoteObjects.end()) {
        return qi::Future<qi::AnyObject>(it->second);
      }
    }

    {
      boost::recursive_mutex::scoped_lock l(_requestsMutex);
      std::map<int, ServiceRequest*>::const_iterator it;
      for (it = _requests.begin(); it != _requests.end(); ++it)
      {
        if (it->second->name == service)
        {
          return it->second->promise.future();
        }
      }
    }

    qi::Future<qi::ServiceInfo> fut = _sdClient->service(service);
    ServiceRequest *rq = new ServiceRequest(service);
    long requestId = ++_requestsIndex;

    {
      boost::recursive_mutex::scoped_lock l(_requestsMutex);
      _requests[requestId] = rq;
    }
    result = rq->promise.future();
    //rq is not valid anymore after addCallbacks, because it could have been handled and cleaned
    fut.connect(boost::bind<void>(&onServiceInfoResultIfExists, this, _1, requestId, protocol, boost::weak_ptr<Session_Service>(_self)));
    return result;
  }
}

#ifdef _MSC_VER
# pragma warning( pop )
#endif
<|endoftext|>