<commit_before>/*************************************************************************
 *
 *  OpenOffice.org - a multi-platform office productivity suite
 *
 *  $RCSfile: OutlineView.hxx,v $
 *
 *  $Revision: 1.12 $
 *
 *  last change: $Author: kz $ $Date: 2006-12-12 17:35:42 $
 *
 *  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 SD_OUTLINE_VIEW_HXX
#define SD_OUTLINE_VIEW_HXX

#ifndef SD_VIEW_HXX
#include "View.hxx"
#endif

class SdPage;
class DrawDocShell;
class SdrPage;
class EditStatus;
class Paragraph;
class SdrTextObj;
class Outliner;
class SfxProgress;

namespace sd { namespace tools {
class EventMultiplexerEvent;
} }

namespace sd {

class OutlineViewShell;
class OutlineViewModelChangeGuard;

static const int MAX_OUTLINERVIEWS = 4;

/*************************************************************************
|*
|* Ableitung von ::sd::View fuer den Outline-Modus
|*
\************************************************************************/

class OutlineView
    : public ::sd::View
{
    friend class OutlineViewModelChangeGuard;
public:
    OutlineView (DrawDocShell* pDocSh,
        ::Window* pWindow,
        OutlineViewShell* pOutlineViewSh);
    ~OutlineView (void);

    /** This method is called by the view shell that owns the view to tell
        the view that it can safely connect to the application.
        This method must not be called before the view shell is on the shell
        stack.
    */
    void ConnectToApplication (void);
    void DisconnectFromApplication (void);

    TYPEINFO();

    SdrTextObj*     GetTitleTextObject(SdrPage* pPage);
    SdrTextObj*     GetOutlineTextObject(SdrPage* pPage);

    SdrTextObj*     CreateTitleTextObject(SdPage* pPage);
    SdrTextObj*     CreateOutlineTextObject(SdPage* pPage);

    virtual void AddWindowToPaintView(OutputDevice* pWin);
    virtual void DeleteWindowFromPaintView(OutputDevice* pWin);

    OutlinerView*   GetViewByWindow (::Window* pWin) const;
    SdrOutliner*    GetOutliner() { return(mpOutliner) ; }

    Paragraph*      GetPrevTitle(const Paragraph* pPara);
    Paragraph*      GetNextTitle(const Paragraph* pPara);
    SdPage*         GetActualPage();
    SdPage*         GetPageForParagraph( ::Outliner* pOutl, Paragraph* pPara );
    Paragraph*      GetParagraphForPage( ::Outliner* pOutl, SdPage* pPage );

    /** selects the paragraph for the given page at the outliner view*/
    void            SetActualPage( SdPage* pActual );

    virtual void Paint (const Rectangle& rRect, ::sd::Window* pWin);
    virtual void AdjustPosSizePixel(
        const Point &rPos,
        const Size &rSize,
        ::sd::Window* pWindow);

                    // Callbacks fuer LINKs
    DECL_LINK( ParagraphInsertedHdl, Outliner * );
    DECL_LINK( ParagraphRemovingHdl, Outliner * );
    DECL_LINK( DepthChangedHdl, Outliner * );
    DECL_LINK( StatusEventHdl, EditStatus * );
    DECL_LINK( BeginMovingHdl, Outliner * );
    DECL_LINK( EndMovingHdl, Outliner * );
    DECL_LINK( RemovingPagesHdl, OutlinerView * );
    DECL_LINK( IndentingPagesHdl, OutlinerView * );
    DECL_LINK( BeginDropHdl, void * );
    DECL_LINK( EndDropHdl, void * );

    ULONG         GetPaperWidth() const { return 21000; }  // DIN A4 Breite

    BOOL          PrepareClose(BOOL bUI = TRUE);

    virtual BOOL    GetAttributes( SfxItemSet& rTargetSet, BOOL bOnlyHardAttr=FALSE ) const;
    virtual BOOL    SetAttributes(const SfxItemSet& rSet, BOOL bReplaceAll = FALSE);

//  virtual BOOL       HasMarkedObjUnused() const;
    void               FillOutliner();
    void               SetLinks();
    void               ResetLinks() const;

    SfxStyleSheet*     GetStyleSheet() const;

    void               SetSelectedPages();

    virtual sal_Int8 AcceptDrop (
        const AcceptDropEvent& rEvt,
        DropTargetHelper& rTargetHelper,
        ::sd::Window* pTargetWindow = NULL,
        USHORT nPage = SDRPAGE_NOTFOUND,
        USHORT nLayer = SDRPAGE_NOTFOUND);
    virtual sal_Int8 ExecuteDrop (
        const ExecuteDropEvent& rEvt,
        DropTargetHelper& rTargetHelper,
        ::sd::Window* pTargetWindow = NULL,
        USHORT nPage = SDRPAGE_NOTFOUND,
        USHORT nLayer = SDRPAGE_NOTFOUND);

    // #97766# Re-implement GetScriptType for this view to get correct results
    virtual sal_uInt16 GetScriptType() const;

    /** After this method has been called with <TRUE/> following changes of
        the current page are ignored in that the corresponding text is not
        selected.
        This is used to supress unwanted side effects between selection and
        cursor position.
    */
    void IgnoreCurrentPageChanges (bool bIgnore);

private:
    /** call this method before you do anything that can modify the outliner
        and or the drawing document model. It will create needed undo actions */
    void BeginModelChange();

    /** call this method after BeginModelChange(), when all possible model
        changes are done. */
    void EndModelChange();

    /** merge edit engine undo actions if possible */
    void TryToMergeUndoActions();

    /** updates all changes in the outliner model to the draw model */
    void UpdateDocument();

    /** creates and inserts an empty slide for the given paragraph */
    SdPage* InsertSlideForParagraph( Paragraph* pPara );

    OutlineViewShell*   mpOutlineViewShell;
    SdrOutliner*        mpOutliner;
    OutlinerView*       mpOutlinerView[MAX_OUTLINERVIEWS];

    List*               mpOldParaOrder;
    List*               mpSelectedParas;

    USHORT              mnPagesToProcess;    // fuer die Fortschrittsanzeige
    USHORT              mnPagesProcessed;

    BOOL                mbFirstPaint;

    SfxProgress*        mpProgress;

    /** stores the last used high contrast mode.
        this is changed in onUpdateStyleSettings()
    */
    bool mbHighContrastMode;

    /** stores the last used document color.
        this is changed in onUpdateStyleSettings()
    */
    Color maDocColor;

    /** While the value of this flag is greater than 0 changes of the current page
        do not lead to selecting the corresponding text in the outliner.
    */
    int mnIgnoreCurrentPageChangesLevel;

    /** updates the high contrast settings and document color if they changed.
        @param bForceUpdate forces the method to set all style settings
    */
    void onUpdateStyleSettings( bool bForceUpdate = false );

    /** this link is called from the vcl applicaten when the stylesettings
        change. Its only purpose is to call onUpdateStyleSettings() then.
    */
    DECL_LINK( AppEventListenerHdl, void * );

    DECL_LINK(EventMultiplexerListener, sd::tools::EventMultiplexerEvent*);

    /** holds a model guard during drag and drop between BeginMovingHdl and EndMovingHdl */
    std::auto_ptr< OutlineViewModelChangeGuard > maDragAndDropModelGuard;
};

// calls IgnoreCurrentPageChangesLevel with true in ctor and with false in dtor
class OutlineViewPageChangesGuard
{
public:
    OutlineViewPageChangesGuard( OutlineView* pView );
    ~OutlineViewPageChangesGuard();
private:
    OutlineView* mpView;
};

// calls BeginModelChange() on c'tor and EndModelChange() on d'tor
class OutlineViewModelChangeGuard
{
public:
    OutlineViewModelChangeGuard( OutlineView& rView );
    ~OutlineViewModelChangeGuard();
private:
    OutlineView& mrView;
};

} // end of namespace sd

#endif
<commit_msg>INTEGRATION: CWS impress122 (1.12.114); FILE MERGED 2007/06/08 04:06:23 liyuan 1.12.114.1: #i71063<commit_after>/*************************************************************************
 *
 *  OpenOffice.org - a multi-platform office productivity suite
 *
 *  $RCSfile: OutlineView.hxx,v $
 *
 *  $Revision: 1.13 $
 *
 *  last change: $Author: hr $ $Date: 2007-06-27 15:43: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 SD_OUTLINE_VIEW_HXX
#define SD_OUTLINE_VIEW_HXX

#ifndef SD_VIEW_HXX
#include "View.hxx"
#endif

class SdPage;
class DrawDocShell;
class SdrPage;
class EditStatus;
class Paragraph;
class SdrTextObj;
class Outliner;
class SfxProgress;

namespace sd { namespace tools {
class EventMultiplexerEvent;
} }

namespace sd {

class OutlineViewShell;
class OutlineViewModelChangeGuard;

static const int MAX_OUTLINERVIEWS = 4;

/*************************************************************************
|*
|* Ableitung von ::sd::View fuer den Outline-Modus
|*
\************************************************************************/

class OutlineView
    : public ::sd::View
{
    friend class OutlineViewModelChangeGuard;
public:
    OutlineView (DrawDocShell* pDocSh,
        ::Window* pWindow,
        OutlineViewShell* pOutlineViewSh);
    ~OutlineView (void);

    /** This method is called by the view shell that owns the view to tell
        the view that it can safely connect to the application.
        This method must not be called before the view shell is on the shell
        stack.
    */
    void ConnectToApplication (void);
    void DisconnectFromApplication (void);

    TYPEINFO();

    SdrTextObj*     GetTitleTextObject(SdrPage* pPage);
    SdrTextObj*     GetOutlineTextObject(SdrPage* pPage);

    SdrTextObj*     CreateTitleTextObject(SdPage* pPage);
    SdrTextObj*     CreateOutlineTextObject(SdPage* pPage);

    virtual void AddWindowToPaintView(OutputDevice* pWin);
    virtual void DeleteWindowFromPaintView(OutputDevice* pWin);

    OutlinerView*   GetViewByWindow (::Window* pWin) const;
    SdrOutliner*    GetOutliner() { return(mpOutliner) ; }

    Paragraph*      GetPrevTitle(const Paragraph* pPara);
    Paragraph*      GetNextTitle(const Paragraph* pPara);
    SdPage*         GetActualPage();
    SdPage*         GetPageForParagraph( ::Outliner* pOutl, Paragraph* pPara );
    Paragraph*      GetParagraphForPage( ::Outliner* pOutl, SdPage* pPage );

    /** selects the paragraph for the given page at the outliner view*/
    void            SetActualPage( SdPage* pActual );

    virtual void Paint (const Rectangle& rRect, ::sd::Window* pWin);
    virtual void AdjustPosSizePixel(
        const Point &rPos,
        const Size &rSize,
        ::sd::Window* pWindow);

                    // Callbacks fuer LINKs
    DECL_LINK( ParagraphInsertedHdl, Outliner * );
    DECL_LINK( ParagraphRemovingHdl, Outliner * );
    DECL_LINK( DepthChangedHdl, Outliner * );
    DECL_LINK( StatusEventHdl, EditStatus * );
    DECL_LINK( BeginMovingHdl, Outliner * );
    DECL_LINK( EndMovingHdl, Outliner * );
    DECL_LINK( RemovingPagesHdl, OutlinerView * );
    DECL_LINK( IndentingPagesHdl, OutlinerView * );
    DECL_LINK( BeginDropHdl, void * );
    DECL_LINK( EndDropHdl, void * );

    ULONG         GetPaperWidth() const { return 21000; }  // DIN A4 Breite

    BOOL          PrepareClose(BOOL bUI = TRUE);

    virtual BOOL    GetAttributes( SfxItemSet& rTargetSet, BOOL bOnlyHardAttr=FALSE ) const;
    virtual BOOL    SetAttributes(const SfxItemSet& rSet, BOOL bReplaceAll = FALSE);

//  virtual BOOL       HasMarkedObjUnused() const;
    void               FillOutliner();
    void               SetLinks();
    void               ResetLinks() const;

    SfxStyleSheet*     GetStyleSheet() const;

    void               SetSelectedPages();

    virtual sal_Int8 AcceptDrop (
        const AcceptDropEvent& rEvt,
        DropTargetHelper& rTargetHelper,
        ::sd::Window* pTargetWindow = NULL,
        USHORT nPage = SDRPAGE_NOTFOUND,
        USHORT nLayer = SDRPAGE_NOTFOUND);
    virtual sal_Int8 ExecuteDrop (
        const ExecuteDropEvent& rEvt,
        DropTargetHelper& rTargetHelper,
        ::sd::Window* pTargetWindow = NULL,
        USHORT nPage = SDRPAGE_NOTFOUND,
        USHORT nLayer = SDRPAGE_NOTFOUND);

    // #97766# Re-implement GetScriptType for this view to get correct results
    virtual sal_uInt16 GetScriptType() const;

    /** After this method has been called with <TRUE/> following changes of
        the current page are ignored in that the corresponding text is not
        selected.
        This is used to supress unwanted side effects between selection and
        cursor position.
    */
    void IgnoreCurrentPageChanges (bool bIgnore);

private:
    /** call this method before you do anything that can modify the outliner
        and or the drawing document model. It will create needed undo actions */
    void BeginModelChange();

    /** call this method after BeginModelChange(), when all possible model
        changes are done. */
    void EndModelChange();

    /** merge edit engine undo actions if possible */
    void TryToMergeUndoActions();

    /** updates all changes in the outliner model to the draw model */
    void UpdateDocument();

    /** creates and inserts an empty slide for the given paragraph */
    SdPage* InsertSlideForParagraph( Paragraph* pPara );

    OutlineViewShell*   mpOutlineViewShell;
    SdrOutliner*        mpOutliner;
    OutlinerView*       mpOutlinerView[MAX_OUTLINERVIEWS];

    List*               mpOldParaOrder;
    List*               mpSelectedParas;

    USHORT              mnPagesToProcess;    // fuer die Fortschrittsanzeige
    USHORT              mnPagesProcessed;

    BOOL                mbFirstPaint;

    SfxProgress*        mpProgress;

    /** stores the last used high contrast mode.
        this is changed in onUpdateStyleSettings()
    */
    bool mbHighContrastMode;

    /** stores the last used document color.
        this is changed in onUpdateStyleSettings()
    */
    Color maDocColor;

    /** updates the high contrast settings and document color if they changed.
        @param bForceUpdate forces the method to set all style settings
    */
    void onUpdateStyleSettings( bool bForceUpdate = false );

    /** this link is called from the vcl applicaten when the stylesettings
        change. Its only purpose is to call onUpdateStyleSettings() then.
    */
    DECL_LINK( AppEventListenerHdl, void * );

    DECL_LINK(EventMultiplexerListener, sd::tools::EventMultiplexerEvent*);

    /** holds a model guard during drag and drop between BeginMovingHdl and EndMovingHdl */
    std::auto_ptr< OutlineViewModelChangeGuard > maDragAndDropModelGuard;
};

// calls IgnoreCurrentPageChangesLevel with true in ctor and with false in dtor
class OutlineViewPageChangesGuard
{
public:
    OutlineViewPageChangesGuard( OutlineView* pView );
    ~OutlineViewPageChangesGuard();
private:
    OutlineView* mpView;
};

// calls BeginModelChange() on c'tor and EndModelChange() on d'tor
class OutlineViewModelChangeGuard
{
public:
    OutlineViewModelChangeGuard( OutlineView& rView );
    ~OutlineViewModelChangeGuard();
private:
    OutlineView& mrView;
};

} // end of namespace sd

