diff --git a/camino/Chimera.pbproj/project.pbxproj b/camino/Chimera.pbproj/project.pbxproj index 0ccf84762ae2..ec57666240c7 100644 --- a/camino/Chimera.pbproj/project.pbxproj +++ b/camino/Chimera.pbproj/project.pbxproj @@ -255,6 +255,7 @@ children = ( 29B97325FDCFA39411CA2CEA, 29B97324FDCFA39411CA2CEA, + 3FB2BAE30545EB52002B9691, ); isa = PBXGroup; name = "Other Frameworks"; @@ -301,6 +302,7 @@ F5A8CE4602DFF039013CA8EC, F51704E7034A305B01026D5D, F5170512034A38FD01026D5D, + 3FB2BADA0545EAA2002B9691, ); isa = PBXGroup; name = Products; @@ -344,10 +346,12 @@ F51704D2034A305B01026D5D, F51704FD034A38FD01026D5D, F5BAAB1902AC45D301A967F3, + 3FB2BAD90545EAA2002B9691, ); }; 29B97314FDCFA39411CA2CEA = { children = ( + 3FB2BA7C0545E9F4002B9691, F51842F30206168101A966FE, 080E96DDFE201D6D7F000001, F5571935022B401B010001CA, @@ -484,6 +488,7 @@ F57F20760299A34A01000106, F51704F3034A31C201026D5D, F5170513034A396401026D5D, + 3FB2BADB0545EAA2002B9691, ); isa = PBXApplicationTarget; name = Camino; @@ -688,7 +693,6 @@ F5DE10EE0209DC0601A967DF, F517395C020CE3750189DA0C, F528E219020FD8400168DE43, - F507BA4A0213AD5F01D93544, F53F2202022B7C78010001CA, F53F2203022B7C78010001CA, F53F2204022B7C78010001CA, @@ -699,8 +703,6 @@ F568C3D1023A4A5B010001CA, F564873B023C3857010001CA, F5137A1202676B9101026D05, - F57074B7026BA85F01A80166, - F57074BB026BFD0201A80166, F57074BF026D80DF01A80166, F51B70B8026EC98B01A80166, F5C3AB830270072A01A80166, @@ -712,12 +714,10 @@ F52F87CF027D75C301A80165, F52F87D0027D75C301A80165, 2E293A01027F33604B000102, - 2EEC3E63028138724B000102, 2E748B74029A448D4B000102, F53E012A02AEE91D01A967F3, F632AF8502B9AEBB01000103, F59236C202C89ACA0100012B, - F5A3669802CCFAF601DC3354, F55C4DD402D2864E0130B065, F50D9DE402ECC2C601BB4219, F50D9DEF02EE0AB101BB4219, @@ -737,9 +737,7 @@ F5F94B920332532801026D5D, F5B34B05034A50C901A80166, F529788E0371820B01026DCE, - F53D36BE037843C201A80166, F58DB2D40381FD3301A9666E, - F55EBC9C0383665201A80166, F57BED9B03A1824801A9666E, F57BEDA003A1825001A9666E, F5C8D55303A2A42401A8016F, @@ -748,10 +746,24 @@ F5F415CB03B9223E01A80166, F527C90403BCD43601A80166, 3F2CF8CD042A88B7005FD42F, - 3F2CF8D2042A8B30005FD42F, 3003B8960445144100B85BF3, 3003B8970445144100B85BF3, 3003B8980445144100B85BF3, + 3FB2BA9A0545EA80002B9691, + 3FB2BA9C0545EA80002B9691, + 3FB2BA9E0545EA80002B9691, + 3FB2BAA00545EA80002B9691, + 3FB2BAA20545EA80002B9691, + 3FB2BAA40545EA80002B9691, + 3FB2BAA60545EA80002B9691, + 3FB2BAA80545EA80002B9691, + 3FB2BAAA0545EA80002B9691, + 3FB2BAAB0545EA80002B9691, + 3FB2BAAD0545EA80002B9691, + 3FB2BAAF0545EA80002B9691, + 3FB2BAB10545EA80002B9691, + 3FB2BAB30545EA80002B9691, + 3FB2BAB50545EA80002B9691, ); isa = PBXHeadersBuildPhase; runOnlyForDeploymentPostprocessing = 0; @@ -842,6 +854,17 @@ 3003B8A5044514B600B85BF3, 3003B8AD044514E300B85BF3, 3FAD95550461E43700A80005, + 3FB2BADC0545EAA2002B9691, + 3FB2BAE80545EBAB002B9691, + 3FB2BAFB0545EBF2002B9691, + 3FB2BAFC0545EBF2002B9691, + 3FB2BAFD0545EBF2002B9691, + 3FB2BAFE0545EBF2002B9691, + 3FB2BAFF0545EBF2002B9691, + 3FB2BB000545EBF2002B9691, + 3FB2BB010545EBF2002B9691, + 3FB2BB020545EBF2002B9691, + 3FB2BB030545EBF2002B9691, ); isa = PBXResourcesBuildPhase; runOnlyForDeploymentPostprocessing = 0; @@ -858,8 +881,6 @@ F5DE10F10209DC0601A967DF, F517395D020CE3750189DA0C, F528E21B020FD9620168DE43, - F507BA4D0213AFF701D93544, - F5A3669902CCFAF601DC3354, F53F220E022B7C78010001CA, F53F220F022B7C78010001CA, F53F2210022B7C78010001CA, @@ -868,8 +889,6 @@ F580736D023A1514010001CA, F568C3D2023A4A5B010001CA, F564873C023C3857010001CA, - F57074B8026BA85F01A80166, - F57074BC026BFD0201A80166, F57074C0026D80DF01A80166, F51B70B9026EC98B01A80166, F5C3AB840270072A01A80166, @@ -881,7 +900,6 @@ F52F87D1027D75C301A80165, F52F87D2027D75C301A80165, 2E293A02027F33604B000102, - 2EEC3E64028138724B000102, 2E748B75029A448D4B000102, F53E012D02AEE93701A967F3, F632AF8602B9AEBC01000103, @@ -905,19 +923,30 @@ F5F94B930332532801026D5D, F5B34B15034A605401A80166, F529788F0371820B01026DCE, - F5B63094037A41E301A80166, F58DB2D50381FD3301A9666E, - F55EBC9D0383665201A80166, F57BED9803A1824001A9666E, F57BED9F03A1825001A9666E, F583E3C003B8228701A80166, F5F415CC03B9223E01A80166, F527C90503BCD43601A80166, 3F2CF8CE042A88B7005FD42F, - 3F2CF8D5042A8C47005FD42F, 3003B88D0445140000B85BF3, 3003B88E0445140000B85BF3, 3003B88F0445140000B85BF3, + 3FB2BA9B0545EA80002B9691, + 3FB2BA9D0545EA80002B9691, + 3FB2BA9F0545EA80002B9691, + 3FB2BAA10545EA80002B9691, + 3FB2BAA30545EA80002B9691, + 3FB2BAA50545EA80002B9691, + 3FB2BAA70545EA80002B9691, + 3FB2BAA90545EA80002B9691, + 3FB2BAAC0545EA80002B9691, + 3FB2BAAE0545EA80002B9691, + 3FB2BAB00545EA80002B9691, + 3FB2BAB20545EA80002B9691, + 3FB2BAB40545EA80002B9691, + 3FB2BAB60545EA80002B9691, ); isa = PBXSourcesBuildPhase; runOnlyForDeploymentPostprocessing = 0; @@ -1008,32 +1037,6 @@ settings = { }; }; - 2EEC3E61028138714B000102 = { - fileEncoding = 30; - isa = PBXFileReference; - name = BookmarksOutlineView.h; - path = src/bookmarks/BookmarksOutlineView.h; - refType = 2; - }; - 2EEC3E62028138714B000102 = { - fileEncoding = 30; - isa = PBXFileReference; - name = BookmarksOutlineView.mm; - path = src/bookmarks/BookmarksOutlineView.mm; - refType = 2; - }; - 2EEC3E63028138724B000102 = { - fileRef = 2EEC3E61028138714B000102; - isa = PBXBuildFile; - settings = { - }; - }; - 2EEC3E64028138724B000102 = { - fileRef = 2EEC3E62028138714B000102; - isa = PBXBuildFile; - settings = { - }; - }; //2E0 //2E1 //2E2 @@ -1352,44 +1355,6 @@ settings = { }; }; - 3F2CF8D1042A8B30005FD42F = { - fileEncoding = 4; - isa = PBXFileReference; - name = BookmarksController.h; - path = src/bookmarks/BookmarksController.h; - refType = 4; - }; - 3F2CF8D2042A8B30005FD42F = { - fileRef = 3F2CF8D1042A8B30005FD42F; - isa = PBXBuildFile; - settings = { - }; - }; - 3F2CF8D3042A8B30005FD42F = { - fileRef = 3F2CF8D1042A8B30005FD42F; - isa = PBXBuildFile; - settings = { - }; - }; - 3F2CF8D4042A8C47005FD42F = { - fileEncoding = 4; - isa = PBXFileReference; - name = BookmarksController.mm; - path = src/bookmarks/BookmarksController.mm; - refType = 4; - }; - 3F2CF8D5042A8C47005FD42F = { - fileRef = 3F2CF8D4042A8C47005FD42F; - isa = PBXBuildFile; - settings = { - }; - }; - 3F2CF8D6042A8C47005FD42F = { - fileRef = 3F2CF8D4042A8C47005FD42F; - isa = PBXBuildFile; - settings = { - }; - }; 3F8D26DE0471ED0F00A80005 = { fileRef = 3FAD95540461E43700A80005; isa = PBXBuildFile; @@ -1434,6 +1399,916 @@ settings = { }; }; + 3FB2BA7C0545E9F4002B9691 = { + children = ( + ); + isa = PBXGroup; + name = "New Files"; + refType = 4; + }; + 3FB2BA7D0545EA80002B9691 = { + fileEncoding = 30; + isa = PBXFileReference; + name = Bookmark.h; + path = src/bookmarks/Bookmark.h; + refType = 2; + }; + 3FB2BA7E0545EA80002B9691 = { + fileEncoding = 30; + isa = PBXFileReference; + name = Bookmark.mm; + path = src/bookmarks/Bookmark.mm; + refType = 2; + }; + 3FB2BA7F0545EA80002B9691 = { + fileEncoding = 30; + isa = PBXFileReference; + name = BookmarkButton.h; + path = src/bookmarks/BookmarkButton.h; + refType = 2; + }; + 3FB2BA800545EA80002B9691 = { + fileEncoding = 30; + isa = PBXFileReference; + name = BookmarkButton.mm; + path = src/bookmarks/BookmarkButton.mm; + refType = 2; + }; + 3FB2BA810545EA80002B9691 = { + fileEncoding = 30; + isa = PBXFileReference; + name = BookmarkFolder.h; + path = src/bookmarks/BookmarkFolder.h; + refType = 2; + }; + 3FB2BA820545EA80002B9691 = { + fileEncoding = 30; + isa = PBXFileReference; + name = BookmarkFolder.mm; + path = src/bookmarks/BookmarkFolder.mm; + refType = 2; + }; + 3FB2BA830545EA80002B9691 = { + fileEncoding = 30; + isa = PBXFileReference; + name = BookmarkImportDlgController.h; + path = src/bookmarks/BookmarkImportDlgController.h; + refType = 2; + }; + 3FB2BA840545EA80002B9691 = { + fileEncoding = 30; + isa = PBXFileReference; + name = BookmarkImportDlgController.mm; + path = src/bookmarks/BookmarkImportDlgController.mm; + refType = 2; + }; + 3FB2BA850545EA80002B9691 = { + fileEncoding = 30; + isa = PBXFileReference; + name = BookmarkItem.h; + path = src/bookmarks/BookmarkItem.h; + refType = 2; + }; + 3FB2BA860545EA80002B9691 = { + fileEncoding = 30; + isa = PBXFileReference; + name = BookmarkItem.m; + path = src/bookmarks/BookmarkItem.m; + refType = 2; + }; + 3FB2BA870545EA80002B9691 = { + fileEncoding = 30; + isa = PBXFileReference; + name = BookmarkManager.h; + path = src/bookmarks/BookmarkManager.h; + refType = 2; + }; + 3FB2BA880545EA80002B9691 = { + fileEncoding = 30; + isa = PBXFileReference; + name = BookmarkManager.mm; + path = src/bookmarks/BookmarkManager.mm; + refType = 2; + }; + 3FB2BA890545EA80002B9691 = { + fileEncoding = 30; + isa = PBXFileReference; + name = BookmarkMenu.h; + path = src/bookmarks/BookmarkMenu.h; + refType = 2; + }; + 3FB2BA8A0545EA80002B9691 = { + fileEncoding = 30; + isa = PBXFileReference; + name = BookmarkMenu.mm; + path = src/bookmarks/BookmarkMenu.mm; + refType = 2; + }; + 3FB2BA8B0545EA80002B9691 = { + fileEncoding = 30; + isa = PBXFileReference; + name = BookmarkOutlineView.h; + path = src/bookmarks/BookmarkOutlineView.h; + refType = 2; + }; + 3FB2BA8C0545EA80002B9691 = { + fileEncoding = 30; + isa = PBXFileReference; + name = BookmarkOutlineView.mm; + path = src/bookmarks/BookmarkOutlineView.mm; + refType = 2; + }; + 3FB2BA8D0545EA80002B9691 = { + fileEncoding = 30; + isa = PBXFileReference; + name = BookmarksClient.h; + path = src/bookmarks/BookmarksClient.h; + refType = 2; + }; + 3FB2BA8E0545EA80002B9691 = { + fileEncoding = 30; + isa = PBXFileReference; + name = BookmarkToolbar.h; + path = src/bookmarks/BookmarkToolbar.h; + refType = 2; + }; + 3FB2BA8F0545EA80002B9691 = { + fileEncoding = 30; + isa = PBXFileReference; + name = BookmarkToolbar.mm; + path = src/bookmarks/BookmarkToolbar.mm; + refType = 2; + }; + 3FB2BA900545EA80002B9691 = { + fileEncoding = 30; + isa = PBXFileReference; + name = BookmarkViewController.h; + path = src/bookmarks/BookmarkViewController.h; + refType = 2; + }; + 3FB2BA910545EA80002B9691 = { + fileEncoding = 30; + isa = PBXFileReference; + name = BookmarkViewController.mm; + path = src/bookmarks/BookmarkViewController.mm; + refType = 2; + }; + 3FB2BA920545EA80002B9691 = { + fileEncoding = 30; + isa = PBXFileReference; + name = ExtendedTableView.h; + path = src/extensions/ExtendedTableView.h; + refType = 2; + }; + 3FB2BA930545EA80002B9691 = { + fileEncoding = 30; + isa = PBXFileReference; + name = ExtendedTableView.mm; + path = src/extensions/ExtendedTableView.mm; + refType = 2; + }; + 3FB2BA940545EA80002B9691 = { + fileEncoding = 30; + isa = PBXFileReference; + name = KindaSmartFolderManager.h; + path = src/bookmarks/KindaSmartFolderManager.h; + refType = 2; + }; + 3FB2BA950545EA80002B9691 = { + fileEncoding = 30; + isa = PBXFileReference; + name = KindaSmartFolderManager.mm; + path = src/bookmarks/KindaSmartFolderManager.mm; + refType = 2; + }; + 3FB2BA960545EA80002B9691 = { + fileEncoding = 30; + isa = PBXFileReference; + name = "NSArray+Utils.h"; + path = "src/extensions/NSArray+Utils.h"; + refType = 2; + }; + 3FB2BA970545EA80002B9691 = { + fileEncoding = 30; + isa = PBXFileReference; + name = "NSArray+Utils.mm"; + path = "src/extensions/NSArray+Utils.mm"; + refType = 2; + }; + 3FB2BA980545EA80002B9691 = { + fileEncoding = 30; + isa = PBXFileReference; + name = RunLoopMessenger.h; + path = src/application/RunLoopMessenger.h; + refType = 2; + }; + 3FB2BA990545EA80002B9691 = { + fileEncoding = 30; + isa = PBXFileReference; + name = RunLoopMessenger.mm; + path = src/application/RunLoopMessenger.mm; + refType = 2; + }; + 3FB2BA9A0545EA80002B9691 = { + fileRef = 3FB2BA7D0545EA80002B9691; + isa = PBXBuildFile; + settings = { + }; + }; + 3FB2BA9B0545EA80002B9691 = { + fileRef = 3FB2BA7E0545EA80002B9691; + isa = PBXBuildFile; + settings = { + }; + }; + 3FB2BA9C0545EA80002B9691 = { + fileRef = 3FB2BA7F0545EA80002B9691; + isa = PBXBuildFile; + settings = { + }; + }; + 3FB2BA9D0545EA80002B9691 = { + fileRef = 3FB2BA800545EA80002B9691; + isa = PBXBuildFile; + settings = { + }; + }; + 3FB2BA9E0545EA80002B9691 = { + fileRef = 3FB2BA810545EA80002B9691; + isa = PBXBuildFile; + settings = { + }; + }; + 3FB2BA9F0545EA80002B9691 = { + fileRef = 3FB2BA820545EA80002B9691; + isa = PBXBuildFile; + settings = { + }; + }; + 3FB2BAA00545EA80002B9691 = { + fileRef = 3FB2BA830545EA80002B9691; + isa = PBXBuildFile; + settings = { + }; + }; + 3FB2BAA10545EA80002B9691 = { + fileRef = 3FB2BA840545EA80002B9691; + isa = PBXBuildFile; + settings = { + }; + }; + 3FB2BAA20545EA80002B9691 = { + fileRef = 3FB2BA850545EA80002B9691; + isa = PBXBuildFile; + settings = { + }; + }; + 3FB2BAA30545EA80002B9691 = { + fileRef = 3FB2BA860545EA80002B9691; + isa = PBXBuildFile; + settings = { + }; + }; + 3FB2BAA40545EA80002B9691 = { + fileRef = 3FB2BA870545EA80002B9691; + isa = PBXBuildFile; + settings = { + }; + }; + 3FB2BAA50545EA80002B9691 = { + fileRef = 3FB2BA880545EA80002B9691; + isa = PBXBuildFile; + settings = { + }; + }; + 3FB2BAA60545EA80002B9691 = { + fileRef = 3FB2BA890545EA80002B9691; + isa = PBXBuildFile; + settings = { + }; + }; + 3FB2BAA70545EA80002B9691 = { + fileRef = 3FB2BA8A0545EA80002B9691; + isa = PBXBuildFile; + settings = { + }; + }; + 3FB2BAA80545EA80002B9691 = { + fileRef = 3FB2BA8B0545EA80002B9691; + isa = PBXBuildFile; + settings = { + }; + }; + 3FB2BAA90545EA80002B9691 = { + fileRef = 3FB2BA8C0545EA80002B9691; + isa = PBXBuildFile; + settings = { + }; + }; + 3FB2BAAA0545EA80002B9691 = { + fileRef = 3FB2BA8D0545EA80002B9691; + isa = PBXBuildFile; + settings = { + }; + }; + 3FB2BAAB0545EA80002B9691 = { + fileRef = 3FB2BA8E0545EA80002B9691; + isa = PBXBuildFile; + settings = { + }; + }; + 3FB2BAAC0545EA80002B9691 = { + fileRef = 3FB2BA8F0545EA80002B9691; + isa = PBXBuildFile; + settings = { + }; + }; + 3FB2BAAD0545EA80002B9691 = { + fileRef = 3FB2BA900545EA80002B9691; + isa = PBXBuildFile; + settings = { + }; + }; + 3FB2BAAE0545EA80002B9691 = { + fileRef = 3FB2BA910545EA80002B9691; + isa = PBXBuildFile; + settings = { + }; + }; + 3FB2BAAF0545EA80002B9691 = { + fileRef = 3FB2BA920545EA80002B9691; + isa = PBXBuildFile; + settings = { + }; + }; + 3FB2BAB00545EA80002B9691 = { + fileRef = 3FB2BA930545EA80002B9691; + isa = PBXBuildFile; + settings = { + }; + }; + 3FB2BAB10545EA80002B9691 = { + fileRef = 3FB2BA940545EA80002B9691; + isa = PBXBuildFile; + settings = { + }; + }; + 3FB2BAB20545EA80002B9691 = { + fileRef = 3FB2BA950545EA80002B9691; + isa = PBXBuildFile; + settings = { + }; + }; + 3FB2BAB30545EA80002B9691 = { + fileRef = 3FB2BA960545EA80002B9691; + isa = PBXBuildFile; + settings = { + }; + }; + 3FB2BAB40545EA80002B9691 = { + fileRef = 3FB2BA970545EA80002B9691; + isa = PBXBuildFile; + settings = { + }; + }; + 3FB2BAB50545EA80002B9691 = { + fileRef = 3FB2BA980545EA80002B9691; + isa = PBXBuildFile; + settings = { + }; + }; + 3FB2BAB60545EA80002B9691 = { + fileRef = 3FB2BA990545EA80002B9691; + isa = PBXBuildFile; + settings = { + }; + }; + 3FB2BAB70545EA80002B9691 = { + fileRef = 3FB2BA7D0545EA80002B9691; + isa = PBXBuildFile; + settings = { + }; + }; + 3FB2BAB80545EA80002B9691 = { + fileRef = 3FB2BA7E0545EA80002B9691; + isa = PBXBuildFile; + settings = { + }; + }; + 3FB2BAB90545EA80002B9691 = { + fileRef = 3FB2BA7F0545EA80002B9691; + isa = PBXBuildFile; + settings = { + }; + }; + 3FB2BABA0545EA80002B9691 = { + fileRef = 3FB2BA800545EA80002B9691; + isa = PBXBuildFile; + settings = { + }; + }; + 3FB2BABB0545EA80002B9691 = { + fileRef = 3FB2BA810545EA80002B9691; + isa = PBXBuildFile; + settings = { + }; + }; + 3FB2BABC0545EA80002B9691 = { + fileRef = 3FB2BA820545EA80002B9691; + isa = PBXBuildFile; + settings = { + }; + }; + 3FB2BABD0545EA80002B9691 = { + fileRef = 3FB2BA830545EA80002B9691; + isa = PBXBuildFile; + settings = { + }; + }; + 3FB2BABE0545EA80002B9691 = { + fileRef = 3FB2BA840545EA80002B9691; + isa = PBXBuildFile; + settings = { + }; + }; + 3FB2BABF0545EA80002B9691 = { + fileRef = 3FB2BA850545EA80002B9691; + isa = PBXBuildFile; + settings = { + }; + }; + 3FB2BAC00545EA80002B9691 = { + fileRef = 3FB2BA860545EA80002B9691; + isa = PBXBuildFile; + settings = { + }; + }; + 3FB2BAC10545EA80002B9691 = { + fileRef = 3FB2BA870545EA80002B9691; + isa = PBXBuildFile; + settings = { + }; + }; + 3FB2BAC20545EA80002B9691 = { + fileRef = 3FB2BA880545EA80002B9691; + isa = PBXBuildFile; + settings = { + }; + }; + 3FB2BAC30545EA80002B9691 = { + fileRef = 3FB2BA890545EA80002B9691; + isa = PBXBuildFile; + settings = { + }; + }; + 3FB2BAC40545EA80002B9691 = { + fileRef = 3FB2BA8A0545EA80002B9691; + isa = PBXBuildFile; + settings = { + }; + }; + 3FB2BAC50545EA80002B9691 = { + fileRef = 3FB2BA8B0545EA80002B9691; + isa = PBXBuildFile; + settings = { + }; + }; + 3FB2BAC60545EA80002B9691 = { + fileRef = 3FB2BA8C0545EA80002B9691; + isa = PBXBuildFile; + settings = { + }; + }; + 3FB2BAC70545EA80002B9691 = { + fileRef = 3FB2BA8D0545EA80002B9691; + isa = PBXBuildFile; + settings = { + }; + }; + 3FB2BAC80545EA80002B9691 = { + fileRef = 3FB2BA8E0545EA80002B9691; + isa = PBXBuildFile; + settings = { + }; + }; + 3FB2BAC90545EA80002B9691 = { + fileRef = 3FB2BA8F0545EA80002B9691; + isa = PBXBuildFile; + settings = { + }; + }; + 3FB2BACA0545EA80002B9691 = { + fileRef = 3FB2BA900545EA80002B9691; + isa = PBXBuildFile; + settings = { + }; + }; + 3FB2BACB0545EA80002B9691 = { + fileRef = 3FB2BA910545EA80002B9691; + isa = PBXBuildFile; + settings = { + }; + }; + 3FB2BACC0545EA80002B9691 = { + fileRef = 3FB2BA920545EA80002B9691; + isa = PBXBuildFile; + settings = { + }; + }; + 3FB2BACD0545EA80002B9691 = { + fileRef = 3FB2BA930545EA80002B9691; + isa = PBXBuildFile; + settings = { + }; + }; + 3FB2BACE0545EA80002B9691 = { + fileRef = 3FB2BA940545EA80002B9691; + isa = PBXBuildFile; + settings = { + }; + }; + 3FB2BACF0545EA80002B9691 = { + fileRef = 3FB2BA950545EA80002B9691; + isa = PBXBuildFile; + settings = { + }; + }; + 3FB2BAD00545EA80002B9691 = { + fileRef = 3FB2BA960545EA80002B9691; + isa = PBXBuildFile; + settings = { + }; + }; + 3FB2BAD10545EA80002B9691 = { + fileRef = 3FB2BA970545EA80002B9691; + isa = PBXBuildFile; + settings = { + }; + }; + 3FB2BAD20545EA80002B9691 = { + fileRef = 3FB2BA980545EA80002B9691; + isa = PBXBuildFile; + settings = { + }; + }; + 3FB2BAD30545EA80002B9691 = { + fileRef = 3FB2BA990545EA80002B9691; + isa = PBXBuildFile; + settings = { + }; + }; + 3FB2BAD40545EAA2002B9691 = { + buildActionMask = 2147483647; + files = ( + 3FB2BAE10545EB30002B9691, + ); + isa = PBXHeadersBuildPhase; + runOnlyForDeploymentPostprocessing = 0; + }; + 3FB2BAD50545EAA2002B9691 = { + buildActionMask = 2147483647; + files = ( + ); + isa = PBXResourcesBuildPhase; + runOnlyForDeploymentPostprocessing = 0; + }; + 3FB2BAD60545EAA2002B9691 = { + buildActionMask = 2147483647; + files = ( + 3FB2BAE20545EB30002B9691, + ); + isa = PBXSourcesBuildPhase; + runOnlyForDeploymentPostprocessing = 0; + }; + 3FB2BAD70545EAA2002B9691 = { + buildActionMask = 2147483647; + files = ( + 3FB2BAE40545EB52002B9691, + 3FB2BB0F05460820002B9691, + ); + isa = PBXFrameworksBuildPhase; + runOnlyForDeploymentPostprocessing = 0; + }; + 3FB2BAD80545EAA2002B9691 = { + buildActionMask = 2147483647; + files = ( + ); + isa = PBXRezBuildPhase; + runOnlyForDeploymentPostprocessing = 0; + }; + 3FB2BAD90545EAA2002B9691 = { + buildPhases = ( + 3FB2BAD40545EAA2002B9691, + 3FB2BAD50545EAA2002B9691, + 3FB2BAD60545EAA2002B9691, + 3FB2BAD70545EAA2002B9691, + 3FB2BAD80545EAA2002B9691, + ); + buildSettings = { + OPTIMIZATION_CFLAGS = ""; + OTHER_CFLAGS = ""; + OTHER_LDFLAGS = ""; + OTHER_REZFLAGS = ""; + PRODUCT_NAME = AddressBookManager; + SECTORDER_FLAGS = ""; + WARNING_CFLAGS = "-Wmost -Wno-four-char-constants -Wno-unknown-pragmas"; + WRAPPER_EXTENSION = bundle; + }; + dependencies = ( + ); + isa = PBXBundleTarget; + name = AddressBookManager; + productInstallPath = "$(USER_LIBRARY_DIR)/Bundles"; + productName = AddressBookManager; + productReference = 3FB2BADA0545EAA2002B9691; + productSettingsXML = " + + + + CFBundleDevelopmentRegion + English + CFBundleExecutable + AddressBookManager + CFBundleGetInfoString + + CFBundleIconFile + + CFBundleIdentifier + org.mozilla.camino.AddressBookManager + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + AddressBookManager + CFBundlePackageType + BNDL + CFBundleShortVersionString + + CFBundleSignature + ???? + CFBundleVersion + 0.0.1d1 + + +"; + }; + 3FB2BADA0545EAA2002B9691 = { + isa = PBXBundleReference; + path = AddressBookManager.bundle; + refType = 3; + }; + 3FB2BADB0545EAA2002B9691 = { + isa = PBXTargetDependency; + target = 3FB2BAD90545EAA2002B9691; + }; + 3FB2BADC0545EAA2002B9691 = { + fileRef = 3FB2BADA0545EAA2002B9691; + isa = PBXBuildFile; + settings = { + }; + }; + 3FB2BADD0545EAA2002B9691 = { + isa = PBXTargetDependency; + target = 3FB2BAD90545EAA2002B9691; + }; + 3FB2BADE0545EAA2002B9691 = { + fileRef = 3FB2BADA0545EAA2002B9691; + isa = PBXBuildFile; + settings = { + }; + }; + 3FB2BADF0545EB30002B9691 = { + fileEncoding = 30; + isa = PBXFileReference; + name = AddressBookManager.h; + path = src/bookmarks/AddressBookManager.h; + refType = 2; + }; + 3FB2BAE00545EB30002B9691 = { + fileEncoding = 30; + isa = PBXFileReference; + name = AddressBookManager.m; + path = src/bookmarks/AddressBookManager.m; + refType = 2; + }; + 3FB2BAE10545EB30002B9691 = { + fileRef = 3FB2BADF0545EB30002B9691; + isa = PBXBuildFile; + settings = { + }; + }; + 3FB2BAE20545EB30002B9691 = { + fileRef = 3FB2BAE00545EB30002B9691; + isa = PBXBuildFile; + settings = { + }; + }; + 3FB2BAE30545EB52002B9691 = { + isa = PBXFrameworkReference; + name = AddressBook.framework; + path = /System/Library/Frameworks/AddressBook.framework; + refType = 0; + }; + 3FB2BAE40545EB52002B9691 = { + fileRef = 3FB2BAE30545EB52002B9691; + isa = PBXBuildFile; + settings = { + }; + }; + 3FB2BAE50545EBAB002B9691 = { + children = ( + 3FB2BAE60545EBAB002B9691, + ); + isa = PBXVariantGroup; + name = BookmarkImportDlg.nib; + path = ""; + refType = 2; + }; + 3FB2BAE60545EBAB002B9691 = { + isa = PBXFileReference; + name = English; + path = resources/localized/English.lproj/BookmarkImportDlg.nib; + refType = 4; + }; + 3FB2BAE70545EBAB002B9691 = { + fileRef = 3FB2BAE50545EBAB002B9691; + isa = PBXBuildFile; + settings = { + }; + }; + 3FB2BAE80545EBAB002B9691 = { + fileRef = 3FB2BAE50545EBAB002B9691; + isa = PBXBuildFile; + settings = { + }; + }; + 3FB2BAE90545EBF2002B9691 = { + isa = PBXFileReference; + name = addressbook_icon.tif; + path = resources/images/toolbar/addressbook_icon.tif; + refType = 2; + }; + 3FB2BAEA0545EBF2002B9691 = { + isa = PBXFileReference; + name = bookmarkmenu_icon.tif; + path = resources/images/toolbar/bookmarkmenu_icon.tif; + refType = 2; + }; + 3FB2BAEB0545EBF2002B9691 = { + isa = PBXFileReference; + name = bookmarktoolbar_icon.tif; + path = resources/images/toolbar/bookmarktoolbar_icon.tif; + refType = 2; + }; + 3FB2BAEC0545EBF2002B9691 = { + isa = PBXFileReference; + name = brokenbookmark_icon.tif; + path = resources/images/toolbar/brokenbookmark_icon.tif; + refType = 2; + }; + 3FB2BAED0545EBF2002B9691 = { + isa = PBXFileReference; + name = delete_on.tif; + path = resources/images/toolbar/delete_on.tif; + refType = 2; + }; + 3FB2BAEE0545EBF2002B9691 = { + isa = PBXFileReference; + name = info_on.tif; + path = resources/images/toolbar/info_on.tif; + refType = 2; + }; + 3FB2BAEF0545EBF2002B9691 = { + isa = PBXFileReference; + name = rendezvous_icon.tif; + path = resources/images/toolbar/rendezvous_icon.tif; + refType = 2; + }; + 3FB2BAF00545EBF2002B9691 = { + isa = PBXFileReference; + name = separator_on.tif; + path = resources/images/toolbar/separator_on.tif; + refType = 2; + }; + 3FB2BAF10545EBF2002B9691 = { + isa = PBXFileReference; + name = top10_icon.tif; + path = resources/images/toolbar/top10_icon.tif; + refType = 2; + }; + 3FB2BAF20545EBF2002B9691 = { + fileRef = 3FB2BAE90545EBF2002B9691; + isa = PBXBuildFile; + settings = { + }; + }; + 3FB2BAF30545EBF2002B9691 = { + fileRef = 3FB2BAEA0545EBF2002B9691; + isa = PBXBuildFile; + settings = { + }; + }; + 3FB2BAF40545EBF2002B9691 = { + fileRef = 3FB2BAEB0545EBF2002B9691; + isa = PBXBuildFile; + settings = { + }; + }; + 3FB2BAF50545EBF2002B9691 = { + fileRef = 3FB2BAEC0545EBF2002B9691; + isa = PBXBuildFile; + settings = { + }; + }; + 3FB2BAF60545EBF2002B9691 = { + fileRef = 3FB2BAED0545EBF2002B9691; + isa = PBXBuildFile; + settings = { + }; + }; + 3FB2BAF70545EBF2002B9691 = { + fileRef = 3FB2BAEE0545EBF2002B9691; + isa = PBXBuildFile; + settings = { + }; + }; + 3FB2BAF80545EBF2002B9691 = { + fileRef = 3FB2BAEF0545EBF2002B9691; + isa = PBXBuildFile; + settings = { + }; + }; + 3FB2BAF90545EBF2002B9691 = { + fileRef = 3FB2BAF00545EBF2002B9691; + isa = PBXBuildFile; + settings = { + }; + }; + 3FB2BAFA0545EBF2002B9691 = { + fileRef = 3FB2BAF10545EBF2002B9691; + isa = PBXBuildFile; + settings = { + }; + }; + 3FB2BAFB0545EBF2002B9691 = { + fileRef = 3FB2BAE90545EBF2002B9691; + isa = PBXBuildFile; + settings = { + }; + }; + 3FB2BAFC0545EBF2002B9691 = { + fileRef = 3FB2BAEA0545EBF2002B9691; + isa = PBXBuildFile; + settings = { + }; + }; + 3FB2BAFD0545EBF2002B9691 = { + fileRef = 3FB2BAEB0545EBF2002B9691; + isa = PBXBuildFile; + settings = { + }; + }; + 3FB2BAFE0545EBF2002B9691 = { + fileRef = 3FB2BAEC0545EBF2002B9691; + isa = PBXBuildFile; + settings = { + }; + }; + 3FB2BAFF0545EBF2002B9691 = { + fileRef = 3FB2BAED0545EBF2002B9691; + isa = PBXBuildFile; + settings = { + }; + }; + 3FB2BB000545EBF2002B9691 = { + fileRef = 3FB2BAEE0545EBF2002B9691; + isa = PBXBuildFile; + settings = { + }; + }; + 3FB2BB010545EBF2002B9691 = { + fileRef = 3FB2BAEF0545EBF2002B9691; + isa = PBXBuildFile; + settings = { + }; + }; + 3FB2BB020545EBF2002B9691 = { + fileRef = 3FB2BAF00545EBF2002B9691; + isa = PBXBuildFile; + settings = { + }; + }; + 3FB2BB030545EBF2002B9691 = { + fileRef = 3FB2BAF10545EBF2002B9691; + isa = PBXBuildFile; + settings = { + }; + }; + 3FB2BB0F05460820002B9691 = { + fileRef = 29B97325FDCFA39411CA2CEA; + isa = PBXBuildFile; + settings = { + }; + }; //3F0 //3F1 //3F2 @@ -1913,32 +2788,6 @@ settings = { }; }; - F507BA480213AD5F01D93544 = { - fileEncoding = 30; - isa = PBXFileReference; - name = BookmarksService.h; - path = src/bookmarks/BookmarksService.h; - refType = 2; - }; - F507BA4A0213AD5F01D93544 = { - fileRef = F507BA480213AD5F01D93544; - isa = PBXBuildFile; - settings = { - }; - }; - F507BA4C0213AFF701D93544 = { - fileEncoding = 30; - isa = PBXFileReference; - name = BookmarksService.mm; - path = src/bookmarks/BookmarksService.mm; - refType = 2; - }; - F507BA4D0213AFF701D93544 = { - fileRef = F507BA4C0213AFF701D93544; - isa = PBXBuildFile; - settings = { - }; - }; F507BA520213C22C01D93544 = { buildActionMask = 2147483647; dstPath = defaults/profile; @@ -1988,6 +2837,15 @@ 3003B89E044514B600B85BF3, 3003B89F044514B600B85BF3, 3003B8A0044514B600B85BF3, + 3FB2BAE90545EBF2002B9691, + 3FB2BAEA0545EBF2002B9691, + 3FB2BAEB0545EBF2002B9691, + 3FB2BAEC0545EBF2002B9691, + 3FB2BAED0545EBF2002B9691, + 3FB2BAEE0545EBF2002B9691, + 3FB2BAEF0545EBF2002B9691, + 3FB2BAF00545EBF2002B9691, + 3FB2BAF10545EBF2002B9691, ); isa = PBXGroup; name = images; @@ -2345,6 +3203,8 @@ F52D5CD8027D3D5001A80166, F5D33CBD02EF61A901A967F3, F58DB2D30381FD3301A9666E, + 3FB2BA980545EA80002B9691, + 3FB2BA990545EA80002B9691, ); isa = PBXGroup; name = Application; @@ -2986,13 +3846,6 @@ F52D5CD3027A88C601A80166, F52D5CCF027A887001A80166, F53E012902AEE91C01A967F3, - F57074B9026BFD0101A80166, - 3F2CF8D1042A8B30005FD42F, - F5A3669302CCFAF601DC3354, - F53D36BD037843C201A80166, - 2EEC3E61028138714B000102, - F507BA480213AD5F01D93544, - F57074B5026BA85F01A80166, F53C1CD6032FF3B301A96654, F5BF71450231B8BC010001CA, F5B950BC030C83B601A96654, @@ -4080,26 +4933,6 @@ settings = { }; }; - F53D36BA037843B801A80166 = { - fileEncoding = 30; - isa = PBXFileReference; - name = BookmarksMenu.mm; - path = src/bookmarks/BookmarksMenu.mm; - refType = 2; - }; - F53D36BD037843C201A80166 = { - fileEncoding = 30; - isa = PBXFileReference; - name = BookmarksMenu.h; - path = src/bookmarks/BookmarksMenu.h; - refType = 2; - }; - F53D36BE037843C201A80166 = { - fileRef = F53D36BD037843C201A80166; - isa = PBXBuildFile; - settings = { - }; - }; F53E012902AEE91C01A967F3 = { fileEncoding = 30; isa = PBXFileReference; @@ -5080,44 +5913,6 @@ settings = { }; }; - F55EBC9A0383665201A80166 = { - fileEncoding = 30; - isa = PBXFileReference; - name = BookmarksExport.h; - path = src/bookmarks/BookmarksExport.h; - refType = 2; - }; - F55EBC9B0383665201A80166 = { - fileEncoding = 30; - isa = PBXFileReference; - name = BookmarksExport.mm; - path = src/bookmarks/BookmarksExport.mm; - refType = 2; - }; - F55EBC9C0383665201A80166 = { - fileRef = F55EBC9A0383665201A80166; - isa = PBXBuildFile; - settings = { - }; - }; - F55EBC9D0383665201A80166 = { - fileRef = F55EBC9B0383665201A80166; - isa = PBXBuildFile; - settings = { - }; - }; - F55EBC9E0383665201A80166 = { - fileRef = F55EBC9A0383665201A80166; - isa = PBXBuildFile; - settings = { - }; - }; - F55EBC9F0383665201A80166 = { - fileRef = F55EBC9B0383665201A80166; - isa = PBXBuildFile; - settings = { - }; - }; F5607CB5023944AD01A967DF = { fileEncoding = 30; isa = PBXFileReference; @@ -5817,58 +6612,6 @@ settings = { }; }; - F57074B5026BA85F01A80166 = { - fileEncoding = 30; - isa = PBXFileReference; - name = BookmarksToolbar.h; - path = src/bookmarks/BookmarksToolbar.h; - refType = 2; - }; - F57074B6026BA85F01A80166 = { - fileEncoding = 30; - isa = PBXFileReference; - name = BookmarksToolbar.mm; - path = src/bookmarks/BookmarksToolbar.mm; - refType = 2; - }; - F57074B7026BA85F01A80166 = { - fileRef = F57074B5026BA85F01A80166; - isa = PBXBuildFile; - settings = { - }; - }; - F57074B8026BA85F01A80166 = { - fileRef = F57074B6026BA85F01A80166; - isa = PBXBuildFile; - settings = { - }; - }; - F57074B9026BFD0101A80166 = { - fileEncoding = 30; - isa = PBXFileReference; - name = BookmarksButton.h; - path = src/bookmarks/BookmarksButton.h; - refType = 2; - }; - F57074BA026BFD0101A80166 = { - fileEncoding = 30; - isa = PBXFileReference; - name = BookmarksButton.mm; - path = src/bookmarks/BookmarksButton.mm; - refType = 2; - }; - F57074BB026BFD0201A80166 = { - fileRef = F57074B9026BFD0101A80166; - isa = PBXBuildFile; - settings = { - }; - }; - F57074BC026BFD0201A80166 = { - fileRef = F57074BA026BFD0101A80166; - isa = PBXBuildFile; - settings = { - }; - }; F57074BD026D80DF01A80166 = { fileEncoding = 30; isa = PBXFileReference; @@ -6590,6 +7333,7 @@ F507A84D03116E7401026D5D, F5F7EF0D03C1F29C01BDD337, F507A85003116E7901026D5D, + 3FB2BAE50545EBAB002B9691, ); isa = PBXGroup; name = Nibs; @@ -6802,37 +7546,39 @@ settings = { }; }; - F5A3669302CCFAF601DC3354 = { - fileEncoding = 30; - isa = PBXFileReference; - name = BookmarksDataSource.h; - path = src/bookmarks/BookmarksDataSource.h; - refType = 2; - }; F5A3669402CCFAF601DC3354 = { children = ( - F55EBC9A0383665201A80166, - F55EBC9B0383665201A80166, - F507BA4C0213AFF701D93544, - F5A3669502CCFAF601DC3354, - F53D36BA037843B801A80166, - F57074B6026BA85F01A80166, - F57074BA026BFD0101A80166, + 3FB2BADF0545EB30002B9691, + 3FB2BAE00545EB30002B9691, + 3FB2BA940545EA80002B9691, + 3FB2BA950545EA80002B9691, F53E012C02AEE93601A967F3, - 2EEC3E62028138714B000102, - 3F2CF8D4042A8C47005FD42F, + 3FB2BA7D0545EA80002B9691, + 3FB2BA7E0545EA80002B9691, + 3FB2BA7F0545EA80002B9691, + 3FB2BA800545EA80002B9691, + 3FB2BA810545EA80002B9691, + 3FB2BA820545EA80002B9691, + 3FB2BA830545EA80002B9691, + 3FB2BA840545EA80002B9691, + 3FB2BA850545EA80002B9691, + 3FB2BA860545EA80002B9691, + 3FB2BA870545EA80002B9691, + 3FB2BA880545EA80002B9691, + 3FB2BA890545EA80002B9691, + 3FB2BA8A0545EA80002B9691, + 3FB2BA8B0545EA80002B9691, + 3FB2BA8C0545EA80002B9691, + 3FB2BA8D0545EA80002B9691, + 3FB2BA8E0545EA80002B9691, + 3FB2BA8F0545EA80002B9691, + 3FB2BA900545EA80002B9691, + 3FB2BA910545EA80002B9691, ); isa = PBXGroup; name = Bookmarks; refType = 4; }; - F5A3669502CCFAF601DC3354 = { - fileEncoding = 30; - isa = PBXFileReference; - name = BookmarksDataSource.mm; - path = src/bookmarks/BookmarksDataSource.mm; - refType = 2; - }; F5A3669602CCFAF601DC3354 = { children = ( F52F87CD027D75C301A80165, @@ -6852,36 +7598,12 @@ name = Autocomplete; refType = 4; }; - F5A3669802CCFAF601DC3354 = { - fileRef = F5A3669302CCFAF601DC3354; - isa = PBXBuildFile; - settings = { - }; - }; - F5A3669902CCFAF601DC3354 = { - fileRef = F5A3669502CCFAF601DC3354; - isa = PBXBuildFile; - settings = { - }; - }; - F5A3669A02CCFB7A01DC3354 = { - fileRef = F5A3669302CCFAF601DC3354; - isa = PBXBuildFile; - settings = { - }; - }; F5A3669B02CCFB7A01DC3354 = { fileRef = F632AF8402B9AEBB01000103; isa = PBXBuildFile; settings = { }; }; - F5A3669C02CCFB7A01DC3354 = { - fileRef = F5A3669502CCFAF601DC3354; - isa = PBXBuildFile; - settings = { - }; - }; F5A6185202E5E3A6018E99BF = { fileRef = F5A8CE4602DFF039013CA8EC; isa = PBXBuildFile; @@ -7450,24 +8172,6 @@ settings = { }; }; - F5B63090037A3A7501A80166 = { - fileRef = F53D36BA037843B801A80166; - isa = PBXBuildFile; - settings = { - }; - }; - F5B63092037A3AAC01A80166 = { - fileRef = F53D36BD037843C201A80166; - isa = PBXBuildFile; - settings = { - }; - }; - F5B63094037A41E301A80166 = { - fileRef = F53D36BA037843B801A80166; - isa = PBXBuildFile; - settings = { - }; - }; F5B8CC460209D1B601A967DF = { isa = PBXFileReference; name = libstring_obsolete_s.a; @@ -7476,6 +8180,8 @@ }; F5B950BA030C833301A96654 = { children = ( + 3FB2BA960545EA80002B9691, + 3FB2BA920545EA80002B9691, 3F2CF8CB042A88B7005FD42F, F5FDF166031AF47301DE816D, F5D98EBC031AC38601A96654, @@ -7495,6 +8201,8 @@ }; F5B950BB030C833301A96654 = { children = ( + 3FB2BA970545EA80002B9691, + 3FB2BA930545EA80002B9691, F5D98EB9031AC37801A96654, F558099E02F22168015DF512, F549ACDF0302DE6001026D5D, @@ -7604,6 +8312,7 @@ F56F24A102AC72EF01A967F3, F51704F4034A31C601026D5D, F5170514034A396701026D5D, + 3FB2BADD0545EAA2002B9691, ); isa = PBXApplicationTarget; name = CaminoStatic; @@ -7808,7 +8517,6 @@ F5BAAB2502AC4A2E01A967F3, F5BAAB2602AC4A2E01A967F3, F5BAAB2702AC4A2E01A967F3, - F5BAAB2802AC4A2E01A967F3, F5BAAB2902AC4A2E01A967F3, F5BAAB2A02AC4A2E01A967F3, F5BAAB2B02AC4A2E01A967F3, @@ -7819,8 +8527,6 @@ F5BAAB3002AC4A2E01A967F3, F5BAAB3102AC4A2E01A967F3, F5BAAB3202AC4A2E01A967F3, - F5BAAB3302AC4A2E01A967F3, - F5BAAB3402AC4A2E01A967F3, F5BAAB3502AC4A2E01A967F3, F5BAAB3602AC4A2E01A967F3, F5BAAB3702AC4A2E01A967F3, @@ -7832,11 +8538,9 @@ F5BAAB3D02AC4A2E01A967F3, F5BAAB3E02AC4A2E01A967F3, F5BAAB3F02AC4A2E01A967F3, - F5BAAB4002AC4A2E01A967F3, F5BAAB4102AC4A2E01A967F3, F53E012B02AEE91D01A967F3, F59236C402C89ACA0100012B, - F5A3669A02CCFB7A01DC3354, F50D9DE702ECC36201BB4219, F50D9DE802ECC36201BB4219, F5E37B0C02EE959601A967F3, @@ -7857,9 +8561,7 @@ F5F94B940332532801026D5D, F5B34B06034A50C901A80166, F529788C0371820B01026DCE, - F5B63092037A3AAC01A80166, F58DB2D60381FD3301A9666E, - F55EBC9E0383665201A80166, F57BED9C03A1824801A9666E, F57BEDA203A1825001A9666E, F5C8D55403A2A42401A8016F, @@ -7868,10 +8570,24 @@ F5F415CD03B9223E01A80166, F527C90603BCD43601A80166, 3F2CF8CF042A88B7005FD42F, - 3F2CF8D3042A8B30005FD42F, 3003B8990445144100B85BF3, 3003B89A0445144100B85BF3, 3003B89B0445144100B85BF3, + 3FB2BAB70545EA80002B9691, + 3FB2BAB90545EA80002B9691, + 3FB2BABB0545EA80002B9691, + 3FB2BABD0545EA80002B9691, + 3FB2BABF0545EA80002B9691, + 3FB2BAC10545EA80002B9691, + 3FB2BAC30545EA80002B9691, + 3FB2BAC50545EA80002B9691, + 3FB2BAC70545EA80002B9691, + 3FB2BAC80545EA80002B9691, + 3FB2BACA0545EA80002B9691, + 3FB2BACC0545EA80002B9691, + 3FB2BACE0545EA80002B9691, + 3FB2BAD00545EA80002B9691, + 3FB2BAD20545EA80002B9691, ); isa = PBXHeadersBuildPhase; runOnlyForDeploymentPostprocessing = 0; @@ -7962,6 +8678,17 @@ 3003B8AA044514B600B85BF3, 3003B8AE044514E300B85BF3, 3F8D26DE0471ED0F00A80005, + 3FB2BADE0545EAA2002B9691, + 3FB2BAE70545EBAB002B9691, + 3FB2BAF20545EBF2002B9691, + 3FB2BAF30545EBF2002B9691, + 3FB2BAF40545EBF2002B9691, + 3FB2BAF50545EBF2002B9691, + 3FB2BAF60545EBF2002B9691, + 3FB2BAF70545EBF2002B9691, + 3FB2BAF80545EBF2002B9691, + 3FB2BAF90545EBF2002B9691, + 3FB2BAFA0545EBF2002B9691, ); isa = PBXResourcesBuildPhase; runOnlyForDeploymentPostprocessing = 0; @@ -7978,7 +8705,6 @@ F5BAAB8002AC4A5301A967F3, F5BAAB8102AC4A5301A967F3, F5BAAB8202AC4A5301A967F3, - F5BAAB8302AC4A5301A967F3, F5BAAB8402AC4A5301A967F3, F5BAAB8502AC4A5301A967F3, F5BAAB8602AC4A5301A967F3, @@ -7987,8 +8713,6 @@ F5BAAB8902AC4A5301A967F3, F5BAAB8A02AC4A5301A967F3, F5BAAB8B02AC4A5301A967F3, - F5BAAB8C02AC4A5301A967F3, - F5BAAB8D02AC4A5301A967F3, F5BAAB8E02AC4A5301A967F3, F5BAAB8F02AC4A5301A967F3, F5BAAB9002AC4A5301A967F3, @@ -8000,13 +8724,11 @@ F5BAAB9602AC4A5301A967F3, F5BAAB9702AC4A5301A967F3, F5BAAB9802AC4A5301A967F3, - F5BAAB9902AC4A5301A967F3, F5BAAB9A02AC4A5301A967F3, F56F242102AC6D0401A967F3, F53E012E02AEE93701A967F3, F59236C502C89ACA0100012B, F5A3669B02CCFB7A01DC3354, - F5A3669C02CCFB7A01DC3354, F50D9DE902ECC36201BB4219, F5E37B1102EE95E201A967F3, F5E37B1202EE95E201A967F3, @@ -8026,19 +8748,30 @@ F5F94B950332532901026D5D, F5A30033034B8B1A01A96660, F529788D0371820B01026DCE, - F5B63090037A3A7501A80166, F58DB2D70381FD3301A9666E, - F55EBC9F0383665201A80166, F57BED9903A1824001A9666E, F57BEDA103A1825001A9666E, F583E3C103B8228701A80166, F5F415CE03B9223E01A80166, F527C90703BCD43601A80166, 3F2CF8D0042A88B7005FD42F, - 3F2CF8D6042A8C47005FD42F, 3003B8900445140000B85BF3, 3003B8910445140000B85BF3, 3003B8920445140000B85BF3, + 3FB2BAB80545EA80002B9691, + 3FB2BABA0545EA80002B9691, + 3FB2BABC0545EA80002B9691, + 3FB2BABE0545EA80002B9691, + 3FB2BAC00545EA80002B9691, + 3FB2BAC20545EA80002B9691, + 3FB2BAC40545EA80002B9691, + 3FB2BAC60545EA80002B9691, + 3FB2BAC90545EA80002B9691, + 3FB2BACB0545EA80002B9691, + 3FB2BACD0545EA80002B9691, + 3FB2BACF0545EA80002B9691, + 3FB2BAD10545EA80002B9691, + 3FB2BAD30545EA80002B9691, ); isa = PBXSourcesBuildPhase; runOnlyForDeploymentPostprocessing = 0; @@ -8144,12 +8877,6 @@ settings = { }; }; - F5BAAB2802AC4A2E01A967F3 = { - fileRef = F507BA480213AD5F01D93544; - isa = PBXBuildFile; - settings = { - }; - }; F5BAAB2902AC4A2E01A967F3 = { fileRef = F53F21EF022B7C77010001CA; isa = PBXBuildFile; @@ -8210,18 +8937,6 @@ settings = { }; }; - F5BAAB3302AC4A2E01A967F3 = { - fileRef = F57074B5026BA85F01A80166; - isa = PBXBuildFile; - settings = { - }; - }; - F5BAAB3402AC4A2E01A967F3 = { - fileRef = F57074B9026BFD0101A80166; - isa = PBXBuildFile; - settings = { - }; - }; F5BAAB3502AC4A2E01A967F3 = { fileRef = F57074BD026D80DF01A80166; isa = PBXBuildFile; @@ -8288,12 +9003,6 @@ settings = { }; }; - F5BAAB4002AC4A2E01A967F3 = { - fileRef = 2EEC3E61028138714B000102; - isa = PBXBuildFile; - settings = { - }; - }; F5BAAB4102AC4A2E01A967F3 = { fileRef = 2E748B72029A448D4B000102; isa = PBXBuildFile; @@ -8612,12 +9321,6 @@ settings = { }; }; - F5BAAB8302AC4A5301A967F3 = { - fileRef = F507BA4C0213AFF701D93544; - isa = PBXBuildFile; - settings = { - }; - }; F5BAAB8402AC4A5301A967F3 = { fileRef = F53F21F0022B7C77010001CA; isa = PBXBuildFile; @@ -8666,18 +9369,6 @@ settings = { }; }; - F5BAAB8C02AC4A5301A967F3 = { - fileRef = F57074B6026BA85F01A80166; - isa = PBXBuildFile; - settings = { - }; - }; - F5BAAB8D02AC4A5301A967F3 = { - fileRef = F57074BA026BFD0101A80166; - isa = PBXBuildFile; - settings = { - }; - }; F5BAAB8E02AC4A5301A967F3 = { fileRef = F57074BE026D80DF01A80166; isa = PBXBuildFile; @@ -8744,12 +9435,6 @@ settings = { }; }; - F5BAAB9902AC4A5301A967F3 = { - fileRef = 2EEC3E62028138714B000102; - isa = PBXBuildFile; - settings = { - }; - }; F5BAAB9A02AC4A5301A967F3 = { fileRef = 2E748B73029A448D4B000102; isa = PBXBuildFile; diff --git a/camino/resources/images/toolbar/addressbook_icon.tif b/camino/resources/images/toolbar/addressbook_icon.tif new file mode 100644 index 000000000000..6215ff0b9545 Binary files /dev/null and b/camino/resources/images/toolbar/addressbook_icon.tif differ diff --git a/camino/resources/images/toolbar/bookmarkmenu_icon.tif b/camino/resources/images/toolbar/bookmarkmenu_icon.tif new file mode 100644 index 000000000000..a91aec70e214 Binary files /dev/null and b/camino/resources/images/toolbar/bookmarkmenu_icon.tif differ diff --git a/camino/resources/images/toolbar/bookmarktoolbar_icon.tif b/camino/resources/images/toolbar/bookmarktoolbar_icon.tif new file mode 100644 index 000000000000..b4991303f639 Binary files /dev/null and b/camino/resources/images/toolbar/bookmarktoolbar_icon.tif differ diff --git a/camino/resources/images/toolbar/brokenbookmark_icon.tif b/camino/resources/images/toolbar/brokenbookmark_icon.tif new file mode 100644 index 000000000000..7249edf4121a Binary files /dev/null and b/camino/resources/images/toolbar/brokenbookmark_icon.tif differ diff --git a/camino/resources/images/toolbar/delete_on.tif b/camino/resources/images/toolbar/delete_on.tif index 83bf42c031c5..aa0e99837528 100644 Binary files a/camino/resources/images/toolbar/delete_on.tif and b/camino/resources/images/toolbar/delete_on.tif differ diff --git a/camino/resources/images/toolbar/info_on.tif b/camino/resources/images/toolbar/info_on.tif new file mode 100755 index 000000000000..0d8d40845fac Binary files /dev/null and b/camino/resources/images/toolbar/info_on.tif differ diff --git a/camino/resources/images/toolbar/rendezvous_icon.tif b/camino/resources/images/toolbar/rendezvous_icon.tif new file mode 100644 index 000000000000..d7771fd1063c Binary files /dev/null and b/camino/resources/images/toolbar/rendezvous_icon.tif differ diff --git a/camino/resources/images/toolbar/separator_on.tif b/camino/resources/images/toolbar/separator_on.tif index 89ea06544126..98979ad7b79a 100644 Binary files a/camino/resources/images/toolbar/separator_on.tif and b/camino/resources/images/toolbar/separator_on.tif differ diff --git a/camino/resources/images/toolbar/top10_icon.tif b/camino/resources/images/toolbar/top10_icon.tif new file mode 100644 index 000000000000..7bdc4f45fc02 Binary files /dev/null and b/camino/resources/images/toolbar/top10_icon.tif differ diff --git a/camino/resources/localized/English.lproj/BookmarkImportDlg.nib/classes.nib b/camino/resources/localized/English.lproj/BookmarkImportDlg.nib/classes.nib new file mode 100644 index 000000000000..b2a48fa129cc --- /dev/null +++ b/camino/resources/localized/English.lproj/BookmarkImportDlg.nib/classes.nib @@ -0,0 +1,17 @@ +{ + IBClasses = ( + { + ACTIONS = {cancel = id; import = id; loadOpenPanel = id; }; + CLASS = BookmarkImportDlgController; + LANGUAGE = ObjC; + OUTLETS = { + mBrowserListButton = NSPopUpButton; + mCancelButton = NSButton; + mImportButton = NSButton; + }; + SUPERCLASS = NSWindowController; + }, + {CLASS = FirstResponder; LANGUAGE = ObjC; SUPERCLASS = NSObject; } + ); + IBVersion = 1; +} \ No newline at end of file diff --git a/camino/resources/localized/English.lproj/BookmarkImportDlg.nib/info.nib b/camino/resources/localized/English.lproj/BookmarkImportDlg.nib/info.nib new file mode 100644 index 000000000000..5526f8bd3c9a --- /dev/null +++ b/camino/resources/localized/English.lproj/BookmarkImportDlg.nib/info.nib @@ -0,0 +1,20 @@ + + + + + IBDocumentLocation + 40 351 356 240 0 0 1152 746 + IBFramework Version + 291.0 + IBLockedObjects + + 5 + + IBOpenObjects + + 5 + + IBSystem Version + 6L60 + + diff --git a/camino/resources/localized/English.lproj/BookmarkImportDlg.nib/objects.nib b/camino/resources/localized/English.lproj/BookmarkImportDlg.nib/objects.nib new file mode 100755 index 000000000000..d8aca6e63b62 Binary files /dev/null and b/camino/resources/localized/English.lproj/BookmarkImportDlg.nib/objects.nib differ diff --git a/camino/resources/localized/English.lproj/BookmarkInfoPanel.nib/classes.nib b/camino/resources/localized/English.lproj/BookmarkInfoPanel.nib/classes.nib index b469623ee944..aeebfeb75471 100644 --- a/camino/resources/localized/English.lproj/BookmarkInfoPanel.nib/classes.nib +++ b/camino/resources/localized/English.lproj/BookmarkInfoPanel.nib/classes.nib @@ -1,25 +1,32 @@ { IBClasses = ( { - ACTIONS = {dockMenuCheckboxClicked = id; tabGroupCheckboxClicked = id; }; + ACTIONS = { + clearVisitCount = id; + dockMenuCheckboxClicked = id; + tabGroupCheckboxClicked = id; + }; CLASS = BookmarkInfoController; LANGUAGE = ObjC; OUTLETS = { - mDescriptionField = NSTextField; - mDescriptionLabel = NSTextField; + mBookmarkDescField = NSTextField; + mBookmarkKeywordField = NSTextField; + mBookmarkLocationField = NSTextField; + mBookmarkNameField = NSTextField; + mClearNumberVisitsButton = NSButton; mDockMenuCheckbox = NSButton; - mKeywordField = NSTextField; - mKeywordLabel = NSTextField; - mLocationField = NSTextField; - mLocationLabel = NSTextField; - mNameField = NSTextField; - mNameLabel = NSTextField; + mFolderDescField = NSTextField; + mFolderKeywordField = NSTextField; + mFolderKeywordLabel = NSTextField; + mFolderNameField = NSTextField; + mLastVisitField = NSTextField; + mNumberVisitsField = NSTextField; + mStatusField = NSTextField; + mTabView = NSTabView; mTabgroupCheckbox = NSButton; - mVariableFieldsContainer = NSView; }; SUPERCLASS = NSWindowController; }, - {CLASS = BookmarkItem; LANGUAGE = ObjC; SUPERCLASS = NSObject; }, {CLASS = FirstResponder; LANGUAGE = ObjC; SUPERCLASS = NSObject; } ); IBVersion = 1; diff --git a/camino/resources/localized/English.lproj/BookmarkInfoPanel.nib/info.nib b/camino/resources/localized/English.lproj/BookmarkInfoPanel.nib/info.nib index d1cd7729ab63..46a532116aaa 100644 --- a/camino/resources/localized/English.lproj/BookmarkInfoPanel.nib/info.nib +++ b/camino/resources/localized/English.lproj/BookmarkInfoPanel.nib/info.nib @@ -3,10 +3,51 @@ IBDocumentLocation - 97 65 366 258 0 0 1600 1002 + 9 7 466 409 0 0 1280 1002 IBFramework Version - 286.0 + 283.0 + IBGroupedObjects + + 13 + + 224 + 225 + + 14 + + 291 + 292 + + 16 + + 296 + 301 + 302 + + 17 + + 295 + 298 + 299 + + 18 + + 280 + 281 + 282 + + + IBLastGroupID + 19 + IBLockedObjects + + 130 + + IBOpenObjects + + 130 + IBSystem Version - 6F21 + 6L60 diff --git a/camino/resources/localized/English.lproj/BookmarkInfoPanel.nib/objects.nib b/camino/resources/localized/English.lproj/BookmarkInfoPanel.nib/objects.nib index 1e08a5d2a60c..ad1fe943fc9a 100644 Binary files a/camino/resources/localized/English.lproj/BookmarkInfoPanel.nib/objects.nib and b/camino/resources/localized/English.lproj/BookmarkInfoPanel.nib/objects.nib differ diff --git a/camino/resources/localized/English.lproj/BrowserWindow.nib/classes.nib b/camino/resources/localized/English.lproj/BrowserWindow.nib/classes.nib index f85041e1c7ac..f14cd7751202 100644 --- a/camino/resources/localized/English.lproj/BrowserWindow.nib/classes.nib +++ b/camino/resources/localized/English.lproj/BrowserWindow.nib/classes.nib @@ -8,56 +8,53 @@ OUTLETS = {mProxyIcon = PageProxyIcon; }; SUPERCLASS = NSTextField; }, - {CLASS = BookmarkItem; LANGUAGE = ObjC; SUPERCLASS = NSObject; }, {CLASS = BookmarkManagerView; LANGUAGE = ObjC; SUPERCLASS = NSView; }, { - ACTIONS = {changeContainer = id; }; - CLASS = BookmarksController; - LANGUAGE = ObjC; - OUTLETS = { - mAddFolderButton = NSButton; - mAddItemButton = NSButton; - mBookmarksSource = BookmarksDataSource; - mContainerPane = NSTableView; - mContainersSplit = NSSplitView; - mHistorySource = HistoryDataSource; - mInfoButton = NSButton; - mItemPane = ExtendedOutlineView; - mItemSearchSplit = NSSplitView; - mSearchPane = NSTableView; - }; - SUPERCLASS = NSObject; - }, - { - ACTIONS = { - addBookmark = id; - addFolder = id; - deleteBookmarks = id; - openBookmarkInNewTab = id; - openBookmarkInNewWindow = id; - showBookmarkInfo = id; - }; - CLASS = BookmarksDataSource; - LANGUAGE = ObjC; - OUTLETS = { - mBrowserWindowController = id; - mDeleteBookmarkButton = id; - mEditBookmarkButton = id; - mOutlineView = id; - }; - SUPERCLASS = NSObject; - }, - { - CLASS = BookmarksOutlineView; + CLASS = BookmarkOutlineView; LANGUAGE = ObjC; SUPERCLASS = ExtendedOutlineView; }, { ACTIONS = {addFolder = id; }; - CLASS = BookmarksToolbar; + CLASS = BookmarkToolbar; LANGUAGE = ObjC; SUPERCLASS = NSView; }, + { + ACTIONS = { + addBookmark = id; + addCollection = id; + addFolder = id; + addSeparator = id; + deleteBookmarks = id; + locateBookmark = id; + openBookmark = id; + openBookmarkInNewTab = id; + openBookmarkInNewWindow = id; + setAsDockMenuFolder = id; + showBookmarkInfo = id; + startSearch = id; + }; + CLASS = BookmarkViewController; + LANGUAGE = ObjC; + OUTLETS = { + mAddBookmarkButton = NSButton; + mAddCollectionButton = NSButton; + mAddFolderButton = NSButton; + mAddSeparatorButton = NSButton; + mBrowserWindowController = BrowserWindowController; + mContainerPane = ExtendedTableView; + mContainersSplit = NSSplitView; + mHistorySource = HistoryDataSource; + mInfoButton = NSButton; + mItemPane = BookmarkOutlineView; + mItemSearchSplit = NSSplitView; + mSearchButton = NSButton; + mSearchField = NSTextField; + mSearchPane = NSTableView; + }; + SUPERCLASS = NSObject; + }, {CLASS = BrowserContainerView; LANGUAGE = ObjC; SUPERCLASS = NSView; }, { ACTIONS = {toggleBookmarkManager = id; }; @@ -65,7 +62,7 @@ LANGUAGE = ObjC; OUTLETS = { mBookmarkManagerView = NSView; - mBookmarksToolbar = BookmarksToolbar; + mBookmarksToolbar = BookmarkToolbar; mBrowserContainerView = NSView; mStatusBar = NSView; }; @@ -142,7 +139,7 @@ mAddBookmarkSheetWindow = NSWindow; mAddBookmarkTitleField = NSTextField; mBackItem = NSMenuItem; - mBookmarksController = BookmarksController; + mBookmarkViewController = BookmarkViewController; mContentView = BrowserContentView; mCopyItem = NSMenuItem; mForwardItem = NSMenuItem; @@ -156,17 +153,13 @@ mLocationToolbarView = NSView; mLock = NSImageView; mPageMenu = NSMenu; - mPersonalToolbar = BookmarksToolbar; + mPersonalToolbar = BookmarkToolbar; mPopupBlocked = NSButton; mProgress = NSProgressIndicator; mProxyIcon = PageProxyIcon; mSearchBar = SearchTextField; mSearchSheetTextField = SearchTextField; mSearchSheetWindow = NSWindow; - mSidebarBookmarksDataSource = BookmarksDataSource; - mSidebarDrawer = NSDrawer; - mSidebarSourceTabView = NSTabView; - mSidebarTabView = NSTabView; mStatus = NSTextField; mStatusBar = NSView; mTabBrowser = BrowserTabView; @@ -176,35 +169,92 @@ SUPERCLASS = NSWindowController; }, { - ACTIONS = {load = id; }; + ACTIONS = {load = id; reloadWithNewCharset = id; }; CLASS = BrowserWrapper; LANGUAGE = ObjC; - OUTLETS = { - mLockIcon = id; - mWindowController = id; - progress = id; - progressSuper = id; - status = id; - urlbar = id; - }; SUPERCLASS = NSView; }, {CLASS = ExtendedOutlineView; LANGUAGE = ObjC; SUPERCLASS = NSOutlineView; }, + {CLASS = ExtendedTableView; LANGUAGE = ObjC; SUPERCLASS = NSTableView; }, {CLASS = FirstResponder; LANGUAGE = ObjC; SUPERCLASS = NSObject; }, { + ACTIONS = {deleteHistoryItems = id; openHistoryItem = id; }; CLASS = HistoryDataSource; LANGUAGE = ObjC; - OUTLETS = {mBrowserWindowController = id; }; + OUTLETS = {mBrowserWindowController = BrowserWindowController; }; SUPERCLASS = RDFOutlineViewDataSource; }, {CLASS = LocationBar; LANGUAGE = ObjC; SUPERCLASS = NSView; }, - {CLASS = MainController; LANGUAGE = ObjC; SUPERCLASS = NSObject; }, + { + ACTIONS = { + aboutServers = id; + addBookmark = id; + addFolder = id; + addSeparator = id; + biggerTextSize = id; + closeTab = id; + connectToServer = id; + displayPreferencesWindow = id; + doReload = id; + doSearch = id; + doStop = id; + downloadsWindow = id; + exportBookmarks = id; + feedbackLink = id; + findAgain = id; + findInPage = id; + findPrevious = id; + getInfo = id; + goBack = id; + goForward = id; + goHome = id; + importBookmarks = id; + infoLink = id; + manageSidebar = id; + newTab = id; + newWindow = id; + nextTab = id; + openFile = id; + openLocation = id; + openMenuBookmark = id; + pageSetup = id; + previousTab = id; + printPage = id; + reloadWithCharset = id; + savePage = id; + sendURL = id; + showAboutBox = id; + smallerTextSize = id; + toggleBookmarksToolbar = id; + toggleOfflineMode = id; + toggleSidebar = id; + viewSource = id; + }; + CLASS = MainController; + LANGUAGE = ObjC; + OUTLETS = { + mAddBookmarkMenuItem = NSMenuItem; + mApplication = NSApplication; + mBookmarksMenu = NSMenu; + mBookmarksToolbarMenuItem = NSMenuItem; + mCloseTabMenuItem = NSMenuItem; + mCloseWindowMenuItem = NSMenuItem; + mCreateBookmarksFolderMenuItem = NSMenuItem; + mCreateBookmarksSeparatorMenuItem = NSMenuItem; + mDockMenu = NSMenu; + mFilterView = NSView; + mGoMenu = NSMenu; + mServersSubmenu = NSMenu; + mToggleSidebarMenuItem = NSMenuItem; + }; + SUPERCLASS = NSObject; + }, {CLASS = NSObject; LANGUAGE = ObjC; }, {CLASS = PageProxyIcon; LANGUAGE = ObjC; SUPERCLASS = NSImageView; }, { CLASS = RDFOutlineViewDataSource; LANGUAGE = ObjC; - OUTLETS = {mOutlineView = id; }; + OUTLETS = {mOutlineView = ExtendedOutlineView; }; SUPERCLASS = NSObject; }, {CLASS = RDFOutlineViewItem; LANGUAGE = ObjC; SUPERCLASS = NSObject; }, diff --git a/camino/resources/localized/English.lproj/BrowserWindow.nib/info.nib b/camino/resources/localized/English.lproj/BrowserWindow.nib/info.nib index add74062bc0b..2a0ca3a4e20b 100644 --- a/camino/resources/localized/English.lproj/BrowserWindow.nib/info.nib +++ b/camino/resources/localized/English.lproj/BrowserWindow.nib/info.nib @@ -3,15 +3,11 @@ IBDocumentLocation - 122 60 653 383 0 0 1280 832 + 40 268 545 473 0 0 1152 746 IBEditorPositions 124 - 83 451 170 144 0 0 1280 832 - 160 - 602 52 195 666 0 0 1024 746 - 28 - 542 342 195 457 0 0 1280 832 + 58 576 170 96 0 0 1152 746 297 82 354 213 294 0 0 1280 832 314 @@ -27,11 +23,13 @@ 654 159 629 198 144 0 0 1280 832 731 - 185 277 654 459 0 0 1024 746 + 249 277 654 459 0 0 1152 746 801 418 469 201 79 0 0 1024 746 826 - 84 461 213 78 0 0 1152 848 + 84 401 213 60 0 0 1152 746 + 894 + 276 641 156 66 0 0 1152 746 IBFramework Version 291.0 @@ -44,14 +42,18 @@ IBLastGroupID - 8 + 10 IBLockedObjects - + + 748 + 910 + 889 + IBOpenObjects - 826 + 731 IBSystem Version - 6L29 + 6L60 diff --git a/camino/resources/localized/English.lproj/BrowserWindow.nib/objects.nib b/camino/resources/localized/English.lproj/BrowserWindow.nib/objects.nib index f27fae47dfd1..f174de22a27b 100644 Binary files a/camino/resources/localized/English.lproj/BrowserWindow.nib/objects.nib and b/camino/resources/localized/English.lproj/BrowserWindow.nib/objects.nib differ diff --git a/camino/resources/localized/English.lproj/Localizable.strings b/camino/resources/localized/English.lproj/Localizable.strings index 7b1c2329a5b6..dc52b50a6001 100644 Binary files a/camino/resources/localized/English.lproj/Localizable.strings and b/camino/resources/localized/English.lproj/Localizable.strings differ diff --git a/camino/src/application/AppDirServiceProvider.cpp b/camino/src/application/AppDirServiceProvider.cpp index 0330560b0905..11cc36f8af72 100644 --- a/camino/src/application/AppDirServiceProvider.cpp +++ b/camino/src/application/AppDirServiceProvider.cpp @@ -81,7 +81,7 @@ AppDirServiceProvider::GetFile(const char *prop, PRBool *persistant, nsIFile **_ *_retval = nsnull; *persistant = PR_TRUE; - if (strcmp(prop, NS_APP_APPLICATION_REGISTRY_DIR) == 0) + if (strcmp(prop, NS_APP_USER_PROFILE_50_DIR) == 0) { rv = GetProductDirectory(getter_AddRefs(localFile)); } diff --git a/camino/src/application/MainController.h b/camino/src/application/MainController.h index c54a03b1f90a..0e78942a32f0 100644 --- a/camino/src/application/MainController.h +++ b/camino/src/application/MainController.h @@ -36,17 +36,16 @@ * ***** END LICENSE BLOCK ***** */ #import -#import "BrowserWindowController.h" -#import "MVPreferencesController.h" -#import "SplashScreenWindow.h" -#import "FindDlgController.h" -#import "PreferenceManager.h" -#import "SharedMenusObj.h" -#import "NetworkServices.h" -@class BookmarksMenu; +@class BookmarkMenu; +@class BookmarkManager; @class KeychainService; -@class NetworkServices; +@class BrowserWindowController; +@class SplashScreenWindow; +@class SharedMenusObj; +@class PreferenceManager; +@class FindDlgController; +@class MVPreferencesController; @interface MainController : NSObject { @@ -74,10 +73,8 @@ SplashScreenWindow* mSplashScreen; - PreferenceManager* mPreferenceManager; - - BookmarksMenu* mMenuBookmarks; - BookmarksMenu* mDockBookmarks; + BookmarkMenu* mMenuBookmarks; + BookmarkMenu* mDockBookmarks; KeychainService* mKeychainService; @@ -88,8 +85,6 @@ NSString* mStartURL; SharedMenusObj* mSharedMenusObj; - NetworkServices* mNetworkServices; - NSMutableDictionary* mCharsets; } @@ -161,9 +156,9 @@ - (NSView*)getSavePanelView; - (NSWindow*)getFrontmostBrowserWindow; +- (void)setupBookmarkMenus:(BookmarkManager *)BookmarkManager; - (MVPreferencesController *)preferencesController; - (void)displayPreferencesWindow:sender; -- (PreferenceManager *)preferenceManager; - (BOOL)isMainWindowABrowserWindow; // if the main window is a browser window, return its controller, otherwise nil diff --git a/camino/src/application/MainController.mm b/camino/src/application/MainController.mm index b12f8e900c1e..bf147afe90fc 100644 --- a/camino/src/application/MainController.mm +++ b/camino/src/application/MainController.mm @@ -43,9 +43,13 @@ #import "ChimeraUIConstants.h" #import "MainController.h" #import "BrowserWindowController.h" -#import "BookmarksMenu.h" -#import "BookmarksService.h" -#import "BookmarkInfoController.h"; +#import "BookmarkMenu.h" +#import "Bookmark.h" +#import "BookmarkFolder.h" +#import "BookmarkInfoController.h" +#import "BookmarkManager.h" +#import "BookmarkToolbar.h" +#import "RunLoopMessenger.h" #import "CHBrowserService.h" #import "AboutBox.h" #import "UserDefaults.h" @@ -54,6 +58,11 @@ #import "ProgressDlgController.h" #import "JSConsole.h" #import "NetworkServices.h" +#import "MVPreferencesController.h" +#import "SplashScreenWindow.h" +#import "FindDlgController.h" +#import "PreferenceManager.h" +#import "SharedMenusObj.h" #include "nsCOMPtr.h" #include "nsEmbedAPI.h" @@ -93,12 +102,9 @@ const int kReuseWindowOnAE = 2; // over to doing things the right way when the time comes. #define kFixedDockMenuAppKitVersion 632 -@interface MainController(MainControllerPrivate) +@interface MainController(Private) - (void)setupStartpage; -- (void)setupNetworkBrowser; -- (void)foundService:(NSNetService*)inService moreComing:(BOOL)more; -- (void)rebuildNetServiceMenu; @end @@ -142,7 +148,6 @@ const int kReuseWindowOnAE = 2; mMenuBookmarks = nil; [NSApp setServicesProvider:self]; - // Initialize shared menu support mSharedMenusObj = [[SharedMenusObj alloc] init]; } @@ -169,10 +174,14 @@ const int kReuseWindowOnAE = 2; #ifdef _BUILD_STATIC_BIN [self updatePrebinding]; #endif - // initialize if we haven't already. - [self preferenceManager]; - + PreferenceManager *pm = [PreferenceManager sharedInstance]; + + // start bookmarks + RunLoopMessenger *mainThreadRunLoopMessenger = [[RunLoopMessenger alloc] init]; + [NSThread detachNewThreadSelector:@selector(startBookmarksManager:) toTarget:[BookmarkManager class] withObject:mainThreadRunLoopMessenger]; + [mainThreadRunLoopMessenger release]; //bookmark manager retains this + [self setupStartpage]; // register our app components with the embed layer @@ -180,46 +189,15 @@ const int kReuseWindowOnAE = 2; const nsModuleComponentInfo* comps = GetAppComponents(&numComps); CHBrowserService::RegisterAppComponents(comps, numComps); - // make sure we have a bookmarks manager - BookmarksManager* bmManager = [BookmarksManager sharedBookmarksManager]; - // don't open a new browser window if we already have one // (for example, from an GetURL Apple Event) NSWindow* browserWindow = [self getFrontmostBrowserWindow]; if (!browserWindow) [self newWindow: self]; - [mSplashScreen close]; + [mSplashScreen close]; //deallocs on close + mSplashScreen = nil; - [mBookmarksMenu setAutoenablesItems: NO]; - - // menubar bookmarks - int firstBookmarkItem = [mBookmarksMenu indexOfItemWithTag:kBookmarksDividerTag] + 1; - mMenuBookmarks = [[BookmarksMenu alloc] initWithMenu: mBookmarksMenu - firstItem: firstBookmarkItem - rootContent: [bmManager getRootContent] - watchedFolder: eBookmarksFolderRoot]; - [bmManager addBookmarksClient:mMenuBookmarks]; - - // dock bookmarks. Note that we disable them on 10.1 because of a bug noted here: - // http://developer.apple.com/samplecode/Sample_Code/Cocoa/DeskPictAppDockMenu.htm - if (NSAppKitVersionNumber >= kFixedDockMenuAppKitVersion) - { - [mDockMenu setAutoenablesItems:NO]; - int firstBookmarkItem = [mDockMenu indexOfItemWithTag:kBookmarksDividerTag] + 1; - mDockBookmarks = [[BookmarksMenu alloc] initWithMenu: mDockMenu - firstItem: firstBookmarkItem - rootContent: [bmManager getDockMenuRoot] - watchedFolder: eBookmarksFolderDockMenu]; - [bmManager addBookmarksClient:mDockBookmarks]; - } - else - { - // just empty the menu - while ([mDockMenu numberOfItems] > 0) - [mDockMenu removeItemAtIndex:0]; - } - // Initialize offline mode. mOffline = NO; nsCOMPtr ioService(do_GetService(ioServiceContractID)); @@ -228,27 +206,28 @@ const int kReuseWindowOnAE = 2; PRBool offline = PR_FALSE; ioService->GetOffline(&offline); mOffline = offline; - + // Initialize the keychain service. mKeychainService = [KeychainService instance]; // bring up the JS console service BOOL success; - if ([[PreferenceManager sharedInstance] getBooleanPref:"chimera.log_js_to_console" withSuccess:&success]) + if ([pm getBooleanPref:"chimera.log_js_to_console" withSuccess:&success]) [JSConsole sharedJSConsole]; BOOL doingRendezvous = NO; - if ([[PreferenceManager sharedInstance] getBooleanPref:"chimera.enable_rendezvous" withSuccess:&success]) + if ([pm getBooleanPref:"chimera.enable_rendezvous" withSuccess:&success]) { // are we on 10.2.3 or higher? The DNS resolution stuff is broken before 10.2.3 long systemVersion; OSErr err = ::Gestalt(gestaltSystemVersion, &systemVersion); if ((err == noErr) && (systemVersion >= 0x00001023)) { - mNetworkServices = [[NetworkServices alloc] init]; - [mNetworkServices registerClient:self]; - [mNetworkServices startServices]; + NSNotificationCenter *nc = [NSNotificationCenter defaultCenter]; + [nc addObserver:self selector:@selector(availableServicesChanged:) name:NetworkServicesAvailableServicesChanged object:nil]; + [nc addObserver:self selector:@selector(serviceResolved:) name:NetworkServicesResolutionSuccess object:nil]; + [nc addObserver:self selector:@selector(serviceResolutionFailed:) name:NetworkServicesResolutionFailure object:nil]; doingRendezvous = YES; } } @@ -277,9 +256,10 @@ const int kReuseWindowOnAE = 2; NSLog(@"App will terminate notification"); #endif - [mNetworkServices unregisterClient:self]; - [mNetworkServices release]; - + [NetworkServices shutdownNetworkServices]; + + [[BookmarkManager sharedBookmarkManager] shutdown]; + // Autosave one of the windows. [[[mApplication mainWindow] windowController] autosaveWindowFrame]; @@ -288,18 +268,10 @@ const int kReuseWindowOnAE = 2; // make sure the info window is closed [BookmarkInfoController closeBookmarkInfoController]; - - BookmarksManager* bmManager = [BookmarksManager sharedBookmarksManagerDontAlloc]; - if (bmManager) - { - [bmManager removeBookmarksClient:mMenuBookmarks]; - [bmManager removeBookmarksClient:mDockBookmarks]; - } - + // Release before calling TermEmbedding since we need to access XPCOM // to save preferences [mPreferencesController release]; - [mPreferenceManager release]; CHBrowserService::TermEmbedding(); @@ -339,6 +311,39 @@ const int kReuseWindowOnAE = 2; } } +// +// this gets called by bookmark load background thread, so it's a possible +// point of contention. but, it's only called once on startup, so probably +// won't be a problem. +// +- (void)setupBookmarkMenus:(BookmarkManager *)BookmarkManager +{ + [mBookmarksMenu setAutoenablesItems: NO]; + + // menubar bookmarks + int firstBookmarkItem = [mBookmarksMenu indexOfItemWithTag:kBookmarksDividerTag] + 1; + mMenuBookmarks = [[BookmarkMenu alloc] initWithMenu: mBookmarksMenu + firstItem: firstBookmarkItem + rootBookmarkFolder: [BookmarkManager bookmarkMenuFolder]]; + + // dock bookmarks. Note that we disable them on 10.1 because of a bug noted here: + // http://developer.apple.com/samplecode/Sample_Code/Cocoa/DeskPictAppDockMenu.htm + if (NSAppKitVersionNumber >= kFixedDockMenuAppKitVersion) + { + [mDockMenu setAutoenablesItems:NO]; + int firstBookmarkItem = [mDockMenu indexOfItemWithTag:kBookmarksDividerTag] + 1; + mDockBookmarks = [[BookmarkMenu alloc] initWithMenu: mDockMenu + firstItem: firstBookmarkItem + rootBookmarkFolder: [BookmarkManager dockMenuFolder]]; + } + else + { + // just empty the menu + while ([mDockMenu numberOfItems] > 0) + [mDockMenu removeItemAtIndex:0]; + } +} + -(IBAction)newWindow:(id)aSender { @@ -349,7 +354,7 @@ const int kReuseWindowOnAE = 2; [[mainWindow windowController] autosaveWindowFrame]; // Now open the new window. - NSString* homePage = mStartURL ? mStartURL : [mPreferenceManager homePage:YES]; + NSString* homePage = mStartURL ? mStartURL : [[PreferenceManager sharedInstance] homePage:YES]; BrowserWindowController* controller = [self openBrowserWindowWithURL:homePage andReferrer:nil]; if ([homePage isEqualToString: @"about:blank"]) @@ -612,9 +617,9 @@ const int kReuseWindowOnAE = 2; - (void)adjustBookmarksMenuItemsEnabling:(BOOL)inBrowserWindowFrontmost; { - [mAddBookmarkMenuItem setEnabled:inBrowserWindowFrontmost]; - [mCreateBookmarksFolderMenuItem setEnabled:inBrowserWindowFrontmost]; - [mCreateBookmarksSeparatorMenuItem setEnabled:NO]; // separators are not implemented yet + [mAddBookmarkMenuItem setEnabled:inBrowserWindowFrontmost]; + [mCreateBookmarksFolderMenuItem setEnabled:inBrowserWindowFrontmost]; + [mCreateBookmarksSeparatorMenuItem setEnabled:YES]; } - (NSView*)getSavePanelView @@ -671,7 +676,7 @@ const int kReuseWindowOnAE = 2; - (void)openNewWindowOrTabWithURL:(NSString*)inURLString andReferrer: (NSString*)aReferrer { // make sure we're initted - [self preferenceManager]; + [PreferenceManager sharedInstance]; PRInt32 reuseWindow = 0; PRBool loadInBackground = PR_FALSE; @@ -712,31 +717,7 @@ const int kReuseWindowOnAE = 2; // Bookmarks menu actions. -(IBAction) importBookmarks:(id)aSender { - // IE favorites: ~/Library/Preferences/Explorer/Favorites.html - // Omniweb favorites: ~/Library/Application Support/Omniweb/Bookmarks.html - // For now, open the panel to IE's favorites. - NSOpenPanel* openPanel = [NSOpenPanel openPanel]; - [openPanel setCanChooseFiles: YES]; - [openPanel setCanChooseDirectories: NO]; - [openPanel setAllowsMultipleSelection: NO]; - NSArray* array = [NSArray arrayWithObjects: @"htm",@"html",@"xml", nil]; - int result = [openPanel runModalForDirectory: @"~/Library/Preferences/Explorer/" - file: @"Favorites.html" - types: array]; - if (result == NSOKButton) { - NSArray* urlArray = [openPanel URLs]; - if ([urlArray count] == 0) - return; - NSURL* url = [urlArray objectAtIndex: 0]; - - NSWindow* browserWindow = [self getFrontmostBrowserWindow]; - if (!browserWindow) { - [self newWindow: self]; - browserWindow = [mApplication mainWindow]; - } - - [[browserWindow windowController] importBookmarks: [url absoluteString]]; - } + [[BookmarkManager sharedBookmarkManager] startImportBookmarks]; } -(IBAction) exportBookmarks:(id)aSender @@ -748,10 +729,7 @@ const int kReuseWindowOnAE = 2; int saveResult = [savePanel runModalForDirectory:@"" file:@"Exported Bookmarks"]; if (saveResult != NSFileHandlingPanelOKButton) return; - - nsAutoString filePath; - [[savePanel filename] assignTo_nsAString:filePath]; - BookmarksService::ExportBookmarksToHTML(filePath); + [[BookmarkManager sharedBookmarkManager] writeHTMLFile:[savePanel filename]]; } -(IBAction) addBookmark:(id)aSender @@ -778,11 +756,15 @@ const int kReuseWindowOnAE = 2; if ([aSender isMemberOfClass:[NSApplication class]]) return; // 10.1. Don't do anything. - BookmarksManager* bmManager = [BookmarksManager sharedBookmarksManager]; - BookmarkItem* item = [bmManager getWrapperForID:[aSender tag]]; - - if ([item isGroup]) - { + BookmarkItem* item = [aSender representedObject]; + BrowserWindowController* browserController; + if ([item isKindOfClass:[Bookmark class]]) { + browserController = [self getMainWindowBrowserController]; + if (browserController) + [browserController loadURL:[(Bookmark *)item url] referrer:nil activate:YES]; + else + [self openBrowserWindowWithURL:[(Bookmark *)item url] andReferrer:nil]; + } else { // item is a group, no doubt. NSWindow* browserWindow = [self getFrontmostBrowserWindow]; if (browserWindow) { @@ -794,20 +776,8 @@ const int kReuseWindowOnAE = 2; [self newWindow:self]; browserWindow = [self getFrontmostBrowserWindow]; } - - BrowserWindowController* browserController = (BrowserWindowController*)[browserWindow delegate]; - - NSArray* uriList = [bmManager getBookmarkGroupURIs:item]; - [browserController openTabGroup:uriList replaceExistingTabs:YES]; - } - else - { - NSString* url = [item url]; - BrowserWindowController* browserController = [self getMainWindowBrowserController]; - if (browserController) - [browserController loadURL:url referrer:nil activate:YES]; - else - [self openBrowserWindowWithURL:url andReferrer:nil]; + browserController = (BrowserWindowController*)[browserWindow delegate]; + [browserController openTabGroup:[(BookmarkFolder *)item childURLs] replaceExistingTabs:YES]; } } @@ -832,13 +802,6 @@ const int kReuseWindowOnAE = 2; [[browserWindow windowController] manageHistory: aSender]; } -- (PreferenceManager *)preferenceManager -{ - if (!mPreferenceManager) - mPreferenceManager = [[PreferenceManager sharedInstance] retain]; - return mPreferenceManager; -} - - (MVPreferencesController *)preferencesController { if (!mPreferencesController) { @@ -1004,7 +967,7 @@ const int kReuseWindowOnAE = 2; // out this menu. if (action == @selector(toggleBookmarksToolbar:)) { if (browserController) { - NSView* bookmarkToolbar = [browserController bookmarksToolbar]; + NSView* bookmarkToolbar = [browserController bookmarkToolbar]; if ( bookmarkToolbar ) { float height = [bookmarkToolbar frame].size.height; BOOL toolbarShowing = (height > 0); @@ -1022,21 +985,10 @@ const int kReuseWindowOnAE = 2; } if (action == @selector(toggleSidebar:)) { - if (browserController) { - NSDrawer *sidebar = [browserController sidebarDrawer]; - if (sidebar) { - int sidebarState = [sidebar state]; - if (sidebarState == NSDrawerOpenState) - [mToggleSidebarMenuItem setTitle: NSLocalizedString(@"HideSidebarMenuItem",@"")]; - else - [mToggleSidebarMenuItem setTitle: NSLocalizedString(@"ShowSidebarMenuItem",@"")]; - return YES; - } - else - return NO; - } - else - return NO; + if (browserController) + return YES; + else + return NO; } if ( action == @selector(getInfo:) ) @@ -1103,10 +1055,10 @@ const int kReuseWindowOnAE = 2; BrowserWindowController* browserController = [self getMainWindowBrowserController]; if (!browserController) return; - float height = [[browserController bookmarksToolbar] frame].size.height; + float height = [[browserController bookmarkToolbar] frame].size.height; BOOL showToolbar = (BOOL)(!(height > 0)); - [[browserController bookmarksToolbar] showBookmarksToolbar: showToolbar]; + [[browserController bookmarkToolbar] showBookmarksToolbar: showToolbar]; // save prefs here NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults]; @@ -1262,26 +1214,30 @@ static int SortByProtocolAndName(NSDictionary* item1, NSDictionary* item2, void return [[item1 objectForKey:@"protocol"] compare:[item2 objectForKey:@"protocol"] options:NSCaseInsensitiveSearch]; } -- (void)rebuildNetServiceMenu +// NetworkServicesClient implementation + +- (void)availableServicesChanged:(NSNotification *)note { // rebuild the submenu, leaving the first item while ([mServersSubmenu numberOfItems] > 1) [mServersSubmenu removeItemAtIndex:[mServersSubmenu numberOfItems] - 1]; - - NSEnumerator* keysEnumerator = [mNetworkServices serviceEnumerator]; + + NetworkServices *netserv = [note object]; + + NSEnumerator* keysEnumerator = [netserv serviceEnumerator]; // build an array of dictionaries, so we can sort it - + NSMutableArray* servicesArray = [[NSMutableArray alloc] initWithCapacity:10]; - + id key; while ((key = [keysEnumerator nextObject])) { NSDictionary* serviceDict = [NSDictionary dictionaryWithObjectsAndKeys: - key, @"id", - [mNetworkServices serviceName:[key intValue]], @"name", - [mNetworkServices serviceProtocol:[key intValue]], @"protocol", - nil]; - + key, @"id", + [netserv serviceName:[key intValue]], @"name", + [netserv serviceProtocol:[key intValue]], @"protocol", + nil]; + [servicesArray addObject:serviceDict]; } @@ -1302,7 +1258,8 @@ static int SortByProtocolAndName(NSDictionary* item1, NSDictionary* item2, void // sort on protocol, then name [servicesArray sortUsingFunction:SortByProtocolAndName context:NULL]; - for (unsigned int i = 0; i < [servicesArray count]; i ++) + unsigned count = [servicesArray count]; + for (unsigned int i = 0; i < count; i ++) { NSDictionary* serviceDict = [servicesArray objectAtIndex:i]; NSString* itemName = [[serviceDict objectForKey:@"name"] stringByAppendingString:NSLocalizedString([serviceDict objectForKey:@"protocol"], @"")]; @@ -1312,40 +1269,40 @@ static int SortByProtocolAndName(NSDictionary* item1, NSDictionary* item2, void [newItem setTarget:self]; } } + // when you alloc, you've got to release . . . + [servicesArray release]; } -// NetworkServicesClient implementation - -- (void)availableServicesChanged:(NetworkServices*)servicesProvider +- (void)serviceResolved:(NSNotification *)note { - [self rebuildNetServiceMenu]; + NSDictionary *dict = [note userInfo]; + if ([dict objectForKey:NetworkServicesClientKey] == self) + [self openNewWindowOrTabWithURL:[dict objectForKey:NetworkServicesResolvedURLKey] andReferrer:nil]; } -- (void)serviceResolved:(int)serviceID withURL:(NSString*)url +// +// handles resolution failure for everybody else +// +- (void)serviceResolutionFailed:(NSNotification *)note { - [self openNewWindowOrTabWithURL:url andReferrer:nil]; -} - -- (void)serviceResolutionFailed:(int)serviceID -{ - NSString* serviceName = [mNetworkServices serviceName:serviceID]; - + NSDictionary *dict = [note userInfo]; + NSString* serviceName = [dict objectForKey:NetworkServicesServiceKey]; NSBeginAlertSheet(NSLocalizedString(@"ServiceResolutionFailedTitle", @""), - @"", // default button - nil, // cancel buttton - nil, // other button - [NSApp mainWindow], // window - nil, // delegate - nil, // end sel - nil, // dismiss sel - NULL, // context - [NSString stringWithFormat:NSLocalizedString(@"ServiceResolutionFailedMsgFormat", @""), serviceName] - ); + @"", // default button + nil, // cancel buttton + nil, // other button + [NSApp mainWindow], // window + nil, // delegate + nil, // end sel + nil, // dismiss sel + NULL, // context + [NSString stringWithFormat:NSLocalizedString(@"ServiceResolutionFailedMsgFormat", @""), serviceName] + ); } -(IBAction) connectToServer:(id)aSender { - [mNetworkServices attemptResolveService:[aSender tag]]; + [[NetworkServices sharedNetworkServices] attemptResolveService:[aSender tag] forSender:self]; } -(IBAction) aboutServers:(id)aSender diff --git a/camino/src/application/RunLoopMessenger.h b/camino/src/application/RunLoopMessenger.h new file mode 100644 index 000000000000..8108292ee2c2 --- /dev/null +++ b/camino/src/application/RunLoopMessenger.h @@ -0,0 +1,62 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** +* Version: NPL 1.1/GPL 2.0/LGPL 2.1 +* +* The contents of this file are subject to the Netscape Public License +* Version 1.1 (the "License"); you may not use this file except in +* compliance with the License. You may obtain a copy of the License at +* http://www.mozilla.org/NPL/ +* +* Software distributed under the License is distributed on an "AS IS" basis, +* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +* for the specific language governing rights and limitations under the +* License. +* +* The Original Code is mozilla.org code. +* +* The Initial Developer of the Original Code is +* Netscape Communications Corporation. +* Portions created by the Initial Developer are Copyright (C) 2002 +* the Initial Developer. All Rights Reserved. +* +* Contributor(s): +* Nathan Day (original author) +* David Haas +* +* +* Alternatively, the contents of this file may be used under the terms of +* either the GNU General Public License Version 2 or later (the "GPL"), or +* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +* in which case the provisions of the GPL or the LGPL are applicable instead +* of those above. If you wish to allow use of your version of this file only +* under the terms of either the GPL or the LGPL, and not to allow others to +* use your version of this file under the terms of the NPL, indicate your +* decision by deleting the provisions above and replace them with the notice +* and other provisions required by the GPL or the LGPL. If you do not delete +* the provisions above, a recipient may use your version of this file under +* the terms of any one of the NPL, the GPL or the LGPL. +* +* This class is taken whole cloth from "NDRunLoopMessenger", written by +* Nathan Day. All I did was remove a couple methods and change some strings. +* The original code can be found at: +* http://homepage.mac.com/nathan_day/pages/source.html#NDRunLoopMessenger +* +* ***** END LICENSE BLOCK ***** */ + +#import + + +@interface RunLoopMessenger : NSObject +{ + NSPort* mPort; +} + ++ (id)runLoopMessengerForCurrentRunLoop; + +- (void)target:(id)aTarget performSelector:(SEL)aSelector; +- (void)target:(id)aTarget performSelector:(SEL)aSelector withObjects:(NSArray *)objectArray; +- (id)target:(id)aTarget performSelector:(SEL)aSelector withResult:(BOOL)aResultFlag; +- (id)target:(id)aTarget performSelector:(SEL)aSelector withObjects:(NSArray *)objectArray withResult:(BOOL)aResultFlag; +- (void)messageInvocation:(NSInvocation *)anInvocation withResult:(BOOL)aResultFlag; + +@end diff --git a/camino/src/application/RunLoopMessenger.mm b/camino/src/application/RunLoopMessenger.mm new file mode 100644 index 000000000000..435e4a3c74a6 --- /dev/null +++ b/camino/src/application/RunLoopMessenger.mm @@ -0,0 +1,250 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** +* Version: NPL 1.1/GPL 2.0/LGPL 2.1 +* +* The contents of this file are subject to the Netscape Public License +* Version 1.1 (the "License"); you may not use this file except in +* compliance with the License. You may obtain a copy of the License at +* http://www.mozilla.org/NPL/ +* +* Software distributed under the License is distributed on an "AS IS" basis, +* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +* for the specific language governing rights and limitations under the +* License. +* +* The Original Code is mozilla.org code. +* +* The Initial Developer of the Original Code is +* Netscape Communications Corporation. +* Portions created by the Initial Developer are Copyright (C) 2002 +* the Initial Developer. All Rights Reserved. +* +* Contributor(s): +* Nathan Day (original author) +* David Haas +* +* +* Alternatively, the contents of this file may be used under the terms of +* either the GNU General Public License Version 2 or later (the "GPL"), or +* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +* in which case the provisions of the GPL or the LGPL are applicable instead +* of those above. If you wish to allow use of your version of this file only +* under the terms of either the GPL or the LGPL, and not to allow others to +* use your version of this file under the terms of the NPL, indicate your +* decision by deleting the provisions above and replace them with the notice +* and other provisions required by the GPL or the LGPL. If you do not delete +* the provisions above, a recipient may use your version of this file under +* the terms of any one of the NPL, the GPL or the LGPL. +* +* This class is taken whole cloth from "NDRunLoopMessenger", written by +* Nathan Day. I removed a couple methods, changed some strings, and made +* the tinest tweaks to sendData() & messageInvocation:withResult. +* The original code can be found at: +* http://homepage.mac.com/nathan_day/pages/source.html#NDRunLoopMessenger +* +* A big problem with this code is that the send loop can fill up, which blocks +* the thread this gets run on. I noticed this when checking bookmark imports. +* Storing things in a queue would be clever, but I'm not that clever. +* +* ***** END LICENSE BLOCK ***** */ + +#import "RunLoopMessenger.h" + +static NSString* kThreadDictionaryKey = @"nd_rml_tdk"; +static NSString* kSendMessageException = @"nd_rlm_sme"; +static NSString* kConnectionDoesNotExistsException = @"nd_rlm_cdnee"; +// +//struct message +// +struct message +{ + NSConditionLock* resultLock; + NSInvocation* invocation; +}; +// +// function sendData +// +void sendData(NSData * aData, NSPort * aPort); +// +// interface RunLoopMessenger +// +@interface RunLoopMessenger (Private) +- (void)createPortForRunLoop:(NSRunLoop *)aRunLoop; +- (void)registerNotificationObservers; +@end + + +@implementation RunLoopMessenger + ++ (id)runLoopMessengerForCurrentRunLoop +{ + RunLoopMessenger* currentRunLoopMessenger; + currentRunLoopMessenger = [[[NSThread currentThread] threadDictionary] objectForKey:kThreadDictionaryKey]; + if( currentRunLoopMessenger == nil ) + currentRunLoopMessenger = [[RunLoopMessenger alloc] init]; + return currentRunLoopMessenger; +} + +- (id)init +{ + if((self = [super init])) + { + NSMutableDictionary* theThreadDictionary; + id theOneForThisThread; + theThreadDictionary = [[NSThread currentThread] threadDictionary]; + if((theOneForThisThread = [theThreadDictionary objectForKey:kThreadDictionaryKey])) + { + [self release]; + self = theOneForThisThread; + } + else + { + [self createPortForRunLoop:[NSRunLoop currentRunLoop]]; + [theThreadDictionary setObject:self forKey:kThreadDictionaryKey]; + [self registerNotificationObservers]; + } + } + return self; +} + +- (void)registerNotificationObservers +{ + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(threadWillExit:) name:NSThreadWillExitNotification object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(portDidBecomeInvalid:) name:NSPortDidBecomeInvalidNotification object:mPort]; +} + +- (void)dealloc +{ + [[NSNotificationCenter defaultCenter] removeObserver:self]; + [[[NSThread currentThread] threadDictionary] removeObjectForKey:kThreadDictionaryKey]; + [mPort removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; + [mPort release]; + [super dealloc]; +} + +- (void)threadWillExit:(NSNotification *)notification +{ + NSThread* thread = [notification object]; + if( [[thread threadDictionary] objectForKey:kThreadDictionaryKey] == self ) + { + [[thread threadDictionary] removeObjectForKey:kThreadDictionaryKey]; + [mPort removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; + mPort= nil; + [[NSNotificationCenter defaultCenter] removeObserver:self]; + } +} + +- (void)portDidBecomeInvalid:(NSNotification *)notification +{ + if( [notification object] == mPort) + { + [[[NSThread currentThread] threadDictionary] removeObjectForKey:kThreadDictionaryKey]; + [mPort removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; + mPort= nil; + [[NSNotificationCenter defaultCenter] removeObserver:self]; + } +} + +- (void)target:(id)aTarget performSelector:(SEL)aSelector +{ + [self target:aTarget performSelector:aSelector withObjects:nil withResult:NO]; +} + +- (void)target:(id)aTarget performSelector:(SEL)aSelector withObjects:(NSArray *)anArray +{ + [self target:aTarget performSelector:aSelector withObjects:anArray withResult:NO]; +} + +- (id)target:(id)aTarget performSelector:(SEL)aSelector withResult:(BOOL)aFlag +{ + return [self target:aTarget performSelector:aSelector withObjects:nil withResult:aFlag]; +} + +- (id)target:(id)aTarget performSelector:(SEL)aSelector withObjects:(NSArray *)anArray withResult:(BOOL)aFlag +{ + NSInvocation* theInvocation; + id anArgument, theResult = nil; + unsigned i,count; + theInvocation = [NSInvocation invocationWithMethodSignature:[aTarget methodSignatureForSelector:aSelector]]; + [theInvocation setSelector:aSelector]; + [theInvocation setTarget:aTarget]; + if (anArray) { + count = [anArray count]; + for (i=0;i < count;i++) { + anArgument = [anArray objectAtIndex:i]; + [theInvocation setArgument:&anArgument atIndex:(i+2)]; + } + } + [self messageInvocation:theInvocation withResult:aFlag]; + if( aFlag ) + [theInvocation getReturnValue:&theResult]; + return theResult; +} + +- (void)messageInvocation:(NSInvocation *)anInvocation withResult:(BOOL)aResultFlag +{ + struct message* theMessage; + NSMutableData* theData; + + [anInvocation retainArguments]; + + theData = [NSMutableData dataWithLength:sizeof(struct message)]; + theMessage = (struct message *)[theData mutableBytes]; + theMessage->invocation = [anInvocation retain]; // will be released by handlePortMessage + theMessage->resultLock = aResultFlag ? [[NSConditionLock alloc] initWithCondition:NO] : nil; + NS_DURING + sendData(theData,mPort); + NS_HANDLER + [theMessage->invocation release]; + [theMessage->resultLock unlock]; + [theMessage->resultLock release]; + return; + NS_ENDHANDLER + if(aResultFlag) + { + [theMessage->resultLock lockWhenCondition:YES]; + [theMessage->resultLock unlock]; + [theMessage->resultLock release]; + } +} + +- (void)handlePortMessage:(NSPortMessage *)aPortMessage +{ + struct message* theMessage; + NSData* theData; + theData = [[aPortMessage components] lastObject]; + theMessage = (struct message*)[theData bytes]; + [theMessage->invocation invoke]; + if( theMessage->resultLock ) + { + [theMessage->resultLock lock]; + [theMessage->resultLock unlockWithCondition:YES]; + } + [theMessage->invocation release]; // to balance messageInvocation:withResult: +} + +- (void)createPortForRunLoop:(NSRunLoop *)aRunLoop +{ + mPort = [NSPort port]; + [mPort setDelegate:self]; + [mPort scheduleInRunLoop:aRunLoop forMode:NSDefaultRunLoopMode]; +} + +void sendData(NSData* aData, NSPort* aPort) +{ + NSPortMessage* thePortMessage; + if(aPort) + { + thePortMessage = [[NSPortMessage alloc] initWithSendPort:aPort receivePort:nil components:[NSArray arrayWithObject:aData]]; + if(![thePortMessage sendBeforeDate:[NSDate dateWithTimeIntervalSinceNow:15.0]]) + [NSException raise:kSendMessageException format:@"An error occured will trying to send the message data %@", [aData description]]; + [thePortMessage release]; + } + else + { + [NSException raise:kConnectionDoesNotExistsException format:@"The connection to the runLoop does not exist"]; + } +} + + +@end diff --git a/camino/src/bookmarks/AddressBookManager.h b/camino/src/bookmarks/AddressBookManager.h new file mode 100644 index 000000000000..0df623120ce7 --- /dev/null +++ b/camino/src/bookmarks/AddressBookManager.h @@ -0,0 +1,59 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** +* Version: NPL 1.1/GPL 2.0/LGPL 2.1 +* +* The contents of this file are subject to the Netscape Public License +* Version 1.1 (the "License"); you may not use this file except in +* compliance with the License. You may obtain a copy of the License at +* http://www.mozilla.org/NPL/ +* +* Software distributed under the License is distributed on an "AS IS" basis, +* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +* for the specific language governing rights and limitations under the +* License. +* +* The Original Code is mozilla.org code. +* +* The Initial Developer of the Original Code is +* Netscape Communications Corporation. +* Portions created by the Initial Developer are Copyright (C) 2002 +* the Initial Developer. All Rights Reserved. +* +* Contributor(s): +* David Haas +* +* +* Alternatively, the contents of this file may be used under the terms of +* either the GNU General Public License Version 2 or later (the "GPL"), or +* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +* in which case the provisions of the GPL or the LGPL are applicable instead +* of those above. If you wish to allow use of your version of this file only +* under the terms of either the GPL or the LGPL, and not to allow others to +* use your version of this file under the terms of the NPL, indicate your +* decision by deleting the provisions above and replace them with the notice +* and other provisions required by the GPL or the LGPL. If you do not delete +* the provisions above, a recipient may use your version of this file under +* the terms of any one of the NPL, the GPL or the LGPL. +* +* Lets us have address book code and still run on 10.1. When/if 10.1 +* support goes away, merge this into SmartBookmarkManger class. +* +* ***** END LICENSE BLOCK ***** */ + +#import + + + +@interface AddressBookManager : NSObject +{ + // + // leave this an id for 10.1 support. when you go to 10.2 + // this whole class should disappear, and the methods should just + // get merged into KindaSmartFolderManager. + id mAddressBookFolder; +} + +-(void)fillAddressBook:(NSNotification *)note; + + +@end diff --git a/camino/src/bookmarks/AddressBookManager.m b/camino/src/bookmarks/AddressBookManager.m new file mode 100644 index 000000000000..e7b57307b8e1 --- /dev/null +++ b/camino/src/bookmarks/AddressBookManager.m @@ -0,0 +1,91 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** +* Version: NPL 1.1/GPL 2.0/LGPL 2.1 +* +* The contents of this file are subject to the Netscape Public License +* Version 1.1 (the "License"); you may not use this file except in +* compliance with the License. You may obtain a copy of the License at +* http://www.mozilla.org/NPL/ +* +* Software distributed under the License is distributed on an "AS IS" basis, +* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +* for the specific language governing rights and limitations under the +* License. +* +* The Original Code is mozilla.org code. +* +* The Initial Developer of the Original Code is +* Netscape Communications Corporation. +* Portions created by the Initial Developer are Copyright (C) 2002 +* the Initial Developer. All Rights Reserved. +* +* Contributor(s): +* David Haas +* +* +* Alternatively, the contents of this file may be used under the terms of +* either the GNU General Public License Version 2 or later (the "GPL"), or +* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +* in which case the provisions of the GPL or the LGPL are applicable instead +* of those above. If you wish to allow use of your version of this file only +* under the terms of either the GPL or the LGPL, and not to allow others to +* use your version of this file under the terms of the NPL, indicate your +* decision by deleting the provisions above and replace them with the notice +* and other provisions required by the GPL or the LGPL. If you do not delete +* the provisions above, a recipient may use your version of this file under +* the terms of any one of the NPL, the GPL or the LGPL. +* +* Lets us have address book code and still run on 10.1. When/if 10.1 +* support goes away, merge this into KindaSmartFolderManger class. +* +* ***** END LICENSE BLOCK ***** */ + +#import "AddressBookManager.h" +#import + + +@implementation AddressBookManager + +-(id)initWithFolder:(id)folder +{ + if ((self = [super init])) { + mAddressBookFolder = [folder retain]; + NSNotificationCenter *nc = [NSNotificationCenter defaultCenter]; + [nc addObserver:self selector:@selector(fillAddressBook:) name:kABDatabaseChangedNotification object:nil]; + [nc addObserver:self selector:@selector(fillAddressBook:) name: kABDatabaseChangedExternallyNotification object:nil]; + [self fillAddressBook:nil]; + } + return self; +} + +-(void)dealloc +{ + [[NSNotificationCenter defaultCenter] removeObserver:self]; + [mAddressBookFolder release]; + [super dealloc]; +} + +-(void)fillAddressBook:(NSNotification *)note +{ + // to fill, you must empty. + unsigned i, count = [mAddressBookFolder count]; + for (i=0; i < count; i++) + [mAddressBookFolder deleteChild:[mAddressBookFolder objectAtIndex:0]]; + // fill address book with people. could probably do this smarter, + // but it's a start for now. + ABAddressBook *ab = [ABAddressBook sharedAddressBook]; + NSEnumerator *peopleEnumerator = [[ab people] objectEnumerator]; + ABPerson* person; + NSString *name, *homepage; + while ((person = [peopleEnumerator nextObject])) { + homepage = [person valueForProperty:kABHomePageProperty]; + if ([homepage length] > 0) { + name = [NSString stringWithFormat:@"%@ %@",[person valueForProperty:kABFirstNameProperty],[person valueForProperty:kABLastNameProperty]]; + id bookmark = [mAddressBookFolder addBookmark]; + [bookmark setTitle:name]; + [bookmark setUrl:homepage]; + } + } +} + +@end diff --git a/camino/src/bookmarks/Bookmark.h b/camino/src/bookmarks/Bookmark.h new file mode 100644 index 000000000000..e999e89e66d9 --- /dev/null +++ b/camino/src/bookmarks/Bookmark.h @@ -0,0 +1,79 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** +* Version: NPL 1.1/GPL 2.0/LGPL 2.1 +* +* The contents of this file are subject to the Netscape Public License +* Version 1.1 (the "License"); you may not use this file except in +* compliance with the License. You may obtain a copy of the License at +* http://www.mozilla.org/NPL/ +* +* Software distributed under the License is distributed on an "AS IS" basis, +* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +* for the specific language governing rights and limitations under the +* License. +* +* The Original Code is mozilla.org code. +* +* The Initial Developer of the Original Code is +* Netscape Communications Corporation. +* Portions created by the Initial Developer are Copyright (C) 2002 +* the Initial Developer. All Rights Reserved. +* +* Contributor(s): +* David Haas +* +* +* Alternatively, the contents of this file may be used under the terms of +* either the GNU General Public License Version 2 or later (the "GPL"), or +* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +* in which case the provisions of the GPL or the LGPL are applicable instead +* of those above. If you wish to allow use of your version of this file only +* under the terms of either the GPL or the LGPL, and not to allow others to +* use your version of this file under the terms of the NPL, indicate your +* decision by deleting the provisions above and replace them with the notice +* and other provisions required by the GPL or the LGPL. If you do not delete +* the provisions above, a recipient may use your version of this file under +* the terms of any one of the NPL, the GPL or the LGPL. +* +* ***** END LICENSE BLOCK ***** */ + +#import "BookmarkItem.h" + +//Status Flags +#define kBookmarkOKStatus 0 +#define kBookmarkBrokenLinkStatus 1 +#define kBookmarkMovedLinkStatus 2 +#define kBookmarkServerErrorStatus 3 +#define kBookmarkNeverCheckStatus 5 +#define kBookmarkSpacerStatus 9 + +@interface Bookmark : BookmarkItem //DBME +{ + NSString* mURL; //DBMu + NSDate* mLastVisit; //DBMv + NSNumber* mStatus; + NSNumber* mNumberOfVisits; +} + +-(NSString *) url; +-(NSDate *) lastVisit; +-(unsigned) numberOfVisits; +-(unsigned) status; +-(BOOL) isMoved; +-(BOOL) isCheckable; +-(BOOL) isSeparator; +-(BOOL) isSick; //DHIs + +-(void) setUrl:(NSString *)aURL; +-(void) setLastVisit:(NSDate *)aLastVisit; +-(void) setStatus:(unsigned)aStatus; +-(void) setIsSeparator:(BOOL)aSeparatorFlag; +-(void) setNumberOfVisits:(unsigned)aNumber; + + +// functions aiding in checking for updates +-(NSURL *)urlAsURL; +-(void) checkForUpdate; + +@end + diff --git a/camino/src/bookmarks/Bookmark.mm b/camino/src/bookmarks/Bookmark.mm new file mode 100644 index 000000000000..07dea3e731f2 --- /dev/null +++ b/camino/src/bookmarks/Bookmark.mm @@ -0,0 +1,632 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** +* Version: NPL 1.1/GPL 2.0/LGPL 2.1 +* +* The contents of this file are subject to the Netscape Public License +* Version 1.1 (the "License"); you may not use this file except in +* compliance with the License. You may obtain a copy of the License at +* http://www.mozilla.org/NPL/ +* +* Software distributed under the License is distributed on an "AS IS" basis, +* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +* for the specific language governing rights and limitations under the +* License. +* +* The Original Code is mozilla.org code. +* +* The Initial Developer of the Original Code is +* Netscape Communications Corporation. +* Portions created by the Initial Developer are Copyright (C) 2002 +* the Initial Developer. All Rights Reserved. +* +* Contributor(s): +* David Haas +* +* +* Alternatively, the contents of this file may be used under the terms of +* either the GNU General Public License Version 2 or later (the "GPL"), or +* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +* in which case the provisions of the GPL or the LGPL are applicable instead +* of those above. If you wish to allow use of your version of this file only +* under the terms of either the GPL or the LGPL, and not to allow others to +* use your version of this file under the terms of the NPL, indicate your +* decision by deleting the provisions above and replace them with the notice +* and other provisions required by the GPL or the LGPL. If you do not delete +* the provisions above, a recipient may use your version of this file under +* the terms of any one of the NPL, the GPL or the LGPL. +* +* ***** END LICENSE BLOCK ***** */ +#import + +#import "Bookmark.h" +#import "BookmarkFolder.h" +#import "BookmarkManager.h" +#import "BookmarksClient.h" +#import "NSString+Utils.h" +#import "SiteIconProvider.h" + +@interface Bookmark (Private) +-(void) siteIconCheck:(NSNotification *)aNote; +-(void) urlLoadCheck:(NSNotification *)aNote; +-(void) invalidSchemeForUpdating; +-(BOOL) hostIsReachable:(NSURL *)aURL; +-(void) doHTTPUpdateRequest:(NSURL *)aURL; +-(void) doFileUpdateRequest:(NSURL *)aURL; +-(void) interpretHTTPUpdateCode:(UInt32) aCode; +-(void) cleanupHTTPCheck:(NSTimer *)aTimer; +-(NSString *)userAgentString; +@end + +// Constants - for update status checking +static const CFOptionFlags kNetworkEvents = kCFStreamEventEndEncountered | kCFStreamEventErrorOccurred; + +// Notification of URL load +NSString *URLLoadNotification = @"url_load"; +NSString *URLLoadSuccessKey = @"url_bool"; + +@implementation Bookmark + +-(id) init +{ + if ((self = [super init])) { + mURL = [[NSString alloc] init]; + NSNumber *tempNumber = [[NSNumber alloc] initWithUnsignedInt:0]; + mStatus = [tempNumber retain]; + mNumberOfVisits = [tempNumber retain]; + [tempNumber release]; + mLastVisit = [[NSDate date] retain]; + mIcon = [[NSImage imageNamed:@"smallbookmark"] retain]; + // register for notifications + NSNotificationCenter *nc = [NSNotificationCenter defaultCenter]; + [nc addObserver:self selector:@selector(siteIconCheck:) name:SiteIconLoadNotificationName object:nil]; + [nc addObserver:self selector:@selector(urlLoadCheck:) name:URLLoadNotification object:nil]; + } + return self; +} + + +-(id) copyWithZone:(NSZone *)zone +{ + id doppleganger = [super copyWithZone:zone]; + [doppleganger setUrl:[self url]]; + [doppleganger setStatus:[self status]]; + [doppleganger setLastVisit:[self lastVisit]]; + [doppleganger setNumberOfVisits:[self numberOfVisits]]; + return doppleganger; +} + +- (void) dealloc +{ + [[NSNotificationCenter defaultCenter] removeObserver:self]; + mParent = NULL; // not retained, so just set to null + [mURL release]; + [mLastVisit release]; + [mStatus release]; + [mNumberOfVisits release]; + [super dealloc]; +} + +// set/get properties + +-(NSString *) url +{ + return mURL; +} + +-(NSDate *) lastVisit +{ + return mLastVisit; +} + +-(unsigned) status +{ + return [mStatus unsignedIntValue]; +} + +-(unsigned) numberOfVisits +{ + return [mNumberOfVisits unsignedIntValue]; +} + +-(BOOL) isSeparator +{ + if ([self status]==kBookmarkSpacerStatus) + return YES; + return NO; +} + +-(BOOL) isMoved +{ + if ([self status]==kBookmarkMovedLinkStatus) + return YES; + return NO; +} + +-(BOOL) isCheckable +{ + unsigned myStatus = [self status]; + if (myStatus!=kBookmarkNeverCheckStatus && + myStatus!=kBookmarkBrokenLinkStatus && + myStatus!=kBookmarkSpacerStatus) + return YES; + return NO; +} + +-(BOOL) isSick +{ + if (([self status]==kBookmarkBrokenLinkStatus) || ([self status]==kBookmarkServerErrorStatus)) + return YES; + return NO; +} + +-(void) setStatus:(unsigned)aStatus +{ + if ((aStatus != [mStatus unsignedIntValue]) && + ((aStatus == kBookmarkBrokenLinkStatus) || + (aStatus == kBookmarkMovedLinkStatus) || + (aStatus == kBookmarkOKStatus) || + (aStatus == kBookmarkSpacerStatus) || + (aStatus == kBookmarkServerErrorStatus) || + (aStatus == kBookmarkNeverCheckStatus))) + { + [mStatus release]; + mStatus = [[NSNumber alloc] initWithUnsignedInt:aStatus]; + [self itemUpdatedNote]; + if (aStatus == kBookmarkSpacerStatus) + [self setTitle:NSLocalizedString(@"",@"")]; + } +} + +- (void) setUrl:(NSString *)aURL +{ + if (aURL) { + [aURL retain]; + [mURL release]; + mURL = aURL; + [self setStatus:kBookmarkOKStatus]; + [self refreshIcon]; + } +} + +- (void) setLastVisit:(NSDate *)aDate +{ + if (aDate) { + [aDate retain]; + [mLastVisit release]; + mLastVisit = aDate; + } +} + +-(void) setNumberOfVisits:(unsigned)aNumber +{ + [mNumberOfVisits release]; + mNumberOfVisits = [[NSNumber alloc] initWithUnsignedInt:aNumber]; + [self itemUpdatedNote]; +} + +-(void) setIsSeparator:(BOOL)aBool +{ + if (aBool) + [self setStatus:kBookmarkSpacerStatus]; + else + [self setStatus:kBookmarkOKStatus]; +} + +-(void) refreshIcon +{ + if ([BookmarkManager sharedBookmarkManager]) { + [[SiteIconProvider sharedFavoriteIconProvider] loadFavoriteIcon:self forURI:[self url] allowNetwork:NO]; + } +} + +-(void) siteIconCheck:(NSNotification *)note +{ + NSDictionary *userInfo = [note userInfo]; + if (userInfo) { + id loadTarget = [note object]; + NSString *iconString = [userInfo objectForKey:SiteIconLoadUserDataKey]; + if (iconString) { + NSURL *iconURL= [NSURL URLWithString:[NSString escapedURLString:iconString]]; + NSURL *myURL = [NSURL URLWithString:[NSString escapedURLString:[self url]]]; + if ((loadTarget == self) || [iconURL isEqual:myURL] || [[[iconURL host] stripWWW] isEqualToString:[[myURL host] stripWWW]]) { + NSImage *iconImage = [userInfo objectForKey:SiteIconLoadImageKey]; + if (iconImage) + [self setIcon:iconImage]; + } + } + } +} +// +// from loads, we only get success or failure - not why things failed. +// so we'll call everything "server error" +// +-(void) urlLoadCheck:(NSNotification *)note +{ + NSString *loadedURL = [note object]; + if ([loadedURL hasSuffix:@"/"]) + loadedURL = [loadedURL substringToIndex:([loadedURL length]-1)]; + NSString *myURL = [self url]; + if ([myURL hasSuffix:@"/"]) + myURL = [myURL substringToIndex:([myURL length]-1)]; + if ([loadedURL isEqualToString:myURL]) { + unsigned loadStatus = [[[note userInfo] objectForKey:URLLoadSuccessKey] unsignedIntValue]; + [self setLastVisit:[NSDate date]]; + [self setStatus:loadStatus]; + if (loadStatus == kBookmarkOKStatus) + [self setNumberOfVisits:([self numberOfVisits]+1)]; + } +} + +#pragma mark - +// +// handles checking for updates & whatnot of itself +// + +-(NSURL *)urlAsURL +{ + // We'll just assume if there's a # it's a fragment marker. Yes, + // this is almost certainly a bug, but just for checking status so who cares. + NSString *escapedURL = (NSString *)CFURLCreateStringByAddingPercentEscapes + (NULL,(CFStringRef)[self url],CFSTR("#"),NULL,kCFStringEncodingUTF8); + NSURL *myURL = [NSURL URLWithString:escapedURL]; + [escapedURL release]; + return myURL; +} + +// for now, we'll only check updates on file or http scheme. +// don't know if https will work. +- (void) checkForUpdate; +{ + NSURL* myURL = [self urlAsURL]; + if (myURL) { + if ([[myURL scheme] isEqualToString:@"http"] && [self hostIsReachable:myURL]) + [self doHTTPUpdateRequest:myURL]; + else if ([[myURL scheme] isEqualToString:@"file"]) + [self doFileUpdateRequest:myURL]; + else + [self setStatus:kBookmarkNeverCheckStatus]; + } +} + +- (BOOL) hostIsReachable:(NSURL *)aURL +{ + const char *hostname = [[aURL host] cString]; + if (!hostname) + return NO; + SCNetworkConnectionFlags flags; + // just like TN 1145 instructs, not that we're using CodeWarrior + assert(sizeof(SCNetworkConnectionFlags) == sizeof(int)); + BOOL isReachable = NO; + if ( SCNetworkCheckReachabilityByName(hostname, &flags) ) { + isReachable = !(flags & kSCNetworkFlagsConnectionRequired) && (flags & kSCNetworkFlagsReachable); + } + return isReachable; +} +// +// CF Callback functions for handling bookmark updating +// + +static void doHTTPUpdateCallBack(CFReadStreamRef stream, CFStreamEventType type, void *bookmark) +{ + CFHTTPMessageRef aResponse = NULL; + CFStreamError anError; + NSString *newURL = NULL; + UInt32 errCode; + switch (type){ + + case kCFStreamEventEndEncountered: + aResponse = (CFHTTPMessageRef) CFReadStreamCopyProperty(stream,kCFStreamPropertyHTTPResponseHeader); + if (aResponse) { + errCode = CFHTTPMessageGetResponseStatusCode(aResponse); + switch (errCode) { + case 301: //permanent move - update URL + newURL = (NSString*)CFHTTPMessageCopyHeaderFieldValue(aResponse,CFSTR("Location")); + [(Bookmark *)bookmark setUrl:newURL]; + [newURL release]; + break; + default: + [(Bookmark *)bookmark interpretHTTPUpdateCode:errCode]; + break; + } + } else //beats me. blame the server. + [(Bookmark *)bookmark interpretHTTPUpdateCode:500]; + break; + + case kCFStreamEventErrorOccurred: + anError = CFReadStreamGetError(stream); + if (anError.domain == kCFStreamErrorDomainHTTP) { + errCode = anError.error; //signed being assigned to unsinged. oh well + [(Bookmark *)bookmark interpretHTTPUpdateCode:errCode]; + } else // call it server error + [(Bookmark *)bookmark interpretHTTPUpdateCode:500]; + break; + + default: + NSLog(@"If you can read this you're too close to the screen."); + break; + } + // + // update our last visit date & cleanup + // + [(Bookmark *)bookmark setLastVisit:[NSDate date]]; + if (aResponse) + CFRelease(aResponse); +} + +// borrowed heavily from Apple's CFNetworkHTTPDownload example +-(void) doHTTPUpdateRequest:(NSURL *)aURL +{ + CFHTTPMessageRef messageRef = NULL; + CFReadStreamRef readStreamRef = NULL; + CFStreamClientContext ctxt= { 0, (void*)self, NULL, NULL, NULL }; //pointer to self lets us update + // get message + messageRef = CFHTTPMessageCreateRequest(kCFAllocatorDefault, CFSTR("HEAD"), (CFURLRef)aURL, kCFHTTPVersion1_1); + if (!messageRef) { + NSLog(@"CheckForUpdate: Can't create CFHTTPMessage for %@",aURL); + return; + } + // set if-modified-since header field, and maybe others if we're bored. + // really, since we just want to see if it's there, don't even need to + // do this. + NSString *httpDate = [[self lastVisit] descriptionWithCalendarFormat:@"%a, %d %b %Y %H:%M:%S GMT" timeZone:[NSTimeZone timeZoneWithAbbreviation:@"GMT"] locale:nil]; + NSString *userAgent = [self userAgentString]; + CFHTTPMessageSetHeaderFieldValue(messageRef,CFSTR("If-Modified-Since"),(CFStringRef)httpDate); + CFHTTPMessageSetHeaderFieldValue(messageRef,CFSTR("User-Agent"),(CFStringRef)userAgent); + + //setup read stream + readStreamRef = CFReadStreamCreateForHTTPRequest(kCFAllocatorDefault, messageRef); + CFRelease(messageRef); + if (!readStreamRef) { + NSLog(@"CheckForUpdate: Can't create CFReadStream for %@",aURL); + return; + } + + // handle http proxy, if necessary + NSDictionary* proxyConfigDict = (NSDictionary *)SCDynamicStoreCopyProxies(NULL); + if (proxyConfigDict) { + if ([[proxyConfigDict objectForKey:(NSString*)kSCPropNetProxiesHTTPEnable] intValue] != 0) { + NSString *proxyURL = [proxyConfigDict objectForKey:(NSString*)kSCPropNetProxiesHTTPProxy]; + NSNumber *proxyPort = [proxyConfigDict objectForKey:(NSString*)kSCPropNetProxiesHTTPPort]; + if (proxyURL && proxyPort) { + CFHTTPReadStreamSetProxy(readStreamRef,(CFStringRef)proxyURL,(CFIndex)[proxyPort unsignedIntValue]); + } + } + [proxyConfigDict release]; + } + + //setup callback function + if (CFReadStreamSetClient(readStreamRef, kNetworkEvents, doHTTPUpdateCallBack, &ctxt ) == false ) { + NSLog(@"CheckForUpdate: Can't set CFReadStream callback for %@",aURL); + CFRelease(readStreamRef); + return; + } + //schedule the stream & open the connection + CFReadStreamScheduleWithRunLoop(readStreamRef, CFRunLoopGetCurrent(), kCFRunLoopCommonModes); + if (CFReadStreamOpen(readStreamRef) == false ){ + NSLog(@"CheckForUpdate: Can't open CFReadStream for %@",aURL); + CFReadStreamSetClient(readStreamRef, NULL, NULL, NULL); + CFRelease(readStreamRef); + return; + } + //schedule a timeout. we'll give it, oh, 60 seconds before killing the check + //this timer is responsible for cleaning up the read stream memory!!!!!!!!! + [NSTimer scheduledTimerWithTimeInterval:60 target:self selector:@selector(cleanupHTTPCheck:) userInfo:(id)readStreamRef repeats:NO]; +} + +// My interpretation of what should & shouldn't happen is +// quite possibly incorrect. Feel free to adjust. +-(void) interpretHTTPUpdateCode:(UInt32) errCode +{ + switch (errCode){ + case 200: //OK - bookmark updated + case 203: //Non-authoritative info - call it same as OK + case 302: //Found - new link, but don't update + case 303: //See other - new link, but don't update + case 304: //Not modified - do nothing + case 307: //Temporary redirect - new link, but don't update + [self setStatus:kBookmarkOKStatus]; + break; + + case 300: //multiple choices - not sure what to do so we'll bail here + case 301: //Moved permananently - should be handled in callback + case 305: //Use proxy (specified) - should retry request + [self setStatus:kBookmarkMovedLinkStatus]; + break; + + case 400: //Bad request - we f'd up + case 403: //Forbidden - not good + case 404: //Not found - clearly not good + case 410: //Gone - nah nah nahnah, nah nah nahnah, hey hey hey, etc. + [self setStatus:kBookmarkBrokenLinkStatus]; + break; + + + case 401: //Unauthorized - need to be clever about checking this + case 402: //Payment required - funk that. + case 405: //Method not allowed - funk that, too. + case 406: //Not Acceptable - shouldn't happen, but oh well. + case 407: //Proxy Authentication Required - need to be cleverer here + case 411: //Length required - need to be cleverer + case 413: //Request entity too large - shouldn't happen + case 414: //Request URI too large - shouldn't happen + case 415: //Request URI too large - shouldn't happen + case 500: //Internal server error + case 501: //Not Implemented + case 502: //Bad Gateway + case 503: //Service Unavailable + case 504: //Gateway Timeout + case 505: //HTTP Version not supported + [self setStatus:kBookmarkServerErrorStatus]; + break; + + case 100: //Continue - just ignore + case 101: //Switching protocols - not that smart yet + case 201: //Created - shouldn't happen + case 202: //Accepted - shouldn't happen + case 204: //No content - shouldn't happen + case 205: //Reset content - shouldn't happen + case 206: //Partial content - shouldn't happen for HEAD request + case 408: //Request timeout - shouldn't happen + case 409: //Conflict - shouldn't happen + case 412: //Precondintion failed - shouldn't happen + case 416: //requested range not satisfiable - shouldn't happen + case 417: //Expectation failed - shouldn't happen + default: + break; + } +} + +-(void) doFileUpdateRequest:(NSURL *)aURL +{ + // if it's here, it's got a scheme of file, so we can call path directly + NSFileManager *fM = [NSFileManager defaultManager]; + if (![fM fileExistsAtPath:[aURL path]]) + [self setStatus:kBookmarkBrokenLinkStatus]; +} + +// this function cleans up after our stream request. +// if you get rid of it, we leak memory +-(void) cleanupHTTPCheck:(NSTimer *)aTimer; +{ + CFReadStreamRef stream = (CFReadStreamRef)[aTimer userInfo]; + CFReadStreamSetClient(stream,NULL,NULL,NULL); + CFRelease(stream); +} + +// this is done poorly. if someone feels like doing this more correctly, more power to you. +-(NSString *)userAgentString +{ + return [NSString stringWithString:@"Mozilla/5.0 (Macintosh; U; PPC Mac OS X Mach-O) Gecko Camino"]; +} + + +#pragma mark - +// +// for reading/writing from/to disk +// + +NSString *BMTitleKey = @"Title"; +NSString *BMDescKey = @"Description"; +NSString *BMStatusKey = @"Status"; +NSString *BMURLKey = @"URL"; +NSString *BMKeywordKey = @"Keyword"; +NSString *BMLastVisitKey = @"LastVisitedDate"; +NSString *BMNumberVisitsKey = @"VisitCount"; + +NSString *SafariURIDictKey = @"URIDictionary"; +NSString *SafariBookmarkTitleKey = @"title"; +NSString *SafariURLStringKey = @"URLString"; + +-(BOOL) readNativeDictionary:(NSDictionary *)aDict +{ + [self setTitle:[aDict objectForKey:BMTitleKey]]; + [self setDescription:[aDict objectForKey:BMDescKey]]; + [self setKeyword:[aDict objectForKey:BMKeywordKey]]; + [self setUrl:[aDict objectForKey:BMURLKey]]; + [self setLastVisit:[aDict objectForKey:BMLastVisitKey]]; + [self setNumberOfVisits:[[aDict objectForKey:BMNumberVisitsKey] unsignedIntValue]]; + [self setStatus:[[aDict objectForKey:BMStatusKey] unsignedIntValue]]; + return YES; +} + +-(BOOL) readSafariDictionary:(NSDictionary *)aDict +{ + NSDictionary *uriDict = [aDict objectForKey:SafariURIDictKey]; + [self setTitle:[uriDict objectForKey:SafariBookmarkTitleKey]]; + [self setUrl:[aDict objectForKey:SafariURLStringKey]]; + return YES; +} + +-(BOOL) readCaminoXML:(CFXMLTreeRef)aTreeRef +{ + CFXMLNodeRef myNode; + CFXMLElementInfo* elementInfoPtr; + myNode = CFXMLTreeGetNode(aTreeRef); + if (myNode) { + // Process our info + if (CFXMLNodeGetTypeCode(myNode)==kCFXMLNodeTypeElement){ + elementInfoPtr = (CFXMLElementInfo *)CFXMLNodeGetInfoPtr(myNode); + if (elementInfoPtr) { + NSDictionary* attribDict = (NSDictionary*)elementInfoPtr->attributes; + [self setTitle:[[attribDict objectForKey:@"name"] stringByRemovingAmpEscapes]]; + [self setKeyword:[[attribDict objectForKey:@"id"] stringByRemovingAmpEscapes]]; + [self setDescription:[[attribDict objectForKey:@"description"] stringByRemovingAmpEscapes]]; + [self setUrl:[[attribDict objectForKey:@"href"] stringByRemovingAmpEscapes]]; + } else { + NSLog(@"Bookmark:readCaminoXML - elementInfoPtr null, load failed"); + return NO; + } + } else { + NSLog(@"Bookmark:readCaminoXML - node not kCFXMLNodeTypeElement, load failed"); + return NO; + } + } else { + NSLog(@"Bookmark:readCaminoXML - urk! CFXMLTreeGetNode null, load failed"); + return NO; + } + return YES; +} + +// for plist in native format +-(NSDictionary *)writeNativeDictionary +{ + NSDictionary* dict; + if (![self isSeparator]) { + dict = [NSDictionary dictionaryWithObjectsAndKeys: + mTitle,BMTitleKey, + mKeyword,BMKeywordKey, + mURL,BMURLKey, + mDescription,BMDescKey , + mLastVisit,BMLastVisitKey, + mNumberOfVisits,BMNumberVisitsKey, + mStatus,BMStatusKey, + nil]; + } else { + dict = [NSDictionary dictionaryWithObjectsAndKeys: + mStatus,BMStatusKey, + nil]; + } + return dict; +} + + +-(NSString *)writeHTML:(unsigned int)aPad +{ + NSString*formatString; + NSMutableString *padString = [NSMutableString string]; + for (unsigned i = 0;i 0) + formatString = [NSString stringWithString:@"%@
%s\n%@
%s\n"]; + else + formatString = [NSString stringWithString:@"%@
%s\n%@\n"]; + return [NSString stringWithFormat:formatString, + padString, + [[self url] UTF8String], + [[mTitle stringByAddingAmpEscapes] UTF8String], + padString, + [[mDescription stringByAddingAmpEscapes] UTF8String]]; +} + +// +// Scripting methods +// + +- (NSScriptObjectSpecifier *)objectSpecifier +{ + id parent = [self parent]; + NSScriptObjectSpecifier *containerRef = [parent objectSpecifier]; + unsigned index = [[parent childArray] indexOfObjectIdenticalTo:self]; + if (index != NSNotFound) { + // Man, this took forever to figure out. + // DHBookmarks always contained in BookmarkFolder - so make + // sure we set that as the container class description. + NSScriptClassDescription *aRef = (NSScriptClassDescription*)[NSClassDescription classDescriptionForClass:[BookmarkFolder class]]; + return [[[NSIndexSpecifier allocWithZone:[self zone]] initWithContainerClassDescription:aRef containerSpecifier:containerRef key:@"childArray" index:index] autorelease]; + } else + return nil; +} + +@end + diff --git a/camino/src/bookmarks/BookmarkButton.h b/camino/src/bookmarks/BookmarkButton.h new file mode 100644 index 000000000000..abdb3fed5c82 --- /dev/null +++ b/camino/src/bookmarks/BookmarkButton.h @@ -0,0 +1,62 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** +* Version: NPL 1.1/GPL 2.0/LGPL 2.1 +* +* The contents of this file are subject to the Netscape Public License +* Version 1.1 (the "License"); you may not use this file except in +* compliance with the License. You may obtain a copy of the License at +* http://www.mozilla.org/NPL/ +* +* Software distributed under the License is distributed on an "AS IS" basis, +* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +* for the specific language governing rights and limitations under the +* License. +* +* The Original Code is mozilla.org code. +* +* The Initial Developer of the Original Code is +* Netscape Communications Corporation. +* Portions created by the Initial Developer are Copyright (C) 2002 +* the Initial Developer. All Rights Reserved. +* +* Contributor(s): +* David Hyatt (Original Author) +* David Haas +* +* +* Alternatively, the contents of this file may be used under the terms of +* either the GNU General Public License Version 2 or later (the "GPL"), or +* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +* in which case the provisions of the GPL or the LGPL are applicable instead +* of those above. If you wish to allow use of your version of this file only +* under the terms of either the GPL or the LGPL, and not to allow others to +* use your version of this file under the terms of the NPL, indicate your +* decision by deleting the provisions above and replace them with the notice +* and other provisions required by the GPL or the LGPL. If you do not delete +* the provisions above, a recipient may use your version of this file under +* the terms of any one of the NPL, the GPL or the LGPL. +* +* ***** END LICENSE BLOCK ***** */ + +#import + +@class BookmarkItem; + +@interface BookmarkButton : NSButton +{ + BookmarkItem* mItem; +} + +-(id)initWithFrame:(NSRect)frame item:(BookmarkItem*)item; + +- (void)setBookmarkItem:(BookmarkItem*)anItem; +- (BookmarkItem*)BookmarkItem; + +-(IBAction)openBookmark:(id)aSender; +-(IBAction)openBookmarkInNewTab:(id)aSender; +-(IBAction)openBookmarkInNewWindow:(id)aSender; +-(IBAction)showBookmarkInfo:(id)aSender; +-(IBAction)deleteBookmarks: (id)aSender; +-(IBAction)addFolder:(id)aSender; + +@end diff --git a/camino/src/bookmarks/BookmarkButton.mm b/camino/src/bookmarks/BookmarkButton.mm new file mode 100644 index 000000000000..501f154c0c5a --- /dev/null +++ b/camino/src/bookmarks/BookmarkButton.mm @@ -0,0 +1,331 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** +* Version: NPL 1.1/GPL 2.0/LGPL 2.1 +* +* The contents of this file are subject to the Netscape Public License +* Version 1.1 (the "License"); you may not use this file except in +* compliance with the License. You may obtain a copy of the License at +* http://www.mozilla.org/NPL/ +* +* Software distributed under the License is distributed on an "AS IS" basis, +* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +* for the specific language governing rights and limitations under the +* License. +* +* The Original Code is mozilla.org code. +* +* The Initial Developer of the Original Code is +* Netscape Communications Corporation. +* Portions created by the Initial Developer are Copyright (C) 2002 +* the Initial Developer. All Rights Reserved. +* +* Contributor(s): +* David Hyatt (Original Author) +* David Haas +* +* +* Alternatively, the contents of this file may be used under the terms of +* either the GNU General Public License Version 2 or later (the "GPL"), or +* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +* in which case the provisions of the GPL or the LGPL are applicable instead +* of those above. If you wish to allow use of your version of this file only +* under the terms of either the GPL or the LGPL, and not to allow others to +* use your version of this file under the terms of the NPL, indicate your +* decision by deleting the provisions above and replace them with the notice +* and other provisions required by the GPL or the LGPL. If you do not delete +* the provisions above, a recipient may use your version of this file under +* the terms of any one of the NPL, the GPL or the LGPL. +* +* ***** END LICENSE BLOCK ***** */ + +#import "BookmarkButton.h" +#import "NSString+Utils.h" +#import "NSArray+Utils.h" +#import "NSPasteboard+Utils.h" +#import "DraggableImageAndTextCell.h" +#import "BookmarkManager.h" +#import "Bookmark.h" +#import "BookmarkFolder.h" +#import "BookmarkMenu.h" +#import "BookmarkInfoController.h" +#import "BrowserWindowController.h" +#import "MainController.h" +#import "PreferenceManager.h" + +@interface BookmarkButton(Private) + +- (void)showFolderPopupAction:(id)aSender; +- (void)showFolderPopup:(NSEvent*)event; + +@end + + +@implementation BookmarkButton +- (id)initWithFrame:(NSRect)frame +{ + if ( (self = [super initWithFrame:frame]) ) + { + DraggableImageAndTextCell* newCell = [[[DraggableImageAndTextCell alloc] init] autorelease]; + [newCell setDraggable:YES]; + [self setCell:newCell]; + + [self setBezelStyle: NSRegularSquareBezelStyle]; + [self setButtonType: NSMomentaryChangeButton]; + [self setBordered: NO]; + [self setImagePosition: NSImageLeft]; + [self setRefusesFirstResponder: YES]; + [self setFont: [NSFont labelFontOfSize: 11.0]]; + } + return self; +} + +-(id)initWithFrame:(NSRect)frame item:(BookmarkItem*)item +{ + if ( (self = [self initWithFrame:frame]) ) + { + [self setBookmarkItem:item]; + } + return self; +} + +- (void)dealloc +{ + [mItem release]; + [super dealloc]; +} + + +- (void)setBookmarkItem:(BookmarkItem*)aItem +{ + [aItem retain]; + [mItem release]; + mItem = aItem; + [self setTitle:[aItem title]]; + [self setImage:[aItem icon]]; + [self setTarget:self]; + if ([aItem isKindOfClass:[Bookmark class]]) { + [self setAction:@selector(openBookmark:)]; + [self setToolTip:[(Bookmark *)aItem url]]; + } else { + [[self cell] setClickHoldTimeout:0.5]; + if ([(BookmarkFolder *)aItem isGroup]) + [self setAction: @selector(openBookmark:)]; + else + [self setAction:@selector(showFolderPopupAction:)]; + } +} + +- (BookmarkItem*)BookmarkItem +{ + return mItem; +} + +-(IBAction)openBookmark:(id)aSender +{ + BrowserWindowController* brController = [[self window] windowController]; + // See if we're a group. + BookmarkItem *item = [self BookmarkItem]; + if ([item isKindOfClass:[BookmarkFolder class]]) { + if ([(BookmarkFolder *)item isGroup]) { + [brController openTabGroup:[(BookmarkFolder *)item childURLs] replaceExistingTabs:YES]; + return; + } + } + // if the command key is down, follow the command-click pref + if (([[NSApp currentEvent] modifierFlags] & NSCommandKeyMask) && + [[PreferenceManager sharedInstance] getBooleanPref:"browser.tabs.opentabfor.middleclick" withSuccess:NULL]) + { + [self openBookmarkInNewTab:aSender]; + return; + } + [brController loadURL:[(Bookmark *)item url] referrer:nil activate:YES]; +} + +-(IBAction)openBookmarkInNewTab:(id)aSender +{ + BookmarkItem *item = [self BookmarkItem]; + BrowserWindowController* brController = [[self window] windowController]; + if ([item isKindOfClass:[Bookmark class]]) { + NSString* hrefStr = [(Bookmark *)item url]; + BOOL loadInBackground = [[PreferenceManager sharedInstance] getBooleanPref:"browser.tabs.loadInBackground" withSuccess:NULL]; + [brController openNewTabWithURL: hrefStr referrer:nil loadInBackground: loadInBackground]; + } else if ([item isKindOfClass:[BookmarkFolder class]]) { + [brController openTabGroup:[(BookmarkFolder *)item childURLs] replaceExistingTabs:YES]; + } +} + +-(IBAction)openBookmarkInNewWindow:(id)aSender +{ + BOOL loadInBackground = [[PreferenceManager sharedInstance] getBooleanPref:"browser.tabs.loadInBackground" withSuccess:NULL]; + BrowserWindowController* brController = [[self window] windowController]; + // See if we're a bookmark. + BookmarkItem *item = [self BookmarkItem]; + if ([item isKindOfClass:[Bookmark class]]) + [brController openNewWindowWithURL:[(Bookmark *)item url] referrer: nil loadInBackground: loadInBackground]; + // if we get to this else, we better be a group. check just in case i'm wrong. + else if ([item isKindOfClass:[BookmarkFolder class]]) { + if ([(BookmarkFolder *)item isGroup]) { + [brController openNewWindowWithGroupURLs:[(BookmarkFolder *)item childURLs] loadInBackground: loadInBackground]; + } + } + return; +} + +-(IBAction)showBookmarkInfo:(id)aSender +{ + BookmarkInfoController *bic = [BookmarkInfoController sharedBookmarkInfoController]; + [bic setBookmark:[self BookmarkItem]]; + [bic showWindow:self]; +} + +-(IBAction)deleteBookmarks: (id)aSender +{ + BookmarkItem *item = [self BookmarkItem]; + [[item parent] deleteChild:item]; + [self removeFromSuperview]; +} + +-(IBAction)addFolder:(id)aSender +{ + BookmarkManager* bmManager = [BookmarkManager sharedBookmarkManager]; + BookmarkFolder* toolbarFolder = [bmManager toolbarFolder]; + BookmarkFolder* aFolder = [toolbarFolder addBookmarkFolder]; + [aFolder setTitle:NSLocalizedString(@"NewBookmarkFolder",@"New Folder")]; +} + +-(void)drawRect:(NSRect)aRect +{ + [super drawRect: aRect]; +} + +-(NSMenu*)menuForEvent:(NSEvent*)aEvent +{ + BookmarkItem *item = [self BookmarkItem]; + if (item) { + NSMenu* contextMenu = [[[self superview] menu] copy]; + [[contextMenu itemArray] makeObjectsPerformSelector:@selector(setTarget:) withObject: self]; + NSString *nulString = [NSString string]; + // clean the menu out + int numItems = [contextMenu numberOfItems]; + int itemIndex = [contextMenu indexOfItemWithTarget:self andAction:@selector(showBookmarkInfo:)]; + while (numItems > (itemIndex+1)) + [contextMenu removeItemAtIndex:(--numItems)]; + // set up menu + if ([item isKindOfClass:[Bookmark class]]) { + itemIndex = [contextMenu indexOfItemWithTarget:self andAction:@selector(openBookmarkInNewWindow:)]; + [[contextMenu itemAtIndex:itemIndex] setTitle:NSLocalizedString(@"Open in New Window",@"Open in New Window")]; + itemIndex = [contextMenu indexOfItemWithTarget:self andAction:@selector(openBookmarkInNewTab:)]; + [[contextMenu itemAtIndex:itemIndex] setTitle:NSLocalizedString(@"Open in New Tab",@"Open in New Tab")]; + } else if ([item isKindOfClass:[BookmarkFolder class]]) { + itemIndex = [contextMenu indexOfItemWithTarget:self andAction:@selector(openBookmarkInNewWindow:)]; + [[contextMenu itemAtIndex:itemIndex] setTitle:NSLocalizedString(@"Open Tabs in New Window",@"Open Tabs in New Window")]; + itemIndex = [contextMenu indexOfItemWithTarget:self andAction:@selector(openBookmarkInNewTab:)]; + [[contextMenu itemAtIndex:itemIndex] setTitle:NSLocalizedString(@"Open in Tabs",@"Open in Tabs")]; + } + // if it's a button, it's got to be on toolbar folder, so we can delete & make new folders + NSMenuItem *menuItem = [[NSMenuItem alloc] initWithTitle:NSLocalizedString(@"Delete",@"Delete") action:@selector(deleteBookmarks:) keyEquivalent:nulString]; + [menuItem setTarget:self]; + [contextMenu addItem:menuItem]; + [menuItem release]; + [contextMenu addItem:[NSMenuItem separatorItem]]; + // create new folder + menuItem = [[NSMenuItem alloc] initWithTitle:NSLocalizedString(@"Create New Folder...",@"Create New Folder...") action:@selector(addFolder:) keyEquivalent:nulString]; + [menuItem setTarget:self]; + [contextMenu addItem:menuItem]; + [menuItem release]; + return [contextMenu autorelease]; + } + return nil; +} + +// +// context menu has only what we need +// +-(BOOL)validateMenuItem:(NSMenuItem*)aMenuItem +{ + return YES; +} + +- (void)showFolderPopupAction:(id)aSender +{ + [self showFolderPopup:[NSApp currentEvent]]; +} + +- (void)showFolderPopup:(NSEvent*)event +{ + + NSMenu* popupMenu = [[NSMenu alloc] init]; + // dummy first item + [popupMenu addItemWithTitle:@"" action:NULL keyEquivalent:@""]; + // make a temporary BookmarkMenu to build the menu + BookmarkMenu* bmMenu = [[BookmarkMenu alloc] initWithMenu:popupMenu firstItem:1 rootBookmarkFolder:(BookmarkFolder *)[self BookmarkItem]]; + // use a temporary NSPopUpButtonCell to display the menu. + NSPopUpButtonCell *popupCell = [[NSPopUpButtonCell alloc] initTextCell:@"" pullsDown:YES]; + [popupCell setMenu: popupMenu]; + [popupCell setFont:[NSFont labelFontOfSize: 11.0]]; + [popupCell trackMouse:event inRect:[self bounds] ofView:self untilMouseUp:YES]; + [popupCell release]; + [bmMenu release]; + [popupMenu release]; +} + +-(void)mouseDown:(NSEvent*)aEvent +{ + [super mouseDown:aEvent]; + if ([[self cell] lastClickHoldTimedOut]) + [self showFolderPopup:aEvent]; +} + +- (unsigned int)draggingSourceOperationMaskForLocal:(BOOL)localFlag +{ + if (localFlag) + return (NSDragOperationCopy | NSDragOperationGeneric | NSDragOperationMove); + + return (NSDragOperationDelete | NSDragOperationGeneric); +} + +- (void) mouseDragged: (NSEvent*) aEvent +{ + BookmarkItem *item = [self BookmarkItem]; + BOOL isSingleBookmark = [item isKindOfClass:[Bookmark class]]; + NSPasteboard *pboard = [NSPasteboard pasteboardWithName:NSDragPboard]; + NSString *title = [item title]; + if (isSingleBookmark) + { + [pboard declareURLPasteboardWithAdditionalTypes:[NSArray arrayWithObject:@"MozBookmarkType"] owner:self]; + NSString *url = [(Bookmark *)item url]; + NSString *cleanedTitle = [title stringByReplacingCharactersInSet:[NSCharacterSet controlCharacterSet] withString:@" "]; + [pboard setDataForURL:url title:cleanedTitle]; + } + else + { + [pboard declareTypes:[NSArray arrayWithObject:@"MozBookmarkType"] owner:self]; + } + // MozBookmarkType + NSArray *pointerArray = [NSArray dataArrayFromPointerArrayForMozBookmarkDrop:[NSArray arrayWithObject:item]]; + [pboard setPropertyList:pointerArray forType: @"MozBookmarkType"]; + [self dragImage: [MainController createImageForDragging:[self image] title:title] + at: NSMakePoint(0,NSHeight([self bounds])) offset: NSMakeSize(0,0) + event: aEvent pasteboard: pboard source: self slideBack: YES]; +} + +- (void)draggedImage:(NSImage *)anImage endedAt:(NSPoint)aPoint operation:(NSDragOperation)operation +{ + if (operation == NSDragOperationDelete) + { + NSPasteboard* pboard = [NSPasteboard pasteboardWithName:NSDragPboard]; + NSArray* bookmarks = [NSArray pointerArrayFromDataArrayForMozBookmarkDrop:[pboard propertyListForType: @"MozBookmarkType"]]; + if (bookmarks) + { + for (unsigned int i = 0; i < [bookmarks count]; ++i) + { + BookmarkItem* item = [bookmarks objectAtIndex:i]; + [[item parent] deleteChild:item]; + } + } + } +} + + + +@end diff --git a/camino/src/bookmarks/BookmarkFolder.h b/camino/src/bookmarks/BookmarkFolder.h new file mode 100644 index 000000000000..07bf013fcef5 --- /dev/null +++ b/camino/src/bookmarks/BookmarkFolder.h @@ -0,0 +1,124 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** +* Version: NPL 1.1/GPL 2.0/LGPL 2.1 +* +* The contents of this file are subject to the Netscape Public License +* Version 1.1 (the "License"); you may not use this file except in +* compliance with the License. You may obtain a copy of the License at +* http://www.mozilla.org/NPL/ +* +* Software distributed under the License is distributed on an "AS IS" basis, +* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +* for the specific language governing rights and limitations under the +* License. +* +* The Original Code is mozilla.org code. +* +* The Initial Developer of the Original Code is +* Netscape Communications Corporation. +* Portions created by the Initial Developer are Copyright (C) 2002 +* the Initial Developer. All Rights Reserved. +* +* Contributor(s): +* David Haas +* +* +* Alternatively, the contents of this file may be used under the terms of +* either the GNU General Public License Version 2 or later (the "GPL"), or +* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +* in which case the provisions of the GPL or the LGPL are applicable instead +* of those above. If you wish to allow use of your version of this file only +* under the terms of either the GPL or the LGPL, and not to allow others to +* use your version of this file under the terms of the NPL, indicate your +* decision by deleting the provisions above and replace them with the notice +* and other provisions required by the GPL or the LGPL. If you do not delete +* the provisions above, a recipient may use your version of this file under +* the terms of any one of the NPL, the GPL or the LGPL. +* +* ***** END LICENSE BLOCK ***** */ + +#import "BookmarkItem.h" +#import "BookmarksClient.h" + +//Special flags +enum { + kBookmarkFolder = 0, + kBookmarkFolderGroup = 1 << 0, + kBookmarkRootFolder = 1 << 1, + kBookmarkToolbarFolder = 1 << 2, + kBookmarkSmartFolder = 1 << 3, + kBookmarkDockMenuFolder = 1 << 4 +}; + + +//root AE code: DB14 +@class Bookmark; + +@interface BookmarkFolder : BookmarkItem //AE code: DBAE +{ + NSMutableArray* mChildArray; + NSNumber* mSpecialFlag; +} + +-(NSMutableArray *) childArray; +-(NSArray *) childURLs; +-(NSArray *) allChildBookmarks; +-(BOOL) isSpecial; +-(BOOL) isToolbar; +-(BOOL) isRoot; +-(BOOL) isGroup; +-(BOOL) isSmartFolder; +-(BOOL) isDockMenu; + +-(void) setChildArray:(NSMutableArray *)aChildArray; //should be private? +-(void) setIsGroup:(BOOL)aGroupFlag; //AE code: DBAg +-(void) setIsRoot:(BOOL)aFlag; +-(void) setIsToolbar:(BOOL)aFlag; +-(void) setIsSmartFolder:(BOOL)aFlag; +-(void) setIsDockMenu:(BOOL)aFlag; +-(void) makeDockMenu:(id)sender; + +// Things added to make it work sort of like an array +-(unsigned) count; +-(id) objectAtIndex:(unsigned)index; +-(unsigned)indexOfObject:(id)object; +-(unsigned)indexOfObjectIdenticalTo:(id)object; + +// ways to add a new bookmark +-(Bookmark *) addBookmark; //adds to end +-(Bookmark *) addBookmark:(NSString *)aTitle url:(NSString *)aURL inPosition:(unsigned)aIndex isSeparator:(BOOL)aBool; +-(Bookmark *) addBookmark:(NSString *)aTitle inPosition:(unsigned)aIndex keyword:(NSString *)aKeyword url:(NSString *)aURL description:(NSString *)aDescription lastVisit:(NSDate *)aDate status:(unsigned)aStatus isSeparator:(BOOL)aBool; + +// ways to add a new bookmark array +-(BookmarkFolder *) addBookmarkFolder; //adds to end +-(BookmarkFolder *) addBookmarkFolder:(NSString *)aTitle inPosition:(unsigned)aIndex isGroup:(BOOL)aFlag; + +// Moving & Copying & inserting bookmarks/bookmark arrays +-(void) insertChild:(BookmarkItem *)aChild; +-(void) insertChild:(BookmarkItem *)aChild atIndex:(unsigned)aIndex isMove:(BOOL)aBool; +-(void) moveChild:(BookmarkItem *)aChild toBookmarkFolder:(BookmarkFolder *)aNewParent atIndex:(unsigned)aIndex; +-(void) copyChild:(BookmarkItem *)aChild toBookmarkFolder:(BookmarkFolder *)aNewParent atIndex:(unsigned)aIndex; + +// Used for deleting bookmarks/bookmark arrays +-(BOOL) deleteChild:(BookmarkItem *)aChild; + +// Smart Folder only methods +-(void) insertIntoSmartFolderChild:(BookmarkItem *)aItem; +-(void) deleteFromSmartFolderChildAtIndex:(unsigned)index; + +// generation menus +-(void) buildFlatFolderList:(NSMenu *)menu depth:(unsigned)pad; + +// searching +-(NSArray *) resolveKeyword:(NSString *)aString; +-(NSSet *) bookmarksWithString:(NSString *)searchString; + +//Scripting - should be a protocol we could use for these +//two, but i'm not sure which one, so we'll declare them here +//and avoid the compiler warning +-(NSArray *) indicesOfObjectsByEvaluatingRelativeSpecifier:(NSRelativeSpecifier *)relSpec; +-(NSArray *) indicesOfObjectsByEvaluatingRangeSpecifier:(NSRangeSpecifier *)rangeSpec; + + + +@end diff --git a/camino/src/bookmarks/BookmarkFolder.mm b/camino/src/bookmarks/BookmarkFolder.mm new file mode 100644 index 000000000000..5a0b98ff8a04 --- /dev/null +++ b/camino/src/bookmarks/BookmarkFolder.mm @@ -0,0 +1,1275 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** +* Version: NPL 1.1/GPL 2.0/LGPL 2.1 +* +* The contents of this file are subject to the Netscape Public License +* Version 1.1 (the "License"); you may not use this file except in +* compliance with the License. You may obtain a copy of the License at +* http://www.mozilla.org/NPL/ +* +* Software distributed under the License is distributed on an "AS IS" basis, +* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +* for the specific language governing rights and limitations under the +* License. +* +* The Original Code is mozilla.org code. +* +* The Initial Developer of the Original Code is +* Netscape Communications Corporation. +* Portions created by the Initial Developer are Copyright (C) 2002 +* the Initial Developer. All Rights Reserved. +* +* Contributor(s): +* David Haas +* +* +* Alternatively, the contents of this file may be used under the terms of +* either the GNU General Public License Version 2 or later (the "GPL"), or +* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +* in which case the provisions of the GPL or the LGPL are applicable instead +* of those above. If you wish to allow use of your version of this file only +* under the terms of either the GPL or the LGPL, and not to allow others to +* use your version of this file under the terms of the NPL, indicate your +* decision by deleting the provisions above and replace them with the notice +* and other provisions required by the GPL or the LGPL. If you do not delete +* the provisions above, a recipient may use your version of this file under +* the terms of any one of the NPL, the GPL or the LGPL. +* +* ***** END LICENSE BLOCK ***** */ + +#import "BookmarkManager.h" +#import "BookmarkFolder.h" +#import "Bookmark.h" +#import "NSString+Utils.h" + +// Notification definitions +NSString* BookmarkFolderAdditionNotification = @"bf_add"; +NSString* BookmarkFolderDeletionNotification = @"bf_del"; +NSString* BookmarkFolderChildKey = @"bf_ck"; +NSString* BookmarkFolderChildIndexKey = @"bf_ik"; +NSString* BookmarkFolderDockMenuChangeNotificaton = @"bf_dmc"; + +@interface BookmarkFolder (Private) +// status stuff +-(unsigned) specialFlag; +-(void) setSpecialFlag:(unsigned)aNumber; +// ways to add a new bookmark +-(BOOL) addBookmarkFromNativeDict:(NSDictionary *)aDict; +-(BOOL) addBookmarkFromSafariDict:(NSDictionary *)aDict; +-(BOOL) addBookmarkFromXML:(CFXMLTreeRef)aTreeRef; +//ways to add a new bookmark folder +-(BOOL) addBookmarkFolderFromNativeDict:(NSDictionary *)aDict; //read in - adds sequentially +-(BOOL) addBookmarkFolderFromSafariDict:(NSDictionary *)aDict; +-(BOOL) addBookmarkFolderFromXML:(CFXMLTreeRef)aTreeRef; +// Deletes the bookmark or bookmark array +-(void) deleteBookmark:(Bookmark *)childBookmark; +-(void) deleteBookmarkFolder:(BookmarkFolder *)childArray; + //Notification methods +-(void) itemAddedNote:(BookmarkItem *)theItem atIndex:(unsigned)anIndex; +-(void) itemRemovedNote:(BookmarkItem *)theItem; +-(void) dockMenuChanged:(NSNotification *)note; +// aids in searching +- (NSString*) expandKeyword:(NSString*)keyword inString:(NSString*)location; +- (NSArray *) folderItemWithClass:(Class)theClass; +- (BOOL) isString:(NSString *)searchString inBookmarkItem:(BookmarkItem *)anItem; + +@end + +@implementation BookmarkFolder + +-(id) init +{ + if ((self = [super init])) + { + mChildArray = [[NSMutableArray alloc] init]; + mSpecialFlag = [[NSNumber alloc] initWithUnsignedInt:kBookmarkFolder]; + mIcon = [[NSImage imageNamed:@"folder"] retain]; + } + return self; +} + +-(id) copyWithZone:(NSZone *)zone +{ + if ([self isSmartFolder]) + return nil; + id doppleganger = [super copyWithZone:zone]; + [doppleganger setSpecialFlag:[self specialFlag]]; + NSEnumerator *enumerator = [[self childArray] objectEnumerator]; + id anItem, aCopiedItem; + //head fake the undomanager + NSUndoManager *undoManager = [[BookmarkManager sharedBookmarkManager] undoManager]; + [undoManager disableUndoRegistration]; + while ((anItem = [enumerator nextObject])) { + aCopiedItem = [anItem copyWithZone:[anItem zone]]; + [doppleganger insertChild:aCopiedItem]; + [aCopiedItem release]; + } + [undoManager enableUndoRegistration]; + return doppleganger; +} + +-(void)dealloc +{ + [[[BookmarkManager sharedBookmarkManager] undoManager] removeAllActionsWithTarget:self]; + // set our all children to have a null parent. + // important if they've got timers running. + NSEnumerator *enumerator = [mChildArray objectEnumerator]; + id kid; + while ((kid = [enumerator nextObject])) + [kid setParent:nil]; + [mChildArray release]; + [mSpecialFlag release]; + [super dealloc]; +} + +// do this so we can start update timers in bookmarks after load +-(void) scheduleTimer +{ + if (![self isSmartFolder]) + [[self childArray] makeObjectsPerformSelector:@selector(scheduleTimer)]; +} + +-(void) refreshIcon +{ + if (![self isSmartFolder]) + [[self childArray] makeObjectsPerformSelector:@selector(refreshIcon)]; +} + +// +// get/set properties +// + +-(NSMutableArray *) childArray +{ + return mChildArray; +} + +// only return bookmark URL's - if we've got folders, we ignore them. +-(NSArray *)childURLs +{ + NSMutableArray *urlArray = [NSMutableArray arrayWithCapacity:[self count]]; + NSEnumerator *enumerator = [[self childArray] objectEnumerator]; + id anItem; + while ((anItem = [enumerator nextObject])) { + if ([anItem isKindOfClass:[Bookmark class]]) + [urlArray addObject:[(Bookmark *)anItem url]]; + } + return urlArray; +} + +-(NSArray *) allChildBookmarks +{ + NSMutableArray *anArray = [NSMutableArray array]; + [anArray addObjectsFromArray:[self folderItemWithClass:[Bookmark class]]]; + NSEnumerator *enumerator = [[self folderItemWithClass:[BookmarkFolder class]] objectEnumerator]; + BookmarkFolder *kid; + while ((kid = [enumerator nextObject])) { + NSArray *kidsBookmarks = [kid allChildBookmarks]; + [anArray addObjectsFromArray:kidsBookmarks]; + } + return anArray; +} + +-(BOOL) isSpecial +{ + if ([self isToolbar] || [self isRoot] || [self isSmartFolder] || [self isDockMenu]) + return YES; + return NO; +} + +-(BOOL) isToolbar +{ + if (([self specialFlag] & kBookmarkToolbarFolder) != 0) + return YES; + return NO; +} + +-(BOOL) isRoot +{ + if (([self specialFlag] & kBookmarkRootFolder) != 0) + return YES; + return NO; +} + + +-(BOOL) isGroup +{ + if (([self specialFlag] & kBookmarkFolderGroup) != 0) + return YES; + return NO; +} + +-(BOOL) isSmartFolder +{ + if (([self specialFlag] & kBookmarkSmartFolder) != 0) + return YES; + return NO; +} + +-(BOOL) isDockMenu +{ + if (([self specialFlag] & kBookmarkDockMenuFolder) != 0) + return YES; + return NO; +} + +-(unsigned) specialFlag +{ + return [mSpecialFlag unsignedIntValue]; +} + + +// world of hurt if you use this incorrectly. +-(void) setChildArray:(NSMutableArray *)aChildArray +{ + if (aChildArray != mChildArray) { + [aChildArray retain]; + [mChildArray release]; //trashes everything the bookmark contained + mChildArray = aChildArray; + } +} + +-(void) setIsGroup:(BOOL)aBool +{ + unsigned curVal = [self specialFlag]; + if (aBool) + curVal |= kBookmarkFolderGroup; + else + curVal &= ~kBookmarkFolderGroup; + [self setSpecialFlag:curVal]; +} + +-(void) setSpecialFlag:(unsigned)aFlag +{ + [mSpecialFlag release]; + mSpecialFlag = [[NSNumber alloc] initWithUnsignedInt:aFlag]; + if ((aFlag & kBookmarkFolderGroup) != 0) + [self setIcon:[NSImage imageNamed:@"groupbookmark"]]; + else + [self setIcon:[NSImage imageNamed:@"folder"]]; + if ((aFlag & kBookmarkDockMenuFolder) != 0) { + NSNotificationCenter *nc = [NSNotificationCenter defaultCenter]; + [nc postNotificationName:BookmarkFolderDockMenuChangeNotificaton object:self]; + [nc addObserver:self selector:@selector(dockMenuChanged:) name:BookmarkFolderDockMenuChangeNotificaton object:nil]; + } +} + +-(void) setIsToolbar:(BOOL)aBool +{ + unsigned curVal = [self specialFlag]; + if (aBool) + curVal |= kBookmarkToolbarFolder; + else + curVal &= ~kBookmarkToolbarFolder; + [self setSpecialFlag:curVal]; +} + +-(void) setIsRoot:(BOOL)aBool +{ + unsigned curVal = [self specialFlag]; + if (aBool) { + if ([[self parent] isKindOfClass:[BookmarkFolder class]]) + return; + curVal |= kBookmarkRootFolder; + } else + curVal &= ~kBookmarkRootFolder; + [self setSpecialFlag:curVal]; +} + +-(void) setIsSmartFolder:(BOOL)aBool +{ + unsigned curVal = [self specialFlag]; + if (aBool) + curVal |= kBookmarkSmartFolder; + else + curVal &= ~kBookmarkSmartFolder; + [self setSpecialFlag:curVal]; +} + +-(void) makeDockMenu:(id)sender +{ + [self setIsDockMenu:YES]; +} + +-(void) setIsDockMenu:(BOOL)aBool; +{ + unsigned curVal = [self specialFlag]; + if (aBool) + curVal |= kBookmarkDockMenuFolder; + else { + curVal &= ~kBookmarkDockMenuFolder; + [[NSNotificationCenter defaultCenter] removeObserver:self name:BookmarkFolderDockMenuChangeNotificaton object:nil]; + } + [self setSpecialFlag:curVal]; +} + +// +// Adding children. +// +-(void) insertChild:(BookmarkItem *) aChild +{ + [self insertChild:aChild atIndex:[self count] isMove:NO]; +} + +-(void) insertChild:(BookmarkItem *)aChild atIndex:(unsigned)aPosition isMove:(BOOL)aBool +{ + [aChild setParent:self]; + unsigned insertPoint = [mChildArray count]; + if (insertPoint > aPosition) + insertPoint = aPosition; + [mChildArray insertObject:aChild atIndex:insertPoint]; + if (!aBool && ![self isSmartFolder]) { + NSUndoManager* undoManager = [[BookmarkManager sharedBookmarkManager] undoManager]; + [[undoManager prepareWithInvocationTarget:self] deleteChild:aChild]; + if (![undoManager isUndoing]) { + if ([aChild isKindOfClass:[BookmarkFolder class]]) + [undoManager setActionName:NSLocalizedString(@"Add Folder",@"Add Folder")]; + else if ([aChild isKindOfClass:[Bookmark class]]) { + if (![(Bookmark *)aChild isSeparator]) + [undoManager setActionName:NSLocalizedString(@"Add Bookmark",@"Add Bookmark")]; + else + [undoManager setActionName:NSLocalizedString(@"Add Separator",@"Add Separator")]; + } + } else { + [aChild itemUpdatedNote]; + } + } + [self itemAddedNote:aChild atIndex:insertPoint]; +} + +// +// Smart folder utilities - we don't set ourself as parent +// +-(void) insertIntoSmartFolderChild:(BookmarkItem *)aItem +{ + if ([self isSmartFolder]) { + [[self childArray] addObject:aItem]; + [self itemAddedNote:aItem atIndex:([self count]-1)]; + } +} +-(void) deleteFromSmartFolderChildAtIndex:(unsigned)index +{ + if ([self isSmartFolder]) { + BookmarkItem *item = [[[self childArray] objectAtIndex:index] retain]; + [[self childArray] removeObjectAtIndex:index]; + [self itemRemovedNote:[item autorelease]]; + } +} + +// +// Adding bookmarks +// + +-(Bookmark *)addBookmark; +{ + if (![self isRoot]) { + Bookmark* theBookmark = [[Bookmark alloc] init]; + [self insertChild:theBookmark]; + [theBookmark release]; //retained on insert + return theBookmark; + } + return nil; +} + +// adding from native plist +-(BOOL) addBookmarkFromNativeDict:(NSDictionary *)aDict +{ + return [[self addBookmark] readNativeDictionary:aDict]; +} + +-(BOOL) addBookmarkFromSafariDict:(NSDictionary *)aDict +{ + return [[self addBookmark] readSafariDictionary:aDict]; +} + +// add from camino xml +-(BOOL) addBookmarkFromXML:(CFXMLTreeRef)aTreeRef +{ + return [[self addBookmark] readCaminoXML:aTreeRef]; +} + +-(Bookmark *) addBookmark:(NSString *)aTitle url:(NSString *)aURL inPosition:(unsigned)aIndex isSeparator:(BOOL)aBool +{ + return [self addBookmark:aTitle inPosition:aIndex keyword:[NSString string] url:aURL description:[NSString string] lastVisit:[NSDate date] status:kBookmarkOKStatus isSeparator:aBool]; +} + +// full bodied addition +-(Bookmark *) addBookmark:(NSString *)aTitle inPosition:(unsigned)aPosition keyword:(NSString *)aKeyword url:(NSString *)aURL description:(NSString *)aDescription lastVisit:(NSDate *)aDate status:(unsigned)aStatus isSeparator:(BOOL)aSeparator +{ + if (![self isRoot]) { + Bookmark *theBookmark = [[Bookmark alloc] init]; + [self insertChild:theBookmark atIndex:aPosition isMove:NO]; + [theBookmark release]; + [theBookmark setTitle:aTitle]; + [theBookmark setKeyword:aKeyword]; + [theBookmark setUrl:aURL]; + [theBookmark setDescription:aDescription]; + [theBookmark setLastVisit:aDate]; + [theBookmark setStatus:aStatus]; + [theBookmark setIsSeparator:aSeparator]; + return theBookmark; + } + return nil; +} + +// +// Adding arrays +// +// used primarily for parsing of html bookmark files. +-(BookmarkFolder *)addBookmarkFolder //adds to end +{ + BookmarkFolder *theFolder = [[BookmarkFolder alloc] init]; + [self insertChild:theFolder]; + [theFolder release]; //retained on insert + return theFolder; +} + +// from native plist file +-(BOOL) addBookmarkFolderFromNativeDict:(NSDictionary *)aDict +{ + return [[self addBookmarkFolder] readNativeDictionary:aDict]; +} + +-(BOOL) addBookmarkFolderFromSafariDict:(NSDictionary *)aDict +{ + return [[self addBookmarkFolder] readSafariDictionary:aDict]; +} + +-(BOOL) addBookmarkFolderFromXML:(CFXMLTreeRef)aTreeRef +{ + return [[self addBookmarkFolder] readCaminoXML:aTreeRef]; +} + +// normal add while programming running +-(BookmarkFolder *)addBookmarkFolder:(NSString *)aName inPosition:(unsigned)aPosition isGroup:(BOOL)aFlag +{ + BookmarkFolder *theFolder = [[BookmarkFolder alloc] init]; + [self insertChild:theFolder atIndex:aPosition isMove:NO]; + [theFolder release]; + [theFolder setTitle:aName]; + if (aFlag) + [theFolder setIsGroup:aFlag]; + return theFolder; +} + +// +// Moving & Copying children +// + +-(void) moveChild:(BookmarkItem *)aChild toBookmarkFolder:(BookmarkFolder *)aNewParent atIndex:(unsigned)aIndex +{ + NSUndoManager* undoManager = [[BookmarkManager sharedBookmarkManager] undoManager]; + unsigned curIndex = [mChildArray indexOfObjectIdenticalTo:aChild]; + if (curIndex == NSNotFound) + return; + BOOL isSeparator = NO; + //Couple sanity checks + if ([aChild isKindOfClass:[BookmarkFolder class]]) { + if ([aNewParent isChildOfItem:aChild]) + return; + else if ([(BookmarkFolder *)aChild isToolbar] && ![aNewParent isRoot]) + return; + else if ([(BookmarkFolder *)aChild isSmartFolder] && ![aNewParent isRoot]) + return; + } else if ([aChild isKindOfClass:[Bookmark class]]){ + if ([aNewParent isRoot]) + return; + if ((isSeparator = [(Bookmark *)aChild isSeparator])) { + BookmarkFolder *menuFolder = [[BookmarkManager sharedBookmarkManager] bookmarkMenuFolder]; + if ((aNewParent != menuFolder) && (![aNewParent isChildOfItem: menuFolder])) + return; + } + } + [undoManager beginUndoGrouping]; + // What we do depends on if we're moving into a new folder, or just + // rearranging ourself a bit. + if (self !=aNewParent) { + [aNewParent insertChild:aChild atIndex:aIndex isMove:YES]; + // DO NOT call deleteChild here. Just quietly remove it from the array. + [mChildArray removeObjectAtIndex:curIndex]; + [self itemRemovedNote:aChild]; + } else { + [aChild retain]; + [mChildArray removeObjectAtIndex:curIndex]; + [self itemRemovedNote:aChild]; + if (curIndex < aIndex) + --aIndex; + [self insertChild:aChild atIndex:aIndex isMove:YES]; + [aChild release]; + } + [[undoManager prepareWithInvocationTarget:aNewParent] moveChild:aChild toBookmarkFolder:self atIndex:curIndex]; + [undoManager endUndoGrouping]; + if ([aChild isKindOfClass:[BookmarkFolder class]]) + [undoManager setActionName:NSLocalizedString(@"Move Folder",@"Move Folder")]; + else if (!isSeparator) + [undoManager setActionName:NSLocalizedString(@"Move Bookmark",@"Move Bookmark")]; + else + [undoManager setActionName:NSLocalizedString(@"Move Separator",@"Move Separator")]; +} + +-(void) copyChild:(BookmarkItem *)aChild toBookmarkFolder:(BookmarkFolder *)aNewParent atIndex:(unsigned)aIndex +{ + if ([aNewParent isRoot] && [aChild isKindOfClass:[Bookmark class]]) + return; + BookmarkItem *copiedChild = [aChild copyWithZone:nil]; + if (copiedChild) { + [aNewParent insertChild:copiedChild atIndex:aIndex isMove:NO]; + [copiedChild release]; + NSUndoManager* undoManager = [[BookmarkManager sharedBookmarkManager] undoManager]; + if ([aChild isKindOfClass:[BookmarkFolder class]]) + [undoManager setActionName:NSLocalizedString(@"Copy Folder",@"Copy Bookmark Folder")]; + else + [undoManager setActionName:NSLocalizedString(@"Copy Bookmark",@"Copy Bookmark")]; + } +} + +// +// Deleting children +// +-(BOOL) deleteChild:(BookmarkItem *)aChild +{ + BOOL itsDead = NO; + if ([[self childArray] indexOfObjectIdenticalTo:aChild] !=NSNotFound) { + if ([aChild isKindOfClass:[Bookmark class]]) + [self deleteBookmark:(Bookmark *)aChild]; + else if ([aChild isKindOfClass:[BookmarkFolder class]]) + [self deleteBookmarkFolder:(BookmarkFolder *)aChild]; + itsDead = YES; + } else { //with current setup, shouldn't ever hit this code path. + NSEnumerator *enumerator = [[self childArray] objectEnumerator]; + id anObject; + while (!itsDead && (anObject = [enumerator nextObject])) { + if ([anObject isKindOfClass:[BookmarkFolder class]]) + itsDead = [anObject deleteChild:aChild]; + } + } + return itsDead; +} + +-(void) deleteBookmark:(Bookmark *)aChild +{ + NSUndoManager* undoManager = [[BookmarkManager sharedBookmarkManager] undoManager]; + //record undo + [[undoManager prepareWithInvocationTarget:self] insertChild:aChild atIndex:[[self childArray] indexOfObjectIdenticalTo:aChild] isMove:NO]; + [aChild setParent:nil]; + [[self childArray] removeObject:aChild]; + if (![undoManager isUndoing] && ![self isSmartFolder]) { + if (![aChild isSeparator]) + [undoManager setActionName:NSLocalizedString(@"Delete Bookmark",@"Delete Bookmark")]; + else + [undoManager setActionName:NSLocalizedString(@"Delete Separator",@"Delete Separator")]; + } + // send message & clean up - undo manager has retained aChild, which prevents crash + [self itemRemovedNote:aChild]; +} + +-(void) deleteBookmarkFolder:(BookmarkFolder *)aChild +{ + NSUndoManager* undoManager = [[BookmarkManager sharedBookmarkManager] undoManager]; + //Make sure it's not a special array - redundant, but oh well. + if ([aChild isSpecial]) + return; + unsigned size = [aChild count]; + [undoManager beginUndoGrouping]; + while (size > 0) + [aChild deleteChild:[aChild objectAtIndex:--size]]; + //record undo + [[undoManager prepareWithInvocationTarget:self] insertChild:aChild atIndex:[[self childArray] indexOfObjectIdenticalTo:aChild] isMove:NO]; + // close undo group + [undoManager endUndoGrouping]; + [aChild setParent:nil]; + [[self childArray] removeObject:aChild]; + if (![undoManager isUndoing]) + [undoManager setActionName:NSLocalizedString(@"Delete Folder",@"Delete Bookmark Folder")]; + // send message - undo manager has retained aChild, which prevents crash + [self itemRemovedNote:aChild]; +} + +// +// Make us look like an array methods +// +// figure out # of top level children +-(unsigned)count +{ + return [mChildArray count]; +} + +-(id) objectAtIndex:(unsigned)index +{ + if ([mChildArray count] > index) + return [mChildArray objectAtIndex:index]; + return nil; +} + +-(unsigned)indexOfObject:(id)object +{ + return [mChildArray indexOfObject:object]; +} + +-(unsigned)indexOfObjectIdenticalTo:(id)object +{ + return [mChildArray indexOfObjectIdenticalTo:object]; +} + +// +// build submenu +// +-(void)buildFlatFolderList:(NSMenu *)menu depth:(unsigned)depth +{ + NSEnumerator *children = [mChildArray objectEnumerator]; + NSMutableString *paddedTitle; + id aKid; + while ((aKid = [children nextObject])) { + if ([aKid isKindOfClass:[BookmarkFolder class]]) { + if (![aKid isSmartFolder]) { + paddedTitle = [[aKid title] mutableCopyWithZone:nil]; + if ([paddedTitle length] > 80) + [paddedTitle stringByTruncatingTo:80 at:kTruncateAtMiddle]; + NSMenuItem* menuItem = [[NSMenuItem alloc] initWithTitle: paddedTitle action: NULL keyEquivalent: @""]; + [paddedTitle release]; + [menuItem setRepresentedObject:aKid]; + NSImage *curIcon = [aKid icon]; + NSSize iconSize = [curIcon size]; + NSImage *shiftedIcon = [[NSImage alloc] initWithSize:NSMakeSize(depth*(iconSize.width), iconSize.height)]; + [shiftedIcon lockFocus]; + [curIcon drawInRect:NSMakeRect(([shiftedIcon size].width-iconSize.width),0,iconSize.width,iconSize.height) fromRect:NSMakeRect(0,0,iconSize.width,iconSize.height) operation:NSCompositeCopy fraction:1]; + [shiftedIcon unlockFocus]; + [menuItem setImage:shiftedIcon]; + [shiftedIcon release]; + [menu addItem:menuItem]; + [menuItem release]; + [aKid buildFlatFolderList:menu depth:(depth+1)]; + } + } + } +} + +// +// searching/keywords processing +// +-(NSArray *)resolveKeyword:(NSString *)aString +{ + // see if it's us + if ([[self keyword] isEqualToString:aString]) + return [self childURLs]; + // see if it's us after an expansion + NSRange spaceRange = [aString rangeOfString:@" "]; + NSString *firstWord = nil; + NSString *secondWord = nil; + if (spaceRange.location != NSNotFound) { + firstWord = [aString substringToIndex:spaceRange.location]; + secondWord = [aString substringFromIndex:(spaceRange.location + spaceRange.length)]; + if ([[self keyword] isEqualToString:firstWord]) { + NSMutableArray *urlArray = (NSMutableArray *)[self childURLs]; + int i, j=[urlArray count]; + for (i = 0; i < j; i++) { + NSString *newURL = [self expandKeyword:secondWord inString:[urlArray objectAtIndex:i]]; + [urlArray replaceObjectAtIndex:i withObject:newURL]; + } + return urlArray; + } + } + // see if it's one of our kids + NSArray *childArray = nil; + NSEnumerator* enumerator = [[self childArray] objectEnumerator]; + id aKid; + while ((aKid = [enumerator nextObject])) { + if ([aKid isKindOfClass:[Bookmark class]]) { + if ([[aKid keyword] isEqualToString:aString]) + return [NSArray arrayWithObject:[aKid url]]; + if (firstWord) { + if ([[aKid keyword] isEqualToString:firstWord]) + return [NSArray arrayWithObject:[self expandKeyword:secondWord inString:[aKid url]]]; + } + } + else if ([aKid isKindOfClass:[BookmarkFolder class]]) { + childArray = [aKid resolveKeyword:aString]; + if (childArray) + return childArray; + } + } + return nil; +} + +- (NSString*)expandKeyword:(NSString*)keyword inString:(NSString*)location +{ + NSRange matchRange = [location rangeOfString:@"%s"]; + if (matchRange.location != NSNotFound) + { + NSString* resultString = [NSString stringWithFormat:@"%@%@%@", + [location substringToIndex:matchRange.location], + keyword, + [location substringFromIndex:(matchRange.location + matchRange.length)]]; + return resultString; + } + return location; +} + +-(NSSet *) bookmarksWithString:(NSString *)searchString +{ + NSMutableSet *foundSet = [NSMutableSet set]; + // see if we match + if ([self isString:searchString inBookmarkItem:self]) + [foundSet addObject:self]; + // see if our kids match + NSEnumerator *enumerator = [[self childArray] objectEnumerator]; + id aKid; + while ((aKid = [enumerator nextObject])) { + if ([aKid isKindOfClass:[Bookmark class]]) { + if ([self isString:searchString inBookmarkItem:aKid]) + [foundSet addObject:aKid]; + } else if ([aKid isKindOfClass:[BookmarkFolder class]]) { + NSSet *childFoundSet = [aKid bookmarksWithString:searchString]; + if ([childFoundSet count] > 0) { + NSEnumerator *kidEnumerator = [childFoundSet objectEnumerator]; + id aKidThing; + while ((aKidThing = [kidEnumerator nextObject])) + [foundSet addObject:aKidThing]; + } + } + } + return foundSet; +} + +-(BOOL) isString:(NSString *)searchString inBookmarkItem:(BookmarkItem *)anItem +{ + BOOL stringFound = NO; + if (([[anItem title] rangeOfString:searchString options:NSCaseInsensitiveSearch].location != NSNotFound) || + ([[anItem keyword] rangeOfString:searchString options:NSCaseInsensitiveSearch].location != NSNotFound) || + ([[anItem description] rangeOfString:searchString options:NSCaseInsensitiveSearch].location != NSNotFound)) + stringFound = YES; + if (!stringFound && [anItem isKindOfClass:[Bookmark class]]) { + if ([[(Bookmark *)anItem url] rangeOfString:searchString options:NSCaseInsensitiveSearch].location != NSNotFound) + stringFound = YES; + } + return stringFound; +} + +// +// Notification stuff +// + +-(void) itemAddedNote:(BookmarkItem *)theItem atIndex:(unsigned)anIndex +{ + NSNotificationCenter *nc = [NSNotificationCenter defaultCenter]; + NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:theItem,BookmarkFolderChildKey, + [NSNumber numberWithUnsignedInt:anIndex],BookmarkFolderChildIndexKey,nil]; + NSNotification *note = [NSNotification notificationWithName:BookmarkFolderAdditionNotification object:self userInfo:dict]; + [nc postNotification:note]; +} + +-(void) itemRemovedNote:(BookmarkItem *)theItem +{ + NSNotificationCenter *nc = [NSNotificationCenter defaultCenter]; + NSDictionary *dict = [NSDictionary dictionaryWithObject:theItem forKey:BookmarkFolderChildKey]; + NSNotification *note = [NSNotification notificationWithName:BookmarkFolderDeletionNotification object:self userInfo:dict]; + [nc postNotification:note]; +} + +-(void) dockMenuChanged:(NSNotification *)note +{ + if (([self isDockMenu]) && ([note object] != self)) + [self setIsDockMenu:NO]; +} + + +// +// reading/writing from/to disk +// +#pragma mark - +// +// keys for both safari & camino +// +NSString *BMFolderTitleKey = @"Title"; +NSString *BMFolderChildrenKey = @"Children"; +// camino only key +NSString *BMFolderDescKey = @"FolderDescription"; +NSString *BMFolderTypeKey = @"FolderType"; +NSString *BMFolderKeywordKey = @"FolderKeyword"; +// safari only keys +NSString *SafariTypeKey = @"WebBookmarkType"; +NSString *SafariLeaf = @"WebBookmarkTypeLeaf"; +NSString *SafariList = @"WebBookmarkTypeList"; +NSString *SafariAutoTab = @"WebBookmarkAutoTab"; + +NSString *CaminoNameKey = @"name"; +NSString *CaminoDescKey = @"description"; +NSString *CaminoTypeKey = @"type"; +NSString *CaminoToolbarKey = @"toolbar"; +NSString *CaminoDockMenuKey = @"dockmenu"; +NSString *CaminoGroupKey = @"group"; +NSString *CaminoBookmarkKey = @"bookmark"; +NSString *CaminoFolderKey = @"folder"; +NSString *CaminoTrueKey = @"true"; + +-(BOOL) readNativeDictionary:(NSDictionary *)aDict +{ + id aKid; + NSEnumerator *enumerator; + BOOL noErr = YES; + [self setTitle:[aDict objectForKey:BMFolderTitleKey]]; + [self setDescription:[aDict objectForKey:BMFolderDescKey]]; + [self setKeyword:[aDict objectForKey:BMFolderKeywordKey]]; + [self setSpecialFlag:[[aDict objectForKey:BMFolderTypeKey] unsignedIntValue]]; + enumerator = [[aDict objectForKey:BMFolderChildrenKey] objectEnumerator]; + while ((aKid = [enumerator nextObject]) && noErr) { + if ([aKid objectForKey:BMFolderChildrenKey]) + noErr = [self addBookmarkFolderFromNativeDict:(NSDictionary *)aKid]; + else + noErr = [self addBookmarkFromNativeDict:(NSDictionary *)aKid]; + } + return noErr; +} + +-(BOOL) readSafariDictionary:(NSDictionary *)aDict +{ + id aKid; + NSEnumerator *enumerator; + BOOL noErr = YES; + [self setTitle:[aDict objectForKey:BMFolderTitleKey]]; + enumerator = [[aDict objectForKey:BMFolderChildrenKey] objectEnumerator]; + while ((aKid = [enumerator nextObject]) && noErr) { + if ([[aKid objectForKey:SafariTypeKey] isEqualToString:SafariLeaf]) + noErr = [self addBookmarkFromSafariDict:(NSDictionary *)aKid]; + else if ([[aKid objectForKey:SafariTypeKey] isEqualToString:SafariList]) + noErr = [self addBookmarkFolderFromSafariDict:(NSDictionary *)aKid]; + // might also be a WebBookmarkTypeProxy - we'll ignore those + } + if ([[aDict objectForKey:SafariAutoTab] boolValue]) + [self setIsGroup:YES]; + return noErr; +} + +-(BOOL) readCaminoXML:(CFXMLTreeRef)aTreeRef +{ + CFXMLTreeRef childTreeRef; + CFXMLNodeRef myNode, childNodeRef; + CFXMLElementInfo* elementInfoPtr; + BOOL noErr = YES; + myNode = CFXMLTreeGetNode(aTreeRef); + if (myNode) { + // Process our info - we load info into tempMuteString, + // then send a cleaned up version to temp string, which gets + // dropped into appropriate variable + if (CFXMLNodeGetTypeCode(myNode)==kCFXMLNodeTypeElement){ + elementInfoPtr = (CFXMLElementInfo*)CFXMLNodeGetInfoPtr(myNode); + if (elementInfoPtr) { + NSDictionary* attribDict = (NSDictionary*)elementInfoPtr->attributes; + [self setTitle:[[attribDict objectForKey:CaminoNameKey] stringByRemovingAmpEscapes]]; + [self setDescription:[[attribDict objectForKey:CaminoDescKey] stringByRemovingAmpEscapes]]; + if ([[attribDict objectForKey:CaminoTypeKey] isEqualToString:CaminoToolbarKey]) + [self setIsToolbar:YES]; + if ([[attribDict objectForKey:CaminoGroupKey] isEqualToString:CaminoTrueKey]) + [self setIsGroup:YES]; + // Process children + unsigned i = 0; + while((childTreeRef = CFTreeGetChildAtIndex(aTreeRef,i++)) && noErr) { + childNodeRef = CFXMLTreeGetNode(childTreeRef); + if (childNodeRef) { + NSString *tempString = (NSString *)CFXMLNodeGetString(childNodeRef); + if ([tempString isEqualToString:CaminoBookmarkKey]) + noErr = [self addBookmarkFromXML:childTreeRef]; + else if ([tempString isEqualToString:CaminoFolderKey]) + noErr = [self addBookmarkFolderFromXML:childTreeRef]; + } + } + } else { + NSLog(@"BookmarkFolder: readCaminoXML- elementInfoPtr null - children not imported"); + noErr = NO; + } + } else { + NSLog(@"BookmarkFolder: readCaminoXML - should be, but isn't a CFXMLNodeTypeElement"); + noErr = NO; + } + } else { + NSLog(@"BookmarkFolder: readCaminoXML - myNode null - bookmark not imported"); + noErr = NO; + } + return noErr; +} + + +-(NSDictionary *)writeNativeDictionary +{ + if (![self isSmartFolder]) { + id item; + NSMutableArray* children = [NSMutableArray array]; + NSEnumerator* enumerator = [mChildArray objectEnumerator]; + //get chillins first + while ((item = [enumerator nextObject])) { + id aDict = [item writeNativeDictionary]; + if (aDict) + [children addObject:aDict]; + } + return [NSDictionary dictionaryWithObjectsAndKeys: + mTitle,BMFolderTitleKey, + mDescription,BMFolderDescKey, + mKeyword,BMFolderKeywordKey, + mSpecialFlag,BMFolderTypeKey, + children,BMFolderChildrenKey, nil]; + } + return nil; +} + +-(NSString *)writeHTML:(unsigned)aPad +{ + id item; + NSString* htmlString; + NSMutableString *padString = [NSMutableString string]; + for (unsigned i = 0;i

%@

\n%@

\n", + padString, + [NSString stringWithUTF8String:[[mTitle stringByAddingAmpEscapes] UTF8String]], + padString]; + // Toolbar Folder + else if ([self isToolbar]) + htmlString = [NSString stringWithFormat:@"%@

%@

\n%@

\n", + padString, + [NSString stringWithUTF8String:[[mTitle stringByAddingAmpEscapes] UTF8String]], + padString]; + // Root Folder + else if ([self isRoot]) + htmlString = [NSString stringWithFormat:@"%@\n%@\n%@\n%@\n\n

\n", + @"", + @"", + @"Bookmarks", + @"

Bookmarks

"]; + // Folder-Not-Appearing-In-This-File Folder (ie, smart folders) + else + htmlString = [NSString stringWithString:@""]; + NSEnumerator* enumerator = [mChildArray objectEnumerator]; + //get chillins first + while ((item = [enumerator nextObject])) + htmlString = [htmlString stringByAppendingString:[item writeHTML:aPad+1]]; + return [htmlString stringByAppendingString:[NSString stringWithFormat:@"%@

\n",padString]]; +} + +#pragma mark - +// +// Scripting methods - thanks for the sketch example, Apple +// +- (NSScriptObjectSpecifier *)objectSpecifier +{ + id parent = [self parent]; + NSScriptObjectSpecifier *containerRef = [parent objectSpecifier]; + NSScriptClassDescription *arrayScriptClassDesc = (NSScriptClassDescription *)[NSClassDescription classDescriptionForClass:[BookmarkFolder class]]; + if ([parent isKindOfClass:[BookmarkFolder class]]) { + unsigned index = [[parent childArray] indexOfObjectIdenticalTo:self]; + if (index != NSNotFound) { + // need to get the appropriate class description + return [[[NSIndexSpecifier allocWithZone:[self zone]] initWithContainerClassDescription:arrayScriptClassDesc containerSpecifier:containerRef key:@"childArray" index:index] autorelease]; + } else + return nil; + } else // root bookmark + return [[[NSPropertySpecifier allocWithZone:[self zone]] initWithContainerSpecifier:containerRef key:@"rootArray"] autorelease]; + return nil; +} + +- (NSArray *)folderItemWithClass:(Class)theClass { + NSArray *kiddies = [self childArray]; + NSMutableArray *result = [NSMutableArray array]; + unsigned i, c = [kiddies count]; + id curItem; + for (i=0; i= 0) && (baseIndex < kiddiesCount)) { + if (keyIsArray) { + [result addObject:[NSNumber numberWithInt:baseIndex]]; + break; + } else { + curObj = [kiddies objectAtIndex:baseIndex]; + curKeyIndex = [relKeyObjects indexOfObjectIdenticalTo:curObj]; + if (curKeyIndex != NSNotFound) { + [result addObject:[NSNumber numberWithInt:curKeyIndex]]; + break; + } + } + if (relPos == NSRelativeBefore) + baseIndex--; + else + baseIndex++; + } + return result; + } + } + return nil; +} + +- (NSArray *)indicesOfObjectsByEvaluatingRangeSpecifier:(NSRangeSpecifier *)rangeSpec +{ + NSString *key = [rangeSpec key]; + if ([key isEqualToString:@"childBookmarks"] || + [key isEqualToString:@"childArray"] || + [key isEqualToString:@"childFolders"] ) + { + NSScriptObjectSpecifier *startSpec = [rangeSpec startSpecifier]; + NSScriptObjectSpecifier *endSpec = [rangeSpec endSpecifier]; + NSString *startKey = [startSpec key]; + NSString *endKey = [endSpec key]; + NSArray *kiddies = [self childArray]; + + if ((startSpec == nil) && (endSpec == nil)) + return nil; + if ([kiddies count] == 0) + return [NSArray array]; + + if ((!startSpec || [startKey isEqualToString:@"childBookmarks"] || [startKey isEqualToString:@"childArray"] || [startKey isEqualToString:@"childFolders"]) && (!endSpec || [endKey isEqualToString:@"childBookmarks"] || [endKey isEqualToString:@"childArray"] || [endKey isEqualToString:@"childFolders"])) + { + unsigned startIndex; + unsigned endIndex; + if (startSpec) { + id startObject = [startSpec objectsByEvaluatingSpecifier]; + if ([startObject isKindOfClass:[NSArray class]]) { + if ([startObject count] == 0) + startObject = nil; + else + startObject = [startObject objectAtIndex:0]; + } + if (!startObject) + return nil; + startIndex = [kiddies indexOfObjectIdenticalTo:startObject]; + if (startIndex == NSNotFound) + return nil; + } else + startIndex = 0; + + if (endSpec) { + id endObject = [endSpec objectsByEvaluatingSpecifier]; + if ([endObject isKindOfClass:[NSArray class]]) { + unsigned endObjectsCount = [endObject count]; + if (endObjectsCount == 0) + endObject = nil; + else + endObject = [endObject objectAtIndex:(endObjectsCount-1)]; + } + if (!endObject) + return nil; + endIndex = [kiddies indexOfObjectIdenticalTo:endObject]; + if (endIndex == NSNotFound) + return nil; + } else + endIndex = [kiddies count] - 1; + + if (endIndex < startIndex) { + int temp = endIndex; + endIndex = startIndex; + startIndex = temp; + } + + NSMutableArray *result = [NSMutableArray array]; + BOOL keyIsArray = [key isEqual:@"childArray"]; + NSArray *rangeKeyObjects = (keyIsArray ? nil : [self valueForKey:key]); + id curObj; + unsigned curKeyIndex, i; + for (i=startIndex; i<=endIndex; i++) { + if (keyIsArray) + [result addObject:[NSNumber numberWithInt:i]]; + else { + curObj = [kiddies objectAtIndex:i]; + curKeyIndex = [rangeKeyObjects indexOfObjectIdenticalTo:curObj]; + if (curKeyIndex != NSNotFound) + [result addObject:[NSNumber numberWithInt:curKeyIndex]]; + } + } + return result; + } + } + return nil; +} + +@end; diff --git a/camino/src/bookmarks/BookmarkImportDlgController.h b/camino/src/bookmarks/BookmarkImportDlgController.h new file mode 100644 index 000000000000..970d340f3fd4 --- /dev/null +++ b/camino/src/bookmarks/BookmarkImportDlgController.h @@ -0,0 +1,56 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** +* Version: NPL 1.1/GPL 2.0/LGPL 2.1 +* +* The contents of this file are subject to the Netscape Public License +* Version 1.1 (the "License"); you may not use this file except in +* compliance with the License. You may obtain a copy of the License at +* http://www.mozilla.org/NPL/ +* +* Software distributed under the License is distributed on an "AS IS" basis, +* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +* for the specific language governing rights and limitations under the +* License. +* +* The Original Code is mozilla.org code. +* +* The Initial Developer of the Original Code is +* Netscape Communications Corporation. +* Portions created by the Initial Developer are Copyright (C) 2002 +* the Initial Developer. All Rights Reserved. +* +* Contributor(s): +* David Haas +* +* +* Alternatively, the contents of this file may be used under the terms of +* either the GNU General Public License Version 2 or later (the "GPL"), or +* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +* in which case the provisions of the GPL or the LGPL are applicable instead +* of those above. If you wish to allow use of your version of this file only +* under the terms of either the GPL or the LGPL, and not to allow others to +* use your version of this file under the terms of the NPL, indicate your +* decision by deleting the provisions above and replace them with the notice +* and other provisions required by the GPL or the LGPL. If you do not delete +* the provisions above, a recipient may use your version of this file under +* the terms of any one of the NPL, the GPL or the LGPL. +* +* ***** END LICENSE BLOCK ***** */ + +#import + + +@interface BookmarkImportDlgController : NSWindowController { + IBOutlet NSPopUpButton* mBrowserListButton; + IBOutlet NSButton* mCancelButton; + IBOutlet NSButton* mImportButton; +} + +-(void) buildAvailableFileList; +-(IBAction) cancel:(id)aSender; +-(IBAction) import:(id)aSender; +-(IBAction) loadOpenPanel:(id)aSender; +-(IBAction) nullAction:(id)aSender; + + +@end diff --git a/camino/src/bookmarks/BookmarkImportDlgController.mm b/camino/src/bookmarks/BookmarkImportDlgController.mm new file mode 100644 index 000000000000..1142989796a1 --- /dev/null +++ b/camino/src/bookmarks/BookmarkImportDlgController.mm @@ -0,0 +1,193 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** +* Version: NPL 1.1/GPL 2.0/LGPL 2.1 +* +* The contents of this file are subject to the Netscape Public License +* Version 1.1 (the "License"); you may not use this file except in +* compliance with the License. You may obtain a copy of the License at +* http://www.mozilla.org/NPL/ +* +* Software distributed under the License is distributed on an "AS IS" basis, +* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +* for the specific language governing rights and limitations under the +* License. +* +* The Original Code is mozilla.org code. +* +* The Initial Developer of the Original Code is +* Netscape Communications Corporation. +* Portions created by the Initial Developer are Copyright (C) 2002 +* the Initial Developer. All Rights Reserved. +* +* Contributor(s): +* David Haas +* +* +* Alternatively, the contents of this file may be used under the terms of +* either the GNU General Public License Version 2 or later (the "GPL"), or +* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +* in which case the provisions of the GPL or the LGPL are applicable instead +* of those above. If you wish to allow use of your version of this file only +* under the terms of either the GPL or the LGPL, and not to allow others to +* use your version of this file under the terms of the NPL, indicate your +* decision by deleting the provisions above and replace them with the notice +* and other provisions required by the GPL or the LGPL. If you do not delete +* the provisions above, a recipient may use your version of this file under +* the terms of any one of the NPL, the GPL or the LGPL. +* +* ***** END LICENSE BLOCK ***** */ + +#import "BookmarkImportDlgController.h" +#import "BookmarkManager.h" +#import "BookmarkFolder.h" + +@implementation BookmarkImportDlgController + +-(void) windowDidLoad +{ + [self buildAvailableFileList]; +} + +-(void) buildAvailableFileList +{ + // check for common webbrower bookmark files and, if they exist, add to button. + NSFileManager *fm = [NSFileManager defaultManager]; + NSMenuItem *browserItem; + NSEnumerator *enumerator; + id aTempItem; + // iCab + NSString *pathString = [[NSString stringWithString:@"~/Library/Preferences/iCab Preferences/Hotlist.html"] stringByExpandingTildeInPath]; + if ([fm fileExistsAtPath:pathString]) { + [mBrowserListButton insertItemWithTitle:@"iCab" atIndex:0]; + browserItem = [mBrowserListButton itemAtIndex:0]; + [browserItem setTarget:self]; + [browserItem setAction:@selector(nullAction:)]; + [browserItem setRepresentedObject:pathString]; + } + // Firebird + pathString = [[NSString stringWithString:@"~/Library/Phoenix/Profiles/default/"] stringByExpandingTildeInPath]; + if ([fm fileExistsAtPath:pathString]) { + NSArray *saltArray = [fm directoryContentsAtPath:pathString]; + enumerator = [saltArray objectEnumerator]; + while ((aTempItem = [enumerator nextObject])) { + if (![aTempItem hasPrefix:@"."]) + break; + } + if (aTempItem) { + pathString = [pathString stringByAppendingFormat:@"/%@/bookmarks.html",aTempItem]; + if ([fm fileExistsAtPath:pathString]) { + [mBrowserListButton insertItemWithTitle:@"Mozilla Firebird" atIndex:0]; + browserItem = [mBrowserListButton itemAtIndex:0]; + [browserItem setTarget:self]; + [browserItem setAction:@selector(nullAction:)]; + [browserItem setRepresentedObject:pathString]; + } + } + } + + // Netscape/Mozilla + pathString = [[NSString stringWithString:@"~/Library/Mozilla/Profiles/default/"] stringByExpandingTildeInPath]; + if ([fm fileExistsAtPath:pathString]) { + NSArray *saltArray = [fm directoryContentsAtPath:pathString]; + enumerator = [saltArray objectEnumerator]; + while ((aTempItem = [enumerator nextObject])) { + if (![aTempItem hasPrefix:@"."]) + break; + } + if (aTempItem) { + pathString = [pathString stringByAppendingFormat:@"/%@/bookmarks.html",aTempItem]; + if ([fm fileExistsAtPath:pathString]) { + [mBrowserListButton insertItemWithTitle:@"Netscape/Mozilla" atIndex:0]; + browserItem = [mBrowserListButton itemAtIndex:0]; + [browserItem setTarget:self]; + [browserItem setAction:@selector(nullAction:)]; + [browserItem setRepresentedObject:pathString]; + } + } + } + + // Omniweb + pathString = [[NSString stringWithString:@"~/Library/Application Support/Omniweb/Bookmarks.html"] stringByStandardizingPath]; + if ([fm fileExistsAtPath:pathString]) { + [mBrowserListButton insertItemWithTitle:@"Omniweb" atIndex:0]; + browserItem = [mBrowserListButton itemAtIndex:0]; + [browserItem setTarget:self]; + [browserItem setAction:@selector(nullAction:)]; + [browserItem setRepresentedObject:pathString]; + } + + // IE + pathString = [[NSString stringWithString:@"~/Library/Preferences/Explorer/Favorites.html"] stringByStandardizingPath]; + if ([fm fileExistsAtPath:pathString]) { + [mBrowserListButton insertItemWithTitle:@"Internet Explorer" atIndex:0]; + browserItem = [mBrowserListButton itemAtIndex:0]; + [browserItem setTarget:self]; + [browserItem setAction:@selector(nullAction:)]; + [browserItem setRepresentedObject:pathString]; + } + + // Safari + pathString = [[NSString stringWithString:@"~/Library/Safari/Bookmarks.plist"] stringByStandardizingPath]; + if ([fm fileExistsAtPath:pathString]) { + [mBrowserListButton insertItemWithTitle:@"Safari" atIndex:0]; + browserItem = [mBrowserListButton itemAtIndex:0]; + [browserItem setTarget:self]; + [browserItem setAction:@selector(nullAction:)]; + [browserItem setRepresentedObject:pathString]; + } + [mBrowserListButton selectItemAtIndex:0]; + [mBrowserListButton synchronizeTitleAndSelectedItem]; +} + +// keeps browsers turned on +-(IBAction) nullAction:(id)aSender +{ +} + +-(IBAction) cancel:(id)aSender +{ + [NSApp stopModal]; + [NSApp endSheet:[self window]]; + [[self window] orderOut:self]; + +} +-(IBAction) import:(id)aSender +{ + [NSApp stopModal]; + [NSApp endSheet:[self window]]; + [[self window] orderOut:self]; + NSMenuItem *selectedItem = [mBrowserListButton selectedItem]; + BookmarkFolder *importFolder = [[[BookmarkManager sharedBookmarkManager] rootBookmarks] addBookmarkFolder]; + NSString *titleString; + if ([[selectedItem title] isEqualToString:@"Internet Explorer"]) + titleString = [[NSString alloc] initWithString:NSLocalizedString(@"Imported IE Favorites",@"Imported IE Favorites")]; + else + titleString = [[NSString alloc] initWithFormat:NSLocalizedString(@"Imported %@ Bookmarks",@"Imported %@ Bookmarks"),[selectedItem title]]; + [importFolder setTitle:titleString]; + [titleString release]; + [[BookmarkManager sharedBookmarkManager] importBookmarks:[selectedItem representedObject] intoFolder:importFolder]; +} + +-(IBAction) loadOpenPanel:(id)aSender +{ + [NSApp stopModal]; + [NSApp endSheet:[self window]]; + [[self window] orderOut:self]; + NSOpenPanel* openPanel = [NSOpenPanel openPanel]; + [openPanel setCanChooseFiles: YES]; + [openPanel setCanChooseDirectories: NO]; + [openPanel setAllowsMultipleSelection: NO]; + NSArray* array = [NSArray arrayWithObjects: @"htm",@"html",@"xml", @"plist",nil]; + int result = [openPanel runModalForDirectory: nil + file: nil + types: array]; + if (result == NSOKButton) { + NSString *pathToFile = [[openPanel filenames] objectAtIndex:0]; + BookmarkManager *bm = [BookmarkManager sharedBookmarkManager]; + BookmarkFolder *importFolder = [[bm rootBookmarks] addBookmarkFolder]; + [importFolder setTitle:NSLocalizedString(@"Imported Bookmarks",@"Imported Bookmarks")]; + [bm importBookmarks:pathToFile intoFolder:importFolder]; + } +} + +@end diff --git a/camino/src/bookmarks/BookmarkInfoController.h b/camino/src/bookmarks/BookmarkInfoController.h index 880f6f6c6d3d..d5789bf9c120 100644 --- a/camino/src/bookmarks/BookmarkInfoController.h +++ b/camino/src/bookmarks/BookmarkInfoController.h @@ -1,49 +1,73 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** +* Version: NPL 1.1/GPL 2.0/LGPL 2.1 * -* The contents of this file are subject to the Mozilla Public -* License Version 1.1 (the "License"); you may not use this file -* except in compliance with the License. You may obtain a copy of -* the License at http://www.mozilla.org/MPL/ +* The contents of this file are subject to the Netscape Public License +* Version 1.1 (the "License"); you may not use this file except in +* compliance with the License. You may obtain a copy of the License at +* http://www.mozilla.org/NPL/ * -* Software distributed under the License is distributed on an "AS -* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or -* implied. See the License for the specific language governing -* rights and limitations under the License. +* Software distributed under the License is distributed on an "AS IS" basis, +* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +* for the specific language governing rights and limitations under the +* License. * -* The Original Code is the Mozilla browser. +* The Original Code is mozilla.org code. * -* The Initial Developer of the Original Code is Netscape -* Communications Corporation. Portions created by Netscape are -* Copyright (C) 2002 Netscape Communications Corporation. All -* Rights Reserved. +* The Initial Developer of the Original Code is +* Netscape Communications Corporation. +* Portions created by the Initial Developer are Copyright (C) 2002 +* the Initial Developer. All Rights Reserved. * * Contributor(s): * Ben Goodger (Original Author) * Simon Fraser * David Haas -*/ +* +* +* Alternatively, the contents of this file may be used under the terms of +* either the GNU General Public License Version 2 or later (the "GPL"), or +* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +* in which case the provisions of the GPL or the LGPL are applicable instead +* of those above. If you wish to allow use of your version of this file only +* under the terms of either the GPL or the LGPL, and not to allow others to +* use your version of this file under the terms of the NPL, indicate your +* decision by deleting the provisions above and replace them with the notice +* and other provisions required by the GPL or the LGPL. If you do not delete +* the provisions above, a recipient may use your version of this file under +* the terms of any one of the NPL, the GPL or the LGPL. +* +* ***** END LICENSE BLOCK ***** */ #import -#import "BookmarksService.h" +#import "BookmarksClient.h" + +@class BookmarkItem; @interface BookmarkInfoController : NSWindowController { - IBOutlet NSTextField* mNameField; - IBOutlet NSTextField* mLocationField; - IBOutlet NSTextField* mKeywordField; - IBOutlet NSTextField* mDescriptionField; - IBOutlet NSTextField* mNameLabel; - IBOutlet NSTextField* mLocationLabel; - IBOutlet NSTextField* mKeywordLabel; - IBOutlet NSTextField* mDescriptionLabel; - - IBOutlet NSView* mVariableFieldsContainer; + IBOutlet NSTabView* mTabView; + IBOutlet NSTextField* mBookmarkNameField; + IBOutlet NSTextField* mBookmarkLocationField; + IBOutlet NSTextField* mBookmarkKeywordField; + IBOutlet NSTextField* mBookmarkDescField; + IBOutlet NSTextField* mFolderNameField; + IBOutlet NSTextField* mFolderKeywordField; + IBOutlet NSTextField* mFolderKeywordLabel; + IBOutlet NSTextField* mFolderDescField; + IBOutlet NSTextField* mLastVisitField; + IBOutlet NSTextField* mStatusField; + IBOutlet NSTextField* mNumberVisitsField; - IBOutlet NSButton* mDockMenuCheckbox; - IBOutlet NSButton* mTabgroupCheckbox; - - BookmarkItem* mBookmarkItem; - NSTextView* mFieldEditor; + IBOutlet NSButton* mTabgroupCheckbox; + IBOutlet NSButton* mDockMenuCheckbox; + IBOutlet NSButton* mClearNumberVisitsButton; + + NSTabViewItem *mBookmarkInfoTabView; + NSTabViewItem *mBookmarkUpdateTabView; + NSTabViewItem *mFolderInfoTabView; + BookmarkItem *mBookmarkItem; + NSTextView *mFieldEditor; } + (id)sharedBookmarkInfoController; @@ -52,7 +76,8 @@ -(void)setBookmark:(BookmarkItem*)aBookmark; -(BookmarkItem*)bookmark; -- (IBAction)dockMenuCheckboxClicked:(id)sender; - (IBAction)tabGroupCheckboxClicked:(id)sender; +- (IBAction)dockMenuCheckboxClicked:(id)sender; +- (IBAction)clearVisitCount:(id)sender; @end diff --git a/camino/src/bookmarks/BookmarkInfoController.mm b/camino/src/bookmarks/BookmarkInfoController.mm index abdafb089d06..85d424afbd29 100644 --- a/camino/src/bookmarks/BookmarkInfoController.mm +++ b/camino/src/bookmarks/BookmarkInfoController.mm @@ -24,17 +24,17 @@ */ #import "NSString+Utils.h" - #import "BookmarkInfoController.h" +#import "Bookmark.h" +#import "BookmarkFolder.h" -#include "nsIContent.h" +// determined through weeks of trial and error +#define kMaxLengthOfWindowTitle 49 @interface BookmarkInfoController(Private) -- (void)showUIElementPair: (id)aLabel control: (id) aControl; -- (void)hideUIElementPair: (id)aLabel control: (id) aControl; - (void)commitChanges:(id)sender; -- (BOOL)commitField:(id)textField toProperty:(nsIAtom*)propertyAtom; +- (void)updateUI:(BookmarkItem *)anItem; @end; @@ -48,7 +48,6 @@ static BookmarkInfoController *sharedBookmarkInfoController = nil; if (!sharedBookmarkInfoController) { sharedBookmarkInfoController = [[BookmarkInfoController alloc] initWithWindowNibName:@"BookmarkInfoPanel"]; } - return sharedBookmarkInfoController; } @@ -66,26 +65,31 @@ static BookmarkInfoController *sharedBookmarkInfoController = nil; mFieldEditor = [[NSTextView alloc] init]; [mFieldEditor setAllowsUndo:YES]; [mFieldEditor setFieldEditor:YES]; - } return self; } -- (void)awakeFromNib +- (void)windowDidLoad { // keep a ref so that we can remove and add to the its superview with impunity - [mNameField retain]; - [mLocationField retain]; - [mKeywordField retain]; - [mDescriptionField retain]; - [mNameLabel retain]; - [mLocationLabel retain]; - [mKeywordLabel retain]; - [mDescriptionLabel retain]; - [mDockMenuCheckbox retain]; + [mFolderKeywordField retain]; + [mFolderKeywordLabel retain]; [mTabgroupCheckbox retain]; - - [[BookmarksManager sharedBookmarksManager] addBookmarksClient:self]; + // find the TabViewItems & retain them, too. Like to do this + // in IB, but just doesn't want to connect. So do it here. + int tabIndex = [mTabView indexOfTabViewItemWithIdentifier:@"bminfo"]; + mBookmarkInfoTabView = [[mTabView tabViewItemAtIndex:tabIndex] retain]; + tabIndex = [mTabView indexOfTabViewItemWithIdentifier:@"bmupdate"]; + mBookmarkUpdateTabView = [[mTabView tabViewItemAtIndex:tabIndex] retain]; + tabIndex = [mTabView indexOfTabViewItemWithIdentifier:@"folinfo"]; + mFolderInfoTabView = [[mTabView tabViewItemAtIndex:tabIndex] retain]; + // it would be nice to do this in IB, but I can't make it connect. + [mBookmarkInfoTabView setInitialFirstResponder:mBookmarkNameField]; + [mFolderInfoTabView setInitialFirstResponder:mFolderNameField]; + [mBookmarkUpdateTabView setInitialFirstResponder:mClearNumberVisitsButton]; + // Generic notifications for Bookmark Client - only care if there's a deletion + NSNotificationCenter *nc = [NSNotificationCenter defaultCenter]; + [nc addObserver:self selector:@selector(bookmarkRemoved:) name:BookmarkFolderDeletionNotification object:nil]; } -(void)dealloc @@ -94,21 +98,16 @@ static BookmarkInfoController *sharedBookmarkInfoController = nil; if (self == sharedBookmarkInfoController) sharedBookmarkInfoController = nil; - [[BookmarksManager sharedBookmarksManagerDontAlloc] removeBookmarksClient:self]; - + [[NSNotificationCenter defaultCenter] removeObserver:self]; + [mBookmarkItem release]; + mBookmarkItem = nil; [mFieldEditor release]; - - [mNameField release]; - [mLocationField release]; - [mKeywordField release]; - [mDescriptionField release]; - [mNameLabel release]; - [mLocationLabel release]; - [mKeywordLabel release]; - [mDescriptionLabel release]; - [mDockMenuCheckbox release]; + [mFolderKeywordField release]; + [mFolderKeywordLabel release]; [mTabgroupCheckbox release]; - + [mBookmarkInfoTabView release]; + [mBookmarkUpdateTabView release]; + [mFolderInfoTabView release]; [super dealloc]; } @@ -120,160 +119,221 @@ static BookmarkInfoController *sharedBookmarkInfoController = nil; -(void)windowDidBecomeKey:(NSNotification*) aNotification { - [[self window] makeFirstResponder:mNameField]; + NSTabViewItem *tabViewItem = [mTabView selectedTabViewItem]; + if (tabViewItem == mBookmarkInfoTabView) + [[self window] makeFirstResponder:mBookmarkNameField]; + else if (tabViewItem == mFolderInfoTabView) + [[self window] makeFirstResponder:mFolderNameField]; + else if (tabViewItem == mBookmarkUpdateTabView) + [[self window] makeFirstResponder:mClearNumberVisitsButton]; } -(void)windowDidResignKey:(NSNotification*) aNotification { [[self window] makeFirstResponder:[self window]]; - if (![[self window] isVisible]) - mBookmarkItem = nil; + if (![[self window] isVisible]) + [self setBookmark:nil]; } - (void)windowWillClose:(NSNotification *)aNotification { [self commitChanges:nil]; - mBookmarkItem = nil; + [self setBookmark:nil]; } - (void)commitChanges:(id)changedField { - if (![mBookmarkItem contentNode]) - return; - - BOOL changed = NO; - // Name - if ((!changedField && [mNameField superview]) || changedField == mNameField) - changed |= [self commitField:mNameField toProperty:BookmarksService::gNameAtom]; - - // Location - if ((!changedField && [mLocationField superview]) || changedField == mLocationField) - changed |= [self commitField:mLocationField toProperty:BookmarksService::gHrefAtom]; - - // Keyword - if ((!changedField && [mKeywordField superview]) || changedField == mKeywordField) - changed |= [self commitField:mKeywordField toProperty:BookmarksService::gKeywordAtom]; - - // Description - if ((!changedField && [mDescriptionField superview]) || changedField == mDescriptionField) - changed |= [self commitField:mDescriptionField toProperty:BookmarksService::gDescriptionAtom]; - - [[mFieldEditor undoManager] removeAllActions]; - if (changed) - [mBookmarkItem itemChanged:YES]; -} - -// return YES if changed -- (BOOL)commitField:(id)textField toProperty:(nsIAtom*)propertyAtom -{ - NSString* newValue = [textField stringValue]; - NSString* oldValue = [mBookmarkItem getAttributeValue:propertyAtom]; - - if (![newValue isEqualToString:oldValue]) - { - [mBookmarkItem setAttribute:propertyAtom toValue:newValue]; - return YES; + NSTabViewItem *tabViewItem = [mTabView selectedTabViewItem]; + BOOL isBookmark; + if ((isBookmark = [mBookmarkItem isKindOfClass:[Bookmark class]])) { + if ([(Bookmark *)mBookmarkItem isSeparator] || ![[mBookmarkItem parent] isKindOfClass:[BookmarkItem class]]) + return; } - - return NO; + if (!changedField) { + if ((tabViewItem == mBookmarkInfoTabView) && isBookmark) { + [mBookmarkItem setTitle:[mBookmarkNameField stringValue]]; + [mBookmarkItem setDescription:[mBookmarkDescField stringValue]]; + [mBookmarkItem setKeyword:[mBookmarkKeywordField stringValue]]; + [(Bookmark *)mBookmarkItem setUrl:[mBookmarkLocationField stringValue]]; + } + else if (tabViewItem == mFolderInfoTabView && !isBookmark) { + [mBookmarkItem setTitle:[mFolderNameField stringValue]]; + [mBookmarkItem setDescription:[mFolderDescField stringValue]]; + if ([(BookmarkFolder *)mBookmarkItem isGroup]) + [mBookmarkItem setKeyword:[mFolderKeywordField stringValue]]; + } + } + else if ((changedField == mBookmarkNameField) || (changedField == mFolderNameField)) + [mBookmarkItem setTitle:[changedField stringValue]]; + else if ((changedField == mBookmarkKeywordField) || (changedField == mFolderKeywordField)) + [mBookmarkItem setKeyword:[changedField stringValue]]; + else if ((changedField == mBookmarkDescField) || (changedField == mFolderDescField)) + [mBookmarkItem setDescription:[changedField stringValue]]; + else if ((changedField == mBookmarkLocationField) && isBookmark) + [(Bookmark *)mBookmarkItem setUrl:[changedField stringValue]]; + + [[mFieldEditor undoManager] removeAllActions]; } -- (IBAction)dockMenuCheckboxClicked:(id)sender +// there's a bug on first load. I don't know why. But this fixes it, so I'll leave it in. +-(IBAction)showWindow:(id)sender { - if ([sender state] == NSOnState) - BookmarksService::SetDockMenuRoot([mBookmarkItem contentNode]); - else - BookmarksService::SetDockMenuRoot(NULL); + [self updateUI:mBookmarkItem]; + [super showWindow:sender]; } - (IBAction)tabGroupCheckboxClicked:(id)sender { - [mBookmarkItem setIsGroup:[sender state] == NSOnState]; - [mBookmarkItem itemChanged:YES]; + if ([mBookmarkItem isKindOfClass:[BookmarkFolder class]]) + [(BookmarkFolder *)mBookmarkItem setIsGroup:[sender state] == NSOnState]; +} + +- (IBAction)dockMenuCheckboxClicked:(id)sender +{ + if ([mBookmarkItem isKindOfClass:[BookmarkFolder class]]) { + [(BookmarkFolder *)mBookmarkItem setIsDockMenu:([sender state] == NSOnState)]; + [mDockMenuCheckbox setEnabled:NO]; + } +} + +- (IBAction)clearVisitCount:(id)sender +{ + if ([mBookmarkItem isKindOfClass:[Bookmark class]]) + [(Bookmark *)mBookmarkItem setNumberOfVisits:0]; + [mNumberVisitsField setIntValue:0]; } -(void)setBookmark: (BookmarkItem*) aBookmark { - // See bug 154081 - don't show this window if Bookmark doesn't exist - // after fix - this should never happen unless disaster strikes. - if (![aBookmark contentNode]) - return; - - BOOL isGroup = [aBookmark isGroup]; - BOOL isFolder = !isGroup && [aBookmark isFolder]; - - // First, Show/Hide the appropriate UI - if (isGroup) - { - [self showUIElementPair: mNameLabel control: mNameField]; - [self hideUIElementPair: mLocationLabel control: mLocationField]; - [self showUIElementPair: mKeywordLabel control: mKeywordField]; - [self showUIElementPair: mDescriptionLabel control: mDescriptionField]; - - [mVariableFieldsContainer addSubview:mTabgroupCheckbox]; - [mVariableFieldsContainer addSubview:mDockMenuCheckbox]; - [mNameField setNextKeyView:mTabgroupCheckbox]; - [mTabgroupCheckbox setNextKeyView:mDockMenuCheckbox]; - [mDockMenuCheckbox setNextKeyView:mKeywordField]; - [mKeywordField setNextKeyView:mDescriptionField]; - - [mTabgroupCheckbox setEnabled:![aBookmark isToobarRoot]]; + // to avoid a hard-to-find bug, we do UI stuff before setting + if (aBookmark) { + [self updateUI:aBookmark]; + [aBookmark retain]; } - else if (isFolder) - { - [self showUIElementPair: mNameLabel control: mNameField]; - [self hideUIElementPair: mLocationLabel control: mLocationField]; - [self hideUIElementPair: mKeywordLabel control: mKeywordField]; - [self showUIElementPair: mDescriptionLabel control: mDescriptionField]; + [mBookmarkItem release]; + mBookmarkItem = aBookmark; +} - [mVariableFieldsContainer addSubview:mDockMenuCheckbox]; - [mVariableFieldsContainer addSubview:mTabgroupCheckbox]; - [mNameField setNextKeyView:mDockMenuCheckbox]; - [mDockMenuCheckbox setNextKeyView:mTabgroupCheckbox]; - [mTabgroupCheckbox setNextKeyView:mDescriptionField]; - - [mTabgroupCheckbox setEnabled:![aBookmark isToobarRoot]]; +-(void)updateUI:(BookmarkItem *)aBookmark +{ + if (aBookmark) { + // + // setup for bookmarks + // + int numTabs = [mTabView numberOfTabViewItems]; + if ([aBookmark isKindOfClass:[Bookmark class]]) { + if (numTabs == 1) { + [mTabView removeTabViewItem:mFolderInfoTabView]; + [mTabView setTabViewType:NSTopTabsBezelBorder]; + [mTabView insertTabViewItem:mBookmarkInfoTabView atIndex:0]; + [mTabView insertTabViewItem:mBookmarkUpdateTabView atIndex:1]; + [mTabView selectLastTabViewItem:self];//have to do this to avoid "2 selected tabs" ugliness + [mTabView selectFirstTabViewItem:self]; + } + else if (numTabs == 3) { + [mTabView removeTabViewItem:mFolderInfoTabView]; + [mTabView selectFirstTabViewItem:self]; + } + [mBookmarkNameField setStringValue: [aBookmark title]]; + [mBookmarkDescField setStringValue: [aBookmark description]]; + [mBookmarkKeywordField setStringValue: [aBookmark keyword]]; + [mBookmarkLocationField setStringValue: [(Bookmark *)aBookmark url]]; + [mNumberVisitsField setIntValue:[(Bookmark *)aBookmark numberOfVisits]]; + [mLastVisitField setStringValue: [[(Bookmark *)aBookmark lastVisit] descriptionWithCalendarFormat:[[mLastVisitField formatter] dateFormat] timeZone:[NSTimeZone localTimeZone] locale:nil]]; + NSString *statusString = nil; + unsigned status = [(Bookmark *)aBookmark status]; + switch (status) { + case (kBookmarkOKStatus): + case (kBookmarkSpacerStatus): + statusString = NSLocalizedString(@"OK",@"OK"); + break; + case (kBookmarkBrokenLinkStatus): + statusString = NSLocalizedString(@"Link Broken",@"Link Broken"); + break; + case (kBookmarkMovedLinkStatus): + statusString = NSLocalizedString(@"Link has Moved",@"Link has Moved"); + break; + case (kBookmarkServerErrorStatus): + statusString = NSLocalizedString(@"Server Unreachable",@"Server Unreachable"); + break; + case (kBookmarkNeverCheckStatus): + statusString = NSLocalizedString(@"Uncheckable",@"Uncheckable"); + break; + default: + statusString = [NSString string]; + } + [mStatusField setStringValue:statusString]; + // if it's parent is a smart folder or it's a menu separator, + // we turn off all the fields. if it isn't, then we turn them all on + id parent = [aBookmark parent]; + if (([parent isKindOfClass:[BookmarkItem class]]) && + (![parent isSmartFolder]) && + (![(Bookmark *)aBookmark isSeparator])) + { + [mBookmarkNameField setEditable:YES]; + [mBookmarkDescField setEditable:YES]; + [mBookmarkKeywordField setEditable:YES]; + [mBookmarkLocationField setEditable:YES]; + } else + { + [mBookmarkNameField setEditable:NO]; + [mBookmarkDescField setEditable:NO]; + [mBookmarkKeywordField setEditable:NO]; + [mBookmarkLocationField setEditable:NO]; + } + } + // + // Folders + // + else if ([aBookmark isKindOfClass:[BookmarkFolder class]]) { + if (numTabs == 2) { + [mTabView removeTabViewItem:mBookmarkInfoTabView]; + [mTabView removeTabViewItem:mBookmarkUpdateTabView]; + [mTabView insertTabViewItem:mFolderInfoTabView atIndex:0]; + [mTabView setTabViewType:NSNoTabsNoBorder]; + } + else if (numTabs == 3) { + [mTabView removeTabViewItem:mBookmarkInfoTabView]; + [mTabView removeTabViewItem:mBookmarkUpdateTabView]; + [mTabView setTabViewType:NSNoTabsNoBorder]; + } + NSView *superview = [mFolderKeywordField superview]; + if ([(BookmarkFolder *)aBookmark isGroup]) { + if (!superview) { + superview = [mFolderNameField superview]; + [superview addSubview:mFolderKeywordField]; + [superview addSubview:mFolderKeywordLabel]; + [mFolderNameField setNextKeyView:mFolderKeywordField]; + } + [mTabgroupCheckbox setState:NSOnState]; + } + else { + if (superview) { + [mFolderKeywordField removeFromSuperview]; + [mFolderKeywordLabel removeFromSuperview]; + [mFolderNameField setNextKeyView:mFolderDescField]; + } + [mTabgroupCheckbox setState:NSOffState]; + } + [mFolderNameField setStringValue: [aBookmark title]]; + [mFolderDescField setStringValue: [aBookmark description]]; + // + // we can't just unselect dock menu - we have to pick a new one + // + if ([(BookmarkFolder *)aBookmark isDockMenu]) { + [mDockMenuCheckbox setState:NSOnState]; + [mDockMenuCheckbox setEnabled:NO]; + } else { + [mDockMenuCheckbox setState:NSOffState]; + [mDockMenuCheckbox setEnabled:YES]; + } + } + // Header + NSMutableString *truncatedTitle = [NSMutableString stringWithString:[aBookmark title]]; + [truncatedTitle truncateTo:kMaxLengthOfWindowTitle at:kTruncateAtEnd]; + NSString* infoForString = [NSString stringWithFormat:NSLocalizedString(@"BookmarkInfoTitle", @"Info for "), truncatedTitle]; + [[self window] setTitle: infoForString]; } - else - { - [self showUIElementPair: mNameLabel control: mNameField]; - [self showUIElementPair: mLocationLabel control: mLocationField]; - [self showUIElementPair: mKeywordLabel control: mKeywordField]; - [self showUIElementPair: mDescriptionLabel control: mDescriptionField]; - - [mDockMenuCheckbox removeFromSuperview]; - [mTabgroupCheckbox removeFromSuperview]; - [mNameField setNextKeyView:mLocationField]; - [mLocationField setNextKeyView:mKeywordField]; - [mKeywordField setNextKeyView:mDescriptionField]; - } - - // Then, fill with appropriate values from Bookmarks - NSString* bookmarkName = [aBookmark name]; - NSString* infoForString = [NSString stringWithFormat:NSLocalizedString(@"BookmarkInfoTitle", @"Info for "), bookmarkName]; - [[self window] setTitle: infoForString]; - - if (isGroup) - { - [mDockMenuCheckbox setState:([aBookmark isDockMenuRoot] ? NSOnState : NSOffState)]; - [mTabgroupCheckbox setState:NSOnState]; - [mKeywordField setStringValue: [aBookmark keyword]]; - } - else if (isFolder) - { - [mDockMenuCheckbox setState:([aBookmark isDockMenuRoot] ? NSOnState : NSOffState)]; - [mTabgroupCheckbox setState:NSOffState]; - } - else - { - [mKeywordField setStringValue: [aBookmark keyword]]; - [mLocationField setStringValue: [aBookmark url]]; - } - - [mNameField setStringValue: bookmarkName]; - [mDescriptionField setStringValue: [aBookmark descriptionString]]; - - mBookmarkItem = aBookmark; } -(BookmarkItem *)bookmark @@ -281,29 +341,6 @@ static BookmarkInfoController *sharedBookmarkInfoController = nil; return mBookmarkItem; } --(void)showUIElementPair: (id)aLabel control:(id)aControl -{ - if ([aLabel superview] == nil) - [mVariableFieldsContainer addSubview: aLabel]; - - if ([aControl superview] == nil) - [mVariableFieldsContainer addSubview: aControl]; - - // we need to resize the fields in case the user resized the window when they were hidden - NSRect containerBounds = [mVariableFieldsContainer bounds]; - NSRect controlFrame = [aControl frame]; - controlFrame.size.width = (containerBounds.size.width - controlFrame.origin.x - 20.0); - [aControl setFrame:controlFrame]; -} - --(void)hideUIElementPair: (id)aLabel control:(id)aControl -{ - if ([aLabel superview] != nil) - [aLabel removeFromSuperview]; - - if ([aControl superview] != nil) - [aControl removeFromSuperview]; -} -(NSText *)windowWillReturnFieldEditor:(NSWindow *)aPanel toObject:(id)aObject { @@ -312,21 +349,21 @@ static BookmarkInfoController *sharedBookmarkInfoController = nil; #pragma mark - -- (void)bookmarkAdded:(nsIContent*)bookmark inContainer:(nsIContent*)container isChangedRoot:(BOOL)isRoot +- (void)bookmarkAdded:(NSNotification *)aNote { } -- (void)bookmarkRemoved:(nsIContent*)bookmark inContainer:(nsIContent*)container isChangedRoot:(BOOL)isRoot +- (void)bookmarkRemoved:(NSNotification *)aNote { - if ([mBookmarkItem contentNode] == bookmark) - mBookmarkItem = nil; + NSDictionary *dict = [aNote userInfo]; + BookmarkItem *item = [dict objectForKey:BookmarkFolderChildKey]; + if ((item == [self bookmark]) && ![item parent]) { + [self setBookmark:nil]; + [[self window] close]; + } } -- (void)bookmarkChanged:(nsIContent*)bookmark -{ -} - -- (void)specialFolder:(EBookmarksFolderType)folderType changedTo:(nsIContent*)newFolderContent +- (void)bookmarkChanged:(NSNotification *)aNote { } diff --git a/camino/src/bookmarks/BookmarkItem.h b/camino/src/bookmarks/BookmarkItem.h new file mode 100644 index 000000000000..92aa64de5ccf --- /dev/null +++ b/camino/src/bookmarks/BookmarkItem.h @@ -0,0 +1,86 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** +* Version: NPL 1.1/GPL 2.0/LGPL 2.1 +* +* The contents of this file are subject to the Netscape Public License +* Version 1.1 (the "License"); you may not use this file except in +* compliance with the License. You may obtain a copy of the License at +* http://www.mozilla.org/NPL/ +* +* Software distributed under the License is distributed on an "AS IS" basis, +* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +* for the specific language governing rights and limitations under the +* License. +* +* The Original Code is mozilla.org code. +* +* The Initial Developer of the Original Code is +* Netscape Communications Corporation. +* Portions created by the Initial Developer are Copyright (C) 2002 +* the Initial Developer. All Rights Reserved. +* +* Contributor(s): +* David Haas +* +* +* Alternatively, the contents of this file may be used under the terms of +* either the GNU General Public License Version 2 or later (the "GPL"), or +* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +* in which case the provisions of the GPL or the LGPL are applicable instead +* of those above. If you wish to allow use of your version of this file only +* under the terms of either the GPL or the LGPL, and not to allow others to +* use your version of this file under the terms of the NPL, indicate your +* decision by deleting the provisions above and replace them with the notice +* and other provisions required by the GPL or the LGPL. If you do not delete +* the provisions above, a recipient may use your version of this file under +* the terms of any one of the NPL, the GPL or the LGPL. +* +* ***** END LICENSE BLOCK ***** */ + +// superclass for Bookmark & BookmarkFolder. +// Basically here to aid in scripting support. + +#import + +@interface BookmarkItem : NSObject +{ + id mParent; //subclasses will use a BookmarkFolder + NSString* mTitle; + NSString* mDescription; + NSString* mKeyword; + NSImage* mIcon; +} + +// Setters/Getters +-(id) parent; +-(NSString *) title; +-(NSString *) description; +-(NSString *) keyword; +-(NSImage *) icon; + +-(void) setParent:(id)aParent; +-(void) setTitle:(NSString *)aString; +-(void) setDescription:(NSString *)aString; +-(void) setKeyword:(NSString *)aKeyword; +-(void) setIcon:(NSImage *)aIcon; + +// Status checks +-(BOOL) isChildOfItem:(BookmarkItem *)anItem; + +// Notificaiton of Change +-(void) itemUpdatedNote; //right now, just on title & icon - for BookmarkButton & BookmarkMenu notes + +// Methods called on startup for both bookmark & folder +-(void) refreshIcon; + + // for reading/writing to disk - unimplemented in BookmarkItem. +-(BOOL) readNativeDictionary:(NSDictionary *)aDict; +-(BOOL) readSafariDictionary:(NSDictionary *)aDict; +-(BOOL) readCaminoXML:(CFXMLTreeRef)aTreeRef; + +-(NSDictionary *)writeNativeDictionary; +-(NSString *)writeHTML:(unsigned)aPad; + + + +@end diff --git a/camino/src/bookmarks/BookmarkItem.m b/camino/src/bookmarks/BookmarkItem.m new file mode 100644 index 000000000000..645a5c4699d1 --- /dev/null +++ b/camino/src/bookmarks/BookmarkItem.m @@ -0,0 +1,210 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** +* Version: NPL 1.1/GPL 2.0/LGPL 2.1 +* +* The contents of this file are subject to the Netscape Public License +* Version 1.1 (the "License"); you may not use this file except in +* compliance with the License. You may obtain a copy of the License at +* http://www.mozilla.org/NPL/ +* +* Software distributed under the License is distributed on an "AS IS" basis, +* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +* for the specific language governing rights and limitations under the +* License. +* +* The Original Code is mozilla.org code. +* +* The Initial Developer of the Original Code is +* Netscape Communications Corporation. +* Portions created by the Initial Developer are Copyright (C) 2002 +* the Initial Developer. All Rights Reserved. +* +* Contributor(s): +* David Haas +* +* +* Alternatively, the contents of this file may be used under the terms of +* either the GNU General Public License Version 2 or later (the "GPL"), or +* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +* in which case the provisions of the GPL or the LGPL are applicable instead +* of those above. If you wish to allow use of your version of this file only +* under the terms of either the GPL or the LGPL, and not to allow others to +* use your version of this file under the terms of the NPL, indicate your +* decision by deleting the provisions above and replace them with the notice +* and other provisions required by the GPL or the LGPL. If you do not delete +* the provisions above, a recipient may use your version of this file under +* the terms of any one of the NPL, the GPL or the LGPL. +* +* ***** END LICENSE BLOCK ***** */ + +#import "BookmarkItem.h" + +// Notifications +NSString *BookmarkItemChangedNotification = @"bi_cg"; + + +@implementation BookmarkItem +//Initialization +-(id) init +{ + if ((self = [super init])) + { +// NSString *tempString = [[NSString alloc] init]; + mParent = NULL; + mTitle = [[NSString alloc] init]; + mKeyword = [[NSString alloc] init]; + mDescription = [[NSString alloc] init]; + // if we set the icon here, we will get a memory leak. so don't. + // subclass will provide icon. + mIcon = NULL; + } + return self; +} + +-(id) copyWithZone:(NSZone *)zone +{ + //descend from NSObject - so don't call super + id doppleganger = [[[self class] allocWithZone:zone] init]; + [doppleganger setTitle:[self title]]; + [doppleganger setDescription:[self description]]; + [doppleganger setKeyword:[self keyword]]; + [doppleganger setParent:[self parent]]; + [doppleganger setIcon:[self icon]]; + return doppleganger; +} + +-(void)dealloc +{ + [mTitle release]; + [mDescription release]; + [mKeyword release]; + [mIcon release]; + [super dealloc]; +} + + +// Basic properties +-(id) parent +{ + return mParent; +} + +-(NSString *) title +{ + return mTitle; +} + +-(NSString *) description +{ + return mDescription; +} + +-(NSString *) keyword +{ + return mKeyword; +} + + +-(NSImage *)icon +{ + return mIcon; +} + +-(BOOL) isChildOfItem:(BookmarkItem *)anItem +{ + if (![[self parent] isKindOfClass:[BookmarkItem class]]) + return NO; + if ([self parent] == anItem) + return YES; + return [[self parent] isChildOfItem:anItem]; +} + +-(void) setParent:(id) aParent +{ + mParent = aParent; // no reference on the parent, so it better not disappear on us. +} + +-(void) setTitle:(NSString *)aTitle +{ + if (!aTitle) + return; + [aTitle retain]; + [mTitle release]; + mTitle = aTitle; + [self itemUpdatedNote]; +} + +-(void) setDescription:(NSString *)aDescription +{ + if (!aDescription) + return; + [aDescription retain]; + [mDescription release]; + mDescription = aDescription; +} + +- (void) setKeyword:(NSString *)aKeyword +{ + if (!aKeyword) + return; + [aKeyword retain]; + [mKeyword release]; + mKeyword = aKeyword; +} + +-(void) setIcon:(NSImage *)aIcon +{ + if (!aIcon) + return; + [aIcon retain]; + [mIcon release]; + mIcon = aIcon; + [self itemUpdatedNote]; +} + +-(void) itemUpdatedNote +{ + NSNotification *note = [NSNotification notificationWithName:BookmarkItemChangedNotification object:self userInfo:nil]; + NSNotificationCenter *nc = [NSNotificationCenter defaultCenter]; + [nc postNotification:note]; + return; +} + +// stub functions to avoid warning + +-(void) refreshIcon +{ +} + +//Reading/writing to & from disk - all just stubs. + +-(BOOL) readNativeDictionary:(NSDictionary *)aDict +{ + return NO; +} + +-(BOOL) readSafariDictionary:(NSDictionary *)aDict +{ + return NO; +} + +-(BOOL) readCaminoXML:(CFXMLTreeRef)aTreeRef +{ + return NO; +} + +-(NSDictionary *)writeNativeDictionary +{ + return [NSDictionary dictionary]; +} + +-(NSString *)writeHTML:(unsigned)aPad +{ + return [NSString string]; +} + + +@end + + + diff --git a/camino/src/bookmarks/BookmarkManager.h b/camino/src/bookmarks/BookmarkManager.h new file mode 100644 index 000000000000..f8a2e82cc005 --- /dev/null +++ b/camino/src/bookmarks/BookmarkManager.h @@ -0,0 +1,106 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** +* Version: NPL 1.1/GPL 2.0/LGPL 2.1 +* +* The contents of this file are subject to the Netscape Public License +* Version 1.1 (the "License"); you may not use this file except in +* compliance with the License. You may obtain a copy of the License at +* http://www.mozilla.org/NPL/ +* +* Software distributed under the License is distributed on an "AS IS" basis, +* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +* for the specific language governing rights and limitations under the +* License. +* +* The Original Code is mozilla.org code. +* +* The Initial Developer of the Original Code is +* Netscape Communications Corporation. +* Portions created by the Initial Developer are Copyright (C) 2002 +* the Initial Developer. All Rights Reserved. +* +* Contributor(s): +* David Haas +* +* +* Alternatively, the contents of this file may be used under the terms of +* either the GNU General Public License Version 2 or later (the "GPL"), or +* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +* in which case the provisions of the GPL or the LGPL are applicable instead +* of those above. If you wish to allow use of your version of this file only +* under the terms of either the GPL or the LGPL, and not to allow others to +* use your version of this file under the terms of the NPL, indicate your +* decision by deleting the provisions above and replace them with the notice +* and other provisions required by the GPL or the LGPL. If you do not delete +* the provisions above, a recipient may use your version of this file under +* the terms of any one of the NPL, the GPL or the LGPL. +* +* ***** END LICENSE BLOCK ***** */ + +#import +#import "BookmarksClient.h" + +@class BookmarkFolder; +@class BookmarkImportDlgController; +@class KindaSmartFolderManager; +@class RunLoopMessenger; + +#define kBookmarkMenuContainerIndex 0 +#define kToolbarContainerIndex 1 +#define kHistoryContainerIndex 2 +#define kTop10ContainerIndex 3 +#define kBrokenBookmarkContainerIndex 4 +#define kRendezvousContainerIndex 5 +#define kAddressBookContainerIndex 6 + +// check 1 bookmark every 2 minutes, but only if we haven't been there in a day +#define kTimeSinceBookmarkLastChecked 86400.0 +#define kTimeToCheckAnotherBookmark 120 + +@interface BookmarkManager : NSObject { + BookmarkFolder *mRootBookmarks; // root bookmark object + KindaSmartFolderManager *mSmartFolderManager; //brains behind 4 smart folders + NSUndoManager *mUndoManager;// handles deletes, adds of bookmarks + BookmarkImportDlgController *mImportDlgController; + NSString *mPathToBookmarkFile; //exactly what it looks like + NSTimer *mUpdateTimer; //we don't actually retain this +} + +// Class Methods & shutdown stuff ++ (void)startBookmarksManager:(RunLoopMessenger *)mainThreadRunLoopMessenger; ++ (BookmarkManager*)sharedBookmarkManager; +- (void)shutdown; + +// Getters/Setters +-(BookmarkFolder *) rootBookmarks; +-(BookmarkFolder *) toolbarFolder; +-(BookmarkFolder *) bookmarkMenuFolder; +-(BookmarkFolder *) dockMenuFolder; +-(BookmarkFolder *) top10Folder; +-(BookmarkFolder *) brokenLinkFolder; +-(BookmarkFolder *) rendezvousFolder; +-(BookmarkFolder *) addressBookFolder; +-(BookmarkFolder *) historyFolder; +-(NSUndoManager *) undoManager; +-(void) setRootBookmarks:(BookmarkFolder *)anArray; + +// Informational things +-(NSArray *)resolveBookmarksKeyword:(NSString *)keyword; +-(NSArray *)searchBookmarksForString:(NSString *)searchString; +-(unsigned) firstUserCollection; +-(BOOL) isDropValid:(NSArray *)items toFolder:(BookmarkFolder *)parent; + +// Reading bookmark files +-(BOOL) readBookmarks; +-(void) startImportBookmarks; +-(void) importBookmarks:(NSString *)pathToFile intoFolder:(BookmarkFolder *)aFolder; +-(NSString *)decodedHTMLfile:(NSString *)pathToFile; +-(BOOL)readHTMLFile:(NSString *)pathToFile intoFolder:(BookmarkFolder *)aFolder; +-(BOOL)readCaminoXMLFile:(NSString *)pathToFile intoFolder:(BookmarkFolder *)aFolder; +-(BOOL)readPropertyListFile:(NSString *)pathToFile intoFolder:(BookmarkFolder *)aFolder; + +// Writing bookmark files +-(void)writeHTMLFile:(NSString *)pathToFile; +-(void)writePropertyListFile:(NSString *)pathToFile; + +@end diff --git a/camino/src/bookmarks/BookmarkManager.mm b/camino/src/bookmarks/BookmarkManager.mm new file mode 100644 index 000000000000..22d164cf95be --- /dev/null +++ b/camino/src/bookmarks/BookmarkManager.mm @@ -0,0 +1,813 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** +* Version: NPL 1.1/GPL 2.0/LGPL 2.1 +* +* The contents of this file are subject to the Netscape Public License +* Version 1.1 (the "License"); you may not use this file except in +* compliance with the License. You may obtain a copy of the License at +* http://www.mozilla.org/NPL/ +* +* Software distributed under the License is distributed on an "AS IS" basis, +* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +* for the specific language governing rights and limitations under the +* License. +* +* The Original Code is mozilla.org code. +* +* The Initial Developer of the Original Code is +* Netscape Communications Corporation. +* Portions created by the Initial Developer are Copyright (C) 2002 +* the Initial Developer. All Rights Reserved. +* +* Contributor(s): +* David Haas +* +* +* Alternatively, the contents of this file may be used under the terms of +* either the GNU General Public License Version 2 or later (the "GPL"), or +* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +* in which case the provisions of the GPL or the LGPL are applicable instead +* of those above. If you wish to allow use of your version of this file only +* under the terms of either the GPL or the LGPL, and not to allow others to +* use your version of this file under the terms of the NPL, indicate your +* decision by deleting the provisions above and replace them with the notice +* and other provisions required by the GPL or the LGPL. If you do not delete +* the provisions above, a recipient may use your version of this file under +* the terms of any one of the NPL, the GPL or the LGPL. +* +* ***** END LICENSE BLOCK ***** */ + +#include "nsString.h" +#include "nsIContent.h" +#include "nsIFile.h" +#include "nsAppDirectoryServiceDefs.h" +#import "NSString+Utils.h" +#import "PreferenceManager.h" +#import "RunLoopMessenger.h" +#import "BookmarkManager.h" +#import "Bookmark.h" +#import "BookmarkFolder.h" +#import "BookmarkImportDlgController.h" +#import "KindaSmartFolderManager.h" +#import "MainController.h" + +@interface BookmarkManager (Private) +- (void)setPathToBookmarkFile:(NSString *)aString; +- (void)setupSmartCollections; +- (void)delayedStartupItems; +- (void)writeBookmarks:(NSNotification *)note; +- (void)checkForUpdates:(NSTimer *)aTimer; +- (BookmarkFolder *)findDockMenuFolderInFolder:(BookmarkFolder *)aFolder; +- (Bookmark *)findABookmarkToCheckInFolder:(BookmarkFolder *)aFolder; +@end + +@implementation BookmarkManager + +static NSString *WriteBookmarkNotification = @"write_bms"; +static BookmarkManager* gBookmarksManager = nil; +static NSLock *startupLock = nil; +static unsigned gFirstUserCollection = 0; + + +// +// Class Methods - we only need RunLoopMessenger for 10.1 Compat. On 10.2+, there +// are built-in methods for running something on the main thread. +// ++ (void)startBookmarksManager:(RunLoopMessenger *)mainThreadRunLoopMessenger +{ + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + if (!gBookmarksManager && !startupLock) + { + startupLock = [[NSLock alloc] init]; + NSLock *avoidRaceLock; + BookmarkManager *aManager = [[BookmarkManager alloc] init]; + [startupLock lock]; + gBookmarksManager = aManager; + avoidRaceLock = startupLock; + startupLock = nil; + [avoidRaceLock unlock]; + [avoidRaceLock release]; + [[NSApp delegate] setupBookmarkMenus:gBookmarksManager]; + [mainThreadRunLoopMessenger target:gBookmarksManager performSelector:@selector(delayedStartupItems)]; + } + [pool release]; +} + ++ (BookmarkManager*)sharedBookmarkManager +{ + BookmarkManager *theManager; + [startupLock lock]; + theManager = gBookmarksManager; + [startupLock unlock]; + return theManager; +} + +// +// Init, dealloc - better get inited on background thread. +// +- (id)init +{ + if ((self = [super init])) + { + BookmarkFolder* root = [[BookmarkFolder alloc] init]; + [root setParent:self]; + [root setIsRoot:YES]; + [root setTitle:NSLocalizedString(@"BookmarksRootName", @"")]; + [self setRootBookmarks:root]; + [root release]; + if (![self readBookmarks]) { + // one of two things happened. we are importing off an old xml file + // for startup, OR we totally muffed reading the bookmarks. we'll hope + // it was the former. + if ([root count] > 0) { + // find the xml toolbar menu. it'll be in top level of bookmark menu folder + NSMutableArray *childArray = [[self bookmarkMenuFolder] childArray]; + unsigned i, j=[childArray count]; + id anObject; + for (i=0;i < j; i++) { + anObject = [childArray objectAtIndex:i]; + if ([anObject isKindOfClass:[BookmarkFolder class]]) { + if ([(BookmarkFolder *)anObject isToolbar]) { //triumph! + [[self bookmarkMenuFolder] moveChild:anObject toBookmarkFolder:root atIndex:kToolbarContainerIndex]; + break; + } + } + } + } else { //we are so totally screwed + BookmarkFolder *aFolder = [root addBookmarkFolder]; + if ([root count] == 1) { + [aFolder setTitle:NSLocalizedString(@"Bookmark Menu",@"Bookmark Menu")]; + aFolder = [root addBookmarkFolder]; + } + [aFolder setTitle:NSLocalizedString(@"Bookmark Toolbar",@"Bookmark Toolbar")]; + } + } + // setup special folders + [self setupSmartCollections]; + mSmartFolderManager = [[KindaSmartFolderManager alloc] initWithBookmarkManager:self]; + // don't do this until after we've read in the bookmarks + mUndoManager = [[NSUndoManager alloc] init]; + // Generic notifications for Bookmark Client + NSNotificationCenter *nc = [NSNotificationCenter defaultCenter]; + [nc addObserver:self selector:@selector(bookmarkAdded:) name:BookmarkFolderAdditionNotification object:nil]; + [nc addObserver:self selector:@selector(bookmarkRemoved:) name:BookmarkFolderDeletionNotification object:nil]; + [nc addObserver:self selector:@selector(bookmarkChanged:) name:BookmarkItemChangedNotification object:nil]; + [nc addObserver:self selector:@selector(writeBookmarks:) name:WriteBookmarkNotification object:nil]; + } + return self; +} + +-(void) dealloc +{ + [[NSNotificationCenter defaultCenter] removeObserver:self]; + if (mUpdateTimer) + [mUpdateTimer invalidate]; //we don't retain this, so don't release it. + [mUndoManager release]; + [mRootBookmarks release]; + [mPathToBookmarkFile release]; + [mSmartFolderManager release]; + if (mImportDlgController) + [mImportDlgController release]; + if (self == gBookmarksManager) + gBookmarksManager = nil; + [super dealloc]; +} + +- (void)delayedStartupItems +{ + // check update status of 1 bookmark every 2 minutes. + mUpdateTimer = [NSTimer scheduledTimerWithTimeInterval:kTimeToCheckAnotherBookmark target:self selector:@selector(checkForUpdates:) userInfo:nil repeats:YES]; + [mSmartFolderManager postStartupInitialization:self]; + [[[self toolbarFolder] objectAtIndex:0] itemUpdatedNote];//makes sure we have toolbar on 1st window + if ([[PreferenceManager sharedInstance] getBooleanPref:"browser.chrome.favicons" withSuccess:NULL]) + [mRootBookmarks refreshIcon]; +} + +- (void)shutdown; +{ + [self writeBookmarks:nil]; +} + +// +// smart collections, as of now, are Rendezvous, Address Book, Top 10 List, Broken Bookmarks, +// We also have history, but that just points to the real history stuff. +- (void)setupSmartCollections +{ + NSArray *names = nil; + if (floor(NSAppKitVersionNumber) <= NSAppKitVersionNumber10_1) //10.1 + names = [[NSArray alloc] initWithObjects: + NSLocalizedString(@"History",@"History"), + NSLocalizedString(@"Top 10 List",@"Top 10 List"), + NSLocalizedString(@"Broken Bookmarks",@"Broken Bookmarks"), + nil]; + else // 10.2 + + names = [[NSArray alloc] initWithObjects: + NSLocalizedString(@"History",@"History"), + NSLocalizedString(@"Top 10 List",@"Top 10 List"), + NSLocalizedString(@"Broken Bookmarks",@"Broken Bookmarks"), + NSLocalizedString(@"Rendezvous",@"Rendezvous"), + NSLocalizedString(@"Address Book",@"Address Book"), + nil]; + gFirstUserCollection = [names count]+2; + unsigned i, j=[names count]; + for (i=0; i < j; i++) { + BookmarkFolder *temp = [[BookmarkFolder alloc] init]; + [temp setTitle:[names objectAtIndex:i]]; + [temp setIsSmartFolder:YES]; + [mRootBookmarks insertChild:temp atIndex:(i+2) isMove:NO]; + [temp release]; + } + [names release]; + // set pretty icons + [[self historyFolder] setIcon:[NSImage imageNamed:@"historyicon"]]; + [[self top10Folder] setIcon:[NSImage imageNamed:@"top10_icon"]]; + [[self bookmarkMenuFolder] setIcon:[NSImage imageNamed:@"bookmarkmenu_icon"]]; + [[self toolbarFolder] setIcon:[NSImage imageNamed:@"bookmarktoolbar_icon"]]; + [[self rendezvousFolder] setIcon:[NSImage imageNamed:@"rendezvous_icon"]]; + [[self addressBookFolder] setIcon:[NSImage imageNamed:@"addressbook_icon"]]; + [[self brokenLinkFolder] setIcon:[NSImage imageNamed:@"brokenbookmark_icon"]]; +} + +// +// Getter/Setter methods +// + +-(BookmarkFolder *) rootBookmarks +{ + return mRootBookmarks; +} + +-(BookmarkFolder *) dockMenuFolder +{ + BookmarkFolder *folder = [self findDockMenuFolderInFolder:[self rootBookmarks]]; + if (folder) + return folder; + else + return [self top10Folder]; +} + +- (BookmarkFolder *)findDockMenuFolderInFolder:(BookmarkFolder *)aFolder +{ + NSEnumerator *enumerator = [[aFolder childArray] objectEnumerator]; + id aKid; + BookmarkFolder *foundFolder = nil; + while ((!foundFolder) && (aKid = [enumerator nextObject])) { + if ([aKid isKindOfClass:[BookmarkFolder class]]) { + if ([(BookmarkFolder *)aKid isDockMenu]) + return aKid; + else + foundFolder = [self findDockMenuFolderInFolder:aKid]; + } + } + return foundFolder; +} + +-(BookmarkFolder *)top10Folder +{ + return [[self rootBookmarks] objectAtIndex:kTop10ContainerIndex]; +} + +-(BookmarkFolder *) brokenLinkFolder +{ + return [[self rootBookmarks] objectAtIndex:kBrokenBookmarkContainerIndex]; +} + +-(BookmarkFolder *) toolbarFolder +{ + return [[self rootBookmarks] objectAtIndex:kToolbarContainerIndex]; +} + +-(BookmarkFolder *) bookmarkMenuFolder +{ + return [[self rootBookmarks] objectAtIndex:kBookmarkMenuContainerIndex]; +} + +-(BookmarkFolder *) historyFolder +{ + return [[self rootBookmarks] objectAtIndex:kHistoryContainerIndex]; +} + +-(BookmarkFolder *) rendezvousFolder +{ + if (floor(NSAppKitVersionNumber) > NSAppKitVersionNumber10_1) + return [[self rootBookmarks] objectAtIndex:kRendezvousContainerIndex]; + else + return nil; +} + +-(BookmarkFolder *) addressBookFolder +{ + if (floor(NSAppKitVersionNumber) > NSAppKitVersionNumber10_1) + return [[self rootBookmarks] objectAtIndex:kAddressBookContainerIndex]; + else + return nil; +} + +-(NSUndoManager *) undoManager +{ + return mUndoManager; +} + +-(unsigned) firstUserCollection +{ + return gFirstUserCollection; +} + +- (void)setPathToBookmarkFile:(NSString *)aString +{ + [aString retain]; + [mPathToBookmarkFile release]; + mPathToBookmarkFile = aString; +} + +-(void) setRootBookmarks:(BookmarkFolder *)anArray +{ + if (anArray != mRootBookmarks) { + [anArray retain]; + [mRootBookmarks release]; + mRootBookmarks = anArray; + } +} + +-(NSArray *)resolveBookmarksKeyword:(NSString *)keyword +{ + NSArray *resolvedArray = nil; + if (![keyword isEqualToString:@""]) + resolvedArray = [[self rootBookmarks] resolveKeyword:keyword]; + if (resolvedArray) + return resolvedArray; + return [NSArray arrayWithObject:keyword]; +} + +-(NSArray *)searchBookmarksForString:(NSString *)searchString +{ + NSMutableArray *matchingArray = nil; + if ((searchString) && ![searchString isEqualToString:@""]) { + NSSet *matchingSet = [[self rootBookmarks] bookmarksWithString:searchString]; + NSEnumerator *enumerator = [matchingSet objectEnumerator]; + id aThingy; + matchingArray = [NSMutableArray array]; + while ((aThingy = [enumerator nextObject])) + [matchingArray addObject:aThingy]; + } + return matchingArray; +} + +// +// every couple of minutes, this gets called +// it finds the first bookmark we haven't been to in 24 hours +// and makes sure it's still there +// +- (void)checkForUpdates:(NSTimer *)aTimer +{ + Bookmark *bm = [self findABookmarkToCheckInFolder:[self rootBookmarks]]; + if (bm) + [bm checkForUpdate]; +} + +-(Bookmark *)findABookmarkToCheckInFolder:(BookmarkFolder *)aFolder +{ + NSEnumerator *enumerator = [[aFolder childArray] objectEnumerator]; + id aKid; + Bookmark *foundBookmark = nil; + while ((!foundBookmark) && (aKid = [enumerator nextObject])) { + if ([aKid isKindOfClass:[Bookmark class]]) { + if (([(Bookmark *)aKid isCheckable]) && + ([[(Bookmark *)aKid lastVisit] timeIntervalSinceNow] < -kTimeSinceBookmarkLastChecked)) + foundBookmark = aKid; + } else if ([aKid isKindOfClass:[BookmarkFolder class]]) + foundBookmark = [self findABookmarkToCheckInFolder:aKid]; + } + return foundBookmark; +} + +// +// Drag & drop +// + +-(BOOL) isDropValid:(NSArray *)items toFolder:(BookmarkFolder *)parent +{ + // Enumerate through items, make sure we're not being dropped into + // a child OR ourself OR that the a bookmark or group is going into root bookmarks. + NSEnumerator *enumerator = [items objectEnumerator]; + id aBookmark; + while ((aBookmark = [enumerator nextObject])) { + if ([aBookmark isKindOfClass:[BookmarkFolder class]]) { + if (aBookmark == parent) + return NO; + if ((parent == [self rootBookmarks]) && [(BookmarkFolder *)aBookmark isGroup]) + return NO; + } else if ([aBookmark isKindOfClass:[Bookmark class]]) { + if (parent == [self rootBookmarks]) + return NO; + BookmarkFolder *menuFolder = [self bookmarkMenuFolder]; + if ([aBookmark isSeparator] && + ((![parent isChildOfItem:menuFolder]) && (parent != menuFolder))) + return NO; + } + if ([parent isChildOfItem:aBookmark]) + return NO; + } + return YES; +} + +#pragma mark - +// +// BookmarkClient protocol - so we know when to write out +// +- (void)bookmarkAdded:(NSNotification *)note +{ + [self bookmarkChanged:nil]; +} + +- (void)bookmarkRemoved:(NSNotification *)note +{ + [self bookmarkChanged:nil]; +} + +- (void)bookmarkChanged:(NSNotification *)aNote +{ + NSNotificationQueue* nq = [NSNotificationQueue defaultQueue]; + NSNotification *note = [NSNotification notificationWithName:WriteBookmarkNotification object:self userInfo:nil]; + [nq enqueueNotification:note postingStyle:NSPostASAP coalesceMask:NSNotificationCoalescingOnName forModes:[NSArray arrayWithObject:NSDefaultRunLoopMode]]; +} + +- (void)writeBookmarks:(NSNotification *)note +{ + [self writePropertyListFile:mPathToBookmarkFile]; +} + +#pragma mark - +// +// Reading/Importing bookmark files +// +-(BOOL) readBookmarks +{ + nsCOMPtr aDir; + NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, getter_AddRefs(aDir)); + if (!aDir) return NO; // should be smarter + nsCAutoString aDirPath; + nsresult rv = aDir->GetNativePath(aDirPath); + if (NS_FAILED(rv)) return NO; // should be smarter. + NSString *profileDir = [NSString stringWithUTF8String:aDirPath.get()]; + // + // figure out where Bookmarks.plist is and store it as mPathToBookmarkFile + // if there is a Bookmarks.plist, read it + // if there isn't a Bookmarks.plist, but there is a bookmarks.xml, read it. + // if there isn't either, move default Bookmarks.plist to profile dir & read it. + // + NSFileManager *fM = [NSFileManager defaultManager]; + NSString *bookmarkPath = [profileDir stringByAppendingPathComponent:@"bookmarks.plist"]; + [self setPathToBookmarkFile:bookmarkPath]; + if ([fM isReadableFileAtPath:bookmarkPath]) { + if ([self readPropertyListFile:bookmarkPath intoFolder:[self rootBookmarks]]) + return YES; // triumph! + } else if ([fM isReadableFileAtPath:[profileDir stringByAppendingPathComponent:@"bookmarks.xml"]]){ + BookmarkFolder *aFolder = [[self rootBookmarks] addBookmarkFolder]; + [aFolder setTitle:NSLocalizedString(@"Bookmark Menu",@"Bookmark Menu")]; + if ([self readCaminoXMLFile:[profileDir stringByAppendingPathComponent:@"bookmarks.xml"] intoFolder:[self bookmarkMenuFolder]]) + return NO; // triumph! - will do post processing in init + } else { + NSString *defaultBookmarks = [[NSBundle mainBundle] pathForResource:@"bookmarks" ofType:@"plist"]; + if ([fM copyPath:defaultBookmarks toPath:bookmarkPath handler:nil]) { + if ([self readPropertyListFile:bookmarkPath intoFolder:[self rootBookmarks]]) + return YES; //triumph! + } + } + // if we're here, we've had a problem + NSString *alert = NSLocalizedString(@"CorruptedBookmarksAlert",@""); + NSString *message = NSLocalizedString(@"CorruptedBookmarksMsg",@""); + NSString *okButton = NSLocalizedString(@"OKButtonText",@""); + NSRunAlertPanel(alert, message, okButton, nil, nil); + return NO; +} + +-(void) startImportBookmarks +{ + if (!mImportDlgController) + mImportDlgController = [[BookmarkImportDlgController alloc] initWithWindowNibName:@"BookmarkImportDlg"]; + [mImportDlgController buildAvailableFileList]; + [NSApp beginSheet:[mImportDlgController window] + modalForWindow:[[NSApp delegate] getFrontmostBrowserWindow] + modalDelegate:nil + didEndSelector:nil + contextInfo:nil]; +} + +-(void) importBookmarks:(NSString *)pathToFile intoFolder:(BookmarkFolder *)aFolder +{ + //I feel dirty doing it this way. But we'll check file extension + //to figure out how to handle this. Damn you, Steve Jobs!! + NSUndoManager *undoManager =[self undoManager]; + [undoManager beginUndoGrouping]; + BOOL success = NO; + NSString *extension =[pathToFile pathExtension]; + if ([extension isEqualToString:@"html"] || [extension isEqualToString:@"htm"]) + success = [self readHTMLFile:pathToFile intoFolder:aFolder]; + else if ([extension isEqualToString:@"xml"]) + success = [self readCaminoXMLFile:pathToFile intoFolder:aFolder]; + else if ([extension isEqualToString:@"plist"] || !success) + success = [self readPropertyListFile:pathToFile intoFolder:aFolder]; + // we don't know the extension, or we failed to load. we'll take another + // crack at it trying everything we know. + if (!success) { + success = [self readHTMLFile:pathToFile intoFolder:aFolder]; + if (!success) + [self readCaminoXMLFile:pathToFile intoFolder:aFolder]; + } + [[undoManager prepareWithInvocationTarget:[self rootBookmarks]] deleteChild:aFolder]; + [undoManager endUndoGrouping]; + [undoManager setActionName:NSLocalizedString(@"Import Bookmarks",@"Import Bookmarks")]; +} + +// spits out html file as NSString with proper encoding. it's pretty shitty, frankly. +-(NSString *)decodedHTMLfile:(NSString *)pathToFile +{ + NSData* fileAsData = [[NSData alloc] initWithContentsOfFile:pathToFile]; + if (!fileAsData) { + NSLog(@"decodedHTMLfile: file %@ cannot be read.",pathToFile); + return nil; + } + // we're gonna assume for now it's ascii and hope for the best. + // i'm doing this because I think we can always read it in as ascii, + // while it might fail if we assume default system encoding. i don't + // know this for sure. but we'll have to do 2 decodings. big whoop. + NSString *fileString = [[NSString alloc] initWithData:fileAsData encoding:NSASCIIStringEncoding]; + if (!fileString) { + NSLog(@"decodedHTMLfile: file %@ doesn't want to become a string. Exiting.",pathToFile); + [fileAsData release]; + return nil; + } + + // Create a dictionary with possible encodings. As I figure out more possible encodings, + // I'll add them to the dictionary. + NSString *utfdash8Key = @"content=\"text/html; charset=utf-8" ; + NSString *xmacromanKey = @"content=\"text/html; charset=x-mac-roman"; + NSString *xmacsystemKey = @"CONTENT=\"text/html; charset=X-MAC-SYSTEM"; + + NSDictionary *encodingDict = [NSDictionary dictionaryWithObjectsAndKeys: + [NSNumber numberWithUnsignedInt:NSUTF8StringEncoding],utfdash8Key, + [NSNumber numberWithUnsignedInt:NSMacOSRomanStringEncoding],xmacromanKey, + [NSNumber numberWithUnsignedInt:[NSString defaultCStringEncoding]],xmacsystemKey, + nil]; + + NSEnumerator *keyEnumerator = [encodingDict keyEnumerator]; + id key; + NSRange aRange; + while ((key = [keyEnumerator nextObject])) { + aRange = [fileString rangeOfString:key options:NSCaseInsensitiveSearch]; + if (aRange.location != NSNotFound) { + [fileString release]; + fileString = [[NSString alloc] initWithData:fileAsData encoding:[[encodingDict objectForKey:key] unsignedIntValue]]; + [fileAsData release]; + return [fileString autorelease]; + } + } + // if we're here, we don't have a clue as to the encoding. we'll guess default + [fileString release]; + if ((fileString = [[NSString alloc] initWithData:fileAsData encoding:[NSString defaultCStringEncoding]])) { + NSLog(@"decodedHTMLFile: file %@ encoding unknown. Assume default and proceed.",pathToFile); + [fileAsData release]; + return [fileString autorelease]; + } + // we suck. this is almost certainly wrong, but oh well. + NSLog(@"decodedHTMLFile: file %@ encoding unknown, and NOT default. Use ASCII and proceed.",pathToFile); + fileString = [[NSString alloc] initWithData:fileAsData encoding:NSASCIIStringEncoding]; + [fileAsData release]; + return [fileString autorelease]; +} + + +-(BOOL)readHTMLFile:(NSString *)pathToFile intoFolder:(BookmarkFolder *)aFolder; +{ + // get file as NSString + NSString* fileAsString = [self decodedHTMLfile:pathToFile]; + if (!fileAsString) { + NSLog(@"couldn't read file. bailing out"); + return NO; + } + // Set up to scan the bookmark file + NSScanner *fileScanner = [[NSScanner alloc] initWithString:fileAsString]; + BOOL isNetscape = YES; + // See if it's a netscape/IE style bookmark file, or omniweb + NSRange aRange = [fileAsString rangeOfString:@"" options:NSCaseInsensitiveSearch]; + if (aRange.location != NSNotFound) { + // netscape/IE setup - start after Title attribute + [fileScanner scanUpToString:@"" intoString:NULL]; + [fileScanner setScanLocation:([fileScanner scanLocation] + 7)]; + } else { + isNetscape = NO; + aRange = [fileAsString rangeOfString:@"" options:NSCaseInsensitiveSearch]; + if (aRange.location != NSNotFound) + // omniweb setup - start at + [fileScanner scanUpToString:@"" intoString:NULL]; + else { + NSLog(@"Unrecognized style of Bookmark File. Read fails."); + [fileScanner release]; + return NO; + } + } + BookmarkFolder *currentArray = aFolder; + BookmarkItem *currentItem; + NSScanner *tokenScanner; + NSString *tokenTag, *tokenString, *tempItem; + unsigned scanIndex; + BOOL justSetTitle = NO; + // Scan through file. As we find a token, do something useful with it. + while (![fileScanner isAtEnd]) { + [fileScanner scanUpToString:@"<" intoString:&tokenString]; + scanIndex = [fileScanner scanLocation]; + if ((scanIndex+3) < [fileAsString length]) { + tokenTag = [[NSString alloc] initWithString:[fileAsString substringWithRange:NSMakeRange(scanIndex,3)]]; + // now we pick out if it's something we want to save. + // check in a "most likely thing first" order + if (([tokenTag isEqualToString:@"

"]) || ([tokenTag isEqualToString:@"

"])) { + [fileScanner setScanLocation:([fileScanner scanLocation]+1)]; + } + else if (([tokenTag isEqualToString:@"" intoString:&tokenString]; + tokenScanner = [[NSScanner alloc] initWithString:tokenString]; + [tokenScanner scanUpToString:@"href=\"" intoString:NULL]; + [tokenScanner setScanLocation:([tokenScanner scanLocation]+6)]; + [tokenScanner scanUpToString:@"\"" intoString:&tempItem]; + currentItem = [currentArray addBookmark]; + [(Bookmark *)currentItem setUrl:[tempItem stringByRemovingAmpEscapes]]; + [tokenScanner scanUpToString:@">" intoString:NULL]; + [currentItem setTitle:[[tokenString substringFromIndex:([tokenScanner scanLocation]+1)] stringByRemovingAmpEscapes]]; + [tokenScanner release]; + justSetTitle = YES; + [fileScanner setScanLocation:([fileScanner scanLocation]+1)]; + } + else if (([tokenTag isEqualToString:@"" intoString:NULL]; + [fileScanner setScanLocation:([fileScanner scanLocation]+1)]; + [fileScanner scanUpToString:@"<" intoString:&tokenString]; + [currentItem setDescription:[tokenString stringByRemovingAmpEscapes]]; + justSetTitle = NO; + } + else if (([tokenTag isEqualToString:@"" intoString:&tokenString]; + currentItem = [currentArray addBookmarkFolder]; + currentArray = (BookmarkFolder *)currentItem; + tokenScanner = [[NSScanner alloc] initWithString:tokenString]; + if (isNetscape) { + [tokenScanner scanUpToString:@">" intoString:NULL]; + [currentItem setTitle:[[tokenString substringFromIndex:([tokenScanner scanLocation]+1)] stringByRemovingAmpEscapes]]; + } else { + [tokenScanner scanUpToString:@"" intoString:NULL]; + [tokenScanner setScanLocation:([tokenScanner scanLocation]+3)]; + [tokenScanner scanUpToString:@"" intoString:&tempItem]; + [currentItem setTitle:[tempItem stringByRemovingAmpEscapes]]; + } + [tokenScanner release]; + [fileScanner setScanLocation:([fileScanner scanLocation]+1)]; + } + else if (([tokenTag isEqualToString:@"" intoString:NULL]; + [fileScanner scanUpToString:@"" intoString:NULL]; + [fileScanner setScanLocation:([fileScanner scanLocation]+1)]; + } + else if (([tokenTag isEqualToString:@"" intoString:NULL]; + [fileScanner setScanLocation:([fileScanner scanLocation]+1)]; + } + else if (([tokenTag isEqualToString:@"" intoString:NULL]; + else { + [tempItem release]; + tempItem = [[NSString alloc] initWithString:[@"<" stringByAppendingString:[tokenString stringByRemovingAmpEscapes]]]; + if (justSetTitle) + [currentItem setTitle:[[currentItem title] stringByAppendingString:tempItem]]; + else + [currentItem setDescription:[[currentItem description] stringByAppendingString:tempItem]]; + [fileScanner setScanLocation:([fileScanner scanLocation]+1)]; + } + [tempItem release]; + } + else { //beats me. just close the tag out and continue. + [fileScanner scanUpToString:@">" intoString:NULL]; + } + [tokenTag release]; + } + } + [fileScanner release]; + return YES; +} + +-(BOOL)readCaminoXMLFile:(NSString *)pathToFile intoFolder:(BookmarkFolder *)aFolder +{ + NSURL* fileURL = [NSURL fileURLWithPath:pathToFile]; + if (!fileURL) { + NSLog(@"URL creation failed"); + return NO; + } + // Thanks, Apple, for example XML parsing code. + // Create CFXMLTree from file. This needs to be released later + CFXMLTreeRef XMLFileTree = CFXMLTreeCreateWithDataFromURL (kCFAllocatorDefault, + (CFURLRef)fileURL, + kCFXMLParserSkipWhitespace, + kCFXMLNodeCurrentVersion); + if (!XMLFileTree) { + NSLog(@"XMLTree creation failed"); + return NO; + } + // process top level nodes. I think we'll find DTD + // before data - so only need to make 1 pass. + int count, index; + CFXMLTreeRef subFileTree; + CFXMLNodeRef bookmarkNode; + CFXMLDocumentTypeInfo *docTypeInfo; + CFURLRef dtdURL; + BOOL aBool; + count = CFTreeGetChildCount(XMLFileTree); + for (index=0;index < count;index++) { + subFileTree = CFTreeGetChildAtIndex(XMLFileTree,index); + if (subFileTree) { + bookmarkNode = CFXMLTreeGetNode(subFileTree); + if (bookmarkNode) { + switch (CFXMLNodeGetTypeCode(bookmarkNode)) { + // make sure it's Camino/Chimera DTD + case (kCFXMLNodeTypeDocumentType): + docTypeInfo = (CFXMLDocumentTypeInfo *)CFXMLNodeGetInfoPtr(bookmarkNode); + dtdURL = docTypeInfo->externalID.systemID; + if (![[(NSURL *)dtdURL absoluteString] isEqualToString:@"http://www.mozilla.org/DTDs/ChimeraBookmarks.dtd"]) { + NSLog(@"not a ChimeraBookmarks xml file. Bail"); + CFRelease(XMLFileTree); + return NO; + } + break; + case (kCFXMLNodeTypeElement): + aBool = [aFolder readCaminoXML:subFileTree]; + CFRelease (XMLFileTree); + return aBool; + break; + default: + break; + } + } + } + } + CFRelease(XMLFileTree); + NSLog(@"run through the tree and didn't find anything interesting. Bailed out"); + return NO; +} + +-(BOOL)readPropertyListFile:(NSString *)pathToFile intoFolder:aFolder +{ + NSDictionary* dict = [NSDictionary dictionaryWithContentsOfFile:pathToFile]; + // see if it's safari + if (![dict objectForKey:@"WebBookmarkType"]) + return [aFolder readNativeDictionary:dict]; + else + return [aFolder readSafariDictionary:dict]; +} + +// +// Writing bookmark files +// + +- (void) writeHTMLFile:(NSString *)pathToFile +{ + NSString *htmlString = [[self rootBookmarks] writeHTML:0]; + if (![htmlString writeToFile:[pathToFile stringByStandardizingPath] atomically:YES]) + NSLog(@"writeHTML: Failed to write file %@",pathToFile); + return; +} + +-(void)writePropertyListFile:(NSString *)pathToFile +{ + NSDictionary* dict = [[self rootBookmarks] writeNativeDictionary]; + if (![dict writeToFile:[pathToFile stringByStandardizingPath] atomically:YES]) + NSLog(@"writePropertyList: Failed to write file %@",pathToFile); + return; +} + + +@end diff --git a/camino/src/bookmarks/BookmarkMenu.h b/camino/src/bookmarks/BookmarkMenu.h new file mode 100644 index 000000000000..8fa7ae2935c6 --- /dev/null +++ b/camino/src/bookmarks/BookmarkMenu.h @@ -0,0 +1,54 @@ +/* ***** BEGIN LICENSE BLOCK ***** +* Version: MPL 1.1/GPL 2.0/LGPL 2.1 +* +* The contents of this file are subject to the Mozilla Public License Version +* 1.1 (the "License"); you may not use this file except in compliance with +* the License. You may obtain a copy of the License at +* http://www.mozilla.org/MPL/ +* +* Software distributed under the License is distributed on an "AS IS" basis, +* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +* for the specific language governing rights and limitations under the +* License. +* +* The Original Code is Chimera code. +* +* The Initial Developer of the Original Code is +* Netscape Communications Corporation. +* Portions created by the Initial Developer are Copyright (C) 2002 +* the Initial Developer. All Rights Reserved. +* +* Contributor(s): +* Simon Fraser +* David Haas +* +* Alternatively, the contents of this file may be used under the terms of +* either the GNU General Public License Version 2 or later (the "GPL"), or +* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +* in which case the provisions of the GPL or the LGPL are applicable instead +* of those above. If you wish to allow use of your version of this file only +* under the terms of either the GPL or the LGPL, and not to allow others to +* use your version of this file under the terms of the MPL, indicate your +* decision by deleting the provisions above and replace them with the notice +* and other provisions required by the GPL or the LGPL. If you do not delete +* the provisions above, a recipient may use your version of this file under +* the terms of any one of the MPL, the GPL or the LGPL. +* +* ***** END LICENSE BLOCK ***** */ + +#import +#import "BookmarksClient.h" + +@class BookmarkFolder; + +@interface BookmarkMenu : NSObject +{ + NSMenu* mMenu; // retained + BookmarkFolder* mRootFolder; + int mFirstItemIndex; + BOOL mIsDockMenu; +} + +- (id)initWithMenu:(NSMenu *)aMenu firstItem:(int)anIndex rootBookmarkFolder:(BookmarkFolder *)aFolder; + +@end diff --git a/camino/src/bookmarks/BookmarkMenu.mm b/camino/src/bookmarks/BookmarkMenu.mm new file mode 100644 index 000000000000..d854477a7e57 --- /dev/null +++ b/camino/src/bookmarks/BookmarkMenu.mm @@ -0,0 +1,265 @@ +/* ***** BEGIN LICENSE BLOCK ***** +* Version: MPL 1.1/GPL 2.0/LGPL 2.1 +* +* The contents of this file are subject to the Mozilla Public License Version +* 1.1 (the "License"); you may not use this file except in compliance with +* the License. You may obtain a copy of the License at +* http://www.mozilla.org/MPL/ +* +* Software distributed under the License is distributed on an "AS IS" basis, +* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +* for the specific language governing rights and limitations under the +* License. +* +* The Original Code is Chimera code. +* +* The Initial Developer of the Original Code is +* Netscape Communications Corporation. +* Portions created by the Initial Developer are Copyright (C) 2002 +* the Initial Developer. All Rights Reserved. +* +* Contributor(s): +* Simon Fraser +* David Haas +* +* Alternatively, the contents of this file may be used under the terms of +* either the GNU General Public License Version 2 or later (the "GPL"), or +* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +* in which case the provisions of the GPL or the LGPL are applicable instead +* of those above. If you wish to allow use of your version of this file only +* under the terms of either the GPL or the LGPL, and not to allow others to +* use your version of this file under the terms of the MPL, indicate your +* decision by deleting the provisions above and replace them with the notice +* and other provisions required by the GPL or the LGPL. If you do not delete +* the provisions above, a recipient may use your version of this file under +* the terms of any one of the MPL, the GPL or the LGPL. +* +* ***** END LICENSE BLOCK ***** */ + +#import "BookmarkManager.h" +#import "BookmarkMenu.h" +#import "BookmarkFolder.h" +#import "Bookmark.h" +#import "NSString+Utils.h" + +// Definitions +#define MENU_TRUNCATION_CHARS 60 + +@interface BookmarkMenu(Private) +- (NSMenu *)menu; +- (BookmarkFolder *)rootBookmarkFolder; +- (int)firstItemIndex; +- (void)setFirstItemIndex:(int)anIndex; +- (void)setRootBookmarkFolder:(BookmarkFolder *)anArray; +- (NSMenu *)locateMenuForItem:(BookmarkItem *)anItem; +- (void)constructMenu:(NSMenu *)menu forBookmarkFolder:(BookmarkFolder *)aFolder; +- (void)flushMenu; +- (void)addItem:(BookmarkItem *)anItem toMenu:(NSMenu *)aMenu atIndex:(int)aIndex; +- (void)dockMenuChanged:(NSNotification *)note; +@end + +@implementation BookmarkMenu +// init & dealloc + +- (id)initWithMenu:(NSMenu *)aMenu firstItem:(int)anIndex rootBookmarkFolder:(BookmarkFolder *)aFolder +{ + if ((self = [super init])) + { + mMenu = [aMenu retain]; + mFirstItemIndex = anIndex; + [self setRootBookmarkFolder:aFolder]; + [self constructMenu:mMenu forBookmarkFolder:aFolder]; + // Generic notifications for Bookmark Client + NSNotificationCenter *nc = [NSNotificationCenter defaultCenter]; + [nc addObserver:self selector:@selector(bookmarkAdded:) name:BookmarkFolderAdditionNotification object:nil]; + [nc addObserver:self selector:@selector(bookmarkRemoved:) name:BookmarkFolderDeletionNotification object:nil]; + [nc addObserver:self selector:@selector(bookmarkChanged:) name:BookmarkItemChangedNotification object:nil]; + if (aFolder == [[BookmarkManager sharedBookmarkManager] dockMenuFolder]) + [nc addObserver:self selector:@selector(dockMenuChanged:) name:BookmarkFolderDockMenuChangeNotificaton object:nil]; + } + return self; +} + +- (void)dealloc +{ + [[NSNotificationCenter defaultCenter] removeObserver:self]; + [mMenu release]; + [mRootFolder release]; + [super dealloc]; +} + +// Getters & setters +-(NSMenu *)menu +{ + return mMenu; +} + +-(BookmarkFolder *)rootBookmarkFolder +{ + return mRootFolder; +} + +-(int)firstItemIndex +{ + return mFirstItemIndex; +} + +-(void)setRootBookmarkFolder:(BookmarkFolder *)aFolder +{ + [aFolder retain]; + [mRootFolder release]; + mRootFolder = aFolder; +} + +-(void)setFirstItemIndex:(int)anIndex +{ + mFirstItemIndex=anIndex; +} + +// +// Utility methods +// + +- (void)flushMenu +{ + int firstItemIndex = [self firstItemIndex]; + NSMenu *menu = [self menu]; + while ([menu numberOfItems] > firstItemIndex) + [menu removeItemAtIndex:firstItemIndex]; +} + +- (void)constructMenu:(NSMenu *)menu forBookmarkFolder:(BookmarkFolder *)aFolder +{ + unsigned i, childCount = [aFolder count]; + for (i = 0; i < childCount; i++) + [self addItem:[aFolder objectAtIndex:i] toMenu:menu atIndex:i]; +} + +- (void)addItem:(BookmarkItem *)anItem toMenu:(NSMenu *)aMenu atIndex:(int)aIndex +{ + NSMenuItem *menuItem; + NSString *title = [[anItem title] stringByTruncatingTo:MENU_TRUNCATION_CHARS at:kTruncateAtMiddle]; + unsigned realIndex = aIndex; + if (aMenu == [self menu]) + realIndex += [self firstItemIndex]; + + if ([anItem isKindOfClass:[Bookmark class]]) { + if (![(Bookmark *)anItem isSeparator]) { // normal bookmark + menuItem = [[NSMenuItem alloc] initWithTitle:title action: NULL keyEquivalent: @""]; + [menuItem setTarget:[NSApp delegate]]; + [menuItem setAction:@selector(openMenuBookmark:)]; + [menuItem setImage:[anItem icon]]; + } else {//separator + menuItem = [NSMenuItem separatorItem]; + } + [aMenu insertItem:menuItem atIndex:realIndex]; + } else if ([anItem isKindOfClass:[BookmarkFolder class]]){ + if (![(BookmarkFolder *)anItem isGroup]) { //normal folder + menuItem = [[NSMenuItem alloc] initWithTitle:title action: NULL keyEquivalent: @""]; + [aMenu insertItem:menuItem atIndex:realIndex]; + [menuItem setImage: [anItem icon]]; + NSMenu* subMenu = [[NSMenu alloc] initWithTitle:title]; + [aMenu setSubmenu: subMenu forItem: menuItem]; + [subMenu setAutoenablesItems: NO]; + [self constructMenu:subMenu forBookmarkFolder:(BookmarkFolder *)anItem]; + [subMenu release]; + } else { //group + menuItem = [[NSMenuItem alloc] initWithTitle:title action: NULL keyEquivalent: @""]; + [aMenu insertItem:menuItem atIndex:realIndex]; + [menuItem setTarget:[NSApp delegate]]; + [menuItem setAction:@selector(openMenuBookmark:)]; + [menuItem setImage:[anItem icon]]; + } + } + [menuItem setRepresentedObject:anItem]; + if (![menuItem isSeparatorItem]) + [menuItem release]; +} + +- (NSMenu *)locateMenuForItem:(BookmarkItem *)anItem +{ + if (![anItem isKindOfClass:[BookmarkItem class]]) + return nil; //make sure we haven't gone to top of menu item doesn't live in. + if (anItem == [self rootBookmarkFolder]) + return [self menu]; + NSMenu* parentMenu = [self locateMenuForItem:[anItem parent]]; + if (parentMenu) { + int index = [parentMenu indexOfItemWithRepresentedObject:anItem]; + if (index != -1) { + if ([anItem isKindOfClass:[BookmarkFolder class]]) { + NSMenuItem* childMenu = [parentMenu itemAtIndex:index]; + return [childMenu submenu]; + } else if ([anItem isKindOfClass:[Bookmark class]]) + return parentMenu; + } + } + return nil; +} + +#pragma mark - +// For the BookmarksClient Protocol + +- (void)bookmarkAdded:(NSNotification *)note +{ + BookmarkFolder *aFolder = [note object]; + NSMenu* menu = nil; + if (aFolder == [self rootBookmarkFolder]) + menu = [self menu]; + else if (![aFolder isGroup]) + menu = [self locateMenuForItem:aFolder]; + if (menu) { + NSDictionary *dict = [note userInfo]; + [self addItem:[dict objectForKey:BookmarkFolderChildKey] toMenu:menu atIndex:[[dict objectForKey:BookmarkFolderChildIndexKey] unsignedIntValue]]; + } +} +- (void)bookmarkRemoved:(NSNotification *)note +{ + BookmarkFolder *aFolder = [note object]; + NSMenu* menu = nil; + if (aFolder == [self rootBookmarkFolder]) + menu = [self menu]; + else if (![aFolder isGroup]) + menu = [self locateMenuForItem:aFolder]; + if (menu) { + BookmarkItem *anItem = [[note userInfo] objectForKey:BookmarkFolderChildKey]; + [menu removeItemAtIndex:[menu indexOfItemWithRepresentedObject:anItem]]; + } +} + +- (void)bookmarkChanged:(NSNotification *)note +{ + BookmarkItem* anItem = [note object]; + NSMenu *menu = nil; + BOOL isSeparator = NO; + if ([[self rootBookmarkFolder] isSmartFolder]) + menu = [self menu]; + else if ([anItem isKindOfClass:[Bookmark class]]) { + menu = [self locateMenuForItem:anItem]; + isSeparator = [(Bookmark *)anItem isSeparator]; + } + else if ([anItem isKindOfClass:[BookmarkFolder class]]) + menu = [self locateMenuForItem:[anItem parent]]; + if (menu) { + int index = [menu indexOfItemWithRepresentedObject:anItem]; + if (index != -1) { + if (!isSeparator) { + NSMenuItem *menuItem = [menu itemAtIndex:index]; + [menuItem setTitle:[[anItem title] stringByTruncatingTo:MENU_TRUNCATION_CHARS at:kTruncateAtMiddle]]; + [menuItem setImage:[anItem icon]]; + } else { + [menu removeItemAtIndex:index]; + [menu insertItem:[NSMenuItem separatorItem] atIndex:index]; + } + } + } +} + +- (void)dockMenuChanged:(NSNotification *)note +{ + BookmarkFolder *aFolder = [note object]; + [self flushMenu]; + [self setRootBookmarkFolder:aFolder]; + [self constructMenu:[self menu] forBookmarkFolder:aFolder]; +} + +@end diff --git a/camino/src/bookmarks/BookmarkOutlineView.h b/camino/src/bookmarks/BookmarkOutlineView.h new file mode 100644 index 000000000000..0a9672f2d640 --- /dev/null +++ b/camino/src/bookmarks/BookmarkOutlineView.h @@ -0,0 +1,47 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** +* Version: NPL 1.1/GPL 2.0/LGPL 2.1 +* +* The contents of this file are subject to the Netscape Public License +* Version 1.1 (the "License"); you may not use this file except in +* compliance with the License. You may obtain a copy of the License at +* http://www.mozilla.org/NPL/ +* +* Software distributed under the License is distributed on an "AS IS" basis, +* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +* for the specific language governing rights and limitations under the +* License. +* +* The Original Code is mozilla.org code. +* +* The Initial Developer of the Original Code is +* Netscape Communications Corporation. +* Portions created by the Initial Developer are Copyright (C) 2002 +* the Initial Developer. All Rights Reserved. +* +* Contributor(s): +* Joe Hewitt (Original Author) +* David Haas +* +* +* Alternatively, the contents of this file may be used under the terms of +* either the GNU General Public License Version 2 or later (the "GPL"), or +* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +* in which case the provisions of the GPL or the LGPL are applicable instead +* of those above. If you wish to allow use of your version of this file only +* under the terms of either the GPL or the LGPL, and not to allow others to +* use your version of this file under the terms of the NPL, indicate your +* decision by deleting the provisions above and replace them with the notice +* and other provisions required by the GPL or the LGPL. If you do not delete +* the provisions above, a recipient may use your version of this file under +* the terms of any one of the NPL, the GPL or the LGPL. +* +* ***** END LICENSE BLOCK ***** */ + +#import +#import "ExtendedOutlineView.h" + +@interface BookmarkOutlineView : ExtendedOutlineView +{ +} +@end diff --git a/camino/src/bookmarks/BookmarkOutlineView.mm b/camino/src/bookmarks/BookmarkOutlineView.mm new file mode 100644 index 000000000000..cbb2de5aec48 --- /dev/null +++ b/camino/src/bookmarks/BookmarkOutlineView.mm @@ -0,0 +1,98 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** +* Version: NPL 1.1/GPL 2.0/LGPL 2.1 +* +* The contents of this file are subject to the Netscape Public License +* Version 1.1 (the "License"); you may not use this file except in +* compliance with the License. You may obtain a copy of the License at +* http://www.mozilla.org/NPL/ +* +* Software distributed under the License is distributed on an "AS IS" basis, +* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +* for the specific language governing rights and limitations under the +* License. +* +* The Original Code is mozilla.org code. +* +* The Initial Developer of the Original Code is +* Netscape Communications Corporation. +* Portions created by the Initial Developer are Copyright (C) 2002 +* the Initial Developer. All Rights Reserved. +* +* Contributor(s): +* Joe Hewitt (Original Author) +* David Haas +* +* +* Alternatively, the contents of this file may be used under the terms of +* either the GNU General Public License Version 2 or later (the "GPL"), or +* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +* in which case the provisions of the GPL or the LGPL are applicable instead +* of those above. If you wish to allow use of your version of this file only +* under the terms of either the GPL or the LGPL, and not to allow others to +* use your version of this file under the terms of the NPL, indicate your +* decision by deleting the provisions above and replace them with the notice +* and other provisions required by the GPL or the LGPL. If you do not delete +* the provisions above, a recipient may use your version of this file under +* the terms of any one of the NPL, the GPL or the LGPL. +* +* ***** END LICENSE BLOCK ***** */ + +#import "BookmarkOutlineView.h" +#import "BookmarkFolder.h" +#import "Bookmark.h" +#import "NSArray+Utils.h" + + +@implementation BookmarkOutlineView + +- (void)awakeFromNib +{ + [self registerForDraggedTypes:[NSArray arrayWithObjects:@"MozURLType", @"MozBookmarkType", NSStringPboardType, NSURLPboardType, nil]]; +} + +- (void)draggedImage:(NSImage *)anImage endedAt:(NSPoint)aPoint operation:(NSDragOperation)operation +{ + if (operation == NSDragOperationDelete) + { + NSPasteboard* pboard = [NSPasteboard pasteboardWithName:NSDragPboard]; + NSArray* bookmarks = [NSArray pointerArrayFromDataArrayForMozBookmarkDrop:[pboard propertyListForType: @"MozBookmarkType"]]; + if (bookmarks) + { + for (unsigned int i = 0; i < [bookmarks count]; ++i) + { + BookmarkItem* item = [bookmarks objectAtIndex:i]; + [[item parent] deleteChild:item]; + } + } + } +} + +// don't edit URL field of folders or menu separators +- (void)_editItem:(id)dummy +{ + id itemToEdit = [self itemAtRow:mRowToBeEdited]; + if ([itemToEdit isKindOfClass:[BookmarkFolder class]]) { + if ((mColumnToBeEdited == [self columnWithIdentifier:@"url"]) || + ((![itemToEdit isGroup]) && (mColumnToBeEdited == [self columnWithIdentifier:@"keyword"]))) { + [super _cancelEditItem]; + return; + } + } else if ([itemToEdit isKindOfClass:[Bookmark class]]) { + if ([(Bookmark *)itemToEdit isSeparator]) { + [super _cancelEditItem]; + return; + } + } + [super _editItem:dummy]; +} + +- (unsigned int)draggingSourceOperationMaskForLocal:(BOOL)localFlag +{ + if (localFlag) + return (NSDragOperationCopy | NSDragOperationGeneric | NSDragOperationMove); + + return (NSDragOperationDelete | NSDragOperationGeneric); +} + +@end diff --git a/camino/src/bookmarks/BookmarkToolbar.h b/camino/src/bookmarks/BookmarkToolbar.h new file mode 100644 index 000000000000..d4bcf899a691 --- /dev/null +++ b/camino/src/bookmarks/BookmarkToolbar.h @@ -0,0 +1,73 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** +* Version: NPL 1.1/GPL 2.0/LGPL 2.1 +* +* The contents of this file are subject to the Netscape Public License +* Version 1.1 (the "License"); you may not use this file except in +* compliance with the License. You may obtain a copy of the License at +* http://www.mozilla.org/NPL/ +* +* Software distributed under the License is distributed on an "AS IS" basis, +* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +* for the specific language governing rights and limitations under the +* License. +* +* The Original Code is mozilla.org code. +* +* The Initial Developer of the Original Code is +* Netscape Communications Corporation. +* Portions created by the Initial Developer are Copyright (C) 2002 +* the Initial Developer. All Rights Reserved. +* +* Contributor(s): +* David Hyatt (Original Author) +* David Haas +* +* +* Alternatively, the contents of this file may be used under the terms of +* either the GNU General Public License Version 2 or later (the "GPL"), or +* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +* in which case the provisions of the GPL or the LGPL are applicable instead +* of those above. If you wish to allow use of your version of this file only +* under the terms of either the GPL or the LGPL, and not to allow others to +* use your version of this file under the terms of the NPL, indicate your +* decision by deleting the provisions above and replace them with the notice +* and other provisions required by the GPL or the LGPL. If you do not delete +* the provisions above, a recipient may use your version of this file under +* the terms of any one of the NPL, the GPL or the LGPL. +* +* ***** END LICENSE BLOCK ***** */ + +#import + +#import "BookmarksClient.h" + +@class BookmarkButton; +@class BookmarkItem; + +@interface BookmarkToolbar : NSView +{ + NSMutableArray* mButtons; + BookmarkButton* mDragInsertionButton; + int mDragInsertionPosition; + BOOL mIsShowing; + BOOL mDrawBorder; +} + + // Called to construct & edit the initial set of personal toolbar buttons. +-(void)buildButtonList; +-(void)addButton:(BookmarkItem*)aItem atIndex:(int)aIndex; +-(void)editButton:(BookmarkItem*)aItem; +-(void)removeButton:(BookmarkItem*)aItem; + + // Called to lay out the buttons on the toolbar. +-(void)reflowButtons; +-(void)reflowButtonsStartingAtIndex: (int)aIndex; + +-(BOOL)isShown; +-(void)setDrawBottomBorder:(BOOL)drawBorder; +-(void)showBookmarksToolbar: (BOOL)aShow; + +-(IBAction)addFolder:(id)aSender; + +@end diff --git a/camino/src/bookmarks/BookmarkToolbar.mm b/camino/src/bookmarks/BookmarkToolbar.mm new file mode 100644 index 000000000000..ac57e0d627d4 --- /dev/null +++ b/camino/src/bookmarks/BookmarkToolbar.mm @@ -0,0 +1,592 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** +* Version: NPL 1.1/GPL 2.0/LGPL 2.1 +* +* The contents of this file are subject to the Netscape Public License +* Version 1.1 (the "License"); you may not use this file except in +* compliance with the License. You may obtain a copy of the License at +* http://www.mozilla.org/NPL/ +* +* Software distributed under the License is distributed on an "AS IS" basis, +* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +* for the specific language governing rights and limitations under the +* License. +* +* The Original Code is mozilla.org code. +* +* The Initial Developer of the Original Code is +* Netscape Communications Corporation. +* Portions created by the Initial Developer are Copyright (C) 2002 +* the Initial Developer. All Rights Reserved. +* +* Contributor(s): +* David Hyatt (Original Author) +* Kathy Brade +* David Haas +* +* +* Alternatively, the contents of this file may be used under the terms of +* either the GNU General Public License Version 2 or later (the "GPL"), or +* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +* in which case the provisions of the GPL or the LGPL are applicable instead +* of those above. If you wish to allow use of your version of this file only +* under the terms of either the GPL or the LGPL, and not to allow others to +* use your version of this file under the terms of the NPL, indicate your +* decision by deleting the provisions above and replace them with the notice +* and other provisions required by the GPL or the LGPL. If you do not delete +* the provisions above, a recipient may use your version of this file under +* the terms of any one of the NPL, the GPL or the LGPL. +* +* ***** END LICENSE BLOCK ***** */ + +#import "BookmarkToolbar.h" + +#import "CHBrowserService.h" +#import "BookmarkButton.h" +#import "BookmarkManager.h" +#import "BrowserWindowController.h" +#import "Bookmark.h" +#import "BookmarkFolder.h" +#import "NSArray+Utils.h" + + +#define CHInsertNone 0 +#define CHInsertInto 1 +#define CHInsertBefore 2 +#define CHInsertAfter 3 + +@interface BookmarkToolbar(Private) + +- (void)setButtonInsertionPoint:(id )sender; +- (NSRect)insertionRectForButton:(NSView*)aButton position:(int)aPosition; +- (BookmarkButton*)makeNewButtonWithItem:(BookmarkItem*)aItem; + +@end + +@implementation BookmarkToolbar + +- (id)initWithFrame:(NSRect)frame +{ + if ( (self = [super initWithFrame:frame]) ) + { + mButtons = [[NSMutableArray alloc] init]; + mDragInsertionButton = nil; + mDragInsertionPosition = CHInsertNone; + mDrawBorder = YES; + [self registerForDraggedTypes:[NSArray arrayWithObjects:@"MozURLType", @"MozBookmarkType", NSStringPboardType, NSURLPboardType, nil]]; + mIsShowing = YES; + // Generic notifications for Bookmark Client + NSNotificationCenter *nc = [NSNotificationCenter defaultCenter]; + [nc addObserver:self selector:@selector(bookmarkAdded:) name:BookmarkFolderAdditionNotification object:nil]; + [nc addObserver:self selector:@selector(bookmarkRemoved:) name:BookmarkFolderDeletionNotification object:nil]; + [nc addObserver:self selector:@selector(bookmarkChanged:) name:BookmarkItemChangedNotification object:nil]; + } + return self; +} + +- (void)dealloc +{ + [[NSNotificationCenter defaultCenter] removeObserver:self]; + [mButtons release]; + [super dealloc]; +} + +- (void)drawRect:(NSRect)aRect +{ + if (mDrawBorder) + { + [[NSColor controlShadowColor] set]; + float height = [self bounds].size.height; + NSRectFill(NSMakeRect(aRect.origin.x, height - 1.0, aRect.size.width, height)); + } + + // The buttons will paint themselves. Just call our base class method. + [super drawRect: aRect]; + + // draw a separator at drag n drop insertion point if there is one + if (mDragInsertionPosition) + { + [[[NSColor controlShadowColor] colorWithAlphaComponent:0.6] set]; + NSRectFill([self insertionRectForButton:mDragInsertionButton position:mDragInsertionPosition]); + } +} + +-(void)buildButtonList +{ + BookmarkFolder* toolbar = [[BookmarkManager sharedBookmarkManager] toolbarFolder]; + + for (unsigned int i = 0; i < [toolbar count]; i ++) + { + BookmarkButton* button = [self makeNewButtonWithItem:[toolbar objectAtIndex:i]]; + [self addSubview: button]; + [mButtons addObject: button]; + } + if ([self isShown]) + [self reflowButtons]; +} + +- (void)resetButtonList +{ + int count = [mButtons count]; + for (int i = 0; i < count; i++) + { + BookmarkButton* button = [mButtons objectAtIndex: i]; + [button removeFromSuperview]; + } + [mButtons removeAllObjects]; + [self setNeedsDisplay:YES]; +} + +-(void)addButton:(BookmarkItem*)aItem atIndex:(int)aIndex +{ + BookmarkButton* button = [self makeNewButtonWithItem:aItem]; + [self addSubview: button]; + [mButtons insertObject: button atIndex: aIndex]; + if ([self isShown]) + [self reflowButtonsStartingAtIndex: aIndex]; +} + +-(void)editButton:(BookmarkItem*)aItem +{ + int count = [mButtons count]; + for (int i = 0; i < count; i++) + { + BookmarkButton* button = [mButtons objectAtIndex: i]; + if ([button BookmarkItem] == aItem) + { + [button setBookmarkItem: aItem]; + if (count > i && [self isShown]) + [self reflowButtonsStartingAtIndex: i]; + break; + } + } + [self setNeedsDisplay:YES]; +} + +-(void)removeButton:(BookmarkItem*)aItem +{ + int count = [mButtons count]; + for (int i = 0; i < count; i++) + { + BookmarkButton* button = [mButtons objectAtIndex: i]; + if ([button BookmarkItem] == aItem) + { + [mButtons removeObjectAtIndex: i]; + [button removeFromSuperview]; + if (count > i && [self isShown]) + [self reflowButtonsStartingAtIndex: i]; + break; + } + } + [self setNeedsDisplay:YES]; +} + +-(void)reflowButtons +{ + [self reflowButtonsStartingAtIndex: 0]; +} + +#define kBookmarkButtonHeight 16.0 +#define kMinBookmarkButtonWidth 16.0 +#define kMaxBookmarkButtonWidth 150.0 +#define kBookmarkButtonHorizPadding 2.0 +#define kBookmarkButtonVerticalPadding 1.0 +#define kBookmarkToolbarBottomPadding 1.0 + +-(void)reflowButtonsStartingAtIndex: (int)aIndex +{ + if (![self isShown]) + return; + + // coordinates for this view are flipped, making it easier to lay out from top left + // to bottom right. + float oldHeight = [self frame].size.height; + int count = [mButtons count]; + float curRowYOrigin = 0.0; + float curX = kBookmarkButtonHorizPadding; + + for (int i = 0; i < count; i ++) + { + BookmarkButton* button = [mButtons objectAtIndex: i]; + NSRect buttonRect; + + if (i < aIndex) + { + buttonRect = [button frame]; + curRowYOrigin = NSMinY(buttonRect) - kBookmarkButtonVerticalPadding; + curX = NSMaxX(buttonRect) + kBookmarkButtonHorizPadding; + } + else + { + [button sizeToFit]; + float width = [button frame].size.width; + + if (width > kMaxBookmarkButtonWidth) + width = kMaxBookmarkButtonWidth; + + buttonRect = NSMakeRect(curX, curRowYOrigin + kBookmarkButtonVerticalPadding, width, kBookmarkButtonHeight); + curX += NSWidth(buttonRect) + kBookmarkButtonHorizPadding; + + if (NSMaxX(buttonRect) > NSWidth([self bounds])) + { + curRowYOrigin += (kBookmarkButtonHeight + 2 * kBookmarkButtonVerticalPadding); + buttonRect = NSMakeRect(kBookmarkButtonHorizPadding, curRowYOrigin + kBookmarkButtonVerticalPadding, width, kBookmarkButtonHeight); + curX = NSWidth(buttonRect); + } + + [button setFrame: buttonRect]; + } + } + + float computedHeight = curRowYOrigin + (kBookmarkButtonHeight + 2 * kBookmarkButtonVerticalPadding + kBookmarkToolbarBottomPadding); + + // our size has changed, readjust our view's frame and the content area + if (computedHeight != oldHeight) + { + [super setFrame: NSMakeRect([self frame].origin.x, [self frame].origin.y + (oldHeight - computedHeight), + [self frame].size.width, computedHeight)]; + [self setNeedsDisplay:YES]; + + // tell the superview to resize its subviews + [[self superview] resizeSubviewsWithOldSize:[[self superview] frame].size]; + } +} + +-(BOOL)isFlipped +{ + return YES; // Use flipped coords, so we can layout out from top row to bottom row. +} + +-(void)setFrame:(NSRect)aRect +{ + NSRect oldFrame = [self frame]; + [super setFrame:aRect]; + + if (oldFrame.size.width == aRect.size.width || aRect.size.height == 0) + return; + + int count = [mButtons count]; + int reflowStart = 0; + + // find out where we need to start reflowing + for (int i = 0; i < count; i ++) + { + BookmarkButton* button = [mButtons objectAtIndex:i]; + NSRect buttonFrame = [button frame]; + + if ((NSMaxX(buttonFrame) > NSMaxX(aRect)) || // we're overhanging the right + (NSMaxY(buttonFrame) > kBookmarkButtonHeight)) // we're on the second row + { + reflowStart = i; + break; + } + } + + [self reflowButtonsStartingAtIndex:reflowStart]; +} + +-(BOOL)isShown +{ + return mIsShowing; +} + +-(void)setDrawBottomBorder:(BOOL)drawBorder +{ + if (mDrawBorder != drawBorder) + { + mDrawBorder = drawBorder; + NSRect dirtyRect = [self bounds]; + dirtyRect.origin.y = dirtyRect.size.height - 1.0; + dirtyRect.size.height = 1.0; + [self setNeedsDisplayInRect:dirtyRect]; + } +} +// +// if the toolbar gets the message, we can only make a new folder. +// kinda dull. but we'll do this on the fly. +// +-(NSMenu*)menuForEvent:(NSEvent*)aEvent +{ + NSMenu* myMenu = [[NSMenu alloc] initWithTitle:@"snookums"]; + NSMenuItem *menuItem = [[NSMenuItem alloc] initWithTitle:NSLocalizedString(@"Create New Folder...",@"Create New Folder...") action:@selector(addFolder:) keyEquivalent:[NSString string]]; + [menuItem setTarget:self]; + [myMenu addItem:menuItem]; + [menuItem release]; + return [myMenu autorelease]; +} + +// +// context menu has only what we need +// +-(BOOL)validateMenuItem:(NSMenuItem*)aMenuItem +{ + return YES; +} + +-(IBAction)addFolder:(id)aSender +{ + BookmarkFolder* toolbar = [[BookmarkManager sharedBookmarkManager] toolbarFolder]; + BookmarkFolder* aFolder = [toolbar addBookmarkFolder]; + [aFolder setTitle:NSLocalizedString(@"NewBookmarkFolder",@"New Folder")]; +} + +-(void)showBookmarksToolbar: (BOOL)aShow +{ + mIsShowing = aShow; + + if (!aShow) + { + [[self superview] setNeedsDisplayInRect:[self frame]]; + NSRect newFrame = [self frame]; + newFrame.origin.y += newFrame.size.height; + newFrame.size.height = 0; + [self setFrame: newFrame]; + + // tell the superview to resize its subviews + [[self superview] resizeSubviewsWithOldSize:[[self superview] frame].size]; + } + else + { + [self reflowButtons]; + [self setNeedsDisplay:YES]; + } + +} + +- (void)setButtonInsertionPoint:(id )sender +{ + NSPoint dragLocation = [sender draggingLocation]; + NSPoint superviewLoc = [[self superview] convertPoint:dragLocation fromView:nil]; // convert from window + NSButton* sourceButton = [sender draggingSource]; + + mDragInsertionButton = nil; + mDragInsertionPosition = CHInsertAfter; + + NSView* foundView = [self hitTest:superviewLoc]; + if (foundView && [foundView isMemberOfClass:[BookmarkButton class]]) + { + BookmarkButton* targetButton = foundView; + BookmarkItem* targetItem = [targetButton BookmarkItem]; + + if ([targetItem isKindOfClass:[BookmarkFolder class]]) + { + mDragInsertionButton = targetButton; + mDragInsertionPosition = CHInsertInto; + } + else if (targetButton != sourceButton) + { + NSPoint localLocation = [[self superview] convertPoint:superviewLoc toView:foundView]; + mDragInsertionButton = targetButton; + if (localLocation.x < NSWidth([targetButton bounds]) / 2.0) + mDragInsertionPosition = CHInsertBefore; + else + mDragInsertionPosition = CHInsertAfter; + } + } + else + { + // throw it in at the end + mDragInsertionButton = ([mButtons count] > 0) ? [mButtons objectAtIndex:[mButtons count] - 1] : 0; + mDragInsertionPosition = CHInsertAfter; + } +} + +- (BOOL)dropDestinationValid:(id )sender +{ + NSPasteboard* draggingPasteboard = [sender draggingPasteboard]; + NSArray* types = [draggingPasteboard types]; + BookmarkManager *bmManager = [BookmarkManager sharedBookmarkManager]; + BookmarkFolder* toolbar = [bmManager toolbarFolder]; + + if (!toolbar) + return NO; + + if ([types containsObject: @"MozBookmarkType"]) + { + NSArray *draggedItems = [NSArray pointerArrayFromDataArrayForMozBookmarkDrop:[draggingPasteboard propertyListForType: @"MozBookmarkType"]]; + BookmarkItem* destItem = nil; + int index = 0; + + if (mDragInsertionPosition == CHInsertInto) + // drop onto folder + { + destItem = [mDragInsertionButton BookmarkItem]; + index = 0; + } + else if (mDragInsertionPosition == CHInsertBefore || + mDragInsertionPosition == CHInsertAfter) // drop onto toolbar + { + index = [mButtons indexOfObjectIdenticalTo:mDragInsertionButton]; + if (mDragInsertionPosition == CHInsertAfter) + ++index; + } + if (![bmManager isDropValid:draggedItems toFolder:toolbar]) + return NO; + } + return YES; +} + +// NSDraggingDestination /////////// + +- (unsigned int)draggingEntered:(id )sender +{ + // we have to set the drag target before we can test for drop validation + [self setButtonInsertionPoint:sender]; + + if (![self dropDestinationValid:sender]) { + mDragInsertionButton = nil; + mDragInsertionPosition = CHInsertNone; + return NSDragOperationNone; + } + + return NSDragOperationGeneric; +} + +- (void)draggingExited:(id )sender +{ + if (mDragInsertionPosition) + [self setNeedsDisplayInRect:[self insertionRectForButton:mDragInsertionButton position:mDragInsertionPosition]]; + + mDragInsertionButton = nil; + mDragInsertionPosition = CHInsertNone; +} + +- (unsigned int)draggingUpdated:(id )sender +{ + if (mDragInsertionPosition) + [self setNeedsDisplayInRect:[self insertionRectForButton:mDragInsertionButton position:mDragInsertionPosition]]; + + // we have to set the drag target before we can test for drop validation + [self setButtonInsertionPoint:sender]; + + if (![self dropDestinationValid:sender]) { + mDragInsertionButton = nil; + mDragInsertionPosition = CHInsertNone; + return NSDragOperationNone; + } + + if (mDragInsertionPosition) + [self setNeedsDisplayInRect:[self insertionRectForButton:mDragInsertionButton position:mDragInsertionPosition]]; + + return NSDragOperationGeneric; +} + +- (BOOL)prepareForDragOperation:(id )sender +{ + return YES; +} + +- (BOOL)performDragOperation:(id )sender +{ + unsigned index = 0; + BookmarkFolder* toolbar = [[BookmarkManager sharedBookmarkManager] toolbarFolder]; + if (!toolbar) + return NO; + + if (mDragInsertionPosition == CHInsertInto) // drop onto folder + { + toolbar = (BookmarkFolder *)[mDragInsertionButton BookmarkItem]; + index = 0; + } + else if (mDragInsertionPosition == CHInsertBefore || + mDragInsertionPosition == CHInsertAfter) // drop onto toolbar + { + index = [mButtons indexOfObjectIdenticalTo: mDragInsertionButton]; + if (index == NSNotFound) + index = [toolbar count]; + else if (mDragInsertionPosition == CHInsertAfter) + index++; + } + else + { + mDragInsertionButton = nil; + mDragInsertionPosition = CHInsertNone; + [self setNeedsDisplay:YES]; + return NO; + } + + BOOL dropHandled = NO; + BOOL isCopy = ([sender draggingSourceOperationMask] == NSDragOperationCopy); + + NSArray *draggedTypes = [[sender draggingPasteboard] types]; + + if ( [draggedTypes containsObject:@"MozBookmarkType"] ) + { + NSArray *draggedItems = [NSArray pointerArrayFromDataArrayForMozBookmarkDrop:[[sender draggingPasteboard] propertyListForType: @"MozBookmarkType"]]; + NSEnumerator *enumerator = [draggedItems objectEnumerator]; + id aKid; + while ((aKid = [enumerator nextObject])) { + if (isCopy) { + [[aKid parent] copyChild:aKid toBookmarkFolder:toolbar atIndex:index]; + } else { + [[aKid parent] moveChild:aKid toBookmarkFolder:toolbar atIndex:index]; + } + } + dropHandled = YES; + } + else if ( [draggedTypes containsObject:@"MozURLType"] ) + { + NSDictionary* proxy = [[sender draggingPasteboard] propertyListForType: @"MozURLType"]; + [toolbar addBookmark:[proxy objectForKey:@"title"] url:[proxy objectForKey:@"url"] inPosition:index isSeparator:NO]; + dropHandled = YES; + } + else if ( [draggedTypes containsObject:NSStringPboardType] ) + { + NSString* draggedText = [[sender draggingPasteboard] stringForType:NSStringPboardType]; + [toolbar addBookmark:draggedText url:draggedText inPosition:index isSeparator:NO]; + dropHandled = YES; + } + else if ([draggedTypes containsObject: NSURLPboardType]) + { + NSURL* urlData = [NSURL URLFromPasteboard:[sender draggingPasteboard]]; + [toolbar addBookmark:[urlData absoluteString] url:[urlData absoluteString] inPosition:index isSeparator:NO]; + dropHandled = YES; + } + mDragInsertionButton = nil; + mDragInsertionPosition = CHInsertNone; + [self setNeedsDisplay:YES]; + return dropHandled; +} + +- (NSRect)insertionRectForButton:(NSView*)aButton position:(int) aPosition +{ + if (aPosition == CHInsertInto) { + return NSMakeRect([aButton frame].origin.x, [aButton frame].origin.y, [aButton frame].size.width, [aButton frame].size.height); + } else if (aPosition == CHInsertAfter) { + return NSMakeRect([aButton frame].origin.x+[aButton frame].size.width, [aButton frame].origin.y, 2, [aButton frame].size.height); + } else {// if (aPosition == BookmarksService::CHInsertBefore) { + return NSMakeRect([aButton frame].origin.x - 2, [aButton frame].origin.y, 2, [aButton frame].size.height); + } + } + +- (BookmarkButton*)makeNewButtonWithItem:(BookmarkItem*)aItem +{ + return [[[BookmarkButton alloc] initWithFrame: NSMakeRect(2, 1, 100, 17) item:aItem] autorelease]; +} + +#pragma mark - + +- (void)bookmarkAdded:(NSNotification *)aNote +{ + BookmarkFolder *anArray = [aNote object]; + if (![anArray isEqual:[[BookmarkManager sharedBookmarkManager] toolbarFolder]]) + return; + NSDictionary *dict = [aNote userInfo]; + [self addButton:[dict objectForKey:BookmarkFolderChildKey] atIndex:[[dict objectForKey:BookmarkFolderChildIndexKey] unsignedIntValue]]; +} + +- (void)bookmarkRemoved:(NSNotification *)aNote +{ + BookmarkFolder *aFolder = [aNote object]; + if (![aFolder isEqual:[[BookmarkManager sharedBookmarkManager] toolbarFolder]]) + return; + NSDictionary *dict = [aNote userInfo]; + [self removeButton:[dict objectForKey:BookmarkFolderChildKey]]; +} + +- (void)bookmarkChanged:(NSNotification *)aNote +{ + [self editButton:[aNote object]]; +} + +@end diff --git a/camino/src/bookmarks/BookmarkViewController.h b/camino/src/bookmarks/BookmarkViewController.h new file mode 100644 index 000000000000..0d90c8888a9b --- /dev/null +++ b/camino/src/bookmarks/BookmarkViewController.h @@ -0,0 +1,116 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** +* Version: NPL 1.1/GPL 2.0/LGPL 2.1 +* +* The contents of this file are subject to the Netscape Public License +* Version 1.1 (the "License"); you may not use this file except in +* compliance with the License. You may obtain a copy of the License at +* http://www.mozilla.org/NPL/ +* +* Software distributed under the License is distributed on an "AS IS" basis, +* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +* for the specific language governing rights and limitations under the +* License. +* +* The Original Code is mozilla.org code. +* +* The Initial Developer of the Original Code is +* Netscape Communications Corporation. +* Portions created by the Initial Developer are Copyright (C) 2002 +* the Initial Developer. All Rights Reserved. +* +* Contributor(s): +* +* Alternatively, the contents of this file may be used under the terms of +* either the GNU General Public License Version 2 or later (the "GPL"), or +* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +* in which case the provisions of the GPL or the LGPL are applicable instead +* of those above. If you wish to allow use of your version of this file only +* under the terms of either the GPL or the LGPL, and not to allow others to +* use your version of this file under the terms of the NPL, indicate your +* decision by deleting the provisions above and replace them with the notice +* and other provisions required by the GPL or the LGPL. If you do not delete +* the provisions above, a recipient may use your version of this file under +* the terms of any one of the NPL, the GPL or the LGPL. +* +* Combines old BookmarkController and BookmarkDataSource classes. When +* history gets brought in to new Bookmark system, HistoryDataSource will +* go away, too. +* +* +* ***** END LICENSE BLOCK ***** */ + +#import +#import "BookmarksClient.h" + +@class HistoryDataSource; +@class ExtendedTableView; +@class BrowserWindowController; +@class BookmarkOutlineView; +@class BookmarkFolder; + + +@interface BookmarkViewController : NSObject { + IBOutlet NSButton* mAddCollectionButton; + IBOutlet NSButton* mAddBookmarkButton; + IBOutlet NSButton* mAddFolderButton; + IBOutlet NSButton* mAddSeparatorButton; + IBOutlet NSButton* mInfoButton; + IBOutlet NSButton* mSearchButton; + IBOutlet NSTextField *mSearchField; + + IBOutlet NSSplitView* mContainersSplit; // vertical split + IBOutlet NSSplitView* mItemSearchSplit; // horizontal split + + IBOutlet BrowserWindowController* mBrowserWindowController; + IBOutlet ExtendedTableView* mContainerPane; + IBOutlet BookmarkOutlineView* mItemPane; + IBOutlet NSTableView* mSearchPane; // shows search results, can be hidden + + IBOutlet HistoryDataSource* mHistorySource; //can swap to this for history data + + NSMutableDictionary *mExpandedStatus; + NSString* mCachedHref; + BookmarkFolder *mActiveRootCollection; + BookmarkFolder *mRootBookmarks; + NSArray *mSearchResultArray; + int mOpenActionFlag; +} + +// +// IBActions +// +-(IBAction) setAsDockMenuFolder:(id)aSender; +-(IBAction) addCollection:(id)aSender; +-(IBAction) addBookmark:(id)aSender; +-(IBAction) addFolder:(id)aSender; +-(IBAction) addSeparator:(id)aSender; +-(IBAction) openBookmark: (id)aSender; +-(IBAction) openBookmarkInNewTab:(id)aSender; +-(IBAction) openBookmarkInNewWindow:(id)aSender; +-(IBAction) deleteBookmarks:(id)aSender; +-(IBAction) showBookmarkInfo:(id)aSender; +-(IBAction) startSearch:(id)aSender; +-(IBAction) locateBookmark:(id)aSender; + + +-(void) selectContainer:(int)inRowIndex; +-(void) selectLastContainer; +-(NSMutableDictionary *)expandedStatusDictionary; +-(void) restoreFolderExpandedStates; +-(BOOL) isExpanded:(id)anItem; +-(BOOL) haveSelectedRow; +-(void) setItem:(BookmarkFolder *)anItem isExpanded:(BOOL)aBool; +-(void) setActiveCollection:(BookmarkFolder *)aFolder; +-(BookmarkFolder *)activeCollection; + +-(void)addItem:(id)aSender useSelection:(BOOL)aSel isFolder:(BOOL)aIsFolder URL:(NSString*)aURL title:(NSString*)aTitle; +-(void)addItem:(id)aSender withParent:(BookmarkFolder *)parent isFolder:(BOOL)aIsFolder URL:(NSString*)aURL title:(NSString*)aTitle; +-(void)endAddBookmark: (int)aCode; + +-(void)deleteCollection:(id)aSender; +-(void) focus; +-(void) windowDidLoad; +-(void) ensureBookmarks; + +@end diff --git a/camino/src/bookmarks/BookmarkViewController.mm b/camino/src/bookmarks/BookmarkViewController.mm new file mode 100644 index 000000000000..fed840586012 --- /dev/null +++ b/camino/src/bookmarks/BookmarkViewController.mm @@ -0,0 +1,1275 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** +* Version: NPL 1.1/GPL 2.0/LGPL 2.1 +* +* The contents of this file are subject to the Netscape Public License +* Version 1.1 (the "License"); you may not use this file except in +* compliance with the License. You may obtain a copy of the License at +* http://www.mozilla.org/NPL/ +* +* Software distributed under the License is distributed on an "AS IS" basis, +* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +* for the specific language governing rights and limitations under the +* License. +* +* The Original Code is mozilla.org code. +* +* The Initial Developer of the Original Code is +* Netscape Communications Corporation. +* Portions created by the Initial Developer are Copyright (C) 2002 +* the Initial Developer. All Rights Reserved. +* +* Contributor(s): +* Simon Fraser +* Max Horn +* David Haas +* +* +* Alternatively, the contents of this file may be used under the terms of +* either the GNU General Public License Version 2 or later (the "GPL"), or +* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +* in which case the provisions of the GPL or the LGPL are applicable instead +* of those above. If you wish to allow use of your version of this file only +* under the terms of either the GPL or the LGPL, and not to allow others to +* use your version of this file under the terms of the NPL, indicate your +* decision by deleting the provisions above and replace them with the notice +* and other provisions required by the GPL or the LGPL. If you do not delete +* the provisions above, a recipient may use your version of this file under +* the terms of any one of the NPL, the GPL or the LGPL. +* +* ***** END LICENSE BLOCK ***** */ + +#import "BookmarkViewController.h" +#import "NSString+Utils.h" +#import "NSArray+Utils.h" +#import "NSPasteboard+Utils.h" +#import "BookmarkManager.h" +#import "BookmarkInfoController.h" +#import "BookmarkFolder.h" +#import "Bookmark.h" +#import "MainController.h" +#import "BrowserWindowController.h" +#import "PreferenceManager.h" +#import "ImageAndTextCell.h" +#import "ExtendedTableView.h" +#import "BookmarkOutlineView.h" +#import "HistoryDataSource.h" +#import "BookmarksClient.h" +#import "NetworkServices.h" + +#define kNoOpenAction 0 +#define kOpenBookmarkAction 1 +#define kOpenInNewTabAction 2 +#define kOpenInNewWindowAction 3 + +#define kGetInfoContextMenuItemTag 9 + +@interface BookmarkViewController (Private) +-(void) setSearchResultArray:(NSArray *)anArray; +-(void) displayBookmarkInOutlineView:(BookmarkItem *)aBookmarkItem; +@end + +@implementation BookmarkViewController + +- (void)dealloc +{ + [[NSNotificationCenter defaultCenter] removeObserver:self]; + [mCachedHref release]; + [mExpandedStatus release]; + [mActiveRootCollection release]; + [mRootBookmarks release]; + [mSearchResultArray release]; + [super dealloc]; +} + + +- (void)windowDidLoad +{ + mCachedHref = nil; + mRootBookmarks = nil; + mOpenActionFlag = 0; + [self setSearchResultArray:[NSMutableArray array]]; + // the standard table item doesn't handle text and icons. Replace it + // with a custom cell that does. + ImageAndTextCell* imageAndTextCell = [[[ImageAndTextCell alloc] init] autorelease]; + [imageAndTextCell setEditable: YES]; + [imageAndTextCell setWraps: NO]; + NSTableColumn* itemNameColumn = [mItemPane tableColumnWithIdentifier: @"title"]; + [itemNameColumn setDataCell:imageAndTextCell]; + NSTableColumn* containerNameColumn = [mContainerPane tableColumnWithIdentifier: @"title"]; + [containerNameColumn setDataCell:imageAndTextCell]; + NSTableColumn* searchNameColumn = [mSearchPane tableColumnWithIdentifier: @"title"]; + [searchNameColumn setDataCell:imageAndTextCell]; + + // set up the font on the item & search views to be smaller + NSArray* columns = [mItemPane tableColumns]; + if ( columns ) { + int numColumns = [columns count]; + NSFont* smallerFont = [NSFont systemFontOfSize:11]; + for ( int i = 0; i < numColumns; ++i ) + [[[columns objectAtIndex:i] dataCell] setFont:smallerFont]; + } + columns = [mSearchPane tableColumns]; + if ( columns ) { + int numColumns = [columns count]; + NSFont* smallerFont = [NSFont systemFontOfSize:11]; + for ( int i = 0; i < numColumns; ++i ) + [[[columns objectAtIndex:i] dataCell] setFont:smallerFont]; + } + // Generic notifications for Bookmark Client + NSNotificationCenter *nc = [NSNotificationCenter defaultCenter]; + [nc addObserver:self selector:@selector(bookmarkAdded:) name:BookmarkFolderAdditionNotification object:nil]; + [nc addObserver:self selector:@selector(bookmarkRemoved:) name:BookmarkFolderDeletionNotification object:nil]; + [nc addObserver:self selector:@selector(bookmarkChanged:) name:BookmarkItemChangedNotification object:nil]; + [nc addObserver:self selector:@selector(serviceResolved:) name:NetworkServicesResolutionSuccess object:nil]; + // register for dragged types + [mContainerPane registerForDraggedTypes:[NSArray arrayWithObject:@"MozBookmarkType"]]; + [mSearchButton setEnabled:NO]; + [self ensureBookmarks]; + // + // these should be settable in the nib. however, whenever + // I try, they disappear as soon as I've saved. Very annoying. + [mItemPane setAutosaveName:@"BMOutlineView"]; + [mSearchPane setAutosaveName:@"BMSearchView"]; + [mContainerPane setAutosaveName:@"BMContainerView"]; + [mItemPane setAutosaveTableColumns:YES]; + [mSearchPane setAutosaveTableColumns:YES]; + [mContainerPane setAutosaveTableColumns:YES]; +} + +-(void)ensureBookmarks +{ + if (!mRootBookmarks) { + [mContainerPane setTarget:self]; + [mContainerPane setDeleteAction:@selector(deleteCollection:)]; + [mItemPane setTarget: self]; + [mItemPane setDoubleAction: @selector(openBookmark:)]; + [mItemPane setDeleteAction: @selector(deleteBookmarks:)]; + [mItemPane reloadData]; + [mSearchPane setTarget: self]; + [mSearchPane setDoubleAction: @selector(openBookmark:)]; + [self restoreFolderExpandedStates]; + [mItemPane setAutosaveTableColumns:YES]; + [mSearchPane setAutosaveTableColumns:YES]; + [mContainerPane setAutosaveTableColumns:YES]; + mRootBookmarks = [[[BookmarkManager sharedBookmarkManager] rootBookmarks] retain]; + } +} + +-(void) setSearchResultArray:(NSArray *)anArray +{ + [anArray retain]; + [mSearchResultArray release]; + mSearchResultArray = anArray; + [mSearchPane reloadData]; +} + +// +// IBActions +// + +- (IBAction) setAsDockMenuFolder:(id)aSender +{ + int rowIndex = [mContainerPane selectedRow]; + if (rowIndex >= 0) { + BookmarkFolder *aFolder = [mRootBookmarks objectAtIndex:[mContainerPane selectedRow]]; + [aFolder setIsDockMenu:YES]; + } +} + +-(IBAction)addBookmark:(id)aSender +{ + [self addItem: aSender useSelection: YES isFolder: NO URL:nil title:nil]; +} + +-(IBAction)addFolder:(id)aSender +{ + [self addItem: aSender useSelection: YES isFolder: YES URL:nil title:nil]; +} + +-(IBAction)addCollection:(id)aSender +{ + BookmarkFolder *aFolder = [mRootBookmarks addBookmarkFolder]; + [aFolder setTitle:NSLocalizedString(@"NewBookmarkFolder",@"New Folder")]; + unsigned index = [mRootBookmarks indexOfObjectIdenticalTo:aFolder]; + [self selectContainer:index]; + [mContainerPane editColumn:0 row:index withEvent:nil select:YES]; +} + +-(IBAction) addSeparator:(id)aSender; +{ + Bookmark *aBookmark = [[Bookmark alloc] init]; + [aBookmark setIsSeparator:YES]; + [[[BookmarkManager sharedBookmarkManager] bookmarkMenuFolder] insertChild:aBookmark]; +} + + + +-(void)addItem:(id)aSender useSelection:(BOOL)aUseSel isFolder:(BOOL)aIsFolder URL:(NSString*)aURL title:(NSString*)aTitle +{ + // We use the selected item to determine the parent only if aUseSel is YES. + BookmarkFolder *parentFolder = nil; + BookmarkItem* item = nil; + if (aUseSel && ([mItemPane numberOfSelectedRows] == 1)) + { + // There is only one selected row. If it is a folder, use it as our parent. + // Otherwise, use selected row's parent. + int index = [mItemPane selectedRow]; + item = [mItemPane itemAtRow: index]; + if ([item isKindOfClass:[BookmarkFolder class]]) + parentFolder = item; + else + parentFolder = [item parent]; + } else + parentFolder = [self activeCollection]; + [self addItem:aSender withParent:parentFolder isFolder:aIsFolder URL:aURL title:aTitle]; +} + +-(void)addItem:(id)aSender withParent:(BookmarkFolder*)parent isFolder:(BOOL)aIsFolder URL:(NSString*)aURL title:(NSString*)aTitle +{ + NSString* titleString = aTitle; + NSString* urlString = aURL; + + if (!aIsFolder) + { + // If no URL and title were specified, get them from the current page. + if (!aURL || !aTitle) + [[mBrowserWindowController getBrowserWrapper] getTitle:&titleString andHref:&urlString]; + + mCachedHref = [NSString stringWithString:urlString]; + [mCachedHref retain]; + + } else { // Folder + mCachedHref = nil; + titleString = NSLocalizedString(@"NewBookmarkFolder", @""); + } + + NSTextField* textField = [mBrowserWindowController getAddBookmarkTitle]; + NSString* bookmarkTitle = titleString; + NSString* cleanedTitle = [bookmarkTitle stringByReplacingCharactersInSet:[NSCharacterSet controlCharacterSet] withString:@" "]; + + [textField setStringValue: cleanedTitle]; + + [mBrowserWindowController cacheBookmarkVC: self]; + + // Show/hide the bookmark all tabs checkbox as appropriate. + NSTabView* tabView = [mBrowserWindowController getTabBrowser]; + id checkbox = [mBrowserWindowController getAddBookmarkCheckbox]; + BOOL hasSuperview = [checkbox superview] != nil; + if (aIsFolder && hasSuperview) { + // Just don't show it at all. + [checkbox removeFromSuperview]; + [checkbox retain]; + } + else if (!aIsFolder && !hasSuperview) { + // Put it back in. + [[[mBrowserWindowController getAddBookmarkSheetWindow] contentView] addSubview: checkbox]; + [checkbox autorelease]; + } + + // Enable the bookmark all tabs checkbox if appropriate. + if (!aIsFolder) + [[mBrowserWindowController getAddBookmarkCheckbox] setEnabled: ([tabView numberOfTabViewItems] > 1)]; + + // Build up the folder list. + NSPopUpButton* popup = [mBrowserWindowController getAddBookmarkFolder]; + [popup removeAllItems]; + [mRootBookmarks buildFlatFolderList:[popup menu] depth:1]; + + int itemIndex = [popup indexOfItemWithRepresentedObject:parent]; + if (itemIndex != -1) + [popup selectItemAtIndex:itemIndex]; + + [popup synchronizeTitleAndSelectedItem]; + + [NSApp beginSheet: [mBrowserWindowController getAddBookmarkSheetWindow] + modalForWindow: [mBrowserWindowController window] + modalDelegate: nil //self + didEndSelector: nil //@selector(sheetDidEnd:) + contextInfo: nil]; +} + +-(void)endAddBookmark: (int)aCode +{ + if (aCode == 0) + return; + + BOOL isGroup = NO; + id checkbox = [mBrowserWindowController getAddBookmarkCheckbox]; + if (([checkbox superview] != nil) && [checkbox isEnabled] && ([checkbox state] == NSOnState)) + { + [mCachedHref release]; + mCachedHref = nil; + isGroup = YES; + } + + NSString* titleString = [[mBrowserWindowController getAddBookmarkTitle] stringValue]; + NSPopUpButton* popup = [mBrowserWindowController getAddBookmarkFolder]; + NSMenuItem* selectedItem = [popup selectedItem]; + BookmarkFolder *parentFolder = [selectedItem representedObject]; + + if (isGroup) + { + BookmarkFolder *newGroup = [parentFolder addBookmarkFolder:titleString inPosition:[parentFolder count] isGroup:YES]; + id tabBrowser = [mBrowserWindowController getTabBrowser]; + int count = [tabBrowser numberOfTabViewItems]; + for (int i = 0; i < count; i++) + { + BrowserWrapper* browserWrapper = (BrowserWrapper*)[[tabBrowser tabViewItemAtIndex: i] view]; + NSString* titleString = nil; + NSString* hrefString = nil; + [browserWrapper getTitle:&titleString andHref:&hrefString]; + [newGroup addBookmark:titleString url:hrefString inPosition:i isSeparator:NO]; + } + } + else + { + if (mCachedHref) + { + [parentFolder addBookmark:titleString url:mCachedHref inPosition:[parentFolder count] isSeparator:NO]; + [mCachedHref release]; + mCachedHref = nil; + } + else + [parentFolder addBookmarkFolder:titleString inPosition:[parentFolder count] isGroup:NO]; + } +} + +-(IBAction)deleteCollection:(id)aSender +{ + BookmarkManager *manager = [BookmarkManager sharedBookmarkManager]; + int index = [mContainerPane selectedRow]; + if (index < (int)[manager firstUserCollection]) + return; + [self selectContainer:(index - 1)]; + [[manager rootBookmarks] deleteChild:[[manager rootBookmarks] objectAtIndex:index]]; +} + +-(IBAction)deleteBookmarks: (id)aSender +{ + int index = [mItemPane selectedRow]; + if (index == -1) + return; + + // A cheap way of having to avoid scanning the list to remove children is to have the + // outliner collapse all items that are being deleted. This will cull the selection + // for us and eliminate any children that happened to be selected. + + BOOL allCollapsed = NO; + id doomedItem; + NSEnumerator* selRows; + while (!allCollapsed) { + allCollapsed = YES; + selRows = [mItemPane selectedRowEnumerator]; + while (allCollapsed && (doomedItem = [selRows nextObject])) { + doomedItem = [mItemPane itemAtRow:[doomedItem intValue]]; + if ([mItemPane isItemExpanded:doomedItem]) { + allCollapsed = NO; + [mItemPane collapseItem:doomedItem]; + } + } + } + + // create array of items we need to delete. Deleting items out of of the + // selection array is problematic for some reason. + NSMutableArray *itemsToDelete = [[NSMutableArray alloc] init]; + selRows = [mItemPane selectedRowEnumerator]; + for (NSNumber* currIndex = [selRows nextObject]; + currIndex != nil; + currIndex = [selRows nextObject]) { + index = [currIndex intValue]; + BookmarkItem* item = [mItemPane itemAtRow: index]; + [itemsToDelete addObject: item]; + } + + // delete all bookmarks that are in our array + int count = [itemsToDelete count]; + for (int i = 0; i < count; i++) { + doomedItem = [itemsToDelete objectAtIndex:i]; + [[doomedItem parent] deleteChild:doomedItem]; + } + [itemsToDelete release]; + + // restore selection to location near last item deleted or last item + int total = [mItemPane numberOfRows]; + if (index >= total) + index = total - 1; + [mItemPane selectRow: index byExtendingSelection: NO]; +} + +-(IBAction)openBookmark: (id)aSender +{ + id item = nil; + if (aSender == mItemPane) { + int index = [mItemPane selectedRow]; + if (index == -1) + return; + item = [mItemPane itemAtRow: index]; + } else if (aSender == mSearchPane) { + int index = [mSearchPane selectedRow]; + if (index == -1) + return; + item = [mSearchResultArray objectAtIndex:index]; + } else if ([aSender isKindOfClass:[BookmarkItem class]]) + item = aSender; + + if (!item) + return; + // see if it's a rendezvous item + id parent = [item parent]; + if (![parent isKindOfClass:[BookmarkItem class]]) { + [[NetworkServices sharedNetworkServices] attemptResolveService:[parent intValue] forSender:item]; + mOpenActionFlag = kOpenBookmarkAction; + return; + } + + if ([item isKindOfClass:[Bookmark class]]) { + // if the command key is down, follow the command-click pref + if (([[NSApp currentEvent] modifierFlags] & NSCommandKeyMask) && + [[PreferenceManager sharedInstance] getBooleanPref:"browser.tabs.opentabfor.middleclick" withSuccess:NULL]) + { + BOOL loadInBackground = [[PreferenceManager sharedInstance] getBooleanPref:"browser.tabs.loadInBackground" withSuccess:NULL]; + [mBrowserWindowController openNewTabWithURL:[item url] referrer:nil loadInBackground: loadInBackground]; + } + else + [[mBrowserWindowController getBrowserWrapper] loadURI:[(Bookmark *)item url] referrer:nil flags:NSLoadFlagsNone activate:YES]; + } else if ([item isKindOfClass:[BookmarkFolder class]]) { + if ([item isGroup]) + [mBrowserWindowController openTabGroup:[item childURLs] replaceExistingTabs:YES]; + else if ([mItemPane isItemExpanded:item]) + [mItemPane collapseItem: item]; + else + [mItemPane expandItem: item]; + } +} + +-(IBAction)openBookmarkInNewTab:(id)aSender +{ + id item = nil; + + if (![aSender isKindOfClass:[BookmarkItem class]]) { + int index = [mItemPane selectedRow]; + if (index == -1) + return; + if ([mItemPane numberOfSelectedRows] == 1) + item = [mItemPane itemAtRow:index]; + } else + item = aSender; + + if (!item) + return; + // see if it's a rendezvous item + id parent = [item parent]; + if (![parent isKindOfClass:[BookmarkItem class]]) { + [[NetworkServices sharedNetworkServices] attemptResolveService:[parent intValue] forSender:item]; + mOpenActionFlag = kOpenInNewTabAction; + return; + } + if ([item isKindOfClass:[Bookmark class]]) { + BOOL loadInBackground = [[PreferenceManager sharedInstance] getBooleanPref:"browser.tabs.loadInBackground" withSuccess:NULL]; + [mBrowserWindowController openNewTabWithURL:[item url] referrer:nil loadInBackground: loadInBackground]; + } else if ([item isKindOfClass:[BookmarkFolder class]]) { + [mBrowserWindowController openTabGroup:[item childURLs] replaceExistingTabs:YES]; + } +} + +-(IBAction)openBookmarkInNewWindow:(id)aSender +{ + id item = nil; + if (![aSender isKindOfClass:[BookmarkItem class]]) { + int index = [mItemPane selectedRow]; + if (index == -1) + return; + if ([mItemPane numberOfSelectedRows] == 1) + item = [mItemPane itemAtRow:index]; + } else + item = aSender; + if (!item) + return; + + // see if it's a rendezvous item + id parent = [item parent]; + if (![parent isKindOfClass:[BookmarkItem class]]) { + [[NetworkServices sharedNetworkServices] attemptResolveService:[parent intValue] forSender:item]; + mOpenActionFlag = kOpenInNewWindowAction; + return; + } + BOOL loadInBackground = [[PreferenceManager sharedInstance] getBooleanPref:"browser.tabs.loadInBackground" withSuccess:NULL]; + if ([item isKindOfClass:[Bookmark class]]) + [mBrowserWindowController openNewWindowWithURL:[item url] referrer: nil loadInBackground: loadInBackground]; + else if ([item isKindOfClass:[BookmarkFolder class]]) { + [mBrowserWindowController openNewWindowWithGroupURLs:[item childURLs] loadInBackground:loadInBackground]; + } +} + +-(IBAction)showBookmarkInfo:(id)aSender +{ + BookmarkInfoController *bic = [BookmarkInfoController sharedBookmarkInfoController]; + BookmarkItem* item = nil; + if ([[mItemPane window] firstResponder] == mSearchPane) { + int index = [mSearchPane selectedRow]; + item = [mSearchResultArray objectAtIndex:index]; + } else { + int index = [mItemPane selectedRow]; + item = [mItemPane itemAtRow: index]; + } + [bic setBookmark:item]; + [bic showWindow:bic]; +} + +-(IBAction)startSearch:(id)aSender +{ + NSString *searchString = [mSearchField stringValue]; + if ([searchString length] > 0) + [self setSearchResultArray:[[BookmarkManager sharedBookmarkManager] searchBookmarksForString:searchString]]; +} + +-(IBAction) locateBookmark:(id)aSender +{ + int index = [mSearchPane selectedRow]; + if (index == -1) + return; + BookmarkItem *item = [mSearchResultArray objectAtIndex:index]; + [self displayBookmarkInOutlineView:item]; + [mItemPane selectRow:[mItemPane rowForItem:item] byExtendingSelection:NO]; +} + +-(void) displayBookmarkInOutlineView:(BookmarkItem *)aBookmarkItem +{ + BookmarkItem *parent = [aBookmarkItem parent]; + if (parent != mRootBookmarks) + [self displayBookmarkInOutlineView:parent]; + else { + int index = [mRootBookmarks indexOfObject:aBookmarkItem]; + [mContainerPane selectRow:index byExtendingSelection:NO]; + [self selectContainer:index]; + return; + } + [mItemPane expandItem:aBookmarkItem]; +} + + +- (void) focus +{ + [[mItemPane window] makeFirstResponder:mItemPane]; +} + +- (void) setCanEditSelectedContainerContents:(BOOL)inCanEdit +{ + [mItemPane setAllowsEditing:inCanEdit]; + // update buttons + [mAddBookmarkButton setEnabled:inCanEdit]; + [mAddFolderButton setEnabled:inCanEdit]; + [mInfoButton setEnabled:inCanEdit]; +} + +-(void) setActiveCollection:(BookmarkFolder *)aFolder +{ + [aFolder retain]; + [mActiveRootCollection release]; + mActiveRootCollection = aFolder; +} + +-(BookmarkFolder *)activeCollection +{ + return mActiveRootCollection; +} + +- (BOOL)isExpanded:(id)anItem +{ + NSMutableDictionary *dict = [self expandedStatusDictionary]; + NSNumber *aBool = [dict objectForKey:[NSNumber numberWithUnsignedInt:[anItem hash]]]; + if (aBool) + return [aBool boolValue]; + return NO; +} + +-(BOOL) isExpandedInView:(NSOutlineView *)aView +{ + return NO; +} + +-(void) setItem:(BookmarkFolder *)anItem isExpanded:(BOOL)aBool +{ + NSMutableDictionary *dict = [self expandedStatusDictionary]; + [dict setObject:[NSNumber numberWithBool:aBool] forKey:[NSNumber numberWithUnsignedInt:[anItem hash]]]; +} + +- (void)restoreFolderExpandedStates +{ + int curRow = 0; + while (curRow < [mItemPane numberOfRows]) + { + id item = [mItemPane itemAtRow:curRow]; + if ([item isKindOfClass:[BookmarkFolder class]]) + { + if ([self isExpanded:item]) + [mItemPane expandItem: item]; + else + [mItemPane collapseItem: item]; + } + curRow ++; + } +} + +-(NSMutableDictionary *)expandedStatusDictionary +{ + if (mExpandedStatus) + return mExpandedStatus; + mExpandedStatus = [[NSMutableDictionary alloc] initWithCapacity:20]; + return mExpandedStatus; +} + + +#pragma mark - +// +// table view things +// +- (void) selectContainer:(int)inRowIndex +{ + [mContainerPane selectRow:inRowIndex byExtendingSelection:NO]; + if ( inRowIndex == kHistoryContainerIndex ) { + [mItemPane setDataSource:mHistorySource]; + [mItemPane setDelegate:mHistorySource]; + [mHistorySource ensureDataSourceLoaded]; + [self setCanEditSelectedContainerContents:NO]; + [mItemPane setTarget:mHistorySource]; + [mItemPane setDoubleAction: @selector(openHistoryItem:)]; + [mItemPane setDeleteAction: @selector(deleteHistoryItems:)]; + } else { + [mItemPane setDataSource:self]; + [mItemPane setDelegate:self]; + BookmarkFolder *activeCollection = [mRootBookmarks objectAtIndex:inRowIndex]; + [self setActiveCollection:activeCollection]; + [self restoreFolderExpandedStates]; + [mItemPane setTarget:self]; + [mItemPane setDoubleAction: @selector(openBookmark:)]; + if ([activeCollection isSmartFolder]) + [self setCanEditSelectedContainerContents:NO]; + else + [self setCanEditSelectedContainerContents:YES]; + [mItemPane setDeleteAction: @selector(deleteBookmarks:)]; + } + [mItemPane reloadData]; +} + +- (void) selectLastContainer +{ + [self selectContainer:[mContainerPane selectedRow]]; +} + +- (int)numberOfRowsInTableView:(NSTableView *)tableView +{ + if ( tableView == mContainerPane ) + return [mRootBookmarks count]; + else if ( tableView == mSearchPane ) + return [mSearchResultArray count]; + return 0; +} + +- (id)tableView:(NSTableView *)tableView objectValueForTableColumn:(NSTableColumn *)tableColumn row:(int)row +{ + id retValue = nil; + id item = nil; + if ( tableView == mContainerPane ) + item = [mRootBookmarks objectAtIndex:row]; + else if (tableView == mSearchPane ) + item = [mSearchResultArray objectAtIndex:row]; + NS_DURING + retValue = [item valueForKey:[tableColumn identifier]]; + NS_HANDLER + retValue = nil; + NS_ENDHANDLER + return retValue; +} + +- (void)tableView:(NSTableView *)inTableView willDisplayCell:(id)inCell forTableColumn:(NSTableColumn *)inTableColumn row:(int)inRowIndex +{ + if ( inTableView == mContainerPane ) { + BookmarkFolder *aFolder = [mRootBookmarks objectAtIndex:inRowIndex]; + [inCell setImage:[aFolder icon]]; + } else if (inTableView == mSearchPane && [[inTableColumn identifier] isEqualToString:@"title"]) { + BookmarkItem *anItem = [mSearchResultArray objectAtIndex:inRowIndex]; + [inCell setImage:[anItem icon]]; + } +} + +- (BOOL)tableView:(NSTableView *)aTableView shouldEditTableColumn:(NSTableColumn *)aTableColumn row:(int)rowIndex +{ + if (aTableView == mContainerPane) { + if (rowIndex >= (int)[[BookmarkManager sharedBookmarkManager] firstUserCollection]) + return YES; + return NO; + } + return NO; +} + +- (void)tableView:(NSTableView *)tableView setObjectValue:(id)object forTableColumn:(NSTableColumn *)tableColumn row:(int)row; +{ + if (tableView == mContainerPane) { + BookmarkFolder *aFolder = [mRootBookmarks objectAtIndex:row]; + [aFolder setTitle:object]; + } +} + +- (BOOL)tableView:(NSTableView *)tv writeRows:(NSArray*)rows toPasteboard:(NSPasteboard*)pboard +{ + int count = [rows count]; + if (count == 0) + return NO; + NSMutableArray *itemArray = [[NSMutableArray alloc] initWithCapacity:count]; + BookmarkManager *manager = [BookmarkManager sharedBookmarkManager]; + NSEnumerator *enumerator = [rows objectEnumerator]; + unsigned firstUserCollection = [manager firstUserCollection]; + int rowVal; + id aRow; + while ((aRow = [enumerator nextObject])) { + rowVal = [aRow intValue]; + if (rowVal > (int)firstUserCollection) + [itemArray addObject:[mRootBookmarks objectAtIndex:rowVal]]; + } + if ([itemArray count] == 0) { + [itemArray release]; + return NO; + } + // Pack pointers to bookmark items into this array + NSArray *pointerArray = [NSArray dataArrayFromPointerArrayForMozBookmarkDrop:itemArray]; + [itemArray release]; + [pboard declareTypes:[NSArray arrayWithObject:@"MozBookmarkType"] owner:self]; + [pboard setPropertyList:pointerArray forType:@"MozBookmarkType"]; + return YES; +} + +- (NSDragOperation)tableView:(NSTableView*)tv validateDrop:(id )info proposedRow:(int)row proposedDropOperation:(NSTableViewDropOperation)op +{ + NSArray* types = [[info draggingPasteboard] types]; + if ([types containsObject:@"MozBookmarkType"] && tv == mContainerPane) { + BookmarkManager *manager = [BookmarkManager sharedBookmarkManager]; + BookmarkFolder *dropFolder = nil; + if ((row < 2) && (op == NSTableViewDropOn)) + dropFolder = [mRootBookmarks objectAtIndex:row]; + else if (row >= (int)[manager firstUserCollection]) { + if (op == NSTableViewDropAbove) + dropFolder = mRootBookmarks; + else + dropFolder = [mRootBookmarks objectAtIndex:row]; + } + if (dropFolder) { + NSArray *draggedItems = [NSArray pointerArrayFromDataArrayForMozBookmarkDrop:[[info draggingPasteboard] propertyListForType: @"MozBookmarkType"]]; + BOOL isOK = [manager isDropValid:draggedItems toFolder:dropFolder]; + return (isOK) ? NSDragOperationGeneric: NSDragOperationNone; + } + } + return NSDragOperationNone; +} + +- (BOOL)tableView:(NSTableView*)tv acceptDrop:(id )info row:(int)row dropOperation:(NSTableViewDropOperation)op +{ + NSArray *types = [[info draggingPasteboard] types]; + if ([types containsObject:@"MozBookmarkType"] && (tv == mContainerPane)) { + BookmarkFolder *dropFolder; + if (op == NSTableViewDropAbove) + dropFolder = mRootBookmarks; + else + dropFolder = [mRootBookmarks objectAtIndex:row]; + BOOL isCopy = ([info draggingSourceOperationMask] == NSDragOperationCopy); + NSArray *draggedItems = [NSArray pointerArrayFromDataArrayForMozBookmarkDrop:[[info draggingPasteboard] propertyListForType: @"MozBookmarkType"]]; + NSEnumerator *enumerator = [draggedItems objectEnumerator]; + id aKid; + while ((aKid = [enumerator nextObject])) { + if (isCopy) { + [[aKid parent] copyChild:aKid toBookmarkFolder:dropFolder atIndex:row]; + } else { + [[aKid parent] moveChild:aKid toBookmarkFolder:dropFolder atIndex:row]; + } + } + return YES; + } + return NO; +} + +- (BOOL)tableView:(NSTableView *)aTableView shouldSelectRow:(int)rowIndex +{ + if (aTableView == mContainerPane) + [self selectContainer:rowIndex]; + return YES; +} + +-(void)tableViewSelectionDidChange:(NSNotification *)note +{ + if ([note object] == mSearchPane) { + BookmarkInfoController *bic = [BookmarkInfoController sharedBookmarkInfoController]; + int index = [mSearchPane selectedRow]; + if (index != -1) + [mSearchButton setEnabled:YES]; + else + [mSearchButton setEnabled:NO]; + if ([[bic window] isVisible]) + [bic setBookmark:[mSearchResultArray objectAtIndex:index]]; + } +} + +-(NSMenu *)tableView:(NSTableView *)aTableView contextMenuForRow:(int)rowIndex +{ + if (aTableView == mContainerPane) { + NSMenu* contextMenu = nil; + if (rowIndex >= 0) { + contextMenu = [aTableView menu]; + int numItems = [contextMenu numberOfItems]; + while (numItems > 2) + [contextMenu removeItemAtIndex:(--numItems)]; + if (rowIndex >= (int)[[BookmarkManager sharedBookmarkManager] firstUserCollection]) { + [contextMenu addItem:[NSMenuItem separatorItem]]; + NSMenuItem *deleteItem = [[NSMenuItem alloc] initWithTitle:NSLocalizedString(@"Delete",@"Delete") action:@selector(deleteCollection:) keyEquivalent:[NSString string]]; + [deleteItem setTarget:self]; + [contextMenu addItem:deleteItem]; + [deleteItem release]; + } + } + return contextMenu; + } + return nil; +} + +#pragma mark - + +// +// outlineView:shouldEditTableColumn:item: (delegate method) +// +// Called by the outliner to determine whether or not we should allow the +// user to edit this item. We always return NO, because we invoke the +// edit methods manually. +// +- (BOOL)outlineView:(NSOutlineView *)outlineView shouldEditTableColumn:(NSTableColumn *)tableColumn item:(id)item +{ + return NO; +} + +- (id)outlineView:(NSOutlineView *)outlineView child:(int)index ofItem:(id)item +{ + if (item) + return [item objectAtIndex:index]; + return [[self activeCollection] objectAtIndex:index]; +} + +- (BOOL)outlineView:(NSOutlineView *)outlineView isItemExpandable:(id)item +{ + if (!(item) || [item isKindOfClass:[BookmarkFolder class]]) + return YES; + return NO; +} + +- (int)outlineView:(NSOutlineView *)outlineView numberOfChildrenOfItem:(id)item +{ + if (item) + return [item count]; + return [[self activeCollection] count]; +} + +- (id)outlineView:(NSOutlineView *)outlineView objectValueForTableColumn:(NSTableColumn *)tableColumn byItem:(id)item +{ + id retValue = nil; + NS_DURING + retValue = [item valueForKey:[tableColumn identifier]]; + NS_HANDLER + if ([item isKindOfClass:[BookmarkFolder class]] && [[tableColumn identifier] isEqualToString:@"url"]) { + NSString* itemCountStr = [NSString stringWithFormat:NSLocalizedString(@"Contains Items", @"%u Items"),[item count]]; + NSDictionary* colorAttributes = [NSDictionary dictionaryWithObject:[NSColor disabledControlTextColor] forKey:NSForegroundColorAttributeName]; + retValue = [[[NSAttributedString alloc] initWithString:itemCountStr attributes:colorAttributes] autorelease]; + } else + retValue = nil; + NS_ENDHANDLER + return retValue; +} + +- (void)outlineView:(NSOutlineView *)outlineView willDisplayCell:(NSCell *)cell forTableColumn:(NSTableColumn *)tableColumn item:(id)item +{ + // set the image on the name column. the url column doesn't have an image. + if ([[tableColumn identifier] isEqualToString: @"title"]) + [cell setImage:[item icon]]; +} + +- (void)outlineView:(NSOutlineView *)outlineView setObjectValue:(id)object forTableColumn:(NSTableColumn *)tableColumn byItem:(id)item +{ + NS_DURING + [item takeValue:object forKey:[tableColumn identifier]]; + NS_HANDLER + return; + NS_ENDHANDLER +} + +- (BOOL)outlineView:(NSOutlineView *)ov writeItems:(NSArray*)items toPasteboard:(NSPasteboard*)pboard +{ + int count = [items count]; + if ((count == 0) || [mActiveRootCollection isSmartFolder]) + return NO; + + // Pack pointers to bookmark items into this array. + NSArray *pointerArray = [NSArray dataArrayFromPointerArrayForMozBookmarkDrop:items]; + if (count == 1) { + id aBookmark = [items objectAtIndex:0]; + if ([aBookmark isKindOfClass:[Bookmark class]]) { + [pboard declareURLPasteboardWithAdditionalTypes:[NSArray arrayWithObject:@"MozBookmarkType"] owner:self]; + [pboard setDataForURL:[aBookmark url] title:[aBookmark title]]; + [pboard setPropertyList:pointerArray forType:@"MozBookmarkType"]; + return YES; + } + } + // it's either a folder or we've got more than 1 thing. ship it + // out as MozBookmarkType + [pboard declareTypes:[NSArray arrayWithObject:@"MozBookmarkType"] owner: self]; + [pboard setPropertyList: pointerArray forType:@"MozBookmarkType"]; + return YES; +} + + +- (NSDragOperation)outlineView:(NSOutlineView*)ov validateDrop:(id )info proposedItem:(id)item proposedChildIndex:(int)index +{ + NSArray* types = [[info draggingPasteboard] types]; + + // if the index is -1, deny the drop + if (index == NSOutlineViewDropOnItemIndex) + return NSDragOperationNone; + + if ([types containsObject: @"MozBookmarkType"]) + { + NSArray *draggedItems = [NSArray pointerArrayFromDataArrayForMozBookmarkDrop:[[info draggingPasteboard] propertyListForType: @"MozBookmarkType"]]; + BookmarkFolder* parent = (item) ? item : [self activeCollection]; + BOOL isOK = [[BookmarkManager sharedBookmarkManager] isDropValid:draggedItems toFolder:parent]; + return (isOK) ? NSDragOperationGeneric : NSDragOperationNone; + } + + if ([types containsObject: NSStringPboardType] || + [types containsObject: NSURLPboardType] || + [types containsObject: @"MozURLType"] ) + return NSDragOperationGeneric; + + return NSDragOperationNone; +} + + +- (BOOL)outlineView:(NSOutlineView*)ov acceptDrop:(id )info item:(id)item childIndex:(int)index +{ + NSArray* types = [[info draggingPasteboard] types]; + BOOL isCopy = ([info draggingSourceOperationMask] == NSDragOperationCopy); + if (!item) + item = [self activeCollection]; + + if ([types containsObject: @"MozBookmarkType"]) + { + NSArray *draggedItems = [NSArray pointerArrayFromDataArrayForMozBookmarkDrop:[[info draggingPasteboard] propertyListForType: @"MozBookmarkType"]]; + NSEnumerator *enumerator = [draggedItems objectEnumerator]; + id aKid; + while ((aKid = [enumerator nextObject])) { + if (isCopy) { + [[aKid parent] copyChild:aKid toBookmarkFolder:item atIndex:index]; + } else { + [[aKid parent] moveChild:aKid toBookmarkFolder:item atIndex:index]; + } + } + return YES; + } + else if ([types containsObject: @"MozURLType"]) + { + NSDictionary* proxy = [[info draggingPasteboard] propertyListForType: @"MozURLType"]; + [item addBookmark:[proxy objectForKey:@"title"] url:[proxy objectForKey:@"url"] inPosition:index isSeparator:NO]; + return YES; + } + else if ([types containsObject: NSStringPboardType]) + { + NSString* draggedText = [[info draggingPasteboard] stringForType:NSStringPboardType]; + [item addBookmark:draggedText url:draggedText inPosition:index isSeparator:NO]; + return YES; + } + else if ([types containsObject: NSURLPboardType]) + { + NSURL* urlData = [NSURL URLFromPasteboard:[info draggingPasteboard]]; + [item addBookmark:[urlData absoluteString] url:[urlData absoluteString] inPosition:index isSeparator:NO]; + return YES; + } + return NO; +} + +- (NSString *)outlineView:(NSOutlineView *)outlineView tooltipStringForItem:(id)item +{ + if ([item isKindOfClass:[Bookmark class]]) { + if ([[item description] length] > 0) + return [NSString stringWithFormat:@"%@\n%@",[item url], [item description]]; + else + return [item url]; + } + else if ([item isKindOfClass:[BookmarkFolder class]]) + return [item description]; + else + return nil; +} + + + - (NSMenu *)outlineView:(NSOutlineView *)outlineView contextMenuForItem:(id)item +{ + NSMenu *contextMenu = nil; + if (item) { + NSString *nulString = [NSString string]; + contextMenu = [mItemPane menu]; + // clean the menu out + int numItems = [contextMenu numberOfItems]; + int itemIndex = [contextMenu indexOfItemWithTarget:self andAction:@selector(showBookmarkInfo:)]; + while (numItems > (itemIndex+1)) + [contextMenu removeItemAtIndex:(--numItems)]; + if ([item isKindOfClass:[Bookmark class]]) { + itemIndex = [contextMenu indexOfItemWithTarget:self andAction:@selector(openBookmarkInNewWindow:)]; + [[contextMenu itemAtIndex:itemIndex] setTitle:NSLocalizedString(@"Open in New Window",@"Open in New Window")]; + itemIndex = [contextMenu indexOfItemWithTarget:self andAction:@selector(openBookmarkInNewTab:)]; + [[contextMenu itemAtIndex:itemIndex] setTitle:NSLocalizedString(@"Open in New Tab",@"Open in New Tab")]; + } else if ([item isKindOfClass:[BookmarkFolder class]]) { + itemIndex = [contextMenu indexOfItemWithTarget:self andAction:@selector(openBookmarkInNewWindow:)]; + [[contextMenu itemAtIndex:itemIndex] setTitle:NSLocalizedString(@"Open Tabs in New Window",@"Open Tabs in New Window")]; + itemIndex = [contextMenu indexOfItemWithTarget:self andAction:@selector(openBookmarkInNewTab:)]; + [[contextMenu itemAtIndex:itemIndex] setTitle:NSLocalizedString(@"Open in Tabs",@"Open in Tabs")]; + NSMenuItem *menuItem = [[NSMenuItem alloc] initWithTitle:NSLocalizedString(@"Use as Dock Menu",@"Use as Dock Menu") action:@selector(makeDockMenu:) keyEquivalent:nulString]; + [menuItem setTarget:item]; + [contextMenu addItem:menuItem]; + [menuItem release]; + [contextMenu addItem:[NSMenuItem separatorItem]]; + } + id parent = [item parent]; + if ([parent isKindOfClass:[BookmarkFolder class]] && ![parent isSmartFolder]) { + // delete + NSMenuItem *menuItem = [[NSMenuItem alloc] initWithTitle:NSLocalizedString(@"Delete",@"Delete") action:@selector(deleteBookmarks:) keyEquivalent:nulString]; + [menuItem setTarget:self]; + [contextMenu addItem:menuItem]; + [menuItem release]; + } + if (![[self activeCollection] isSmartFolder]) { + // space + [contextMenu addItem:[NSMenuItem separatorItem]]; + // create new folder + NSMenuItem *menuItem = [[NSMenuItem alloc] initWithTitle:NSLocalizedString(@"Create New Folder...",@"Create New Folder...") action:@selector(addFolder:) keyEquivalent:nulString]; + [menuItem setTarget:self]; + [contextMenu addItem:menuItem]; + [menuItem release]; + } + } + return contextMenu; +} + +- (void)reloadDataForItem:(id)item reloadChildren: (BOOL)aReloadChildren +{ + if (!item) + [mItemPane reloadData]; + else + [mItemPane reloadItem: item reloadChildren: aReloadChildren]; +} + +- (BOOL)haveSelectedRow +{ + return ([mItemPane selectedRow] != -1); +} + +-(void)outlineViewSelectionDidChange: (NSNotification*) aNotification +{ + BookmarkInfoController *bic = [BookmarkInfoController sharedBookmarkInfoController]; + int index = [mItemPane selectedRow]; + if (index == -1) + { + [mInfoButton setEnabled:NO]; + [bic close]; + } + else + { + id item = [mItemPane itemAtRow: index]; + [mInfoButton setEnabled:YES]; + if ([[bic window] isVisible]) + [bic setBookmark:item]; + } +} + +/* +-(BOOL)validateMenuItem:(NSMenuItem*)aMenuItem +{ + int index = [mItemPane selectedRow]; + BOOL haveSelection = (index != -1); + BOOL multiSelection = ([mItemPane numberOfSelectedRows] > 1); + BOOL isBookmark = NO; + BOOL isToolbar = NO; + BOOL isGroup = NO; + + id item = nil; + + if (haveSelection) + item = [mItemPane itemAtRow: index]; + if ([item isKindOfClass:[Bookmark class]]) + isBookmark = YES; + else if ([item isKindOfClass:[BookmarkFolder class]]) { + isGroup = [item isGroup]; + isToolbar = [item isToolbar]; + } + + // Bookmarks and Bookmark Groups can be opened in a new window + if (([aMenuItem action] == @selector(openBookmarkInNewWindow:))) + return (isBookmark || isGroup); + + // Only Bookmarks can be opened in new tabs + if (([aMenuItem action] == @selector(openBookmarkInNewTab:))) + return isBookmark && [mBrowserWindowController newTabsAllowed]; + + if (([aMenuItem action] == @selector(showBookmarkInfo:))) + return haveSelection; + + if (([aMenuItem action] == @selector(deleteBookmarks:))) + return (multiSelection || (haveSelection && !isToolbar)); + + if (([aMenuItem action] == @selector(addFolder:))) + return YES; + + return YES; +} +*/ +- (void)outlineViewItemDidExpand:(NSNotification *)notification +{ + id item = [[notification userInfo] objectForKey:@"NSObject"]; + [self setItem:item isExpanded:YES]; +} + +- (void)outlineViewItemDidCollapse:(NSNotification *)notification +{ + id item = [[notification userInfo] objectForKey:@"NSObject"]; + [self setItem:item isExpanded:NO]; +} + +#pragma mark - +// +// Network services protocol +// +- (void)availableServicesChanged:(NSNotification *)note +{ +} + +// +// we've got to to a delayed call here in case we received +// the note before the bookmark was updated +// +- (void)serviceResolved:(NSNotification *)note +{ + if (mOpenActionFlag == kNoOpenAction) + return; + NSDictionary *dict = [note userInfo]; + id aClient = [dict objectForKey:NetworkServicesClientKey]; + if ([aClient isKindOfClass:[Bookmark class]]) { + switch (mOpenActionFlag) { + case (kOpenBookmarkAction): + [[NSRunLoop currentRunLoop] performSelector:@selector(openBookmark:) target:self argument:aClient order:10 modes:[NSArray arrayWithObject:NSDefaultRunLoopMode]]; + break; + case (kOpenInNewTabAction): + [[NSRunLoop currentRunLoop] performSelector:@selector(openBookmarkInNewTab:) target:self argument:aClient order:10 modes:[NSArray arrayWithObject:NSDefaultRunLoopMode]]; + break; + case (kOpenInNewWindowAction): + [[NSRunLoop currentRunLoop] performSelector:@selector(openBookmarkInNewWindow:) target:self argument:aClient order:10 modes:[NSArray arrayWithObject:NSDefaultRunLoopMode]]; + break; + default: + break; + } + mOpenActionFlag = kNoOpenAction; + } +} + +- (void)serviceResolutionFailed:(NSNotification *)note +{ +} + + + +#pragma mark - +// +// BookmarksClient protocol +// +- (void)bookmarkAdded:(NSNotification *)note +{ + BookmarkFolder *aFolder = [note object]; + if ((aFolder == [[BookmarkManager sharedBookmarkManager] rootBookmarks])) + { + [mContainerPane reloadData]; + BookmarkFolder *updatedFolder = [[note userInfo] objectForKey:BookmarkFolderChildKey]; + [self selectContainer:[aFolder indexOfObjectIdenticalTo:updatedFolder]]; + return; + } + else if (aFolder == mActiveRootCollection) + aFolder = nil; + [self reloadDataForItem:aFolder reloadChildren:YES]; +} + +- (void)bookmarkRemoved:(NSNotification *)note +{ + BookmarkFolder *aFolder = [note object]; + if ((aFolder == [[BookmarkManager sharedBookmarkManager] rootBookmarks])) { + [mContainerPane reloadData]; + return; + } else if (aFolder == mActiveRootCollection) + aFolder = nil; + [self reloadDataForItem:aFolder reloadChildren:YES]; +} + +- (void)bookmarkChanged:(NSNotification *)note +{ + [self reloadDataForItem:[note object] reloadChildren:NO]; +} + +#pragma mark - + +// +// - splitViewDidResizeSubviews: +// +// Called when one of the views got resized. We want to ensure that the "add bookmark +// item" button gets lined up with the left edge of the item panel. If the container/item +// split was the one that changed, move it accordingly +// +- (void)splitViewDidResizeSubviews:(NSNotification *)notification +{ + const int kButtonGutter = 8; + + if ( [notification object] == mContainersSplit ) { + // get the position of the item view relative to the window and set the button + // to that X value. Yes, this will fall down if the bookmark view is inset from the window + // but i think we can safely assume it won't be. + NSRect windowRect = [mItemPane convertRect:[mItemPane bounds] toView:nil]; + NSRect newButtonLocation = [mAddBookmarkButton frame]; + newButtonLocation.origin.x = windowRect.origin.x; + [mAddBookmarkButton setFrame:newButtonLocation]; + [mAddBookmarkButton setNeedsDisplay:YES]; + + // offset by the width of the button and the gutter and we've got the location + // of the add folder button next to it. + newButtonLocation.origin.x += newButtonLocation.size.width + kButtonGutter; + [mAddFolderButton setFrame:newButtonLocation]; + [mAddFolderButton setNeedsDisplay:YES]; + } +} + +// +// - splitView:canCollapseSubview: +// +// Called when appkit wants to ask if it can collapse a subview. The only subview +// of our splits that we allow to be hidden is the search panel. +// +- (BOOL)splitView:(NSSplitView *)sender canCollapseSubview:(NSView *)subview +{ + BOOL retVal = NO; + // subview will be a NSScrollView, so we have to get the superview of the + // search pane for comparison. + if ( sender == mItemSearchSplit && subview == [mSearchPane superview] ) + retVal = YES; + return retVal; +} + + +- (float)splitView:(NSSplitView *)sender constrainMinCoordinate:(float)proposedCoord ofSubviewAt:(int)offset +{ + const int kMinimumContainerSplitWidth = 150; + float retVal = proposedCoord; + if ( sender == mContainersSplit ) + retVal = kMinimumContainerSplitWidth; + return retVal; +} + + +@end diff --git a/camino/src/bookmarks/BookmarksClient.h b/camino/src/bookmarks/BookmarksClient.h new file mode 100644 index 000000000000..ec23ce95d628 --- /dev/null +++ b/camino/src/bookmarks/BookmarksClient.h @@ -0,0 +1,63 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** +* Version: NPL 1.1/GPL 2.0/LGPL 2.1 +* +* The contents of this file are subject to the Netscape Public License +* Version 1.1 (the "License"); you may not use this file except in +* compliance with the License. You may obtain a copy of the License at +* http://www.mozilla.org/NPL/ +* +* Software distributed under the License is distributed on an "AS IS" basis, +* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +* for the specific language governing rights and limitations under the +* License. +* +* The Original Code is mozilla.org code. +* +* The Initial Developer of the Original Code is +* Netscape Communications Corporation. +* Portions created by the Initial Developer are Copyright (C) 2002 +* the Initial Developer. All Rights Reserved. +* +* Contributor(s): +* David Haas +* +* +* Alternatively, the contents of this file may be used under the terms of +* either the GNU General Public License Version 2 or later (the "GPL"), or +* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +* in which case the provisions of the GPL or the LGPL are applicable instead +* of those above. If you wish to allow use of your version of this file only +* under the terms of either the GPL or the LGPL, and not to allow others to +* use your version of this file under the terms of the NPL, indicate your +* decision by deleting the provisions above and replace them with the notice +* and other provisions required by the GPL or the LGPL. If you do not delete +* the provisions above, a recipient may use your version of this file under +* the terms of any one of the NPL, the GPL or the LGPL. +* +* ***** END LICENSE BLOCK ***** */ +#import + +@protocol BookmarksClient + +- (void)bookmarkAdded:(NSNotification *)note; +- (void)bookmarkRemoved:(NSNotification *)note; +- (void)bookmarkChanged:(NSNotification *)note; + +@end + +// Notification keys +// defined in Bookmark.h +extern NSString *URLLoadNotification; //object is NSString of URL, userinfo has NSNum of success/fail +extern NSString *URLLoadSuccessKey; //key for bool of load success/fail +// defined in BookmarkFolder.h +extern NSString* BookmarkFolderAdditionNotification; //self is obj, userinfo has added item/index +extern NSString* BookmarkFolderDeletionNotification; //self is obj, userinfo dict has removed item +extern NSString* BookmarkFolderChildKey;//key for added/removed object in userinfo dict +extern NSString* BookmarkFolderChildIndexKey; // key for added object index in userinfo dict +extern NSString* BookmarkFolderDockMenuChangeNotificaton; //self is NEW dock menu OR nil +// Defined in BookmarkItem.h +extern NSString *BookmarkItemChangedNotification; //no userinfo, self is object + + + diff --git a/camino/src/bookmarks/KindaSmartFolderManager.h b/camino/src/bookmarks/KindaSmartFolderManager.h new file mode 100644 index 000000000000..d4f17d4a777c --- /dev/null +++ b/camino/src/bookmarks/KindaSmartFolderManager.h @@ -0,0 +1,61 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** +* Version: NPL 1.1/GPL 2.0/LGPL 2.1 +* +* The contents of this file are subject to the Netscape Public License +* Version 1.1 (the "License"); you may not use this file except in +* compliance with the License. You may obtain a copy of the License at +* http://www.mozilla.org/NPL/ +* +* Software distributed under the License is distributed on an "AS IS" basis, +* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +* for the specific language governing rights and limitations under the +* License. +* +* The Original Code is mozilla.org code. +* +* The Initial Developer of the Original Code is +* Netscape Communications Corporation. +* Portions created by the Initial Developer are Copyright (C) 2002 +* the Initial Developer. All Rights Reserved. +* +* Contributor(s): +* David Haas +* +* +* Alternatively, the contents of this file may be used under the terms of +* either the GNU General Public License Version 2 or later (the "GPL"), or +* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +* in which case the provisions of the GPL or the LGPL are applicable instead +* of those above. If you wish to allow use of your version of this file only +* under the terms of either the GPL or the LGPL, and not to allow others to +* use your version of this file under the terms of the NPL, indicate your +* decision by deleting the provisions above and replace them with the notice +* and other provisions required by the GPL or the LGPL. If you do not delete +* the provisions above, a recipient may use your version of this file under +* the terms of any one of the NPL, the GPL or the LGPL. +* +* By the way - this is a total hack. Somebody should really do this in +* a more intelligent manner. +* +* ***** END LICENSE BLOCK ***** */ + +#import + +@class BookmarkFolder; +@class BookmarkManager; + +@interface KindaSmartFolderManager : NSObject { + BookmarkFolder* mBrokenBookmarkFolder; + BookmarkFolder* mUpdatedBookmarkFolder; + BookmarkFolder* mTop10Folder; + BookmarkFolder* mAddressBookFolder; + BookmarkFolder* mRendezvousFolder; + id mAddressBookManager; + unsigned mFewestVisits; +} + +-(id)initWithBookmarkManager:(BookmarkManager *)manager; +-(void)postStartupInitialization:(BookmarkManager *)manager; + +@end diff --git a/camino/src/bookmarks/KindaSmartFolderManager.mm b/camino/src/bookmarks/KindaSmartFolderManager.mm new file mode 100644 index 000000000000..1e3654e26697 --- /dev/null +++ b/camino/src/bookmarks/KindaSmartFolderManager.mm @@ -0,0 +1,301 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** +* Version: NPL 1.1/GPL 2.0/LGPL 2.1 +* +* The contents of this file are subject to the Netscape Public License +* Version 1.1 (the "License"); you may not use this file except in +* compliance with the License. You may obtain a copy of the License at +* http://www.mozilla.org/NPL/ +* +* Software distributed under the License is distributed on an "AS IS" basis, +* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +* for the specific language governing rights and limitations under the +* License. +* +* The Original Code is mozilla.org code. +* +* The Initial Developer of the Original Code is +* Netscape Communications Corporation. +* Portions created by the Initial Developer are Copyright (C) 2002 +* the Initial Developer. All Rights Reserved. +* +* Contributor(s): +* David Haas +* +* +* Alternatively, the contents of this file may be used under the terms of +* either the GNU General Public License Version 2 or later (the "GPL"), or +* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +* in which case the provisions of the GPL or the LGPL are applicable instead +* of those above. If you wish to allow use of your version of this file only +* under the terms of either the GPL or the LGPL, and not to allow others to +* use your version of this file under the terms of the NPL, indicate your +* decision by deleting the provisions above and replace them with the notice +* and other provisions required by the GPL or the LGPL. If you do not delete +* the provisions above, a recipient may use your version of this file under +* the terms of any one of the NPL, the GPL or the LGPL. +* +* By the way - this is a total hack. Somebody should really do this in +* a more intelligent manner. +* +* ***** END LICENSE BLOCK ***** */ +#import "KindaSmartFolderManager.h" +#import "BookmarkFolder.h" +#import "Bookmark.h" +#import "BookmarkManager.h" +#import "NetworkServices.h" +#import "BookmarksClient.h" + +@interface KindaSmartFolderManager (Private) +-(void)addBookmark:(Bookmark *)aBookmark toSmartFolder:(BookmarkFolder *)aFolder; +-(void)removeBookmark:(Bookmark *)aBookmark fromSmartFolder:(BookmarkFolder *)aFolder; +-(void)checkForNewTop10:(Bookmark *)aBookmark; +-(void)setupAddressBook; +-(void)rebuildTop10List; +@end + +@implementation KindaSmartFolderManager + +-(id)initWithBookmarkManager:(BookmarkManager *)manager +{ + if ((self = [super init])) { + // retain all our smart folders, just to be safe + mBrokenBookmarkFolder = [[manager brokenLinkFolder] retain]; + mTop10Folder = [[manager top10Folder] retain]; + mAddressBookFolder = [[manager addressBookFolder] retain]; + mRendezvousFolder = [[manager rendezvousFolder] retain]; + mFewestVisits = 0; + // client notifications + NSNotificationCenter *nc = [NSNotificationCenter defaultCenter]; + [nc addObserver:self selector:@selector(bookmarkRemoved:) name:BookmarkFolderDeletionNotification object:nil]; + [nc addObserver:self selector:@selector(bookmarkChanged:) name:BookmarkItemChangedNotification object:nil]; + } + return self; +} + +-(void) dealloc +{ + [[NSNotificationCenter defaultCenter] removeObserver:self]; + [mBrokenBookmarkFolder release]; + [mTop10Folder release]; + [mAddressBookFolder release]; + [mRendezvousFolder release]; + [super dealloc]; +} + +-(void)postStartupInitialization:(BookmarkManager *)manager +{ + // register for Rendezvous - if we need to + if (mRendezvousFolder) { + [NetworkServices sharedNetworkServices]; + NSNotificationCenter *nc = [NSNotificationCenter defaultCenter]; + [nc addObserver:self selector:@selector(availableServicesChanged:) name:NetworkServicesAvailableServicesChanged object:nil]; + [nc addObserver:self selector:@selector(serviceResolved:) name:NetworkServicesResolutionSuccess object:nil]; + } + if (mAddressBookFolder) + [self setupAddressBook]; + // get top 10 list started + NSArray *bookmarkArray = [[manager rootBookmarks] allChildBookmarks]; + unsigned i, j = [bookmarkArray count]; + for (i=0; i < j; i++) { + Bookmark *aBookmark = [bookmarkArray objectAtIndex:i]; + [self checkForNewTop10:aBookmark]; + if ([aBookmark isSick]) + [self addBookmark:aBookmark toSmartFolder:mBrokenBookmarkFolder]; + } +} + +// when 10.1 support is dropped, most of "init" method of AddressBookManager goes here. +// we'd also need to add that class' fillAddressBook method to this class +-(void) setupAddressBook +{ + NSBundle *appBundle = [NSBundle mainBundle]; + NSString *addressBookManagerBundlePath = [[appBundle resourcePath] stringByAppendingPathComponent:@"AddressBookManager.bundle"]; + NSBundle *addressBookBundle = [NSBundle bundleWithPath:addressBookManagerBundlePath]; + Class principalClass = [addressBookBundle principalClass]; + mAddressBookManager = [[principalClass alloc] initWithFolder:mAddressBookFolder]; + if (mAddressBookManager) + [mAddressBookFolder release]; +} + +// +// flush top 10 list & rebuild from scratch +// +-(void)rebuildTop10List +{ + unsigned i, count = [mTop10Folder count]; + for (i=0;i < count;i++) + [mTop10Folder deleteFromSmartFolderChildAtIndex:0]; + mFewestVisits = 0; + // get top 10 list started + BookmarkManager *manager = [BookmarkManager sharedBookmarkManager]; + NSArray *bookmarkArray = [[manager rootBookmarks] allChildBookmarks]; + count = [bookmarkArray count]; + for (i=0; i < count; i++) { + Bookmark *aBookmark = [bookmarkArray objectAtIndex:i]; + [self checkForNewTop10:aBookmark]; + } +} + + +-(void)checkForNewTop10:(Bookmark *)aBookmark +{ + unsigned smallVisit = [aBookmark numberOfVisits]; + if (smallVisit == 0) { + if ([mTop10Folder indexOfObjectIdenticalTo:aBookmark] != NSNotFound) + //whoops. we just cleared the visits on a top 10 item. rebuild from scratch. + [self rebuildTop10List]; + return; + } + if (([mTop10Folder indexOfObjectIdenticalTo:aBookmark] != NSNotFound)) + return; + // cycle through list of children + // find item with fewest visits, mark it for destruction + // if the URL from the new bookmark is already present in the + // list, we bail out + NSMutableArray *childArray = [mTop10Folder childArray]; + unsigned i, kidVisit, count = [childArray count]; //j should be 10 + if ((count >=10) && (smallVisit < mFewestVisits)) + return; + Bookmark *aKid = nil; + NSString *newURL = [aBookmark url]; + int doomedKidIndex = -1; + for (i=0; i< count; i++) { + aKid = [childArray objectAtIndex:i]; + if ([newURL isEqualToString:[aKid url]]) + return; + kidVisit = [aKid numberOfVisits]; + if (kidVisit == mFewestVisits) + doomedKidIndex = i; + else if (smallVisit > kidVisit) + smallVisit = kidVisit; + } + if ((doomedKidIndex != -1) && (count >= 10)) + [mTop10Folder deleteFromSmartFolderChildAtIndex:doomedKidIndex]; + [mTop10Folder insertIntoSmartFolderChild:aBookmark]; + mFewestVisits = smallVisit; +} + + +// +// if we don't already have it, add it +// +-(void)addBookmark:(Bookmark *)aBookmark toSmartFolder:(BookmarkFolder *)aFolder +{ + unsigned index = [aFolder indexOfObjectIdenticalTo:aBookmark]; + if (index == NSNotFound) + [aFolder insertIntoSmartFolderChild:aBookmark]; +} +// +// if we have this item, remove it +// +-(void)removeBookmark:(Bookmark *)anItem fromSmartFolder:(BookmarkFolder *)aFolder +{ + unsigned index = [aFolder indexOfObjectIdenticalTo:anItem]; + if (index != NSNotFound) + [aFolder deleteFromSmartFolderChildAtIndex:index]; +} + +#pragma mark - + +static int SortByProtocolAndName(NSDictionary* item1, NSDictionary* item2, void *context) +{ + NSComparisonResult protocolCompare = [[item1 objectForKey:@"name"] compare:[item2 objectForKey:@"name"] options:NSCaseInsensitiveSearch]; + if (protocolCompare != NSOrderedSame) + return protocolCompare; + + return [[item1 objectForKey:@"protocol"] compare:[item2 objectForKey:@"protocol"] options:NSCaseInsensitiveSearch]; +} + +- (void)availableServicesChanged:(NSNotification *)note +{ + // empty the rendezvous folder + unsigned i, count = [mRendezvousFolder count]; + for (i=0; i < count; i++) + [mRendezvousFolder deleteChild:[mRendezvousFolder objectAtIndex:0]]; + NetworkServices *netserv = [note object]; + NSEnumerator* keysEnumerator = [netserv serviceEnumerator]; + NSMutableArray* servicesArray = [[NSMutableArray alloc] initWithCapacity:10]; + id key; + while ((key = [keysEnumerator nextObject])) { + NSDictionary* serviceDict = [NSDictionary dictionaryWithObjectsAndKeys: + key, @"id", + [netserv serviceName:[key intValue]], @"name", + [netserv serviceProtocol:[key intValue]], @"protocol", + nil]; + [servicesArray addObject:serviceDict]; + } + if ([servicesArray count] != 0) { + // sort on protocol, then name + [servicesArray sortUsingFunction:SortByProtocolAndName context:NULL]; + unsigned count = [servicesArray count]; + for (unsigned int i = 0; i < count; i ++) + { + NSDictionary* serviceDict = [servicesArray objectAtIndex:i]; + NSString* itemName = [[serviceDict objectForKey:@"name"] stringByAppendingString:NSLocalizedString([serviceDict objectForKey:@"protocol"], @"")]; + Bookmark *aBookmark = [mRendezvousFolder addBookmark]; + [aBookmark setTitle:itemName]; + [aBookmark setParent:[serviceDict objectForKey:@"id"]]; + } + } + [servicesArray release]; +} + +- (void)serviceResolved:(NSNotification *)note +{ + NSDictionary *dict = [note userInfo]; + id aClient = [dict objectForKey:NetworkServicesClientKey]; + if ([aClient isKindOfClass:[Bookmark class]]) { + Bookmark *aKid; + NSEnumerator* enumerator = [[mRendezvousFolder childArray] objectEnumerator]; + while ((aKid = [enumerator nextObject])) { + if (aKid == aClient) + { + [aClient setUrl:[dict objectForKey:NetworkServicesResolvedURLKey]]; + [aClient setParent:mRendezvousFolder]; + } + } + } + return; +} +- (void)serviceResolutionFailed:(NSNotification *)note +{ + return; +} + + +#pragma mark - +// +// BookmarkClient protocol +// +- (void)bookmarkAdded:(NSNotification *)note +{ +} + +// +// need to tell top 10 list, broken items +// +- (void)bookmarkRemoved:(NSNotification *)note +{ + BookmarkItem *anItem = [[note userInfo] objectForKey:BookmarkFolderChildKey]; + if (![anItem parent] && [anItem isKindOfClass:[Bookmark class]]) { + [self removeBookmark:anItem fromSmartFolder:mBrokenBookmarkFolder]; + [self removeBookmark:anItem fromSmartFolder:mTop10Folder]; + } +} + +- (void)bookmarkChanged:(NSNotification *)note +{ + BookmarkItem *anItem = [note object]; + if ([anItem isKindOfClass:[Bookmark class]]) { + [self checkForNewTop10:anItem]; + // see what the status is + if ([(Bookmark *)anItem isSick]) + [self addBookmark:anItem toSmartFolder:mBrokenBookmarkFolder]; + else { + [self removeBookmark:anItem fromSmartFolder:mBrokenBookmarkFolder]; + } + } +} + +@end diff --git a/camino/src/browser/AutoCompleteTextField.h b/camino/src/browser/AutoCompleteTextField.h index bb076bbfaaa7..3c8c1f9f4eff 100644 --- a/camino/src/browser/AutoCompleteTextField.h +++ b/camino/src/browser/AutoCompleteTextField.h @@ -28,7 +28,7 @@ #include "nsIAutoCompleteResults.h" #include "nsIAutoCompleteListener.h" -@class BookmarksOutlineView, PageProxyIcon; +@class PageProxyIcon; @interface AutoCompleteTextField : NSTextField { diff --git a/camino/src/browser/AutoCompleteTextField.mm b/camino/src/browser/AutoCompleteTextField.mm index e46b3527a602..e41a7f124350 100644 --- a/camino/src/browser/AutoCompleteTextField.mm +++ b/camino/src/browser/AutoCompleteTextField.mm @@ -509,7 +509,7 @@ NS_IMPL_ISUPPORTS1(AutoCompleteListener, nsIAutoCompleteListener) if (url) { [self clearResults]; NSTextView *fieldEditor = [self fieldEditor]; - [[fieldEditor undoManager] removeAllActions]; + [[fieldEditor undoManager] removeAllActionsWithTarget:self]; [fieldEditor setString:url]; [fieldEditor selectAll:self]; } @@ -679,7 +679,8 @@ NS_IMPL_ISUPPORTS1(AutoCompleteListener, nsIAutoCompleteListener) - (void)controlTextDidEndEditing:(NSNotification *)aNote { [self clearResults]; - [[[[aNote userInfo] objectForKey:@"NSFieldEditor"] undoManager] removeAllActions]; + id fieldEditor = [[aNote userInfo] objectForKey:@"NSFieldEditor"]; + [[fieldEditor undoManager] removeAllActionsWithTarget:fieldEditor]; } - (BOOL)control:(NSControl *)control textView:(NSTextView *)textView doCommandBySelector:(SEL)command diff --git a/camino/src/browser/BrowserContentViews.h b/camino/src/browser/BrowserContentViews.h index 46a970317021..3973a0d5e330 100644 --- a/camino/src/browser/BrowserContentViews.h +++ b/camino/src/browser/BrowserContentViews.h @@ -38,12 +38,12 @@ #import -@class BookmarksToolbar; +@class BookmarkToolbar; @class BrowserTabView; @interface BrowserContentView : NSView { - IBOutlet BookmarksToolbar *mBookmarksToolbar; + IBOutlet BookmarkToolbar *mBookmarksToolbar; IBOutlet NSView *mBrowserContainerView; // manages tabs and web content IBOutlet NSView *mBookmarkManagerView; // swapped in and out by activating bm manager, replacing browser container IBOutlet NSView *mStatusBar; diff --git a/camino/src/browser/BrowserContentViews.mm b/camino/src/browser/BrowserContentViews.mm index 804437240282..7572bfaae138 100644 --- a/camino/src/browser/BrowserContentViews.mm +++ b/camino/src/browser/BrowserContentViews.mm @@ -38,7 +38,7 @@ #import "BrowserContentViews.h" -#import "BookmarksToolbar.h" +#import "BookmarkToolbar.h" #import "BrowserTabView.h" @@ -65,7 +65,7 @@ | _________________________________________________________________ | BrowserContentView | | ____________________________________________________________ | - | | BookmarksToolbar | | + | | BookmarkToolbar | | | |___________________________________________________________| | | | | ____________________________________________________________ | diff --git a/camino/src/browser/BrowserTabView.mm b/camino/src/browser/BrowserTabView.mm index 92c25723f236..453cad8fd721 100644 --- a/camino/src/browser/BrowserTabView.mm +++ b/camino/src/browser/BrowserTabView.mm @@ -39,11 +39,14 @@ #import "NSString+Utils.h" #import "NSPasteboard+Utils.h" +#import "NSArray+Utils.h" #import "BrowserTabView.h" -#import "BookmarksService.h" #import "BrowserWrapper.h" #import "BrowserWindowController.h" +#import "BookmarkFolder.h" +#import "Bookmark.h" +#import "BookmarkToolbar.h" ////////////////////////// // NEEDS IMPLEMENTED : Implement drag tracking for moving tabs around. @@ -228,7 +231,7 @@ if (tabVisibilityChanged) { - [[[[self window] windowController] bookmarksToolbar] setDrawBottomBorder:!tabsVisible]; + [[[[self window] windowController] bookmarkToolbar] setDrawBottomBorder:!tabsVisible]; // tell the superview to resize its subviews [[self superview] resizeSubviewsWithOldSize:[[self superview] frame].size]; @@ -363,27 +366,30 @@ const float kTabsInvisibleTopGap = -7.0; // space removed to push tab content if ([pasteBoardTypes containsObject: @"MozBookmarkType"]) { - NSArray* contentIds = [[sender draggingPasteboard] propertyListForType: @"MozBookmarkType"]; - if (contentIds) + NSArray* draggedItems = [NSArray pointerArrayFromDataArrayForMozBookmarkDrop:[[sender draggingPasteboard] propertyListForType: @"MozBookmarkType"]]; + if (draggedItems) { - BookmarksManager* bmManager = [BookmarksManager sharedBookmarksManager]; - - // drag type is chimera bookmarks - for (unsigned int i = 0; i < [contentIds count]; ++i) - { - BookmarkItem* item = [bmManager getWrapperForNumber:[contentIds objectAtIndex:i]]; - if ([item isGroup]) - { - NSArray* groupURLs = [bmManager getBookmarkGroupURIs:item]; - [[[self window] windowController] openTabGroup:groupURLs replaceExistingTabs:YES]; + id aBookmark; + if ([draggedItems count] == 1) { + aBookmark = [draggedItems objectAtIndex:0]; + if ([aBookmark isKindOfClass:[Bookmark class]]) + return [self handleDropOnTab:overTabViewItem overContent:overContentArea withURL:[aBookmark url]]; + else if ([aBookmark isKindOfClass:[BookmarkFolder class]]) { + [[[self window] windowController] openTabGroup:[aBookmark childURLs] replaceExistingTabs:YES]; + return YES; } - else - { - // handle multiple items? - return [self handleDropOnTab:overTabViewItem overContent:overContentArea withURL:[item url]]; + } else if ([draggedItems count] > 1) { + NSMutableArray *urlArray = [NSMutableArray arrayWithCapacity:[draggedItems count]]; + NSEnumerator *enumerator = [draggedItems objectEnumerator]; + while ((aBookmark = [enumerator nextObject])) { + if ([aBookmark isKindOfClass:[Bookmark class]]) + [urlArray addObject:[aBookmark url]]; + else if ([aBookmark isKindOfClass:[BookmarkFolder class]]) + [urlArray addObjectsFromArray:[aBookmark childURLs]]; } - - } // for each item + [[[self window] windowController] openTabGroup:urlArray replaceExistingTabs:YES]; + return YES; + } } } else if ([pasteBoardTypes containsObject: @"MozURLType"]) diff --git a/camino/src/browser/BrowserTabViewItem.mm b/camino/src/browser/BrowserTabViewItem.mm index f857b90e016e..f12234c6b8f2 100644 --- a/camino/src/browser/BrowserTabViewItem.mm +++ b/camino/src/browser/BrowserTabViewItem.mm @@ -293,7 +293,7 @@ NSWindowController *windowController = [[[mTabViewItem view] window] windowController]; if ([windowController isMemberOfClass:[BrowserWindowController class]]) { - if (sender == [windowController proxyIconView]) + if (sender == [(BrowserWindowController *)windowController proxyIconView]) return NO; } @@ -421,7 +421,7 @@ { // set the tag of every menu item to the tab view item's tag, // so that the target of the menu commands know which one they apply to. - for (unsigned int i = 0; i < [aMenu numberOfItems]; i ++) + for (int i = 0; i < [aMenu numberOfItems]; i ++) [[aMenu itemAtIndex:i] setTag:[mTabViewItem tag]]; [super setMenu:aMenu]; diff --git a/camino/src/browser/BrowserWindowController.h b/camino/src/browser/BrowserWindowController.h index 9a14af772350..da69cb627f30 100644 --- a/camino/src/browser/BrowserWindowController.h +++ b/camino/src/browser/BrowserWindowController.h @@ -38,14 +38,12 @@ #import #import "BrowserWrapper.h" #import "Find.h" -#import "BookmarksToolbar.h" class nsIURIFixup; class nsIBrowserHistory; class nsIDOMEvent; class nsIDOMNode; -@class BookmarksController; // // ThrobberHandler @@ -87,7 +85,8 @@ typedef enum } ENewTabContents; -@class BookmarksDataSource; +@class BookmarkViewController; +@class BookmarkToolbar; @class HistoryDataSource; @class BrowserTabView; @class PageProxyIcon; @@ -99,9 +98,6 @@ typedef enum @interface BrowserWindowController : NSWindowController { IBOutlet BrowserTabView* mTabBrowser; - IBOutlet NSDrawer* mSidebarDrawer; - IBOutlet NSTabView* mSidebarTabView; - IBOutlet NSTabView* mSidebarSourceTabView; IBOutlet NSView* mLocationToolbarView; IBOutlet AutoCompleteTextField* mURLBar; IBOutlet NSTextField* mStatus; @@ -114,12 +110,9 @@ typedef enum IBOutlet PageProxyIcon* mProxyIcon; IBOutlet BrowserContentView* mContentView; - IBOutlet BookmarksDataSource* mSidebarBookmarksDataSource; + IBOutlet BookmarkViewController* mBookmarkViewController; + IBOutlet BookmarkToolbar* mPersonalToolbar; IBOutlet HistoryDataSource* mHistoryDataSource; - IBOutlet BookmarksController* mBookmarksController; - - IBOutlet BookmarksToolbar* mPersonalToolbar; - IBOutlet NSWindow* mAddBookmarkSheetWindow; IBOutlet NSTextField* mAddBookmarkTitleField; IBOutlet NSPopUpButton* mAddBookmarkFolderField; @@ -158,10 +151,6 @@ typedef enum BOOL mShouldAutosave; BOOL mShouldLoadHomePage; - BOOL mDrawerCachedFrame; - NSRect mCachedFrameBeforeDrawerOpen; // This is used by the drawer to figure out if the window should - // be returned to its original position when the drawer closes. - NSRect mCachedFrameAfterDrawerOpen; unsigned int mChromeMask; // Indicates which parts of the window to show (e.g., don't show toolbars) @@ -171,7 +160,7 @@ typedef enum nsIDOMNode* mContextMenuNode; // Cached bookmark ds used when adding through a sheet - BookmarksDataSource* mCachedBMDS; + BookmarkViewController* mCachedBMVC; // Throbber state variables. ThrobberHandler* mThrobberHandler; @@ -229,7 +218,7 @@ typedef enum - (IBAction)cancelAddBookmarkSheet:(id)sender; - (IBAction)endAddBookmarkSheet:(id)sender; -- (void)cacheBookmarkDS:(BookmarksDataSource*)aDS; +- (void)cacheBookmarkVC:(BookmarkViewController *)aDS; - (NSSize)windowWillResize:(NSWindow *)sender toSize:(NSSize)proposedFrameSize; @@ -259,7 +248,6 @@ typedef enum - (void)addBookmarkExtended: (BOOL)aIsFromMenu isFolder:(BOOL)aIsFolder URL:(NSString*)aURL title:(NSString*)aTitle; - (IBAction)manageBookmarks: (id)aSender; - (IBAction)manageHistory: (id)aSender; -- (void)importBookmarks: (NSString*)aURLSpec; - (IBAction)toggleSidebar:(id)aSender; - (BOOL)bookmarksAreVisible:(BOOL)inRequireSelection; @@ -290,7 +278,7 @@ typedef enum - (IBAction)frameToThisWindow:(id)sender; - (void)openNewWindowWithURL: (NSString*)aURLSpec referrer:(NSString*)aReferrer loadInBackground: (BOOL)aLoadInBG; -- (void)openNewWindowWithGroup: (nsIContent*)aFolderContent loadInBackground: (BOOL)aLoadInBG; +- (void)openNewWindowWithGroupURLs: (NSArray *)urlArray loadInBackground: (BOOL)aLoadInBG; - (void)openNewTabWithURL: (NSString*)aURLSpec referrer: (NSString*)aReferrer loadInBackground: (BOOL)aLoadInBG; - (void)openTabGroup:(NSArray*)urlArray replaceExistingTabs:(BOOL)replaceExisting; @@ -334,7 +322,7 @@ typedef enum - (IBAction)copyImage:(id)sender; - (IBAction)copyImageLocation:(id)sender; -- (BookmarksToolbar*) bookmarksToolbar; +- (BookmarkToolbar*) bookmarkToolbar; - (NSProgressIndicator*) progressIndicator; - (void) showProgressIndicator; @@ -357,14 +345,11 @@ typedef enum // cache the toolbar defaults we parse from a plist + (NSArray*) toolbarDefaults; -// Accessor to get the sidebar drawer -- (NSDrawer *)sidebarDrawer; - // Accessor to get the proxy icon view - (PageProxyIcon *)proxyIconView; // Accessor for the bm data source -- (BookmarksDataSource*)bookmarksDataSource; +- (BookmarkViewController *)bookmarkViewController; - (void)toggleBookmarkManager:(id)sender; - (void)ensureBrowserVisible:(id)sender; diff --git a/camino/src/browser/BrowserWindowController.mm b/camino/src/browser/BrowserWindowController.mm index 307a5f0e48a2..c6be76e30e72 100644 --- a/camino/src/browser/BrowserWindowController.mm +++ b/camino/src/browser/BrowserWindowController.mm @@ -40,19 +40,22 @@ #import "BrowserWindowController.h" #import "BrowserWindow.h" +#import "BookmarkToolbar.h" +#import "BookmarkViewController.h" +#import "BookmarkManager.h" + #import "BrowserContentViews.h" #import "BrowserWrapper.h" #import "PreferenceManager.h" -#import "BookmarksDataSource.h" #import "HistoryDataSource.h" #import "BrowserTabView.h" #import "UserDefaults.h" #import "PageProxyIcon.h" #import "AutoCompleteTextField.h" -#import "BookmarksController.h" #import "SearchTextField.h" #import "SearchTextFieldCell.h" #import "STFPopUpButtonCell.h" +#import "MainController.h" #include "nsIWebNavigation.h" #include "nsIDOMDocument.h" @@ -121,6 +124,7 @@ static NSArray* sToolbarDefaults = nil; @interface AutoCompleteTextFieldEditor : NSTextView { NSFont* mDefaultFont; // will be needed if editing empty field + NSUndoManager *mUndoManager; //we handle our own undo to avoid stomping on bookmark undo } - (id)initWithFrame:(NSRect)bounds defaultFont:(NSFont*)defaultFont; @end @@ -132,10 +136,18 @@ static NSArray* sToolbarDefaults = nil; { if ((self = [super initWithFrame:bounds])) { mDefaultFont = defaultFont; + mUndoManager = [[NSUndoManager alloc] init]; + [self setDelegate:self]; } return self; } +-(void) dealloc +{ + [mUndoManager release]; + [super dealloc]; +} + -(void)paste:(id)sender { NSPasteboard *pboard = [NSPasteboard generalPasteboard]; @@ -160,6 +172,13 @@ static NSArray* sToolbarDefaults = nil; } } +- (NSUndoManager *)undoManagerForTextView:(NSTextView *)aTextView +{ + if (aTextView == self) + return mUndoManager; + return nil; +} + @end ////////////////////////////////////// @@ -344,8 +363,6 @@ static NSArray* sToolbarDefaults = nil; #if DEBUG NSLog(@"Window will close notification."); #endif - [mSidebarBookmarksDataSource windowClosing]; - [self autosaveWindowFrame]; { // scope... @@ -511,8 +528,6 @@ static NSArray* sToolbarDefaults = nil; mustResizeChrome = YES; mInitialized = YES; - - mDrawerCachedFrame = NO; [[self window] setAcceptsMouseMovedEvents: YES]; @@ -542,11 +557,6 @@ static NSArray* sToolbarDefaults = nil; mPendingURL = mPendingReferrer = nil; } -#if USE_DRAWER_FOR_BOOKMARKS - [mSidebarDrawer setDelegate: self]; - [self setupSidebarTabs]; -#endif - if ( mChromeMask && !(mChromeMask & nsIWebBrowserChrome::CHROME_PERSONAL_TOOLBAR) ) { // remove the personal toolbar and adjust the content area upwards. Removing it // from the parent view releases it, so we have to clear out the member var. @@ -557,7 +567,7 @@ static NSArray* sToolbarDefaults = nil; } else { - [mPersonalToolbar initializeToolbar]; + [mPersonalToolbar buildButtonList]; if (![self shouldShowBookmarkToolbar]) [mPersonalToolbar showBookmarksToolbar:NO]; @@ -590,7 +600,7 @@ static NSArray* sToolbarDefaults = nil; } // let the in-window bookmark controller finish up some initialization - [mBookmarksController windowDidLoad]; + [mBookmarkViewController windowDidLoad]; } - (NSSize)windowWillResize:(NSWindow *)sender toSize:(NSSize)proposedFrameSize @@ -604,7 +614,7 @@ static NSArray* sToolbarDefaults = nil; #if 0 - (void)drawerWillOpen: (NSNotification*)aNotification { - [mSidebarBookmarksDataSource ensureBookmarks]; + [mBookmarkViewController ensureBookmarks]; if ([[[mSidebarTabView selectedTabViewItem] identifier] isEqual:@"historySidebarCHIconTabViewItem"]) { [mHistoryDataSource ensureDataSourceLoaded]; @@ -1100,19 +1110,19 @@ static NSArray* sToolbarDefaults = nil; { [mAddBookmarkSheetWindow orderOut:self]; [NSApp endSheet:mAddBookmarkSheetWindow returnCode:0]; - [mCachedBMDS endAddBookmark: 0]; + [mCachedBMVC endAddBookmark: 0]; } -(IBAction)endAddBookmarkSheet:(id)sender { [mAddBookmarkSheetWindow orderOut:self]; [NSApp endSheet:mAddBookmarkSheetWindow returnCode:0]; - [mCachedBMDS endAddBookmark: 1]; + [mCachedBMVC endAddBookmark: 1]; } -- (void)cacheBookmarkDS:(BookmarksDataSource*)aDS +- (void)cacheBookmarkVC:(BookmarkViewController *)aVC { - mCachedBMDS = aDS; + mCachedBMVC = aVC; } -(IBAction)manageBookmarks: (id)aSender @@ -1120,7 +1130,7 @@ static NSArray* sToolbarDefaults = nil; if ( ![mContentView isBookmarkManagerVisible] ) [self toggleBookmarkManager: self]; - [mBookmarksController selectContainer:kBookmarksMenuContainer]; + [mBookmarkViewController selectContainer:kBookmarkMenuContainerIndex]; } -(IBAction)manageHistory: (id)aSender @@ -1128,20 +1138,7 @@ static NSArray* sToolbarDefaults = nil; if ( ![mContentView isBookmarkManagerVisible] ) [self toggleBookmarkManager: self]; - [mBookmarksController selectContainer:kHistoryContainer]; -} - -- (void)importBookmarks: (NSString*)aURLSpec -{ - // Open the bookmarks sidebar. - [self manageBookmarks: self]; - - // Now do the importing. - BrowserWrapper* newView = [[[BrowserWrapper alloc] initWithTab: nil andWindow: [self window]] autorelease]; - [newView setFrame: NSZeroRect]; - [newView setIsBookmarksImport: YES]; - [[[self window] contentView] addSubview: newView]; - [newView loadURI:aURLSpec referrer: nil flags:NSLoadFlagsNone activate:NO]; + [mBookmarkViewController selectContainer:kHistoryContainerIndex]; } - (IBAction)goToLocationFromToolbarURLField:(id)sender @@ -1150,7 +1147,7 @@ static NSArray* sToolbarDefaults = nil; NSString *theURL = [[sender stringValue] stringByTrimmingWhitespace]; // look for bookmarks keywords match - NSArray *resolvedURLs = [[BookmarksManager sharedBookmarksManager] resolveBookmarksKeyword:theURL]; + NSArray *resolvedURLs = [[BookmarkManager sharedBookmarkManager] resolveBookmarksKeyword:theURL]; NSString* resolvedURL = nil; if ([resolvedURLs count] == 1) @@ -1466,14 +1463,13 @@ static NSArray* sToolbarDefaults = nil; - (void)addBookmarkExtended: (BOOL)aIsFromMenu isFolder:(BOOL)aIsFolder URL:(NSString*)aURL title:(NSString*)aTitle { - [mSidebarBookmarksDataSource ensureBookmarks]; + [mBookmarkViewController ensureBookmarks]; BOOL useSel = aIsFromMenu; if (aIsFromMenu) { // Use selection only if the sidebar is open and the bookmarks panel is displaying. useSel = [self bookmarksAreVisible:NO]; } - - [mSidebarBookmarksDataSource addBookmark: self useSelection: useSel isFolder: aIsFolder URL:aURL title:aTitle]; + [mBookmarkViewController addItem: self useSelection: useSel isFolder: aIsFolder URL:aURL title:aTitle]; } - (BOOL)bookmarksAreVisible:(BOOL)inRequireSelection @@ -1481,7 +1477,7 @@ static NSArray* sToolbarDefaults = nil; BOOL bookmarksShowing = [mContentView isBookmarkManagerVisible]; if (inRequireSelection) - bookmarksShowing &= ([mSidebarBookmarksDataSource haveSelectedRow]); + bookmarksShowing &= ([mBookmarkViewController haveSelectedRow]); return bookmarksShowing; } @@ -1849,21 +1845,6 @@ static NSArray* sToolbarDefaults = nil; } } -- (void)tabView:(NSTabView *)tabView willSelectTabViewItem:(NSTabViewItem *)tabViewItem -{ - // we'll get called for browser tab views as well. ignore any calls coming from - // there, we're only interested in the sidebar. - if (tabView != mSidebarTabView) - return; - - if ([[tabViewItem identifier] isEqual:@"historySidebarCHIconTabViewItem"]) { - [mHistoryDataSource ensureDataSourceLoaded]; - [mHistoryDataSource enableObserver]; - } - else - [mHistoryDataSource disableObserver]; -} - - (void)tabView:(NSTabView *)aTabView didSelectTabViewItem:(NSTabViewItem *)aTabViewItem { // we'll get called for the sidebar tabs as well. ignore any calls coming from @@ -1922,7 +1903,7 @@ static NSArray* sToolbarDefaults = nil; [browser showWindow:self]; } -- (void)openNewWindowWithGroup: (nsIContent*)aFolderContent loadInBackground: (BOOL)aLoadInBG +- (void)openNewWindowWithGroupURLs: (NSArray *)urlArray loadInBackground: (BOOL)aLoadInBG { // Autosave our dimensions before we open a new window. That ensures the size ends up matching. [self autosaveWindowFrame]; @@ -1938,12 +1919,7 @@ static NSArray* sToolbarDefaults = nil; } else [browser showWindow:self]; - - BookmarksManager* bmManager = [BookmarksManager sharedBookmarksManager]; - BookmarkItem* item = [bmManager getWrapperForContent:aFolderContent]; - - NSArray* groupURLs = [bmManager getBookmarkGroupURIs:item]; - [browser openTabGroup:groupURLs replaceExistingTabs:YES]; + [browser openTabGroup:urlArray replaceExistingTabs:YES]; } -(void)openNewTabWithURL: (NSString*)aURLSpec referrer:(NSString*)aReferrer loadInBackground: (BOOL)aLoadInBG @@ -2103,8 +2079,8 @@ static NSArray* sToolbarDefaults = nil; - (void)getInfo:(id)sender { - [mSidebarBookmarksDataSource ensureBookmarks]; - [mSidebarBookmarksDataSource showBookmarkInfo:sender]; + [mBookmarkViewController ensureBookmarks]; + [mBookmarkViewController showBookmarkInfo:sender]; } - (BOOL)canGetInfo @@ -2343,7 +2319,7 @@ static NSArray* sToolbarDefaults = nil; } } -- (BookmarksToolbar*) bookmarksToolbar +- (BookmarkToolbar*) bookmarkToolbar { return mPersonalToolbar; } @@ -2553,19 +2529,15 @@ static NSArray* sToolbarDefaults = nil; [[mBrowserView getBrowserView] setActive:newResponderIsGecko]; } -- (NSDrawer *)sidebarDrawer -{ - return mSidebarDrawer; -} - (PageProxyIcon *)proxyIconView { return mProxyIcon; } -- (BookmarksDataSource*)bookmarksDataSource +- (BookmarkViewController *)bookmarkViewController { - return mSidebarBookmarksDataSource; + return mBookmarkViewController; } - (id)windowWillReturnFieldEditor:(NSWindow *)aWindow toObject:(id)anObject @@ -2582,6 +2554,10 @@ static NSArray* sToolbarDefaults = nil; return nil; } +- (NSUndoManager *)windowWillReturnUndoManager:(NSWindow *)sender +{ + return [[BookmarkManager sharedBookmarkManager] undoManager]; +} - (IBAction)reloadWithNewCharset:(NSString*)charset { @@ -2618,10 +2594,10 @@ static NSArray* sToolbarDefaults = nil; // cancel all pending loads. safari does this, i think we should too [self stopAllPendingLoads]; - [mBookmarksController selectLastContainer]; + [mBookmarkViewController selectLastContainer]; // set focus to appropriate area of bm manager - [mBookmarksController focus]; + [mBookmarkViewController focus]; } else { CHBrowserView* browserView = [mBrowserView getBrowserView]; diff --git a/camino/src/browser/BrowserWrapper.h b/camino/src/browser/BrowserWrapper.h index 5834350d25c1..43bba2c0d97e 100644 --- a/camino/src/browser/BrowserWrapper.h +++ b/camino/src/browser/BrowserWrapper.h @@ -80,8 +80,6 @@ class nsISupportsArray; BOOL mOffline; BOOL mListenersAttached; // We hook up our click and context menu listeners lazily. // If we never become the primary view, we don't bother creating the listeners. - BOOL mIsBookmarksImport; // This view was created for the purpose of importing bookmarks. Upon - // completion, we need to do the import and then destroy ourselves. BOOL mActivateOnLoad; // If set, activate the browser view when loading starts. } @@ -111,8 +109,6 @@ class nsISupportsArray; - (NSWindow*)getNativeWindow; - (NSMenu*)getContextMenu; -- (void)setIsBookmarksImport:(BOOL)aIsImport; - - (void)getTitle:(NSString **)outTitle andHref:(NSString**)outHrefString; // CHBrowserListener messages diff --git a/camino/src/browser/BrowserWrapper.mm b/camino/src/browser/BrowserWrapper.mm index e015d91f11fe..6bb6eda45e3a 100644 --- a/camino/src/browser/BrowserWrapper.mm +++ b/camino/src/browser/BrowserWrapper.mm @@ -40,7 +40,7 @@ #import "PreferenceManager.h" #import "BrowserWrapper.h" #import "BrowserWindowController.h" -#import "BookmarksService.h" +#import "BookmarksClient.h" #import "SiteIconProvider.h" #import "BrowserTabViewItem.h" #import "ToolTip.h" @@ -92,7 +92,6 @@ const NSString* kOfflineNotificationName = @"offlineModeChanged"; { mTabItem = aTab; mWindow = aWindow; - mIsBookmarksImport = NO; return [self initWithFrame: NSZeroRect]; } @@ -388,26 +387,17 @@ const NSString* kOfflineNotificationName = @"offlineModeChanged"; mProgress = 1.0; mIsBusy = NO; - // need to check succeeded here because for a charset-induced reload, - // this can get called initially with a failure code. - if (mIsBookmarksImport && succeeded) - { - nsCOMPtr domWindow; - nsCOMPtr webBrowser = getter_AddRefs([mBrowserView getWebBrowser]); - webBrowser->GetContentDOMWindow(getter_AddRefs(domWindow)); - if (domWindow) - { - nsCOMPtr domDocument; - domWindow->GetDocument(getter_AddRefs(domDocument)); - if (domDocument) - BookmarksService::ImportBookmarks(domDocument); - } - [self windowClosed]; - [self removeFromSuperview]; - } - if (mWindowController) [mWindowController loadingDone]; + // send a little love to the bookmarks + NSString *urlString = nil; + NSString *titleString = nil; + [self getTitle:&titleString andHref:&urlString]; + NSDictionary *userInfo = [NSDictionary dictionaryWithObject:[NSNumber numberWithUnsignedInt:0] forKey:URLLoadSuccessKey]; + if (urlString && ![urlString isEqualToString:@"about:blank"]) { + NSNotification *note = [NSNotification notificationWithName:URLLoadNotification object:urlString userInfo:userInfo]; + [[NSNotificationQueue defaultQueue] enqueueNotification:note postingStyle:NSPostWhenIdle]; + } } - (void)onProgressChange:(int)currentBytes outOf:(int)maxBytes @@ -438,7 +428,7 @@ const NSString* kOfflineNotificationName = @"offlineModeChanged"; // if the favicon uri has changed, fire off favicon load. When it completes, our // imageLoadedNotification selector gets called. if (![faviconURI isEqualToString:mSiteIconURI]) - siteIconLoadInitiated = [faviconProvider loadFavoriteIcon:self forURI:urlSpec withUserData:nil allowNetwork:YES]; + siteIconLoadInitiated = [faviconProvider loadFavoriteIcon:self forURI:urlSpec allowNetwork:YES]; } else { @@ -675,11 +665,6 @@ const NSString* kOfflineNotificationName = @"offlineModeChanged"; *outTitle = [NSString stringWithString:*outHrefString]; } --(void)setIsBookmarksImport:(BOOL)aIsImport -{ - mIsBookmarksImport = aIsImport; -} - - (void)offlineModeChanged: (NSNotification*)aNotification { nsCOMPtr ioService(do_GetService(ioServiceContractID)); @@ -827,10 +812,6 @@ const NSString* kOfflineNotificationName = @"offlineModeChanged"; if (resetTabIcon || ![tabItem tabIcon]) [tabItem setTabIcon:mSiteIconImage isDraggable:tabIconDraggable]; } - - // make sure any bookmark items that use this favicon uri are updated - if (inSiteIcon) - [[BookmarksManager sharedBookmarksManager] updateProxyImage:inSiteIcon forSiteIcon:inSiteIconURI]; } - (void)registerNotificationListener diff --git a/camino/src/browser/PageProxyIcon.mm b/camino/src/browser/PageProxyIcon.mm index 23d5c03c2942..89437fd6596f 100644 --- a/camino/src/browser/PageProxyIcon.mm +++ b/camino/src/browser/PageProxyIcon.mm @@ -23,10 +23,9 @@ #import "NSString+Utils.h" #import "NSPasteboard+Utils.h" - +#import "BrowserWindowController.h" #import "PageProxyIcon.h" -#import "BookmarksService.h" #import "MainController.h" #include "nsCRT.h" diff --git a/camino/src/browser/SiteIconProvider.h b/camino/src/browser/SiteIconProvider.h index 00add5f6bdc2..7d2868f1bb96 100644 --- a/camino/src/browser/SiteIconProvider.h +++ b/camino/src/browser/SiteIconProvider.h @@ -49,6 +49,7 @@ class NeckoCacheHelper; @interface SiteIconProvider : NSObject { NeckoCacheHelper* mMissedIconsCacheHelper; + NSMutableDictionary *mRequestDict; } + (SiteIconProvider*)sharedFavoriteIconProvider; @@ -64,7 +65,7 @@ class NeckoCacheHelper; // This method returns YES if the uri request was dispatched (i.e. if we know // that we've looked for, and failed to find, this icon before). If it returns // YES, then the 'SiteIconLoadNotificationName' notification will be sent out. -// userData is not retained. -- (BOOL)loadFavoriteIcon:(id)sender forURI:(NSString *)inURI withUserData:(id)userData allowNetwork:(BOOL)inAllowNetwork; + +- (BOOL)loadFavoriteIcon:(id)sender forURI:(NSString *)inURI allowNetwork:(BOOL)inAllowNetwork; @end diff --git a/camino/src/browser/SiteIconProvider.mm b/camino/src/browser/SiteIconProvider.mm index ce89f0cf405a..9748fee0a9c1 100644 --- a/camino/src/browser/SiteIconProvider.mm +++ b/camino/src/browser/SiteIconProvider.mm @@ -211,6 +211,7 @@ static nsresult MakeFaviconURIFromURI(const nsAString& inURIString, nsAString& o if ((self = [super init])) { mMissedIconsCacheHelper = new NeckoCacheHelper("Favicon", "Missed"); + mRequestDict = [[NSMutableDictionary alloc] initWithCapacity:5]; nsresult rv = mMissedIconsCacheHelper->Init("MissedIconsCache"); if (NS_FAILED(rv)) { delete mMissedIconsCacheHelper; @@ -224,6 +225,7 @@ static nsresult MakeFaviconURIFromURI(const nsAString& inURIString, nsAString& o - (void)dealloc { delete mMissedIconsCacheHelper; + [mRequestDict release]; [super dealloc]; } @@ -246,7 +248,7 @@ static nsresult MakeFaviconURIFromURI(const nsAString& inURIString, nsAString& o return inCache; } -- (BOOL)loadFavoriteIcon:(id)sender forURI:(NSString *)inURI withUserData:(id)userData allowNetwork:(BOOL)inAllowNetwork +- (BOOL)loadFavoriteIcon:(id)sender forURI:(NSString *)inURI allowNetwork:(BOOL)inAllowNetwork { // look for a favicon nsAutoString uriString; @@ -264,9 +266,10 @@ static nsresult MakeFaviconURIFromURI(const nsAString& inURIString, nsAString& o { return NO; } - + // preserve requesting URI for later notification + [mRequestDict setObject:inURI forKey:faviconString]; RemoteDataProvider* dataProvider = [RemoteDataProvider sharedRemoteDataProvider]; - return [dataProvider loadURI:faviconString forTarget:sender withListener:self withUserData:userData allowNetworking:inAllowNetwork]; + return [dataProvider loadURI:faviconString forTarget:sender withListener:self withUserData:nil allowNetworking:inAllowNetwork]; } #define SITE_ICON_EXPIRATION_SECONDS (60 * 60 * 24 * 7) // 1 week @@ -304,17 +307,23 @@ static nsresult MakeFaviconURIFromURI(const nsAString& inURIString, nsAString& o [faviconImage setScalesWhenResized:YES]; [faviconImage setSize:NSMakeSize(16, 16)]; } + // figure out what URL triggered this favicon request + + NSString *requestURL = [mRequestDict objectForKey:inURI]; + if (!requestURL) + requestURL = [NSString string]; // we always send out the notification, so that clients know // about failed requests NSDictionary* notificationData = [NSDictionary dictionaryWithObjectsAndKeys: inURI, SiteIconLoadURIKey, faviconImage, SiteIconLoadImageKey, // may be nil - userData, SiteIconLoadUserDataKey, + requestURL, SiteIconLoadUserDataKey, nil]; - - [[NSNotificationCenter defaultCenter] postNotificationName: SiteIconLoadNotificationName - object:target userInfo:notificationData]; + NSNotification *note = [NSNotification notificationWithName: SiteIconLoadNotificationName object:target userInfo:notificationData]; + [[NSNotificationQueue defaultQueue] enqueueNotification: note postingStyle:NSPostWhenIdle]; + // cleanup our key holder + [mRequestDict removeObjectForKey:inURI]; } #pragma mark - diff --git a/camino/src/embedding/CHBrowserListener.mm b/camino/src/embedding/CHBrowserListener.mm index 2c2cd1b73c62..088ef209f113 100644 --- a/camino/src/embedding/CHBrowserListener.mm +++ b/camino/src/embedding/CHBrowserListener.mm @@ -45,15 +45,16 @@ #include "nsIURI.h" #include "nsIDOMWindow.h" //#include "nsIWidget.h" - // XPCOM and String includes +#include "nsIRequest.h" #include "nsCRT.h" #include "nsString.h" #include "nsCOMPtr.h" #include "nsIDOMPopupBlockedEvent.h" - +#include "nsNetError.h" #import "CHBrowserView.h" - +#import "BookmarksClient.h" +#import "Bookmark.h" #include "CHBrowserListener.h" @@ -529,15 +530,36 @@ CHBrowserListener::OnStateChange(nsIWebProgress *aWebProgress, nsIRequest *aRequ { NSEnumerator* enumerator = [mListeners objectEnumerator]; id obj; - if (aStateFlags & nsIWebProgressListener::STATE_IS_NETWORK) { if (aStateFlags & nsIWebProgressListener::STATE_START) { while ((obj = [enumerator nextObject])) [obj onLoadingStarted]; } else if (aStateFlags & nsIWebProgressListener::STATE_STOP) { - while ((obj = [enumerator nextObject])) + // we need to pass along errors like this so our bookmarks know + // if they're still OK + NSNumber *errNum = nil; + if ((aStatus == NS_ERROR_UNKNOWN_HOST || + aStatus == NS_ERROR_CONNECTION_REFUSED || + aStatus == NS_ERROR_UNKNOWN_PROXY_HOST || + aStatus == NS_ERROR_PROXY_CONNECTION_REFUSED)) + errNum = [NSNumber numberWithUnsignedInt:kBookmarkServerErrorStatus]; + else if (( aStatus == NS_ERROR_MALFORMED_URI || + aStatus == NS_ERROR_UNKNOWN_PROTOCOL)) + errNum = [NSNumber numberWithUnsignedInt:kBookmarkBrokenLinkStatus]; + else if ((aStatus == NS_BINDING_REDIRECTED)) + errNum = [NSNumber numberWithUnsignedInt:kBookmarkMovedLinkStatus]; + if (errNum) { + nsCAutoString uriString; + aRequest->GetName(uriString); + NSString *fixedURL = [NSString stringWithCString:uriString.get()]; + NSDictionary *userInfo = [NSDictionary dictionaryWithObject:errNum forKey:URLLoadSuccessKey]; + NSNotification *note = [NSNotification notificationWithName:URLLoadNotification object:fixedURL userInfo:userInfo]; + [[NSNotificationQueue defaultQueue] enqueueNotification:note postingStyle:NSPostWhenIdle]; + } + while ((obj = [enumerator nextObject])) { [obj onLoadingCompleted:(NS_SUCCEEDED(aStatus))]; + } } } diff --git a/camino/src/extensions/ExtendedOutlineView.h b/camino/src/extensions/ExtendedOutlineView.h index 2b9ec4b02a91..6f3c5fb9dbab 100644 --- a/camino/src/extensions/ExtendedOutlineView.h +++ b/camino/src/extensions/ExtendedOutlineView.h @@ -1,26 +1,42 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** +* Version: NPL 1.1/GPL 2.0/LGPL 2.1 * -* The contents of this file are subject to the Mozilla Public -* License Version 1.1 (the "License"); you may not use this file -* except in compliance with the License. You may obtain a copy of -* the License at http://www.mozilla.org/MPL/ +* The contents of this file are subject to the Netscape Public License +* Version 1.1 (the "License"); you may not use this file except in +* compliance with the License. You may obtain a copy of the License at +* http://www.mozilla.org/NPL/ * -* Software distributed under the License is distributed on an "AS -* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or -* implied. See the License for the specific language governing -* rights and limitations under the License. +* Software distributed under the License is distributed on an "AS IS" basis, +* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +* for the specific language governing rights and limitations under the +* License. * -* The Original Code is the Mozilla browser. +* The Original Code is mozilla.org code. * -* The Initial Developer of the Original Code is Netscape -* Communications Corporation. Portions created by Netscape are -* Copyright (C) 2002 Netscape Communications Corporation. All -* Rights Reserved. +* The Initial Developer of the Original Code is +* Netscape Communications Corporation. +* Portions created by the Initial Developer are Copyright (C) 2002 +* the Initial Developer. All Rights Reserved. * * Contributor(s): * David Hyatt (Original Author) * Max Horn (Context menu & tooltip code) -*/ +* +* +* Alternatively, the contents of this file may be used under the terms of +* either the GNU General Public License Version 2 or later (the "GPL"), or +* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +* in which case the provisions of the GPL or the LGPL are applicable instead +* of those above. If you wish to allow use of your version of this file only +* under the terms of either the GPL or the LGPL, and not to allow others to +* use your version of this file under the terms of the NPL, indicate your +* decision by deleting the provisions above and replace them with the notice +* and other provisions required by the GPL or the LGPL. If you do not delete +* the provisions above, a recipient may use your version of this file under +* the terms of any one of the NPL, the GPL or the LGPL. +* +* ***** END LICENSE BLOCK ***** */ #import @@ -43,7 +59,8 @@ -(void)setDeleteAction: (SEL)deleteAction; -(SEL)deleteAction; --(void)setDelegate:(id)anObject; +-(void)_editItem:(id)item; +-(void)_cancelEditItem; @end diff --git a/camino/src/extensions/ExtendedOutlineView.mm b/camino/src/extensions/ExtendedOutlineView.mm index 7c10847a529a..f928c05027bc 100644 --- a/camino/src/extensions/ExtendedOutlineView.mm +++ b/camino/src/extensions/ExtendedOutlineView.mm @@ -1,26 +1,42 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** +* Version: NPL 1.1/GPL 2.0/LGPL 2.1 * -* The contents of this file are subject to the Mozilla Public -* License Version 1.1 (the "License"); you may not use this file -* except in compliance with the License. You may obtain a copy of -* the License at http://www.mozilla.org/MPL/ +* The contents of this file are subject to the Netscape Public License +* Version 1.1 (the "License"); you may not use this file except in +* compliance with the License. You may obtain a copy of the License at +* http://www.mozilla.org/NPL/ * -* Software distributed under the License is distributed on an "AS -* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or -* implied. See the License for the specific language governing -* rights and limitations under the License. +* Software distributed under the License is distributed on an "AS IS" basis, +* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +* for the specific language governing rights and limitations under the +* License. * -* The Original Code is the Mozilla browser. +* The Original Code is mozilla.org code. * -* The Initial Developer of the Original Code is Netscape -* Communications Corporation. Portions created by Netscape are -* Copyright (C) 2002 Netscape Communications Corporation. All -* Rights Reserved. +* The Initial Developer of the Original Code is +* Netscape Communications Corporation. +* Portions created by the Initial Developer are Copyright (C) 2002 +* the Initial Developer. All Rights Reserved. * * Contributor(s): * David Hyatt (Original Author) * Max Horn (Context menu, tooltip code, and editing) -*/ +* +* +* Alternatively, the contents of this file may be used under the terms of +* either the GNU General Public License Version 2 or later (the "GPL"), or +* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +* in which case the provisions of the GPL or the LGPL are applicable instead +* of those above. If you wish to allow use of your version of this file only +* under the terms of either the GPL or the LGPL, and not to allow others to +* use your version of this file under the terms of the NPL, indicate your +* decision by deleting the provisions above and replace them with the notice +* and other provisions required by the GPL or the LGPL. If you do not delete +* the provisions above, a recipient may use your version of this file under +* the terms of any one of the NPL, the GPL or the LGPL. +* +* ***** END LICENSE BLOCK ***** */ #import "ExtendedOutlineView.h" @@ -186,7 +202,9 @@ if ([delegate respondsToSelector:@selector(outlineView:contextMenuForItem:)]) return [delegate outlineView:self contextMenuForItem:item]; - } + } else + // no item, no context menu + return nil; } // Just return the default context menu @@ -283,7 +301,7 @@ // Little trick: if editing was already in progress, then the field editor // will be first responder. For our purposes this is the same as if we // were first responder, so pretend it were so. - if (oldEditRow >= 0) + if (oldEditRow > -1) wasFirstResponder = YES; // If we already were first responder of the main window, and the click was diff --git a/camino/src/extensions/ExtendedTableView.h b/camino/src/extensions/ExtendedTableView.h new file mode 100644 index 000000000000..f9ae258f398b --- /dev/null +++ b/camino/src/extensions/ExtendedTableView.h @@ -0,0 +1,50 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** +* Version: NPL 1.1/GPL 2.0/LGPL 2.1 +* +* The contents of this file are subject to the Netscape Public License +* Version 1.1 (the "License"); you may not use this file except in +* compliance with the License. You may obtain a copy of the License at +* http://www.mozilla.org/NPL/ +* +* Software distributed under the License is distributed on an "AS IS" basis, +* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +* for the specific language governing rights and limitations under the +* License. +* +* The Original Code is mozilla.org code. +* +* The Initial Developer of the Original Code is +* Netscape Communications Corporation. +* Portions created by the Initial Developer are Copyright (C) 2002 +* the Initial Developer. All Rights Reserved. +* +* Contributor(s): +* David Haas +* +* +* Alternatively, the contents of this file may be used under the terms of +* either the GNU General Public License Version 2 or later (the "GPL"), or +* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +* in which case the provisions of the GPL or the LGPL are applicable instead +* of those above. If you wish to allow use of your version of this file only +* under the terms of either the GPL or the LGPL, and not to allow others to +* use your version of this file under the terms of the NPL, indicate your +* decision by deleting the provisions above and replace them with the notice +* and other provisions required by the GPL or the LGPL. If you do not delete +* the provisions above, a recipient may use your version of this file under +* the terms of any one of the NPL, the GPL or the LGPL. +* +* ***** END LICENSE BLOCK ***** */ + +#import + + +@interface ExtendedTableView : NSTableView { + SEL mDeleteAction; +} + +-(void)setDeleteAction: (SEL)deleteAction; +-(SEL)deleteAction; + +@end diff --git a/camino/src/extensions/ExtendedTableView.mm b/camino/src/extensions/ExtendedTableView.mm new file mode 100644 index 000000000000..0318676fc030 --- /dev/null +++ b/camino/src/extensions/ExtendedTableView.mm @@ -0,0 +1,97 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** +* Version: NPL 1.1/GPL 2.0/LGPL 2.1 +* +* The contents of this file are subject to the Netscape Public License +* Version 1.1 (the "License"); you may not use this file except in +* compliance with the License. You may obtain a copy of the License at +* http://www.mozilla.org/NPL/ +* +* Software distributed under the License is distributed on an "AS IS" basis, +* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +* for the specific language governing rights and limitations under the +* License. +* +* The Original Code is mozilla.org code. +* +* The Initial Developer of the Original Code is +* Netscape Communications Corporation. +* Portions created by the Initial Developer are Copyright (C) 2002 +* the Initial Developer. All Rights Reserved. +* +* Contributor(s): +* David Haas +* +* +* Alternatively, the contents of this file may be used under the terms of +* either the GNU General Public License Version 2 or later (the "GPL"), or +* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +* in which case the provisions of the GPL or the LGPL are applicable instead +* of those above. If you wish to allow use of your version of this file only +* under the terms of either the GPL or the LGPL, and not to allow others to +* use your version of this file under the terms of the NPL, indicate your +* decision by deleting the provisions above and replace them with the notice +* and other provisions required by the GPL or the LGPL. If you do not delete +* the provisions above, a recipient may use your version of this file under +* the terms of any one of the NPL, the GPL or the LGPL. +* +* ***** END LICENSE BLOCK ***** */ + +#import "ExtendedTableView.h" + + +@implementation ExtendedTableView + +-(void)setDeleteAction: (SEL)aDeleteAction +{ + mDeleteAction = aDeleteAction; +} + +-(SEL)deleteAction +{ + return mDeleteAction; +} + +-(void)keyDown:(NSEvent*)aEvent +{ + const unichar kForwardDeleteChar = 0xf728; // couldn't find this in any cocoa header + + // check each char in the event array. it should be just 1 char, but + // just in case we use a loop. + int len = [[aEvent characters] length]; + for ( int i = 0; i < len; ++i ) { + unichar c = [[aEvent characters] characterAtIndex:i]; + + // Check for a certain set of special keys. + if (c == NSDeleteCharacter || c == NSBackspaceCharacter || c == kForwardDeleteChar) { + // delete the bookmark + if (mDeleteAction) + [NSApp sendAction: mDeleteAction to: [self target] from: self]; + return; + } + } + return [super keyDown: aEvent]; +} + +-(NSMenu *)menuForEvent:(NSEvent *)theEvent +{ + int rowIndex; + NSPoint point; + point = [self convertPoint:[theEvent locationInWindow] fromView:nil]; + rowIndex = [self rowAtPoint:point]; + if (rowIndex >= 0) { + [self abortEditing]; + id delegate = [self delegate]; + if (![self isRowSelected:rowIndex]) { + if ([delegate respondsToSelector:@selector(tableView:shouldSelectRow:)]) { + if (![delegate tableView:self shouldSelectRow:rowIndex]) + return nil; + } + } + if ([delegate respondsToSelector:@selector(tableView:contextMenuForRow:)]) + return [delegate tableView:self contextMenuForRow:rowIndex]; + } + return nil; +} + +@end diff --git a/camino/src/extensions/NSArray+Utils.h b/camino/src/extensions/NSArray+Utils.h new file mode 100644 index 000000000000..6ad052ecc59d --- /dev/null +++ b/camino/src/extensions/NSArray+Utils.h @@ -0,0 +1,46 @@ +/* ***** BEGIN LICENSE BLOCK ***** +* Version: MPL 1.1/GPL 2.0/LGPL 2.1 +* +* The contents of this file are subject to the Mozilla Public License Version +* 1.1 (the "License"); you may not use this file except in compliance with +* the License. You may obtain a copy of the License at +* http://www.mozilla.org/MPL/ +* +* Software distributed under the License is distributed on an "AS IS" basis, +* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +* for the specific language governing rights and limitations under the +* License. +* +* The Original Code is Chimera code. +* +* The Initial Developer of the Original Code is +* Netscape Communications Corporation. +* Portions created by the Initial Developer are Copyright (C) 2002 +* the Initial Developer. All Rights Reserved. +* +* Contributor(s): +* David Haas +* +* Alternatively, the contents of this file may be used under the terms of +* either the GNU General Public License Version 2 or later (the "GPL"), or +* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +* in which case the provisions of the GPL or the LGPL are applicable instead +* of those above. If you wish to allow use of your version of this file only +* under the terms of either the GPL or the LGPL, and not to allow others to +* use your version of this file under the terms of the MPL, indicate your +* decision by deleting the provisions above and replace them with the notice +* and other provisions required by the GPL or the LGPL. If you do not delete +* the provisions above, a recipient may use your version of this file under +* the terms of any one of the MPL, the GPL or the LGPL. +* +* ***** END LICENSE BLOCK ***** */ + +#import + + +@interface NSArray (ChimeraArrayUtils) +// Packages pointers to BookmarkItems into NSData objects for pasteboard ++(NSArray *)dataArrayFromPointerArrayForMozBookmarkDrop:(NSArray *)dragArray; +// Converts NSData objects on a pasteboard back into pointers to BookmarkItems. ++(NSArray *)pointerArrayFromDataArrayForMozBookmarkDrop:(NSArray *)dragArray; +@end diff --git a/camino/src/extensions/NSArray+Utils.mm b/camino/src/extensions/NSArray+Utils.mm new file mode 100644 index 000000000000..f03fc4a8aefb --- /dev/null +++ b/camino/src/extensions/NSArray+Utils.mm @@ -0,0 +1,66 @@ +/* ***** BEGIN LICENSE BLOCK ***** +* Version: MPL 1.1/GPL 2.0/LGPL 2.1 +* +* The contents of this file are subject to the Mozilla Public License Version +* 1.1 (the "License"); you may not use this file except in compliance with +* the License. You may obtain a copy of the License at +* http://www.mozilla.org/MPL/ +* +* Software distributed under the License is distributed on an "AS IS" basis, +* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +* for the specific language governing rights and limitations under the +* License. +* +* The Original Code is Chimera code. +* +* The Initial Developer of the Original Code is +* Netscape Communications Corporation. +* Portions created by the Initial Developer are Copyright (C) 2002 +* the Initial Developer. All Rights Reserved. +* +* Contributor(s): +* David Haas +* +* Alternatively, the contents of this file may be used under the terms of +* either the GNU General Public License Version 2 or later (the "GPL"), or +* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +* in which case the provisions of the GPL or the LGPL are applicable instead +* of those above. If you wish to allow use of your version of this file only +* under the terms of either the GPL or the LGPL, and not to allow others to +* use your version of this file under the terms of the MPL, indicate your +* decision by deleting the provisions above and replace them with the notice +* and other provisions required by the GPL or the LGPL. If you do not delete +* the provisions above, a recipient may use your version of this file under +* the terms of any one of the MPL, the GPL or the LGPL. +* +* ***** END LICENSE BLOCK ***** */ + +#import "NSArray+Utils.h" + + +@implementation NSArray (ChimeraArrayUtils) + ++(NSArray *)pointerArrayFromDataArrayForMozBookmarkDrop:(NSArray *)dragArray +{ + NSMutableArray *fixedArray = [NSMutableArray arrayWithCapacity:[dragArray count]]; + NSEnumerator *enumerator = [dragArray objectEnumerator]; + id aDataToPointer, aPointerToBookmark; + while ((aDataToPointer = [enumerator nextObject])) { + [aDataToPointer getBytes:&aPointerToBookmark length:(sizeof(id))]; + [fixedArray addObject:aPointerToBookmark]; + } + return [NSArray arrayWithArray:fixedArray]; +} + ++(NSArray *)dataArrayFromPointerArrayForMozBookmarkDrop:(NSArray *)dragArray +{ + NSMutableArray *dataArray = [NSMutableArray arrayWithCapacity:[dragArray count]]; + NSEnumerator *enumerator = [dragArray objectEnumerator]; + id aBookmark; + while ((aBookmark = [enumerator nextObject])) + [dataArray addObject:[NSData dataWithBytes:&aBookmark length:(sizeof(id))]]; + return [NSArray arrayWithArray:dataArray]; +} + + +@end diff --git a/camino/src/extensions/NSString+Utils.h b/camino/src/extensions/NSString+Utils.h index a5b6b8afb2ad..c3808c9720eb 100644 --- a/camino/src/extensions/NSString+Utils.h +++ b/camino/src/extensions/NSString+Utils.h @@ -52,6 +52,7 @@ typedef enum @interface NSString (ChimeraStringUtils) + (id)ellipsisString; ++ (id)escapedURLString:(NSString *)unescapedString; + (id)stringWithPRUnichars:(const PRUnichar*)inString; + (id)stringWith_nsAString:(const nsAString&)inString; - (void)assignTo_nsAString:(nsAString&)ioString; @@ -60,6 +61,9 @@ typedef enum - (NSString *)stringByReplacingCharactersInSet:(NSCharacterSet*)characterSet withString:(NSString*)string; - (NSString *)stringByTruncatingTo:(unsigned int)maxCharacters at:(ETruncationType)truncationType; - (NSString *)stringByTrimmingWhitespace; +-(NSString *)stringByRemovingAmpEscapes; +-(NSString *)stringByAddingAmpEscapes; +-(NSString *)stripWWW; // allocate a new unicode buffer with the contents of the current string. Caller // is responsible for freeing the buffer. diff --git a/camino/src/extensions/NSString+Utils.mm b/camino/src/extensions/NSString+Utils.mm index 9ebfef07142c..3b93eacb6398 100644 --- a/camino/src/extensions/NSString+Utils.mm +++ b/camino/src/extensions/NSString+Utils.mm @@ -20,6 +20,7 @@ * * Contributor(s): * Simon Fraser + * David Haas * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or @@ -59,6 +60,12 @@ return sEllipsisString; } ++ (id)escapedURLString:(NSString *)unescapedString +{ + NSString *escapedString = (NSString *) CFURLCreateStringByAddingPercentEscapes(NULL, (CFStringRef)unescapedString, NULL, NULL, kCFStringEncodingUTF8); + return [escapedString autorelease]; +} + + (id)stringWithPRUnichars:(const PRUnichar*)inString { if (inString) @@ -167,6 +174,91 @@ return retStr; } +-(NSString *)stringByRemovingAmpEscapes +{ + NSMutableString* dirtyStringMutant = [NSMutableString stringWithString:self]; + //10.2 and later + if ([dirtyStringMutant respondsToSelector:@selector(replaceOccurrencesOfString:withString:options:range:)]){ + [dirtyStringMutant replaceOccurrencesOfString:@"&"withString:@"&" options:NSLiteralSearch range:NSMakeRange(0,[dirtyStringMutant length])]; + [dirtyStringMutant replaceOccurrencesOfString:@"""withString:@"\"" options:NSLiteralSearch range:NSMakeRange(0,[dirtyStringMutant length])]; + [dirtyStringMutant replaceOccurrencesOfString:@"<"withString:@"<" options:NSLiteralSearch range:NSMakeRange(0,[dirtyStringMutant length])]; + [dirtyStringMutant replaceOccurrencesOfString:@">"withString:@">" options:NSLiteralSearch range:NSMakeRange(0,[dirtyStringMutant length])]; + [dirtyStringMutant replaceOccurrencesOfString:@"—"withString:@"-" options:NSLiteralSearch range:NSMakeRange(0,[dirtyStringMutant length])]; + return [dirtyStringMutant stringByRemovingCharactersInSet:[NSCharacterSet controlCharacterSet]]; + } + + // 10.1. + NSRange ampRange = [dirtyStringMutant rangeOfString:@"&" options:NSLiteralSearch]; + while (ampRange.location != NSNotFound) { + [dirtyStringMutant replaceCharactersInRange:ampRange withString:@"&"]; + ampRange = [dirtyStringMutant rangeOfString:@"&" options:NSLiteralSearch range:NSMakeRange(ampRange.location,[dirtyStringMutant length]-ampRange.location)]; + } + ampRange = [dirtyStringMutant rangeOfString:@""" options:NSLiteralSearch]; + while (ampRange.location != NSNotFound) { + [dirtyStringMutant replaceCharactersInRange:ampRange withString:@"\""]; + ampRange = [dirtyStringMutant rangeOfString:@""" options:NSLiteralSearch range:NSMakeRange(ampRange.location,[dirtyStringMutant length]-ampRange.location)]; + } + ampRange = [dirtyStringMutant rangeOfString:@"<" options:NSLiteralSearch]; + while (ampRange.location != NSNotFound) { + [dirtyStringMutant replaceCharactersInRange:ampRange withString:@"<"]; + ampRange = [dirtyStringMutant rangeOfString:@"<" options:NSLiteralSearch range:NSMakeRange(ampRange.location,[dirtyStringMutant length]-ampRange.location)]; + } + ampRange = [dirtyStringMutant rangeOfString:@">" options:NSLiteralSearch]; + while (ampRange.location != NSNotFound) { + [dirtyStringMutant replaceCharactersInRange:ampRange withString:@">"]; + ampRange = [dirtyStringMutant rangeOfString:@">" options:NSLiteralSearch range:NSMakeRange(ampRange.location,[dirtyStringMutant length]-ampRange.location)]; + } + ampRange = [dirtyStringMutant rangeOfString:@"—" options:NSLiteralSearch]; + while (ampRange.location != NSNotFound) { + [dirtyStringMutant replaceCharactersInRange:ampRange withString:@"-"]; + ampRange = [dirtyStringMutant rangeOfString:@"—" options:NSLiteralSearch range:NSMakeRange(ampRange.location,[dirtyStringMutant length]-ampRange.location)]; + } + + return [dirtyStringMutant stringByRemovingCharactersInSet:[NSCharacterSet controlCharacterSet]]; +} + +-(NSString *)stringByAddingAmpEscapes +{ + NSMutableString* dirtyStringMutant = [NSMutableString stringWithString:self]; + //10.2 & later + if ([dirtyStringMutant respondsToSelector:@selector(replaceOccurrencesOfString:withString:options:range:)]){ + [dirtyStringMutant replaceOccurrencesOfString:@"&"withString:@"&" options:NSLiteralSearch range:NSMakeRange(0,[dirtyStringMutant length])]; + [dirtyStringMutant replaceOccurrencesOfString:@"\""withString:@""" options:NSLiteralSearch range:NSMakeRange(0,[dirtyStringMutant length])]; + [dirtyStringMutant replaceOccurrencesOfString:@"<"withString:@"<" options:NSLiteralSearch range:NSMakeRange(0,[dirtyStringMutant length])]; + [dirtyStringMutant replaceOccurrencesOfString:@">"withString:@">" options:NSLiteralSearch range:NSMakeRange(0,[dirtyStringMutant length])]; + return [NSString stringWithString:dirtyStringMutant]; + } + // 10.1. + NSRange ampRange = [dirtyStringMutant rangeOfString:@"&" options:NSLiteralSearch]; + while (ampRange.location != NSNotFound) { + [dirtyStringMutant replaceCharactersInRange:ampRange withString:@"&"]; + ampRange = [dirtyStringMutant rangeOfString:@"&" options:NSLiteralSearch range:NSMakeRange(ampRange.location+1,[dirtyStringMutant length]-ampRange.location-1)]; + } + ampRange = [dirtyStringMutant rangeOfString:@"\"" options:NSLiteralSearch]; + while (ampRange.location != NSNotFound) { + [dirtyStringMutant replaceCharactersInRange:ampRange withString:@"""]; + ampRange = [dirtyStringMutant rangeOfString:@"\"" options:NSLiteralSearch range:NSMakeRange(ampRange.location,[dirtyStringMutant length]-ampRange.location)]; + } + ampRange = [dirtyStringMutant rangeOfString:@"<" options:NSLiteralSearch]; + while (ampRange.location != NSNotFound) { + [dirtyStringMutant replaceCharactersInRange:ampRange withString:@"<"]; + ampRange = [dirtyStringMutant rangeOfString:@"<" options:NSLiteralSearch range:NSMakeRange(ampRange.location,[dirtyStringMutant length]-ampRange.location)]; + } + ampRange = [dirtyStringMutant rangeOfString:@">" options:NSLiteralSearch]; + while (ampRange.location != NSNotFound) { + [dirtyStringMutant replaceCharactersInRange:ampRange withString:@">"]; + ampRange = [dirtyStringMutant rangeOfString:@">" options:NSLiteralSearch range:NSMakeRange(ampRange.location,[dirtyStringMutant length]-ampRange.location)]; + } + return [NSString stringWithString:dirtyStringMutant]; +} + +-(NSString *)stripWWW +{ + if ([self hasPrefix:@"www."] && ([self length]>4)) + return [self substringFromIndex:4]; + return self; +} + @end diff --git a/camino/src/extensions/RDFOutlineViewDataSource.h b/camino/src/extensions/RDFOutlineViewDataSource.h index 596fbae51a12..fb8829a14782 100644 --- a/camino/src/extensions/RDFOutlineViewDataSource.h +++ b/camino/src/extensions/RDFOutlineViewDataSource.h @@ -36,15 +36,14 @@ * * ***** END LICENSE BLOCK ***** */ -#import #import -#import "ExtendedOutlineView.h" class nsIRDFDataSource; class nsIRDFContainer; class nsIRDFContainerUtils; class nsIRDFResource; class nsIRDFService; +@class ExtendedOutlineView; // RDF Resource Wrapper to make the Outline View happy diff --git a/camino/src/extensions/RDFOutlineViewDataSource.mm b/camino/src/extensions/RDFOutlineViewDataSource.mm index c3dbd4e58ed5..3c7d1e72a852 100644 --- a/camino/src/extensions/RDFOutlineViewDataSource.mm +++ b/camino/src/extensions/RDFOutlineViewDataSource.mm @@ -40,6 +40,7 @@ #import "RDFOutlineViewDataSource.h" #import "CHBrowserService.h" +#import "ExtendedOutlineView.h" #include "nsCRT.h" #include "nsIRDFDataSource.h" @@ -333,8 +334,12 @@ // The table column's identifier is the last part of the RDF Resource URI of the property // being displayed in that column, e.g. "http://home.netscape.com/NC-rdf#Name" + // little hack inserted until history moves to new bookmark format + NSString *identifier = [aTableColumn identifier]; + if ([identifier isEqualToString:@"title"]) + identifier = [NSString stringWithString:@"Name"]; NSString* columnPropertyURI = [NSString stringWithFormat:@"http://home.netscape.com/NC-rdf#%@", - [aTableColumn identifier]]; + identifier]; NSString* propString = [self getPropertyString:columnPropertyURI forItem:aItem]; return [self createCellContents:propString withColumn:columnPropertyURI byItem:aItem]; diff --git a/camino/src/history/HistoryDataSource.h b/camino/src/history/HistoryDataSource.h index 0c3180ffaac9..e40d2e4435c9 100644 --- a/camino/src/history/HistoryDataSource.h +++ b/camino/src/history/HistoryDataSource.h @@ -36,8 +36,6 @@ * * ***** END LICENSE BLOCK ***** */ -#import - #import "RDFOutlineViewDataSource.h" class nsAString; diff --git a/camino/src/history/HistoryDataSource.mm b/camino/src/history/HistoryDataSource.mm index 702f4b890821..bd6359fb8248 100644 --- a/camino/src/history/HistoryDataSource.mm +++ b/camino/src/history/HistoryDataSource.mm @@ -41,6 +41,7 @@ #import "BrowserWindowController.h" #import "HistoryDataSource.h" #import "CHBrowserView.h" +#import "ExtendedOutlineView.h" #include "nsIRDFService.h" #include "nsIRDFDataSource.h" @@ -265,7 +266,8 @@ HistoryDataSourceObserver::OnChange(nsIRDFDataSource*, nsIRDFResource*, // -(id) createCellContents:(NSString*)inValue withColumn:(NSString*)inColumn byItem:(id) inItem { - if ([inValue length] == 0) + NSString *fragment = [[NSURL URLWithString:inColumn] fragment]; + if (([inValue length] == 0) && [fragment isEqualToString:@"Name"]) inValue = [self getPropertyString:@"http://home.netscape.com/NC-rdf#URL" forItem:inItem]; return inValue; } @@ -430,7 +432,7 @@ HistoryDataSourceObserver::OnChange(nsIRDFDataSource*, nsIRDFResource*, - (void)outlineView:(NSOutlineView *)outlineView willDisplayCell:(NSCell *)inCell forTableColumn:(NSTableColumn *)tableColumn item:(id)item { // set the image on the name column. the url column doesn't have an image. - if ([[tableColumn identifier] isEqualToString: @"Name"]) { + if ([[tableColumn identifier] isEqualToString: @"title"]) { if ( [outlineView isExpandable: item] ) [inCell setImage:[NSImage imageNamed:@"folder"]]; else diff --git a/camino/src/rendezvous/NetworkServices.h b/camino/src/rendezvous/NetworkServices.h index 9094f2f00b3d..3bde5dd3df39 100644 --- a/camino/src/rendezvous/NetworkServices.h +++ b/camino/src/rendezvous/NetworkServices.h @@ -45,9 +45,9 @@ @protocol NetworkServicesClient -- (void)availableServicesChanged:(NetworkServices*)servicesProvider; -- (void)serviceResolved:(int)serviceID withURL:(NSString*)url; -- (void)serviceResolutionFailed:(int)serviceID; +- (void)availableServicesChanged:(NSNotification *)note; +- (void)serviceResolved:(NSNotification *)note; +- (void)serviceResolutionFailed:(NSNotification *)note; @end @@ -60,22 +60,27 @@ NSNetServiceBrowser* mFtpBrowser; int mCurServiceID; // unique ID for each service - NSMutableDictionary* mNetworkServices; // services keyed by ID + NSMutableDictionary* mNetworkServices; // services keyed by ID - NSMutableArray* mClients; // array of id + NSMutableDictionary* mClients; // dictionary of cliend id's for a request } ++ (id)sharedNetworkServices; ++ (void)shutdownNetworkServices; - (void)startServices; - (void)stopServices; - -- (void)registerClient:(id)client; -- (void)unregisterClient:(id)client; - -- (void)attemptResolveService:(int)serviceID; +- (void)attemptResolveService:(int)serviceID forSender:(id)aSender; - (NSString*)serviceName:(int)serviceID; - (NSString*)serviceProtocol:(int)serviceID; - - (NSEnumerator*)serviceEnumerator; +// Notifications + extern NSString *NetworkServicesAvailableServicesChanged; + extern NSString *NetworkServicesResolutionSuccess; + extern NSString *NetworkServicesResolutionFailure; + extern NSString *NetworkServicesClientKey; + extern NSString *NetworkServicesResolvedURLKey; + extern NSString *NetworkServicesServiceKey; + @end diff --git a/camino/src/rendezvous/NetworkServices.mm b/camino/src/rendezvous/NetworkServices.mm index 4c333ac4e31a..c81e1dbe2059 100644 --- a/camino/src/rendezvous/NetworkServices.mm +++ b/camino/src/rendezvous/NetworkServices.mm @@ -21,6 +21,7 @@ * * Contributor(s): * Simon Fraser + * David Haas * * * Alternatively, the contents of this file may be used under the terms of @@ -51,6 +52,14 @@ @class NSNetServiceBrowser; +// client notifications +NSString *NetworkServicesAvailableServicesChanged = @"netserv_asc"; +NSString *NetworkServicesResolutionSuccess = @"netserv_resok"; +NSString *NetworkServicesClientKey = @"netserv_clikey"; +NSString *NetworkServicesResolvedURLKey = @"netserv_urlkey"; +NSString *NetworkServicesResolutionFailure = @"netserv_resbad"; +NSString *NetworkServicesServiceKey = @"netserv_srvkey"; + @interface NetworkServices(Private) @@ -59,25 +68,39 @@ - (void)notifyClientsOfServicesChange; -- (void)notifyClientsOfServiceResolution:(int)serviceID withURL:(NSString*)url; -- (void)notifyClientsOfServiceResolutionFailure:(int)serviceID; +- (void)notifyClientsOfServiceResolution:(NSNetService *)aService withURL:(NSString*)url; +- (void)notifyClientsOfServiceResolutionFailure:(NSNetService *)aService; - (void)serviceAppeared:(NSNetService*)service; - (void)serviceDisappeared:(NSNetService*)service; - (void)serviceResolved:(NSNetService*)service; -- (int)getServiceID:(NSNetService*)service; - @end @implementation NetworkServices +static NetworkServices* gNetworkServices = nil; + ++(id)sharedNetworkServices +{ + if (!gNetworkServices) + gNetworkServices = [[NetworkServices alloc] init]; + return gNetworkServices; +} + ++(void)shutdownNetworkServices +{ + [gNetworkServices release]; //this'll put the hammer on things +} + - (id)init { if ((self = [super init])) { mNetworkServices = [[NSMutableDictionary alloc] initWithCapacity:5]; + mClients = [[NSMutableDictionary alloc] initWithCapacity:2]; mCurServiceID = 0; + [self setupNetworkBrowsers]; } return self; @@ -88,6 +111,8 @@ [self stopServices]; [mNetworkServices release]; [mClients release]; + if (self == gNetworkServices) + gNetworkServices = nil; [super dealloc]; } @@ -120,20 +145,6 @@ } } -- (void)registerClient:(id)client -{ - if (!mClients) - mClients = [[NSMutableArray alloc] initWithCapacity:2]; - - if ([mClients indexOfObject:client] == NSNotFound) - [mClients addObject:client]; -} - -- (void)unregisterClient:(id)client -{ - [mClients removeObject:client]; -} - - (NSString*)serviceName:(int)serviceID { NSNetService* service = [mNetworkServices objectForKey:[NSNumber numberWithInt:serviceID]]; @@ -150,11 +161,12 @@ return [service type]; } -- (void)attemptResolveService:(int)serviceID +- (void)attemptResolveService:(int)serviceID forSender:(id)aSender; { - NSNetService* service = [mNetworkServices objectForKey:[NSNumber numberWithInt:serviceID]]; + NSNumber *serviceKey = [NSNumber numberWithInt:serviceID]; + NSNetService* service = [mNetworkServices objectForKey:serviceKey]; if (!service) return; - + [mClients setObject:aSender forKey:serviceKey]; [service resolve]; } @@ -165,13 +177,6 @@ #pragma mark - -- (int)getServiceID:(NSNetService*)service -{ - NSArray* serviceKeys = [mNetworkServices allKeysForObject:service]; - // there should only ever be one key - return [[serviceKeys objectAtIndex:0] intValue]; -} - - (void)setupNetworkBrowsers { mHttpBrowser = [[NSNetServiceBrowser alloc] init]; @@ -190,33 +195,27 @@ - (void)notifyClientsOfServicesChange { - for (unsigned int i = 0; i < [mClients count]; i ++) - { - id client = [mClients objectAtIndex:i]; - [client availableServicesChanged:self]; - } + NSNotification *note = [NSNotification notificationWithName:NetworkServicesAvailableServicesChanged object:self userInfo:nil]; + NSNotificationQueue *nc = [NSNotificationQueue defaultQueue]; + [nc enqueueNotification:note postingStyle:NSPostWhenIdle coalesceMask:NSNotificationCoalescingOnName forModes:[NSArray arrayWithObject:NSDefaultRunLoopMode]]; } -- (void)notifyClientsOfServiceResolution:(int)serviceID withURL:(NSString*)url +- (void)notifyClientsOfServiceResolution:(NSNetService *)aService withURL:(NSString*)url { - // ldeally we'd keep track of which client issued the attemptResolveService - // request, and only give it the callback. But we don't do that yet. - for (unsigned int i = 0; i < [mClients count]; i ++) - { - id client = [mClients objectAtIndex:i]; - [client serviceResolved:serviceID withURL:url]; - } + NSNumber *serviceKey = [[mNetworkServices allKeysForObject:aService] objectAtIndex:0]; + id aClient = [mClients objectForKey:serviceKey]; + NSDictionary *userInfo = [NSDictionary dictionaryWithObjectsAndKeys:aClient,NetworkServicesClientKey,url,NetworkServicesResolvedURLKey,[aService name],NetworkServicesServiceKey,nil]; + [[NSNotificationCenter defaultCenter] postNotificationName:NetworkServicesResolutionSuccess object:self userInfo:userInfo]; + [mClients removeObjectForKey:aService]; } -- (void)notifyClientsOfServiceResolutionFailure:(int)serviceID +- (void)notifyClientsOfServiceResolutionFailure:(NSNetService *)aService { - // ldeally we'd keep track of which client issued the attemptResolveService - // request, and only give it the callback. But we don't do that yet. - for (unsigned int i = 0; i < [mClients count]; i ++) - { - id client = [mClients objectAtIndex:i]; - [client serviceResolutionFailed:serviceID]; - } + NSNumber *serviceKey = [[mNetworkServices allKeysForObject:aService] objectAtIndex:0]; + id aClient = [mClients objectForKey:serviceKey]; + NSDictionary *userInfo = [NSDictionary dictionaryWithObjectsAndKeys:aClient, NetworkServicesClientKey, [aService name],NetworkServicesServiceKey,nil]; + [[NSNotificationCenter defaultCenter] postNotificationName:NetworkServicesResolutionFailure object:self userInfo:userInfo]; + [mClients removeObjectForKey:aService]; } - (void)serviceAppeared:(NSNetService*)service @@ -365,7 +364,7 @@ static inline u_int ns_get16(u_char* buffer) else urlString = [NSString stringWithFormat:@"%s//%s:%u%s", protocol, escapedTarget, port, pathBuffer]; - [self notifyClientsOfServiceResolution:[self getServiceID:netService] withURL:urlString]; + [self notifyClientsOfServiceResolution:netService withURL:urlString]; return; } } @@ -373,7 +372,7 @@ static inline u_int ns_get16(u_char* buffer) } // only get here on failure - [self notifyClientsOfServiceResolutionFailure:[self getServiceID:netService]]; + [self notifyClientsOfServiceResolutionFailure:netService]; } - (void)netServiceDidResolveAddress:(NSNetService *)netService @@ -385,7 +384,7 @@ static inline u_int ns_get16(u_char* buffer) - (void)netService:(NSNetService *)sender didNotResolve:(NSDictionary *)errorDict { - [self notifyClientsOfServiceResolutionFailure:[self getServiceID:sender]]; + [self notifyClientsOfServiceResolutionFailure:sender]; // now clear out the state of the service [sender stop]; } @@ -401,8 +400,7 @@ static inline u_int ns_get16(u_char* buffer) { [self serviceAppeared:aNetService]; [aNetService setDelegate:self]; - - // rebuild the menu + // trigger notification if (!moreComing) [self notifyClientsOfServicesChange]; }