<commit_before>/*************************************************************************
 *
 *  OpenOffice.org - a multi-platform office productivity suite
 *
 *  $RCSfile: imestatuswindow.hxx,v $
 *
 *  $Revision: 1.4 $
 *
 *  last change: $Author: obo $ $Date: 2006-10-12 15:47:52 $
 *
 *  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
 *
 ************************************************************************/

#if !defined INCLUDED_SFX2_APPL_IMESTATUSWINDOW_HXX
#define INCLUDED_SFX2_APPL_IMESTATUSWINDOW_HXX

#include "com/sun/star/beans/XPropertyChangeListener.hpp"
#include "com/sun/star/uno/Reference.hxx"
#include "cppuhelper/implbase1.hxx"
#include "osl/mutex.hxx"

namespace com { namespace sun { namespace star {
    namespace beans { class XPropertySet; }
    namespace lang { class XMultiServiceFactory; }
} } }
class SfxApplication;

namespace sfx2 { namespace appl {

// The MS compiler needs this typedef work-around to accept the using
// declarations within ImeStatusWindow:
typedef cppu::WeakImplHelper1< com::sun::star::beans::XPropertyChangeListener >
ImeStatusWindow_Impl;

/** Control the behavior of any (platform-dependent) IME status windows.

    The decision of whether a status window shall be displayed or not can be
    stored permanently in the configuration (under key
    org.openoffice.office.Common/I18N/InputMethod/ShowStatusWindow; if that
    entry is nil, VCL is asked for a default).
 */
class ImeStatusWindow: private ImeStatusWindow_Impl
{
public:
    ImeStatusWindow(SfxApplication & rApplication,
                    com::sun::star::uno::Reference<
                            com::sun::star::lang::XMultiServiceFactory > const &
                        rServiceFactory);

    /** Set up VCL according to the configuration.

        Is it not strictly required that this method is called exactly once
        (though that will be the typical use).

        Must only be called with the Solar mutex locked.
     */
    void init();

    /** Return true if the status window is toggled on.

        This is only meaningful when canToggle returns true.

        Can be called without the Solar mutex locked.
     */
    bool isShowing();

    /** Toggle the status window on or off.

        This only works if canToggle returns true (otherwise, any calls of this
        method are ignored).

        Must only be called with the Solar mutex locked.
     */
    void show(bool bShow);

    /** Return true if the status window can be toggled on and off externally.

        Must only be called with the Solar mutex locked.
     */
    bool canToggle() const;

    using ImeStatusWindow_Impl::acquire;
    using ImeStatusWindow_Impl::release;
    using ImeStatusWindow_Impl::operator new;
    using ImeStatusWindow_Impl::operator delete;

private:
    ImeStatusWindow(ImeStatusWindow &); // not implemented
    void operator =(ImeStatusWindow); // not implemented

    virtual ~ImeStatusWindow();

    virtual void SAL_CALL
    disposing(com::sun::star::lang::EventObject const & rSource)
        throw (com::sun::star::uno::RuntimeException);

    virtual void SAL_CALL
    propertyChange(com::sun::star::beans::PropertyChangeEvent const & rEvent)
        throw (com::sun::star::uno::RuntimeException);

    com::sun::star::uno::Reference< com::sun::star::beans::XPropertySet >
    getConfig();

    SfxApplication & m_rApplication;
    com::sun::star::uno::Reference< com::sun::star::lang::XMultiServiceFactory >
        m_xServiceFactory;

    osl::Mutex m_aMutex;
    com::sun::star::uno::Reference< com::sun::star::beans::XPropertySet >
        m_xConfig;
    bool m_bDisposed;
};

} }

#endif // INCLUDED_SFX2_APPL_IMESTATUSWINDOW_HXX
<commit_msg>INTEGRATION: CWS changefileheader (1.4.416); FILE MERGED 2008/03/31 13:38:01 rt 1.4.416.1: #i87441# Change license header to LPGL v3.<commit_after>/*************************************************************************
 *
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * Copyright 2008 by Sun Microsystems, Inc.
 *
 * OpenOffice.org - a multi-platform office productivity suite
 *
 * $RCSfile: imestatuswindow.hxx,v $
 * $Revision: 1.5 $
 *
 * This file is part of OpenOffice.org.
 *
 * OpenOffice.org is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License version 3
 * only, as published by the Free Software Foundation.
 *
 * OpenOffice.org is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License version 3 for more details
 * (a copy is included in the LICENSE file that accompanied this code).
 *
 * You should have received a copy of the GNU Lesser General Public License
 * version 3 along with OpenOffice.org.  If not, see
 * <http://www.openoffice.org/license.html>
 * for a copy of the LGPLv3 License.
 *
 ************************************************************************/

#if !defined INCLUDED_SFX2_APPL_IMESTATUSWINDOW_HXX
#define INCLUDED_SFX2_APPL_IMESTATUSWINDOW_HXX

#include "com/sun/star/beans/XPropertyChangeListener.hpp"
#include "com/sun/star/uno/Reference.hxx"
#include "cppuhelper/implbase1.hxx"
#include "osl/mutex.hxx"

namespace com { namespace sun { namespace star {
    namespace beans { class XPropertySet; }
    namespace lang { class XMultiServiceFactory; }
} } }
class SfxApplication;

namespace sfx2 { namespace appl {

// The MS compiler needs this typedef work-around to accept the using
// declarations within ImeStatusWindow:
typedef cppu::WeakImplHelper1< com::sun::star::beans::XPropertyChangeListener >
ImeStatusWindow_Impl;

/** Control the behavior of any (platform-dependent) IME status windows.

    The decision of whether a status window shall be displayed or not can be
    stored permanently in the configuration (under key
    org.openoffice.office.Common/I18N/InputMethod/ShowStatusWindow; if that
    entry is nil, VCL is asked for a default).
 */
class ImeStatusWindow: private ImeStatusWindow_Impl
{
public:
    ImeStatusWindow(SfxApplication & rApplication,
                    com::sun::star::uno::Reference<
                            com::sun::star::lang::XMultiServiceFactory > const &
                        rServiceFactory);

    /** Set up VCL according to the configuration.

        Is it not strictly required that this method is called exactly once
        (though that will be the typical use).

        Must only be called with the Solar mutex locked.
     */
    void init();

    /** Return true if the status window is toggled on.

        This is only meaningful when canToggle returns true.

        Can be called without the Solar mutex locked.
     */
    bool isShowing();

    /** Toggle the status window on or off.

        This only works if canToggle returns true (otherwise, any calls of this
        method are ignored).

        Must only be called with the Solar mutex locked.
     */
    void show(bool bShow);

    /** Return true if the status window can be toggled on and off externally.

        Must only be called with the Solar mutex locked.
     */
    bool canToggle() const;

    using ImeStatusWindow_Impl::acquire;
    using ImeStatusWindow_Impl::release;
    using ImeStatusWindow_Impl::operator new;
    using ImeStatusWindow_Impl::operator delete;

private:
    ImeStatusWindow(ImeStatusWindow &); // not implemented
    void operator =(ImeStatusWindow); // not implemented

    virtual ~ImeStatusWindow();

    virtual void SAL_CALL
    disposing(com::sun::star::lang::EventObject const & rSource)
        throw (com::sun::star::uno::RuntimeException);

    virtual void SAL_CALL
    propertyChange(com::sun::star::beans::PropertyChangeEvent const & rEvent)
        throw (com::sun::star::uno::RuntimeException);

    com::sun::star::uno::Reference< com::sun::star::beans::XPropertySet >
    getConfig();

    SfxApplication & m_rApplication;
    com::sun::star::uno::Reference< com::sun::star::lang::XMultiServiceFactory >
        m_xServiceFactory;

    osl::Mutex m_aMutex;
    com::sun::star::uno::Reference< com::sun::star::beans::XPropertySet >
        m_xConfig;
    bool m_bDisposed;
};

} }

#endif // INCLUDED_SFX2_APPL_IMESTATUSWINDOW_HXX
<|endoftext|>


<commit_before>
#ifdef ENABLE_QUICKSTART_APPLET

#ifndef INCLUDED_SVTOOLS_MODULEOPTIONS_HXX
#include <svtools/moduleoptions.hxx>
#endif

#include <svtools/dynamicmenuoptions.hxx>

#include <gtk/gtk.h>
#include <glib.h>
#include <eggtray/eggtrayicon.h>
#include <vos/mutex.hxx>

#ifndef _SV_BITMAPEX_HXX
#include <vcl/bitmapex.hxx>
#endif
#ifndef _SV_BMPACC_HXX
#include <vcl/bmpacc.hxx>
#endif
#ifndef _SFXAPP_HXX
#include <sfx2/app.hxx>
#endif
#ifndef _SFX_APP_HRC
#include "app.hrc"
#endif
#ifndef __SHUTDOWNICON_HXX__
#define USE_APP_SHORTCUTS
#include "shutdownicon.hxx"
#endif

// Cut/paste from vcl/inc/svids.hrc
#define SV_ICON_SMALL_START                 25000

#define SV_ICON_ID_OFFICE                       1
#define SV_ICON_ID_TEXT                         2
#define SV_ICON_ID_SPREADSHEET                  4
#define SV_ICON_ID_DRAWING                      6
#define SV_ICON_ID_PRESENTATION                 8
#define SV_ICON_ID_DATABASE                    14
#define SV_ICON_ID_TEMPLATE                    16

using namespace ::rtl;
using namespace ::osl;

static ResMgr *pVCLResMgr;
static EggTrayIcon *pTrayIcon;
static GtkWidget *pExitMenuItem = NULL;
static GtkWidget *pOpenMenuItem = NULL;

static void open_url_cb( GtkWidget *, gpointer data )
{
    ShutdownIcon::OpenURL( *(OUString *)data,
                           OUString( RTL_CONSTASCII_USTRINGPARAM( "_default" ) ) );
}

static void open_file_cb( GtkWidget * )
{
    if ( !ShutdownIcon::bModalMode )
        ShutdownIcon::FileOpen();
}

static void open_template_cb( GtkWidget * )
{
    if ( !ShutdownIcon::bModalMode )
        ShutdownIcon::FromTemplate();
}

static void systray_disable_cb()
{
    ShutdownIcon::SetAutostart( false );
    ShutdownIcon::terminateDesktop();
}

static void exit_quickstarter_cb( GtkWidget * )
{
    egg_tray_icon_cancel_message (pTrayIcon, 1 );
    ShutdownIcon::getInstance()->terminateDesktop();
    plugin_shutdown_sys_tray();
}

static void menu_deactivate_cb( GtkWidget *pMenu )
{
    gtk_menu_popdown( GTK_MENU( pMenu ) );
}

static GdkPixbuf * ResIdToPixbuf( USHORT nResId )
{
    ResId aResId( SV_ICON_SMALL_START + nResId, *pVCLResMgr );
    BitmapEx aIcon( aResId );
    Bitmap pInSalBitmap = aIcon.GetBitmap();
    AlphaMask pInSalAlpha = aIcon.GetAlpha();

    BitmapReadAccess* pSalBitmap = pInSalBitmap.AcquireReadAccess();
    BitmapReadAccess* pSalAlpha = pInSalAlpha.AcquireReadAccess();

    g_return_val_if_fail( pSalBitmap != NULL, NULL );

    Size aSize( pSalBitmap->Width(), pSalBitmap->Height() );
    g_return_val_if_fail( Size( pSalAlpha->Width(), pSalAlpha->Height() ) == aSize, NULL );

    int nX, nY;
    guchar *pPixbufData = ( guchar * )g_malloc( 4 * aSize.Width() * aSize.Height() );
    guchar *pDestData = pPixbufData;

    for( nY = 0; nY < pSalBitmap->Height(); nY++ )
    {
        for( nX = 0; nX < pSalBitmap->Width(); nX++ )
        {
            BitmapColor aPix;
            aPix = pSalBitmap->GetPixel( nY, nX );
            pDestData[0] = aPix.GetRed();
            pDestData[1] = aPix.GetGreen();
            pDestData[2] = aPix.GetBlue();
            if (pSalAlpha)
            {
                aPix = pSalAlpha->GetPixel( nY, nX );
                pDestData[3] = 255 - aPix.GetIndex();
            }
            else
                pDestData[3] = 255;
            pDestData += 4;
        }
    }

    pInSalBitmap.ReleaseAccess( pSalBitmap );
    if( pSalAlpha )
        pInSalAlpha.ReleaseAccess( pSalAlpha );

    return gdk_pixbuf_new_from_data( pPixbufData,
        GDK_COLORSPACE_RGB, TRUE, 8,
        aSize.Width(), aSize.Height(),
        aSize.Width() * 4,
        (GdkPixbufDestroyNotify) g_free,
        NULL );
}

extern "C" {
static void oustring_delete (gpointer  data,
                             GClosure * /* closure */)
{
    OUString *pURL = (OUString *) data;
    delete pURL;
}
}

static void add_item( GtkMenuShell *pMenuShell, const char *pAsciiURL,
                      OUString *pOverrideLabel,
                      USHORT nResId, GCallback pFnCallback )
{
    OUString *pURL = new OUString (OStringToOUString( pAsciiURL,
                                                      RTL_TEXTENCODING_UTF8 ));
    OString aLabel;
    if (pOverrideLabel)
        aLabel = OUStringToOString (*pOverrideLabel, RTL_TEXTENCODING_UTF8);
    else
    {
        ShutdownIcon *pShutdownIcon = ShutdownIcon::getInstance();
        aLabel = OUStringToOString (pShutdownIcon->GetUrlDescription( *pURL ),
                                    RTL_TEXTENCODING_UTF8);
    }

    GdkPixbuf *pPixbuf= ResIdToPixbuf( nResId );
    GtkWidget *pImage = gtk_image_new_from_pixbuf( pPixbuf );
    g_object_unref( G_OBJECT( pPixbuf ) );

    GtkWidget *pMenuItem = gtk_image_menu_item_new_with_label( aLabel );
    gtk_image_menu_item_set_image( GTK_IMAGE_MENU_ITEM( pMenuItem ), pImage );
    g_signal_connect_data( pMenuItem, "activate", pFnCallback, pURL,
                           oustring_delete, GConnectFlags(0));

    gtk_menu_shell_append( pMenuShell, pMenuItem );
}