#endif
<|endoftext|>


<commit_before>/*
 * SVN.cpp : Implementation of CSVNWorkingCopy
 *
 * ====================================================================
 * Copyright (c) 2001 CollabNet.  All rights reserved.
 *
 * This software is licensed as described in the file COPYING, which
 * you should have received as part of this distribution.  The terms
 * are also available at http://subversion.tigris.org/license-1.html.
 * If newer versions of this license are posted there, you may use a
 * newer version instead, at your option.
 * ====================================================================
 */

#include "stdafx.h"

#include "SVNCOM.h"
#include "SVN.h"
#include "misc.h"
#include "SVNStatus.h"

/////////////////////////////////////////////////////////////////////////////
// CSVNWorkingCopy

STDMETHODIMP
CSVNWorkingCopy::InterfaceSupportsErrorInfo(REFIID riid)
{
	static const IID* arr[] =
	{
		&IID_ISVNWorkingCopy
	};
	for (int i=0; i < sizeof(arr) / sizeof(arr[0]); i++)
	{
		if (InlineIsEqualGUID(*arr[i],riid))
			return S_OK;
	}
	return S_FALSE;
}

//
// Sets pfIsValid to VARIANT_TRUE if the directory contains
// valid SVN meta-data.
//
STDMETHODIMP
CSVNWorkingCopy::check_wc(BSTR bstrDir, VARIANT_BOOL *pfIsValid)
{
	USES_CONVERSION;
	HRESULT hr;
	svn_error_t *error;
	svn_boolean_t is_wc;
	svn_string_t *path;

	if (pfIsValid == NULL)
		return E_POINTER;

	path = svn_string_create(W2A(bstrDir), g_pool);

	error = svn_wc_check_wc(path, &is_wc, g_pool);

	if (error) {
		hr = convert_err_to_hresult(error);
		goto Cleanup;
	}

	if (is_wc)
		*pfIsValid = VARIANT_TRUE;
	else
		*pfIsValid = VARIANT_FALSE;

	hr = S_OK;

Cleanup:
	svn_pool_clear(g_pool);
	return hr;
}

//
// Create a secondary thread to watch for file changes
// in the specified directory (bstrDir).
// This secondary thread fires "RefreshFiles" when
// a change is detected.
//
STDMETHODIMP
CSVNWorkingCopy::watch_dir(BSTR bstrDir)
{
	USES_CONVERSION;
	HRESULT hr;
	CHAR *psz;

	EnterCriticalSection(&_csNewDir);

	if (_hFindNotification_Stop == NULL)
	{
		_hFindNotification_NewDir = CreateEvent(
			NULL, FALSE, FALSE, NULL);
		if (_hFindNotification_NewDir == NULL)
		{
			hr = E_FAIL;
			goto Error;
		}

		_hFindNotification_Stop = CreateEvent(
			NULL, FALSE, FALSE, NULL);
		if (_hFindNotification_Stop == NULL)
		{
			hr = E_FAIL;
			goto Error;
		}

		_hFindNotification_Thread = (HANDLE)_beginthread(
			FileNotificationThreadProc,
			0,
			this);
		if (_hFindNotification_Thread == NULL)
		{
			hr = E_FAIL;
			goto Cleanup;
		}
	}
	else
	{
		// Free previous directory.
		free(_pszNotification_Dir);
	}

	// Setup new direcotry
	psz = W2A(bstrDir);
	_pszNotification_Dir = (char *)malloc(strlen(psz) + 1);
	strcpy(_pszNotification_Dir, psz);

	// Tell the thread that we have a directory to care about.
	SetEvent(_hFindNotification_NewDir);

	hr = S_OK;
Cleanup:
	LeaveCriticalSection(&_csNewDir);
	return hr;
Error:
	Assert(FAILED(hr));
	if (_hFindNotification_Stop != NULL)
		CloseHandle(_hFindNotification_Stop);
	if (_hFindNotification_NewDir != NULL)
		CloseHandle(_hFindNotification_NewDir);
	goto Cleanup;
}

// Pump any waiting messages in the message queue.
static void
PumpWaitingMessages(void)
{
	MSG msg;

	//
	// Read all of the messages in this next loop,
	// removing each message as we read it.
	//
	while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
	{
		//
		// If it's a quit message, we're out of here.
		//
		if (msg.message == WM_QUIT)
		{
			break;
		}

		//
		// Otherwise, dispatch the message.
		//
		DispatchMessage(&msg);
	}
}

//
// Thread procedure for the file/directory notification
// thread. This thread is created for the first time when
// some calls watch_dir.
//
void
CSVNWorkingCopy::FileNotificationThreadProc(void *pv)
{
	USES_CONVERSION;
	CSVNWorkingCopy *wc;
	// Keep track of the current directory we're watching.
	CHAR *pszCurrentDir = NULL;
	HANDLE aHandles[3];
	DWORD dw;
	CComBSTR sbstr;

	// Initialize COM on this thread
	if (FAILED(CoInitializeEx(NULL, COINIT_MULTITHREADED)))
		abort();

	wc = static_cast<CSVNWorkingCopy *>(pv);

	// Wait for initial all clear.
	WaitForSingleObject(wc->_hFindNotification_NewDir, INFINITE);

	aHandles[1] = wc->_hFindNotification_NewDir;
	aHandles[2] = wc->_hFindNotification_Stop;

NewDirectory:
	if (wc->_hFindNotification != NULL)
	{
		FindCloseChangeNotification(wc->_hFindNotification);
	}

	// Enter critical section to lock down _pszNotification_Dir
	EnterCriticalSection(&wc->_csNewDir);

	if (pszCurrentDir != NULL)
	{
		free(pszCurrentDir);
	}

	pszCurrentDir = (CHAR *)malloc(strlen(wc->_pszNotification_Dir) + 1);
	if (pszCurrentDir == NULL)
	{
		abort();
	}

	strcpy(pszCurrentDir, wc->_pszNotification_Dir);

	// Leave the critical section.
	LeaveCriticalSection(&wc->_csNewDir);

	sbstr.Empty();
	sbstr = A2BSTR(pszCurrentDir);

	wc->_hFindNotification = FindFirstChangeNotification(
		pszCurrentDir, FALSE,
		FILE_NOTIFY_CHANGE_FILE_NAME |
		FILE_NOTIFY_CHANGE_LAST_WRITE);
	if (wc->_hFindNotification == INVALID_HANDLE_VALUE)
	{
		abort();
	}

	aHandles[0] = wc->_hFindNotification;

	while (1)
	{
		dw = MsgWaitForMultipleObjects(
			3, aHandles,
			FALSE, INFINITE, QS_ALLINPUT);
		switch (dw)
		{
		// Change notification
		case WAIT_OBJECT_0:
			wc->Fire_RefreshFiles(sbstr);
			if (!FindNextChangeNotification(wc->_hFindNotification))
				abort();
			continue;
		// New directory
		case WAIT_OBJECT_0 + 1:
			goto NewDirectory;
		// End Thread
		case WAIT_OBJECT_0 + 2:
			_endthread();
		// Since this thread is COM enabled,
		// we have to be good citizens, and
		// pump any incoming messages to
		// our thread, so that we prevent broadcasted
		// SendMessage calls from hanging.
		case WAIT_OBJECT_0 + 3:
			PumpWaitingMessages();
			continue;
		}
	}
}

STDMETHODIMP
CSVNWorkingCopy::wc_statuses(BSTR bstrPath, SAFEARRAY **ppsa)
{
	USES_CONVERSION;
	HRESULT hr;
	apr_hash_t *hash;
	svn_error_t *error;
	svn_wc_status_t *status;
	svn_string_t *path;
	apr_hash_index_t *hi;
	SAFEARRAY *psa;
	SAFEARRAYBOUND rgsBound;
	IDispatch **paDisp;
	apr_size_t klen;
	int i;
	apr_size_t count;
	CHAR *pszKey;
	CComObject<CSVNStatus> *com_status = NULL;
	svn_boolean_t fLockedSA = FALSE;

	hash = apr_make_hash(g_pool);
	path = svn_string_create(W2A(bstrPath), g_pool);
	error = svn_wc_statuses(hash, path, FALSE, g_pool);
	if (error) {
		hr = convert_err_to_hresult(error);
		goto Cleanup;
	}

	count = apr_hash_count(hash);
	rgsBound.cElements = count;
	rgsBound.lLbound = 0;

	psa = SafeArrayCreate(VT_DISPATCH, 1, &rgsBound);
	if (psa == NULL)
		abort();

	hr = SafeArrayAccessData(psa, (void **)&paDisp);
	if (FAILED(hr))
		goto Cleanup;
	fLockedSA = TRUE;

	for (i = 0, hi = apr_hash_first(hash); hi; i++, hi = apr_hash_next(hi)) {
		apr_hash_this(hi, (const void **)&pszKey, &klen, (void **)&status);
		hr = CComObject<CSVNStatus>::CreateInstance(&com_status);
		if (FAILED(hr))
			goto Cleanup;
		// This is what we want since the hash key is an absolute path.
		pszKey = ((svn_string_t *)apr_hash_get (status->entry->attributes,
							   (const void *)SVN_WC_ENTRY_ATTR_NAME,
							   strlen(SVN_WC_ENTRY_ATTR_NAME)))->data;
		// SVN_WC_ENTRY_THIS_DIR is ".", we don't care about its status.
		if (strcmp(pszKey, SVN_WC_ENTRY_THIS_DIR) == 0) {
			delete com_status;
			com_status = NULL;
			// Pretend the iteration didn't happen.
			i--;
			continue;
		}
		hr = com_status->init(status, pszKey);
		if (FAILED(hr))
			goto Cleanup;
		hr = com_status->QueryInterface(&paDisp[i]);
		if (FAILED(hr))
			goto Cleanup;

		// NULL out the local variable,
		// the COM reference count is now owned by paDisp[i]
		com_status = NULL;
	}

	SafeArrayUnaccessData(psa);
	fLockedSA = FALSE;

	if (rgsBound.cElements > (UINT)i) {
		rgsBound.cElements = i;
		hr = SafeArrayRedim(psa, &rgsBound);
		if (FAILED(hr))
			goto Cleanup;
	}

	*ppsa = psa;

	hr = S_OK;
Cleanup:
	if (fLockedSA)
		SafeArrayUnaccessData(psa);
	delete com_status;
	svn_pool_clear(g_pool);
	return hr;
}
<commit_msg>SVN.cpp (CSVNWorkingCopy::wc_statuses): Rename apr_make_hash to apr_hash_make to follow APR's lead.<commit_after>/*
 * SVN.cpp : Implementation of CSVNWorkingCopy
 *
 * ====================================================================
 * Copyright (c) 2001 CollabNet.  All rights reserved.
 *
 * This software is licensed as described in the file COPYING, which
 * you should have received as part of this distribution.  The terms
 * are also available at http://subversion.tigris.org/license-1.html.
 * If newer versions of this license are posted there, you may use a
 * newer version instead, at your option.
 * ====================================================================
 */

