從壓縮包/托盤圖示筆記本程式設計
這個程式的想法很簡單:將剪貼簿上的任何文字儲存在一個方便的小托盤圖示上,它就會被儲存到 SQLite 資料庫中。看起來沒什麼變化,但文字被儲存在一個新的記錄中。
還可以搜尋已經儲存的筆記。當您在托盤圖示上單擊滑鼠中鍵時,會彈出一個視窗。我更希望它在右鍵單擊圖示時出現,但右鍵單擊只能顯示選單。這就是右鍵單擊的作用:它們顯示上下文選單。現在,如果您有一個帶有滾輪或按鈕的滑鼠,可以單擊中鍵。對於那些使用筆記型電腦觸控板的人來說,中鍵單擊可以透過同時單擊左右按鈕來模擬。在 KDE 中,系統設定中有一個選項可以停用此功能,但預設情況下它是啟用的。
如果您願意,可以嘗試在應用程式啟動時隱藏主視窗,或者不使用 skiptaskbar 屬性,這樣最小化的主視窗就不會在面板中與其他開啟的應用程式一起列出,但這些效果並不吸引人,因此它們保持預設設定。
透過 Google 圖片搜尋可以找到適合托盤圖示的圖片
它不需要是任何特定的尺寸。它在程式執行時會很好地自動調整大小。
視窗有一個大的文字區域來顯示筆記,以及左上角的一個文字框,用於輸入搜尋文字。還有三個標籤顯示儲存筆記的日期/時間、筆記在使用該搜尋文字找到的所有筆記中的位置,以及為了便於檢視,筆記的記錄 ID。
托盤圖示(左下角)可以放置在任何位置。它不會出現在視窗中。它出現在系統托盤中(通常位於螢幕的右下角,在 KDE 的預設面板中)。
托盤圖示有自己的元件,因此請檢查專案 > 屬性以檢視它和資料庫元件是否已包含。
總之,要使用此筆記本
- 將任何文字複製到剪貼簿。
- 單擊托盤圖示,您的文字將被儲存。
- 同時單擊滑鼠左鍵和右鍵單擊托盤圖示,以搜尋筆記。
有時,儲存文字然後開啟視窗並新增一些關鍵詞來幫助您再次找到筆記會很有用。我有時會新增 JOKE、QUOTE 或 FAMILY HISTORY 等詞語。這樣,透過在搜尋框中鍵入“QUOTE”,我所有的引用都會出現,我可以一次檢視一個。複製所有選定的筆記將是一個有用的功能。
出於便於檢視的目的,SQL select 語句將出現在視窗標題中。
為了確保完善,文字選單有四個條目用於調整文字
輸入更多... 將游標定位在可見筆記文字的末尾,以便您輸入更多內容。
整理 將多個空格、多個製表符和多個空行刪除,並將開頭和結尾的空格刪除。
句子 將斷句合併。從電子郵件中複製的文字通常有不同的行。
雙空格 用空行分隔段落。
這些文字操作在沒有選擇文字的情況下會對整個文字進行操作,如果有選擇文字,則會對選定的文字進行操作。快捷鍵在那裡是因為我經常發現自己會連續快速執行最後三個操作來使文字看起來體面。
其他選單是
資料庫是一個 SQLite 檔案。您可能想知道 OPEN 和 NEW 選單項在哪裡。為簡單起見,程式在開啟時會建立一個新的資料庫,如果不存在與它預期名稱和位置的資料庫。它名為 Notebook.sqlite,它位於 Home 目錄中。如果您願意,可以很容易地更改程式碼以將其放置在 Documents 目錄中。
檔案 > 備份 是一個有用的選單項,它將資料庫檔案複製到 Documents 資料夾中的 Backups 資料夾,並使用日期戳來顯示檔案建立的時間,並防止干擾之前的備份。
檔案 > 重新編號 使記錄 ID 連續,沒有間隙。它和下面的 Vacuum 項是多餘的。Vacuum 將整理資料庫檔案的內部結構。它使用 SQLite 的內建 vacuum 命令。
檔案 > 退出 關閉應用程式。檔案 > 隱藏 使視窗不可見。單擊視窗上的關閉框也只會隱藏視窗;它不會關閉應用程式。為了實現此技巧,視窗的 Persistent 屬性設定為 true。當單擊視窗的關閉框時,視窗會持續存在,但不可見。
當您在搜尋文字框中鍵入時,一個 SQL Select 語句會選擇包含該文字的所有筆記。這是一個簡單的搜尋:它不會搜尋包含任何單詞的筆記,從最匹配到最不匹配排序。它搜尋與鍵入的文字完全匹配的文字。當您鍵入每個字母時,都會執行搜尋。mdb 模組中的一個公共屬性 Public rs As Result 儲存搜尋結果。筆記 > 顯示全部 清除搜尋並查詢所有筆記。這在程式啟動時發生:所有筆記都被選中。
筆記 > 新建... 允許您鍵入一個新的筆記,當您離開文字區域時該筆記會被儲存。
筆記 > 刪除 從資料庫中刪除當前顯示的筆記。
筆記 > 清除全部 從資料庫中清除所有筆記。沒有撤消操作,但有機會退出。
視窗屬性是
排列 = 垂直
堆疊 = 上方
寬度 = 800
高度 = 500
需要設定的托盤圖示 (ti1) 屬性是
工具提示 = 單擊以儲存剪貼簿文字 可見 = True
請務必將其設定為可見,否則您在執行程式時將看不到托盤圖示,並且在隱藏最初顯示的視窗後,您將無法以優雅的方式退出正在執行的程式。
堆疊 屬性確保視窗始終位於其他視窗之上。例如,如果您單擊網頁瀏覽器的視窗,筆記本視窗不會被瀏覽器視窗覆蓋,而是會始終位於最上面,漂浮在其之上。這對於實用程式型別的程式來說非常有用。
在檢視程式碼之前,最後要說明的是使用鍵盤的箭頭鍵。UP 和 DOWN 分別將您帶到找到的筆記的第一條和最後一條。LEFT 和 RIGHT 分別將您在找到的筆記中向前或向後移動。“找到的筆記”是指那些包含您在文字框中鍵入的文字字串的筆記,或者如果沒有鍵入任何內容,則指所有筆記。
程式碼後面有一些註釋。
與資料庫相關的程式碼收集在一個名為 mdb 的模組中。
' Gambas module file
Public db1 As New Connection
Public rs As Result
Public SQLSel As String
Public Sub CreateDatabase()
db1.Type = "sqlite"
db1.host = User.home
db1.name = ""
'delete an existing Notebook.sqlite
If Exist(User.home & "/Notebook.sqlite") Then
Kill User.home & "/Notebook.sqlite"
Endif
'create Notebook.sqlite
db1.Open
db1.Databases.Add("Notebook.sqlite")
db1.Close
End
Public Sub ConnectDatabase()
db1.Type = "sqlite"
db1.host = User.home
db1.name = "Notebook.sqlite"
db1.Open
SelectAllNotes
Debug("Notes file connected:<br>" & db1.Host &/ db1.Name & "<br><br>" & rs.Count & " records")
End
Public Sub MakeTable()
Dim hTable As Table
db1.name = "Notebook.sqlite"
db1.Open
hTable = db1.Tables.Add("Notes")
hTable.Fields.Add("KeyID", db.Integer)
hTable.Fields.Add("Created", db.Date)
hTable.Fields.Add("Note", db.String)
hTable.PrimaryKey = ["KeyID"]
hTable.Update
Message("Notes file created:<br>" & db1.Host &/ db1.Name)
End
Public Sub SelectAllNotes()
rs = db1.Exec("SELECT * FROM Notes")
FMain.Caption = "SELECT * FROM Notes"
rs.MoveLast
End
Public Sub Massage(z As String) As String
While InStr(z, "''") > 0 'this avoids a build-up of single apostrophes
Replace(z, "''", "'")
Wend
Return Replace(z, "'", "''")
End
Public Sub AddRecord(s As String, t As Date) As String
Dim rs1 As Result
Dim NextID As Integer
If rs.Max = -1 Then NextID = 1 Else NextID = db1.Exec("SELECT Max(KeyID) AS TheMax FROM Notes")!TheMax + 1
db1.Begin
rs1 = db1.Create("Notes")
rs1!KeyID = NextID
rs1!Created = t 'time
rs1!Note = Massage(s)
rs1.Update
db1.Commit
SelectAllNotes
Return NextID
Catch
db1.Rollback
Message.Error(Error.Text)
End
Public Sub UpdateRecord(RecNum As Integer, NewText As String)
db1.Exec("UPDATE Notes SET Note='" & Massage(NewText) & "' WHERE KeyID=" & RecNum)
Dim pos As Integer = rs.Index
'Refresh the result cursor, so the text in it is updated as well as in the database file. This is tricky.
If IsNull(SQLSel) Then rs = db.Exec("SELECT * FROM Notes") Else rs = db.Exec(SQLSel) 'SQLSel is the last search, set by typing in tbSearch
rs.MoveTo(pos) 'Ooooh yes! It did it.
Catch
Message.Error("<b>Update error.</b><br><br>" & Error.Text)
End
Public Sub MoveRecord(KeyCode As Integer) As Boolean
If rs.Count = 0 Then Return False
Select Case KeyCode
Case Key.Left
If rs.Index > 0 Then rs.MovePrevious Else rs.MoveLast
Return True
Case Key.Right
If rs.Index < rs.Max Then rs.MoveNext Else rs.MoveFirst
Return True
Case Key.Up
rs.MoveFirst
Return True
Case Key.Down
rs.MoveLast
Return True
End Select
Return False
End
Public Sub ClearAll()
If Message.Warning("Delete all notes? This cannot be undone.", "Ok", "Cancel") = 1 Then
db1.Exec("DELETE FROM Notes")
SelectAllNotes
Endif
End
Public Sub DeleteRecord(RecNum As Integer)
db1.Exec("DELETE FROM Notes WHERE KeyID='" & RecNum & "'")
SelectAllNotes
End
Public Sub SearchFor(s As String)
SQLSel = "SELECT * FROM Notes WHERE Note LIKE '%" & Massage(s) & "%'"
If IsNull(s) Then
SelectAllNotes
SQLSel = ""
Else
FMain.Caption = SQLSel
rs = db1.Exec(SQLSel)
Endif
End
Public Sub Renumber()
Dim res As Result = db.Exec("SELECT * FROM Notes ORDER BY KeyID")
Dim i As Integer = 1
Dim x As Integer
Application.Busy += 1
While res.Available
x = res!KeyID
db.Exec("UPDATE Notes SET KeyID=" & i & " WHERE KeyID=" & x)
i += 1
res.MoveNext
Wend
SelectAllNotes
Application.Busy -= 1
End
Public Sub Vacuum() As String
Dim fSize1, fSize2 As Float
fSize1 = Stat(db1.Host &/ db1.Name).Size / 1000 'kB
db1.Exec("Vacuum")
fSize2 = Stat(db1.Host &/ db1.Name).Size / 1000 'kB
Dim Units As String = "kB"
If fSize1 > 1000 Then 'megabyte range
fSize1 /= 1000
fSize2 /= 1000
Units = "MB"
Endif
Return Format(fSize1, "#.0") & Units & " -> " & Format(fSize2, "#.0") & Units & " (" & Format(fSize1 - fSize2, "#.00") & Units & ")"
End
Public Sub Backup() As String
If Not Exist(User.Home &/ "Documents/Backups/") Then Mkdir User.Home &/ "Documents/Backups"
Dim fn As String = "Notebook " & Format(Now, "yyyy-mm-dd hh-nn")
Dim source As String = db1.Host &/ db1.Name
Dim dest As String = User.Home &/ "Documents/Backups/" & fn
Try Copy source To dest
If Error Then Return "Couldn't save -> " & Error.Text Else Return "Saved -> /Documents/Backups/" & fn
End
' Gambas class file
Public OriginalText As String
Public Sub ti1_Click()
Dim TimeAdded As String
If Clipboard.Type = Clipboard.Text Then TimeAdded = mdb.AddRecord(Clipboard.Paste("text/plain"), Now())
End
Public Sub Form_Open()
If Not Exist(User.Home &/ "Notebook.sqlite") Then 'create notebook data file
mdb.CreateDatabase
mdb.MakeTable
mdb.SelectAllNotes
Else
mdb.ConnectDatabase
ShowRecord
Endif
End
Public Sub MenuQuit_Click()
mdb.db1.Close
ti1.Delete
Quit
End
Public Sub Form_KeyPress()
If mdb.MoveRecord(Key.Code) Then
ShowRecord
Stop Event
Endif
End
Public Sub ShowRecord()
If mdb.rs.count = 0 Then
ClearFields
Return
Endif
ta1.Text = Replace(mdb.rs!Note, "''", "'")
Dim d As Date = mdb.rs!Created
labTime.text = Format(d, gb.MediumDate) & " " & Format(d, gb.LongTime)
labRecID.text = mdb.rs!KeyID
labLocation.text = Str(mdb.rs.Index + 1) & "/" & mdb.rs.Count
OriginalText = ta1.Text
End
Public Sub MenuClear_Click()
mdb.ClearAll
ClearFields
labTime.Text = "No records"
End
Public Sub MenuCopy_Click()
Clipboard.Copy(ta1.Text)
End
Public Sub MenuDeleteNote_Click()
Dim RecNum As Integer = Val(labRecID.Text)
mdb.DeleteRecord(RecNum) 'after which all records selected; now to relocate...
Dim res As Result = db.Exec("SELECT * FROM Notes WHERE KeyID<" & RecNum)
res.MoveLast
Dim i As Integer = res.Index
mdb.rs.MoveTo(i)
ShowRecord
End
Public Sub ClearFields()
ta1.Text = ""
labRecID.Text = ""
labTime.Text = ""
labLocation.Text = ""
End
Public Sub MenuNewNote_Click()
ClearFields
ta1.SetFocus
End
Public Sub ta1_GotFocus()
OriginalText = ta1.Text
End
Public Sub ta1_LostFocus()
If ta1.Text = OriginalText Then Return 'no change
SaveOrUpdate
End
Public Sub tbSearch_Change()
mdb.SearchFor(tbSearch.Text)
ShowRecord
Catch
Message.Error(Error.Text)
End
Public Sub ta1_KeyPress()
If Key.Code = Key.Esc Then Me.SetFocus 'clear focus from textarea; this triggers a record update
End
Public Sub tbSearch_KeyPress()
If Key.Code = Key.Esc Then Me.SetFocus
End
Public Sub MenuShowAll_Click()
mdb.SelectAllNotes
ShowRecord
End
Public Sub KeepReplacing(InThis As String, LookFor As String, Becomes As String) As String
Dim z As String = InThis
While InStr(z, LookFor) > 0
z = Replace(z, LookFor, Becomes)
Wend
Return z
End
Public Sub SaveOrUpdate()
If IsNull(labRecID.Text) Then 'new record
If IsNull(ta1.Text) Then Return
Dim d As Date = Now()
labRecID.Text = mdb.AddRecord(ta1.Text, d)
labTime.text = Format(d, gb.MediumDate) & " " & Format(d, gb.LongTime)
Else 'update
If IsNull(ta1.Text) Then
mdb.DeleteRecord(Val(labRecID.Text))
ClearFields 'maybe leave everything empty?
Else
mdb.UpdateRecord(Val(labRecID.Text), ta1.Text)
Endif
Endif
End
Public Sub MenuTidy_Click()
OriginalText = ta1.Text
If IsNull(ta1.Text) Then Return
Dim z As String = If(ta1.Selection.Length = 0, Trim(ta1.Text), Trim(ta1.Selection.Text))
z = KeepReplacing(z, gb.NewLine, "|")
z = KeepReplacing(z, gb.Tab & gb.Tab, gb.Tab)
z = KeepReplacing(z, " ", " ")
z = KeepReplacing(z, "| ", "|")
z = KeepReplacing(z, "|" & gb.tab, "|")
z = KeepReplacing(z, "||", "|")
z = KeepReplacing(z, "|", gb.NewLine)
If ta1.Selection.Length = 0 Then ta1.Text = z Else ta1.Selection.Text = z
SaveOrUpdate
End
Public Sub MenuSentences_Click()
OriginalText = ta1.Text
If IsNull(ta1.Text) Then Return
Dim z As String = If(ta1.Selection.Length = 0, Trim(ta1.Text), Trim(ta1.Selection.Text))
z = KeepReplacing(z, gb.NewLine, "~")
z = KeepReplacing(z, "~ ", "~")
z = KeepReplacing(z, ".~", "|")
z = KeepReplacing(z, "~", " ")
z = KeepReplacing(z, " ", " ")
z = KeepReplacing(z, "|", "." & gb.NewLine)
If ta1.Selection.Length = 0 Then ta1.Text = z Else ta1.Selection.Text = z
SaveOrUpdate
End
Public Sub MenuUndo_Click()
Dim z As String = ta1.Text
ta1.Text = OriginalText
OriginalText = z
SaveOrUpdate
End
Public Sub MenuRenumber_Click()
mdb.Renumber
ShowRecord
End
Public Sub MenuVacuum_Click()
Me.Caption = "File size -> " & mdb.Vacuum()
End
Public Sub MenuBackup_Click()
Me.Caption = mdb.Backup()
End
Public Sub MenuTypeExtra_Click()
ta1.SetFocus
ta1.Text &= gb.NewLine & gb.NewLine
ta1.Select(ta1.Text.Len)
End
Public Sub MenuDoubleSpace_Click()
OriginalText = ta1.Text
If IsNull(ta1.Text) Then Return
Dim z As String = If(ta1.Selection.Length = 0, Trim(ta1.Text), Trim(ta1.Selection.Text))
z = KeepReplacing(z, gb.NewLine, "|")
z = KeepReplacing(z, "||", "|")
z = KeepReplacing(z, "|", gb.NewLine & gb.NewLine)
If ta1.Selection.Length = 0 Then ta1.Text = z Else ta1.Selection.Text = z
SaveOrUpdate
End
Public Sub ti1_MiddleClick()
Me.Show
Me.Activate
tbSearch.Text = ""
mdb.SelectAllNotes
ShowRecord
End
Public Sub MenuHide_Click()
Me.Hide
End
Massage(string) 函式對於處理包含單引號的文字的儲存是必要的。SQL 語句使用單引號來包圍字串。字串中的單引號將終止字串,後面的內容將是語法錯誤,因為它將是不可理解的。要包含單引號,它必須加倍。例如,要儲存字串 Fred’s house,它必須首先被轉換為(“按摩”為)Fred’’s house。
KeepReplacing(InThis, LookFor, ReplaceWithThis) 函式執行替換,直到 LookFor 字串不再存在。例如,如果您想從 abcxxxxdef 中刪除多個 x,只保留一個 x,您不能只使用 Replace(“abcxxxxdef”, “xx”, “x”),因為這將產生 abcxxdef。第一個雙 x 變成一個 x,第二個雙 x 變成一個 x。您仍然有兩個 x。您必須不斷替換,直到不再存在雙 x。
好了,各位,除了參考附錄。我可以從開始的地方結束,向 Benoît Minisini 表示感謝。這個程式設計環境非常令人愉快。我們所有使用者都懷著感激之情,舉杯高歌,“因為他是一位快樂的紳士,我們大家都這麼說”。
Gerard Buzolic
2019 年 9 月 25 日