// Unbelievably nasty
using namespace ::com::sun::star::uno;
using namespace ::com::sun::star::task;
using namespace ::com::sun::star::lang;
using namespace ::com::sun::star::beans;

static void add_ugly_db_item( GtkMenuShell *pMenuShell, const char *pAsciiURL,
                              USHORT nResId, GCallback pFnCallback )
{
    SvtDynamicMenuOptions aOpt;
    Sequence < Sequence < PropertyValue > > aMenu = aOpt.GetMenu( E_NEWMENU );
    for ( sal_Int32 n=0; n<aMenu.getLength(); n++ )
    {
        ::rtl::OUString aURL;
        ::rtl::OUString aDescription;
        Sequence < PropertyValue >& aEntry = aMenu[n];
        for ( sal_Int32 m=0; m<aEntry.getLength(); m++ )
        {
            if ( aEntry[m].Name.equalsAsciiL( "URL", 3 ) )
                aEntry[m].Value >>= aURL;
            if ( aEntry[m].Name.equalsAsciiL( "Title", 5 ) )
                aEntry[m].Value >>= aDescription;
        }

        if ( aURL.equalsAscii( BASE_URL ) && aDescription.getLength() )
        {
            add_item (pMenuShell, pAsciiURL, &aDescription, nResId, pFnCallback);
            break;
        }
    }
}

static GtkWidget *
add_image_menu_item( GtkMenuShell *pMenuShell,
                     const gchar *stock_id,
                     rtl::OUString aLabel,
                     GCallback     activate_cb )
{
    OString aUtfLabel = rtl::OUStringToOString (aLabel, RTL_TEXTENCODING_UTF8 );

    GtkWidget *pImage;
    pImage = gtk_image_new_from_stock( stock_id, GTK_ICON_SIZE_MENU );

    GtkWidget *pMenuItem;
    pMenuItem = gtk_image_menu_item_new_with_label( aUtfLabel );
    gtk_image_menu_item_set_image( GTK_IMAGE_MENU_ITEM( pMenuItem ), pImage );

    gtk_menu_shell_append( pMenuShell, pMenuItem );
    g_signal_connect( pMenuItem, "activate", activate_cb, NULL);

    return pMenuItem;
}

static void populate_menu( GtkWidget *pMenu )
{
    ShutdownIcon *pShutdownIcon = ShutdownIcon::getInstance();
    GtkMenuShell *pMenuShell = GTK_MENU_SHELL( pMenu );
    SvtModuleOptions aModuleOptions;

    if ( aModuleOptions.IsWriter() )
        add_item (pMenuShell, WRITER_URL, NULL,
                  SV_ICON_ID_TEXT, G_CALLBACK( open_url_cb ));

    if ( aModuleOptions.IsCalc() )
        add_item (pMenuShell, CALC_URL, NULL,
                  SV_ICON_ID_SPREADSHEET, G_CALLBACK( open_url_cb ));

    if ( aModuleOptions.IsImpress() )
        add_item (pMenuShell, IMPRESS_URL, NULL,
                  SV_ICON_ID_PRESENTATION, G_CALLBACK( open_url_cb ));

    if ( aModuleOptions.IsDraw() )
        add_item (pMenuShell, DRAW_URL, NULL,
                  SV_ICON_ID_DRAWING, G_CALLBACK( open_url_cb ));

    if ( aModuleOptions.IsDataBase() )
        add_ugly_db_item (pMenuShell, BASE_URL,
                          SV_ICON_ID_DATABASE, G_CALLBACK( open_url_cb ));

    OUString aULabel = pShutdownIcon->GetResString( STR_QUICKSTART_FROMTEMPLATE );
    add_item (pMenuShell, "dummy", &aULabel,
              SV_ICON_ID_TEMPLATE, G_CALLBACK( open_template_cb ));

    OString aLabel;
    GtkWidget *pMenuItem;

    pMenuItem = gtk_separator_menu_item_new();
    gtk_menu_shell_append( pMenuShell, pMenuItem );

    pOpenMenuItem = add_image_menu_item
        (pMenuShell, GTK_STOCK_OPEN,
         pShutdownIcon->GetResString( STR_QUICKSTART_FILEOPEN ),
         G_CALLBACK( open_file_cb ));

    pMenuItem = gtk_separator_menu_item_new();
    gtk_menu_shell_append( pMenuShell, pMenuItem );

    (void) add_image_menu_item
        ( pMenuShell, GTK_STOCK_CLOSE,
          pShutdownIcon->GetResString( STR_QUICKSTART_PRELAUNCH_UNX ),
          G_CALLBACK( systray_disable_cb ) );

    pMenuItem = gtk_separator_menu_item_new();
    gtk_menu_shell_append( pMenuShell, pMenuItem );

    pExitMenuItem = add_image_menu_item
        ( pMenuShell, GTK_STOCK_QUIT,
          pShutdownIcon->GetResString( STR_QUICKSTART_EXIT ),
          G_CALLBACK( exit_quickstarter_cb ) );

    gtk_widget_show_all( pMenu );
}

static void refresh_menu( GtkWidget *pMenu )
{
    if (!pExitMenuItem)
        populate_menu( pMenu );

    bool bModal = ShutdownIcon::bModalMode;
    gtk_widget_set_sensitive( pExitMenuItem, !bModal);
    gtk_widget_set_sensitive( pOpenMenuItem, !bModal);
}

static void
layout_menu( GtkMenu *menu,
             gint *x, gint *y, gboolean *push_in,
             gpointer )
{
    GtkRequisition req;
    GtkWidget *ebox = GTK_BIN( pTrayIcon )->child;

    gtk_widget_size_request( GTK_WIDGET( menu ), &req );
    gdk_window_get_origin( ebox->window, x, y );

    (*x) += ebox->allocation.x;
    (*y) += ebox->allocation.y;

    if (*y >= gdk_screen_get_height (gtk_widget_get_screen (ebox)) / 2)
        (*y) -= req.height;
    else
        (*y) += ebox->allocation.height;

    *push_in = TRUE;
}