#include "stdafx.h"

#include "SVNCOM.h"
#include "SVN.h"
#include "misc.h"
#include "SVNStatus.h"

/////////////////////////////////////////////////////////////////////////////
// CSVNWorkingCopy

STDMETHODIMP
CSVNWorkingCopy::InterfaceSupportsErrorInfo(REFIID riid)
{
	static const IID* arr[] =
	{
		&IID_ISVNWorkingCopy
	};
	for (int i=0; i < sizeof(arr) / sizeof(arr[0]); i++)
	{
		if (InlineIsEqualGUID(*arr[i],riid))
			return S_OK;
	}
	return S_FALSE;
}

//
// Sets pfIsValid to VARIANT_TRUE if the directory contains
// valid SVN meta-data.
//
STDMETHODIMP
CSVNWorkingCopy::check_wc(BSTR bstrDir, VARIANT_BOOL *pfIsValid)
{
	USES_CONVERSION;
	HRESULT hr;
	svn_error_t *error;
	svn_boolean_t is_wc;
	svn_string_t *path;

	if (pfIsValid == NULL)
		return E_POINTER;

	path = svn_string_create(W2A(bstrDir), g_pool);

	error = svn_wc_check_wc(path, &is_wc, g_pool);

	if (error) {
		hr = convert_err_to_hresult(error);
		goto Cleanup;
	}

	if (is_wc)
		*pfIsValid = VARIANT_TRUE;
	else
		*pfIsValid = VARIANT_FALSE;

	hr = S_OK;

Cleanup:
	svn_pool_clear(g_pool);
	return hr;
}

//
// Create a secondary thread to watch for file changes
// in the specified directory (bstrDir).
// This secondary thread fires "RefreshFiles" when
// a change is detected.
//
STDMETHODIMP
CSVNWorkingCopy::watch_dir(BSTR bstrDir)
{
	USES_CONVERSION;
	HRESULT hr;
	CHAR *psz;

	EnterCriticalSection(&_csNewDir);

	if (_hFindNotification_Stop == NULL)
	{
		_hFindNotification_NewDir = CreateEvent(
			NULL, FALSE, FALSE, NULL);
		if (_hFindNotification_NewDir == NULL)
		{
			hr = E_FAIL;
			goto Error;
		}

		_hFindNotification_Stop = CreateEvent(
			NULL, FALSE, FALSE, NULL);
		if (_hFindNotification_Stop == NULL)
		{
			hr = E_FAIL;
			goto Error;
		}

		_hFindNotification_Thread = (HANDLE)_beginthread(
			FileNotificationThreadProc,
			0,
			this);
		if (_hFindNotification_Thread == NULL)
		{
			hr = E_FAIL;
			goto Cleanup;
		}
	}
	else
	{
		// Free previous directory.
		free(_pszNotification_Dir);
	}

	// Setup new direcotry
	psz = W2A(bstrDir);
	_pszNotification_Dir = (char *)malloc(strlen(psz) + 1);
	strcpy(_pszNotification_Dir, psz);

	// Tell the thread that we have a directory to care about.
	SetEvent(_hFindNotification_NewDir);

	hr = S_OK;
Cleanup:
	LeaveCriticalSection(&_csNewDir);
	return hr;
Error:
	Assert(FAILED(hr));
	if (_hFindNotification_Stop != NULL)
		CloseHandle(_hFindNotification_Stop);
	if (_hFindNotification_NewDir != NULL)
		CloseHandle(_hFindNotification_NewDir);
	goto Cleanup;
}

// Pump any waiting messages in the message queue.
static void
PumpWaitingMessages(void)
{
	MSG msg;

	//
	// Read all of the messages in this next loop,
	// removing each message as we read it.
	//
	while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
	{
		//
		// If it's a quit message, we're out of here.
		//
		if (msg.message == WM_QUIT)
		{
			break;
		}

		//
		// Otherwise, dispatch the message.
		//
		DispatchMessage(&msg);
	}
}

//
// Thread procedure for the file/directory notification
// thread. This thread is created for the first time when
// some calls watch_dir.
//
void
CSVNWorkingCopy::FileNotificationThreadProc(void *pv)
{
	USES_CONVERSION;
	CSVNWorkingCopy *wc;
	// Keep track of the current directory we're watching.
	CHAR *pszCurrentDir = NULL;
	HANDLE aHandles[3];
	DWORD dw;
	CComBSTR sbstr;

	// Initialize COM on this thread
	if (FAILED(CoInitializeEx(NULL, COINIT_MULTITHREADED)))
		abort();

	wc = static_cast<CSVNWorkingCopy *>(pv);

	// Wait for initial all clear.
	WaitForSingleObject(wc->_hFindNotification_NewDir, INFINITE);

	aHandles[1] = wc->_hFindNotification_NewDir;
	aHandles[2] = wc->_hFindNotification_Stop;

NewDirectory:
	if (wc->_hFindNotification != NULL)
	{
		FindCloseChangeNotification(wc->_hFindNotification);
	}

	// Enter critical section to lock down _pszNotification_Dir
	EnterCriticalSection(&wc->_csNewDir);

	if (pszCurrentDir != NULL)
	{
		free(pszCurrentDir);
	}

	pszCurrentDir = (CHAR *)malloc(strlen(wc->_pszNotification_Dir) + 1);
	if (pszCurrentDir == NULL)
	{
		abort();
	}

	strcpy(pszCurrentDir, wc->_pszNotification_Dir);

	// Leave the critical section.
	LeaveCriticalSection(&wc->_csNewDir);

	sbstr.Empty();
	sbstr = A2BSTR(pszCurrentDir);

	wc->_hFindNotification = FindFirstChangeNotification(
		pszCurrentDir, FALSE,
		FILE_NOTIFY_CHANGE_FILE_NAME |
		FILE_NOTIFY_CHANGE_LAST_WRITE);
	if (wc->_hFindNotification == INVALID_HANDLE_VALUE)
	{
		abort();
	}

	aHandles[0] = wc->_hFindNotification;

	while (1)
	{
		dw = MsgWaitForMultipleObjects(
			3, aHandles,
			FALSE, INFINITE, QS_ALLINPUT);
		switch (dw)
		{
		// Change notification
		case WAIT_OBJECT_0:
			wc->Fire_RefreshFiles(sbstr);
			if (!FindNextChangeNotification(wc->_hFindNotification))
				abort();
			continue;
		// New directory
		case WAIT_OBJECT_0 + 1:
			goto NewDirectory;
		// End Thread
		case WAIT_OBJECT_0 + 2:
			_endthread();
		// Since this thread is COM enabled,
		// we have to be good citizens, and
		// pump any incoming messages to
		// our thread, so that we prevent broadcasted
		// SendMessage calls from hanging.
		case WAIT_OBJECT_0 + 3:
			PumpWaitingMessages();
			continue;
		}
	}
}

STDMETHODIMP
CSVNWorkingCopy::wc_statuses(BSTR bstrPath, SAFEARRAY **ppsa)
{
	USES_CONVERSION;
	HRESULT hr;
	apr_hash_t *hash;
	svn_error_t *error;
	svn_wc_status_t *status;
	svn_string_t *path;
	apr_hash_index_t *hi;
	SAFEARRAY *psa;
	SAFEARRAYBOUND rgsBound;
	IDispatch **paDisp;
	apr_size_t klen;
	int i;
	apr_size_t count;
	CHAR *pszKey;
	CComObject<CSVNStatus> *com_status = NULL;
	svn_boolean_t fLockedSA = FALSE;

	hash = apr_hash_make(g_pool);
	path = svn_string_create(W2A(bstrPath), g_pool);
	error = svn_wc_statuses(hash, path, FALSE, g_pool);
	if (error) {
		hr = convert_err_to_hresult(error);
		goto Cleanup;
	}

	count = apr_hash_count(hash);
	rgsBound.cElements = count;
	rgsBound.lLbound = 0;

	psa = SafeArrayCreate(VT_DISPATCH, 1, &rgsBound);
	if (psa == NULL)
		abort();

	hr = SafeArrayAccessData(psa, (void **)&paDisp);
	if (FAILED(hr))
		goto Cleanup;
	fLockedSA = TRUE;

	for (i = 0, hi = apr_hash_first(hash); hi; i++, hi = apr_hash_next(hi)) {
		apr_hash_this(hi, (const void **)&pszKey, &klen, (void **)&status);
		hr = CComObject<CSVNStatus>::CreateInstance(&com_status);
		if (FAILED(hr))
			goto Cleanup;
		// This is what we want since the hash key is an absolute path.
		pszKey = ((svn_string_t *)apr_hash_get (status->entry->attributes,
							   (const void *)SVN_WC_ENTRY_ATTR_NAME,
							   strlen(SVN_WC_ENTRY_ATTR_NAME)))->data;
		// SVN_WC_ENTRY_THIS_DIR is ".", we don't care about its status.
		if (strcmp(pszKey, SVN_WC_ENTRY_THIS_DIR) == 0) {
			delete com_status;
			com_status = NULL;
			// Pretend the iteration didn't happen.
			i--;
			continue;
		}
		hr = com_status->init(status, pszKey);
		if (FAILED(hr))
			goto Cleanup;
		hr = com_status->QueryInterface(&paDisp[i]);
		if (FAILED(hr))
			goto Cleanup;

		// NULL out the local variable,
		// the COM reference count is now owned by paDisp[i]
		com_status = NULL;
	}

	SafeArrayUnaccessData(psa);
	fLockedSA = FALSE;

	if (rgsBound.cElements > (UINT)i) {
		rgsBound.cElements = i;
		hr = SafeArrayRedim(psa, &rgsBound);
		if (FAILED(hr))
			goto Cleanup;
	}

	*ppsa = psa;

	hr = S_OK;
Cleanup:
	if (fLockedSA)
		SafeArrayUnaccessData(psa);
	delete com_status;
	svn_pool_clear(g_pool);
	return hr;
}
<|endoftext|>


<commit_before>/*
 * Copyright(c) Sophist Solutions, Inc. 1990-2015.  All rights reserved
 */
#ifndef _Stroika_Foundation_Math_ReBin_inl_
#define _Stroika_Foundation_Math_ReBin_inl_ 1

/*
 ********************************************************************************
 ***************************** Implementation Details ***************************
 ********************************************************************************
 */
#include    "../Debug/Assertions.h"
#include    "../Traversal/Generator.h"
#include    "../Traversal/Partition.h"

#include    "Common.h"

namespace   Stroika {
    namespace   Foundation {
        namespace   Math {
            namespace   ReBin {

