commit 3bde7c3d5f946765ba772683cc3b4be422066ee2 Author: Jayvant Javier Pujara Date: Thu Nov 1 16:05:12 2012 -0400 Reload diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..412eeda --- /dev/null +++ b/.gitattributes @@ -0,0 +1,22 @@ +# Auto detect text files and perform LF normalization +* text=auto + +# Custom for Visual Studio +*.cs diff=csharp +*.sln merge=union +*.csproj merge=union +*.vbproj merge=union +*.fsproj merge=union +*.dbproj merge=union + +# Standard to msysgit +*.doc diff=astextplain +*.DOC diff=astextplain +*.docx diff=astextplain +*.DOCX diff=astextplain +*.dot diff=astextplain +*.DOT diff=astextplain +*.pdf diff=astextplain +*.PDF diff=astextplain +*.rtf diff=astextplain +*.RTF diff=astextplain diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2b87aa6 --- /dev/null +++ b/.gitignore @@ -0,0 +1,166 @@ +*.exe +*.org + +################# +## Eclipse +################# + +*.pydevproject +.project +.metadata +bin/ +tmp/ +*.tmp +*.bak +*.swp +*~.nib +local.properties +.classpath +.settings/ +.loadpath + +# External tool builders +.externalToolBuilders/ + +# Locally stored "Eclipse launch configurations" +*.launch + +# CDT-specific +.cproject + +# PDT-specific +.buildpath + + +################# +## Visual Studio +################# + +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. + +# User-specific files +*.suo +*.user +*.sln.docstates + +# Build results +[Dd]ebug/ +[Rr]elease/ +*_i.c +*_p.c +*.ilk +*.meta +*.obj +*.pch +*.pdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.vspscc +.builds +*.dotCover + +## TODO: If you have NuGet Package Restore enabled, uncomment this +#packages/ + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opensdf +*.sdf + +# Visual Studio profiler +*.psess +*.vsp + +# ReSharper is a .NET coding add-in +_ReSharper* + +# Installshield output folder +[Ee]xpress + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish + +# Others +[Bb]in +[Oo]bj +sql +TestResults +*.Cache +ClientBin +stylecop.* +~$* +*.dbmdl +Generated_Code #added for RIA/Silverlight projects + +# Backup & report files from converting an old project file to a newer +# Visual Studio version. Backup files are not needed, because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML + + + +############ +## Windows +############ + +# Windows image file caches +Thumbs.db + +# Folder config file +Desktop.ini + + +############# +## Python +############# + +*.py[co] + +# Packages +*.egg +*.egg-info +dist +build +eggs +parts +bin +var +sdist +develop-eggs +.installed.cfg + +# Installer logs +pip-log.txt + +# Unit test / coverage reports +.coverage +.tox + +#Translations +*.mo + +#Mr Developer +.mr.developer.cfg + +# Mac crap +.DS_Store diff --git a/About.ahk b/About.ahk new file mode 100644 index 0000000..64409d9 --- /dev/null +++ b/About.ahk @@ -0,0 +1,61 @@ +About: +; Cursor change for about box links: +; Load the cursor and start the hook: +hCurs:=DllCall("LoadCursor","UInt",NULL,"Int",32649,"UInt") ;IDC_HAND +OnMessage(0x200,"WM_MOUSEMOVE") + +GuiChildInit("AboutBox") +ABw = 250 +ABh = 150 +ABx := CenterX(ABw) +ABy := CenterY(ABh) + +DevEmail := "JJPujara@gmail.com" +SiteUrl := "http://www.reddit.com/r/LifeRPG/" + +Gui, AboutBox:Add, Picture, w32 h-1, res/WP_RPG_VG.ico + +Gui, AboutBox:Font, bold +Gui, AboutBox:Add, Text, x+10, LifeRPG r2 +Gui, AboutBox:Font + +Gui, AboutBox:Add, Text, y+1, by Jayvant Javier Pujara +Gui, AboutBox:Font, cBlue +Gui, AboutBox:Add, Text, y+1 gAboutLinkEmail vAboutLinkEmail, %DevEmail% +Gui, AboutBox:Font + +Gui, AboutBox:Add, Text, xm y+10, For help and discussion,`nvisit the LifeRPG community on reddit: +Gui, AboutBox:Font, cBlue +Gui, AboutBox:Add, Text, y+1 gAboutLinkSite, %SiteUrl% +Gui, AboutBox:Font, + +Gui, AboutBox:Add, Button, y+15 w80 Default gAboutBoxGuiClose, OK +Gui, AboutBox:Show, w%ABw% h%ABh% x%ABx% y%ABy%, About +return + +AboutLinkEmail: +Run, mailto:%DevEmail% +return + +AboutLinkSite: +Run, %SiteUrl% +return + +AboutBoxGuiClose: +AboutBoxGuiEscape: +; Disable the hook and destroy the cursor: +OnMessage(0x200,"") +DllCall("DestroyCursor","Uint",hCurs) +GuiChildClose("AboutBox") +return + +; Cursor hook: +WM_MOUSEMOVE(wParam,lParam) +{ + Global hCurs, AboutLinkEmail + MouseGetPos,,,,ctrl + ; Only change over certain controls, use Windows Spy to find them. + If ctrl in Static4,Static6 + DllCall("SetCursor","UInt",hCurs) + Return +} \ No newline at end of file diff --git a/Data/.gitignore b/Data/.gitignore new file mode 100644 index 0000000..920844e --- /dev/null +++ b/Data/.gitignore @@ -0,0 +1,166 @@ +*.db +*.ini + +################# +## Eclipse +################# + +*.pydevproject +.project +.metadata +bin/ +tmp/ +*.tmp +*.bak +*.swp +*~.nib +local.properties +.classpath +.settings/ +.loadpath + +# External tool builders +.externalToolBuilders/ + +# Locally stored "Eclipse launch configurations" +*.launch + +# CDT-specific +.cproject + +# PDT-specific +.buildpath + + +################# +## Visual Studio +################# + +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. + +# User-specific files +*.suo +*.user +*.sln.docstates + +# Build results +[Dd]ebug/ +[Rr]elease/ +*_i.c +*_p.c +*.ilk +*.meta +*.obj +*.pch +*.pdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.vspscc +.builds +*.dotCover + +## TODO: If you have NuGet Package Restore enabled, uncomment this +#packages/ + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opensdf +*.sdf + +# Visual Studio profiler +*.psess +*.vsp + +# ReSharper is a .NET coding add-in +_ReSharper* + +# Installshield output folder +[Ee]xpress + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish + +# Others +[Bb]in +[Oo]bj +sql +TestResults +*.Cache +ClientBin +stylecop.* +~$* +*.dbmdl +Generated_Code #added for RIA/Silverlight projects + +# Backup & report files from converting an old project file to a newer +# Visual Studio version. Backup files are not needed, because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML + + + +############ +## Windows +############ + +# Windows image file caches +Thumbs.db + +# Folder config file +Desktop.ini + + +############# +## Python +############# + +*.py[co] + +# Packages +*.egg +*.egg-info +dist +build +eggs +parts +bin +var +sdist +develop-eggs +.installed.cfg + +# Installer logs +pip-log.txt + +# Unit test / coverage reports +.coverage +.tox + +#Translations +*.mo + +#Mr Developer +.mr.developer.cfg + +# Mac crap +.DS_Store diff --git a/Electrolize-Regular.ttf b/Electrolize-Regular.ttf new file mode 100644 index 0000000..7f31212 Binary files /dev/null and b/Electrolize-Regular.ttf differ diff --git a/FileManage.ahk b/FileManage.ahk new file mode 100644 index 0000000..5acdb87 --- /dev/null +++ b/FileManage.ahk @@ -0,0 +1,117 @@ +; Database Open/Select: ===================================================== +FileOpen: +Gui, +OwnDialogs +FileSelectFile, NewDB, , data, Open a projects database, LifeRPG Database (*.db) +if (NewDB <> "") +{ + if (IsObject(db)) + { + OldDB := db + OldDB.Close() + } + ; Set the db var to the new database: + db := DBA.DataBaseFactory.OpenDataBase("SQLite", NewDB) + + ; Check to see if database is old and needs to be updated: + if (ProfileGet("release") = "") + { + MsgBox Updating Database + ; Add columns to projects table: + ProjectsNewCols := {"dateDone":"NUMERIC", "dateEntered":"NUMERIC", "skill":"TEXT", "levelDone":"NUMERIC"} + for col, type in ProjectsNewCols + { + db.Query("ALTER TABLE projects ADD " . col . " " . type) + } + + ; Create inventory table: + CreateInventory := "CREATE TABLE inventory ( id INTEGER PRIMARY KEY, item TEXT, description TEXT, value NUMERIC, date NUMERIC, category TEXT )" + db.Query(CreateInventory) + + ; Create finances table: + CreateFinances := "CREATE TABLE finances ( date NUMERIC, id INTEGER PRIMARY KEY, description TEXT, amount NUMERIC, category TEXT )" + db.Query(CreateFinances) + + ; Create profile table and fill in settings: + ; points: 0 + ; threshold: 100 + ; name: + ; momentum: 100 + ; MMTLastUpdate: Right now + ; title: + ; release: 2 + CreateProfile := "CREATE TABLE profile ( setting TEXT, value TEXT )" + db.Query(CreateProfile) + ProfileSettings := {"points": 0, "threshold": 100, "name":"Edit Profile", "momentum": 100, "MMTLastUpdate": FormatTime(,"yyyyMMdd"), "title":"", "release":2} + for setting, value in ProfileSettings + { + ProfileRecord := {} + ProfileRecord.Setting := setting + ProfileRecord.value := value + db.Insert(ProfileRecord, "profile") + } + } + + ; Update GUI controls to display new database data (HUD and main projects ListView) + FileOpenGUI_Refresh() +} +return + +FileNew: +Gui, +OwnDialogs +; Present dialog to set database file name +FileSelectFile, NewDB_Path, S24, New LifeRPG.db, New projects database, LifeRPG Database (*.db) +if (NewDB_Path <> "") +{ + ; Get the desired filename: + SplitPath, NewDB_Path, NewDB_Name, NewDB_Dir, NewDB_Ext + + ; Refresh everything needed to "load" database, set as default, add to recents menu + if (NewDB_Ext = "") + { + NewDB_Name .= ".db" + NewDB_Path .= ".db" + } + + NewDB := NewDB_Path + + if (IsObject(db)) + { + OldDB := db + OldDB.Close() + } + + if (FileExist(NewDB_Path)) + FileDelete, %NewDB_Path% + ; Copy blank database to selected location and rename to desired name: + FileCopy, Res\Blank.db, %NewDB_Path% + + ; Point the db var to the new database: + db := DBA.DataBaseFactory.OpenDataBase("SQLite", NewDB) + + ; Update GUI controls to display new database data (HUD and main projects ListView) + FileOpenGUI_Refresh() +} +return + +FileOpenGUI_Refresh() +{ + global + if (OldDB) + { + gosub ClearSearch + MomentumLastUpdate := ProfileGet("MMTLastUpdate") + HUD_Refresh() + } + SettingSet("File", "LastOpened", NewDB) ; Update settings file to point to new "current" database: +} + +SettingSet(Section, Key, Value) +{ + IniWrite, %Value%, data/Settings.ini, %Section%, %Key% +} + +SettingGet(Section, Key) +{ + IniRead, Setting, data/Settings.ini, %Section%, %Key% + return Setting +} \ No newline at end of file diff --git a/Functions.ahk b/Functions.ahk new file mode 100644 index 0000000..fe0a522 --- /dev/null +++ b/Functions.ahk @@ -0,0 +1,382 @@ +;~ =============================================================================== +;~ Useful Functions: + +; Stops user from being able to resize the listView columns on the main window: +;~ OnMessage(0x4E,"WM_NOTIFY") +;~ WM_NOTIFY(wParam, lParam, msg, hwnd) +;~ { Critical + ;~ Static HDN_BEGINTRACKA = -306, HDN_BEGINTRACKW = -326 + ;~ Code := -(~NumGet(lParam+0, 8))-1 + ;~ Return, Code = HDN_BEGINTRACKA || Code = HDN_BEGINTRACKW ? True : "" +;~ } + + + +; Colored Listview rows and text + +; OnMessage( WM_NOTIFY := 0x4E, "WM_NOTIFY" ) ; this line must be executed for the function to work + +WM_NOTIFY( wparam, lparam, msg, hwnd ) { + Static psz, pty, lvitem, itext, offset_code, offset_row, offset_color, LVM_GETITEMTEXT + +; Up to 4 istviews can be colored at a time. Remeber to forcibly redraw them if more than one is +; fully drawn at once. + Global Colored_LV_1, Colored_LV_2, Colored_LV_3, Colored_LV_4 + , Colored_LV_1_BG, Colored_LV_2_BG, Colored_LV_3_BG, Colored_LV_4_BG + , Colored_LV_1_TX, Colored_LV_2_TX, Colored_LV_3_TX, Colored_LV_4_TX + + Critical + + If !( psz ) + { +; Prep the static vars on first run, including LVITEM for getting the color values from the listview. + LVM_GETITEMTEXT := 0x1000 + ( A_IsUnicode ? 115 : 45 ) + psz := A_PtrSize ? A_PtrSize : 4 + pty := A_PtrSize = 8 ? "UPtr" : "UInt" + offset_code := 2 * psz + offset_row := 3 * psz + 24 + offset_color := 5 * psz + 28 + VarSetCapacity( lvitem, 52 + 2 * psz, 0 ) + VarSetCapacity( itext, 250 << !! A_IsUnicode, 0 ) + NumPut( 1, lvitem, 0, "UInt" ) + NumPut( &itext, lvitem, 20, pty ) + NumPut( 250, lvitem, 20 + psz, "UInt" ) + } + +; Get the HWND of the controls sending this notification and see if it's one of our listviews + ct_hwnd := NumGet( lparam + 0, 0, pty ) + If ( ( ct_hwnd = Colored_LV_1 && which_lv := "1" ) || ( ct_hwnd = Colored_LV_2 && which_lv := "2" ) + || ( ct_hwnd = Colored_LV_3 && which_lv := "3" ) || ( ct_hwnd = Colored_LV_4 && which_lv := "4" ) ) + && ( -12 = NumGet( lparam + 0, offset_code, "Int" ) ) ; NM_CUSTOMDRAW = -12 + If ( 1 = draw_stage := NumGet( lparam + 0, offset_code + 4, "Int" ) ) ; CDDS_PREPAINT = 1 + Return 0x20 ; CDRF_NOTIFYITEMDRAW = 0x20 + Else If ( draw_stage = 0x10001 ) ; CDDS_PREPAINT = 0x1, CDDS_ITEM = 0x10001 + { +; Now we know the notification is for an item prepaint, so we can adjust the text and bg colors. +; The colors are kept in the listview itself + item := NumGet( lparam + 0, offset_row, "UInt" ) + If ( 0 < 0 | Colored_LV_%which_lv%_TX ) + { + NumPut( Colored_LV_%which_lv%_TX - 1, lvitem, 8, "UInt" ) + SendMessage, LVM_GETITEMTEXT, item, &lvitem,, % "AHK_ID " ct_hwnd + VarSetCapacity( itext, -1 ) + NumPut( Round( itext ), lparam + 0, offset_color, "UInt" ) + } + If ( 0 < 0 | Colored_LV_%which_lv%_BG ) + { + NumPut( Colored_LV_%which_lv%_BG - 1, lvitem, 8, "UInt" ) + SendMessage, LVM_GETITEMTEXT, item, &lvitem,, % "AHK_ID " ct_hwnd + VarSetCapacity( itext, -1 ) + NumPut( Round( itext ), lparam + 0, offset_color + 4, "UInt" ) + } + } +; Else If ( draw_stage = 0x10002 ) ; CDDS_POSTPAINT = 0x2, CDDS_ITEM = 0x10001 +; { +; ; Put here drawing to do after the item is drawn. E.g: draw custom grid lines. +; } + Static HDN_BEGINTRACKA = -306, HDN_BEGINTRACKW = -326 + Code := -(~NumGet(lParam+0, 8))-1 + Return, Code = HDN_BEGINTRACKA || Code = HDN_BEGINTRACKW ? True : "" +} + +ProfileSet(setting, value) +{ + global db + s := db.Query("UPDATE profile SET value = '" . SafeQuote(value) . "' WHERE setting = '" . setting . "'") + return s +} + +FormatTime(Time="", Format="") +{ + FormatTime, Out, %Time%, %Format% + return Out +} + +ProfileGet(setting) +{ + global db + ProfileSet := db.OpenRecordSet("SELECT value FROM profile WHERE setting = '" . setting . "'") + while (!ProfileSet.EOF) + { + Value := ProfileSet["value"] + ProfileSet.MoveNext() + } + ProfileSet.Close() + return Value +} + +Uppercase(String) +{ + StringUpper, String, String + return String +} + +Capitalize(String) +{ + StringUpper, String, String, T + return String +} + +; Call to refresh skills list after adding a new skill: +RefreshSkillsList(SkillChosen="All") +{ + global + if (SkillChosen = "All" || SkillChosen = "") + { + GuiControl, , FilterSkill, |All||None| + GuiControl, , FilterSkill, % ListSkills() + } + else if (SkillChosen = "None") + { + GuiControl, , FilterSkill, |All|None|| + GuiControl, , FilterSkill, % ListSkills() + } + else + { + PickSkill := ListSkills() + if (InStr(PickSkill, SkillChosen)) + { + GuiControl, , FilterSkill, |All|None| + StringReplace, PickedSkill, PickSkill, %SkillChosen%, %SkillChosen%| + GuiControl, , FilterSkill, % PickedSkill + } + else + { + GuiControl, , FilterSkill, |All||None| + GuiControl, , FilterSkill, % ListSkills() + } + } + GuiControlGet, FilterSkillSelected, , FilterSkill +} + + +ListSkills(Selected="") +{ + global db + SkillList := Object() + Skills := db.OpenRecordSet("SELECT DISTINCT skill FROM projects ORDER BY skill") + while(!Skills.EOF) + { + Skill := Skills["skill"] + If (Skill <> "") + SkillList.Insert(Skill) + Skills.MoveNext() + } + Skills.Close() + SkillComboList = + For Num, Skill in SkillList + { + SkillComboList .= Skill . "|" + if (Selected and Skill = Selected) + SkillComboList .= "|" + } + return SkillComboList +} + +ListDifficulties(SetDifficulty="") +{ + global Difficulties + For k, diff in Difficulties + { + if (diff = SetDifficulty) + diff := diff . "|" + else if (k = 1) + diff := diff . "|" + DifficultyList .= diff . "|" + } + return DifficultyList +} + +ListPriorities(SetPriority="") +{ + global Priorities + For k, imp in Priorities + { + if (imp = SetPriority) + imp := imp . "|" + else if (k = 1 && SetPriority <> "All") + imp := imp . "|" + PriorityList .= imp . "|" + } + return PriorityList +} + +UpdateList(NextSelection="", ImportanceSelected="All", Skill="All") +{ + global + IDCol = 1 + DifficultyCol = 2 + ImportanceCol = 4 + ParentCol = 5 + Critical + Gui, 1:Default + GuiControlGet, SearchString, , SearchQuery + GuiControl, -ReDraw, MainList + LV_Delete() + Filter= + if (ImportanceSelected <> "" || FilterShowDone <> "" || Skill <> "" || SearchString <> "") + Filter .= "WHERE " + if (SearchString <> "") + Filter .= "project LIKE '%" SafeQuote(SearchString) "%' AND" + if (FilterShowDone <> 1) + Filter .= " difficulty <> 'Done' " + else if (FilterShowDone = 1) + Filter .= " difficulty = 'Done' " + if (ImportanceSelected <> "All") + Filter .= " AND importance = '" SafeQuote(ImportanceSelected) "' " + if (Skill = "None") + filter .= " AND skill = '' " + else if (Skill <> "All") + Filter .= " AND skill = '" SafeQuote(Skill) "' " + + ;Notification("","Select * from projects " . Filter) + + Projects := db.OpenRecordSet("Select * from projects " . Filter) + while (!Projects.EOF) + { + ID := Projects["id"] + Difficulty := Projects["difficulty"] + Project := Projects["project"] + Parent := Projects["parent"] + Importance := Projects["importance"] + for k, v in Priorities + { + If (Importance = v) + Importance := k ". " v + } + If (Importance = "") + Importance := "None" + LV_Add("", ID,Difficulty,Project,Importance,Parent) + Projects.MoveNext() + } + Projects.Close() + GuiControl, -ReDraw, MainList + LV_ModifyCol(1, "Integer sortdesc") ; Enable this to sort by ID, which could show most recent or oldest first, depending. + LV_ModifyCol(ImportanceCol, "sort") ; First sort by importance + LV_ModifyCol(DifficultyCol, "sortdesc") ; Then sort by difficulty, descending. Just so happens my scheme sorts alright on its own, what luck. + + If (NextSelection) + LV_Modify(NextSelection, "Focus Select Vis") + Times := LV_GetCount() + Loop % Times + { + ThisLine := A_Index + LV_GetText(Text, ThisLine, ImportanceCol) + for k, v in Priorities ; Remove those numbers added to the priorities to allow for proper sorting + { + P := k ". " v + if (Text = P) + { + LV_Modify(ThisLine,"Col" . ImportanceCol,v) + } + } + + LV_GetText(ParentID, ThisLine, ParentCol) + + GetParent := db.OpenRecordSet("SELECT project FROM projects WHERE id = " ParentID) + while (!GetParent.EOF) + { + ParentName := GetParent["project"] + GetParent.MoveNext() + } + GetParent.Close() + LV_Modify(ThisLine, "Col" . ParentCol,ParentName) + } + LV_ModifyCol() + Loop % LV_GetCount("Col") + LV_ModifyCol(A_Index,"AutoHdr") + LV_ModifyCol(IDCol, 0) ; Hide ID column + + Loop % LV_GetCount() + { + RowNum := A_Index + LV_GetText(RowDiff, RowNum, 2) + for kDiff, vDiff in Difficulties + { + for kCol, vCol in Colors + { + if (RowDiff = vDiff) + { + if (kDiff = kCol) + LV_Modify(RowNum, "Col6", vCol) + } + else if (RowDiff = "Done") + LV_Modify(RowNum, "Col6", BGR("F5FFFA")) + } + } + } + OnMessage( WM_NOTIFY := 0x4E, "WM_NOTIFY" ) + LV_ModifyCol(6, 0) ; Hide Color column + GuiControl, +ReDraw, MainList + return +} + +GetParentName() +{ + global + Times := LV_GetCount() + Loop % Times + { + ThisLine := A_Index + LV_GetText(ParentID, ThisLine, ParentCol) + + GetParent := db.OpenRecordSet("SELECT project FROM projects WHERE id = " ParentID) + while (!GetParent.EOF) + { + ParentName := GetParent["project"] + GetParent.MoveNext() + } + GetParent.Close() + LV_Modify(ThisLine, "Col" . ParentCol,ParentName) + } +} + +SafeQuote(string) ; Escape single quotes for sql update. Insert doesn't seem to need it because the DB library handles it. +{ + StringReplace, string, string, ','', All + return string +} + +CenterX(w) +{ + global WindowFind + WinGetPos,Fx,Fy,Fw,Fh,A + return Fx + Round(Fw/2) - Round(w/2) +} + +CenterY(h) +{ + global WindowFind + WinGetPos,Fx,Fy,Fw,Fh,A + return Fy + Round(Fh/2) - Round(h/2) +} + +GuiMsgBox(Name, Title, Text, w=170, h=60) +{ + GuiChildInit(Name) + Gui, %Name%:Add, Text, w%w% Center, %Text% + Gui, %Name%:Add, Button, % "Default g" Name "Yes w40 x" Round((w-80)/2), &Yes + Gui, %Name%:Add, Button, Default x+1 g%Name%No w40, &No + MX := CenterX(w) + MY := CenterY(h) + Gui, %Name%:Show, w%w% h%h% x%mx% y%my%, %Title% + Gui, %Name%:-MinimizeBox -MaximizeBox + return +} + +GuiChildInit(Child, Parent=1) +{ + Gui, %Child%:New + Gui, %Child%:+Owner%Parent% + Gui, %Parent%:+Disabled + Gui, %Child%:Default + return +} + +GuiChildClose(Child, Parent=1) +{ + Gui, %Parent%:-Disabled + Gui, %Child%:Cancel + Gui, %Parent%:Default + return +} \ No newline at end of file diff --git a/HUD.ahk b/HUD.ahk new file mode 100644 index 0000000..667e73e --- /dev/null +++ b/HUD.ahk @@ -0,0 +1,343 @@ +;~ =============================================================================== +; HUD and functions: + +HUD_Color = 15384E +HUD_Trans = 200 +HUD_Color2 = 48B1DF + +; Create a new independent Guis for the HUD + +; Level Module: +LevelX = 80 +LevelY = 45 +LevelW = 450 +LevelH = 80 +Gui, HUD_Level:New +Gui, HUD_Level:+LastFound +AlwaysOnTop -Caption +ToolWindow +Gui, HUD_Level:Color, %HUD_Color% +;Gui, HUD_Level:Add, Picture, x0 y0 w400 h70 , Res\BG.png +Gui, HUD_Level:Font, S14 Q5 bold, Electrolize +Gui, HUD_Level:Add, Progress, vHUD_Progress x12 y12 w425 h18 cWhite Background48B1DF +NameSize = 260 +Gui, HUD_Level:Add, Text, vHUD_Name x12 y+1 w%NameSize% r1 c%HUD_Color2% BackgroundTrans, % ProfileGet("name") +Gui, HUD_Level:Font, s10 +PointsSize := 424 - NameSize +Gui, HUD_Level:Add, Text, vHUD_Points x+1 w%PointsSize% Right cWhite BackgroundTrans, +Gui, HUD_Level:Font, s14 +Gui, HUD_Level:Add, Text, vHUD_Text x12 y+7 w425 cWhite BackgroundTrans ; Shows current level and temporarily shows new XP awards. +HUD_LevelText := "LEVEL " +HUD_LevelTitle := +;Gui, HUD_Level:Color, 15384E +WinSet, Transparent, %HUD_Trans% +Winset, ExStyle, +0x20 +Gui, HUD_Level:Show, x%LevelX% y%LevelY% w%LevelW% h%LevelH% NoActivate +Gui, HUD_Level:Hide + +; Momentum Module: +Gui, HUD_Momentum:New +Gui, HUD_Momentum:+LastFound +AlwaysOnTop -Caption +ToolWindow +Gui, HUD_Momentum:Color, %HUD_Color% +Gui, HUD_Momentum:Font, S14 Q5 bold, Electrolize +Gui, HUD_Momentum:Add, Text, x9 y4 cWhite BackgroundTrans, MMT +MMTStart := ProfileGet("momentum") +Gui, HUD_Momentum:Add, Progress, vHUD_MomentumBar x+5 y8 w325 h13 cRed Background48B1DF, % MMTStart +Gui, HUD_Momentum:Add, Text, vHUD_MomentumPerc x388 y4 w59 cWhite BackgroundTrans Center, % MMTStart . "%" +WinSet, Transparent, %HUD_Trans% +Winset, ExStyle, +0x20 +Gui, HUD_Momentum:Show, x80 y135 w450 h30 NoActivate +Gui, HUD_Momentum:Hide + +HUD_Refresh() +{ + global + ; HUD Update: + ; name + ; level + title + ; points/threshold + ; momentum bar + ; progress bar! + GuiControl, HUD_Level:, HUD_Progress, % ProgressGet() + GuiControl, HUD_Level:, HUD_Name, % ProfileGet("name") + GuiControl, HUD_Level:, HUD_Text, % HUD_LevelText . LevelCheck() . " " . ProfileGet("title") + GuiControl, HUD_Level:, HUD_Points, % PointsCheck() . "/" . ThreshCheck() + MMTNow := ProfileGet("momentum") + GuiControl, HUD_Momentum:, HUD_MomentumBar, % MMTNow + GuiControl, HUD_Momentum:, HUD_MomentumPerc, % MMTNow . "%" +} + + +HUD_MouseOverHide(ByRef hX, ByRef hY, ByRef hW, ByRef hH) +{ + global HUD_Trans + SetTimer, Mouse, 100 + + Mouse: + CoordMode, Mouse, Screen + MouseGetPos, x, y + + ;ToolTip, %GuiX% (%GuiW% + %GuiX%) `n %x% %y% + ; if the mouse (x) is located horizontally in a greater position than the hud's X starting position + ; and less than that x position plus the HUD's width + ; and vertically (y) greater than the HUD's y position + ; and lower than that y pos plus the HUD's height + ; then hide the HUD. + if (((x >= hX && x <= (hX+hW))) && ((y >= hY) && (y <= (165)))) ; 80-530; 45-125 ; hY+hH+ + { + Gui, HUD_Level:+LastFound + WinSet, Transparent, 0 + WinSet, ExStyle, +0x20 + + Gui, HUD_Momentum:+LastFound + WinSet, Transparent, 0 + WinSet, ExStyle, +0x20 + } + else + { + Gui, HUD_Level:+LastFound + WinSet, Transparent, %HUD_Trans% + WinSet, AlwaysOnTop, On + + Gui, HUD_Momentum:+LastFound + WinSet, Transparent, %HUD_Trans% + WinSet, AlwaysOnTop, On + } + return +} + + +HUD_Progress(PreviousLevelPoints="toggle",PreviousLevel="") +{ + global + split = 0 + ;SetTimer, DestProg, Off + SetTimer, ClearAwardText, off + SetTimer, HideAgain, off + static VisibState = 0 + Gui, HUD_Level:Default + if (VisibState = 1) ; HUD is visible + { + if (PreviousLevelPoints = "toggle") ; toggle called, so hide HUD and return + { + Gui, HUD_Level:Hide + Gui, HUD_Momentum:Hide + VisibState = 0 ; HUD now hidden + } + else ; update progress bar and then clear award text from control after a few seconds. + { + HUD_Update(PreviousLevelPoints, PreviousLevel) + SetTimer, ClearAwardText, 2000 + return + + ClearAwardText: + Critical + Gui, HUD_Level:Default + GuiControl, , HUD_Text, % HUD_LevelText . LevelCheck() . " " . ProfileGet("title") + SetTimer, ClearAwardText, off + return + } + } + else if (VisibState = 0) ; HUD is not visible + { + if (PreviousLevelPoints = "toggle") ; toggle called, so show HUD + { + GuiControl,, HUD_Progress, % ProgressGet() ; Update progress bar + GuiControl,, HUD_Text, % HUD_LevelText . LevelCheck() . " " . ProfileGet("title") + GuiControl,, HUD_Points, % PointsCheck() . "/" . ThreshCheck() + + Gui, HUD_Level:Show, x80 y45 NoActivate + WinSet, AlwaysOnTop, On + Gui, HUD_Momentum:Show, NoActivate + WinSet, AlwaysOnTop, On + HUD_MouseOverHide(LevelX, LevelY, LevelW, LevelH) + + VisibState = 1 ; HUD now showing + } + else ; show HUD temporarily when points are awarded, update progress bar and text, and then hide again. + { + + Gui, HUD_Level:Show, x80 y45 NoActivate + WinSet, AlwaysOnTop, On + Gui, HUD_Momentum:Show, NoActivate + WinSet, AlwaysOnTop, On + + HUD_Update(PreviousLevelPoints, PreviousLevel) + SetTimer, HideAgain, 2500 + return + + HideAgain: + Critical + Gui, HUD_Level:Hide + Gui, HUD_Momentum:Hide + SetTimer, HideAgain, off + return + } + } + return +} + +; Animate the progress bars and numbers and check for leveling up event: +HUD_Update(PreviousLevelPoints, PreviousLevel) +{ + global + Gui, HUD_Level:Default ; Operate on the Level module + CurrentLevelPoints := ProgressGet() + if (PreviousLevelPoints < CurrentLevelPoints) + { + ; slide up to sub100 value CurrentLevelPoints + GuiControl,, HUD_Progress, % PreviousLevelPoints + if (CurrentLevelPoints >= 100) + { + split = 1 + CurrentLevelPoints = 100 + } + else + split = 0 + AnimationCount := CurrentLevelPoints - PreviousLevelPoints + AnimPoints := PointsCheck() - AnimationCount + Loop % AnimationCount + { + GuiControl,, HUD_Progress, % PreviousLevelPoints + A_Index + ;GuiControl,, HUD_Text, % HUD_LevelText . PreviousLevel . " +" . A_Index . " XP" + GuiControl,, HUD_Text, % HUD_LevelText . PreviousLevel . " +" . A_Index . " XP " . ProfileGet("title") + GuiControl,, HUD_Points, % AnimPoints + A_Index . "/" . ThreshCheck() + Sleep 50 + } + if (split = 1) + { + GuiControl,, HUD_Progress, 0 + NewLevelPoints := ProgressGet() - 100 + Loop % NewLevelPoints + { + GuiControl,, HUD_Progress, % A_Index + ;GuiControl,, HUD_Text, % HUD_LevelText . LevelCheck() . " +" . A_Index + GuiControl,, HUD_Text, % HUD_LevelText . LevelCheck() . " +" . A_Index . " XP " . ProfileGet("title") + GuiControl,, HUD_Points, % (PointsCheck()-NewLevelPoints) + A_Index . "/" . ThreshCheck() + Sleep 50 + } + } + } + LevelCheck() +} + + +HUD_Message(message, duration="2500") +{ + ;Gui, 2:Destroy + Gui, Message:New + ; Example: On-screen display (OSD) via transparent window: + CustomColor = 9AFF9A ; Can be any RGB color (it will be made transparent below). + Gui Message:+LastFound +AlwaysOnTop -Caption +ToolWindow ; +ToolWindow avoids a taskbar button and an alt-tab menu item. + Gui, Message:Color, %CustomColor% + Gui, Message:Font, s25 Q5, Electrolize ; Set a large font size (32-point). + Gui, Message:Add, Text, Center cLime, %message% ; XX & YY serve to auto-size the window. + ; Make all pixels of this color transparent and make the text itself translucent (150): + WinSet, TransColor, %CustomColor% 255 + + ;VertPos := A_ScreenHeight - offset + Gui, Message:Show, x60 y99 NoActivate ; NoActivate avoids deactivating the currently active window. + ;Sleep 2000 + SetTimer, DestroyMsg, %duration% + return + + DestroyMsg: + Gui, Message:Destroy + SetTimer, DestroyMsg, Off + return +} + + +PointsCheck() +{ + ; The current number of points I have + global db + PointsSet := db.OpenRecordSet("SELECT value FROM profile WHERE setting = 'points'") + while (!PointsSet.EOF) + { + Points := PointsSet["value"] + PointsSet.MoveNext() + } + PointsSet.Close() + return Points +} + +; Could combine these two functions into one ^ \/, plus the writing ones: + +ThreshCheck() +{ + ; The next upcoming point threshold to level up again + global db + ThresholdSet := db.OpenRecordSet("SELECT value FROM profile WHERE setting = 'threshold'") + while (!ThresholdSet.EOF) + { + Threshold := ThresholdSet["value"] + ThresholdSet.MoveNext() + } + ThresholdSet.Close() + return Threshold +} + + +PointsWrite(Points) +{ + ;global PointsFile + ;IniWrite, %Points%, %PointsFile%, Data, Points ; Store certain number of awarded points in file + global db + bool := db.Query("UPDATE profile SET value = " . Points . " WHERE setting = 'points'") + return +} + +ThreshWrite(Threshold) +{ + ;global PointsFile + ;IniWrite, %Threshold%, %PointsFile%, Data, Threshold + global db + bool := db.Query("UPDATE profile SET value = " . Threshold . " WHERE setting = 'threshold'") + return +} + +ProgressGet() { + CurrentProgress := 100 - (ThreshCheck() - PointsCheck()) ; How many points until next level up event + return CurrentProgress ; What shows up on progress bar +} + + +LevelCheck() { + global LevelUpSound + ; Threshold starts at 100, i.e. you start at level 1 + If (PointsCheck() >= ThreshCheck()) + { + ;Set next threshold + ;Threshold should go up. + if (FileExist(LevelUpSound)) + SoundPlay, %LevelUpSound% + ThreshWrite(ThreshCheck() + 100) ; Write new threshold + LevelNow := Floor(ThreshCheck()/100) + ;HUD_Message("Level Up! Level " LevelNow, 5000) ; This *could* be a fancier notification than just a tray notification + Notification("LEVEL UP!", "You have reached Level " . LevelNow) + } + Return Floor(ThreshCheck()/100) +} + +LevelGet() +{ + return Floor(ThreshCheck()/100) +} + +; Main function to call to award points: +UpdateProgress(Message, Award, Sound="") ; Call to give user some points and show a notification +{ + PreviousLevelPoints := ProgressGet() + PreviousLevel := LevelCheck() + ;SoundPlay, %Sound% + ;HUD_Message(Message) HUD_message should be altered to be a fancy HUD message + Notification(Message, "+" . Award . " XP Awarded") + PointsWrite(PointsCheck() + Award) + HUD_Progress(PreviousLevelPoints, PreviousLevel) + return +} + +Notification(Title, Message="", Duration=9) +{ + Notify(Title, Message, Duration, "GC=15384E GR=0 GT=200 TS=14 TC=ffffff TF=Electrolize MS=14 MC=48B1DF MF=Electrolize BW=0 BR=0") + return +} \ No newline at end of file diff --git a/Help.ahk b/Help.ahk new file mode 100644 index 0000000..38007de --- /dev/null +++ b/Help.ahk @@ -0,0 +1,38 @@ +; Help menu items:======================================================================== +ReferenceHotkeys: +GuiChildInit("RefHkeys") +RHw = 300 +RHh = 250 +RHx := CenterX(RHw) +RHy := CenterY(RHh) + +HKRefText = +( +To toggle the Heads-Up Display, press: Alt+F2 + +To quickly add a project to your list for later, from anywhere; when you're doing anything, press: +Ctrl+Alt+A + +To quickly log a finished project without having to add it to the list first, press: +Ctrl+Alt+D + +To quickly give yourself points, use the following: +5 Points: Ctrl+Shift+1 +10 Points: Ctrl+Shift+2 +25 Points: Ctrl+Shift+3 +100 Points (Instantly go up a whole level!): Ctrl+Shift+4 +) + +Gui, RefHkeys:Add, Edit,% "ReadOnly w" RHw-20 " h" RHh-20, % HKRefText + +Gui, RefHkeys:Show, w%RHw% h%RHh% x%RHx% y%RHy%, Reference +return + +RefHKeysGuiEscape: +RefHKeysGuiClose: +GuiChildClose("RefHKeys") +return + +Discussion: +Run http://www.reddit.com/r/LifeRPG +return \ No newline at end of file diff --git a/Hotkeys.ahk b/Hotkeys.ahk new file mode 100644 index 0000000..cefef86 --- /dev/null +++ b/Hotkeys.ahk @@ -0,0 +1,48 @@ +;~ =============================================================================== +;~ Hotkeys: + +;~ Pressing Alt+V focuses user on the ListView: +#If WinActive(WindowFind) +!x:: +GuiControl, Focus, MainList +LV_Modify(1, "Select Focus") +return + +;~ Enables Ctrl+Backspace deletion in edit fields: +#If WinActive("ahk_class AutoHotkeyGUI") +^BS:: +send, ^+{left}{delete} +return + +;~ Give yourself points manually: +#If ; Clear out context sensitivity +; Easy tasks +^+1:: +UpdateProgress("Really Easy Achievement", 5, "increase.wav") +return + +; Medium difficulty +^+2:: +UpdateProgress("Pretty Easy Achievement", 10, "medium.wav") +return + +; Heavy lifting +^+3:: +UpdateProgress("Medium Achievement", 25, "hard.wav") +return + +; Completed big project +^+4:: +UpdateProgress("Hard Achievement", 100, "goal.wav") +return + +!F2:: +HUD_Progress() +return + +;~ !F1:: +;~ if (WinActive(WindowFind)) + ;~ WinMinimize, %WindowFind% +;~ else + ;~ WinActivate, %WindowFind% +;~ return diff --git a/LVCustomColors.ahk b/LVCustomColors.ahk new file mode 100644 index 0000000..b0dfb1a --- /dev/null +++ b/LVCustomColors.ahk @@ -0,0 +1,389 @@ +LV_Initialize(Gui_Number="", Control="", Column="") +{ + local hGUI, hLV + ;Get either class or hWnd of control + If !Control ;Control omitted + { + If (Gui_Number > 99) + hLV := Gui_Number + Else ;No hWnd => default + Control = SysListView321 + } + Else If RegExMatch(Control, "^[1-9]\d*$") ;ClassNN Number provided + Control = SysListView32%Control% + Else If !RegExMatch(Control, "^(SysListView32)?[1-9]\d*$") ;Not a ClassNN => control's associated var + { + If (!(Gui_Number > 0) || (Gui_Number > 99)) + Gui_Number = 1 + If _%Gui_Number%_%Control%_ + Return + GuiControlGet, hLV, %Gui_Number%:hWnd, %Control% + If ErrorLevel + Return + _%Gui_Number%_%Control%_ := hLV + } ;Otherwise, ClassNN was provided. + + If hLV + { + If (_%hLV%_ || !HWND2GuiNClass(hLV, Gui_Number, Control)) + Return + } + Else If Control ;Control found/provided + { + If (!(Gui_Number > 0) || (Gui_Number > 99)) + Gui_Number = 1 + If _%Gui_Number%_%Control%_ + Return + Gui, %Gui_Number%:+LastFoundExist + If !(hGUI := WinExist()) + Return + GuiControlGet, hLV, %Gui_Number%:HWND, %Control% + If ErrorLevel + Return + } + Else + Return + + hLV+=0 + ;Save handle to quickly get it from gui+control + _%Gui_Number%_%Control%_ := hLV + ;Save gui and control to quickly get it from handle + _%hLV%_ := Gui_Number "|" Control + ;Save column containing indexes + If !Column + _%hLV%_Col_ = 1 + Else + _%hLV%_Col_ := Column + ;Maintain a list of registered handles for wm_notify to operate on every registered control + If !_LTV_h_List_ + _LTV_h_List_ := "|" hLV "|" + Else + _LTV_h_List_ .= hLV "|" + ;Maintain a list of modified indexes for disposal + ;Colors bound to indexes + _%hLV%_0_Text = | + _%hLV%_0_Back = | + ;Colors bound to lines + _%hLV%_0_LText = | + _%hLV%_0_LBack = | + OnMessage( 0x4E, "WM_NOTIFY" ) + Return hLV +} + +LV_Change(Gui_Number="", Control="", Select="", Column="") +{ + local hLV + ;Get either class or hWnd of control + If !Control ;Control omitted + { + If (Gui_Number > 99) + hLV := Gui_Number + Else ;No hWnd => default + Control = SysListView321 + } + Else If RegExMatch(Control, "^[1-9]\d*$") ;ClassNN Number provided + Control = SysListView32%Control% + Else If !RegExMatch(Control, "^(SysListView32)?[1-9]\d*$") ;Not a ClassNN or a NN => control's associated var + { + If (!(Gui_Number > 0) || (Gui_Number > 99)) + Gui_Number = 1 + If !(hLV := _%Gui_Number%_%Control%_) ;May not have been initialized + { + If !(hLV := LV_Initialize(Gui_Number, Control, Column)) + Return + } + } ;Otherwise, ClassNN was provided. + + If hLV + { + hLV+=0 + If !_%hLV%_ ;May not have been initialized + { + If !LV_Initialize(hLV, "", Column) + Return + } + Loop, Parse, _%hLV%_, | + { + If (A_Index = 1) + Gui_Number := A_LoopField + Else + Control := A_LoopField + } + } + Else If Control ;Control found/provided + { + If (!(Gui_Number > 0) || (Gui_Number > 99)) + Gui_Number = 1 + If !(hLV := _%Gui_Number%_%Control%_) ;May not have been initialized + { + If !(hLV := LV_Initialize(Gui_Number, Control, Column)) + Return + } + } + Else + Return + + _LV_h_ := hLV+0 + If (Select != 0) + { + Gui, %Gui_Number%:Default + Gui, ListView, %Control% + } + If (Column && (Column != _%hLV%_Col_)) + _%hLV%_Col_ := Column + Return 1 +} + + +LV_SetColor(Index="", TextColor="", BackColor="", Redraw=1) +{ + local i, j, L + If !_LV_h_ + Return + Index+=0 + If (Index < 0) + { + L = L + i = 1 + Index := -Index-1 + } + Else If (Index > 0) + { + i = 1 + Index-- + } + Else If ((Index = "-0") || (Index = "-")) + { + L = L + Index = 0 + ControlGet, i, List, Count, , ahk_id %_LV_h_% + } + Else + { + Index = 0 + ControlGet, i, List, Count, , ahk_id %_LV_h_% + } + Loop, %i% + { + j := A_Index+Index + If (TextColor != "") + { + If (TextColor >= 0) + { + If !InStr(_%_LV_h_%_0_%L%Text, "|" j "|") + _%_LV_h_%_0_%L%Text .= j "|" + _%_LV_h_%_%j%_%L%Text := TextColor + } + Else + { + _%_LV_h_%_%j%_%L%Text = + StringReplace, _%_LV_h_%_0_%L%Text, _%_LV_h_%_0_%L%Text, |%j%|, | + } + } + If (BackColor != "") + { + If (BackColor >= 0) + { + If !InStr(_%_LV_h_%_0_%L%Back, "|" j "|") + _%_LV_h_%_0_%L%Back .= j "|" + _%_LV_h_%_%j%_%L%Back := BackColor + } + Else + { + _%_LV_h_%_%j%_%L%Back = + StringReplace, _%_LV_h_%_0_%L%Back, _%_LV_h_%_0_%L%Back, |%j%|, | + } + } + } + If Redraw + WinSet, Redraw,, ahk_id %_LV_h_% + Return 1 +} + + +LV_GetColor(Index, T="Text") ;Index of the item from which to get color , T="Text" ; T="Back" , L=0 : linked to lines; L=1 : linked to rows +{ + local L + If (Index<0) + { + L = L + Index := -Index + } + Return _%_LV_h_%_%Index%_%L%%T% +} + + +LV_Destroy(Gui_Number="", Control="", DeactivateWMNotify="") +{ + local hLV + ;Get either class or hWnd of control + If !Control ;Control omitted + { + If (Gui_Number > 99) + hLV := Gui_Number + Else ;No hWnd => default + Control = SysListView321 + } + Else If Control RegExMatch(Control, "^[1-9]\d*$") ;ClassNN Number provided + Control = SysListView32%Control% + Else If !RegExMatch(Control, "^(SysListView32)?[1-9]\d*$") ;Not a ClassNN or a NN => control's associated var + { + If (!(Gui_Number > 0) || (Gui_Number > 99)) + Gui_Number = 1 + If !(hLV := _%Gui_Number%_%Control%_) + Return + } ;Otherwise, ClassNN was provided. + + If hLV + { + hLV+=0 + If !_%hLV%_ + Return + Loop, Parse, _%hLV%_, | + { + If (A_Index = 1) + Gui_Number := A_LoopField + Else + Control := A_LoopField + } + } + Else If Control ;Control found/provided + { + If (!(Gui_Number > 0) || (Gui_Number > 99)) + Gui_Number = 1 + If !(hLV := _%Gui_Number%_%Control%_) + Return + } + Else + Return + + Loop, Parse, _%hLV%_0_Text, | + _%hLV%_%A_LoopField%_Text = + _%hLV%_0_Text = + Loop, Parse, _%hLV%_0_Back, | + _%hLV%_%A_LoopField%_Back = + _%hLV%_0_Back = + Loop, Parse, _%hLV%_0_LText, | + _%hLV%_%A_LoopField%_LText = + _%hLV%_0_LText = + Loop, Parse, _%hLV%_0_LBack, | + _%hLV%_%A_LoopField%_LBack = + _%hLV%_0_LBack = + _%Gui_Number%_%Control%_ = + _%hLV%_Col_ = + _%hLV%_ = + WinSet, Redraw,, ahk_id %hLV% + StringReplace, _LTV_h_List_, _LTV_h_List_, |%hLV%|, |, A + If ((_LTV_h_List_ = "|") && DeactivateWMNotify) + OnMessage( 0x4E, "" ) + If (hLV = _LV_h_) + _LV_h_ = + + Return 1 +} + + +DecodeInteger( p_type, p_address, p_offset) ;, p_hex=false ) +{ + ;old_FormatInteger := A_FormatInteger + ;ifEqual, p_hex, 1, SetFormat, Integer, hex + ;else, SetFormat, Integer, dec + StringRight, size, p_type, 1 + loop, %size% + value += *( ( p_address+p_offset )+( A_Index-1 ) ) << ( 8*( A_Index-1 ) ) + if ( size <= 4 and InStr( p_type, "u" ) != 1 and *( p_address+p_offset+( size-1 ) ) & 0x80 ) + value := -( ( ~value+1 ) & ( ( 2**( 8*size ) )-1 ) ) + ;SetFormat, Integer, %old_FormatInteger% + return, value +} + + +EncodeInteger( p_value, p_size, p_address, p_offset ) +{ + loop, %p_size% + DllCall( "RtlFillMemory", "uint", p_address+p_offset+A_Index-1, "uint", 1, "uchar", p_value >> ( 8*( A_Index-1 ) ) ) +} + + +;Retrieves gui number and classNN from hwnd of a gui control +HWND2GuiNClass(hWnd, ByRef Gui = "", ByRef Control = "") +{ + WinGetClass, Cc, ahk_id %hWnd% + Loop, 99 + { + Gui, %A_Index%:+LastFoundExist + If !WinExist() + Continue + Gui_Number := A_Index + Loop + { + GuiControlGet, hWCc, %Gui_Number%:HWND, %Cc%%A_Index% + If !hWCc + Break + If (hWnd = hWCc) + { + Ctrl := Cc A_Index + Break + } + } + If Ctrl + { + Gui := A_Index + Control := Ctrl + Return 1 + } + } +} + + +LV_WM_NOTIFY(p_l) +{ + local draw_stage, Current_Line, hLV, Index1, Index + static IndexList + Critical + If InStr(_LTV_h_List_, "|" (hLV := DecodeInteger( "uint4", p_l, 0 )) "|") + { + If (DecodeInteger( "int4", p_l, 8 ) = -12) ; NM_CUSTOMDRAW + { + draw_stage := DecodeInteger( "uint4", p_l, 12 ) + If ( draw_stage = 1 ) ; CDDS_PREPAINT + { + ControlGet, IndexList, List, % "Col" _%hLV%_Col_, , ahk_id %hLV% + If !RegexMatch(IndexList, "S)^([1-9]\d*\n)*[1-9]\d*$") ;The index column must contain exclusively strictly positive decimal integers + IndexList = + Return, 0x20 ; CDRF_NOTIFYITEMDRAW + } + Else If ( draw_stage = 0x10001 ) ; CDDS_ITEM + { + Current_Line := DecodeInteger( "uint4", p_l, 36 )+1 + If IndexList + RegexMatch(IndexList, "S)(?:.*?\n){" Current_Line-1 "}(.*?)(?:\n|$)", Index) + If (IndexList && (_%hLV%_%Index1%_Text != "")) + EncodeInteger( _%hLV%_%Index1%_Text, 4, p_l, 48 ) ; indexed foreground + Else If (_%hLV%_%Current_Line%_LText != "") + EncodeInteger( _%hLV%_%Current_Line%_LText, 4, p_l, 48 ) ; line foreground + If (IndexList && (_%hLV%_%Index1%_Back != "")) + EncodeInteger( _%hLV%_%Index1%_Back, 4, p_l, 52 ) ; indexed background + Else If (_%hLV%_%Current_Line%_LBack != "") + EncodeInteger( _%hLV%_%Current_Line%_LBack, 4, p_l, 52 ) ; line background + } + } + } + +} + + +WM_NOTIFY( p_w, p_l, p_m ) +{ + Critical + ;/* + ;Prevents column resizing, uncomment if resizing is buggy + Index := DecodeInteger( "int4", p_l, 8 ) + If ((Index = -326) || (Index = -306)) ; HDN_BEGINTRACKA = -306, HDN_BEGINTRACKW = -326 + Return 1 + ;*/ + + ;ADD YOUR CODE HERE + + Return LV_WM_NOTIFY(p_l) +} diff --git a/Lib/ADO.ahk b/Lib/ADO.ahk new file mode 100644 index 0000000..1d27b8b --- /dev/null +++ b/Lib/ADO.ahk @@ -0,0 +1,52 @@ + +/* +* Provides Static ADO Helper classes and Enums +* +*/ +class ADO +{ + class CursorType + { + static adOpenUnspecified := -1 + static adOpenForwardOnly := 0 + static adOpenKeyset := 1 + static adOpenDynamic := 2 + static adOpenStatic := 3 + } + + class LockType + { + static adLockUnspecified := -1 + static adLockReadOnly := 1 + static adLockPessimistic := 2 + static adLockOptimistic := 3 + static adLockBatchOptimistic := 4 + } + + class CommandType + { + static adCmdUnspecified := -1 + static adCmdText := 1 + static adCmdTable := 2 + static adCmdStoredProc := 4 + static adCmdUnknown := 8 + static adCmdFile := 256 + static adCmdTableDirect := 512 + } + + class AffectEnum + { + static adAffectCurrent := 1 + static adAffectGroup := 2 + } + + class ObjectStateEnum + { + static adStateClosed := 0 ; The object is closed + static adStateOpen := 1 ; The object is open + static adStateConnecting := 2 ; The object is connecting + static adStateExecuting := 4 ; The object is executing a command + static adStateFetching := 8 ; The rows of the object are being retrieved + } + +} \ No newline at end of file diff --git a/Lib/Base.ahk b/Lib/Base.ahk new file mode 100644 index 0000000..f0a4353 --- /dev/null +++ b/Lib/Base.ahk @@ -0,0 +1,94 @@ +/************************************** + base classes +*************************************** +*/ + +global null := 0 ; for better readability + + +/* + Check for same (base) Type +*/ +is(obj, type){ + + if(IsObject(type)) + type := typeof(type) + + while(IsObject(obj)){ + + if(obj.__Class == type){ + return true + } + obj := obj.base + } + return false +} + +typeof(obj){ + if(IsObject(obj)){ + cls := obj.__Class + + if(cls != "") + return cls + + while(IsObject(obj)){ + if(obj.__Class != ""){ + return obj.__Class + } + obj := obj.base + } + return "Object" + } + return "NonObject" +} + +IsObjectMember(obj, memberStr){ + if(IsObject(obj)){ + return ObjHasKey(obj, memberStr) || IsMetaProperty(memberStr) + } +} + + +IsMetaProperty(str){ + static metaProps := "__New,__Get,__Set,__Class" + if str in %metaProps% + return true + else + return false +} + + +/** +* Provides some common used Exception Templates +* +*/ +class Exceptions +{ + NotImplemented(){ + return Exception("A not implemented Method was called.",-1) + } + + MustOverride(){ + return Exception("This Method must be overriden",-1) + } + + ArgumentException(furtherInfo=""){ + return Exception("A wrong Argument has been passed to this Method`n" furtherInfo,-1) + } +} + + + + +;Base +{ + "".base.__Call := "Default__Warn" + "".base.__Set := "Default__Warn" + "".base.__Get := "Default__Warn" + + Default__Warn(nonobj, p1="", p2="", p3="", p4="") + { + ListLines + MsgBox A non-object value was improperly invoked.`n`nSpecifically: %nonobj% + } +} \ No newline at end of file diff --git a/Lib/Collection.ahk b/Lib/Collection.ahk new file mode 100644 index 0000000..b8c92fd --- /dev/null +++ b/Lib/Collection.ahk @@ -0,0 +1,104 @@ +#Include +/* + Basic Collection implementation +*/ +class Collection +{ + ; Methoden Implementation + /* + Fügt ein Element der Collection hinzu + */ + Add(obj){ + this.Insert(obj) + } + + /* + Fügt eine Auflistung dieser Collection hinzu + */ + AddRange(objs){ + if(IsObject(objs)){ + for each, item in objs + this.Insert(item) + } else + throw Exceptions.ArgumentException("Must submit Array!") + } + + Clear(){ + this.Remove(this.MinIndex(), this.MaxIndex()) + } + + RemoveItem(item){ + for k, e in this + if(e = item) + this.Remove(k) + } + + /* + Returns the count of elements contained in this collection + */ + Count(){ + return this.SetCapacity(0) + } + + /* + * Returns true if this collection is empty + */ + IsEmpty(){ + return this.Count() == 0 + } + + First(){ + return this[this.MinIndex()] + } + + Last(){ + return this[this.MaxIndex()] + } + + + /* + Sortiert die Liste + */ + Sort(comparer=""){ + if(IsFunc(comparer)) + comparer := "F " comparer + + for each, num in this + nums .= num "`n" + Sort, nums, % comparer + this.Clear() + Loop, parse, nums, `, + this.Add(A_LoopField) + } + + ToString(){ + str := "" + for k, v in this + { + valStr := "" + if(IsObject(v)){ + valStr := "{" . typeof(v) . "}" + if(IsFunc(v.ToString)){ + valStr .= " " . v.ToString() + } + }else{ + valStr := "'" v "'" + } + + + str .= k ": " . valStr . "`n" + } + return str + } + + /* + Konstruktor - erstellt eine neue, (leere) Collection + + enum : Element die zubign vorhanden sein sollen + */ + __New(enum = 0){ + if(IsObject(enum)){ + this.AddRange(enum) + } + } +} \ No newline at end of file diff --git a/Lib/DBA.ahk b/Lib/DBA.ahk new file mode 100644 index 0000000..3c1b042 --- /dev/null +++ b/Lib/DBA.ahk @@ -0,0 +1,27 @@ +/* + DataBase NameSpace Import +*/ + +#Include +#Include + +;drivers +#Include +#Include +#Include + +class DBA ; namespace DBA +{ + #Include + #Include + + + ; Concrete SQL Providers + #Include + #Include + #Include + + #Include + #Include + #Include +} \ No newline at end of file diff --git a/Lib/DataBaseADO.ahk b/Lib/DataBaseADO.ahk new file mode 100644 index 0000000..df913e9 --- /dev/null +++ b/Lib/DataBaseADO.ahk @@ -0,0 +1,200 @@ +;namespace DBA + +/* + Represents a Connection to a ADO Database +*/ +class DataBaseADO extends DBA.DataBase +{ + _connection := null + _connectionData := "" + + __New(connectionString){ + this._connectionData := connectionString + this.Connect() + } + + /* + (Re) Connects to the db with the given creditals + */ + Connect(){ + if(IsObject(this._connection)) + { + this.Close() + } + this._connection := ComObjCreate("ADODB.connection") + + ;connection.Open connectionstring,userID,password,options + this._connection.Open(this._connectionData) + } + + Close(){ + if(this.IsConnected()) + { + this._connection.Close() + this._connection := null + } + } + + /* + * Is this connection open? + */ + IsConnected(){ + return (IsObject(this._connection) && this._connection.State != ADO.ObjectStateEnum.adStateClosed) + } + + IsValid(){ + return IsObject(this._connection) + } + + GetLastError(){ + ; todo + } + + GetLastErrorMsg(){ + + errMsg := "" + for objErr in this._connection.Errors + { + errMsg .= objErr.Number " " objErr.Description " Source:" objErr.Source "`n" + } + + return errMsg + } + + SetTimeout(timeout = 1000){ + if(this.IsValid()) + this._connection.ConnectionTimeout := (timeout / 1000) + } + + + + Changes() { + /* + ToDo + */ + } + + + /* + Querys the DB and returns a RecordSet + */ + OpenRecordSet(sql, editable = false){ + return new DBA.RecordSetADO(sql, this._connection, editable) + } + + /* + Querys the DB and returns a ResultTable or true/false + */ + Query(sql){ + ret := false + if(this.IsValid()) + { + ;Execute( commandtext,ra,options) + affectedRows := 0 + rs := this._connection.Execute(sql, affectedRows) + if(IsObject(rs) && rs.State != ADO.ObjectStateEnum.adStateClosed) + { + ret := this.FetchADORecordSet(rs) + rs.Close() + }else{ + ret := affectedRows + } + } + return ret + } + + EscapeString(str){ + return Mysql_escape_string(str) + } + + + BeginTransaction(){ + if(this.IsValid()) + this._connection.BeginTrans() + } + + EndTransaction(){ + if(this.IsValid()) + this._connection.CommitTrans() + } + + Rollback(){ + if(this.IsValid()) + this._connection.RollbackTrans() + } + + FetchADORecordSet(adoRS){ + tbl := null + if(IsObject(adoRS) && !adoRS.EOF) + { + columnNames := new Collection() + myRows := new Collection() + + + for field in adoRS.Fields + columnNames.add(field.Name) + + fetchedArray := adoRS.GetRows() ; returns a COM-SafeArray Wrapper + colSize := fetchedArray.MaxIndex(1) + 1 + rowSize := fetchedArray.MaxIndex(2) + 1 + + loop, % rowSize + { + i := A_index - 1 + datafields := new Collection() + loop, % colSize + { + j := A_index - 1 + datafields.add(fetchedArray[j,i]) + } + myRows.Add(new DBA.Row(columnNames, datafields)) + } + + tbl := new DBA.Table(myRows, columnNames) + } + return tbl + } + + InsertMany(records, tableName){ + + ;objRecordset.Open source,actconn,cursortyp,locktyp,opt + + rs := ComObjCreate("ADODB.Recordset") + /* batch + rs.Open(tableName, this._connection, ADO.CursorType.adOpenKeyset, ADO.LockType.adLockBatchOptimistic, ADO.CommandType.adCmdTable) + + for each, record in records + { + rs.AddNew() + + for column, value in record + { + rs.Fields[column].Value := value + } + } + rs.UpdateBatch() + */ + + rs.Open(tableName, this._connection, ADO.CursorType.adOpenKeyset, ADO.LockType.adLockOptimistic, ADO.CommandType.adCmdTable) + + for each, record in records + { + rs.AddNew() + + for column, value in record + { + rs.Fields[column].Value := value + } + rs.Update() + } + + rs.Close() + } + + Insert(record, tableName){ + records := new Collection() + records.Add(record) + return this.InsertMany(records, tableName) + } + +} diff --git a/Lib/DataBaseAbstract.ahk b/Lib/DataBaseAbstract.ahk new file mode 100644 index 0000000..edff5c9 --- /dev/null +++ b/Lib/DataBaseAbstract.ahk @@ -0,0 +1,308 @@ +; namespace DBA + +/* +##################################################################################### + Abstract Database Classes + Base for all concrete implementations for the supported DataBases. +##################################################################################### +*/ + +/* + data := Row[index] + data := Row["ColumnName"] +*/ + +class Row +{ + _columns := 0 + _fields := new Collection() + + Count(){ + return this._fields.Count() + } + + ToString(){ + return this._fields.ToString() + } + + __Get(param){ + + if(IsObject(param)){ + throw Exception("Expected Index or Column Name!", -1) + } + + if(!IsObjectMember(this, param)){ + if param is Integer + { + ; // assume that an indexed access is desired + ; // return the corresponding ROW + if(this.ContainsIndex(param)) + return this._fields[param] + } else { + ; // assume that an columnname access is desired + ; // find index + + index := 0 + for i, col in this._columns + { + if(col = param){ + index := i + break + } + } + if(this.ContainsIndex(index)){ + return this._fields[index] + } + } + } + } + + ContainsIndex(index){ + return ((index > 0) && (index <= this._fields.Count())) + } + + /* + Creates a New Row. + columns : Collection of the Columnames + fields: Collection of the Fields (Data) + */ + __New(columns, fields){ + + if(!is(columns, "Collection")){ + throw Exception("columns must be a Collection Object",-1) + } + + if(!is(fields, "Collection")){ + throw Exception("fields must be a Collection Object",-1) + } + + + this._fields := fields + this._columns := columns + } + + __NewEnum() { + return new DBA.Row.Enumerator(this) + } + + class Enumerator { + __new(row) { + this.columnEnum := ObjNewEnum(row.columns) + this.fieldEnum := ObjNewEnum(row.fields) + } + + next(ByRef key, ByRef val) { + return this.columnEnum.next("", key) + && this.fieldEnum.next("",val) + } + } +} + +/* + row := table[index] +*/ + +class Table +{ + Rows := new Collection() + Columns := new Collection() + + Count(){ + return this.Rows.Count() + } + + ToString(){ + colstr := this.Columns.ToString() + StringReplace, colstr, colstr, `n, | + return "(" this.Rows.Count() ")" . colstr + } + + __Get(param){ + + if(IsObject(param)){ + throw Exception("Expected non-Object Index!",-1) + } + if(!IsObjectMember(this, param)){ + if param is Integer + { + ; // assume that an indexed access is desired + ; // return the corresponding ROW + if((param > 0) && (param < this.Rows.Count()) ) + return this.Rows[param] + } + } + } + + /* + Creates a New Table. + rows: Collection of the Rows (Data) + columns : Collection of the Columnames + */ + __New(rows, columns){ + + if(!is(rows, "Collection")){ + throw Exception("rows must be a Collection Object",-1) + } + + if(!is(columns, "Collection")){ + throw Exception("rows must be a Collection Object",-1) + } + + this.Rows := rows + this.Columns := columns + } + + __NewEnum() { + return ObjNewEnum(this.rows) + } +} + +class DataBase +{ + static NULL := Object() + static TRUE := Object() + static FALSE := Object() + + __delete() { + this.Close() + } + + IsValid(){ + throw Exceptions.MustOverride() + } + + Query(sql){ + throw Exceptions.MustOverride() + } + + QueryValue(sQry){ + rs := this.OpenRecordSet(sQry) + value := rs[1] + rs.Close() + return value + } + + QueryRow(sQry){ + rs := this.OpenRecordSet(sQry) + myrow := rs.getCurrentRow() + rs.Close() + return myrow + } + + OpenRecordSet(sql, editable = false){ + throw Exceptions.MustOverride() + } + + ToSqlLiteral(value) { + if (IsObject(value)) { + if (value == DBA.DataBase.NULL) + return "NULL" + if (value == DBA.DataBase.TRUE) + return "TRUE" + if (value == DBA.DataBase.FALSE) + return "FALSE" + } + return "'" this.EscapeString(value) "'" + } + + EscapeString(string){ + throw Exceptions.MustOverride() + } + + QuoteIdentifier(identifier){ + throw Exceptions.MustOverride() + } + + BeginTransaction(){ + throw Exceptions.MustOverride() + } + + EndTransaction(){ + throw Exceptions.MustOverride() + } + + Rollback(){ + throw Exceptions.MustOverride() + } + + Insert(record, tableName){ + throw Exceptions.MustOverride() + } + + InsertMany(records, tableName){ + throw Exceptions.MustOverride() + } + + Update(fields, constraints, tableName, safe = True){ + throw Exceptions.MustOverride() + } + + Close(){ + throw Exceptions.MustOverride() + } +} + +class RecordSet +{ + _currentRow := 0 ; Row + + __delete() { + this.Close() + } + + AddNew(){ + throw Exceptions.MustOverride() + } + + MoveNext(){ + throw Exceptions.MustOverride() + } + + Delete(){ + throw Exceptions.MustOverride() + } + + Update(){ + throw Exceptions.MustOverride() + } + + Close(){ + throw Exceptions.MustOverride() + } + + getEOF(){ + throw Exceptions.MustOverride() + } + + IsValid(){ + throw Exceptions.MustOverride() + } + + getColumnNames(){ + throw Exceptions.MustOverride() + } + + getCurrentRow(){ + return this._currentRow + } + + __Get(param){ + + if(IsObject(param)){ + throw Exception("Expected Index or Column Name!",-1) + } + + if(param = "EOF") + return this.getEOF() + + + if(!IsObjectMember(this, param) && param != "_currentRow"){ + + if(!is(this._currentRow, DBA.Row)) + return "" + + ;// assume memberaccess are the column names/indexes + return this._currentRow[param] + } + } +} \ No newline at end of file diff --git a/Lib/DataBaseFactory.ahk b/Lib/DataBaseFactory.ahk new file mode 100644 index 0000000..08d9752 --- /dev/null +++ b/Lib/DataBaseFactory.ahk @@ -0,0 +1,36 @@ +class DataBaseFactory +{ + static AvaiableTypes := ["SQLite", "MySQL", "ADO"] + + /* + This static Method returns an Instance of an DataBase derived Object + */ + OpenDataBase(dbType, connectionString){ + if(dbType = "SQLite") + { + OutputDebug, Open Database of known type [%dbType%] + SQLite_Startup() + ;//parse connection string. for now assume its a path to the requested DB + handle := SQLite_OpenDB(connectionString) + + if(handle == 0) + throw Exception("SQLite: The connection to the the given Datebase could not be etablished. Is the following SQLite connection string valid?`n`n" connectionString,-1) + return new DBA.DataBaseSQLLite(handle) + + } if(dbType = "MySQL") { + OutputDebug, Open Database of known type [%dbType%] + MySQL_StartUp() + conData := MySQL_CreateConnectionData(connectionString) + return new DBA.DataBaseMySQL(conData) + } if(dbType = "ADO") { + OutputDebug, Open Database of known type [%dbType%] + return new DBA.DataBaseADO(connectionString) + } else { + throw Exception("The given Database Type is unknown! [" . dbType "]",-1) + } + } + + __New(){ + throw Exception("This is a static class, dont instante it!",-1) + } +} \ No newline at end of file diff --git a/Lib/DataBaseMySQL.ahk b/Lib/DataBaseMySQL.ahk new file mode 100644 index 0000000..6c03320 --- /dev/null +++ b/Lib/DataBaseMySQL.ahk @@ -0,0 +1,249 @@ +;namespace DBA + +/* + Represents a Connection to a SQLite Database +*/ +class DataBaseMySQL extends DBA.DataBase +{ + _handleDB := 0 + _connectionData := [] + + __New(connectionData){ + if(!IsObject(connectionData)) + throw Exception("Expected connectionData Array!") + this._connectionData := connectionData + + this.Connect() + } + + /* + (Re) Connects to the db with the given creditals + */ + Connect(){ + connectionData := this._connectionData + + if(!connectionData.Port){ + dbHandle := MySQL_Connect(connectionData.Server, connectionData.Uid, connectionData.Pwd, connectionData.Database) + } else { + dbHandle := MySQL_Connect(connectionData.Server, connectionData.Uid, connectionData.Pwd, connectionData.Database, connectionData.Port) + } + this._handleDB := dbHandle + } + + Close(){ + /* + ToDo! + */ + } + + IsValid(){ + return (this._handleDB != 0) + } + + GetLastError(){ + return MySQL_GetLastErrorNo(this._handleDB) + } + + GetLastErrorMsg(){ + return MySQL_GetLastErrorMsg(this._handleDB) + } + + SetTimeout(timeout = 1000){ + /* + todo + */ + } + + + ErrMsg() { + return DllCall("libmySQL.dll\mysql_error", "UInt", this._handleDB, "AStr") + } + + ErrCode() { + return DllCall("libmySQL.dll\mysql_errno", "UInt", this._handleDB) ; "Cdecl UInt" + } + + Changes() { + /* + ToDo + */ + } + + + /* + Querys the DB and returns a RecordSet + */ + OpenRecordSet(sql, editable = false){ + + result := MySQL_Query(this._handleDB, sql) + + if (result != 0) { + errCode := this.ErrCode() + if(errCode == 2003 || errCode == 2006 || errCode == 0){ ;// we've lost the connection + ;// try reconnect + this.Connect() + result := MySQL_Query(this._handleDB, sql) + if (result != 0) + throw new Exception(BuildMySQLErrorStr(this._handleDB, "Query failed because of lost connection. Reconnect failed too." errCode, sql), -1) + } else { + throw new Exception(BuildMySQLErrorStr(this._handleDB, "Query Failed Error " errCode, sql), -1) + } + } + + requestResult := MySQL_Use_Result(this._handleDB) + if(!requestResult) + return false + + return new DBA.RecordSetMySQL(this._handleDB, requestResult) + } + + /* + Querys the DB and returns a ResultTable or true/false + */ + Query(sql){ + return this._GetTableObj(sql) + } + + EscapeString(str){ + return Mysql_escape_string(str) + } + + QuoteIdentifier(identifier) { + ; ` characters are actually valid. Technically everthing but a literal null U+0000. + ; Everything else is fair game: http://dev.mysql.com/doc/refman/5.0/en/identifiers.html + StringReplace, identifier, identifier, ``, ````, All + return "``" identifier "``" + } + + BeginTransaction(){ + this.Query("START TRANSACTION;") + } + + EndTransaction(){ + this.Query("COMMIT;") + } + + Rollback(){ + this.Query("ROLLBACK;") + } + + InsertMany(records, tableName){ + + if(!is(records, Collection) || records.IsEmpty()) + return false + + sql := "INSERT INTO " tableName "`n" + colString := "" + + for column, value in records.First() + { + colstring .= this.QuoteIdentifier(column) "," + } + StringTrimRight, colstring, colstring, 1 + sql .= "(" colstring ")`nVALUES`n" + + for each, record in records + { + valString := "" + for column, value in record + { + valString .= this.ToSqlLiteral(value) "," + } + StringTrimRight, valString, valString, 1 + sql .= "(" valString "),`n" + } + StringTrimRight, colstring, colstring, 1 + sql := Trim(sql," `t`r`n,") ";" + + ;FileAppend,`n---------`n%sql%`n, dba_sql.log + return this.Query(sql) + } + + + Insert(record, tableName){ + records := new Collection() + records.Add(record) + return this.InsertMany(records, tableName) + } + + Update(fields, constraints, tableName, safe = True) { + if (safe) ;limitation: information_schema doesn't work with temp tables + for k, row in this.Query("SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE COLUMN_KEY = 'PRI' AND TABLE_NAME = " this.ToSqlLiteral(tableName)).Rows + if (!constraints.HasKey(row[1])) + return -1 ; error handling.... + + WHERE := "" + for col, val in constraints + WHERE .= ", " this.QuoteIdentifier(col) " = " this.ToSqlLiteral(val) + WHERE := SubStr(WHERE, 3) + + SET := "" + for col, val in fields + SET .= "AND " this.QuoteIdentifier(col) " = " this.EscapeString(val) + SET := SubStr(SET, 5) + + query := "UPDATE " this.QuoteIdentifier(tableName) " SET " SET " WHERE " WHERE + return db.Query(query) + } + + _GetTableObj(sql, maxResult = -1) { + + result := MySQL_Query(this._handleDB, sql) + + /* + * Instant reconnect attempt + */ + if (result != 0) { + errCode := this.ErrCode() + if(errCode == 2003 || errCode == 2006 || errCode == 0){ ;// we've lost the connection + ;// try reconnect + this.Connect() + result := MySQL_Query(this._handleDB, sql) + if (result != 0) + throw new Exception(BuildMySQLErrorStr(this._handleDB, "Query failed because of lost connection. Reconnect failed too." errCode, sql), -1) + } else { + throw new Exception(BuildMySQLErrorStr(this._handleDB, "Query Failed Error " errCode, sql), -1) + } + } + + + requestResult := MySql_Store_Result(this._handleDB) + + if (!requestResult) ; the query was a non {SELECT, SHOW, DESCRIBE, EXPLAIN or CHECK TABLE} statement which doesn't yield any resultset + return + + mysqlFields := MySQL_fetch_fields(requestResult) + colNames := new Collection() + columnCount := 0 + for each, mysqlField in mysqlFields + { + colNames.Add(mysqlField.Name()) + columnCount++ + } + + rowptr := 0 + myRows := new Collection() + while((rowptr := MySQL_fetch_row(requestResult))) + { + rowIndex := A_Index + datafields := new Collection() + + lengths := MySQL_fetch_lengths(requestResult) + Loop, % columnCount + { + length := GetUIntAtAddress(lengths, A_Index - 1) + fieldPointer := GetPtrAtAddress(rowptr, A_Index - 1) + if (fieldPointer != 0) ; "NULL values in the row are indicated by NULL pointers." See http://dev.mysql.com/doc/refman/5.0/en/mysql-fetch-row.html + fieldValue := StrGet(fieldPointer, length, "CP0") + else + fieldValue := "" ; Should use DBA.DataBase.NULL from database-types branch? + datafields.Add(fieldValue) + } + myRows.Add(new DBA.Row(colNames, datafields)) + } + MySQL_free_result(requestResult) + + tbl := new DBA.Table(myRows, colNames) + return tbl + } +} diff --git a/Lib/DataBaseSQLLite.ahk b/Lib/DataBaseSQLLite.ahk new file mode 100644 index 0000000..5c72a38 --- /dev/null +++ b/Lib/DataBaseSQLLite.ahk @@ -0,0 +1,310 @@ + +; namespace DBA + +class SQLite +{ + GetVersion(){ + return SQLite_LibVersion() + } + + SQLiteExe(dbFile, commands, ByRef output){ + return SQLite_SQLiteExe(dbFile, commands, output) + } + + __New(){ + throw Exception("This is a static Class. Don't create Instances from it!",-1) + } +} + +/* + Represents a Connection to a SQLite Database +*/ +class DataBaseSQLLite extends DBA.DataBase +{ + _handleDB := 0 + + __New(handleDB){ + this._handleDB := handleDB + if(!this.IsValid()) + { + throw Exception("Can not create a DataBaseSQLLite instance, because the connection handle is not valid!") + } + } + + + Close(){ + return SQLite_CloseDB(this._handleDB) + } + + IsValid(){ + return (this._handleDB != 0) + } + + GetLastError(){ + code := 0 + SQLite_ErrCode(this._handleDB, code) + return code + } + + GetLastErrorMsg(){ + msg := "" + SQLite_ErrMsg(this._handleDB, msg) + return msg + } + + SetTimeout(timeout = 1000){ + return SQLite_SetTimeout(this._handleDB, timeout) + } + + + ErrMsg() { + if (RC := DllCall("SQLite3\sqlite3_errmsg", "UInt", this._handleDB, "Cdecl UInt")) + return StrGet(RC, "UTF-8") + return "" + } + + ErrCode() { + return DllCall("SQLite3\sqlite3_errcode", "UInt", this._handleDB, "Cdecl UInt") + } + + Changes() { + return DllCall("SQLite3\sqlite3_changes", "UInt", this._handleDB, "Cdecl UInt") + } + + + /* + Querys the DB and returns a RecordSet + */ + OpenRecordSet(sql, editable = false){ + return new DBA.RecordSetSqlLite(this, SQlite_Query(this._handleDB, sql)) + } + + /* + Querys the DB and returns a ResultTable or true/false + */ + Query(sql){ + + ret := null + + if (RegExMatch(sql, "i)^\s*SELECT\s")){ ; check if this is a selection query + + try + { + ret := this._GetTableObj(sql) + } catch e + throw Exception("Select Query failed.`n`n" sql "`n`nChild Exception:`n" e.What "`n" e.Message "`n" e.File "@" e.Line, -1) + } else { + + try + { + ret := SQLite_Exec(this._handleDB, sql) + } catch e + throw Exception("Non Selection Query failed.`n`n" sql "`n`nChild Exception:`n" e.What " `n" e.Message, -1) + } + + return ret + } + + EscapeString(str){ + StringReplace, str, str, ', '', All ; replace all single quotes with double single-quotes. pascal escape' + return str + } + + QuoteIdentifier(identifier) { + ; ` characters are actually valid. Technically everthing but a literal null U+0000. + ; Everything else is fair game: http://dev.mysql.com/doc/refman/5.0/en/identifiers.html + StringReplace, identifier, identifier, ``, ````, All + return "``" identifier "``" + } + + + BeginTransaction(){ + this.Query("BEGIN TRANSACTION;") + } + + EndTransaction(){ + this.Query("COMMIT TRANSACTION;") + } + + Rollback(){ + this.Query("ROLLBACK TRANSACTION;") + } + + InsertMany(records, tableName){ + if(!is(records, Collection) || records.IsEmpty()) + return false + + colString := "" + valString := "" + columns := {} + + for column, value in records.First() + { + colString .= "," this.QuoteIdentifier(column) + valString .= ",?" + columns[column] := A_Index + } + sql := "INSERT INTO " this.QuoteIdentifier(tableName) "`n(" SubStr(colstring, 2) ")`nVALUES`n(" SubStr(valString, 2) ")" + + types := [] + for i,row in this._GetTableObj("PRAGMA table_info(" this.QuoteIdentifier(tableName) ")").Rows + { + if columns.HasKey(row.name) + types[columns[row.name]] := row.types + } + + this.BeginTransaction() + + query := SQLite_Query(this._handleDB, sql) ;prepare the query + if ErrorLevel + msgbox % errorlevel + + try + { + for i, record in records + { + for col, val in record + { + if (!columns.HasKey(col) || !types.HasKey(columns[col])) + throw "Irregular params" + SQLite_bind(query, columns[col], val, types[columns[col]]) + } + SQLite_Step(query) + SQLite_Reset(query) + } + } + catch e + { + this.Rollback() + throw Exception("InsertMany failed.`n`nChild Exception:`n" e.What " `n" e.Message, -1) + } + SQLite_QueryFinalize(query) + this.EndTransaction() + return True + } + + Insert(record, tableName){ + col := new Collection() + col.Add(record) + return this.InsertMany(col, tableName) + } + + + + _GetTableObj(sql, maxResult = -1) { + + err := 0, rc := 0, GetRows := 0 + i := 0 + rows := cols := 0 + names := new Collection() + dbh := this._handleDB + + SQLite_LastError(" ") + + if(!_SQLite_CheckDB(dbh)) { + SQLite_LastError("ERROR: Invalid database handle " . dbh) + ErrorLevel := _SQLite_ReturnCode("SQLITE_ERROR") + return False + } + if maxResult Is Not Integer + maxResult := -1 + if (maxResult < -1) + maxResult := -1 + mytable := "" + Err := 0 + + _SQLite_StrToUTF8(SQL, UTF8) + RC := DllCall("SQlite3\sqlite3_get_table", "Ptr", dbh, "Ptr", &UTF8, "Ptr*", mytable + , "Ptr*", rows, "Ptr*", cols, "Ptr*", err, "Cdecl Int") + If (ErrorLevel) { + SQLite_LastError("ERROR: DLLCall sqlite3_get_table failed!") + Return False + } + If (rc) { + SQLite_LastError(StrGet(err, "UTF-8")) + DllCall("SQLite3\sqlite3_free", "Ptr", err, "cdecl") + ErrorLevel := rc + return false + } + + + + if (maxResult = 0) { + DllCall("SQLite3\sqlite3_free_table", "Ptr", mytable, "Cdecl") + If (ErrorLevel) { + SQLite_LastError("ERROR: DLLCall sqlite3_close failed!") + Return False + } + Return True + } + + if (maxResult = 1) + GetRows := 0 + else if (maxResult > 1) && (maxResult < rows) + GetRows := MaxResult + else + GetRows := rows + Offset := 0 + + Loop, % cols + { + names.Add(StrGet(NumGet(mytable+0, Offset), "UTF-8")) + Offset += A_PtrSize + } + + myRows := new Collection() + Loop, %GetRows% { + i := A_Index + fields := new Collection() + Loop, % Cols + { + fields.Add(StrGet(NumGet(mytable+0, Offset), "UTF-8")) + Offset += A_PtrSize + } + myRows.Add(new DBA.Row(Names, fields)) + } + tbl := new DBA.Table(myRows, Names) + + ; Free Results Memory + DllCall("SQLite3\sqlite3_free_table", "Ptr", mytable, "Cdecl") + if (ErrorLevel) { + SQLite_LastError("ERROR: DLLCall sqlite3_close failed!") + return false + } + return tbl + } + + + ReturnCode(RC) { + static RCODE := {SQLITE_OK: 0 ; Successful result + , SQLITE_ERROR: 1 ; SQL error or missing database + , SQLITE_INTERNAL: 2 ; NOT USED. Internal logic error in SQLite + , SQLITE_PERM: 3 ; Access permission denied + , SQLITE_ABORT: 4 ; Callback routine requested an abort + , SQLITE_BUSY: 5 ; The database file is locked + , SQLITE_LOCKED: 6 ; A table in the database is locked + , SQLITE_NOMEM: 7 ; A malloc() failed + , SQLITE_READONLY: 8 ; Attempt to write a readonly database + , SQLITE_INTERRUPT: 9 ; Operation terminated by sqlite3_interrupt() + , SQLITE_IOERR: 10 ; Some kind of disk I/O error occurred + , SQLITE_CORRUPT: 11 ; The database disk image is malformed + , SQLITE_NOTFOUND: 12 ; NOT USED. Table or record not found + , SQLITE_FULL: 13 ; Insertion failed because database is full + , SQLITE_CANTOPEN: 14 ; Unable to open the database file + , SQLITE_PROTOCOL: 15 ; NOT USED. Database lock protocol error + , SQLITE_EMPTY: 16 ; Database is empty + , SQLITE_SCHEMA: 17 ; The database schema changed + , SQLITE_TOOBIG: 18 ; String or BLOB exceeds size limit + , SQLITE_CONSTRAINT: 19 ; Abort due to constraint violation + , SQLITE_MISMATCH: 20 ; Data type mismatch + , SQLITE_MISUSE: 21 ; Library used incorrectly + , SQLITE_NOLFS: 22 ; Uses OS features not supported on host + , SQLITE_AUTH: 23 ; Authorization denied + , SQLITE_FORMAT: 24 ; Auxiliary database format error + , SQLITE_RANGE: 25 ; 2nd parameter to sqlite3_bind out of range + , SQLITE_NOTADB: 26 ; File opened that is not a database file + , SQLITE_ROW: 100 ; sqlite3_step() has another row ready + , SQLITE_DONE: 101} ; sqlite3_step() has finished executing + return RCODE.HasKey(RC) ? RCODE[RC] : "" + } +} diff --git a/Lib/Notify.ahk b/Lib/Notify.ahk new file mode 100644 index 0000000..1fd25b6 --- /dev/null +++ b/Lib/Notify.ahk @@ -0,0 +1,415 @@ +;—————————————————————————————————————————————————————— +;———————— Notify() 0.4991 by gwarble ———————— +;————— ————— +;——— easy multiple tray area notifications ——— +;—— http://www.autohotkey.net/~gwarble/Notify/ —— +;—————————————————————————————————————————————————————— +; +; Notify([Title,Message,Duration,Options]) +; +; Duration seconds to show notification [Default: 30] +; 0 for permanent/remain until clicked (flashing) +; -3 negative value to ExitApp on click/timeout +; "-0" for permanent and ExitApp when clicked (needs "") +; +; Options string of options, single-space seperated, ie: +; "TS=16 TM=8 TF=Times New Roman GC_=Blue SI_=1000" +; most options are remembered (static), some not (local) +; Option_= can be used for non-static call, ie: +; "GC=Blue" makes all future blue, "GC_=Blue" only takes effect once +; "Wait=ID" to wait for a notification +; "Update=ID" to change Title, Message, and Progress Bar (with 'Duration') +; +; Return ID (Gui Number used) +; 0 if failed (too many open most likely) +; VarValue if Options includes: Return=VarName +;—————————————————————————————————————————————————————— + +Notify(Title="Notify()",Message="",Duration="",Options="") +{ + static GNList, ACList, ATList, AXList, Exit, _Wallpaper_, _Title_, _Message_, _Progress_, _Image_, Saved + static GF := 50 ; Gui First Number + static GL := 74 ; Gui Last Number (which defines range and allowed count) + static GC,GR,GT,BC,BK,BW,BR,BT,BF ; static options, remembered between calls + static TS,TW,TC,TF,MS,MW,MC,MF + static SI,SC,ST,IW,IH,IN,XC,XS,XW,PC,PB + + If (Options) ; skip parsing steps if Options param isn't used + { + If (A_AutoTrim = "Off") + { + AutoTrim, On + _AutoTrim = 1 + } ; ¶ + Options = %Options% + Options.=" " ; poor whitespace handling for next parsing step (ensures last option is parsed) + Loop,Parse,Options,= ; parse options string at "="s, needs better whitespace handling + { + If A_Index = 1 ; first option handling + Option := A_LoopField ; sets options VarName + Else ; for the rest after the first, + { ; split at the last space, apply the first chunk to the VarValue for the last Option + %Option% := SubStr(A_LoopField, 1, (pos := InStr(A_LoopField, A_Space, false, 0))-1) + %Option% = % %Option% + Option := SubStr(A_LoopField, pos+1) ; and set the next option to the last chunk (from the last space to the "=") + } + } + If _AutoTrim + AutoTrim, Off + If Wait <> ; option Wait=ID used, normal Notify window not being created + { + If Wait Is Number ; waits for a specific notify + { + Gui %Wait%:+LastFound ; i'd like to remove this to not affect calling script... + If NotifyGuiID := WinExist() ; but think i have to use hWnd's for reference instead of gui numbers which will + { ; probably happen in my AHK_L transition since gui numbers won't matter anymore + WinWaitClose, , , % Abs(Duration) ; wait to close for duration + If (ErrorLevel && Duration < 1) ; destroys window when done waiting if duration is negative + { ; otherwise lets the calling script procede after waiting the duration (without destroying) + Gui, % Wait + GL - GF + 1 ":Destroy" ; destroys border gui + If ST + DllCall("AnimateWindow","UInt",NotifyGuiID,"Int",ST,"UInt","0x00050001") ; slides window out to the right if ST or SC are used + Gui, %Wait%:Destroy ; and destroys it + } + } + } + Else ; wait for all notify's if "Wait=All" is used in the options string + { ; loops through all existing notify's and performs the same wait logic + Loop, % GL-GF ; (with or without destroying if negative or not) + { + Wait := A_Index + GF - 1 + Gui %Wait%:+LastFound + If NotifyGuiID := WinExist() + { + WinWaitClose, , , % Abs(Duration) + If (ErrorLevel && Duration < 1) + { + Gui, % Wait + GL - GF + 1 ":Destroy" ; destroys border gui + If ST + DllCall("AnimateWindow","UInt",NotifyGuiID,"Int",ST,"UInt","0x00050001") ; slides window out to the right if ST or SC are used + Gui, %Wait%:Destroy ; and destroys it + } + } + } + GNList := ACList := ATList := AXList := "" ; clears internal variables since they're all destroyed now + } + Return + } + If Update <> ; option "Update=ID" being used, Notify window will not be created + { ; title, message, image and progress position can be updated + If Title <> + GuiControl, %Update%:,_Title_,%Title% + If Message <> + GuiControl, %Update%:,_Message_,%Message% + If Duration <> + GuiControl, %Update%:,_Progress_,%Duration% + If Image <> + GuiControl, %Update%:,_Image_,%Image% + If Wallpaper <> + GuiControl, %Update%:,_Wallpaper_,%Image% + Return + } + If Style = Save ; option "Style=Save" is used to save the existing window style + { ; and call it back later with "Style=Load" + Saved := Options " GC=" GC " GR=" GR " GT=" GT " BC=" BC " BK=" BK " BW=" BW " BR=" BR " BT=" BT " BF=" BF + Saved .= " TS=" TS " TW=" TW " TC=" TC " TF=" TF " MS=" MS " MW=" MW " MC=" MC " MF=" MF + Saved .= " IW=" IW " IH=" IH " IN=" IN " PW=" PW " PH=" PH " PC=" PC " PB=" PB " XC=" XC " XS=" MS " XW=" XW + Saved .= " SI=" SI " SC=" SC " ST=" ST " WF=" Image " IF=" IF + } ; this needs some major improvement to have multiple saved instead of just one, otherwise pointless + If Return <> + Return, % (%Return%) + If Style <> ; option "Style=Default will reset all variables back to defaults... except options also specified + { ; so "Style=Default GC=Blue" is allowed, which will reset all defaults and then set GC=Blue + If Style = Default + Return % Notify(Title,Message,Duration, ; maybe handled poorly by calling itself, but it saves having to have the defaults set in two areas... thoughts? +( +"GC= GR= GT= BC= BK= BW= BR= BT= BF= TS= TW= TC= TF= + MS= MW= MC= MF= SI= ST= SC= IW= + IH= IN= XC= XS= XW= PC= PB= " Options "Style=") +) ; below are more internally saved styles, which may move to an auxiliary function at some point, but could use some improvement + Else If Style = ToolTip + Return % Notify(Title,Message,Duration,"SI=50 GC=FFFFAA BC=00000 GR=0 BR=0 BW=1 BT=255 TS=8 MS=8 " Options "Style=") + Else If Style = BalloonTip + Return % Notify(Title,Message,Duration,"SI=350 GC=FFFFAA BC=00000 GR=13 BR=15 BW=1 BT=255 TS=10 MS=8 AX=1 XC=999922 IN=8 Image=" A_WinDir "\explorer.exe " Options "Style=") + Else If Style = Error + Return % Notify(Title,Message,Duration,"SI=250 GC=Default BC=00000 GR=0 BR=0 BW=1 BT=255 TS=12 MS=12 AX=1 XC=666666 IN=10 IW=32 IH=32 Image=" A_WinDir "\explorer.exe " Options "Style=") + Else If Style = Warning + Return % Notify(Title,Message,Duration,"SI=250 GC=Default BC=00000 GR=0 BR=0 BW=1 BT=255 TS=12 MS=12 AX=1 XC=666666 IN=9 IW=32 IH=32 Image=" A_WinDir "\explorer.exe " Options "Style=") + Else If Style = Info + Return % Notify(Title,Message,Duration,"SI=250 GC=Default BC=00000 GR=0 BR=0 BW=1 BT=255 TS=12 MS=12 AX=1 XC=666666 IN=8 IW=32 IH=32 Image=" A_WinDir "\explorer.exe " Options "Style=") + Else If Style = Question + Return % Notify(Title,Message,Duration,"SI=250 GC=Default BC=00000 GR=0 BR=0 BW=1 BT=255 TS=12 MS=12 AX=1 XC=666666 Image=24 IW=32 IH=32 " Options "Style=") + Else If Style = Progress + Return % Notify(Title,Message,Duration,"SI=100 GC=Default BC=00000 GR=9 BR=13 BW=2 BT=105 TS=10 MS=10 PG=100 PH=10 GW=300 " Options "Style=") + Else If Style = Huge + Return % Notify(Title,Message,Duration,"SI=100 ST=200 SC=200 GC=FFFFAA BC=00000 GR=27 BR=39 BW=6 BT=105 TS=24 MS=22 " Options "Style=") + Else If Style = Load + Return % Notify(Title,Message,Duration,Saved) + } + } +;—————— end if options ———————————————————————————————————————————————————————————————————————————— + + GC_ := GC_<>"" ? GC_ : GC := GC<>"" ? GC : "FFFFAA" ; defaults are set here, and static overrides are used and saved + GR_ := GR_<>"" ? GR_ : GR := GR<>"" ? GR : 9 ; and non static options (with OP_=) are used but not saved + GT_ := GT_<>"" ? GT_ : GT := GT<>"" ? GT : "Off" + BC_ := BC_<>"" ? BC_ : BC := BC<>"" ? BC : "000000" + BK_ := BK_<>"" ? BK_ : BK := BK<>"" ? BK : "Silver" + BW_ := BW_<>"" ? BW_ : BW := BW<>"" ? BW : 2 + BR_ := BR_<>"" ? BR_ : BR := BR<>"" ? BR : 13 + BT_ := BT_<>"" ? BT_ : BT := BT<>"" ? BT : 105 + BF_ := BF_<>"" ? BF_ : BF := BF<>"" ? BF : 350 + TS_ := TS_<>"" ? TS_ : TS := TS<>"" ? TS : 10 + TW_ := TW_<>"" ? TW_ : TW := TW<>"" ? TW : 625 + TC_ := TC_<>"" ? TC_ : TC := TC<>"" ? TC : "Default" + TF_ := TF_<>"" ? TF_ : TF := TF<>"" ? TF : "Default" + MS_ := MS_<>"" ? MS_ : MS := MS<>"" ? MS : 10 + MW_ := MW_<>"" ? MW_ : MW := MW<>"" ? MW : "Default" + MC_ := MC_<>"" ? MC_ : MC := MC<>"" ? MC : "Default" + MF_ := MF_<>"" ? MF_ : MF := MF<>"" ? MF : "Default" + SI_ := SI_<>"" ? SI_ : SI := SI<>"" ? SI : 0 + SC_ := SC_<>"" ? SC_ : SC := SC<>"" ? SC : 0 + ST_ := ST_<>"" ? ST_ : ST := ST<>"" ? ST : 0 + IW_ := IW_<>"" ? IW_ : IW := IW<>"" ? IW : 32 + IH_ := IH_<>"" ? IH_ : IH := IH<>"" ? IH : 32 + IN_ := IN_<>"" ? IN_ : IN := IN<>"" ? IN : 0 + XF_ := XF_<>"" ? XF_ : XF := XF<>"" ? XF : "Arial Black" + XC_ := XC_<>"" ? XC_ : XC := XC<>"" ? XC : "Default" + XS_ := XS_<>"" ? XS_ : XS := XS<>"" ? XS : 12 + XW_ := XW_<>"" ? XW_ : XW := XW<>"" ? XW : 800 + PC_ := PC_<>"" ? PC_ : PC := PC<>"" ? PC : "Default" + PB_ := PB_<>"" ? PB_ : PB := PB<>"" ? PB : "Default" + + wPW := ((PW<>"") ? ("w" PW) : ("")) ; needs improvement, poor handling of explicit sizes and progress widths + hPH := ((PH<>"") ? ("h" PH) : ("")) + If GW <> + { + wGW = w%GW% + wPW := "w" GW - 20 + } + hGH := ((GH<>"") ? ("h" GH) : ("")) + wGW_ := ((GW<>"") ? ("w" GW - 20) : ("")) + hGH_ := ((GH<>"") ? ("h" GH - 20) : ("")) +;———————————————————————————————————————————————————————————————————————— + If Duration = ; default if duration is not used or set to "" + Duration = 30 + GN := GF ; find the next available gui number to use, starting from GF (default 50) + Loop ; within the defined range GF to GL + IfNotInString, GNList, % "|" GN + Break + Else + If (++GN > GL) ;=== too many notifications open, returns 0, handle this error in the calling script + Return 0 ; this is uncommon as the screen is too cluttered by this point anyway + GNList .= "|" GN + GN2 := GN + GL - GF + 1 + + If AC <> ; saves the action to be used when clicked or timeout (or x-button is clicked) + ACList .= "|" GN "=" AC ; need to add different clicks for Title, Message, Image as well + If AT <> ; saved internally in a list, then parsed by the timer or click routine + ATList .= "|" GN "=" AT ; to run the script-side subroutine/label "AC=LabelName" + If AX <> + AXList .= "|" GN "=" AX + + + P_DHW := A_DetectHiddenWindows ; start finding location based on what other Notify() windows are on the screen + P_TMM := A_TitleMatchMode ; saved to restore these settings after changing them, so the calling script won't know + DetectHiddenWindows On ; as they are needed to find all as they are being made as well... or hidden for some reason... + SetTitleMatchMode 1 ; and specific window title match is a little more failsafe + If (WinExist("_Notify()_GUI_")) ;=== find all Notifications from ALL scripts, for placement + WinGetPos, OtherX, OtherY ;=== change this to a loop for all open notifications and find the highest? + DetectHiddenWindows %P_DHW% ;=== using the last Notify() made at this point, which may be better + SetTitleMatchMode %P_TMM% ; and the global settings are restored for the calling thread + + Gui, %GN%:-Caption +ToolWindow +AlwaysOnTop -Border ; here begins the creation of the window + Gui, %GN%:Color, %GC_% ; with the logic to add or not add certain controls, Wallpaper, Image, Title, Progress, Message + If FileExist(WP) ; and some placement logic depending if they are used or not... could definitely be improved + { + Gui, %GN%:Add, Picture, x0 y0 w0 h0 v_Wallpaper_, % WP ; wallpaper added first, stretched to size later + ImageOptions = x+8 y+4 + } + If Image <> ; icon image added next, sized, and spacing added for whats next + { + If FileExist(Image) + Gui, %GN%:Add, Picture, w%IW_% h%IH_% Icon%IN_% v_Image_ %ImageOptions%, % Image + Else + Gui, %GN%:Add, Picture, w%IW_% h%IH_% Icon%Image% v_Image_ %ImageOptions%, %A_WinDir%\system32\shell32.dll + ImageOptions = x+10 + } + If Title <> ; title text control added next, if used + { + Gui, %GN%:Font, w%TW_% s%TS_% c%TC_%, %TF_% + Gui, %GN%:Add, Text, %ImageOptions% BackgroundTrans v_Title_, % Title + } + If PG ; then the progress bar, if called for + Gui, %GN%:Add, Progress, Range0-%PG% %wPW% %hPH% c%PC_% Background%PB_% v_Progress_ + Else + If ((Title) && (Message)) ; some spacing tweaks if both used + Gui, %GN%:Margin, , -5 + If Message <> ; and finally the message text control if used + { + Gui, %GN%:Font, w%MW_% s%MS_% c%MC_%, %MF_% + Gui, %GN%:Add, Text, BackgroundTrans v_Message_, % Message + } + If ((Title) && (Message)) ; final spacing + Gui, %GN%:Margin, , 8 + Gui, %GN%:Show, Hide %wGW% %hGH%, _Notify()_GUI_ ; final sizing + Gui %GN%:+LastFound ; would like to get rid of this to prevent calling script being affected + WinGetPos, GX, GY, GW, GH ; final positioning + GuiControl, %GN%:, _Wallpaper_, % "*w" GW " *h" GH " " WP ; stretch that wallpaper to size + GuiControl, %GN%:MoveDraw, _Title_, % "w" GW-20 " h" GH-10 ; poor handling of text wrapping when gui has explicit size called + GuiControl, %GN%:MoveDraw, _Message_, % "w" GW-20 " h" GH-10 ; needs improvement (and if image is used or not) + If AX <> ; add the corner "X" for closing with a different action than otherwise clicked + { + GW += 10 + Gui, %GN%:Font, w%XW_% s%XS_% c%XC_%, Arial Black ; × (multiply) is the character used for the X-Button + Gui, %GN%:Add, Text, % "x" GW-15 " y-2 Center w12 h20 g_Notify_Kill_" GN - GF + 1, % chr(0x00D7) ;×× + } + Gui, %GN%:Add, Text, x0 y0 w%GW% h%GH% BackgroundTrans g_Notify_Action_Clicked_ ; to catch clicks anywhere on the gui + If (GR_) ; may have to be removed for seperate title/message/etc actions + WinSet, Region, % "0-0 w" GW " h" GH " R" GR_ "-" GR_ + If (GT_) ; non-functioning GT option, since the border gui gets in the way + WinSet, Transparent, % GT_ ; will be addressed someday, leaving it in + + SysGet, Workspace, MonitorWorkArea ; positioning + NewX := WorkSpaceRight-GW-5 + If (OtherY) + NewY := OtherY-GH-2-BW_*2 + Else + NewY := WorkspaceBottom-GH-5 + If NewY < % WorkspaceTop + NewY := WorkspaceBottom-GH-5 + + Gui, %GN2%:-Caption +ToolWindow +AlwaysOnTop -Border +E0x20 ; border gui + Gui, %GN2%:Color, %BC_% + Gui %GN2%:+LastFound + If (BR_) + WinSet, Region, % "0-0 w" GW+(BW_*2) " h" GH+(BW_*2) " R" BR_ "-" BR_ + If (BT_) + WinSet, Transparent, % BT_ + + Gui, %GN2%:Show, % "Hide x" NewX-BW_ " y" NewY-BW_ " w" GW+(BW_*2) " h" GH+(BW_*2), _Notify()_BGGUI_ ; actual creation of border gui! but still not shown + Gui, %GN%:Show, % "Hide x" NewX " y" NewY " w" GW, _Notify()_GUI_ ; actual creation of Notify() gui! but still not shown + Gui %GN%:+LastFound ; need to get rid of this so calling script isn't affected + If SI_ + DllCall("AnimateWindow","UInt",WinExist(),"Int",SI_,"UInt","0x00040008") ; animated in, if SI is used + Else + Gui, %GN%:Show, NA, _Notify()_GUI_ ; otherwise, just shown + Gui, %GN2%:Show, NA, _Notify()_BGGUI_ ; and the border shown + WinSet, AlwaysOnTop, On ; and set to Always on Top + + If ((Duration < 0) OR (Duration = "-0")) ; saves internally that ExitApp should happen when this + Exit := GN ; notify dissappears + If (Duration) + SetTimer, % "_Notify_Kill_" GN - GF + 1, % - Abs(Duration) * 1000 ; timer set depending on Duration parameter + Else + SetTimer, % "_Notify_Flash_" GN - GF + 1, % BF_ ; timer set to flash border if the Notify has 0 (infinite) duration + +Return %GN% ; end of Notify(), returns Gui ID number used + +;========================================================================== +;========================================== when a notification is clicked: +_Notify_Action_Clicked_: ; option AC=Label means Label: subroutine will be called here when clicked + ; Critical + SetTimer, % "_Notify_Kill_" A_Gui - GF + 1, Off + Gui, % A_Gui + GL - GF + 1 ":Destroy" + If SC + { + Gui, %A_Gui%:+LastFound + DllCall("AnimateWindow","UInt",WinExist(),"Int",SC,"UInt", "0x00050001") + } + Gui, %A_Gui%:Destroy + If (ACList) + Loop,Parse,ACList,| + If ((Action := SubStr(A_LoopField,1,2)) = A_Gui) + { + Temp_Notify_Action:= SubStr(A_LoopField,4) + StringReplace, ACList, ACList, % "|" A_Gui "=" Temp_Notify_Action, , All + If IsLabel(_Notify_Action := Temp_Notify_Action) + Gosub, %_Notify_Action% + _Notify_Action = + Break + } + StringReplace, GNList, GNList, % "|" A_Gui, , All + SetTimer, % "_Notify_Flash_" A_Gui - GF + 1, Off + If (Exit = A_Gui) + ExitApp +Return + +;========================================================================== +;=========================================== when a notification times out: +_Notify_Kill_1: +_Notify_Kill_2: ; this needs a different method, too many labels +_Notify_Kill_3: ; they are used for Timers, different for each Notify() based on duration... +_Notify_Kill_4: +_Notify_Kill_5: +_Notify_Kill_6: +_Notify_Kill_7: +_Notify_Kill_8: +_Notify_Kill_9: +_Notify_Kill_10: +_Notify_Kill_11: +_Notify_Kill_12: +_Notify_Kill_13: +_Notify_Kill_14: +_Notify_Kill_15: +_Notify_Kill_16: +_Notify_Kill_17: +_Notify_Kill_18: +_Notify_Kill_19: +_Notify_Kill_20: +_Notify_Kill_21: +_Notify_Kill_22: +_Notify_Kill_23: +_Notify_Kill_24: +_Notify_Kill_25: + Critical + StringReplace, GK, A_ThisLabel, _Notify_Kill_ + SetTimer, _Notify_Flash_%GK%, Off + GK := GK + GF - 1 + Gui, % GK + GL - GF + 1 ":Destroy" + If ST + { + Gui, %GK%:+LastFound + DllCall("AnimateWindow","UInt",WinExist(),"Int",ST,"UInt", "0x00050001") + } + Gui, %GK%:Destroy + StringReplace, GNList, GNList, % "|" GK, , All + If (Exit = GK) + ExitApp +Return 1 + +;========================================================================== +;======================================== flashes a permanent notification: +_Notify_Flash_1: +_Notify_Flash_2: +_Notify_Flash_3: +_Notify_Flash_4: ; this needs a different method, too many labels +_Notify_Flash_5: ; they are used for Timers, different for each Notify() based on flash speed... +_Notify_Flash_6: ; when duration is 0 (infinite) +_Notify_Flash_7: ; this may feature may be removed completely, Update given the ability to affect GC and BC +_Notify_Flash_8: ; and then the flashing could be handled script-side via returned gui number and a script-side timer +_Notify_Flash_9: +_Notify_Flash_10: +_Notify_Flash_11: +_Notify_Flash_12: +_Notify_Flash_13: +_Notify_Flash_14: +_Notify_Flash_15: +_Notify_Flash_16: +_Notify_Flash_17: +_Notify_Flash_18: +_Notify_Flash_19: +_Notify_Flash_20: +_Notify_Flash_21: +_Notify_Flash_22: +_Notify_Flash_23: +_Notify_Flash_24: +_Notify_Flash_25: + StringReplace, FlashGN, A_ThisLabel, _Notify_Flash_ + FlashGN += GF - 1 + FlashGN2 := FlashGN + GL - GF + 1 + If Flashed%FlashGN2% := !Flashed%FlashGN2% + Gui, %FlashGN2%:Color, %BK% + Else + Gui, %FlashGN2%:Color, %BC% +Return +} \ No newline at end of file diff --git a/Lib/RecordSetADO.ahk b/Lib/RecordSetADO.ahk new file mode 100644 index 0000000..a150384 --- /dev/null +++ b/Lib/RecordSetADO.ahk @@ -0,0 +1,109 @@ +;namespace DBA + +/* + Represents a result set of ADO + http://www.w3schools.com/ado/ado_ref_recordset.asp +*/ +class RecordSetADO extends DBA.RecordSet +{ + _adoRS := 0 ; ado recordset + + __New(sql, adoConnection, editable = false){ + this._adoRS := ComObjCreate("ADODB.Recordset") + if(editable) + this._adoRS.Open(sql, adoConnection, ADO.CursorType.adOpenKeyset, ADO.LockType.adLockOptimistic, ADO.CommandType.adCmdTable) + else + this._adoRS.Open(sql, adoConnection) + } + + /* + Is this RecordSet valid? + */ + IsValid(){ + return (IsObject(this._adoRS)) + } + + /* + Returns an Array with all Column Names + */ + getColumnNames(){ + + colNames := new Collection() + + for adoField in this._adoRS.Fields + colNames.add(adoField.Name) + + return colNames + } + + getEOF(){ + return this._adoRS.EOF + } + + AddNew(){ + if(this.IsValid()) + { + this._adoRS.AddNew() + } + } + + MoveNext() { + if(this.IsValid()) + { + this._adoRS.MoveNext() + } + } + + Delete(){ + if(this.IsValid() && !this.getEOF()) + { + this._adoRS.Delete(ADO.AffectEnum.adAffectCurrent) + } + } + + Update(){ + if(this.IsValid() && !this.getEOF()) + { + this._adoRS.Update() + } + } + + Reset() { + if(this.IsValid()){ + this._adoRS.MoveFirst() + } + } + + Count(){ + cnt := 0 + if(this.IsValid()) + cnt := this._adoRS.RecordCount + return cnt + } + + + Close() { + if(this.IsValid()) + this._adoRS.Close() + } + + + __Get(param){ + + if(IsObject(param)){ + throw Exception("Expected Index or Column Name!",-1) + } + + if(param = "EOF") + return this.getEOF() + + if(!IsObjectMember(this, param) && param != "_currentRow"){ + if(this.IsValid()) + { + df := this._adoRS.Fields[param] + return df.Value + } + } + } +} + diff --git a/Lib/RecordSetMySQL.ahk b/Lib/RecordSetMySQL.ahk new file mode 100644 index 0000000..9a829b9 --- /dev/null +++ b/Lib/RecordSetMySQL.ahk @@ -0,0 +1,109 @@ +;namespace DBA + +/* + Represents a result set of an MySQL Query +*/ +class RecordSetMySQL extends DBA.RecordSet +{ + _colNames := 0 ; Collection + _colCount := 0 + _query := 0 ; ptr to Resultset/Query + _db := 0 ; ptr to DataBase + _eof := false ; bool + CurrentRow := 0 ; int - row number + + + __New(db, requestResult){ + this._db := db + this._query := requestResult + + if(this._query != 0){ + this._colNames := this.getColumnNames() + this.MoveNext() + } + } + + + /* + Is this RecordSet valid? + */ + IsValid(){ + return (this._query != 0) + } + + /* + Returns an Array with all Column Names + */ + getColumnNames(){ + mysqlFields := MySQL_fetch_fields(this._query) + colNames := new Collection() + i := 0 + for each, mysqlField in mysqlFields + { + colNames.Add(mysqlField.Name()) + i++ + } + this._colCount := i + return colNames + } + + getEOF(){ + return this._eof + } + + + MoveNext() { + static EOR := -1 + this.ErrorMsg := "" + this.ErrorCode := 0 + this._currentRow := 0 + + if (!this._query) { + this.ErrorMsg := "Invalid query handle!" + this._eof := true + return false + } + + rowptr := MySQL_fetch_row(this._query) + if (!rowptr){ + ; // we reached eof + this.ErrorMsg := "RecordSet is empty! (eof)" + this.ErrorCode := 1 + this._eof := true + return false + } + + lengths := MySQL_fetch_lengths(this._query) + datafields := new Collection() + Loop % this._colCount + { + length := GetUIntAtAddress(lengths, A_Index - 1) + fieldPointer := GetPtrAtAddress(rowptr, A_Index - 1) + fieldValue := StrGet(fieldPointer, length, "CP0") + datafields.Add(fieldValue) + } + this._currentRow := new DBA.Row(this._colNames, datafields) + this.CurrentRow++ + return true + } + + + + Reset() { + throw Exception("Not Supported!",-1) + } + + + Close() { + this.ErrorMsg := "" + this.ErrorCode := 0 + if(this._query == 0) + return true + + MySQL_free_result(this._query) + + this._query := 0 + return true + } +} + diff --git a/Lib/RecordSetSqlLite.ahk b/Lib/RecordSetSqlLite.ahk new file mode 100644 index 0000000..773fe3e --- /dev/null +++ b/Lib/RecordSetSqlLite.ahk @@ -0,0 +1,139 @@ +;namespace DBA + +/* + Represents a result set of an SQLite Query +*/ +class RecordSetSqlLite extends DBA.RecordSet +{ + _currentRow := 0 ; Row + _colNames := 0 ; Collection + _query := 0 ; int Handle to the Query + _db := 0 ; SQLiteDataBase + _eof := false ; bool + + /* + Is this RecordSet valid? + */ + IsValid(){ + return (this._query != 0) + } + + /* + Returns an Array with all Column Names + */ + getColumnNames(){ + SQLite_FetchNames(this._query, names) + return new Collection(names) + } + + getEOF(){ + return this._eof + } + + + MoveNext() { + static SQLITE_NULL := 5 + static EOR := -1 + + this.ErrorMsg := "" + this.ErrorCode := 0 + this._currentRow := 0 + + if (!this._query) { + this.ErrorMsg := "Invalid query handle!" + this._eof := true + return false + } + rc := DllCall("SQlite3\sqlite3_step", "UInt", this._query, "Cdecl Int") + + if (rc != this._db.ReturnCode("SQLITE_ROW")) { + if (rc = this._db.ReturnCode("SQLITE_DONE")) { + this.ErrorMsg := "EOR" + this.ErrorCode := rc + this._eof := true + return EOR + } + this.ErrorMessage := This._db.ErrMsg() + this.ErrorCode := rc + this._eof := true + return false + } + rc := DllCall("SQlite3\sqlite3_data_count", "UInt", this._query, "Cdecl Int") + + if (rc < 1) { + this.ErrorMsg := "RecordSet is empty!" + this.ErrorCode := this._db.ReturnCode("SQLITE_EMPTY") + this._eof := true + return false + } + + ; fill the internal row structure + ;_currentRow := new Row() + fields := new Collection() + Loop, %rc% { + ctype := DllCall("SQlite3\sqlite3_column_type", "UInt", this._query, "Int", A_Index - 1, "Cdecl Int") + if (ctype == SQLITE_NULL) { + fields[A_Index] := "" + } else { + strPtr := DllCall("SQlite3\sqlite3_column_text", "UInt", this._query, "Int", A_Index - 1, "Cdecl UInt") + fields[A_Index] := StrGet(strPtr, "UTF-8") + } + } + this._currentRow := new DBA.Row(this._colNames, fields) + this.CurrentRow++ + return true + } + + + + Reset() { + this.ErrorMsg := "" + this.ErrorCode := 0 + + if (!this._query) { + this.ErrorMsg := "Invalid query handle!" + return false + } + rc := DllCall("SQlite3\sqlite3_reset", "UInt", this._query, "Cdecl Int") + + if (rc) { + this.ErrorMsg := This._db.ErrMsg() + this.ErrorCode := rc + return false + } + this.CurrentRow := 0 + this.MoveNext() + return true + } + + + Close() { + this.ErrorMsg := "" + this.ErrorCode := 0 + if(this._query == 0) + return true + + rc := DllCall("SQlite3\sqlite3_finalize", "UInt", this._query, "Cdecl Int") + + if (rc) { + this.ErrorMsg := this._db.ErrMsg() + this.ErrorCode := rc + return false + } + this._query := 0 + return true + } + + __New(db, query){ + if(!is(db, DBA.DataBaseSQLLite)){ + throw Exception("db must be a DataBaseSQLLite Object",-1) + } + this._db := db + this._query := query + if(query != 0){ + this._colNames := this.getColumnNames() + this.MoveNext() + } + } +} + diff --git a/Lib/SQLite_L.ahk b/Lib/SQLite_L.ahk new file mode 100644 index 0000000..0e26857 --- /dev/null +++ b/Lib/SQLite_L.ahk @@ -0,0 +1,1044 @@ +/* +;======================================================================================================================= +; Function: Wrapper functions for the SQLite.dll to work with SQLite DBs. +; AHK version: L 1.1.00.00 (U 32) +; Language: English +; Tested on: Win XPSP3, Win VistaSP2 (32 Bit) +; Version: 1.0.00.00/2011-05-01/ich_L +; Remarks: Encoding of SQLite DBs is assumed to be UTF-8 +;======================================================================================================================= +; Many of these functions are transcripted from the AutoIt3-UDF SQLite.au3 +; THX piccaso (Fida Florian) +;======================================================================================================================= +; This software is provided 'as-is', without any express or +; implied warranty. In no event will the authors be held liable for any +; damages arising from the use of this software. +;======================================================================================================================= +; List of Functions: +;======================================================================================================================= +; - Load SQLite3.dll +; SQLite_Startup() +; - Unload SQLite3.dll +; SQLite_Shutdown() +; - Open DB connection +; SQLite_OpenDB(DBFile) +; - Close DB connection +; SQLite_CloseDB(DB) +; - Get full result for SQL query (SELECT) +; SQLite_GetTable(DB, SQL, ByRef Rows, ByRef Cols, ByRef Names, ByRef Result, MaxResult = -1) +; - Execute non query SQL statements +; SQLite_Exec(DB, SQL) +; - Prepare SQL query +; SQlite_Query(DB, SQL) +; - Get column names from prepared query +; SQLite_FetchNames(Query, ByRef Names) +; - Get next row of data from prepared query +; SQLite_FetchData(Query, ByRef Row) +; - Free prepared query +; SQLite_QueryFinalize(Query) +; - Reset prepared query for reuse +; SQLite_QueryReset(Query) +; - Execute SQLite3.exe with given commands +; SQLite_SQLiteExe(DBFile, Commands, ByRef Output) +; - Get SQLite3.dll version number +; SQLite_LibVersion() +; - Get the ROWID of the last inserted row +; SQLite_LastInsertRowID(DB, ByRef RowID) +; - Get number of changes caused by last SQL statement +; SQLite_Changes(DB, ByRef Rows) +; - Get number of changes since connecting to database +; SQLite_TotalChanges(DB, ByRef Rows) +; - Get the SQLite error message caused by last SQL statement +; SQLite_ErrMsg(DB, ByRef Msg) +; - Get the SQLite error code caused by last SQL statement +; SQLite_ErrCode(DB, ByRef Code) +; - Set SQLite's busy timer's timeout +; SQLite_SetTimeout(DB, Timeout = 1000) +; - Get description for last error +; SQLite_LastError(Error = "") +; - Set/get path for SQLite3.dll +; SQLite_DLLPath(Path = "") +; - Set/get path for SQLite.exe +; SQLite_EXEPath(Path = "") +; * Internal functions ***************************************************************************** +; _SQLite_StrToUTF8(Str, UTF8) +; _SQLite_UTF8ToStr(UTF8, Str) +; _SQLite_ModuleHandle(Handle = "") +; _SQLite_CurrentDB(DB = "") +; _SQLite_CheckDB(hDB, Action = "") +; _SQLite_CurrentQuery(Query = "") +; _SQLite_CheckQuery(Query, DB = "") +; _SQLite_ReturnCode(RC) +;======================================================================================================================= +; SQLite Returncodes +;======================================================================================================================= +; see _SQLite_ReturnCode() +;======================================================================================================================= +; Function Name: SQLite_StartUP() +; Description: Loads SQLite3.dll +; Requirements: Valid path to SQLite3.dll stored in SQLite_DLLPath(). +; Default: A_ScriptDir . "\SQLite3.dll" +; Parameter(s): None +; Return Value(s): On Success - True +; On Failure - False +;======================================================================================================================= +*/ +SQLite_Startup() { + Static MinVersion := "35" + + sqliteDllPath := SQLite_DLLPath() + + if(FileExist(sqliteDllPath)) + { + DLL := DllCall("LoadLibrary", "Str", sqliteDllPath) + if(!DLL) + throw Exception("Can't load " . sqliteDllPath . "!", -1) + + ver := SQLite_LibVersion() + + if(SubStr(RegExReplace(ver, "\."), 1, 2) < MinVersion) + throw Exception("SQLite ERROR: Version " . ver . " of SQLite3.dll is not supported!", -1) + + _SQLite_ModuleHandle(DLL) + } else + throw Exception("SQLite Dll not found:`n" . sqliteDllPath, -1) + + Return true +} +;======================================================================================================================= +; Function Name: SQLite_Shutdown() +; Description: Unloads SQLite3.dll +; Parameter(s): None +; Return Value(s): On Success - True +; On Failure - False, check ErrorLevel for details +;======================================================================================================================= +SQLite_Shutdown() { + DllCall("FreeLibrary", "UInt", _SQLite_ModuleHandle()) + Return (ErrorLevel ? false : true) +} +;======================================================================================================================= +; Function Name: SQLite_OpenDB() +; Description: Opens a database. +; Parameter(s): DBFile - Filepath of the DB +; Return Value(s): On Success - DB handle +; On Failure - False, check ErrorLevel for details +; For additional error message call SQLite_LastError() +;======================================================================================================================= +SQLite_OpenDB(DBFile) { + Static SQLITE_OPEN_READONLY := 0x01 ; Database opened as read-only + Static SQLITE_OPEN_READWRITE := 0x02 ; Database opened as read-write + Static SQLITE_OPEN_CREATE := 0x04 ; Database will be created if not exists + + flags := SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE + SQLite_LastError(" ") + + if (_SQLite_ModuleHandle() = "") { + if !(SQLite_Startup()) + throw Exception("ERROR: Could not find the SQLite3.dll!",-1) + } + + if (DBFile = "") + DBFile := ":memory:" + else if (!SQLite_IsFilePathValid(DBFile)) + throw Exception("Filepath to the SQLite DB seems to be invalid.",-1,DBFile) + + _SQLite_StrToUTF8(DBFile, UTF8) + DB := 0 + RC := DllCall("SQlite3\sqlite3_open_v2", "Ptr", &UTF8, "Ptr*", DB, "UInt", flags, "Ptr", 0, "Cdecl Int") + + if (ErrorLevel) { + throw Exception("ERROR: DLLCall sqlite3_open_v2 failed!",-1) + } + if (RC) { + if SQLite_ErrMsg(DB, Msg) + SQLite_LastError(Msg) + ErrorLevel := RC + Return False + } + _SQLite_CheckDB(DB, "Store") + _SQLite_CurrentDB(DB) + Return DB +} + +SQLite_IsFilePathValid(path) { + if FileExist(path) + return true + if IsObject(FileOpen(path, "a")) + { + FileDelete, %path% + return true + } + return false +} + +;======================================================================================================================= +; Function Name: SQLite_CloseDB() +; Description: Closes an open database. +; Waits until SQLite <> _SQLITE_BUSY until 'Timeout' has elapsed +; Parameter(s): DB - DB handle, -1 for last opened DB +; Return Value(s): On Success - True +; On Failure - False, check ErrorLevel for details +; For additional error message call SQLite_LastError() +;======================================================================================================================= +SQLite_CloseDB(DB) { + SQLite_LastError(" ") + if (DB = -1) + DB := _SQLite_CurrentDB() + if !_SQLite_CheckDB(DB) + Return True + RC := DllCall("SQlite3\sqlite3_close", "Ptr", DB, "Cdecl Int") + if (ErrorLevel) { + SQLite_LastError("ERROR: DLLCall sqlite3_close failed!") + Return False + } + if (RC) { + if SQLite_ErrMsg(DB, Msg) + SQLite_LastError(Msg) + ErrorLevel := RC + Return False + } + _SQLite_CheckDB(DB, "Free") + Return True +} +;======================================================================================================================= +; Function Name: SQLite_GetTable() +; Description: Provides the number of rows, the number of columns, the +; column names and the column values for a given query. +; Names are returned as an array. Result is an array of arrays +; containing the column values for each row. +; Parameter(s): DB - DB handle, -1 for last opened DB +; SQL - SQL statement to be executed +; ByRef Rows - Passes out the number of 'Data' rows +; ByRef Cols - Passes out the number of columns +; ByRef Names - Passes out an array containing the column names +; ByRef Result - Passes out an array of arrays containing the column values. +; Optional MaxResult - Number of rows to be returned +; Default = -1 : All rows +; Specify 0 to get only the number of rows and columns +; Specify 1 to get column names also +; Return Value(s): On Success - True +; On Failure - False, check ErrorLevel for details +; For additional error message call SQLite_LastError() +;======================================================================================================================= +SQLite_GetTable(DB, SQL, ByRef Rows, ByRef Cols, ByRef Names, ByRef Result, MaxResult = -1) { + Table := "", Err := 0, RC := 0, GetRows := 0 + I := 0 + SQLite_LastError(" ") + Result := "" + Rows := Cols := 0 + Names := "" + if (DB = -1) + DB := _SQLite_CurrentDB() + if !_SQLite_CheckDB(DB) { + SQLite_LastError("ERROR: Invalid database handle " . DB) + ErrorLevel := _SQLite_ReturnCode("SQLITE_ERROR") + Return False + } + if MaxResult Is Not Integer + MaxResult := -1 + if (MaxResult < -1) + MaxResult := -1 + if (MaxResult < -1) + MaxResult := -1 + Table := "" + Err := 0 + _SQLite_StrToUTF8(SQL, UTF8) + RC := DllCall("SQlite3\sqlite3_get_table", "Ptr", DB, "Ptr", &UTF8, "Ptr*", Table + , "Ptr*", Rows, "Ptr*", Cols, "Ptr*", Err, "Cdecl Int") + if (ErrorLevel) { + SQLite_LastError("ERROR: DLLCall sqlite3_get_table failed!") + Return False + } + if (RC) { + SQLite_LastError(StrGet(Err, "UTF-8")) + DllCall("SQLite3\sqlite3_free", "Ptr", Err, "cdecl") + ErrorLevel := RC + Return False + } + Result := Array() + if (MaxResult = 0) { + DllCall("SQLite3\sqlite3_free_table", "Ptr", Table, "Cdecl") + if (ErrorLevel) { + SQLite_LastError("ERROR: DLLCall sqlite3_close failed!") + Return False + } + Return True + } + if (MaxResult = 1) + GetRows := 0 + Else if (MaxResult > 1) && (MaxResult < Rows) + GetRows := MaxResult + Else + GetRows := Rows + Offset := 0 + Names := Array() + Loop, %Cols% { + Names[A_Index] := StrGet(NumGet(Table+0, Offset), "UTF-8") + Offset += A_PtrSize + } + Loop, %GetRows% { + I := A_Index + Result[I] := Array() + Loop, %Cols% { + Result[I][A_Index] := StrGet(NumGet(Table+0, Offset), "UTF-8") + Offset += A_PtrSize + } + } + ; Free Results Memory + DllCall("SQLite3\sqlite3_free_table", "Ptr", Table, "Cdecl") + if (ErrorLevel) { + SQLite_LastError("ERROR: DLLCall sqlite3_close failed!") + Return False + } + Return True +} + +SQLite_Bind(query, idx, val, type = "auto") { + if type not in int,double,text,null + if val is integer + type = int + else if val is float + type = double + else if (val == DBA.Database.NULL) + type = null + else + type = text + if type = int + return SQLite_bind_int(query, idx, val) + if type = double + return SQLite_bind_double(query, idx, val) + if type = text + return SQLite_bind_text(query, idx, val) + if type = null + return SQLite_bind_null(query, idx) + return -1 +} + +SQLite_Bind_blob(query, idx, addr, bytes) { + return DllCall("SQLite3\sqlite3_bind_blob", "Ptr", Query, "int", idx, "ptr", addr, "int", bytes, "ptr", -1, "CDecl int") ; SQLITE_TRANSIENT = -1 +} + +SQLite_Bind_text(query, idx, text) { + static fn := "SQLite3\sqlite3_bind_text" (A_IsUnicode ? "16" : "") + return DllCall(fn, "ptr", Query, "int", idx, "ptr", &text, "int", StrLen(text) * (A_IsUnicode+1), "ptr", -1, "CDecl int") ; SQLITE_TRANSIENT = -1 +} + +SQLite_bind_double(query, idx, double) { + return DllCall("SQLite3\sqlite3_bind_double", "ptr", query, "int", idx, "double", double, "CDecl int") +} + +SQLite_bind_int(query, idx, int) { + return DllCall("SQLite3\sqlite3_bind_int64", "ptr", query, "int", idx, "int64", int, "CDecl int") +} + +SQLite_bind_null(query, idx) { + return DllCall("SQLite3\sqlite3_bind_null", "ptr", query, "int", idx, "CDecl int") +} + +SQLite_Step(query) { + return DllCall("SQLite3\sqlite3_step", "ptr", query, "CDecl int") +} + +SQLite_Reset(query) { + return DllCall("SQLite3\sqlite3_reset", "ptr", query, "CDecl int") +} + +;======================================================================================================================= +; Function Name: SQLite_Exec() +; Description: Executes a 'non query' SQLite statement, does not handle results. +; Parameter(s): DB - DB handle, -1 for last opened DB +; SQL - SQL statement to be executed +; Return Value(s): On Success - True +; On Failure - False, check ErrorLevel for details +; For additional error message call SQLite_LastError() +;======================================================================================================================= +SQLite_Exec(DB, SQL) { + + ret := false + try + { + SQLite_LastError(" ") + + if(DB = -1) + DB := _SQLite_CurrentDB() + + if(!_SQLite_CheckDB(DB)){ + throw Exception("ERROR: Invalid database handle " DB "`nReturn Code: " _SQLite_ReturnCode("SQLITE_ERROR"),-1) + } else { + _SQLite_StrToUTF8(SQL, UTF8) + Err := 0 + RC := DllCall("SQlite3\sqlite3_exec", "Ptr", DB, "Ptr", &UTF8, "Ptr", 0, "Ptr", 0, "Ptr*", Err, "Cdecl Int") + if (ErrorLevel) { + throw Exception("DLLCall sqlite3_exec failed!",-1) + } else { + if (RC) { + + SQLite_LastError(StrGet(Err, "UTF-8")) + + try + { + DllCall("SQLite3\sqlite3_free", "Ptr", Err, "cdecl") + ErrorLevel := RC + } catch e + { + ;throw Exception("sqlite3_free failed.`n`nErr:" Err "`n`nChild Exception:`n" e.What " `n" e.Message, -1) + ; just igonre for now + } + + } else + ret := true + } + } + } catch e + throw Exception("SQLite_Exec failed.`n`n" sql "`n`nChild Exception:`n" e.What " `n" e.Message, -1) + + return ret +} +;======================================================================================================================= +; Function Name: SQlite_Query() +; Description: Prepares a single statement SQLite query, +; Parameter(s): DB - DB handle, -1 for last opened DB +; SQL - SQL statement to be executed +; Return Value(s): On Success - Query handle +; On Failure - False, check ErrorLevel for details +; For additional error message call SQLite_LastError() +;======================================================================================================================= +SQlite_Query(DB, SQL) { + SQLite_LastError(" ") + if (DB = -1) + DB := _SQLite_CurrentDB() + if !_SQLite_CheckDB(DB) { + SQLite_LastError("ERROR: Invalid database handle " . DB) + ErrorLevel := _SQLite_ReturnCode("SQLITE_ERROR") + Return False + } + Query := pSQL := 0 + Len := _SQLite_StrToUTF8(SQL, UTF8) + RC := DllCall("SQlite3\sqlite3_prepare", "Ptr", DB, "Ptr", &UTF8, "Int", Len + , "Ptr*", Query, "Ptr*", pSQL, "Cdecl Int") + if (ErrorLeveL) { + SQLite_LastError("ERROR: DLLCall sqlite3_prepare failed!") + Return False + } + if (RC) { + if SQLite_ErrMsg(DB, Msg) + SQLite_LastError(Msg) + ErrorLevel := RC + Return False + } + _SQLite_CheckQuery(Query, DB) + _SQLite_CurrentQuery(Query) + Return Query +} +;======================================================================================================================= +; Function Name: SQLite_FetchNames() +; Description: Provides the column names of a SQLite_Query() based query +; Parameter(s): Query - Query handle, -1 for last prepared query +; ByRef Names - Passes out an array containing the column names +; Return Value(s): On Success - True +; On Failure - False, check ErrorLevel for details +; For additional error message call SQLite_LastError() +;======================================================================================================================= +SQLite_FetchNames(Query, ByRef Names) { + SQLite_LastError(" ") + Names := Array() + if (Query = -1) + Query := _SQLite_CurrentQuery() + if !(DB := _SQLite_CheckQuery(Query)) { + SQLite_LastError("ERROR: Invalid query handle " . Query) + ErrorLevel := _SQLite_ReturnCode("SQLITE_ERROR") + Return False + } + RC := DllCall("SQlite3\sqlite3_column_count", "Ptr", Query, "Cdecl Int") + if (ErrorLevel) { + SQLite_LastError("ERROR: DLLCall sqlite3_column_count failed!") + Return False + } + if (RC < 1) { + SQLite_LastError("ERROR: Query result is empty!") + ErrorLevel := _SQLite_ReturnCode("SQLITE_EMPTY") + Return False + } + Loop, %RC% { + StrPtr := DllCall("SQlite3\sqlite3_column_name", "Ptr", Query, "Int", A_Index - 1, "Cdecl Ptr") + if (ErrorLevel) { + SQLite_LastError("ERROR: DLLCall sqlite3_column_name failed!") + Return False + } + Names[A_Index] := StrGet(StrPtr, "UTF-8") + } + Return True +} +;======================================================================================================================= +; Function Name: SQLite_FetchData() +; Description: Fetches next row of data from a SQLite_Query() based query +; Parameter(s): Query - Query handle, -1 for last prepared query +; ByRef Row - Passes out an array containing the column values of one row of data +; Return Value(s): On Success - Number of columns, -1 on end of data +; On Failure - False, check ErrorLevel for details +; For additional error message call SQLite_LastError() +;======================================================================================================================= +SQLite_FetchData(Query, ByRef Row) { + Static SQLITE_NULL := 5 + SQLite_LastError(" ") + Row := "" + if (Query = -1) + Query := _SQLite_CurrentQuery() + if !(DB := _SQLite_CheckQuery(Query)) { + SQLite_LastError("ERROR: Invalid query handle " . Query) + ErrorLevel := _SQLite_ReturnCode("SQLITE_ERROR") + Return False + } + RC := DllCall("SQlite3\sqlite3_step", "Ptr", Query, "Cdecl Int") + if (ErrorLevel) { + SQLite_LastError("ERROR: DLLCall sqlite3_step failed!") + Return False + } + if (RC <> _SQLite_ReturnCode("SQLITE_ROW")) { + if (RC = _SQLite_ReturnCode("SQLITE_DONE")) { + Return -1 + } + SQLite_QueryFinalize(Query) + if SQLite_ErrMsg(DB, Msg) + SQLite_LastError(Msg) + ErrorLevel := RC + Return False + } + RC := DllCall("SQlite3\sqlite3_data_count", "Ptr", Query, "Cdecl Int") + if (ErrorLevel) { + SQLite_LastError("ERROR: DLLCall sqlite3_data_count failed!") + Return False + } + if (RC < 1) { + SQLite_LastError("ERROR: Query result is empty!") + ErrorLevel := _SQLite_ReturnCode("SQLITE_EMPTY") + Return False + } + Row := Array() + Loop, %RC% { + CType := DllCall("SQlite3\sqlite3_column_type", "Ptr", Query, "Int", A_Index - 1, "Cdecl Int") + if (ErrorLevel) { + SQLite_LastError("ERROR: DLLCall sqlite3_column_type failed!") + Return False + } + if (CType = SQLITE_NULL) { + Row[A_Index] := "" + } Else { + StrPtr := DllCall("SQlite3\sqlite3_column_text", "Ptr", Query, "Int", A_Index - 1, "Cdecl Ptr") + if (ErrorLevel) { + SQLite_LastError("ERROR: DLLCall sqlite3_column_text failed!") + Return False + } + Row[A_Index] := StrGet(StrPtr, "UTF-8") + } + } + Return RC +} +;======================================================================================================================= +; Function Name: SQLite_QueryFinalize() +; Description: Finalizes SQLite_Query() based query, +; Query handle will be not valid any more +; Parameter(s): Query - Query handle, -1 for last prepared query +; Return Value(s): On Success - True +; On Failure - False, check ErrorLevel for details +; For additional error message call SQLite_LastError() +;======================================================================================================================= +SQLite_QueryFinalize(Query) { + SQLite_LastError(" ") + if (Query = -1) + Query := _SQLite_CurrentQuery() + if !(DB := _SQLite_CheckQuery(Query)) { + SQLite_LastError("ERROR: Invalid query handle " . Query) + ErrorLevel := _SQLite_ReturnCode("SQLITE_ERROR") + Return False + } + RC := DllCall("SQlite3\sqlite3_finalize", "Ptr", Query, "Cdecl Int") + if (ErrorLevel) { + SQLite_LastError("ERROR: DLLCall sqlite3_finalize failed!") + Return False + } + if (RC) { + if SQLite_ErrMsg(DB, Msg) + SQLite_LastError(Msg) + ErrorLevel := RC + Return False + } + _SQLite_CheckQuery(Query, 0) + Return True +} +;======================================================================================================================= +; Function Name: SQLite_QueryReset() +; Description: Resets SQLite_Query() based query for reuse +; Parameter(s): Query - Query handle, -1 for last prepared query +; Return Value(s): On Success - True +; On Failure - False, check ErrorLevel for details +; For additional error message call SQLite_LastError() +;======================================================================================================================= +SQLite_QueryReset(Query) { + SQLite_LastError(" ") + if (Query = -1) + Query := _SQLite_CurrentQuery() + if !(DB := _SQLite_CheckQuery(Query)) { + SQLite_LastError("ERROR: Invalid query handle " . Query) + ErrorLevel := _SQLite_ReturnCode("SQLITE_ERROR") + Return False + } + RC := DllCall("SQlite3\sqlite3_reset", "Ptr", Query, "Cdecl Int") + if (ErrorLevel) { + SQLite_LastError("ERROR: DLLCall sqlite3_finalize failed!") + Return False + } + if (RC) { + if SQLite_ErrMsg(DB, Msg) + SQLite_LastError(Msg) + ErrorLevel := RC + Return False + } + Return True +} +;======================================================================================================================= +; Function Name: SQLite_SQLiteExe() +; Description: Executes commands with SQLite3.exe +; Requirements: Valid path for SQLite3.exe stored in SQLite_EXEPath(). +; Default: A_ScriptDir . "\SQLite3.EXE" +; Parameter(s): DBFile - DB filename +; Commands - Commands for SQLite3.exe +; ByRef Output - Raw output from SQLite3.exe +; Return Value(s): On Success - True +; On Failure - False, check ErrorLevel for details +; For additional error message call SQLite_LastError() +;======================================================================================================================= +SQLite_SQLiteExe(DBFile, Commands, ByRef Output) { + Static InputFile := "~SQLINP.TXT" + Static OutputFile := "~SQLOUT.TXT" + SQLite_LastError(" ") + Output := "" + SQLiteExe := SQLite_EXEPath() + if !FileExist(SQLiteExe) { + SQLite_LastError("ERROR: Unable to find " . SQLiteExe . "!") + ErrorLevel := _SQLite_ReturnCode("SQLITE_ERROR") + Return False + } + if FileExist(InputFile) { + FileDelete, %InputFile% + if (ErrorLevel) { + SQLite_LastError("ERROR: Unable to delete " . InputFile . "!") + Return False + } + } + if FileExist(OutputFile) { + FileDelete, %OutputFile% + if (ErrorLevel) { + SQLite_LastError("ERROR: Unable to delete " . OutputFile . "!") + Return False + } + } + if !InStr(Commands, ".output stdout") + Commands := ".output stdout`n" . Commands + FileAppend, %Commands%, %InputFile%, UTF-8-RAW + if (ErrorLevel) { + SQLite_LastError("ERROR: Unable to create " . InputFile . "!") + Return False + } + Cmd = ""%SQLiteExe%" "%DBFile%" < "%InputFile%" > "%OutputFile%"" ;" + + RunWait %comspec% /c %Cmd%, , Hide UseErrorLevel + if (Errorlevel) { + SQLite_LastError("ERROR: Error occured running " . SQLiteExe . "!") + Return False + } + FileRead, Output, %OutputFile% + if (ErrorLevel) { + SQLite_LastError("ERROR: Unable to read " . OutputFile . "!") + Return False + } + if InStr(Output, "SQL error:") || InStr(Output, "Incomplete SQL:") { + SQLite_LastError("ERROR: " . SQLiteExe . " reported an Error!") + ErrorLevel := _SQLite_ReturnCode("SQLITE_ERROR") + Return False + } + Return True +} +;======================================================================================================================= +; Function Name: SQLite_LibVersion() +; Description: Returns the version number of the SQLite3.dll +; Parameter(s): None +; Return Value(s): On Success - Version number +; On Failure - False, check ErrorLevel for details +; For additional error message call SQLite_LastError() +;======================================================================================================================= +SQLite_LibVersion() { + SQLite_LastError(" ") + StrPtr := DllCall("SQlite3\sqlite3_libversion", "Cdecl Ptr") + if (ErrorLevel) { + SQLite_LastError("ERROR: DLLCall sqlite3_libversion failed!") + Return False + } + Return StrGet(StrPtr, "UTF-8") +} +;======================================================================================================================= +; Function Name: SQLite_LastInsertRowID() +; Description: Returns the ROWID of the most recent INSERT in the DB +; Parameter(s): DB - DB handle, -1 for last opened DB +; ByRef RowID - passes out ROWID +; Return Value(s): On Success - True +; On Failure - False, check ErrorLevel for details +; For additional error message call SQLite_LastError() +;======================================================================================================================= +SQLite_LastInsertRowID(DB, ByRef rowId) { + SQLite_LastError(" ") + RowID := 0 + if (DB = -1) + DB := _SQLite_CurrentDB() + if !_SQLite_CheckDB(DB) { + SQLite_LastError("ERROR: Invalid DB Handle " . DB . "!") + Return False + } + rowId := DllCall("SQLite3\sqlite3_last_insert_rowid", "Ptr", DB, "Cdecl Int64") ; Each entry in an SQLite table has a unique 64-bit signed integer key called the "rowid". + if (ErrorLevel) { + SQLite_LastError("ERROR: DLLCall sqlite3_last_insert_rowid failed!") + Return False + } + Return True +} +;======================================================================================================================= +; Function Name: SQLite_Changes() +; Description: Returns the number of DB rows that were changed +; by the most recently completed query +; Parameter(s): DB - DB handle, -1 for last opened DB +; ByRef Rows - Passes out number of changes +; Return Value(s): On Success - True +; On Failure - False, check ErrorLevel for details +; For additional error message call SQLite_LastError() +;======================================================================================================================= +SQLite_Changes(DB, ByRef Rows) { + SQLite_LastError(" ") + Rows := 0 + if (DB = -1) + DB := _SQLite_CurrentDB() + if !_SQLite_CheckDB(DB) { + SQLite_LastError("ERROR: Invalid DB Handle " . DB . "!") + Return False + } + RC := DllCall("SQLite3\sqlite3_changes", "Ptr", DB, "Cdecl Ptr") + if (ErrorLevel) { + SQLite_LastError("ERROR: DLLCall sqlite3_changes failed!") + Return False + } + Rows := RC + Return True +} +;======================================================================================================================= +; Function Name: SQLite_TotalChanges() +; Description: Returns the total number of DB rows that have been +; modified, inserted, or deleted since the DB connection +; was created +; Parameter(s): DB - DB handle, -1 for last opened DB +; ByRef Rows - Passes out the number of changes +; Return Value(s): On Success - True +; On Failure - False, check ErrorLevel for details +; For additional error message call SQLite_LastError() +;======================================================================================================================= +SQLite_TotalChanges(DB, ByRef Rows) { + SQLite_LastError(" ") + Rows := 0 + if (DB = -1) + DB := _SQLite_CurrentDB() + if !_SQLite_CheckDB(DB) { + SQLite_LastError("ERROR: Invalid DB Handle " . DB . "!") + Return False + } + RC := DllCall("SQLite3\sqlite3_total_changes", "Ptr", DB, "Cdecl Ptr") + if (ErrorLevel) { + SQLite_LastError("ERROR: DLLCall sqlite3_total_changes failed!") + Return False + } + Rows := RC + Return True +} +;======================================================================================================================= +; Function Name: SQLite_ErrMsg() +; Description: Returns the error message for the most recent sqlite3_* API call as string +; Parameter(s): DB - DB handle, -1 for last opened DB +; ByRef Msg - Passes out the error message +; Return Value(s): On Success - True +; On Failure - False, check ErrorLevel for details +; For additional error message call SQLite_LastError() +;======================================================================================================================= +SQLite_ErrMsg(DB, ByRef Msg) { + SQLite_LastError(" ") + Msg := "" + if (DB = -1) + DB := _SQLite_CurrentDB() + if !_SQLite_CheckDB(DB) { + SQLite_LastError("ERROR: Invalid DB Handle " . DB . "!") + Return False + } + messagePtr := DllCall("SQLite3\sqlite3_errmsg", "Ptr", DB, "Cdecl Ptr") + if (ErrorLevel) { + SQLite_LastError("ERROR: DLLCall sqlite3_errmsg failed!") + Return False + } + Msg := StrGet(messagePtr, "UTF-8") + Return True +} +;======================================================================================================================= +; Function Name: SQLite_ErrCode() +; Description: Returns the error code for the most recent sqlite3_* API call as string. +; Parameter(s): DB - DB handle, -1 for last opened DB +; ByRef Code - Passes out the error code +; Return Value(s): On Success - True +; On Failure - False, check ErrorLevel for details +; For additional error message call SQLite_LastError() +;======================================================================================================================= +SQLite_ErrCode(DB, ByRef Code) +{ + SQLite_LastError(" ") + Code := "" + if (DB = -1) + DB := _SQLite_CurrentDB() + if !_SQLite_CheckDB(DB) { + SQLite_LastError("ERROR: Invalid DB Handle " . DB . "!") + Return False + } + RC := DllCall("SQLite3\sqlite3_errcode", "Ptr", DB, "Cdecl Ptr") + if (ErrorLevel) { + SQLite_LastError("ERROR: DLLCall sqlite3_errcode failed!") + Return False + } + Code := RC + Return True +} +;======================================================================================================================= +; Function Name: SQLite_SetTimeout() +; Description: Sets timeout for DB's "busy handler" +; Parameter(s): hDB - DB handle, -1 for last opened DB +; Optional Timeout - Timeout [msec] +; Return Value(s): On Success - True +; On Failure - False, check ErrorLevel for details +; For additional error message call SQLite_LastError() +;======================================================================================================================= +SQLite_SetTimeout(DB, Timeout = 1000) { + SQLite_LastError(" ") + Msg := "" + if (DB = -1) + DB := _SQLite_CurrentDB() + if !_SQLite_CheckDB(DB) { + SQLite_LastError("ERROR: Invalid DB Handle " . DB . "!") + Return False + } + if Timeout Is Not Integer + Timeout := 1000 + RC := DllCall("SQLite3\sqlite3_busy_timeout", "Ptr", DB, "Cdecl Int") + if (ErrorLevel) { + SQLite_LastError("ERROR: DLLCall sqlite3_busy_timeout failed!") + Return False + } + if (RC) { + if SQLite_ErrMsg(DB, Msg) + SQLite_LastError(Msg) + ErrorLevel := RC + Return False + } + Return True +} +;======================================================================================================================= +; Function Name: SQLite_LastError() +; Description: Provides additional error description for the last error +; Parameter(s): Optional Error - for internal use only!!! +; Return Value(s): Error description or "" +;======================================================================================================================= +SQLite_LastError(Error = "") { + Static LastError := "" + if (Error != "") + LastError := Error + Return LastError +} +;======================================================================================================================= +; Function Name: SQLite_DLLPath() +; Description: Stores/provides the path for SQLite3.dll +; SQLite DLL is assumed to be in the scripts directory, if not +; you have to call the function with the valid path before any +; other function calls! +; Parameter(s): Optional Path - Path for SQLite3.dll +; Return Value: Path to SQLite DLL +;======================================================================================================================= + +SQLite_DLLPath(forcedPath = "") { + static DLLPath := "" + static dllname := "SQLite3.dll" + + if(DLLPath == ""){ + ; search the dll + prefix := (A_PtrSize == 8) ? "x64\" : "" + dllpath := prefix . dllname + + if (FileExist(A_ScriptDir . "\" . dllpath)) + DLLPath := A_ScriptDir . "\" . dllpath + else + DLLPath := A_ScriptDir . "\Lib\" . dllpath + } + + if (forcedPath != "") + DLLPath := forcedPath + + return DLLPath +} + + +;======================================================================================================================= +; Function Name: SQLite_EXEPath() +; Description: Stores/provides the path for SQLite3.exe +; SQLite EXE is assumed to be in the scripts directory, if not +; you have to call the function with the valid path before any +; calls on SQLite_SQLite_Exe()! +; Parameter(s): Optional Path - Path for SQLite3.exe +; Return Value: Path to SQLite DLL +;======================================================================================================================= +SQLite_EXEPath(forcedPath = "") { + static EXEPath := "" + + if (EXEPath == ""){ + if (FileExist(A_ScriptDir . "\SQLite3.exe")) + EXEPath := A_ScriptDir . "\SQLite3.exe" + else if (FileExist(A_ScriptDir . "\Lib\SQLite3.exe")) + EXEPath := A_ScriptDir . "\Lib\SQLite3.exe" + } + if (forcedPath != "") + EXEPath := forcedPath + Return EXEPath +} +;======================================================================================================================= +; !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! +; !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! +; !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! +; !!! Following functions and classes are for internal use only !!! +; !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! +; !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! +; !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! +;======================================================================================================================= +; Function Name: _SQLite_StrToUTF8() +; Description: Converts Str to UTF-8 +;======================================================================================================================= +_SQLite_StrToUTF8(Str, ByRef UTF8) { + VarSetCapacity(UTF8, StrPut(Str, "UTF-8"), 0) + Return StrPut(Str, &UTF8, "UTF-8") +} +;======================================================================================================================= +; Function Name: _SQLite_UTF8ToStr() +; Description: Converts UTF-8 to Str +;======================================================================================================================= +_SQLite_UTF8ToStr(UTF8, ByRef Str) { + Str := StrGet(&UTF8, "UTF-8") + Return StrLen(Str) +} +;======================================================================================================================= +; Function Name: _SQLite_ModuleHandle() +; Description: Stores/provides DLL's module handle +;======================================================================================================================= +_SQLite_ModuleHandle(Handle = "") { + Static ModuleHandle := "" + if (Handle != "") + ModuleHandle := Handle + Return ModuleHandle +} +;======================================================================================================================= +; Function Name: _SQLite_CurrentDB() +; Description: Stores\provides the current (last opened) DB handle +;======================================================================================================================= +_SQLite_CurrentDB(DB = "") { + Static CurrentDB := 0 + if (DB != "") + CurrentDB := DB + Return CurrentDB +} +;======================================================================================================================= +; Function Name: _SQLite_CheckDB() +; Description: Stores\frees\validates the given DB handle +;======================================================================================================================= +_SQLite_CheckDB(DB, Action = "") { + Static ValidHandles := {} + DB += 0 + if DB Is Not Integer + Return False + if (DB = 0) + Return False + if (Action = "Store") { + ValidHandles[DB] := True + Return True + } + if (Action = "Free") { + if ValidHandles.HasKey(DB) + ValidHandles.Remove(DB, "") + Return True + } + Return ValidHandles.HasKey(DB) +} +;======================================================================================================================= +; Function Name: _SQLite_CurrentQuery() +; Description: Stores\provides the current (last prepared) query handle +;======================================================================================================================= +_SQLite_CurrentQuery(Query = "") { + Static CurrentQuery := 0 + if (Query != "") + CurrentQuery := Query + Return CurrentQuery +} +;======================================================================================================================= +; Function Name: _SQLite_CheckQuery() +; Description: Stores\frees\validates the given query handle +;======================================================================================================================= +_SQLite_CheckQuery(Query, DB = "") { + Static ValidQueries := {} + Query += 0 + if Query Is Not Integer + Return False + if (Query = 0) + Return False + if (DB = 0) { + if ValidQueries.HasKey(Query) + ValidQueries.Remove(Query, "") + Return True + } + if (DB != "") { + ValidQueries[Query] := DB + Return True + } + Return ValidQueries.HasKey(Query) ? ValidQueries[Query] : False +} +;======================================================================================================================= +; Function Name: _SQLite_ReturnCode(RC) +; Description: Returns numeric RC for literal RC +;======================================================================================================================= +_SQLite_ReturnCode(RC) { + Static RCTXT :={ SQLITE_OK: 0 ; Successful result + , SQLITE_ERROR: 1 ; SQL error or missing database + , SQLITE_INTERNAL: 2 ; NOT USED. Internal logic error in SQLite + , SQLITE_PERM: 3 ; Access permission denied + , SQLITE_ABORT: 4 ; Callback routine requested an abort + , SQLITE_BUSY: 5 ; The database file is locked + , SQLITE_LOCKED: 6 ; A table in the database is locked + , SQLITE_NOMEM: 7 ; A malloc() failed + , SQLITE_READONLY: 8 ; Attempt to write a readonly database + , SQLITE_INTERRUPT: 9 ; Operation terminated by sqlite3_interrupt() + , SQLITE_IOERR: 10 ; Some kind of disk I/O error occurred + , SQLITE_CORRUPT: 11 ; The database disk image is malformed + , SQLITE_NOTFOUND: 12 ; NOT USED. Table or record not found + , SQLITE_FULL: 13 ; Insertion failed because database is full + , SQLITE_CANTOPEN: 14 ; Unable to open the database file + , SQLITE_PROTOCOL: 15 ; NOT USED. Database lock protocol error + , SQLITE_EMPTY: 16 ; Database is empty + , SQLITE_SCHEMA: 17 ; The database schema changed + , SQLITE_TOOBIG: 18 ; String or BLOB exceeds size limit + , SQLITE_CONSTRAINT: 19 ; Abort due to constraint violation + , SQLITE_MISMATCH: 20 ; Data type mismatch + , SQLITE_MISUSE: 21 ; Library used incorrectly + , SQLITE_NOLFS: 22 ; Uses OS features not supported on host + , SQLITE_AUTH: 23 ; Authorization denied + , SQLITE_FORMAT: 24 ; Auxiliary database format error + , SQLITE_RANGE: 25 ; 2nd parameter to sqlite3_bind out of range + , SQLITE_NOTADB: 26 ; File opened that is not a database file + , SQLITE_ROW: 100 ; sqlite3_step() has another row ready + , SQLITE_DONE: 101} ; sqlite3_step() has finished executing + Return RCTXT.HasKey(RC) ? RCTXT[RC] : "" +} diff --git a/Lib/clean.bat b/Lib/clean.bat new file mode 100644 index 0000000..90bddf2 --- /dev/null +++ b/Lib/clean.bat @@ -0,0 +1 @@ +del *.bak \ No newline at end of file diff --git a/Lib/license.txt b/Lib/license.txt new file mode 100644 index 0000000..b60e3da --- /dev/null +++ b/Lib/license.txt @@ -0,0 +1,15 @@ + AHK DBA - OOP Database Access Framework + Copyright (C) 2012 IsNull and other contributors + + 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 3 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, see . \ No newline at end of file diff --git a/Lib/mySQL.ahk b/Lib/mySQL.ahk new file mode 100644 index 0000000..5fa8016 --- /dev/null +++ b/Lib/mySQL.ahk @@ -0,0 +1,431 @@ +/*============================================================ +mysql.ahk +Provides a set of functions to connect and query a mysql database + +Based upon the published lib of panofish +http://www.autohotkey.com/forum/topic67280.html + + +Offical Documentation of the C-API +http://dev.mysql.com/doc/refman/5.0/en/c.html +============================================================ +*/ + + +/* + Parses the given Connectionstring to a ConnectionData + + An typical Connectionstring looks like: + Server=myServerAddress;Port=1234;Database=myDataBase;Uid=myUsername;Pwd=myPassword; + + Further Info: http://www.connectionstrings.com/mysql +*/ +MySQL_CreateConnectionData(connectionString){ + connectionData := {} + StringSplit, connstr, connectionString, `; + Loop, % connstr0 + { + StringSplit, segment, connstr%a_index%, = + connectionData[segment1] := segment2 + } + return connectionData +} + + + +MySQL_StartUp(){ + global MySQL_ExternDir + MySQL_ExternDir := A_WorkingDir + + libDllpath := MySQL_DLLPath() + + if(!FileExist(libDllpath)) + { + msg := "MySQL Libaray not found!`n" libDllpath " (file missing)" + OutputDebug, %msg% + throw Exception(msg,-1) + } + + + hModule := DllCall("LoadLibrary", "Str", libDllpath) + + if (hModule == 0) + { + msg := "LoadLibrary failed, can't load module:`n" libDllpath + OutputDebug, %msg% + throw Exception(msg, -1) + }else + return hModule +} + +MySQL_DLLPath(forcedPath = "") { + static DLLPath := "" + static dllname := "libmySQL.dll" + + if(DLLPath == ""){ + ; search the dll + prefix := (A_PtrSize == 8) ? "x64\" : "" + dllpath := prefix . dllname + + if (FileExist(A_ScriptDir . "\" . dllpath)) + DLLPath := A_ScriptDir . "\" . dllpath + else + DLLPath := A_ScriptDir . "\Lib\" . dllpath + } + + if (forcedPath != "") + DLLPath := forcedPath + + return DLLPath +} + + +/***************************************************************** + Connect to mysql database and return db handle + + host: + user: + password: + database: + port: 3306(default) +****************************************************************** +*/ +MySQL_Connect(host, user, password, database, port = 3306){ + + + db := DllCall("libmySQL.dll\mysql_init", "ptr", 0) + + if (db = 0) + throw Exception("MySQL Error 445, Not enough memory to connect to MySQL", -1) + + connection := DllCall("libmySQL.dll\mysql_real_connect" + , "ptr", db + , "AStr", host ; host name + , "AStr", user ; user name + , "AStr", password ; password + , "AStr", database ; database name + , "UInt", port ; port + , "UInt", 0 ; unix_socket + , "UInt", 0) ; client_flag + + If (connection == 0) + throw Exception(BuildMySQLErrorStr(db, "Cannot connect to database"), -1) + + ;debugging only: + ;MsgBox % "Ping database: " . MySQL_Ping(db) . "`nServer version: " . MySQL_GetVersion(db) + + return db +} + +MySQL_Close(db){ + DllCall("libmySQL.dll\mysql_close", "ptr", db) +} + + + +MySQL_GetVersion(db){ + serverVersion := DllCall("libmySQL.dll\mysql_get_server_info", "ptr", db, "AStr") + return serverVersion +} +MySQL_Ping(db){ + return DllCall("libmySQL.dll\mysql_ping", "ptr", db) +} + +MySQL_GetLastErrorNo(db){ + return DllCall("libmySQL.dll\mysql_errno", "ptr", db) +} + +MySQL_GetLastErrorMsg(db){ + return DllCall("libmySQL.dll\mysql_error", "ptr", db, "AStr") +} + +/* +Retrieves a complete result set to the client. +*/ +MySQL_Store_Result(db) { + return DllCall("libmySQL.dll\mysql_store_result", "ptr", db) +} + +/* +Retrieves the resultset row-by-row +*/ +MySQL_Use_Result(db) { + return DllCall("libmySQL.dll\mysql_use_result", "ptr", db) +} + +/* +Returns a requestResult for the given query +*/ +MySQL_Query(db, query){ + return DllCall("libmySQL.dll\mysql_query", "ptr", db , "AStr", query) +} + +MySQL_free_result(requestResult){ + return DllCall("libmySQL.dll\mysql_free_result", "ptr", requestResult) +} + +/* +Returns the number of columns in a result set. +*/ +MySQL_num_fields(requestResult) { + Return DllCall("libmySQL.dll\mysql_num_fields", "ptr", requestResult) +} + +/* +Returns the lengths of all columns in the current row. +*/ +MySQL_fetch_lengths(requestResult) { + Return , DllCall("libmySQL.dll\mysql_fetch_lengths", "ptr", requestResult) +} + + +/* +Fetches the next row from the result set. +*/ +MySQL_fetch_row(requestResult) { + Return , DllCall("libmySQL.dll\mysql_fetch_row", "ptr", requestResult) +} + + +/* +Fetches given Field +*/ +Mysql_fetch_field_direct(requestResult, fieldnum) { + return DllCall("libmySQL.dll\mysql_fetch_field_direct", "ptr", requestResult, "Uint", fieldnum) +} + +/* +Fetches the next field from the result set. +*/ +Mysql_fetch_field(requestResult) { + return DllCall("libmySQL.dll\mysql_fetch_field", "ptr", requestResult) +} + +/* +Fetches all fields of the resultSet +*/ +MySQL_fetch_fields(requestResult){ + global MySQL_Field + + fields := [] + fieldCount := MySQL_num_fields(requestResult) + + Loop, % fieldCount + { + fptr := Mysql_fetch_field(requestResult) + fields[A_index] := new MySQL_Field(fptr) + } + return fields +} + + +/* +; mysql error handling +*/ +BuildMySQLErrorStr(db, message, query="") { + errorCode := DllCall("libmySQL.dll\mysql_errno", "UInt", db) + errorStr := DllCall("libmySQL.dll\mysql_error", "UInt", db, "AStr") + Return, "MySQL Error: " message "Error " errorCode ": " errorStr "`n`n" query +} + + + + + +;============================================================ +; mysql get address +;============================================================ + + +GetUIntAtAddress(_addr, _offset) +{ + return NumGet(_addr+0,_offset * 4, "uint") +} + +GetPtrAtAddress(_addr, _offset) +{ + return NumGet(_addr+0,_offset * A_PtrSize, "ptr") +} + + +;============================================================ +; internal: dump resultset from given Query to string +;============================================================ +__MySQL_Query_Dump(_db, _query) +{ + local resultString, result, requestResult, fieldCount + local row, lengths, length, fieldPointer, field + + + result := DllCall("libmySQL.dll\mysql_query", "UInt", _db , "AStr", _query) + + If (result != 0) + throw new Exception(BuildMySQLErrorStr(_db, "dbQuery Fail", RegExReplace(_query , "\t", " ")), -1) + + + requestResult := MySql_Store_Result(_db) + + if (requestResult = 0) { ; call must have been an insert or delete ... a select would return results to pass back + return + } + + fieldCount := MySQL_num_fields(requestResult) + + + myfields := MySQL_fetch_fields(requestResult) + for each, fifi in myfields + { + MsgBox % "name: " fifi.Name() "`n org name: " fifi.OrgName() "`ntable: " fifi.Table() "`norg table: " fifi.OrgTable() + } + + Loop + { + row := MySQL_fetch_row(requestResult) + if (!row) + break + + ; Get a pointer on a table of lengths (unsigned long) + lengths := MySQL_fetch_lengths(requestResult) + + Loop %fieldCount% + { + length := GetUIntAtAddress(lengths, A_Index - 1) + fieldPointer := GetPtrAtAddress(row, A_Index - 1) + field := StrGet(fieldPointer, length, "CP0") + resultString := resultString . field + if (A_Index < fieldCount) + resultString := resultString . "|" ; seperator for fields + } + resultString := resultString . "`n" ; seperator for records + } + MySQL_free_result(requestResult) + resultString := RegExReplace(resultString , "`n$", "") + + return resultString +} + + + + ;============================================================ + ; Escape mysql special characters + ; This must be done to sql insert columns where the characters might contain special characters, such as user input fields + ; + ; Escape Sequence Character Represented by Sequence + ; \' A single quote (“'”) character. + ; \" A double quote (“"”) character. + ; \n A newline (linefeed) character. + ; \r A carriage return character. + ; \t A tab character. + ; \\ A backslash (“\”) character. + ; \% A “%” character. Usually indicates a wildcard character + ; \_ A “_” character. Usually indicates a wildcard character + ; \b A backspace character. + ; + ; these 2 have not yet been included yet + ; \Z ASCII 26 (Control+Z). Stands for END-OF-FILE on Windows + ; \0 An ASCII NUL (0x00) character. + ; + ; example call: + ; description := mysql_escape_string(description) + ;============================================================ + + Mysql_escape_string(unescaped_string) + { + escaped_string := RegExReplace(unescaped_string, "\\", "\\") ; \ + escaped_string := RegExReplace(escaped_string, "'", "\'") ; ' + + escaped_string := RegExReplace(escaped_string, "`t", "\t") ; \t + escaped_string := RegExReplace(escaped_string, "`n", "\n") ; \n + escaped_string := RegExReplace(escaped_string, "`r", "\r") ; \r + escaped_string := RegExReplace(escaped_string, "`b", "\b") ; \b + + ; these characters appear to insert fine in mysql + ;escaped_string := RegExReplace(escaped_string, "%", "\%") ; % + ;escaped_string := RegExReplace(escaped_string, "_", "\_") ; _ + ;escaped_string := RegExReplace(escaped_string, """", "\""") ; " + + return escaped_string + } + + +/* +typedef struct st_mysql_field { + char *name; /* Name of column */ + char *org_name; /* Original column name, if an alias */ + char *table; /* Table of column if column was a field */ + char *org_table; /* Org table name, if table was an alias */ + char *db; /* Database for table */ + char *catalog; /* Catalog for table */ + char *def; /* Default value (set by mysql_list_fields) */ + unsigned long length; /* Width of column (create length) */ + unsigned long max_length; /* Max width for selected set */ + unsigned int name_length; + unsigned int org_name_length; + unsigned int table_length; + unsigned int org_table_length; + unsigned int db_length; + unsigned int catalog_length; + unsigned int def_length; + unsigned int flags; /* Div flags */ + unsigned int decimals; /* Number of decimals in field */ + unsigned int charsetnr; /* Character set */ + enum enum_field_types type; /* Type of field. See mysql_com.h for types */ + void *extension; +} MYSQL_FIELD; +*/ + +/* +'mysql_port is a long +'mysql_unix port is a long (pointer) +'sizeof(MYSQL_FIELD)=32 +Public Type API_MYSQL_FIELD + name As Long + table As Long + def As Long + type As API_enum_field_types + length As Long + max_length As Long + flags As Long + decimals As Long +End Type +*/ +class MySQL_Field +{ + ptr := 0 + + __new(ptr){ + this.ptr := ptr + } + + Name(){ + adr := GetPtrAtAddress(this.ptr, 0) + return StrGet(adr, 255, "CP0") + } + + OrgName(){ + adr := GetPtrAtAddress(this.ptr, 4) + return StrGet(adr, 255, "CP0") + } + + Table(){ + adr := GetPtrAtAddress(this.ptr, 8) + return StrGet(adr, 255, "CP0") + } + + OrgTable(){ + adr := GetPtrAtAddress(this.ptr, 12) + return StrGet(adr, 255, "CP0") + } + +} + + + + + + + + + + + diff --git a/Lib/readme.txt b/Lib/readme.txt new file mode 100644 index 0000000..dbc12a2 --- /dev/null +++ b/Lib/readme.txt @@ -0,0 +1,27 @@ + + AHK DBA - OOP Database Access Framework for AutoHotkey (_L) + + Currently DBA supports SQLite, MySQL and ADO. + + + DBA is an object oriented wrapper around several different + databases/database providers to standardize the access interface. + It is similar to ADO from MS or the jdbc driver in Java. + + + + + Copyright (C) 2012 IsNull and other contributors + + 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 3 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, see . \ No newline at end of file diff --git a/Lib/sqlite3.def b/Lib/sqlite3.def new file mode 100644 index 0000000..58e4dff --- /dev/null +++ b/Lib/sqlite3.def @@ -0,0 +1,203 @@ +EXPORTS +sqlite3_aggregate_context +sqlite3_aggregate_count +sqlite3_auto_extension +sqlite3_backup_finish +sqlite3_backup_init +sqlite3_backup_pagecount +sqlite3_backup_remaining +sqlite3_backup_step +sqlite3_bind_blob +sqlite3_bind_double +sqlite3_bind_int +sqlite3_bind_int64 +sqlite3_bind_null +sqlite3_bind_parameter_count +sqlite3_bind_parameter_index +sqlite3_bind_parameter_name +sqlite3_bind_text +sqlite3_bind_text16 +sqlite3_bind_value +sqlite3_bind_zeroblob +sqlite3_blob_bytes +sqlite3_blob_close +sqlite3_blob_open +sqlite3_blob_read +sqlite3_blob_reopen +sqlite3_blob_write +sqlite3_busy_handler +sqlite3_busy_timeout +sqlite3_changes +sqlite3_clear_bindings +sqlite3_close +sqlite3_collation_needed +sqlite3_collation_needed16 +sqlite3_column_blob +sqlite3_column_bytes +sqlite3_column_bytes16 +sqlite3_column_count +sqlite3_column_database_name +sqlite3_column_database_name16 +sqlite3_column_decltype +sqlite3_column_decltype16 +sqlite3_column_double +sqlite3_column_int +sqlite3_column_int64 +sqlite3_column_name +sqlite3_column_name16 +sqlite3_column_origin_name +sqlite3_column_origin_name16 +sqlite3_column_table_name +sqlite3_column_table_name16 +sqlite3_column_text +sqlite3_column_text16 +sqlite3_column_type +sqlite3_column_value +sqlite3_commit_hook +sqlite3_compileoption_get +sqlite3_compileoption_used +sqlite3_complete +sqlite3_complete16 +sqlite3_config +sqlite3_context_db_handle +sqlite3_create_collation +sqlite3_create_collation16 +sqlite3_create_collation_v2 +sqlite3_create_function +sqlite3_create_function16 +sqlite3_create_function_v2 +sqlite3_create_module +sqlite3_create_module_v2 +sqlite3_data_count +sqlite3_db_config +sqlite3_db_filename +sqlite3_db_handle +sqlite3_db_mutex +sqlite3_db_readonly +sqlite3_db_release_memory +sqlite3_db_status +sqlite3_declare_vtab +sqlite3_enable_load_extension +sqlite3_enable_shared_cache +sqlite3_errcode +sqlite3_errmsg +sqlite3_errmsg16 +sqlite3_exec +sqlite3_expired +sqlite3_extended_errcode +sqlite3_extended_result_codes +sqlite3_file_control +sqlite3_finalize +sqlite3_free +sqlite3_free_table +sqlite3_get_autocommit +sqlite3_get_auxdata +sqlite3_get_table +sqlite3_global_recover +sqlite3_initialize +sqlite3_interrupt +sqlite3_last_insert_rowid +sqlite3_libversion +sqlite3_libversion_number +sqlite3_limit +sqlite3_load_extension +sqlite3_log +sqlite3_malloc +sqlite3_memory_alarm +sqlite3_memory_highwater +sqlite3_memory_used +sqlite3_mprintf +sqlite3_mutex_alloc +sqlite3_mutex_enter +sqlite3_mutex_free +sqlite3_mutex_leave +sqlite3_mutex_try +sqlite3_next_stmt +sqlite3_open +sqlite3_open16 +sqlite3_open_v2 +sqlite3_os_end +sqlite3_os_init +sqlite3_overload_function +sqlite3_prepare +sqlite3_prepare16 +sqlite3_prepare16_v2 +sqlite3_prepare_v2 +sqlite3_profile +sqlite3_progress_handler +sqlite3_randomness +sqlite3_realloc +sqlite3_release_memory +sqlite3_reset +sqlite3_reset_auto_extension +sqlite3_result_blob +sqlite3_result_double +sqlite3_result_error +sqlite3_result_error16 +sqlite3_result_error_code +sqlite3_result_error_nomem +sqlite3_result_error_toobig +sqlite3_result_int +sqlite3_result_int64 +sqlite3_result_null +sqlite3_result_text +sqlite3_result_text16 +sqlite3_result_text16be +sqlite3_result_text16le +sqlite3_result_value +sqlite3_result_zeroblob +sqlite3_rollback_hook +sqlite3_rtree_geometry_callback +sqlite3_set_authorizer +sqlite3_set_auxdata +sqlite3_shutdown +sqlite3_sleep +sqlite3_snprintf +sqlite3_soft_heap_limit +sqlite3_soft_heap_limit64 +sqlite3_sourceid +sqlite3_sql +sqlite3_status +sqlite3_step +sqlite3_stmt_busy +sqlite3_stmt_readonly +sqlite3_stmt_status +sqlite3_stricmp +sqlite3_strnicmp +sqlite3_table_column_metadata +sqlite3_test_control +sqlite3_thread_cleanup +sqlite3_threadsafe +sqlite3_total_changes +sqlite3_trace +sqlite3_transfer_bindings +sqlite3_update_hook +sqlite3_uri_boolean +sqlite3_uri_int64 +sqlite3_uri_parameter +sqlite3_user_data +sqlite3_value_blob +sqlite3_value_bytes +sqlite3_value_bytes16 +sqlite3_value_double +sqlite3_value_int +sqlite3_value_int64 +sqlite3_value_numeric_type +sqlite3_value_text +sqlite3_value_text16 +sqlite3_value_text16be +sqlite3_value_text16le +sqlite3_value_type +sqlite3_vfs_find +sqlite3_vfs_register +sqlite3_vfs_unregister +sqlite3_vmprintf +sqlite3_vsnprintf +sqlite3_vtab_config +sqlite3_vtab_on_conflict +sqlite3_wal_autocheckpoint +sqlite3_wal_checkpoint +sqlite3_wal_checkpoint_v2 +sqlite3_wal_hook +sqlite3_win32_mbcs_to_utf8 +sqlite3_win32_utf8_to_mbcs diff --git a/Lib/sqlite3.dll b/Lib/sqlite3.dll new file mode 100644 index 0000000..981efa1 Binary files /dev/null and b/Lib/sqlite3.dll differ diff --git a/Lib/x64/sqlite3.dll b/Lib/x64/sqlite3.dll new file mode 100644 index 0000000..4b2fffd Binary files /dev/null and b/Lib/x64/sqlite3.dll differ diff --git a/Main.ahk b/Main.ahk new file mode 100644 index 0000000..0d2d90e --- /dev/null +++ b/Main.ahk @@ -0,0 +1,43 @@ +#NoEnv ; Recommended for performance and compatibility with future AutoHotkey releases. +;#Warn ; Recommended for catching common errors. +SendMode Input ; Recommended for new scripts due to its superior speed and reliability. +SetWorkingDir, %A_ScriptDir% ; Ensures a consistent starting directory. +SetBatchLines -1 +#SingleInstance force +#NoTrayIcon + +/* =============================================================================== + * LifeRPG r2 - Motivation and Confidence Building System + * Initial Release 9/20/2012 + * + * Copyright (c) 2012 by Jayvant Javier Pujara + * Licensed under GPL + * JJPujara@gmail.com + * + * + * =============================================================================== + */ + +#Include +#Include Settings.ahk +#Include HUD.ahk +#Include Momentum.ahk +#Include Functions.ahk +#Include MenuBar.ahk +#Include ProjectsView.ahk +#Include Hotkeys.ahk +#Include Search.ahk +#Include ProjectManage.ahk +#Include ProjectRemove.ahk +#Include ProjectComplete.ahk +#Include SubprojectAdd.ahk +#Include SkillsView.ahk +#Include ProjectLog.ahk +#Include ProfileEdit.ahk +#Include SettingsEdit.ahk +#Include About.ahk +#Include Help.ahk +#Include FileManage.ahk + +MenuHandler: +return \ No newline at end of file diff --git a/MenuBar.ahk b/MenuBar.ahk new file mode 100644 index 0000000..9f2f1d2 --- /dev/null +++ b/MenuBar.ahk @@ -0,0 +1,39 @@ +; Menu Bar: =================================================================================== +Gui, 1:Default +; File:========================================== +Menu, FileMenu, Add, &New...`tCtrl+N, FileNew +Menu, FileMenu, Add, &Open...`tCtrl+O, FileOpen + +;~ ; Create another menu destined to become a submenu of the above menu. +;~ Menu, Submenu1, Add, Item1, MenuHandler +;~ Menu, Submenu1, Add, Item2, MenuHandler +;~ ; Create a submenu in the first menu (a right-arrow indicator). When the user selects it, the second menu is displayed. +;~ Menu, FileMenu, Add, Recently Opened, :Submenu1 + +;~ ^ Leave for later release + +Menu, FileMenu, Add +Menu, FileMenu, Add, E&xit, GuiClose + +; View:=========================================== +Menu, ViewMenu, Add, &Skill Stats...`tCtrl+K, SkillsView +Menu, ViewMenu, Add, &Project Log...`tCtrl+L, ProjectLog + +; Options:========================================= +Menu, OptionsMenu, Add, &Profile...`tCtrl+P, ProfileEdit +Menu, OptionsMenu, Add, &Settings...`tCtrl+S, SettingsEdit + +; Help:=========================================== +Menu, HelpMenu, Add, &Reference..., ReferenceHotkeys +Menu, HelpMenu, Add, &Discussion, Discussion +Menu, HelpMenu, Add +Menu, HelpMenu, Add, &About, About + + +; Attach the sub-menus that were created above. +Menu, MenuBar, Add, &File, :FileMenu +Menu, MenuBar, Add, &View, :ViewMenu +Menu, MenuBar, Add, &Options, :OptionsMenu +Menu, MenuBar, Add, &Help, :HelpMenu + +Gui, Menu, MenuBar \ No newline at end of file diff --git a/Momentum.ahk b/Momentum.ahk new file mode 100644 index 0000000..f8d3464 --- /dev/null +++ b/Momentum.ahk @@ -0,0 +1,41 @@ +; Momentum Bar: ================================================== +; Get date momentum bar last updated: +MomentumLastUpdate := ProfileGet("MMTLastUpdate") + +MomentumTimer() + +MomentumTimer(){ + global db, HUD_MomentumBar, HUD_MomentumPerc, MomentumLastUpdate + ; Start timer to check current date: + gosub MomentumUpdate + SetTimer, MomentumUpdate, 1000 + return + + MomentumUpdate: + CurrentDate := FormatTime(,"yyyyMMdd") + ; When current date does not match date momentum bar last updated, + if (MomentumLastUpdate <> CurrentDate) ; Momentum bar needs to be lowered: + { + ; Compare both dates to see how long ago in days last update was: + DateDiff := CurrentDate + DateDiff -= MomentumLastUpdate, Days + ; Multiply difference in days by percentage loss in MMT bar, + MMTLoss := DateDiff * 15 + ; and move MMT down: + ; Check the database to see what the current momentum level is. + MMTCurrent := ProfileGet("momentum") + ; Calculate current level minus calculated loss. + MMTNew := MMTCurrent - MMTLoss + ; If result is 0 or less than 0, just make the MMT level 0: + if (MMTNew <= 0) + MMTNew = 0 + ; Update database and HUD momentum bar: + db.Query("UPDATE profile SET value = " . MMTNew . " WHERE setting = 'momentum'") ; update momentum value in database + db.Query("UPDATE profile SET value = " . CurrentDate . " WHERE setting = 'MMTLastUpdate'") ; update when MMT last updated + MMTNow := ProfileGet("momentum") + GuiControl, HUD_Momentum:, HUD_MomentumBar, % MMTNow + GuiControl, HUD_Momentum:, HUD_MomentumPerc, % MMTNow . "%" + MomentumLastUpdate := ProfileGet("MMTLastUpdate") + } + return +} \ No newline at end of file diff --git a/OFL.txt b/OFL.txt new file mode 100644 index 0000000..af921e9 --- /dev/null +++ b/OFL.txt @@ -0,0 +1,93 @@ +Copyright (c) 2011, Cyreal (www.cyreal.org), +with Reserved Font Name "Electrolize". +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +http://scripts.sil.org/OFL + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/ProfileEdit.ahk b/ProfileEdit.ahk new file mode 100644 index 0000000..7b29a23 --- /dev/null +++ b/ProfileEdit.ahk @@ -0,0 +1,41 @@ +; Edit User Profile:================================================================ +ProfileEdit: +; Initialize modal child GUI window: +GuiChildInit("Profile") +; Define size and title etc: +ProfileW = 230 +ProfileH = 140 +ProfileX := CenterX(ProfileW) +ProfileY := CenterY(ProfileH) +ProfileTitle := "Edit Your Profile" + +; Create content and fields: +; Name: +Gui, Profile:Add, Text, , Name: +Gui, Profile:Add, Edit, vProfileNameEdit w120 Limit21 r1, % ProfileGet("name") +; Title: +Gui, Profile:Add, Text, , Title: +Gui, Profile:Add, Edit, vProfileTitleEdit w200 r1, % ProfileGet("title") +; Save button: +Gui, Profile:Add, Button, Default y+10 w80 gProfileSubmit, Save +; Cancel: +Gui, Profile:Add, Button, x+10 w80 gProfileGuiClose, Cancel + +; Show GUI: +Gui, Show, w%ProfileW% h%ProfileH% x%ProfileX% y%ProfileY%, %ProfileTitle% +; hang out here until user saves or closes: +return + +; What do to when user submits: +ProfileSubmit: +Gui, Profile:Submit, NoHide +db.Query("UPDATE profile SET value = '" . SafeQuote(ProfileNameEdit) . "' WHERE setting = 'name'") +db.Query("UPDATE profile SET value = '" . SafeQuote(ProfileTitleEdit) . "' WHERE setting = 'title'") +GuiControl, HUD_Level:, HUD_Name, % ProfileGet("name") +GuiControl, HUD_Level:, HUD_Text, % HUD_LevelText . LevelCheck() . " " . ProfileGet("title") + +; What to do when user closes or escapes window: +ProfileGuiClose: +ProfileGuiEscape: +GuiChildClose("Profile") ; Close up GUI child window. +return \ No newline at end of file diff --git a/ProjectComplete.ahk b/ProjectComplete.ahk new file mode 100644 index 0000000..0e0cb1e --- /dev/null +++ b/ProjectComplete.ahk @@ -0,0 +1,88 @@ +;~ =============================================================================== +;~ Confirm Project completion: + +CompleteProject: +Selection := LV_GetNext("","F") +LV_GetText(SelectedProjectID, Selection, 1) +LV_GetText(ProjectCompletionState, Selection, 2) +If (SelectedProjectID == "ID" || ProjectCompletionState = "Done") +{ + return +} +else +{ + GuiMsgBox("CompleteProject", "Complete Project", "Done with project?") + return + + CompleteProjectYes: + Gui, CompleteProject:Submit, NoHide + GuiChildClose("CompleteProject") + + CompleteProject(SelectedProjectID) + MomentumPrev := ProfileGet("momentum") + if (MomentumPrev < 100) + { + Anim := 100 - MomentumPrev + Loop % Anim + { + GuiControl, HUD_Momentum:, HUD_MomentumBar, % MomentumPrev + A_Index + GuiControl, HUD_Momentum:, HUD_MomentumPerc, % MomentumPrev + A_Index . "%" + Sleep 10 + } + ProfileSet("momentum", 100) + Notification(Uppercase("Momentum Restored"), "Your MMT is back to 100%") + } + gosub FilterUpdate + RefreshSkillsList(FilterSkillSelected) + return + + CompleteProjectNo: + CompleteProjectGuiClose: + CompleteProjectGuiEscape: + GuiChildClose("CompleteProject") + return +} +return + +CompleteProject(SelectedProjectID) +{ + global db, Difficulties, Awards + ; Get the difficulty to know how many points to award and the skill to show in notification + CompletedProject := db.OpenRecordSet("SELECT * FROM projects WHERE id = " SelectedProjectID) + while (!CompletedProject.EOF) + { + DifficultyToAward := CompletedProject["difficulty"] + SkillToIncrease := CompletedProject["skill"] + CompletedProject.MoveNext() + } + CompletedProject.Close() + + ; Mark project as done: + db.Query("UPDATE projects SET difficulty = 'Done', importance = '', dateDone = " . A_Now . ", levelDone = " . LevelGet() . " WHERE id = " SelectedProjectID) + + ; Get the level count for the skill if the project has one: + if (SkillToIncrease) + { + Table := db.Query("SELECT COUNT(skill) FROM projects WHERE skill = '" . SkillToIncrease . "' AND difficulty = 'Done'") + columnCount := table.Columns.Count() + for each, row in table.Rows + { + Loop, % columnCount + SkillLevel := row[A_index] + } + } + + ; Get the amount of points to award for the chosen level + for Num, Difficulty in Difficulties + { + if (DifficultyToAward = Difficulty) + for Key, Award in Awards + { + if (Num = Key) + AwardGiven := Award + } + } + UpdateProgress(DifficultyToAward . " Achievement", AwardGiven) + if (SkillToIncrease) + Notification("SKILL INCREASED", SkillToIncrease . " increased to " . SkillLevel) +} \ No newline at end of file diff --git a/ProjectLog.ahk b/ProjectLog.ahk new file mode 100644 index 0000000..09cf0df --- /dev/null +++ b/ProjectLog.ahk @@ -0,0 +1,78 @@ +; Project Log Dialog/Window: ============================================ + +;#If !WinActive(ProjectLogTitle . " ahk_class AutoHotkeyGUI") && WinActive("LifeRPG ahk_class AutoHotkeyGUI") +;^l:: +ProjectLog: +ProjectLogTitle := "Project Log" +GuiChildInit("ProjectLog") +;Notification(FilterSkillSelected,"") +Gui, ProjectLog:Add, Button, gProjectLogDateMoveBack, < +Gui, ProjectLog:Add, DateTime, vProjectLogDate gProjectLogRefresh x+1, LongDate +Gui, ProjectLog:Add, Button, gProjectLogDateMoveForward x+1, > +ColProjLogTime = 1 +ColProjLogName = 2 +ColProjLogSkill = 3 +ColProjLogLevel = 4 +PLw = 600 +PLh = 400 +Gui, ProjectLog:Add, ListView, y+1 xm w%PLw% r10 -Multi vProjectLogList gProjectLogRefresh, Time|Project|Skill|Level ; Set up skills list LV +PLx := CenterX(PLw) +PLy := CenterY(PLh) +gosub ProjectLogRefresh +Gui, ProjectLog:Show, x%PLx% y%PLy%, % ProjectLogTitle ;Project Log ; Show Project Log window +Send {Right 2} +return + +ProjectLogRefresh: +Gui, ProjectLog:ListView, ProjectLogList +GuiControlGet, ProjectLogDate, , ProjectLogDate +LV_Delete() +ProjectLogSet := db.OpenRecordSet("SELECT * FROM projects WHERE dateDone LIKE '" . FormatTime(ProjectLogDate,"yyyyMMdd") . "%'") +while (!ProjectLogSet.EOF) +{ + ProjectLogTime := ProjectLogSet["dateDone"] + ProjectLogName := ProjectLogSet["project"] + ProjectLogSkill := ProjectLogSet["skill"] + ProjectLogLevel := ProjectLogSet["levelDone"] + LV_Add("", ProjectLogTime, ProjectLogName, ProjectLogSkill, ProjectLogLevel) + ProjectLogSet.MoveNext() +} +ProjectLogSet.Close() +GuiControl, -Redraw, ProjectLogList +LV_ModifyCol(ColProjLogTime, "sortasc") +Loop % LV_GetCount() +{ + LV_GetText(PLRow, A_Index, ColProjLogTime) + LV_Modify(A_Index, "", FormatTime(PLRow, "Time")) +} +LV_ModifyCol() +Loop % LV_GetCount("Col") +{ + LV_ModifyCol(A_Index, "AutoHDR") +} +GuiControl, +Redraw, ProjectLogList +return + +ProjectLogDateMoveBack: +ProjectLogDateMove("Backward") +return + +ProjectLogDateMoveForward: +ProjectLogDateMove("Forward") +return + +ProjectLogDateMove(Direction) +{ + GuiControlGet, ProjLogCurrDate, , ProjectLogDate + if (Direction = "Forward") + ProjLogCurrDate += 1, Days + else if (Direction = "Backward") + ProjLogCurrDate += -1, Days + GuiControl, ProjectLog:, ProjectLogDate, % ProjLogCurrDate + gosub ProjectLogRefresh +} + +ProjectLogGuiEscape: +ProjectLogGuiClose: +GuiChildClose("ProjectLog") +return \ No newline at end of file diff --git a/ProjectManage.ahk b/ProjectManage.ahk new file mode 100644 index 0000000..0090de8 --- /dev/null +++ b/ProjectManage.ahk @@ -0,0 +1,215 @@ +; QuickAdd: +#If !WinExist("ahk_group exclude") +^!d:: +Action := "QuickDone" +ProjectManage(Action) +return +#If + +#If !WinExist("ahk_group exclude") +^!a:: +Action := "QuickAdd" +ProjectManage(Action) +return +#If + +; Add a new project: +AddProject: +Action := "Add" +ProjectManage(Action) +return + +; Edit a selected project: +EditProject: +Action := "Edit" +ProjectManage(Action) +return + +SkillAutoComplete: +Critical +Gui, ProjectManager:Submit, NoHide +If (!GetKeyState("BackSpace","P") && ProjectSkillEdit && Pos := InStr(SkillsDB, "|" . ProjectSkillEdit)) +{ + Found := SubStr(SkillsDB, pos+1, InStr(SkillsDB, "|", 1, Pos + 1) - Pos - 1) + GuiControl, ProjectManager:Text, ProjectSkillEdit, %Found% + SendInput % "{End}" . "+{Left " . StrLen(Found) - StrLen(ProjectSkillEdit) . "}" +} +return + +ProjectManagerSubmit: +Gui, ProjectManager:Submit, NoHide +if (ProjectNameEdit = "") +{ + MsgBox, 8192, Error, Can't make a project with no name! + return +} +if (ProjectSkillEdit = "All" || ProjectSkillEdit = "None") +{ + MsgBox, 8192, Error, "All" and "None" can't be used as skill names! + return +} +if (Action = "Add" || Action = "QuickDone" || Action = "QuickAdd") +{ + Record := {} + Record.Project := ProjectNameEdit + Record.Difficulty := ProjectDifficultyEdit + Record.Importance := ProjectImportanceEdit + Record.Skill := Capitalize(ProjectSkillEdit) + Record.dateEntered := A_Now + S := db.Insert(Record, "projects") + if (Action = "QuickDone" || Action = "QuickAdd") + { + Gui, ProjectManager:Cancel + Gui, 1:Default + if (Action = "QuickDone") + { + table := db.Query("SELECT MAX(id) FROM projects") + columnCount := table.Columns.Count() + for each, row in table.Rows + { + Loop, % columnCount + QuickID := row[A_index] + } + CompleteProject(QuickID) + } + } +} +else if (Action = "Edit") +{ + db.Query("UPDATE projects SET project = '" SafeQuote(ProjectNameEdit) "' WHERE ID = " SelectedProjectID ) + db.Query("UPDATE projects SET difficulty = '" SafeQuote(ProjectDifficultyEdit) "' WHERE ID = " SelectedProjectID ) + db.Query("UPDATE projects SET importance = '" SafeQuote(ProjectImportanceEdit) "' WHERE ID = " SelectedProjectID ) + ; Make sure skill is title cased, just to look good: + StringUpper, ProjectSkillEdit, ProjectSkillEdit, T + db.Query("UPDATE projects SET skill = '" SafeQuote(ProjectSkillEdit) "' WHERE ID = " SelectedProjectID ) +} +;UpdateList(Selection, FilterImportanceSelected, FilterSkillSelected) +;Notification(Selection . FilterImportanceSelected . FilterSkillSelected, "") +;GuiControlGet, FilterSkillSelected, , FilterSkill +if (Action = "Add" || Action = "Edit") +{ + GuiChildClose("ProjectManager") +} +else if (Action = "QuickAdd" || Action = "QuickDone") +{ + Gui, ProjectManager:Cancel + Gui, 1:Default +} +gosub FilterUpdate +RefreshSkillsList(FilterSkillSelected) +return + +; Fall through below to close window. +ProjectManagerGuiEscape: +ProjectManagerGuiClose: +if (Action = "Add" || Action = "Edit") +{ + GuiChildClose("ProjectManager") +} +else if (Action = "QuickAdd" || Action = "QuickDone") +{ + Gui, ProjectManager:Cancel + Gui, 1:Default +} +return + + +ProjectManage(Action) +{ + ;global db, ProjectNameEdit, ProjectDifficultyEdit, ProjectImportanceEdit, ProjectSkillEdit + global + ProjectNameEdit = + ProjectDifficultyEdit = + ProjectImportanceEdit = + ProjectSkillEdit = + ; Get the row number of the selected project from the main project ListView: + Selection := LV_GetNext("","F") + ; If editing, get the ID number of that project: + if (Action = "Edit") + { + LV_GetText(SelectedProjectID, Selection, 1) ; Get project ID number from hidden column of ListView + ; If no row is selected and edit is called, do nothing and go back: + If (SelectedProjectID == "ID") + { + return + } + else ; Get the data for the selected project to populate the edit fields: + { + ProjectInfo := db.OpenRecordSet("SELECT * FROM projects WHERE id = " SelectedProjectID ) + while(!ProjectInfo.EOF) + { + ProjectName := ProjectInfo["project"] + ProjectDifficulty := ProjectInfo["difficulty"] + ProjectImportance := ProjectInfo["importance"] + ProjectSkill := ProjectInfo["skill"] + ProjectInfo.MoveNext() + } + ProjectInfo.Close() + } + } + else if (Action = "Add" || Action = "QuickDone" || Action = "QuickAdd") + { + ProjectName = + ProjectDifficulty = + ProjectImportance = + ProjectSkill = + } + ; Build the GUI window to either add or edit a project: + ; Initiate a modal child window owned by the main window (by default): + if (Action = "Add" || Action = "Edit") + GuiChildInit("ProjectManager") + else if (Action = "QuickDone" || Action = "QuickAdd") + { + Gui, ProjectManager:New + Gui, ProjectManager:Default + } + + ; Name of project: + Gui, ProjectManager:Add, Text, , &Project Name: + Gui, ProjectManager:Add, Edit, vProjectNameEdit W270 r1, %ProjectName% + + ; Difficulty: + Gui, ProjectManager:Add, Text, , &Difficulty: + DifficultyList := ListDifficulties(ProjectDifficulty) + Gui, ProjectManager:Add, DropDownList, vProjectDifficultyEdit, %DifficultyList% + + ; Importance: + if (Action <> "QuickDone") + { + Gui, ProjectManager:Add, Text, , &Importance: + PriorityList := ListPriorities(ProjectImportance) + Gui, ProjectManager:Add, DropDownList, vProjectImportanceEdit, %PriorityList% + } + + ; Skill: + Gui, ProjectManager:Add, Text, x+20 y52, Set S&kill: + ; To set this, we need to go through all the skills on the table and get all the distinct skills: + SkillsDB := ListSkills(ProjectSkill) + Gui, ProjectManager:Add, ComboBox, vProjectSkillEdit gSkillAutoComplete w130 r7, % SkillsDB ;% ListSkills(ProjectSkill) ;Diplomacy|Art|Piracy|Cleaning|Hunting + + ; Submit button: + Gui, ProjectManager:Add, Button, Default gProjectManagerSubmit w80 x15 y+70, &Submit + + ; Set size of this window: + Width = 300 + Height = 200 + + ; Calculate position for centering this child GUI window on wherever the main project list window is: + xc := CenterX(300) + yc := CenterY(200) + ; Show window: + StringUpper, Action, Action, T + if (Action = "QuickDone") + { + + StringReplace, PMTitle, Action, done, Done + } + else if (Action = "QuickAdd") + StringReplace, PMTitle, Action, add, Add + else + PMTitle := Action + Gui, ProjectManager:Show, w%Width% h%Height% x%xc% y%yc%, %PMTitle% Project + return +} + +; Kind of duplicate with other skills get function I just made diff --git a/ProjectRemove.ahk b/ProjectRemove.ahk new file mode 100644 index 0000000..4087bb5 --- /dev/null +++ b/ProjectRemove.ahk @@ -0,0 +1,33 @@ +;~ =============================================================================== +;~ Confirm project deletion/removal: + +RemoveProject: +Selection := LV_GetNext("","F") +LV_GetText(SelectedProjectID, Selection, 1) +If (SelectedProjectID == "ID") +{ + return +} +else +{ + GuiMsgBox("RemoveProject", "Remove Project", "Delete this project?") + return + + RemoveProjectYes: + Gui, RemoveProject:Submit, NoHide + db.Query("DELETE FROM projects WHERE id = " SelectedProjectID ) + GuiChildClose("RemoveProject") + RefreshSkillsList(FilterSkillSelected) + ;Notification("RefreshSkillList(",FilterSkillSelected) + gosub FilterUpdate + ;Notification(Selection . ", " . FilterImportanceSelected . ", " . FilterSkillSelected,"") + UpdateList(Selection, FilterImportanceSelected, FilterSkillSelected) + return + + RemoveProjectNo: + RemoveProjectGuiClose: + RemoveProjectGuiEscape: + GuiChildClose("RemoveProject") + return +} +return \ No newline at end of file diff --git a/ProjectsView.ahk b/ProjectsView.ahk new file mode 100644 index 0000000..48ac09a --- /dev/null +++ b/ProjectsView.ahk @@ -0,0 +1,78 @@ +;~ =============================================================================== +;~ Building and Displaying the Main GUI: +Send !{F2} + +; Improves performance for adding elements to ListView: +CountUp := db.Query("SELECT * FROM projects") +CountUp := CountUp.Rows.Count() + +WinVis = true +; Buttons for Main Gui Window: +Gui, 1:Default +Gui, Add, Button, y3 x15 gAddProject, &Add Project ; Press Alt+A to add project +Gui, Add, Button, y3 x+1 gEditProject, &Edit Project ; Edit project (Alt+E, and so on) +Gui, Add, Button, y3 x+1 gAddSubproject, Su&bproject ; Create subproject for selected task +Gui, Add, Button, y3 x+1 gCompleteProject, Project &Done ; Confirm project is done +Gui, Add, Button, y3 x+1 gRemoveProject, &Remove Project ; Confirm project deletion + +;~ Search bar: +Gui, Add, Text, x15 y+1, &Search:%A_Space% ; Pressing Alt+C once focuses on search box +try { +Gui, Add, Edit, vSearchQuery gSearch x+1 w320 h20, +Gui, Add, Button, gClearSearch vClearSearchButton x+1, &Clear ; Pressing Alt+C again clears the search and thus resets the ListView + + +;~ Filter view by importance: +Gui, Add, Text, x+10 vImpChooseText, &Importance: +Gui, Add, DropDownList, vImportanceChoose gImportanceUpdate x+5 w60, All|| ; Filtering subroutines are located in Search.ahk +GuiControl, , ImportanceChoose, % ListPriorities("All") + +; Filter view by skill: +Gui, Add, Text, x+10 vSkillChooseText, S&kill: +Gui, Add, DropDownList, vFilterSkill gFilterSkillUpdate x+5 r10, All||None| +GuiControl, , FilterSkill, % ListSkills() + +; Show done or not: +Gui, Add, Checkbox, vFilterShowDone gFilterUpdate x+10, Show do&ne + + +;~ ListView: +Gui, Add, ListView, x0 y+15 r20 Grid AltSubmit -Multi Count%CountUp% vMainList hwndColored_LV_1, ID|Difficulty|Project|Importance|Parent|Color +} +Colored_LV_1_BG = 6 +GuiControl, Focus, SearchQuery ; Focus on search bar by default +Gui, Show, w827 h600, %AppTitle% ; Show the GUI we've created +UpdateList() ; Show all projects +Gui, +Resize +MinSize621x ; Make GUI resizable +return + +;~ =============================================================================== +;~ Main GUI Resizing information: + +GuiSize: +if A_EventInfo = 1 ; The window has been minimized. No action needed. + return +; Otherwise, the window has been resized or maximized. Resize the controls to match. +GuiControl, Move, Mainlist, % "H" . (A_GuiHeight - 55) . " W" . (A_GuiWidth) +; Resize search bar to fit dropdown filter controls: +if (A_GuiWidth > 811) ;827) +{ + SearchBarWidth := Round(A_GuiWidth*.40) +} +else if (A_GuiWidth <= 811) +{ + SearchBarWidth := Round(A_GuiWidth*.20) +} +GuiControl, MoveDraw, SearchQuery, % "w" SearchBarWidth +GuiControl, MoveDraw, ClearSearchButton, % "x" 50 + SearchBarWidth + 10 +GuiControl, MoveDraw, ImpChooseText, % "x" 50 + SearchBarWidth + 55 +GuiControl, MoveDraw, ImportanceChoose, % "x" 50 + SearchBarWidth + 120 +GuiControl, MoveDraw, SkillChooseText, % "x" 50 + SearchBarWidth + 190 +GuiControl, MoveDraw, FilterSkill, % "x" 50 + SearchBarWidth + 220 +GuiControl, MoveDraw, FilterShowDone, % "x" 50 + SearchBarWidth + 350 +return + +;~ =============================================================================== +;~ What to do when main window is closed: +GuiClose: +ExitApp \ No newline at end of file diff --git a/Res/.gitignore b/Res/.gitignore new file mode 100644 index 0000000..bf266b1 --- /dev/null +++ b/Res/.gitignore @@ -0,0 +1,168 @@ +*.exe +*.org +*.wav +*.db + +################# +## Eclipse +################# + +*.pydevproject +.project +.metadata +bin/ +tmp/ +*.tmp +*.bak +*.swp +*~.nib +local.properties +.classpath +.settings/ +.loadpath + +# External tool builders +.externalToolBuilders/ + +# Locally stored "Eclipse launch configurations" +*.launch + +# CDT-specific +.cproject + +# PDT-specific +.buildpath + + +################# +## Visual Studio +################# + +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. + +# User-specific files +*.suo +*.user +*.sln.docstates + +# Build results +[Dd]ebug/ +[Rr]elease/ +*_i.c +*_p.c +*.ilk +*.meta +*.obj +*.pch +*.pdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.vspscc +.builds +*.dotCover + +## TODO: If you have NuGet Package Restore enabled, uncomment this +#packages/ + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opensdf +*.sdf + +# Visual Studio profiler +*.psess +*.vsp + +# ReSharper is a .NET coding add-in +_ReSharper* + +# Installshield output folder +[Ee]xpress + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish + +# Others +[Bb]in +[Oo]bj +sql +TestResults +*.Cache +ClientBin +stylecop.* +~$* +*.dbmdl +Generated_Code #added for RIA/Silverlight projects + +# Backup & report files from converting an old project file to a newer +# Visual Studio version. Backup files are not needed, because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML + + + +############ +## Windows +############ + +# Windows image file caches +Thumbs.db + +# Folder config file +Desktop.ini + + +############# +## Python +############# + +*.py[co] + +# Packages +*.egg +*.egg-info +dist +build +eggs +parts +bin +var +sdist +develop-eggs +.installed.cfg + +# Installer logs +pip-log.txt + +# Unit test / coverage reports +.coverage +.tox + +#Translations +*.mo + +#Mr Developer +.mr.developer.cfg + +# Mac crap +.DS_Store diff --git a/Res/128px-Role-playing_video_game_icon.svg.png b/Res/128px-Role-playing_video_game_icon.svg.png new file mode 100644 index 0000000..72990a4 Binary files /dev/null and b/Res/128px-Role-playing_video_game_icon.svg.png differ diff --git a/Res/WP_RPG_VG.ico b/Res/WP_RPG_VG.ico new file mode 100644 index 0000000..0c72118 Binary files /dev/null and b/Res/WP_RPG_VG.ico differ diff --git a/Search.ahk b/Search.ahk new file mode 100644 index 0000000..6aa319c --- /dev/null +++ b/Search.ahk @@ -0,0 +1,45 @@ +;~ =============================================================================== +;~ Filter ListView by priority: + + +;~ UpdateList(,FilterImportanceSelected,FilterSkillSelected) +;~ return + +;~ =============================================================================== +; Filter main projects ListView by available skills: +FilterUpdate: +ImportanceUpdate: +FilterSkillUpdate: +GuiControlGet, FilterImportanceSelected, 1:, ImportanceChoose +GuiControlGet, FilterSkillSelected, 1:, FilterSkill +GuiControlGet, FilterShowDone, 1:, FilterShowDone +UpdateList(Selection,FilterImportanceSelected,FilterSkillSelected) +return + +;~ =============================================================================== +;~ Clear the search bar and reset the ListView: + +ClearSearch: +Critical +GuiControl, , ImportanceChoose, |All|| +GuiControl, , ImportanceChoose, % ListPriorities() + +GuiControl, , FilterSkill, |All||None| ; Put | at start to reset out the DDL +GuiControl, , FilterSkill, % ListSkills() + +GuiControl, , FilterShowDone, 0 +GuiControl, , SearchQuery +GuiControl, Focus, SearchQuery +return + +;~ =============================================================================== +;~ Search subroutine: + +Search: +Critical +GuiControlGet, SearchString, , SearchQuery +GuiControlGet, FilterImportanceSelected, , ImportanceChoose +GuiControlGet, FilterSkillSelected, , FilterSkill +GuiControlGet, FilterShowDone, +UpdateList(Selection, FilterImportanceSelected, FilterSkillSelected) +return \ No newline at end of file diff --git a/Settings.ahk b/Settings.ahk new file mode 100644 index 0000000..a35204f --- /dev/null +++ b/Settings.ahk @@ -0,0 +1,86 @@ +;~ Autoload and initial settings loading section:============================================ + +;~ "Character" class, disabled for now b/c have to make HUD progress bar automatically fit: +;CharacterClass := "Artist" + +;~ Set icon for window corner: +IconFile := "res/WP_RPG_VG.ico" +if FileExist(IconFile) + Menu, Tray, Icon, %IconFile% +Menu, Tray, NoStandard + +;~ Project difficulties: +Difficulties := ["Really Easy", "Pretty Easy", "Medium", "Hard"] + +; Award points for each difficulty: +Awards := [5, 10, 25, 100] + +; Difficulty colors: +Colors := [BGR("ADFF2F"), BGR("87CEFA"), BGR("FFD700"), BGR("FF6347")] + +;~ Priorities: +Priorities := ["Must", "Should", "Could", "Want"] + +BGR(RGB) +{ + R := SubStr(RGB, 1, 2) + G := SubStr(RGB, 3, 2) + B := SubStr(RGB, 5, 2) + return "0x" . B . G . R +} + +;~ The window title text: +AppTitle := "LifeRPG" + +;~ Make it easier for the script to identify its own window if need be: +WindowFind := AppTitle . " ahk_class AutoHotkeyGUI" + +;~ Level up sound location: +LevelUpSound := SettingGet("Sound", "LevelUp") +if (LevelUpSound = "Error" || !FileExist(LevelUpSound)) + LevelUpSound := "" + +; Open connection to SQLite database: +ConnectionString := SettingGet("File", "LastOpened") ; Get last used database from settings. +if (ConnectionString = "Error" || ConnectionString = "") ; That means it's the first time it was run, so load the default db. + ConnectionString := "data/LifeRPG.db" +AskLoad: +if (!FileExist(ConnectionString)) ; User must have deleted or moved last used db, so ask to pick another or make a new one. +{ + Gui +OwnDialogs + MsgBox, 51, %AppTitle% Error, Last loaded database `n"%connectionString%" `nwas not found.`n`nWould you like to open a different database?`nIf not, you must create a new one before you can continue.`n`nOtherwise, hit Cancel to quit the program. + IfMsgBox Yes + { + gosub FileOpen + if (!IsObject(db)) + gosub AskLoad + } + else IfMsgBox No + { + gosub FileNew + if (!IsObject(db)) + gosub AskLoad + } + else + ExitApp +} +else ; we can go ahead and load the last used db: + db := DBA.DataBaseFactory.OpenDataBase("SQLite", ConnectionString) + +; Hotkey do not activate list: +GroupAdd, exclude, New projects database +GroupAdd, exclude, Open a projects database +GroupAdd, exclude, Add Project ahk_class AutoHotkeyGUI +GroupAdd, exclude, Reference ahk_class AutoHotkeyGUI +GroupAdd, exclude, Edit Project ahk_class AutoHotkeyGUI +GroupAdd, exclude, Add Subproject ahk_class AutoHotkeyGUI +GroupAdd, exclude, Remove Project ahk_class AutoHotkeyGUI +GroupAdd, exclude, Complete Project ahk_class AutoHotkeyGUI +GroupAdd, exclude, QuickDone Project ahk_class AutoHotkeyGUI +GroupAdd, exclude, QuickAdd Project ahk_class AutoHotkeyGUI +GroupAdd, exclude, Skill Stats ahk_class AutoHotkeyGUI +GroupAdd, exclude, About ahk_class AutoHotkeyGUI +GroupAdd, exclude, Edit Your Profile ahk_class AutoHotkeyGUI +GroupAdd, exclude, Project Log ahk_class AutoHotkeyGUI +SettingsTitle := "Edit LifeRPG Settings" +GroupAdd, exclude, % SettingsTitle . " ahk_class AutoHotkeyGUI" diff --git a/SettingsEdit.ahk b/SettingsEdit.ahk new file mode 100644 index 0000000..05be03d --- /dev/null +++ b/SettingsEdit.ahk @@ -0,0 +1,59 @@ +; Edit app settings: =================================================== +;#If !WinActive("Skill Stats ahk_class AutoHotkeyGUI") && WinActive("LifeRPG ahk_class AutoHotkeyGUI") +;^s:: +SettingsEdit: +GuiChildInit("SettingsEdit") +; Define size and positions: +SettingsW = 400 +SettingsH = 140 +SettingsX := CenterX(SettingsW) +SettingsY := CenterY(SettingsH) + +; Create content and fields: +; Level Up Sound: +Gui, SettingsEdit:Add, Text, , Select sound file to use for &Level-Up Sound: +SettingLocationLevelUp := SettingGet("Sound","LevelUp") +if (SettingLocationLevelUp = "Error") + SettingLocationLevelUp := "" +Gui, SettingsEdit:Add, Edit, vSettingsEditLevelUpEdit w300 r1, % SettingLocationLevelUp +Gui, SettingsEdit:Add, Button, x+1 gLevelUpSoundBrowse w80, &Browse +Gui, SettingsEdit:Add, Button, y+1 xm gSoundTestLevelUp w40, Test +Gui, SettingsEdit:Add, Button, x+1 gSoundTestLevelUpStop w40, Stop + +; Save button: +Gui, SettingsEdit:Add, Button, Default y+30 xm w80 gSettingsEditSubmit, &Save +; Cancel: +Gui, SettingsEdit:Add, Button, x+10 w80 gSettingsEditGuiClose, &Cancel + +; Show GUI: +Gui, SettingsEdit:Show, w%SettingsW% h%SettingsH% x%SettingsX% y%SettingsY%, %SettingsTitle% +; hang out here until user saves or closes: +return + +LevelUpSoundBrowse: +Gui +OwnDialogs +FileSelectFile, NewLocationLevelUpSound, , , Select a sound file , Audio (*.wav; *.mp3) +GuiControl, SettingsEdit:, SettingsEditLevelUpEdit, % NewLocationLevelUpSound +return + +SoundTestLevelUp: +GuiControlGet, LUSFile, SettingsEdit:, SettingsEditLevelUpEdit +SoundPlay % LUSFile +return + +SoundTestLevelUpStop: +SoundPlay 341589134759384759348.wav +return + +; What do to when user submits: +SettingsEditSubmit: +Gui, SettingsEdit:Submit, NoHide +SettingSet("Sound","LevelUp", SettingsEditLevelUpEdit) +LevelUpSound := SettingsEditLevelUpEdit + + +; What to do when user closes or escapes window: +SettingsEditGuiClose: +SettingsEditGuiEscape: +GuiChildClose("SettingsEdit") ; Close up GUI child window. +return \ No newline at end of file diff --git a/SkillsView.ahk b/SkillsView.ahk new file mode 100644 index 0000000..1878e81 --- /dev/null +++ b/SkillsView.ahk @@ -0,0 +1,85 @@ +; View the user's current levels in all the skills he has completed projects in: + +SkillsView: +GuiChildInit("SkillsView") +;Notification(FilterSkillSelected,"") +ColSkillName = 1 +ColSkillLevel = 2 +Gui, SkillsView:Add, ListView, w300 r20 -Multi gSkillsListEvent vSkillsListView, Skill|Level ; Set up skills list LV +Gui, SkillsView:Add, Button, Hidden Default w0 h0 gSkillsListEvent, OK +SVw = 300 +SVh = 400 +SVx := CenterX(SVw) +SVy := CenterY(SVh) +; Populate the Skill Stats ListView with skills and stats: +; 1. Get the skills count for all done items from the projects table +; First we need to add all skills to the LV +; SELECT DISTINCT skill FROM projects WHERE difficulty <> 'Done' ORDER BY skill +; 2. Add to ListView +SkillsList := db.OpenRecordSet("SELECT DISTINCT skill FROM projects WHERE skill IS NOT NULL AND skill <> '' ORDER BY skill") +while (!SkillsList.EOF) +{ + SkillListName := SkillsList["skill"] + LV_Add("", SkillListName) + RowNum := A_Index + Table := db.Query("SELECT COUNT(skill) FROM projects WHERE skill = '" . SkillListName . "' AND difficulty = 'Done'") + columnCount := table.Columns.Count() + for each, row in table.Rows + { + Loop, % columnCount + ;msgbox % row[A_index] + LV_Modify(RowNum,"Col2", row[A_Index]) + } + SkillsList.MoveNext() +} +SkillsList.Close() +;LV_ModifyCol() +LV_ModifyCol(ColSkillLevel, "AutoHDR integer sortdesc") +Loop % LV_GetCount("Col") +{ + LV_ModifyCol(A_Index, "AutoHDR") +} +Gui, SkillsView:Show, x%SVx% y%SVy%, Skill Stats ; Show skills list window +SkillCount := LV_GetCount() +if (FilterSkillSelected = "All" || FilterSkillSelected = "None" || !FilterSkillSelected) + return +else +{ + Loop % SkillCount + { + HighlightLine := A_Index + LV_GetText(SkillToHighlight, HighlightLine, ColSkillName) + if (SkillToHighlight = FilterSkillSelected) + { + LV_Modify(HighlightLine, "Focus Select Vis") + } + } +} +return + +SkillsListEvent: ; Jump to double-clicked skill +GuiControlGet, FocusedControl, FocusV +if (FocusedControl = "SkillsListView") +{ + if (LV_GetNext(0, "Focused") = 0) + return + else + LV_GetText(SDC, LV_GetNext(0, "Focused")) +} +else if (A_GuiEvent = "DoubleClick" ) + LV_GetText(SDC, A_EventInfo) +GuiChildClose("SkillsView") +RefreshSkillsList(SDC) +UpdateList(,,FilterSkillSelected) +return + +SkillsViewGuiEscape: +SkillsViewGuiClose: +GuiChildClose("SkillsView") +return + +ExploreObj(Obj, NewRow="`n", Equal=" = ", Indent="`t", Depth=12, CurIndent="") { + for k,v in Obj + ToReturn .= CurIndent . k . (IsObject(v) && depth>1 ? NewRow . ExploreObj(v, NewRow, Equal, Indent, Depth-1, CurIndent . Indent) : Equal . v) . NewRow + return RTrim(ToReturn, NewRow) +} \ No newline at end of file diff --git a/SubprojectAdd.ahk b/SubprojectAdd.ahk new file mode 100644 index 0000000..d51002c --- /dev/null +++ b/SubprojectAdd.ahk @@ -0,0 +1,75 @@ +;~ =============================================================================== +;~ Add subproject for a selected project: + +AddSubproject: +Selection := LV_GetNext("","F") +LV_GetText(SelectedProjectID, Selection, 1) +If (SelectedProjectID == "ID") +{ + return +} +else +{ + ProjectInfo := db.OpenRecordSet("SELECT * FROM projects WHERE id = " SelectedProjectID ) + while(!ProjectInfo.EOF) + { + ParentProjectName := ProjectInfo["project"] + ProjectInfo.MoveNext() + } + ProjectInfo.Close() + ;UpdateList(Selection) +} +GuiChildInit("AddSubproject") +Gui, AddSubproject:Add, Text, w270, Parent Project:`n%ParentProjectName% +;Gui, AddSubproject:Add, Text, vParentName W270, %ParentProjectName% + +Gui, AddSubproject:Add, Text, , Subproject Name: +Gui, AddSubproject:Add, Edit, vProjectName W270, + +Gui, AddSubproject:Add, Text, section, &Difficulty: +Gui, AddSubproject:Add, DropDownList, vProjectDifficulty, % ListDifficulties("Really Easy") + +Gui, AddSubproject:Add, Text, ys, Set S&kill: +SPSkills := ListSkills() +Gui, AddSubproject:Add, ComboBox, vProjectSkill gSPSkillAutoComplete w130 r7, % SPSkills + +Gui, AddSubproject:Add, Text, xm, Impo&rtance: +Gui, AddSubproject:Add, DropDownList, vProjectImportance, % ListPriorities("Must") + +Gui, AddSubproject:Add, Button, Default gAddSubprojectSubmit w80 xm y+20, &Submit + +WinGetPos,xd,yd,wd,hd,%WindowFind% +xc := CenterX(300) +yc := CenterY(200) +Gui, AddSubproject:Show, w300 h240 x%xc% y%yc%, Add Subproject +return + +SPSkillAutoComplete: +Critical +Gui, AddSubproject:Submit, NoHide +If (!GetKeyState("BackSpace","P") && ProjectSkill && Pos := InStr(SPSkills, "|" . ProjectSkill)) +{ + Found := SubStr(SPSkills, pos+1, InStr(SPSkills, "|", 1, Pos + 1) - Pos - 1) + GuiControl, AddSubproject:Text, ProjectSkill, %Found% + SendInput % "{End}" . "+{Left " . StrLen(Found) - StrLen(ProjectSkill) . "}" +} +return + +AddSubprojectSubmit: +Gui, AddSubproject:Submit, NoHide +Record := {} +Record.Project := ProjectName +Record.Difficulty := ProjectDifficulty +Record.Importance := ProjectImportance +Record.Parent := SelectedProjectID +Record.skill := ProjectSkill +Record.dateEntered := A_Now +S := db.Insert(Record, "projects") +gosub FilterUpdate +RefreshSkillsList(FilterSkillSelected) + +AddSubprojectGuiEscape: +AddSubprojectGuiClose: +GuiChildClose("AddSubproject") +;UpdateList(Selection) +return \ No newline at end of file diff --git a/license.txt b/license.txt new file mode 100644 index 0000000..b9a09db --- /dev/null +++ b/license.txt @@ -0,0 +1,15 @@ + LifeRPG - Confidence and Motivation Building System + Copyright (C) 2012 Jayvant Javier Pujara and other contributors + + 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 3 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, see . \ No newline at end of file diff --git a/readme.txt b/readme.txt new file mode 100644 index 0000000..ec50d20 --- /dev/null +++ b/readme.txt @@ -0,0 +1,27 @@ + LifeRPG Release 2 - Confidence and Motivation Building System + by Jayvant Javier Pujara + JJPujara@gmail.com + + About: + Organize your life; build confidence, momentum, motivation, and have fun. + + Discussion: + http://www.reddit.com/r/LifeRPG/ + + + + + Copyright (C) 2012 Jayvant Javier Pujara and other contributors + + 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 3 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, see . \ No newline at end of file