diff --git a/CMakeLists.txt b/CMakeLists.txt index 9d40b2c3c..6a929f306 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,7 +7,7 @@ CMAKE_MINIMUM_REQUIRED( VERSION 2.8 ) SET( TOMAHAWK_ORGANIZATION_NAME "Tomahawk" ) SET( TOMAHAWK_ORGANIZATION_DOMAIN "tomahawk-player.org" ) SET( TOMAHAWK_APPLICATION_NAME "Tomahawk" ) -SET( TOMAHAWK_VERSION "0.0.2" ) +SET( TOMAHAWK_VERSION "0.0.3" ) # set paths diff --git a/ChangeLog b/ChangeLog index c3a791448..877b048fa 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,7 +1,25 @@ +Version 0.1.0: + * Watch folders for changes and automatically update your collection. This + is on by default; you can turn it off on the Local Music tab in the + settings dialog. Note that this triggers only on files or folders being + added to or removed from folders; it is not watch individual files as + most OSes can't support enough file watches to handle a normal-sized + music collection. + Version 0.0.3: - * Fix crashes in Twitter authentication. For reals now. + * Fixed an issue which caused duplicate items when rescanning. + * Revert change introduced in 0.0.2 causing Twitter protocol to not try + to reconnect to a peer if it couldn't connect the first time the plugin + was connected. This caused confusing (and for most unwanted) behavior. + * Fix crashes in Twitter authentication. * Properly honor the chosen port number if a static host and port are marked as preferred. + * Don't automatically try to resolve all incoming playback logs. This + speeds up importing sources a lot. + * Faster painting of playlists with lots of unresolved tracks. + * The tomahawk:// protocol handler works on Windows now. + * Fixed launching Tomahawk from Windows installer with admin privileges. + * Prefer local results when results' score is equal. Version 0.0.2: * Don't reconnect to Jabber if the settings dialog is closed successfully diff --git a/admin/win/README.txt b/admin/win/README.txt deleted file mode 100755 index e69de29bb..000000000 diff --git a/admin/win/nsi/RELEASE_NOTES.txt b/admin/win/nsi/RELEASE_NOTES.txt old mode 100755 new mode 100644 diff --git a/admin/win/nsi/installer.ico b/admin/win/nsi/installer.ico old mode 100755 new mode 100644 diff --git a/admin/win/nsi/nsis_processes/bin/Processes.dll b/admin/win/nsi/nsis_processes/bin/Processes.dll new file mode 100755 index 000000000..e532bf8bb Binary files /dev/null and b/admin/win/nsi/nsis_processes/bin/Processes.dll differ diff --git a/admin/win/nsi/nsis_processes/license.rtf b/admin/win/nsi/nsis_processes/license.rtf new file mode 100755 index 000000000..2ce5a58c9 --- /dev/null +++ b/admin/win/nsi/nsis_processes/license.rtf @@ -0,0 +1,35 @@ +{\rtf1\ansi\ansicpg1252\uc1\deff0\stshfdbch0\stshfloch0\stshfhich0\stshfbi0\deflang1033\deflangfe1033{\fonttbl{\f0\froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman;}{\f39\fswiss\fcharset0\fprq2{\*\panose 020b0604030504040204}Verdana;} +{\f172\froman\fcharset238\fprq2 Times New Roman CE;}{\f173\froman\fcharset204\fprq2 Times New Roman Cyr;}{\f175\froman\fcharset161\fprq2 Times New Roman Greek;}{\f176\froman\fcharset162\fprq2 Times New Roman Tur;} +{\f177\froman\fcharset177\fprq2 Times New Roman (Hebrew);}{\f178\froman\fcharset178\fprq2 Times New Roman (Arabic);}{\f179\froman\fcharset186\fprq2 Times New Roman Baltic;}{\f180\froman\fcharset163\fprq2 Times New Roman (Vietnamese);} +{\f562\fswiss\fcharset238\fprq2 Verdana CE;}{\f563\fswiss\fcharset204\fprq2 Verdana Cyr;}{\f565\fswiss\fcharset161\fprq2 Verdana Greek;}{\f566\fswiss\fcharset162\fprq2 Verdana Tur;}{\f569\fswiss\fcharset186\fprq2 Verdana Baltic;} +{\f570\fswiss\fcharset163\fprq2 Verdana (Vietnamese);}}{\colortbl;\red0\green0\blue0;\red0\green0\blue255;\red0\green255\blue255;\red0\green255\blue0;\red255\green0\blue255;\red255\green0\blue0;\red255\green255\blue0;\red255\green255\blue255; +\red0\green0\blue128;\red0\green128\blue128;\red0\green128\blue0;\red128\green0\blue128;\red128\green0\blue0;\red128\green128\blue0;\red128\green128\blue128;\red192\green192\blue192;}{\stylesheet{ +\ql \li0\ri0\widctlpar\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \fs24\lang1033\langfe1033\cgrid\langnp1033\langfenp1033 \snext0 Normal;}{\*\cs10 \additive \ssemihidden Default Paragraph Font;}{\* +\ts11\tsrowd\trftsWidthB3\trpaddl108\trpaddr108\trpaddfl3\trpaddft3\trpaddfb3\trpaddfr3\trcbpat1\trcfpat1\tscellwidthfts0\tsvertalt\tsbrdrt\tsbrdrl\tsbrdrb\tsbrdrr\tsbrdrdgl\tsbrdrdgr\tsbrdrh\tsbrdrv +\ql \li0\ri0\widctlpar\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \fs20\lang1024\langfe1024\cgrid\langnp1024\langfenp1024 \snext11 \ssemihidden Normal Table;}{\*\cs15 \additive \ul\cf2 \sbasedon10 \styrsid7485074 Hyperlink;}} +{\*\latentstyles\lsdstimax156\lsdlockeddef0}{\*\rsidtbl \rsid6712196\rsid7485074\rsid11352300\rsid15940516}{\*\generator Microsoft Word 11.0.5604;}{\info{\title Processes v1}{\author Hardwired}{\operator Hardwired}{\creatim\yr2004\mo12\dy12\hr23\min42} +{\revtim\yr2004\mo12\dy12\hr23\min51}{\version2}{\edmins9}{\nofpages1}{\nofwords80}{\nofchars458}{\nofcharsws537}{\vern24689}}\widowctrl\ftnbj\aenddoc\noxlattoyen\expshrtn\noultrlspc\dntblnsbdb\nospaceforul\formshade\horzdoc\dgmargin\dghspace180 +\dgvspace180\dghorigin1800\dgvorigin1440\dghshow1\dgvshow1 +\jexpand\viewkind1\viewscale100\pgbrdrhead\pgbrdrfoot\splytwnine\ftnlytwnine\htmautsp\nolnhtadjtbl\useltbaln\alntblind\lytcalctblwd\lyttblrtgr\lnbrkrule\nobrkwrptbl\snaptogridincell\allowfieldendsel\wrppunct +\asianbrkrule\rsidroot7485074\newtblstyruls\nogrowautofit \fet0\sectd \linex0\endnhere\sectlinegrid360\sectdefaultcl\sftnbj {\*\pnseclvl1\pnucrm\pnstart1\pnindent720\pnhang {\pntxta .}}{\*\pnseclvl2\pnucltr\pnstart1\pnindent720\pnhang {\pntxta .}} +{\*\pnseclvl3\pndec\pnstart1\pnindent720\pnhang {\pntxta .}}{\*\pnseclvl4\pnlcltr\pnstart1\pnindent720\pnhang {\pntxta )}}{\*\pnseclvl5\pndec\pnstart1\pnindent720\pnhang {\pntxtb (}{\pntxta )}}{\*\pnseclvl6\pnlcltr\pnstart1\pnindent720\pnhang {\pntxtb (} +{\pntxta )}}{\*\pnseclvl7\pnlcrm\pnstart1\pnindent720\pnhang {\pntxtb (}{\pntxta )}}{\*\pnseclvl8\pnlcltr\pnstart1\pnindent720\pnhang {\pntxtb (}{\pntxta )}}{\*\pnseclvl9\pnlcrm\pnstart1\pnindent720\pnhang {\pntxtb (}{\pntxta )}}\pard\plain +\qj \li0\ri0\widctlpar\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0\pararsid7485074 \fs24\lang1033\langfe1033\cgrid\langnp1033\langfenp1033 {\b\f39\insrsid7485074\charrsid7485074 Processes v1.0}{\f39\insrsid7485074\charrsid7485074 .0.1 +\par }{\f39\fs20\insrsid7485074 +\par }\pard \qj \li0\ri0\widctlpar\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0\pararsid15940516 {\f39\fs20\insrsid15940516 This software binaries and source-code are free for any kind of use, including commercial use. }{ +\f39\fs20\insrsid7485074\charrsid7485074 There is no restriction and no guaranty for using}{\f39\fs20\insrsid7485074\charrsid7485074 t}{\f39\fs20\insrsid7485074\charrsid7485074 his software}{\f39\fs20\insrsid7485074\charrsid7485074 and/or it +s source-code. }{\f39\fs20\insrsid15940516 +\par I}{\f39\fs20\insrsid7485074\charrsid7485074 f you use the plug}{\f39\fs20\insrsid7485074\charrsid7485074 -}{\f39\fs20\insrsid7485074\charrsid7485074 in }{\f39\fs20\insrsid7485074\charrsid7485074 and/}{\f39\fs20\insrsid7485074\charrsid7485074 or it}{ +\f39\fs20\insrsid7485074\charrsid7485074 s}{\f39\fs20\insrsid7485074\charrsid7485074 source-code, I would }{\f39\fs20\insrsid7485074\charrsid7485074 appreciate }{\f39\fs20\insrsid7485074\charrsid7485074 if my name is mentioned.}{ +\f39\fs20\insrsid7485074\charrsid7485074 +\par }\pard \qj \li0\ri0\widctlpar\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0\pararsid7485074 {\f39\fs20\insrsid7485074\charrsid7485074 +\par }{\b\f39\fs20\insrsid7485074\charrsid7485074 Andrei Ciubotaru [Hardwired] +\par }{\f39\fs20\insrsid7485074\charrsid7485074 Lead Developer ICode&Ideas SRL (}{\field\flddirty{\*\fldinst {\f39\fs20\insrsid7485074\charrsid7485074 HYPERLINK "http://www.icode.ro/" }{\f39\fs20\insrsid7485074\charrsid7485074 {\*\datafield +00d0c9ea79f9bace118c8200aa004ba90b02000000170000001500000068007400740070003a002f002f007700770077002e00690063006f00640065002e0072006f002f000000e0c9ea79f9bace118c8200aa004ba90b2a00000068007400740070003a002f002f007700770077002e00690063006f00640065002e007200 +6f002f000000}}}{\fldrslt {\cs15\f39\fs20\ul\cf2\insrsid7485074\charrsid7485074 http://www.icode.ro/}}}{\f39\fs20\insrsid7485074\charrsid7485074 ) +\par }{\field{\*\fldinst {\f39\fs20\insrsid7485074 HYPERLINK "hardwiredteks@gmail.com" }{\f39\fs20\insrsid15940516\charrsid7485074 {\*\datafield +00d0c9ea79f9bace118c8200aa004ba90b02000000010000000303000000000000c00000000000004600001800000068617264776972656474656b7340676d61696c2e636f6d00ffffadde000000000000000000000000000000000000000000000000}}}{\fldrslt { +\cs15\f39\fs20\ul\cf2\insrsid7485074\charrsid7485074 hardwiredteks@gmail.com}}}{\f39\fs20\insrsid7485074\charrsid7485074 , }{\field{\*\fldinst {\f39\fs20\insrsid7485074 HYPERLINK "hardwired@icode.ro" }{\f39\fs20\insrsid15940516\charrsid7485074 +{\*\datafield 00d0c9ea79f9bace118c8200aa004ba90b02000000010000000303000000000000c0000000000000460000130000006861726477697265644069636f64652e726f00ffffadde000000000000000000000000000000000000000000000000}}}{\fldrslt { +\cs15\f39\fs20\ul\cf2\insrsid7485074\charrsid7485074 hardwired@icode.ro}}}{\f39\fs20\insrsid7485074\charrsid7485074 +\par }} \ No newline at end of file diff --git a/admin/win/nsi/nsis_processes/readme.txt b/admin/win/nsi/nsis_processes/readme.txt new file mode 100755 index 000000000..8529c39ad --- /dev/null +++ b/admin/win/nsi/nsis_processes/readme.txt @@ -0,0 +1,122 @@ +---------------------------------------------------------------- +---------------------------------------------------------------- +Processes (Processes.dll) +Version: 1.0.1.0 +Release: 24.february.2005 +Description: Nullsoft Installer (NSIS) plug-in for managing?! + Windows processes. + +Copyright: © 2004-2005 Hardwired. No rights reserved. + There is no restriction and no guaranty for using + this software. + +Author: Andrei Ciubotaru [Hardwired] + Lead Developer ICode&Ideas SRL (http://www.icode.ro/) + hardwiredteks@gmail.com, hardwired@icode.ro + +---------------------------------------------------------------- +---------------------------------------------------------------- +INTRODUCTION + + The Need For Plug-in - I need it for the one of my installers. + + Briefly: Use it when you need to find\kill a process when +installing\uninstalling some application. Also, use it when you +need to test the presence of a device driver. + + +SUPPORT + + Supported platforms are: WinNT,Win2K,WinXP and Win2003 Server. + + +DESCRIPTION + + Processes::FindProcess ;without ".exe" + + Searches the currently running processes for the given + process name. + + return: 1 - the process was found + 0 - the process was not found + + Processes::KillProcess ; without ".exe" + + Searches the currently running processes for the given + process name. If the process is found then the it gets + killed. + + return: 1 - the process was found and killed + 0 - the process was not found or the process + cannot be killed (insuficient rights) + + Processes::FindDevice + + Searches the installed devices drivers for the given + device base name. + (important: I said BASE NAME not FILENAME) + + return: 1 - the device driver was found + 0 - the device driver was not found + + +USAGE + + First of all, does not matter where you use it. Ofcourse, the +routines must be called inside of a Section/Function scope. + + Processes::FindProcess "process_name" + Pop $R0 + + StrCmp $R0 "1" make_my_day noooooo + + make_my_day: + ... + + noooooo: + ... + + + Processes::KillProcess "process_name" + Pop $R0 + + StrCmp $R0 "1" dead_meat why_wont_you_die + + dead_meat: + ... + + why_wont_you_die: + ... + + + Processes::FindDevice "device_base_name" + Pop $R0 + + StrCmp $R0 "1" blabla more_blabla + + blabla: + ... + + more_blabla: + ... + + +THANKS + + Sunil Kamath for inspiring me. I wanted to use its FindProcDLL +but my requirements made it imposible. + + Nullsoft for creating this very powerfull installer. One big, +free and full-featured (hmmm... and guiless for the moment) mean +install machine!:) + + ME for being such a great coder... + ... HAHAHAHAHAHAHA! + +ONE MORE THING + + If you use the plugin or it's source-code, I would apreciate +if my name is mentioned. + +---------------------------------------------------------------- +---------------------------------------------------------------- diff --git a/admin/win/nsi/nsis_processes/src/StdAfx.cpp b/admin/win/nsi/nsis_processes/src/StdAfx.cpp new file mode 100755 index 000000000..f38accc8a --- /dev/null +++ b/admin/win/nsi/nsis_processes/src/StdAfx.cpp @@ -0,0 +1,8 @@ +// stdafx.cpp : source file that includes just the standard includes +// KillProcDLL.pch will be the pre-compiled header +// stdafx.obj will contain the pre-compiled type information + +#include "stdafx.h" + +// TODO: reference any additional headers you need in STDAFX.H +// and not in this file diff --git a/admin/win/nsi/nsis_processes/src/StdAfx.h b/admin/win/nsi/nsis_processes/src/StdAfx.h new file mode 100755 index 000000000..dd49f99b9 --- /dev/null +++ b/admin/win/nsi/nsis_processes/src/StdAfx.h @@ -0,0 +1,34 @@ +// stdafx.h : include file for standard system include files, +// or project specific include files that are used frequently, but +// are changed infrequently +// + +#if !defined(AFX_STDAFX_H__780690DC_E128_403D_BC07_780D1B2CC101__INCLUDED_) +#define AFX_STDAFX_H__780690DC_E128_403D_BC07_780D1B2CC101__INCLUDED_ + +#if _MSC_VER > 1000 +#pragma once +#endif // _MSC_VER > 1000 + +#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers + +#include + +#include // String management... + +//From exam28.cpp +#include +//#include + +#ifdef BORLANDC + #include + #include +#endif + +//To make it a NSIS Plug-In +#include "exdll.h" + +//{{AFX_INSERT_LOCATION}} +// Microsoft Visual C++ will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_STDAFX_H__780690DC_E128_403D_BC07_780D1B2CC101__INCLUDED_) diff --git a/admin/win/nsi/nsis_processes/src/exdll.c b/admin/win/nsi/nsis_processes/src/exdll.c new file mode 100755 index 000000000..7092cb840 --- /dev/null +++ b/admin/win/nsi/nsis_processes/src/exdll.c @@ -0,0 +1,37 @@ +#include +#include "exdll.h" + +HINSTANCE g_hInstance; + +HWND g_hwndParent; + +void __declspec(dllexport) myFunction(HWND hwndParent, int string_size, + char *variables, stack_t **stacktop) +{ + g_hwndParent=hwndParent; + + EXDLL_INIT(); + + + // note if you want parameters from the stack, pop them off in order. + // i.e. if you are called via exdll::myFunction file.dat poop.dat + // calling popstring() the first time would give you file.dat, + // and the second time would give you poop.dat. + // you should empty the stack of your parameters, and ONLY your + // parameters. + + // do your stuff here + { + char buf[1024]; + wsprintf(buf,"$0=%s\n",getuservariable(INST_0)); + MessageBox(g_hwndParent,buf,0,MB_OK); + } +} + + + +BOOL WINAPI _DllMainCRTStartup(HANDLE hInst, ULONG ul_reason_for_call, LPVOID lpReserved) +{ + g_hInstance=hInst; + return TRUE; +} diff --git a/admin/win/nsi/nsis_processes/src/exdll.h b/admin/win/nsi/nsis_processes/src/exdll.h new file mode 100755 index 000000000..777d93be5 --- /dev/null +++ b/admin/win/nsi/nsis_processes/src/exdll.h @@ -0,0 +1,136 @@ +#ifndef _EXDLL_H_ +#define _EXDLL_H_ + + + + + +// +// only include this file from one place in your DLL. +// (it is all static, if you use it in two places it will fail) +// +#define EXDLL_INIT() { \ + g_stringsize = string_size; \ + g_stacktop = stacktop; \ + g_variables = variables; } + + + + +// +// For page showing plug-ins +// +#define WM_NOTIFY_OUTER_NEXT (WM_USER+0x8) +#define WM_NOTIFY_CUSTOM_READY (WM_USER+0xd) +#define NOTIFY_BYE_BYE 'x' + +typedef struct _stack_t +{ + struct _stack_t *next; + char text[1]; // this should be the length of string_size +} stack_t; + + +static unsigned int g_stringsize; +static stack_t **g_stacktop; +static char *g_variables; + +enum +{ +INST_0, // $0 +INST_1, // $1 +INST_2, // $2 +INST_3, // $3 +INST_4, // $4 +INST_5, // $5 +INST_6, // $6 +INST_7, // $7 +INST_8, // $8 +INST_9, // $9 +INST_R0, // $R0 +INST_R1, // $R1 +INST_R2, // $R2 +INST_R3, // $R3 +INST_R4, // $R4 +INST_R5, // $R5 +INST_R6, // $R6 +INST_R7, // $R7 +INST_R8, // $R8 +INST_R9, // $R9 +INST_CMDLINE, // $CMDLINE +INST_INSTDIR, // $INSTDIR +INST_OUTDIR, // $OUTDIR +INST_EXEDIR, // $EXEDIR +INST_LANG, // $LANGUAGE +__INST_LAST +}; + + + + + +// +// utility functions (not required but often useful) +// +static int popstring( char *str ) +{ + stack_t *th; + + + if( !g_stacktop || + !*g_stacktop ) + return 1; + + th = (*g_stacktop); + lstrcpy( str, th->text ); + *g_stacktop = th->next; + GlobalFree( (HGLOBAL)th ); + + return 0; +} + + + + +static void pushstring( char *str ) +{ + stack_t *th; + + + if( !g_stacktop ) + return; + + th = (stack_t*)GlobalAlloc( GPTR, sizeof(stack_t) + g_stringsize ); + lstrcpyn( th->text, str, g_stringsize ); + th->next = *g_stacktop; + *g_stacktop = th; +} + + + + + +static char *getuservariable( int varnum ) +{ + if( varnum < 0 || + varnum >= __INST_LAST ) + return NULL; + + return (g_variables + varnum*g_stringsize); +} + + + + + +static void setuservariable( int varnum, char *var ) +{ + if( var != NULL && + varnum >= 0 && + varnum < __INST_LAST ) + lstrcpy( g_variables + varnum*g_stringsize, var ); +} + + + +#endif//_EXDLL_H_ \ No newline at end of file diff --git a/admin/win/nsi/nsis_processes/src/processes.cpp b/admin/win/nsi/nsis_processes/src/processes.cpp new file mode 100755 index 000000000..c15f8f94a --- /dev/null +++ b/admin/win/nsi/nsis_processes/src/processes.cpp @@ -0,0 +1,411 @@ +#include "stdafx.h" +#include "processes.h" +#include "string.h" + + + + + + +//------------------------------------------------------------------------------------------- +// global variables +lpfEnumProcesses EnumProcesses; +lpfEnumProcessModules EnumProcessModules; +lpfGetModuleBaseName GetModuleBaseName; +lpfEnumDeviceDrivers EnumDeviceDrivers; +lpfGetDeviceDriverBaseName GetDeviceDriverBaseName; + +HINSTANCE g_hInstance; +HWND g_hwndParent; +HINSTANCE g_hInstLib; + + + + + +//------------------------------------------------------------------------------------------- +// main DLL entry +BOOL WINAPI _DllMainCRTStartup( HANDLE hInst, + ULONG ul_reason_for_call, + LPVOID lpReserved ) +{ + g_hInstance = (struct HINSTANCE__ *)hInst; + + return TRUE; +} + + + + + +//------------------------------------------------------------------------------------------- +// loads the psapi routines +bool LoadPSAPIRoutines( void ) +{ + if( NULL == (g_hInstLib = LoadLibraryA( "PSAPI.DLL" )) ) + return false; + + EnumProcesses = (lpfEnumProcesses) GetProcAddress( g_hInstLib, "EnumProcesses" ); + EnumProcessModules = (lpfEnumProcessModules) GetProcAddress( g_hInstLib, "EnumProcessModules" ); + GetModuleBaseName = (lpfGetModuleBaseName) GetProcAddress( g_hInstLib, "GetModuleBaseNameA" ); + EnumDeviceDrivers = (lpfEnumDeviceDrivers) GetProcAddress( g_hInstLib, "EnumDeviceDrivers" ); + GetDeviceDriverBaseName = (lpfGetDeviceDriverBaseName) GetProcAddress( g_hInstLib, "GetDeviceDriverBaseNameA" ); + + if( ( NULL == EnumProcesses ) || + ( NULL == EnumProcessModules ) || + ( NULL == EnumDeviceDrivers ) || + ( NULL == GetModuleBaseName ) || + ( NULL == GetDeviceDriverBaseName ) ) + { + FreeLibrary( g_hInstLib ); + + return false; + } + + return true; +} + + + + + +//------------------------------------------------------------------------------------------- +// free the psapi routines +bool FreePSAPIRoutines( void ) +{ + EnumProcesses = NULL; + EnumProcessModules = NULL; + GetModuleBaseName = NULL; + EnumDeviceDrivers = NULL; + + if( FALSE == FreeLibrary( g_hInstLib ) ) + return false; + + return true; +} + + + + + +//------------------------------------------------------------------------------------------- +// find a process by name +// return value: true - process was found +// false - process not found +bool FindProc( char *szProcess ) +{ + char szProcessName[ 1024 ]; + char szCurrentProcessName[ 1024 ]; + DWORD dPID[ 1024 ]; + DWORD dPIDSize( 1024 ); + DWORD dSize( 1024 ); + HANDLE hProcess; + HMODULE phModule[ 1024 ]; + + + // + // make the name lower case + // + memset( szProcessName, 0, 1024*sizeof(char) ); + sprintf( szProcessName, "%s", szProcess ); + strlwr( szProcessName ); + + // + // load PSAPI routines + // + if( false == LoadPSAPIRoutines() ) + return false; + + // + // enumerate processes names + // + if( FALSE == EnumProcesses( dPID, dSize, &dPIDSize ) ) + { + FreePSAPIRoutines(); + + return false; + } + + // + // walk trough and compare see if the process is running + // + for( int k( dPIDSize / sizeof( DWORD ) ); k >= 0; k-- ) + { + memset( szCurrentProcessName, 0, 1024*sizeof(char) ); + + if( NULL != ( hProcess = OpenProcess( PROCESS_ALL_ACCESS, FALSE, dPID[ k ] ) ) ) + { + if( TRUE == EnumProcessModules( hProcess, phModule, sizeof(HMODULE)*1024, &dPIDSize ) ) + if( GetModuleBaseName( hProcess, phModule[ 0 ], szCurrentProcessName, 1024 ) > 0 ) + { + strlwr( szCurrentProcessName ); + + if( NULL != strstr( szCurrentProcessName, szProcessName ) ) + { + FreePSAPIRoutines(); + CloseHandle( hProcess ); + + return true; + } + } + + CloseHandle( hProcess ); + } + } + + // + // free PSAPI routines + // + FreePSAPIRoutines(); + + return false; +} + + + + + +//------------------------------------------------------------------------------------------- +// kills a process by name +// return value: true - process was found +// false - process not found +bool KillProc( char *szProcess ) +{ + char szProcessName[ 1024 ]; + char szCurrentProcessName[ 1024 ]; + DWORD dPID[ 1024 ]; + DWORD dPIDSize( 1024 ); + DWORD dSize( 1024 ); + HANDLE hProcess; + HMODULE phModule[ 1024 ]; + + + // + // make the name lower case + // + memset( szProcessName, 0, 1024*sizeof(char) ); + sprintf( szProcessName, "%s", szProcess ); + strlwr( szProcessName ); + + // + // load PSAPI routines + // + if( false == LoadPSAPIRoutines() ) + return false; + + // + // enumerate processes names + // + if( FALSE == EnumProcesses( dPID, dSize, &dPIDSize ) ) + { + FreePSAPIRoutines(); + + return false; + } + + // + // walk trough and compare see if the process is running + // + for( int k( dPIDSize / sizeof( DWORD ) ); k >= 0; k-- ) + { + memset( szCurrentProcessName, 0, 1024*sizeof(char) ); + + if( NULL != ( hProcess = OpenProcess( PROCESS_ALL_ACCESS, FALSE, dPID[ k ] ) ) ) + { + if( TRUE == EnumProcessModules( hProcess, phModule, sizeof(HMODULE)*1024, &dPIDSize ) ) + if( GetModuleBaseName( hProcess, phModule[ 0 ], szCurrentProcessName, 1024 ) > 0 ) + { + strlwr( szCurrentProcessName ); + + if( NULL != strstr( szCurrentProcessName, szProcessName ) ) + { + FreePSAPIRoutines(); + + // + // kill process + // + if( false == TerminateProcess( hProcess, 0 ) ) + { + CloseHandle( hProcess ); + + return true; + } + + // + // refresh systray + // + UpdateWindow( FindWindow( NULL, "Shell_TrayWnd" ) ); + + // + // refresh desktop window + // + UpdateWindow( GetDesktopWindow() ); + + CloseHandle( hProcess ); + + return true; + } + } + + CloseHandle( hProcess ); + } + } + + // + // free PSAPI routines + // + FreePSAPIRoutines(); + + return false; +} + + + + + +//------------------------------------------------------------------------------------------- +bool FindDev( char *szDriverName ) +{ + char szDeviceName[ 1024 ]; + char szCurrentDeviceName[ 1024 ]; + LPVOID lpDevices[ 1024 ]; + DWORD dDevicesSize( 1024 ); + DWORD dSize( 1024 ); + TCHAR tszCurrentDeviceName[ 1024 ]; + DWORD dNameSize( 1024 ); + + + // + // make the name lower case + // + memset( szDeviceName, 0, 1024*sizeof(char) ); + sprintf( szDeviceName, "%s", strlwr( szDriverName ) ); + + // + // load PSAPI routines + // + if( false == LoadPSAPIRoutines() ) + return false; + + // + // enumerate devices + // + if( FALSE == EnumDeviceDrivers( lpDevices, dSize, &dDevicesSize ) ) + { + FreePSAPIRoutines(); + + return false; + } + + // + // walk trough and compare see if the device driver exists + // + for( int k( dDevicesSize / sizeof( LPVOID ) ); k >= 0; k-- ) + { + memset( szCurrentDeviceName, 0, 1024*sizeof(char) ); + memset( tszCurrentDeviceName, 0, 1024*sizeof(TCHAR) ); + + if( 0 != GetDeviceDriverBaseName( lpDevices[ k ], tszCurrentDeviceName, dNameSize ) ) + { + sprintf( szCurrentDeviceName, "%S", tszCurrentDeviceName ); + + if( 0 == strcmp( strlwr( szCurrentDeviceName ), szDeviceName ) ) + { + FreePSAPIRoutines(); + + return true; + } + } + } + + // + // free PSAPI routines + // + FreePSAPIRoutines(); + + return false; +} + + + + + +//------------------------------------------------------------------------------------------- +extern "C" __declspec(dllexport) void FindProcess( HWND hwndParent, + int string_size, + char *variables, + stack_t **stacktop ) +{ + char szParameter[ 1024 ]; + + + g_hwndParent = hwndParent; + + EXDLL_INIT(); + { + popstring( szParameter ); + + if( true == FindProc( szParameter ) ) + wsprintf( szParameter, "1" ); + else + wsprintf( szParameter, "0" ); + + setuservariable( INST_R0, szParameter ); + } +} + + + + + +//------------------------------------------------------------------------------------------- +extern "C" __declspec(dllexport) void KillProcess( HWND hwndParent, + int string_size, + char *variables, + stack_t **stacktop ) +{ + char szParameter[ 1024 ]; + + + g_hwndParent = hwndParent; + + EXDLL_INIT(); + { + popstring( szParameter ); + + if( true == KillProc( szParameter ) ) + wsprintf( szParameter, "1" ); + else + wsprintf( szParameter, "0" ); + + setuservariable( INST_R0, szParameter ); + } +} + + + + + +//------------------------------------------------------------------------------------------- +extern "C" __declspec(dllexport) void FindDevice( HWND hwndParent, + int string_size, + char *variables, + stack_t **stacktop ) +{ + char szParameter[ 1024 ]; + + + g_hwndParent = hwndParent; + + EXDLL_INIT(); + { + popstring( szParameter ); + + if( true == FindDev( szParameter ) ) + wsprintf( szParameter, "1" ); + else + wsprintf( szParameter, "0" ); + + setuservariable( INST_R0, szParameter ); + } +} diff --git a/admin/win/nsi/nsis_processes/src/processes.h b/admin/win/nsi/nsis_processes/src/processes.h new file mode 100755 index 000000000..9bd069101 --- /dev/null +++ b/admin/win/nsi/nsis_processes/src/processes.h @@ -0,0 +1,49 @@ +#pragma once + + + + + +//------------------------------------------------------------------------------------------- +// PSAPI function pointers +typedef BOOL (WINAPI *lpfEnumProcesses) ( DWORD *, DWORD, DWORD * ); +typedef BOOL (WINAPI *lpfEnumProcessModules) ( HANDLE, HMODULE *, DWORD, LPDWORD ); +typedef DWORD (WINAPI *lpfGetModuleBaseName) ( HANDLE, HMODULE, LPTSTR, DWORD ); +typedef BOOL (WINAPI *lpfEnumDeviceDrivers) ( LPVOID *, DWORD, LPDWORD ); +typedef BOOL (WINAPI *lpfGetDeviceDriverBaseName)( LPVOID, LPTSTR, DWORD ); + + + + + + +//------------------------------------------------------------------------------------------- +// Internal use routines +bool LoadPSAPIRoutines( void ); +bool FreePSAPIRoutines( void ); + +bool FindProc( char *szProcess ); +bool KillProc( char *szProcess ); + +bool FindDev( char *szDriverName ); + + + + + +//------------------------------------------------------------------------------------------- +// Exported routines +extern "C" __declspec(dllexport) void FindProcess( HWND hwndParent, + int string_size, + char *variables, + stack_t **stacktop ); + +extern "C" __declspec(dllexport) void KillProcess( HWND hwndParent, + int string_size, + char *variables, + stack_t **stacktop ); + +extern "C" __declspec(dllexport) void FindDevice( HWND hwndParent, + int string_size, + char *variables, + stack_t **stacktop ); diff --git a/admin/win/nsi/nsis_processes/src/processes.ncb b/admin/win/nsi/nsis_processes/src/processes.ncb new file mode 100755 index 000000000..c1a5f281f Binary files /dev/null and b/admin/win/nsi/nsis_processes/src/processes.ncb differ diff --git a/admin/win/nsi/nsis_processes/src/processes.rc b/admin/win/nsi/nsis_processes/src/processes.rc new file mode 100755 index 000000000..c6e62a3c8 --- /dev/null +++ b/admin/win/nsi/nsis_processes/src/processes.rc @@ -0,0 +1,103 @@ +// Microsoft Visual C++ generated resource script. +// +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "afxres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (U.S.) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) +#endif //_WIN32 + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""afxres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +VS_VERSION_INFO VERSIONINFO + FILEVERSION 1,0,0,1 + PRODUCTVERSION 1,0,0,1 + FILEFLAGSMASK 0x17L +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x4L + FILETYPE 0x2L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "Comments", "NSIS Plug-in for Windows process management. Only WinNT, Win2K, WinXP and Win2003 Server supported." + VALUE "CompanyName", "Andrei Ciubotaru [Hardwired]" + VALUE "FileDescription", "Windows Processes Management" + VALUE "FileVersion", "1, 0, 0, 1" + VALUE "InternalName", "Processes" + VALUE "LegalCopyright", "Copyright (c) 2004 Hardwired. No rights reserved." + VALUE "OriginalFilename", "Processes.dll" + VALUE "ProductName", "Processes" + VALUE "ProductVersion", "1, 0, 0, 1" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END + +#endif // English (U.S.) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/admin/win/nsi/nsis_processes/src/processes.sln b/admin/win/nsi/nsis_processes/src/processes.sln new file mode 100755 index 000000000..73fc989e2 --- /dev/null +++ b/admin/win/nsi/nsis_processes/src/processes.sln @@ -0,0 +1,21 @@ +Microsoft Visual Studio Solution File, Format Version 8.00 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "processes", "processes.vcproj", "{3438467F-A719-46DC-93E5-137A8B691727}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Global + GlobalSection(SolutionConfiguration) = preSolution + Debug = Debug + Release = Release + EndGlobalSection + GlobalSection(ProjectConfiguration) = postSolution + {3438467F-A719-46DC-93E5-137A8B691727}.Debug.ActiveCfg = Debug|Win32 + {3438467F-A719-46DC-93E5-137A8B691727}.Debug.Build.0 = Debug|Win32 + {3438467F-A719-46DC-93E5-137A8B691727}.Release.ActiveCfg = Release|Win32 + {3438467F-A719-46DC-93E5-137A8B691727}.Release.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + EndGlobalSection + GlobalSection(ExtensibilityAddIns) = postSolution + EndGlobalSection +EndGlobal diff --git a/admin/win/nsi/nsis_processes/src/processes.txt b/admin/win/nsi/nsis_processes/src/processes.txt new file mode 100755 index 000000000..51d11902a --- /dev/null +++ b/admin/win/nsi/nsis_processes/src/processes.txt @@ -0,0 +1,122 @@ +---------------------------------------------------------------- +---------------------------------------------------------------- +Processes (Processes.dll) +Version: 1.0.0.1 +Release: 12.december.2004 +Description:Nullsoft Installer (NSIS) plug-in for managing?! + Windows processes. + +Copyright: © 2004 Hardwired. No rights reserved. + There is no restriction and no guaranty for using + this software. + +Author: Andrei Ciubotaru [Hardwired] + Lead Developer ICode&Ideas SRL (http://www.icode.ro) + hardwiredteks@gmail.com, hardwired@icode.ro + +---------------------------------------------------------------- +---------------------------------------------------------------- +INTRODUCTION + + The Need For Plug-in - I need it for the one of my installers. + + Briefly: Use it when you need to find\kill a process when +installing\uninstalling some application. Also, use it when you +need to test the presence of a device driver. + + +SUPPORT + + Supported platforms are: WinNT,Win2K,WinXP and Win2003 Server. + + +DESCRIPTION + + Processes::FindProcess + + Searches the currently running processes for the given + process name. + + return: 1 - the process was found + 0 - the process was not found + + Processes::KillProcess + + Searches the currently running processes for the given + process name. If the process is found then the it gets + killed. + + return: 1 - the process was found and killed + 0 - the process was not found or the process + cannot be killed (insuficient rights) + + Processes::FindDevice + + Searches the installed devices drivers for the given + device base name. + (important: I said BASE NAME not FILENAME) + + return: 1 - the device driver was found + 0 - the device driver was not found + + +USAGE + + First of all, does not matter where you use it. Ofcourse, the +routines must be called inside of a Section/Function scope. + + Processes::FindProcess "process_name.exe" + Pop $R0 + + StrCmp $R0 "1" make_my_day noooooo + + make_my_day: + ... + + noooooo: + ... + + + Processes::KillProcess "process_name.exe" + Pop $R0 + + StrCmp $R0 "1" dead_meat why_wont_you_die + + dead_meat: + ... + + why_wont_you_die: + ... + + + Processes::FindDevice "device_base_name" + Pop $R0 + + StrCmp $R0 "1" blabla more_blabla + + blabla: + ... + + more_blabla: + ... + + +THANKS + + Sunil Kamath for inspiring me. I wanted to use its FindProcDLL +but my requirements made it imposible. + + Nullsoft for creating this very powerfull installer. One big, +free and full-featured (hmmm... and guiless for the moment) mean +install machine!:) + + ME for being such a great coder... + ... HAHAHAHAHAHAHA! + +ONE MORE THING + + If you use the plugin or it's source-code, I would apreciate +if my name is mentioned. + +---------------------------------------------------------------- +---------------------------------------------------------------- diff --git a/admin/win/nsi/nsis_processes/src/processes.vcproj b/admin/win/nsi/nsis_processes/src/processes.vcproj new file mode 100755 index 000000000..245cbc99f --- /dev/null +++ b/admin/win/nsi/nsis_processes/src/processes.vcproj @@ -0,0 +1,222 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/admin/win/nsi/nsis_processes/src/resource.h b/admin/win/nsi/nsis_processes/src/resource.h new file mode 100755 index 000000000..506377e21 --- /dev/null +++ b/admin/win/nsi/nsis_processes/src/resource.h @@ -0,0 +1,15 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by processes.rc +// + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 101 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1001 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/admin/win/nsi/page_header.bmp b/admin/win/nsi/page_header.bmp old mode 100755 new mode 100644 diff --git a/admin/win/nsi/revision.txt b/admin/win/nsi/revision.txt index 56749c830..105d7d9ad 100644 --- a/admin/win/nsi/revision.txt +++ b/admin/win/nsi/revision.txt @@ -1 +1 @@ -96 \ No newline at end of file +100 \ No newline at end of file diff --git a/admin/win/nsi/tomahawk.ini b/admin/win/nsi/tomahawk.ini old mode 100755 new mode 100644 diff --git a/admin/win/nsi/tomahawk.nsi b/admin/win/nsi/tomahawk.nsi old mode 100755 new mode 100644 index 4bdb57fa4..416944114 --- a/admin/win/nsi/tomahawk.nsi +++ b/admin/win/nsi/tomahawk.nsi @@ -4,12 +4,12 @@ ; Some installer script options (comment-out options not required) ;----------------------------------------------------------------------------- ;!define OPTION_LICENSE_AGREEMENT -;!define OPTION_UAC_PLUGIN_ENHANCED +!define OPTION_UAC_PLUGIN_ENHANCED !define OPTION_SECTION_SC_START_MENU !define OPTION_SECTION_SC_DESKTOP !define OPTION_SECTION_SC_QUICK_LAUNCH !define OPTION_FINISHPAGE -!define OPTION_FINISHPAGE_LAUNCHER +;!define OPTION_FINISHPAGE_LAUNCHER !define OPTION_FINISHPAGE_RELEASE_NOTES ;----------------------------------------------------------------------------- @@ -35,7 +35,7 @@ !define VER_MAJOR "0" !define VER_MINOR "0" -!define VER_BUILD "2" +!define VER_BUILD "3" !define VERSION "${VER_MAJOR}.${VER_MINOR}.${VER_BUILD}" @@ -58,17 +58,10 @@ InstType Full InstType Minimal CRCCheck On SetCompressor /SOLID lzma +RequestExecutionLevel user ;Now using the UAC plugin. ReserveFile tomahawk.ini ReserveFile "${NSISDIR}\Plugins\InstallOptions.dll" -;The UAC plugin provides an elevated user. -;Otherwise request admin level here. -!ifdef OPTION_UAC_PLUGIN_ENHANCED - RequestExecutionLevel user -!else - RequestExecutionLevel admin -!endif - ;----------------------------------------------------------------------------- ; Include some required header files. ;----------------------------------------------------------------------------- @@ -79,9 +72,7 @@ ReserveFile "${NSISDIR}\Plugins\InstallOptions.dll" !include Memento.nsh ;Remember user selections. !include WinVer.nsh ;Windows version detection. !include WordFunc.nsh ;Used by VersionCompare macro function. -!ifdef OPTION_UAC_PLUGIN_ENHANCED - !include UAC.nsh ;Used by the UAC elevation to install as user or admin. -!endif +!include UAC.nsh ;Used by the UAC elevation to install as user or admin. ;----------------------------------------------------------------------------- ; Memento selections stored in registry. @@ -146,18 +137,46 @@ UninstPage custom un.UnPageUserAppData un.UnPageUserAppDataLeave ############################################################################## Function LaunchTomahawk - !ifdef OPTION_UAC_PLUGIN_ENHANCED - ${UAC.CallFunctionAsUser} LaunchTomahawkAsUser - !else - Exec "$INSTDIR\tomahawk.exe" - !endif + ${UAC.CallFunctionAsUser} LaunchTomahawkAsUser FunctionEnd -!ifdef OPTION_UAC_PLUGIN_ENHANCED Function LaunchTomahawkAsUser Exec "$INSTDIR\tomahawk.exe" FunctionEnd -!endif + +############################################################################## +# # +# PROCESS HANDLING FUNCTIONS AND MACROS # +# # +############################################################################## + +!macro CheckForProcess processName gotoWhenFound gotoWhenNotFound + Processes::FindProcess ${processName} + StrCmp $R0 "0" ${gotoWhenNotFound} ${gotoWhenFound} +!macroend + +!macro ConfirmEndProcess processName + MessageBox MB_YESNO|MB_ICONEXCLAMATION \ + "Found ${processName} process(s) which need to be stopped.$\nDo you want the installer to stop these for you?" \ + IDYES process_${processName}_kill IDNO process_${processName}_ended + process_${processName}_kill: + DetailPrint "Killing ${processName} processes." + Processes::KillProcess ${processName} + Sleep 1500 + StrCmp $R0 "1" process_${processName}_ended + DetailPrint "Process to kill not found!" + process_${processName}_ended: +!macroend + +!macro CheckAndConfirmEndProcess processName + !insertmacro CheckForProcess ${processName} 0 no_process_${processName}_to_end + !insertmacro ConfirmEndProcess ${processName} + no_process_${processName}_to_end: +!macroend + +Function EnsureTomahawkShutdown + !insertmacro CheckAndConfirmEndProcess "tomahawk.exe" +FunctionEnd ############################################################################## # # @@ -226,12 +245,10 @@ Function PageLeaveReinstall Delete $R1 RMDir $INSTDIR no_remove_uninstaller: - StrCmp $R0 "2" +2 0 + StrCmp $R0 "2" 0 +3 + UAC::Unload + Quit BringToFront - !ifdef OPTION_UAC_PLUGIN_ENHANCED - UAC::Unload - Quit - !endif reinst_done: FunctionEnd @@ -346,7 +363,7 @@ SectionGroup "Shortcuts" CreateShortCut "$SMPROGRAMS\Tomahawk\LICENSE.lnk" "$INSTDIR\LICENSE.txt" CreateShortCut "$SMPROGRAMS\Tomahawk\Tomahawk.lnk" "$INSTDIR\tomahawk.exe" CreateShortCut "$SMPROGRAMS\Tomahawk\Release notes.lnk" "$INSTDIR\NOTES.txt" - CreateShortCut "$SMPROGRAMS\Tomahawk\Uninstall.lnk" "$INSTDIR\Uninstall.exe" + CreateShortCut "$SMPROGRAMS\Tomahawk\Uninstall.lnk" "$INSTDIR\uninstall.exe" SetShellVarContext current ${MementoSectionEnd} !endif @@ -390,7 +407,7 @@ Section -post SetDetailsPrint textonly DetailPrint "Writing Uninstaller" SetDetailsPrint listonly - WriteUninstaller $INSTDIR\Uninstall.exe + WriteUninstaller $INSTDIR\uninstall.exe ;Registry keys required for installer version handling and uninstaller. SetDetailsPrint textonly @@ -418,6 +435,12 @@ Section -post WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Tomahawk" "NoModify" "1" WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Tomahawk" "NoRepair" "1" + ; Register tomahawk:// protocol handler + WriteRegStr HKCR "tomahawk" "" "URL: Tomahawk Protocol" + WriteRegStr HKCR "tomahawk\DefaultIcon" "" $INSTDIR\tomahawk.exe,1 + WriteRegStr HKCR "tomahawk\shell" "" "open" + WriteRegStr HKCR "tomahawk\shell\open\command" "" '"$INSTDIR\tomahawk.exe" "%1"' + SetDetailsPrint textonly DetailPrint "Finsihed." SectionEnd @@ -477,6 +500,8 @@ Section Uninstall DeleteRegValue HKLM "Software\Tomahawk" "" DeleteRegKey HKLM "Software\Tomahawk" + DeleteRegKey HKCR "tomahawk" + ;Start menu shortcuts. !ifdef OPTION_SECTION_SC_START_MENU SetShellVarContext all @@ -526,27 +551,25 @@ Function .onInit ${MementoSectionRestore} - !ifdef OPTION_UAC_PLUGIN_ENHANCED - UAC_Elevate: - UAC::RunElevated - StrCmp 1223 $0 UAC_ElevationAborted ; UAC dialog aborted by user? - StrCmp 0 $0 0 UAC_Err ; Error? - StrCmp 1 $1 0 UAC_Success ;Are we the real deal or just the wrapper? - Quit + UAC_Elevate: + UAC::RunElevated + StrCmp 1223 $0 UAC_ElevationAborted ; UAC dialog aborted by user? + StrCmp 0 $0 0 UAC_Err ; Error? + StrCmp 1 $1 0 UAC_Success ;Are we the real deal or just the wrapper? + Quit - UAC_Err: - MessageBox MB_ICONSTOP "Unable to elevate, error $0" - Abort + UAC_Err: + MessageBox MB_ICONSTOP "Unable to elevate, error $0" + Abort - UAC_ElevationAborted: - Abort + UAC_ElevationAborted: + Abort - UAC_Success: - StrCmp 1 $3 +4 ;Admin? - StrCmp 3 $1 0 UAC_ElevationAborted ;Try again? - MessageBox MB_ICONSTOP "This installer requires admin access, try again" - goto UAC_Elevate - !endif + UAC_Success: + StrCmp 1 $3 +4 ;Admin? + StrCmp 3 $1 0 UAC_ElevationAborted ;Try again? + MessageBox MB_ICONSTOP "This installer requires admin access, try again" + goto UAC_Elevate ;Prevent multiple instances. System::Call 'kernel32::CreateMutexA(i 0, i 0, t "tomahawkInstaller") i .r1 ?e' @@ -554,19 +577,25 @@ Function .onInit StrCmp $R0 0 +3 MessageBox MB_OK|MB_ICONEXCLAMATION "The installer is already running." Abort + + ;Use available InstallLocation when possible. This is useful in the uninstaller + ;via re-install, which would otherwise use a default location - a bug. + ReadRegStr $R0 HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Tomahawk" "InstallLocation" + StrCmp $R0 "" SkipSetInstDir + StrCpy $INSTDIR $R0 + SkipSetInstDir: + + ;Shutdown Tomahawk in case Add/Remove re-installer option used. + Call EnsureTomahawkShutdown FunctionEnd Function .onInstSuccess ${MementoSectionSave} - !ifdef OPTION_UAC_PLUGIN_ENHANCED - UAC::Unload ;Must call unload! - !endif + UAC::Unload ;Must call unload! FunctionEnd Function .onInstFailed - !ifdef OPTION_UAC_PLUGIN_ENHANCED - UAC::Unload ;Must call unload! - !endif + UAC::Unload ;Must call unload! FunctionEnd ############################################################################## @@ -576,27 +605,25 @@ FunctionEnd ############################################################################## Function un.onInit - !ifdef OPTION_UAC_PLUGIN_ENHANCED - UAC_Elevate: - UAC::RunElevated - StrCmp 1223 $0 UAC_ElevationAborted ; UAC dialog aborted by user? - StrCmp 0 $0 0 UAC_Err ; Error? - StrCmp 1 $1 0 UAC_Success ;Are we the real deal or just the wrapper? - Quit + UAC_Elevate: + UAC::RunElevated + StrCmp 1223 $0 UAC_ElevationAborted ; UAC dialog aborted by user? + StrCmp 0 $0 0 UAC_Err ; Error? + StrCmp 1 $1 0 UAC_Success ;Are we the real deal or just the wrapper? + Quit - UAC_Err: - MessageBox MB_ICONSTOP "Unable to elevate, error $0" - Abort + UAC_Err: + MessageBox MB_ICONSTOP "Unable to elevate, error $0" + Abort - UAC_ElevationAborted: - Abort + UAC_ElevationAborted: + Abort - UAC_Success: - StrCmp 1 $3 +4 ;Admin? - StrCmp 3 $1 0 UAC_ElevationAborted ;Try again? - MessageBox MB_ICONSTOP "This uninstaller requires admin access, try again" - goto UAC_Elevate - !endif + UAC_Success: + StrCmp 1 $3 +4 ;Admin? + StrCmp 3 $1 0 UAC_ElevationAborted ;Try again? + MessageBox MB_ICONSTOP "This uninstaller requires admin access, try again" + goto UAC_Elevate ;Prevent multiple instances. System::Call 'kernel32::CreateMutexA(i 0, i 0, t "tomahawkUninstaller") i .r1 ?e' @@ -607,13 +634,9 @@ Function un.onInit FunctionEnd Function un.onUnInstSuccess - !ifdef OPTION_UAC_PLUGIN_ENHANCED - UAC::Unload ;Must call unload! - !endif + UAC::Unload ;Must call unload! FunctionEnd Function un.onUnInstFailed - !ifdef OPTION_UAC_PLUGIN_ENHANCED - UAC::Unload ;Must call unload! - !endif + UAC::Unload ;Must call unload! FunctionEnd diff --git a/admin/win/nsi/welcome.bmp b/admin/win/nsi/welcome.bmp old mode 100755 new mode 100644 diff --git a/include/tomahawk/infosystem.h b/include/tomahawk/infosystem.h index 30c43f5d2..e0efec86b 100644 --- a/include/tomahawk/infosystem.h +++ b/include/tomahawk/infosystem.h @@ -19,6 +19,7 @@ #ifndef TOMAHAWK_INFOSYSTEM_H #define TOMAHAWK_INFOSYSTEM_H +#include #include #include #include @@ -93,7 +94,8 @@ enum InfoType { typedef QMap< InfoType, QVariant > InfoMap; typedef QMap< QString, QMap< QString, QString > > InfoGenericMap; -typedef QHash< QString, QVariant > InfoCustomDataHash; +typedef QHash< QString, QVariant > InfoCustomData; +typedef QHash< QString, QString > InfoCacheCriteria; class InfoPlugin : public QObject { @@ -107,15 +109,19 @@ public: qDebug() << Q_FUNC_INFO; } - virtual void getInfo( const QString &caller, const InfoType type, const QVariant &data, Tomahawk::InfoSystem::InfoCustomDataHash customData ) = 0; + virtual void getInfo( const QString &caller, const InfoType type, const QVariant &data, InfoCustomData customData ) = 0; signals: - void info( QString caller, Tomahawk::InfoSystem::InfoType type, QVariant input, QVariant output, Tomahawk::InfoSystem::InfoCustomDataHash customData ); - void getCachedInfo( QHash< QString, QString > criteria, QString caller, Tomahawk::InfoSystem::InfoType type, QVariant input, QVariant output, Tomahawk::InfoSystem::InfoCustomDataHash customData ); + void getCachedInfo( Tomahawk::InfoSystem::InfoCacheCriteria criteria, QString caller, Tomahawk::InfoSystem::InfoType type, QVariant input, Tomahawk::InfoSystem::InfoCustomData customData ); + void updateCache( Tomahawk::InfoSystem::InfoCacheCriteria criteria, Tomahawk::InfoSystem::InfoType type, QVariant output ); + void info( QString caller, Tomahawk::InfoSystem::InfoType type, QVariant input, QVariant output, Tomahawk::InfoSystem::InfoCustomData customData ); void finished( QString, Tomahawk::InfoSystem::InfoType ); -//public slots: - //void notInCacheSlot( QString caller, Tomahawk::InfoSystem::InfoType type, QVariant input, QVariant output, Tomahawk::InfoSystem::InfoCustomDataHash customData ) = 0; +public slots: + //FIXME: Make pure virtual when everything supports it + virtual void notInCacheSlot( Tomahawk::InfoSystem::InfoCacheCriteria criteria, QString caller, Tomahawk::InfoSystem::InfoType type, QVariant input, Tomahawk::InfoSystem::InfoCustomData customData ) + { + } protected: InfoType m_type; @@ -134,18 +140,17 @@ public: void registerInfoTypes( const InfoPluginPtr &plugin, const QSet< InfoType > &types ); - void getInfo( const QString &caller, const InfoType type, const QVariant &data, InfoCustomDataHash customData ); - void getInfo( const QString &caller, const InfoMap &input, InfoCustomDataHash customData ); + void getInfo( const QString &caller, const InfoType type, const QVariant &data, InfoCustomData customData ); + void getInfo( const QString &caller, const InfoMap &input, InfoCustomData customData ); InfoSystemCache* getCache() { return m_cache; } signals: - void info( QString caller, Tomahawk::InfoSystem::InfoType, QVariant input, QVariant output, Tomahawk::InfoSystem::InfoCustomDataHash customData ); + void info( QString caller, Tomahawk::InfoSystem::InfoType, QVariant input, QVariant output, Tomahawk::InfoSystem::InfoCustomData customData ); void finished( QString target ); public slots: - void infoSlot( QString target, Tomahawk::InfoSystem::InfoType type, QVariant input, QVariant output, Tomahawk::InfoSystem::InfoCustomDataHash customData ); - void finishedSlot( QString target,Tomahawk::InfoSystem::InfoType type); + void infoSlot( QString target, Tomahawk::InfoSystem::InfoType type, QVariant input, QVariant output, Tomahawk::InfoSystem::InfoCustomData customData ); private: QLinkedList< InfoPluginPtr > determineOrderedMatches( const InfoType type ) const; @@ -155,7 +160,7 @@ private: // For now, statically instantiate plugins; this is just somewhere to keep them QLinkedList< InfoPluginPtr > m_plugins; - QHash< QString, QHash< Tomahawk::InfoSystem::InfoType, int > > m_dataTracker; + QHash< QString, QHash< InfoType, int > > m_dataTracker; InfoSystemCache* m_cache; QThread* m_infoSystemCacheThreadController; @@ -165,7 +170,26 @@ private: } +inline uint qHash( Tomahawk::InfoSystem::InfoCacheCriteria hash ) +{ + QCryptographicHash md5( QCryptographicHash::Md5 ); + foreach( QString key, hash.keys() ) + md5.addData( key.toUtf8() ); + foreach( QString value, hash.values() ) + md5.addData( value.toUtf8() ); + + QString hexData = md5.result(); + + uint returnval = 0; + + foreach( uint val, hexData.toUcs4() ) + returnval += val; + + return returnval; +} + Q_DECLARE_METATYPE( Tomahawk::InfoSystem::InfoGenericMap ); -Q_DECLARE_METATYPE( Tomahawk::InfoSystem::InfoCustomDataHash ); +Q_DECLARE_METATYPE( Tomahawk::InfoSystem::InfoCustomData ); +Q_DECLARE_METATYPE( Tomahawk::InfoSystem::InfoCacheCriteria ); #endif // TOMAHAWK_INFOSYSTEM_H diff --git a/include/tomahawk/tomahawkapp.h b/include/tomahawk/tomahawkapp.h index bf83f6e86..7775a6528 100644 --- a/include/tomahawk/tomahawkapp.h +++ b/include/tomahawk/tomahawkapp.h @@ -40,6 +40,7 @@ #include "network/servent.h" #include "utils/tomahawkutils.h" +#include "kdsingleapplicationguard/kdsingleapplicationguard.h" class AudioEngine; class Database; @@ -98,9 +99,11 @@ public: // because QApplication::arguments() is expensive bool scrubFriendlyName() const { return m_scrubFriendlyName; } +public slots: + void instanceStarted( KDSingleApplicationGuard::Instance ); + private slots: void setupSIP(); - void messageReceived( const QString& ); private: void initLocalCollection(); diff --git a/lang/tomahawk_de.ts b/lang/tomahawk_de.ts new file mode 100644 index 000000000..1a028aea7 --- /dev/null +++ b/lang/tomahawk_de.ts @@ -0,0 +1,1484 @@ + + + + + AlbumModel + + + Album + Album + + + + + All albums from %1 + Alle Alben von %1 + + + + AudioControls + + + Form + + + + + Prev + + + + + Play + + + + + Pause + + + + + Next + + + + + TextLabel + + + + + Artist + + + + + Album + + + + + Owner + + + + + Time + + + + + Time Left + + + + + Shuffle + + + + + Repeat + + + + + Low + + + + + High + + + + + ClearButton + + + Clear + Leeren + + + + CollectionFlatModel + + + Your Collection + Deine Sammlung + + + + Collection of %1 + Deine Sammlung von %1 + + + + CollectionModel + + + Name + Name + + + + Tracks + Stücke + + + + Duration + Spieldauer + + + + Origin + Quelle + + + + CollectionView + + + &Play + &Abspielen + + + + Add to &Queue + Zur &Warteschlange hinzufügen + + + + This collection is empty. + Diese Sammlung ist leer. + + + + InfoBar + + + InfoBar + + + + + + + TextLabel + + + + + JabberPlugin + + + Add Friend... + Freund hinzufügen… + + + + + Add Friend + Freund hinzufügen + + + + + Enter Jabber ID: + Jabber-ID eingeben: + + + + Jabber_p + + + Authorize User + Benutzer authorisieren + + + + Do you want to grant <b>%1</b> access to your Collection? + Willst du <b>%1</b> wirklich den Zugriff auf deine Sammlung erlauben? + + + + NewPlaylistWidget + + + Enter a title for the new playlist: + Gib einen Titel für die neue Playliste ein: + + + + Tomahawk offers a variety of ways to help you create playlists and find music you enjoy! + Tomahawk bietet verschiedene Wege, Playlisten zu erstellen und Musik zu finden, die du magst! + + + + Just enter a genre or tag name and Tomahawk will suggest a few songs to get you started with your new playlist: + Gib einfach ein Genre oder einen Tagnamen ein und Tomahawk wird dir einige Lieder vorschlagen, um dir zu helfen, eine neue Playliste zu erstellen: + + + + &Create Playlist + Playliste &erstellen + + + + Create a new playlist + Erstelle eine neue Playliste + + + + PlaylistDelegate + + + %1 tracks + %1 Stücke + + + + PlaylistManager + + + All available tracks + Alle verfügbaren Stücke + + + + All available albums + Alle verfügbaren Alben + + + + PlaylistModel + + + A playlist by %1 + Eine Playliste von %1 + + + + you + dir + + + + PlaylistView + + + &Play + &Abspielen + + + + Add to &Queue + In &Warteschlange einreihen + + + + &Delete Items + Elemente &entfernen + + + + &Delete Item + Element &entfernen + + + + This playlist is currently empty. Add some tracks to it and enjoy the music! + Die Playliste ist derzeit leer. Füge einige Stücke hinzu und genieße die Musik! + + + + ProxyDialog + + + Proxy Settings + Proxy-Einstellungen + + + + Host + Rechnername + + + + Port + Port + + + + User + Benutzer + + + + Password + Passwort + + + + Type + Typ + + + + QueueView + + + + Click to show queue + Klicke hier, um die Warteschlange anzuzeigen + + + + Click to hide queue + Klicke hier, um die Warteschlange auszublenden + + + + SearchLineEdit + + + Search + Suchen + + + + SettingsDialog + + + Music Player Settings + Übersetzung eher dürftig + Einstellungen für das Musikabspielprogramm + + + + Jabber + Jabber + + + + Jabber ID: + Jabber-ID: + + + + + Password: + Passwort: + + + + Advanced Jabber Settings + Erweiterte Einstellungen für Jabber + + + + Server: + Server: + + + + Port: + Port: + + + + Network + Netzwerk + + + + Advanced Network Settings + Erweiterte Netzwerkeinstellungen + + + + If you're having difficulty connecting to peers, try setting this to your external IP address/host name and a port number (default 50210). Make sure to forward that port to this machine! + Wenn du Schwierigkeiten hast, zu anderen Leuten zu verbinden, versuche diene externe IP-Addresse/Rechnernamen und eine Portnummer (Standard 50210) hier einzutragen. Stelle sicher, den Port entsprechend an diesen Rechner weiterzuleiten! + + + + Static Host Name: + Statischer Rechnername: + + + + Static Port: + Statischer Port: + + + + Always use static host name/port? (Overrides UPnP discovery/port forwarding) + Statischen Rechnernamen/Port immer benutzen? (Überschreibt UPnP-discovery/Portweiterleitung) + + + + Proxy Settings... + Proxy-Einstellungen… + + + + Playdar HTTP API + Playdar HTTP API + + + + Connect automatically when Tomahawk starts + Automatisch beim Start von Tomahawk verbinden + + + + Use UPnP to establish port forward + Benutze UPnP um die Portweiterleitung zu konfigurieren + + + + Local Music + Lokale Musik + + + + Path to scan for music files: + Pfad zu den Musikdateien: + + + + ... + … + + + + Last.fm + Last.fm + + + + Scrobble tracks to Last.fm + Gespielte Stücke an Last.fm übertragen + + + + Last.fm Login + Last.fm Anmeldung + + + + Username: + Benutzername: + + + + Test Login + Anmeldung Testen + + + + Script Resolvers + + + + + Loaded script resolvers: + + + + + Select Music Folder + Musikordner auswählen + + + + + Failed + Fehlgeschlagen + + + + Success + Erfolgreich + + + + Could not contact server + Konnte den Server nicht erreichen + + + + Load script resolver file + + + + + SourceDelegate + + + Offline + Nicht Verbunden + + + + All available tracks + Alle verfügbaren Stücke + + + + Online + Verbunden + + + + SourceInfoWidget + + + Recent Albums + Aktuelle Alben + + + + Latest Additions to their Collection + Zuletzt zur Sammlung hinzugefügte Stücke + + + + Recently played Tracks + Aktuell gespiele Stücke + + + + Info about %1 + Information über %1 + + + + Your Collection + Deine Sammlung + + + + SourceTreeItem + + + Super Collection + Komplettsammlung + + + + SourceTreeItemWidget + + + Form + + + + + + + + TextLabel + + + + + Off + + + + + Info + + + + + Super Collection + Komplettsammlung + + + + All available tracks + Alle verfügbaren Stücke + + + + Idle + + + + + %L1 tracks + %L1 Stücke + + + + Checking + Teste + + + + Fetching + Hole + + + + Parsing + Parse + + + + Saving + Speichere + + + + Synced + Synchronisiert + + + + Scanning (%L1 tracks) + Durchsuche (%L1 Stücke) + + + + Offline + Nicht Verbunden + + + + SourceTreeView + + + &Load Playlist + &Lade Playliste + + + + &Rename Playlist + Playliste &umbenennen + + + + &Delete Playlist + Playliste &löschen + + + + Tomahawk::DynamicControlList + + + Click to collapse + Klicken zum Zusammenfalten + + + + Tomahawk::DynamicModel + + + + Could not find a playable track. + +Please change the filters or try again. + Konnte kein spielbares Stück finden. + +Bitte ändere den Filter oder versuche es erneut. + + + + Tomahawk::DynamicSetupWidget + + + Type: + Typ: + + + + Generate + Erzeugen + + + + Tomahawk::DynamicView + + + Add some filters above to seed this station! + Füge einige Filter hinzu, um diese Station zu initialisieren! + + + + Press Generate to get started! + Drücke Erzeugen, um zu beginnen! + + + + Add some filters above, and press Generate to get started! + Füge oben einige Filter hinzu und drücke Erzeugen um zu beginnen! + + + + Tomahawk::EchonestControl + + + + + + is + ist + + + + + + + + + Less + Kleiner + + + + + + + + + More + Größer + + + + 0 BPM + + + + + 500 BPM + + + + + 0 secs + 0 s + + + + 3600 secs + 3600 s + + + + -100 dB + + + + + 100 dB + + + + + Major + Dur + + + + Minor + Moll + + + + C + C + + + + C Sharp + Cis + + + + D + D + + + + E Flat + Es + + + + E + E + + + + F + F + + + + F Sharp + Fis + + + + G + G + + + + A Flat + As + + + + A + A + + + + B Flat + stimmt das? + B + + + + B + H + + + + Ascending + Aufsteigend + + + + Descending + Absteigend + + + + Tempo + Tempo + + + + Duration + Dauer + + + + Loudness + Lautstärke + + + + Artist Familiarity + + + + + Artist Hotttnesss + + + + + Song Hotttnesss + + + + + Latitude + Breitengrad + + + + Longitude + Längengrad + + + + Mode + Modus + + + + Key + Schlüssel + + + + Energy + Energie + + + + Danceability + Tanzbarkeit + + + + Tomahawk::EchonestSteerer + + + Steer this station: + Steuere diese Station: + + + + Takes effect on track change + Wird nach dem Wechsel eines Stückes aktiv + + + + Much less + Viel Weniger + + + + Less + Weniger + + + + A bit less + Etwas Weniger + + + + Keep at current + So belassen + + + + A bit more + Etwas Mehr + + + + More + Mehr + + + + Much more + Viel Mehr + + + + Tempo + Tempo + + + + Loudness + Lautstärke + + + + Danceability + Tanzbarkeit + + + + Energy + Energie + + + + Song Hotttnesss + + + + + Artist Hotttnesss + + + + + Artist Familiarity + + + + + By Description + Von der Beschreibung + + + + Enter a description + Gib eine Beschreibung ein + + + + Reset all steering commands + Setze alle Steuerkommandos zurück + + + + Tomahawk::Source + + + + Scanning (%L1 tracks) + Scanne (%L1 Stücke) + + + + Checking + Teste + + + + Fetching + Hole + + + + Parsing + Parse + + + + Saving + Speichere + + + + TomahawkTrayIcon + + + Play + Abspielen + + + + Pause + Pause + + + + Stop + Anhalten + + + + Previous Track + Vorheriges Stück + + + + Next Track + Nächstes Stück + + + + Quit + Verlassen + + + + Currently not playing. + Derzeit wird nichts gespielt. + + + + TomahawkWindow + + + Tomahawk + Tomahawk + + + + &Settings + &Einstellungen + + + + &Music Player + &Abspielprogramm + + + + &Playlist + &Playliste + + + + &Network + &Netzwerk + + + + &Help + &Hilfe + + + + &Quit + &Verlassen + + + + Ctrl+Q + Strg+Q + + + + + Go &online + &Verbindung herstellen + + + + Add &Friend... + Freund &hinzufügen… + + + + Re&scan Collection... + Sammlung neu&laden… + + + + &Configure Tomahawk... + Tomahawk &einrichten… + + + + Load &XSPF... + &XSPF-Datei laden… + + + + Create &New Playlist... + Neue &Playliste erstellen… + + + + About &Tomahawk... + Über &Tomahawk… + + + + Create New &Automatic Playlist + Neue, &automatische Playliste erstellen + + + + Create New &Station + Neue &Station erstellen + + + + Show Offline Sources + Nicht-Verfügbare Quellen anzeigen + + + + Hide Offline Sources + Nicht-Verfügbare Quellen ausblenden + + + + + Check for updates... + Teste auf updates… + + + + Back + Zurück + + + + Forward + Forwärts + + + + Home + + + + + + + Connect To Peer + Zu Gegenstelle verbinden + + + + Enter peer address: + Gib die Adresse der Gegenstelle ein: + + + + Enter peer port: + Gib den Port der Gegenstelle ein: + + + + Enter peer key: + Gib den Schlüssel der Gegenstelle ein: + + + + Go &offline + Verbindung &trennen + + + + Authentication Error + Authentifizierungsfehler + + + + by + von + + + + <h2><b>Tomahawk %1</h2>Copyright 2010, 2011<br/>Christian Muehlhaeuser &lt;muesli@tomahawk-player.org&gt;<br/><br/>Thanks to: Leo Franchi, Jeff Mitchell, Dominik Schmidt, Jason Herskowitz, Alejandro Wainzinger, Harald Sitter and Steve Robertson + + + + + TopBar + + + Form + + + + + 0 Sources + + + + + 0 Tracks + + + + + 0 Artists + + + + + 0 Shown + + + + + Tracks + Stücke + + + + Artists + Künstler + + + + Sources + Quellen + + + + Shown + Angezeigt + + + + TrackModel + + + Artist + Künstler + + + + Track + Titel + + + + Album + Album + + + + Duration + Spieldauer + + + + Bitrate + Bitrate + + + + Age + Alter + + + + Year + Jahr + + + + Size + Größe + + + + Origin + Quelle + + + + TrackView + + + Sorry, your filter '%1' did not match any results. + Entschuldige, dein Filter '%1' erzeugte keine Ergebnisse. + + + + TransferView + + + Peer + Gegenstelle + + + + Rate + Rate + + + + Track + Stück + + + + TwitterConfigWidget + + + Authenticating with Twitter allows you to discover and play music from your Twitter friends running Tomahawk. + + + + + This feature works best when you have set a static host name in the "Network" settings tab under Advanced Settings, but may work even if you do not. Tomahawk uses Direct Messages and this will only work when both Twitter users have followed each other. + + + + + Status: No saved credentials + + + + + Authenticate with Twitter + + + + + Here's how it works: just press one of the buttons below to tweet "Got Tomahawk?" and some necessary information. Then be (very) patient. Twitter is an asynchronous protocol so it can take a bit! + +If connections to peers seem to have been lost, just press the appropriate button again to re-post a tweet for resynchronization. + + + + + Select the kind of tweet you would like, then press the button to post it: + + + + + Global Tweet + + + + + @Mention + + + + + Direct Message + + + + + e.g. @tomahawkplayer + + + + + Tweet! + + + + + WelcomeWidget + + + Recently played playlists: + Aktuell gespielte Playlisten: + + + + Recently played tracks: + Aktuell gespielte Stücke: + + + + You have not played any playlists yet. + Du hast bisher keine Playlisten abgespielt. + + + + Welcome to Tomahawk + Willkommen bei Tomahawk + + + + XSPFLoader + + + New Playlist + Neue Playliste + + + + Failed to save tracks + Konnte Stücke nicht abspeichern + + + + Some tracks in the playlist do not contain an artist and a title. They will be ignored. + Einige Stücke in der Playliste enthalten weder Künstler noch Titel. Diese werden ignoriert. + + + + XSPF Error + XSPF-Fehler + + + + This is not a valid XSPF playlist. + Dies ist keine valide XSPF-Playliste. + + + diff --git a/lang/tomahawk_i18n.qrc b/lang/tomahawk_i18n.qrc new file mode 100644 index 000000000..a309b5d51 --- /dev/null +++ b/lang/tomahawk_i18n.qrc @@ -0,0 +1,5 @@ + + +tomahawk_de.qm + + diff --git a/lang/translations.cmake b/lang/translations.cmake new file mode 100644 index 000000000..a5b92f2f4 --- /dev/null +++ b/lang/translations.cmake @@ -0,0 +1,25 @@ +FILE (GLOB TS_FILES ${CMAKE_SOURCE_DIR}/lang/*.ts) +QT4_ADD_TRANSLATION(QM_FILES ${TS_FILES}) + +## HACK HACK HACK - around rcc limitations to allow out of source-tree building +SET( trans_file tomahawk_i18n ) +SET( trans_srcfile ${CMAKE_SOURCE_DIR}/lang/${trans_file}.qrc) +SET( trans_infile ${CMAKE_CURRENT_BINARY_DIR}/${trans_file}.qrc) +SET( trans_outfile ${CMAKE_CURRENT_BINARY_DIR}/qrc_${trans_file}.cxx) + +# Copy the QRC file to the output directory +ADD_CUSTOM_COMMAND( + OUTPUT ${trans_infile} + COMMAND ${CMAKE_COMMAND} -E copy ${trans_srcfile} ${trans_infile} + MAIN_DEPENDENCY ${trans_srcfile} +) + +# Run the resource compiler (rcc_options should already be set) +ADD_CUSTOM_COMMAND( + OUTPUT ${trans_outfile} + COMMAND ${QT_RCC_EXECUTABLE} + ARGS ${rcc_options} -name ${trans_file} -o ${trans_outfile} ${trans_infile} + MAIN_DEPENDENCY ${trans_infile} + DEPENDS ${QM_FILES} +) + diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 259756b05..69081156a 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -186,7 +186,9 @@ qt4_wrap_cpp( tomahawkMoc ${tomahawkHeaders} ) configure_file(${CMAKE_CURRENT_SOURCE_DIR}/config.h.in ${CMAKE_CURRENT_BINARY_DIR}/config.h) -SET( final_src ${final_src} ${tomahawkMoc} ${tomahawkSources} ${tomahawkHeaders} ) +include( ${CMAKE_SOURCE_DIR}/lang/translations.cmake ) + +SET( final_src ${final_src} ${tomahawkMoc} ${tomahawkSources} ${tomahawkHeaders} ${trans_outfile}) IF( "${gui}" STREQUAL "no" ) ELSE() diff --git a/src/audiocontrols.cpp b/src/audiocontrols.cpp index cd6f7682e..8f4214ed8 100644 --- a/src/audiocontrols.cpp +++ b/src/audiocontrols.cpp @@ -168,8 +168,8 @@ AudioControls::AudioControls( QWidget* parent ) .scaled( ui->coverImage->size(), Qt::IgnoreAspectRatio, Qt::SmoothTransformation ); connect( TomahawkApp::instance()->infoSystem(), - SIGNAL( info( QString, Tomahawk::InfoSystem::InfoType, QVariant, QVariant, Tomahawk::InfoSystem::InfoCustomDataHash ) ), - SLOT( infoSystemInfo( QString, Tomahawk::InfoSystem::InfoType, QVariant, QVariant, Tomahawk::InfoSystem::InfoCustomDataHash ) ) ); + SIGNAL( info( QString, Tomahawk::InfoSystem::InfoType, QVariant, QVariant, Tomahawk::InfoSystem::InfoCustomData ) ), + SLOT( infoSystemInfo( QString, Tomahawk::InfoSystem::InfoType, QVariant, QVariant, Tomahawk::InfoSystem::InfoCustomData ) ) ); connect( TomahawkApp::instance()->infoSystem(), SIGNAL( finished( QString ) ), SLOT( infoSystemFinished( QString ) ) ); @@ -252,17 +252,17 @@ AudioControls::onPlaybackStarted( const Tomahawk::result_ptr& result ) QString artistName = result->artist()->name(); QString albumName = result->album()->name(); - Tomahawk::InfoSystem::InfoCustomDataHash trackInfo; + Tomahawk::InfoSystem::InfoCustomData trackInfo; trackInfo["artist"] = QVariant::fromValue< QString >( result->artist()->name() ); trackInfo["album"] = QVariant::fromValue< QString >( result->album()->name() ); TomahawkApp::instance()->infoSystem()->getInfo( s_infoIdentifier, Tomahawk::InfoSystem::InfoAlbumCoverArt, - QVariant::fromValue< Tomahawk::InfoSystem::InfoCustomDataHash >( trackInfo ), Tomahawk::InfoSystem::InfoCustomDataHash() ); + QVariant::fromValue< Tomahawk::InfoSystem::InfoCustomData >( trackInfo ), Tomahawk::InfoSystem::InfoCustomData() ); } void -AudioControls::infoSystemInfo( QString caller, Tomahawk::InfoSystem::InfoType type, QVariant input, QVariant output, Tomahawk::InfoSystem::InfoCustomDataHash customData ) +AudioControls::infoSystemInfo( QString caller, Tomahawk::InfoSystem::InfoType type, QVariant input, QVariant output, Tomahawk::InfoSystem::InfoCustomData customData ) { qDebug() << Q_FUNC_INFO; if ( caller != s_infoIdentifier || type != Tomahawk::InfoSystem::InfoAlbumCoverArt ) @@ -277,13 +277,13 @@ AudioControls::infoSystemInfo( QString caller, Tomahawk::InfoSystem::InfoType ty return; } - if ( !output.canConvert< Tomahawk::InfoSystem::InfoCustomDataHash >() ) + if ( !output.canConvert< Tomahawk::InfoSystem::InfoCustomData >() ) { qDebug() << "Cannot convert fetched art from a QByteArray"; return; } - Tomahawk::InfoSystem::InfoCustomDataHash returnedData = output.value< Tomahawk::InfoSystem::InfoCustomDataHash >(); + Tomahawk::InfoSystem::InfoCustomData returnedData = output.value< Tomahawk::InfoSystem::InfoCustomData >(); const QByteArray ba = returnedData["imgbytes"].toByteArray(); if ( ba.length() ) { diff --git a/src/audiocontrols.h b/src/audiocontrols.h index 0491745c3..3b90005d6 100644 --- a/src/audiocontrols.h +++ b/src/audiocontrols.h @@ -45,7 +45,7 @@ signals: public slots: void onRepeatModeChanged( PlaylistInterface::RepeatMode mode ); void onShuffleModeChanged( bool enabled ); - void infoSystemInfo( QString caller, Tomahawk::InfoSystem::InfoType type, QVariant input, QVariant output, Tomahawk::InfoSystem::InfoCustomDataHash customData ); + void infoSystemInfo( QString caller, Tomahawk::InfoSystem::InfoType type, QVariant input, QVariant output, Tomahawk::InfoSystem::InfoCustomData customData ); void infoSystemFinished( QString target ); protected: diff --git a/src/headlesscheck.h b/src/headlesscheck.h index 189f1127e..f82fcea33 100644 --- a/src/headlesscheck.h +++ b/src/headlesscheck.h @@ -21,14 +21,14 @@ #ifdef ENABLE_HEADLESS -#define TOMAHAWK_APPLICATION QtSingleCoreApplication +#define TOMAHAWK_APPLICATION QCoreApplication #define TOMAHAWK_HEADLESS -#include "qtsingleapp/qtsingleapplication.h" +#include > #else -#define TOMAHAWK_APPLICATION QtSingleApplication -#include "qtsingleapp/qtsingleapplication.h" +#define TOMAHAWK_APPLICATION QApplication +#include #include "tomahawkwindow.h" #endif diff --git a/src/infosystem/infoplugins/echonestplugin.cpp b/src/infosystem/infoplugins/echonestplugin.cpp index fab7c9bc4..38f64ed4c 100644 --- a/src/infosystem/infoplugins/echonestplugin.cpp +++ b/src/infosystem/infoplugins/echonestplugin.cpp @@ -41,7 +41,7 @@ EchoNestPlugin::~EchoNestPlugin() qDebug() << Q_FUNC_INFO; } -void EchoNestPlugin::getInfo(const QString &caller, const InfoType type, const QVariant& data, InfoCustomDataHash customData) +void EchoNestPlugin::getInfo(const QString &caller, const InfoType type, const QVariant& data, InfoCustomData customData) { switch (type) { @@ -65,7 +65,7 @@ void EchoNestPlugin::getInfo(const QString &caller, const InfoType type, const Q } } -void EchoNestPlugin::getSongProfile(const QString &caller, const QVariant& data, InfoCustomDataHash &customData, const QString &item) +void EchoNestPlugin::getSongProfile(const QString &caller, const QVariant& data, InfoCustomData &customData, const QString &item) { //WARNING: Totally not implemented yet @@ -80,7 +80,7 @@ void EchoNestPlugin::getSongProfile(const QString &caller, const QVariant& data, // connect(reply, SIGNAL(finished()), SLOT(getArtistBiographySlot())); } -void EchoNestPlugin::getArtistBiography(const QString &caller, const QVariant& data, InfoCustomDataHash &customData) +void EchoNestPlugin::getArtistBiography(const QString &caller, const QVariant& data, InfoCustomData &customData) { if( !isValidArtistData( caller, data, customData ) ) return; @@ -94,7 +94,7 @@ void EchoNestPlugin::getArtistBiography(const QString &caller, const QVariant& d connect(reply, SIGNAL(finished()), SLOT(getArtistBiographySlot())); } -void EchoNestPlugin::getArtistFamiliarity(const QString &caller, const QVariant& data, InfoCustomDataHash &customData) +void EchoNestPlugin::getArtistFamiliarity(const QString &caller, const QVariant& data, InfoCustomData &customData) { if( !isValidArtistData( caller, data, customData ) ) return; @@ -109,7 +109,7 @@ void EchoNestPlugin::getArtistFamiliarity(const QString &caller, const QVariant& connect(reply, SIGNAL(finished()), SLOT(getArtistFamiliaritySlot())); } -void EchoNestPlugin::getArtistHotttnesss(const QString &caller, const QVariant& data, InfoCustomDataHash &customData) +void EchoNestPlugin::getArtistHotttnesss(const QString &caller, const QVariant& data, InfoCustomData &customData) { if( !isValidArtistData( caller, data, customData ) ) return; @@ -123,7 +123,7 @@ void EchoNestPlugin::getArtistHotttnesss(const QString &caller, const QVariant& connect(reply, SIGNAL(finished()), SLOT(getArtistHotttnesssSlot())); } -void EchoNestPlugin::getArtistTerms(const QString &caller, const QVariant& data, InfoCustomDataHash &customData) +void EchoNestPlugin::getArtistTerms(const QString &caller, const QVariant& data, InfoCustomData &customData) { if( !isValidArtistData( caller, data, customData ) ) return; @@ -137,7 +137,7 @@ void EchoNestPlugin::getArtistTerms(const QString &caller, const QVariant& data, connect(reply, SIGNAL(finished()), SLOT(getArtistTermsSlot())); } -void EchoNestPlugin::getMiscTopTerms(const QString &caller, const QVariant& data, InfoCustomDataHash& customData) +void EchoNestPlugin::getMiscTopTerms(const QString &caller, const QVariant& data, InfoCustomData& customData) { QNetworkReply* reply = Echonest::Artist::topTerms( 20 ); m_replyMap[reply] = customData; @@ -163,7 +163,6 @@ void EchoNestPlugin::getArtistBiographySlot() } emit info( m_callerMap[reply], Tomahawk::InfoSystem::InfoArtistBiography, reply->property( "data" ), QVariant::fromValue(biographyMap), m_replyMap[reply] ); - emit finished( m_callerMap[reply], Tomahawk::InfoSystem::InfoArtistBiography); m_replyMap.remove(reply); m_callerMap.remove(reply); reply->deleteLater(); @@ -175,7 +174,6 @@ void EchoNestPlugin::getArtistFamiliaritySlot() Echonest::Artist artist = artistFromReply( reply ); qreal familiarity = artist.familiarity(); emit info( m_callerMap[reply], Tomahawk::InfoSystem::InfoArtistFamiliarity, reply->property( "data" ), familiarity, m_replyMap[reply] ); - emit finished( m_callerMap[reply], Tomahawk::InfoSystem::InfoArtistFamiliarity); m_replyMap.remove(reply); m_callerMap.remove(reply); reply->deleteLater(); @@ -187,7 +185,6 @@ void EchoNestPlugin::getArtistHotttnesssSlot() Echonest::Artist artist = artistFromReply( reply ); qreal hotttnesss = artist.hotttnesss(); emit info( m_callerMap[reply], Tomahawk::InfoSystem::InfoArtistHotttness, reply->property( "data" ), hotttnesss, m_replyMap[reply] ); - emit finished( m_callerMap[reply], Tomahawk::InfoSystem::InfoArtistHotttness); m_replyMap.remove(reply); m_callerMap.remove(reply); reply->deleteLater(); @@ -206,7 +203,6 @@ void EchoNestPlugin::getArtistTermsSlot() termsMap[ term.name() ] = termMap; } emit info( m_callerMap[reply], Tomahawk::InfoSystem::InfoArtistTerms, reply->property( "data" ), QVariant::fromValue(termsMap), m_replyMap[reply] ); - emit finished( m_callerMap[reply], Tomahawk::InfoSystem::InfoArtistTerms); m_replyMap.remove(reply); m_callerMap.remove(reply); reply->deleteLater(); @@ -224,13 +220,12 @@ void EchoNestPlugin::getMiscTopSlot() termsMap[ term.name().toLower() ] = termMap; } emit info( m_callerMap[reply], Tomahawk::InfoSystem::InfoMiscTopTerms, QVariant(), QVariant::fromValue(termsMap), m_replyMap[reply] ); - emit finished( m_callerMap[reply], Tomahawk::InfoSystem::InfoMiscTopTerms); m_replyMap.remove(reply); m_callerMap.remove(reply); reply->deleteLater(); } -bool EchoNestPlugin::isValidArtistData(const QString &caller, const QVariant& data, InfoCustomDataHash &customData) +bool EchoNestPlugin::isValidArtistData(const QString &caller, const QVariant& data, InfoCustomData &customData) { if (data.isNull() || !data.isValid() || !data.canConvert()) { @@ -246,7 +241,7 @@ bool EchoNestPlugin::isValidArtistData(const QString &caller, const QVariant& da return true; } -bool EchoNestPlugin::isValidTrackData(const QString &caller, const QVariant& data, InfoCustomDataHash &customData) +bool EchoNestPlugin::isValidTrackData(const QString &caller, const QVariant& data, InfoCustomData &customData) { if (data.isNull() || !data.isValid() || !data.canConvert()) { diff --git a/src/infosystem/infoplugins/echonestplugin.h b/src/infosystem/infoplugins/echonestplugin.h index 734ceecd2..d4a4db367 100644 --- a/src/infosystem/infoplugins/echonestplugin.h +++ b/src/infosystem/infoplugins/echonestplugin.h @@ -42,18 +42,18 @@ public: EchoNestPlugin(QObject *parent); virtual ~EchoNestPlugin(); - void getInfo( const QString &caller, const InfoType type, const QVariant &data, InfoCustomDataHash customData ); + void getInfo( const QString &caller, const InfoType type, const QVariant &data, InfoCustomData customData ); private: - void getSongProfile( const QString &caller, const QVariant &data, InfoCustomDataHash &customData, const QString &item = QString() ); - void getArtistBiography ( const QString &caller, const QVariant &data, InfoCustomDataHash &customData ); - void getArtistFamiliarity( const QString &caller, const QVariant &data, InfoCustomDataHash &customData ); - void getArtistHotttnesss( const QString &caller, const QVariant &data, InfoCustomDataHash &customData ); - void getArtistTerms( const QString &caller, const QVariant &data, InfoCustomDataHash &customData ); - void getMiscTopTerms( const QString &caller, const QVariant &data, InfoCustomDataHash &customData ); + void getSongProfile( const QString &caller, const QVariant &data, InfoCustomData &customData, const QString &item = QString() ); + void getArtistBiography ( const QString &caller, const QVariant &data, InfoCustomData &customData ); + void getArtistFamiliarity( const QString &caller, const QVariant &data, InfoCustomData &customData ); + void getArtistHotttnesss( const QString &caller, const QVariant &data, InfoCustomData &customData ); + void getArtistTerms( const QString &caller, const QVariant &data, InfoCustomData &customData ); + void getMiscTopTerms( const QString &caller, const QVariant &data, InfoCustomData &customData ); - bool isValidArtistData( const QString &caller, const QVariant& data, InfoCustomDataHash& customData ); - bool isValidTrackData( const QString &caller, const QVariant& data, InfoCustomDataHash& customData ); + bool isValidArtistData( const QString &caller, const QVariant& data, InfoCustomData& customData ); + bool isValidTrackData( const QString &caller, const QVariant& data, InfoCustomData& customData ); Echonest::Artist artistFromReply( QNetworkReply* ); private slots: @@ -64,7 +64,7 @@ private slots: void getMiscTopSlot(); private: - QHash< QNetworkReply*, InfoCustomDataHash > m_replyMap; + QHash< QNetworkReply*, InfoCustomData > m_replyMap; QHash< QNetworkReply*, QString > m_callerMap; }; diff --git a/src/infosystem/infoplugins/lastfmplugin.cpp b/src/infosystem/infoplugins/lastfmplugin.cpp index 81272b86f..652d87414 100644 --- a/src/infosystem/infoplugins/lastfmplugin.cpp +++ b/src/infosystem/infoplugins/lastfmplugin.cpp @@ -92,15 +92,14 @@ LastFmPlugin::~LastFmPlugin() } void -LastFmPlugin::dataError( const QString &caller, const InfoType type, const QVariant& data, Tomahawk::InfoSystem::InfoCustomDataHash &customData ) +LastFmPlugin::dataError( const QString &caller, const InfoType type, const QVariant& data, Tomahawk::InfoSystem::InfoCustomData &customData ) { emit info( caller, type, data, QVariant(), customData ); - emit finished( caller, type ); return; } void -LastFmPlugin::getInfo( const QString &caller, const InfoType type, const QVariant& data, Tomahawk::InfoSystem::InfoCustomDataHash customData ) +LastFmPlugin::getInfo( const QString &caller, const InfoType type, const QVariant& data, Tomahawk::InfoSystem::InfoCustomData customData ) { qDebug() << Q_FUNC_INFO; if ( type == InfoMiscSubmitNowPlaying ) @@ -114,14 +113,14 @@ LastFmPlugin::getInfo( const QString &caller, const InfoType type, const QVarian } void -LastFmPlugin::nowPlaying( const QString &caller, const InfoType type, const QVariant& data, Tomahawk::InfoSystem::InfoCustomDataHash &customData ) +LastFmPlugin::nowPlaying( const QString &caller, const InfoType type, const QVariant& data, Tomahawk::InfoSystem::InfoCustomData &customData ) { - if ( !data.canConvert< Tomahawk::InfoSystem::InfoCustomDataHash >() || !m_scrobbler ) + if ( !data.canConvert< Tomahawk::InfoSystem::InfoCustomData >() || !m_scrobbler ) { dataError( caller, type, data, customData ); return; } - InfoCustomDataHash hash = data.value< Tomahawk::InfoSystem::InfoCustomDataHash >(); + InfoCustomData hash = data.value< Tomahawk::InfoSystem::InfoCustomData >(); if ( !hash.contains( "title" ) || !hash.contains( "artist" ) || !hash.contains( "album" ) || !hash.contains( "duration" ) ) { dataError( caller, type, data, customData ); @@ -139,11 +138,10 @@ LastFmPlugin::nowPlaying( const QString &caller, const InfoType type, const QVar m_scrobbler->nowPlaying( m_track ); emit info( caller, type, data, QVariant(), customData ); - emit finished( caller, type ); } void -LastFmPlugin::scrobble( const QString &caller, const InfoType type, const QVariant& data, Tomahawk::InfoSystem::InfoCustomDataHash &customData ) +LastFmPlugin::scrobble( const QString &caller, const InfoType type, const QVariant& data, Tomahawk::InfoSystem::InfoCustomData &customData ) { Q_ASSERT( QThread::currentThread() == thread() ); @@ -158,37 +156,53 @@ LastFmPlugin::scrobble( const QString &caller, const InfoType type, const QVaria m_scrobbler->submit(); emit info( caller, type, data, QVariant(), customData ); - emit finished( caller, type ); } void -LastFmPlugin::fetchCoverArt( const QString &caller, const InfoType type, const QVariant& data, Tomahawk::InfoSystem::InfoCustomDataHash &customData ) +LastFmPlugin::fetchCoverArt( const QString &caller, const InfoType type, const QVariant& data, Tomahawk::InfoSystem::InfoCustomData &customData ) { qDebug() << Q_FUNC_INFO; - if ( !data.canConvert< Tomahawk::InfoSystem::InfoCustomDataHash >() ) + if ( !data.canConvert< Tomahawk::InfoSystem::InfoCustomData >() ) { dataError( caller, type, data, customData ); return; } - InfoCustomDataHash hash = data.value< Tomahawk::InfoSystem::InfoCustomDataHash >(); + InfoCustomData hash = data.value< Tomahawk::InfoSystem::InfoCustomData >(); if ( !hash.contains( "artist" ) || !hash.contains( "album" ) ) { dataError( caller, type, data, customData ); return; } - QString artistName = hash["artist"].toString(); - QString albumName = hash["album"].toString(); + Tomahawk::InfoSystem::InfoCacheCriteria criteria; + criteria["artist"] = hash["artist"].toString(); + criteria["album"] = hash["album"].toString(); - QString imgurl = "http://ws.audioscrobbler.com/2.0/?method=album.imageredirect&artist=%1&album=%2&size=medium&api_key=7a90f6672a04b809ee309af169f34b8b"; - QNetworkRequest req( imgurl.arg( artistName ).arg( albumName ) ); - QNetworkReply* reply = TomahawkUtils::nam()->get( req ); - reply->setProperty("customData", QVariant::fromValue(customData)); - reply->setProperty("origData", data); - reply->setProperty("caller", caller); - reply->setProperty("type", (uint)(type) ); + emit getCachedInfo( criteria, caller, type, data, customData ); +} - connect( reply, SIGNAL( finished() ), SLOT( coverArtReturned() ) ); +void +LastFmPlugin::notInCacheSlot( QHash criteria, QString caller, Tomahawk::InfoSystem::InfoType type, QVariant input, Tomahawk::InfoSystem::InfoCustomData customData ) +{ + qDebug() << Q_FUNC_INFO; + if ( type == InfoAlbumCoverArt ) + { + QString artistName = criteria["artist"]; + QString albumName = criteria["album"]; + + QString imgurl = "http://ws.audioscrobbler.com/2.0/?method=album.imageredirect&artist=%1&album=%2&size=medium&api_key=7a90f6672a04b809ee309af169f34b8b"; + QNetworkRequest req( imgurl.arg( artistName ).arg( albumName ) ); + QNetworkReply* reply = TomahawkUtils::nam()->get( req ); + reply->setProperty( "customData", QVariant::fromValue( customData ) ); + reply->setProperty( "origData", input ); + reply->setProperty( "caller", caller ); + reply->setProperty( "type", (uint)(type) ); + + connect( reply, SIGNAL( finished() ), SLOT( coverArtReturned() ) ); + return; + } + else + qDebug() << "Couldn't figure out what to do with this type of request after cache miss"; } void @@ -200,17 +214,25 @@ LastFmPlugin::coverArtReturned() if ( redir.isEmpty() ) { const QByteArray ba = reply->readAll(); - Tomahawk::InfoSystem::InfoCustomDataHash returnedData; + InfoCustomData returnedData; returnedData["imgbytes"] = ba; returnedData["url"] = reply->url().toString(); + + InfoCustomData customData = reply->property( "customData" ).value< Tomahawk::InfoSystem::InfoCustomData >(); + InfoType type = (Tomahawk::InfoSystem::InfoType)(reply->property( "type" ).toUInt()); emit info( reply->property( "caller" ).toString(), - (Tomahawk::InfoSystem::InfoType)(reply->property( "type" ).toUInt()), + type, reply->property( "origData" ), returnedData, - reply->property( "customData" ).value< Tomahawk::InfoSystem::InfoCustomDataHash >() + customData ); - emit finished( reply->property( "caller" ).toString(), (Tomahawk::InfoSystem::InfoType)(reply->property( "type" ).toUInt()) ); + + InfoCustomData origData = reply->property( "origData" ).value< Tomahawk::InfoSystem::InfoCustomData >(); + Tomahawk::InfoSystem::InfoCacheCriteria criteria; + criteria["artist"] = origData["artist"].toString(); + criteria["album"] = origData["album"].toString(); + emit updateCache( criteria, type, returnedData ); } else { diff --git a/src/infosystem/infoplugins/lastfmplugin.h b/src/infosystem/infoplugins/lastfmplugin.h index 3f396edfb..589d40db1 100644 --- a/src/infosystem/infoplugins/lastfmplugin.h +++ b/src/infosystem/infoplugins/lastfmplugin.h @@ -43,19 +43,20 @@ public: LastFmPlugin( QObject *parent ); virtual ~LastFmPlugin(); - void getInfo( const QString &caller, const InfoType type, const QVariant &data, InfoCustomDataHash customData ); + void getInfo( const QString &caller, const InfoType type, const QVariant &data, InfoCustomData customData ); public slots: void settingsChanged(); void onAuthenticated(); void coverArtReturned(); + virtual void notInCacheSlot( Tomahawk::InfoSystem::InfoCacheCriteria criteria, QString caller, Tomahawk::InfoSystem::InfoType type, QVariant input, Tomahawk::InfoSystem::InfoCustomData customData ); private: - void fetchCoverArt( const QString &caller, const InfoType type, const QVariant& data, Tomahawk::InfoSystem::InfoCustomDataHash &customData ); - void scrobble( const QString &caller, const InfoType type, const QVariant& data, InfoCustomDataHash &customData ); + void fetchCoverArt( const QString &caller, const InfoType type, const QVariant& data, Tomahawk::InfoSystem::InfoCustomData &customData ); + void scrobble( const QString &caller, const InfoType type, const QVariant& data, InfoCustomData &customData ); void createScrobbler(); - void nowPlaying( const QString &caller, const InfoType type, const QVariant& data, InfoCustomDataHash &customData ); - void dataError( const QString &caller, const InfoType type, const QVariant& data, InfoCustomDataHash &customData ); + void nowPlaying( const QString &caller, const InfoType type, const QVariant& data, InfoCustomData &customData ); + void dataError( const QString &caller, const InfoType type, const QVariant& data, InfoCustomData &customData ); lastfm::MutableTrack m_track; lastfm::Audioscrobbler* m_scrobbler; diff --git a/src/infosystem/infoplugins/musixmatchplugin.cpp b/src/infosystem/infoplugins/musixmatchplugin.cpp index 0ee319f78..128085ca6 100644 --- a/src/infosystem/infoplugins/musixmatchplugin.cpp +++ b/src/infosystem/infoplugins/musixmatchplugin.cpp @@ -42,18 +42,17 @@ MusixMatchPlugin::~MusixMatchPlugin() qDebug() << Q_FUNC_INFO; } -void MusixMatchPlugin::getInfo(const QString &caller, const InfoType type, const QVariant& data, Tomahawk::InfoSystem::InfoCustomDataHash customData) +void MusixMatchPlugin::getInfo(const QString &caller, const InfoType type, const QVariant& data, Tomahawk::InfoSystem::InfoCustomData customData) { qDebug() << Q_FUNC_INFO; - if( !isValidTrackData(caller, data, customData) || !data.canConvert()) + if( !isValidTrackData(caller, data, customData) || !data.canConvert()) return; - Tomahawk::InfoSystem::InfoCustomDataHash hash = data.value(); + Tomahawk::InfoSystem::InfoCustomData hash = data.value(); QString artist = hash["artistName"].toString(); QString track = hash["trackName"].toString(); if( artist.isEmpty() || track.isEmpty() ) { emit info(caller, Tomahawk::InfoSystem::InfoTrackLyrics, data, QVariant(), customData); - emit finished(caller, Tomahawk::InfoSystem::InfoTrackLyrics); return; } qDebug() << "artist is " << artist << ", track is " << track; @@ -63,35 +62,32 @@ void MusixMatchPlugin::getInfo(const QString &caller, const InfoType type, const url.addQueryItem("q_artist", artist); url.addQueryItem("q_track", track); QNetworkReply* reply = TomahawkUtils::nam()->get(QNetworkRequest(url)); - reply->setProperty("customData", QVariant::fromValue(customData)); + reply->setProperty("customData", QVariant::fromValue(customData)); reply->setProperty("origData", data); reply->setProperty("caller", caller); connect(reply, SIGNAL(finished()), SLOT(trackSearchSlot())); } -bool MusixMatchPlugin::isValidTrackData(const QString &caller, const QVariant& data, Tomahawk::InfoSystem::InfoCustomDataHash &customData) +bool MusixMatchPlugin::isValidTrackData(const QString &caller, const QVariant& data, Tomahawk::InfoSystem::InfoCustomData &customData) { qDebug() << Q_FUNC_INFO; - if (data.isNull() || !data.isValid() || !data.canConvert()) + if (data.isNull() || !data.isValid() || !data.canConvert()) { emit info(caller, Tomahawk::InfoSystem::InfoTrackLyrics, data, QVariant(), customData); - emit finished(caller, Tomahawk::InfoSystem::InfoTrackLyrics); qDebug() << "MusixMatchPlugin::isValidTrackData: Data null, invalid, or can't convert"; return false; } - InfoCustomDataHash hash = data.value(); + InfoCustomData hash = data.value(); if (hash["trackName"].toString().isEmpty() ) { emit info(caller, Tomahawk::InfoSystem::InfoTrackLyrics, data, QVariant(), customData); - emit finished(caller, Tomahawk::InfoSystem::InfoTrackLyrics); qDebug() << "MusixMatchPlugin::isValidTrackData: Track name is empty"; return false; } if (hash["artistName"].toString().isEmpty() ) { emit info(caller, Tomahawk::InfoSystem::InfoTrackLyrics, data, QVariant(), customData); - emit finished(caller, Tomahawk::InfoSystem::InfoTrackLyrics); qDebug() << "MusixMatchPlugin::isValidTrackData: No artist name found"; return false; } @@ -104,7 +100,7 @@ void MusixMatchPlugin::trackSearchSlot() QNetworkReply* oldReply = qobject_cast( sender() ); if (!oldReply) { - emit info(QString(), Tomahawk::InfoSystem::InfoTrackLyrics, QVariant(), QVariant(), Tomahawk::InfoSystem::InfoCustomDataHash()); + emit info(QString(), Tomahawk::InfoSystem::InfoTrackLyrics, QVariant(), QVariant(), Tomahawk::InfoSystem::InfoCustomData()); return; } QDomDocument doc; @@ -113,8 +109,7 @@ void MusixMatchPlugin::trackSearchSlot() QDomNodeList domNodeList = doc.elementsByTagName("track_id"); if (domNodeList.isEmpty()) { - emit info(oldReply->property("caller").toString(), Tomahawk::InfoSystem::InfoTrackLyrics, oldReply->property("origData"), QVariant(), oldReply->property("customData").value()); - emit finished(oldReply->property("caller").toString(), Tomahawk::InfoSystem::InfoTrackLyrics); + emit info(oldReply->property("caller").toString(), Tomahawk::InfoSystem::InfoTrackLyrics, oldReply->property("origData"), QVariant(), oldReply->property("customData").value()); return; } QString track_id = domNodeList.at(0).toElement().text(); @@ -135,7 +130,7 @@ void MusixMatchPlugin::trackLyricsSlot() QNetworkReply* reply = qobject_cast( sender() ); if (!reply) { - emit info(QString(), Tomahawk::InfoSystem::InfoTrackLyrics, QVariant(), QVariant(), Tomahawk::InfoSystem::InfoCustomDataHash()); + emit info(QString(), Tomahawk::InfoSystem::InfoTrackLyrics, QVariant(), QVariant(), Tomahawk::InfoSystem::InfoCustomData()); return; } QDomDocument doc; @@ -143,12 +138,10 @@ void MusixMatchPlugin::trackLyricsSlot() QDomNodeList domNodeList = doc.elementsByTagName("lyrics_body"); if (domNodeList.isEmpty()) { - emit info(reply->property("caller").toString(), Tomahawk::InfoSystem::InfoTrackLyrics, reply->property("origData"), QVariant(), reply->property("customData").value()); - emit finished(reply->property("caller").toString(), Tomahawk::InfoSystem::InfoTrackLyrics); + emit info(reply->property("caller").toString(), Tomahawk::InfoSystem::InfoTrackLyrics, reply->property("origData"), QVariant(), reply->property("customData").value()); return; } QString lyrics = domNodeList.at(0).toElement().text(); qDebug() << "Emitting lyrics: " << lyrics; - emit info(reply->property("caller").toString(), Tomahawk::InfoSystem::InfoTrackLyrics, reply->property("origData"), QVariant(lyrics), reply->property("customData").value()); - emit finished(reply->property("caller").toString(), Tomahawk::InfoSystem::InfoTrackLyrics); + emit info(reply->property("caller").toString(), Tomahawk::InfoSystem::InfoTrackLyrics, reply->property("origData"), QVariant(lyrics), reply->property("customData").value()); } diff --git a/src/infosystem/infoplugins/musixmatchplugin.h b/src/infosystem/infoplugins/musixmatchplugin.h index 284c81515..255ebd53d 100644 --- a/src/infosystem/infoplugins/musixmatchplugin.h +++ b/src/infosystem/infoplugins/musixmatchplugin.h @@ -36,10 +36,10 @@ public: MusixMatchPlugin(QObject *parent); virtual ~MusixMatchPlugin(); - void getInfo(const QString &caller, const InfoType type, const QVariant &data, InfoCustomDataHash customData); + void getInfo(const QString &caller, const InfoType type, const QVariant &data, InfoCustomData customData); private: - bool isValidTrackData( const QString &caller, const QVariant& data, InfoCustomDataHash &customData ); + bool isValidTrackData( const QString &caller, const QVariant& data, InfoCustomData &customData ); public slots: void trackSearchSlot(); diff --git a/src/infosystem/infosystem.cpp b/src/infosystem/infosystem.cpp index e4a6a6c2a..7fd6158c6 100644 --- a/src/infosystem/infosystem.cpp +++ b/src/infosystem/infosystem.cpp @@ -25,7 +25,11 @@ #include "infoplugins/musixmatchplugin.h" #include "infoplugins/lastfmplugin.h" -using namespace Tomahawk::InfoSystem; +namespace Tomahawk +{ + +namespace InfoSystem +{ InfoPlugin::InfoPlugin(QObject *parent) :QObject( parent ) @@ -33,11 +37,26 @@ InfoPlugin::InfoPlugin(QObject *parent) qDebug() << Q_FUNC_INFO; InfoSystem *system = qobject_cast< InfoSystem* >( parent ); if( system ) - QObject::connect( system->getCache(), - SIGNAL( notInCache( QString, Tomahawk::InfoSystem::InfoType, QVariant, QVariant, Tomahawk::InfoSystem::InfoCustomDataHash ) ), - this, - SLOT( notInCacheSlot( QString, Tomahawk::InfoSystem::InfoType, QVariant, QVariant, Tomahawk::InfoSystem::InfoCustomDataHash ) ) - ); + { + QObject::connect( + this, + SIGNAL( getCachedInfo( Tomahawk::InfoSystem::InfoCacheCriteria, QString, Tomahawk::InfoSystem::InfoType, QVariant, Tomahawk::InfoSystem::InfoCustomData ) ), + system->getCache(), + SLOT( getCachedInfoSlot( Tomahawk::InfoSystem::InfoCacheCriteria, QString, Tomahawk::InfoSystem::InfoType, QVariant, Tomahawk::InfoSystem::InfoCustomData ) ) + ); + QObject::connect( + system->getCache(), + SIGNAL( notInCache( Tomahawk::InfoSystem::InfoCacheCriteria, QString, Tomahawk::InfoSystem::InfoType, QVariant, Tomahawk::InfoSystem::InfoCustomData ) ), + this, + SLOT( notInCacheSlot( Tomahawk::InfoSystem::InfoCacheCriteria, QString, Tomahawk::InfoSystem::InfoType, QVariant, Tomahawk::InfoSystem::InfoCustomData ) ) + ); + QObject::connect( + this, + SIGNAL( updateCache( Tomahawk::InfoSystem::InfoCacheCriteria, Tomahawk::InfoSystem::InfoType, QVariant ) ), + system->getCache(), + SLOT( updateCacheSlot( Tomahawk::InfoSystem::InfoCacheCriteria, Tomahawk::InfoSystem::InfoType, QVariant ) ) + ); + } } @@ -45,20 +64,35 @@ InfoSystem::InfoSystem(QObject *parent) : QObject(parent) { qDebug() << Q_FUNC_INFO; - qRegisterMetaType > >("Tomahawk::InfoSystem::InfoGenericMap"); - qRegisterMetaType >("Tomahawk::InfoSystem::InfoCustomDataHash"); + qRegisterMetaType< QMap< QString, QMap< QString, QString > > >( "Tomahawk::InfoSystem::InfoGenericMap" ); + qRegisterMetaType< QHash< QString, QVariant > >( "Tomahawk::InfoSystem::InfoCustomData" ); + qRegisterMetaType< QHash< QString, QString > >( "Tomahawk::InfoSystem::InfoCacheCriteria" ); + qRegisterMetaType< Tomahawk::InfoSystem::InfoType >( "Tomahawk::InfoSystem::InfoType" ); m_infoSystemCacheThreadController = new QThread( this ); - m_cache = new Tomahawk::InfoSystem::InfoSystemCache(); + m_cache = new InfoSystemCache(); m_cache->moveToThread( m_infoSystemCacheThreadController ); m_infoSystemCacheThreadController->start( QThread::IdlePriority ); - InfoPluginPtr enptr(new EchoNestPlugin(this)); - m_plugins.append(enptr); - InfoPluginPtr mmptr(new MusixMatchPlugin(this)); - m_plugins.append(mmptr); - InfoPluginPtr lfmptr(new LastFmPlugin(this)); - m_plugins.append(lfmptr); + InfoPluginPtr enptr( new EchoNestPlugin( this ) ); + m_plugins.append( enptr ); + InfoPluginPtr mmptr( new MusixMatchPlugin( this ) ); + m_plugins.append( mmptr ); + InfoPluginPtr lfmptr( new LastFmPlugin( this ) ); + m_plugins.append( lfmptr ); + + Q_FOREACH( InfoPluginPtr plugin, m_plugins ) + { + connect( + plugin.data(), + SIGNAL( info( QString, Tomahawk::InfoSystem::InfoType, QVariant, QVariant, Tomahawk::InfoSystem::InfoCustomData ) ), + this, + SLOT( infoSlot( QString, Tomahawk::InfoSystem::InfoType, QVariant, QVariant, Tomahawk::InfoSystem::InfoCustomData ) ), + Qt::UniqueConnection + ); + } + connect( m_cache, SIGNAL( info( QString, Tomahawk::InfoSystem::InfoType, QVariant, QVariant, Tomahawk::InfoSystem::InfoCustomData ) ), + this, SLOT( infoSlot( QString, Tomahawk::InfoSystem::InfoType, QVariant, QVariant, Tomahawk::InfoSystem::InfoCustomData ) ), Qt::UniqueConnection ); } InfoSystem::~InfoSystem() @@ -108,13 +142,13 @@ QLinkedList< InfoPluginPtr > InfoSystem::determineOrderedMatches(const InfoType return providers; } -void InfoSystem::getInfo(const QString &caller, const InfoType type, const QVariant& data, InfoCustomDataHash customData) +void InfoSystem::getInfo(const QString &caller, const InfoType type, const QVariant& data, InfoCustomData customData) { qDebug() << Q_FUNC_INFO; QLinkedList< InfoPluginPtr > providers = determineOrderedMatches(type); if (providers.isEmpty()) { - emit info(QString(), Tomahawk::InfoSystem::InfoNoInfo, QVariant(), QVariant(), customData); + emit info(QString(), InfoNoInfo, QVariant(), QVariant(), customData); emit finished(caller); return; } @@ -122,27 +156,23 @@ void InfoSystem::getInfo(const QString &caller, const InfoType type, const QVari InfoPluginPtr ptr = providers.first(); if (!ptr) { - emit info(QString(), Tomahawk::InfoSystem::InfoNoInfo, QVariant(), QVariant(), customData); + emit info(QString(), InfoNoInfo, QVariant(), QVariant(), customData); emit finished(caller); return; } m_dataTracker[caller][type] = m_dataTracker[caller][type] + 1; qDebug() << "current count in dataTracker for type" << type << "is" << m_dataTracker[caller][type]; - connect(ptr.data(), SIGNAL(info(QString, Tomahawk::InfoSystem::InfoType, QVariant, QVariant, Tomahawk::InfoSystem::InfoCustomDataHash)), - this, SLOT(infoSlot(QString, Tomahawk::InfoSystem::InfoType, QVariant, QVariant, Tomahawk::InfoSystem::InfoCustomDataHash)), Qt::UniqueConnection); - connect(ptr.data(), SIGNAL(finished(QString, Tomahawk::InfoSystem::InfoType)), - this, SLOT(finishedSlot(QString, Tomahawk::InfoSystem::InfoType)), Qt::UniqueConnection); ptr.data()->getInfo(caller, type, data, customData); } -void InfoSystem::getInfo(const QString &caller, const InfoMap &input, InfoCustomDataHash customData) +void InfoSystem::getInfo(const QString &caller, const InfoMap &input, InfoCustomData customData) { Q_FOREACH( InfoType type, input.keys() ) getInfo(caller, type, input[type], customData); } -void InfoSystem::infoSlot(QString target, Tomahawk::InfoSystem::InfoType type, QVariant input, QVariant output, Tomahawk::InfoSystem::InfoCustomDataHash customData) +void InfoSystem::infoSlot(QString target, InfoType type, QVariant input, QVariant output, InfoCustomData customData) { qDebug() << Q_FUNC_INFO; qDebug() << "current count in dataTracker is " << m_dataTracker[target][type]; @@ -152,14 +182,10 @@ void InfoSystem::infoSlot(QString target, Tomahawk::InfoSystem::InfoType type, Q return; } emit info(target, type, input, output, customData); -} - -void InfoSystem::finishedSlot(QString target, Tomahawk::InfoSystem::InfoType type) -{ - qDebug() << Q_FUNC_INFO; + m_dataTracker[target][type] = m_dataTracker[target][type] - 1; qDebug() << "current count in dataTracker is " << m_dataTracker[target][type]; - Q_FOREACH(Tomahawk::InfoSystem::InfoType testtype, m_dataTracker[target].keys()) + Q_FOREACH(InfoType testtype, m_dataTracker[target].keys()) { if (m_dataTracker[target][testtype] != 0) { @@ -170,3 +196,7 @@ void InfoSystem::finishedSlot(QString target, Tomahawk::InfoSystem::InfoType typ qDebug() << "emitting finished with target" << target; emit finished(target); } + +} //namespace InfoSystem + +} //namespace Tomahawk \ No newline at end of file diff --git a/src/infosystem/infosystemcache.cpp b/src/infosystem/infosystemcache.cpp index e69de29bb..8e4801540 100644 --- a/src/infosystem/infosystemcache.cpp +++ b/src/infosystem/infosystemcache.cpp @@ -0,0 +1,45 @@ +/* === This file is part of Tomahawk Player - === + * + * Copyright 2010-2011, Christian Muehlhaeuser + * + * Tomahawk is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Tomahawk is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Tomahawk. If not, see . + */ + +#include + +#include "infosystemcache.h" + +void +Tomahawk::InfoSystem::InfoSystemCache::getCachedInfoSlot( Tomahawk::InfoSystem::InfoCacheCriteria criteria, QString caller, Tomahawk::InfoSystem::InfoType type, QVariant input, Tomahawk::InfoSystem::InfoCustomData customData ) +{ + qDebug() << Q_FUNC_INFO; + if( !m_memCache.contains( type ) || !m_memCache[type].contains( criteria ) ) + { + emit notInCache( criteria, caller, type, input, customData ); + return; + } + + emit info( caller, type, input, m_memCache[type][criteria], customData ); +} + +void +Tomahawk::InfoSystem::InfoSystemCache::updateCacheSlot( Tomahawk::InfoSystem::InfoCacheCriteria criteria, Tomahawk::InfoSystem::InfoType type, QVariant output ) +{ + qDebug() << Q_FUNC_INFO; + QHash< InfoCacheCriteria, QVariant > typecache; + if( m_memCache.contains( type ) ) + typecache = m_memCache[type]; + typecache[criteria] = output; + m_memCache[type] = typecache; +} diff --git a/src/infosystem/infosystemcache.h b/src/infosystem/infosystemcache.h index 97990b7a1..ef31710c2 100644 --- a/src/infosystem/infosystemcache.h +++ b/src/infosystem/infosystemcache.h @@ -22,6 +22,8 @@ #include #include +#include "tomahawk/infosystem.h" + namespace Tomahawk { @@ -44,6 +46,16 @@ public: qDebug() << Q_FUNC_INFO; } +signals: + void notInCache( Tomahawk::InfoSystem::InfoCacheCriteria criteria, QString caller, Tomahawk::InfoSystem::InfoType type, QVariant input, Tomahawk::InfoSystem::InfoCustomData customData ); + void info( QString caller, Tomahawk::InfoSystem::InfoType type, QVariant input, QVariant output, Tomahawk::InfoSystem::InfoCustomData customData ); + +public slots: + void getCachedInfoSlot( Tomahawk::InfoSystem::InfoCacheCriteria criteria, QString caller, Tomahawk::InfoSystem::InfoType type, QVariant input, Tomahawk::InfoSystem::InfoCustomData customData ); + void updateCacheSlot( Tomahawk::InfoSystem::InfoCacheCriteria criteria, Tomahawk::InfoSystem::InfoType type, QVariant output ); + +private: + QHash< InfoType, QHash< InfoCacheCriteria, QVariant > > m_memCache; }; } //namespace InfoSystem diff --git a/src/libtomahawk/CMakeLists.txt b/src/libtomahawk/CMakeLists.txt index b7f214428..c7f3ac4e2 100644 --- a/src/libtomahawk/CMakeLists.txt +++ b/src/libtomahawk/CMakeLists.txt @@ -146,8 +146,10 @@ set( libSources widgets/overlaywidget.cpp widgets/infowidgets/sourceinfowidget.cpp - qtsingleapp/qtlocalpeer.cpp - qtsingleapp/qtsingleapplication.cpp + kdsingleapplicationguard/kdsingleapplicationguard.cpp + kdsingleapplicationguard/kdsharedmemorylocker.cpp + kdsingleapplicationguard/kdtoolsglobal.cpp + kdsingleapplicationguard/kdlockedsharedmemorypointer.cpp ) set( libHeaders @@ -289,8 +291,10 @@ set( libHeaders widgets/overlaywidget.h widgets/infowidgets/sourceinfowidget.h - qtsingleapp/qtlocalpeer.h - qtsingleapp/qtsingleapplication.h + kdsingleapplicationguard/kdsingleapplicationguard.h + kdsingleapplicationguard/kdsharedmemorylocker.h + kdsingleapplicationguard/kdtoolsglobal.h + kdsingleapplicationguard/kdlockedsharedmemorypointer.h ) set( libHeaders_NoMOC diff --git a/src/libtomahawk/aclsystem.cpp b/src/libtomahawk/aclsystem.cpp index 7b7d70e1f..15c75a407 100644 --- a/src/libtomahawk/aclsystem.cpp +++ b/src/libtomahawk/aclsystem.cpp @@ -124,7 +124,7 @@ void ACLSystem::authorizePath( const QString& dbid, const QString& path, ACLSystem::ACL type ) { TomahawkSettings *s = TomahawkSettings::instance(); - if( !s->scannerPath().contains( path ) ) + if( !s->scannerPaths().contains( path ) ) { qDebug() << "path selected is not in our scanner path!"; return; diff --git a/src/libtomahawk/database/database.cpp b/src/libtomahawk/database/database.cpp index 46e61badb..4436f6cfa 100644 --- a/src/libtomahawk/database/database.cpp +++ b/src/libtomahawk/database/database.cpp @@ -32,11 +32,16 @@ Database::instance() Database::Database( const QString& dbname, QObject* parent ) : QObject( parent ) + , m_ready( false ) , m_impl( new DatabaseImpl( dbname, this ) ) , m_workerRW( new DatabaseWorker( m_impl, this, true ) ) { s_instance = this; + connect( m_impl, SIGNAL( indexReady() ), SIGNAL( indexReady() ) ); + connect( m_impl, SIGNAL( indexReady() ), SIGNAL( ready() ) ); + connect( m_impl, SIGNAL( indexReady() ), SLOT( setIsReadyTrue() ) ); + m_workerRW->start(); } diff --git a/src/libtomahawk/database/database.h b/src/libtomahawk/database/database.h index 498f5239b..a6890fbb6 100644 --- a/src/libtomahawk/database/database.h +++ b/src/libtomahawk/database/database.h @@ -41,6 +41,7 @@ class DLLEXPORT Database : public QObject { Q_OBJECT + public: static Database* instance(); @@ -51,16 +52,24 @@ public: const bool indexReady() const { return m_indexReady; } void loadIndex(); + + bool isReady() const { return m_ready; } signals: void indexReady(); // search index + void ready(); + void newJobRO( QSharedPointer ); void newJobRW( QSharedPointer ); public slots: void enqueue( QSharedPointer lc ); +private slots: + void setIsReadyTrue() { m_ready = true; } + private: + bool m_ready; DatabaseImpl* m_impl; DatabaseWorker* m_workerRW; QHash< QString, DatabaseWorker* > m_workers; diff --git a/src/libtomahawk/database/databasecommand_deletefiles.h b/src/libtomahawk/database/databasecommand_deletefiles.h index 4668d0249..53d16f314 100644 --- a/src/libtomahawk/database/databasecommand_deletefiles.h +++ b/src/libtomahawk/database/databasecommand_deletefiles.h @@ -45,6 +45,12 @@ public: setSource( source ); } + explicit DatabaseCommand_DeleteFiles( const QVariantList& ids, const Tomahawk::source_ptr& source, QObject* parent = 0 ) + : DatabaseCommandLoggable( parent ), m_ids( ids ) + { + setSource( source ); + } + virtual QString commandname() const { return "deletefiles"; } virtual void exec( DatabaseImpl* ); diff --git a/src/libtomahawk/database/databasecommand_dirmtimes.cpp b/src/libtomahawk/database/databasecommand_dirmtimes.cpp index 829a609c8..07fbad637 100644 --- a/src/libtomahawk/database/databasecommand_dirmtimes.cpp +++ b/src/libtomahawk/database/databasecommand_dirmtimes.cpp @@ -38,21 +38,35 @@ DatabaseCommand_DirMtimes::execSelect( DatabaseImpl* dbi ) { QMap mtimes; TomahawkSqlQuery query = dbi->newquery(); - if( m_prefix.isEmpty() ) + if( m_prefix.isEmpty() && m_prefixes.isEmpty() ) query.exec( "SELECT name, mtime FROM dirs_scanned" ); + else if( m_prefixes.isEmpty() ) + execSelectPath( dbi, m_prefix, mtimes ); else { - query.prepare( QString( "SELECT name, mtime " - "FROM dirs_scanned " - "WHERE name LIKE '%1%'" ).arg( m_prefix.replace( '\'',"''" ) ) ); - query.exec(); + if( !m_prefix.isEmpty() ) + execSelectPath( dbi, m_prefix, mtimes ); + foreach( QString path, m_prefixes ) + execSelectPath( dbi, path, mtimes ); } + emit done( mtimes ); +} + +void +DatabaseCommand_DirMtimes::execSelectPath( DatabaseImpl *dbi, const QDir& path, QMap &mtimes ) +{ + TomahawkSqlQuery query = dbi->newquery(); + query.prepare( QString( "SELECT name, mtime " + "FROM dirs_scanned " + "WHERE name LIKE :prefix" ) ); + + query.bindValue( ":prefix", path.absolutePath() + "%" ); + query.exec(); + while( query.next() ) { mtimes.insert( query.value( 0 ).toString(), query.value( 1 ).toUInt() ); } - - emit done( mtimes ); } diff --git a/src/libtomahawk/database/databasecommand_dirmtimes.h b/src/libtomahawk/database/databasecommand_dirmtimes.h index f35a78437..9677a2317 100644 --- a/src/libtomahawk/database/databasecommand_dirmtimes.h +++ b/src/libtomahawk/database/databasecommand_dirmtimes.h @@ -34,9 +34,13 @@ class DLLEXPORT DatabaseCommand_DirMtimes : public DatabaseCommand Q_OBJECT public: - explicit DatabaseCommand_DirMtimes( const QString& prefix = "", QObject* parent = 0 ) + explicit DatabaseCommand_DirMtimes( const QString& prefix = QString(), QObject* parent = 0 ) : DatabaseCommand( parent ), m_prefix( prefix ), m_update( false ) {} + + explicit DatabaseCommand_DirMtimes( const QStringList& prefixes = QStringList(), QObject* parent = 0 ) + : DatabaseCommand( parent ), m_prefixes( prefixes ), m_update( false ) + {} explicit DatabaseCommand_DirMtimes( QMap tosave, QObject* parent = 0 ) : DatabaseCommand( parent ), m_update( true ), m_tosave( tosave ) @@ -52,9 +56,12 @@ signals: public slots: private: + void execSelectPath( DatabaseImpl *dbi, const QDir& path, QMap &mtimes ); + void execSelect( DatabaseImpl* dbi ); void execUpdate( DatabaseImpl* dbi ); QString m_prefix; + QStringList m_prefixes; bool m_update; QMap m_tosave; }; diff --git a/src/libtomahawk/database/databasecommand_loaddynamicplaylist.cpp b/src/libtomahawk/database/databasecommand_loaddynamicplaylist.cpp index eb80312b0..f8b449e61 100644 --- a/src/libtomahawk/database/databasecommand_loaddynamicplaylist.cpp +++ b/src/libtomahawk/database/databasecommand_loaddynamicplaylist.cpp @@ -52,9 +52,9 @@ DatabaseCommand_LoadDynamicPlaylist::exec( DatabaseImpl* dbi ) QList< QVariantMap > controls; QString playlist_guid; qDebug() << "Loading controls..." << revisionGuid(); - qDebug() << "SELECT playlist_revision.playlist, controls, plmode, pltype " - "FROM dynamic_playlist_revision, playlist_revision " - "WHERE dynamic_playlist_revision.guid = "<< revisionGuid() << " AND playlist_revision.guid = dynamic_playlist_revision.guid"; +// qDebug() << "SELECT playlist_revision.playlist, controls, plmode, pltype " +// "FROM dynamic_playlist_revision, playlist_revision " +// "WHERE dynamic_playlist_revision.guid = "<< revisionGuid() << " AND playlist_revision.guid = dynamic_playlist_revision.guid"; if( controlsQuery.first() ) { playlist_guid = controlsQuery.value( 0 ).toString(); diff --git a/src/libtomahawk/database/databasecommand_loadplaylistentries.cpp b/src/libtomahawk/database/databasecommand_loadplaylistentries.cpp index 20143dd53..f29dc8a40 100644 --- a/src/libtomahawk/database/databasecommand_loadplaylistentries.cpp +++ b/src/libtomahawk/database/databasecommand_loadplaylistentries.cpp @@ -45,7 +45,7 @@ DatabaseCommand_LoadPlaylistEntries::generateEntries( DatabaseImpl* dbi ) query_entries.bindValue( ":guid", m_revguid ); query_entries.exec(); - qDebug() << "trying to load entries:" << m_revguid; +// qDebug() << "trying to load entries:" << m_revguid; QString prevrev; QJson::Parser parser; bool ok; @@ -55,7 +55,6 @@ DatabaseCommand_LoadPlaylistEntries::generateEntries( DatabaseImpl* dbi ) QVariant v = parser.parse( query_entries.value(0).toByteArray(), &ok ); Q_ASSERT( ok && v.type() == QVariant::List ); //TODO m_guids = v.toStringList(); - // qDebug() << "Entries:" << guids; QString inclause = QString("('%1')").arg(m_guids.join("', '")); @@ -115,5 +114,5 @@ DatabaseCommand_LoadPlaylistEntries::generateEntries( DatabaseImpl* dbi ) m_islatest = query_entries_old.value( 1 ).toBool(); } - qDebug() << Q_FUNC_INFO << "entrymap:" << m_entrymap; +// qDebug() << Q_FUNC_INFO << "entrymap:" << m_entrymap; } diff --git a/src/libtomahawk/database/databasecommand_logplayback.cpp b/src/libtomahawk/database/databasecommand_logplayback.cpp index 9254ea979..6443b2216 100644 --- a/src/libtomahawk/database/databasecommand_logplayback.cpp +++ b/src/libtomahawk/database/databasecommand_logplayback.cpp @@ -44,7 +44,8 @@ DatabaseCommand_LogPlayback::postCommitHook() connect( this, SIGNAL( trackPlayed( Tomahawk::query_ptr ) ), source().data(), SLOT( onPlaybackFinished( Tomahawk::query_ptr ) ), Qt::QueuedConnection ); - Tomahawk::query_ptr q = Tomahawk::Query::get( m_artist, m_track, QString(), uuid() ); + // do not auto resolve this track + Tomahawk::query_ptr q = Tomahawk::Query::get( m_artist, m_track, QString() ); if ( m_action == Finished ) { diff --git a/src/libtomahawk/database/databaseimpl.cpp b/src/libtomahawk/database/databaseimpl.cpp index 485c7a5ef..ff96c3d7d 100644 --- a/src/libtomahawk/database/databaseimpl.cpp +++ b/src/libtomahawk/database/databaseimpl.cpp @@ -46,8 +46,6 @@ DatabaseImpl::DatabaseImpl( const QString& dbname, Database* parent ) , m_lastalbid( 0 ) , m_lasttrkid( 0 ) { - connect( this, SIGNAL( indexReady() ), parent, SIGNAL( indexReady() ) ); - db = QSqlDatabase::addDatabase( "QSQLITE", "tomahawk" ); db.setDatabaseName( dbname ); if ( !db.open() ) diff --git a/src/libtomahawk/database/databaseimpl.h b/src/libtomahawk/database/databaseimpl.h index 579850b7e..e2965f553 100644 --- a/src/libtomahawk/database/databaseimpl.h +++ b/src/libtomahawk/database/databaseimpl.h @@ -84,6 +84,8 @@ signals: public slots: private: + bool m_ready; + bool updateSchema( int currentver ); QSqlDatabase db; diff --git a/src/libtomahawk/kdsingleapplicationguard/kdlockedsharedmemorypointer.cpp b/src/libtomahawk/kdsingleapplicationguard/kdlockedsharedmemorypointer.cpp new file mode 100644 index 000000000..e1fe10a4d --- /dev/null +++ b/src/libtomahawk/kdsingleapplicationguard/kdlockedsharedmemorypointer.cpp @@ -0,0 +1,475 @@ +#include "kdlockedsharedmemorypointer.h" + +#if QT_VERSION >= 0x040400 || defined( DOXYGEN_RUN ) +#ifndef QT_NO_SHAREDMEMORY + +namespace kdtools +{ +} +using namespace kdtools; + +KDLockedSharedMemoryPointerBase::KDLockedSharedMemoryPointerBase( QSharedMemory * m ) + : locker( m ), + mem( m ) +{ + +} + +KDLockedSharedMemoryPointerBase::KDLockedSharedMemoryPointerBase( QSharedMemory & m ) + : locker( &m ), + mem( &m ) +{ + +} + +KDLockedSharedMemoryPointerBase::~KDLockedSharedMemoryPointerBase() {} + +void * KDLockedSharedMemoryPointerBase::get() { + return mem ? mem->data() : 0 ; +} + +const void * KDLockedSharedMemoryPointerBase::get() const { + return mem ? mem->data() : 0 ; +} + +size_t KDLockedSharedMemoryPointerBase::byteSize() const { + return mem->size(); +} + +/*! + \class KDLockedSharedMemoryPointer + \ingroup core raii smartptr + \brief Locking pointer for Qt shared memory segments + \since_c 2.1 + + (The exception safety of this class has not been evaluated yet.) + + KDLockedSharedMemoryPointer is a smart immutable pointer, which gives convenient and safe access to a QSharedMemory data segment. + The content of a KDLockedSharedMemoryPointer cannot be changed during it's lifetime. + + You can use this class like a normal pointer to the shared memory segment and be sure it's locked while accessing it. + \note You can only put simple types/structs/classes into it. structs and classes shall not contain any other pointers. See the + documentation of QSharedMemory for details. +*/ + +/*! + \fn KDLockedSharedMemoryPointer::KDLockedSharedMemoryPointer( QSharedMemory * mem ) + + Constructor. Constructs a KDLockedSharedMemory pointer which points to the data segment of \a mem. + The constructor locks \a mem. If the memory segment is already locked by another process, this constructor + blocks until the lock is released. + + \post data() == mem->data() and the memory segment has been locked +*/ + +/*! + \fn KDLockedSharedMemoryPointer::KDLockedSharedMemoryPointer( QSharedMemory & mem ) + + \overload + + \post data() == mem.data() and the memory segment has been locked +*/ + +/*! + \fn KDLockedSharedMemoryPointer::~KDLockedSharedMemoryPointer() + + Destructor. Unlocks the shared memory segment. + + \post The shared memory segment has been unlocked +*/ + +/*! + \fn T * KDLockedSharedMemoryPointer::get() + + \returns a pointer to the contained object. +*/ + +/*! + \fn const T * KDLockedSharedMemoryPointer::get() const + + \returns a const pointer to the contained object + \overload +*/ + +/*! + \fn T * KDLockedSharedMemoryPointer::data() + + Equivalent to get(), provided for consistency with Qt naming conventions. +*/ + +/*! + \fn const T * KDLockedSharedMemoryPointer::data() const + + \overload +*/ + +/*! + \fn T & KDLockedSharedMemoryPointer::operator*() + + Dereference operator. Returns \link get() *get()\endlink. +*/ + +/*! + \fn const T & KDLockedSharedMemoryPointer::operator*() const + + Dereference operator. Returns \link get() *get()\endlink. + \overload +*/ + +/*! + \fn T * KDLockedSharedMemoryPointer::operator->() + + Member-by-pointer operator. Returns get(). +*/ + +/*! + \fn const T * KDLockedSharedMemoryPointer::operator->() const + + Member-by-pointer operator. Returns get(). + \overload +*/ + +/*! + \class KDLockedSharedMemoryArray + \ingroup core raii smartptr + \brief Locking array pointer to Qt shared memory segments + \since_c 2.1 + + (The exception safety of this class has not been evaluated yet.) + + KDLockedSharedMemoryArray is a smart immutable pointer, which gives convenient and safe access to array data stored in a QSharedMemory + data segment. + The content of a KDLockedSharedMemoryArray cannot be changed during it's lifetime. + + You can use this class like a normal pointer to the shared memory segment and be sure it's locked while accessing it. + \note You can only put arrays of simple types/structs/classes into it. structs and classes shall not contain any other pointers. See the + documentation of QSharedMemory for details. + + \sa KDLockedSharedMemoryPointer +*/ + +/*! + \fn KDLockedSharedMemoryArray::KDLockedSharedMemoryArray( QSharedMemory* mem ) + Constructor. Constructs a KDLockedSharedMemoryArray which points to the data segment of \a mem. The constructor locks \a mem. If the memory + segment is already locked by another process, this constructor blocks until the lock is release. + + \post get() == mem->data() and the memory segment has been locked +*/ + +/*! + \fn KDLockedSharedMemoryArray::KDLockedSharedMemoryArray( QSharedMemory& mem ) + \overload + + \post get() == mem->data() and the memory segment has been locked +*/ + + +/*! + \typedef KDLockedSharedMemoryArray::size_type + Typedef for std::size_t. Provided for STL compatibility. +*/ + +/*! + \typedef KDLockedSharedMemoryArray::difference_type + Typedef for std::ptrdiff_t. Provided for STL compatibility. +*/ + +/*! + \typedef KDLockedSharedMemoryArray::iterator + Typedef for T*. Provided for STL compatibility. + \since_t 2.2 +*/ + +/*! + \typedef KDLockedSharedMemoryArray::const_iterator + Typedef for const T*. Provided for STL compatibility. + \since_t 2.2 +*/ + +/*! + \typedef KDLockedSharedMemoryArray::reverse_iterator + Typedef for std::reverse_iterator< \link KDLockedSharedMemoryArray::iterator iterator\endlink >. Provided for STL compatibility. + \since_t 2.2 +*/ + +/*! + \typedef KDLockedSharedMemoryArray::const_reverse_iterator + Typedef for std::reverse_iterator< \link KDLockedSharedMemoryArray::const_iterator const_iterator\endlink >. Provided for STL compatibility. + \since_t 2.2 +*/ + +/*! + \fn KDLockedSharedMemoryArray::iterator KDLockedSharedMemoryArray::begin() + Returns an \link KDLockedSharedMemoryArray::iterator iterator\endlink pointing to the first item of the array. + \since_f 2.2 +*/ + +/*! + \fn KDLockedSharedMemoryArray::const_iterator KDLockedSharedMemoryArray::begin() const + \overload + \since_f 2.2 +*/ + +/*! + \fn KDLockedSharedMemoryArray::iterator KDLockedSharedMemoryArray::end() + Returns an \link KDLockedSharedMemoryArray::iterator iterator\endlink pointing to the item after the last item of the array. + \since_f 2.2 +*/ + +/*! + \fn KDLockedSharedMemoryArray::const_iterator KDLockedSharedMemoryArray::end() const + \overload + \since_f 2.2 +*/ + +/*! + \fn KDLockedSharedMemoryArray::reverse_iterator KDLockedSharedMemoryArray::rbegin() + Returns an \link KDLockedSharedMemoryArray::reverse_iterator reverse_iterator\endlink pointing to the item after the last item of the array. + \since_f 2.2 +*/ + +/*! + \fn KDLockedSharedMemoryArray::const_reverse_iterator KDLockedSharedMemoryArray::rbegin() const + \overload + \since_f 2.2 +*/ + +/*! + \fn KDLockedSharedMemoryArray::reverse_iterator KDLockedSharedMemoryArray::rend() + Returns an \link KDLockedSharedMemoryArray::reverse_iterator reverse_iterator\endlink pointing to the first item of the array. + \since_f 2.2 +*/ + +/*! + \fn KDLockedSharedMemoryArray::const_reverse_iterator KDLockedSharedMemoryArray::rend() const + \overload + \since_f 2.2 +*/ + +/*! + \fn KDLockedSharedMemoryArray::size_type KDLockedSharedMemoryArray::size() const + Returns the size of this array. The size is calculated from the storage size of T and + the size of the shared memory segment. + \since_f 2.2 +*/ + +/*! + \fn T& KDLockedSharedMemoryArray::operator[]( difference_type n ) + Array access operator. Returns a reference to the item at index position \a n. +*/ + +/*! + \fn const T& KDLockedSharedMemoryArray::operator[]( difference_type n ) const + \overload +*/ + +/*! + \fn T& KDLockedSharedMemoryArray::front() + Returns a reference to the first item in the array. This is the same as operator[](0). +*/ + +/*! + \fn const T& KDLockedSharedMemoryArray::front() const + \overload +*/ + +/*! + \fn T& KDLockedSharedMemoryArray::back() + Returns a reference to the last item in the array. This is the same as operator[](size()-1). + \since_f 2.2 +*/ + +/*! + \fn const T& KDLockedSharedMemoryArray::back() const + \overload + \since_f 2.2 +*/ + + +#ifdef eKDTOOLSCORE_UNITTESTS + +#include + +#include +#include + +namespace +{ + struct TestStruct + { + TestStruct( uint nn = 0 ) + : n( nn ), + f( 0.0 ), + c( '\0' ), + b( false ) + { + } + uint n; + double f; + char c; + bool b; + }; + + bool operator==( const TestStruct& lhs, const TestStruct& rhs ) + { + return lhs.n == rhs.n && lhs.f == rhs.f && lhs.c == rhs.c && lhs.b == rhs.b; + } + + class TestThread : public QThread + { + public: + TestThread( const QString& key ) + : mem( key ) + { + mem.attach(); + } + + void run() + { + while( true ) + { + msleep( 100 ); + kdtools::KDLockedSharedMemoryPointer< TestStruct > p( &mem ); + if( !p->b ) + continue; + + p->n = 5; + p->f = 3.14; + p->c = 'A'; + p->b = false; + return; + } + } + + QSharedMemory mem; + }; + + bool isConst( TestStruct* ) + { + return false; + } + + bool isConst( const TestStruct* ) + { + return true; + } +} + + +KDAB_UNITTEST_SIMPLE( KDLockedSharedMemoryPointer, "kdcoretools" ) { + + const QString key = QUuid::createUuid(); + QSharedMemory mem( key ); + const bool created = mem.create( sizeof( TestStruct ) ); + assertTrue( created ); + if ( !created ) + return; // don't execute tests if shm coulnd't be created + + // On Windows, shared mem is only available in increments of page + // size (4k), so don't fail if the segment is larger: + const unsigned long mem_size = mem.size(); + assertGreaterOrEqual( mem_size, sizeof( TestStruct ) ); + + { + kdtools::KDLockedSharedMemoryPointer< TestStruct > p( &mem ); + assertTrue( p ); + *p = TestStruct(); + assertEqual( p->n, 0u ); + assertEqual( p->f, 0.0 ); + assertEqual( p->c, '\0' ); + assertFalse( p->b ); + } + + { + TestThread thread( key ); + assertEqual( thread.mem.key().toStdString(), key.toStdString() ); + assertEqual( static_cast< unsigned long >( thread.mem.size() ), mem_size ); + thread.start(); + + assertTrue( thread.isRunning() ); + thread.wait( 2000 ); + assertTrue( thread.isRunning() ); + + { + kdtools::KDLockedSharedMemoryPointer< TestStruct > p( &mem ); + p->b = true; + } + + thread.wait( 2000 ); + assertFalse( thread.isRunning() ); + } + + { + kdtools::KDLockedSharedMemoryPointer< TestStruct > p( &mem ); + assertEqual( p->n, 5u ); + assertEqual( p->f, 3.14 ); + assertEqual( p->c, 'A' ); + assertFalse( p->b ); + } + + { + kdtools::KDLockedSharedMemoryPointer< TestStruct > p( mem ); + assertEqual( mem.data(), p.get() ); + assertEqual( p.get(), p.operator->() ); + assertEqual( p.get(), &(*p) ); + assertEqual( p.get(), p.data() ); + assertFalse( isConst( p.get() ) ); + } + + { + const kdtools::KDLockedSharedMemoryPointer< TestStruct > p( &mem ); + assertEqual( mem.data(), p.get() ); + assertEqual( p.get(), p.operator->() ); + assertEqual( p.get(), &(*p) ); + assertEqual( p.get(), p.data() ); + assertTrue( isConst( p.get() ) ); + } + + { + QSharedMemory mem2( key + key ); + const bool created2 = mem2.create( 16 * sizeof( TestStruct ) ); + assertTrue( created2 ); + if ( !created2 ) + return; // don't execute tests if shm coulnd't be created + + kdtools::KDLockedSharedMemoryArray a( mem2 ); + assertTrue( a ); + assertEqual( a.get(), mem2.data() ); + assertEqual( &a[0], a.get() ); + + a[1] = a[0]; + assertTrue( a[0] == a[1] ); + + TestStruct ts; + ts.n = 5; + ts.f = 3.14; + a[0] = ts; + assertFalse( a[0] == a[1] ); + assertEqual( a.front().n, ts.n ); + assertEqual( a[0].f, ts.f ); + a[0].n = 10; + assertEqual( a.front().n, 10u ); + ts = a[0]; + assertEqual( ts.n, 10u ); + + std::vector< TestStruct > v; + for( uint i = 0; i < a.size(); ++i ) + v.push_back( TestStruct( i ) ); + + std::copy( v.begin(), v.end(), a.begin() ); + for( uint i = 0; i < a.size(); ++i ) + assertEqual( a[ i ].n, i ); + assertEqual( a.front().n, 0u ); + assertEqual( a.back().n, a.size() - 1 ); + + std::copy( v.begin(), v.end(), a.rbegin() ); + for( uint i = 0; i < a.size(); ++i ) + assertEqual( a[ i ].n, a.size() - 1 - i ); + assertEqual( a.front().n, a.size() - 1 ); + assertEqual( a.back().n, 0u ); + } + +} +#endif // KDTOOLSCORE_UNITTESTS +#endif // QT_NO_SHAREDMEMORY +#endif // QT_VERSION >= 0x040400 || defined( DOXYGEN_RUN ) diff --git a/src/libtomahawk/kdsingleapplicationguard/kdlockedsharedmemorypointer.h b/src/libtomahawk/kdsingleapplicationguard/kdlockedsharedmemorypointer.h new file mode 100644 index 000000000..df0ea4998 --- /dev/null +++ b/src/libtomahawk/kdsingleapplicationguard/kdlockedsharedmemorypointer.h @@ -0,0 +1,115 @@ +#ifndef __KDTOOLS__CORE__KDLOCKEDSHAREDMEMORYPOINTER_H__ +#define __KDTOOLS__CORE__KDLOCKEDSHAREDMEMORYPOINTER_H__ + +#include + +#if QT_VERSION >= 0x040400 || defined( DOXYGEN_RUN ) +#ifndef QT_NO_SHAREDMEMORY + +#include "kdsharedmemorylocker.h" +#include + +#include + +#ifndef DOXYGEN_RUN +namespace kdtools { +#endif + +class KDLockedSharedMemoryPointerBase { +protected: + explicit KDLockedSharedMemoryPointerBase( QSharedMemory * mem ); + explicit KDLockedSharedMemoryPointerBase( QSharedMemory & mem ); + ~KDLockedSharedMemoryPointerBase(); + + // PENDING(marc) do we really want const propagation here? I + // usually declare all my RAII objects const... + void * get(); + const void * get() const; + + KDAB_IMPLEMENT_SAFE_BOOL_OPERATOR( get() ) + + size_t byteSize() const; + +private: + KDSharedMemoryLocker locker; + QSharedMemory * const mem; +}; + +template< typename T> +class MAKEINCLUDES_EXPORT KDLockedSharedMemoryPointer : KDLockedSharedMemoryPointerBase { + KDAB_DISABLE_COPY( KDLockedSharedMemoryPointer ); +public: + explicit KDLockedSharedMemoryPointer( QSharedMemory * m ) + : KDLockedSharedMemoryPointerBase( m ) {} + explicit KDLockedSharedMemoryPointer( QSharedMemory & m ) + : KDLockedSharedMemoryPointerBase( m ) {} + + T * get() { return static_cast( KDLockedSharedMemoryPointerBase::get() ); } + const T * get() const { return static_cast( KDLockedSharedMemoryPointerBase::get() ); } + + T * data() { return static_cast( get() ); } + const T * data() const { return static_cast( get() ); } + + T & operator*() { assert( get() ); return *get(); } + const T & operator*() const { assert( get() ); return *get(); } + + T * operator->() { return get(); } + const T * operator->() const { return get(); } + + KDAB_USING_SAFE_BOOL_OPERATOR( KDLockedSharedMemoryPointerBase ) +}; + +template +class MAKEINCLUDES_EXPORT KDLockedSharedMemoryArray : KDLockedSharedMemoryPointerBase { + KDAB_DISABLE_COPY( KDLockedSharedMemoryArray ); +public: + explicit KDLockedSharedMemoryArray( QSharedMemory * m ) + : KDLockedSharedMemoryPointerBase( m ) {} + explicit KDLockedSharedMemoryArray( QSharedMemory & m ) + : KDLockedSharedMemoryPointerBase( m ) {} + + typedef std::size_t size_type; + typedef std::ptrdiff_t difference_type; + typedef T* iterator; + typedef const T* const_iterator; + typedef std::reverse_iterator< const_iterator > const_reverse_iterator; + typedef std::reverse_iterator< iterator > reverse_iterator; + + iterator begin() { return get(); } + const_iterator begin() const { return get(); } + + iterator end() { return begin() + size(); } + const_iterator end() const { return begin() + size(); } + + reverse_iterator rbegin() { return reverse_iterator( end() ); } + const_reverse_iterator rbegin() const { return reverse_iterator( end() ); } + + reverse_iterator rend() { return reverse_iterator( begin() ); } + const_reverse_iterator rend() const { return const_reverse_iterator( begin() ); } + + size_type size() const { return byteSize() / sizeof( T ); } + + T * get() { return static_cast( KDLockedSharedMemoryPointerBase::get() ); } + const T * get() const { return static_cast( KDLockedSharedMemoryPointerBase::get() ); } + + T & operator[]( difference_type n ) { assert( get() ); return *(get()+n); } + const T & operator[]( difference_type n ) const { assert( get() ); return *(get()+n); } + + T & front() { assert( get() ); return *get(); } + const T & front() const { assert( get() ); return *get(); } + + T & back() { assert( get() ); return *( get() + size() - 1 ); } + const T & back() const { assert( get() ); return *( get() + size() - 1 ); } + + KDAB_USING_SAFE_BOOL_OPERATOR( KDLockedSharedMemoryPointerBase ) +}; + +#ifndef DOXYGEN_RUN +} +#endif + +#endif /* QT_NO_SHAREDMEMORY */ + +#endif /* QT_VERSION >= 0x040400 || defined( DOXYGEN_RUN ) */ + +#endif /* __KDTOOLS__CORE__KDLOCKEDSHAREDMEMORYPOINTER_H__ */ diff --git a/src/libtomahawk/kdsingleapplicationguard/kdsharedmemorylocker.cpp b/src/libtomahawk/kdsingleapplicationguard/kdsharedmemorylocker.cpp new file mode 100644 index 000000000..0c99b8fff --- /dev/null +++ b/src/libtomahawk/kdsingleapplicationguard/kdsharedmemorylocker.cpp @@ -0,0 +1,40 @@ +#include "kdsharedmemorylocker.h" + +#if QT_VERSION >= 0x040400 || defined( DOXYGEN_RUN ) + +#include + +using namespace kdtools; + +/*! + \class KDSharedMemoryLocker + \ingroup raii core + \brief Exception-safe and convenient wrapper around QSharedMemory::lock() +*/ + +/** + * Constructor. Locks the shared memory segment \a mem. + * If another process has locking the segment, this constructor blocks + * until the lock is released. The memory segments needs to be properly created or attached. + */ +KDSharedMemoryLocker::KDSharedMemoryLocker( QSharedMemory* mem ) + : mem( mem ) +{ + mem->lock(); +} + +/** + * Destructor. Unlocks the shared memory segment associated with this + * KDSharedMemoryLocker. + */ +KDSharedMemoryLocker::~KDSharedMemoryLocker() +{ + mem->unlock(); +} + +#ifdef KDAB_EVAL +#include KDAB_EVAL +static const EvalDialogChecker evalChecker( "KD Tools", false ); +#endif + +#endif diff --git a/src/libtomahawk/kdsingleapplicationguard/kdsharedmemorylocker.h b/src/libtomahawk/kdsingleapplicationguard/kdsharedmemorylocker.h new file mode 100644 index 000000000..7ae83e771 --- /dev/null +++ b/src/libtomahawk/kdsingleapplicationguard/kdsharedmemorylocker.h @@ -0,0 +1,36 @@ +#ifndef __KDTOOLS__CORE__KDSHAREDMEMORYLOCKER_H +#define __KDTOOLS__CORE__KDSHAREDMEMORYLOCKER_H + +#include "kdtoolsglobal.h" + +#if QT_VERSION < 0x040400 && !defined( DOXYGEN_RUN ) +#ifdef Q_CC_GNU +#warning "Can't use KDTools KDSharedMemoryLocker with Qt versions prior to 4.4" +#endif +#else + +class QSharedMemory; + +#ifndef DOXYGEN_RUN +namespace kdtools +{ +#endif + +class KDTOOLSCORE_EXPORT KDSharedMemoryLocker +{ + Q_DISABLE_COPY( KDSharedMemoryLocker ) +public: + KDSharedMemoryLocker( QSharedMemory* mem ); + ~KDSharedMemoryLocker(); + +private: + QSharedMemory* const mem; +}; + +#ifndef DOXYGEN_RUN +} +#endif + +#endif + +#endif diff --git a/src/libtomahawk/kdsingleapplicationguard/kdsingleapplicationguard.cpp b/src/libtomahawk/kdsingleapplicationguard/kdsingleapplicationguard.cpp new file mode 100644 index 000000000..63893dbde --- /dev/null +++ b/src/libtomahawk/kdsingleapplicationguard/kdsingleapplicationguard.cpp @@ -0,0 +1,622 @@ +#include "kdsingleapplicationguard.h" + +#ifndef KDSINGLEAPPLICATIONGUARD_NUMBER_OF_PROCESSES +#define KDSINGLEAPPLICATIONGUARD_NUMBER_OF_PROCESSES 128 +#endif + +#ifndef KDSINGLEAPPLICATIONGUARD_MAX_COMMAND_LINE +#define KDSINGLEAPPLICATIONGUARD_MAX_COMMAND_LINE 1024 +#endif + + + +KDSingleApplicationGuard::Instance::Instance( const QStringList& args, qint64 p ) + : arguments( args ), + pid( p ) +{ +} + +#if QT_VERSION < 0x040400 + +class KDSingleApplicationGuard::Private +{ +}; + +KDSingleApplicationGuard::KDSingleApplicationGuard( QCoreApplication*, Policy ) +{ + qWarning( "KD Tools was compiled with a Qt version prior to 4.4. SingleApplicationGuard won't work." ); +} + +KDSingleApplicationGuard::~KDSingleApplicationGuard() +{ +} + +void KDSingleApplicationGuard::shutdownOtherInstances() +{ +} + +void KDSingleApplicationGuard::killOtherInstances() +{ +} + +void KDSingleApplicationGuard::timerEvent( QTimerEvent* ) +{ +} +#else + +#include +#include + +#include "kdsharedmemorylocker.h" +#include "kdlockedsharedmemorypointer.h" + +#include +#include +#include + +#ifndef Q_WS_WIN +#include +#endif + +using namespace kdtools; + +/*! + \class KDSingleApplicationGuard KDSingleApplicationGuard + \brief A guard to protect an application from having several instances. + + KDSingleApplicationGuard can be used to make sure only one instance of an + application is running at the same time. + + \note As KDSingleApplicationGuard uses QSharedMemory Qt 4.4 or later is required + */ + +/*! + \fn void KDSingleApplicationGuard::instanceStarted() + This signal is emitted by the primary instance when ever one other + instance was started. + */ + +/*! + \fn void KDSingleApplicationGuard::instanceExited() + This signal is emitted by the primary instance when ever one other + instance was exited. + */ + +/*! + \fn void KDSingleApplicationGuard::becamePrimaryInstance() + This signal is emitted, when the current running application gets the new + primary application. The old primary application has quit. + */ + +enum Command +{ + NoCommand = 0x00, + ExitedInstance = 0x01, + NewInstance = 0x02, + FreeInstance = 0x04, + ShutDownCommand = 0x08, + KillCommand = 0x10, + BecomePrimaryCommand = 0x20 +}; + +Q_DECLARE_FLAGS( Commands, Command ) +Q_DECLARE_OPERATORS_FOR_FLAGS( Commands ) + +struct ProcessInfo +{ + explicit ProcessInfo( Command c = FreeInstance, const QStringList& arguments = QStringList(), qint64 p = -1 ) + : command( c ), + pid( p ) + { + std::fill_n( commandline, KDSINGLEAPPLICATIONGUARD_MAX_COMMAND_LINE, '\0' ); + + int argpos = 0; + for( QStringList::const_iterator it = arguments.begin(); it != arguments.end(); ++it ) + { + const QByteArray arg = it->toLatin1(); + const int count = qMin( KDSINGLEAPPLICATIONGUARD_MAX_COMMAND_LINE - argpos, arg.count() ); + std::copy( arg.begin(), arg.begin() + count, commandline + argpos ); + argpos += arg.count() + 1; // makes sure there's a \0 between every parameter + } + } + + QStringList arguments() const + { + QStringList result; + + QByteArray arg; + for( int i = 0; i < KDSINGLEAPPLICATIONGUARD_MAX_COMMAND_LINE; ++i ) + { + if( commandline[ i ] == '\0' && !arg.isEmpty() ) + { + result.push_back( QString::fromLatin1( arg ) ); + arg.clear(); + } + else if( !commandline[ i ] == '\0' ) + { + arg.push_back( commandline[ i ] ); + } + } + + return result; + } + + Commands command; + char commandline[ KDSINGLEAPPLICATIONGUARD_MAX_COMMAND_LINE ]; + qint64 pid; +}; + +bool operator==( const ProcessInfo& lhs, const ProcessInfo& rhs ) +{ + return lhs.command == rhs.command && + ::memcmp( lhs.commandline, rhs.commandline, KDSINGLEAPPLICATIONGUARD_MAX_COMMAND_LINE ) == 0; +} + +bool operator!=( const ProcessInfo& lhs, const ProcessInfo& rhs ) +{ + return !operator==( lhs, rhs ); +} + +/*! + This struct contains information about the managed process system. + \internal + */ +struct InstanceRegister +{ + InstanceRegister( KDSingleApplicationGuard::Policy policy = KDSingleApplicationGuard::NoPolicy ) + : policy( policy ) + { + std::fill_n( info, KDSINGLEAPPLICATIONGUARD_NUMBER_OF_PROCESSES, ProcessInfo() ); + ::memcpy( magicCookie, "kdsingleapp", 12 ); + } + + /*! + Returns wheter this register was properly initialized by the first instance. + */ + bool isValid() const + { + return ::strcmp( magicCookie, "kdsingleapp" ) == 0; + } + + ProcessInfo info[ KDSINGLEAPPLICATIONGUARD_NUMBER_OF_PROCESSES ]; + KDSingleApplicationGuard::Policy policy; + char magicCookie[ 12 ]; +}; + +bool operator==( const InstanceRegister& lhs, const InstanceRegister& rhs ) +{ + if( lhs.policy != rhs.policy ) + return false; + + for( int i = 0; i < KDSINGLEAPPLICATIONGUARD_NUMBER_OF_PROCESSES; ++i ) + if( lhs.info[ i ] != rhs.info[ i ] ) + return false; + + return true; +} + +/*! + \internal + */ +class KDSingleApplicationGuard::Private +{ +public: + Private( KDSingleApplicationGuard* qq ) + : q( qq ), + id( -1 ) + { + if( primaryInstance == 0 ) + primaryInstance = q; + } + + ~Private() + { + if( primaryInstance == q ) + primaryInstance = 0; + } + + void shutdownInstance() + { + KDLockedSharedMemoryPointer< InstanceRegister > instances( &q->d->mem ); + instances->info[ q->d->id ].command = ExitedInstance; + + if( q->isPrimaryInstance() ) + { + // ohh... we need a new primary instance... + for( int i = 1; i < KDSINGLEAPPLICATIONGUARD_NUMBER_OF_PROCESSES; ++i ) + { + if( ( instances->info[ i ].command & ( FreeInstance | ExitedInstance | ShutDownCommand | KillCommand ) ) == 0 ) + { + instances->info[ i ].command |= BecomePrimaryCommand; + return; + } + } + // none found? then my species is dead :-( + } + } + + static KDSingleApplicationGuard* primaryInstance; + +private: + KDSingleApplicationGuard* const q; + +public: + Policy policy; + QSharedMemory mem; + int id; +}; + +KDSingleApplicationGuard* KDSingleApplicationGuard::Private::primaryInstance = 0; + +#ifndef Q_WS_WIN +void SIGINT_handler( int sig ) +{ + if( sig == SIGINT && KDSingleApplicationGuard::Private::primaryInstance != 0 ) + KDSingleApplicationGuard::Private::primaryInstance->d->shutdownInstance(); + ::exit( 1 ); +} +#endif + +/*! + Creates a new KDSingleApplicationGuard guarding \a parent from mulitply instances. + If \a policy is AutoKillOtherInstances (the default), all instances, which try to start, + are killed automatically and instanceStarted() is emitted. + If \a policy is NoPolicy, the other instance will run and instanceStarted() is emitted. + */ +KDSingleApplicationGuard::KDSingleApplicationGuard( QCoreApplication* parent, Policy policy ) + : QObject( parent ), + d( new Private( this ) ) +{ + const QString name = parent->applicationName(); + Q_ASSERT_X( !name.isEmpty(), "KDSingleApplicationGuard::KDSingleApplicationGuard", "applicationName must not be emty" ); + d->mem.setKey( name ); + + // if another instance crashed, the shared memory segment is still there on Unix + // the following lines trigger deletion in that case +#ifndef Q_WS_WIN + d->mem.attach(); + d->mem.detach(); +#endif + + d->policy = policy; + + const bool created = d->mem.create( sizeof( InstanceRegister ) ); + if( !created ) + { + if( !d->mem.attach() ) + { + qWarning( "KDSingleApplicationGuard: Could neither create nor attach to shared memory segment." ); + return; + } + + // lets wait till the other instance initialized the register + bool initialized = false; + while( !initialized ) + { + const KDLockedSharedMemoryPointer< InstanceRegister > instances( &d->mem ); + initialized = instances->isValid(); + } + } + + + KDLockedSharedMemoryPointer< InstanceRegister > instances( &d->mem ); + + if( !created ) + { + // we're _not_ the first instance + // but the + bool killOurSelf = false; + + // find a new slot... + d->id = std::find( instances->info, instances->info + KDSINGLEAPPLICATIONGUARD_NUMBER_OF_PROCESSES, ProcessInfo() ) - instances->info; + ProcessInfo& info = instances->info[ d->id ]; + info = ProcessInfo( NewInstance, parent->arguments(), QCoreApplication::applicationPid() ); + killOurSelf = instances->policy == AutoKillOtherInstances; + d->policy = instances->policy; + + // but the signal that we tried to start was sent to the primary application + if( killOurSelf ) + { + info.command |= ExitedInstance; + exit( 1 ); + } + } + else + { + // ok.... we are the first instance + InstanceRegister reg( policy ); // create a new list + d->id = 0; // our id = 0 + // and we've no command + reg.info[ 0 ] = ProcessInfo( NoCommand, parent->arguments(), QCoreApplication::applicationPid() ); + *instances = reg; // push this is the process list into shared memory + } + +#ifndef Q_WS_WIN + ::signal( SIGINT, SIGINT_handler ); +#endif + + // now listen for commands + startTimer( 250 ); +} + +/*! + Destroys this SingleApplicationGuard. + If this instance has been the primary instance and no other instance is existing anymore, + the application is shut down completely. Otherwise the destructor selects another instance to + be the primary instances. + */ +KDSingleApplicationGuard::~KDSingleApplicationGuard() +{ + if( d->id == -1 ) + return; + + d->shutdownInstance(); +} + +/*! + \property KDSingleApplicationGuard::primaryInstance + Determines wheter this instance is the primary instance. + The primary instance is the first instance which was started or an instance which + got selected by KDSingleApplicationGuard's destructor, when the primary instance was + shut down. + + Get this property's value using %isPrimaryInstance(), and monitor changes to it + using becamePrimaryInstance(). + */ +bool KDSingleApplicationGuard::isPrimaryInstance() const +{ + return d->id == 0; +} + +/*! + \property KDSingleApplicationGuard::Policy + Specifies the policy KDSingleApplicationGuard is using when new instances are started. + This can only be set in the primary instance. + + Get this property's value using %policy(), set it using %setPolicy(), and monitor changes + to it using policyChanged(). + */ +KDSingleApplicationGuard::Policy KDSingleApplicationGuard::policy() const +{ + return d->policy; +} + +void KDSingleApplicationGuard::setPolicy( Policy policy ) +{ + Q_ASSERT( isPrimaryInstance() ); + if( d->policy == policy ) + return; + + d->policy = policy; + emit policyChanged(); + KDLockedSharedMemoryPointer< InstanceRegister > instances( &d->mem ); + instances->policy = policy; +} + +/*! + Returns a list of all currently running instances. + */ +QList< KDSingleApplicationGuard::Instance > KDSingleApplicationGuard::instances() const +{ + QList< Instance > result; + const KDLockedSharedMemoryPointer< InstanceRegister > instances( const_cast< QSharedMemory* >( &d->mem ) ); + for( int i = 0; i < KDSINGLEAPPLICATIONGUARD_NUMBER_OF_PROCESSES; ++i ) + { + const ProcessInfo& info = instances->info[ i ]; + if( ( info.command & ( FreeInstance | ExitedInstance ) ) == 0 ) + result.push_back( Instance( info.arguments(), info.pid ) ); + } + return result; +} + +/*! + Shuts down all other instances. This can only be called from the + the primary instance. + Shut down is done gracefully via QCoreApplication::quit(). + */ +void KDSingleApplicationGuard::shutdownOtherInstances() +{ + Q_ASSERT( isPrimaryInstance() ); + KDLockedSharedMemoryPointer< InstanceRegister > instances( &d->mem ); + for( int i = 1; i < KDSINGLEAPPLICATIONGUARD_NUMBER_OF_PROCESSES; ++i ) + { + if( ( instances->info[ i ].command & ( FreeInstance | ExitedInstance ) ) == 0 ) + instances->info[ i ].command = ShutDownCommand; + } +} + +/*! + Kills all other instances. This can only be called from the + the primary instance. + Killing is done via exit(1) + */ +void KDSingleApplicationGuard::killOtherInstances() +{ + Q_ASSERT( isPrimaryInstance() ); + KDLockedSharedMemoryPointer< InstanceRegister > instances( &d->mem ); + for( int i = 1; i < KDSINGLEAPPLICATIONGUARD_NUMBER_OF_PROCESSES; ++i ) + { + if( ( instances->info[ i ].command & ( FreeInstance | ExitedInstance ) ) == 0 ) + instances->info[ i ].command = KillCommand; + } +} + +/*! + \reimp + */ +void KDSingleApplicationGuard::timerEvent( QTimerEvent* event ) +{ + Q_UNUSED( event ) + + if( isPrimaryInstance() ) + { + // only the primary instance will get notified about new instances + QList< Instance > exitedInstances; + QList< Instance > startedInstances; + + { + KDLockedSharedMemoryPointer< InstanceRegister > instances( &d->mem ); + + for( int i = 1; i < KDSINGLEAPPLICATIONGUARD_NUMBER_OF_PROCESSES; ++i ) + { + ProcessInfo& info = instances->info[ i ]; + if( info.command & NewInstance ) + { + startedInstances.push_back( Instance( info.arguments(), info.pid ) ); + info.command &= ~NewInstance; // clear NewInstance flag + } + else if( info.command & ExitedInstance ) + { + exitedInstances.push_back( Instance( info.arguments(), info.pid ) ); + info.command = FreeInstance; // set FreeInstance flag + } + } + } + + // one signal for every new instance - _after_ the memory segment was unlocked again + for( QList< Instance >::const_iterator it = startedInstances.begin(); it != startedInstances.end(); ++it ) + emit instanceStarted( *it ); + for( QList< Instance >::const_iterator it = exitedInstances.begin(); it != exitedInstances.end(); ++it ) + emit instanceExited( *it ); + } + else + { + // do we have a command? + bool killOurSelf = false; + bool shutDownOurSelf = false; + bool policyDidChange = false; + + { + KDLockedSharedMemoryPointer< InstanceRegister > instances( &d->mem ); + + policyDidChange = instances->policy != d->policy; + d->policy = instances->policy; + + if( instances->info[ d->id ].command & BecomePrimaryCommand ) + { + // we became primary! + instances->info[ 0 ] = instances->info[ d->id ]; + instances->info[ d->id ] = ProcessInfo(); // change our id to 0 and declare the old slot as free + d->id = 0; + emit becamePrimaryInstance(); + } + + killOurSelf = instances->info[ d->id ].command & KillCommand; // check for kill command + shutDownOurSelf = instances->info[ d->id ].command & ShutDownCommand; // check for shut down command + instances->info[ d->id ].command &= ~( KillCommand | ShutDownCommand | BecomePrimaryCommand ); // reset both flags + if( killOurSelf ) + { + instances->info[ d->id ].command |= ExitedInstance; // upon kill, we have to set the ExitedInstance flag + d->id = -1; // becauso our d'tor won't be called anymore + } + } + + if( killOurSelf ) // kill our self takes precedence + exit( 1 ); + else if( shutDownOurSelf ) + qApp->quit(); + else if( policyDidChange ) + emit policyChanged(); + } +} + +#ifdef KDTOOLSCORE_UNITTESTS + +#include + +#include + +#include +#include +#include + +Q_DECLARE_METATYPE( KDSingleApplicationGuard::Instance ); + +static void wait( int msec ) +{ + QTime t; + t.start(); + while( t.elapsed() < msec ) + { + qApp->processEvents( QEventLoop::WaitForMoreEvents, msec - t.elapsed() ); + } +} + +static std::ostream& operator<<( std::ostream& stream, const QStringList& list ) +{ + stream << "QStringList("; + for( QStringList::const_iterator it = list.begin(); it != list.end(); ++it ) + { + stream << " " << it->toLocal8Bit().data(); + if( it + 1 != list.end() ) + stream << ","; + } + stream << " )"; + return stream; +} + + +KDAB_UNITTEST_SIMPLE( KDSingleApplicationGuard, "kdcoretools" ) { + + // set it to an unique name + qApp->setApplicationName( QUuid::createUuid().toString() ); + + qRegisterMetaType< KDSingleApplicationGuard::Instance >(); + + KDSingleApplicationGuard* guard3 = 0; + QSignalSpy* spy3 = 0; + + { + KDSingleApplicationGuard guard1( qApp ); + assertEqual( guard1.policy(), KDSingleApplicationGuard::AutoKillOtherInstances ); + assertEqual( guard1.instances().count(), 1 ); + assertTrue( guard1.isPrimaryInstance() ); + + guard1.setPolicy( KDSingleApplicationGuard::NoPolicy ); + assertEqual( guard1.policy(), KDSingleApplicationGuard::NoPolicy ); + + QSignalSpy spy1( &guard1, SIGNAL( instanceStarted( KDSingleApplicationGuard::Instance ) ) ); + + KDSingleApplicationGuard guard2( qApp ); + assertEqual( guard1.instances().count(), 2 ); + assertEqual( guard2.instances().count(), 2 ); + assertEqual( guard2.policy(), KDSingleApplicationGuard::NoPolicy ); + assertFalse( guard2.isPrimaryInstance() ); + + wait( 1000 ); + + assertEqual( spy1.count(), 1 ); + guard3 = new KDSingleApplicationGuard( qApp ); + spy3 = new QSignalSpy( guard3, SIGNAL( becamePrimaryInstance() ) ); + assertFalse( guard3->isPrimaryInstance() ); + } + + wait( 1000 ); + assertEqual( spy3->count(), 1 ); + assertEqual( guard3->instances().count(), 1 ); + assertTrue( guard3->isPrimaryInstance() ); + + assertEqual( guard3->instances().first().arguments, qApp->arguments() ); + + QSignalSpy spyStarted( guard3, SIGNAL( instanceStarted( KDSingleApplicationGuard::Instance ) ) ); + QSignalSpy spyExited( guard3, SIGNAL( instanceExited( KDSingleApplicationGuard::Instance ) ) ); + + { + KDSingleApplicationGuard guard1( qApp ); + KDSingleApplicationGuard guard2( qApp ); + + wait( 1000 ); + + assertEqual( spyStarted.count(), 2 ); + } + + wait( 1000 ); + assertEqual( spyExited.count(), 2 ); + + delete spy3; + delete guard3; + } + +#endif // KDTOOLSCORE_UNITTESTS + +#endif // QT_VERSION < 0x040400 diff --git a/src/libtomahawk/kdsingleapplicationguard/kdsingleapplicationguard.h b/src/libtomahawk/kdsingleapplicationguard/kdsingleapplicationguard.h new file mode 100644 index 000000000..14706e4d0 --- /dev/null +++ b/src/libtomahawk/kdsingleapplicationguard/kdsingleapplicationguard.h @@ -0,0 +1,75 @@ +#ifndef __KDTOOLSCORE_KDSINGLEAPPLICATIONGUARD_H__ +#define __KDTOOLSCORE_KDSINGLEAPPLICATIONGUARD_H__ + +#include +#include + +#include "pimpl_ptr.h" +#include "dllmacro.h" + +class QCoreApplication; + +#ifndef Q_WS_WIN +void SIGINT_handler( int sig ); +#endif + +class DLLEXPORT KDSingleApplicationGuard : public QObject +{ + Q_OBJECT +#ifndef Q_WS_WIN + friend void ::SIGINT_handler( int ); +#endif + +public: + enum Policy + { + NoPolicy = 0, + AutoKillOtherInstances = 1 + }; + + Q_PROPERTY( bool primaryInstance READ isPrimaryInstance NOTIFY becamePrimaryInstance ) + Q_PROPERTY( Policy policy READ policy WRITE setPolicy NOTIFY policyChanged ) + + explicit KDSingleApplicationGuard( QCoreApplication* parent, Policy policy = AutoKillOtherInstances ); + ~KDSingleApplicationGuard(); + + bool isPrimaryInstance() const; + + Policy policy() const; + void setPolicy( Policy policy ); + + struct Instance + { + Instance( const QStringList& arguments = QStringList(), qint64 pid = -1 ); + + QStringList arguments; + qint64 pid; + }; + + QList< Instance > instances() const; + +Q_SIGNALS: + void instanceStarted( KDSingleApplicationGuard::Instance instance ); + void instanceExited( KDSingleApplicationGuard::Instance instance ); + void becamePrimaryInstance(); + void policyChanged(); + +public Q_SLOTS: + void shutdownOtherInstances(); + void killOtherInstances(); + +protected: + void timerEvent( QTimerEvent* event ); + +private: + class Private; + kdtools::pimpl_ptr< Private > d; +}; + +#if QT_VERSION < 0x040400 +#ifdef Q_CC_GNU +#warning "Can't use KDSingleApplicationGuard with Qt versions prior to 4.4" +#endif +#endif + +#endif diff --git a/src/libtomahawk/kdsingleapplicationguard/kdtoolsglobal.cpp b/src/libtomahawk/kdsingleapplicationguard/kdtoolsglobal.cpp new file mode 100644 index 000000000..5997fe64c --- /dev/null +++ b/src/libtomahawk/kdsingleapplicationguard/kdtoolsglobal.cpp @@ -0,0 +1,32 @@ +#include "kdtoolsglobal.h" + +#include + +#include + +namespace { + struct Version { + unsigned char v[3]; + }; + + static inline bool operator<( const Version & lhs, const Version & rhs ) { + return std::lexicographical_compare( lhs.v, lhs.v + 3, rhs.v, rhs.v + 3 ); + } + static inline bool operator==( const Version & lhs, const Version & rhs ) { + return std::equal( lhs.v, lhs.v + 3, rhs.v ); + } + KDTOOLS_MAKE_RELATION_OPERATORS( Version, static inline ) +} + +static Version kdParseQtVersion( const char * const version ) { + if ( !version || qstrlen( version ) < 5 || version[1] != '.' || version[3] != '.' || version[5] != 0 && version[5] != '.' && version[5] != '-' ) + return Version(); // parse error + const Version result = { { version[0] - '0', version[2] - '0', version[4] - '0' } }; + return result; +} + +bool _kdCheckQtVersion_impl( int major, int minor, int patchlevel ) { + static const Version actual = kdParseQtVersion( qVersion() ); // do this only once each run... + const Version requested = { { major, minor, patchlevel } }; + return actual >= requested; +} diff --git a/src/libtomahawk/kdsingleapplicationguard/kdtoolsglobal.h b/src/libtomahawk/kdsingleapplicationguard/kdtoolsglobal.h new file mode 100644 index 000000000..4e8d0d673 --- /dev/null +++ b/src/libtomahawk/kdsingleapplicationguard/kdtoolsglobal.h @@ -0,0 +1,113 @@ +#ifndef __KDTOOLS_KDTOOLSGLOBAL_H__ +#define __KDTOOLS_KDTOOLSGLOBAL_H__ + +#include + +#define KDAB_DISABLE_COPY( x ) private: x( const x & ); x & operator=( const x & ) + +#ifdef KDTOOLS_SHARED +# ifdef BUILD_SHARED_KDTOOLSCORE +# define KDTOOLSCORE_EXPORT Q_DECL_EXPORT +# else +# define KDTOOLSCORE_EXPORT Q_DECL_IMPORT +# endif +# ifdef BUILD_SHARED_KDTOOLSGUI +# define KDTOOLSGUI_EXPORT Q_DECL_EXPORT +# else +# define KDTOOLSGUI_EXPORT Q_DECL_IMPORT +# endif +# ifdef BUILD_SHARED_KDTOOLSXML +# define KDTOOLSXML_EXPORT Q_DECL_EXPORT +# else +# define KDTOOLSXML_EXPORT Q_DECL_IMPORT +# endif +# ifdef BUILD_SHARED_KDUPDATER +# define KDTOOLS_UPDATER_EXPORT Q_DECL_EXPORT +# else +# define KDTOOLS_UPDATER_EXPORT Q_DECL_IMPORT +# endif +#else // KDTOOLS_SHARED +# define KDTOOLSCORE_EXPORT +# define KDTOOLSGUI_EXPORT +# define KDTOOLSXML_EXPORT +# define KDTOOLS_UPDATER_EXPORT +#endif // KDTOOLS_SHARED + +#define MAKEINCLUDES_EXPORT + +#define DOXYGEN_PROPERTY( x ) +#ifdef DOXYGEN_RUN +# define KDAB_IMPLEMENT_SAFE_BOOL_OPERATOR( func ) operator unspecified_bool_type() const { return func; } +# define KDAB_USING_SAFE_BOOL_OPERATOR( Class ) operator unspecified_bool_type() const; +#else +# define KDAB_IMPLEMENT_SAFE_BOOL_OPERATOR( func ) \ + private: struct __safe_bool_dummy__ { void nonnull() {} }; \ + typedef void ( __safe_bool_dummy__::*unspecified_bool_type )(); \ + public: \ + operator unspecified_bool_type() const { \ + return ( func ) ? &__safe_bool_dummy__::nonnull : 0 ; \ + } +#define KDAB_USING_SAFE_BOOL_OPERATOR( Class ) \ + using Class::operator Class::unspecified_bool_type; +#endif + +#define KDTOOLS_MAKE_RELATION_OPERATORS( Class, linkage ) \ + linkage bool operator>( const Class & lhs, const Class & rhs ) { \ + return operator<( rhs, lhs ); \ + } \ + linkage bool operator!=( const Class & lhs, const Class & rhs ) { \ + return !operator==( lhs, rhs ); \ + } \ + linkage bool operator<=( const Class & lhs, const Class & rhs ) { \ + return !operator>( lhs, rhs ); \ + } \ + linkage bool operator>=( const Class & lhs, const Class & rhs ) { \ + return !operator<( lhs, rhs ); \ + } + +template +inline T & __kdtools__dereference_for_methodcall( T & o ) { + return o; +} + +template +inline T & __kdtools__dereference_for_methodcall( T * o ) { + return *o; +} + +#define KDAB_SET_OBJECT_NAME( x ) __kdtools__dereference_for_methodcall( x ).setObjectName( QLatin1String( #x ) ) + +KDTOOLSCORE_EXPORT bool _kdCheckQtVersion_impl( int major, int minor=0, int patchlevel=0 ); +static inline bool kdCheckQtVersion( unsigned int major, unsigned int minor=0, unsigned int patchlevel=0 ) { + return (major<<16|minor<<8|patchlevel) <= static_cast(QT_VERSION) + || _kdCheckQtVersion_impl( major, minor, patchlevel ); +} + +#define KDTOOLS_DECLARE_PRIVATE_BASE( Class ) \ +protected: \ + class Private; \ + Private * d_func() { return _d; } \ + const Private * d_func() const { return _d; } \ + Class( Private * _d_, bool b ) : _d( _d_ ) { init(b); } \ +private: \ + void init(bool); \ +private: \ + Private * _d + +#define KDTOOLS_DECLARE_PRIVATE_DERIVED( Class, Base ) \ +protected: \ + class Private; \ + Private * d_func() { \ + return reinterpret_cast( Base::d_func() ); \ + } \ + const Private * d_func() const { \ + return reinterpret_cast( Base::d_func() ); \ + } \ + Class( Private * _d_, bool b ) \ + : Base( reinterpret_cast(_d_), b ) { init(b); } \ +private: \ + void init(bool) + + +#endif /* __KDTOOLS_KDTOOLSGLOBAL_H__ */ + diff --git a/src/libtomahawk/kdsingleapplicationguard/license-gpl b/src/libtomahawk/kdsingleapplicationguard/license-gpl new file mode 100644 index 000000000..332ed973c --- /dev/null +++ b/src/libtomahawk/kdsingleapplicationguard/license-gpl @@ -0,0 +1,349 @@ + + The KD Tools Library is Copyright (C) 2001-2003 Klarälvdalens Datakonsult AB. + + You may use, distribute and copy the KD Tools Library under the terms of + GNU General Public License version 2, which is displayed below. + +------------------------------------------------------------------------- + + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 675 Mass Ave, Cambridge, MA 02139, USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + Appendix: How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) 19yy + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) 19yy name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. + +------------------------------------------------------------------------- diff --git a/src/libtomahawk/kdsingleapplicationguard/pimpl_ptr.cpp b/src/libtomahawk/kdsingleapplicationguard/pimpl_ptr.cpp new file mode 100644 index 000000000..3045ebc2a --- /dev/null +++ b/src/libtomahawk/kdsingleapplicationguard/pimpl_ptr.cpp @@ -0,0 +1,203 @@ +#include "pimpl_ptr.h" + +/*! + \class pimpl_ptr: + \ingroup core smartptr + \brief Owning pointer for private implementations + \since_c 2.1 + + (The exception safety of this class has not been evaluated yet.) + + pimpl_ptr is a smart immutable pointer, which owns the contained object. Unlike other smart pointers, + it creates a standard constructed object when instanciated via the + \link pimpl_ptr() standard constructor\endlink. + Additionally, pimpl_ptr respects constness of the pointer object and returns \c const \c T* for + a const pimpl_ptr object. + + The content of a pimpl_ptr cannot be changed during it's lifetime. + + \section general-use General Use + + The general use case of pimpl_ptr is the "Pimpl Idiom", i.e. hiding the private implementation of a class + from the user's compiler which see \c MyClass as + + \code + class MyClass + { + public: + MyClass(); + ~MyClass(); + + // public class API + int value() const; + + private: + class Private; // defined later + kdtools::pimpl_ptr< Private > d; + }; + \endcode + + but not the private parts of it. These can only be seen (and accessed) by the code knowing \c MyClass::Private: + + \code + class MyClass::Private + { + public: + int value; + }; + + MyClass::MyClass() + { + // d was automatically filled with new Private + d->value = 42; + } + + MyClass::~MyClass() + { + // the content of d gets deleted automatically + } + + int MyClass::value() const + { + // access the private part: + // since MyClass::value() is const, the returned pointee is const, too + return d->value; + } + \endcode + +*/ + +/*! + \fn pimpl_ptr::pimpl_ptr() + + Default constructor. Constructs a pimpl_tr that contains (owns) a standard constructed + instance of \c T. + + \post \c *this owns a new object. +*/ + +/*! + \fn pimpl_ptr::pimpl_ptr( T * t ) + + Constructor. Constructs a pimpl_ptr that contains (owns) \a t. + + \post get() == obj +*/ + +/*! + \fn pimpl_ptr::~pimpl_ptr() + + Destructor. + + \post The object previously owned by \c *this has been deleted. +*/ + +/*! + \fn const T * pimpl_ptr::get() const + + \returns a const pointer to the contained (owned) object. + \overload +*/ + +/*! + \fn T * pimpl_ptr::get() + + \returns a pointer to the contained (owned) object. +*/ + +/*! + \fn const T & pimpl_ptr::operator*() const + + Dereference operator. Returns \link get() *get()\endlink. + \overload +*/ + +/*! + \fn T & pimpl_ptr::operator*() + + Dereference operator. Returns \link get() *get()\endlink. +*/ + +/*! + \fn const T * pimpl_ptr::operator->() const + + Member-by-pointer operator. Returns get(). + \overload +*/ + +/*! + \fn T * pimpl_ptr::operator->() + + Member-by-pointer operator. Returns get(). +*/ + +#ifdef KDTOOLSCORE_UNITTESTS + +#include + +#include +#include + +namespace +{ + struct ConstTester + { + bool isConst() + { + return false; + } + + bool isConst() const + { + return true; + } + }; +} + +KDAB_UNITTEST_SIMPLE( pimpl_ptr, "kdcoretools" ) { + + { + kdtools::pimpl_ptr< QObject > p; + assertNotNull( p.get() ); + assertNull( p->parent() ); + } + + + { + QPointer< QObject > o; + { + kdtools::pimpl_ptr< QObject > qobject( new QObject ); + o = qobject.get(); + assertEqual( o, qobject.operator->() ); + assertEqual( o, &(qobject.operator*()) ); + } + assertNull( o ); + } + + { + const kdtools::pimpl_ptr< QObject > qobject( new QObject ); + const QObject* o = qobject.get(); + assertEqual( o, qobject.operator->() ); + assertEqual( o, &(qobject.operator*()) ); + } + + { + kdtools::pimpl_ptr< QObject > o1; + assertTrue( o1 ); + kdtools::pimpl_ptr< QObject > o2( 0 ); + assertFalse( o2 ); + } + + { + const kdtools::pimpl_ptr< ConstTester > o1; + kdtools::pimpl_ptr< ConstTester > o2; + assertTrue( o1->isConst() ); + assertFalse( o2->isConst() ); + assertTrue( (*o1).isConst() ); + assertFalse( (*o2).isConst() ); + assertTrue( o1.get()->isConst() ); + assertFalse( o2.get()->isConst() ); + } +} + +#endif // KDTOOLSCORE_UNITTESTS diff --git a/src/libtomahawk/kdsingleapplicationguard/pimpl_ptr.h b/src/libtomahawk/kdsingleapplicationguard/pimpl_ptr.h new file mode 100644 index 000000000..7b7f36839 --- /dev/null +++ b/src/libtomahawk/kdsingleapplicationguard/pimpl_ptr.h @@ -0,0 +1,44 @@ +#ifndef __KDTOOLSCORE__PIMPL_PTR_H__ +#define __KDTOOLSCORE__PIMPL_PTR_H__ + +#include "kdtoolsglobal.h" + +#ifndef DOXYGEN_RUN +namespace kdtools { +#endif + + template + class pimpl_ptr { + KDAB_DISABLE_COPY( pimpl_ptr ); + T * d; + public: + pimpl_ptr() : d( new T ) {} + explicit pimpl_ptr( T * t ) : d( t ) {} + ~pimpl_ptr() { delete d; d = 0; } + + T * get() { return d; } + const T * get() const { return d; } + + T * operator->() { return get(); } + const T * operator->() const { return get(); } + + T & operator*() { return *get(); } + const T & operator*() const { return *get(); } + + KDAB_IMPLEMENT_SAFE_BOOL_OPERATOR( get() ) + }; + + // these are not implemented, so's we can catch their use at + // link-time. Leaving them undeclared would open up a comparison + // via operator unspecified-bool-type(). + template + void operator==( const pimpl_ptr &, const pimpl_ptr & ); + template + void operator!=( const pimpl_ptr &, const pimpl_ptr & ); + +#ifndef DOXYGEN_RUN +} // namespace kdtools +#endif + +#endif /* __KDTOOLSCORE__PIMPL_PTR_H__ */ + diff --git a/src/libtomahawk/network/connection.cpp b/src/libtomahawk/network/connection.cpp index afa009713..59342fba9 100644 --- a/src/libtomahawk/network/connection.cpp +++ b/src/libtomahawk/network/connection.cpp @@ -62,16 +62,12 @@ Connection::Connection( Servent* parent ) Connection::~Connection() { - qDebug() << "DTOR connection (super)" << id() << thread(); + qDebug() << "DTOR connection (super)" << id() << thread() << m_sock.isNull(); if( !m_sock.isNull() ) { - qDebug() << "deleteLatering sock" << m_sock; +// qDebug() << "deleteLatering sock" << m_sock; m_sock->deleteLater(); } - else - { - qDebug() << "no valid sock to delete"; - } delete m_statstimer; } @@ -118,7 +114,7 @@ Connection::setFirstMessage( msg_ptr m ) void Connection::shutdown( bool waitUntilSentAll ) { - qDebug() << Q_FUNC_INFO << waitUntilSentAll; + qDebug() << Q_FUNC_INFO << waitUntilSentAll << id(); if ( m_do_shutdown ) { //qDebug() << id() << " already shutting down"; @@ -128,7 +124,7 @@ Connection::shutdown( bool waitUntilSentAll ) m_do_shutdown = true; if ( !waitUntilSentAll ) { - qDebug() << "Shutting down immediately " << id(); +// qDebug() << "Shutting down immediately " << id(); actualShutdown(); } else @@ -146,10 +142,9 @@ Connection::shutdown( bool waitUntilSentAll ) void Connection::actualShutdown() { - qDebug() << Q_FUNC_INFO; + qDebug() << Q_FUNC_INFO << m_actually_shutting_down << id(); if( m_actually_shutting_down ) { - qDebug() << "(already actually shutting down)"; return; } m_actually_shutting_down = true; @@ -159,7 +154,7 @@ Connection::actualShutdown() m_sock->disconnectFromHost(); } - qDebug() << "EMITTING finished()"; +// qDebug() << "EMITTING finished()"; emit finished(); } diff --git a/src/libtomahawk/network/controlconnection.cpp b/src/libtomahawk/network/controlconnection.cpp index 28fefcec3..24cc6af6d 100644 --- a/src/libtomahawk/network/controlconnection.cpp +++ b/src/libtomahawk/network/controlconnection.cpp @@ -75,6 +75,13 @@ ControlConnection::setup() { qDebug() << Q_FUNC_INFO << id() << name(); + if ( !m_source.isNull() ) + { + qDebug() << "This source seems to be online already."; + Q_ASSERT( false ); + return; + } + QString friendlyName; if ( Servent::isIPWhitelisted( m_sock->peerAddress() ) ) { @@ -85,7 +92,7 @@ ControlConnection::setup() } else friendlyName = name(); - + // setup source and remote collection for this peer m_source = SourceList::instance()->get( id(), friendlyName ); m_source->setControlConnection( this ); diff --git a/src/libtomahawk/network/dbsyncconnection.cpp b/src/libtomahawk/network/dbsyncconnection.cpp index a25d8ab8f..b3559088f 100644 --- a/src/libtomahawk/network/dbsyncconnection.cpp +++ b/src/libtomahawk/network/dbsyncconnection.cpp @@ -75,7 +75,7 @@ DBSyncConnection::~DBSyncConnection() void DBSyncConnection::idleTimeout() { - qDebug() << Q_FUNC_INFO << "*************"; + qDebug() << Q_FUNC_INFO; shutdown( true ); } @@ -87,11 +87,6 @@ DBSyncConnection::changeState( State newstate ) m_state = newstate; qDebug() << "DBSYNC State changed from" << s << "to" << newstate; emit stateChanged( newstate, s, "" ); - - if ( newstate == SYNCED ) - { - qDebug() << "Synced :)"; - } } @@ -197,7 +192,7 @@ DBSyncConnection::handleMsg( msg_ptr msg ) msg->is( Msg::DBOP ) && msg->payload() == "ok" ) { - qDebug() << "No ops to apply, we are synced."; +// qDebug() << "No ops to apply, we are synced."; changeState( SYNCED ); // calc the collection stats, to updates the "X tracks" in the sidebar etc // this is done automatically if you run a dbcmd to add files. diff --git a/src/libtomahawk/network/servent.cpp b/src/libtomahawk/network/servent.cpp index 182c8a2f4..083e67d92 100644 --- a/src/libtomahawk/network/servent.cpp +++ b/src/libtomahawk/network/servent.cpp @@ -676,12 +676,12 @@ Servent::checkACL( const Connection* conn, const QString &nodeid, bool showDialo qDebug() << "ACL for this node not found"; QMessageBox msgBox; msgBox.setIcon( QMessageBox::Question ); - msgBox.setText( "Incoming Connection Attempt" ); - msgBox.setInformativeText( QString( "Another Tomahawk instance is attempting to connect to you. Select whether to allow or deny this connection.\n\nPeer name: %1\nPeer ID: %2\n\nRemember: Only allow peers to connect if you have the legal right for them to stream music from you.").arg( conn->name(), nodeid ) ); - QPushButton *denyButton = msgBox.addButton( "Deny", QMessageBox::HelpRole ); - QPushButton *alwaysDenyButton = msgBox.addButton( "Always Deny", QMessageBox::YesRole ); - QPushButton *allowButton = msgBox.addButton( "Allow", QMessageBox::NoRole ); - QPushButton *alwaysAllowButton = msgBox.addButton( "Always Allow", QMessageBox::ActionRole ); + msgBox.setText( tr( "Incoming Connection Attempt" ) ); + msgBox.setInformativeText( tr( "Another Tomahawk instance is attempting to connect to you. Select whether to allow or deny this connection.\n\nPeer name: %1\nPeer ID: %2\n\nRemember: Only allow peers to connect if you have the legal right for them to stream music from you.").arg( conn->name(), nodeid ) ); + QPushButton *denyButton = msgBox.addButton( tr( "Deny" ), QMessageBox::HelpRole ); + QPushButton *alwaysDenyButton = msgBox.addButton( tr( "Always Deny" ), QMessageBox::YesRole ); + QPushButton *allowButton = msgBox.addButton( tr( "Allow" ), QMessageBox::NoRole ); + QPushButton *alwaysAllowButton = msgBox.addButton( tr( "Always Allow" ), QMessageBox::ActionRole ); msgBox.setDefaultButton( denyButton ); msgBox.setEscapeButton( denyButton ); @@ -792,8 +792,11 @@ Servent::isIPWhitelisted( QHostAddress ip ) bool Servent::connectedToSession( const QString& session ) { + qDebug() << Q_FUNC_INFO; + qDebug() << "Checking against " << session; foreach( ControlConnection* cc, m_controlconnections ) { + qDebug() << "Checking session " << cc->id(); if( cc->id() == session ) return true; } diff --git a/src/libtomahawk/pipeline.cpp b/src/libtomahawk/pipeline.cpp index 3ffc4a154..b7d538a37 100644 --- a/src/libtomahawk/pipeline.cpp +++ b/src/libtomahawk/pipeline.cpp @@ -102,7 +102,7 @@ Pipeline::resolve( const QList& qlist, bool prioritized ) int i = 0; foreach( const query_ptr& q, qlist ) { - qDebug() << Q_FUNC_INFO << (qlonglong)q.data() << q->toString(); +// qDebug() << Q_FUNC_INFO << (qlonglong)q.data() << q->toString(); if ( !m_qids.contains( q->id() ) ) { m_qids.insert( q->id(), q ); @@ -189,7 +189,7 @@ Pipeline::reportResults( QID qid, const QList< result_ptr >& results ) if ( decQIDState( q ) == 0 ) { // All resolvers have reported back their results for this query now - qDebug() << "Finished resolving:" << q->toString(); + qDebug() << "Finished resolving:" << q->toString() << q->numResults(); if ( !q->solved() ) q->onResolvingFinished(); @@ -216,7 +216,7 @@ Pipeline::shuntNext() return; } - qDebug() << Q_FUNC_INFO << m_qidsState.count(); +// qDebug() << Q_FUNC_INFO << m_qidsState.count(); // Check if we are ready to dispatch more queries if ( m_qidsState.count() >= CONCURRENT_QUERIES ) return; @@ -246,8 +246,8 @@ Pipeline::shunt( const query_ptr& q ) if ( q->solved() ) { - qDebug() << "Query solved, pipeline aborted:" << q->toString() - << "numresults:" << q->results().length(); +// qDebug() << "Query solved, pipeline aborted:" << q->toString() +// << "numresults:" << q->results().length(); QList< result_ptr > rl; reportResults( q->id(), rl ); @@ -275,7 +275,7 @@ Pipeline::shunt( const query_ptr& q ) lasttimeout = r->timeout(); // resolvers aren't allowed to block in this call: - qDebug() << "Dispatching to resolver" << r->name(); + qDebug() << "Dispatching to resolver" << r->name() << q->toString(); thisResolver = i; r->resolve( q ); @@ -291,7 +291,7 @@ Pipeline::shunt( const query_ptr& q ) if ( thisResolver < m_resolvers.count() ) { incQIDState( q ); - qDebug() << "Shunting in" << lasttimeout << "ms, q:" << q->toString(); +// qDebug() << "Shunting in" << lasttimeout << "ms, q:" << q->toString(); new FuncTimeout( lasttimeout, boost::bind( &Pipeline::shunt, this, q ) ); } } @@ -329,7 +329,7 @@ Pipeline::incQIDState( const Tomahawk::query_ptr& query ) state = m_qidsState.value( query->id() ) + 1; } - qDebug() << Q_FUNC_INFO << "inserting to qidsstate:" << query->id() << state; +// qDebug() << Q_FUNC_INFO << "inserting to qidsstate:" << query->id() << state; m_qidsState.insert( query->id(), state ); return state; @@ -344,12 +344,12 @@ Pipeline::decQIDState( const Tomahawk::query_ptr& query ) int state = m_qidsState.value( query->id() ) - 1; if ( state ) { - qDebug() << Q_FUNC_INFO << "replacing" << query->id() << state; +// qDebug() << Q_FUNC_INFO << "replacing" << query->id() << state; m_qidsState.insert( query->id(), state ); } else { - qDebug() << Q_FUNC_INFO << "removing" << query->id() << state; +// qDebug() << Q_FUNC_INFO << "removing" << query->id() << state; m_qidsState.remove( query->id() ); } diff --git a/src/libtomahawk/playlist.cpp b/src/libtomahawk/playlist.cpp index f27e592e0..1ca21e31e 100644 --- a/src/libtomahawk/playlist.cpp +++ b/src/libtomahawk/playlist.cpp @@ -112,7 +112,7 @@ Playlist::Playlist( const source_ptr& src, , m_lastmodified( lastmod ) , m_shared( shared ) { - qDebug() << Q_FUNC_INFO << "1"; +// qDebug() << Q_FUNC_INFO << "1"; init(); } diff --git a/src/libtomahawk/playlist/dynamic/DynamicPlaylist.cpp b/src/libtomahawk/playlist/dynamic/DynamicPlaylist.cpp index 554ff5f98..6ab5eb30f 100644 --- a/src/libtomahawk/playlist/dynamic/DynamicPlaylist.cpp +++ b/src/libtomahawk/playlist/dynamic/DynamicPlaylist.cpp @@ -253,8 +253,8 @@ DynamicPlaylist::reportCreated( const Tomahawk::dynplaylist_ptr& self ) Q_ASSERT( !author().isNull() ); Q_ASSERT( !author()->collection().isNull() ); // will emit Collection::playlistCreated(...) - qDebug() << "Creating dynplaylist belonging to:" << author().data() << author().isNull(); - qDebug() << "REPORTING DYNAMIC PLAYLIST CREATED:" << this << author()->friendlyName(); +// qDebug() << "Creating dynplaylist belonging to:" << author().data() << author().isNull(); +// qDebug() << "REPORTING DYNAMIC PLAYLIST CREATED:" << this << author()->friendlyName(); author()->collection()->addDynamicPlaylist( self ); } @@ -431,7 +431,7 @@ QList< dyncontrol_ptr > DynamicPlaylist::variantsToControl( const QList< QVarian QList realControls; foreach( QVariantMap controlV, controlsV ) { dyncontrol_ptr control = GeneratorFactory::createControl( controlV.value( "type" ).toString(), controlV.value( "selectedType" ).toString() ); - qDebug() << "CReating control with data:" << controlV; + qDebug() << "Creating control with data:" << controlV; QJson::QObjectHelper::qvariant2qobject( controlV, control.data() ); realControls << control; } diff --git a/src/libtomahawk/playlist/dynamic/widgets/DynamicWidget.cpp b/src/libtomahawk/playlist/dynamic/widgets/DynamicWidget.cpp index 75fc46351..6f1f2302a 100644 --- a/src/libtomahawk/playlist/dynamic/widgets/DynamicWidget.cpp +++ b/src/libtomahawk/playlist/dynamic/widgets/DynamicWidget.cpp @@ -345,6 +345,8 @@ DynamicWidget::controlsChanged() return; m_playlist->createNewRevision(); m_seqRevLaunched++; + + emit descriptionChanged( m_playlist->generator()->sentenceSummary() ); } void @@ -356,6 +358,8 @@ DynamicWidget::controlChanged( const Tomahawk::dyncontrol_ptr& control ) m_seqRevLaunched++; showPreview(); + + emit descriptionChanged( m_playlist->generator()->sentenceSummary() ); } void diff --git a/src/libtomahawk/playlist/dynamic/widgets/DynamicWidget.h b/src/libtomahawk/playlist/dynamic/widgets/DynamicWidget.h index 441db1e13..1771fd07b 100644 --- a/src/libtomahawk/playlist/dynamic/widgets/DynamicWidget.h +++ b/src/libtomahawk/playlist/dynamic/widgets/DynamicWidget.h @@ -94,6 +94,9 @@ public slots: void playlistChanged( PlaylistInterface* ); void tracksAdded(); +signals: + void descriptionChanged( const QString& caption ); + private slots: void generate( int = -1 ); void tracksGenerated( const QList< Tomahawk::query_ptr>& queries ); diff --git a/src/libtomahawk/playlist/playlistitemdelegate.cpp b/src/libtomahawk/playlist/playlistitemdelegate.cpp index d014691c5..0edc22e7f 100644 --- a/src/libtomahawk/playlist/playlistitemdelegate.cpp +++ b/src/libtomahawk/playlist/playlistitemdelegate.cpp @@ -72,14 +72,21 @@ PlaylistItemDelegate::paint( QPainter* painter, const QStyleOptionViewItem& opti if ( !item || item->query().isNull() ) return; + float opacity = 0.0; painter->save(); if ( item->query()->results().count() ) - painter->setOpacity( item->query()->results().at( 0 )->score() ); - else - painter->setOpacity( 0.0 ); + opacity = item->query()->results().first()->score(); - if ( painter->opacity() < 0.3 ) - painter->setOpacity( 0.3 ); + QColor textcol, bgcol; + textcol = option.palette.color( QPalette::Foreground ); + bgcol = option.palette.color( QPalette::Background ); + + opacity = qMax( (float)0.3, opacity ); + int r = textcol.red(), g = textcol.green(), b = textcol.blue(); + r = opacity * r + ( 1 - opacity ) * bgcol.red(); + g = opacity * g + ( 1 - opacity ) * bgcol.green(); + b = opacity * b + ( 1 - opacity ) * bgcol.blue(); + textcol = QColor( r, g, b ); if ( item->isPlaying() ) { @@ -113,7 +120,14 @@ PlaylistItemDelegate::paint( QPainter* painter, const QStyleOptionViewItem& opti } else { - QStyledItemDelegate::paint( painter, option, index ); + if ( const QStyleOptionViewItem *vioption = qstyleoption_cast(&option)) + { + QStyleOptionViewItemV4 o( *vioption ); + o.palette.setColor( QPalette::Text, textcol ); + QStyledItemDelegate::paint( painter, o, index ); + } + else + QStyledItemDelegate::paint( painter, option, index ); } painter->restore(); diff --git a/src/libtomahawk/playlist/playlistmanager.cpp b/src/libtomahawk/playlist/playlistmanager.cpp index 31570a41f..2ba638c48 100644 --- a/src/libtomahawk/playlist/playlistmanager.cpp +++ b/src/libtomahawk/playlist/playlistmanager.cpp @@ -553,6 +553,13 @@ PlaylistManager::setPage( ViewPage* page, bool trackHistory ) if ( !AudioEngine::instance()->isPlaying() ) AudioEngine::instance()->setPlaylist( currentPlaylistInterface() ); + // UGH! + if( QObject* obj = dynamic_cast< QObject* >( currentPage() ) ) { +// qDebug() << SIGNAL( descriptionChanged( QString ) ) << QMetaObject::normalizedSignature( SIGNAL( descriptionChanged( QString ) ) ) << obj->metaObject()->indexOfSignal( QMetaObject::normalizedSignature( SIGNAL( descriptionChanged( QString ) ) ) ); +// if( obj->metaObject()->indexOfSignal( QMetaObject::normalizedSignature( SIGNAL( descriptionChanged( QString ) ) ) ) > -1 ) // if the signal exists (just to hide the qobject runtime warning...) + connect( obj, SIGNAL( descriptionChanged( QString ) ), m_infobar, SLOT( setDescription( QString ) ) ); + } + m_stack->setCurrentWidget( page->widget() ); updateView(); } diff --git a/src/libtomahawk/playlist/playlistmodel.cpp b/src/libtomahawk/playlist/playlistmodel.cpp index 8e4d24ae6..2ab23bf51 100644 --- a/src/libtomahawk/playlist/playlistmodel.cpp +++ b/src/libtomahawk/playlist/playlistmodel.cpp @@ -396,12 +396,16 @@ PlaylistModel::playlistEntries() const } +void +PlaylistModel::remove( unsigned int row, bool moreToCome ) +{ + removeIndex( index( row, 0, QModelIndex() ), moreToCome ); +} + + void PlaylistModel::removeIndex( const QModelIndex& index, bool moreToCome ) { - if ( isReadOnly() ) - return; - TrackModel::removeIndex( index ); if ( !moreToCome && !m_playlist.isNull() ) diff --git a/src/libtomahawk/playlist/playlistmodel.h b/src/libtomahawk/playlist/playlistmodel.h index e69bf59e1..bc743a18a 100644 --- a/src/libtomahawk/playlist/playlistmodel.h +++ b/src/libtomahawk/playlist/playlistmodel.h @@ -62,6 +62,7 @@ public: void insert( unsigned int row, const Tomahawk::query_ptr& query ); + void remove( unsigned int row, bool moreToCome = false ); virtual void removeIndex( const QModelIndex& index, bool moreToCome = false ); signals: diff --git a/src/libtomahawk/playlist/trackmodel.cpp b/src/libtomahawk/playlist/trackmodel.cpp index eceadd401..f96d3b026 100644 --- a/src/libtomahawk/playlist/trackmodel.cpp +++ b/src/libtomahawk/playlist/trackmodel.cpp @@ -26,6 +26,7 @@ #include "utils/tomahawkutils.h" #include "album.h" +#include "pipeline.h" using namespace Tomahawk; @@ -302,7 +303,7 @@ TrackModel::removeIndex( const QModelIndex& index, bool moreToCome ) { if ( QThread::currentThread() != thread() ) { - qDebug() << "Reinvoking in correct thread:" << Q_FUNC_INFO; +// qDebug() << "Reinvoking in correct thread:" << Q_FUNC_INFO; QMetaObject::invokeMethod( this, "removeIndex", Qt::QueuedConnection, Q_ARG(const QModelIndex, index), @@ -311,8 +312,6 @@ TrackModel::removeIndex( const QModelIndex& index, bool moreToCome ) return; } - qDebug() << Q_FUNC_INFO; - if ( index.column() > 0 ) return; @@ -370,3 +369,16 @@ TrackModel::onPlaybackStopped() oldEntry->setIsPlaying( false ); } } + + +void +TrackModel::ensureResolved() +{ + for( int i = 0; i < rowCount( QModelIndex() ); i++ ) + { + query_ptr query = itemFromIndex( index( i, 0, QModelIndex() ) )->query(); + + if ( !query->numResults() ) + Pipeline::instance()->resolve( query ); + } +} diff --git a/src/libtomahawk/playlist/trackmodel.h b/src/libtomahawk/playlist/trackmodel.h index d9cec03d8..b3a5e0c24 100644 --- a/src/libtomahawk/playlist/trackmodel.h +++ b/src/libtomahawk/playlist/trackmodel.h @@ -76,6 +76,8 @@ public: virtual PlaylistInterface::RepeatMode repeatMode() const { return PlaylistInterface::NoRepeat; } virtual bool shuffled() const { return false; } + virtual void ensureResolved(); + virtual void append( const Tomahawk::query_ptr& query ) = 0; PlItem* itemFromIndex( const QModelIndex& index ) const; diff --git a/src/libtomahawk/qtsingleapp/qtlocalpeer.cpp b/src/libtomahawk/qtsingleapp/qtlocalpeer.cpp deleted file mode 100644 index 382d182dc..000000000 --- a/src/libtomahawk/qtsingleapp/qtlocalpeer.cpp +++ /dev/null @@ -1,199 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of a Qt Solutions component. -** -** You may use this file under the terms of the BSD license as follows: -** -** "Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions are -** met: -** * Redistributions of source code must retain the above copyright -** notice, this list of conditions and the following disclaimer. -** * Redistributions in binary form must reproduce the above copyright -** notice, this list of conditions and the following disclaimer in -** the documentation and/or other materials provided with the -** distribution. -** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor -** the names of its contributors may be used to endorse or promote -** products derived from this software without specific prior written -** permission. -** -** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." -** -****************************************************************************/ - - -#include "qtlocalpeer.h" -#include -#include - -#if defined(Q_OS_WIN) -#include -#include -typedef BOOL(WINAPI*PProcessIdToSessionId)(DWORD,DWORD*); -static PProcessIdToSessionId pProcessIdToSessionId = 0; -#endif -#if defined(Q_OS_UNIX) -#include -#endif - -namespace QtLP_Private { -#include "qtlockedfile.cpp" -#if defined(Q_OS_WIN) -#include "qtlockedfile_win.cpp" -#else -#include "qtlockedfile_unix.cpp" -#endif -} - -const char* QtLocalPeer::ack = "ack"; - -QtLocalPeer::QtLocalPeer(QObject* parent, const QString &appId) - : QObject(parent), id(appId) -{ - QString prefix = id; - if (id.isEmpty()) { - id = QCoreApplication::applicationFilePath(); -#if defined(Q_OS_WIN) - id = id.toLower(); -#endif - prefix = id.section(QLatin1Char('/'), -1); - } - prefix.remove(QRegExp("[^a-zA-Z]")); - prefix.truncate(6); - - QByteArray idc = id.toUtf8(); - quint16 idNum = qChecksum(idc.constData(), idc.size()); - socketName = QLatin1String("qtsingleapp-") + prefix - + QLatin1Char('-') + QString::number(idNum, 16); - -#if defined(Q_OS_WIN) - if (!pProcessIdToSessionId) { - QLibrary lib("kernel32"); - pProcessIdToSessionId = (PProcessIdToSessionId)lib.resolve("ProcessIdToSessionId"); - } - if (pProcessIdToSessionId) { - DWORD sessionId = 0; - pProcessIdToSessionId(GetCurrentProcessId(), &sessionId); - socketName += QLatin1Char('-') + QString::number(sessionId, 16); - } -#else - socketName += QLatin1Char('-') + QString::number(::getuid(), 16); -#endif - - server = new QLocalServer(this); - QString lockName = QDir(QDir::tempPath()).absolutePath() - + QLatin1Char('/') + socketName - + QLatin1String("-lockfile"); - lockFile.setFileName(lockName); - lockFile.open(QIODevice::ReadWrite); -} - - - -bool QtLocalPeer::isClient() -{ - if (lockFile.isLocked()) - return false; - - if (!lockFile.lock(QtLP_Private::QtLockedFile::WriteLock, false)) - return true; - - bool res = server->listen(socketName); -#if defined(Q_OS_UNIX) && (QT_VERSION >= QT_VERSION_CHECK(4,5,0)) - // ### Workaround - if (!res && server->serverError() == QAbstractSocket::AddressInUseError) { - QFile::remove(QDir::cleanPath(QDir::tempPath())+QLatin1Char('/')+socketName); - res = server->listen(socketName); - } -#endif - if (!res) - qWarning("QtSingleCoreApplication: listen on local socket failed, %s", qPrintable(server->errorString())); - QObject::connect(server, SIGNAL(newConnection()), SLOT(receiveConnection())); - return false; -} - - -bool QtLocalPeer::sendMessage(const QString &message, int timeout) -{ - if (!isClient()) - return false; - - QLocalSocket socket; - bool connOk = false; - for(int i = 0; i < 2; i++) { - // Try twice, in case the other instance is just starting up - socket.connectToServer(socketName); - connOk = socket.waitForConnected(timeout/2); - if (connOk || i) - break; - int ms = 250; -#if defined(Q_OS_WIN) - Sleep(DWORD(ms)); -#else - struct timespec ts = { ms / 1000, (ms % 1000) * 1000 * 1000 }; - nanosleep(&ts, NULL); -#endif - } - if (!connOk) - return false; - - QByteArray uMsg(message.toUtf8()); - QDataStream ds(&socket); - ds.writeBytes(uMsg.constData(), uMsg.size()); - bool res = socket.waitForBytesWritten(timeout); - if (res) { - res &= socket.waitForReadyRead(timeout); // wait for ack - if (res) - res &= (socket.read(qstrlen(ack)) == ack); - } - return res; -} - - -void QtLocalPeer::receiveConnection() -{ - QLocalSocket* socket = server->nextPendingConnection(); - if (!socket) - return; - - while (socket->bytesAvailable() < (int)sizeof(quint32)) - socket->waitForReadyRead(); - QDataStream ds(socket); - QByteArray uMsg; - quint32 remaining; - ds >> remaining; - uMsg.resize(remaining); - int got = 0; - char* uMsgBuf = uMsg.data(); - do { - got = ds.readRawData(uMsgBuf, remaining); - remaining -= got; - uMsgBuf += got; - } while (remaining && got >= 0 && socket->waitForReadyRead(2000)); - if (got < 0) { - qWarning("QtLocalPeer: Message reception failed %s", socket->errorString().toLatin1().constData()); - delete socket; - return; - } - QString message(QString::fromUtf8(uMsg)); - socket->write(ack, qstrlen(ack)); - socket->waitForBytesWritten(1000); - delete socket; - emit messageReceived(message); //### (might take a long time to return) -} diff --git a/src/libtomahawk/qtsingleapp/qtlocalpeer.h b/src/libtomahawk/qtsingleapp/qtlocalpeer.h deleted file mode 100644 index 869af2ac2..000000000 --- a/src/libtomahawk/qtsingleapp/qtlocalpeer.h +++ /dev/null @@ -1,72 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of a Qt Solutions component. -** -** You may use this file under the terms of the BSD license as follows: -** -** "Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions are -** met: -** * Redistributions of source code must retain the above copyright -** notice, this list of conditions and the following disclaimer. -** * Redistributions in binary form must reproduce the above copyright -** notice, this list of conditions and the following disclaimer in -** the documentation and/or other materials provided with the -** distribution. -** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor -** the names of its contributors may be used to endorse or promote -** products derived from this software without specific prior written -** permission. -** -** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." -** -****************************************************************************/ - - -#include -#include -#include - -#include "qtlockedfile.h" - -class QtLocalPeer : public QObject -{ - Q_OBJECT - -public: - QtLocalPeer(QObject *parent = 0, const QString &appId = QString()); - bool isClient(); - bool sendMessage(const QString &message, int timeout); - QString applicationId() const - { return id; } - -Q_SIGNALS: - void messageReceived(const QString &message); - -protected Q_SLOTS: - void receiveConnection(); - -protected: - QString id; - QString socketName; - QLocalServer* server; - QtLP_Private::QtLockedFile lockFile; - -private: - static const char* ack; -}; diff --git a/src/libtomahawk/qtsingleapp/qtlockedfile.cpp b/src/libtomahawk/qtsingleapp/qtlockedfile.cpp deleted file mode 100644 index 3e73ba652..000000000 --- a/src/libtomahawk/qtsingleapp/qtlockedfile.cpp +++ /dev/null @@ -1,192 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of a Qt Solutions component. -** -** You may use this file under the terms of the BSD license as follows: -** -** "Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions are -** met: -** * Redistributions of source code must retain the above copyright -** notice, this list of conditions and the following disclaimer. -** * Redistributions in binary form must reproduce the above copyright -** notice, this list of conditions and the following disclaimer in -** the documentation and/or other materials provided with the -** distribution. -** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor -** the names of its contributors may be used to endorse or promote -** products derived from this software without specific prior written -** permission. -** -** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." -** -****************************************************************************/ - -#include "qtlockedfile.h" - -/*! - \class QtLockedFile - - \brief The QtLockedFile class extends QFile with advisory locking - functions. - - A file may be locked in read or write mode. Multiple instances of - \e QtLockedFile, created in multiple processes running on the same - machine, may have a file locked in read mode. Exactly one instance - may have it locked in write mode. A read and a write lock cannot - exist simultaneously on the same file. - - The file locks are advisory. This means that nothing prevents - another process from manipulating a locked file using QFile or - file system functions offered by the OS. Serialization is only - guaranteed if all processes that access the file use - QLockedFile. Also, while holding a lock on a file, a process - must not open the same file again (through any API), or locks - can be unexpectedly lost. - - The lock provided by an instance of \e QtLockedFile is released - whenever the program terminates. This is true even when the - program crashes and no destructors are called. -*/ - -/*! \enum QtLockedFile::LockMode - - This enum describes the available lock modes. - - \value ReadLock A read lock. - \value WriteLock A write lock. - \value NoLock Neither a read lock nor a write lock. -*/ - -/*! - Constructs an unlocked \e QtLockedFile object. This constructor - behaves in the same way as \e QFile::QFile(). - - \sa QFile::QFile() -*/ -QtLockedFile::QtLockedFile() - : QFile() -{ -#ifdef Q_OS_WIN - wmutex = 0; - rmutex = 0; -#endif - m_lock_mode = NoLock; -} - -/*! - Constructs an unlocked QtLockedFile object with file \a name. This - constructor behaves in the same way as \e QFile::QFile(const - QString&). - - \sa QFile::QFile() -*/ -QtLockedFile::QtLockedFile(const QString &name) - : QFile(name) -{ -#ifdef Q_OS_WIN - wmutex = 0; - rmutex = 0; -#endif - m_lock_mode = NoLock; -} - -/*! - Opens the file in OpenMode \a mode. - - This is identical to QFile::open(), with the one exception that the - Truncate mode flag is disallowed. Truncation would conflict with the - advisory file locking, since the file would be modified before the - write lock is obtained. If truncation is required, use resize(0) - after obtaining the write lock. - - Returns true if successful; otherwise false. - - \sa QFile::open(), QFile::resize() -*/ -bool QtLockedFile::open(OpenMode mode) -{ - if (mode & QIODevice::Truncate) { - qWarning("QtLockedFile::open(): Truncate mode not allowed."); - return false; - } - return QFile::open(mode); -} - -/*! - Returns \e true if this object has a in read or write lock; - otherwise returns \e false. - - \sa lockMode() -*/ -bool QtLockedFile::isLocked() const -{ - return m_lock_mode != NoLock; -} - -/*! - Returns the type of lock currently held by this object, or \e - QtLockedFile::NoLock. - - \sa isLocked() -*/ -QtLockedFile::LockMode QtLockedFile::lockMode() const -{ - return m_lock_mode; -} - -/*! - \fn bool QtLockedFile::lock(LockMode mode, bool block = true) - - Obtains a lock of type \a mode. The file must be opened before it - can be locked. - - If \a block is true, this function will block until the lock is - aquired. If \a block is false, this function returns \e false - immediately if the lock cannot be aquired. - - If this object already has a lock of type \a mode, this function - returns \e true immediately. If this object has a lock of a - different type than \a mode, the lock is first released and then a - new lock is obtained. - - This function returns \e true if, after it executes, the file is - locked by this object, and \e false otherwise. - - \sa unlock(), isLocked(), lockMode() -*/ - -/*! - \fn bool QtLockedFile::unlock() - - Releases a lock. - - If the object has no lock, this function returns immediately. - - This function returns \e true if, after it executes, the file is - not locked by this object, and \e false otherwise. - - \sa lock(), isLocked(), lockMode() -*/ - -/*! - \fn QtLockedFile::~QtLockedFile() - - Destroys the \e QtLockedFile object. If any locks were held, they - are released. -*/ diff --git a/src/libtomahawk/qtsingleapp/qtlockedfile.h b/src/libtomahawk/qtsingleapp/qtlockedfile.h deleted file mode 100644 index 07a42bffb..000000000 --- a/src/libtomahawk/qtsingleapp/qtlockedfile.h +++ /dev/null @@ -1,96 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of a Qt Solutions component. -** -** You may use this file under the terms of the BSD license as follows: -** -** "Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions are -** met: -** * Redistributions of source code must retain the above copyright -** notice, this list of conditions and the following disclaimer. -** * Redistributions in binary form must reproduce the above copyright -** notice, this list of conditions and the following disclaimer in -** the documentation and/or other materials provided with the -** distribution. -** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor -** the names of its contributors may be used to endorse or promote -** products derived from this software without specific prior written -** permission. -** -** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." -** -****************************************************************************/ - -#ifndef QTLOCKEDFILE_H -#define QTLOCKEDFILE_H - -#include -#ifdef Q_OS_WIN -#include -#endif - -#if defined(Q_WS_WIN) -# if !defined(QT_QTLOCKEDFILE_EXPORT) && !defined(QT_QTLOCKEDFILE_IMPORT) -# define QT_QTLOCKEDFILE_EXPORT -# elif defined(QT_QTLOCKEDFILE_IMPORT) -# if defined(QT_QTLOCKEDFILE_EXPORT) -# undef QT_QTLOCKEDFILE_EXPORT -# endif -# define QT_QTLOCKEDFILE_EXPORT __declspec(dllimport) -# elif defined(QT_QTLOCKEDFILE_EXPORT) -# undef QT_QTLOCKEDFILE_EXPORT -# define QT_QTLOCKEDFILE_EXPORT __declspec(dllexport) -# endif -#else -# define QT_QTLOCKEDFILE_EXPORT -#endif - -namespace QtLP_Private { - -class QT_QTLOCKEDFILE_EXPORT QtLockedFile : public QFile -{ -public: - enum LockMode { NoLock = 0, ReadLock, WriteLock }; - - QtLockedFile(); - QtLockedFile(const QString &name); - ~QtLockedFile(); - - bool open(OpenMode mode); - - bool lock(LockMode mode, bool block = true); - bool unlock(); - bool isLocked() const; - LockMode lockMode() const; - -private: -#ifdef Q_OS_WIN - Qt::HANDLE wmutex; - Qt::HANDLE rmutex; - QVector rmutexes; - QString mutexname; - - Qt::HANDLE getMutexHandle(int idx, bool doCreate); - bool waitMutex(Qt::HANDLE mutex, bool doBlock); - -#endif - LockMode m_lock_mode; -}; -} -#endif diff --git a/src/libtomahawk/qtsingleapp/qtlockedfile_unix.cpp b/src/libtomahawk/qtsingleapp/qtlockedfile_unix.cpp deleted file mode 100644 index 715c7d9b1..000000000 --- a/src/libtomahawk/qtsingleapp/qtlockedfile_unix.cpp +++ /dev/null @@ -1,114 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of a Qt Solutions component. -** -** You may use this file under the terms of the BSD license as follows: -** -** "Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions are -** met: -** * Redistributions of source code must retain the above copyright -** notice, this list of conditions and the following disclaimer. -** * Redistributions in binary form must reproduce the above copyright -** notice, this list of conditions and the following disclaimer in -** the documentation and/or other materials provided with the -** distribution. -** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor -** the names of its contributors may be used to endorse or promote -** products derived from this software without specific prior written -** permission. -** -** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." -** -****************************************************************************/ - -#include -#include -#include -#include - -#include "qtlockedfile.h" - -bool QtLockedFile::lock(LockMode mode, bool block) -{ - if (!isOpen()) { - qWarning("QtLockedFile::lock(): file is not opened"); - return false; - } - - if (mode == NoLock) - return unlock(); - - if (mode == m_lock_mode) - return true; - - if (m_lock_mode != NoLock) - unlock(); - - struct flock fl; - fl.l_whence = SEEK_SET; - fl.l_start = 0; - fl.l_len = 0; - fl.l_type = (mode == ReadLock) ? F_RDLCK : F_WRLCK; - int cmd = block ? F_SETLKW : F_SETLK; - int ret = fcntl(handle(), cmd, &fl); - - if (ret == -1) { - if (errno != EINTR && errno != EAGAIN) - qWarning("QtLockedFile::lock(): fcntl: %s", strerror(errno)); - return false; - } - - - m_lock_mode = mode; - return true; -} - - -bool QtLockedFile::unlock() -{ - if (!isOpen()) { - qWarning("QtLockedFile::unlock(): file is not opened"); - return false; - } - - if (!isLocked()) - return true; - - struct flock fl; - fl.l_whence = SEEK_SET; - fl.l_start = 0; - fl.l_len = 0; - fl.l_type = F_UNLCK; - int ret = fcntl(handle(), F_SETLKW, &fl); - - if (ret == -1) { - qWarning("QtLockedFile::lock(): fcntl: %s", strerror(errno)); - return false; - } - - m_lock_mode = NoLock; - return true; -} - -QtLockedFile::~QtLockedFile() -{ - if (isOpen()) - unlock(); -} - diff --git a/src/libtomahawk/qtsingleapp/qtlockedfile_win.cpp b/src/libtomahawk/qtsingleapp/qtlockedfile_win.cpp deleted file mode 100644 index 8090470cd..000000000 --- a/src/libtomahawk/qtsingleapp/qtlockedfile_win.cpp +++ /dev/null @@ -1,208 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of a Qt Solutions component. -** -** You may use this file under the terms of the BSD license as follows: -** -** "Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions are -** met: -** * Redistributions of source code must retain the above copyright -** notice, this list of conditions and the following disclaimer. -** * Redistributions in binary form must reproduce the above copyright -** notice, this list of conditions and the following disclaimer in -** the documentation and/or other materials provided with the -** distribution. -** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor -** the names of its contributors may be used to endorse or promote -** products derived from this software without specific prior written -** permission. -** -** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." -** -****************************************************************************/ - -#include "qtlockedfile.h" -#include -#include - -#define MUTEX_PREFIX "QtLockedFile mutex " -// Maximum number of concurrent read locks. Must not be greater than MAXIMUM_WAIT_OBJECTS -#define MAX_READERS MAXIMUM_WAIT_OBJECTS - -#define TCHAR WCHAR - -Qt::HANDLE QtLockedFile::getMutexHandle(int idx, bool doCreate) -{ - if (mutexname.isEmpty()) { - QFileInfo fi(*this); - mutexname = QString::fromLatin1(MUTEX_PREFIX) - + fi.absoluteFilePath().toLower(); - } - QString mname(mutexname); - if (idx >= 0) - mname += QString::number(idx); - - Qt::HANDLE mutex; - if (doCreate) { - QT_WA( { mutex = CreateMutexW(NULL, FALSE, (TCHAR*)mname.utf16()); }, - { mutex = CreateMutexA(NULL, FALSE, mname.toLocal8Bit().constData()); } ); - if (!mutex) { - qErrnoWarning("QtLockedFile::lock(): CreateMutex failed"); - return 0; - } - } - else { - QT_WA( { mutex = OpenMutexW(SYNCHRONIZE | MUTEX_MODIFY_STATE, FALSE, (TCHAR*)mname.utf16()); }, - { mutex = OpenMutexA(SYNCHRONIZE | MUTEX_MODIFY_STATE, FALSE, mname.toLocal8Bit().constData()); } ); - if (!mutex) { - if (GetLastError() != ERROR_FILE_NOT_FOUND) - qErrnoWarning("QtLockedFile::lock(): OpenMutex failed"); - return 0; - } - } - return mutex; -} - -bool QtLockedFile::waitMutex(Qt::HANDLE mutex, bool doBlock) -{ - Q_ASSERT(mutex); - DWORD res = WaitForSingleObject(mutex, doBlock ? INFINITE : 0); - switch (res) { - case WAIT_OBJECT_0: - case WAIT_ABANDONED: - return true; - break; - case WAIT_TIMEOUT: - break; - default: - qErrnoWarning("QtLockedFile::lock(): WaitForSingleObject failed"); - } - return false; -} - - - -bool QtLockedFile::lock(LockMode mode, bool block) -{ - if (!isOpen()) { - qWarning("QtLockedFile::lock(): file is not opened"); - return false; - } - - if (mode == NoLock) - return unlock(); - - if (mode == m_lock_mode) - return true; - - if (m_lock_mode != NoLock) - unlock(); - - if (!wmutex && !(wmutex = getMutexHandle(-1, true))) - return false; - - if (!waitMutex(wmutex, block)) - return false; - - if (mode == ReadLock) { - int idx = 0; - for (; idx < MAX_READERS; idx++) { - rmutex = getMutexHandle(idx, false); - if (!rmutex || waitMutex(rmutex, false)) - break; - CloseHandle(rmutex); - } - bool ok = true; - if (idx >= MAX_READERS) { - qWarning("QtLockedFile::lock(): too many readers"); - rmutex = 0; - ok = false; - } - else if (!rmutex) { - rmutex = getMutexHandle(idx, true); - if (!rmutex || !waitMutex(rmutex, false)) - ok = false; - } - if (!ok && rmutex) { - CloseHandle(rmutex); - rmutex = 0; - } - ReleaseMutex(wmutex); - if (!ok) - return false; - } - else { - Q_ASSERT(rmutexes.isEmpty()); - for (int i = 0; i < MAX_READERS; i++) { - Qt::HANDLE mutex = getMutexHandle(i, false); - if (mutex) - rmutexes.append(mutex); - } - if (rmutexes.size()) { - DWORD res = WaitForMultipleObjects(rmutexes.size(), rmutexes.constData(), - TRUE, block ? INFINITE : 0); - if (res != WAIT_OBJECT_0 && res != WAIT_ABANDONED) { - if (res != WAIT_TIMEOUT) - qErrnoWarning("QtLockedFile::lock(): WaitForMultipleObjects failed"); - m_lock_mode = WriteLock; // trick unlock() to clean up - semiyucky - unlock(); - return false; - } - } - } - - m_lock_mode = mode; - return true; -} - -bool QtLockedFile::unlock() -{ - if (!isOpen()) { - qWarning("QtLockedFile::unlock(): file is not opened"); - return false; - } - - if (!isLocked()) - return true; - - if (m_lock_mode == ReadLock) { - ReleaseMutex(rmutex); - CloseHandle(rmutex); - rmutex = 0; - } - else { - foreach(Qt::HANDLE mutex, rmutexes) { - ReleaseMutex(mutex); - CloseHandle(mutex); - } - rmutexes.clear(); - ReleaseMutex(wmutex); - } - - m_lock_mode = QtLockedFile::NoLock; - return true; -} - -QtLockedFile::~QtLockedFile() -{ - if (isOpen()) - unlock(); - if (wmutex) - CloseHandle(wmutex); -} diff --git a/src/libtomahawk/qtsingleapp/qtsingleapplication.cpp b/src/libtomahawk/qtsingleapp/qtsingleapplication.cpp deleted file mode 100644 index 5a8f1b035..000000000 --- a/src/libtomahawk/qtsingleapp/qtsingleapplication.cpp +++ /dev/null @@ -1,344 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of a Qt Solutions component. -** -** You may use this file under the terms of the BSD license as follows: -** -** "Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions are -** met: -** * Redistributions of source code must retain the above copyright -** notice, this list of conditions and the following disclaimer. -** * Redistributions in binary form must reproduce the above copyright -** notice, this list of conditions and the following disclaimer in -** the documentation and/or other materials provided with the -** distribution. -** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor -** the names of its contributors may be used to endorse or promote -** products derived from this software without specific prior written -** permission. -** -** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." -** -****************************************************************************/ - - -#include "qtsingleapplication.h" -#include "qtlocalpeer.h" -#include - - -/*! - \class QtSingleApplication qtsingleapplication.h - \brief The QtSingleApplication class provides an API to detect and - communicate with running instances of an application. - - This class allows you to create applications where only one - instance should be running at a time. I.e., if the user tries to - launch another instance, the already running instance will be - activated instead. Another usecase is a client-server system, - where the first started instance will assume the role of server, - and the later instances will act as clients of that server. - - By default, the full path of the executable file is used to - determine whether two processes are instances of the same - application. You can also provide an explicit identifier string - that will be compared instead. - - The application should create the QtSingleApplication object early - in the startup phase, and call isRunning() to find out if another - instance of this application is already running. If isRunning() - returns false, it means that no other instance is running, and - this instance has assumed the role as the running instance. In - this case, the application should continue with the initialization - of the application user interface before entering the event loop - with exec(), as normal. - - The messageReceived() signal will be emitted when the running - application receives messages from another instance of the same - application. When a message is received it might be helpful to the - user to raise the application so that it becomes visible. To - facilitate this, QtSingleApplication provides the - setActivationWindow() function and the activateWindow() slot. - - If isRunning() returns true, another instance is already - running. It may be alerted to the fact that another instance has - started by using the sendMessage() function. Also data such as - startup parameters (e.g. the name of the file the user wanted this - new instance to open) can be passed to the running instance with - this function. Then, the application should terminate (or enter - client mode). - - If isRunning() returns true, but sendMessage() fails, that is an - indication that the running instance is frozen. - - Here's an example that shows how to convert an existing - application to use QtSingleApplication. It is very simple and does - not make use of all QtSingleApplication's functionality (see the - examples for that). - - \code - // Original - int main(int argc, char **argv) - { - QApplication app(argc, argv); - - MyMainWidget mmw; - mmw.show(); - return app.exec(); - } - - // Single instance - int main(int argc, char **argv) - { - QtSingleApplication app(argc, argv); - - if (app.isRunning()) - return !app.sendMessage(someDataString); - - MyMainWidget mmw; - app.setActivationWindow(&mmw); - mmw.show(); - return app.exec(); - } - \endcode - - Once this QtSingleApplication instance is destroyed (normally when - the process exits or crashes), when the user next attempts to run the - application this instance will not, of course, be encountered. The - next instance to call isRunning() or sendMessage() will assume the - role as the new running instance. - - For console (non-GUI) applications, QtSingleCoreApplication may be - used instead of this class, to avoid the dependency on the QtGui - library. - - \sa QtSingleCoreApplication -*/ - - -void QtSingleApplication::sysInit(const QString &appId) -{ - actWin = 0; - peer = new QtLocalPeer(this, appId); - connect(peer, SIGNAL(messageReceived(const QString&)), SIGNAL(messageReceived(const QString&))); -} - - -/*! - Creates a QtSingleApplication object. The application identifier - will be QCoreApplication::applicationFilePath(). \a argc, \a - argv, and \a GUIenabled are passed on to the QAppliation constructor. - - If you are creating a console application (i.e. setting \a - GUIenabled to false), you may consider using - QtSingleCoreApplication instead. -*/ - -QtSingleApplication::QtSingleApplication(int &argc, char **argv, bool GUIenabled) - : QApplication(argc, argv, GUIenabled) -{ - sysInit(); -} - - -/*! - Creates a QtSingleApplication object with the application - identifier \a appId. \a argc and \a argv are passed on to the - QAppliation constructor. -*/ - -QtSingleApplication::QtSingleApplication(const QString &appId, int &argc, char **argv) - : QApplication(argc, argv) -{ - sysInit(appId); -} - - -/*! - Creates a QtSingleApplication object. The application identifier - will be QCoreApplication::applicationFilePath(). \a argc, \a - argv, and \a type are passed on to the QAppliation constructor. -*/ -QtSingleApplication::QtSingleApplication(int &argc, char **argv, Type type) - : QApplication(argc, argv, type) -{ - sysInit(); -} - - -#if defined(Q_WS_X11) -/*! - Special constructor for X11, ref. the documentation of - QApplication's corresponding constructor. The application identifier - will be QCoreApplication::applicationFilePath(). \a dpy, \a visual, - and \a cmap are passed on to the QApplication constructor. -*/ -QtSingleApplication::QtSingleApplication(Display* dpy, Qt::HANDLE visual, Qt::HANDLE cmap) - : QApplication(dpy, visual, cmap) -{ - sysInit(); -} - -/*! - Special constructor for X11, ref. the documentation of - QApplication's corresponding constructor. The application identifier - will be QCoreApplication::applicationFilePath(). \a dpy, \a argc, \a - argv, \a visual, and \a cmap are passed on to the QApplication - constructor. -*/ -QtSingleApplication::QtSingleApplication(Display *dpy, int &argc, char **argv, Qt::HANDLE visual, Qt::HANDLE cmap) - : QApplication(dpy, argc, argv, visual, cmap) -{ - sysInit(); -} - -/*! - Special constructor for X11, ref. the documentation of - QApplication's corresponding constructor. The application identifier - will be \a appId. \a dpy, \a argc, \a - argv, \a visual, and \a cmap are passed on to the QApplication - constructor. -*/ -QtSingleApplication::QtSingleApplication(Display* dpy, const QString &appId, int argc, char **argv, Qt::HANDLE visual, Qt::HANDLE cmap) - : QApplication(dpy, argc, argv, visual, cmap) -{ - sysInit(appId); -} -#endif - - -/*! - Returns true if another instance of this application is running; - otherwise false. - - This function does not find instances of this application that are - being run by a different user (on Windows: that are running in - another session). - - \sa sendMessage() -*/ - -bool QtSingleApplication::isRunning() -{ - return peer->isClient(); -} - - -/*! - Tries to send the text \a message to the currently running - instance. The QtSingleApplication object in the running instance - will emit the messageReceived() signal when it receives the - message. - - This function returns true if the message has been sent to, and - processed by, the current instance. If there is no instance - currently running, or if the running instance fails to process the - message within \a timeout milliseconds, this function return false. - - \sa isRunning(), messageReceived() -*/ -bool QtSingleApplication::sendMessage(const QString &message, int timeout) -{ - return peer->sendMessage(message, timeout); -} - - -/*! - Returns the application identifier. Two processes with the same - identifier will be regarded as instances of the same application. -*/ -QString QtSingleApplication::id() const -{ - return peer->applicationId(); -} - - -/*! - Sets the activation window of this application to \a aw. The - activation window is the widget that will be activated by - activateWindow(). This is typically the application's main window. - - If \a activateOnMessage is true (the default), the window will be - activated automatically every time a message is received, just prior - to the messageReceived() signal being emitted. - - \sa activateWindow(), messageReceived() -*/ - -void QtSingleApplication::setActivationWindow(QWidget* aw, bool activateOnMessage) -{ - actWin = aw; - if (activateOnMessage) - connect(peer, SIGNAL(messageReceived(const QString&)), this, SLOT(activateWindow())); - else - disconnect(peer, SIGNAL(messageReceived(const QString&)), this, SLOT(activateWindow())); -} - - -/*! - Returns the applications activation window if one has been set by - calling setActivationWindow(), otherwise returns 0. - - \sa setActivationWindow() -*/ -QWidget* QtSingleApplication::activationWindow() const -{ - return actWin; -} - - -/*! - De-minimizes, raises, and activates this application's activation window. - This function does nothing if no activation window has been set. - - This is a convenience function to show the user that this - application instance has been activated when he has tried to start - another instance. - - This function should typically be called in response to the - messageReceived() signal. By default, that will happen - automatically, if an activation window has been set. - - \sa setActivationWindow(), messageReceived(), initialize() -*/ -void QtSingleApplication::activateWindow() -{ - if (actWin) { - actWin->setWindowState(actWin->windowState() & ~Qt::WindowMinimized); - actWin->raise(); - actWin->activateWindow(); - } -} - - -/*! - \fn void QtSingleApplication::messageReceived(const QString& message) - - This signal is emitted when the current instance receives a \a - message from another instance of this application. - - \sa sendMessage(), setActivationWindow(), activateWindow() -*/ - - -/*! - \fn void QtSingleApplication::initialize(bool dummy = true) - - \obsolete -*/ diff --git a/src/libtomahawk/qtsingleapp/qtsingleapplication.h b/src/libtomahawk/qtsingleapp/qtsingleapplication.h deleted file mode 100644 index c696d60ce..000000000 --- a/src/libtomahawk/qtsingleapp/qtsingleapplication.h +++ /dev/null @@ -1,84 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of a Qt Solutions component. -** -** You may use this file under the terms of the BSD license as follows: -** -** "Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions are -** met: -** * Redistributions of source code must retain the above copyright -** notice, this list of conditions and the following disclaimer. -** * Redistributions in binary form must reproduce the above copyright -** notice, this list of conditions and the following disclaimer in -** the documentation and/or other materials provided with the -** distribution. -** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor -** the names of its contributors may be used to endorse or promote -** products derived from this software without specific prior written -** permission. -** -** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." -** -****************************************************************************/ - - -#include - -#include "dllmacro.h" - -class QtLocalPeer; - -class DLLEXPORT QtSingleApplication : public QApplication -{ - Q_OBJECT - -public: - QtSingleApplication(int &argc, char **argv, bool GUIenabled = true); - QtSingleApplication(const QString &id, int &argc, char **argv); - QtSingleApplication(int &argc, char **argv, Type type); -#if defined(Q_WS_X11) - QtSingleApplication(Display* dpy, Qt::HANDLE visual = 0, Qt::HANDLE colormap = 0); - QtSingleApplication(Display *dpy, int &argc, char **argv, Qt::HANDLE visual = 0, Qt::HANDLE cmap= 0); - QtSingleApplication(Display* dpy, const QString &appId, int argc, char **argv, Qt::HANDLE visual = 0, Qt::HANDLE colormap = 0); -#endif - - bool isRunning(); - QString id() const; - - void setActivationWindow(QWidget* aw, bool activateOnMessage = true); - QWidget* activationWindow() const; - - // Obsolete: - void initialize(bool dummy = true) - { isRunning(); Q_UNUSED(dummy) } - -public Q_SLOTS: - bool sendMessage(const QString &message, int timeout = 5000); - void activateWindow(); - - -Q_SIGNALS: - void messageReceived(const QString &message); - - -private: - void sysInit(const QString &appId = QString()); - QtLocalPeer *peer; - QWidget *actWin; -}; diff --git a/src/libtomahawk/qtsingleapp/qtsinglecoreapplication.cpp b/src/libtomahawk/qtsingleapp/qtsinglecoreapplication.cpp deleted file mode 100644 index cf607710e..000000000 --- a/src/libtomahawk/qtsingleapp/qtsinglecoreapplication.cpp +++ /dev/null @@ -1,148 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of a Qt Solutions component. -** -** You may use this file under the terms of the BSD license as follows: -** -** "Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions are -** met: -** * Redistributions of source code must retain the above copyright -** notice, this list of conditions and the following disclaimer. -** * Redistributions in binary form must reproduce the above copyright -** notice, this list of conditions and the following disclaimer in -** the documentation and/or other materials provided with the -** distribution. -** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor -** the names of its contributors may be used to endorse or promote -** products derived from this software without specific prior written -** permission. -** -** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." -** -****************************************************************************/ - - -#include "qtsinglecoreapplication.h" -#include "qtlocalpeer.h" - -/*! - \class QtSingleCoreApplication qtsinglecoreapplication.h - \brief A variant of the QtSingleApplication class for non-GUI applications. - - This class is a variant of QtSingleApplication suited for use in - console (non-GUI) applications. It is an extension of - QCoreApplication (instead of QApplication). It does not require - the QtGui library. - - The API and usage is identical to QtSingleApplication, except that - functions relating to the "activation window" are not present, for - obvious reasons. Please refer to the QtSingleApplication - documentation for explanation of the usage. - - A QtSingleCoreApplication instance can communicate to a - QtSingleApplication instance if they share the same application - id. Hence, this class can be used to create a light-weight - command-line tool that sends commands to a GUI application. - - \sa QtSingleApplication -*/ - -/*! - Creates a QtSingleCoreApplication object. The application identifier - will be QCoreApplication::applicationFilePath(). \a argc and \a - argv are passed on to the QCoreAppliation constructor. -*/ - -QtSingleCoreApplication::QtSingleCoreApplication(int &argc, char **argv) - : QCoreApplication(argc, argv) -{ - peer = new QtLocalPeer(this); - connect(peer, SIGNAL(messageReceived(const QString&)), SIGNAL(messageReceived(const QString&))); -} - - -/*! - Creates a QtSingleCoreApplication object with the application - identifier \a appId. \a argc and \a argv are passed on to the - QCoreAppliation constructor. -*/ -QtSingleCoreApplication::QtSingleCoreApplication(const QString &appId, int &argc, char **argv) - : QCoreApplication(argc, argv) -{ - peer = new QtLocalPeer(this, appId); - connect(peer, SIGNAL(messageReceived(const QString&)), SIGNAL(messageReceived(const QString&))); -} - - -/*! - Returns true if another instance of this application is running; - otherwise false. - - This function does not find instances of this application that are - being run by a different user (on Windows: that are running in - another session). - - \sa sendMessage() -*/ - -bool QtSingleCoreApplication::isRunning() -{ - return peer->isClient(); -} - - -/*! - Tries to send the text \a message to the currently running - instance. The QtSingleCoreApplication object in the running instance - will emit the messageReceived() signal when it receives the - message. - - This function returns true if the message has been sent to, and - processed by, the current instance. If there is no instance - currently running, or if the running instance fails to process the - message within \a timeout milliseconds, this function return false. - - \sa isRunning(), messageReceived() -*/ - -bool QtSingleCoreApplication::sendMessage(const QString &message, int timeout) -{ - return peer->sendMessage(message, timeout); -} - - -/*! - Returns the application identifier. Two processes with the same - identifier will be regarded as instances of the same application. -*/ - -QString QtSingleCoreApplication::id() const -{ - return peer->applicationId(); -} - - -/*! - \fn void QtSingleCoreApplication::messageReceived(const QString& message) - - This signal is emitted when the current instance receives a \a - message from another instance of this application. - - \sa sendMessage() -*/ diff --git a/src/libtomahawk/qtsingleapp/qtsinglecoreapplication.h b/src/libtomahawk/qtsingleapp/qtsinglecoreapplication.h deleted file mode 100644 index ef529a8f6..000000000 --- a/src/libtomahawk/qtsingleapp/qtsinglecoreapplication.h +++ /dev/null @@ -1,66 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of a Qt Solutions component. -** -** You may use this file under the terms of the BSD license as follows: -** -** "Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions are -** met: -** * Redistributions of source code must retain the above copyright -** notice, this list of conditions and the following disclaimer. -** * Redistributions in binary form must reproduce the above copyright -** notice, this list of conditions and the following disclaimer in -** the documentation and/or other materials provided with the -** distribution. -** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor -** the names of its contributors may be used to endorse or promote -** products derived from this software without specific prior written -** permission. -** -** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." -** -****************************************************************************/ - - -#include - -class QtLocalPeer; - -class QtSingleCoreApplication : public QCoreApplication -{ - Q_OBJECT - -public: - QtSingleCoreApplication(int &argc, char **argv); - QtSingleCoreApplication(const QString &id, int &argc, char **argv); - - bool isRunning(); - QString id() const; - -public Q_SLOTS: - bool sendMessage(const QString &message, int timeout = 5000); - - -Q_SIGNALS: - void messageReceived(const QString &message); - - -private: - QtLocalPeer* peer; -}; diff --git a/src/libtomahawk/query.cpp b/src/libtomahawk/query.cpp index b8c48e83c..10cf98c81 100644 --- a/src/libtomahawk/query.cpp +++ b/src/libtomahawk/query.cpp @@ -63,7 +63,7 @@ Query::addResults( const QList< Tomahawk::result_ptr >& newresults ) { bool becameSolved = false; { -// QMutexLocker lock( &m_mut ); + QMutexLocker lock( &m_mutex ); m_results.append( newresults ); qStableSort( m_results.begin(), m_results.end(), Query::resultSorter ); @@ -101,7 +101,7 @@ void Query::removeResult( const Tomahawk::result_ptr& result ) { { -// QMutexLocker lock( &m_mut ); + QMutexLocker lock( &m_mutex ); m_results.removeAll( result ); } @@ -121,7 +121,7 @@ Query::onResolvingFinished() QList< result_ptr > Query::results() const { -// QMutexLocker lock( &m_mut ); + QMutexLocker lock( &m_mutex ); return m_results; } @@ -129,7 +129,7 @@ Query::results() const unsigned int Query::numResults() const { -// QMutexLocker lock( &m_mut ); + QMutexLocker lock( &m_mutex ); return m_results.length(); } @@ -149,7 +149,18 @@ Query::id() const bool Query::resultSorter( const result_ptr& left, const result_ptr& right ) { - return left->score() > right->score(); + const float ls = left->score(); + const float rs = right->score(); + + if ( ls == rs ) + { + if ( !left->collection().isNull() && left->collection()->source()->isLocal() ) + return true; + else + return false; + } + + return ls > rs; } diff --git a/src/libtomahawk/query.h b/src/libtomahawk/query.h index bfa2b3d1d..9c84d68bf 100644 --- a/src/libtomahawk/query.h +++ b/src/libtomahawk/query.h @@ -116,6 +116,8 @@ private: QString m_track; int m_duration; QString m_resultHint; + + mutable QMutex m_mutex; }; }; //ns diff --git a/src/libtomahawk/source.cpp b/src/libtomahawk/source.cpp index 59ad94688..54c4394b3 100644 --- a/src/libtomahawk/source.cpp +++ b/src/libtomahawk/source.cpp @@ -30,7 +30,7 @@ using namespace Tomahawk; -Source::Source( int id, const QString &username ) +Source::Source( int id, const QString& username ) : QObject() , m_isLocal( false ) , m_online( false ) @@ -38,7 +38,7 @@ Source::Source( int id, const QString &username ) , m_id( id ) , m_cc( 0 ) { - qDebug() << Q_FUNC_INFO; + qDebug() << Q_FUNC_INFO << id << username; if ( id == 0 ) { @@ -146,6 +146,7 @@ Source::setOnline() { if ( m_online ) return; + m_online = true; // ensure username is in the database DatabaseCommand_addSource* cmd = new DatabaseCommand_addSource( m_username, m_friendlyname ); @@ -153,7 +154,6 @@ Source::setOnline() SLOT( dbLoaded( unsigned int, const QString& ) ) ); Database::instance()->enqueue( QSharedPointer(cmd) ); - m_online = true; emit online(); } diff --git a/src/libtomahawk/tomahawksettings.cpp b/src/libtomahawk/tomahawksettings.cpp index 82d04d75c..bdbe3c171 100644 --- a/src/libtomahawk/tomahawksettings.cpp +++ b/src/libtomahawk/tomahawksettings.cpp @@ -66,29 +66,50 @@ TomahawkSettings::~TomahawkSettings() QStringList -TomahawkSettings::scannerPath() const +TomahawkSettings::scannerPaths() { + //FIXME: After enough time, remove this hack (and make const) #ifndef TOMAHAWK_HEADLESS - return value( "scannerpath", QDesktopServices::storageLocation( QDesktopServices::MusicLocation ) ).toStringList(); + if( value( "scannerpaths" ).isNull() ) + setValue( "scannerpaths", value( "scannerpath" ) ); + return value( "scannerpaths", QDesktopServices::storageLocation( QDesktopServices::MusicLocation ) ).toStringList(); #else - return value( "scannerpath", "" ).toStringList(); + if( value( "scannerpaths" ).isNull() ) + setValue( "scannerpaths", value( "scannerpath" ) ); + return value( "scannerpaths", "" ).toStringList(); #endif } void -TomahawkSettings::setScannerPath( const QStringList& path ) +TomahawkSettings::setScannerPaths( const QStringList& paths ) { - setValue( "scannerpath", path ); + setValue( "scannerpaths", paths ); } bool -TomahawkSettings::hasScannerPath() const +TomahawkSettings::hasScannerPaths() const { - return contains( "scannerpath" ); + //FIXME: After enough time, remove this hack + return contains( "scannerpaths" ) || contains( "scannerpath" ); } + +bool +TomahawkSettings::watchForChanges() const +{ + return value( "watchForChanges", true ).toBool(); +} + + +void +TomahawkSettings::setWatchForChanges( bool watch ) +{ + setValue( "watchForChanges", watch ); +} + + void TomahawkSettings::setAcceptedLegalWarning( bool accept ) { diff --git a/src/libtomahawk/tomahawksettings.h b/src/libtomahawk/tomahawksettings.h index cc2b2923f..42daa91e3 100644 --- a/src/libtomahawk/tomahawksettings.h +++ b/src/libtomahawk/tomahawksettings.h @@ -41,9 +41,12 @@ public: void applyChanges() { emit changed(); } /// General settings - QStringList scannerPath() const; /// QDesktopServices::MusicLocation by default - void setScannerPath( const QStringList& path ); - bool hasScannerPath() const; + QStringList scannerPaths(); /// QDesktopServices::MusicLocation by default + void setScannerPaths( const QStringList& paths ); + bool hasScannerPaths() const; + + bool watchForChanges() const; + void setWatchForChanges( bool watch ); bool acceptedLegalWarning() const; void setAcceptedLegalWarning( bool accept ); diff --git a/src/libtomahawk/utils/animatedsplitter.cpp b/src/libtomahawk/utils/animatedsplitter.cpp index 3d9607ee3..3ec748052 100644 --- a/src/libtomahawk/utils/animatedsplitter.cpp +++ b/src/libtomahawk/utils/animatedsplitter.cpp @@ -83,7 +83,7 @@ AnimatedSplitter::hide( int index, bool animate ) emit hidden( w ); w->setMinimumHeight( minHeight ); - qDebug() << "animating to:" << w->height() << "from" << minHeight; +// qDebug() << "animating to:" << w->height() << "from" << minHeight; m_animateForward = false; if ( animate ) diff --git a/src/libtomahawk/widgets/welcomewidget.cpp b/src/libtomahawk/widgets/welcomewidget.cpp index cc4460895..d7ad62938 100644 --- a/src/libtomahawk/widgets/welcomewidget.cpp +++ b/src/libtomahawk/widgets/welcomewidget.cpp @@ -31,7 +31,9 @@ #include -#define FILTER_TIMEOUT 280 +#define HISTORY_TRACK_ITEMS 50 +#define HISTORY_PLAYLIST_ITEMS 10 +#define HISTORY_RESOLVING_TIMEOUT 2500 WelcomeWidget::WelcomeWidget( QWidget* parent ) @@ -46,7 +48,10 @@ WelcomeWidget::WelcomeWidget( QWidget* parent ) m_tracksModel = new PlaylistModel( ui->tracksView ); ui->tracksView->setModel( m_tracksModel ); - m_tracksModel->loadHistory( Tomahawk::source_ptr() ); + m_tracksModel->loadHistory( Tomahawk::source_ptr(), HISTORY_TRACK_ITEMS ); + + m_timer = new QTimer( this ); + connect( m_timer, SIGNAL( timeout() ), SLOT( checkQueries() ) ); connect( SourceList::instance(), SIGNAL( sourceAdded( Tomahawk::source_ptr ) ), SLOT( onSourceAdded( Tomahawk::source_ptr ) ) ); @@ -96,10 +101,25 @@ WelcomeWidget::onSourceAdded( const Tomahawk::source_ptr& source ) } +void +WelcomeWidget::checkQueries() +{ + m_timer->stop(); + m_tracksModel->ensureResolved(); +} + + void WelcomeWidget::onPlaybackFinished( const Tomahawk::query_ptr& query ) { m_tracksModel->insert( 0, query ); + + if ( m_tracksModel->trackCount() > HISTORY_TRACK_ITEMS ) + m_tracksModel->remove( HISTORY_TRACK_ITEMS ); + + if ( m_timer->isActive() ) + m_timer->stop(); + m_timer->start( HISTORY_RESOLVING_TIMEOUT ); } diff --git a/src/libtomahawk/widgets/welcomewidget.h b/src/libtomahawk/widgets/welcomewidget.h index 9ec4e249c..036e3d5ca 100644 --- a/src/libtomahawk/widgets/welcomewidget.h +++ b/src/libtomahawk/widgets/welcomewidget.h @@ -124,10 +124,13 @@ private slots: void onPlaylistActivated( QListWidgetItem* item ); void onPlaybackFinished( const Tomahawk::query_ptr& query ); + void checkQueries(); + private: Ui::WelcomeWidget *ui; PlaylistModel* m_tracksModel; + QTimer* m_timer; }; #endif // WELCOMEWIDGET_H diff --git a/src/main.cpp b/src/main.cpp index 916ffbb2e..592258023 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -18,13 +18,16 @@ #include "tomahawk/tomahawkapp.h" +#include "kdsingleapplicationguard/kdsingleapplicationguard.h" +#include + #ifdef Q_WS_MAC #include "tomahawkapp_mac.h" #include static pascal OSErr appleEventHandler( const AppleEvent*, AppleEvent*, long ); #endif -#include + int main( int argc, char *argv[] ) { @@ -36,19 +39,21 @@ main( int argc, char *argv[] ) // used for url handler AEEventHandlerUPP h = AEEventHandlerUPP( appleEventHandler ); AEInstallEventHandler( 'GURL', 'GURL', h, 0, false ); - #endif - try - { - TomahawkApp a( argc, argv ); - return a.exec(); - } - catch( const std::runtime_error& e ) - { - return 0; - } + + TomahawkApp a( argc, argv ); + KDSingleApplicationGuard guard( &a, KDSingleApplicationGuard::AutoKillOtherInstances ); + QObject::connect( &guard, SIGNAL( instanceStarted( KDSingleApplicationGuard::Instance ) ), &a, SLOT( instanceStarted( KDSingleApplicationGuard::Instance ) ) ); + + QString locale = QLocale::system().name(); + + QTranslator translator; + translator.load( QString( ":/lang/tomahawk_" ) + locale ); + a.installTranslator( &translator ); + return a.exec(); } + #ifdef Q_WS_MAC static pascal OSErr appleEventHandler( const AppleEvent* e, AppleEvent*, long ) diff --git a/src/musicscanner.cpp b/src/musicscanner.cpp index a97171416..3cc36305e 100644 --- a/src/musicscanner.cpp +++ b/src/musicscanner.cpp @@ -19,6 +19,7 @@ #include "musicscanner.h" #include "tomahawk/tomahawkapp.h" +#include "tomahawksettings.h" #include "sourcelist.h" #include "database/database.h" #include "database/databasecommand_dirmtimes.h" @@ -31,14 +32,47 @@ using namespace Tomahawk; void DirLister::go() { - scanDir( m_dir, 0 ); + qDebug() << Q_FUNC_INFO; + qDebug() << "Recursive? : " << (m_recursive ? "true" : "false"); + if( !m_recursive ) + { + foreach( QString dir, m_dirs ) + { + if( m_dirmtimes.contains( dir ) ) + { + qDebug() << "Removing " << dir << " from m_dirmtimes because it's specifically requested"; + m_dirmtimes.remove( dir ); + } + QStringList filtered = QStringList( m_dirmtimes.keys() ).filter( dir ); + foreach( QString filteredDir, filtered ) + { + if( !QDir( filteredDir ).exists() ) + { + qDebug() << "Removing " << filteredDir << " from m_dirmtimes because it does not exist"; + m_dirmtimes.remove( filteredDir ); + } + } + } + m_newdirmtimes = m_dirmtimes; + } + + foreach( QString dir, m_dirs ) + scanDir( QDir( dir, 0 ), 0, ( m_recursive ? DirLister::Recursive : DirLister::NonRecursive ) ); emit finished( m_newdirmtimes ); } void -DirLister::scanDir( QDir dir, int depth ) +DirLister::scanDir( QDir dir, int depth, DirLister::Mode mode ) { + qDebug() << "DirLister::scanDir scanning: " << dir.absolutePath() << " with mode " << mode; + + if( !dir.exists() ) + { + qDebug() << "Dir no longer exists, not scanning"; + return; + } + QFileInfoList dirs; const uint mtime = QFileInfo( dir.absolutePath() ).lastModified().toUTC().toTime_t(); m_newdirmtimes.insert( dir.absolutePath(), mtime ); @@ -49,7 +83,7 @@ DirLister::scanDir( QDir dir, int depth ) } else { - if ( m_dirmtimes.contains( dir.absolutePath() ) ) + if( m_dirmtimes.contains( dir.absolutePath() ) || !m_recursive ) Database::instance()->enqueue( QSharedPointer( new DatabaseCommand_DeleteFiles( dir, SourceList::instance()->getLocal() ) ) ); dir.setFilter( QDir::Files | QDir::Readable | QDir::NoDotAndDotDot ); @@ -62,17 +96,21 @@ DirLister::scanDir( QDir dir, int depth ) } dir.setFilter( QDir::Dirs | QDir::Readable | QDir::NoDotAndDotDot ); dirs = dir.entryInfoList(); - + foreach( const QFileInfo& di, dirs ) { - scanDir( di.absoluteFilePath(), depth + 1 ); + qDebug() << "Considering dir " << di.absoluteFilePath(); + qDebug() << "m_dirmtimes contains it? " << (m_dirmtimes.contains( di.absoluteFilePath() ) ? "true" : "false"); + if( mode == DirLister::Recursive || !m_dirmtimes.contains( di.absoluteFilePath() ) ) + scanDir( di.absoluteFilePath(), depth + 1, DirLister::Recursive ); } } -MusicScanner::MusicScanner( const QStringList& dirs, quint32 bs ) +MusicScanner::MusicScanner( const QStringList& dirs, bool recursive, quint32 bs ) : QObject() , m_dirs( dirs ) + , m_recursive( recursive ) , m_batchsize( bs ) , m_dirLister( 0 ) , m_dirListerThreadController( 0 ) @@ -122,12 +160,9 @@ MusicScanner::startScan() m_skippedFiles.clear(); // trigger the scan once we've loaded old mtimes for dirs below our path - //FIXME: MULTIPLECOLLECTIONDIRS - DatabaseCommand_DirMtimes* cmd = new DatabaseCommand_DirMtimes( m_dirs.first() ); + DatabaseCommand_DirMtimes* cmd = new DatabaseCommand_DirMtimes( TomahawkSettings::instance()->scannerPaths() ); connect( cmd, SIGNAL( done( QMap ) ), SLOT( setMtimes( QMap ) ) ); - connect( cmd, SIGNAL( done( QMap ) ), - SLOT( scan() ) ); Database::instance()->enqueue( QSharedPointer(cmd) ); } @@ -136,7 +171,9 @@ MusicScanner::startScan() void MusicScanner::setMtimes( const QMap& m ) { + qDebug() << Q_FUNC_INFO << m.count(); m_dirmtimes = m; + scan(); } @@ -150,8 +187,7 @@ MusicScanner::scan() m_dirListerThreadController = new QThread( this ); - //FIXME: MULTIPLECOLLECTIONDIRS - m_dirLister = new DirLister( QDir( m_dirs.first(), 0 ), m_dirmtimes ); + m_dirLister = new DirLister( m_dirs, m_dirmtimes, m_recursive ); m_dirLister->moveToThread( m_dirListerThreadController ); connect( m_dirLister, SIGNAL( fileToScan( QFileInfo ) ), @@ -185,9 +221,12 @@ MusicScanner::listerFinished( const QMap& newmtimes ) { qDebug() << "Removing stale dir:" << path; Database::instance()->enqueue( QSharedPointer( new DatabaseCommand_DeleteFiles( path, SourceList::instance()->getLocal() ) ) ); + emit removeWatchedDir( path ); } } + emit addWatchedDirs( newmtimes.keys() ); + // save mtimes, then quit thread DatabaseCommand_DirMtimes* cmd = new DatabaseCommand_DirMtimes( newmtimes ); connect( cmd, SIGNAL( finished() ), SLOT( deleteLister() ) ); diff --git a/src/musicscanner.h b/src/musicscanner.h index c4fc5bb5b..77fb62071 100644 --- a/src/musicscanner.h +++ b/src/musicscanner.h @@ -39,8 +39,15 @@ class DirLister : public QObject Q_OBJECT public: - DirLister( QDir d, QMap& mtimes ) - : QObject(), m_dir( d ), m_dirmtimes( mtimes ) + + enum Mode { + NonRecursive, + Recursive, + MTimeOnly + }; + + DirLister( QStringList dirs, QMap& mtimes, bool recursive ) + : QObject(), m_dirs( dirs ), m_dirmtimes( mtimes ), m_recursive( recursive ) { qDebug() << Q_FUNC_INFO; } @@ -56,11 +63,13 @@ signals: private slots: void go(); - void scanDir( QDir dir, int depth ); + void scanDir( QDir dir, int depth, DirLister::Mode mode ); private: - QDir m_dir; + QStringList m_dirs; QMap m_dirmtimes; + bool m_recursive; + QMap m_newdirmtimes; }; @@ -69,13 +78,15 @@ class MusicScanner : public QObject Q_OBJECT public: - MusicScanner( const QStringList& dirs, quint32 bs = 0 ); + MusicScanner( const QStringList& dirs, bool recursive = true, quint32 bs = 0 ); ~MusicScanner(); signals: //void fileScanned( QVariantMap ); void finished(); void batchReady( const QVariantList& ); + void addWatchedDirs( const QStringList & ); + void removeWatchedDir( const QString & ); private: QVariant readFile( const QFileInfo& fi ); @@ -103,6 +114,7 @@ private: QMap m_newdirmtimes; QList m_scannedfiles; + bool m_recursive; quint32 m_batchsize; DirLister* m_dirLister; diff --git a/src/resolvers/qtscriptresolver.cpp b/src/resolvers/qtscriptresolver.cpp index d4c0cc5f5..3d1773fe8 100644 --- a/src/resolvers/qtscriptresolver.cpp +++ b/src/resolvers/qtscriptresolver.cpp @@ -27,15 +27,12 @@ QtScriptResolver::QtScriptResolver( const QString& scriptPath ) : Tomahawk::ExternalResolver( scriptPath ) - , m_engine( new ScriptEngine( this ) ) - , m_thread( new QThread( this ) ) , m_ready( false ) , m_stopped( false ) { qDebug() << Q_FUNC_INFO << scriptPath; - m_thread->start(); - + m_engine = new ScriptEngine( this ); QFile scriptFile( scriptPath ); if ( !scriptFile.open( QIODevice::ReadOnly ) ) { @@ -54,17 +51,10 @@ QtScriptResolver::QtScriptResolver( const QString& scriptPath ) m_timeout = m.value( "timeout", 25 ).toUInt() * 1000; m_preference = m.value( "preference", 0 ).toUInt(); - qDebug() << "QTSCRIPT" << filePath() << "READY," << endl - << "name" << m_name << endl - << "weight" << m_weight << endl - << "timeout" << m_timeout << endl - << "preference" << m_preference; + qDebug() << Q_FUNC_INFO << m_name << m_weight << m_timeout << m_preference; - m_engine->moveToThread( m_thread ); m_ready = true; Tomahawk::Pipeline::instance()->addResolver( this ); - - connect( this, SIGNAL( destroyed( QObject* ) ), m_thread, SLOT( deleteLater() ) ); } @@ -78,23 +68,23 @@ QtScriptResolver::~QtScriptResolver() void QtScriptResolver::resolve( const Tomahawk::query_ptr& query ) { - QMetaObject::invokeMethod( m_engine, "resolve", Qt::QueuedConnection, Q_ARG( Tomahawk::query_ptr, query ) ); -} + if ( QThread::currentThread() != thread() ) + { + qDebug() << "Reinvoking in correct thread:" << Q_FUNC_INFO; + QMetaObject::invokeMethod( this, "resolve", Qt::QueuedConnection, Q_ARG(Tomahawk::query_ptr, query) ); + return; + } - -void -ScriptEngine::resolve( const Tomahawk::query_ptr& query ) -{ qDebug() << Q_FUNC_INFO << query->toString(); QString eval = QString( "resolve( '%1', '%2', '%3', '%4' );" ) - .arg( query->id().replace( "'", "\\'" ) ) - .arg( query->artist().replace( "'", "\\'" ) ) - .arg( query->album().replace( "'", "\\'" ) ) - .arg( query->track().replace( "'", "\\'" ) ); + .arg( query->id().replace( "'", "\\'" ) ) + .arg( query->artist().replace( "'", "\\'" ) ) + .arg( query->album().replace( "'", "\\'" ) ) + .arg( query->track().replace( "'", "\\'" ) ); QList< Tomahawk::result_ptr > results; - QVariantMap m = mainFrame()->evaluateJavaScript( eval ).toMap(); + QVariantMap m = m_engine->mainFrame()->evaluateJavaScript( eval ).toMap(); qDebug() << "JavaScript Result:" << m; const QString qid = query->id(); @@ -113,9 +103,9 @@ ScriptEngine::resolve( const Tomahawk::query_ptr& query ) rp->setBitrate( m.value( "bitrate" ).toUInt() ); rp->setUrl( m.value( "url" ).toString() ); rp->setSize( m.value( "size" ).toUInt() ); - rp->setScore( m.value( "score" ).toFloat() * ( (float)m_parent->weight() / 100.0 ) ); + rp->setScore( m.value( "score" ).toFloat() * ( (float)weight() / 100.0 ) ); rp->setRID( uuid() ); - rp->setFriendlySource( m_parent->name() ); + rp->setFriendlySource( name() ); if ( m.contains( "year" ) ) { diff --git a/src/resolvers/qtscriptresolver.h b/src/resolvers/qtscriptresolver.h index 05b55cc71..a6850ac96 100644 --- a/src/resolvers/qtscriptresolver.h +++ b/src/resolvers/qtscriptresolver.h @@ -26,6 +26,7 @@ #include #include #include +#include #include #include @@ -37,16 +38,15 @@ Q_OBJECT public: explicit ScriptEngine( QtScriptResolver* parent ) - : QWebPage( (QObject*)parent ) + : QWebPage( (QObject*) parent ) , m_parent( parent ) - {} + { + } public slots: - void resolve( const Tomahawk::query_ptr& query ); - bool shouldInterruptJavaScript() { - return false; + return true; } protected: @@ -57,6 +57,7 @@ private: QtScriptResolver* m_parent; }; + class QtScriptResolver : public Tomahawk::ExternalResolver { Q_OBJECT @@ -76,12 +77,9 @@ public slots: signals: void finished(); - -private slots: private: ScriptEngine* m_engine; - QThread* m_thread; QString m_name; unsigned int m_weight, m_preference, m_timeout; diff --git a/src/scanmanager.cpp b/src/scanmanager.cpp index 017dc8171..0073c73b9 100644 --- a/src/scanmanager.cpp +++ b/src/scanmanager.cpp @@ -21,11 +21,16 @@ #include #include #include +#include +#include #include "musicscanner.h" #include "tomahawksettings.h" #include "tomahawkutils.h" +#include "database/database.h" +#include "database/databasecommand_dirmtimes.h" + ScanManager* ScanManager::s_instance = 0; @@ -40,13 +45,33 @@ ScanManager::ScanManager( QObject* parent ) : QObject( parent ) , m_scanner( 0 ) , m_musicScannerThreadController( 0 ) + , m_currScannerPaths() + , m_dirWatcher( 0 ) + , m_queuedScanTimer( 0 ) + , m_deferredScanTimer( 0 ) + , m_queuedChangedDirs() + , m_deferredDirs() { s_instance = this; - connect( TomahawkSettings::instance(), SIGNAL( changed() ), SLOT( onSettingsChanged() ) ); + m_queuedScanTimer = new QTimer( this ); + m_queuedScanTimer->setSingleShot( true ); + m_deferredScanTimer = new QTimer( this ); + m_deferredScanTimer->setSingleShot( false ); + m_deferredScanTimer->setInterval( 1000 ); + m_dirWatcher = new QFileSystemWatcher( this ); - if ( TomahawkSettings::instance()->hasScannerPath() ) - m_currScannerPath = TomahawkSettings::instance()->scannerPath(); + connect( TomahawkSettings::instance(), SIGNAL( changed() ), SLOT( onSettingsChanged() ) ); + connect( m_queuedScanTimer, SIGNAL( timeout() ), SLOT( queuedScanTimeout() ) ); + connect( m_deferredScanTimer, SIGNAL( timeout() ), SLOT( deferredScanTimeout() ) ); + connect( m_dirWatcher, SIGNAL( directoryChanged( const QString & ) ), SLOT( handleChangedDir( const QString & ) ) ); + + if ( TomahawkSettings::instance()->hasScannerPaths() ) + m_currScannerPaths = TomahawkSettings::instance()->scannerPaths(); + + qDebug() << "loading initial directories to watch"; + QTimer::singleShot( 1000, this, SLOT( startupWatchPaths() ) ); + m_deferredScanTimer->start(); } @@ -79,31 +104,150 @@ ScanManager::~ScanManager() void ScanManager::onSettingsChanged() { - if ( TomahawkSettings::instance()->hasScannerPath() && - m_currScannerPath != TomahawkSettings::instance()->scannerPath() ) + if ( TomahawkSettings::instance()->hasScannerPaths() && + m_currScannerPaths != TomahawkSettings::instance()->scannerPaths() ) { - m_currScannerPath = TomahawkSettings::instance()->scannerPath(); - runManualScan( m_currScannerPath ); + m_currScannerPaths = TomahawkSettings::instance()->scannerPaths(); + m_dirWatcher->removePaths( m_dirWatcher->directories() ); + m_dirWatcher->addPaths( m_currScannerPaths ); + runManualScan( m_currScannerPaths ); } + + if( TomahawkSettings::instance()->watchForChanges() && + !m_queuedChangedDirs.isEmpty() ) + runManualScan( m_queuedChangedDirs, false ); } void -ScanManager::runManualScan( const QStringList& path ) +ScanManager::startupWatchPaths() +{ + qDebug() << Q_FUNC_INFO; + + if( !Database::instance() || ( Database::instance() && !Database::instance()->isReady() ) ) + { + QTimer::singleShot( 1000, this, SLOT( startupWatchPaths() ) ); + return; + } + + DatabaseCommand_DirMtimes* cmd = new DatabaseCommand_DirMtimes( m_currScannerPaths ); + connect( cmd, SIGNAL( done( QMap< QString, unsigned int > ) ), + SLOT( setInitialPaths( QMap< QString, unsigned int > ) ) ); + Database::instance()->enqueue( QSharedPointer< DatabaseCommand >( cmd ) ); +} + + +void +ScanManager::setInitialPaths( QMap< QString, unsigned int > pathMap ) +{ + qDebug() << Q_FUNC_INFO; + foreach( QString path, pathMap.keys() ) + { + qDebug() << "Adding " << path << " to watcher"; + m_dirWatcher->addPath( path ); + } + if( TomahawkSettings::instance()->hasScannerPaths() ) + runManualScan( TomahawkSettings::instance()->scannerPaths() ); +} + + +void +ScanManager::runManualScan( const QStringList& paths, bool recursive ) { qDebug() << Q_FUNC_INFO; if ( !m_musicScannerThreadController && !m_scanner ) //still running if these are not zero { m_musicScannerThreadController = new QThread( this ); - m_scanner = new MusicScanner( path ); + QStringList allPaths = paths; + foreach( QString path, m_deferredDirs[recursive] ) + { + if( !allPaths.contains( path ) ) + allPaths << path; + } + m_scanner = new MusicScanner( paths, recursive ); m_scanner->moveToThread( m_musicScannerThreadController ); connect( m_scanner, SIGNAL( finished() ), SLOT( scannerFinished() ) ); + connect( m_scanner, SIGNAL( addWatchedDirs( const QStringList & ) ), SLOT( addWatchedDirs( const QStringList & ) ) ); + connect( m_scanner, SIGNAL( removeWatchedDir( const QString & ) ), SLOT( removeWatchedDir( const QString & ) ) ); m_musicScannerThreadController->start( QThread::IdlePriority ); QMetaObject::invokeMethod( m_scanner, "startScan" ); + m_deferredDirs[recursive].clear(); } else - qDebug() << "Could not run manual scan, old scan still running"; + { + qDebug() << "Could not run manual scan, old scan still running; deferring paths"; + foreach( QString path, paths ) + { + if( !m_deferredDirs[recursive].contains( path ) ) + { + qDebug() << "Deferring path " << path; + m_deferredDirs[recursive] << path; + } + } + } +} + + +void +ScanManager::addWatchedDirs( const QStringList& paths ) +{ + qDebug() << Q_FUNC_INFO; + QStringList currentWatchedPaths = m_dirWatcher->directories(); + foreach( QString path, paths ) + { + if( !currentWatchedPaths.contains( path ) ) + { + qDebug() << "adding " << path << " to watched dirs"; + m_dirWatcher->addPath( path ); + } + } +} + + +void +ScanManager::removeWatchedDir( const QString& path ) +{ + qDebug() << Q_FUNC_INFO; + qDebug() << "removing " << path << " from watched dirs"; + m_dirWatcher->removePath( path ); +} + + +void +ScanManager::handleChangedDir( const QString& path ) +{ + qDebug() << Q_FUNC_INFO; + qDebug() << "Dir changed: " << path; + if( !m_queuedChangedDirs.contains( path ) ) + m_queuedChangedDirs << path; + if( TomahawkSettings::instance()->watchForChanges() ) + m_queuedScanTimer->start( 10000 ); +} + + +void +ScanManager::queuedScanTimeout() +{ + qDebug() << Q_FUNC_INFO; + runManualScan( m_queuedChangedDirs, false ); + m_queuedChangedDirs.clear(); +} + + +void +ScanManager::deferredScanTimeout() +{ + if( !m_deferredDirs[true].isEmpty() ) + { + qDebug() << "Running scan for deferred recursive paths"; + runManualScan( m_deferredDirs[true], true ); + } + else if( !m_deferredDirs[false].isEmpty() ) + { + qDebug() << "Running scan for deferred non-recursive paths"; + runManualScan( m_deferredDirs[false], false ); + } } diff --git a/src/scanmanager.h b/src/scanmanager.h index a20d9990f..fdec0cf15 100644 --- a/src/scanmanager.h +++ b/src/scanmanager.h @@ -19,6 +19,8 @@ #ifndef SCANMANAGER_H #define SCANMANAGER_H +#include +#include #include #include @@ -26,6 +28,8 @@ class MusicScanner; class QThread; +class QFileSystemWatcher; +class QTimer; class ScanManager : public QObject { @@ -36,16 +40,25 @@ public: explicit ScanManager( QObject* parent = 0 ); virtual ~ScanManager(); - - void runManualScan( const QStringList& path ); signals: void finished(); +public slots: + void runManualScan( const QStringList& paths, bool recursive = true ); + void handleChangedDir( const QString& path ); + void addWatchedDirs( const QStringList& paths ); + void removeWatchedDir( const QString& path ); + void setInitialPaths( QMap< QString, unsigned int > pathMap ); + private slots: void scannerQuit(); void scannerFinished(); void scannerDestroyed( QObject* scanner ); + + void startupWatchPaths(); + void queuedScanTimeout(); + void deferredScanTimeout(); void onSettingsChanged(); @@ -54,7 +67,13 @@ private: MusicScanner* m_scanner; QThread* m_musicScannerThreadController; - QStringList m_currScannerPath; + QStringList m_currScannerPaths; + QFileSystemWatcher* m_dirWatcher; + + QTimer* m_queuedScanTimer; + QTimer* m_deferredScanTimer; + QStringList m_queuedChangedDirs; + QHash< bool, QStringList > m_deferredDirs; }; #endif diff --git a/src/scrobbler.cpp b/src/scrobbler.cpp index 06ebe33f5..ef599db76 100644 --- a/src/scrobbler.cpp +++ b/src/scrobbler.cpp @@ -39,8 +39,8 @@ Scrobbler::Scrobbler( QObject* parent ) SLOT( engineTick( unsigned int ) ), Qt::QueuedConnection ); connect( TomahawkApp::instance()->infoSystem(), - SIGNAL( info( QString, Tomahawk::InfoSystem::InfoType, QVariant, QVariant, Tomahawk::InfoSystem::InfoCustomDataHash ) ), - SLOT( infoSystemInfo( QString, Tomahawk::InfoSystem::InfoType, QVariant, QVariant, Tomahawk::InfoSystem::InfoCustomDataHash ) ) ); + SIGNAL( info( QString, Tomahawk::InfoSystem::InfoType, QVariant, QVariant, Tomahawk::InfoSystem::InfoCustomData ) ), + SLOT( infoSystemInfo( QString, Tomahawk::InfoSystem::InfoType, QVariant, QVariant, Tomahawk::InfoSystem::InfoCustomData ) ) ); connect( TomahawkApp::instance()->infoSystem(), SIGNAL( finished( QString ) ), SLOT( infoSystemFinished( QString ) ) ); } @@ -63,7 +63,7 @@ Scrobbler::trackStarted( const Tomahawk::result_ptr& track ) scrobble(); } - Tomahawk::InfoSystem::InfoCustomDataHash trackInfo; + Tomahawk::InfoSystem::InfoCustomData trackInfo; trackInfo["title"] = QVariant::fromValue< QString >( track->track() ); trackInfo["artist"] = QVariant::fromValue< QString >( track->artist()->name() ); @@ -71,7 +71,7 @@ Scrobbler::trackStarted( const Tomahawk::result_ptr& track ) trackInfo["duration"] = QVariant::fromValue< uint >( track->duration() ); TomahawkApp::instance()->infoSystem()->getInfo( s_infoIdentifier, Tomahawk::InfoSystem::InfoMiscSubmitNowPlaying, - QVariant::fromValue< Tomahawk::InfoSystem::InfoCustomDataHash >( trackInfo ), Tomahawk::InfoSystem::InfoCustomDataHash() ); + QVariant::fromValue< Tomahawk::InfoSystem::InfoCustomData >( trackInfo ), Tomahawk::InfoSystem::InfoCustomData() ); m_scrobblePoint = ScrobblePoint( track->duration() / 2 ); } @@ -119,11 +119,11 @@ Scrobbler::scrobble() TomahawkApp::instance()->infoSystem()->getInfo( s_infoIdentifier, Tomahawk::InfoSystem::InfoMiscSubmitScrobble, - QVariant(), Tomahawk::InfoSystem::InfoCustomDataHash() ); + QVariant(), Tomahawk::InfoSystem::InfoCustomData() ); } void -Scrobbler::infoSystemInfo( QString caller, Tomahawk::InfoSystem::InfoType type, QVariant input, QVariant output, Tomahawk::InfoSystem::InfoCustomDataHash customData ) +Scrobbler::infoSystemInfo( QString caller, Tomahawk::InfoSystem::InfoType type, QVariant input, QVariant output, Tomahawk::InfoSystem::InfoCustomData customData ) { if ( caller == s_infoIdentifier ) { diff --git a/src/scrobbler.h b/src/scrobbler.h index 4ed020fba..11741beec 100644 --- a/src/scrobbler.h +++ b/src/scrobbler.h @@ -45,7 +45,7 @@ public slots: void trackStopped(); void engineTick( unsigned int secondsElapsed ); - void infoSystemInfo( QString caller, Tomahawk::InfoSystem::InfoType type, QVariant input, QVariant output, Tomahawk::InfoSystem::InfoCustomDataHash customData ); + void infoSystemInfo( QString caller, Tomahawk::InfoSystem::InfoType type, QVariant input, QVariant output, Tomahawk::InfoSystem::InfoCustomData customData ); void infoSystemFinished( QString target ); private: diff --git a/src/settingsdialog.cpp b/src/settingsdialog.cpp index 58fbd1f96..72bc4edbe 100644 --- a/src/settingsdialog.cpp +++ b/src/settingsdialog.cpp @@ -87,7 +87,9 @@ SettingsDialog::SettingsDialog( QWidget *parent ) // MUSIC SCANNER //FIXME: MULTIPLECOLLECTIONDIRS - ui->lineEditMusicPath->setText( s->scannerPath().first() ); + if ( s->scannerPaths().count() ) + ui->lineEditMusicPath->setText( s->scannerPaths().first() ); + ui->checkBoxWatchForChanges->setChecked( s->watchForChanges() ); // LAST FM ui->checkBoxEnableLastfm->setChecked( s->scrobblingEnabled() ); @@ -134,7 +136,8 @@ SettingsDialog::~SettingsDialog() s->setExternalHostname( ui->staticHostName->text() ); s->setExternalPort( ui->staticPort->value() ); - s->setScannerPath( QStringList( ui->lineEditMusicPath->text() ) ); + s->setScannerPaths( QStringList( ui->lineEditMusicPath->text() ) ); + s->setWatchForChanges( ui->checkBoxWatchForChanges->isChecked() ); s->setScrobblingEnabled( ui->checkBoxEnableLastfm->isChecked() ); s->setLastFmUsername( ui->lineEditLastfmUsername->text() ); diff --git a/src/settingsdialog.ui b/src/settingsdialog.ui index c3f302da8..0558568b6 100644 --- a/src/settingsdialog.ui +++ b/src/settingsdialog.ui @@ -66,7 +66,7 @@ - e.g. user@example.com + e.g. user@example.com @@ -454,6 +454,19 @@ + + + + + 0 + 0 + + + + Watch for changes + + + diff --git a/src/sip/SipHandler.cpp b/src/sip/SipHandler.cpp index b07eb796c..59aa8ea12 100644 --- a/src/sip/SipHandler.cpp +++ b/src/sip/SipHandler.cpp @@ -182,9 +182,9 @@ SipHandler::connectPlugins( bool startup, const QString &pluginName ) if ( !TomahawkSettings::instance()->acceptedLegalWarning() ) { int result = QMessageBox::question( - TomahawkApp::instance()->mainWindow(), "Legal Warning", - "By pressing OK below, you agree that your use of Tomahawk will be in accordance with any applicable laws, including copyright and intellectual property laws, in effect in your country of residence, and indemnify the Tomahawk developers and project from liability should you choose to break those laws.\n\nFor more information, please see http://gettomahawk.com/legal", - "I Do Not Agree", "I Agree" + TomahawkApp::instance()->mainWindow(), tr( "Legal Warning" ), + tr( "By pressing OK below, you agree that your use of Tomahawk will be in accordance with any applicable laws, including copyright and intellectual property laws, in effect in your country of residence, and indemnify the Tomahawk developers and project from liability should you choose to break those laws.\n\nFor more information, please see http://gettomahawk.com/legal" ), + tr( "I Do Not Agree" ), tr( "I Agree" ) ); if ( result != 1 ) return; diff --git a/src/sip/jabber/jabber.cpp b/src/sip/jabber/jabber.cpp index ce62909c4..dab22ac5b 100644 --- a/src/sip/jabber/jabber.cpp +++ b/src/sip/jabber/jabber.cpp @@ -120,7 +120,7 @@ JabberPlugin::onConnected() { if ( !m_menu ) { - m_menu = new QMenu( QString( "Jabber (" ).append( accountName() ).append( ")" ) ); + m_menu = new QMenu( tr( "Jabber (%1)" ).arg( accountName() ) ); m_addFriendAction = m_menu->addAction( tr( "Add Friend..." ) ); connect( m_addFriendAction, SIGNAL( triggered() ), SLOT( showAddFriendDialog() ) ) ; diff --git a/src/sip/jabber/jabber_p.cpp b/src/sip/jabber/jabber_p.cpp index c4f48a658..267322f6b 100644 --- a/src/sip/jabber/jabber_p.cpp +++ b/src/sip/jabber/jabber_p.cpp @@ -54,7 +54,7 @@ Jabber_p::Jabber_p( const QString& jid, const QString& password, const QString& if( m_jid.resource().find( "tomahawk" ) == std::string::npos ) { - qDebug() << "!!! Setting your resource to 'tomahawk' prior to logging in to jabber"; +// qDebug() << "!!! Setting your resource to 'tomahawk' prior to logging in to jabber"; m_jid.setResource( QString( "tomahawk%1" ).arg( qrand() ).toStdString() ); } @@ -230,8 +230,8 @@ Jabber_p::sendMsg( const QString& to, const QString& msg ) { if ( QThread::currentThread() != thread() ) { - qDebug() << Q_FUNC_INFO << "invoking in correct thread, not" - << QThread::currentThread(); +// qDebug() << Q_FUNC_INFO << "invoking in correct thread, not" +// << QThread::currentThread(); QMetaObject::invokeMethod( this, "sendMsg", Qt::QueuedConnection, @@ -297,7 +297,7 @@ Jabber_p::addContact( const QString& jid, const QString& msg ) void Jabber_p::onConnect() { - qDebug() << "Connected to the XMPP server"; + qDebug() << "Connected to the XMPP server" << m_jid.full().c_str(); // update jid resource, servers like gtalk use resource binding and may // have changed our requested /resource if ( m_client->resource() != m_jid.resource() ) @@ -307,7 +307,6 @@ Jabber_p::onConnect() emit jidChanged( jidstr ); } - qDebug() << "Connected as:" << m_jid.full().c_str(); emit connected(); } @@ -536,7 +535,7 @@ gloox::Roster::const_iterator it = roster.begin(); for ( ; it != roster.end(); ++it ) { if ( (*it).second->subscription() != gloox::S10nBoth ) continue; - qDebug() << (*it).second->jid().c_str() << (*it).second->name().c_str(); +// qDebug() << (*it).second->jid().c_str() << (*it).second->name().c_str(); //printf("JID: %s\n", (*it).second->jid().c_str()); } @@ -559,8 +558,6 @@ Jabber_p::handlePresence( const gloox::Presence& presence ) gloox::JID jid = presence.from(); QString fulljid( jid.full().c_str() ); - qDebug() << "* handleRosterPresence" << fulljid << presence.subtype(); - if( jid == m_jid ) return; @@ -582,6 +579,7 @@ Jabber_p::handlePresence( const gloox::Presence& presence ) return; } + qDebug() << "* handleRosterPresence" << fulljid << presence.subtype(); //qDebug() << "handling presence for resource of" << res; //qDebug() << Q_FUNC_INFO << "jid:" << QString::fromStdString(item.jid()) diff --git a/src/sip/twitter/twitter.cpp b/src/sip/twitter/twitter.cpp index 655dd832b..7947042ae 100644 --- a/src/sip/twitter/twitter.cpp +++ b/src/sip/twitter/twitter.cpp @@ -87,7 +87,7 @@ TwitterPlugin::name() const QString TwitterPlugin::friendlyName() { - return QString("Twitter"); + return tr("Twitter"); } const QString @@ -178,7 +178,6 @@ TwitterPlugin::disconnectPlugin() delete m_twitterAuth.data(); m_cachedPeers.empty(); - m_attemptedConnects.empty(); m_isOnline = false; } @@ -571,7 +570,6 @@ TwitterPlugin::registerOffer( const QString &screenName, const QHash< QString, Q { m_cachedPeers[screenName] = QVariant::fromValue< QHash< QString, QVariant > >( _peerData ); TomahawkSettings::instance()->setTwitterCachedPeers( m_cachedPeers ); - m_attemptedConnects[screenName] = false; } if ( m_isOnline && _peerData.contains( "host" ) && _peerData.contains( "port" ) && _peerData.contains( "pkey" ) ) @@ -597,14 +595,9 @@ void TwitterPlugin::makeConnection( const QString &screenName, const QHash< QString, QVariant > &peerData ) { qDebug() << Q_FUNC_INFO; - if ( m_attemptedConnects.contains( screenName ) && m_attemptedConnects[screenName] ) - { - qDebug() << "Already attempted to connect to this peer with no change in their status, not trying again for now"; - return; - } if ( !peerData.contains( "host" ) || !peerData.contains( "port" ) || !peerData.contains( "pkey" ) || !peerData.contains( "node" ) ) { - qDebug() << "TwitterPlugin could not find host and/or port and/or pkey for peer " << screenName; + qDebug() << "TwitterPlugin could not find host and/or port and/or pkey and/or node for peer " << screenName; return; } QString friendlyName = QString( '@' + screenName ); @@ -614,7 +607,6 @@ TwitterPlugin::makeConnection( const QString &screenName, const QHash< QString, peerData["pkey"].toString(), friendlyName, peerData["node"].toString() ); - m_attemptedConnects[screenName] = true; } void diff --git a/src/sip/twitter/twitter.h b/src/sip/twitter/twitter.h index 4e8a98a31..c0da00d7f 100644 --- a/src/sip/twitter/twitter.h +++ b/src/sip/twitter/twitter.h @@ -108,7 +108,6 @@ private: qint64 m_cachedMentionsSinceId; qint64 m_cachedDirectMessagesSinceId; QHash< QString, QVariant > m_cachedPeers; - QHash< QString, bool > m_attemptedConnects; QSet m_keyCache; bool m_finishedFriends; bool m_finishedMentions; diff --git a/src/sip/twitter/twitterconfigwidget.cpp b/src/sip/twitter/twitterconfigwidget.cpp index 85660318a..00eb46d87 100644 --- a/src/sip/twitter/twitterconfigwidget.cpp +++ b/src/sip/twitter/twitterconfigwidget.cpp @@ -51,8 +51,8 @@ TwitterConfigWidget::TwitterConfigWidget( SipPlugin* plugin, QWidget *parent ) : TomahawkSettings* s = TomahawkSettings::instance(); if ( s->twitterOAuthToken().isEmpty() || s->twitterOAuthTokenSecret().isEmpty() || s->twitterScreenName().isEmpty() ) { - ui->twitterStatusLabel->setText("Status: No saved credentials"); - ui->twitterAuthenticateButton->setText( "Authenticate" ); + ui->twitterStatusLabel->setText( tr( "Status: No saved credentials" ) ); + ui->twitterAuthenticateButton->setText( tr( "Authenticate" ) ); ui->twitterInstructionsInfoLabel->setVisible( false ); ui->twitterGlobalTweetLabel->setVisible( false ); ui->twitterTweetGotTomahawkButton->setVisible( false ); @@ -63,8 +63,8 @@ TwitterConfigWidget::TwitterConfigWidget( SipPlugin* plugin, QWidget *parent ) : } else { - ui->twitterStatusLabel->setText("Status: Credentials saved for " + s->twitterScreenName() ); - ui->twitterAuthenticateButton->setText( "De-authenticate" ); + ui->twitterStatusLabel->setText( tr( "Status: Credentials saved for %1" ).arg( s->twitterScreenName() ) ); + ui->twitterAuthenticateButton->setText( tr( "De-authenticate" ) ); ui->twitterInstructionsInfoLabel->setVisible( true ); ui->twitterGlobalTweetLabel->setVisible( true ); ui->twitterTweetGotTomahawkButton->setVisible( true ); @@ -84,7 +84,7 @@ TwitterConfigWidget::~TwitterConfigWidget() void TwitterConfigWidget::authDeauthTwitter() { - if ( ui->twitterAuthenticateButton->text() == "Authenticate" ) + if ( ui->twitterAuthenticateButton->text() == tr( "Authenticate" ) ) //FIXME: don't rely on UI strings here! authenticateTwitter(); else deauthenticateTwitter(); @@ -114,7 +114,7 @@ TwitterConfigWidget::authenticateVerifyReply( const QTweetUser &user ) qDebug() << Q_FUNC_INFO; if ( user.id() == 0 ) { - QMessageBox::critical( 0, QString("Tweetin' Error"), QString("The credentials could not be verified.\nYou may wish to try re-authenticating.") ); + QMessageBox::critical( this, tr("Tweetin' Error"), tr("The credentials could not be verified.\nYou may wish to try re-authenticating.") ); emit twitterAuthed( false ); return; } @@ -124,8 +124,8 @@ TwitterConfigWidget::authenticateVerifyReply( const QTweetUser &user ) s->setTwitterCachedFriendsSinceId( 0 ); s->setTwitterCachedMentionsSinceId( 0 ); - ui->twitterStatusLabel->setText("Status: Credentials saved for " + s->twitterScreenName() ); - ui->twitterAuthenticateButton->setText( "De-authenticate" ); + ui->twitterStatusLabel->setText( tr( "Status: Credentials saved for %1" ).arg( s->twitterScreenName() ) ); + ui->twitterAuthenticateButton->setText( tr( "De-authenticate" ) ); ui->twitterInstructionsInfoLabel->setVisible( true ); ui->twitterGlobalTweetLabel->setVisible( true ); ui->twitterTweetGotTomahawkButton->setVisible( true ); @@ -142,7 +142,7 @@ TwitterConfigWidget::authenticateVerifyError( QTweetNetBase::ErrorCode code, con { qDebug() << Q_FUNC_INFO; qDebug() << "Error validating credentials, error code is " << code << ", error message is " << errorMsg; - ui->twitterStatusLabel->setText("Status: Error validating credentials"); + ui->twitterStatusLabel->setText(tr("Status: Error validating credentials")); emit twitterAuthed( false ); return; } @@ -156,8 +156,8 @@ TwitterConfigWidget::deauthenticateTwitter() s->setTwitterOAuthTokenSecret( QString() ); s->setTwitterScreenName( QString() ); - ui->twitterStatusLabel->setText("Status: No saved credentials"); - ui->twitterAuthenticateButton->setText( "Authenticate" ); + ui->twitterStatusLabel->setText(tr("Status: No saved credentials")); + ui->twitterAuthenticateButton->setText( tr( "Authenticate" ) ); ui->twitterInstructionsInfoLabel->setVisible( false ); ui->twitterGlobalTweetLabel->setVisible( false ); ui->twitterTweetGotTomahawkButton->setVisible( false ); @@ -170,7 +170,7 @@ TwitterConfigWidget::deauthenticateTwitter() void TwitterConfigWidget::tweetComboBoxIndexChanged( int index ) { - if( ui->twitterTweetComboBox->currentText() == "Global Tweet" ) + if( ui->twitterTweetComboBox->currentText() == tr( "Global Tweet" ) ) //FIXME: use data! { ui->twitterUserTweetLineEdit->setReadOnly( true ); ui->twitterUserTweetLineEdit->setEnabled( false ); @@ -181,10 +181,10 @@ TwitterConfigWidget::tweetComboBoxIndexChanged( int index ) ui->twitterUserTweetLineEdit->setEnabled( true ); } - if( ui->twitterTweetComboBox->currentText() == "Direct Message" ) - ui->twitterTweetGotTomahawkButton->setText( "Send Message!" ); + if( ui->twitterTweetComboBox->currentText() == tr( "Direct Message" ) ) //FIXME: use data! + ui->twitterTweetGotTomahawkButton->setText( tr( "Send Message!" ) ); else - ui->twitterTweetGotTomahawkButton->setText( "Tweet!" ); + ui->twitterTweetGotTomahawkButton->setText( tr( "Tweet!" ) ); } void @@ -194,7 +194,7 @@ TwitterConfigWidget::startPostGotTomahawkStatus() if ( m_postGTtype != "Global Tweet" && ( ui->twitterUserTweetLineEdit->text().isEmpty() || ui->twitterUserTweetLineEdit->text() == "@" ) ) { - QMessageBox::critical( 0, QString("Tweetin' Error"), QString("You must enter a user name for this type of tweet.") ); + QMessageBox::critical( this, tr("Tweetin' Error"), tr("You must enter a user name for this type of tweet.") ); return; } @@ -202,7 +202,7 @@ TwitterConfigWidget::startPostGotTomahawkStatus() TomahawkSettings* s = TomahawkSettings::instance(); if ( s->twitterOAuthToken().isEmpty() || s->twitterOAuthTokenSecret().isEmpty() || s->twitterScreenName().isEmpty() ) { - QMessageBox::critical( 0, QString("Tweetin' Error"), QString("Your saved credentials could not be loaded.\nYou may wish to try re-authenticating.") ); + QMessageBox::critical( this, tr("Tweetin' Error"), tr("Your saved credentials could not be loaded.\nYou may wish to try re-authenticating.") ); emit twitterAuthed( false ); return; } @@ -220,7 +220,7 @@ TwitterConfigWidget::postGotTomahawkStatusAuthVerifyReply( const QTweetUser &use { if ( user.id() == 0 ) { - QMessageBox::critical( 0, QString("Tweetin' Error"), QString("Your saved credentials could not be verified.\nYou may wish to try re-authenticating.") ); + QMessageBox::critical( this, tr("Tweetin' Error"), tr("Your saved credentials could not be verified.\nYou may wish to try re-authenticating.") ); emit twitterAuthed( false ); return; } @@ -264,18 +264,18 @@ void TwitterConfigWidget::postGotTomahawkStatusUpdateReply( const QTweetStatus& status ) { if ( status.id() == 0 ) - QMessageBox::critical( 0, QString("Tweetin' Error"), QString("There was an error posting your status -- sorry!") ); + QMessageBox::critical( this, tr("Tweetin' Error"), tr("There was an error posting your status -- sorry!") ); else - QMessageBox::information( 0, QString("Tweeted!"), QString("Your tweet has been posted!") ); + QMessageBox::information( this, tr("Tweeted!"), tr("Your tweet has been posted!") ); } void TwitterConfigWidget::postGotTomahawkDirectMessageReply( const QTweetDMStatus& status ) { if ( status.id() == 0 ) - QMessageBox::critical( 0, QString("Tweetin' Error"), QString("There was an error posting your direct message -- sorry!") ); + QMessageBox::critical( this, tr("Tweetin' Error"), tr("There was an error posting your direct message -- sorry!") ); else - QMessageBox::information( 0, QString("Tweeted!"), QString("Your message has been posted!") ); + QMessageBox::information( this, tr("Tweeted!"), tr("Your message has been posted!") ); } void @@ -283,5 +283,5 @@ TwitterConfigWidget::postGotTomahawkStatusUpdateError( QTweetNetBase::ErrorCode { qDebug() << Q_FUNC_INFO; qDebug() << "Error posting Got Tomahawk message, error code is " << code << ", error message is " << errorMsg; - QMessageBox::critical( 0, QString("Tweetin' Error"), QString("There was an error posting your status -- sorry!") ); + QMessageBox::critical( this, tr("Tweetin' Error"), tr("There was an error posting your status -- sorry!") ); } diff --git a/src/tomahawkapp.cpp b/src/tomahawkapp.cpp index cc76a919d..b00b6472a 100644 --- a/src/tomahawkapp.cpp +++ b/src/tomahawkapp.cpp @@ -81,6 +81,9 @@ ofstream logfile; void TomahawkLogHandler( QtMsgType type, const char *msg ) { + static QMutex s_mutex; + + QMutexLocker locker( &s_mutex ); switch( type ) { case QtDebugMsg: @@ -152,14 +155,6 @@ TomahawkApp::TomahawkApp( int& argc, char *argv[] ) { qsrand( QTime( 0, 0, 0 ).secsTo( QTime::currentTime() ) ); - // send the first arg to an already running instance, but don't open twice no matter what - if( ( argc > 1 && sendMessage( argv[ 1 ] ) ) || sendMessage( "" ) ) { - qDebug() << "Sent message, already exists"; - throw runtime_error( "Already Running" ); - } - - connect( this, SIGNAL( messageReceived( QString ) ), this, SLOT( messageReceived( QString ) ) ); - #ifdef TOMAHAWK_HEADLESS m_headless = true; #else @@ -285,7 +280,7 @@ TomahawkApp::TomahawkApp( int& argc, char *argv[] ) } #ifndef TOMAHAWK_HEADLESS - if ( !TomahawkSettings::instance()->hasScannerPath() ) + if ( !TomahawkSettings::instance()->hasScannerPaths() ) { m_mainwindow->showSettingsDialog(); } @@ -535,13 +530,15 @@ TomahawkApp::loadUrl( const QString& url ) void -TomahawkApp::messageReceived( const QString& msg ) +TomahawkApp::instanceStarted( KDSingleApplicationGuard::Instance instance ) { - qDebug() << "MESSAGE RECEIVED" << msg; - if( msg.isEmpty() ) { + qDebug() << "INSTANCE STARTED!" << instance.pid << instance.arguments; + + if( instance.arguments.size() < 2 ) + { return; } - loadUrl( msg ); + loadUrl( instance.arguments.at( 1 ) ); } diff --git a/src/tomahawkwindow.cpp b/src/tomahawkwindow.cpp index 868f1609b..15b355d28 100644 --- a/src/tomahawkwindow.cpp +++ b/src/tomahawkwindow.cpp @@ -147,7 +147,7 @@ TomahawkWindow::TomahawkWindow( QWidget* parent ) toolbar->installEventFilter( new WidgetDragFilter( toolbar ) ); #if defined( Q_OS_DARWIN ) && defined( HAVE_SPARKLE ) - QAction* checkForUpdates = ui->menu_Help->addAction( tr( "Check for updates...") ); + QAction* checkForUpdates = ui->menu_Help->addAction( tr( "Check For Updates...") ); checkForUpdates->setMenuRole( QAction::ApplicationSpecificRole ); connect(checkForUpdates, SIGNAL( triggered( bool ) ), SLOT( checkForUpdates() ) ); #elif defined( WIN32 ) @@ -163,7 +163,7 @@ TomahawkWindow::TomahawkWindow( QWidget* parent ) updater->SetVersion( VERSION ); ui->menu_Help->addSeparator(); - QAction* checkForUpdates = ui->menu_Help->addAction( tr( "Check for updates...") ); + QAction* checkForUpdates = ui->menu_Help->addAction( tr( "Check For Updates...") ); connect( checkForUpdates, SIGNAL( triggered() ), updater, SLOT( CheckNow() ) ); #endif @@ -319,8 +319,8 @@ TomahawkWindow::showSettingsDialog() void TomahawkWindow::updateCollectionManually() { - if ( TomahawkSettings::instance()->hasScannerPath() ) - ScanManager::instance()->runManualScan( TomahawkSettings::instance()->scannerPath() ); + if ( TomahawkSettings::instance()->hasScannerPaths() ) + ScanManager::instance()->runManualScan( TomahawkSettings::instance()->scannerPaths() ); } @@ -380,7 +380,7 @@ void TomahawkWindow::loadSpiff() { bool ok; - QString urlstr = QInputDialog::getText( this, "Load XSPF", "Path:", QLineEdit::Normal, "http://ws.audioscrobbler.com/1.0/tag/metal/toptracks.xspf", &ok ); + QString urlstr = QInputDialog::getText( this, tr( "Load XSPF" ), tr( "Path:" ), QLineEdit::Normal, "http://ws.audioscrobbler.com/1.0/tag/metal/toptracks.xspf", &ok ); if ( !ok || urlstr.isEmpty() ) return; @@ -395,7 +395,7 @@ void TomahawkWindow::createAutomaticPlaylist() { bool ok; - QString name = QInputDialog::getText( this, "Create New Automatic Playlist", "Name:", QLineEdit::Normal, "New Automatic Playlist", &ok ); + QString name = QInputDialog::getText( this, tr( "Create New Automatic Playlist" ), tr( "Name:" ), QLineEdit::Normal, tr( "New Automatic Playlist" ), &ok ); if ( !ok || name.isEmpty() ) return; @@ -414,7 +414,7 @@ void TomahawkWindow::createStation() { bool ok; - QString name = QInputDialog::getText( this, "Create New Station", "Name:", QLineEdit::Normal, "New Station", &ok ); + QString name = QInputDialog::getText( this, tr( "Create New Station" ), tr( "Name:" ), QLineEdit::Normal, tr( "New Station" ), &ok ); if ( !ok || name.isEmpty() ) return; @@ -493,8 +493,8 @@ TomahawkWindow::setWindowTitle( const QString& title ) QMainWindow::setWindowTitle( title ); else { - QString s = m_currentTrack->track() + " " + tr( "by" ) + " " + m_currentTrack->artist()->name(); - QMainWindow::setWindowTitle( s + " - " + title ); + QString s = tr( "%1 by %2", "track, artist name" ).arg( m_currentTrack->track(), m_currentTrack->artist()->name() ); + QMainWindow::setWindowTitle( tr( "%1 - %2", "current track, some window title" ).arg( s, title ) ); } } @@ -502,7 +502,7 @@ TomahawkWindow::setWindowTitle( const QString& title ) void TomahawkWindow::showAboutTomahawk() { - QMessageBox::about( this, "About Tomahawk", + QMessageBox::about( this, tr( "About Tomahawk" ), tr( "