                /*
                 ********************************************************************************
                 ********************** Math::ReBin::BasicDataDescriptor ************************
                 ********************************************************************************
                 */
                template    <typename X_TYPE, typename VALUE_TYPE>
                inline  BasicDataDescriptor<X_TYPE, VALUE_TYPE>::BasicDataDescriptor (const ValueType* bucketStart, const ValueType* bucketEnd, XType xStart, XType xEnd)
                    : fBucketDataStart_ (bucketStart)
                    , fBucketDataEnd_ (bucketEnd)
                    , fXStart_ (xStart)
                    , fXEnd_ (xEnd)
                {
                    RequireNotNull (bucketStart);
                    RequireNotNull (bucketEnd);
                    Require (bucketStart < bucketEnd);
                    Require (xStart < xEnd);
                }
                template    <typename X_TYPE, typename VALUE_TYPE>
                inline  typename BasicDataDescriptor<X_TYPE, VALUE_TYPE>::BucketIndexType  BasicDataDescriptor<X_TYPE, VALUE_TYPE>::GetBucketCount () const
                {
                    BucketIndexType result = fBucketDataEnd_ - fBucketDataStart_;
                    Ensure (result > 0);
                    return result;
                }
                template    <typename X_TYPE, typename VALUE_TYPE>
                inline  auto   BasicDataDescriptor<X_TYPE, VALUE_TYPE>::GetBucketRange (BucketIndexType bucket) const -> Traversal::Range<XType> {
                    using Traversal::Range;
                    Require (bucket < GetBucketCount ());
                    X_TYPE  kDelta_ = (fXEnd_ - fXStart_) * (static_cast<X_TYPE> (1) / static_cast<X_TYPE> (GetBucketCount ()));
                    X_TYPE  s = fXStart_ + (static_cast<X_TYPE> (bucket) * kDelta_);
                    return Range<X_TYPE> (s, s + kDelta_, Traversal::Openness::eClosed, Traversal::Openness::eOpen);
                }
                template    <typename X_TYPE, typename VALUE_TYPE>
                auto BasicDataDescriptor<X_TYPE, VALUE_TYPE>::GetMappedBucketRange (const Traversal::Range<XType>& xrange) const -> Traversal::DiscreteRange<BucketIndexType> {
                    using Traversal::DiscreteRange;
                    if (xrange.GetUpperBound () < fXStart_)
                    {
                        return DiscreteRange<BucketIndexType> ();
                    }
                    if (xrange.GetLowerBound () > fXEnd_)
                    {
                        return DiscreteRange<BucketIndexType> ();
                    }

                    X_TYPE  kDelta_ = (fXEnd_ - fXStart_) * (static_cast<X_TYPE> (1) / static_cast<X_TYPE> (GetBucketCount ()));

                    // very roughly (quick hack)
                    X_TYPE bucketLowerBound = (xrange.GetLowerBound () - fXStart_) / kDelta_;
                    X_TYPE bucketUpperBound = (xrange.GetUpperBound () - fXStart_) / kDelta_;

                    bucketLowerBound = (bucketLowerBound < 0) ? 0 : bucketLowerBound;
                    bucketUpperBound = (bucketUpperBound < 0) ? 0 : bucketUpperBound;

                    BucketIndexType    bucketLB = Math::PinInRange<BucketIndexType> (static_cast<BucketIndexType> (floor (bucketLowerBound)), 0, GetBucketCount () - 1);
                    BucketIndexType    bucketUB = Math::PinInRange<BucketIndexType> (static_cast<BucketIndexType> (ceil (bucketUpperBound)), bucketLB, GetBucketCount () - 1);

                    return DiscreteRange<BucketIndexType> (bucketLB, bucketUB);
                }
                template    <typename X_TYPE, typename VALUE_TYPE>
                inline  VALUE_TYPE  BasicDataDescriptor<X_TYPE, VALUE_TYPE>::GetValue (BucketIndexType bucket) const
                {
                    Require (bucket < GetBucketCount ());
                    return fBucketDataStart_[bucket];
                }
#if qCompilerAndStdLib_constexpr_Buggy
                template    <typename X_TYPE, typename VALUE_TYPE>
                const VALUE_TYPE BasicDataDescriptor<X_TYPE, VALUE_TYPE>::kZero = 0;
#endif

                /*
                 ********************************************************************************
                 ***************** Math::ReBin::UpdatableDataDescriptor *************************
                 ********************************************************************************
                 */
                template    <typename X_TYPE, typename VALUE_TYPE>
                inline  UpdatableDataDescriptor<X_TYPE, VALUE_TYPE>::UpdatableDataDescriptor (VALUE_TYPE* bucketStart, VALUE_TYPE* bucketEnd, X_TYPE xStart, X_TYPE xEnd)
                    : inherited (bucketStart, bucketEnd, xStart, xEnd)
                {
                }
                template    <typename X_TYPE, typename VALUE_TYPE>
                inline  void  UpdatableDataDescriptor<X_TYPE, VALUE_TYPE>::AccumulateValue (typename inherited::BucketIndexType bucket, VALUE_TYPE delta)
                {
                    Require (bucket < inherited::GetBucketCount ());
                    VALUE_TYPE*  updatableStart = const_cast<VALUE_TYPE*> (inherited::fBucketDataStart_);
                    updatableStart[bucket] += delta;
                }
                template    <typename X_TYPE, typename VALUE_TYPE>
                inline  void    UpdatableDataDescriptor<X_TYPE, VALUE_TYPE>::ZeroBuckets ()
                {
                    VALUE_TYPE*  updatableStart = const_cast<VALUE_TYPE*> (inherited::fBucketDataStart_);
                    for (VALUE_TYPE* i = updatableStart; i != inherited::fBucketDataEnd_; ++i) {
                        *i = inherited::kZero;
                    }
                }

                /*
                 ********************************************************************************
                 ************************************* PRIVATE_ *********************************
                 ********************************************************************************
                 */
                namespace PRIVATE_ {
                    template    <typename DATA_DESCRIPTOR_TYPE>
                    void    CheckRebinDataDescriptorInvariant_ (const DATA_DESCRIPTOR_TYPE& d)
                    {
#if     qDebug
                        using   namespace   Traversal;
                        using   Memory::Optional;
                        using   BucketIndexType = typename DATA_DESCRIPTOR_TYPE::BucketIndexType;
                        using   XType = typename DATA_DESCRIPTOR_TYPE::XType;
                        auto myContext = shared_ptr<BucketIndexType> (new BucketIndexType (0));
                        auto bucketCount = d.GetBucketCount ();
                        auto getNext = [myContext, bucketCount, d] () -> Optional<Range<XType>> {
                            Optional<Range<XType>>   result;
                            if (*myContext < bucketCount)
                            {
                                result = d.GetBucketRange (*myContext);
                                (*myContext)++;
                            }
                            return result;
                        };
                        Assert (IsPartition (CreateGenerator<Range<XType>> (getNext)));
#endif
                    }
                }

                /*
                 ********************************************************************************
                 ********************************** Math::ReBin *********************************
                 ********************************************************************************
                 */
                template    <typename   SRC_DATA_DESCRIPTOR, typename TRG_DATA_DESCRIPTOR>
                void    ReBin (
                    const SRC_DATA_DESCRIPTOR& srcData,
                    TRG_DATA_DESCRIPTOR* trgData
                )
                {
                    RequireNotNull (trgData);

                    using   namespace Traversal;
                    using   BucketIndexType =   typename SRC_DATA_DESCRIPTOR::BucketIndexType;

                    /*
                     *  OLD OBSOLETE Algorithm:
                     *      Two iterators - one marking (start/end of target buckets), and one marking current
                     *      src bucket. Iterate over outer buckets. Move contents to new bucket. And adjust new
                     *      iterators. When they overlap and must advance - proportionally add to bucket,
                     *      advance and add rest to next target bucket.
                     *
                     *      This is probably more efficent than the algorithm below, but trickier to generalize
                     *      so we can have differently scaled source buckets.
                     */

                    /*
                     *  By default, zero all target buckets, but you could override that in the TRG argument
                     *  to avoid zeroing (say to accumulate multiple sources)
                     */
                    trgData->ZeroBuckets ();

#if     qDebug
                    PRIVATE_::CheckRebinDataDescriptorInvariant_ (srcData);
                    PRIVATE_::CheckRebinDataDescriptorInvariant_ (*trgData);
#endif

                    /*
                     *  x               0    1    2    3    4    5
                     *
                     *  SRC-BUCKETS:    |    |    |    |    |    |
                     *  TRG-BUCKETS:    |      |      |      |
                     *
                     *  Iterate over each source bucket. It represnts data from its sourceXStart, to srcXEnd (bucket X domain)
                     *  all data spread evently.
                     *
                     *  See where that start x/endx map to in y buckets, and spread it over them. Assume that in the TARGET
                     *  the buckets are contiguous.
                     *
                     *  For each one source bucket, put part into current ti (proportional), and put reset into next
                     *  ti (proportional).
                     */
                    BucketIndexType      srcBucketCount = srcData.GetBucketCount ();
                    for (BucketIndexType srcBucketIdx = 0; srcBucketIdx < srcBucketCount; ++srcBucketIdx) {
                        Range<typename SRC_DATA_DESCRIPTOR::XType>  curSrcBucketX       =   srcData.GetBucketRange(srcBucketIdx);
                        auto                                        curSrcBucketXWidth  =   curSrcBucketX.GetDistanceSpanned ();
                        typename SRC_DATA_DESCRIPTOR::ValueType     thisSrcBucketValue  =   srcData.GetValue(srcBucketIdx);

                        Assert (curSrcBucketXWidth >= 0);   // can ever be zero?

                        // Check special value of zero so we dont waste time spreading zeros around
                        if (thisSrcBucketValue != SRC_DATA_DESCRIPTOR::kZero and curSrcBucketXWidth != 0) {
                            /*
                             *  find range of target buckets to distribute value.
                             *
                             *  Each bucket returned may have little overall contribution from the source bucket. We look at
                             *  the degree of overlap.
                             */
                            for (auto targetBucket : trgData->GetMappedBucketRange (curSrcBucketX).Elements ()) {
                                Range<typename SRC_DATA_DESCRIPTOR::XType>  trgBucketIntersectRange =   trgData->GetBucketRange(targetBucket).Intersection (curSrcBucketX);
                                auto                                        trgBucketXWidth         =   trgBucketIntersectRange.GetDistanceSpanned ();
                                trgData->AccumulateValue (targetBucket, thisSrcBucketValue * (trgBucketXWidth / curSrcBucketXWidth));
                            }
                        }
                    }
                }
                template    <typename SRC_BUCKET_TYPE, typename TRG_BUCKET_TYPE, typename X_OFFSET_TYPE>
                void    ReBin (
                    const SRC_BUCKET_TYPE* srcStart, const SRC_BUCKET_TYPE* srcEnd,
                    TRG_BUCKET_TYPE* trgStart, TRG_BUCKET_TYPE* trgEnd
                )
                {
                    using   SRC_DATA_DESCRIPTOR     =   BasicDataDescriptor<X_OFFSET_TYPE, SRC_BUCKET_TYPE>;
                    using   TRG_DATA_DESCRIPTOR     =   UpdatableDataDescriptor<X_OFFSET_TYPE, TRG_BUCKET_TYPE>;

                    SRC_DATA_DESCRIPTOR srcData (srcStart, srcEnd, 1, 2);
                    TRG_DATA_DESCRIPTOR trgData (trgStart, trgEnd, 1, 2);
                    ReBin (srcData, &trgData);
                }

            }
        }
    }
}
#endif  /*_Stroika_Foundation_Math_ReBin_inl_*/
<commit_msg>Cleanup Rebin code<commit_after>/*
 * Copyright(c) Sophist Solutions, Inc. 1990-2015.  All rights reserved
 */
#ifndef _Stroika_Foundation_Math_ReBin_inl_
#define _Stroika_Foundation_Math_ReBin_inl_ 1

/*
 ********************************************************************************
 ***************************** Implementation Details ***************************
 ********************************************************************************
 */
#include    "../Debug/Assertions.h"
#include    "../Traversal/Generator.h"
#include    "../Traversal/Partition.h"

#include    "Common.h"

namespace   Stroika {
    namespace   Foundation {
        namespace   Math {
            namespace   ReBin {

