// QuickNote: type directly on the Windows desktop. // Double-click an empty spot on the wallpaper and start typing. The text stays // on the desktop (behind your windows, like desktop icons) and is saved automatically. // Drag across text to select it, Ctrl+C to copy. Drag the grip on the left edge to move a note. #ifndef UNICODE #define UNICODE #endif #ifndef _UNICODE #define _UNICODE #endif #define WIN32_LEAN_AND_MEAN #define _WIN32_WINNT 0x0A00 #define COBJMACROS #include #include #include #include #include #include #include #define APP_NAME L"QuickNote" #define WM_TRAY (WM_APP + 1) #define WM_DESKDBL (WM_APP + 2) #define WM_KILLNOTE (WM_APP + 3) #define MAX_NOTES 256 #define MAX_LINES 2000 #define FONT_PT 10 #define TIMER_BLINK 1 #define TIMER_SAVE 2 #define TIMER_CHECK 3 #define IDM_NEW 100 #define IDM_HIDE 101 #define IDM_STARTUP 102 #define IDM_HELP 103 #define IDM_EXIT 104 #define IDM_COLOR0 200 #define IDM_COPY 400 #define IDM_COPYALL 401 #define IDM_PASTE 402 #define IDM_SELALL 403 #define IDM_DELETE 404 typedef struct { BOOL used; HWND hwnd; WCHAR *text; int len, cap; int caret; // cursor position int anchor; // other end of the selection (== caret when nothing is selected) COLORREF color; BOOL active, caretOn, hover; } Note; static const COLORREF COLORS[] = { RGB(0, 0, 0), RGB(255, 255, 255), RGB(255, 214, 10), RGB(255, 140, 40), RGB(240, 70, 70), RGB(70, 200, 110), RGB(80, 160, 255), RGB(235, 110, 200) }; static const WCHAR *COLOR_NAMES[] = { L"Black", L"White", L"Yellow", L"Orange", L"Red", L"Green", L"Blue", L"Pink" }; #define NCOLORS 8 static Note g_notes[MAX_NOTES]; static HINSTANCE g_inst; static HWND g_main; static HHOOK g_hook; static NOTIFYICONDATAW g_nid; static UINT g_taskbarCreated; static int g_dpi = 96; static HFONT g_font; static WCHAR g_file[MAX_PATH]; static BOOL g_dirty, g_hidden; static HWND g_lastFg; static int g_lastColor = 0; // mouse state inside a note static BOOL g_moving, g_selecting; static POINT g_dragStart, g_dragOrig; // double-click detection in the hook static DWORD g_lastDownTime; static POINT g_lastDownPt; static int S(int v) { return MulDiv(v, g_dpi, 96); } static int GripW(void) { return S(8); } // grip square size /* ---------------- settings ---------------- */ static const WCHAR *RUN_KEY = L"Software\\Microsoft\\Windows\\CurrentVersion\\Run"; static BOOL StartupEnabled(void) { WCHAR buf[MAX_PATH * 2]; DWORD sz = sizeof(buf); return RegGetValueW(HKEY_CURRENT_USER, RUN_KEY, APP_NAME, RRF_RT_REG_SZ, NULL, buf, &sz) == ERROR_SUCCESS; } static void SetStartup(BOOL on) { HKEY k; if (RegOpenKeyExW(HKEY_CURRENT_USER, RUN_KEY, 0, KEY_WRITE, &k) != ERROR_SUCCESS) return; if (on) { WCHAR exe[MAX_PATH], val[MAX_PATH + 4]; GetModuleFileNameW(NULL, exe, MAX_PATH); swprintf(val, MAX_PATH + 4, L"\"%ls\"", exe); RegSetValueExW(k, APP_NAME, 0, REG_SZ, (BYTE *)val, (DWORD)((wcslen(val) + 1) * sizeof(WCHAR))); } else { RegDeleteValueW(k, APP_NAME); } RegCloseKey(k); } static DWORD RegGetFlag(const WCHAR *name) { DWORD v = 0, sz = sizeof(v); RegGetValueW(HKEY_CURRENT_USER, L"Software\\QuickNote", name, RRF_RT_REG_DWORD, NULL, &v, &sz); return v; } static void RegSetFlag(const WCHAR *name, DWORD v) { HKEY k; if (RegCreateKeyExW(HKEY_CURRENT_USER, L"Software\\QuickNote", 0, NULL, 0, KEY_WRITE, NULL, &k, NULL) == ERROR_SUCCESS) { RegSetValueExW(k, name, 0, REG_DWORD, (BYTE *)&v, sizeof(v)); RegCloseKey(k); } } /* ---------------- text buffer ---------------- */ static void EnsureCap(Note *n, int need) { if (n->cap >= need + 1) return; int c = n->cap ? n->cap : 64; while (c < need + 1) c *= 2; n->text = (WCHAR *)realloc(n->text, c * sizeof(WCHAR)); n->cap = c; } static BOOL HasSel(Note *n) { return n->anchor != n->caret; } static void SelRange(Note *n, int *a, int *b) { *a = n->anchor < n->caret ? n->anchor : n->caret; *b = n->anchor < n->caret ? n->caret : n->anchor; } static void DeleteRange(Note *n, int a, int b) { if (a < 0) a = 0; if (b > n->len) b = n->len; if (b <= a) return; memmove(n->text + a, n->text + b, (n->len - b) * sizeof(WCHAR)); n->len -= (b - a); n->text[n->len] = 0; n->caret = n->anchor = a; } static void DeleteSel(Note *n) { if (!HasSel(n)) return; int a, b; SelRange(n, &a, &b); DeleteRange(n, a, b); } static void InsertText(Note *n, const WCHAR *s, int cnt) { DeleteSel(n); EnsureCap(n, n->len + cnt); memmove(n->text + n->caret + cnt, n->text + n->caret, (n->len - n->caret) * sizeof(WCHAR)); memcpy(n->text + n->caret, s, cnt * sizeof(WCHAR)); n->len += cnt; n->caret += cnt; n->anchor = n->caret; n->text[n->len] = 0; } static int LineStarts(Note *n, int *starts) { int c = 0; starts[c++] = 0; for (int i = 0; i < n->len && c < MAX_LINES; i++) if (n->text[i] == L'\n') starts[c++] = i + 1; return c; } static int LineEnd(Note *n, int *starts, int count, int line) { return (line + 1 < count) ? starts[line + 1] - 1 : n->len; } static int LineOfIndex(int *starts, int count, int idx) { int l = 0; while (l + 1 < count && starts[l + 1] <= idx) l++; return l; } static BOOL IsWordChar(WCHAR c) { return c != L' ' && c != L'\t' && c != L'\n'; } /* ---------------- layout ---------------- */ typedef struct { int left, pad, lineH, w, h, lines; } Layout; static void NoteLayout(Note *n, HDC dc, int *starts, Layout *L) { TEXTMETRICW tm; GetTextMetricsW(dc, &tm); L->pad = S(4); L->left = GripW() + S(3); L->lineH = tm.tmHeight; L->lines = LineStarts(n, starts); int maxw = 0; for (int i = 0; i < L->lines; i++) { int a = starts[i], b = LineEnd(n, starts, L->lines, i); SIZE sz = { 0, 0 }; if (b > a) GetTextExtentPoint32W(dc, n->text + a, b - a, &sz); if (sz.cx > maxw) maxw = sz.cx; } if (maxw < S(20)) maxw = S(20); L->w = L->left + maxw + L->pad + S(4); L->h = L->lines * L->lineH + L->pad * 2; } static int TextX(HDC dc, Note *n, int lineStart, int idx) { SIZE sz = { 0, 0 }; if (idx > lineStart) GetTextExtentPoint32W(dc, n->text + lineStart, idx - lineStart, &sz); return sz.cx; } static int CaretFromPoint(Note *n, int x, int y) { HDC dc = CreateCompatibleDC(NULL); SelectObject(dc, g_font); static int starts[MAX_LINES]; Layout L; NoteLayout(n, dc, starts, &L); int line = (y < L.pad) ? 0 : (y - L.pad) / L.lineH; if (line >= L.lines) line = L.lines - 1; int a = starts[line], b = LineEnd(n, starts, L.lines, line), cnt = b - a; int lx = x - L.left, result = a; if (y < 0) result = 0; // dragged above the note: start of text else if (y >= L.h) result = n->len; // dragged below: end of text else if (cnt > 0 && lx > 0) { int *dx = (int *)malloc(cnt * sizeof(int)); SIZE sz; GetTextExtentExPointW(dc, n->text + a, cnt, 0, NULL, dx, &sz); result = b; for (int i = 0; i < cnt; i++) { int left = i ? dx[i - 1] : 0, right = dx[i]; if (lx < (left + right) / 2) { result = a + i; break; } } free(dx); } DeleteDC(dc); return result; } /* ---------------- desktop z-order ---------------- */ static HWND FindDesktopHost(void) { HWND p = FindWindowW(L"Progman", NULL); if (p && FindWindowExW(p, NULL, L"SHELLDLL_DefView", NULL)) return p; HWND w = NULL; while ((w = FindWindowExW(NULL, w, L"WorkerW", NULL)) != NULL) if (FindWindowExW(w, NULL, L"SHELLDLL_DefView", NULL)) return w; return p; } static void PushToDesktop(HWND h) { SetWindowPos(h, HWND_BOTTOM, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE); // adjusted in WM_WINDOWPOSCHANGING } static BOOL IsDesktopClass(HWND h) { WCHAR cls[64]; if (!h || !GetClassNameW(h, cls, 64)) return FALSE; return !wcscmp(cls, L"Progman") || !wcscmp(cls, L"WorkerW"); } /* ---------------- rendering ---------------- */ static void DrawTextLayer(HDC dc, Note *n, int *starts, Layout *L) { SetBkMode(dc, TRANSPARENT); SelectObject(dc, g_font); int lum = (GetRValue(n->color) * 3 + GetGValue(n->color) * 6 + GetBValue(n->color)) / 10; for (int pass = (lum > 128 ? 0 : 1); pass < 2; pass++) { // light text gets a dark shadow; dark text none SetTextColor(dc, pass == 0 ? RGB(0, 0, 0) : n->color); int off = pass == 0 ? 1 : 0; for (int i = 0; i < L->lines; i++) { int a = starts[i], b = LineEnd(n, starts, L->lines, i); if (b <= a) continue; TextOutW(dc, L->left + off, L->pad + i * L->lineH + off, n->text + a, b - a); } } } static void RenderNote(Note *n) { if (!n->used || !n->hwnd) return; static int starts[MAX_LINES]; HDC screen = GetDC(NULL); HDC mdc = CreateCompatibleDC(screen); SelectObject(mdc, g_font); Layout L; NoteLayout(n, mdc, starts, &L); int W = L.w, H = L.h; // caret position int cline = LineOfIndex(starts, L.lines, n->caret); int caretX = L.left + TextX(mdc, n, starts[cline], n->caret), caretY = L.pad + cline * L.lineH; // selection: per line pixel span [x1, x2) int *selX1 = NULL, *selX2 = NULL, sa = 0, sb = 0, la = 0, lb = -1; if (HasSel(n)) { SelRange(n, &sa, &sb); la = LineOfIndex(starts, L.lines, sa); lb = LineOfIndex(starts, L.lines, sb); selX1 = (int *)calloc(L.lines, sizeof(int)); selX2 = (int *)calloc(L.lines, sizeof(int)); for (int i = la; i <= lb; i++) { int ls = starts[i], le = LineEnd(n, starts, L.lines, i); selX1[i] = L.left + (i == la ? TextX(mdc, n, ls, sa) : 0); selX2[i] = L.left + (i == lb ? TextX(mdc, n, ls, sb) : TextX(mdc, n, ls, le) + S(4)); } } DeleteDC(mdc); BITMAPINFO bi; ZeroMemory(&bi, sizeof(bi)); bi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); bi.bmiHeader.biWidth = W; bi.bmiHeader.biHeight = -H; bi.bmiHeader.biPlanes = 1; bi.bmiHeader.biBitCount = 32; bi.bmiHeader.biCompression = BI_RGB; BYTE *pb = NULL, *pw = NULL, *po = NULL; HDC db = CreateCompatibleDC(screen), dw = CreateCompatibleDC(screen), dout = CreateCompatibleDC(screen); HBITMAP bb = CreateDIBSection(screen, &bi, DIB_RGB_COLORS, (void **)&pb, NULL, 0); HBITMAP bw = CreateDIBSection(screen, &bi, DIB_RGB_COLORS, (void **)&pw, NULL, 0); HBITMAP bo = CreateDIBSection(screen, &bi, DIB_RGB_COLORS, (void **)&po, NULL, 0); if (bb && bw && bo) { HGDIOBJ ob = SelectObject(db, bb), ow = SelectObject(dw, bw), oo = SelectObject(dout, bo); RECT all = { 0, 0, W, H }; FillRect(db, &all, (HBRUSH)GetStockObject(BLACK_BRUSH)); FillRect(dw, &all, (HBRUSH)GetStockObject(WHITE_BRUSH)); DrawTextLayer(db, n, starts, &L); DrawTextLayer(dw, n, starts, &L); GdiFlush(); // the only extra mark is a small grip square, shown while pointing at or editing the note BOOL showGrip = n->active || n->hover; int gw = GripW(), gy = L.pad + (L.lineH - gw) / 2; int cw = S(1) > 1 ? S(1) : 1; BYTE tr = GetRValue(n->color), tg = GetGValue(n->color), tb = GetBValue(n->color); for (int y = 0; y < H; y++) { int line = (y >= L.pad) ? (y - L.pad) / L.lineH : -1; BOOL lineSel = selX1 && line >= la && line <= lb && line < L.lines; for (int x = 0; x < W; x++) { int i = (y * W + x) * 4; int d = ((pw[i] - pb[i]) + (pw[i + 1] - pb[i + 1]) + (pw[i + 2] - pb[i + 2])) / 3; int a = 255 - d; if (a < 0) a = 0; if (a > 255) a = 255; int c0 = pb[i], c1 = pb[i + 1], c2 = pb[i + 2]; if (c0 > a) c0 = a; if (c1 > a) c1 = a; if (c2 > a) c2 = a; // layers under the text: selection highlight and the grip square int ua = 0, ur = 0, ug = 0, ub = 0; if (lineSel && x >= selX1[line] && x < selX2[line]) { ur = 51; ug = 153; ub = 255; ua = 130; } else if (showGrip && x < gw && y >= gy && y < gy + gw) { ur = ug = ub = 120; ua = 210; } if (ua) { int inv = 255 - a; c0 += (ub * ua / 255) * inv / 255; c1 += (ug * ua / 255) * inv / 255; c2 += (ur * ua / 255) * inv / 255; a += ua * inv / 255; } if (n->active && n->caretOn && !HasSel(n) && x >= caretX && x < caretX + cw && y >= caretY && y < caretY + L.lineH) { c0 = tb; c1 = tg; c2 = tr; a = 255; } if (a < 1) a = 1; // whole box stays clickable po[i] = (BYTE)c0; po[i + 1] = (BYTE)c1; po[i + 2] = (BYTE)c2; po[i + 3] = (BYTE)a; } } SIZE sz = { W, H }; POINT src = { 0, 0 }; BLENDFUNCTION bf = { AC_SRC_OVER, 0, 255, AC_SRC_ALPHA }; UpdateLayeredWindow(n->hwnd, screen, NULL, &sz, dout, &src, 0, &bf, ULW_ALPHA); SelectObject(db, ob); SelectObject(dw, ow); SelectObject(dout, oo); } free(selX1); free(selX2); if (bb) DeleteObject(bb); if (bw) DeleteObject(bw); if (bo) DeleteObject(bo); DeleteDC(db); DeleteDC(dw); DeleteDC(dout); ReleaseDC(NULL, screen); } /* ---------------- saving ---------------- */ static void MarkDirty(void) { g_dirty = TRUE; SetTimer(g_main, TIMER_SAVE, 1200, NULL); } static void SaveNotes(void) { g_dirty = FALSE; size_t cap = 4096, len = 0; WCHAR *buf = (WCHAR *)malloc(cap * sizeof(WCHAR)); for (int k = 0; k < MAX_NOTES; k++) { Note *n = &g_notes[k]; if (!n->used || n->len == 0) continue; RECT r; GetWindowRect(n->hwnd, &r); WCHAR head[96]; int hl = swprintf(head, 96, L"%ld\t%ld\t%d\t%06lX\t", r.left, r.top, FONT_PT, (unsigned long)n->color); size_t need = len + hl + n->len * 2 + 4; if (need > cap) { while (need > cap) cap *= 2; buf = (WCHAR *)realloc(buf, cap * sizeof(WCHAR)); } memcpy(buf + len, head, hl * sizeof(WCHAR)); len += hl; for (int i = 0; i < n->len; i++) { WCHAR c = n->text[i]; if (c == L'\\') { buf[len++] = L'\\'; buf[len++] = L'\\'; } else if (c == L'\n') { buf[len++] = L'\\'; buf[len++] = L'n'; } else if (c == L'\t') { buf[len++] = L'\\'; buf[len++] = L't'; } else if (c == L'\r') { } else buf[len++] = c; } buf[len++] = L'\n'; } int bytes = WideCharToMultiByte(CP_UTF8, 0, buf, (int)len, NULL, 0, NULL, NULL); char *utf8 = (char *)malloc(bytes + 1); WideCharToMultiByte(CP_UTF8, 0, buf, (int)len, utf8, bytes, NULL, NULL); WCHAR tmp[MAX_PATH + 8]; swprintf(tmp, MAX_PATH + 8, L"%ls.tmp", g_file); HANDLE f = CreateFileW(tmp, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); if (f != INVALID_HANDLE_VALUE) { DWORD wr; WriteFile(f, utf8, bytes, &wr, NULL); CloseHandle(f); MoveFileExW(tmp, g_file, MOVEFILE_REPLACE_EXISTING); } free(utf8); free(buf); } /* ---------------- notes ---------------- */ static Note *NewNoteSlot(void) { for (int i = 0; i < MAX_NOTES; i++) if (!g_notes[i].used) { ZeroMemory(&g_notes[i], sizeof(Note)); return &g_notes[i]; } return NULL; } static void ForceForeground(HWND h) { HWND fg = GetForegroundWindow(); DWORD ft = fg ? GetWindowThreadProcessId(fg, NULL) : 0, me = GetCurrentThreadId(); if (ft && ft != me) AttachThreadInput(me, ft, TRUE); BringWindowToTop(h); SetForegroundWindow(h); SetFocus(h); if (ft && ft != me) AttachThreadInput(me, ft, FALSE); } static Note *CreateNote(int x, int y, const WCHAR *text, int tlen, COLORREF color, BOOL activate) { Note *n = NewNoteSlot(); if (!n) return NULL; n->used = TRUE; n->color = color; EnsureCap(n, tlen); if (tlen) memcpy(n->text, text, tlen * sizeof(WCHAR)); n->len = tlen; n->text[n->len] = 0; n->caret = n->anchor = tlen; n->active = activate; n->caretOn = TRUE; n->hwnd = CreateWindowExW(WS_EX_LAYERED | WS_EX_TOOLWINDOW, L"QuickNoteText", L"QuickNote", WS_POPUP, x, y, 10, 10, NULL, NULL, g_inst, n); if (!n->hwnd) { free(n->text); n->used = FALSE; return NULL; } RenderNote(n); if (g_hidden) return n; if (activate) { ShowWindow(n->hwnd, SW_SHOW); ForceForeground(n->hwnd); } else { ShowWindow(n->hwnd, SW_SHOWNA); PushToDesktop(n->hwnd); } return n; } static void DeleteNote(Note *n) { if (!n->used) return; n->used = FALSE; if (n->hwnd) DestroyWindow(n->hwnd); n->hwnd = NULL; free(n->text); n->text = NULL; n->len = n->cap = 0; MarkDirty(); } static void LoadNotes(void) { HANDLE f = CreateFileW(g_file, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); if (f == INVALID_HANDLE_VALUE) return; DWORD size = GetFileSize(f, NULL), rd = 0; char *utf8 = (char *)malloc(size + 1); ReadFile(f, utf8, size, &rd, NULL); CloseHandle(f); int wl = MultiByteToWideChar(CP_UTF8, 0, utf8, (int)rd, NULL, 0); WCHAR *w = (WCHAR *)malloc((wl + 1) * sizeof(WCHAR)); MultiByteToWideChar(CP_UTF8, 0, utf8, (int)rd, w, wl); w[wl] = 0; free(utf8); WCHAR *line = w; while (line && *line) { WCHAR *nl = wcschr(line, L'\n'); if (nl) *nl = 0; long x, y; int sz; unsigned long col; int consumed = 0; if (swscanf(line, L"%ld\t%ld\t%d\t%lx\t%n", &x, &y, &sz, &col, &consumed) >= 4 && consumed > 0) { WCHAR *src = line + consumed; int sl = (int)wcslen(src); WCHAR *txt = (WCHAR *)malloc((sl + 1) * sizeof(WCHAR)); int tl = 0; for (int i = 0; i < sl; i++) { if (src[i] == L'\\' && i + 1 < sl) { WCHAR e = src[++i]; txt[tl++] = e == L'n' ? L'\n' : e == L't' ? L'\t' : e; } else if (src[i] != L'\r') txt[tl++] = src[i]; } POINT p = { x + 5, y + 5 }; if (!MonitorFromPoint(p, MONITOR_DEFAULTTONULL)) { RECT wa; SystemParametersInfoW(SPI_GETWORKAREA, 0, &wa, 0); x = wa.left + S(40); y = wa.top + S(40); } if (tl > 0) CreateNote((int)x, (int)y, txt, tl, (COLORREF)col, FALSE); free(txt); } line = nl ? nl + 1 : NULL; } free(w); } static void SetAllHidden(BOOL hide) { g_hidden = hide; for (int i = 0; i < MAX_NOTES; i++) { Note *n = &g_notes[i]; if (!n->used) continue; if (hide) ShowWindow(n->hwnd, SW_HIDE); else { ShowWindow(n->hwnd, SW_SHOWNA); PushToDesktop(n->hwnd); } } } /* ---------------- clipboard ---------------- */ static void CopyToClipboard(HWND h, const WCHAR *s, int len) { if (len <= 0 || !OpenClipboard(h)) return; EmptyClipboard(); int extra = 0; for (int i = 0; i < len; i++) if (s[i] == L'\n') extra++; HGLOBAL g = GlobalAlloc(GMEM_MOVEABLE, (len + extra + 1) * sizeof(WCHAR)); if (g) { WCHAR *p = (WCHAR *)GlobalLock(g); int k = 0; for (int i = 0; i < len; i++) { if (s[i] == L'\n') p[k++] = L'\r'; p[k++] = s[i]; } p[k] = 0; GlobalUnlock(g); SetClipboardData(CF_UNICODETEXT, g); } CloseClipboard(); } // copies the selection, or the whole note when nothing is selected static void CopyNote(HWND h, Note *n, BOOL whole) { if (!whole && HasSel(n)) { int a, b; SelRange(n, &a, &b); CopyToClipboard(h, n->text + a, b - a); } else { CopyToClipboard(h, n->text, n->len); } } static void PasteFromClipboard(HWND h, Note *n) { if (!OpenClipboard(h)) return; HANDLE g = GetClipboardData(CF_UNICODETEXT); if (g) { const WCHAR *p = (const WCHAR *)GlobalLock(g); if (p) { int l = (int)wcslen(p); WCHAR *clean = (WCHAR *)malloc((l + 1) * sizeof(WCHAR)); int k = 0; for (int i = 0; i < l; i++) if (p[i] != L'\r') clean[k++] = p[i]; InsertText(n, clean, k); free(clean); GlobalUnlock(g); } } CloseClipboard(); } /* ---------------- note window ---------------- */ static void MoveCaretVertical(Note *n, int dir) { static int starts[MAX_LINES]; HDC dc = CreateCompatibleDC(NULL); SelectObject(dc, g_font); Layout L; NoteLayout(n, dc, starts, &L); int line = LineOfIndex(starts, L.lines, n->caret); int x = L.left + TextX(dc, n, starts[line], n->caret); DeleteDC(dc); int target = line + dir; if (target < 0) { n->caret = 0; return; } if (target >= L.lines) { n->caret = n->len; return; } n->caret = CaretFromPoint(n, x, L.pad + target * L.lineH + L.lineH / 2); } static void SelectWordAt(Note *n, int idx) { int a = idx, b = idx; if (a < n->len && !IsWordChar(n->text[a]) && a > 0 && IsWordChar(n->text[a - 1])) { a--; b--; } while (a > 0 && IsWordChar(n->text[a - 1])) a--; while (b < n->len && IsWordChar(n->text[b])) b++; n->anchor = a; n->caret = b; } static void ShowNoteMenu(HWND h, Note *n) { HMENU m = CreatePopupMenu(), mc = CreatePopupMenu(); for (int i = 0; i < NCOLORS; i++) AppendMenuW(mc, MF_STRING | (n->color == COLORS[i] ? MF_CHECKED : 0), IDM_COLOR0 + i, COLOR_NAMES[i]); AppendMenuW(m, MF_STRING | (HasSel(n) ? 0 : MF_GRAYED), IDM_COPY, L"Copy\tCtrl+C"); AppendMenuW(m, MF_STRING, IDM_COPYALL, L"Copy all text"); AppendMenuW(m, MF_STRING | (IsClipboardFormatAvailable(CF_UNICODETEXT) ? 0 : MF_GRAYED), IDM_PASTE, L"Paste\tCtrl+V"); AppendMenuW(m, MF_STRING, IDM_SELALL, L"Select all\tCtrl+A"); AppendMenuW(m, MF_SEPARATOR, 0, NULL); AppendMenuW(m, MF_POPUP, (UINT_PTR)mc, L"Color"); AppendMenuW(m, MF_SEPARATOR, 0, NULL); AppendMenuW(m, MF_STRING, IDM_DELETE, L"Delete this note"); POINT p; GetCursorPos(&p); int cmd = TrackPopupMenu(m, TPM_RIGHTBUTTON | TPM_RETURNCMD, p.x, p.y, 0, h, NULL); DestroyMenu(m); if (cmd >= IDM_COLOR0 && cmd < IDM_COLOR0 + NCOLORS) { n->color = COLORS[cmd - IDM_COLOR0]; g_lastColor = cmd - IDM_COLOR0; RegSetFlag(L"LastColor", g_lastColor); RenderNote(n); MarkDirty(); } else if (cmd == IDM_COPY) { CopyNote(h, n, FALSE); } else if (cmd == IDM_COPYALL) { CopyNote(h, n, TRUE); } else if (cmd == IDM_PASTE) { PasteFromClipboard(h, n); RenderNote(n); MarkDirty(); } else if (cmd == IDM_SELALL) { n->anchor = 0; n->caret = n->len; RenderNote(n); } else if (cmd == IDM_DELETE) { PostMessageW(g_main, WM_KILLNOTE, (WPARAM)(n - g_notes), 0); } } static BOOL InGrip(Note *n, int x, int y) { // the grip square sits beside the first line of text HDC dc = CreateCompatibleDC(NULL); SelectObject(dc, g_font); TEXTMETRICW tm; GetTextMetricsW(dc, &tm); DeleteDC(dc); int g = GripW(), gy = S(4) + (tm.tmHeight - g) / 2; (void)n; return x >= 0 && x < g + S(2) && y >= gy - S(2) && y < gy + g + S(2); } static LRESULT CALLBACK NoteProc(HWND h, UINT msg, WPARAM wp, LPARAM lp) { Note *n = (Note *)GetWindowLongPtrW(h, GWLP_USERDATA); switch (msg) { case WM_NCCREATE: { CREATESTRUCTW *cs = (CREATESTRUCTW *)lp; SetWindowLongPtrW(h, GWLP_USERDATA, (LONG_PTR)cs->lpCreateParams); break; } case WM_SETCURSOR: { POINT p; GetCursorPos(&p); ScreenToClient(h, &p); BOOL move = g_moving || (!g_selecting && (InGrip(n, p.x, p.y) || (GetKeyState(VK_MENU) & 0x8000))); SetCursor(LoadCursorW(NULL, move ? IDC_SIZEALL : IDC_IBEAM)); return TRUE; } case WM_MOUSEACTIVATE: if (n) n->active = TRUE; return MA_ACTIVATE; case WM_ACTIVATE: if (!n) break; if (LOWORD(wp) != WA_INACTIVE) { n->active = TRUE; n->caretOn = TRUE; SetTimer(h, TIMER_BLINK, GetCaretBlinkTime(), NULL); RenderNote(n); } else { n->active = FALSE; n->anchor = n->caret; KillTimer(h, TIMER_BLINK); if (n->len == 0) { PostMessageW(g_main, WM_KILLNOTE, (WPARAM)(n - g_notes), 0); return 0; } RenderNote(n); PushToDesktop(h); MarkDirty(); } return 0; case WM_WINDOWPOSCHANGING: if (n && !n->active) { WINDOWPOS *pos = (WINDOWPOS *)lp; if (!(pos->flags & SWP_NOZORDER)) { HWND host = FindDesktopHost(); if (host) { HWND above = GetWindow(host, GW_HWNDPREV); if (above == h) pos->flags |= SWP_NOZORDER; else pos->hwndInsertAfter = above ? above : HWND_TOP; } } } break; case WM_TIMER: if (wp == TIMER_BLINK && n) { n->caretOn = !n->caretOn; RenderNote(n); } return 0; case WM_LBUTTONDOWN: { if (!n) return 0; int cx = (short)LOWORD(lp), cy = (short)HIWORD(lp); if (InGrip(n, cx, cy) || (GetKeyState(VK_MENU) & 0x8000)) { // move the note RECT r; GetWindowRect(h, &r); g_dragOrig.x = r.left; g_dragOrig.y = r.top; GetCursorPos(&g_dragStart); g_moving = TRUE; SetCursor(LoadCursorW(NULL, IDC_SIZEALL)); } else { // place the cursor / start selecting text n->caret = CaretFromPoint(n, cx, cy); if (!(wp & MK_SHIFT)) n->anchor = n->caret; n->caretOn = TRUE; g_selecting = TRUE; RenderNote(n); } SetCapture(h); return 0; } case WM_MOUSEMOVE: if (n && !n->hover) { n->hover = TRUE; TRACKMOUSEEVENT t = { sizeof(t), TME_LEAVE, h, 0 }; TrackMouseEvent(&t); RenderNote(n); } if (GetCapture() == h && n) { if (g_moving) { POINT p; GetCursorPos(&p); SetWindowPos(h, NULL, g_dragOrig.x + p.x - g_dragStart.x, g_dragOrig.y + p.y - g_dragStart.y, 0, 0, SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE); } else if (g_selecting) { int c = CaretFromPoint(n, (short)LOWORD(lp), (short)HIWORD(lp)); if (c != n->caret) { n->caret = c; RenderNote(n); } } } return 0; case WM_LBUTTONUP: { BOOL wasMove = g_moving; g_moving = FALSE; g_selecting = FALSE; if (GetCapture() == h) ReleaseCapture(); if (wasMove) MarkDirty(); return 0; } case WM_CAPTURECHANGED: g_moving = FALSE; g_selecting = FALSE; return 0; case WM_LBUTTONDBLCLK: if (n && !InGrip(n, (short)LOWORD(lp), (short)HIWORD(lp))) { SelectWordAt(n, CaretFromPoint(n, (short)LOWORD(lp), (short)HIWORD(lp))); RenderNote(n); } return 0; case WM_MOUSELEAVE: if (n && n->hover) { n->hover = FALSE; RenderNote(n); } return 0; case WM_RBUTTONUP: if (n) ShowNoteMenu(h, n); return 0; case WM_CHAR: { if (!n) return 0; WCHAR c = (WCHAR)wp; if (c == 8) { // backspace if (HasSel(n)) DeleteSel(n); else if (n->caret > 0) DeleteRange(n, n->caret - 1, n->caret); } else if (c == 13) { WCHAR nl = L'\n'; InsertText(n, &nl, 1); } else if (c == 27) { SetForegroundWindow(GetShellWindow()); return 0; } else if (c == 9 || (c >= 32 && c != 127)) { InsertText(n, &c, 1); } else { return 0; } n->caretOn = TRUE; RenderNote(n); MarkDirty(); return 0; } case WM_KEYDOWN: { if (!n) return 0; BOOL ctrl = (GetKeyState(VK_CONTROL) & 0x8000) != 0; BOOL shift = (GetKeyState(VK_SHIFT) & 0x8000) != 0; BOOL changed = FALSE, moved = FALSE; int a, b; SelRange(n, &a, &b); switch (wp) { case VK_LEFT: if (HasSel(n) && !shift) n->caret = a; else if (n->caret > 0) n->caret--; moved = TRUE; break; case VK_RIGHT: if (HasSel(n) && !shift) n->caret = b; else if (n->caret < n->len) n->caret++; moved = TRUE; break; case VK_UP: MoveCaretVertical(n, -1); moved = TRUE; break; case VK_DOWN: MoveCaretVertical(n, +1); moved = TRUE; break; case VK_HOME: if (ctrl) n->caret = 0; else while (n->caret > 0 && n->text[n->caret - 1] != L'\n') n->caret--; moved = TRUE; break; case VK_END: if (ctrl) n->caret = n->len; else while (n->caret < n->len && n->text[n->caret] != L'\n') n->caret++; moved = TRUE; break; case VK_DELETE: if (HasSel(n)) DeleteSel(n); else if (n->caret < n->len) DeleteRange(n, n->caret, n->caret + 1); changed = TRUE; break; case 'A': if (!ctrl) return 0; n->anchor = 0; n->caret = n->len; break; case 'C': if (ctrl) CopyNote(h, n, FALSE); return 0; case 'X': if (!ctrl) return 0; CopyNote(h, n, FALSE); if (HasSel(n)) DeleteSel(n); else { n->anchor = 0; n->caret = n->len; DeleteSel(n); } changed = TRUE; break; case 'V': if (!ctrl) return 0; PasteFromClipboard(h, n); changed = TRUE; break; default: return 0; } if (moved && !shift) n->anchor = n->caret; n->caretOn = TRUE; RenderNote(n); if (changed) MarkDirty(); return 0; } } return DefWindowProcW(h, msg, wp, lp); } /* ---------------- desktop double-click detection ---------------- */ static BOOL IsDesktopSurface(HWND w) { if (!w) return FALSE; WCHAR cls[64], pcls[64] = L""; GetClassNameW(w, cls, 64); HWND p = GetParent(w); if (p) GetClassNameW(p, pcls, 64); if (!wcscmp(cls, L"SysListView32") && !wcscmp(pcls, L"SHELLDLL_DefView")) return TRUE; if (!wcscmp(cls, L"SHELLDLL_DefView") || !wcscmp(cls, L"Progman") || !wcscmp(cls, L"WorkerW")) return TRUE; return FALSE; } // TRUE if a desktop icon is under the point (so a double-click should open it, not start a note) static BOOL DesktopIconAt(POINT pt) { IAccessible *acc = NULL; VARIANT child; VariantInit(&child); if (FAILED(AccessibleObjectFromPoint(pt, &acc, &child)) || !acc) return FALSE; BOOL icon = FALSE; VARIANT role; VariantInit(&role); if (SUCCEEDED(IAccessible_get_accRole(acc, child, &role)) && role.vt == VT_I4) { long r = role.lVal; icon = (r == ROLE_SYSTEM_LISTITEM || r == ROLE_SYSTEM_STATICTEXT || r == ROLE_SYSTEM_GRAPHIC || r == ROLE_SYSTEM_TEXT || r == ROLE_SYSTEM_PUSHBUTTON); } VariantClear(&role); VariantClear(&child); IAccessible_Release(acc); return icon; } static LRESULT CALLBACK MouseLL(int code, WPARAM wp, LPARAM lp) { if (code == HC_ACTION && wp == WM_LBUTTONDOWN) { MSLLHOOKSTRUCT *m = (MSLLHOOKSTRUCT *)lp; HWND w = WindowFromPoint(m->pt); if (IsDesktopSurface(w)) { DWORD dt = m->time - g_lastDownTime; if (g_lastDownTime && dt <= GetDoubleClickTime() && abs(m->pt.x - g_lastDownPt.x) <= GetSystemMetrics(SM_CXDOUBLECLK) && abs(m->pt.y - g_lastDownPt.y) <= GetSystemMetrics(SM_CYDOUBLECLK)) { PostMessageW(g_main, WM_DESKDBL, 0, MAKELPARAM((WORD)(SHORT)m->pt.x, (WORD)(SHORT)m->pt.y)); g_lastDownTime = 0; } else { g_lastDownTime = m->time ? m->time : 1; g_lastDownPt = m->pt; } } else { g_lastDownTime = 0; } } return CallNextHookEx(g_hook, code, wp, lp); } /* ---------------- tray + main ---------------- */ static void Balloon(const WCHAR *title, const WCHAR *text) { g_nid.uFlags = NIF_INFO; wcsncpy(g_nid.szInfoTitle, title, 63); g_nid.szInfoTitle[63] = 0; wcsncpy(g_nid.szInfo, text, 255); g_nid.szInfo[255] = 0; g_nid.dwInfoFlags = NIIF_INFO; Shell_NotifyIconW(NIM_MODIFY, &g_nid); } static void AddTrayIcon(void) { ZeroMemory(&g_nid, sizeof(g_nid)); g_nid.cbSize = sizeof(g_nid); g_nid.hWnd = g_main; g_nid.uID = 1; g_nid.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP; g_nid.uCallbackMessage = WM_TRAY; g_nid.hIcon = (HICON)LoadImageW(g_inst, MAKEINTRESOURCEW(1), IMAGE_ICON, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), 0); wcscpy(g_nid.szTip, L"QuickNote"); Shell_NotifyIconW(NIM_ADD, &g_nid); } static void ShowHelp(void) { MessageBoxW(NULL, L"Write on your desktop:\n" L" Double-click any empty spot on the wallpaper and start typing.\n" L" Click anywhere else when you're done. The text stays on the desktop,\n" L" behind your windows, just like desktop icons.\n\n" L"Copy text out of a note:\n" L" Drag across the text to select it, then press Ctrl+C.\n" L" Double-click a word to select it. Ctrl+A selects everything.\n" L" Or right-click the note and choose Copy all text.\n\n" L"Move a note:\n" L" Drag the small gray square at its top left corner (it shows when\n" L" you point at the note), or hold Alt and drag anywhere on it.\n\n" L"Right-click a note to change its color or delete it.\n" L"Enter starts a new line. Esc finishes editing.\n" L"A note you empty out disappears on its own.\n" L"Notes save automatically and come back after a restart.", L"How to use QuickNote", MB_OK | MB_ICONINFORMATION); } static void ShowTrayMenu(void) { HMENU m = CreatePopupMenu(); AppendMenuW(m, MF_STRING | MF_GRAYED, 0, L"QuickNote"); AppendMenuW(m, MF_SEPARATOR, 0, NULL); AppendMenuW(m, MF_STRING, IDM_NEW, L"New note"); AppendMenuW(m, MF_STRING | (g_hidden ? MF_CHECKED : 0), IDM_HIDE, L"Hide all notes"); AppendMenuW(m, MF_STRING | (StartupEnabled() ? MF_CHECKED : 0), IDM_STARTUP, L"Start with Windows"); AppendMenuW(m, MF_STRING, IDM_HELP, L"How to use"); AppendMenuW(m, MF_SEPARATOR, 0, NULL); AppendMenuW(m, MF_STRING, IDM_EXIT, L"Exit"); POINT p; GetCursorPos(&p); SetForegroundWindow(g_main); TrackPopupMenu(m, TPM_RIGHTBUTTON | TPM_BOTTOMALIGN, p.x, p.y, 0, g_main, NULL); PostMessageW(g_main, WM_NULL, 0, 0); DestroyMenu(m); } static void NewNoteAt(int x, int y) { if (g_hidden) SetAllHidden(FALSE); HDC dc = CreateCompatibleDC(NULL); SelectObject(dc, g_font); TEXTMETRICW tm; GetTextMetricsW(dc, &tm); DeleteDC(dc); // place it so the cursor lands right where you clicked CreateNote(x - GripW() - S(3), y - S(4) - tm.tmHeight / 2, L"", 0, COLORS[g_lastColor], TRUE); } static LRESULT CALLBACK MainProc(HWND h, UINT msg, WPARAM wp, LPARAM lp) { if (msg == g_taskbarCreated && g_taskbarCreated) { AddTrayIcon(); return 0; } switch (msg) { case WM_TRAY: if (LOWORD(lp) == WM_RBUTTONUP || LOWORD(lp) == WM_LBUTTONUP) ShowTrayMenu(); return 0; case WM_DESKDBL: { POINT pt = { (short)LOWORD(lp), (short)HIWORD(lp) }; if (!DesktopIconAt(pt)) NewNoteAt(pt.x, pt.y); return 0; } case WM_KILLNOTE: if (wp < MAX_NOTES) DeleteNote(&g_notes[wp]); return 0; case WM_TIMER: if (wp == TIMER_SAVE) { KillTimer(h, TIMER_SAVE); if (g_dirty) SaveNotes(); } else if (wp == TIMER_CHECK && !g_hidden) { // keep notes visible after Win+D and sitting just above the wallpaper for (int i = 0; i < MAX_NOTES; i++) { Note *n = &g_notes[i]; if (!n->used) continue; if (IsIconic(n->hwnd) || !IsWindowVisible(n->hwnd)) ShowWindow(n->hwnd, SW_SHOWNA); } HWND fg = GetForegroundWindow(); if (fg != g_lastFg) { g_lastFg = fg; if (IsDesktopClass(fg)) for (int i = 0; i < MAX_NOTES; i++) if (g_notes[i].used && !g_notes[i].active) PushToDesktop(g_notes[i].hwnd); } } return 0; case WM_COMMAND: switch (LOWORD(wp)) { case IDM_NEW: { RECT wa; SystemParametersInfoW(SPI_GETWORKAREA, 0, &wa, 0); NewNoteAt((wa.left + wa.right) / 2 - S(60), (wa.top + wa.bottom) / 2); break; } case IDM_HIDE: SetAllHidden(!g_hidden); break; case IDM_STARTUP: SetStartup(!StartupEnabled()); break; case IDM_HELP: ShowHelp(); break; case IDM_EXIT: DestroyWindow(h); break; } return 0; case WM_QUERYENDSESSION: SaveNotes(); return TRUE; case WM_DESTROY: SaveNotes(); if (g_hook) UnhookWindowsHookEx(g_hook); Shell_NotifyIconW(NIM_DELETE, &g_nid); PostQuitMessage(0); return 0; } return DefWindowProcW(h, msg, wp, lp); } typedef BOOL (WINAPI *SetDpiCtxFn)(HANDLE); int WINAPI wWinMain(HINSTANCE inst, HINSTANCE prev, PWSTR cmd, int show) { (void)prev; (void)cmd; (void)show; g_inst = inst; HANDLE mutex = CreateMutexW(NULL, TRUE, L"QuickNote_SingleInstance_Mutex"); if (GetLastError() == ERROR_ALREADY_EXISTS) { MessageBoxW(NULL, L"QuickNote is already running. Look for its icon in the system tray.", APP_NAME, MB_OK | MB_ICONINFORMATION); return 0; } CoInitialize(NULL); SetDpiCtxFn setCtx = (SetDpiCtxFn)(void *)GetProcAddress(GetModuleHandleW(L"user32.dll"), "SetProcessDpiAwarenessContext"); if (!setCtx || !setCtx((HANDLE)-4)) SetProcessDPIAware(); HDC sdc = GetDC(NULL); g_dpi = GetDeviceCaps(sdc, LOGPIXELSY); ReleaseDC(NULL, sdc); if (g_dpi < 96) g_dpi = 96; g_font = CreateFontW(-MulDiv(FONT_PT, g_dpi, 72), 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, ANTIALIASED_QUALITY, DEFAULT_PITCH, L"Segoe UI"); WCHAR dir[MAX_PATH]; if (SUCCEEDED(SHGetFolderPathW(NULL, CSIDL_LOCAL_APPDATA, NULL, 0, dir))) { wcscat(dir, L"\\QuickNote"); CreateDirectoryW(dir, NULL); swprintf(g_file, MAX_PATH, L"%ls\\notes.txt", dir); } else { GetTempPathW(MAX_PATH, dir); swprintf(g_file, MAX_PATH, L"%lsQuickNote_notes.txt", dir); } DWORD lc = RegGetFlag(L"LastColor"); if (lc < NCOLORS) g_lastColor = (int)lc; WNDCLASSEXW wc; ZeroMemory(&wc, sizeof(wc)); wc.cbSize = sizeof(wc); wc.lpfnWndProc = MainProc; wc.hInstance = inst; wc.lpszClassName = L"QuickNoteMain"; wc.hIcon = LoadIconW(inst, MAKEINTRESOURCEW(1)); RegisterClassExW(&wc); WNDCLASSEXW nc; ZeroMemory(&nc, sizeof(nc)); nc.cbSize = sizeof(nc); nc.style = CS_DBLCLKS; nc.lpfnWndProc = NoteProc; nc.hInstance = inst; nc.hCursor = LoadCursorW(NULL, IDC_IBEAM); nc.lpszClassName = L"QuickNoteText"; nc.hIcon = LoadIconW(inst, MAKEINTRESOURCEW(1)); RegisterClassExW(&nc); g_main = CreateWindowExW(0, L"QuickNoteMain", APP_NAME, 0, 0, 0, 0, 0, NULL, NULL, inst, NULL); g_taskbarCreated = RegisterWindowMessageW(L"TaskbarCreated"); AddTrayIcon(); LoadNotes(); g_hook = SetWindowsHookExW(WH_MOUSE_LL, MouseLL, inst, 0); SetTimer(g_main, TIMER_CHECK, 1000, NULL); if (!RegGetFlag(L"Welcomed")) { RegSetFlag(L"Welcomed", 1); Balloon(L"QuickNote is running", L"Double-click any empty spot on your desktop and start typing."); } MSG m; while (GetMessageW(&m, NULL, 0, 0) > 0) { TranslateMessage(&m); DispatchMessageW(&m); } if (g_font) DeleteObject(g_font); CoUninitialize(); if (mutex) { ReleaseMutex(mutex); CloseHandle(mutex); } return 0; }