static gboolean display_menu_cb( GtkWidget *,
                                 GdkEventButton *event, GtkWidget *pMenu )
{
    if (event->button == 2)
        return FALSE;

#ifdef TEMPLATE_DIALOG_MORE_POLISHED
    if (event->button == 1 &&
        event->type == GDK_2BUTTON_PRESS)
    {
        open_template_cb( NULL );
        return TRUE;
    }
    if (event->button == 3)
    {
        ... as below ...
#endif

    refresh_menu( pMenu );

    gtk_menu_popup( GTK_MENU( pMenu ), NULL, NULL,
                    layout_menu, NULL, 0, event->time );

    return TRUE;
}

extern "C" {
    static gboolean
    show_at_idle( gpointer )
    {
        ::vos::OGuard aGuard( Application::GetSolarMutex() );
        gtk_widget_show_all( GTK_WIDGET( pTrayIcon ) );
        return FALSE;
    }
}

void SAL_DLLPUBLIC_EXPORT plugin_init_sys_tray()
{
    ::vos::OGuard aGuard( Application::GetSolarMutex() );

    if( !g_type_from_name( "GdkDisplay" ) )
        return;

    OString aLabel;
    ShutdownIcon *pShutdownIcon = ShutdownIcon::getInstance();

    aLabel = rtl::OUStringToOString (
            pShutdownIcon->GetResString( STR_QUICKSTART_TIP ),
            RTL_TEXTENCODING_UTF8 );

    pTrayIcon = egg_tray_icon_new( aLabel );

    GtkWidget *pParent = gtk_event_box_new();
    GtkTooltips *pTooltips = gtk_tooltips_new();
    gtk_tooltips_set_tip( GTK_TOOLTIPS( pTooltips ), pParent, aLabel, NULL );

    GtkWidget *pIconImage = gtk_image_new();
    gtk_container_add( GTK_CONTAINER( pParent ), pIconImage );

    pVCLResMgr = CREATEVERSIONRESMGR( vcl );

    GdkPixbuf *pPixbuf = ResIdToPixbuf( SV_ICON_ID_OFFICE );
    gtk_image_set_from_pixbuf( GTK_IMAGE( pIconImage ), pPixbuf );
    g_object_unref( pPixbuf );

    GtkWidget *pMenu = gtk_menu_new();
    g_signal_connect (pMenu, "deactivate",
                      G_CALLBACK (menu_deactivate_cb), NULL);
    g_signal_connect( pParent, "button_press_event",
                      G_CALLBACK( display_menu_cb ), pMenu );
    gtk_container_add( GTK_CONTAINER( pTrayIcon ), pParent );

    // Show at idle to avoid artefacts at startup
    g_idle_add (show_at_idle, (gpointer) pTrayIcon);

    // disable shutdown
    pShutdownIcon->SetVeto( true );
    pShutdownIcon->addTerminateListener();
}

void SAL_DLLPUBLIC_EXPORT plugin_shutdown_sys_tray()
{
    ::vos::OGuard aGuard( Application::GetSolarMutex() );
    if( !pTrayIcon )
        return;
    gtk_widget_destroy( GTK_WIDGET( pTrayIcon ) );
    pTrayIcon = NULL;
    pExitMenuItem = NULL;
    pOpenMenuItem = NULL;
}

#endif // ENABLE_QUICKSTART_APPLET
<commit_msg>INTEGRATION: CWS changefileheader (1.7.216); FILE MERGED 2008/04/01 15:38:50 thb 1.7.216.2: #i85898# Stripping all external header guards 2008/04/01 12:40:41 thb 1.7.216.1: #i85898# Stripping all external header guards<commit_after>
#ifdef ENABLE_QUICKSTART_APPLET

#include <svtools/moduleoptions.hxx>

#include <svtools/dynamicmenuoptions.hxx>

#include <gtk/gtk.h>
#include <glib.h>
#include <eggtray/eggtrayicon.h>
#include <vos/mutex.hxx>
#include <vcl/bitmapex.hxx>
#include <vcl/bmpacc.hxx>
#include <sfx2/app.hxx>
#ifndef _SFX_APP_HRC
#include "app.hrc"
#endif
#ifndef __SHUTDOWNICON_HXX__
#define USE_APP_SHORTCUTS
#include "shutdownicon.hxx"
#endif

// Cut/paste from vcl/inc/svids.hrc
#define SV_ICON_SMALL_START                 25000

#define SV_ICON_ID_OFFICE                       1
#define SV_ICON_ID_TEXT                         2
#define SV_ICON_ID_SPREADSHEET                  4
#define SV_ICON_ID_DRAWING                      6
#define SV_ICON_ID_PRESENTATION                 8
#define SV_ICON_ID_DATABASE                    14
#define SV_ICON_ID_TEMPLATE                    16

using namespace ::rtl;
using namespace ::osl;

static ResMgr *pVCLResMgr;
static EggTrayIcon *pTrayIcon;
static GtkWidget *pExitMenuItem = NULL;
static GtkWidget *pOpenMenuItem = NULL;

static void open_url_cb( GtkWidget *, gpointer data )
{
    ShutdownIcon::OpenURL( *(OUString *)data,
                           OUString( RTL_CONSTASCII_USTRINGPARAM( "_default" ) ) );
}

static void open_file_cb( GtkWidget * )
{
    if ( !ShutdownIcon::bModalMode )
        ShutdownIcon::FileOpen();
}

static void open_template_cb( GtkWidget * )
{
    if ( !ShutdownIcon::bModalMode )
        ShutdownIcon::FromTemplate();
}

static void systray_disable_cb()
{
    ShutdownIcon::SetAutostart( false );
    ShutdownIcon::terminateDesktop();
}

static void exit_quickstarter_cb( GtkWidget * )
{
    egg_tray_icon_cancel_message (pTrayIcon, 1 );
    ShutdownIcon::getInstance()->terminateDesktop();
    plugin_shutdown_sys_tray();
}

static void menu_deactivate_cb( GtkWidget *pMenu )
{
    gtk_menu_popdown( GTK_MENU( pMenu ) );
}

static GdkPixbuf * ResIdToPixbuf( USHORT nResId )
{
    ResId aResId( SV_ICON_SMALL_START + nResId, *pVCLResMgr );
    BitmapEx aIcon( aResId );
    Bitmap pInSalBitmap = aIcon.GetBitmap();
    AlphaMask pInSalAlpha = aIcon.GetAlpha();

    BitmapReadAccess* pSalBitmap = pInSalBitmap.AcquireReadAccess();
    BitmapReadAccess* pSalAlpha = pInSalAlpha.AcquireReadAccess();

    g_return_val_if_fail( pSalBitmap != NULL, NULL );

    Size aSize( pSalBitmap->Width(), pSalBitmap->Height() );
    g_return_val_if_fail( Size( pSalAlpha->Width(), pSalAlpha->Height() ) == aSize, NULL );

    int nX, nY;
    guchar *pPixbufData = ( guchar * )g_malloc( 4 * aSize.Width() * aSize.Height() );
    guchar *pDestData = pPixbufData;

    for( nY = 0; nY < pSalBitmap->Height(); nY++ )
    {
        for( nX = 0; nX < pSalBitmap->Width(); nX++ )
        {
            BitmapColor aPix;
            aPix = pSalBitmap->GetPixel( nY, nX );
            pDestData[0] = aPix.GetRed();
            pDestData[1] = aPix.GetGreen();
            pDestData[2] = aPix.GetBlue();
            if (pSalAlpha)
            {
                aPix = pSalAlpha->GetPixel( nY, nX );
                pDestData[3] = 255 - aPix.GetIndex();
            }
            else
                pDestData[3] = 255;
            pDestData += 4;
        }
    }

    pInSalBitmap.ReleaseAccess( pSalBitmap );
    if( pSalAlpha )
        pInSalAlpha.ReleaseAccess( pSalAlpha );

    return gdk_pixbuf_new_from_data( pPixbufData,
        GDK_COLORSPACE_RGB, TRUE, 8,
        aSize.Width(), aSize.Height(),
        aSize.Width() * 4,
        (GdkPixbufDestroyNotify) g_free,
        NULL );
}

extern "C" {
static void oustring_delete (gpointer  data,
                             GClosure * /* closure */)
{
    OUString *pURL = (OUString *) data;
    delete pURL;
}
}

static void add_item( GtkMenuShell *pMenuShell, const char *pAsciiURL,
                      OUString *pOverrideLabel,
                      USHORT nResId, GCallback pFnCallback )
{
    OUString *pURL = new OUString (OStringToOUString( pAsciiURL,
                                                      RTL_TEXTENCODING_UTF8 ));
    OString aLabel;
    if (pOverrideLabel)
        aLabel = OUStringToOString (*pOverrideLabel, RTL_TEXTENCODING_UTF8);
    else
    {
        ShutdownIcon *pShutdownIcon = ShutdownIcon::getInstance();
        aLabel = OUStringToOString (pShutdownIcon->GetUrlDescription( *pURL ),
                                    RTL_TEXTENCODING_UTF8);
    }

    GdkPixbuf *pPixbuf= ResIdToPixbuf( nResId );
    GtkWidget *pImage = gtk_image_new_from_pixbuf( pPixbuf );
    g_object_unref( G_OBJECT( pPixbuf ) );

    GtkWidget *pMenuItem = gtk_image_menu_item_new_with_label( aLabel );
    gtk_image_menu_item_set_image( GTK_IMAGE_MENU_ITEM( pMenuItem ), pImage );
    g_signal_connect_data( pMenuItem, "activate", pFnCallback, pURL,
                           oustring_delete, GConnectFlags(0));

    gtk_menu_shell_append( pMenuShell, pMenuItem );
}

// Unbelievably nasty
using namespace ::com::sun::star::uno;
using namespace ::com::sun::star::task;
using namespace ::com::sun::star::lang;
using namespace ::com::sun::star::beans;

static void add_ugly_db_item( GtkMenuShell *pMenuShell, const char *pAsciiURL,
                              USHORT nResId, GCallback pFnCallback )
{
    SvtDynamicMenuOptions aOpt;
    Sequence < Sequence < PropertyValue > > aMenu = aOpt.GetMenu( E_NEWMENU );
    for ( sal_Int32 n=0; n<aMenu.getLength(); n++ )
    {
        ::rtl::OUString aURL;
        ::rtl::OUString aDescription;
        Sequence < PropertyValue >& aEntry = aMenu[n];
        for ( sal_Int32 m=0; m<aEntry.getLength(); m++ )
        {
            if ( aEntry[m].Name.equalsAsciiL( "URL", 3 ) )
                aEntry[m].Value >>= aURL;
            if ( aEntry[m].Name.equalsAsciiL( "Title", 5 ) )
                aEntry[m].Value >>= aDescription;
        }

        if ( aURL.equalsAscii( BASE_URL ) && aDescription.getLength() )
        {
            add_item (pMenuShell, pAsciiURL, &aDescription, nResId, pFnCallback);
            break;
        }
    }
}

static GtkWidget *
add_image_menu_item( GtkMenuShell *pMenuShell,
                     const gchar *stock_id,
                     rtl::OUString aLabel,
                     GCallback     activate_cb )
{
    OString aUtfLabel = rtl::OUStringToOString (aLabel, RTL_TEXTENCODING_UTF8 );

    GtkWidget *pImage;
    pImage = gtk_image_new_from_stock( stock_id, GTK_ICON_SIZE_MENU );

    GtkWidget *pMenuItem;
    pMenuItem = gtk_image_menu_item_new_with_label( aUtfLabel );
    gtk_image_menu_item_set_image( GTK_IMAGE_MENU_ITEM( pMenuItem ), pImage );

    gtk_menu_shell_append( pMenuShell, pMenuItem );
    g_signal_connect( pMenuItem, "activate", activate_cb, NULL);

    return pMenuItem;
}

static void populate_menu( GtkWidget *pMenu )
{
    ShutdownIcon *pShutdownIcon = ShutdownIcon::getInstance();
    GtkMenuShell *pMenuShell = GTK_MENU_SHELL( pMenu );
    SvtModuleOptions aModuleOptions;

    if ( aModuleOptions.IsWriter() )
        add_item (pMenuShell, WRITER_URL, NULL,
                  SV_ICON_ID_TEXT, G_CALLBACK( open_url_cb ));

    if ( aModuleOptions.IsCalc() )
        add_item (pMenuShell, CALC_URL, NULL,
                  SV_ICON_ID_SPREADSHEET, G_CALLBACK( open_url_cb ));

    if ( aModuleOptions.IsImpress() )
        add_item (pMenuShell, IMPRESS_URL, NULL,
                  SV_ICON_ID_PRESENTATION, G_CALLBACK( open_url_cb ));

    if ( aModuleOptions.IsDraw() )
        add_item (pMenuShell, DRAW_URL, NULL,
                  SV_ICON_ID_DRAWING, G_CALLBACK( open_url_cb ));

    if ( aModuleOptions.IsDataBase() )
        add_ugly_db_item (pMenuShell, BASE_URL,
                          SV_ICON_ID_DATABASE, G_CALLBACK( open_url_cb ));

    OUString aULabel = pShutdownIcon->GetResString( STR_QUICKSTART_FROMTEMPLATE );
    add_item (pMenuShell, "dummy", &aULabel,
              SV_ICON_ID_TEMPLATE, G_CALLBACK( open_template_cb ));

    OString aLabel;
    GtkWidget *pMenuItem;

    pMenuItem = gtk_separator_menu_item_new();
    gtk_menu_shell_append( pMenuShell, pMenuItem );

    pOpenMenuItem = add_image_menu_item
        (pMenuShell, GTK_STOCK_OPEN,
         pShutdownIcon->GetResString( STR_QUICKSTART_FILEOPEN ),
         G_CALLBACK( open_file_cb ));

    pMenuItem = gtk_separator_menu_item_new();
    gtk_menu_shell_append( pMenuShell, pMenuItem );

    (void) add_image_menu_item
        ( pMenuShell, GTK_STOCK_CLOSE,
          pShutdownIcon->GetResString( STR_QUICKSTART_PRELAUNCH_UNX ),
          G_CALLBACK( systray_disable_cb ) );

    pMenuItem = gtk_separator_menu_item_new();
    gtk_menu_shell_append( pMenuShell, pMenuItem );

    pExitMenuItem = add_image_menu_item
        ( pMenuShell, GTK_STOCK_QUIT,
          pShutdownIcon->GetResString( STR_QUICKSTART_EXIT ),
          G_CALLBACK( exit_quickstarter_cb ) );

    gtk_widget_show_all( pMenu );
}

static void refresh_menu( GtkWidget *pMenu )
{
    if (!pExitMenuItem)
        populate_menu( pMenu );

    bool bModal = ShutdownIcon::bModalMode;
    gtk_widget_set_sensitive( pExitMenuItem, !bModal);
    gtk_widget_set_sensitive( pOpenMenuItem, !bModal);
}

static void
layout_menu( GtkMenu *menu,
             gint *x, gint *y, gboolean *push_in,
             gpointer )
{
    GtkRequisition req;
    GtkWidget *ebox = GTK_BIN( pTrayIcon )->child;

    gtk_widget_size_request( GTK_WIDGET( menu ), &req );
    gdk_window_get_origin( ebox->window, x, y );

    (*x) += ebox->allocation.x;
    (*y) += ebox->allocation.y;

    if (*y >= gdk_screen_get_height (gtk_widget_get_screen (ebox)) / 2)
        (*y) -= req.height;
    else
        (*y) += ebox->allocation.height;

    *push_in = TRUE;
}

static gboolean display_menu_cb( GtkWidget *,
                                 GdkEventButton *event, GtkWidget *pMenu )
{
    if (event->button == 2)
        return FALSE;

#ifdef TEMPLATE_DIALOG_MORE_POLISHED
    if (event->button == 1 &&
        event->type == GDK_2BUTTON_PRESS)
    {
        open_template_cb( NULL );
        return TRUE;
    }
    if (event->button == 3)
    {
        ... as below ...
#endif

    refresh_menu( pMenu );

    gtk_menu_popup( GTK_MENU( pMenu ), NULL, NULL,
                    layout_menu, NULL, 0, event->time );

    return TRUE;
}

extern "C" {
    static gboolean
    show_at_idle( gpointer )
    {
        ::vos::OGuard aGuard( Application::GetSolarMutex() );
        gtk_widget_show_all( GTK_WIDGET( pTrayIcon ) );
        return FALSE;
    }
}

void SAL_DLLPUBLIC_EXPORT plugin_init_sys_tray()
{
    ::vos::OGuard aGuard( Application::GetSolarMutex() );

    if( !g_type_from_name( "GdkDisplay" ) )
        return;

    OString aLabel;
    ShutdownIcon *pShutdownIcon = ShutdownIcon::getInstance();

    aLabel = rtl::OUStringToOString (
            pShutdownIcon->GetResString( STR_QUICKSTART_TIP ),
            RTL_TEXTENCODING_UTF8 );

    pTrayIcon = egg_tray_icon_new( aLabel );

    GtkWidget *pParent = gtk_event_box_new();
    GtkTooltips *pTooltips = gtk_tooltips_new();
    gtk_tooltips_set_tip( GTK_TOOLTIPS( pTooltips ), pParent, aLabel, NULL );

    GtkWidget *pIconImage = gtk_image_new();
    gtk_container_add( GTK_CONTAINER( pParent ), pIconImage );

    pVCLResMgr = CREATEVERSIONRESMGR( vcl );

    GdkPixbuf *pPixbuf = ResIdToPixbuf( SV_ICON_ID_OFFICE );
    gtk_image_set_from_pixbuf( GTK_IMAGE( pIconImage ), pPixbuf );
    g_object_unref( pPixbuf );

    GtkWidget *pMenu = gtk_menu_new();
    g_signal_connect (pMenu, "deactivate",
                      G_CALLBACK (menu_deactivate_cb), NULL);
    g_signal_connect( pParent, "button_press_event",
                      G_CALLBACK( display_menu_cb ), pMenu );
    gtk_container_add( GTK_CONTAINER( pTrayIcon ), pParent );

    // Show at idle to avoid artefacts at startup
    g_idle_add (show_at_idle, (gpointer) pTrayIcon);

    // disable shutdown
    pShutdownIcon->SetVeto( true );
    pShutdownIcon->addTerminateListener();
}

void SAL_DLLPUBLIC_EXPORT plugin_shutdown_sys_tray()
{
    ::vos::OGuard aGuard( Application::GetSolarMutex() );
    if( !pTrayIcon )
        return;
    gtk_widget_destroy( GTK_WIDGET( pTrayIcon ) );
    pTrayIcon = NULL;
    pExitMenuItem = NULL;
    pOpenMenuItem = NULL;
}

#endif // ENABLE_QUICKSTART_APPLET
<|endoftext|>


<commit_before>///
/// @file  IteratorHelper.cpp
///        Functions used to calculate the next start and stop
///        numbers for primesieve::iterator.
///
/// Copyright (C) 2022 Kim Walisch, <kim.walisch@gmail.com>
///
/// This file is distributed under the BSD License. See the COPYING
/// file in the top level directory.
///

#include <primesieve/config.hpp>
#include <primesieve/IteratorHelper.hpp>
#include <primesieve/PrimeGenerator.hpp>
#include <primesieve/pmath.hpp>

#include <stdint.h>
#include <algorithm>
#include <cmath>
#include <limits>

using namespace primesieve;

namespace {

uint64_t getNextDist(uint64_t start, uint64_t dist)
{
  uint64_t minDist = (uint64_t) std::sqrt(start);
  uint64_t maxDist = 1ull << 60;

  dist *= 4;
  dist = std::max(dist, PrimeGenerator::maxCachedPrime());
  dist = inBetween(minDist, dist, maxDist);

  return dist;
}

uint64_t getPrevDist(uint64_t stop, uint64_t dist)
{
  double x = std::max(10.0, (double) stop);
  uint64_t logx = (uint64_t) std::log(x);
  uint64_t minDist = (config::MIN_CACHE_ITERATOR / sizeof(uint64_t)) * logx;
  uint64_t maxDist = (config::MAX_CACHE_ITERATOR / sizeof(uint64_t)) * logx;
  uint64_t tinyDist = PrimeGenerator::maxCachedPrime() * 4;
  uint64_t defaultDist = (uint64_t) (std::sqrt(stop) * 2);

  dist *= 4;
  dist = inBetween(tinyDist, dist, minDist);
  dist = inBetween(dist, defaultDist, maxDist);

  return dist;
}

} // namespace

namespace primesieve {

void IteratorHelper::next(uint64_t* start,
                          uint64_t* stop,
                          uint64_t stopHint,
                          uint64_t* dist)
{
  *start = checkedAdd(*stop, 1);
  *dist = getNextDist(*start, *dist);

  if (stopHint >= *start &&
      stopHint < std::numeric_limits<uint64_t>::max())
  {
    // For primesieve::iterator it is advantageous to buffer
    // slightly more primes than the stopHint since the
    // stopHint is often not 100% accurate and the user
    // might iterate over a few primes > stopHint.
    *stop = checkedAdd(stopHint, maxPrimeGap(stopHint));
  }
  else
  {
    // In case the user has used the default stopHint=UINT64_MAX
    // we take a conservative approach and only buffer a
    // small number of primes. If the user uses more primes
    // than we have buffered, then we will increase the sieving
    // distance and buffer more primes (than last time).
    *stop = checkedAdd(*start, *dist);
  }
}

void IteratorHelper::prev(uint64_t* start,
                          uint64_t* stop,
                          uint64_t stopHint,
                          uint64_t* dist)
{
  *stop = checkedSub(*start, 1);
  *dist = getPrevDist(*stop, *dist);
  *start = checkedSub(*stop, *dist);

  if (stopHint >= *start &&
      stopHint <= *stop)
    *start = checkedSub(stopHint, maxPrimeGap(stopHint));
}

} // namespace
<commit_msg>Refactor<commit_after>///
/// @file  IteratorHelper.cpp
///        Functions used to calculate the next start and stop
///        numbers for primesieve::iterator.
///
/// Copyright (C) 2022 Kim Walisch, <kim.walisch@gmail.com>
///
/// This file is distributed under the BSD License. See the COPYING
/// file in the top level directory.
///

#include <primesieve/config.hpp>
#include <primesieve/IteratorHelper.hpp>
#include <primesieve/PrimeGenerator.hpp>
#include <primesieve/pmath.hpp>

#include <stdint.h>
#include <algorithm>
#include <cmath>
#include <limits>

using namespace primesieve;

namespace {

uint64_t getNextDist(uint64_t start, uint64_t dist)
{
  uint64_t minDist = (uint64_t) std::sqrt(start);
  uint64_t maxDist = 1ull << 60;

  dist *= 4;
  dist = std::max(PrimeGenerator::maxCachedPrime(), dist);
  return inBetween(minDist, dist, maxDist);
}

uint64_t getPrevDist(uint64_t stop, uint64_t dist)
{
  double x = std::max(10.0, (double) stop);
  uint64_t logx = (uint64_t) std::log(x);
  uint64_t minDist = (config::MIN_CACHE_ITERATOR / sizeof(uint64_t)) * logx;
  uint64_t maxDist = (config::MAX_CACHE_ITERATOR / sizeof(uint64_t)) * logx;
  uint64_t tinyDist = PrimeGenerator::maxCachedPrime() * 4;
  uint64_t defaultDist = (uint64_t) (std::sqrt(stop) * 2);

  dist *= 4;
  minDist = inBetween(tinyDist, dist, minDist);
  return inBetween(minDist, defaultDist, maxDist);
}

} // namespace

namespace primesieve {

void IteratorHelper::next(uint64_t* start,
                          uint64_t* stop,
                          uint64_t stopHint,
                          uint64_t* dist)
{
  *start = checkedAdd(*stop, 1);
  *dist = getNextDist(*start, *dist);

  if (stopHint >= *start &&
      stopHint < std::numeric_limits<uint64_t>::max())
  {
    // For primesieve::iterator it is advantageous to buffer
    // slightly more primes than the stopHint since the
    // stopHint is often not 100% accurate and the user
    // might iterate over a few primes > stopHint.
    *stop = checkedAdd(stopHint, maxPrimeGap(stopHint));
  }
  else
  {
    // In case the user has used the default stopHint=UINT64_MAX
    // we take a conservative approach and only buffer a
    // small number of primes. If the user uses more primes
    // than we have buffered, then we will increase the sieving
    // distance and buffer more primes (than last time).
    *stop = checkedAdd(*start, *dist);
  }
}

void IteratorHelper::prev(uint64_t* start,
                          uint64_t* stop,
                          uint64_t stopHint,
                          uint64_t* dist)
{
  *stop = checkedSub(*start, 1);
  *dist = getPrevDist(*stop, *dist);
  *start = checkedSub(*stop, *dist);

  if (stopHint >= *start &&
      stopHint <= *stop)
    *start = checkedSub(stopHint, maxPrimeGap(stopHint));
}

} // namespace
<|endoftext|>


<commit_before>// $Header$

#include "KineTools.h"

#include <TObject.h>
#include <TTree.h>
#include <TBranchElement.h>
#include <TClonesArray.h>

#include <AliStack.h>
#include <AliTrackReference.h>

#include "Reve/Track.h"
#include "Reve/RenderElement.h"

#include <algorithm>
#include <map>

//______________________________________________________________________
// KineTools
//

using namespace Reve;
using namespace Alieve;
using namespace std;

ClassImp(KineTools)

KineTools::KineTools()
{}

/**************************************************************************/

void KineTools::SetDaughterPathMarks(TrackList* cont,  AliStack* stack)
{
  // Import daughters birth points.

  RenderElement::List_i  iter = cont->BeginChildren();

  while(iter != cont->EndChildren())
  {
    Track* track = dynamic_cast<Track*>(*iter);
    TParticle* p = stack->Particle(track->GetLabel());
    if(p->GetNDaughters()) {
      Int_t d0 = p->GetDaughter(0), d1 = p->GetDaughter(1);
      for(int d=d0; d>0 && d<=d1;++d)
      {
	TParticle* dp = stack->Particle(d);
	Reve::PathMark* pm = new PathMark( PathMark::Daughter);
        pm->V.Set(dp->Vx(),dp->Vy(), dp->Vz());
	pm->P.Set(dp->Px(),dp->Py(), dp->Pz());
        pm->time = dp->T();
        track->AddPathMark(pm);
      }
    }
    ++iter;
  }
}

/**************************************************************************/

namespace {
struct cmp_pathmark {
  bool operator()(PathMark* const & a, PathMark* const & b)
  { return a->time < b->time; }
};
}

void KineTools::SetPathMarks(TrackList* cont, AliStack* stack , TTree* treeTR)
{
  // set decay and reference points

  static const Exc_t eH("KineTools::ImportPathMarks");

  if(treeTR == 0) {
    SetDaughterPathMarks(cont, stack);
    return;
  }

  map<Int_t, Track::vpPathMark_t > refs;

  Int_t nPrimaries = (Int_t) treeTR->GetEntries();
  TIter next(treeTR->GetListOfBranches());
  TBranchElement* el;
  Bool_t isRef = kTRUE;
  treeTR->SetBranchStatus("*", 0);

  while ((el = (TBranchElement*) next()))
  {
    if (strcmp("AliRun",el->GetName()) == 0)
      isRef = kFALSE;

    treeTR->SetBranchStatus(Form("%s*", el->GetName()), 1);
    for (Int_t iPrimPart = 0; iPrimPart<nPrimaries; iPrimPart++)
    {

      TClonesArray* arr = 0;
      treeTR->SetBranchAddress(el->GetName(), &arr);
      treeTR->GetEntry(iPrimPart);

      for (Int_t iTrackRef = 0; iTrackRef < arr->GetEntriesFast(); iTrackRef++)
      {
	AliTrackReference* atr = (AliTrackReference*)arr->At(iTrackRef);
	Int_t track = atr->GetTrack();
        if(atr->TestBit(TObject::kNotDeleted)) {
	  if(track > 0)
	  {
            PathMark* pm;
	    if(isRef)
	      pm = new PathMark(PathMark::Reference);
	    else
 	      pm = new PathMark(PathMark::Decay);

	    pm->V.Set(atr->X(),atr->Y(), atr->Z());
	    pm->P.Set(atr->Px(),atr->Py(), atr->Pz());
	    pm->time = atr->GetTime();

	    Track::vpPathMark_t& v = refs[track];
            v.push_back(pm);
	  }
	  else
	    throw(eH + Form("negative label for entry %d in branch %s.",
			    iTrackRef, el->GetName());
	}
      } // loop track refs
      treeTR->SetBranchAddress(el->GetName(), 0);
    } // loop primaries, clones arrays
    treeTR->SetBranchStatus(Form("%s*", el->GetName()), 0);
  } // end loop through top branches
  treeTR->SetBranchStatus("*", 1);

  // sort references and add it to tracks
  RenderElement::List_i  cit = cont->BeginChildren();
  while(cit != cont->EndChildren())
  {
    Track* track = dynamic_cast<Track*>(*cit);

    // add daughters path marks in the map
    TParticle* p = stack->Particle(track->GetLabel());
    if(p->GetNDaughters()) {
      for(int d=p->GetFirstDaughter(); d>0 && d<=p->GetLastDaughter();++d)
      {
	TParticle* dp = stack->Particle(d);
	Reve::PathMark* pm = new PathMark( PathMark::Daughter);
	pm->V.Set(dp->Vx(),dp->Vy(), dp->Vz());
	pm->P.Set(dp->Px(),dp->Py(), dp->Pz());
	pm->time = dp->T();
	Track::vpPathMark_t& v = refs[track->GetLabel()];
	v.push_back(pm);
      }
    }

    map<Int_t, Track::vpPathMark_t > ::iterator mi = refs.find(track->GetLabel());
    if(mi != refs.end()) {
      Track::vpPathMark_t& v = mi->second;
      sort(v.begin(), v.end(), cmp_pathmark());
      for(Track::vpPathMark_i i=v.begin(); i!=v.end(); ++i){
	track->AddPathMark(*i);
      }
    }
    ++cit;
  }
}
<commit_msg>Bug fix<commit_after>// $Header$

#include "KineTools.h"

#include <TObject.h>
#include <TTree.h>
#include <TBranchElement.h>
#include <TClonesArray.h>

#include <AliStack.h>
#include <AliTrackReference.h>

#include "Reve/Track.h"
#include "Reve/RenderElement.h"

#include <algorithm>
#include <map>

//______________________________________________________________________
// KineTools
//

using namespace Reve;
using namespace Alieve;
using namespace std;

ClassImp(KineTools)

KineTools::KineTools()
{}

/**************************************************************************/

void KineTools::SetDaughterPathMarks(TrackList* cont,  AliStack* stack)
{
  // Import daughters birth points.

  RenderElement::List_i  iter = cont->BeginChildren();

  while(iter != cont->EndChildren())
  {
    Track* track = dynamic_cast<Track*>(*iter);
    TParticle* p = stack->Particle(track->GetLabel());
    if(p->GetNDaughters()) {
      Int_t d0 = p->GetDaughter(0), d1 = p->GetDaughter(1);
      for(int d=d0; d>0 && d<=d1;++d)
      {
	TParticle* dp = stack->Particle(d);
	Reve::PathMark* pm = new PathMark( PathMark::Daughter);
        pm->V.Set(dp->Vx(),dp->Vy(), dp->Vz());
	pm->P.Set(dp->Px(),dp->Py(), dp->Pz());
        pm->time = dp->T();
        track->AddPathMark(pm);
      }
    }
    ++iter;
  }
}

/**************************************************************************/

namespace {
struct cmp_pathmark {
  bool operator()(PathMark* const & a, PathMark* const & b)
  { return a->time < b->time; }
};
}

void KineTools::SetPathMarks(TrackList* cont, AliStack* stack , TTree* treeTR)
{
  // set decay and reference points

  static const Exc_t eH("KineTools::ImportPathMarks");

  if(treeTR == 0) {
    SetDaughterPathMarks(cont, stack);
    return;
  }

  map<Int_t, Track::vpPathMark_t > refs;

  Int_t nPrimaries = (Int_t) treeTR->GetEntries();
  TIter next(treeTR->GetListOfBranches());
  TBranchElement* el;
  Bool_t isRef = kTRUE;
  treeTR->SetBranchStatus("*", 0);

  while ((el = (TBranchElement*) next()))
  {
    if (strcmp("AliRun",el->GetName()) == 0)
      isRef = kFALSE;

    treeTR->SetBranchStatus(Form("%s*", el->GetName()), 1);
    for (Int_t iPrimPart = 0; iPrimPart<nPrimaries; iPrimPart++)
    {

      TClonesArray* arr = 0;
      treeTR->SetBranchAddress(el->GetName(), &arr);
      treeTR->GetEntry(iPrimPart);

      for (Int_t iTrackRef = 0; iTrackRef < arr->GetEntriesFast(); iTrackRef++)
      {
	AliTrackReference* atr = (AliTrackReference*)arr->At(iTrackRef);
	Int_t track = atr->GetTrack();
        if(atr->TestBit(TObject::kNotDeleted)) {
	  if(track > 0)
	  {
            PathMark* pm;
	    if(isRef)
	      pm = new PathMark(PathMark::Reference);
	    else
 	      pm = new PathMark(PathMark::Decay);

	    pm->V.Set(atr->X(),atr->Y(), atr->Z());
	    pm->P.Set(atr->Px(),atr->Py(), atr->Pz());
	    pm->time = atr->GetTime();

	    Track::vpPathMark_t& v = refs[track];
            v.push_back(pm);
	  }
	  else
	    throw(eH + Form("negative label for entry %d in branch %s.",
			    iTrackRef, el->GetName()));
	}
      } // loop track refs
      treeTR->SetBranchAddress(el->GetName(), 0);
    } // loop primaries, clones arrays
    treeTR->SetBranchStatus(Form("%s*", el->GetName()), 0);
  } // end loop through top branches
  treeTR->SetBranchStatus("*", 1);

  // sort references and add it to tracks
  RenderElement::List_i  cit = cont->BeginChildren();
  while(cit != cont->EndChildren())
  {
    Track* track = dynamic_cast<Track*>(*cit);

    // add daughters path marks in the map
    TParticle* p = stack->Particle(track->GetLabel());
    if(p->GetNDaughters()) {
      for(int d=p->GetFirstDaughter(); d>0 && d<=p->GetLastDaughter();++d)
      {
	TParticle* dp = stack->Particle(d);
	Reve::PathMark* pm = new PathMark( PathMark::Daughter);
	pm->V.Set(dp->Vx(),dp->Vy(), dp->Vz());
	pm->P.Set(dp->Px(),dp->Py(), dp->Pz());
	pm->time = dp->T();
	Track::vpPathMark_t& v = refs[track->GetLabel()];
	v.push_back(pm);
      }
    }

    map<Int_t, Track::vpPathMark_t > ::iterator mi = refs.find(track->GetLabel());
    if(mi != refs.end()) {
      Track::vpPathMark_t& v = mi->second;
      sort(v.begin(), v.end(), cmp_pathmark());
      for(Track::vpPathMark_i i=v.begin(); i!=v.end(); ++i){
	track->AddPathMark(*i);
      }
    }
    ++cit;
  }
}
<|endoftext|>


<commit_before>#include "stdafx.h"
#include "bindings.h"
#include "helper.h"
#include <game_sa\CPed.h>
#include <injector\calling.hpp>

using namespace Bindings;

JsValueRef CALLBACK getFloat(JsValueRef callee, bool isConstructCall, JsValueRef *arguments, unsigned short argumentCount, void *callbackState);
JsValueRef CALLBACK setFloat(JsValueRef callee, bool isConstructCall, JsValueRef *arguments, unsigned short argumentCount, void *callbackState);

JsValueRef CALLBACK getInt(JsValueRef callee, bool isConstructCall, JsValueRef *arguments, unsigned short argumentCount, void *callbackState);
JsValueRef CALLBACK setInt(JsValueRef callee, bool isConstructCall, JsValueRef *arguments, unsigned short argumentCount, void *callbackState);

JsValueRef CALLBACK getBool(JsValueRef callee, bool isConstructCall, JsValueRef *arguments, unsigned short argumentCount, void *callbackState);
JsValueRef CALLBACK setBool(JsValueRef callee, bool isConstructCall, JsValueRef *arguments, unsigned short argumentCount, void *callbackState);

void Ped(JsValueRef ped, Container& container) {
	void* data;
	JsGetExternalData(ped, &data);
	CPed* pedNative = (CPed*)data;

	Js::DefineProperty(ped, L"health", getFloat, setFloat, &pedNative->m_fHealth);
	Js::DefineProperty(ped, L"armor", getFloat, setFloat, &pedNative->m_fArmour);
	Js::DefineProperty(ped, L"isDriving", getBool, nullptr, container.create_pointer(pedNative, 0x46C, 8));
	/*Js::DefineProperty(ped, L"isArrested", getBool, nullptr, &pedNative->m_bIsBeingArrested);*/
	Js::DefineProperty(ped, L"isInvisible", getBool, setBool, container.create_pointer(pedNative, 0x474, 1));
	Js::DefineProperty(ped, L"model", getInt, nullptr, &pedNative->m_wModelIndex);
}

JsValueRef CALLBACK getFloat(JsValueRef callee, bool isConstructCall, JsValueRef *arguments, unsigned short argumentCount, void *callbackState)
{
	float* floatValue = (float*)callbackState;

	JsValueRef numberValue;
	JsDoubleToNumber(*floatValue, &numberValue);

	return numberValue;
}

JsValueRef CALLBACK setFloat(JsValueRef callee, bool isConstructCall, JsValueRef *arguments, unsigned short argumentCount, void *callbackState)
{
	float* floatValue = (float*)callbackState;

	double doubleValue;
	JsNumberToDouble(arguments[1], &doubleValue);

	*floatValue = doubleValue;

	return JS_INVALID_REFERENCE;
}

JsValueRef CALLBACK getInt(JsValueRef callee, bool isConstructCall, JsValueRef *arguments, unsigned short argumentCount, void *callbackState)
{
	int* intValue = (int*)callbackState;

	JsValueRef numberValue;
	JsIntToNumber(*intValue, &numberValue);

	return numberValue;
}

JsValueRef CALLBACK setInt(JsValueRef callee, bool isConstructCall, JsValueRef *arguments, unsigned short argumentCount, void *callbackState)
{
	int* intValue = (int*)callbackState;

	int tempIntValue;
	JsNumberToInt(arguments[1], &tempIntValue);

	*intValue = tempIntValue;

	return JS_INVALID_REFERENCE;
}

JsValueRef CALLBACK getBool(JsValueRef callee, bool isConstructCall, JsValueRef *arguments, unsigned short argumentCount, void *callbackState)
{
	fake_ptr* ptr = (fake_ptr*)callbackState;
	bool boolValue = get(ptr->pointer, ptr->offset, ptr->shift);

	JsValueRef booleanValue;
	JsBoolToBoolean(booleanValue, &booleanValue);

	return booleanValue;
}

JsValueRef CALLBACK setBool(JsValueRef callee, bool isConstructCall, JsValueRef *arguments, unsigned short argumentCount, void *callbackState)
{
	fake_ptr* ptr = (fake_ptr*)callbackState;

	bool tempBoolValue;
	JsBooleanToBool(arguments[1], &tempBoolValue);

	set(ptr->pointer, ptr->offset, ptr->shift, tempBoolValue);

	return JS_INVALID_REFERENCE;
}

fake_ptr::fake_ptr(void* pointer, int offset, int shift)
{
	this->pointer = pointer;
	this->offset = offset;
	this->shift = shift;
}

Container::Container()
{
}

Container::~Container()
{
	while (!pointers.empty())
	{
		delete pointers.front();
		pointers.pop();
	}
}

fake_ptr* Container::create_pointer(void* base, int offset, int shift)
{
	auto pointer = new fake_ptr(base, offset, shift);
	pointers.push(pointer);
	return pointer
}
<commit_msg>Forgot semicolon :sweat_smile:<commit_after>#include "stdafx.h"
#include "bindings.h"
#include "helper.h"
#include <game_sa\CPed.h>
#include <injector\calling.hpp>

using namespace Bindings;

JsValueRef CALLBACK getFloat(JsValueRef callee, bool isConstructCall, JsValueRef *arguments, unsigned short argumentCount, void *callbackState);
JsValueRef CALLBACK setFloat(JsValueRef callee, bool isConstructCall, JsValueRef *arguments, unsigned short argumentCount, void *callbackState);

JsValueRef CALLBACK getInt(JsValueRef callee, bool isConstructCall, JsValueRef *arguments, unsigned short argumentCount, void *callbackState);
JsValueRef CALLBACK setInt(JsValueRef callee, bool isConstructCall, JsValueRef *arguments, unsigned short argumentCount, void *callbackState);

JsValueRef CALLBACK getBool(JsValueRef callee, bool isConstructCall, JsValueRef *arguments, unsigned short argumentCount, void *callbackState);
JsValueRef CALLBACK setBool(JsValueRef callee, bool isConstructCall, JsValueRef *arguments, unsigned short argumentCount, void *callbackState);

void Ped(JsValueRef ped, Container& container) {
	void* data;
	JsGetExternalData(ped, &data);
	CPed* pedNative = (CPed*)data;

	Js::DefineProperty(ped, L"health", getFloat, setFloat, &pedNative->m_fHealth);
	Js::DefineProperty(ped, L"armor", getFloat, setFloat, &pedNative->m_fArmour);
	Js::DefineProperty(ped, L"isDriving", getBool, nullptr, container.create_pointer(pedNative, 0x46C, 8));
	/*Js::DefineProperty(ped, L"isArrested", getBool, nullptr, &pedNative->m_bIsBeingArrested);*/
	Js::DefineProperty(ped, L"isInvisible", getBool, setBool, container.create_pointer(pedNative, 0x474, 1));
	Js::DefineProperty(ped, L"model", getInt, nullptr, &pedNative->m_wModelIndex);
}

JsValueRef CALLBACK getFloat(JsValueRef callee, bool isConstructCall, JsValueRef *arguments, unsigned short argumentCount, void *callbackState)
{
	float* floatValue = (float*)callbackState;

	JsValueRef numberValue;
	JsDoubleToNumber(*floatValue, &numberValue);

	return numberValue;
}

JsValueRef CALLBACK setFloat(JsValueRef callee, bool isConstructCall, JsValueRef *arguments, unsigned short argumentCount, void *callbackState)
{
	float* floatValue = (float*)callbackState;

	double doubleValue;
	JsNumberToDouble(arguments[1], &doubleValue);

	*floatValue = doubleValue;

	return JS_INVALID_REFERENCE;
}

JsValueRef CALLBACK getInt(JsValueRef callee, bool isConstructCall, JsValueRef *arguments, unsigned short argumentCount, void *callbackState)
{
	int* intValue = (int*)callbackState;

	JsValueRef numberValue;
	JsIntToNumber(*intValue, &numberValue);

	return numberValue;
}

JsValueRef CALLBACK setInt(JsValueRef callee, bool isConstructCall, JsValueRef *arguments, unsigned short argumentCount, void *callbackState)
{
	int* intValue = (int*)callbackState;

	int tempIntValue;
	JsNumberToInt(arguments[1], &tempIntValue);

	*intValue = tempIntValue;

	return JS_INVALID_REFERENCE;
}

JsValueRef CALLBACK getBool(JsValueRef callee, bool isConstructCall, JsValueRef *arguments, unsigned short argumentCount, void *callbackState)
{
	fake_ptr* ptr = (fake_ptr*)callbackState;
	bool boolValue = get(ptr->pointer, ptr->offset, ptr->shift);

	JsValueRef booleanValue;
	JsBoolToBoolean(booleanValue, &booleanValue);

	return booleanValue;
}

JsValueRef CALLBACK setBool(JsValueRef callee, bool isConstructCall, JsValueRef *arguments, unsigned short argumentCount, void *callbackState)
{
	fake_ptr* ptr = (fake_ptr*)callbackState;

	bool tempBoolValue;
	JsBooleanToBool(arguments[1], &tempBoolValue);

	set(ptr->pointer, ptr->offset, ptr->shift, tempBoolValue);

	return JS_INVALID_REFERENCE;
}

fake_ptr::fake_ptr(void* pointer, int offset, int shift)
{
	this->pointer = pointer;
	this->offset = offset;
	this->shift = shift;
}

Container::Container()
{
}

Container::~Container()
{
	while (!pointers.empty())
	{
		delete pointers.front();
		pointers.pop();
	}
}

fake_ptr* Container::create_pointer(void* base, int offset, int shift)
{
	auto pointer = new fake_ptr(base, offset, shift);
	pointers.push(pointer);
	return pointer;
}
<|endoftext|>


<commit_before>#include "dataload_csv.h"
#include <QTextStream>
#include <QFile>
#include <QMessageBox>
#include <QDebug>
#include <QSettings>
#include <QProgressDialog>
#include <QDateTime>
#include <QInputDialog>

const int TIME_INDEX_NOT_DEFINED = -2;
const int TIME_INDEX_GENERATED = -1;

QStringList SpliLine(const QString& line, QChar separator)
{
  QStringList parts;
  bool inside_quotes = false;
  bool quoted_word = false;
  int start_pos = 0;

  int quote_start = 0;
  int quote_end = 0;

  for( int pos = 0; pos < line.size(); pos++ )
  {
    if( line[pos] == '"' )
    {
      if( inside_quotes )
      {
        quoted_word = true;
        quote_end = pos-1;
      }
      else{
        quote_start = pos+1;
      }
      inside_quotes = !inside_quotes;
    }

    bool part_completed = false;
    bool add_empty = false;
    int end_pos = pos;

    if( (!inside_quotes && line[pos] == separator) )
    {
      part_completed = true;
    }
    if( pos+1 == line.size() )
    {
      part_completed = true;
      end_pos = pos+1;
      // special case
      if( line[pos] == separator )
      {
        end_pos = pos;
        add_empty = true;
      }
    }

    if( part_completed )
    {
      QString part;
      if ( quoted_word ) {
        part = line.mid(quote_start, quote_end - quote_start + 1 );
      }
      else{
        part = line.mid(start_pos, end_pos - start_pos );
      }

      parts.push_back( part.trimmed() );
      start_pos = pos+1;
      quoted_word = false;
      inside_quotes = false;
    }
    if( add_empty )
    {
      parts.push_back( QString() );
    }
  }

  return parts;
}

DataLoadCSV::DataLoadCSV()
{
  _extensions.push_back("csv");
  _separator = ',';
  // setup the dialog

  _dialog = new QDialog();
  _ui = new Ui::DialogCSV();
  _ui->setupUi(_dialog);

  connect ( _ui->radioButtonSelect, &QRadioButton::toggled,
          this, [this]( bool checked ) {
            _ui->listWidgetSeries->setEnabled( checked );
            auto selected = _ui->listWidgetSeries->selectionModel()->selectedIndexes();
            bool box_enabled = !checked || selected.size() == 1;
            _ui->buttonBox->setEnabled( box_enabled );
          });
  connect ( _ui->listWidgetSeries, &QListWidget::itemSelectionChanged,
          this, [this]() {
            auto selected = _ui->listWidgetSeries->selectionModel()->selectedIndexes();
            bool box_enabled = _ui->radioButtonIndex->isChecked() || selected.size() == 1;
            _ui->buttonBox->setEnabled( box_enabled );
          });

  connect ( _ui->listWidgetSeries, &QListWidget::itemDoubleClicked,
          this, [this]() {
            emit _ui->buttonBox->accepted();
          });

  connect(_ui->checkBoxDateFormat, &QCheckBox::toggled,
          this,  [this](bool checked) {
          _ui->lineEditDateFormat->setEnabled( checked );
        });

  _ui->splitter->setStretchFactor(0, 1);
  _ui->splitter->setStretchFactor(1, 2);
}

DataLoadCSV::~DataLoadCSV()
{
  delete _ui;
  delete _dialog;
}

const std::vector<const char*>& DataLoadCSV::compatibleFileExtensions() const
{
  return _extensions;
}

void DataLoadCSV::parseHeader(QFile& file,
                              std::vector<std::string>* column_names )
{
  file.open(QFile::ReadOnly);

  column_names->clear();
  _ui->listWidgetSeries->clear();

  QTextStream inA(&file);
  // The first line should contain the header. If it contains a number, we will
  // apply a name ourselves
  QString first_line = inA.readLine();

  QString preview_lines = first_line + "\n";

  QStringList firstline_items = SpliLine( first_line, _separator );

  int is_number_count = 0;

  // check if all the elements in first row are numbers
  for (int i = 0; i < firstline_items.size(); i++)
  {
    bool isNum;
    firstline_items[i].trimmed().toDouble(&isNum);
    if( isNum )
    {
      is_number_count++;
    }
  }

  if( is_number_count == firstline_items.size() )
  {
    for (int i = 0; i < firstline_items.size(); i++)
    {
      auto field_name = QString("_Column_%1").arg(i);
      _ui->listWidgetSeries->addItem( field_name );
      column_names->push_back(field_name.toStdString());
    }
  }
  else
  {
    for (int i = 0; i < firstline_items.size(); i++)
    {
      // remove annoying prefix
      QString field_name(firstline_items[i].trimmed());

      if (field_name.isEmpty())
      {
        field_name = QString("_Column_%1").arg(i);
      }
      _ui->listWidgetSeries->addItem( field_name );
      column_names->push_back(field_name.toStdString());
    }
  }

  int linecount = 1;
  while (!inA.atEnd())
  {
    auto line = inA.readLine();
    if( linecount++ < 100 )
    {
      preview_lines += line + "\n";
    }
    else{
      break;
    }
  }
  _ui->rawText->setPlainText(preview_lines);

  file.close();
}

int DataLoadCSV::launchDialog(QFile &file, std::vector<std::string> *column_names)
{
  column_names->clear();

  QSettings settings;
  _dialog->restoreGeometry(settings.value("DataLoadCSV.geometry").toByteArray());

  _ui->radioButtonIndex->setChecked( settings.value("DataLoadCSV.useIndex", false).toBool() );
  _ui->checkBoxDateFormat->setChecked( settings.value("DataLoadCSV.useDateFormat", false).toBool() );
  _ui->lineEditDateFormat->setText( settings.value("DataLoadCSV.dateFormat","yyyy-MM-dd hh:mm:ss").toString() );

  // suggest separator
  {
    file.open(QFile::ReadOnly);
    QTextStream in(&file);

    QString first_line = in.readLine();
    int comma_count = first_line.count(QLatin1Char(','));
    int semicolon_count = first_line.count(QLatin1Char(';'));
    int space_count = first_line.count(QLatin1Char(' '));

    if( comma_count > 3 && comma_count > semicolon_count )
    {
      _ui->comboBox->setCurrentIndex( 0 );
      _separator = ',';
    }
    if( semicolon_count > 3 && semicolon_count > comma_count )
    {
      _ui->comboBox->setCurrentIndex( 1 );
      _separator = ';';
    }
    if( space_count > 3 && comma_count == 0 && semicolon_count == 0 )
    {
      _ui->comboBox->setCurrentIndex( 2 );
      _separator = ' ';
    }
    file.close();
  }

  // temporaryy connection
  std::unique_ptr<QObject> pcontext (new QObject);
  QObject *context = pcontext.get();
  QObject::connect( _ui->comboBox, qOverload<int>( &QComboBox::currentIndexChanged ),
                    context, [&](int index)
  {
    switch( index )
    {
    case 0: _separator = ','; break;
    case 1: _separator = ';'; break;
    case 2: _separator = ' '; break;
    }
    parseHeader( file, column_names );
  });

  // parse the header once and launch the dialog
  parseHeader(file, column_names);

  int res = _dialog->exec();

  settings.setValue("DataLoadCSV.geometry", _dialog->saveGeometry());
  settings.setValue("DataLoadCSV.useIndex", _ui->radioButtonIndex->isChecked() );
  settings.setValue("DataLoadCSV.useDateFormat", _ui->checkBoxDateFormat->isChecked() );
  settings.setValue("DataLoadCSV.dateFormat", _ui->lineEditDateFormat->text() );

  if (res == QDialog::Rejected)
  {
    return TIME_INDEX_NOT_DEFINED;
  }

  if( _ui->radioButtonIndex->isChecked() )
  {
    return TIME_INDEX_GENERATED;
  }

  QModelIndexList indexes = _ui->listWidgetSeries->selectionModel()->selectedIndexes();
  if( indexes.size() == 1)
  {
    return indexes.front().row();
  }

  return TIME_INDEX_NOT_DEFINED;
}

bool DataLoadCSV::readDataFromFile(FileLoadInfo* info, PlotDataMapRef& plot_data)
{
  bool use_provided_configuration = false;
  _default_time_axis.clear();

  if (info->plugin_config.hasChildNodes())
  {
    use_provided_configuration = true;
    xmlLoadState(info->plugin_config.firstChildElement());
  }

  QFile file( info->filename);
  std::vector<std::string> column_names;

  int time_index = TIME_INDEX_NOT_DEFINED;

  if( !use_provided_configuration )
  {
    time_index = launchDialog( file, &column_names );
  }
  else
  {
    parseHeader(file, &column_names );
    if (_default_time_axis == "__TIME_INDEX_GENERATED__")
    {
      time_index = TIME_INDEX_GENERATED;
    }
    else{
      for( size_t i=0; i<column_names.size(); i++ )
      {
        if ( column_names[i] == _default_time_axis )
        {
          time_index = i;
          break;
        }
      }
    }
  }

  if( time_index == TIME_INDEX_NOT_DEFINED )
  {
    return false;
  }

  //-----------------------------------
  bool interrupted = false;
  int linecount = 0;

  // count the number of lines first
  int tot_lines = 0;
  {
    file.open(QFile::ReadOnly);
    QTextStream in(&file);
    while (!in.atEnd())
    {
      in.readLine();
      tot_lines++;
    }
    file.close();
  }

  QProgressDialog progress_dialog;
  progress_dialog.setLabelText("Loading... please wait");
  progress_dialog.setWindowModality(Qt::ApplicationModal);
  progress_dialog.setRange(0, tot_lines - 1);
  progress_dialog.setAutoClose(true);
  progress_dialog.setAutoReset(true);
  progress_dialog.show();

  //---- build plots_vector from header  ------

  std::vector<PlotData*> plots_vector;
  for (unsigned i = 0; i < column_names.size(); i++)
  {
    const std::string& field_name = (column_names[i]);
    auto it = plot_data.addNumeric(field_name);
    plots_vector.push_back(&(it->second));
  }

  //-----------------
  double prev_time = -std::numeric_limits<double>::max();
  bool parse_date_format = _ui->checkBoxDateFormat->isChecked();
  QString format_string = _ui->lineEditDateFormat->text();

  auto ParseNumber = [&](QString str, bool& is_number)
  {
    double val = str.trimmed().toDouble(&is_number);
    if( !is_number && parse_date_format && !format_string.isEmpty() ) {
      QDateTime ts = QDateTime::fromString(str, format_string);
      is_number = ts.isValid();
      if( is_number )
      {
         val = ts.toMSecsSinceEpoch()/1000.0;
      }
    }
    return val;
  };

  file.open(QFile::ReadOnly);
  QTextStream in(&file);
  // remove first line (header)
  in.readLine();

  while (!in.atEnd())
  {
    QString line = in.readLine();
    QStringList string_items = SpliLine( line, _separator );

    if (string_items.size() != column_names.size())
    {
      auto err_msg = QString("The number of values at line %1 is %2,\n"
                             "but the expected number of columns is %3.\n"
                             "Aborting...")
          .arg(linecount+1)
          .arg(string_items.size())
          .arg(column_names.size());

      QMessageBox::warning(nullptr, "Error reading file", err_msg );
      return false;
    }

    double t = linecount;

    if (time_index >= 0)
    {
      bool is_number = false;
      QString str = string_items[time_index];
      t = ParseNumber ( str, is_number );

      if (!is_number)
      {
          QMessageBox::StandardButton reply;
          reply = QMessageBox::warning(nullptr, tr("Error reading file"),
                  tr("Couldn't parse timestamp with string \"%1\" . Aborting.\n").arg(str) );
          return false;
      }

      if (prev_time > t)
      {
        QMessageBox::StandardButton reply;
        reply = QMessageBox::warning(nullptr, tr("Error reading file"),
                                     tr("Selected time in not strictly monotonic. "
                                        "Loading will be aborted\n"));
        return false;
      }

      prev_time = t;
    }

    for (unsigned i = 0; i < string_items.size(); i++)
    {
      bool is_number = false;
      double y = ParseNumber( string_items[i], is_number);
      if (is_number)
      {
        PlotData::Point point(t, y);
        plots_vector[i]->pushBack(point);
      }
    }

    if (linecount++ % 100 == 0)
    {
      progress_dialog.setValue(linecount);
      QApplication::processEvents();
      if (progress_dialog.wasCanceled())
      {
        interrupted = true;
        break;
      }
    }
  }

  if (interrupted)
  {
    progress_dialog.cancel();
    plot_data.clear();
  }

  if( time_index >= 0){
    _default_time_axis = column_names[ time_index ];
  }
  return true;
}

bool DataLoadCSV::xmlSaveState(QDomDocument& doc, QDomElement& parent_element) const
{
  QDomElement elem = doc.createElement("default");
  elem.setAttribute("time_axis", _default_time_axis.c_str());
  QString date_format;
  if( _ui->checkBoxDateFormat->isChecked() )
  {
    elem.setAttribute("date_format", _ui->lineEditDateFormat->text() );
  }

  parent_element.appendChild(elem);
  return true;
}

bool DataLoadCSV::xmlLoadState(const QDomElement& parent_element)
{
  QDomElement elem = parent_element.firstChildElement("default");
  if (!elem.isNull())
  {
    if (elem.hasAttribute("time_axis"))
    {
      _default_time_axis = elem.attribute("time_axis").toStdString();
    }
    if (elem.hasAttribute("date_format"))
    {
      _ui->checkBoxDateFormat->setChecked( true );
      _ui->lineEditDateFormat->setText( elem.attribute("date_format") );
    }
  }
  return true;
}
<commit_msg>add string visualization to CSV<commit_after>#include "dataload_csv.h"
#include <QTextStream>
#include <QFile>
#include <QMessageBox>
#include <QDebug>
#include <QSettings>
#include <QProgressDialog>
#include <QDateTime>
#include <QInputDialog>

const int TIME_INDEX_NOT_DEFINED = -2;
const int TIME_INDEX_GENERATED = -1;

QStringList SplitLine(const QString& line, QChar separator)
{
  QStringList parts;
  bool inside_quotes = false;
  bool quoted_word = false;
  int start_pos = 0;

  int quote_start = 0;
  int quote_end = 0;

  for( int pos = 0; pos < line.size(); pos++ )
  {
    if( line[pos] == '"' )
    {
      if( inside_quotes )
      {
        quoted_word = true;
        quote_end = pos-1;
      }
      else{
        quote_start = pos+1;
      }
      inside_quotes = !inside_quotes;
    }

    bool part_completed = false;
    bool add_empty = false;
    int end_pos = pos;

    if( (!inside_quotes && line[pos] == separator) )
    {
      part_completed = true;
    }
    if( pos+1 == line.size() )
    {
      part_completed = true;
      end_pos = pos+1;
      // special case
      if( line[pos] == separator )
      {
        end_pos = pos;
        add_empty = true;
      }
    }

    if( part_completed )
    {
      QString part;
      if ( quoted_word ) {
        part = line.mid(quote_start, quote_end - quote_start + 1 );
      }
      else{
        part = line.mid(start_pos, end_pos - start_pos );
      }

      parts.push_back( part.trimmed() );
      start_pos = pos+1;
      quoted_word = false;
      inside_quotes = false;
    }
    if( add_empty )
    {
      parts.push_back( QString() );
    }
  }

  return parts;
}

DataLoadCSV::DataLoadCSV()
{
  _extensions.push_back("csv");
  _separator = ',';
  // setup the dialog

  _dialog = new QDialog();
  _ui = new Ui::DialogCSV();
  _ui->setupUi(_dialog);

  connect ( _ui->radioButtonSelect, &QRadioButton::toggled,
          this, [this]( bool checked ) {
            _ui->listWidgetSeries->setEnabled( checked );
            auto selected = _ui->listWidgetSeries->selectionModel()->selectedIndexes();
            bool box_enabled = !checked || selected.size() == 1;
            _ui->buttonBox->setEnabled( box_enabled );
          });
  connect ( _ui->listWidgetSeries, &QListWidget::itemSelectionChanged,
          this, [this]() {
            auto selected = _ui->listWidgetSeries->selectionModel()->selectedIndexes();
            bool box_enabled = _ui->radioButtonIndex->isChecked() || selected.size() == 1;
            _ui->buttonBox->setEnabled( box_enabled );
          });

  connect ( _ui->listWidgetSeries, &QListWidget::itemDoubleClicked,
          this, [this]() {
            emit _ui->buttonBox->accepted();
          });

  connect(_ui->checkBoxDateFormat, &QCheckBox::toggled,
          this,  [this](bool checked) {
          _ui->lineEditDateFormat->setEnabled( checked );
        });

  _ui->splitter->setStretchFactor(0, 1);
  _ui->splitter->setStretchFactor(1, 2);
}

DataLoadCSV::~DataLoadCSV()
{
  delete _ui;
  delete _dialog;
}

const std::vector<const char*>& DataLoadCSV::compatibleFileExtensions() const
{
  return _extensions;
}

void DataLoadCSV::parseHeader(QFile& file,
                              std::vector<std::string>* column_names )
{
  file.open(QFile::ReadOnly);

  column_names->clear();
  _ui->listWidgetSeries->clear();

  QTextStream inA(&file);
  // The first line should contain the header. If it contains a number, we will
  // apply a name ourselves
  QString first_line = inA.readLine();

  QString preview_lines = first_line + "\n";

  QStringList firstline_items = SplitLine( first_line, _separator );

  int is_number_count = 0;

  // check if all the elements in first row are numbers
  for (int i = 0; i < firstline_items.size(); i++)
  {
    bool isNum;
    firstline_items[i].trimmed().toDouble(&isNum);
    if( isNum )
    {
      is_number_count++;
    }
  }

  if( is_number_count == firstline_items.size() )
  {
    for (int i = 0; i < firstline_items.size(); i++)
    {
      auto field_name = QString("_Column_%1").arg(i);
      _ui->listWidgetSeries->addItem( field_name );
      column_names->push_back(field_name.toStdString());
    }
  }
  else
  {
    for (int i = 0; i < firstline_items.size(); i++)
    {
      // remove annoying prefix
      QString field_name(firstline_items[i].trimmed());

      if (field_name.isEmpty())
      {
        field_name = QString("_Column_%1").arg(i);
      }
      _ui->listWidgetSeries->addItem( field_name );
      column_names->push_back(field_name.toStdString());
    }
  }

  int linecount = 1;
  while (!inA.atEnd())
  {
    auto line = inA.readLine();
    if( linecount++ < 100 )
    {
      preview_lines += line + "\n";
    }
    else{
      break;
    }
  }
  _ui->rawText->setPlainText(preview_lines);

  file.close();
}

int DataLoadCSV::launchDialog(QFile &file, std::vector<std::string> *column_names)
{
  column_names->clear();

  QSettings settings;
  _dialog->restoreGeometry(settings.value("DataLoadCSV.geometry").toByteArray());

  _ui->radioButtonIndex->setChecked( settings.value("DataLoadCSV.useIndex", false).toBool() );
  _ui->checkBoxDateFormat->setChecked( settings.value("DataLoadCSV.useDateFormat", false).toBool() );
  _ui->lineEditDateFormat->setText( settings.value("DataLoadCSV.dateFormat","yyyy-MM-dd hh:mm:ss").toString() );

  // suggest separator
  {
    file.open(QFile::ReadOnly);
    QTextStream in(&file);

    QString first_line = in.readLine();
    int comma_count = first_line.count(QLatin1Char(','));
    int semicolon_count = first_line.count(QLatin1Char(';'));
    int space_count = first_line.count(QLatin1Char(' '));

    if( comma_count > 3 && comma_count > semicolon_count )
    {
      _ui->comboBox->setCurrentIndex( 0 );
      _separator = ',';
    }
    if( semicolon_count > 3 && semicolon_count > comma_count )
    {
      _ui->comboBox->setCurrentIndex( 1 );
      _separator = ';';
    }
    if( space_count > 3 && comma_count == 0 && semicolon_count == 0 )
    {
      _ui->comboBox->setCurrentIndex( 2 );
      _separator = ' ';
    }
    file.close();
  }

  // temporaryy connection
  std::unique_ptr<QObject> pcontext (new QObject);
  QObject *context = pcontext.get();
  QObject::connect( _ui->comboBox, qOverload<int>( &QComboBox::currentIndexChanged ),
                    context, [&](int index)
  {
    switch( index )
    {
    case 0: _separator = ','; break;
    case 1: _separator = ';'; break;
    case 2: _separator = ' '; break;
    }
    parseHeader( file, column_names );
  });

  // parse the header once and launch the dialog
  parseHeader(file, column_names);

  int res = _dialog->exec();

  settings.setValue("DataLoadCSV.geometry", _dialog->saveGeometry());
  settings.setValue("DataLoadCSV.useIndex", _ui->radioButtonIndex->isChecked() );
  settings.setValue("DataLoadCSV.useDateFormat", _ui->checkBoxDateFormat->isChecked() );
  settings.setValue("DataLoadCSV.dateFormat", _ui->lineEditDateFormat->text() );

  if (res == QDialog::Rejected)
  {
    return TIME_INDEX_NOT_DEFINED;
  }

  if( _ui->radioButtonIndex->isChecked() )
  {
    return TIME_INDEX_GENERATED;
  }

  QModelIndexList indexes = _ui->listWidgetSeries->selectionModel()->selectedIndexes();
  if( indexes.size() == 1)
  {
    return indexes.front().row();
  }

  return TIME_INDEX_NOT_DEFINED;
}

bool DataLoadCSV::readDataFromFile(FileLoadInfo* info, PlotDataMapRef& plot_data)
{
  bool use_provided_configuration = false;
  _default_time_axis.clear();

  if (info->plugin_config.hasChildNodes())
  {
    use_provided_configuration = true;
    xmlLoadState(info->plugin_config.firstChildElement());
  }

  QFile file( info->filename);
  std::vector<std::string> column_names;

  int time_index = TIME_INDEX_NOT_DEFINED;

  if( !use_provided_configuration )
  {
    time_index = launchDialog( file, &column_names );
  }
  else
  {
    parseHeader(file, &column_names );
    if (_default_time_axis == "__TIME_INDEX_GENERATED__")
    {
      time_index = TIME_INDEX_GENERATED;
    }
    else{
      for( size_t i=0; i<column_names.size(); i++ )
      {
        if ( column_names[i] == _default_time_axis )
        {
          time_index = i;
          break;
        }
      }
    }
  }

  if( time_index == TIME_INDEX_NOT_DEFINED )
  {
    return false;
  }

  //-----------------------------------
  bool interrupted = false;
  int linecount = 0;

  // count the number of lines first
  int tot_lines = 0;
  {
    file.open(QFile::ReadOnly);
    QTextStream in(&file);
    while (!in.atEnd())
    {
      in.readLine();
      tot_lines++;
    }
    file.close();
  }

  QProgressDialog progress_dialog;
  progress_dialog.setLabelText("Loading... please wait");
  progress_dialog.setWindowModality(Qt::ApplicationModal);
  progress_dialog.setRange(0, tot_lines - 1);
  progress_dialog.setAutoClose(true);
  progress_dialog.setAutoReset(true);
  progress_dialog.show();

  //---- build plots_vector from header  ------

  std::vector<PlotData*> plots_vector;
  std::vector<StringSeries*> string_vector;

  for (unsigned i = 0; i < column_names.size(); i++)
  {
    const std::string& field_name = (column_names[i]);
    auto num_it = plot_data.addNumeric(field_name);
    plots_vector.push_back(&(num_it->second));

    auto str_it = plot_data.addStringSeries(field_name);
    string_vector.push_back(&(str_it->second));
  }

  //-----------------
  double prev_time = -std::numeric_limits<double>::max();
  bool parse_date_format = _ui->checkBoxDateFormat->isChecked();
  QString format_string = _ui->lineEditDateFormat->text();

  auto ParseNumber = [&](QString str, bool& is_number)
  {
    double val = str.trimmed().toDouble(&is_number);
    if( !is_number && parse_date_format && !format_string.isEmpty() ) {
      QDateTime ts = QDateTime::fromString(str, format_string);
      is_number = ts.isValid();
      if( is_number )
      {
         val = ts.toMSecsSinceEpoch()/1000.0;
      }
    }
    return val;
  };

  file.open(QFile::ReadOnly);
  QTextStream in(&file);
  // remove first line (header)
  in.readLine();

  while (!in.atEnd())
  {
    QString line = in.readLine();
    QStringList string_items = SplitLine( line, _separator );

    if (string_items.size() != column_names.size())
    {
      auto err_msg = QString("The number of values at line %1 is %2,\n"
                             "but the expected number of columns is %3.\n"
                             "Aborting...")
          .arg(linecount+1)
          .arg(string_items.size())
          .arg(column_names.size());

      QMessageBox::warning(nullptr, "Error reading file", err_msg );
      return false;
    }

    double t = linecount;

    if (time_index >= 0)
    {
      bool is_number = false;
      QString str = string_items[time_index];
      t = ParseNumber ( str, is_number );

      if (!is_number)
      {
          QMessageBox::StandardButton reply;
          reply = QMessageBox::warning(nullptr, tr("Error reading file"),
                  tr("Couldn't parse timestamp with string \"%1\" . Aborting.\n").arg(str) );
          return false;
      }

      if (prev_time > t)
      {
        QMessageBox::StandardButton reply;
        reply = QMessageBox::warning(nullptr, tr("Error reading file"),
                                     tr("Selected time in not strictly monotonic. "
                                        "Loading will be aborted\n"));
        return false;
      }

      prev_time = t;
    }

    for (unsigned i = 0; i < string_items.size(); i++)
    {
      bool is_number = false;
      const auto& str = string_items[i];
      double y = ParseNumber( str, is_number);
      if (is_number)
      {
        plots_vector[i]->pushBack( {t, y} );
      }
      else{
        string_vector[i]->pushBack( {t, str.toStdString()} );
      }
    }

    if (linecount++ % 100 == 0)
    {
      progress_dialog.setValue(linecount);
      QApplication::processEvents();
      if (progress_dialog.wasCanceled())
      {
        interrupted = true;
        break;
      }
    }
  }

  if (interrupted)
  {
    progress_dialog.cancel();
    plot_data.clear();
  }

  if( time_index >= 0){
    _default_time_axis = column_names[ time_index ];
  }

  // cleanups
  for (unsigned i = 0; i < column_names.size(); i++)
  {
    const auto& name = column_names[i];
    bool is_numeric = true;
    if( plots_vector[i]->size() == 0 && string_vector[i]->size() > 0 )
    {
      is_numeric = false;
    }
    if( is_numeric )
    {
      plot_data.strings.erase( plot_data.strings.find(name) );
    }
    else{
      plot_data.numeric.erase( plot_data.numeric.find(name) );
    }
  }
  return true;
}

bool DataLoadCSV::xmlSaveState(QDomDocument& doc, QDomElement& parent_element) const
{
  QDomElement elem = doc.createElement("default");
  elem.setAttribute("time_axis", _default_time_axis.c_str());
  QString date_format;
  if( _ui->checkBoxDateFormat->isChecked() )
  {
    elem.setAttribute("date_format", _ui->lineEditDateFormat->text() );
  }

  parent_element.appendChild(elem);
  return true;
}

bool DataLoadCSV::xmlLoadState(const QDomElement& parent_element)
{
  QDomElement elem = parent_element.firstChildElement("default");
  if (!elem.isNull())
  {
    if (elem.hasAttribute("time_axis"))
    {
      _default_time_axis = elem.attribute("time_axis").toStdString();
    }
    if (elem.hasAttribute("date_format"))
    {
      _ui->checkBoxDateFormat->setChecked( true );
      _ui->lineEditDateFormat->setText( elem.attribute("date_format") );
    }
  }
  return true;
}
<|endoftext|>


<commit_before>#include "building_morph.h"
#include <hook_tools.h>
#include <SCBW/api.h>
#include <algorithm>

namespace {

//-------- isMorphingBuilding() --------//

void __declspec(naked) isMorphingBuildingWrapper() {
  static CUnit *unit;
  __asm {
    PUSHAD
    MOV EBP, ESP
    MOV unit, EAX
  }

  if (hooks::isMorphingBuildingHook(unit)) {
    __asm {
      POPAD
      MOV EAX, 1
      RETN
    }
  }
  else {
    __asm {
      POPAD
      XOR EAX, EAX  ;//Set EAX to 0
      RETN
    }
  }
}

//-------- UnitStatAct_ProgressBarIconText --------//

const u32 Hook_IsMorphedBuilding_ConsoleProgressBarTextBack = 0x00426B74;
void __declspec(naked) isMorphedBuildingWrapper_ConsoleProgressBarText() {
  static const CUnit *unit;
  static u16 tblErrorMessage;
  __asm {
    PUSHAD
    MOV EBP, ESP
  }

  unit = *activePortraitUnit;
  if (unit->status & UnitStatus::GroundedBuilding && unit->isFrozen())
  {
    tblErrorMessage = 828;    //<6>Disabled<0>
  }
  else {
    u16 unitId = unit->buildQueue[unit->buildQueueSlot];
    if (unit->status & UnitStatus::Completed
        || !hooks::isMorphedBuildingHook(unitId))
      unitId = unit->id;

    if (unitId == UnitId::gateway || unitId == UnitId::stargate)
      tblErrorMessage = 771;  //Morphing<0>, Building<0>, Opening Warp Gate<0>
    else
      tblErrorMessage = 774;  //Morphing<0>, Building<0>, Building<0>
  }

  __asm {
    POPAD
    MOV ECX, unit
    MOVZX EDX, tblErrorMessage
    JMP Hook_IsMorphedBuilding_ConsoleProgressBarTextBack
  }
}

//-------- AI_MorphBehavior() --------//

const u32 Hook_AI_MorphBehavior_IsMorphedBuildingYes = 0x0043508B;
void __declspec(naked) isMorphedBuildingWrapper_AI_MorphBehavior() {
  static u16 buildUnitId;
  __asm {
    PUSHAD
    MOV EBP, ESP
    MOV buildUnitId, DI
  }

  if (hooks::isMorphedBuildingHook(buildUnitId)) {
    __asm {
      POPAD
      JMP Hook_AI_MorphBehavior_IsMorphedBuildingYes
    }
  }
  else {  //Code at 0x00435122
    __asm {
      POPAD
      POP EDI
      XOR EAX, EAX
      POP ESI
      POP EBP
      RETN 8
    }
  }
}

//-------- statdata_UnitWireframeUpdate() --------//

const u32 Hook_IsMorphedBuilding_ConsoleWireframeUpdateBack = 0x00456955;
void __declspec(naked) isMorphedBuildingWrapper_ConsoleWireFrameUpdate() {
  static CUnit *unit;
  static u16 buildUnitId;
  __asm {
    PUSHAD
    MOV EBP, ESP
    MOV unit, ESI
  }

  buildUnitId = unit->buildQueue[unit->buildQueueSlot];
  if (unit->status & UnitStatus::Completed
      || !hooks::isMorphedBuildingHook(buildUnitId))
    buildUnitId = unit->id;

  __asm {
    POPAD
    MOVZX ECX, buildUnitId
    JMP Hook_IsMorphedBuilding_ConsoleWireframeUpdateBack
  }
}

//-------- orders_ZergBuildSelf() --------//

const u32 Hook_isMorphedBuilding_ZergBuildSelf_GetTypeBack = 0x0045D53F;
void __declspec(naked) isMorphedBuildingWrapper_ZergBuildSelf_GetType() {
  static CUnit *unit;
  static u16 buildUnitId;
  __asm {
    PUSHAD
    MOV EBP, ESP
    MOV unit, EDI
  }

  buildUnitId = unit->buildQueue[unit->buildQueueSlot];
  if (!hooks::isMorphedBuildingHook(buildUnitId))
    buildUnitId = unit->id;

  __asm {
    POPAD
    MOVZX EBX, buildUnitId
    JMP Hook_isMorphedBuilding_ZergBuildSelf_GetTypeBack
  }
}

void __declspec(naked) isMorphedBuildingWrapper_ZergBuildSelf_Complete() {
  static u16 unitId;
  __asm {
    PUSHAD
    MOV EBP, ESP
    MOV unitId, AX
  }

  if (hooks::isMorphedBuildingHook(unitId)) {
    __asm {
      POPAD
      MOV EAX, 1
      RETN
    }
  }
  else {
    __asm {
      POPAD
      XOR EAX, EAX  ;//Set EAX to 0
      RETN
    }
  }
}

//This was moved out of the naked function to prevent stack corruption bug
void zergBuildSelf_UpdateBuildTimerAndHp(CUnit *unit, u16 unitId) {
  bool isOperationCwalEnabled = scbw::isCheatEnabled(CheatFlags::OperationCwal);
  if (unit->remainingBuildTime) {
    if (isOperationCwalEnabled)
      unit->remainingBuildTime -= std::min<u16>(unit->remainingBuildTime, 16);
    else
      unit->remainingBuildTime--;
  }
  if (!hooks::isMorphedBuildingHook(unitId)) {
    u32 hpGain = unit->buildRepairHPGain;
    if (isOperationCwalEnabled)
      hpGain *= 16;
    unit->setHp(unit->hitPoints + hpGain);
  }
}

void __declspec(naked) isMorphedBuildingWrapper_ZergBuildSelf_SetTimer() {
  static CUnit *unit;
  static u16 unitId;
  __asm {
    PUSHAD
    MOV EBP, ESP
    MOV unit, EDI
    MOV unitId, BX
  }

  zergBuildSelf_UpdateBuildTimerAndHp(unit, unitId);

  __asm {
    POPAD
    POP EDI
    POP ESI
    POP EBX
    RETN
  }
}

//-------- cancelZergBuilding --------//

const u32 Hook_IsMorphedBuilding_CancelZergBuildingContinue = 0x0045DA93;
const u32 Func_CancelZergBuildingRemorph = 0x0045D410;
void __declspec(naked) isMorphedBuildingWrapper_CancelZergBuilding() {
  static CUnit *unit;
  __asm {
    PUSHAD
    MOV EBP, ESP
    MOV unit, EBX
  }

  if (!hooks::isMorphingBuildingHook(unit)) {
    __asm {
      POPAD
      MOV EAX, EBX
      CALL Func_CancelZergBuildingRemorph
      POP EBX
      MOV ESP, EBP
      POP EBP
      RETN 4
    }
  }
  else {
    __asm {
      POPAD
      JMP Hook_IsMorphedBuilding_CancelZergBuildingContinue
    }
  }
}

//-------- AI_GetWaitBuildUnitCount --------//

//Inject @ 0x00433286
const u32 Hook_AI_GetWaitBuildUnitCount_Return = 0x004333D3;
void __declspec(naked) getMorphBuildingTypeCountWrapper_AI_GetWaitBuildUnitCount() {
  static CUnit *unit;
  static u16 unitId;
  static Bool32 ignoreIncomplete;
  static u32 unitCount;

  __asm {
    PUSHAD
    MOV ignoreIncomplete, EDX
    MOV unitId, DI
    MOV unit, ESI
  }

  unitCount = hooks::getMorphBuildingTypeCountHook(unit, unitId, ignoreIncomplete != 0);

  __asm {
    POPAD
    MOV EAX, unitCount
    JMP Hook_AI_GetWaitBuildUnitCount_Return
  }
}

//-------- AI_GetUnitCount --------//

//Inject @ 0x004334D9
const u32 Hook_AI_GetUnitCount_Return = 0x00433588;
void __declspec(naked) getMorphBuildingTypeCountWrapper_AI_GetUnitCount() {
  static CUnit *unit;
  static u16 unitId;
  static u32 unitCount;

  __asm {
    PUSHAD
    MOV unitId, DI
    MOV unit, ESI
    MOV unitCount, EBX
  }

  unitCount += hooks::getMorphBuildingTypeCountHook(unit, unitId, false);

  __asm {
    POPAD
    MOV EBX, unitCount
    JMP Hook_AI_GetUnitCount_Return
  }
}

//-------- AI_ManageBases --------//

//Inject @ 0x00436439
const u32 Hook_AI_ManageBases_Return = 0x004364F7;
void __declspec(naked) getMorphBuildingTypeCountWrapper_AI_ManageBases() {
  static CUnit *unit;
  static u16 unitId;
  static u32 unitCount;

  __asm {
    PUSHAD
    MOV EAX, [EBP-16]
    MOV unitId, AX
    MOV unit, ESI
    MOV unitCount, EBX
  }

  unitCount += hooks::getMorphBuildingTypeCountHook(unit, unitId, false);

  __asm {
    POPAD
    MOV EBX, unitCount
    JMP Hook_AI_ManageBases_Return
  }
}

} //unnamed namespace

namespace hooks {

void injectBuildingMorphHooks() {
  jmpPatch(isMorphingBuildingWrapper, 0x0045CD00);
  jmpPatch(isMorphedBuildingWrapper_ConsoleProgressBarText, 0x00426AA2);
  jmpPatch(isMorphedBuildingWrapper_AI_MorphBehavior,       0x0043505F);
  jmpPatch(isMorphedBuildingWrapper_ConsoleWireFrameUpdate, 0x00456911);
  jmpPatch(isMorphedBuildingWrapper_ZergBuildSelf_GetType,  0x0045D505);
  callPatch(isMorphedBuildingWrapper_ZergBuildSelf_Complete, 0x0045D65D);
  jmpPatch(isMorphedBuildingWrapper_ZergBuildSelf_SetTimer, 0x0045D56C);
  jmpPatch(isMorphedBuildingWrapper_CancelZergBuilding,     0x0045DA4A);

  jmpPatch(getMorphBuildingTypeCountWrapper_AI_GetWaitBuildUnitCount, 0x00433286);
  jmpPatch(getMorphBuildingTypeCountWrapper_AI_GetUnitCount, 0x004334D9);
  jmpPatch(getMorphBuildingTypeCountWrapper_AI_ManageBases, 0x00436439);
}

bool isMorphingBuildingHook(const CUnit *unit) {
  return !(unit->status & UnitStatus::Completed)
    && hooks::isMorphedBuildingHook(unit->buildQueue[unit->buildQueueSlot]);
}

} //hooks
<commit_msg>GPTP: Fixed a bug that caused Zerg buildings to stay there even after being cancelled.<commit_after>#include "building_morph.h"
#include <hook_tools.h>
#include <SCBW/api.h>
#include <algorithm>

namespace {

//-------- isMorphingBuilding() --------//

void __declspec(naked) isMorphingBuildingWrapper() {
  static CUnit *unit;
  __asm {
    PUSHAD
    MOV EBP, ESP
    MOV unit, EAX
  }

  if (hooks::isMorphingBuildingHook(unit)) {
    __asm {
      POPAD
      MOV EAX, 1
      RETN
    }
  }
  else {
    __asm {
      POPAD
      XOR EAX, EAX  ;//Set EAX to 0
      RETN
    }
  }
}

//-------- UnitStatAct_ProgressBarIconText --------//

const u32 Hook_IsMorphedBuilding_ConsoleProgressBarTextBack = 0x00426B74;
void __declspec(naked) isMorphedBuildingWrapper_ConsoleProgressBarText() {
  static const CUnit *unit;
  static u16 tblErrorMessage;
  __asm {
    PUSHAD
    MOV EBP, ESP
  }

  unit = *activePortraitUnit;
  if (unit->status & UnitStatus::GroundedBuilding && unit->isFrozen())
  {
    tblErrorMessage = 828;    //<6>Disabled<0>
  }
  else {
    u16 unitId = unit->buildQueue[unit->buildQueueSlot];
    if (unit->status & UnitStatus::Completed
        || !hooks::isMorphedBuildingHook(unitId))
      unitId = unit->id;

    if (unitId == UnitId::gateway || unitId == UnitId::stargate)
      tblErrorMessage = 771;  //Morphing<0>, Building<0>, Opening Warp Gate<0>
    else
      tblErrorMessage = 774;  //Morphing<0>, Building<0>, Building<0>
  }

  __asm {
    POPAD
    MOV ECX, unit
    MOVZX EDX, tblErrorMessage
    JMP Hook_IsMorphedBuilding_ConsoleProgressBarTextBack
  }
}

//-------- AI_MorphBehavior() --------//

const u32 Hook_AI_MorphBehavior_IsMorphedBuildingYes = 0x0043508B;
void __declspec(naked) isMorphedBuildingWrapper_AI_MorphBehavior() {
  static u16 buildUnitId;
  __asm {
    PUSHAD
    MOV EBP, ESP
    MOV buildUnitId, DI
  }

  if (hooks::isMorphedBuildingHook(buildUnitId)) {
    __asm {
      POPAD
      JMP Hook_AI_MorphBehavior_IsMorphedBuildingYes
    }
  }
  else {  //Code at 0x00435122
    __asm {
      POPAD
      POP EDI
      XOR EAX, EAX
      POP ESI
      POP EBP
      RETN 8
    }
  }
}

//-------- statdata_UnitWireframeUpdate() --------//

const u32 Hook_IsMorphedBuilding_ConsoleWireframeUpdateBack = 0x00456955;
void __declspec(naked) isMorphedBuildingWrapper_ConsoleWireFrameUpdate() {
  static CUnit *unit;
  static u16 buildUnitId;
  __asm {
    PUSHAD
    MOV EBP, ESP
    MOV unit, ESI
  }

  buildUnitId = unit->buildQueue[unit->buildQueueSlot];
  if (unit->status & UnitStatus::Completed
      || !hooks::isMorphedBuildingHook(buildUnitId))
    buildUnitId = unit->id;

  __asm {
    POPAD
    MOVZX ECX, buildUnitId
    JMP Hook_IsMorphedBuilding_ConsoleWireframeUpdateBack
  }
}

//-------- orders_ZergBuildSelf() --------//

const u32 Hook_isMorphedBuilding_ZergBuildSelf_GetTypeBack = 0x0045D53F;
void __declspec(naked) isMorphedBuildingWrapper_ZergBuildSelf_GetType() {
  static CUnit *unit;
  static u16 buildUnitId;
  __asm {
    PUSHAD
    MOV EBP, ESP
    MOV unit, EDI
  }

  buildUnitId = unit->buildQueue[unit->buildQueueSlot];
  if (!hooks::isMorphedBuildingHook(buildUnitId))
    buildUnitId = unit->id;

  __asm {
    POPAD
    MOVZX EBX, buildUnitId
    JMP Hook_isMorphedBuilding_ZergBuildSelf_GetTypeBack
  }
}

void __declspec(naked) isMorphedBuildingWrapper_ZergBuildSelf_Complete() {
  static u16 unitId;
  __asm {
    PUSHAD
    MOV EBP, ESP
    MOV unitId, AX
  }

  if (hooks::isMorphedBuildingHook(unitId)) {
    __asm {
      POPAD
      MOV EAX, 1
      RETN
    }
  }
  else {
    __asm {
      POPAD
      XOR EAX, EAX  ;//Set EAX to 0
      RETN
    }
  }
}

//This was moved out of the naked function to prevent stack corruption bug
void zergBuildSelf_UpdateBuildTimerAndHp(CUnit *unit, u16 unitId) {
  bool isOperationCwalEnabled = scbw::isCheatEnabled(CheatFlags::OperationCwal);
  if (unit->remainingBuildTime) {
    if (isOperationCwalEnabled)
      unit->remainingBuildTime -= std::min<u16>(unit->remainingBuildTime, 16);
    else
      unit->remainingBuildTime--;
  }
  if (!hooks::isMorphedBuildingHook(unitId)) {
    u32 hpGain = unit->buildRepairHPGain;
    if (isOperationCwalEnabled)
      hpGain *= 16;
    unit->setHp(unit->hitPoints + hpGain);
  }
}

void __declspec(naked) isMorphedBuildingWrapper_ZergBuildSelf_SetTimer() {
  static CUnit *unit;
  static u16 unitId;
  __asm {
    PUSHAD
    MOV EBP, ESP
    MOV unit, EDI
    MOV unitId, BX
  }

  zergBuildSelf_UpdateBuildTimerAndHp(unit, unitId);

  __asm {
    POPAD
    POP EDI
    POP ESI
    POP EBX
    RETN
  }
}

//-------- cancelZergBuilding --------//

const u32 Hook_IsMorphedBuilding_CancelZergBuildingContinue = 0x0045DA93;
const u32 Func_CancelZergBuildingRemorph = 0x0045D410;
void __declspec(naked) isMorphedBuildingWrapper_CancelZergBuilding() {
  static CUnit *unit;
  __asm {
    PUSHAD
    MOV EBP, ESP
    MOV unit, EBX
  }

  if (hooks::isMorphingBuildingHook(unit)) {
    __asm {
      POPAD
      MOV EAX, EBX
      CALL Func_CancelZergBuildingRemorph
      POP EBX
      MOV ESP, EBP
      POP EBP
      RETN 4
    }
  }
  else {
    __asm {
      POPAD
      JMP Hook_IsMorphedBuilding_CancelZergBuildingContinue
    }
  }
}

//-------- AI_GetWaitBuildUnitCount --------//

//Inject @ 0x00433286
const u32 Hook_AI_GetWaitBuildUnitCount_Return = 0x004333D3;
void __declspec(naked) getMorphBuildingTypeCountWrapper_AI_GetWaitBuildUnitCount() {
  static CUnit *unit;
  static u16 unitId;
  static Bool32 ignoreIncomplete;
  static u32 unitCount;

  __asm {
    PUSHAD
    MOV ignoreIncomplete, EDX
    MOV unitId, DI
    MOV unit, ESI
  }

  unitCount = hooks::getMorphBuildingTypeCountHook(unit, unitId, ignoreIncomplete != 0);

  __asm {
    POPAD
    MOV EAX, unitCount
    JMP Hook_AI_GetWaitBuildUnitCount_Return
  }
}

//-------- AI_GetUnitCount --------//

//Inject @ 0x004334D9
const u32 Hook_AI_GetUnitCount_Return = 0x00433588;
void __declspec(naked) getMorphBuildingTypeCountWrapper_AI_GetUnitCount() {
  static CUnit *unit;
  static u16 unitId;
  static u32 unitCount;

  __asm {
    PUSHAD
    MOV unitId, DI
    MOV unit, ESI
    MOV unitCount, EBX
  }

  unitCount += hooks::getMorphBuildingTypeCountHook(unit, unitId, false);

  __asm {
    POPAD
    MOV EBX, unitCount
    JMP Hook_AI_GetUnitCount_Return
  }
}

//-------- AI_ManageBases --------//

//Inject @ 0x00436439
const u32 Hook_AI_ManageBases_Return = 0x004364F7;
void __declspec(naked) getMorphBuildingTypeCountWrapper_AI_ManageBases() {
  static CUnit *unit;
  static u16 unitId;
  static u32 unitCount;

  __asm {
    PUSHAD
    MOV EAX, [EBP-16]
    MOV unitId, AX
    MOV unit, ESI
    MOV unitCount, EBX
  }

  unitCount += hooks::getMorphBuildingTypeCountHook(unit, unitId, false);

  __asm {
    POPAD
    MOV EBX, unitCount
    JMP Hook_AI_ManageBases_Return
  }
}

} //unnamed namespace

namespace hooks {

void injectBuildingMorphHooks() {
  jmpPatch(isMorphingBuildingWrapper, 0x0045CD00);
  jmpPatch(isMorphedBuildingWrapper_ConsoleProgressBarText, 0x00426AA2);
  jmpPatch(isMorphedBuildingWrapper_AI_MorphBehavior,       0x0043505F);
  jmpPatch(isMorphedBuildingWrapper_ConsoleWireFrameUpdate, 0x00456911);
  jmpPatch(isMorphedBuildingWrapper_ZergBuildSelf_GetType,  0x0045D505);
  callPatch(isMorphedBuildingWrapper_ZergBuildSelf_Complete, 0x0045D65D);
  jmpPatch(isMorphedBuildingWrapper_ZergBuildSelf_SetTimer, 0x0045D56C);
  jmpPatch(isMorphedBuildingWrapper_CancelZergBuilding,     0x0045DA4A);

  jmpPatch(getMorphBuildingTypeCountWrapper_AI_GetWaitBuildUnitCount, 0x00433286);
  jmpPatch(getMorphBuildingTypeCountWrapper_AI_GetUnitCount, 0x004334D9);
  jmpPatch(getMorphBuildingTypeCountWrapper_AI_ManageBases, 0x00436439);
}

bool isMorphingBuildingHook(const CUnit *unit) {
  return !(unit->status & UnitStatus::Completed)
    && hooks::isMorphedBuildingHook(unit->buildQueue[unit->buildQueueSlot]);
}

} //hooks
<|endoftext|>