                /*
                 ********************************************************************************
                 ********************** Math::ReBin::BasicDataDescriptor ************************
                 ********************************************************************************
                 */
                template    <typename X_TYPE, typename VALUE_TYPE>
                inline  BasicDataDescriptor<X_TYPE, VALUE_TYPE>::BasicDataDescriptor (const ValueType* bucketStart, const ValueType* bucketEnd, XType xStart, XType xEnd)
                    : fBucketDataStart_ (bucketStart)
                    , fBucketDataEnd_ (bucketEnd)
                    , fXStart_ (xStart)
                    , fXEnd_ (xEnd)
                {
                    RequireNotNull (bucketStart);
                    RequireNotNull (bucketEnd);
                    Require (bucketStart < bucketEnd);
                    Require (xStart < xEnd);
                }
                template    <typename X_TYPE, typename VALUE_TYPE>
                inline  typename BasicDataDescriptor<X_TYPE, VALUE_TYPE>::BucketIndexType  BasicDataDescriptor<X_TYPE, VALUE_TYPE>::GetBucketCount () const
                {
                    BucketIndexType result = fBucketDataEnd_ - fBucketDataStart_;
                    Ensure (result > 0);
                    return result;
                }
                template    <typename X_TYPE, typename VALUE_TYPE>
                inline  auto   BasicDataDescriptor<X_TYPE, VALUE_TYPE>::GetBucketRange (BucketIndexType bucket) const -> Traversal::Range<XType> {
                    using Traversal::Range;
                    Require (bucket < GetBucketCount ());
                    X_TYPE  kDelta_ = (fXEnd_ - fXStart_) * (static_cast<X_TYPE> (1) / static_cast<X_TYPE> (GetBucketCount ()));
                    X_TYPE  s = fXStart_ + (static_cast<X_TYPE> (bucket) * kDelta_);
                    return Range<X_TYPE> (s, s + kDelta_, Traversal::Openness::eClosed, Traversal::Openness::eOpen);
                }
                template    <typename X_TYPE, typename VALUE_TYPE>
                auto BasicDataDescriptor<X_TYPE, VALUE_TYPE>::GetMappedBucketRange (const Traversal::Range<XType>& xrange) const -> Traversal::DiscreteRange<BucketIndexType> {
                    using Traversal::DiscreteRange;
                    if (xrange.GetUpperBound () < fXStart_)
                    {
                        return DiscreteRange<BucketIndexType> ();
                    }
                    if (xrange.GetLowerBound () > fXEnd_)
                    {
                        return DiscreteRange<BucketIndexType> ();
                    }

                    X_TYPE  kDelta_ = (fXEnd_ - fXStart_) * (static_cast<X_TYPE> (1) / static_cast<X_TYPE> (GetBucketCount ()));

                    // very roughly (quick hack)
                    X_TYPE bucketLowerBound = (xrange.GetLowerBound () - fXStart_) / kDelta_;
                    X_TYPE bucketUpperBound = (xrange.GetUpperBound () - fXStart_) / kDelta_;

                    bucketLowerBound = (bucketLowerBound < 0) ? 0 : bucketLowerBound;
                    bucketUpperBound = (bucketUpperBound < 0) ? 0 : bucketUpperBound;

                    BucketIndexType    bucketLB = Math::PinInRange<BucketIndexType> (static_cast<BucketIndexType> (floor (bucketLowerBound)), 0, GetBucketCount () - 1);
                    BucketIndexType    bucketUB = Math::PinInRange<BucketIndexType> (static_cast<BucketIndexType> (ceil (bucketUpperBound)), bucketLB, GetBucketCount () - 1);

                    return DiscreteRange<BucketIndexType> (bucketLB, bucketUB);
                }
                template    <typename X_TYPE, typename VALUE_TYPE>
                inline  auto  BasicDataDescriptor<X_TYPE, VALUE_TYPE>::GetValue (BucketIndexType bucket) const -> ValueType
                {
                    Require (bucket < GetBucketCount ());
                    return fBucketDataStart_[bucket];
                }
#if qCompilerAndStdLib_constexpr_Buggy
                template    <typename X_TYPE, typename VALUE_TYPE>
                const VALUE_TYPE BasicDataDescriptor<X_TYPE, VALUE_TYPE>::kZero = 0;
#endif

                /*
                 ********************************************************************************
                 ***************** Math::ReBin::UpdatableDataDescriptor *************************
                 ********************************************************************************
                 */
                template    <typename X_TYPE, typename VALUE_TYPE>
                inline  UpdatableDataDescriptor<X_TYPE, VALUE_TYPE>::UpdatableDataDescriptor (VALUE_TYPE* bucketStart, VALUE_TYPE* bucketEnd, X_TYPE xStart, X_TYPE xEnd)
                    : inherited (bucketStart, bucketEnd, xStart, xEnd)
                {
                }
                template    <typename X_TYPE, typename VALUE_TYPE>
                inline  void  UpdatableDataDescriptor<X_TYPE, VALUE_TYPE>::AccumulateValue (typename inherited::BucketIndexType bucket, VALUE_TYPE delta)
                {
                    Require (bucket < inherited::GetBucketCount ());
                    VALUE_TYPE*  updatableStart = const_cast<VALUE_TYPE*> (inherited::fBucketDataStart_);
                    updatableStart[bucket] += delta;
                }
                template    <typename X_TYPE, typename VALUE_TYPE>
                inline  void    UpdatableDataDescriptor<X_TYPE, VALUE_TYPE>::ZeroBuckets ()
                {
                    VALUE_TYPE*  updatableStart = const_cast<VALUE_TYPE*> (inherited::fBucketDataStart_);
                    for (VALUE_TYPE* i = updatableStart; i != inherited::fBucketDataEnd_; ++i) {
                        *i = inherited::kZero;
                    }
                }

                /*
                 ********************************************************************************
                 ************************************* PRIVATE_ *********************************
                 ********************************************************************************
                 */
                namespace PRIVATE_ {
                    template    <typename DATA_DESCRIPTOR_TYPE>
                    void    CheckRebinDataDescriptorInvariant_ (const DATA_DESCRIPTOR_TYPE& d)
                    {
#if     qDebug
                        using   namespace   Traversal;
                        using   Memory::Optional;
                        using   BucketIndexType = typename DATA_DESCRIPTOR_TYPE::BucketIndexType;
                        using   XType = typename DATA_DESCRIPTOR_TYPE::XType;
                        auto myContext = shared_ptr<BucketIndexType> (new BucketIndexType (0));
                        auto bucketCount = d.GetBucketCount ();
                        auto getNext = [myContext, bucketCount, d] () -> Optional<Range<XType>> {
                            Optional<Range<XType>>   result;
                            if (*myContext < bucketCount)
                            {
                                result = d.GetBucketRange (*myContext);
                                (*myContext)++;
                            }
                            return result;
                        };
                        Assert (IsPartition (CreateGenerator<Range<XType>> (getNext)));
#endif
                    }
                }

                /*
                 ********************************************************************************
                 ********************************** Math::ReBin *********************************
                 ********************************************************************************
                 */
                template    <typename   SRC_DATA_DESCRIPTOR, typename TRG_DATA_DESCRIPTOR>
                void    ReBin (
                    const SRC_DATA_DESCRIPTOR& srcData,
                    TRG_DATA_DESCRIPTOR* trgData
                )
                {
                    RequireNotNull (trgData);

                    using   namespace Traversal;
                    using   BucketIndexType =   typename SRC_DATA_DESCRIPTOR::BucketIndexType;

                    /*
                     *  OLD OBSOLETE Algorithm:
                     *      Two iterators - one marking (start/end of target buckets), and one marking current
                     *      src bucket. Iterate over outer buckets. Move contents to new bucket. And adjust new
                     *      iterators. When they overlap and must advance - proportionally add to bucket,
                     *      advance and add rest to next target bucket.
                     *
                     *      This is probably more efficent than the algorithm below, but trickier to generalize
                     *      so we can have differently scaled source buckets.
                     */

                    /*
                     *  By default, zero all target buckets, but you could override that in the TRG argument
                     *  to avoid zeroing (say to accumulate multiple sources)
                     */
                    trgData->ZeroBuckets ();

#if     qDebug
                    PRIVATE_::CheckRebinDataDescriptorInvariant_ (srcData);
                    PRIVATE_::CheckRebinDataDescriptorInvariant_ (*trgData);
#endif

                    /*
                     *  x               0    1    2    3    4    5
                     *
                     *  SRC-BUCKETS:    |    |    |    |    |    |
                     *  TRG-BUCKETS:    |      |      |      |
                     *
                     *  Iterate over each source bucket. It represnts data from its sourceXStart, to srcXEnd (bucket X domain)
                     *  all data spread evently.
                     *
                     *  See where that start x/endx map to in y buckets, and spread it over them. Assume that in the TARGET
                     *  the buckets are contiguous.
                     *
                     *  For each one source bucket, put part into current ti (proportional), and put reset into next
                     *  ti (proportional).
                     */
                    BucketIndexType      srcBucketCount = srcData.GetBucketCount ();
                    for (BucketIndexType srcBucketIdx = 0; srcBucketIdx < srcBucketCount; ++srcBucketIdx) {
                        Range<typename SRC_DATA_DESCRIPTOR::XType>  curSrcBucketX       =   srcData.GetBucketRange(srcBucketIdx);
                        auto                                        curSrcBucketXWidth  =   curSrcBucketX.GetDistanceSpanned ();
                        typename SRC_DATA_DESCRIPTOR::ValueType     thisSrcBucketValue  =   srcData.GetValue(srcBucketIdx);

                        Assert (curSrcBucketXWidth >= 0);   // can ever be zero?

                        // Check special value of zero so we dont waste time spreading zeros around
                        if (thisSrcBucketValue != SRC_DATA_DESCRIPTOR::kZero and curSrcBucketXWidth != 0) {
                            /*
                             *  find range of target buckets to distribute value.
                             *
                             *  Each bucket returned may have little overall contribution from the source bucket. We look at
                             *  the degree of overlap.
                             */
                            for (auto targetBucket : trgData->GetMappedBucketRange (curSrcBucketX).Elements ()) {
                                Range<typename SRC_DATA_DESCRIPTOR::XType>  trgBucketIntersectRange =   trgData->GetBucketRange(targetBucket).Intersection (curSrcBucketX);
                                auto                                        trgBucketXWidth         =   trgBucketIntersectRange.GetDistanceSpanned ();
                                trgData->AccumulateValue (targetBucket, thisSrcBucketValue * (trgBucketXWidth / curSrcBucketXWidth));
                            }
                        }
                    }
                }
                template    <typename SRC_BUCKET_TYPE, typename TRG_BUCKET_TYPE, typename X_OFFSET_TYPE>
                void    ReBin (
                    const SRC_BUCKET_TYPE* srcStart, const SRC_BUCKET_TYPE* srcEnd,
                    TRG_BUCKET_TYPE* trgStart, TRG_BUCKET_TYPE* trgEnd
                )
                {
                    using   SRC_DATA_DESCRIPTOR     =   BasicDataDescriptor<X_OFFSET_TYPE, SRC_BUCKET_TYPE>;
                    using   TRG_DATA_DESCRIPTOR     =   UpdatableDataDescriptor<X_OFFSET_TYPE, TRG_BUCKET_TYPE>;

                    SRC_DATA_DESCRIPTOR srcData (srcStart, srcEnd, 1, 2);
                    TRG_DATA_DESCRIPTOR trgData (trgStart, trgEnd, 1, 2);
                    ReBin (srcData, &trgData);
                }

            }
        }
    }
}
#endif  /*_Stroika_Foundation_Math_ReBin_inl_*/
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2017 Smirnov Vladimir mapron1@gmail.com
 * Source code licensed under the Apache License, Version 2.0 (the "License");
 * You may not use this file except in compliance with the License.
 * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 or in file COPYING-APACHE-2.0.txt
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.h
 */

#include "InvocationRewriter.h"

#include "GccCommandLineParser.h"
#include "MsvcCommandLineParser.h"

#include <CommonTypes.h>
#include <FileUtils.h>
#include <StringUtils.h>
#include <Syslogger.h>
#include <algorithm>

