mirror of https://github.com/roytam1/kmeleon.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1429 lines
46 KiB
1429 lines
46 KiB
/* |
|
* Copyright (C) 2003 Ulf Erikson <ulferikson@fastmail.fm> |
|
* |
|
* This program is free software; you can redistribute it and/or modify |
|
* it under the terms of the GNU General Public License as published by |
|
* the Free Software Foundation; either version 2, or (at your option) |
|
* any later version. |
|
* |
|
* This program is distributed in the hope that it will be useful, |
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
* GNU General Public License for more details. |
|
* |
|
* You should have received a copy of the GNU General Public License |
|
* along with this program; if not, write to the Free Software |
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. |
|
*/ |
|
|
|
#ifdef __MINGW32__ |
|
# define _WIN32_IE 0x0500 |
|
# include <windows.h> |
|
# include <commctrl.h> |
|
# include "../missing.h" |
|
#endif |
|
|
|
|
|
#include "history.h" |
|
#include "HistoryNode.h" |
|
#include "../kmeleon_plugin.h" |
|
#include "../KMeleonConst.h" |
|
#include "../LocalesUtils.h" |
|
extern Locale* gLoc; |
|
// Include for global history |
|
#include "nsCOMPtr.h" |
|
#include "nsEmbedString.h" |
|
#include "rdf.h" |
|
#include "nsServiceManagerUtils.h" |
|
#include "nsISimpleEnumerator.h" |
|
#include "nsIRDFDataSource.h" |
|
#include "nsIRDFService.h" |
|
#include "prtime.h" |
|
#include "nsMemory.h" |
|
|
|
#include "../Utils.h" |
|
#include <wininet.h> |
|
|
|
#define VK_OEM_MINUS 0xBD |
|
#define VK_OEM_PERIOD 0xBE |
|
#define VK_MINUS VK_OEM_MINUS |
|
#define VK_PERIOD VK_OEM_PERIOD |
|
|
|
extern kmeleonPlugin kPlugin; |
|
extern void * KMeleonWndProc; |
|
|
|
static CHistoryNode gHistoryRoot("", HISTORY_FOLDER, 0); |
|
static CHistoryNode *workingHist; |
|
static CHistoryNode *freeNode; |
|
static HWND hEditWnd; |
|
|
|
#define SEARCH_LEN 256 |
|
static char str[SEARCH_LEN]; |
|
static int len; |
|
static int pos; |
|
static int circling; |
|
|
|
static BOOL zoom = 0; |
|
static BOOL maximized; |
|
static int sortOrder; |
|
|
|
#define CUT 1 |
|
#define KILL 1 |
|
#define PASTE 1 |
|
|
|
#define SORT_BY_DATE 0 |
|
#define SORT_BY_URL 1 |
|
#define SORT_BY_DATE_BACKWARDS 2 |
|
|
|
static void FillTree(HWND hTree, HTREEITEM parent, CHistoryNode &node, int level); |
|
static void DeleteItem(HWND hTree, HTREEITEM item); |
|
static void OnSize(int height, int width); |
|
static void OnRClick(HWND hTree); |
|
|
|
|
|
static inline CHistoryNode *GetHistoryNode(HWND hTree, HTREEITEM htItem) { |
|
TVITEMEX tvItem; |
|
tvItem.mask = TVIF_PARAM; |
|
tvItem.hItem = htItem; |
|
TreeView_GetItem(hTree, &tvItem); |
|
|
|
return (CHistoryNode *)tvItem.lParam; |
|
} |
|
|
|
static inline void UnixTimeToSystemTime(time_t t, LPSYSTEMTIME pst) { |
|
// Note that LONGLONG is a 64-bit value |
|
LONGLONG ll; |
|
FILETIME ft1; |
|
FILETIME ft2; |
|
|
|
#ifdef __MINGW32__ |
|
ll = Int32x32To64(t, 10000000) + 116444736000000000LL; |
|
#else |
|
ll = Int32x32To64(t, 10000000) + 116444736000000000L; |
|
#endif |
|
ft1.dwLowDateTime = (DWORD)ll; |
|
ft1.dwHighDateTime = ll >> 32; |
|
|
|
FileTimeToLocalFileTime(&ft1, &ft2); |
|
FileTimeToSystemTime(&ft2, pst); |
|
} |
|
|
|
|
|
static int parseInt64(char *buf, LONGLONG *outValue) |
|
{ |
|
int c, charsRead = 0; |
|
LONGLONG value = 0; |
|
|
|
while (buf && *buf) { |
|
c = *buf; |
|
if (c == 0 || !isdigit(c)) |
|
break; |
|
|
|
++charsRead; |
|
LONGLONG digit = c - '0'; |
|
value *= 10L; |
|
value += digit; |
|
} |
|
if (!charsRead) |
|
return -1; |
|
*outValue = value; |
|
return 0; |
|
} |
|
|
|
|
|
// Hook function for keyboard hook |
|
LRESULT CALLBACK KeyboardProc(int nCode, WPARAM wParam, LPARAM lParam) { |
|
if ( (nCode != HC_ACTION) || (lParam & 0x80000000) ) { // highest bit of lParam high == key being released |
|
return CallNextHookEx(NULL, nCode, wParam, lParam); |
|
} |
|
|
|
BOOL fEatKeystroke = false; |
|
BOOL fakedKey = false; |
|
HWND hasFocus = GetFocus(); |
|
HWND hTree = GetDlgItem(hEditWnd, IDC_TREE_HOTLIST); |
|
|
|
if (!(hasFocus == hTree || |
|
hasFocus == GetDlgItem(hEditWnd, IDC_URL) || |
|
hasFocus == GetDlgItem(hEditWnd, IDOK) || |
|
hasFocus == GetDlgItem(hEditWnd, IDCANCEL))) |
|
return CallNextHookEx(NULL, nCode, wParam, lParam); |
|
|
|
int searching = 0; |
|
|
|
if (wParam == VK_ESCAPE) { |
|
fEatKeystroke = true; |
|
if (len == 0) |
|
SendMessage(GetDlgItem(hEditWnd, IDCANCEL), BM_CLICK, 0, 0); |
|
else |
|
len = 0; |
|
} |
|
else if (hasFocus == hTree) { |
|
HTREEITEM hItem = TreeView_GetSelection(hTree); |
|
if (hItem) { |
|
switch (wParam) { |
|
case VK_UP: |
|
if (GetKeyState(VK_MENU) & 0x80) { // what genius decided to call ALT "VK_MENU"??? |
|
fEatKeystroke = true; |
|
HTREEITEM hItemSelect = TreeView_GetPrevSibling(hTree, hItem); |
|
if (!hItemSelect) hItemSelect = TreeView_GetPrevVisible(hTree, hItem); |
|
if (!hItemSelect) break; |
|
TreeView_SelectItem(hTree, hItemSelect); |
|
} |
|
break; |
|
case VK_DOWN: |
|
if (GetKeyState(VK_MENU) & 0x80) { |
|
fEatKeystroke = true; |
|
HTREEITEM hItemSelect = TreeView_GetNextSibling(hTree, hItem); |
|
if (!hItemSelect) hItemSelect = TreeView_GetNextSibling(hTree, TreeView_GetParent(hTree, hItem)); |
|
if (!hItemSelect) hItemSelect = TreeView_GetNextVisible(hTree, hItem); |
|
if (!hItemSelect) break; |
|
TreeView_SelectItem(hTree, hItemSelect); |
|
} |
|
break; |
|
case VK_RETURN: |
|
{ |
|
fEatKeystroke = true; |
|
CHistoryNode *node = GetHistoryNode(hTree, hItem); |
|
if (node->type == HISTORY_BOOKMARK) { |
|
node->lastVisit = time(NULL); |
|
if (GetKeyState(VK_CONTROL) & 0x80) { |
|
kPlugin.kFuncs->NavigateTo(node->url.c_str(), OPEN_BACKGROUND, NULL); |
|
} |
|
else { |
|
kPlugin.kFuncs->NavigateTo(node->url.c_str(), OPEN_NORMAL, NULL); |
|
} |
|
TreeView_SelectItem(hTree, hItem); // just to fire off a SELCHANGED notifier to update the status (last visited!) |
|
PostMessage(hWndFront, WM_COMMAND, wm_deferbringtotop, (LPARAM)NULL); |
|
} |
|
else if (node->type == HISTORY_FOLDER) { |
|
TreeView_Expand(hTree, hItem, TVE_TOGGLE); |
|
} |
|
} |
|
break; |
|
case VK_TAB: |
|
{ |
|
if (len > 0) { |
|
wParam = str[len-1]; |
|
wParam = toupper(wParam); |
|
fakedKey = true; |
|
goto search_again; |
|
} |
|
|
|
fEatKeystroke = true; |
|
if (zoom) |
|
break; |
|
|
|
int idc_next = IDC_URL; |
|
if (IsWindowEnabled(GetDlgItem(hEditWnd, IDC_URL)) == 0) |
|
idc_next = IDOK; |
|
if (GetKeyState(VK_SHIFT) & 0x80) |
|
idc_next = IDCANCEL; |
|
if (idc_next == IDCANCEL || idc_next == IDOK) |
|
SendMessage(GetDlgItem(hEditWnd, idc_next), BM_SETSTYLE, BS_DEFPUSHBUTTON, TRUE); |
|
SetFocus(GetDlgItem(hEditWnd, idc_next)); |
|
if (idc_next != IDCANCEL && idc_next != IDOK) |
|
SendDlgItemMessage(hEditWnd, idc_next, EM_SETSEL, 0, -1); // select all |
|
} |
|
break; |
|
case VK_DELETE: |
|
// DeleteItem(hTree, hItem); |
|
break; |
|
case ' ': |
|
{ |
|
CHistoryNode *node = GetHistoryNode(hTree, hItem); |
|
if (node->type == HISTORY_FOLDER && len == 0) { |
|
fEatKeystroke = true; |
|
TreeView_Expand(hTree, hItem, TVE_TOGGLE); |
|
break; |
|
} |
|
} |
|
// Fall through! |
|
default: |
|
{ |
|
if (GetKeyState(VK_CONTROL) & 0x80) { |
|
switch (wParam) { |
|
case 'U': |
|
len = 0; |
|
break; |
|
case 'G': |
|
if (len > 0) { |
|
wParam = str[len-1]; |
|
wParam = toupper(wParam); |
|
fakedKey = true; |
|
goto search_again; |
|
} |
|
break; |
|
} |
|
break; |
|
} |
|
else if (GetKeyState(VK_MENU) & 0x80) { |
|
break; |
|
} |
|
else { |
|
switch (wParam) { |
|
case VK_F3: |
|
if (len > 0) { |
|
wParam = str[len-1]; |
|
wParam = toupper(wParam); |
|
fakedKey = true; |
|
goto search_again; |
|
} |
|
break; |
|
} |
|
} |
|
|
|
if (wParam == VK_BACK && len > 0) { |
|
circling = 0; |
|
len--; |
|
} |
|
else if ( (wParam == ' ' || |
|
(wParam >= '0' && wParam <= '9') || |
|
(wParam >= 'A' && wParam <= 'Z')) && |
|
len < (SEARCH_LEN-1) ) { |
|
str[len] = tolower(wParam); |
|
len++; |
|
} |
|
else if ( (wParam == VK_MINUS) && |
|
len < (SEARCH_LEN-1) ) { |
|
str[len] = '-'; |
|
len++; |
|
} |
|
else if ( wParam == VK_PERIOD && |
|
len < (SEARCH_LEN-1) ) { |
|
str[len] = '.'; |
|
len++; |
|
} |
|
else |
|
break; |
|
|
|
search_again: |
|
fEatKeystroke = true; |
|
searching = 1; |
|
str[len] = 0; |
|
if (len > 0) { |
|
CHistoryNode *node; |
|
|
|
int mypos = 0; |
|
int firstpos = -1; |
|
int searchfrom = pos; |
|
|
|
if (len == 1 && !fakedKey) { |
|
HTREEITEM hItem = TreeView_GetSelection(hTree); |
|
node = GetHistoryNode(hTree, hItem); |
|
if (workingHist->Index(searchfrom, node) == -1) |
|
searchfrom = pos; |
|
else |
|
pos = searchfrom; |
|
} |
|
|
|
int newpos = workingHist->Search(str, searchfrom, mypos, firstpos, &node); |
|
|
|
if (fakedKey || newpos == -1 || |
|
(circling && len > 1 && str[len-1] == str[len-2])) { |
|
if (fakedKey || (len > 1 && str[len-1] == str[len-2])) |
|
searchfrom++; |
|
if (!fakedKey) |
|
len--; |
|
str[len] = 0; |
|
mypos = 0; |
|
firstpos = -1; |
|
if (len > 0) { |
|
newpos = workingHist->Search(str, searchfrom, mypos, firstpos, &node); |
|
if (!fakedKey && (newpos == pos || (len > 1 && str[len-1] == str[len-2]))) { |
|
circling = 1; |
|
while (len > 1 && str[len-1] == str[len-2]) |
|
len--; |
|
str[len] = 0; |
|
mypos = 0; |
|
firstpos = -1; |
|
newpos = workingHist->Search(str, searchfrom, mypos, firstpos, &node); |
|
} |
|
} |
|
} |
|
else |
|
circling = 0; |
|
|
|
if (newpos == -1 || len == 0) { |
|
pos = 0; |
|
len = 0; |
|
str[len] = 0; |
|
searching = 0; |
|
} |
|
else { |
|
TCHAR tmp[SEARCH_LEN+64]; |
|
_stprintf(tmp, gLoc->GetString(IDS_FIND), str); |
|
SetWindowText( hEditWnd, tmp ); |
|
|
|
HTREEITEM hItem = TreeView_GetRoot(hTree); |
|
int i = 1; |
|
while (hItem && i < newpos) { |
|
HTREEITEM hTmp = TreeView_GetChild(hTree, hItem); |
|
if (!hTmp) |
|
hTmp = TreeView_GetNextSibling(hTree, hItem); |
|
if (!hTmp) { |
|
HTREEITEM hTmp2 = TreeView_GetParent(hTree, hItem); |
|
while (!hTmp && hTmp2) { |
|
hTmp = TreeView_GetNextSibling(hTree, hTmp2); |
|
hTmp2 = TreeView_GetParent(hTree, hTmp2); |
|
} |
|
} |
|
hItem = hTmp; |
|
i++; |
|
} |
|
|
|
if (hItem) { |
|
TreeView_SelectItem(hTree, hItem); |
|
TreeView_EnsureVisible(hTree, hItem); |
|
} |
|
|
|
pos = newpos; |
|
} |
|
} |
|
} |
|
} |
|
} |
|
} |
|
else if (hasFocus == GetDlgItem(hEditWnd, IDOK) && wParam == VK_RETURN) { |
|
SendMessage(GetDlgItem(hEditWnd, IDOK), BM_CLICK, 0, 0); |
|
} |
|
else if (hasFocus == GetDlgItem(hEditWnd, IDCANCEL) && wParam == VK_RETURN) { |
|
SendMessage(GetDlgItem(hEditWnd, IDCANCEL), BM_CLICK, 0, 0); |
|
} |
|
else if (hasFocus == GetDlgItem(hEditWnd, IDC_URL) && wParam == VK_RETURN) { |
|
fEatKeystroke = true; |
|
SetFocus(GetDlgItem(hEditWnd, IDC_TREE_HOTLIST)); |
|
} |
|
else if (wParam == VK_TAB) { |
|
int fields[] = {IDC_TREE_HOTLIST, IDC_URL, |
|
IDOK, IDCANCEL, IDC_TREE_HOTLIST}; |
|
if (GetDlgItem(hEditWnd, IDOK) == hasFocus || |
|
GetDlgItem(hEditWnd, IDCANCEL) == hasFocus) |
|
SendMessage(hasFocus, BM_SETSTYLE, BS_PUSHBUTTON, TRUE); |
|
for (int i=1; fields[i]!=IDC_TREE_HOTLIST; i++) { |
|
if (GetDlgItem(hEditWnd, fields[i]) == hasFocus) { |
|
int idc_next = fields[i + ((GetKeyState(VK_SHIFT) & 0x80) ? -1 : 1)]; |
|
if (idc_next == IDC_TREE_HOTLIST || |
|
idc_next == IDOK || idc_next == IDCANCEL || |
|
IsWindowEnabled(GetDlgItem(hEditWnd, idc_next))) { |
|
fEatKeystroke = true; |
|
if (idc_next == IDOK || idc_next == IDCANCEL) |
|
SendMessage(GetDlgItem(hEditWnd, idc_next), BM_SETSTYLE, BS_DEFPUSHBUTTON, TRUE); |
|
SetFocus(GetDlgItem(hEditWnd, idc_next)); |
|
if (idc_next != IDC_TREE_HOTLIST && |
|
idc_next != IDOK && idc_next != IDCANCEL) |
|
SendDlgItemMessage(hEditWnd, idc_next, EM_SETSEL, 0, -1); // select all |
|
break; |
|
} |
|
else { |
|
hasFocus = GetDlgItem(hEditWnd, idc_next); |
|
i = 0; |
|
continue; |
|
} |
|
} |
|
} |
|
} |
|
|
|
if ((searching == 0 || len==0) && |
|
wParam != VK_SHIFT && wParam != VK_MENU && wParam != VK_CONTROL) { |
|
len = 0; |
|
pos = 0; |
|
str[len] = 0; |
|
SetWindowText( hEditWnd, gLoc->GetString(IDS_HISTORY) ); |
|
circling = 0; |
|
} |
|
|
|
return(fEatKeystroke ? 1 : CallNextHookEx(NULL, nCode, wParam, lParam)); |
|
} |
|
/* |
|
int ParseHistoryEntry(char **p, CHistoryNode &node) |
|
{ |
|
char *q = NULL; |
|
char *r1 = NULL; |
|
char *r2 = NULL; |
|
char szURL[INTERNET_MAX_URL_LENGTH] = {0}; |
|
time_t lastVisit=0; |
|
|
|
if (*p && **p && (q = strchr(*p, ':')) != NULL) { |
|
*q++ = 0; |
|
|
|
// Ugly conversion from milliseconds to seconds.. |
|
if (q-(*p) > 4) |
|
*(q-4) = 0; |
|
|
|
lastVisit = atol(*p); |
|
strncpy(szURL, q, INTERNET_MAX_URL_LENGTH); |
|
szURL[INTERNET_MAX_URL_LENGTH-1] = 0; |
|
|
|
CHistoryNode * newNode = |
|
new CHistoryNode(szURL, NULL, HISTORY_BOOKMARK, lastVisit); |
|
if (newNode) { |
|
node.AddChild(newNode); |
|
return 1; |
|
} |
|
} |
|
|
|
return 0; |
|
} |
|
|
|
int ParseHistory(char **p, CHistoryNode &node) |
|
{ |
|
int size = 0; |
|
char *q1 = strchr(*p, '\r'); |
|
char *q2 = strchr(*p, '\n'); |
|
|
|
while (*p && **p) { |
|
if (!q1 || (q2 && q2 < q1)) |
|
q1 = q2; |
|
if (q1) |
|
*q1++ = 0; |
|
|
|
while (*p && **p && isspace(**p)) |
|
(*p)++; |
|
|
|
if (*p && isdigit(**p)) |
|
size += ParseHistoryEntry(p, node); |
|
|
|
if ( (*p = q1) != NULL ) { |
|
q1 = strchr(*p, '\r'); |
|
q2 = strchr(*p, '\n'); |
|
} |
|
} |
|
|
|
*p = q1; |
|
return size; |
|
} |
|
|
|
*/ |
|
|
|
int HistoryAttr(nsCOMPtr<nsIRDFNode> target, TCHAR **v) |
|
{ |
|
nsCOMPtr<nsIRDFLiteral> aR = do_QueryInterface(target); |
|
if (aR) |
|
{ |
|
PRUnichar *name; |
|
|
|
aR->GetValue(&name); |
|
int len; |
|
len = WideCharToMultiByte(CP_ACP, 0, name, -1, 0, 0, NULL, NULL); |
|
char *s = new char[len +1]; |
|
len = WideCharToMultiByte(CP_ACP, 0, name, -1, s, len, NULL, NULL); |
|
s[len] = 0; |
|
*v = s; |
|
nsMemory::Free(name); |
|
|
|
|
|
return true; |
|
} |
|
else |
|
{ |
|
*v = NULL; |
|
return false; |
|
} |
|
} |
|
|
|
int readHistory() { |
|
|
|
nsresult rv; |
|
unsigned int HistorySize = 0; |
|
|
|
while (gHistoryRoot.child) |
|
gHistoryRoot.RemoveChild(); |
|
while (gHistoryRoot.next) |
|
gHistoryRoot.RemoveNext(); |
|
|
|
nsCOMPtr<nsIRDFDataSource> HistoryRdfDataSource; |
|
nsCOMPtr<nsISupports> isupports; |
|
|
|
nsCOMPtr<nsIRDFService> RDFService(do_GetService("@mozilla.org/rdf/rdf-service;1", &rv)); |
|
if (NS_FAILED(rv)) return false; |
|
|
|
nsIRDFResource *arcChild; |
|
nsIRDFResource *HistoryRoot; |
|
nsIRDFResource *arcName; |
|
nsIRDFResource *arcURL; |
|
nsIRDFResource *arcDate; |
|
|
|
rv = RDFService->GetResource(nsDependentCString("NC:HistoryRoot"),&HistoryRoot); |
|
if (NS_FAILED(rv)) return false; |
|
|
|
rv = RDFService->GetDataSourceBlocking("rdf:history",getter_AddRefs(HistoryRdfDataSource)); |
|
if (NS_FAILED(rv)) return false; |
|
|
|
rv = RDFService->GetResource(nsCString("http://home.netscape.com/NC-rdf#child"), &arcChild); |
|
if (NS_FAILED(rv)) return false; |
|
|
|
rv = RDFService->GetResource(nsCString("http://home.netscape.com/NC-rdf#URL"), &arcURL); |
|
if (NS_FAILED(rv)) return false; |
|
rv = RDFService->GetResource(nsCString("http://home.netscape.com/NC-rdf#Name"), &arcName); |
|
if (NS_FAILED(rv)) return false; |
|
rv = RDFService->GetResource(nsCString("http://home.netscape.com/NC-rdf#Date"), &arcDate); |
|
if (NS_FAILED(rv)) return false; |
|
|
|
nsCOMPtr<nsISimpleEnumerator> targets; |
|
rv = HistoryRdfDataSource->GetTargets(HistoryRoot,arcChild,true,getter_AddRefs(targets)); |
|
if (NS_FAILED(rv)) return false; |
|
|
|
// Another way to count the number |
|
// of history entries? |
|
|
|
while ( (rv=targets->GetNext(getter_AddRefs(isupports))) == NS_OK) |
|
HistorySize++; |
|
|
|
// Should be NS_ERROR_FAILURE... |
|
if (rv != NS_ERROR_UNEXPECTED) return false; |
|
|
|
rv = HistoryRdfDataSource->GetTargets(HistoryRoot,arcChild,true,getter_AddRefs(targets)); |
|
if (NS_FAILED(rv)) return false; |
|
|
|
for (unsigned int i=0; i < HistorySize; i++) |
|
{ |
|
PRTime date; |
|
TCHAR *name = NULL; |
|
TCHAR *URL = NULL; |
|
|
|
nsCOMPtr<nsISupports> isupports; |
|
rv = targets->GetNext(getter_AddRefs(isupports)); |
|
if (NS_FAILED(rv)) break; |
|
|
|
nsCOMPtr<nsIRDFResource> aTarget = do_QueryInterface(isupports); |
|
|
|
nsCOMPtr<nsIRDFNode> target; |
|
|
|
rv = HistoryRdfDataSource->GetTarget(aTarget,arcDate,true,getter_AddRefs(target)); |
|
if (NS_FAILED(rv)) break; |
|
|
|
nsCOMPtr<nsIRDFDate> nDate; |
|
nDate = do_QueryInterface(target); |
|
if (nDate) |
|
{ |
|
rv = nDate->GetValue(&date); |
|
if (NS_FAILED(rv)) break; |
|
} |
|
|
|
rv = HistoryRdfDataSource->GetTarget(aTarget,arcName,true,getter_AddRefs(target)); |
|
if (NS_FAILED(rv)) break; |
|
HistoryAttr(target, &name); |
|
|
|
rv = HistoryRdfDataSource->GetTarget(aTarget,arcURL,true,getter_AddRefs(target)); |
|
if (NS_FAILED(rv)) {delete name;break;} |
|
HistoryAttr(target, &URL); |
|
|
|
time_t t = date / PR_USEC_PER_SEC; |
|
CHistoryNode * newNode = new CHistoryNode(URL, name, HISTORY_BOOKMARK, t); |
|
if (newNode) gHistoryRoot.AddChild(newNode); |
|
|
|
delete name; |
|
delete URL; |
|
} |
|
|
|
return true; |
|
/* |
|
// char szHistFile[MAX_PATH]; |
|
kPlugin.kFuncs->GetPreference(PREF_STRING, "kmeleon.general.profileDir", szHistFile, (char*)""); |
|
strcat(szHistFile, "history.txt"); |
|
FILE *hFile = fopen(szHistFile, "r"); |
|
|
|
if (hFile){ |
|
long hFileSize = FileSize(hFile); |
|
|
|
char *hFileBuffer = new char[hFileSize+1]; |
|
if (hFileBuffer){ |
|
|
|
fread(hFileBuffer, sizeof(char), hFileSize, hFile); |
|
hFileBuffer[hFileSize] = 0; |
|
|
|
char *szTmpBuf = hFileBuffer; |
|
ret = ParseHistory(&szTmpBuf, gHistoryRoot); |
|
|
|
delete [] hFileBuffer; |
|
} |
|
fclose(hFile); |
|
} |
|
|
|
return ret;*/ |
|
} |
|
|
|
|
|
CHistoryNode *groupURLs(CHistoryNode *oldList) |
|
{ |
|
if (!oldList) |
|
return NULL; |
|
|
|
CHistoryNode *newList; |
|
CHistoryNode *tmp; |
|
newList = new CHistoryNode(oldList->url.c_str(), oldList->name.c_str(), HISTORY_FOLDER, 0); |
|
|
|
sortOrder = SORT_BY_URL; |
|
oldList->flatten(); |
|
oldList->sort(sortOrder); |
|
tmp = oldList->child; |
|
|
|
while (tmp) { |
|
int len; |
|
char *str = (char*)tmp->url.c_str(); |
|
char *p = strchr(str, '/'); |
|
if (p && *(p+1) == '/') |
|
p = strchr(p+2, '/'); |
|
if (p) { |
|
len = p - str; |
|
*p = 0; |
|
} |
|
else |
|
len = strlen(str); |
|
|
|
CHistoryNode *newFolder = |
|
new CHistoryNode(str, str, HISTORY_FOLDER, 0); |
|
newList->AddChild(newFolder); |
|
|
|
if (p) { |
|
*p = '/'; |
|
} |
|
|
|
while (tmp && strncmp(str, tmp->url.c_str(), len) == 0) { |
|
CHistoryNode *newNode = new CHistoryNode(tmp->url.c_str(), tmp->name.c_str(), HISTORY_BOOKMARK, tmp->lastVisit); |
|
newFolder->AddChild(newNode); |
|
tmp = tmp->next; |
|
} |
|
} |
|
while (oldList->child) |
|
oldList->RemoveChild(); |
|
while (oldList->next) |
|
oldList->RemoveNext(); |
|
|
|
oldList->child = newList->child; |
|
oldList->next = newList->next; |
|
return newList; |
|
} |
|
|
|
|
|
struct tm subSec(struct tm t, int sec) { |
|
time_t date = mktime(&t); |
|
date -= sec; |
|
struct tm *pt = localtime(&date); |
|
return *pt; |
|
} |
|
|
|
struct tm subMin(struct tm t, int min) { |
|
return subSec(t, 60*min); |
|
} |
|
|
|
struct tm subHour(struct tm t, int h) { |
|
return subMin(t, 60*h); |
|
} |
|
|
|
struct tm subDay(struct tm t, int d) { |
|
return subHour(t, 24*d); |
|
} |
|
|
|
struct tm subMonth(struct tm t, int m) { |
|
t.tm_mon -= m; |
|
while (t.tm_mon < 0) { |
|
t.tm_mon += 12; |
|
t.tm_year--; |
|
} |
|
|
|
time_t date = mktime(&t); |
|
struct tm *pt = localtime(&date); |
|
return *pt; |
|
} |
|
|
|
struct tm subYear(struct tm t, int y) { |
|
struct tm newt = {0}; |
|
|
|
t.tm_year -= y; |
|
time_t date = mktime(&t); |
|
struct tm *pt = localtime(&date); |
|
|
|
if (pt) |
|
newt = *pt; |
|
|
|
return newt; |
|
} |
|
|
|
|
|
CHistoryNode *groupDates(CHistoryNode *oldList) |
|
{ |
|
const UINT weekday[] = {IDS_Sunday, IDS_Monday, IDS_Tuesday, IDS_Wednesday, IDS_Thursday, IDS_Friday, IDS_Saturday}; |
|
const UINT month[] = {IDS_January, IDS_February, IDS_March, IDS_April, IDS_May, IDS_June, IDS_July, IDS_August, IDS_September, IDS_October, IDS_November, IDS_December}; |
|
|
|
if (!oldList) |
|
return NULL; |
|
|
|
CHistoryNode *newList; |
|
CHistoryNode *tmp; |
|
newList = new CHistoryNode(oldList->url.c_str(), oldList->name.c_str(), HISTORY_FOLDER, 0); |
|
|
|
sortOrder = SORT_BY_DATE; |
|
oldList->flatten(); |
|
oldList->sort(SORT_BY_DATE_BACKWARDS); |
|
tmp = oldList->child; |
|
|
|
int pass = 0; |
|
time_t date = time(NULL); |
|
|
|
struct tm *pt = localtime(&date); |
|
struct tm n = *pt; |
|
|
|
while (tmp) { |
|
char str[100]; |
|
strcpy(str, gLoc->GetString(IDS_ERROR)); |
|
|
|
pt = localtime(&date); |
|
struct tm t = *pt; |
|
|
|
switch (pass) { |
|
case 0: |
|
t.tm_sec = 0; |
|
t.tm_min = 0; |
|
strcpy(str, gLoc->GetString(IDS_LAST_HOUR)); |
|
break; |
|
case 1: |
|
t.tm_hour = 0; |
|
strcpy(str, gLoc->GetString(IDS_TODAY)); |
|
break; |
|
case 2: |
|
case 3: |
|
case 4: |
|
t = subDay(t, 1); |
|
strcpy(str, gLoc->GetString(weekday[t.tm_wday])); |
|
break; |
|
case 5: |
|
if (t.tm_wday < n.tm_wday) { |
|
t = subDay(t, t.tm_wday); |
|
strcpy(str, gLoc->GetString(IDS_THIS_WEEK)); |
|
} |
|
else { |
|
t = subDay(t, t.tm_wday); |
|
strcpy(str, gLoc->GetString(IDS_LAST_WEEK)); |
|
pass++; |
|
} |
|
break; |
|
case 6: |
|
t = subDay(t, 7); |
|
strcpy(str, gLoc->GetString(IDS_LAST_WEEK)); |
|
break; |
|
case 7: |
|
t = subDay(t, 7); |
|
strcpy(str, gLoc->GetString(IDS_2WEEKS_AGO)); |
|
break; |
|
case 8: |
|
t = subDay(t, 7); |
|
strcpy(str, gLoc->GetString(IDS_3WEEKS_AGO)); |
|
break; |
|
case 9: |
|
if (t.tm_mday < n.tm_mday) { |
|
t = subDay(t, (t.tm_mday-1)); |
|
strcpy(str, gLoc->GetString(month[t.tm_mon])); |
|
} |
|
else { |
|
t = subDay(t, (t.tm_mday-1)); |
|
strcpy(str, gLoc->GetString(month[t.tm_mon])); |
|
pass++; |
|
} |
|
break; |
|
case 10: |
|
case 11: |
|
case 12: |
|
case 13: |
|
case 14: |
|
case 15: |
|
t = subMonth(t, 1); |
|
strcpy(str, gLoc->GetString(month[t.tm_mon])); |
|
break; |
|
case 16: |
|
if (t.tm_yday > n.tm_yday) |
|
break; |
|
t = subMonth(t, t.tm_mon); |
|
itoa(t.tm_year, str, 10); |
|
break; |
|
default: |
|
t = subYear(t, 1); |
|
if (t.tm_year) |
|
itoa(1900 + t.tm_year, str, 10); |
|
else |
|
strcpy(str, gLoc->GetString(IDS_NEVER)); |
|
break; |
|
} |
|
|
|
date = mktime(&t); |
|
|
|
if (tmp->lastVisit >= date) { |
|
|
|
CHistoryNode *newFolder = new CHistoryNode(str, str, HISTORY_FOLDER, 0); |
|
while (tmp && (tmp->lastVisit > date)) { |
|
CHistoryNode *newNode = new CHistoryNode(tmp->url.c_str(), tmp->name.c_str(), HISTORY_BOOKMARK, tmp->lastVisit); |
|
newFolder->AddChild(newNode); |
|
tmp = tmp->next; |
|
} |
|
|
|
CHistoryNode *tmpFolder = groupURLs(newFolder); |
|
newList->AddChild(tmpFolder); |
|
|
|
tmpFolder = tmpFolder->child; |
|
while (tmpFolder) { |
|
tmpFolder->sort(SORT_BY_DATE); |
|
tmpFolder = tmpFolder->next; |
|
} |
|
|
|
newFolder->child = NULL; |
|
newFolder->next = NULL; |
|
delete newFolder; |
|
} |
|
|
|
pass++; |
|
} |
|
|
|
while (oldList->child) |
|
oldList->RemoveChild(); |
|
while (oldList->next) |
|
oldList->RemoveNext(); |
|
|
|
oldList->child = newList->child; |
|
oldList->next = newList->next; |
|
sortOrder = SORT_BY_DATE; |
|
return newList; |
|
} |
|
|
|
|
|
int CALLBACK ViewProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) |
|
{ |
|
static int error; |
|
static HHOOK hHook; |
|
static HWND hTree; |
|
|
|
switch (uMsg){ |
|
case WM_INITDIALOG: |
|
{ |
|
if (!readHistory()) { |
|
MessageBox(NULL, gLoc->GetString(IDS_LOAD_FAILED), gLoc->GetString(IDS_TITLE), MB_OK); |
|
ghWndView = NULL; |
|
error = 1; |
|
PostMessage(GetDlgItem(hDlg, IDCANCEL), BM_CLICK, 0, 0); |
|
EndDialog(hDlg, 0); |
|
return 0; |
|
} |
|
|
|
error = 0; |
|
hEditWnd = hDlg; |
|
len = 0; |
|
str[len] = 0; |
|
pos = 0; |
|
circling = 0; |
|
|
|
freeNode = new CHistoryNode(_Tr("Delete"), _Tr("Delete"), HISTORY_FOLDER, 0); |
|
|
|
HICON hIcon; |
|
TCHAR szFullPath[MAX_PATH]; |
|
FindSkinFile(szFullPath, _T("history-view.ico")); |
|
|
|
if (*szFullPath==0 || (hIcon = (HICON)LoadImage( NULL, szFullPath, IMAGE_ICON, 0,0, LR_DEFAULTSIZE | LR_LOADFROMFILE ))==NULL) |
|
hIcon = (HICON)LoadImage( kPlugin.hDllInstance, MAKEINTRESOURCE(IDB_ICON), IMAGE_ICON, 0,0, LR_DEFAULTSIZE ); |
|
if (hIcon) |
|
SendMessage(hDlg, WM_SETICON, ICON_BIG, (LPARAM) hIcon); |
|
|
|
if (*szFullPath==0 || (hIcon = (HICON)LoadImage( NULL, szFullPath, IMAGE_ICON, 16,16, LR_LOADFROMFILE ))==NULL) |
|
hIcon = (HICON)LoadImage( kPlugin.hDllInstance, MAKEINTRESOURCE(IDB_ICON), IMAGE_ICON, 16,16, 0 ); |
|
if (hIcon) |
|
SendMessage(hDlg, WM_SETICON, ICON_SMALL, (LPARAM) hIcon); |
|
|
|
hTree = GetDlgItem(hDlg, IDC_TREE_HOTLIST); |
|
TreeView_SetImageList(hTree, gImagelist, TVSIL_NORMAL); |
|
|
|
sortOrder = SORT_BY_DATE; |
|
kPlugin.kFuncs->GetPreference(PREF_INT, PREFERENCE_VIEW_SORT_ORDER, &sortOrder, &sortOrder); |
|
if (sortOrder == SORT_BY_DATE) |
|
workingHist = groupDates(&gHistoryRoot); |
|
else |
|
workingHist = groupURLs(&gHistoryRoot); |
|
|
|
TVINSERTSTRUCT tvis; |
|
tvis.hParent = NULL; |
|
tvis.hInsertAfter = NULL; |
|
tvis.itemex.mask = TVIF_TEXT | TVIF_PARAM | TVIF_IMAGE | TVIF_SELECTEDIMAGE; |
|
tvis.itemex.iImage = IMAGE_FOLDER_SPECIAL_CLOSED; |
|
tvis.itemex.iSelectedImage = IMAGE_FOLDER_SPECIAL_OPEN; |
|
tvis.itemex.pszText = (TCHAR*)_Tr("All URLs"); |
|
tvis.itemex.lParam = (long)&workingHist; |
|
|
|
// HTREEITEM newItem = TreeView_InsertItem(hTree, &tvis); |
|
|
|
FillTree(hTree, NULL, *workingHist, sortOrder == SORT_BY_URL); |
|
|
|
TreeView_SelectItem(hTree, TreeView_GetRoot(hTree)); |
|
|
|
int dialogleft = 50, dialogtop = 50, dialogwidth = 500, dialogheight = 500; |
|
kPlugin.kFuncs->GetPreference(PREF_INT, PREFERENCE_VIEW_DLG_LEFT, &dialogleft, &dialogleft); |
|
kPlugin.kFuncs->GetPreference(PREF_INT, PREFERENCE_VIEW_DLG_TOP, &dialogtop, &dialogtop); |
|
kPlugin.kFuncs->GetPreference(PREF_INT, PREFERENCE_VIEW_DLG_WIDTH, &dialogwidth, &dialogwidth); |
|
kPlugin.kFuncs->GetPreference(PREF_INT, PREFERENCE_VIEW_DLG_HEIGHT, &dialogheight, &dialogheight); |
|
SetWindowPos(hDlg, 0, dialogleft, dialogtop, dialogwidth, dialogheight, 0); |
|
kPlugin.kFuncs->GetPreference(PREF_BOOL, PREFERENCE_VIEW_ZOOM, &zoom, &zoom); |
|
kPlugin.kFuncs->GetPreference(PREF_BOOL, PREFERENCE_VIEW_MAX, &maximized, &maximized); |
|
SetWindowPos(hDlg, 0, dialogleft, dialogtop, dialogwidth, dialogheight, 0); |
|
if (maximized) |
|
ShowWindow(hDlg, SW_MAXIMIZE); |
|
else |
|
ShowWindow(hDlg, SW_NORMAL); |
|
|
|
hHook = SetWindowsHookEx(WH_KEYBOARD, KeyboardProc, NULL, GetCurrentThreadId()); |
|
RECT rect; |
|
GetClientRect(hDlg, &rect); |
|
OnSize(rect.bottom, rect.right); |
|
|
|
TreeView_SelectItem(hTree, TreeView_GetRoot(hTree)); // just to fire off a SELCHANGED notifier to update the view (zoom) |
|
if (sortOrder == SORT_BY_DATE) |
|
TreeView_Expand(hTree, TreeView_GetRoot(hTree), TVE_EXPAND); |
|
} |
|
return false; |
|
|
|
case WM_NOTIFY: |
|
{ |
|
NMTREEVIEW *nmtv = (NMTREEVIEW *)lParam; |
|
|
|
// Selection changed |
|
if (nmtv->hdr.code == TVN_SELCHANGED){ |
|
// Put the new url/title into the box |
|
CHistoryNode *newNode = (CHistoryNode *)nmtv->itemNew.lParam; |
|
|
|
if (newNode == workingHist) { |
|
// root and separators have nothing |
|
SetDlgItemText(hDlg, IDC_URL, _T("")); |
|
SetDlgItemText(hDlg, IDC_LAST_VISIT, _T("")); |
|
EnableWindow(GetDlgItem(hDlg, IDC_STATIC_URL), false); |
|
EnableWindow(GetDlgItem(hDlg, IDC_URL), false); |
|
EnableWindow(GetDlgItem(hDlg, IDC_STATIC_VISITED), false); |
|
EnableWindow(GetDlgItem(hDlg, IDC_STATIC_PROPERTIES), false); |
|
return true; // a lazy-man's "else" |
|
} |
|
|
|
// everything else has at least title, added date, and properties in general |
|
EnableWindow(GetDlgItem(hDlg, IDC_STATIC_PROPERTIES), true); |
|
|
|
SYSTEMTIME st; |
|
TCHAR pszTmp[1024]; |
|
TCHAR pszDate[900]; |
|
|
|
if (newNode->type == HISTORY_BOOKMARK) { |
|
EnableWindow(GetDlgItem(hDlg, IDC_STATIC_URL), true); |
|
EnableWindow(GetDlgItem(hDlg, IDC_URL), true); |
|
SetDlgItemText(hDlg, IDC_URL, newNode->url.c_str()); |
|
EnableWindow(GetDlgItem(hDlg, IDC_STATIC_VISITED), true); |
|
EnableWindow(GetDlgItem(hDlg, IDC_LAST_VISIT), true); |
|
} |
|
else { |
|
SetDlgItemText(hDlg, IDC_URL, _T("")); |
|
EnableWindow(GetDlgItem(hDlg, IDC_STATIC_URL), false); |
|
EnableWindow(GetDlgItem(hDlg, IDC_URL), false); |
|
EnableWindow(GetDlgItem(hDlg, IDC_STATIC_VISITED), false); |
|
EnableWindow(GetDlgItem(hDlg, IDC_LAST_VISIT), false); |
|
} |
|
|
|
if (newNode->lastVisit) { |
|
UnixTimeToSystemTime(newNode->lastVisit, &st); |
|
GetDateFormat(GetThreadLocale(), DATE_SHORTDATE, &st, NULL, pszDate, 899); |
|
_tcscpy(pszTmp, pszDate); |
|
_tcscat(pszTmp, _T(" ")); |
|
GetTimeFormat(GetThreadLocale(), NULL, &st, NULL, pszDate, 899); |
|
_tcscat(pszTmp, pszDate); |
|
SetDlgItemText(hDlg, IDC_LAST_VISIT, pszTmp); |
|
} |
|
else { |
|
SetDlgItemText(hDlg, IDC_LAST_VISIT, newNode->type == HISTORY_BOOKMARK ? _Tr("Never") : _T("")); |
|
} |
|
} |
|
else if (nmtv->hdr.code == (UINT) NM_DBLCLK){ |
|
TVHITTESTINFO hti; |
|
GetCursorPos(&hti.pt); |
|
ScreenToClient(hTree, &hti.pt); |
|
|
|
HTREEITEM hItem = TreeView_HitTest(hTree, &hti); |
|
if (hItem){ |
|
CHistoryNode *node = GetHistoryNode(hTree, hItem); |
|
if (node->type == HISTORY_BOOKMARK) { |
|
node->lastVisit = time(NULL); |
|
kPlugin.kFuncs->NavigateTo(node->url.c_str(), OPEN_NORMAL, NULL); |
|
TreeView_SelectItem(hTree, hItem); // just to fire off a SELCHANGED notifier to update the status (last visited!) |
|
PostMessage(hWndFront, WM_COMMAND, wm_deferbringtotop, (LPARAM)NULL); |
|
|
|
return true; |
|
} |
|
} |
|
} |
|
// right click... |
|
else if (nmtv->hdr.code == (UINT) NM_RCLICK){ |
|
OnRClick(hTree); |
|
} |
|
} |
|
break; |
|
|
|
case WM_SIZE: |
|
{ |
|
if(wParam != SIZE_MINIMIZED) { |
|
RECT rect; |
|
GetClientRect(hDlg, &rect); |
|
OnSize(rect.bottom, rect.right); |
|
} |
|
} |
|
break; |
|
|
|
case WM_GETMINMAXINFO: |
|
LPMINMAXINFO lpminmaxinfo; |
|
lpminmaxinfo=(LPMINMAXINFO)lParam; |
|
lpminmaxinfo->ptMinTrackSize.x = 195; |
|
lpminmaxinfo->ptMinTrackSize.y = 300; |
|
break; |
|
|
|
case WM_COMMAND: |
|
{ |
|
if (HIWORD(wParam) == BN_CLICKED) { |
|
WORD id = LOWORD(wParam); |
|
switch(id){ |
|
|
|
case IDCANCEL: |
|
if (error) { |
|
error = 0; |
|
ghWndView = NULL; |
|
if (hWndFront) |
|
PostMessage(hWndFront, WM_COMMAND, wm_deferbringtotop, (LPARAM)NULL); |
|
DestroyWindow(hDlg); |
|
break; |
|
} |
|
if (!zoom) { |
|
ghWndView = NULL; |
|
|
|
if (gHistoryRoot.child) |
|
delete gHistoryRoot.child; |
|
if (gHistoryRoot.next) |
|
delete gHistoryRoot.next; |
|
gHistoryRoot.child = NULL; |
|
gHistoryRoot.next = NULL; |
|
|
|
UnhookWindowsHookEx(hHook); |
|
if (hWndFront) |
|
PostMessage(hWndFront, WM_COMMAND, wm_deferbringtotop, (LPARAM)NULL); |
|
DestroyIcon((HICON)SendMessage(hDlg, WM_GETICON, ICON_SMALL, 0)); |
|
DestroyIcon((HICON)SendMessage(hDlg, WM_GETICON, ICON_BIG, 0)); |
|
DestroyWindow(hDlg); |
|
break; |
|
} |
|
// fall through! |
|
|
|
case IDOK: |
|
{ |
|
WINDOWPLACEMENT wp; |
|
wp.length = sizeof(wp); |
|
GetWindowPlacement(hDlg, &wp); |
|
|
|
kPlugin.kFuncs->SetPreference(PREF_INT, PREFERENCE_VIEW_DLG_LEFT, &wp.rcNormalPosition.left, FALSE); |
|
kPlugin.kFuncs->SetPreference(PREF_INT, PREFERENCE_VIEW_DLG_TOP, &wp.rcNormalPosition.top, FALSE); |
|
int temp; |
|
temp = wp.rcNormalPosition.right - wp.rcNormalPosition.left; |
|
kPlugin.kFuncs->SetPreference(PREF_INT, PREFERENCE_VIEW_DLG_WIDTH, &temp, FALSE); |
|
temp = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top; |
|
kPlugin.kFuncs->SetPreference(PREF_INT, PREFERENCE_VIEW_DLG_HEIGHT, &temp, FALSE); |
|
kPlugin.kFuncs->SetPreference(PREF_BOOL, PREFERENCE_VIEW_ZOOM, &zoom, FALSE); |
|
temp = (wp.showCmd == SW_SHOWMAXIMIZED); |
|
kPlugin.kFuncs->SetPreference(PREF_BOOL, PREFERENCE_VIEW_MAX, &temp, FALSE); |
|
|
|
kPlugin.kFuncs->SetPreference(PREF_INT, PREFERENCE_VIEW_SORT_ORDER, &sortOrder, FALSE); |
|
|
|
ghWndView = NULL; |
|
|
|
if (gHistoryRoot.child) |
|
delete gHistoryRoot.child; |
|
if (gHistoryRoot.next) |
|
delete gHistoryRoot.next; |
|
gHistoryRoot.child = NULL; |
|
gHistoryRoot.next = NULL; |
|
|
|
UnhookWindowsHookEx(hHook); |
|
if (hWndFront) |
|
PostMessage(hWndFront, WM_COMMAND, wm_deferbringtotop, (LPARAM)NULL); |
|
DestroyIcon((HICON)SendMessage(hDlg, WM_GETICON, ICON_SMALL, 0)); |
|
DestroyIcon((HICON)SendMessage(hDlg, WM_GETICON, ICON_BIG, 0)); |
|
DestroyWindow(hDlg); |
|
|
|
// Delete all selected history entries |
|
if (freeNode && freeNode->child) { |
|
// This is not likely to be implemented in a while.. |
|
|
|
MessageBox(NULL, _Tr("Deletion is not implemented"), _Tr("History"), MB_OK); |
|
|
|
/* |
|
freeNode->flatten(); |
|
CHistoryNode *ptr = freeNode->child; |
|
while (ptr) { |
|
MessageBox(NULL, ptr->url.c_str(), "Delete", MB_OK); |
|
ptr = ptr->next; |
|
} |
|
*/ |
|
} |
|
if (freeNode) |
|
delete freeNode; |
|
|
|
break; |
|
} |
|
} |
|
} |
|
} |
|
break; |
|
} |
|
|
|
return false; |
|
} |
|
|
|
static void FillTree(HWND hTree, HTREEITEM parent, CHistoryNode &node, int level) |
|
{ |
|
TVINSERTSTRUCT tvis; |
|
tvis.hParent = parent; |
|
tvis.hInsertAfter = NULL; |
|
tvis.itemex.mask = TVIF_TEXT | TVIF_PARAM | TVIF_IMAGE | TVIF_SELECTEDIMAGE; |
|
tvis.itemex.iImage = IMAGE_BLANK; |
|
tvis.itemex.iSelectedImage = IMAGE_BLANK; |
|
|
|
int type; |
|
CHistoryNode *child; |
|
for (child=node.child; child; child=child->next) { |
|
tvis.itemex.lParam = (long)child; |
|
|
|
type = child->type; |
|
if (type == HISTORY_FOLDER) { |
|
tvis.itemex.iImage = level == 0 ? IMAGE_FOLDER_SPECIAL_CLOSED : IMAGE_FOLDER_CLOSED; |
|
tvis.itemex.iSelectedImage = level == 0 ? IMAGE_FOLDER_SPECIAL_OPEN : IMAGE_FOLDER_OPEN; |
|
tvis.itemex.pszText = (char *)child->name.c_str(); |
|
|
|
HTREEITEM thisItem = TreeView_InsertItem(hTree, &tvis); |
|
|
|
FillTree(hTree, thisItem, *child, level+1); |
|
} |
|
else if (type == HISTORY_SEPARATOR) { |
|
tvis.itemex.iImage = IMAGE_BLANK; |
|
tvis.itemex.iSelectedImage = IMAGE_BLANK; |
|
tvis.itemex.pszText = _T("---"); |
|
TreeView_InsertItem(hTree, &tvis); |
|
} |
|
else { |
|
tvis.itemex.iImage = IMAGE_BOOKMARK; |
|
tvis.itemex.iSelectedImage = IMAGE_BOOKMARK; |
|
tvis.itemex.pszText = (char *)child->name.c_str(); |
|
TreeView_InsertItem(hTree, &tvis); |
|
} |
|
} |
|
} |
|
|
|
|
|
static void DeleteItem(HWND hTree, HTREEITEM item) { |
|
CHistoryNode *node, *parentNode; |
|
|
|
node = GetHistoryNode(hTree, item); |
|
|
|
HTREEITEM parent = TreeView_GetParent(hTree, item); |
|
if (parent){ |
|
parentNode = GetHistoryNode(hTree, parent); |
|
} |
|
else{ |
|
parentNode = workingHist; |
|
} |
|
|
|
if (freeNode && parentNode && parentNode->UnlinkNode(node)) |
|
freeNode->AddChild(node); |
|
|
|
// select a new item |
|
HTREEITEM hSelect = TreeView_GetNextSibling(hTree, item); |
|
if (!hSelect) hSelect = TreeView_GetPrevSibling(hTree, item); |
|
if (!hSelect) hSelect = TreeView_GetParent(hTree, item); |
|
|
|
TreeView_DeleteItem(hTree, item); |
|
|
|
// must call selectitem after deleteitem, otherwise the SELCHANGED handler will try to update the deleted node! |
|
TreeView_SelectItem(hTree, hSelect); |
|
|
|
} |
|
|
|
|
|
static void OnRClick(HWND hTree) |
|
{ |
|
POINT mouse; |
|
GetCursorPos(&mouse); |
|
|
|
TVHITTESTINFO hti; |
|
hti.pt.x = mouse.x; |
|
hti.pt.y = mouse.y; |
|
ScreenToClient(hTree, &hti.pt); |
|
|
|
HTREEITEM hItem = TreeView_HitTest(hTree, &hti); |
|
if (hItem) { |
|
TreeView_SelectItem(hTree, hItem); |
|
|
|
HMENU topMenu = gLoc->LoadMenu(IDR_CONTEXTMENU); |
|
HMENU contextMenu = GetSubMenu(topMenu, 0); |
|
|
|
CheckMenuItem(contextMenu, ID__SORT_DATE, MF_BYCOMMAND | MF_UNCHECKED); |
|
CheckMenuItem(contextMenu, ID__SORT_URL, MF_BYCOMMAND | MF_UNCHECKED); |
|
if (sortOrder == SORT_BY_DATE) |
|
CheckMenuItem(contextMenu, ID__SORT_DATE, MF_BYCOMMAND | MF_CHECKED); |
|
if (sortOrder == SORT_BY_URL) |
|
CheckMenuItem(contextMenu, ID__SORT_URL, MF_BYCOMMAND | MF_CHECKED); |
|
|
|
if (zoom) |
|
CheckMenuItem(contextMenu, ID__ZOOM, MF_BYCOMMAND | MF_UNCHECKED); |
|
else |
|
CheckMenuItem(contextMenu, ID__ZOOM, MF_BYCOMMAND | MF_CHECKED); |
|
|
|
int command = TrackPopupMenu(contextMenu, TPM_RIGHTBUTTON | TPM_LEFTALIGN | TPM_RETURNCMD, mouse.x, mouse.y, 0, hTree, NULL); |
|
DestroyMenu(topMenu); |
|
switch (command) { |
|
case ID__OPEN: |
|
{ |
|
CHistoryNode *node = GetHistoryNode(hTree, hItem); |
|
if (node->type == HISTORY_BOOKMARK) { |
|
kPlugin.kFuncs->NavigateTo(node->url.c_str(), OPEN_NORMAL, NULL); |
|
TreeView_SelectItem(hTree, hItem); // just to fire off a SELCHANGED notifier to update the status (last visited!) |
|
PostMessage(hWndFront, WM_COMMAND, wm_deferbringtotop, (LPARAM)NULL); |
|
} |
|
} |
|
break; |
|
case ID__OPEN_BACKGROUND: |
|
{ |
|
CHistoryNode *node = GetHistoryNode(hTree, hItem); |
|
if (node->type == HISTORY_BOOKMARK) { |
|
kPlugin.kFuncs->NavigateTo(node->url.c_str(), OPEN_BACKGROUND, NULL); |
|
TreeView_SelectItem(hTree, hItem); // just to fire off a SELCHANGED notifier to update the status (last visited!) |
|
} |
|
} |
|
break; |
|
case ID__BOOKMARK_DELETE: |
|
DeleteItem(hTree, hItem); |
|
break; |
|
break; |
|
|
|
case ID__SORT_URL: |
|
case ID__SORT_DATE: |
|
{ |
|
if ((command == ID__SORT_URL && sortOrder == SORT_BY_URL) || |
|
(command == ID__SORT_DATE && sortOrder == SORT_BY_DATE)) { |
|
break; |
|
} |
|
|
|
// TreeView_DeleteAllItems(hTree); |
|
|
|
TreeView_SelectItem(hTree, TreeView_GetRoot(hTree)); |
|
HTREEITEM hTmp = TreeView_GetLastVisible(hTree); |
|
while (hTmp) { |
|
TreeView_DeleteItem(hTree, hTmp); |
|
hTmp = TreeView_GetLastVisible(hTree); |
|
} |
|
|
|
if (command == ID__SORT_URL) { |
|
workingHist = groupURLs(&gHistoryRoot); |
|
} |
|
else if (command == ID__SORT_DATE) { |
|
workingHist = groupDates(&gHistoryRoot); |
|
} |
|
|
|
FillTree(hTree, NULL, *workingHist, command == ID__SORT_URL); |
|
|
|
TreeView_SelectItem(hTree, TreeView_GetRoot(hTree)); |
|
if (command == ID__SORT_DATE) |
|
TreeView_Expand(hTree, TreeView_GetRoot(hTree), TVE_EXPAND); |
|
|
|
break; |
|
} |
|
case ID__ZOOM: |
|
{ |
|
zoom = !zoom; |
|
|
|
RECT rect; |
|
GetClientRect(hEditWnd, &rect); |
|
OnSize(rect.bottom, rect.right); |
|
|
|
TreeView_SelectItem(hTree, hItem); // just to fire off a SELCHANGED notifier |
|
} |
|
break; |
|
} |
|
} |
|
} |
|
|
|
|
|
// this is ugly, see... all this need to be changed each time the dialog is re-arranged... |
|
#define BORDER 8 // this one is pixels, the rest are DLUs |
|
#define BUTTON_HEIGHT 15 |
|
#define OK_WIDTH 45 |
|
#define CANCEL_WIDTH 45 |
|
#define PROPERTIES_HEIGHT 50 |
|
#define EDITBOXES_LEFT 36 |
|
#define EDITBOXES_TOP (276-174) |
|
#define EDITBOXES_HEIGHT 12 |
|
#define DATES_WIDTH 78 |
|
#define OTHER_WIDTH 35 |
|
|
|
#define convX(x) MulDiv((int)(x), buX, 4) |
|
#define convY(y) MulDiv((int)(y), buY, 8) |
|
|
|
static void OnSize(int height, int width) { |
|
int buX, buY; |
|
RECT rc; // GetDialogBaseUnits returns incorrect values...? |
|
SetRect(&rc, 0, 0, 4, 8); |
|
MapDialogRect(hEditWnd, &rc); |
|
buY = rc.bottom; |
|
buX = rc.right; |
|
|
|
// resize tree |
|
SetWindowPos(GetDlgItem(hEditWnd, IDC_TREE_HOTLIST), 0, |
|
zoom ? 0 : BORDER, |
|
zoom ? 0 : BORDER, |
|
zoom ? width : width-(BORDER*2), |
|
zoom ? height : height-BORDER*4-convY(BUTTON_HEIGHT+PROPERTIES_HEIGHT), |
|
0); |
|
|
|
// move cancel button |
|
SetWindowPos(GetDlgItem(hEditWnd, IDCANCEL), 0, |
|
width-BORDER-convX(CANCEL_WIDTH), |
|
zoom ? height + BORDER : height-BORDER-convY(BUTTON_HEIGHT), |
|
0, 0, SWP_NOSIZE); |
|
|
|
// move ok button |
|
SetWindowPos(GetDlgItem(hEditWnd, IDOK), 0, |
|
width-BORDER*2-convX(CANCEL_WIDTH+OK_WIDTH), |
|
zoom ? height + BORDER : height-BORDER-convY(BUTTON_HEIGHT), |
|
0, 0, SWP_NOSIZE); |
|
|
|
// move/resize properties box |
|
SetWindowPos(GetDlgItem(hEditWnd, IDC_STATIC_PROPERTIES), 0, |
|
BORDER, |
|
zoom ? height + BORDER : height-BORDER*2-convY(BUTTON_HEIGHT+PROPERTIES_HEIGHT), |
|
width-BORDER*2, |
|
convY(PROPERTIES_HEIGHT), |
|
0); |
|
|
|
// move/resize properties widgets |
|
int x_max = convX(EDITBOXES_LEFT+DATES_WIDTH)+BORDER; |
|
int x_half = width/2 + BORDER/2; |
|
int x2 = min(x_max, x_half); |
|
int w1 = x2 - convX(EDITBOXES_LEFT) - BORDER; |
|
int w2 = width - x2 - 2*BORDER; |
|
|
|
SetWindowPos(GetDlgItem(hEditWnd, IDC_STATIC_VISITED), 0, |
|
BORDER*2, |
|
zoom ? height + BORDER : height-convY(EDITBOXES_TOP-EDITBOXES_HEIGHT*5.0), |
|
0, |
|
0, SWP_NOSIZE); |
|
SetWindowPos(GetDlgItem(hEditWnd, IDC_LAST_VISIT), 0, |
|
convX(EDITBOXES_LEFT)+5, |
|
zoom ? height + BORDER : height-convY(EDITBOXES_TOP-EDITBOXES_HEIGHT*5.0), |
|
w1, // convX(DATES_WIDTH), |
|
convY(EDITBOXES_HEIGHT), |
|
0); |
|
SetWindowPos(GetDlgItem(hEditWnd, IDC_STATIC_URL), 0, |
|
BORDER*2, |
|
zoom ? height + BORDER : height-convY(EDITBOXES_TOP-EDITBOXES_HEIGHT*3.75), |
|
0, 0, SWP_NOSIZE); |
|
SetWindowPos(GetDlgItem(hEditWnd, IDC_URL), 0, |
|
convX(EDITBOXES_LEFT), |
|
zoom ? height + BORDER : height-convY(EDITBOXES_TOP-EDITBOXES_HEIGHT*3.75)-2, |
|
width-convX(EDITBOXES_LEFT)-BORDER*2, |
|
convY(EDITBOXES_HEIGHT), |
|
0); |
|
InvalidateRgn(hEditWnd, NULL, FALSE); |
|
UpdateWindow(hEditWnd); |
|
}
|
|
|