diff --git a/README.md b/README.md index 3199fe9..8b11ac6 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,10 @@ Sqlite-gui is a lightweight Windows GUI for [SQLite](https://www.sqlite.org/index.html) powered by C++, WinAPI and [Code::Blocks 17](http://www.codeblocks.org/).
-|[**Download latest version**](https://github.com/little-brother/sqlite-gui/releases/latest)| +|[**Download the latest version**](https://github.com/little-brother/sqlite-gui/releases/latest)| |-------------------------------------------------------------------------------------------| -![View](resources/image.webp) +![View](resources/demo.webp) ### Features @@ -15,17 +15,19 @@ Sqlite-gui is a lightweight Windows GUI for [SQLite](https://www.sqlite.org/inde * Database comparison * Search text in the whole database * [Workflow manager](https://github.com/little-brother/sqlite-wf) (ETL) -* Quick data references ([video](https://youtu.be/XL1_lFhzLKo)) -* Terminal mode -* Charts +* [Quick data references](https://github.com/little-brother/sqlite-gui/wiki#quick-references) ([video](https://youtu.be/XL1_lFhzLKo)) +* [Terminal mode](https://raw.githubusercontent.com/little-brother/sqlite-gui/master/resources/terminal.webp) +* [Charts](https://github.com/little-brother/sqlite-gui/wiki#charts) +* [Query parameters](https://github.com/little-brother/sqlite-gui/wiki#query-parameters) * Data generator -* Most usefull extensions are included +* [Extension pack](https://github.com/little-brother/sqlite-gui/wiki#extensions) * Demo database "Bookstore" for beginners * Does not require installation ### Cons * Only utf-8 is supported * NULL is displayed as an empty string and an empty string is set to NULL when data is edit +* The application is a single threaded. Therefore, the interface freeze on long operations If you like the project, press the like-button [here](https://alternativeto.net/software/sqlite-gui/) or write something in a [Reddit](https://www.reddit.com/r/sqlite/comments/iaao8x/a_new_lightweight_sqlite_tool_for_windows/) topic to support it.
If you have any problems, comments or suggestions, check [Wiki](https://github.com/little-brother/sqlite-gui/wiki) or just let me know lb.im@yandex.ru. diff --git a/extensions/ora.c b/extensions/ora.c index 049fa48..32f7d2c 100644 --- a/extensions/ora.c +++ b/extensions/ora.c @@ -17,14 +17,19 @@ md5(str) Calculate md5 checksum + + strpart(str, delimiter, partno) + Returns substring for a delimiter and a part number + select strpart('ab-cd-ef', '-', 2) --> 'cd' + select strpart('20.01.2021', '.', 3) --> 2021 */ #include "sqlite3ext.h" SQLITE_EXTENSION_INIT1 -#include #include #include -#include -#include + +typedef unsigned char UINT8; +typedef unsigned int UINT; static void rownum(sqlite3_context *ctx, int argc, sqlite3_value **argv){ int *pCounter = (int*)sqlite3_get_auxdata(ctx, 0); @@ -167,18 +172,18 @@ const UINT r[] = { #define LEFTROTATE(x, c) (((x) << (c)) | ((x) >> (32 - (c)))) -void to_bytes(UINT val, UINT8 *bytes) { +static void to_bytes(UINT val, UINT8 *bytes) { bytes[0] = (UINT8) val; bytes[1] = (UINT8) (val >> 8); bytes[2] = (UINT8) (val >> 16); bytes[3] = (UINT8) (val >> 24); } -UINT to_int32(const UINT8 *bytes) { +static UINT to_int32(const UINT8 *bytes) { return (UINT) bytes[0] | ((UINT) bytes[1] << 8) | ((UINT) bytes[2] << 16) | ((UINT) bytes[3] << 24); } -void _md5(const UINT8 *initial_msg, size_t initial_len, UINT8 *digest) { +static void _md5(const UINT8 *initial_msg, size_t initial_len, UINT8 *digest) { UINT h0, h1, h2, h3; UINT8 *msg = NULL; @@ -248,23 +253,57 @@ void _md5(const UINT8 *initial_msg, size_t initial_len, UINT8 *digest) { to_bytes(h3, digest + 12); } +char const hex_chars[16] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; + static void md5 (sqlite3_context *ctx, int argc, sqlite3_value **argv) { - UINT8 r[16]; - _md5(sqlite3_value_text(argv[0]), strlen(sqlite3_value_text(argv[0])), r); + UINT8 res[16]; + _md5(sqlite3_value_text(argv[0]), strlen(sqlite3_value_text(argv[0])), res); + + char buf[33]; + for(int i = 0; i < 16; i++) { + char byte = res[i]; + buf[2 * i] = hex_chars[(byte & 0xF0) >> 4]; + buf[2 * i + 1] = hex_chars[(byte & 0x0F) >> 0]; + } + buf[32] = 0; + + sqlite3_result_text(ctx, buf, -1, SQLITE_TRANSIENT); +} - char buf[17]; - sprintf(buf, "%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x", r[0], r[1], r[2], r[3], r[4], r[5], r[6], r[7], r[8], r[9], r[10], r[11], r[12], r[13], r[14], r[15]); - sqlite3_result_text(ctx, buf, -1, SQLITE_TRANSIENT); +static void strpart (sqlite3_context *ctx, int argc, sqlite3_value **argv) { + const char* instr = sqlite3_value_text(argv[0]); + const char* delim = sqlite3_value_text(argv[1]); + int no = sqlite3_value_int(argv[2]); + + if (no < 1) { + sqlite3_result_null(ctx); + return; + } + + char str[strlen(instr) + 1]; + strcpy(str, instr); + + char *ptr = strtok(str, delim); + int curr = 0; + while (ptr != NULL && curr < no - 1) { + ptr = strtok(NULL, delim); + curr++; + } + + if (ptr) + sqlite3_result_text(ctx, ptr, -1, SQLITE_TRANSIENT); + else + sqlite3_result_null(ctx); } __declspec(dllexport) int sqlite3_ora_init(sqlite3 *db, char **pzErrMsg, const sqlite3_api_routines *pApi) { - int rc = SQLITE_OK; SQLITE_EXTENSION_INIT2(pApi); (void)pzErrMsg; /* Unused parameter */ return SQLITE_OK == sqlite3_create_function(db, "rownum", 1, SQLITE_UTF8, 0, rownum, 0, 0) && SQLITE_OK == sqlite3_create_function(db, "concat", -1, SQLITE_UTF8, 0, concat, 0, 0) && SQLITE_OK == sqlite3_create_function(db, "decode", -1, SQLITE_UTF8, 0, decode, 0, 0) && SQLITE_OK == sqlite3_create_function(db, "crc32", 1, SQLITE_UTF8, 0, crc32, 0, 0) && - SQLITE_OK == sqlite3_create_function(db, "md5", 1, SQLITE_UTF8, 0, md5, 0, 0) ? + SQLITE_OK == sqlite3_create_function(db, "md5", 1, SQLITE_UTF8, 0, md5, 0, 0) && + SQLITE_OK == sqlite3_create_function(db, "strpart", 3, SQLITE_UTF8, 0, strpart, 0, 0) ? SQLITE_OK : SQLITE_ERROR; } \ No newline at end of file diff --git a/resources/btn_add.bmp b/resources/btn_add.bmp deleted file mode 100644 index bf62cbc..0000000 Binary files a/resources/btn_add.bmp and /dev/null differ diff --git a/resources/btn_delete.bmp b/resources/btn_delete.bmp deleted file mode 100644 index e822891..0000000 Binary files a/resources/btn_delete.bmp and /dev/null differ diff --git a/resources/btn_refresh.bmp b/resources/btn_refresh.bmp deleted file mode 100644 index 7a789fc..0000000 Binary files a/resources/btn_refresh.bmp and /dev/null differ diff --git a/resources/charts.webp b/resources/charts.webp new file mode 100644 index 0000000..326ea2f Binary files /dev/null and b/resources/charts.webp differ diff --git a/resources/demo.webp b/resources/demo.webp new file mode 100644 index 0000000..5dc9b0a Binary files /dev/null and b/resources/demo.webp differ diff --git a/resources/image.webp b/resources/image.webp deleted file mode 100644 index 8eeb490..0000000 Binary files a/resources/image.webp and /dev/null differ diff --git a/resources/terminal.webp b/resources/terminal.webp new file mode 100644 index 0000000..0159c19 Binary files /dev/null and b/resources/terminal.webp differ diff --git a/resources/toolbar_data.bmp b/resources/toolbar_data.bmp new file mode 100644 index 0000000..076920c Binary files /dev/null and b/resources/toolbar_data.bmp differ diff --git a/sqlite-gui.cbp b/sqlite-gui.cbp index 8305d7a..81e938c 100644 --- a/sqlite-gui.cbp +++ b/sqlite-gui.cbp @@ -40,8 +40,11 @@ + + + diff --git a/sqlite-gui.depend b/sqlite-gui.depend index 2466e52..c0f2c3f 100644 --- a/sqlite-gui.depend +++ b/sqlite-gui.depend @@ -22,20 +22,20 @@ 1562796420 d:\codes\sqlite-gui\sqlite3.h -1610099075 source:d:\codes\sqlite-gui\src\resource.rc +1611058507 source:d:\codes\sqlite-gui\src\resource.rc "resource.h" -1609978095 d:\codes\sqlite-gui\src\resource.h +1610981283 d:\codes\sqlite-gui\src\resource.h -1609924139 source:d:\codes\sqlite-gui\src\prefs.cpp +1611090146 source:d:\codes\sqlite-gui\src\prefs.cpp "prefs.h" "sqlite3.h" -1602007947 d:\codes\sqlite-gui\src\prefs.h +1611089165 d:\codes\sqlite-gui\src\prefs.h @@ -43,7 +43,7 @@ 1583506689 d:\codes\sqlite-gui\src\sqlite3.h -1610058432 source:d:\codes\sqlite-gui\src\main.cpp +1611003745 source:d:\codes\sqlite-gui\src\main.cpp "global.h" "missing.h" "resource.h" @@ -57,19 +57,19 @@ 1583970008 d:\codes\sqlite-gui\src\main.h -1605587592 d:\codes\sqlite-gui\src\utils.h +1610994001 d:\codes\sqlite-gui\src\utils.h "sqlite3.h" -1605587816 source:d:\codes\sqlite-gui\src\utils.cpp +1610994064 source:d:\codes\sqlite-gui\src\utils.cpp "utils.h" -1610099913 source:d:\codes\sqlite-gui\src\tools.cpp +1611001401 source:d:\codes\sqlite-gui\src\tools.cpp "global.h" "missing.h" "resource.h" @@ -78,7 +78,7 @@ "dialogs.h" "prefs.h" -1610048754 d:\codes\sqlite-gui\src\global.h +1610376139 d:\codes\sqlite-gui\src\global.h @@ -90,20 +90,22 @@ + "missing.h" "sqlite3.h" 1605592862 d:\codes\sqlite-gui\src\tools.h -1609944947 d:\codes\sqlite-gui\src\dialogs.h +1610981781 d:\codes\sqlite-gui\src\dialogs.h -1610099817 source:d:\codes\sqlite-gui\src\dialogs.cpp +1611092472 source:d:\codes\sqlite-gui\src\dialogs.cpp "resource.h" "global.h" "prefs.h" "utils.h" "dialogs.h" + "tools.h" 1606840569 d:\codes\sqlite-gui\include\sqlite3.h @@ -116,7 +118,7 @@ "oaidl.h" "ocidl.h" -1609795113 d:\codes\sqlite-gui\src\missing.h +1610377907 d:\codes\sqlite-gui\src\missing.h 1595499199 c:\mingw\include\windows.h diff --git a/sqlite-gui.layout b/sqlite-gui.layout index e453ada..2352c7d 100644 --- a/sqlite-gui.layout +++ b/sqlite-gui.layout @@ -2,74 +2,74 @@ - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + diff --git a/src/dialogs.cpp b/src/dialogs.cpp index a449764..38f85f7 100644 --- a/src/dialogs.cpp +++ b/src/dialogs.cpp @@ -1,13 +1,15 @@ -#include"resource.h" -#include"global.h" +#include "resource.h" +#include "global.h" #include "prefs.h" #include "utils.h" -#include"dialogs.h" +#include "dialogs.h" +#include "tools.h" namespace dialogs { - WNDPROC cbOldEditDataEdit, cbOldAddTableCell; + WNDPROC cbOldEditDataEdit, cbOldAddTableCell, cbOldHeaderEdit; LRESULT CALLBACK cbNewEditDataEdit(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); LRESULT CALLBACK cbNewAddTableCell(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); + LRESULT CALLBACK cbNewFilterEdit(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); BOOL CALLBACK cbDlgEditDataValue (HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); bool ListView_UpdateCell(HWND hListWnd, int rowNo, int colNo, TCHAR* value16); @@ -18,12 +20,6 @@ namespace dialogs { bool isRequireHighligth = false; bool isRequireParenthesisHighligth = false; - HBITMAP hButtonIcons [] = { - (HBITMAP)LoadImage(GetModuleHandle(0), MAKEINTRESOURCE(IDB_BTN_ADD), IMAGE_BITMAP, 16, 16, LR_LOADTRANSPARENT | LR_LOADMAP3DCOLORS), - (HBITMAP)LoadImage(GetModuleHandle(0), MAKEINTRESOURCE(IDB_BTN_DELETE), IMAGE_BITMAP, 16, 16, LR_LOADTRANSPARENT | LR_LOADMAP3DCOLORS), - (HBITMAP)LoadImage(GetModuleHandle(0), MAKEINTRESOURCE(IDB_BTN_REFRESH), IMAGE_BITMAP, 16, 16, LR_LOADTRANSPARENT | LR_LOADMAP3DCOLORS) - }; - BOOL CALLBACK cbDlgAddEdit (HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) { switch (msg) { case WM_INITDIALOG: { @@ -504,7 +500,6 @@ namespace dialogs { break; case WM_COMMAND: { - if (HIWORD(wParam) == EN_CHANGE && (HWND)lParam == GetDlgItem(hWnd, IDC_DLG_QUERYFILTER) && (HWND)lParam == GetFocus()) { KillTimer(hWnd, IDT_EDIT_DATA); SetTimer(hWnd, IDT_EDIT_DATA, 300, NULL); @@ -545,7 +540,7 @@ namespace dialogs { HWND hFilterWnd = GetDlgItem(hWnd, IDC_DLG_QUERYFILTER); int size = GetWindowTextLength(hFilterWnd); - TCHAR* filter16 = new TCHAR[size + 1]{0}; + TCHAR filter16[size + 1]{0}; GetWindowText(hFilterWnd, filter16, size + 1); int idx = GetWindowLong(hWnd, GWL_USERDATA); @@ -588,7 +583,7 @@ namespace dialogs { ListView_SetItemState (hListWnd, 0, LVIS_FOCUSED | LVIS_SELECTED, 0x000F); delete [] filter8; - delete [] filter16; + return true; } break; @@ -601,7 +596,6 @@ namespace dialogs { return false; } - char filterQuery8[MAX_TEXT_LENGTH]{0}; BOOL CALLBACK cbDlgEditData (HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) { switch (msg) { case WM_INITDIALOG: { @@ -639,88 +633,120 @@ namespace dialogs { } sqlite3_finalize(stmt); } + HWND hListWnd = GetDlgItem(hWnd, IDC_DLG_QUERYLIST); + HWND hHeader = ListView_GetHeader(hListWnd); + LONG_PTR styles = GetWindowLongPtr(hHeader, GWL_STYLE); + SetWindowLongPtr(hHeader, GWL_STYLE, styles | HDS_FILTERBAR); + + bool isTable = false; + sprintf(query8, "select lower(type) = 'table' from %s.sqlite_master where tbl_name = \"%s\" and type in ('view', 'table')", schema8, tablename8); + if ((SQLITE_OK == sqlite3_prepare_v2(db, query8, -1, &stmt, 0)) && (SQLITE_ROW == sqlite3_step(stmt))) + isTable = sqlite3_column_int(stmt, 0); + sqlite3_finalize(stmt); + SetWindowLong(hWnd, GWL_USERDATA, +isTable); - HWND hFilterWnd = GetDlgItem(hWnd, IDC_DLG_QUERYFILTER); SendMessage(hWnd, WMU_UPDATE_DATA, 0 , 0); - SetFocus(hFilterWnd); - sprintf(filterQuery8, - "select '\"***\" || coalesce(' || group_concat(name, ', \"\") || \"***\" || coalesce(') || ', \"\") || \"***\"', "\ - "(select type from %s.sqlite_master where tbl_name = \"%s\" and type in ('view', 'table')) type from pragma_table_info t where schema = '%s' and arg = '%s' and upper(t.type) <> 'BLOB'", - schema8, tablename8, schema8, tablename8); + TBBUTTON tbTableButtons [] = { + {2, IDM_ROW_REFRESH, TBSTATE_ENABLED, TBSTYLE_BUTTON | TBSTYLE_AUTOSIZE, {0}, 0L, (INT_PTR)TEXT("Refresh")}, + {0, IDM_ROW_ADD, TBSTATE_ENABLED, TBSTYLE_BUTTON | TBSTYLE_AUTOSIZE, {0}, 0L, (INT_PTR)TEXT("Add")}, + {1, IDM_ROW_DELETE, TBSTATE_ENABLED, TBSTYLE_BUTTON | TBSTYLE_AUTOSIZE, {0}, 0L, (INT_PTR)TEXT("Delete")}, + {3, IDM_GENERATE_DATA, TBSTATE_ENABLED, TBSTYLE_BUTTON | TBSTYLE_AUTOSIZE, {0}, 0L, (INT_PTR)TEXT("Generate data")}, + {-1, IDM_LAST_SEPARATOR, TBSTATE_ENABLED, TBSTYLE_SEP, {0}, 0L, 0} + }; + + TBBUTTON tbViewButtons [] = { + {2, IDM_ROW_REFRESH, TBSTATE_ENABLED, TBSTYLE_BUTTON | TBSTYLE_AUTOSIZE, {0}, 0L, (INT_PTR)TEXT("Refresh")}, + {-1, IDM_LAST_SEPARATOR, TBSTATE_ENABLED, TBSTYLE_SEP, {0}, 0L, 0} + }; + + int btnCount = isTable ? sizeof(tbTableButtons)/sizeof(tbTableButtons[0]) : sizeof(tbViewButtons)/sizeof(tbViewButtons[0]); + HWND hToolbarWnd = CreateToolbarEx (hWnd, WS_CHILD | WS_BORDER | WS_VISIBLE | TBSTYLE_TOOLTIPS | TBSTYLE_FLAT | TBSTYLE_LIST, IDC_DLG_TOOLBAR, 0, NULL, 0, + isTable ? tbTableButtons : tbViewButtons, btnCount, + 0, 0, 0, 0, sizeof (TBBUTTON)); + SendMessage(hToolbarWnd, TB_SETIMAGELIST,0, (LPARAM)ImageList_LoadBitmap(GetModuleHandle(0), MAKEINTRESOURCE(IDB_TOOLBAR_DATA), 0, 0, RGB(255,255,255))); - bool isTable = false; - if ((SQLITE_OK == sqlite3_prepare_v2(db, filterQuery8, -1, &stmt, 0)) && (SQLITE_ROW == sqlite3_step(stmt))) { - sprintf(filterQuery8, "select *, %s from \"%s\".\"%s\" where %s like \"***%%%%%%s%%%%***\"", - hasRowid ? "rowid" : (char*)GetProp(hWnd, TEXT("MD5KEYS8")), schema8, tablename8, sqlite3_column_text(stmt, 0)); - isTable = strcmp((char*)sqlite3_column_text(stmt, 1), "table") == 0; - } else { - showDbError(hWnd); + RECT rc{0}; + SendMessage(hToolbarWnd, TB_GETRECT, IDM_LAST_SEPARATOR, (LPARAM)&rc); + HWND hFilterWnd = CreateWindowEx(0L, WC_EDIT, NULL, WS_CHILD | WS_BORDER | WS_VISIBLE | ES_LEFT | ES_AUTOHSCROLL, rc.right, 2, 180, 19, hToolbarWnd, (HMENU) IDC_DLG_FILTER, GetModuleHandle(0), 0); + SendMessage(hFilterWnd, WM_SETFONT, (LPARAM)SendMessage(hToolbarWnd, WM_GETFONT, 0, 0), true); + cbOldHeaderEdit = (WNDPROC)SetWindowLong(hFilterWnd, GWL_WNDPROC, (LONG)cbNewFilterEdit); + + int colCount = Header_GetItemCount(hHeader); + HFONT hFont = (HFONT)SendMessage(hListWnd, WM_GETFONT, 0, 0); + for (int i = 0; i < colCount; i++) { + RECT rc; + Header_GetItemRect(hHeader, i, &rc); + HWND hEdit = CreateWindowEx(WS_EX_TOPMOST, WC_EDIT, NULL, ES_CENTER | ES_AUTOHSCROLL | WS_VISIBLE | WS_CHILD | WS_TABSTOP, 0, 0, 0, 0, hHeader, (HMENU)(IDC_HEADER_EDIT + i), GetModuleHandle(0), NULL); + SendMessage(hEdit, WM_SETFONT, (LPARAM)hFont, true); + cbOldHeaderEdit = (WNDPROC)SetWindowLong(hEdit, GWL_WNDPROC, (LONG)cbNewFilterEdit); + CreateWindowEx(WS_EX_TOPMOST, WC_STATIC, NULL, WS_VISIBLE | WS_CHILD | SS_WHITEFRAME, 0, 0, 0, 0, hHeader, (HMENU)(IDC_HEADER_STATIC + i), GetModuleHandle(0), NULL); } - sqlite3_finalize(stmt); - SetWindowLong(hWnd, GWL_USERDATA, +isTable); - ShowWindow(GetDlgItem(hWnd, IDC_DLG_ROW_ADD), isTable ? SW_SHOW : SW_HIDE); + int* widths = new int[colCount]{0}; + for (int i = 0; i < colCount; i++) + widths[i] = ListView_GetColumnWidth(hListWnd, i); + SetProp(hWnd, TEXT("WIDTHS"), (HANDLE)widths); SetWindowPos(hWnd, 0, prefs::get("x") + 40, prefs::get("y") + 80, prefs::get("width") - 80, prefs::get("height") - 120, SWP_NOZORDER); ShowWindow (hWnd, prefs::get("maximized") == 1 ? SW_MAXIMIZE : SW_SHOW); - - HWND hBtnWnd = GetDlgItem(hWnd, IDC_DLG_ROW_ADD); - SendMessage(hBtnWnd, STM_SETIMAGE, IMAGE_BITMAP, (LPARAM)hButtonIcons[0]); - hBtnWnd = GetDlgItem(hWnd, IDC_DLG_ROW_DEL); - SendMessage(hBtnWnd, STM_SETIMAGE, IMAGE_BITMAP, (LPARAM)hButtonIcons[1]); - hBtnWnd = GetDlgItem(hWnd, IDC_DLG_REFRESH); - SendMessage(hBtnWnd, STM_SETIMAGE, IMAGE_BITMAP, (LPARAM)hButtonIcons[2]); - cbOldResultList = (WNDPROC)SetWindowLong(GetDlgItem(hWnd, IDC_DLG_QUERYLIST), GWL_WNDPROC, (LONG)cbNewResultList); - } - break; - - case WM_TIMER: { - KillTimer(hWnd, IDT_EDIT_DATA); - SendMessage(hWnd, WMU_UPDATE_DATA, 0, 0); } break; case WM_SIZE: { - bool isTable = GetWindowLong(hWnd, GWL_USERDATA) == 1; - HWND hRefreshBtn = GetDlgItem(hWnd, IDC_DLG_REFRESH); HWND hListWnd = GetDlgItem(hWnd, IDC_DLG_QUERYLIST); - HWND hFilterWnd = GetDlgItem(hWnd, IDC_DLG_QUERYFILTER); + HWND hToolbarWnd = GetDlgItem(hWnd, IDC_DLG_TOOLBAR); - RECT rc; + SendMessage(hToolbarWnd, WM_SIZE, 0, 0); + RECT rc, rc2, rc3; GetClientRect(hWnd, &rc); - int w = isTable ? 44 : 0; - SetWindowPos(hRefreshBtn, 0, rc.right - rc.left - 21, 0, 20, 20, SWP_NOZORDER); - SetWindowPos(hFilterWnd, 0, w, 0, rc.right - rc.left - 0 - w - 22, 20, SWP_NOZORDER); - SetWindowPos(hListWnd, 0, 0, 0, rc.right - rc.left, rc.bottom - rc.top - 21, SWP_NOZORDER | SWP_NOMOVE); + GetClientRect(hToolbarWnd, &rc2); + SetWindowPos(hListWnd, 0, 0, rc2.bottom + 2, rc.right - rc.left, rc.bottom - rc.top - 28, SWP_NOZORDER); + + HWND hFilterWnd = GetDlgItem(hToolbarWnd, IDC_DLG_FILTER); + GetWindowRect(hFilterWnd, &rc3); + POINT p{rc3.left, rc3.bottom}; + ScreenToClient(hToolbarWnd, &p); + SetWindowPos(hFilterWnd, 0, 0, 0, rc.right - p.x, 19, SWP_NOZORDER | SWP_NOMOVE); } break; case WMU_UPDATE_DATA: { HWND hListWnd = GetDlgItem(hWnd, IDC_DLG_QUERYLIST); - HWND hFilterWnd = GetDlgItem(hWnd, IDC_DLG_QUERYFILTER); + HWND hHeader = ListView_GetHeader(hListWnd); bool isTable = GetWindowLong(hWnd, GWL_USERDATA) == 1; bool hasRowid = GetProp(hWnd, TEXT("HASROWID")); + HWND hFilterWnd = GetDlgItem(GetDlgItem(hWnd, IDC_DLG_TOOLBAR),IDC_DLG_FILTER); int size = GetWindowTextLength(hFilterWnd); TCHAR filter16[size + 1]{0}; GetWindowText(hFilterWnd, filter16, size + 1); - char* filter8 = utils::utf16to8(filter16); + SetWindowText(hWnd, TEXT("Fetching data...")); + char* tablename8 = (char*)GetProp(hWnd, TEXT("TABLENAME8")); char* schema8 = (char*)GetProp(hWnd, TEXT("SCHEMA8")); + char* md5keys = (char*)GetProp(hWnd, TEXT("MD5KEYS8")); - char query8[MAX_TEXT_LENGTH]{0}; - sprintf(query8, "select *, %s rowid from \"%s\".\"%s\" t %s", hasRowid ? "rowid" : (char*)GetProp(hWnd, TEXT("MD5KEYS8")), schema8, tablename8, filter8 && strlen(filter8) ? filter8 : ""); - - if (!isQueryValid(query8)) { - sprintf(query8, "select *, %s rowid from \"%s\".\"%s\" where %s", hasRowid ? "rowid" : (char*)GetProp(hWnd, TEXT("MD5KEYS8")), schema8, tablename8, filter8); + TCHAR where16[MAX_TEXT_LENGTH]{0}; + _tcscat(where16, TEXT("where (")); + _tcscat(where16, _tcslen(filter16) ? filter16 : TEXT("1 = 1")); + _tcscat(where16, TEXT(") ")); + + for (int colNo = 1; colNo < Header_GetItemCount(hHeader); colNo++) { + if (GetWindowTextLength(GetDlgItem(hHeader, IDC_HEADER_EDIT + colNo)) > 0) { + TCHAR colname16[256]{0}; + Header_GetItemText(hHeader, colNo, colname16, 255); + _tcscat(where16, TEXT(" and \"")); + _tcscat(where16, colname16); + _tcscat(where16, TEXT("\" like '%' || ? || '%' ")); + } - // "where 4" or "where true" are valid filters - bool isValid = isQueryValid(query8); - if (!isValid || (isValid && strchr(filter8, ' ') == NULL)) - sprintf(query8, filterQuery8, filter8); } + char* where8 = utils::utf16to8(where16); + + char query8[MAX_TEXT_LENGTH]{0}; + sprintf(query8, "select *, %s rowid from \"%s\".\"%s\" t %s", hasRowid ? "rowid" : md5keys, schema8, tablename8, where8 && strlen(where8) ? where8 : ""); sqlite3_stmt *stmt; if (SQLITE_OK == sqlite3_prepare_v2(db, query8, -1, &stmt, 0)) { @@ -732,9 +758,30 @@ namespace dialogs { SetProp(hWnd, TEXT("BLOBS"), (HANDLE)types); } + int bindNo = 0; + for (int colNo = 1; (colNo < colCount) && strlen(where8); colNo++) { + HWND hEdit = GetDlgItem(hHeader, IDC_HEADER_EDIT + colNo); + int size = GetWindowTextLength(hEdit); + if (size > 0) { + + TCHAR value16[size + 1]{0}; + GetWindowText(hEdit, value16, size + 1); + char* value8 = utils::utf16to8(value16); + sqlite3_bind_text(stmt, bindNo + 1, value8, strlen(value8), SQLITE_TRANSIENT); + delete [] value8; + bindNo++; + } + } + + ShowWindow(hListWnd, SW_HIDE); int rowCount = ListView_SetData(hListWnd, stmt, true); ListView_SetColumnWidth(hListWnd, colCount, 0); // last column is rowid + int* widths = (int*)GetProp(hWnd, TEXT("WIDTHS")); + for (int i = 0; i < colCount && widths; i++) + ListView_SetColumnWidth(hListWnd, i, widths[i]); + ShowWindow(hListWnd, SW_SHOW); + TCHAR buf[256]{0}; _stprintf(buf, TEXT("%s \"%s\" [%s%i rows]"), isTable ? TEXT("Table") : TEXT("View"), editTableData16, rowCount < 0 ? TEXT("Show only first ") : TEXT(""), abs(rowCount)); SetWindowText(hWnd, buf); @@ -744,8 +791,74 @@ namespace dialogs { } ListView_SetItemState(hListWnd, 0, LVIS_SELECTED | LVIS_FOCUSED, LVIS_SELECTED | LVIS_FOCUSED); + SendMessage(hWnd, WMU_SET_CURRENT_CELL, 0, 1); - delete [] filter8; + delete [] where8; + PostMessage(hWnd, WMU_UPDATE_COLSIZE, 0, 0); + InvalidateRect(hHeader, NULL, true); + return true; + } + break; + + case WMU_UPDATE_COLSIZE: { + HWND hListWnd = GetDlgItem(hWnd, IDC_DLG_QUERYLIST); + HWND hHeader = ListView_GetHeader(hListWnd); + SendMessage(hHeader, WM_SIZE, 0, 0); + for (int i = 0; i < Header_GetItemCount(hHeader); i++) { + RECT rc; + Header_GetItemRect(hHeader, i, &rc); + SetWindowPos(GetDlgItem(hHeader, IDC_HEADER_STATIC + i), 0, rc.left + 1, rc.top + 20, rc.right - rc.left - 2, 2, SWP_NOZORDER); + SetWindowPos(GetDlgItem(hHeader, IDC_HEADER_EDIT + i), 0, rc.left + 1, rc.top + 20 + 2, rc.right - rc.left - 2, rc.bottom - rc.top - 21 - 2, SWP_NOZORDER); + } + } + break; + + case WMU_SET_CURRENT_CELL: { + HWND hListWnd = GetDlgItem(hWnd, IDC_DLG_QUERYLIST); + RECT rect; + ListView_GetSubItemRect(hListWnd, currCell.iItem, currCell.iSubItem, LVIR_BOUNDS, &rect); + InvalidateRect(hListWnd, &rect, true); + + currCell = {hListWnd, (int)wParam, (int)lParam}; + + ListView_GetSubItemRect(hListWnd, currCell.iItem, currCell.iSubItem, LVIR_BOUNDS, &rect); + InvalidateRect(hListWnd, &rect, true); + SetFocus(hListWnd); + } + break; + + case WMU_SYNC_CURRENT_CELL: { + HWND hListWnd = GetDlgItem(hWnd, IDC_DLG_QUERYLIST); + int rowNo = ListView_GetNextItem(hListWnd, -1, LVNI_SELECTED); + SendMessage(hWnd, WMU_SET_CURRENT_CELL, rowNo, currCell.iSubItem); + } + break; + + case WMU_EDIT_VALUE: { + HWND hListWnd = GetDlgItem(hWnd, IDC_DLG_QUERYLIST); + bool withText = wParam != 0; + + RECT rect; + ListView_GetSubItemRect(hListWnd, currCell.iItem, currCell.iSubItem, LVIR_BOUNDS, &rect); + int h = rect.bottom - rect.top; + int w = ListView_GetColumnWidth(hListWnd, currCell.iSubItem); + + TCHAR buf[MAX_TEXT_LENGTH]; + ListView_GetItemText(hListWnd, currCell.iItem, currCell.iSubItem, buf, MAX_TEXT_LENGTH); + + if (_tcscmp(buf, TEXT("(BLOB)")) == 0 || currCell.iSubItem < 1) + return true; + + HWND hEdit = CreateWindowEx(0, WC_EDIT, withText ? buf : NULL, WS_CHILD | WS_VISIBLE | WS_BORDER | ES_AUTOHSCROLL, rect.left, rect.top, w, h, hListWnd, 0, GetModuleHandle(NULL), NULL); + SetWindowLong(hEdit, GWL_USERDATA, MAKELPARAM(currCell.iItem, currCell.iSubItem)); + int end = GetWindowTextLength(hEdit); + SendMessage(hEdit, EM_SETSEL, end, end); + SendMessage(hEdit, WM_SETFONT, (LPARAM)hDefFont, true); + cbOldEditDataEdit = (WNDPROC)SetWindowLong(hEdit, GWL_WNDPROC, (LONG)cbNewEditDataEdit); + SetFocus(hEdit); + + if (!withText) + keybd_event(lParam, 0, 0, 0); } break; @@ -762,7 +875,8 @@ namespace dialogs { if (pHdr->code == (DWORD)NM_RCLICK && pHdr->hwndFrom == hListWnd) { NMITEMACTIVATE* ia = (LPNMITEMACTIVATE) lParam; - currCell = {hListWnd, ia->iItem, ia->iSubItem}; + SendMessage(hWnd, WMU_SET_CURRENT_CELL, ia->iItem, ia->iSubItem); + POINT p; GetCursorPos(&p); @@ -785,28 +899,123 @@ namespace dialogs { return ListView_ShowRef(hListWnd, ia->iItem, ia->iSubItem); } - if (pHdr->code == (DWORD)NM_DBLCLK && pHdr->hwndFrom == hListWnd && !isTable) { + if (!isTable && pHdr->code == (DWORD)NM_DBLCLK && pHdr->hwndFrom == hListWnd) { NMITEMACTIVATE* ia = (LPNMITEMACTIVATE) lParam; if (ia->iItem != -1) SendMessage(hWnd, WM_COMMAND, IDM_ROW_EDIT, 0); } + if (pHdr->code == (DWORD)NM_CLICK && pHdr->hwndFrom == hListWnd) { + NMITEMACTIVATE* ia = (LPNMITEMACTIVATE) lParam; + return SendMessage(hWnd, WMU_SET_CURRENT_CELL, ia->iItem, ia->iSubItem); + } + + if (isTable && pHdr->hwndFrom == hListWnd && pHdr->code == (UINT)NM_CUSTOMDRAW) { + NMLVCUSTOMDRAW* pCustomDraw = (LPNMLVCUSTOMDRAW)lParam; + + int result = CDRF_DODEFAULT; + if (pCustomDraw->nmcd.dwDrawStage == CDDS_PREPAINT) + result = CDRF_NOTIFYITEMDRAW; + + if (pCustomDraw->nmcd.dwDrawStage == CDDS_ITEMPREPAINT) + result = CDRF_NOTIFYSUBITEMDRAW | CDRF_NEWFONT; + + if ((pCustomDraw->nmcd.dwDrawStage == CDDS_POSTPAINT) | CDDS_ITEM) { + if (pCustomDraw->nmcd.dwItemSpec == (DWORD)currCell.iItem) { + RECT rect; + ListView_GetSubItemRect(hListWnd, currCell.iItem, currCell.iSubItem, LVIR_BOUNDS, &rect); + + HPEN hPen = CreatePen(PS_SOLID, 1, RGB(0, 255, 0)); + HDC hdc = pCustomDraw->nmcd.hdc; + SelectObject(hdc, hPen); + + RECT rc {0}; + ListView_GetSubItemRect(hListWnd, currCell.iItem, currCell.iSubItem, LVIR_BOUNDS, &rc); + MoveToEx(hdc, rc.left - 2, rc.top, 0); + LineTo(hdc, rc.right - 1, rc.top); + LineTo(hdc, rc.right - 1, rc.bottom - 2); + LineTo(hdc, rc.left + 1, rc.bottom - 2); + LineTo(hdc, rc.left + 1, rc.top); + } + } + + SetWindowLongPtr(hWnd, DWLP_MSGRESULT, result); + return true; + } + if (pHdr->code == LVN_KEYDOWN && pHdr->hwndFrom == hListWnd) { NMLVKEYDOWN* kd = (LPNMLVKEYDOWN) lParam; - if (kd->wVKey == 0x43 && GetKeyState(VK_CONTROL)) { // Ctrl + C + + bool isControl = GetAsyncKeyState(VK_CONTROL); + if (kd->wVKey == VK_F5) { + PostMessage(hWnd, WM_COMMAND, IDM_ROW_REFRESH, 0); + return true; + } + + if (kd->wVKey == 0x43 && isControl) { // Ctrl + C currCell = {hListWnd, 0, 0}; PostMessage(hWnd, WM_COMMAND, IDM_RESULT_COPY_ROW, 0); + return true; } - if (kd->wVKey == 0x41 && GetKeyState(VK_CONTROL)) // Ctrl + A + if (kd->wVKey == 0x41 && isControl) { // Ctrl + A ListView_SetItemState(hListWnd, -1, LVIS_SELECTED, LVIS_SELECTED); + return true; + } - if (isTable && kd->wVKey == VK_DELETE) + if (kd->wVKey == 0x56 && isControl && currCell.iSubItem > 0) { // Ctrl + V + TCHAR* clipboard = utils::getClipboardText(); + ListView_UpdateCell(hListWnd, currCell.iItem, currCell.iSubItem, clipboard); + delete [] clipboard; + return true; + } + + if (isTable && kd->wVKey == VK_DELETE) { PostMessage(hWnd, WM_COMMAND, IDM_ROW_DELETE, 0); + return true; + } + + if (isTable && (kd->wVKey == VK_NEXT || kd->wVKey == VK_PRIOR || kd->wVKey == VK_HOME || kd->wVKey == VK_END)) { + PostMessage(hWnd, WMU_SYNC_CURRENT_CELL, 0, 0); + return true; + } + + if (isTable && (kd->wVKey == VK_UP || kd->wVKey == VK_DOWN)) { + int rowCount = ListView_GetItemCount(hListWnd); + int rowNo = (currCell.iItem + rowCount + (kd->wVKey == VK_UP ? -1 : 1)) % rowCount; + SendMessage(hWnd, WMU_SET_CURRENT_CELL, rowNo, currCell.iSubItem); + ListView_SetItemState(hListWnd, -1, 0, LVIS_SELECTED); + ListView_SetItemState(hListWnd, rowNo, LVIS_SELECTED, LVIS_SELECTED); + SetWindowLongPtr(hWnd, DWLP_MSGRESULT, true); + return true; + } + + if (isTable && (kd->wVKey == VK_LEFT || kd->wVKey == VK_RIGHT)) { + int colCount = Header_GetItemCount(ListView_GetHeader(hListWnd)); + int colNo = (currCell.iSubItem + colCount + (kd->wVKey == VK_LEFT ? -1 : 1)) % colCount; + colNo = colNo == 0 && kd->wVKey == VK_LEFT ? colCount - 2 : colNo == colCount - 1 && kd->wVKey == VK_RIGHT ? 1 : colNo; + SendMessage(hWnd, WMU_SET_CURRENT_CELL, currCell.iItem, colNo); + SetWindowLongPtr(hWnd, DWLP_MSGRESULT, true); + return true; + } + bool isNum = kd->wVKey >= 0x31 && kd->wVKey <= 0x39; bool isNumPad = kd->wVKey >= 0x61 && kd->wVKey <= 0x69; - if ((isNum || isNumPad) && GetKeyState(VK_CONTROL)) // Ctrl + 1-9 - return ListView_Sort(pHdr->hwndFrom, kd->wVKey - (isNum ? 0x31 : 0x61) + 1 ); + if ((isNum || isNumPad) && isControl) {// Ctrl + 1-9 + ListView_Sort(pHdr->hwndFrom, kd->wVKey - (isNum ? 0x31 : 0x61) + 1); + if (isTable) + PostMessage(hWnd, WMU_SYNC_CURRENT_CELL, 0, 0); + return true; + } + + if (isTable && !isControl && kd->wVKey >= 0x30 && kd->wVKey <= 0x5A) { // 0, 1, ..., y, z + SendMessage(hWnd, WMU_EDIT_VALUE, 0, kd->wVKey); + SetWindowLongPtr(hWnd, DWLP_MSGRESULT, true); + return true; + } + + if (isTable && kd->wVKey == VK_INSERT) + SendMessage(hWnd, WM_COMMAND, IDM_ROW_ADD, 0); } if (isTable && pHdr->code == (DWORD)NM_DBLCLK && pHdr->hwndFrom == hListWnd) { @@ -818,28 +1027,18 @@ namespace dialogs { if (ia->iSubItem == 0 || ia->iSubItem == Header_GetItemCount(ListView_GetHeader(hListWnd)) - 1) return true; - RECT rect; - ListView_GetSubItemRect(hListWnd, ia->iItem, ia->iSubItem, LVIR_BOUNDS, &rect); - int h = rect.bottom - rect.top; - int w = ListView_GetColumnWidth(hListWnd, ia->iSubItem); - - TCHAR buf[MAX_TEXT_LENGTH]; - ListView_GetItemText(hListWnd, ia->iItem, ia->iSubItem, buf, MAX_TEXT_LENGTH); - - if (_tcscmp(buf, TEXT("(BLOB)")) == 0) - return 1; + currCell = {hListWnd, ia->iItem, ia->iSubItem}; + } - HWND hBtn = CreateWindowEx(0, WC_BUTTON, TEXT("..."), WS_CHILD | WS_VISIBLE | BS_FLAT, rect.left + w - 20, rect.top, 20, h, hListWnd, 0, GetModuleHandle(NULL), NULL); - SendMessage(hBtn, WM_SETFONT, (LPARAM)hDefFont, true); - SetWindowLong(hBtn, GWL_USERDATA, MAKELPARAM(ia->iItem, ia->iSubItem)); + if (isTable && pHdr->code == (DWORD)NM_DBLCLK && pHdr->hwndFrom == hListWnd) + return SendMessage(hWnd, WMU_EDIT_VALUE, 1, 0); - HWND hEdit = CreateWindowEx(0, WC_EDIT, buf, WS_CHILD | WS_VISIBLE | WS_BORDER | ES_AUTOHSCROLL, rect.left, rect.top, w - 20, h, hListWnd, 0, GetModuleHandle(NULL), NULL); - SetWindowLong(hEdit, GWL_USERDATA, MAKELPARAM(ia->iItem, ia->iSubItem)); - int end = GetWindowTextLength(hEdit); - SendMessage(hEdit, EM_SETSEL, end, end); - SendMessage(hEdit, WM_SETFONT, (LPARAM)hDefFont, true); - cbOldEditDataEdit = (WNDPROC)SetWindowLong(hEdit, GWL_WNDPROC, (LONG)cbNewEditDataEdit); - SetFocus(hEdit); + // This event is triggered on ListView_SetData too. So ListView is hiding to prevent processing of the notification. + if (pHdr->code == HDN_ITEMCHANGED && pHdr->hwndFrom == ListView_GetHeader(hListWnd) && IsWindowVisible(hListWnd)) { + int* widths = (int*)GetProp(hWnd, TEXT("WIDTHS")); + int colNo = ((LPNMHEADER)lParam)->iItem; + widths[colNo] = ListView_GetColumnWidth(hListWnd, colNo); + SendMessage(hWnd, WMU_UPDATE_COLSIZE, 0, 0); } } break; @@ -853,7 +1052,7 @@ namespace dialogs { if (wParam == IDOK) { // User push Enter int pos = ListView_GetNextItem(hListWnd, -1, LVNI_SELECTED); if (hListWnd == GetFocus() && pos != -1) { - currCell = {hListWnd, pos, 0}; + currCell = {hListWnd, pos, currCell.iSubItem}; PostMessage(hWnd, WM_COMMAND, IDM_ROW_EDIT, 0); } } @@ -861,23 +1060,29 @@ namespace dialogs { if (cmd == IDC_DLG_CANCEL || cmd == IDCANCEL) SendMessage(hWnd, WM_CLOSE, 0, 0); - if (cmd == IDM_RESULT_COPY_CELL || cmd == IDM_RESULT_COPY_ROW || cmd == IDM_RESULT_EXPORT) + if (cmd == IDM_RESULT_CHART || cmd == IDM_RESULT_COPY_CELL || cmd == IDM_RESULT_COPY_ROW || cmd == IDM_RESULT_EXPORT) onListViewMenu(cmd, true); - if (cmd == IDC_DLG_ROW_ADD) + if (cmd == IDM_ROW_ADD) DialogBoxParam(GetModuleHandle(0), MAKEINTRESOURCE(IDD_ROW), hWnd, (DLGPROC)&cbDlgRow, MAKELPARAM(ROW_ADD, 0)); - if (cmd == IDC_DLG_ROW_DEL) - SendMessage(hWnd, WM_COMMAND, MAKELPARAM(IDM_ROW_DELETE, 0), 0); - - if (cmd == IDC_DLG_REFRESH) + if (cmd == IDM_ROW_REFRESH) SendMessage(hWnd, WMU_UPDATE_DATA, 0, 0); - if (cmd == IDM_ROW_EDIT) + if (cmd == IDM_VALUE_EDIT) { + TCHAR buf[MAX_TEXT_LENGTH]{0}; + ListView_GetItemText(hListWnd, currCell.iItem, currCell.iSubItem, buf, MAX_TEXT_LENGTH); + + if (DLG_OK == DialogBoxParam(GetModuleHandle(0), MAKEINTRESOURCE(IDD_EDITDATA_VALUE), hWnd, (DLGPROC)&cbDlgEditDataValue, (LPARAM)buf)) + ListView_UpdateCell(hListWnd, currCell.iItem, currCell.iSubItem, buf); + } + + if (cmd == IDM_ROW_EDIT) { DialogBoxParam(GetModuleHandle(0), MAKEINTRESOURCE(IDD_ROW), hWnd, (DLGPROC)&cbDlgRow, MAKELPARAM(isTable ? ROW_EDIT: ROW_VIEW, 0)); + SendMessage(hWnd, WMU_SET_CURRENT_CELL, currCell.iItem, currCell.iSubItem); + } if (cmd == IDM_ROW_DELETE) { - HWND hListWnd = GetDlgItem(hWnd, IDC_DLG_QUERYLIST); int count = ListView_GetSelectedCount(hListWnd); if (!count) return true; @@ -907,8 +1112,6 @@ namespace dialogs { delete [] buf8; } - - if (SQLITE_DONE == sqlite3_step(stmt)) { pos = -1; while((pos = ListView_GetNextItem(hListWnd, -1, LVNI_SELECTED)) != -1) @@ -1047,6 +1250,9 @@ namespace dialogs { delete [] path8; } + if (cmd == IDM_GENERATE_DATA && (DLG_OK == DialogBoxParam(GetModuleHandle(0), MAKEINTRESOURCE(IDD_TOOL_GENERATE_DATA), hMainWnd, (DLGPROC)&tools::cbDlgDataGenerator, (LPARAM)editTableData16))) + SendMessage(hWnd, WMU_UPDATE_DATA, 0, 0); + if (HIWORD(wParam) == EN_CHANGE && (HWND)lParam == GetDlgItem(hWnd, IDC_DLG_QUERYFILTER) && (HWND)lParam == GetFocus()) { KillTimer(hWnd, IDT_EDIT_DATA); SetTimer(hWnd, IDT_EDIT_DATA, 300, NULL); @@ -1054,34 +1260,6 @@ namespace dialogs { } break; - case WM_PARENTNOTIFY: { - if (LOWORD(wParam) == WM_LBUTTONDOWN) { - POINT p = {LOWORD(lParam), HIWORD(lParam)}; - ClientToScreen(hWnd, &p); - - HWND hBtn = WindowFromPoint(p); - TCHAR wndClass[256]; - GetClassName(hBtn, wndClass, 256); - if (hBtn && !_tcscmp(wndClass, WC_BUTTON)) - PostMessage(hWnd, WMU_EDIT_VALUE, GetWindowLong(hBtn, GWL_USERDATA), 0); - } - } - break; - - case WMU_EDIT_VALUE: { - // DialogBoxParam raises beep value cause focus changed - // To avoid beep use a post action - LONG data = wParam; - HWND hListWnd = GetDlgItem(hWnd, IDC_DLG_QUERYLIST); - - TCHAR buf[MAX_TEXT_LENGTH]{0}; - ListView_GetItemText(hListWnd, LOWORD(data), HIWORD(data), buf, MAX_TEXT_LENGTH); - - if (DLG_OK == DialogBoxParam(GetModuleHandle(0), MAKEINTRESOURCE(IDD_EDITDATA_VALUE), hWnd, (DLGPROC)&cbDlgEditDataValue, (LPARAM)buf)) - ListView_UpdateCell(hListWnd, LOWORD(data), HIWORD(data), buf); - } - break; - case WM_CLOSE: { char* tablename8 = (char*)GetProp(hWnd, TEXT("TABLENAME8")); delete [] tablename8; @@ -1095,6 +1273,10 @@ namespace dialogs { delete [] blobs; RemoveProp(hWnd, TEXT("BLOBS")); + int* widths = (int*)GetProp(hWnd, TEXT("WIDTHS")); + delete [] widths; + RemoveProp(hWnd, TEXT("WIDTHS")); + RemoveProp(hWnd, TEXT("HASROWID")); RemoveProp(hWnd, TEXT("KEYCOUNT")); @@ -1645,7 +1827,7 @@ namespace dialogs { #define CHART_BORDER 40 #define CHART_GRID 5 - #define CHART_BARS_LEFT 150 + #define CHART_BARS_LEFT 180 #define CHART_BAR_HEIGHT 20 #define CHART_BAR_SPACE 3 @@ -1985,12 +2167,13 @@ namespace dialogs { TextOut(hdc, x, y, name16, _tcslen(name16)); lineNo++; } + } - if (lineNo == 0 && type != CHART_NONE) { - SetProp(hWnd, TEXT("TYPE"), (HANDLE)CHART_NONE); - PostMessage(hWnd, WM_PAINT, 0, 0); - } + if (lineNo == 0) { + SetProp(hWnd, TEXT("TYPE"), (HANDLE)CHART_NONE); + PostMessage(hWnd, WM_PAINT, 0, 0); } + DeleteObject(hPen); } @@ -2044,6 +2227,92 @@ namespace dialogs { return false; } + // lParam and USERDATA are sqlite3 statement handle + BOOL CALLBACK cbDlgBindParameters (HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) { + switch (msg) { + case WM_INITDIALOG: { + SetWindowLong(hWnd, GWL_USERDATA, (LONG)lParam); + + sqlite3_stmt* stmt = (sqlite3_stmt*)lParam; + sqlite3_stmt* stmt2; + sqlite3_prepare_v2(db, "select value from preferences.query_params where lower(dbname) = lower(?1) and lower(name) = lower(?2)", -1, &stmt2, 0); + char* dbname8 = utils::getFileName(sqlite3_db_filename(db, 0)); + + int paramCount = sqlite3_bind_parameter_count(stmt); + for (int i = 1; i < paramCount + 1; i++) { + const char* name8 = (char*)sqlite3_bind_parameter_name(stmt, i); + TCHAR* name16 = utils::utf8to16(name8); + if (_tcslen(name16)) { + CreateWindow(WC_STATIC, name16, WS_VISIBLE | WS_CHILD | SS_RIGHT, 5, 10 + 30 * (i - 1), 100, 18, hWnd, (HMENU)(IDC_ROW_LABEL + i), GetModuleHandle(0), 0); + HWND hComboWnd = CreateWindow(WC_COMBOBOX, NULL, WS_VISIBLE | WS_CHILD | WS_BORDER | WS_CLIPSIBLINGS | WS_TABSTOP | CBS_DROPDOWN, 110, 7 + 30 * (i - 1), 184, 200, hWnd, (HMENU)(IDC_ROW_EDIT + i), GetModuleHandle(0), 0); + + sqlite3_reset(stmt2); + sqlite3_bind_text(stmt2, 1, dbname8, strlen(dbname8), SQLITE_TRANSIENT); + sqlite3_bind_text(stmt2, 2, name8, strlen(name8), SQLITE_TRANSIENT); + + while(SQLITE_ROW == sqlite3_step(stmt2)) { + TCHAR* value16 = utils::utf8to16((char*)sqlite3_column_text(stmt2, 0)); + ComboBox_AddString(hComboWnd, value16); + delete [] value16; + } + } else { + CreateWindow(WC_STATIC, TEXT("(Unnamed)"), WS_VISIBLE | WS_CHILD | SS_RIGHT, 5, 10 + 30 * (i - 1), 100, 18, hWnd, (HMENU)(IDC_ROW_LABEL + i), GetModuleHandle(0), 0); + CreateWindow(WC_EDIT, NULL, WS_VISIBLE | WS_CHILD | WS_BORDER | WS_CLIPSIBLINGS | WS_TABSTOP | CBS_DROPDOWN, 110, 7 + 30 * (i - 1), 184, 20, hWnd, (HMENU)(IDC_ROW_EDIT + i), GetModuleHandle(0), 0); + } + delete [] name16; + } + sqlite3_finalize(stmt2); + EnumChildWindows(hWnd, (WNDENUMPROC)cbEnumChildren, (LPARAM)ACTION_SETDEFFONT); + + SetWindowPos(hWnd, 0, 0, 0, 305, paramCount * 30 + 62, SWP_NOMOVE | SWP_NOZORDER); + SetWindowPos(GetDlgItem(hWnd, IDC_DLG_OK), 0, 204, paramCount * 30 + 8, 0, 0, SWP_NOSIZE | SWP_NOZORDER); + delete [] dbname8; + + SetFocus(GetDlgItem(hWnd, IDC_ROW_EDIT + 1)); + } + break; + + case WM_COMMAND: { + if (wParam == IDC_DLG_OK) { + sqlite3_stmt* stmt = (sqlite3_stmt*)GetWindowLong(hWnd, GWL_USERDATA);; + sqlite3_stmt* stmt2; + sqlite3_prepare_v2(db, "replace into preferences.query_params (dbname, name, value) values (?1, ?2, ?3);", -1, &stmt2, 0); + char* dbname8 = utils::getFileName(sqlite3_db_filename(db, 0)); + + int paramCount = sqlite3_bind_parameter_count(stmt); + for (int i = 1; i <= paramCount; i++) { + TCHAR name16[1024]; + GetDlgItemText(hWnd, IDC_ROW_LABEL + i, name16, 1023); + char* name8 = utils::utf16to8(name16); + + TCHAR value16[1024]; + GetDlgItemText(hWnd, IDC_ROW_EDIT + i, value16, 1023); + char* value8 = utils::utf16to8(value16); + utils::sqlite3_bind_variant(stmt, i, value8); + + sqlite3_reset(stmt2); + sqlite3_bind_text(stmt2, 1, dbname8, strlen(dbname8), SQLITE_TRANSIENT); + sqlite3_bind_text(stmt2, 2, name8, strlen(name8), SQLITE_TRANSIENT); + sqlite3_bind_text(stmt2, 3, value8, strlen(value8), SQLITE_TRANSIENT); + sqlite3_step(stmt2); + + delete [] name8; + delete [] value8; + } + sqlite3_finalize(stmt2); + + EndDialog(hWnd, DLG_OK); + } + + if (wParam == IDC_DLG_CANCEL || wParam == IDCANCEL) + EndDialog(hWnd, DLG_CANCEL); + } + break; + } + + return false; + } + BOOL CALLBACK cbEnumFont(LPLOGFONT lplf, LPNEWTEXTMETRIC lpntm, DWORD fontType, LPVOID hWnd) { if (fontType & TRUETYPE_FONTTYPE && lplf->lfFaceName[0] != TEXT('@')) ComboBox_AddString((HWND)hWnd, lplf->lfFaceName); @@ -2177,15 +2446,16 @@ namespace dialogs { DestroyWindow(hBtn); int data = GetWindowLong(hWnd, GWL_USERDATA); - if (!data) // Exit by Esc - return 0; - - int size = GetWindowTextLength(hWnd); - TCHAR value16[size + 1]{0}; - GetWindowText(hWnd, value16, size + 1); - ListView_UpdateCell(hListWnd, LOWORD(data), HIWORD(data), value16); + if (data) { + int size = GetWindowTextLength(hWnd); + TCHAR value16[size + 1]{0}; + GetWindowText(hWnd, value16, size + 1); + ListView_UpdateCell(hListWnd, LOWORD(data), HIWORD(data), value16); + } + SetFocus(hListWnd); } break; + case WM_KILLFOCUS: { DestroyWindow(hWnd); } @@ -2193,10 +2463,6 @@ namespace dialogs { case WM_KEYDOWN: { if (wParam == VK_RETURN) { - int style = GetWindowLong(hWnd, GWL_STYLE); - if((style & ES_MULTILINE) == ES_MULTILINE && GetAsyncKeyState(VK_CONTROL)) - break; - DestroyWindow(hWnd); } @@ -2254,6 +2520,29 @@ namespace dialogs { return CallWindowProc(cbOldAddTableCell, hWnd, msg, wParam, lParam); } + LRESULT CALLBACK cbNewFilterEdit(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) { + if (msg == WM_GETDLGCODE) + return (DLGC_WANTALLKEYS | CallWindowProc(cbOldHeaderEdit, hWnd, msg, wParam, lParam)); + + switch(msg){ + case WM_CHAR: + case WM_KEYDOWN: { + if (wParam == VK_RETURN || wParam == VK_ESCAPE) { + HWND hDlg = GetParent(GetParent(hWnd)); + if (msg == WM_KEYDOWN) { + SendMessage(hDlg, wParam == VK_RETURN ? WMU_UPDATE_DATA : WM_CLOSE, 0, 0); + SendMessage(GetParent(hDlg), wParam == VK_RETURN ? WMU_UPDATE_DATA : WM_CLOSE, 0, 0); + } + return 0; + } + } + break; + } + + return CallWindowProc(cbOldHeaderEdit, hWnd, msg, wParam, lParam); + } + + bool ListView_UpdateCell(HWND hListWnd, int rowNo, int colNo, TCHAR* value16) { HWND hHeader = (HWND)ListView_GetHeader(hListWnd); TCHAR column16[256]{0}; diff --git a/src/dialogs.h b/src/dialogs.h index 973ec85..9838f7d 100644 --- a/src/dialogs.h +++ b/src/dialogs.h @@ -16,6 +16,7 @@ namespace dialogs { BOOL CALLBACK cbDlgFind (HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); BOOL CALLBACK cbDlgDDL (HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); BOOL CALLBACK cbDlgChart (HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); + BOOL CALLBACK cbDlgBindParameters (HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); } #endif diff --git a/src/global.h b/src/global.h index 87232a7..b1ffc74 100644 --- a/src/global.h +++ b/src/global.h @@ -44,6 +44,7 @@ #include #include +#include "missing.h" #include "sqlite3.h" extern sqlite3 *db; @@ -59,8 +60,8 @@ extern const TCHAR *TYPES16u[5]; extern const TCHAR *TYPES16p[5]; extern HFONT hDefFont; -extern WNDPROC cbOldResultList; -LRESULT CALLBACK cbNewResultList(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); +extern WNDPROC cbOldListView; +LRESULT CALLBACK cbNewListView(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); bool CALLBACK cbEnumChildren (HWND hWnd, LPARAM action); int CALLBACK cbListComparator(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort); diff --git a/src/main.cpp b/src/main.cpp index 9b0c8b8..2d710b1 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -81,12 +81,12 @@ int enableDbObject(const char* name8, int type); int disableDbObject(const char* name8, int type); bool isQueryValid(const char* query); -WNDPROC cbOldMainTab, cbOldMainTabRenameEdit, cbOldTreeItemEdit, cbOldAutoComplete, cbOldResultList; +WNDPROC cbOldMainTab, cbOldMainTabRenameEdit, cbOldTreeItemEdit, cbOldAutoComplete, cbOldListView; LRESULT CALLBACK cbNewTreeItemEdit(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); LRESULT CALLBACK cbNewMainTab(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); LRESULT CALLBACK cbNewMainTabRename(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); LRESULT CALLBACK cbNewAutoComplete(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); -LRESULT CALLBACK cbNewResultList(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); +LRESULT CALLBACK cbNewListView(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); LRESULT CALLBACK cbMainWindow (HWND, UINT, WPARAM, LPARAM); int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { @@ -168,7 +168,7 @@ int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLin TCHAR* version16 = utils::utf8to16(version8); SendMessage(hStatusWnd, SB_SETTEXT, 0, (LPARAM)version16); delete [] version16; - SendMessage(hStatusWnd, SB_SETTEXT, 1, (LPARAM)TEXT(" GUI: 1.3.7")); + SendMessage(hStatusWnd, SB_SETTEXT, 1, (LPARAM)TEXT(" GUI: 1.3.8")); hTreeWnd = CreateWindowEx(0, WC_TREEVIEW, NULL, WS_VISIBLE | WS_CHILD | TVS_HASBUTTONS | TVS_HASLINES | TVS_LINESATROOT | WS_DISABLED | TVS_EDITLABELS, 0, 0, 100, 100, hMainWnd, (HMENU)IDC_TREE, hInstance, NULL); hMainTabWnd = CreateWindowEx(0, WC_STATIC, NULL, WS_VISIBLE | WS_CHILD | SS_NOTIFY, 100, 0, 100, 100, hMainWnd, (HMENU)IDC_MAINTAB, hInstance, NULL); @@ -294,6 +294,7 @@ LRESULT CALLBACK cbMainWindow (HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam switch (msg) { case WM_DESTROY: { sqlite3_close(db); + prefs::setSyncMode(0); if (prefs::get("restore-editor")) { int tabCurrent = SendMessage(hMainTabWnd, WMU_TAB_GET_CURRENT, 0, 0); @@ -332,6 +333,8 @@ LRESULT CALLBACK cbMainWindow (HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam if (!prefs::save()) MessageBox(0, TEXT("Settings saving failed"), TEXT("Error"), MB_OK); + prefs::setSyncMode(1); + PostQuitMessage (0); } break; @@ -475,9 +478,9 @@ LRESULT CALLBACK cbMainWindow (HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam SendMessage(hMainWnd, WM_CLOSE, 0, 0); if (cmd == IDM_OPEN) { - TCHAR path[MAX_PATH]{0}; - if (utils::openFile(path, TEXT("Databases (*.sqlite, *.sqlite3, *.db)\0*.sqlite;.sqlite3;.id\0All\0*.*\0"))) - openDb(path); + TCHAR path16[MAX_PATH]{0}; + if (utils::openFile(path16, TEXT("Databases (*.sqlite, *.sqlite3, *.db)\0*.sqlite;*.sqlite3;*.db\0All\0*.*\0"))) + openDb(path16); } if (cmd == IDM_CLOSE) @@ -485,12 +488,12 @@ LRESULT CALLBACK cbMainWindow (HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam if (cmd == IDM_ATTACH) { TCHAR path16[MAX_PATH]{0}; - if (utils::openFile(path16, TEXT("*.sqlite\0*.sqlite\0*.db\0*.db\0All\0*.*\0"))) { - TCHAR name16[256]{0}; + if (utils::openFile(path16, TEXT("Databases (*.sqlite, *.sqlite3, *.db)\0*.sqlite;*.sqlite3;*.db\0All\0*.*\0"))) { + TCHAR name16[MAX_PATH]{0}; _tsplitpath(path16, NULL, NULL, name16, NULL); for(int i = 0; name16[i]; i++) name16[i] = _totlower(name16[i]); - TCHAR query16[256]{0}; + TCHAR query16[2 * _tcslen(path16) + 255]{0}; _stprintf(query16, TEXT("attach database \"%s\" as \"%s\""), path16, name16); executeCommandQuery(query16); } @@ -1128,18 +1131,18 @@ LRESULT CALLBACK cbMainWindow (HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam } if (pHdr->hwndFrom == hTreeWnd && pHdr->code == (UINT)NM_CUSTOMDRAW) { - LPNMTVCUSTOMDRAW pCustomDraw = (LPNMTVCUSTOMDRAW)lParam; - if (pCustomDraw->nmcd.dwDrawStage == CDDS_PREPAINT) - return CDRF_NOTIFYITEMDRAW; - - if (pCustomDraw->nmcd.dwDrawStage == CDDS_ITEMPREPAINT) { - BOOL isCut = TreeView_GetItemState(pHdr->hwndFrom, (HTREEITEM)pCustomDraw->nmcd.dwItemSpec, TVIS_CUT) & TVIS_CUT; - pCustomDraw->clrText = pCustomDraw->clrTextBk != RGB(255, 255, 255) ? RGB(255, 255, 255) : - isCut ? RGB(200, 200, 200) : RGB(0, 0, 0); - } + LPNMTVCUSTOMDRAW pCustomDraw = (LPNMTVCUSTOMDRAW)lParam; + if (pCustomDraw->nmcd.dwDrawStage == CDDS_PREPAINT) + return CDRF_NOTIFYITEMDRAW; + + if (pCustomDraw->nmcd.dwDrawStage == CDDS_ITEMPREPAINT) { + BOOL isCut = TreeView_GetItemState(pHdr->hwndFrom, (HTREEITEM)pCustomDraw->nmcd.dwItemSpec, TVIS_CUT) & TVIS_CUT; + pCustomDraw->clrText = pCustomDraw->clrTextBk != RGB(255, 255, 255) ? RGB(255, 255, 255) : + isCut ? RGB(200, 200, 200) : RGB(0, 0, 0); + } - return CDRF_DODEFAULT; - } + return CDRF_DODEFAULT; + } if (pHdr->code == TTN_GETDISPINFO) { LPTOOLTIPTEXT pTtt = (LPTOOLTIPTEXT) lParam; @@ -1309,14 +1312,22 @@ LRESULT CALLBACK cbMainWindow (HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam return 0; } -LRESULT CALLBACK cbNewResultList(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) { +LRESULT CALLBACK cbNewListView(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) { + // Quick reference tooltip if (msg == WM_SYSKEYDOWN && wParam == VK_MENU) return 1; if (msg == WM_SYSKEYUP && wParam == VK_MENU && IsWindowVisible(hTooltipWnd)) SendMessage(hTooltipWnd, TTM_TRACKACTIVATE, false, 0); - return CallWindowProc(cbOldResultList, hWnd, msg, wParam, lParam); + // Prevent zero-width column resizing + if(msg == WM_NOTIFY) { + NMHDR* pHdr = (LPNMHDR)lParam; + if ((pHdr->code == HDN_BEGINTRACK || pHdr->code == HDN_DIVIDERDBLCLICK) && ListView_GetColumnWidth(hWnd, ((LPNMHEADER)lParam)->iItem) == 0) + return true; + } + + return CallWindowProc(cbOldListView, hWnd, msg, wParam, lParam); } LRESULT CALLBACK cbNewTreeItemEdit(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) { @@ -1362,7 +1373,17 @@ int executeCLIQuery(bool isPlan) { rc = sqlite3_prepare_v2(db, sql8, -1, &stmt, 0); } + if (sqlite3_bind_parameter_count(stmt)) + DialogBoxParam (GetModuleHandle(0), MAKEINTRESOURCE(IDD_BIND_PARAMETERS), hMainWnd, (DLGPROC)&dialogs::cbDlgBindParameters, (LPARAM)stmt); + TCHAR result16[MAX_TEXT_LENGTH]{0}; + if (sqlite3_bind_parameter_count(stmt)) { + _tcscat(result16, TEXT("EXECUTE: ")); + TCHAR* esql16 = utils::utf8to16(sqlite3_expanded_sql(stmt)); + _tcscat(result16, esql16); + _tcscat(result16, TEXT("\n")); + delete [] esql16; + } if (rc == SQLITE_OK) { DWORD tStart = GetTickCount(); @@ -1623,11 +1644,13 @@ int executeQuery(TCHAR* query, int tabId, bool isPlan) { } int colCount = rc == SQLITE_OK ? sqlite3_column_count(stmt) : 0; + if (sqlite3_bind_parameter_count(stmt)) + DialogBoxParam (GetModuleHandle(0), MAKEINTRESOURCE(IDD_BIND_PARAMETERS), hMainWnd, (DLGPROC)&dialogs::cbDlgBindParameters, (LPARAM)stmt); + HWND hResultWnd = 0; int rowCount = 0; if (rc == SQLITE_OK && colCount > 0) { hResultWnd = CreateWindow(WC_LISTVIEW, NULL, WS_TABSTOP | WS_CHILD | LVS_AUTOARRANGE | LVS_REPORT | LVS_SHOWSELALWAYS, 20, 20, 100, 100, tab->hTabWnd, (HMENU)0, GetModuleHandle(0), NULL); - cbOldResultList = (WNDPROC)SetWindowLong(hResultWnd, GWL_WNDPROC, (LONG)cbNewResultList); rowCount = ListView_SetData(hResultWnd, stmt, true); rc = sqlite3_errcode(db); if (rc != SQLITE_OK && rowCount == 0) { @@ -2566,6 +2589,9 @@ int ListView_SetData(HWND hListWnd, sqlite3_stmt *stmt, bool isRef) { ListView_SetColumnWidth(hListWnd, i, 60); } + if ((WNDPROC)GetWindowLong(hListWnd, GWL_WNDPROC) != cbNewListView) + cbOldListView = (WNDPROC)SetWindowLong(hListWnd, GWL_WNDPROC, (LONG)cbNewListView); + return isStopByLimit ? -rowNo : rowNo; } diff --git a/src/missing.h b/src/missing.h index 0484663..18f6370 100644 --- a/src/missing.h +++ b/src/missing.h @@ -11,4 +11,14 @@ typedef struct tagTVKEYDOWN { #define LVS_EX_AUTOSIZECOLUMNS 0x10000000 #define CFM_BACKCOLOR 0x04000000 +#define HDN_FILTERCHANGE (HDN_FIRST-12) +#define HDN_FILTERBTNCLICK (HDN_FIRST-13) +#define HDN_BEGINFILTEREDIT (HDN_FIRST-14) +#define HDN_ENDFILTEREDIT (HDN_FIRST-15) + +#define HDM_SETFILTERCHANGETIMEOUT (HDM_FIRST + 22) +#define HDM_CLEARFILTER (HDM_FIRST + 24) +#define LVN_INCREMENTALSEARCH (LVN_FIRST-63) + + #endif diff --git a/src/prefs.cpp b/src/prefs.cpp index c7f2f0d..9c5477f 100644 --- a/src/prefs.cpp +++ b/src/prefs.cpp @@ -22,7 +22,7 @@ namespace prefs { 100, 100, 800, 600, 200, 200, 0, 10, 1000, 1, 0, 8, 10, 20, - 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, + 0, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 10000, @@ -38,10 +38,15 @@ namespace prefs { return 0; } - void set(const char* name, int value) { + bool set(const char* name, int value) { + bool res = false; for (int i = 0; i < ICOUNT; i++) - if (!strcmp(iprops[i], name)) + if (!strcmp(iprops[i], name)) { ivalues[i] = value; + res = true; + } + + return res; } char* get(const char* name, const char* def) { @@ -58,7 +63,7 @@ namespace prefs { return value; } - bool set(const char* name, char* value) { + bool set(const char* name, const char* value) { sqlite3_stmt* stmt; if (SQLITE_OK != sqlite3_prepare(db, "replace into 'prefs' (name, value) values (?1, ?2);", -1, &stmt, 0)) return false; @@ -87,9 +92,9 @@ namespace prefs { "create table if not exists disabled (dbpath text not null, type text not null, name text not null, sql text, primary key (dbpath, type, name)); " \ "create table if not exists cli (\"time\" real, dbname text not null, query text not null, elapsed integer, result text); " \ "create table if not exists diagrams (dbname text, tblname text, x integer, y integer, width integer, height integer, primary key (dbname, tblname));" \ + "create table if not exists query_params (dbname text, name text, value text, primary key (dbname, name, value));" \ "create index if not exists idx_cli on cli (\"time\" desc, dbname);" \ - "commit;" \ - "pragma synchronous = 0;"; + "commit;"; if (SQLITE_OK != sqlite3_exec(db, sql8, 0, 0, 0)) return false; @@ -257,6 +262,7 @@ namespace prefs { return rc == SQLITE_DONE || rc == SQLITE_OK; } + bool setDiagramRect(const char* dbname, const char* table, RECT rect) { sqlite3_stmt* stmt; int rc = sqlite3_prepare(db, "replace into 'diagrams' (dbname, tblname, x, y, width, height) values (?1, ?2, ?3, ?4, ?5, ?6)", -1, &stmt, 0); @@ -274,4 +280,10 @@ namespace prefs { return rc == SQLITE_DONE || rc == SQLITE_OK; } + + bool setSyncMode(int mode) { + char query[255]; + sprintf(query, "pragma synchronous = %i;", mode); + return SQLITE_DONE == sqlite3_exec(db, query, 0, 0, 0); + } } diff --git a/src/prefs.h b/src/prefs.h index b4965c3..5d7f8f0 100644 --- a/src/prefs.h +++ b/src/prefs.h @@ -11,10 +11,10 @@ namespace prefs { bool backup(); int get(const char* name); - void set(const char* name, int value); + bool set(const char* name, int value); char* get(const char* name, const char* def); - bool set(const char* name, char* value); + bool set(const char* name, const char* value); bool setRecent(char* path); int getRecents(char** recents); @@ -25,6 +25,8 @@ namespace prefs { bool getDiagramRect(const char* dbname, const char* table, RECT* rect); bool setDiagramRect(const char* dbname, const char* table, RECT rect); + + bool setSyncMode(int mode); } #endif diff --git a/src/resource.h b/src/resource.h index 5443c5a..c289a1b 100644 --- a/src/resource.h +++ b/src/resource.h @@ -10,6 +10,7 @@ #define IDD_CHART 20 #define IDD_EDITDATA_VALUE 21 #define IDD_VIEWDATA_VALUE 22 +#define IDD_BIND_PARAMETERS 23 #define IDD_TOOL_EXPORT_CSV 30 #define IDD_TOOL_EXPORT_SQL 31 @@ -115,6 +116,8 @@ #define IDC_DLG_SEARCH_RESULT 193 #define IDC_DLG_SEARCH_ROWS 194 #define IDC_DLG_STATISTICS 195 +#define IDC_DLG_FILTER 196 +#define IDM_LAST_SEPARATOR 197 #define IDC_DLG_GEN_ISTRUNCATE 260 #define IDC_DLG_GEN_ROW_COUNT 261 @@ -134,6 +137,9 @@ #define IDC_TAB_EDIT 300 +#define IDC_HEADER_EDIT 400 // Iteratable +#define IDC_HEADER_STATIC 450 // Iteratable + #define IDC_MENU_MAIN 500 #define IDC_MENU_EDITOR 501 #define IDC_MENU_RESULT 502 @@ -215,7 +221,6 @@ #define IDM_EDITOR_DELETE 1604 #define IDM_EDITOR_FIND 1605 - #define IDM_RESULT_CHART 1610 #define IDM_RESULT_COPY_CELL 1611 #define IDM_RESULT_COPY_ROW 1612 @@ -247,9 +252,12 @@ #define IDM_ENABLE_ALL 1717 #define IDM_DISABLE_ALL 1718 -#define IDM_ROW_EDIT 1722 -#define IDM_ROW_DELETE 1723 -#define IDM_ROW_DUPLICATE 1724 +#define IDM_VALUE_EDIT 1721 +#define IDM_ROW_ADD 1722 +#define IDM_ROW_EDIT 1723 +#define IDM_ROW_DELETE 1724 +#define IDM_ROW_REFRESH 1725 +#define IDM_ROW_DUPLICATE 1726 #define IDM_LINK_FK 1731 #define IDM_LINK_VIEW 1732 @@ -258,10 +266,8 @@ #define IDI_LOGO 3000 #define IDB_TREEVIEW 3001 #define IDB_TOOLBAR 3002 -#define IDB_BTN_ADD 3003 -#define IDB_BTN_DELETE 3004 -#define IDB_BTN_REFRESH 3005 #define IDB_DIAGRAM_TOOLBAR 3006 +#define IDB_TOOLBAR_DATA 3010 #define IDS_CREATE_DDL 10000 #define IDS_CREATE_TABLE 10001 @@ -296,6 +302,9 @@ #define WMU_SHOW_TABLE_INFO WM_USER + 10 #define WMU_RESET_LISTVIEW WM_USER + 11 #define WMU_EDIT_VALUE WM_USER + 12 +#define WMU_UPDATE_COLSIZE WM_USER + 13 +#define WMU_SET_CURRENT_CELL WM_USER + 14 +#define WMU_SYNC_CURRENT_CELL WM_USER + 15 #define WMU_TAB_ADD WM_USER + 30 #define WMU_TAB_DELETE WM_USER + 31 @@ -305,7 +314,6 @@ #define WMU_TAB_GET_COUNT WM_USER + 36 #define WMU_TAB_GET_CURRENT WM_USER + 37 - #define NM_TAB_ADD WM_USER + 40 #define NM_TAB_DELETE WM_USER + 41 #define NM_TAB_REQUEST_DELETE WM_USER + 42 diff --git a/src/resource.rc b/src/resource.rc index be304df..53b7bbe 100644 --- a/src/resource.rc +++ b/src/resource.rc @@ -4,15 +4,15 @@ #include "resource.h" 1 VERSIONINFO -FILEVERSION 1, 3, 7, 0 +FILEVERSION 1, 3, 8, 0 FILEOS VOS_DOS_WINDOWS32 FILETYPE VFT_APP { BLOCK "StringFileInfo" { BLOCK "040904E4" { VALUE "FileDescription", "SQLite GUI\000" - VALUE "FileVersion", "1.3.7\000" + VALUE "FileVersion", "1.3.8\000" VALUE "ProductName", "sqlite-gui\000" - VALUE "ProductVersion", "1.3.7\000" + VALUE "ProductVersion", "1.3.8\000" VALUE "LegalCopyright", "Copyright \251 Little Brother 2020-2021\000" VALUE "OriginalFilename", "sqlite-gui.exe\000" } @@ -101,10 +101,6 @@ IDD_EDITDATA DIALOGEX 50, 50, 500, 300 CAPTION "View/Edit table data" FONT 10, "MS Shell Dlg", 400, 0, 0x1 BEGIN - CONTROL "", IDC_DLG_ROW_ADD, WC_STATIC, SS_LEFT | SS_BITMAP | SS_CENTERIMAGE | SS_NOTIFY, 0, 0, 10, 10, 0 - CONTROL "", IDC_DLG_ROW_DEL, WC_STATIC, SS_LEFT | SS_BITMAP | SS_CENTERIMAGE | SS_NOTIFY, 11, 0, 10, 10, 0 - EDITTEXT IDC_DLG_QUERYFILTER, 22, 0, 300, 10, WS_BORDER | WS_TABSTOP, 0 - CONTROL "", IDC_DLG_REFRESH, WC_STATIC, SS_LEFT | SS_BITMAP | SS_CENTERIMAGE | SS_NOTIFY, 300, 0, 10, 10, 0 CONTROL "", IDC_DLG_QUERYLIST, WC_LISTVIEW, WS_VSCROLL | WS_TABSTOP | LVS_REPORT, 0, 10, 400, 80, WS_EX_STATICEDGE END @@ -325,7 +321,7 @@ IDD_TOOL_GENERATE_DATA DIALOGEX 50, 50, 250, 300 FONT 8, "MS Shell Dlg", 400, 0, 0x1 BEGIN LTEXT "Target", IDC_DLG_LABEL, 5, 7, 30, 14, SS_LEFT - COMBOBOX IDC_DLG_TABLENAME, 30, 5, 120, 100, WS_TABSTOP | WS_VSCROLL | CBS_DROPDOWNLIST | CBS_SORT + COMBOBOX IDC_DLG_TABLENAME, 30, 5, 120, 100, WS_TABSTOP | WS_VSCROLL | CBS_DROPDOWNLIST LTEXT "Row count", IDC_DLG_LABEL, 165, 7, 40, 14, SS_LEFT EDITTEXT IDC_DLG_GEN_ROW_COUNT, 205, 5, 40, 12, WS_BORDER | WS_TABSTOP | ES_NUMBER | ES_CENTER AUTOCHECKBOX "Truncate data before generation", IDC_DLG_GEN_ISTRUNCATE, 5, 20, 140, 16, BS_NOTIFY @@ -366,6 +362,15 @@ IDD_CHART DIALOGEX 40, 30, 420, 260 BEGIN END +LANGUAGE 0, SUBLANG_NEUTRAL +IDD_BIND_PARAMETERS DIALOGEX 40, 30, 200, 260 + STYLE WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | DS_MODALFRAME + CAPTION "Binding values" + FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + PUSHBUTTON "OK", IDC_DLG_OK, 0, 0, 60, 14, WS_TABSTOP +END + LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL IDC_MENU_MAIN MENU BEGIN @@ -401,12 +406,12 @@ BEGIN MENUITEM "Database search", IDM_DATABASE_SEARCH, GRAYED MENUITEM "Workflow manager", IDM_WORKFLOW_MANAGER, GRAYED MENUITEM SEPARATOR - MENUITEM "Execute SQL file", IDM_IMPORT_SQL, GRAYED - MENUITEM "Import from CSV file", IDM_IMPORT_CSV, GRAYED + MENUITEM "Import SQL file", IDM_IMPORT_SQL, GRAYED + MENUITEM "Import CSV file", IDM_IMPORT_CSV, GRAYED MENUITEM "Import via ODBC", IDM_IMPORT_ODBC, GRAYED MENUITEM "Export to SQL file", IDM_EXPORT_SQL, GRAYED MENUITEM "Export as CSV file", IDM_EXPORT_CSV, GRAYED - MENUITEM "Data generator", IDM_GENERATE_DATA, GRAYED + MENUITEM "Generate data", IDM_GENERATE_DATA, GRAYED MENUITEM SEPARATOR MENUITEM "Integrity check", IDM_CHECK_INTEGRITY, GRAYED MENUITEM "Vacuum database", IDM_VACUUM, GRAYED @@ -571,6 +576,8 @@ IDC_MENU_EDIT_DATA MENU BEGIN POPUP "Edit data" BEGIN + MENUITEM "Edit value", IDM_VALUE_EDIT + MENUITEM SEPARATOR MENUITEM "Edit\tEnter", IDM_ROW_EDIT MENUITEM "Delete\tDel", IDM_ROW_DELETE MENUITEM "Duplicate", IDM_ROW_DUPLICATE @@ -676,8 +683,12 @@ BEGIN "* Do you need a newer version of sqlite?\n Just replace sqlite3.dll! 32-bit version is required.\n\n" \ "* Data generator requires ora extension.\n\n" \ "* Disabled indexes and triggers are stored in ""preferences.disabled"" table.\n\n" \ - "Cons\n* Only utf-8 is supported\n* NULL is displayed as an empty string\n* An empty string is set to NULL when data is edit\n* Data diagram isn't accurate with building references in views/triggers\n" - IDS_EXTENSIONS "Json1\n" \ + "Cons\n" \ + "* Only utf-8 is supported\n* NULL is displayed as an empty string\n" \ + "* An empty string is set to NULL when data is edit\n" \ + "* Data diagram isn't accurate with building references in views/triggers\n" \ + "* The app is a single threaded, so the interface freeze on long operations \n" + IDS_EXTENSIONS L"Json1\n" \ "Various SQL functions and table-valued functions for processing JSON.\n\n" \ "Math\n" \ "This library provides common mathematical and string functions: acos, asin, atan, atn2, atan2, acosh, asinh, atanh, difference, degrees, radians, cos, sin, tan, cot, cosh, sinh, tanh, coth, exp, log, log10, power, sign, sqrt, square, ceil, floor, pi, replicate, charindex, leftstr, rightstr, ltrim, rtrim, trim, replace, reverse, proper, padl, padr, padc, strfilter, stdev, variance, mode, median, lower_quartile, upper_quartile.\n\n" \ @@ -689,9 +700,9 @@ BEGIN "Regexp\n"\ "Implements regexp(regexp, stringToMatch) function and ""B regexp A"" operator. This is a non-POSIX regexp.\n\n" \ "Ora\n"\ - "Adds rownum(start), concat(str1, str2, ...), decode(expr, key1, value1, key2, value2, ..., defValue), crc32(str) and md5(str) functions.\n\n" \ + "Adds rownum(start), concat(str1, str2, ...), decode(expr, key1, value1, key2, value2, ..., defValue), strpart(str, delims, partno), crc32(str) and md5(str) functions.\n\n" \ "Icu (third party)\n"\ - "Adds partial support for national symbols e.g. ""select lower('٧);"" returns """"\n\n" \ + "Adds partial support for national symbols e.g. ""select lower('\x042B');"" returns ""\x044B""\n\n" \ "SQLite Workflow Manager adds auxiliary extensions: exec, odbc and transform. Check tool's Wiki to get info about them.\n\n" @@ -699,8 +710,7 @@ BEGIN "The type is defined by ""key"" column.\nSet a ""value""-column as null if sometimes generated value should be null.\n" \ "You should restart master to apply changes." - IDS_WELCOME "-- Welcome!\n" \ - "-- Thanks for choosing sqlite-gui.\n" \ + IDS_WELCOME "-- Welcome! Thanks for choosing sqlite-gui.\n" \ "-- bookstore.sqlite is a demo database for beginners.\n" \ "-- Push ""Execute"" to get all rows from ""books"" table\n" \ "select * from books;" @@ -730,7 +740,5 @@ END IDI_LOGO ICON "resources/logo.ico" IDB_TOOLBAR BITMAP "resources/toolbar.bmp" -IDB_BTN_ADD BITMAP "resources/btn_add.bmp" -IDB_BTN_DELETE BITMAP "resources/btn_delete.bmp" -IDB_BTN_REFRESH BITMAP "resources/btn_refresh.bmp" +IDB_TOOLBAR_DATA BITMAP "resources/toolbar_data.bmp" IDB_DIAGRAM_TOOLBAR BITMAP "resources/toolbar_diagram.bmp" diff --git a/src/tools.cpp b/src/tools.cpp index 2710898..6720bf6 100644 --- a/src/tools.cpp +++ b/src/tools.cpp @@ -453,8 +453,15 @@ namespace tools { TCHAR create16[MAX_TEXT_LENGTH]{0}; TCHAR insert16[MAX_TEXT_LENGTH]{0}; GetDlgItemText(hWnd, IDC_DLG_TABLENAME, buf16, 255); - _stprintf(create16, TEXT("create table \"%s\" ("), buf16); - _stprintf(insert16, TEXT("insert into \"%s\" ("), buf16); + + TCHAR* schema16 = utils::getName(buf16, true); + TCHAR* tablename16 = utils::getName(buf16); + + _stprintf(create16, TEXT("create table \"%s\".\"%s\" ("), schema16, tablename16); + _stprintf(insert16, TEXT("insert into \"%s\".\"%s\" ("), schema16, tablename16); + + delete [] tablename16; + delete [] schema16; auto catQuotted = [](TCHAR* a, TCHAR* b) { _tcscat(a, TEXT("\"")); @@ -693,7 +700,6 @@ namespace tools { if (!_tcslen(connectionString16)) return MessageBox(0, TEXT("Specify a valid connection string"), NULL, 0); - if (Button_GetCheck(GetDlgItem(hWnd, IDC_DLG_RECREATE_TARGET)) == BST_CHECKED) { TCHAR* schema16 = utils::getName(target16, true); TCHAR* tablename16 = utils::getName(target16); @@ -788,7 +794,7 @@ namespace tools { case WM_COMMAND: { TCHAR path16[MAX_PATH]{0}; - if (wParam == IDC_DLG_DATABASE_SELECTOR && utils::openFile(path16, TEXT("Databases (*.sqlite, *.sqlite3, *.db)\0*.sqlite;.sqlite3;.id\0All\0*.*\0"))) + if (wParam == IDC_DLG_DATABASE_SELECTOR && utils::openFile(path16, TEXT("Databases (*.sqlite, *.sqlite3, *.db)\0*.sqlite;*.sqlite3;*.db\0All\0*.*\0"))) SetDlgItemText(hWnd, IDC_DLG_DATABASE, path16); if (wParam == IDC_DLG_COMPARE_SCHEMA || wParam == IDC_DLG_COMPARE_DATA) { @@ -1203,6 +1209,8 @@ namespace tools { return CallWindowProc(cbOldCombobox, hWnd, msg, wParam, lParam); } + // USERDATA = 1 if a last generation is ok + // lParam is a target table (optional) BOOL CALLBACK cbDlgDataGenerator (HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) { switch (msg) { case WM_INITDIALOG: { @@ -1217,15 +1225,32 @@ namespace tools { if (prefs::get("data-generator-truncate")) Button_SetCheck(GetDlgItem(hWnd, IDC_DLG_GEN_ISTRUNCATE), BST_CHECKED); - sqlite3_stmt *stmt; - if (SQLITE_OK == sqlite3_prepare_v2(db, "select name from sqlite_master where type = 'table' and name <> 'sqlite_sequence' order by 1", -1, &stmt, 0)) { - while (SQLITE_ROW == sqlite3_step(stmt)) { - TCHAR* name16 = utils::utf8to16((char *)sqlite3_column_text(stmt, 0)); - ComboBox_AddString(hTable, name16); - delete [] name16; + if (lParam) { + ComboBox_AddString(hTable, (TCHAR*)lParam); + EnableWindow(hTable, !lParam); + } else { + sqlite3_stmt *stmt, *stmt2; + if (SQLITE_OK == sqlite3_prepare_v2(db, + "with t as (select name from pragma_database_list()) " \ + "select name from t order by iif(name = 'main', 1, name)", -1, &stmt, 0)) { + while (SQLITE_ROW == sqlite3_step(stmt)) { + char* schema8 = (char *)sqlite3_column_text(stmt, 0); + char query8[strlen(schema8) + 1024]; + sprintf(query8, + "select iif('%s' = 'main', name, '%s' || '.' || name) from \"%s\".sqlite_master where type = 'table' and name <> 'sqlite_sequence' order by 1", + schema8, schema8, schema8); + if (SQLITE_OK == sqlite3_prepare_v2(db, query8, -1, &stmt2, 0)) { + while (SQLITE_ROW == sqlite3_step(stmt2)) { + TCHAR* name16 = utils::utf8to16((char *)sqlite3_column_text(stmt2, 0)); + ComboBox_AddString(hTable, name16); + delete [] name16; + } + } + sqlite3_finalize(stmt2); + } } + sqlite3_finalize(stmt); } - sqlite3_finalize(stmt); ComboBox_SetCurSel(hTable, 0); if (!GENERATOR_TYPE[0]) { @@ -1261,6 +1286,7 @@ namespace tools { } sqlite3_finalize(stmt); } + SendMessage(hWnd, WMU_TARGET_CHANGED, 0, 0); SetFocus(hTable); } @@ -1275,13 +1301,17 @@ namespace tools { HWND hColumnsWnd = GetDlgItem(hWnd, IDC_DLG_GEN_COLUMNS); EnumChildWindows(hColumnsWnd, (WNDENUMPROC)cbEnumChildren, (LPARAM)ACTION_DESTROY); - TCHAR buf16[255]{0}; + TCHAR name16[1024]{0}; + GetDlgItemText(hWnd, IDC_DLG_TABLENAME, name16, 1024); + TCHAR* schema16 = utils::getName(name16, true); + TCHAR* tablename16 = utils::getName(name16); + TCHAR query16[MAX_TEXT_LENGTH]{0}; - GetDlgItemText(hWnd, IDC_DLG_TABLENAME, buf16, 255); + _stprintf(query16, TEXT("select name from pragma_table_info(\"%s\") where schema = \"%s\" order by cid"), tablename16, schema16); + delete [] tablename16; + delete [] schema16; - _stprintf(query16, TEXT("select name from pragma_table_info(\"%s\") order by cid"), buf16); char* query8 = utils::utf16to8(query16); - sqlite3_stmt *stmt; if (SQLITE_OK == sqlite3_prepare_v2(db, query8, -1, &stmt, 0)) { int rowNo = 0; @@ -1397,7 +1427,7 @@ namespace tools { SendMessage(hWnd, WMU_TARGET_CHANGED, 0, 0); if (wParam == IDC_DLG_CANCEL || wParam == IDCANCEL) { - EndDialog(hWnd, DLG_CANCEL); + EndDialog(hWnd, GetWindowLong(hWnd, GWL_USERDATA) ? DLG_OK : DLG_CANCEL); } if (wParam == IDC_DLG_GEN_DICTIONARY) { @@ -1418,12 +1448,18 @@ namespace tools { if (isTruncate && MessageBox(hWnd, TEXT("All data from table will be erased. Continue?"), TEXT("Confirmation"), MB_OKCANCEL | MB_ICONASTERISK) != IDOK) return true; - TCHAR table16[128]{0}; - GetDlgItemText(hWnd, IDC_DLG_TABLENAME, table16, 127); + TCHAR name16[1024]{0}; + GetDlgItemText(hWnd, IDC_DLG_TABLENAME, name16, 1024); + TCHAR* schema16 = utils::getName(name16, true); + TCHAR* tablename16 = utils::getName(name16); + + char* schema8 = utils::utf16to8(schema16); + char* tablename8 = utils::utf16to8(tablename16); + delete [] schema16; + delete [] tablename16; - char* table8 = utils::utf16to8(table16); char query8[MAX_TEXT_LENGTH]{0}; - sprintf(query8, "create table temp.data_generator as select null rownum, t.* from \"%s\" t where 1 = 2", table8); + sprintf(query8, "create table temp.data_generator as select null rownum, t.* from \"%s\".\"%s\" t where 1 = 2", schema8, tablename8); execute(query8); int rowCount = getDlgItemTextAsNumber(hWnd, IDC_DLG_GEN_ROW_COUNT); @@ -1475,7 +1511,7 @@ namespace tools { "series(val) as (select 1 union all select val + 1 from series limit (select ceil(%i.0/count(1)) from t)), " \ "t2 as (select t.value FROM t, series order by random()), " \ "t3 as (select rownum(1) rownum, t2.value from t2 order by 1 limit %i)" - "update temp.data_generator set \"%s\" = (select value from t3 where t3.rownum = temp.data_generator.rownum)"), + "update temp.data_generator set \"%s\" = t3.value from t3 where t3.rownum = temp.data_generator.rownum"), refcolumn16, reftable16, rowCount, rowCount, name16); } @@ -1509,22 +1545,24 @@ namespace tools { hColumnWnd = GetWindow(hColumnWnd, GW_HWNDNEXT); } - prefs::set("data-generator-row-count", rowCount); prefs::set("data-generator-truncate", +isTruncate); if (isTruncate) { - sprintf(query8, "delete from \"%s\"", table8); + sprintf(query8, "delete from \"%s\".\"%s\"", schema8, tablename8); execute(query8); } - snprintf(query8, MAX_TEXT_LENGTH, "insert into \"%s\" (%s) select %s from temp.data_generator", table8, columns8, columns8); - if (execute(query8)) + snprintf(query8, MAX_TEXT_LENGTH, "insert into \"%s\".\"%s\" (%s) select %s from temp.data_generator", schema8, tablename8, columns8, columns8); + int rc = execute(query8); + if (rc) MessageBox(hWnd, TEXT("Done!"), TEXT("Info"), MB_OK); else showDbError(hWnd); + SetWindowLong(hWnd, GWL_USERDATA, rc); - delete [] table8; + delete [] schema8; + delete [] tablename8; } } break; @@ -1659,7 +1697,9 @@ namespace tools { GetWindowText(hWnd, table16, 255); char* table8 = utils::utf16to8(table16); RECT rc = {pos.x, pos.y, rcSize.right - rcSize.left, rcSize.bottom - rcSize.top}; + prefs::setSyncMode(0); prefs::setDiagramRect(dbname8, table8, rc); + prefs::setSyncMode(1); delete [] table8; } @@ -1801,7 +1841,9 @@ namespace tools { } sqlite3_finalize(stmt); + prefs::setSyncMode(0); EnumChildWindows(hWnd, (WNDENUMPROC)cbEnumChildren, (LPARAM)ACTION_SETDEFFONT); + prefs::setSyncMode(1); SetWindowPos(hWnd, 0, prefs::get("x") + 30, prefs::get("y") + 70, prefs::get("width") - 60, prefs::get("height") - 100, SWP_NOZORDER); ShowWindow (hWnd, prefs::get("maximized") == 1 ? SW_MAXIMIZE : SW_SHOW); @@ -1829,6 +1871,7 @@ namespace tools { int dy = cursor.y - GET_Y_LPARAM(lParam); if (isMove && (dx != 0 ||dy != 0)) { + prefs::setSyncMode(0); int tblNo = 0; while(HWND hTableWnd = GetDlgItem(hWnd, IDC_DATABASE_DIAGRAM_TABLE + tblNo)) { RECT rc; @@ -1838,6 +1881,7 @@ namespace tools { SetWindowPos(hTableWnd, 0, p.x - dx, p.y - dy, 0, 0, SWP_NOZORDER | SWP_NOSIZE); tblNo++; } + prefs::setSyncMode(1); } isMove = false; diff --git a/src/utils.cpp b/src/utils.cpp index ee30d6f..ea7777f 100644 --- a/src/utils.cpp +++ b/src/utils.cpp @@ -136,6 +136,20 @@ namespace utils { CloseClipboard(); } + TCHAR* getClipboardText() { + if (OpenClipboard(NULL)) { + HANDLE clip = GetClipboardData(CF_UNICODETEXT); + TCHAR* str = (LPWSTR)GlobalLock(clip); + TCHAR* res = new TCHAR[_tcslen(str) + 1]{0}; + _tcscpy(res, str); + GlobalUnlock(clip); + CloseClipboard(); + return res; + } + + return new TCHAR[1]{0}; + } + int openFile(TCHAR* path, const TCHAR* filter) { OPENFILENAME ofn = {0}; diff --git a/src/utils.h b/src/utils.h index a23e409..c2b5f59 100644 --- a/src/utils.h +++ b/src/utils.h @@ -16,6 +16,7 @@ namespace utils { char* utf16to8(const TCHAR* in); void setClipboardText(const TCHAR* text); + TCHAR* getClipboardText(); int openFile(TCHAR* path, const TCHAR* filter); int saveFile(TCHAR* path, const TCHAR* filter); bool isFileExists(const TCHAR* path);