namespace Wuild
{

InvocationRewriter::InvocationRewriter()
{
}

void InvocationRewriter::SetConfig(const IInvocationRewriter::Config &config)
{
	m_config = config;
}

const IInvocationRewriter::Config &InvocationRewriter::GetConfig() const
{
	return m_config;
}

bool InvocationRewriter::SplitInvocation(const ToolInvocation & original, ToolInvocation &preprocessor, ToolInvocation &compilation, std::string * remoteToolId)
{
	ToolInfo toolInfo = CompileInfoById(original.m_id);
	if (!toolInfo.m_parser)
		return false;
	ToolInvocation origComplete = CompleteInvocation(original);

	if (origComplete.m_type != ToolInvocation::InvokeType::Compile)
		return false;

	if (remoteToolId)
		*remoteToolId = toolInfo.m_remoteId;
	toolInfo.m_parser->SetToolInvocation(origComplete);
	toolInfo.m_parser->SetInvokeType(ToolInvocation::InvokeType::Preprocess);
	toolInfo.m_parser->RemovePDB();
	preprocessor = toolInfo.m_parser->GetToolInvocation();

	toolInfo.m_parser->SetToolInvocation(origComplete);
	toolInfo.m_parser->RemovePDB();
	toolInfo.m_parser->RemovePrepocessorFlags();
	toolInfo.m_parser->RemoveDependencyFiles();
	compilation = toolInfo.m_parser->GetToolInvocation();

	const std::string srcFilename = origComplete.GetInput(); // we hope  .cpp is coming after -c flag. It's naive.
	const std::string objFilename = origComplete.GetOutput();
	std::string preprocessedFilename = objFilename;
	bool outIsVar = preprocessedFilename[0] == '$'; // ninja hack...
	if (!outIsVar)
	{
		preprocessedFilename = GetPreprocessedPath(srcFilename, objFilename);
		preprocessor.SetOutput(preprocessedFilename);
		compilation.SetInput(preprocessedFilename);
	}

	return true;
}

ToolInvocation InvocationRewriter::CompleteInvocation(const ToolInvocation &original) const
{
	ToolInvocation inv;
	inv.m_args.clear();
	inv.m_ignoredArgs = original.m_ignoredArgs;
	for (auto arg : original.m_args)
	{
		StringVector argSplit;
		StringUtils::SplitString(StringUtils::Trim(arg), argSplit, ' ', false, true); //TODO: paths with spaces not supported!
		inv.m_args.insert(inv.m_args.end(), argSplit.begin(), argSplit.end());
	}

	ToolInfo info = CompileInfoById(original.m_id);
	if (info.m_valid)
	{
		inv.m_id = info.m_id;
		inv.m_type = original.m_type;
		inv = info.m_parser->ProcessToolInvocation(inv);
	}
	return inv;
}

bool InvocationRewriter::CheckRemotePossibleForFlags(const ToolInvocation & original) const
{
	ToolInfo info = CompileInfoById(original.m_id);
	if (info.m_valid)
	{
		ToolInvocation flags = CompleteInvocation(original);
		info.m_parser->SetToolInvocation(flags);
		return info.m_parser->IsRemotePossible();
	}
	return false;
}

ToolInvocation InvocationRewriter::FilterFlags(const ToolInvocation &original) const
{
	ToolInfo info = CompileInfoById(original.m_id);
	if (info.m_valid)
	{
		 ToolInvocation flags = CompleteInvocation(original);
		 if (flags.m_type == ToolInvocation::InvokeType::Preprocess)
		 {
			info.m_parser->SetToolInvocation(flags);
			info.m_parser->RemovePDB();
			return info.m_parser->GetToolInvocation();
		 }
		 else if (flags.m_type == ToolInvocation::InvokeType::Compile)
		 {
			info.m_parser->SetToolInvocation(flags);
			info.m_parser->RemovePrepocessorFlags();
			info.m_parser->RemoveDependencyFiles();
			info.m_parser->RemovePDB();
			return info.m_parser->GetToolInvocation();
		 }
	}
	return original;
}

std::string InvocationRewriter::GetPreprocessedPath(const std::string & sourcePath,
												const std::string & objectPath) const
{
	FileInfo sourceInfo(sourcePath);
	FileInfo objectInfo(objectPath);

	const std::string preprocessed = objectInfo.GetDir(true) + "pp_" + objectInfo.GetNameWE() + sourceInfo.GetFullExtension();
	return preprocessed;
}

ToolInvocation InvocationRewriter::PrepareRemote(const ToolInvocation &original) const
{
	ToolInvocation inv = CompleteInvocation(original);
	ToolInfo info = CompileInfoById(original.m_id);
	if (info.m_valid)
	{
		inv.m_id = info.m_id;
		inv.m_type = original.m_type;
		inv = info.m_parser->ProcessToolInvocation(inv);
		if (!info.m_tool.m_appendRemote.empty())
			inv.m_args.push_back(info.m_tool.m_appendRemote);

		if (!info.m_tool.m_removeRemote.empty())
		{
			StringVector newArgs;
			for (const auto & arg : inv.m_args)
				if (arg != info.m_tool.m_removeRemote)
					newArgs.push_back(arg);
			newArgs.swap(inv.m_args);
		}
		inv.m_id.m_toolId = info.m_remoteId;
	}
	inv.SetInput (FileInfo(inv.GetInput() ).GetFullname());
	inv.SetOutput(FileInfo(inv.GetOutput()).GetFullname());
	return inv;
}

InvocationRewriter::ToolInfo InvocationRewriter::CompileInfoById(const ToolInvocation::Id &id) const
{
	if (id.m_toolId.empty())
		return CompileInfoByExecutable(id.m_toolExecutable);

	return CompileInfoByToolId(id.m_toolId);
}

InvocationRewriter::ToolInfo InvocationRewriter::CompileInfoByExecutable(const std::string &executable) const
{
	const std::string exec = FileInfo::ToPlatformPath(executable);
	InvocationRewriter::ToolInfo info;
	for (const Config::Tool & unit : m_config.m_tools)
	{
		if (std::find(unit.m_names.cbegin(), unit.m_names.cend(), exec) != unit.m_names.cend())
		{
			return CompileInfoByUnit(unit);
		}
	}
	return info;
}

InvocationRewriter::ToolInfo InvocationRewriter::CompileInfoByToolId(const std::string &toolId) const
{
	InvocationRewriter::ToolInfo info;
	for (const Config::Tool & unit : m_config.m_tools)
	{
		if (unit.m_id == toolId)
			return CompileInfoByUnit(unit);
	}
	return info;
}

InvocationRewriter::ToolInfo InvocationRewriter::CompileInfoByUnit(const IInvocationRewriter::Config::Tool &unit) const
{
	ToolInfo info;
	info.m_tool = unit;
	info.m_id.m_toolId = unit.m_id;
	info.m_id.m_toolExecutable = unit.m_names[0];
	if (unit.m_type == Config::ToolchainType::GCC)
		info.m_parser.reset(new GccCommandLineParser());
	else if (unit.m_type == Config::ToolchainType::MSVC)
		info.m_parser.reset(new MsvcCommandLineParser());
	if (info.m_parser)
		info.m_valid = true;
	info.m_remoteId = info.m_id.m_toolId;
	if (!unit.m_remoteAlias.empty())
	{
		info.m_remoteId = unit.m_remoteAlias;
	}
	return info;
}

}
<commit_msg>Fixed crosscompiler invocation parsing.<commit_after>/*
 * Copyright (C) 2017 Smirnov Vladimir mapron1@gmail.com
 * Source code licensed under the Apache License, Version 2.0 (the "License");
 * You may not use this file except in compliance with the License.
 * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 or in file COPYING-APACHE-2.0.txt
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.h
 */

#include "InvocationRewriter.h"

#include "GccCommandLineParser.h"
#include "MsvcCommandLineParser.h"

#include <CommonTypes.h>
#include <FileUtils.h>
#include <StringUtils.h>
#include <Syslogger.h>
#include <algorithm>

namespace Wuild
{

InvocationRewriter::InvocationRewriter()
{
}

void InvocationRewriter::SetConfig(const IInvocationRewriter::Config &config)
{
	m_config = config;
}

const IInvocationRewriter::Config &InvocationRewriter::GetConfig() const
{
	return m_config;
}

bool InvocationRewriter::SplitInvocation(const ToolInvocation & original, ToolInvocation &preprocessor, ToolInvocation &compilation, std::string * remoteToolId)
{
	ToolInfo toolInfo = CompileInfoById(original.m_id);
	if (!toolInfo.m_parser)
		return false;
	ToolInvocation origComplete = CompleteInvocation(original);

	if (origComplete.m_type != ToolInvocation::InvokeType::Compile)
		return false;

	if (remoteToolId)
		*remoteToolId = toolInfo.m_remoteId;
	toolInfo.m_parser->SetToolInvocation(origComplete);
	toolInfo.m_parser->SetInvokeType(ToolInvocation::InvokeType::Preprocess);
	toolInfo.m_parser->RemovePDB();
	preprocessor = toolInfo.m_parser->GetToolInvocation();

	toolInfo.m_parser->SetToolInvocation(origComplete);
	toolInfo.m_parser->RemovePDB();
	toolInfo.m_parser->RemovePrepocessorFlags();
	toolInfo.m_parser->RemoveDependencyFiles();
	compilation = toolInfo.m_parser->GetToolInvocation();

	const std::string srcFilename = origComplete.GetInput(); // we hope  .cpp is coming after -c flag. It's naive.
	const std::string objFilename = origComplete.GetOutput();
	std::string preprocessedFilename = objFilename;
	bool outIsVar = preprocessedFilename[0] == '$'; // ninja hack...
	if (!outIsVar)
	{
		preprocessedFilename = GetPreprocessedPath(srcFilename, objFilename);
		preprocessor.SetOutput(preprocessedFilename);
		compilation.SetInput(preprocessedFilename);
	}

	return true;
}

ToolInvocation InvocationRewriter::CompleteInvocation(const ToolInvocation &original) const
{
	ToolInvocation inv;
	inv.m_args.clear();
	inv.m_ignoredArgs = original.m_ignoredArgs;
	for (auto arg : original.m_args)
	{
		StringVector argSplit;
		StringUtils::SplitString(StringUtils::Trim(arg), argSplit, ' ', false, true); //TODO: paths with spaces not supported!
		inv.m_args.insert(inv.m_args.end(), argSplit.begin(), argSplit.end());
	}

	ToolInfo info = CompileInfoById(original.m_id);
	if (info.m_valid)
	{
		inv.m_id = info.m_id;
		inv.m_type = original.m_type;
		inv = info.m_parser->ProcessToolInvocation(inv);
	}
	return inv;
}

bool InvocationRewriter::CheckRemotePossibleForFlags(const ToolInvocation & original) const
{
	ToolInfo info = CompileInfoById(original.m_id);
	if (info.m_valid)
	{
		ToolInvocation flags = CompleteInvocation(original);
		info.m_parser->SetToolInvocation(flags);
		return info.m_parser->IsRemotePossible();
	}
	return false;
}

ToolInvocation InvocationRewriter::FilterFlags(const ToolInvocation &original) const
{
	ToolInfo info = CompileInfoById(original.m_id);
	if (info.m_valid)
	{
		 ToolInvocation flags = CompleteInvocation(original);
		 if (flags.m_type == ToolInvocation::InvokeType::Preprocess)
		 {
			info.m_parser->SetToolInvocation(flags);
			info.m_parser->RemovePDB();
			return info.m_parser->GetToolInvocation();
		 }
		 else if (flags.m_type == ToolInvocation::InvokeType::Compile)
		 {
			info.m_parser->SetToolInvocation(flags);
			info.m_parser->RemovePrepocessorFlags();
			info.m_parser->RemoveDependencyFiles();
			info.m_parser->RemovePDB();
			return info.m_parser->GetToolInvocation();
		 }
	}
	return original;
}

std::string InvocationRewriter::GetPreprocessedPath(const std::string & sourcePath,
												const std::string & objectPath) const
{
	FileInfo sourceInfo(sourcePath);
	FileInfo objectInfo(objectPath);

	const std::string preprocessed = objectInfo.GetDir(true) + "pp_" + objectInfo.GetNameWE() + sourceInfo.GetFullExtension();
	return preprocessed;
}

ToolInvocation InvocationRewriter::PrepareRemote(const ToolInvocation &original) const
{
	ToolInvocation inv = CompleteInvocation(original);
	ToolInfo info = CompileInfoById(original.m_id);
	if (info.m_valid)
	{
		inv.m_id = info.m_id;
		inv.m_type = original.m_type;
		inv = info.m_parser->ProcessToolInvocation(inv);
		if (!info.m_tool.m_appendRemote.empty())
			inv.m_args.push_back(info.m_tool.m_appendRemote);

		if (!info.m_tool.m_removeRemote.empty())
		{
			StringVector newArgs;
			for (const auto & arg : inv.m_args)
				if (arg != info.m_tool.m_removeRemote)
					newArgs.push_back(arg);
			newArgs.swap(inv.m_args);
		}
		inv.m_id.m_toolId = info.m_remoteId;
	}
	inv.SetInput (FileInfo(inv.GetInput() ).GetFullname());
	inv.SetOutput(FileInfo(inv.GetOutput()).GetFullname());
	return inv;
}

InvocationRewriter::ToolInfo InvocationRewriter::CompileInfoById(const ToolInvocation::Id &id) const
{
	if (id.m_toolId.empty())
		return CompileInfoByExecutable(id.m_toolExecutable);

	return CompileInfoByToolId(id.m_toolId);
}

InvocationRewriter::ToolInfo InvocationRewriter::CompileInfoByExecutable(const std::string &executable) const
{
	const std::string exec = FileInfo::ToPlatformPath(executable);
	InvocationRewriter::ToolInfo info;
	for (const Config::Tool & unit : m_config.m_tools)
	{
		if (std::find(unit.m_names.cbegin(), unit.m_names.cend(), exec) != unit.m_names.cend())
		{
			return CompileInfoByUnit(unit);
		}
	}
	return info;
}

InvocationRewriter::ToolInfo InvocationRewriter::CompileInfoByToolId(const std::string &toolId) const
{
	InvocationRewriter::ToolInfo info;
	if (toolId.empty())
		return info;

	for (const Config::Tool & unit : m_config.m_tools)
	{
		if (unit.m_id == toolId)
			return CompileInfoByUnit(unit);
	}
	for (const Config::Tool & unit : m_config.m_tools)
	{
		if (unit.m_remoteAlias == toolId)
			return CompileInfoByUnit(unit);
	}
	return info;
}

InvocationRewriter::ToolInfo InvocationRewriter::CompileInfoByUnit(const IInvocationRewriter::Config::Tool &unit) const
{
	ToolInfo info;
	info.m_tool = unit;
	info.m_id.m_toolId = unit.m_id;
	info.m_id.m_toolExecutable = unit.m_names[0];
	if (unit.m_type == Config::ToolchainType::GCC)
		info.m_parser.reset(new GccCommandLineParser());
	else if (unit.m_type == Config::ToolchainType::MSVC)
		info.m_parser.reset(new MsvcCommandLineParser());
	if (info.m_parser)
		info.m_valid = true;
	info.m_remoteId = info.m_id.m_toolId;
	if (!unit.m_remoteAlias.empty())
	{
		info.m_remoteId = unit.m_remoteAlias;
	}
	return info;
}

}
<|endoftext|>