Tomahawk %1

Copyright 2010, 2011
Christian Muehlhaeuser <muesli@tomahawk-player.org>

" "Thanks to: Leo Franchi, Jeff Mitchell, Dominik Schmidt, Jason Herskowitz, Alejandro Wainzinger, Harald Sitter and Steve Robertson" ) .arg( qApp->applicationVersion() ) ); diff --git a/src/xmppbot/xmppbot.cpp b/src/xmppbot/xmppbot.cpp index e6be8a57c..464e09ede 100644 --- a/src/xmppbot/xmppbot.cpp +++ b/src/xmppbot/xmppbot.cpp @@ -67,8 +67,8 @@ XMPPBot::XMPPBot(QObject *parent) SLOT(newTrackSlot(const Tomahawk::result_ptr &))); connect(TomahawkApp::instance()->infoSystem(), - SIGNAL(info(QString, Tomahawk::InfoSystem::InfoType, QVariant, QVariant, Tomahawk::InfoSystem::InfoCustomDataHash)), - SLOT(infoReturnedSlot(QString, Tomahawk::InfoSystem::InfoType, QVariant, QVariant, Tomahawk::InfoSystem::InfoCustomDataHash))); + SIGNAL(info(QString, Tomahawk::InfoSystem::InfoType, QVariant, QVariant, Tomahawk::InfoSystem::InfoCustomData)), + SLOT(infoReturnedSlot(QString, Tomahawk::InfoSystem::InfoType, QVariant, QVariant, Tomahawk::InfoSystem::InfoCustomData))); connect(TomahawkApp::instance()->infoSystem(), SIGNAL(finished(QString)), SLOT(infoFinishedSlot(QString))); @@ -215,10 +215,10 @@ void XMPPBot::handleMessage(const Message& msg, MessageSession* session) infoMap[InfoArtistFamiliarity] = m_currTrack.data()->artist()->name(); if (token == "lyrics") { - InfoCustomDataHash myhash; + InfoCustomData myhash; myhash["trackName"] = QVariant::fromValue(m_currTrack.data()->track()); myhash["artistName"] = QVariant::fromValue(m_currTrack.data()->artist()->name()); - infoMap[InfoTrackLyrics] = QVariant::fromValue(myhash); + infoMap[InfoTrackLyrics] = QVariant::fromValue(myhash); } } @@ -235,12 +235,12 @@ void XMPPBot::handleMessage(const Message& msg, MessageSession* session) QString waitMsg("Please wait..."); Message retMsg(Message::Chat, JID(originatingJid.toStdString()), waitMsg.toStdString()); m_client.data()->send(retMsg); - Tomahawk::InfoSystem::InfoCustomDataHash hash; + Tomahawk::InfoSystem::InfoCustomData hash; hash["XMPPBotSendToJID"] = originatingJid; TomahawkApp::instance()->infoSystem()->getInfo(s_infoIdentifier, infoMap, hash); } -void XMPPBot::infoReturnedSlot(QString caller, Tomahawk::InfoSystem::InfoType type, QVariant input, QVariant output, Tomahawk::InfoSystem::InfoCustomDataHash customData) +void XMPPBot::infoReturnedSlot(QString caller, Tomahawk::InfoSystem::InfoType type, QVariant input, QVariant output, Tomahawk::InfoSystem::InfoCustomData customData) { qDebug() << Q_FUNC_INFO; @@ -306,9 +306,9 @@ void XMPPBot::infoReturnedSlot(QString caller, Tomahawk::InfoSystem::InfoType ty } InfoGenericMap tmap = output.value(); QString artist = input.toString(); - m_currReturnMessage += QString("\nTerms for %1:\n").arg(artist); + m_currReturnMessage += tr("\nTerms for %1:\n").arg(artist); if (tmap.isEmpty()) - m_currReturnMessage += QString("No terms found, sorry."); + m_currReturnMessage += tr("No terms found, sorry."); else { bool first = true; @@ -341,7 +341,7 @@ void XMPPBot::infoReturnedSlot(QString caller, Tomahawk::InfoSystem::InfoType ty QString artist = input.toString(); qreal retVal = output.toReal(); QString retValString = (retVal == 0.0 ? "(none)" : QString::number(retVal)); - m_currReturnMessage += QString("\nHotttness for %1: %2\n").arg(artist).arg(retValString); + m_currReturnMessage += tr("\nHotttness for %1: %2\n").arg(artist, retValString); break; } case InfoArtistFamiliarity: @@ -357,25 +357,25 @@ void XMPPBot::infoReturnedSlot(QString caller, Tomahawk::InfoSystem::InfoType ty QString artist = input.toString(); qreal retVal = output.toReal(); QString retValString = (retVal == 0.0 ? "(none)" : QString::number(retVal)); - m_currReturnMessage += QString("\nFamiliartiy for %1: %2\n").arg(artist).arg(retValString); + m_currReturnMessage += tr("\nFamiliarity for %1: %2\n").arg(artist, retValString); break; } case InfoTrackLyrics: { qDebug() << "Lyrics requested"; if (!output.canConvert() || - !input.canConvert() + !input.canConvert() ) { qDebug() << "Variants failed to be valid"; break; } - InfoCustomDataHash inHash = input.value(); + InfoCustomData inHash = input.value(); QString artist = inHash["artistName"].toString(); QString track = inHash["trackName"].toString(); QString lyrics = output.toString(); qDebug() << "lyrics = " << lyrics; - m_currReturnMessage += QString("\nLyrics for \"%1\" by %2:\n\n%3\n").arg(track).arg(artist).arg(lyrics); + m_currReturnMessage += tr("\nLyrics for \"%1\" by %2:\n\n%3\n").arg(track, artist, lyrics); break; } default: diff --git a/src/xmppbot/xmppbot.h b/src/xmppbot/xmppbot.h index e88704a44..ee5a2b38a 100644 --- a/src/xmppbot/xmppbot.h +++ b/src/xmppbot/xmppbot.h @@ -66,7 +66,7 @@ public: public slots: virtual void newTrackSlot(const Tomahawk::result_ptr &track); - virtual void infoReturnedSlot(QString caller, Tomahawk::InfoSystem::InfoType type, QVariant input, QVariant output, Tomahawk::InfoSystem::InfoCustomDataHash customData); + virtual void infoReturnedSlot(QString caller, Tomahawk::InfoSystem::InfoType type, QVariant input, QVariant output, Tomahawk::InfoSystem::InfoCustomData customData); virtual void infoFinishedSlot(QString caller); protected: diff --git a/thirdparty/qtweetlib/tomahawk-custom/tomahawkoauthtwitter.cpp b/thirdparty/qtweetlib/tomahawk-custom/tomahawkoauthtwitter.cpp index 0d4c3e06b..8f2549643 100644 --- a/thirdparty/qtweetlib/tomahawk-custom/tomahawkoauthtwitter.cpp +++ b/thirdparty/qtweetlib/tomahawk-custom/tomahawkoauthtwitter.cpp @@ -12,7 +12,7 @@ int TomahawkOAuthTwitter::authorizationWidget() { bool ok; - int i = QInputDialog::getInt(0, QString( "Twitter PIN" ), QString( "After authenticating on Twitter's web site,\nenter the displayed PIN number here:" ), 0, 0, 2147483647, 1, &ok); + int i = QInputDialog::getInt(0, tr( "Twitter PIN" ), tr( "After authenticating on Twitter's web site,\nenter the displayed PIN number here:" ), 0, 0, 2147483647, 1, &ok); if (ok) return i;