<commit_before>#include "selfdrive/ui/qt/widgets/prime.h"

#include <QDebug>
#include <QJsonDocument>
#include <QJsonObject>
#include <QLabel>
#include <QPushButton>
#include <QStackedWidget>
#include <QTimer>
#include <QVBoxLayout>

#include <QrCode.hpp>

#include "selfdrive/ui/qt/request_repeater.h"
#include "selfdrive/ui/qt/util.h"
#include "selfdrive/ui/qt/qt_window.h"

using qrcodegen::QrCode;

PairingQRWidget::PairingQRWidget(QWidget* parent) : QWidget(parent) {
  timer = new QTimer(this);
  connect(timer, &QTimer::timeout, this, &PairingQRWidget::refresh);
}

void PairingQRWidget::showEvent(QShowEvent *event) {
  refresh();
  timer->start(5 * 60 * 1000);
}

void PairingQRWidget::hideEvent(QHideEvent *event) {
  timer->stop();
}

void PairingQRWidget::refresh() {
  QString pairToken = CommaApi::create_jwt({{"pair", true}});
  QString qrString = "https://connect.comma.ai/?pair=" + pairToken;
  this->updateQrCode(qrString);
}

void PairingQRWidget::updateQrCode(const QString &text) {
  QrCode qr = QrCode::encodeText(text.toUtf8().data(), QrCode::Ecc::LOW);
  qint32 sz = qr.getSize();
  QImage im(sz, sz, QImage::Format_RGB32);

  QRgb black = qRgb(0, 0, 0);
  QRgb white = qRgb(255, 255, 255);
  for (int y = 0; y < sz; y++) {
    for (int x = 0; x < sz; x++) {
      im.setPixel(x, y, qr.getModule(x, y) ? black : white);
    }
  }

  // Integer division to prevent anti-aliasing
  int final_sz = ((width() / sz) - 1) * sz;
  img = QPixmap::fromImage(im.scaled(final_sz, final_sz, Qt::KeepAspectRatio), Qt::MonoOnly);
}

void PairingQRWidget::paintEvent(QPaintEvent *e) {
  QPainter p(this);
  p.fillRect(rect(), Qt::white);

  QSize s = (size() - img.size()) / 2;
  p.drawPixmap(s.width(), s.height(), img);
}

PairingPopup::PairingPopup(QWidget *parent) : QDialogBase(parent) {
  QHBoxLayout *hlayout = new QHBoxLayout(this);
  hlayout->setContentsMargins(0, 0, 0, 0);
  hlayout->setSpacing(0);

  setStyleSheet("PairingPopup { background-color: #E0E0E0; }");

  // text
  QVBoxLayout *vlayout = new QVBoxLayout();
  vlayout->setContentsMargins(85, 70, 50, 70);
  vlayout->setSpacing(50);
  hlayout->addLayout(vlayout, 1);
  {
    QPushButton *close = new QPushButton(QIcon(":/icons/close.svg"), "", this);
    close->setIconSize(QSize(80, 80));
    close->setStyleSheet("border: none;");
    vlayout->addWidget(close, 0, Qt::AlignLeft);
    QObject::connect(close, &QPushButton::clicked, this, &QDialog::reject);

    vlayout->addSpacing(30);

    QLabel *title = new QLabel(tr("Pair your device to your comma account"), this);
    title->setStyleSheet("font-size: 75px; color: black;");
    title->setWordWrap(true);
    vlayout->addWidget(title);

    QLabel *instructions = new QLabel(QString(R"(

%1

%2

%3

    )").arg(tr("Go to https://connect.comma.ai on your phone"))
    .arg(tr("Click \"add new device\" and scan the QR code on the right"))
    .arg(tr("Bookmark connect.comma.ai to your home screen to use it like an app")), this);

    instructions->setStyleSheet("font-size: 47px; font-weight: bold; color: black;");
    instructions->setWordWrap(true);
    vlayout->addWidget(instructions);

    vlayout->addStretch();
  }

  // QR code
  PairingQRWidget *qr = new PairingQRWidget(this);
  hlayout->addWidget(qr, 1);
}

PrimeUserWidget::PrimeUserWidget(QWidget* parent) : QWidget(parent) {
  mainLayout = new QVBoxLayout(this);
  mainLayout->setMargin(0);
  mainLayout->setSpacing(30);

  // subscribed prime layout
  QWidget *primeWidget = new QWidget;
  primeWidget->setObjectName("primeWidget");
  QVBoxLayout *primeLayout = new QVBoxLayout(primeWidget);
  primeLayout->setMargin(0);
  primeWidget->setContentsMargins(60, 50, 60, 50);

  QLabel* subscribed = new QLabel(tr("✓ SUBSCRIBED"));
  subscribed->setStyleSheet("font-size: 41px; font-weight: bold; color: #86FF4E;");
  primeLayout->addWidget(subscribed, 0, Qt::AlignTop);

  primeLayout->addSpacing(60);

  QLabel* commaPrime = new QLabel(tr("comma prime"));
  commaPrime->setStyleSheet("font-size: 75px; font-weight: bold;");
  primeLayout->addWidget(commaPrime, 0, Qt::AlignTop);

  primeLayout->addSpacing(20);

  QLabel* connectUrl = new QLabel(tr("CONNECT.COMMA.AI"));
  connectUrl->setStyleSheet("font-size: 41px; font-family: Inter SemiBold; color: #A0A0A0;");
  primeLayout->addWidget(connectUrl, 0, Qt::AlignTop);

  mainLayout->addWidget(primeWidget);

  // comma points layout
  QWidget *pointsWidget = new QWidget;
  pointsWidget->setObjectName("primeWidget");
  QVBoxLayout *pointsLayout = new QVBoxLayout(pointsWidget);
  pointsLayout->setMargin(0);
  pointsWidget->setContentsMargins(60, 50, 60, 50);

  QLabel* commaPoints = new QLabel(tr("COMMA POINTS"));
  commaPoints->setStyleSheet("font-size: 41px; font-family: Inter SemiBold;");
  pointsLayout->addWidget(commaPoints, 0, Qt::AlignTop);

  points = new QLabel("0");
  points->setStyleSheet("font-size: 91px; font-weight: bold;");
  pointsLayout->addWidget(points, 0, Qt::AlignTop);

  mainLayout->addWidget(pointsWidget);

  mainLayout->addStretch();

  // set up API requests
  if (auto dongleId = getDongleId()) {
    QString url = CommaApi::BASE_URL + "/v1/devices/" + *dongleId + "/owner";
    RequestRepeater *repeater = new RequestRepeater(this, url, "ApiCache_Owner", 6);
    QObject::connect(repeater, &RequestRepeater::requestDone, this, &PrimeUserWidget::replyFinished);
  }
}

void PrimeUserWidget::replyFinished(const QString &response) {
  QJsonDocument doc = QJsonDocument::fromJson(response.toUtf8());
  if (doc.isNull()) {
    qDebug() << "JSON Parse failed on getting points";
    return;
  }

  QJsonObject json = doc.object();
  points->setText(QString::number(json["points"].toInt()));
}

PrimeAdWidget::PrimeAdWidget(QWidget* parent) : QFrame(parent) {
  QVBoxLayout* main_layout = new QVBoxLayout(this);
  main_layout->setContentsMargins(80, 90, 80, 60);
  main_layout->setSpacing(0);

  QLabel *upgrade = new QLabel(tr("Upgrade Now"));
  upgrade->setStyleSheet("font-size: 75px; font-weight: bold;");
  main_layout->addWidget(upgrade, 0, Qt::AlignTop);
  main_layout->addSpacing(50);

  QLabel *description = new QLabel(tr("Become a comma prime member at connect.comma.ai"));
  description->setStyleSheet("font-size: 60px; font-weight: light; color: white;");
  description->setWordWrap(true);
  main_layout->addWidget(description, 0, Qt::AlignTop);

  main_layout->addStretch();

  QLabel *features = new QLabel(tr("PRIME FEATURES:"));
  features->setStyleSheet("font-size: 41px; font-weight: bold; color: #E5E5E5;");
  main_layout->addWidget(features, 0, Qt::AlignBottom);
  main_layout->addSpacing(30);

  QVector<QString> bullets = {tr("Remote access"), tr("1 year of storage"), tr("Developer perks")};
  for (auto &b: bullets) {
    const QString check = "✓ ";
    QLabel *l = new QLabel(check + b);
    l->setAlignment(Qt::AlignLeft);
    l->setStyleSheet("font-size: 50px; margin-bottom: 15px;");
    main_layout->addWidget(l, 0, Qt::AlignBottom);
  }

  setStyleSheet(R"(
    PrimeAdWidget {
      border-radius: 10px;
      background-color: #333333;
    }
  )");
}

SetupWidget::SetupWidget(QWidget* parent) : QFrame(parent) {
  mainLayout = new QStackedWidget;

  // Unpaired, registration prompt layout

  QWidget* finishRegistration = new QWidget;
  finishRegistration->setObjectName("primeWidget");
  QVBoxLayout* finishRegistationLayout = new QVBoxLayout(finishRegistration);
  finishRegistationLayout->setContentsMargins(30, 75, 30, 45);
  finishRegistationLayout->setSpacing(0);

  QLabel* registrationTitle = new QLabel(tr("Finish Setup"));
  registrationTitle->setStyleSheet("font-size: 75px; font-weight: bold; margin-left: 55px;");
  finishRegistationLayout->addWidget(registrationTitle);

  finishRegistationLayout->addSpacing(30);

  QLabel* registrationDescription = new QLabel(tr("Pair your device with comma connect (connect.comma.ai) and claim your comma prime offer."));
  registrationDescription->setWordWrap(true);
  registrationDescription->setStyleSheet("font-size: 55px; font-weight: light; margin-left: 55px;");
  finishRegistationLayout->addWidget(registrationDescription);

  finishRegistationLayout->addStretch();

  QPushButton* pair = new QPushButton(tr("Pair device"));
  pair->setFixedHeight(220);
  pair->setStyleSheet(R"(
    QPushButton {
      font-size: 55px;
      font-weight: 400;
      border-radius: 10px;
      background-color: #465BEA;
    }
    QPushButton:pressed {
      background-color: #3049F4;
    }
  )");
  finishRegistationLayout->addWidget(pair);

  popup = new PairingPopup(this);
  QObject::connect(pair, &QPushButton::clicked, popup, &PairingPopup::exec);

  mainLayout->addWidget(finishRegistration);

  // build stacked layout
  QVBoxLayout *outer_layout = new QVBoxLayout(this);
  outer_layout->setContentsMargins(0, 0, 0, 0);
  outer_layout->addWidget(mainLayout);

  primeAd = new PrimeAdWidget;
  mainLayout->addWidget(primeAd);

  primeUser = new PrimeUserWidget;
  mainLayout->addWidget(primeUser);

  mainLayout->setCurrentWidget(primeAd);

  setFixedWidth(750);
  setStyleSheet(R"(
    #primeWidget {
      border-radius: 10px;
      background-color: #333333;
    }
  )");

  // Retain size while hidden
  QSizePolicy sp_retain = sizePolicy();
  sp_retain.setRetainSizeWhenHidden(true);
  setSizePolicy(sp_retain);

  // set up API requests
  if (auto dongleId = getDongleId()) {
    QString url = CommaApi::BASE_URL + "/v1.1/devices/" + *dongleId + "/";
    RequestRepeater* repeater = new RequestRepeater(this, url, "ApiCache_Device", 5);

    QObject::connect(repeater, &RequestRepeater::requestDone, this, &SetupWidget::replyFinished);
  }
  hide(); // Only show when first request comes back
}

void SetupWidget::replyFinished(const QString &response, bool success) {
  show();
  if (!success) return;

  QJsonDocument doc = QJsonDocument::fromJson(response.toUtf8());
  if (doc.isNull()) {
    qDebug() << "JSON Parse failed on getting pairing and prime status";
    return;
  }

  QJsonObject json = doc.object();
  int prime_type = json["prime_type"].toInt();

  if (uiState()->prime_type != prime_type) {
    uiState()->prime_type = prime_type;
    Params().put("PrimeType", std::to_string(prime_type));
  }

  if (!json["is_paired"].toBool()) {
    mainLayout->setCurrentIndex(0);
  } else {
    popup->reject();

    if (prime_type) {
      mainLayout->setCurrentWidget(primeUser);
    } else {
      mainLayout->setCurrentWidget(primeAd);
    }
  }
}
<commit_msg>prime.cc: call update() after QR code changed (#25740)<commit_after>#include "selfdrive/ui/qt/widgets/prime.h"

#include <QDebug>
#include <QJsonDocument>
#include <QJsonObject>
#include <QLabel>
#include <QPushButton>
#include <QStackedWidget>
#include <QTimer>
#include <QVBoxLayout>

#include <QrCode.hpp>

#include "selfdrive/ui/qt/request_repeater.h"
#include "selfdrive/ui/qt/util.h"
#include "selfdrive/ui/qt/qt_window.h"

using qrcodegen::QrCode;

PairingQRWidget::PairingQRWidget(QWidget* parent) : QWidget(parent) {
  timer = new QTimer(this);
  connect(timer, &QTimer::timeout, this, &PairingQRWidget::refresh);
}

void PairingQRWidget::showEvent(QShowEvent *event) {
  refresh();
  timer->start(5 * 60 * 1000);
}

void PairingQRWidget::hideEvent(QHideEvent *event) {
  timer->stop();
}

void PairingQRWidget::refresh() {
  QString pairToken = CommaApi::create_jwt({{"pair", true}});
  QString qrString = "https://connect.comma.ai/?pair=" + pairToken;
  this->updateQrCode(qrString);
  update();
}

void PairingQRWidget::updateQrCode(const QString &text) {
  QrCode qr = QrCode::encodeText(text.toUtf8().data(), QrCode::Ecc::LOW);
  qint32 sz = qr.getSize();
  QImage im(sz, sz, QImage::Format_RGB32);

  QRgb black = qRgb(0, 0, 0);
  QRgb white = qRgb(255, 255, 255);
  for (int y = 0; y < sz; y++) {
    for (int x = 0; x < sz; x++) {
      im.setPixel(x, y, qr.getModule(x, y) ? black : white);
    }
  }

  // Integer division to prevent anti-aliasing
  int final_sz = ((width() / sz) - 1) * sz;
  img = QPixmap::fromImage(im.scaled(final_sz, final_sz, Qt::KeepAspectRatio), Qt::MonoOnly);
}

void PairingQRWidget::paintEvent(QPaintEvent *e) {
  QPainter p(this);
  p.fillRect(rect(), Qt::white);

  QSize s = (size() - img.size()) / 2;
  p.drawPixmap(s.width(), s.height(), img);
}

PairingPopup::PairingPopup(QWidget *parent) : QDialogBase(parent) {
  QHBoxLayout *hlayout = new QHBoxLayout(this);
  hlayout->setContentsMargins(0, 0, 0, 0);
  hlayout->setSpacing(0);

  setStyleSheet("PairingPopup { background-color: #E0E0E0; }");

  // text
  QVBoxLayout *vlayout = new QVBoxLayout();
  vlayout->setContentsMargins(85, 70, 50, 70);
  vlayout->setSpacing(50);
  hlayout->addLayout(vlayout, 1);
  {
    QPushButton *close = new QPushButton(QIcon(":/icons/close.svg"), "", this);
    close->setIconSize(QSize(80, 80));
    close->setStyleSheet("border: none;");
    vlayout->addWidget(close, 0, Qt::AlignLeft);
    QObject::connect(close, &QPushButton::clicked, this, &QDialog::reject);

    vlayout->addSpacing(30);

    QLabel *title = new QLabel(tr("Pair your device to your comma account"), this);
    title->setStyleSheet("font-size: 75px; color: black;");
    title->setWordWrap(true);
    vlayout->addWidget(title);

    QLabel *instructions = new QLabel(QString(R"(

%1

%2

%3

    )").arg(tr("Go to https://connect.comma.ai on your phone"))
    .arg(tr("Click \"add new device\" and scan the QR code on the right"))
    .arg(tr("Bookmark connect.comma.ai to your home screen to use it like an app")), this);

    instructions->setStyleSheet("font-size: 47px; font-weight: bold; color: black;");
    instructions->setWordWrap(true);
    vlayout->addWidget(instructions);

    vlayout->addStretch();
  }

  // QR code
  PairingQRWidget *qr = new PairingQRWidget(this);
  hlayout->addWidget(qr, 1);
}

PrimeUserWidget::PrimeUserWidget(QWidget* parent) : QWidget(parent) {
  mainLayout = new QVBoxLayout(this);
  mainLayout->setMargin(0);
  mainLayout->setSpacing(30);

  // subscribed prime layout
  QWidget *primeWidget = new QWidget;
  primeWidget->setObjectName("primeWidget");
  QVBoxLayout *primeLayout = new QVBoxLayout(primeWidget);
  primeLayout->setMargin(0);
  primeWidget->setContentsMargins(60, 50, 60, 50);

  QLabel* subscribed = new QLabel(tr("✓ SUBSCRIBED"));
  subscribed->setStyleSheet("font-size: 41px; font-weight: bold; color: #86FF4E;");
  primeLayout->addWidget(subscribed, 0, Qt::AlignTop);

  primeLayout->addSpacing(60);

  QLabel* commaPrime = new QLabel(tr("comma prime"));
  commaPrime->setStyleSheet("font-size: 75px; font-weight: bold;");
  primeLayout->addWidget(commaPrime, 0, Qt::AlignTop);

  primeLayout->addSpacing(20);

  QLabel* connectUrl = new QLabel(tr("CONNECT.COMMA.AI"));
  connectUrl->setStyleSheet("font-size: 41px; font-family: Inter SemiBold; color: #A0A0A0;");
  primeLayout->addWidget(connectUrl, 0, Qt::AlignTop);

  mainLayout->addWidget(primeWidget);

  // comma points layout
  QWidget *pointsWidget = new QWidget;
  pointsWidget->setObjectName("primeWidget");
  QVBoxLayout *pointsLayout = new QVBoxLayout(pointsWidget);
  pointsLayout->setMargin(0);
  pointsWidget->setContentsMargins(60, 50, 60, 50);

  QLabel* commaPoints = new QLabel(tr("COMMA POINTS"));
  commaPoints->setStyleSheet("font-size: 41px; font-family: Inter SemiBold;");
  pointsLayout->addWidget(commaPoints, 0, Qt::AlignTop);

  points = new QLabel("0");
  points->setStyleSheet("font-size: 91px; font-weight: bold;");
  pointsLayout->addWidget(points, 0, Qt::AlignTop);

  mainLayout->addWidget(pointsWidget);

  mainLayout->addStretch();

  // set up API requests
  if (auto dongleId = getDongleId()) {
    QString url = CommaApi::BASE_URL + "/v1/devices/" + *dongleId + "/owner";
    RequestRepeater *repeater = new RequestRepeater(this, url, "ApiCache_Owner", 6);
    QObject::connect(repeater, &RequestRepeater::requestDone, this, &PrimeUserWidget::replyFinished);
  }
}

void PrimeUserWidget::replyFinished(const QString &response) {
  QJsonDocument doc = QJsonDocument::fromJson(response.toUtf8());
  if (doc.isNull()) {
    qDebug() << "JSON Parse failed on getting points";
    return;
  }

  QJsonObject json = doc.object();
  points->setText(QString::number(json["points"].toInt()));
}

PrimeAdWidget::PrimeAdWidget(QWidget* parent) : QFrame(parent) {
  QVBoxLayout* main_layout = new QVBoxLayout(this);
  main_layout->setContentsMargins(80, 90, 80, 60);
  main_layout->setSpacing(0);

  QLabel *upgrade = new QLabel(tr("Upgrade Now"));
  upgrade->setStyleSheet("font-size: 75px; font-weight: bold;");
  main_layout->addWidget(upgrade, 0, Qt::AlignTop);
  main_layout->addSpacing(50);

  QLabel *description = new QLabel(tr("Become a comma prime member at connect.comma.ai"));
  description->setStyleSheet("font-size: 60px; font-weight: light; color: white;");
  description->setWordWrap(true);
  main_layout->addWidget(description, 0, Qt::AlignTop);

  main_layout->addStretch();

  QLabel *features = new QLabel(tr("PRIME FEATURES:"));
  features->setStyleSheet("font-size: 41px; font-weight: bold; color: #E5E5E5;");
  main_layout->addWidget(features, 0, Qt::AlignBottom);
  main_layout->addSpacing(30);

  QVector<QString> bullets = {tr("Remote access"), tr("1 year of storage"), tr("Developer perks")};
  for (auto &b: bullets) {
    const QString check = "✓ ";
    QLabel *l = new QLabel(check + b);
    l->setAlignment(Qt::AlignLeft);
    l->setStyleSheet("font-size: 50px; margin-bottom: 15px;");
    main_layout->addWidget(l, 0, Qt::AlignBottom);
  }

  setStyleSheet(R"(
    PrimeAdWidget {
      border-radius: 10px;
      background-color: #333333;
    }
  )");
}

SetupWidget::SetupWidget(QWidget* parent) : QFrame(parent) {
  mainLayout = new QStackedWidget;

  // Unpaired, registration prompt layout

  QWidget* finishRegistration = new QWidget;
  finishRegistration->setObjectName("primeWidget");
  QVBoxLayout* finishRegistationLayout = new QVBoxLayout(finishRegistration);
  finishRegistationLayout->setContentsMargins(30, 75, 30, 45);
  finishRegistationLayout->setSpacing(0);

  QLabel* registrationTitle = new QLabel(tr("Finish Setup"));
  registrationTitle->setStyleSheet("font-size: 75px; font-weight: bold; margin-left: 55px;");
  finishRegistationLayout->addWidget(registrationTitle);

  finishRegistationLayout->addSpacing(30);

  QLabel* registrationDescription = new QLabel(tr("Pair your device with comma connect (connect.comma.ai) and claim your comma prime offer."));
  registrationDescription->setWordWrap(true);
  registrationDescription->setStyleSheet("font-size: 55px; font-weight: light; margin-left: 55px;");
  finishRegistationLayout->addWidget(registrationDescription);

  finishRegistationLayout->addStretch();

  QPushButton* pair = new QPushButton(tr("Pair device"));
  pair->setFixedHeight(220);
  pair->setStyleSheet(R"(
    QPushButton {
      font-size: 55px;
      font-weight: 400;
      border-radius: 10px;
      background-color: #465BEA;
    }
    QPushButton:pressed {
      background-color: #3049F4;
    }
  )");
  finishRegistationLayout->addWidget(pair);

  popup = new PairingPopup(this);
  QObject::connect(pair, &QPushButton::clicked, popup, &PairingPopup::exec);

  mainLayout->addWidget(finishRegistration);

  // build stacked layout
  QVBoxLayout *outer_layout = new QVBoxLayout(this);
  outer_layout->setContentsMargins(0, 0, 0, 0);
  outer_layout->addWidget(mainLayout);

  primeAd = new PrimeAdWidget;
  mainLayout->addWidget(primeAd);

  primeUser = new PrimeUserWidget;
  mainLayout->addWidget(primeUser);

  mainLayout->setCurrentWidget(primeAd);

  setFixedWidth(750);
  setStyleSheet(R"(
    #primeWidget {
      border-radius: 10px;
      background-color: #333333;
    }
  )");

  // Retain size while hidden
  QSizePolicy sp_retain = sizePolicy();
  sp_retain.setRetainSizeWhenHidden(true);
  setSizePolicy(sp_retain);

  // set up API requests
  if (auto dongleId = getDongleId()) {
    QString url = CommaApi::BASE_URL + "/v1.1/devices/" + *dongleId + "/";
    RequestRepeater* repeater = new RequestRepeater(this, url, "ApiCache_Device", 5);

    QObject::connect(repeater, &RequestRepeater::requestDone, this, &SetupWidget::replyFinished);
  }
  hide(); // Only show when first request comes back
}

void SetupWidget::replyFinished(const QString &response, bool success) {
  show();
  if (!success) return;

  QJsonDocument doc = QJsonDocument::fromJson(response.toUtf8());
  if (doc.isNull()) {
    qDebug() << "JSON Parse failed on getting pairing and prime status";
    return;
  }

  QJsonObject json = doc.object();
  int prime_type = json["prime_type"].toInt();

  if (uiState()->prime_type != prime_type) {
    uiState()->prime_type = prime_type;
    Params().put("PrimeType", std::to_string(prime_type));
  }

  if (!json["is_paired"].toBool()) {
    mainLayout->setCurrentIndex(0);
  } else {
    popup->reject();

    if (prime_type) {
      mainLayout->setCurrentWidget(primeUser);
    } else {
      mainLayout->setCurrentWidget(primeAd);
    }
  }
}
<|endoftext|>