diff --git a/build/jam/FileRules b/build/jam/FileRules index 2a5b896000..86fbe8b2c6 100644 --- a/build/jam/FileRules +++ b/build/jam/FileRules @@ -199,7 +199,7 @@ rule ExtractArchive directory : entries : archiveFile : grist # and combo. # # - The directory into which to extract the archive file. The - # directory is created is by this rule and it is the target + # directory is created by this rule and it is the target # that the extract action is associated with. # - The entries of the archive file one is interested in. The # rule always extracts the complete archive file, from the diff --git a/build/jam/HaikuImage b/build/jam/HaikuImage index ffc8d7ea74..61adf50d76 100644 --- a/build/jam/HaikuImage +++ b/build/jam/HaikuImage @@ -239,7 +239,8 @@ AddFilesToHaikuImage system add-ons kernel busses usb AddFilesToHaikuImage system add-ons kernel console : vga_text ; AddFilesToHaikuImage system add-ons kernel debugger : demangle $(X86_ONLY)disasm hangman - invalidate_on_exit usb_keyboard run_on_exit ; + invalidate_on_exit usb_keyboard qrencode + run_on_exit ; AddFilesToHaikuImage system add-ons kernel file_systems : $(SYSTEM_ADD_ONS_FILE_SYSTEMS) ; AddFilesToHaikuImage system add-ons kernel generic diff --git a/build/jam/OptionalBuildFeatures b/build/jam/OptionalBuildFeatures index 34ec079ec6..6f4d542f38 100644 --- a/build/jam/OptionalBuildFeatures +++ b/build/jam/OptionalBuildFeatures @@ -12,9 +12,9 @@ if [ IsOptionalHaikuImagePackageAdded OpenSSL ] { } if $(HAIKU_GCC_VERSION[1]) >= 4 { - HAIKU_OPENSSL_PACKAGE = openssl-1.0.0e-x86-gcc4-2011-09-08.zip ; + HAIKU_OPENSSL_PACKAGE = openssl-1.0.0j-x86-gcc4-2012-06-19.zip ; } else { - HAIKU_OPENSSL_PACKAGE = openssl-1.0.0e-x86-gcc2-2011-09-09.zip ; + HAIKU_OPENSSL_PACKAGE = openssl-1.0.0j-x86-gcc2-2012-06-19.zip ; } local baseURL = http://haiku-files.org/files/optional-packages ; @@ -369,11 +369,11 @@ if $(TARGET_ARCH) = x86 { local freetypeBaseURL = http://haiku-files.org/files/optional-packages/lib ; if $(TARGET_ARCH) = ppc || $(TARGET_ARCH) = x86 { if $(TARGET_ARCH) = ppc { - HAIKU_FREETYPE_FILE = freetype-2.4.6-ppc-gcc4-2012-03-17.zip ; + HAIKU_FREETYPE_FILE = freetype-2.4.9-ppc-gcc4-2012-06-26.zip ; } else if $(HAIKU_GCC_VERSION[1]) >= 4 { - HAIKU_FREETYPE_FILE = freetype-2.4.6-x86-gcc4-2012-03-15.zip ; + HAIKU_FREETYPE_FILE = freetype-2.4.9-x86-gcc4-2012-06-15.zip ; } else { - HAIKU_FREETYPE_FILE = freetype-2.4.6-x86-gcc2-2012-03-15.zip ; + HAIKU_FREETYPE_FILE = freetype-2.4.9-x86-gcc2-2012-06-19.zip ; } local freetypeZipFile = [ DownloadFile $(HAIKU_FREETYPE_FILE) @@ -393,7 +393,7 @@ if $(TARGET_ARCH) = ppc || $(TARGET_ARCH) = x86 { : extracted-freetype ] ; HAIKU_FREETYPE_CURRENT_LIB = [ ExtractArchive $(HAIKU_FREETYPE_DIR) : - common/lib/libfreetype.so.6.7.1 + common/lib/libfreetype.so.6.8.1 : $(freetypeZipFile) : extracted-freetype ] ; Depends $(HAIKU_FREETYPE_LIB) $(HAIKU_FREETYPE_CURRENT_LIB) : $(HAIKU_FREETYPE_HEADERS_DEPENDENCY) ; @@ -417,7 +417,7 @@ if [ IsOptionalHaikuImagePackageAdded TagLib ] { } local baseURL = http://haiku-files.org/files/optional-packages ; -HAIKU_TAGLIB_PACKAGE = taglib-1.6.3-r1a2-x86-gcc2-2010-04-25.zip ; +HAIKU_TAGLIB_PACKAGE = taglib-1.6.3-r1a3-x86-gcc2-2011-05-20.zip ; HAIKU_TAGLIB_URL = $(baseURL)/$(HAIKU_TAGLIB_PACKAGE) ; if $(HAIKU_BUILD_FEATURE_TAGLIB) { @@ -451,3 +451,36 @@ if $(HAIKU_BUILD_FEATURE_TAGLIB) { } } + +# WebKit +local webkitBaseURL = http://haiku-files.org/files/optional-packages ; +if $(TARGET_ARCH) = x86 { + if $(HAIKU_GCC_VERSION[1]) < 4 { + Echo "WebKit support not available on gcc $(HAIKU_GCC_VERSION[1])" ; + } else { + HAIKU_WEBKIT_FILE = webkit-0.1.1-x86-gcc4-2012-07-03.zip ; + + local zipFile = [ DownloadFile $(HAIKU_WEBKIT_FILE) + : $(webkitBaseURL)/$(HAIKU_WEBKIT_FILE) ] ; + + HAIKU_WEBKIT_DIR = [ FDirName $(HAIKU_OPTIONAL_BUILD_PACKAGES_DIR) + $(HAIKU_WEBKIT_FILE:B) ] ; + + HAIKU_WEBKIT_HEADERS_DEPENDENCY = [ ExtractArchive $(HAIKU_WEBKIT_DIR) + : include/ : $(zipFile) : extracted-webkit ] ; + + HAIKU_WEBKIT_LIBS = [ ExtractArchive $(HAIKU_WEBKIT_DIR) + : + lib/libwtf.so + lib/libjavascriptcore.so + lib/libwebcore.so + lib/libwebkit.so + : $(zipFile) + : extracted-webkit + ] ; + + HAIKU_WEBKIT_HEADERS = [ FDirName $(HAIKU_WEBKIT_DIR) include ] ; + } +} else { + Echo "WebKit support not available on $(TARGET_ARCH)" ; +} diff --git a/build/jam/OptionalLibPackages b/build/jam/OptionalLibPackages index 82042787c3..e2f58aa5e4 100644 --- a/build/jam/OptionalLibPackages +++ b/build/jam/OptionalLibPackages @@ -227,78 +227,78 @@ if [ IsOptionalHaikuImagePackageAdded SDLLibs ] { Echo "No optional package SDLLibs available for $(TARGET_ARCH)" ; } else if $(HAIKU_GCC_VERSION[1]) >= 4 { InstallOptionalHaikuImagePackage - libsdl-1.2-hg-r1a3-x86-gcc4-2011-05-29.zip - : $(baseURL)/lib/libsdl-1.2-hg-r1a3-x86-gcc4-2011-05-29.zip ; + libsdl-1.2.15-x86-gcc4-2012-05-31.zip + : $(baseURL)/lib/libsdl-1.2.15-x86-gcc4-2012-05-31.zip ; InstallOptionalHaikuImagePackage - guilib-1.2.1-r1a3-x86-gcc4-2011-05-26.zip - : $(baseURL)/lib/guilib-1.2.1-r1a3-x86-gcc4-2011-05-26.zip ; + guilib-1.2.1-x86-gcc4-2012-05-31.zip + : $(baseURL)/lib/guilib-1.2.1-x86-gcc4-2012-05-31.zip ; InstallOptionalHaikuImagePackage - sdl-gfx-2.0.22-r1a3-x86-gcc4-2011-05-26.zip - : $(baseURL)/lib/sdl-gfx-2.0.22-r1a3-x86-gcc4-2011-05-26.zip ; + sdl-gfx-2.0.23-x86-gcc4-2011-05-31.zip + : $(baseURL)/lib/sdl-gfx-2.0.23-x86-gcc4-2012-05-31.zip ; InstallOptionalHaikuImagePackage - sdl-image-1.2.10-r1a3-x86-gcc4-2011-05-26.zip - : $(baseURL)/lib/sdl-image-1.2.10-r1a3-x86-gcc4-2011-05-26.zip ; + sdl-image-1.2.12-x86-gcc4-2012-05-31.zip + : $(baseURL)/lib/sdl-image-1.2.12-x86-gcc4-2011-05-31.zip ; InstallOptionalHaikuImagePackage - sdl-mixer-1.2.11-r1a3-x86-gcc4-2011-05-26.zip - : $(baseURL)/lib/sdl-mixer-1.2.11-r1a3-x86-gcc4-2011-05-26.zip ; + sdl-mixer-1.2.11-x86-gcc4-2012-05-31.zip + : $(baseURL)/lib/sdl-mixer-1.2.11-x86-gcc4-2012-05-31.zip ; InstallOptionalHaikuImagePackage - sdl-net-1.2.7-r1a3-x86-gcc4-2011-05-26.zip - : $(baseURL)/lib/sdl-net-1.2.7-r1a3-x86-gcc4-2011-05-26.zip ; + sdl-net-1.2.8-x86-gcc4-2012-05-31.zip + : $(baseURL)/lib/sdl-net-1.2.8-x86-gcc4-2012-05-31.zip ; InstallOptionalHaikuImagePackage - sdl-sound-1.0.3-r1a3-x86-gcc4-2011-05-26.zip - : $(baseURL)/lib/sdl-sound-1.0.3-r1a3-x86-gcc4-2011-05-26.zip ; + sdl-sound-1.0.3-x86-gcc4-2012-05-31.zip + : $(baseURL)/lib/sdl-sound-1.0.3-x86-gcc4-2012-05-31.zip ; InstallOptionalHaikuImagePackage sdl-rtf-0.1.0-r1a3-x86-gcc4-2011-05-26.zip : $(baseURL)/lib/sdl-rtf-0.1.0-r1a3-x86-gcc4-2011-05-26.zip ; InstallOptionalHaikuImagePackage - sdl-ttf-2.0.10-r1a3-x86-gcc4-2011-05-26.zip - : $(baseURL)/lib/sdl-ttf-2.0.10-r1a3-x86-gcc4-2011-05-26.zip ; + sdl-ttf-2.0.11-x86-gcc4-2012-05-31.zip + : $(baseURL)/lib/sdl-ttf-2.0.11-x86-gcc4-2012-05-31.zip ; InstallOptionalHaikuImagePackage - sge-030809-r1a3-x86-gcc4-2011-05-26.zip - : $(baseURL)/lib/sge-030809-r1a3-x86-gcc4-2011-05-26.zip ; + sge-030809-x86-gcc4-2012-05-31.zip + : $(baseURL)/lib/sge-030809-x86-gcc4-2012-05-31.zip ; InstallOptionalHaikuImagePackage - smjpeg-0.2.1-r1a3-x86-gcc4-2011-05-26.zip - : $(baseURL)/lib/smjpeg-0.2.1-r1a3-x86-gcc4-2011-05-26.zip ; + smjpeg-0.2.1-x86-gcc4-2012-05-31.zip + : $(baseURL)/lib/smjpeg-0.2.1-x86-gcc4-2012-05-31.zip ; InstallOptionalHaikuImagePackage - smpeg-0.4.5-r1a3-x86-gcc4-2011-05-26.zip - : $(baseURL)/lib/smpeg-0.4.5-r1a3-x86-gcc4-2011-05-26.zip ; + smpeg-0.4.5-x86-gcc4-2011-05-31.zip + : $(baseURL)/lib/smpeg-0.4.5-x86-gcc4-2012-05-31.zip ; } else { InstallOptionalHaikuImagePackage - libsdl-1.2-hg-r1a3-x86-gcc2-2011-05-29.zip - : $(baseURL)/lib/libsdl-1.2-hg-r1a3-x86-gcc2-2011-05-29.zip ; + libsdl-1.2.15-x86-gcc2-2012-06-24.zip + : $(baseURL)/lib/libsdl-1.2.15-x86-gcc2-2012-06-24.zip ; InstallOptionalHaikuImagePackage - guilib-1.2.1-r1a3-x86-gcc2-2011-05-19.zip - : $(baseURL)/lib/guilib-1.2.1-r1a3-x86-gcc2-2011-05-19.zip ; + guilib-1.2.1-x86-gcc2-2012-06-24.zip + : $(baseURL)/lib/guilib-1.2.1-x86-gcc2-2012-06-24.zip ; InstallOptionalHaikuImagePackage - sdl-gfx-2.0.22-r1a3-x86-gcc2-2011-05-19.zip - : $(baseURL)/lib/sdl-gfx-2.0.22-r1a3-x86-gcc2-2011-05-19.zip ; + sdl-gfx-2.0.23-x86-gcc2-2012-06-24.zip + : $(baseURL)/lib/sdl-gfx-2.0.23-x86-gcc2-2012-06-24.zip ; InstallOptionalHaikuImagePackage - sdl-image-1.2.10-x86-r1a3-x86-gcc2-2011-05-19.zip - : $(baseURL)/lib/sdl-image-1.2.10-r1a3-x86-gcc2-2011-05-19.zip ; + sdl-image-1.2.12-x86-x86-gcc2-2012-06-24.zip + : $(baseURL)/lib/sdl-image-1.2.12-x86-gcc2-2012-06-24.zip ; InstallOptionalHaikuImagePackage - sdl-mixer-1.2.11-r1a3-x86-gcc2-2011-05-19.zip - : $(baseURL)/lib/sdl-mixer-1.2.11-r1a3-x86-gcc2-2011-05-19.zip ; + sdl-mixer-1.2.11-x86-gcc2-2012-06-25.zip + : $(baseURL)/lib/sdl-mixer-1.2.11-x86-gcc2-2012-06-25.zip ; InstallOptionalHaikuImagePackage - sdl-net-1.2.7-r1a3-x86-gcc2-2011-05-19.zip - : $(baseURL)/lib/sdl-net-1.2.7-r1a3-x86-gcc2-2011-05-19.zip ; + sdl-net-1.2.8-x86-gcc2-2012-06-24.zip + : $(baseURL)/lib/sdl-net-1.2.8-x86-gcc2-2012-06-24.zip ; InstallOptionalHaikuImagePackage - sdl-sound-1.0.3-r1a3-x86-gcc2-2011-05-19.zip - : $(baseURL)/lib/sdl-sound-1.0.3-r1a3-x86-gcc2-2011-05-19.zip ; + sdl-sound-1.0.3-x86-gcc2-2012-06-24.zip + : $(baseURL)/lib/sdl-sound-1.0.3-x86-gcc2-2012-06-24.zip ; InstallOptionalHaikuImagePackage sdl-rtf-0.1.0-r1a3-x86-gcc2-2011-05-20.zip : $(baseURL)/lib/sdl-rtf-0.1.0-r1a3-x86-gcc2-2011-05-20.zip ; InstallOptionalHaikuImagePackage - sdl-ttf-2.0.10-r1a3-x86-gcc2-2011-05-19.zip - : $(baseURL)/lib/sdl-ttf-2.0.10-r1a3-x86-gcc2-2011-05-19.zip ; + sdl-ttf-2.0.11-x86-gcc2-2012-06-24.zip + : $(baseURL)/lib/sdl-ttf-2.0.11-x86-gcc2-2012-06-24.zip ; InstallOptionalHaikuImagePackage - sge-030809-r1a3-x86-gcc2-2011-05-19.zip - : $(baseURL)/lib/sge-030809-r1a3-x86-gcc2-2011-05-19.zip ; + sge-030809-x86-gcc2-2012-06-24.zip + : $(baseURL)/lib/sge-030809-x86-gcc2-2012-06-24.zip ; InstallOptionalHaikuImagePackage - smjpeg-0.2.1-r1a3-x86-gcc2-2011-05-19.zip - : $(baseURL)/lib/smjpeg-0.2.1-r1a3-x86-gcc2-2011-05-19.zip ; + smjpeg-0.2.1-x86-gcc2-2012-06-24.zip + : $(baseURL)/lib/smjpeg-0.2.1-x86-gcc2-2012-06-24.zip ; InstallOptionalHaikuImagePackage - smpeg-0.4.5-r1a3-x86-gcc2-2011-05-20.zip - : $(baseURL)/lib/smpeg-0.4.5-r1a3-x86-gcc2-2011-05-20.zip ; + smpeg-0.4.5-x86-gcc2-2012-06-24.zip + : $(baseURL)/lib/smpeg-0.4.5-x86-gcc2-2012-06-24.zip ; } } diff --git a/build/jam/OptionalPackageDependencies b/build/jam/OptionalPackageDependencies index a706c5387f..92d475d54d 100644 --- a/build/jam/OptionalPackageDependencies +++ b/build/jam/OptionalPackageDependencies @@ -17,6 +17,7 @@ OptionalPackageDependencies Development : DevelopmentBase Perl ; OptionalPackageDependencies DevelopmentPowerPC : DevelopmentMin ; OptionalPackageDependencies GetText : LibIconv ; OptionalPackageDependencies Git : Expat Curl OpenSSL LibIconv ; +OptionalPackageDependencies GitDoc : Man Git ; OptionalPackageDependencies ICU-devel : DevelopmentBase ; OptionalPackageDependencies LibLayout : DevelopmentBase ; OptionalPackageDependencies Man : Groff ; diff --git a/build/jam/OptionalPackages b/build/jam/OptionalPackages index d612b8ed9e..926469fda5 100644 --- a/build/jam/OptionalPackages +++ b/build/jam/OptionalPackages @@ -50,6 +50,7 @@ if $(HAIKU_ADD_ALTERNATIVE_GCC_LIBS) = 1 # friss - RSS/ATOM/... feeds reader # GetText - a framework to help produce multi-lingual messages. # Git - the distributed version control system +# GitDoc - documentation for the distributed version control system # GPerf - the perfect hash function generator. # Groff - text formatter used for man pages # ICU-devel - the headers and lib-links for ICU (for development) @@ -122,13 +123,13 @@ if [ IsOptionalHaikuImagePackageAdded APR ] { Echo "No optional package APR available for $(TARGET_ARCH)" ; } else if $(HAIKU_GCC_VERSION[1]) >= 4 { InstallOptionalHaikuImagePackage - apr-1.4.5-x86-gcc4-2011-08-03.zip - : $(baseURL)/apr-1.4.5-x86-gcc4-2011-08-03.zip + apr-1.4.6-x86-gcc4-2012-06-19.zip + : $(baseURL)/apr-1.4.6-x86-gcc4-2012-06-19.zip : : true ; } else { InstallOptionalHaikuImagePackage - apr-1.4.5-x86-gcc2-2011-08-02.zip - : $(baseURL)/apr-1.4.5-x86-gcc2-2011-08-02.zip + apr-1.4.6-x86-gcc2-2012-06-19.zip + : $(baseURL)/apr-1.4.6-x86-gcc2-2012-06-19.zip : : true ; } } @@ -140,13 +141,13 @@ if [ IsOptionalHaikuImagePackageAdded APR-util ] { Echo "No optional package APR-util available for $(TARGET_ARCH)" ; } else if $(HAIKU_GCC_VERSION[1]) >= 4 { InstallOptionalHaikuImagePackage - apr-util-1.3.12-x86-gcc4-2011-08-03.zip - : $(baseURL)/apr-util-1.3.12-x86-gcc4-2011-08-03.zip + apr-util-1.4.1-x86-gcc4-2012-06-19.zip + : $(baseURL)/apr-util-1.4.1-x86-gcc4-2012-06-19.zip : : true ; } else { InstallOptionalHaikuImagePackage - apr-util-1.3.12-x86-gcc2-2011-08-02.zip - : $(baseURL)/apr-util-1.3.12-x86-gcc2-2011-08-02.zip + apr-util-1.4.1-x86-gcc2-2012-06-19.zip + : $(baseURL)/apr-util-1.4.1-x86-gcc2-2012-06-19.zip : : true ; } } @@ -173,11 +174,11 @@ if [ IsOptionalHaikuImagePackageAdded Bazaar ] { if $(TARGET_ARCH) != x86 { Echo "No optional package Bazaar available for $(TARGET_ARCH)" ; } else if $(HAIKU_GCC_VERSION[1]) >= 4 { - InstallOptionalHaikuImagePackage bzr-2.6b1-x86-gcc4-2012-03-30.zip - : $(baseURL)/bzr-2.6b1-x86-gcc4-2012-03-30.zip ; + InstallOptionalHaikuImagePackage bzr-2.5.1-x86-gcc4-2012-06-22.zip + : $(baseURL)/bzr-2.5.1-x86-gcc4-2012-06-22.zip ; } else { - InstallOptionalHaikuImagePackage bzr-2.6b1-x86-gcc2-2012-03-31.zip - : $(baseURL)/bzr-2.6b1-x86-gcc2-2012-03-31.zip ; + InstallOptionalHaikuImagePackage bzr-2.5.1-x86-gcc2-2012-06-22.zip + : $(baseURL)/bzr-2.5.1-x86-gcc2-2012-06-22.zip ; } } @@ -358,8 +359,8 @@ if [ IsOptionalHaikuImagePackageAdded Bzip ] { # CARootCertificates if [ IsOptionalHaikuImagePackageAdded CARootCertificates ] { - InstallOptionalHaikuImagePackage cert-2012-05-02.zip - : $(baseURL)/cert-2012-05-02.zip + InstallOptionalHaikuImagePackage cert-2012-07-04.zip + : $(baseURL)/cert-2012-07-04.zip : common data ssl ; } @@ -408,12 +409,12 @@ if [ IsOptionalHaikuImagePackageAdded CDRecord ] { Echo "No optional package CDRecord available for $(TARGET_ARCH)" ; } else if $(HAIKU_GCC_VERSION[1]) >= 4 { InstallOptionalHaikuImagePackage - cdrtools-3.01a01-r1a3-x86-gcc4-2011-05-23.zip - : $(baseURL)/cdrtools-3.01a01-r1a3-x86-gcc4-2011-05-23.zip ; + cdrtools-3.01a06-x86-gcc4-2012-06-19.zip + : $(baseURL)/cdrtools-3.01a06-x86-gcc4-2012-06-19.zip ; } else { InstallOptionalHaikuImagePackage - cdrtools-3.01a01-r1a3-x86-gcc2-2011-05-17.zip - : $(baseURL)/cdrtools-3.01a01-r1a3-x86-gcc2-2011-05-17.zip ; + cdrtools-3.01a06-x86-gcc2-2012-06-20.zip + : $(baseURL)/cdrtools-3.01a06-x86-gcc2-2012-06-20.zip ; } } @@ -532,11 +533,11 @@ if [ IsOptionalHaikuImagePackageAdded Curl ] { if $(TARGET_ARCH) != x86 { Echo "No optional package Curl available for $(TARGET_ARCH)" ; } else if $(HAIKU_GCC_VERSION[1]) >= 4 { - InstallOptionalHaikuImagePackage curl-7.21.7-x86-gcc4-2012-02-18.zip - : $(baseURL)/curl-7.21.7-x86-gcc4-2012-02-18.zip ; + InstallOptionalHaikuImagePackage curl-7.26.0-x86-gcc4-2012-06-20.zip + : $(baseURL)/curl-7.26.0-x86-gcc4-2012-06-20.zip ; } else { - InstallOptionalHaikuImagePackage curl-7.21.7-x86-gcc2-2012-02-18.zip - : $(baseURL)/curl-7.21.7-x86-gcc2-2012-02-18.zip ; + InstallOptionalHaikuImagePackage curl-7.26.0-x86-gcc2-2012-06-21.zip + : $(baseURL)/curl-7.26.0-x86-gcc2-2012-06-21.zip ; } } @@ -548,13 +549,13 @@ if [ IsOptionalHaikuImagePackageAdded CVS ] { } else { if $(HAIKU_GCC_VERSION[1]) >= 4 { InstallOptionalHaikuImagePackage - cvs-1.12.13.1-r1a3-x86-gcc4-2011-05-24.zip - : $(baseURL)/cvs-1.12.13.1-r1a3-x86-gcc4-2011-05-24.zip + cvs-1.12.13.1-x86-gcc4-2012-06-22.zip + : $(baseURL)/cvs-1.12.13.1-x86-gcc4-2012-06-22.zip : : true ; } else { InstallOptionalHaikuImagePackage - cvs-1.12.13.1-r1a3-x86-gcc2-2011-05-18.zip - : $(baseURL)/cvs-1.12.13.1-r1a3-x86-gcc2-2011-05-18.zip + cvs-1.12.13.1-x86-gcc2-2012-06-20.zip + : $(baseURL)/cvs-1.12.13.1-x86-gcc2-2012-06-20.zip : : true ; } } @@ -912,8 +913,8 @@ if [ IsOptionalHaikuImagePackageAdded Expat ] { : : true ; } else { InstallOptionalHaikuImagePackage - expat-2.0.1-r1a3-x86-gcc2-2011-05-20.zip - : $(baseURL)/expat-2.0.1-r1a3-x86-gcc2-2011-05-20.zip + expat-2.0.1-x86-gcc2-2012-06-20.zip + : $(baseURL)/expat-2.0.1-x86-gcc2-2012-06-20.zip : : true ; } } @@ -983,13 +984,33 @@ if [ IsOptionalHaikuImagePackageAdded Git ] { } else { if $(HAIKU_GCC_VERSION[1]) >= 4 { InstallOptionalHaikuImagePackage - git-1.7.7.2-x86-gcc4-2011-11-04.zip - : $(baseURL)/git-1.7.7.2-x86-gcc4-2011-11-04.zip + git-1.7.10.2-x86-gcc4-2012-06-19.zip + : $(baseURL)/git-1.7.10.2-x86-gcc4-2012-06-19.zip : : true ; } else { InstallOptionalHaikuImagePackage - git-1.7.7.2-x86-gcc2-2011-11-04.zip - : $(baseURL)/git-1.7.7.2-x86-gcc2-2011-11-04.zip + git-1.7.10.2-x86-gcc2-2012-06-20.zip + : $(baseURL)/git-1.7.10.2-x86-gcc2-2012-06-20.zip + : : true ; + } + } +} + + +# GitDoc +if [ IsOptionalHaikuImagePackageAdded GitDoc ] { + if $(TARGET_ARCH) != x86 { + Echo "No optional package GitDoc available for $(TARGET_ARCH)" ; + } else { + if $(HAIKU_GCC_VERSION[1]) >= 4 { + InstallOptionalHaikuImagePackage + gitdoc-1.7.10.2-x86-gcc4-2012-06-20.zip + : $(baseURL)/gitdoc-1.7.10.2-x86-gcc4-2012-06-20.zip + : : true ; + } else { + InstallOptionalHaikuImagePackage + gitdoc-1.7.10.2-x86-gcc2-2012-06-20.zip + : $(baseURL)/gitdoc-1.7.10.2-x86-gcc2-2012-06-20.zip : : true ; } } @@ -1185,12 +1206,12 @@ if [ IsOptionalHaikuImagePackageAdded LibXML2 ] { } else { if $(HAIKU_GCC_VERSION[1]) >= 4 { InstallOptionalHaikuImagePackage - libxml2-2.7.8-r1a3-x86-gcc4-2011-05-24.zip - : $(baseURL)/libxml2-2.7.8-r1a3-x86-gcc4-2011-05-24.zip ; + libxml2-2.8.0-x86-gcc4-2012-06-18.zip + : $(baseURL)/libxml2-2.8.0-x86-gcc4-2012-06-18.zip ; } else { InstallOptionalHaikuImagePackage - libxml2-2.7.8-r1a3-x86-gcc2-2011-05-18.zip - : $(baseURL)/libxml2-2.7.8-r1a3-x86-gcc2-2011-05-18.zip ; + libxml2-2.8.0-x86-gcc2-2012-06-21.zip + : $(baseURL)/libxml2-2.8.0-x86-gcc2-2012-06-21.zip ; } } } @@ -1254,12 +1275,12 @@ if [ IsOptionalHaikuImagePackageAdded Man ] { Echo "No optional package Man available for $(TARGET_ARCH)" ; } else if $(HAIKU_GCC_VERSION[1]) >= 4 { InstallOptionalHaikuImagePackage - man-1.6f-r1a3-x86-gcc4-2011-05-24.zip - : $(baseURL)/man-1.6f-r1a3-x86-gcc4-2011-05-24.zip ; + man-1.6g-x86-gcc4-2012-06-19.zip + : $(baseURL)/man-1.6g-x86-gcc4-2012-06-19.zip ; } else { InstallOptionalHaikuImagePackage - man-1.6f-r1a3-x86-gcc2-2011-05-18.zip - : $(baseURL)/man-1.6f-r1a3-x86-gcc2-2011-05-18.zip ; + man-1.6g-x86-gcc2-2012-06-21.zip + : $(baseURL)/man-1.6g-x86-gcc2-2012-06-21.zip ; } } @@ -1277,13 +1298,13 @@ if [ IsOptionalHaikuImagePackageAdded Mercurial ] { } else { if $(HAIKU_GCC_VERSION[1]) >= 4 { InstallOptionalHaikuImagePackage - mercurial-1.8.3-r1a3-x86-gcc4-2011-05-24.zip - : $(baseURL)/mercurial-1.8.3-r1a3-x86-gcc4-2011-05-24.zip + mercurial-2.2.2-x86-gcc4-2012-06-19.zip + : $(baseURL)/mercurial-2.2.2-x86-gcc4-2012-06-19.zip : : true ; } else { InstallOptionalHaikuImagePackage - mercurial-1.8.3-r1a3-x86-gcc2-2011-05-17.zip - : $(baseURL)/mercurial-1.8.3-r1a3-x86-gcc2-2011-05-17.zip + mercurial-2.2.2-x86-gcc2-2012-06-20.zip + : $(baseURL)/mercurial-2.2.2-x86-gcc2-2012-06-20.zip : : true ; } } @@ -1315,12 +1336,12 @@ if [ IsOptionalHaikuImagePackageAdded Neon ] { } else { if $(HAIKU_GCC_VERSION[1]) >= 4 { InstallOptionalHaikuImagePackage - neon-0.29.6-r1a3-x86-gcc4-2011-05-24.zip - : $(baseURL)/neon-0.29.6-r1a3-x86-gcc4-2011-05-24.zip ; + neon-0.29.6-x86-gcc4-2012-06-19.zip + : $(baseURL)/neon-0.29.6-x86-gcc4-2012-06-19.zip ; } else { InstallOptionalHaikuImagePackage - neon-0.29.6-r1a3-x86-gcc2-2011-05-18.zip - : $(baseURL)/neon-0.29.6-r1a3-x86-gcc2-2011-05-18.zip ; + neon-0.29.6-x86-gcc2-2012-06-21.zip + : $(baseURL)/neon-0.29.6-x86-gcc2-2012-06-21.zip ; } } } @@ -1412,12 +1433,12 @@ if [ IsOptionalHaikuImagePackageAdded OpenSSH ] { if $(HAIKU_GCC_VERSION[1]) >= 4 { InstallOptionalHaikuImagePackage - openssh-5.9p1-x86-gcc4-2011-11-30.zip - : $(baseURL)/openssh-5.9p1-x86-gcc4-2011-11-30.zip ; + openssh-6.0p1-x86-gcc4-2012-06-20.zip + : $(baseURL)/openssh-6.0p1-x86-gcc4-2012-06-20.zip ; } else { InstallOptionalHaikuImagePackage - openssh-5.9p1-x86-gcc2-2011-11-30.zip - : $(baseURL)/openssh-5.9p1-x86-gcc2-2011-11-30.zip ; + openssh-6.0p1-x86-gcc2-2012-06-21.zip + : $(baseURL)/openssh-6.0p1-x86-gcc2-2012-06-21.zip ; } AddUserToHaikuImage sshd : 1001 : 100 : /var/empty : /bin/true @@ -1449,12 +1470,12 @@ if [ IsOptionalHaikuImagePackageAdded P7zip ] { } else { if $(HAIKU_GCC_VERSION[1]) >= 4 { InstallOptionalHaikuImagePackage - p7zip-9.13-r1a3-x86-gcc4-2011-05-24.zip - : $(baseURL)/p7zip-9.13-r1a3-x86-gcc4-2011-05-24.zip ; + p7zip-9.20.1-x86-gcc4-2012-06-19.zip + : $(baseURL)/p7zip-9.20.1-x86-gcc4-2012-06-19.zip ; } else { InstallOptionalHaikuImagePackage - p7zip-9.13-r1a3-x86-gcc2-2011-05-18.zip - : $(baseURL)/p7zip-9.13-r1a3-x86-gcc2-2011-05-18.zip ; + p7zip-9.20.1-x86-gcc2-2012-06-21.zip + : $(baseURL)/p7zip-9.20.1-x86-gcc2-2012-06-21.zip ; } AddExpanderRuleToHaikuImage "application/x-7z-compressed" : .7z : "7za l \\0045s" @@ -1627,12 +1648,12 @@ if [ IsOptionalHaikuImagePackageAdded SQLite ] { } else { if $(HAIKU_GCC_VERSION[1]) >= 4 { InstallOptionalHaikuImagePackage - sqlite-3.7.7.1-x86-gcc4-2012-03-30.zip - : $(baseURL)/sqlite-3.7.7.1-x86-gcc4-2012-03-30.zip ; + sqlite-3.7.13-x86-gcc4-2012-06-19.zip + : $(baseURL)/sqlite-3.7.13-x86-gcc4-2012-06-19.zip ; } else { InstallOptionalHaikuImagePackage - sqlite-3.7.7.1-x86-gcc2-2012-03-31.zip - : $(baseURL)/sqlite-3.7.7.1-x86-gcc2-2012-03-31.zip ; + sqlite-3.7.13-x86-gcc2-2012-06-20.zip + : $(baseURL)/sqlite-3.7.13-x86-gcc2-2012-06-20.zip ; } } } @@ -1645,13 +1666,13 @@ if [ IsOptionalHaikuImagePackageAdded Subversion ] { } else { if $(HAIKU_GCC_VERSION[1]) >= 4 { InstallOptionalHaikuImagePackage - subversion-1.6.17-x86-gcc4-2011-08-03.zip - : $(baseURL)/subversion-1.6.17-x86-gcc4-2011-08-03.zip + subversion-1.6.18-x86-gcc4-2012-06-19.zip + : $(baseURL)/subversion-1.6.18-x86-gcc4-2012-06-19.zip : : true ; } else { InstallOptionalHaikuImagePackage - subversion-1.6.17-x86-gcc2-2011-08-02.zip - : $(baseURL)/subversion-1.6.17-x86-gcc2-2011-08-02.zip + subversion-1.6.18-x86-gcc2-2012-06-21.zip + : $(baseURL)/subversion-1.6.18-x86-gcc2-2012-06-21.zip : : true ; } } @@ -1681,12 +1702,12 @@ if [ IsOptionalHaikuImagePackageAdded Tar ] { } else { if $(HAIKU_GCC_VERSION[1]) >= 4 { InstallOptionalHaikuImagePackage - tar-1.25-r1a3-x86-gcc4-2011-05-24.zip - : $(baseURL)/tar-1.25-r1a3-x86-gcc4-2011-05-24.zip ; + tar-1.26-x86-gcc4-2012-06-19.zip + : $(baseURL)/tar-1.26-x86-gcc4-2012-06-19.zip ; } else { InstallOptionalHaikuImagePackage - tar-1.25-r1a3-x86-gcc2-2011-05-20.zip - : $(baseURL)/tar-1.25-r1a3-x86-gcc2-2011-05-20.zip ; + tar-1.26-x86-gcc2-2012-06-21.zip + : $(baseURL)/tar-1.26-x86-gcc2-2012-06-21.zip ; } } } @@ -1869,7 +1890,7 @@ if [ IsOptionalHaikuImagePackageAdded WifiFirmwareScriptData ] { # firmware cutter local broadcomFWCutterArchive = b43-fwcutter-012.tar.bz2 ; local broadcomFWCutterURL = - http://bu3sch.de/b43/fwcutter/$(broadcomFWCutterArchive) ; + http://www.haiku-files.org/files/wifi-firmwares/b43/fwcutter/$(broadcomFWCutterArchive) ; local broadcomFWCutterFile = [ DownloadFile $(broadcomFWCutterArchive) : $(broadcomFWCutterURL) ] ; AddFilesToHaikuImage system data firmware broadcom43xx b43-fwcutter @@ -1889,16 +1910,16 @@ if [ IsOptionalHaikuImagePackageAdded WifiFirmwareScriptData ] { # file containing firmware local broadcom43xxFile ; broadcom43xxFile = [ DownloadFile wl_apsta-3.130.20.0.o - : http://downloads.openwrt.org/sources/wl_apsta-3.130.20.0.o ] ; + : http://www.haiku-files.org/files/wifi-firmwares/b43/wl_apsta-3.130.20.0.o ] ; AddFilesToHaikuImage system data firmware broadcom43xx : $(broadcom43xxFile) ; # marvell88w8335 - local marvelArchive = malo-firmware-1.4.tgz ; - local marvelURL = http://www.nazgul.ch/malo/$(marvelArchive) ; - local marvelFile = [ DownloadFile $(marvelArchive) : $(marvelURL) ] ; + local marvellArchive = malo-firmware-1.4.tgz ; + local marvellURL = http://www.haiku-files.org/files/wifi-firmwares/marvell/$(marvellArchive) ; + local marvellFile = [ DownloadFile $(marvellArchive) : $(marvellURL) ] ; AddFilesToHaikuImage system data firmware marvell88w8335 - : $(marvelFile) ; + : $(marvellFile) ; } } @@ -1973,11 +1994,11 @@ if [ IsOptionalHaikuImagePackageAdded Yasm ] { Echo "No optional package Yasm available for $(TARGET_ARCH)" ; } else if $(HAIKU_GCC_VERSION[1]) >= 4 { InstallOptionalHaikuImagePackage - yasm-1.1.0-r1a3-x86-gcc4-2011-05-24.zip - : $(baseURL)/yasm-1.1.0-r1a3-x86-gcc4-2011-05-24.zip ; + yasm-1.2.0-x86-gcc4-2012-06-18.zip + : $(baseURL)/yasm-1.2.0-x86-gcc4-2012-06-18.zip ; } else { InstallOptionalHaikuImagePackage - yasm-1.1.0-r1a3-x86-gcc2-2011-05-17.zip - : $(baseURL)/yasm-1.1.0-r1a3-x86-gcc2-2011-05-17.zip ; + yasm-1.2.0-x86-gcc2-2012-06-19.zip + : $(baseURL)/yasm-1.2.0-x86-gcc2-2012-06-19.zip ; } } diff --git a/data/system/data/fonts/ttfonts/DejaVuSans-Bold.ttf b/data/system/data/fonts/ttfonts/DejaVuSans-Bold.ttf index ec1a2ebaf2..0f4d5e9d48 100644 Binary files a/data/system/data/fonts/ttfonts/DejaVuSans-Bold.ttf and b/data/system/data/fonts/ttfonts/DejaVuSans-Bold.ttf differ diff --git a/data/system/data/fonts/ttfonts/DejaVuSans-BoldOblique.ttf b/data/system/data/fonts/ttfonts/DejaVuSans-BoldOblique.ttf index 1a5576460d..48f06a863a 100644 Binary files a/data/system/data/fonts/ttfonts/DejaVuSans-BoldOblique.ttf and b/data/system/data/fonts/ttfonts/DejaVuSans-BoldOblique.ttf differ diff --git a/data/system/data/fonts/ttfonts/DejaVuSans-ExtraLight.ttf b/data/system/data/fonts/ttfonts/DejaVuSans-ExtraLight.ttf index 833af86257..01a56521d1 100644 Binary files a/data/system/data/fonts/ttfonts/DejaVuSans-ExtraLight.ttf and b/data/system/data/fonts/ttfonts/DejaVuSans-ExtraLight.ttf differ diff --git a/data/system/data/fonts/ttfonts/DejaVuSans-Oblique.ttf b/data/system/data/fonts/ttfonts/DejaVuSans-Oblique.ttf index becc549927..44856388da 100644 Binary files a/data/system/data/fonts/ttfonts/DejaVuSans-Oblique.ttf and b/data/system/data/fonts/ttfonts/DejaVuSans-Oblique.ttf differ diff --git a/data/system/data/fonts/ttfonts/DejaVuSans.ttf b/data/system/data/fonts/ttfonts/DejaVuSans.ttf index c1b19d8705..27cff476ef 100644 Binary files a/data/system/data/fonts/ttfonts/DejaVuSans.ttf and b/data/system/data/fonts/ttfonts/DejaVuSans.ttf differ diff --git a/data/system/data/fonts/ttfonts/DejaVuSansCondensed-Bold.ttf b/data/system/data/fonts/ttfonts/DejaVuSansCondensed-Bold.ttf index cb48a02b46..9edd89c3dc 100644 Binary files a/data/system/data/fonts/ttfonts/DejaVuSansCondensed-Bold.ttf and b/data/system/data/fonts/ttfonts/DejaVuSansCondensed-Bold.ttf differ diff --git a/data/system/data/fonts/ttfonts/DejaVuSansCondensed-BoldOblique.ttf b/data/system/data/fonts/ttfonts/DejaVuSansCondensed-BoldOblique.ttf index 08007e0e16..e2db5ce0c1 100644 Binary files a/data/system/data/fonts/ttfonts/DejaVuSansCondensed-BoldOblique.ttf and b/data/system/data/fonts/ttfonts/DejaVuSansCondensed-BoldOblique.ttf differ diff --git a/data/system/data/fonts/ttfonts/DejaVuSansCondensed-Oblique.ttf b/data/system/data/fonts/ttfonts/DejaVuSansCondensed-Oblique.ttf index f50b87ed60..7cec04c2e0 100644 Binary files a/data/system/data/fonts/ttfonts/DejaVuSansCondensed-Oblique.ttf and b/data/system/data/fonts/ttfonts/DejaVuSansCondensed-Oblique.ttf differ diff --git a/data/system/data/fonts/ttfonts/DejaVuSansCondensed.ttf b/data/system/data/fonts/ttfonts/DejaVuSansCondensed.ttf index 783eae4e3b..826e619b78 100644 Binary files a/data/system/data/fonts/ttfonts/DejaVuSansCondensed.ttf and b/data/system/data/fonts/ttfonts/DejaVuSansCondensed.ttf differ diff --git a/data/system/data/fonts/ttfonts/DejaVuSansMono-Bold.ttf b/data/system/data/fonts/ttfonts/DejaVuSansMono-Bold.ttf index 32a8695f70..09d42796a1 100644 Binary files a/data/system/data/fonts/ttfonts/DejaVuSansMono-Bold.ttf and b/data/system/data/fonts/ttfonts/DejaVuSansMono-Bold.ttf differ diff --git a/data/system/data/fonts/ttfonts/DejaVuSansMono-BoldOblique.ttf b/data/system/data/fonts/ttfonts/DejaVuSansMono-BoldOblique.ttf index 5e2ee0339c..0344c22709 100644 Binary files a/data/system/data/fonts/ttfonts/DejaVuSansMono-BoldOblique.ttf and b/data/system/data/fonts/ttfonts/DejaVuSansMono-BoldOblique.ttf differ diff --git a/data/system/data/fonts/ttfonts/DejaVuSansMono-Oblique.ttf b/data/system/data/fonts/ttfonts/DejaVuSansMono-Oblique.ttf index 9789ea057c..bc16d51bd5 100644 Binary files a/data/system/data/fonts/ttfonts/DejaVuSansMono-Oblique.ttf and b/data/system/data/fonts/ttfonts/DejaVuSansMono-Oblique.ttf differ diff --git a/data/system/data/fonts/ttfonts/DejaVuSansMono.ttf b/data/system/data/fonts/ttfonts/DejaVuSansMono.ttf index 6bc854ddae..7260bd65e0 100644 Binary files a/data/system/data/fonts/ttfonts/DejaVuSansMono.ttf and b/data/system/data/fonts/ttfonts/DejaVuSansMono.ttf differ diff --git a/data/system/data/fonts/ttfonts/DejaVuSerif-Bold.ttf b/data/system/data/fonts/ttfonts/DejaVuSerif-Bold.ttf index aee1ae626f..afa2d5ec3d 100644 Binary files a/data/system/data/fonts/ttfonts/DejaVuSerif-Bold.ttf and b/data/system/data/fonts/ttfonts/DejaVuSerif-Bold.ttf differ diff --git a/data/system/data/fonts/ttfonts/DejaVuSerif-BoldItalic.ttf b/data/system/data/fonts/ttfonts/DejaVuSerif-BoldItalic.ttf index 1b55e27214..ce9443eaea 100644 Binary files a/data/system/data/fonts/ttfonts/DejaVuSerif-BoldItalic.ttf and b/data/system/data/fonts/ttfonts/DejaVuSerif-BoldItalic.ttf differ diff --git a/data/system/data/fonts/ttfonts/DejaVuSerif-Italic.ttf b/data/system/data/fonts/ttfonts/DejaVuSerif-Italic.ttf index 4464868f75..c1864a3071 100644 Binary files a/data/system/data/fonts/ttfonts/DejaVuSerif-Italic.ttf and b/data/system/data/fonts/ttfonts/DejaVuSerif-Italic.ttf differ diff --git a/data/system/data/fonts/ttfonts/DejaVuSerif.ttf b/data/system/data/fonts/ttfonts/DejaVuSerif.ttf index 1f2c94fa01..ed53a2967c 100644 Binary files a/data/system/data/fonts/ttfonts/DejaVuSerif.ttf and b/data/system/data/fonts/ttfonts/DejaVuSerif.ttf differ diff --git a/data/system/data/fonts/ttfonts/DejaVuSerifCondensed-Bold.ttf b/data/system/data/fonts/ttfonts/DejaVuSerifCondensed-Bold.ttf index 9483a5f102..55adca4120 100644 Binary files a/data/system/data/fonts/ttfonts/DejaVuSerifCondensed-Bold.ttf and b/data/system/data/fonts/ttfonts/DejaVuSerifCondensed-Bold.ttf differ diff --git a/data/system/data/fonts/ttfonts/DejaVuSerifCondensed-BoldItalic.ttf b/data/system/data/fonts/ttfonts/DejaVuSerifCondensed-BoldItalic.ttf index d141211def..e211957344 100644 Binary files a/data/system/data/fonts/ttfonts/DejaVuSerifCondensed-BoldItalic.ttf and b/data/system/data/fonts/ttfonts/DejaVuSerifCondensed-BoldItalic.ttf differ diff --git a/data/system/data/fonts/ttfonts/DejaVuSerifCondensed-Italic.ttf b/data/system/data/fonts/ttfonts/DejaVuSerifCondensed-Italic.ttf index 437824a3d2..275b26d953 100644 Binary files a/data/system/data/fonts/ttfonts/DejaVuSerifCondensed-Italic.ttf and b/data/system/data/fonts/ttfonts/DejaVuSerifCondensed-Italic.ttf differ diff --git a/data/system/data/fonts/ttfonts/DejaVuSerifCondensed.ttf b/data/system/data/fonts/ttfonts/DejaVuSerifCondensed.ttf index 2fc9db9011..83120cd9d3 100644 Binary files a/data/system/data/fonts/ttfonts/DejaVuSerifCondensed.ttf and b/data/system/data/fonts/ttfonts/DejaVuSerifCondensed.ttf differ diff --git a/data/system/data/fonts/ttfonts/VL-Gothic-Regular.ttf b/data/system/data/fonts/ttfonts/VL-Gothic-Regular.ttf index 016461965c..f880d709e7 100644 Binary files a/data/system/data/fonts/ttfonts/VL-Gothic-Regular.ttf and b/data/system/data/fonts/ttfonts/VL-Gothic-Regular.ttf differ diff --git a/data/system/data/fonts/ttfonts/VL-PGothic-Regular.ttf b/data/system/data/fonts/ttfonts/VL-PGothic-Regular.ttf index 6512f78020..293825428c 100644 Binary files a/data/system/data/fonts/ttfonts/VL-PGothic-Regular.ttf and b/data/system/data/fonts/ttfonts/VL-PGothic-Regular.ttf differ diff --git a/headers/os/interface/StringView.h b/headers/os/interface/StringView.h index 283bc8a2a5..a8a36b61b0 100644 --- a/headers/os/interface/StringView.h +++ b/headers/os/interface/StringView.h @@ -9,7 +9,7 @@ #include -class BStringView : public BView{ +class BStringView : public BView { public: BStringView(BRect bounds, const char* name, const char* text, uint32 resizeFlags diff --git a/headers/os/interface/View.h b/headers/os/interface/View.h index 8418f03e21..4471ddd1a6 100644 --- a/headers/os/interface/View.h +++ b/headers/os/interface/View.h @@ -584,6 +584,8 @@ protected: virtual void LayoutChanged(); + void ScrollWithMouseWheelDelta(BScrollBar*, float); + private: void _Layout(bool force, BLayoutContext* context); void _LayoutLeft(BLayout* deleted); diff --git a/headers/private/kernel/debug_heap.h b/headers/private/kernel/debug_heap.h index ae110b9840..552a07a8cd 100644 --- a/headers/private/kernel/debug_heap.h +++ b/headers/private/kernel/debug_heap.h @@ -19,6 +19,7 @@ extern "C" { debug_alloc_pool* create_debug_alloc_pool(); void delete_debug_alloc_pool(debug_alloc_pool* pool); void* debug_malloc(size_t size); +void* debug_calloc(size_t num, size_t size); void debug_free(void* address); void debug_heap_init(); diff --git a/headers/private/kernel/util/AVLTreeMap.h b/headers/private/kernel/util/AVLTreeMap.h index 2405bb79b8..ea436be3c1 100644 --- a/headers/private/kernel/util/AVLTreeMap.h +++ b/headers/private/kernel/util/AVLTreeMap.h @@ -26,7 +26,7 @@ namespace AVLTreeMapStrategy { typename Node; inline Node* Allocate(const Key& key, const Value& value) inline void Free(Node* node) - inline Key GetKey(Node* node) const + inline const Key GetKey(const Node* node) const inline Value& GetValue(Node* node) const inline AVLTreeNode* GetAVLTreeNode(Node* node) const inline Node* GetNode(AVLTreeNode* node) const @@ -622,7 +622,7 @@ public: fAllocator.Deallocate(node); } - inline Key& GetKey(Node* node) const + inline const Key& GetKey(const Node* node) const { return node->key; } diff --git a/headers/private/shared/StringForRate.h b/headers/private/shared/StringForRate.h new file mode 100644 index 0000000000..65e17407da --- /dev/null +++ b/headers/private/shared/StringForRate.h @@ -0,0 +1,24 @@ +/* + * Copyright 2010 Haiku Inc. All rights reserved. + * Distributed under the terms of the MIT License. + */ +#ifndef STRING_FOR_RATE_H +#define STRING_FOR_RATE_H + +#include + + +namespace BPrivate { + + +const char* string_for_rate(double rate, char* string, size_t stringSize, + double base = 1000.0f); + + +} // namespace BPrivate + + +using BPrivate::string_for_rate; + + +#endif // COLOR_QUANTIZER_H diff --git a/src/add-ons/kernel/debugger/Jamfile b/src/add-ons/kernel/debugger/Jamfile index dcf3f4a83d..ef05494cf9 100644 --- a/src/add-ons/kernel/debugger/Jamfile +++ b/src/add-ons/kernel/debugger/Jamfile @@ -7,5 +7,6 @@ SubInclude HAIKU_TOP src add-ons kernel debugger disasm ; SubInclude HAIKU_TOP src add-ons kernel debugger hangman ; SubInclude HAIKU_TOP src add-ons kernel debugger invalidate_on_exit ; SubInclude HAIKU_TOP src add-ons kernel debugger laplinkll ; +SubInclude HAIKU_TOP src add-ons kernel debugger qrencode ; SubInclude HAIKU_TOP src add-ons kernel debugger run_on_exit ; SubInclude HAIKU_TOP src add-ons kernel debugger usb_keyboard ; diff --git a/src/add-ons/kernel/debugger/qrencode/Jamfile b/src/add-ons/kernel/debugger/qrencode/Jamfile new file mode 100644 index 0000000000..c196777fbe --- /dev/null +++ b/src/add-ons/kernel/debugger/qrencode/Jamfile @@ -0,0 +1,22 @@ +SubDir HAIKU_TOP src add-ons kernel debugger qrencode ; + +UsePrivateHeaders kernel ; + +DEFINES += HAVE_CONFIG_H ; +DEFINES += malloc=debug_malloc ; +DEFINES += calloc=debug_calloc ; +DEFINES += free=debug_free ; + +KernelAddon qrencode : + bitstream.c + mask.c + mmask.c + mqrspec.c + qrencode.c + qrinput.c + qrspec.c + rscode.c + split.c + + module.cpp + ; diff --git a/src/add-ons/kernel/debugger/qrencode/bitstream.c b/src/add-ons/kernel/debugger/qrencode/bitstream.c new file mode 100644 index 0000000000..a0b9283cb0 --- /dev/null +++ b/src/add-ons/kernel/debugger/qrencode/bitstream.c @@ -0,0 +1,238 @@ +/* + * qrencode - QR Code encoder + * + * Binary sequence class. + * Copyright (C) 2006-2011 Kentaro Fukuchi + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#if HAVE_CONFIG_H +# include "config.h" +#endif +#include +#include +#include + +#include "bitstream.h" + +BitStream *BitStream_new(void) +{ + BitStream *bstream; + + bstream = (BitStream *)malloc(sizeof(BitStream)); + if(bstream == NULL) return NULL; + + bstream->length = 0; + bstream->data = NULL; + + return bstream; +} + +static int BitStream_allocate(BitStream *bstream, int length) +{ + unsigned char *data; + + if(bstream == NULL) { + return -1; + } + + data = (unsigned char *)malloc(length); + if(data == NULL) { + return -1; + } + + if(bstream->data) { + free(bstream->data); + } + bstream->length = length; + bstream->data = data; + + return 0; +} + +static BitStream *BitStream_newFromNum(int bits, unsigned int num) +{ + unsigned int mask; + int i; + unsigned char *p; + BitStream *bstream; + + bstream = BitStream_new(); + if(bstream == NULL) return NULL; + + if(BitStream_allocate(bstream, bits)) { + BitStream_free(bstream); + return NULL; + } + + p = bstream->data; + mask = 1 << (bits - 1); + for(i=0; i> 1; + } + + return bstream; +} + +static BitStream *BitStream_newFromBytes(int size, unsigned char *data) +{ + unsigned char mask; + int i, j; + unsigned char *p; + BitStream *bstream; + + bstream = BitStream_new(); + if(bstream == NULL) return NULL; + + if(BitStream_allocate(bstream, size * 8)) { + BitStream_free(bstream); + return NULL; + } + + p = bstream->data; + for(i=0; i> 1; + } + } + + return bstream; +} + +int BitStream_append(BitStream *bstream, BitStream *arg) +{ + unsigned char *data; + + if(arg == NULL) { + return -1; + } + if(arg->length == 0) { + return 0; + } + if(bstream->length == 0) { + if(BitStream_allocate(bstream, arg->length)) { + return -1; + } + memcpy(bstream->data, arg->data, arg->length); + return 0; + } + + data = (unsigned char *)malloc(bstream->length + arg->length); + if(data == NULL) { + return -1; + } + memcpy(data, bstream->data, bstream->length); + memcpy(data + bstream->length, arg->data, arg->length); + + free(bstream->data); + bstream->length += arg->length; + bstream->data = data; + + return 0; +} + +int BitStream_appendNum(BitStream *bstream, int bits, unsigned int num) +{ + BitStream *b; + int ret; + + if(bits == 0) return 0; + + b = BitStream_newFromNum(bits, num); + if(b == NULL) return -1; + + ret = BitStream_append(bstream, b); + BitStream_free(b); + + return ret; +} + +int BitStream_appendBytes(BitStream *bstream, int size, unsigned char *data) +{ + BitStream *b; + int ret; + + if(size == 0) return 0; + + b = BitStream_newFromBytes(size, data); + if(b == NULL) return -1; + + ret = BitStream_append(bstream, b); + BitStream_free(b); + + return ret; +} + +unsigned char *BitStream_toByte(BitStream *bstream) +{ + int i, j, size, bytes; + unsigned char *data, v; + unsigned char *p; + + size = BitStream_size(bstream); + if(size == 0) { + return NULL; + } + data = (unsigned char *)malloc((size + 7) / 8); + if(data == NULL) { + return NULL; + } + + bytes = size / 8; + + p = bstream->data; + for(i=0; idata); + free(bstream); + } +} diff --git a/src/add-ons/kernel/debugger/qrencode/bitstream.h b/src/add-ons/kernel/debugger/qrencode/bitstream.h new file mode 100644 index 0000000000..ffe743c5e0 --- /dev/null +++ b/src/add-ons/kernel/debugger/qrencode/bitstream.h @@ -0,0 +1,38 @@ +/* + * qrencode - QR Code encoder + * + * Binary sequence class. + * Copyright (C) 2006-2011 Kentaro Fukuchi + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef __BITSTREAM_H__ +#define __BITSTREAM_H__ + +typedef struct { + int length; + unsigned char *data; +} BitStream; + +extern BitStream *BitStream_new(void); +extern int BitStream_append(BitStream *bstream, BitStream *arg); +extern int BitStream_appendNum(BitStream *bstream, int bits, unsigned int num); +extern int BitStream_appendBytes(BitStream *bstream, int size, unsigned char *data); +#define BitStream_size(__bstream__) (__bstream__->length) +extern unsigned char *BitStream_toByte(BitStream *bstream); +extern void BitStream_free(BitStream *bstream); + +#endif /* __BITSTREAM_H__ */ diff --git a/src/add-ons/kernel/debugger/qrencode/config.h b/src/add-ons/kernel/debugger/qrencode/config.h new file mode 100644 index 0000000000..20515db864 --- /dev/null +++ b/src/add-ons/kernel/debugger/qrencode/config.h @@ -0,0 +1,5 @@ +#define MAJOR_VERSION 3 +#define MINOR_VERSION 3 +#define MICRO_VERSION 0 +#define VERSION "3.3.0" +#define __STATIC static diff --git a/src/add-ons/kernel/debugger/qrencode/mask.c b/src/add-ons/kernel/debugger/qrencode/mask.c new file mode 100644 index 0000000000..47c658ca0e --- /dev/null +++ b/src/add-ons/kernel/debugger/qrencode/mask.c @@ -0,0 +1,330 @@ +/* + * qrencode - QR Code encoder + * + * Masking. + * Copyright (C) 2006-2011 Kentaro Fukuchi + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#if HAVE_CONFIG_H +# include "config.h" +#endif +#include +#include +#include +#include + +#include "qrencode.h" +#include "qrspec.h" +#include "mask.h" + +__STATIC int Mask_writeFormatInformation(int width, unsigned char *frame, int mask, QRecLevel level) +{ + unsigned int format; + unsigned char v; + int i; + int blacks = 0; + + format = QRspec_getFormatInfo(mask, level); + + for(i=0; i<8; i++) { + if(format & 1) { + blacks += 2; + v = 0x85; + } else { + v = 0x84; + } + frame[width * 8 + width - 1 - i] = v; + if(i < 6) { + frame[width * i + 8] = v; + } else { + frame[width * (i + 1) + 8] = v; + } + format= format >> 1; + } + for(i=0; i<7; i++) { + if(format & 1) { + blacks += 2; + v = 0x85; + } else { + v = 0x84; + } + frame[width * (width - 7 + i) + 8] = v; + if(i == 0) { + frame[width * 8 + 7] = v; + } else { + frame[width * 8 + 6 - i] = v; + } + format= format >> 1; + } + + return blacks; +} + +/** + * Demerit coefficients. + * See Section 8.8.2, pp.45, JIS X0510:2004. + */ +#define N1 (3) +#define N2 (3) +#define N3 (40) +#define N4 (10) + +#define MASKMAKER(__exp__) \ + int x, y;\ + int b = 0;\ +\ + for(y=0; y= maskNum) { + errno = EINVAL; + return NULL; + } + + masked = (unsigned char *)malloc(width * width); + if(masked == NULL) return NULL; + + maskMakers[mask](width, frame, masked); + Mask_writeFormatInformation(width, masked, mask, level); + + return masked; +} + + +//static int n1; +//static int n2; +//static int n3; +//static int n4; + +__STATIC int Mask_calcN1N3(int length, int *runLength) +{ + int i; + int demerit = 0; + int fact; + + for(i=0; i= 5) { + demerit += N1 + (runLength[i] - 5); + //n1 += N1 + (runLength[i] - 5); + } + if((i & 1)) { + if(i >= 3 && i < length-2 && (runLength[i] % 3) == 0) { + fact = runLength[i] / 3; + if(runLength[i-2] == fact && + runLength[i-1] == fact && + runLength[i+1] == fact && + runLength[i+2] == fact) { + if(i == 3 || runLength[i-3] >= 4 * fact) { + demerit += N3; + //n3 += N3; + } else if(i+4 >= length || runLength[i+3] >= 4 * fact) { + demerit += N3; + //n3 += N3; + } + } + } + } + } + + return demerit; +} + +__STATIC int Mask_calcN2(int width, unsigned char *frame) +{ + int x, y; + unsigned char *p; + unsigned char b22, w22; + int demerit = 0; + + p = frame + width + 1; + for(y=1; y + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef __MASK_H__ +#define __MASK_H__ + +extern unsigned char *Mask_makeMask(int width, unsigned char *frame, int mask, QRecLevel level); +extern unsigned char *Mask_mask(int width, unsigned char *frame, QRecLevel level); + +#ifdef WITH_TESTS +extern int Mask_calcN2(int width, unsigned char *frame); +extern int Mask_calcN1N3(int length, int *runLength); +extern int Mask_calcRunLength(int width, unsigned char *frame, int dir, int *runLength); +extern int Mask_evaluateSymbol(int width, unsigned char *frame); +extern int Mask_writeFormatInformation(int width, unsigned char *frame, int mask, QRecLevel level); +extern unsigned char *Mask_makeMaskedFrame(int width, unsigned char *frame, int mask); +#endif + +#endif /* __MASK_H__ */ diff --git a/src/add-ons/kernel/debugger/qrencode/mmask.c b/src/add-ons/kernel/debugger/qrencode/mmask.c new file mode 100644 index 0000000000..99470f2008 --- /dev/null +++ b/src/add-ons/kernel/debugger/qrencode/mmask.c @@ -0,0 +1,177 @@ +/* + * qrencode - QR Code encoder + * + * Masking for Micro QR Code. + * Copyright (C) 2006-2011 Kentaro Fukuchi + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#if HAVE_CONFIG_H +# include "config.h" +#endif +#include +#include +#include +#include + +#include "qrencode.h" +#include "mqrspec.h" +#include "mmask.h" + +__STATIC void MMask_writeFormatInformation(int version, int width, unsigned char *frame, int mask, QRecLevel level) +{ + unsigned int format; + unsigned char v; + int i; + + format = MQRspec_getFormatInfo(mask, version, level); + + for(i=0; i<8; i++) { + v = 0x84 | (format & 1); + frame[width * (i + 1) + 8] = v; + format = format >> 1; + } + for(i=0; i<7; i++) { + v = 0x84 | (format & 1); + frame[width * 8 + 7 - i] = v; + format = format >> 1; + } +} + +#define MASKMAKER(__exp__) \ + int x, y;\ +\ + for(y=0; y= maskNum) { + errno = EINVAL; + return NULL; + } + + width = MQRspec_getWidth(version); + masked = (unsigned char *)malloc(width * width); + if(masked == NULL) return NULL; + + maskMakers[mask](width, frame, masked); + MMask_writeFormatInformation(version, width, masked, mask, level); + + return masked; +} + +__STATIC int MMask_evaluateSymbol(int width, unsigned char *frame) +{ + int x, y; + unsigned char *p; + int sum1 = 0, sum2 = 0; + + p = frame + width * (width - 1); + for(x=1; x maxScore) { + maxScore = score; + free(bestMask); + bestMask = mask; + mask = (unsigned char *)malloc(width * width); + if(mask == NULL) break; + } + } + free(mask); + return bestMask; +} diff --git a/src/add-ons/kernel/debugger/qrencode/mmask.h b/src/add-ons/kernel/debugger/qrencode/mmask.h new file mode 100644 index 0000000000..f6556e877d --- /dev/null +++ b/src/add-ons/kernel/debugger/qrencode/mmask.h @@ -0,0 +1,34 @@ +/* + * qrencode - QR Code encoder + * + * Masking for Micro QR Code. + * Copyright (C) 2006-2011 Kentaro Fukuchi + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef __MMASK_H__ +#define __MMASK_H__ + +extern unsigned char *MMask_makeMask(int version, unsigned char *frame, int mask, QRecLevel level); +extern unsigned char *MMask_mask(int version, unsigned char *frame, QRecLevel level); + +#ifdef WITH_TESTS +extern int MMask_evaluateSymbol(int width, unsigned char *frame); +extern void MMask_writeFormatInformation(int version, int width, unsigned char *frame, int mask, QRecLevel level); +extern unsigned char *MMask_makeMaskedFrame(int width, unsigned char *frame, int mask); +#endif + +#endif /* __MMASK_H__ */ diff --git a/src/add-ons/kernel/debugger/qrencode/module.cpp b/src/add-ons/kernel/debugger/qrencode/module.cpp new file mode 100644 index 0000000000..b2f215ddce --- /dev/null +++ b/src/add-ons/kernel/debugger/qrencode/module.cpp @@ -0,0 +1,389 @@ +/* + * Copyright 2012, Haiku, Inc. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Michael Lotz + */ + + +#include +#include +#include +#include + +extern "C" { +#include "qrencode.h" +#include "qrspec.h" +} + + +extern "C" { +extern void abort_debugger_command(); +} + + +static const char* kWebPostBaseURL = "http://mlotz.ch/q"; + +static char sStringBuffer[16 * 1024]; +static char sEncodeBuffer[3 * 1024]; +static int sBufferPosition = 0; +static int sQRCodeVersion = 19; +static QRecLevel sQRCodeLevel = QR_ECLEVEL_L; +static char sWebPostId[64]; +static int sWebPostCounter = 0; + + +static bool +qrcode_bit(QRcode* qrCode, int x, int y) +{ + if (x >= qrCode->width || y >= qrCode->width) + return false; + + return (qrCode->data[y * qrCode->width + x] & 0x01) == 1; +} + + +static void +move_to_and_clear_line(int line) +{ + kprintf(" \x1b[%dd\x1b[G\x1b[K", line + 1); +} + + +static bool +print_qrcode(QRcode* qrCode, bool waitForKey) +{ + move_to_and_clear_line(0); + for (int y = 0; y < qrCode->width; y += 2) { + move_to_and_clear_line(y / 2 + 1); + kputs(" "); + + for (int x = 0; x < qrCode->width; x++) { + bool upper = qrcode_bit(qrCode, x, y); + bool lower = qrcode_bit(qrCode, x, y + 1); + if (upper == lower) + kputs(upper ? "\x11" : " "); + else + kputs(upper ? "\x12" : "\x13"); + } + } + + move_to_and_clear_line(qrCode->width / 2 + 2); + move_to_and_clear_line(qrCode->width / 2 + 3); + + if (waitForKey) { + kputs("press q to abort or any other key to continue...\x1b[1B\x1b[G"); + if (kgetc() == 'q') + return false; + } + + return true; +} + + +static int +encode_url(const char* query, const char* data, int encodeLength, + int inputLength) +{ + sEncodeBuffer[0] = 0; + strlcat(sEncodeBuffer, kWebPostBaseURL, encodeLength + 1); + strlcat(sEncodeBuffer, "?i=", encodeLength + 1); + strlcat(sEncodeBuffer, sWebPostId, encodeLength + 1); + strlcat(sEncodeBuffer, "&", encodeLength + 1); + strlcat(sEncodeBuffer, query, encodeLength + 1); + int position = strlcat(sEncodeBuffer, "=", encodeLength + 1); + if (position > encodeLength) + return -1; + + int copyCount = 0; + while (inputLength > 0 && position < encodeLength) { + char character = data[copyCount]; + if ((character >= 'a' && character <= 'z') + || (character >= 'A' && character <= 'Z') + || (character >= '0' && character <= '9') + || character == '.' || character == '-' || character == '_' + || character == '~' + // These aren't strictly valid, but seem to work. + || character == '/' || character == '(' || character == ')' + || character == '=' || character == '^' || character == '?' + || character == '|' || character == '*' || character == '@' + || character == ';' || character == ':' || character == ',' + || character == '{' || character == '}' || character == '[' + || character == ']' || character == '<' || character == '>' + || character == '!' || character == '\\') { + sEncodeBuffer[position++] = character; + sEncodeBuffer[position] = 0; + } else if (character == ' ') { + // Encode spaces as '+' as that's shorter than %20. + sEncodeBuffer[position++] = '+'; + sEncodeBuffer[position] = 0; + } else { + // Encode to a %xx escape. + if (encodeLength - position < 3) { + // Doesn't fit anymore, we're done. + break; + } + + char escaped[4]; + sprintf(escaped, "%%%.2x", character); + position = strlcat(sEncodeBuffer, escaped, encodeLength + 1); + } + + inputLength--; + copyCount++; + } + + return copyCount; +} + + +static int +qrencode(int argc, char* argv[]) +{ + if (argc > 1 && strcmp(argv[1], "--help") == 0) { + kprintf("%s []\n", argv[0]); + kprintf("If an argument is given, encodes that string as a QR code.\n" + "Otherwise the current QR buffer is encoded as QR codes.\n" + "When encoding from the QR buffer, the buffer is left intact.\n" + "use qrclear to clear the QR buffer or qrflush to encode/clear.\n"); + return 1; + } + + const char* source = NULL; + if (argc < 2) { + sStringBuffer[sBufferPosition] = 0; + source = sStringBuffer; + } else + source = argv[1]; + + int inputLength = strlen(source); + int encodeLength = QRspec_getDataLength(sQRCodeVersion, sQRCodeLevel) - 3; + while (inputLength > 0) { + int copyCount = 0; + if (sWebPostId[0] != 0) { + copyCount = encode_url(sWebPostCounter++ == 0 ? "clear" : "d", + source, encodeLength, inputLength); + if (copyCount < 0) { + kprintf("Failed to URL encode buffer.\n"); + QRcode_clearCache(); + return 1; + } + } else { + copyCount = inputLength < encodeLength ? inputLength : encodeLength; + memcpy(sEncodeBuffer, source, copyCount); + sEncodeBuffer[copyCount] = 0; + } + + QRcode* qrCode = QRcode_encodeString8bit(sEncodeBuffer, sQRCodeVersion, + sQRCodeLevel); + if (qrCode == NULL) { + kprintf("Failed to encode buffer into qr code.\n"); + QRcode_clearCache(); + return 1; + } + + source += copyCount; + inputLength -= copyCount; + + bool doContinue = print_qrcode(qrCode, inputLength > 0); + QRcode_free(qrCode); + + if (!doContinue) { + QRcode_clearCache(); + abort_debugger_command(); + // Does not return. + } + } + + QRcode_clearCache(); + return 0; +} + + +static int +qrclear(int argc, char* argv[]) +{ + if (argc > 1 && strcmp(argv[1], "--help") == 0) { + kprintf("Clears the current QR buffer.\n"); + return 0; + } + + sBufferPosition = 0; + sStringBuffer[0] = 0; + return 0; +} + + +static int +qrflush(int argc, char* argv[]) +{ + if (argc > 1 && strcmp(argv[1], "--help") == 0) { + kprintf("Flushes the current QR buffer by encoding QR codes from\n" + "the data and then clears the QR buffer.\n"); + return 1; + } + + qrencode(0, NULL); + qrclear(0, NULL); + return 0; +} + + +static int +qrappend(int argc, char* argv[]) +{ + if (argc < 2 || (argc > 1 && strcmp(argv[1], "--help") == 0)) { + kprintf("%s \n", argv[0]); + kprintf("Appends the given string to the QR buffer. Can be used as\n" + "the target of a pipe command to accumulate the output of other\n" + "commands in the QR buffer.\n" + "Note that this command will flush the QR buffer when it runs\n" + "full to make room for the new string. This will cause QR codes\n" + "to be generated while the append command still runs. As these\n" + "implicit flushes only happen to make room in the buffer, the\n" + "strings are afterwards appended aren't flushed, so make sure to\n" + "execute the qrflush command to generate codes for these as\n" + "well.\n"); + return 1; + } + + const char* source = argv[1]; + int length = strlen(source) + 1; + + while (length > 0) { + int copyCount = sizeof(sStringBuffer) - sBufferPosition - 1; + if (copyCount == 0) { + // The buffer is full, we need to flush it. + qrflush(0, NULL); + } + + if (length < copyCount) + copyCount = length; + + memcpy(sStringBuffer + sBufferPosition, source, copyCount); + sBufferPosition += copyCount; + source += copyCount; + length -= copyCount; + } + + // Overwrite the 0 byte that was copied extra with a newline. + if (sBufferPosition > 0) + sStringBuffer[sBufferPosition - 1] = '\n'; + + return 0; +} + + +static int +qrconfig(int argc, char* argv[]) +{ + if (argc < 2 || (argc > 1 && strcmp(argv[1], "--help") == 0)) { + kprintf("%s \n", argv[0]); + kprintf("Sets the QR version to be used. Valid versions range from 1\n" + "to 40 where each version determines the size of the QR code.\n" + "Version 1 is 21x21 in size and each version increments that size\n" + "by 4x4 up to 177x177 for version 40.\n" + "Bigger QR codes can hold more data, so ideally you use a version\n" + "that makes the QR code as big as possible while still fitting on\n" + "your screen.\n" + "You can test the new config by running qrencode\n" + "with a test string.\n" + "Note that due to memory constraints in the kernel debugger, some\n" + "of the higher versions may actually not work. The kernel\n" + "debugger heap size can be adjusted using the KDEBUG_HEAP define\n" + "in the kernel_debug_config.h header\n"); + return 1; + } + + int newVersion = atoi(argv[1]); + if (newVersion <= 0 || newVersion > 40) { + kprintf("Invalid QR code version supplied, " + "must be between 1 and 40.\n"); + return 1; + } + + sQRCodeVersion = newVersion; + return 0; +} + + +static int +qrwebpost(int argc, char* argv[]) +{ + if (argc >= 3 && strcmp(argv[1], "start") == 0) { + strlcpy(sWebPostId, argv[2], sizeof(sWebPostId)); + sWebPostCounter = 0; + + // Generate the clear code. + const char* args[2] = { "", "yes" }; + qrencode(2, (char**)args); + } else if (argc >= 2 && strcmp(argv[1], "stop") == 0) { + sWebPostId[0] = 0; + } else { + kprintf("%s start \n", argv[0]); + kprintf("%s stop\n", argv[0]); + kprintf("Causes the QR codes to be rendered as URLs that resolve to\n" + "a service that allows appending the data of multiple QR codes\n" + "for easier handling.\n" + "An initial QR code is generated that invokes a clear operation\n" + "to prepare the output file on the server.\n" + "Note that there is no logic behind the service, if you and\n" + "someone else use the same id at the same time, they will\n" + "overwrite eachother. Therefore use a reasonably unique name.\n"); + return 1; + } + + return 0; +} + + +static status_t +std_ops(int32 op, ...) +{ + switch (op) { + case B_MODULE_INIT: + add_debugger_command("qrencode", qrencode, + "encodes a string / the current QR buffer"); + add_debugger_command("qrappend", qrappend, + "appends a string to the QR buffer"); + add_debugger_command("qrclear", qrclear, + "clears the current QR buffer"); + add_debugger_command("qrflush", qrflush, + "encodes the current QR buffer and clears it"); + add_debugger_command("qrconfig", qrconfig, + "sets the QR code version to be used"); + add_debugger_command("qrwebpost", qrwebpost, + "sets up URL encoding for QR codes"); + return B_OK; + case B_MODULE_UNINIT: + remove_debugger_command("qrencode", qrencode); + remove_debugger_command("qrappend", qrappend); + remove_debugger_command("qrclear", qrclear); + remove_debugger_command("qrflush", qrflush); + remove_debugger_command("qrconfig", qrconfig); + remove_debugger_command("qrwebpost", qrwebpost); + return B_OK; + } + + return B_BAD_VALUE; +} + + +static struct debugger_module_info sModuleInfo = { + { + "debugger/qrencode/v1", + 0, + &std_ops + }, + NULL, + NULL, + NULL, + NULL +}; + +module_info *modules[] = { + (module_info *)&sModuleInfo, + NULL +}; + diff --git a/src/add-ons/kernel/debugger/qrencode/mqrspec.c b/src/add-ons/kernel/debugger/qrencode/mqrspec.c new file mode 100644 index 0000000000..76d2d261a6 --- /dev/null +++ b/src/add-ons/kernel/debugger/qrencode/mqrspec.c @@ -0,0 +1,280 @@ +/* + * qrencode - QR Code encoder + * + * Micor QR Code specification in convenient format. + * Copyright (C) 2006-2011 Kentaro Fukuchi + * + * The following data / specifications are taken from + * "Two dimensional symbol -- QR-code -- Basic Specification" (JIS X0510:2004) + * or + * "Automatic identification and data capture techniques -- + * QR Code 2005 bar code symbology specification" (ISO/IEC 18004:2006) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#if HAVE_CONFIG_H +# include "config.h" +#endif +#include +#include +#include +#include +#ifdef HAVE_LIBPTHREAD +#include +#endif + +#include "mqrspec.h" + +/****************************************************************************** + * Version and capacity + *****************************************************************************/ + +typedef struct { + int width; //< Edge length of the symbol + int ec[4]; //< Number of ECC code (bytes) +} MQRspec_Capacity; + +/** + * Table of the capacity of symbols + * See Table 1 (pp.106) and Table 8 (pp.113) of Appendix 1, JIS X0510:2004. + */ +static const MQRspec_Capacity mqrspecCapacity[MQRSPEC_VERSION_MAX + 1] = { + { 0, {0, 0, 0, 0}}, + { 11, {2, 0, 0, 0}}, + { 13, {5, 6, 0, 0}}, + { 15, {6, 8, 0, 0}}, + { 17, {8, 10, 14, 0}} +}; + +int MQRspec_getDataLengthBit(int version, QRecLevel level) +{ + int w; + int ecc; + + w = mqrspecCapacity[version].width - 1; + ecc = mqrspecCapacity[version].ec[level]; + if(ecc == 0) return 0; + return w * w - 64 - ecc * 8; +} + +int MQRspec_getDataLength(int version, QRecLevel level) +{ + return (MQRspec_getDataLengthBit(version, level) + 4) / 8; +} + +int MQRspec_getECCLength(int version, QRecLevel level) +{ + return mqrspecCapacity[version].ec[level]; +} + +int MQRspec_getWidth(int version) +{ + return mqrspecCapacity[version].width; +} + +/****************************************************************************** + * Length indicator + *****************************************************************************/ + +/** + * See Table 3 (pp.107) of Appendix 1, JIS X0510:2004. + */ +static const int lengthTableBits[4][4] = { + { 3, 4, 5, 6}, + { 0, 3, 4, 5}, + { 0, 0, 4, 5}, + { 0, 0, 3, 4} +}; + +int MQRspec_lengthIndicator(QRencodeMode mode, int version) +{ + return lengthTableBits[mode][version - 1]; +} + +int MQRspec_maximumWords(QRencodeMode mode, int version) +{ + int bits; + int words; + + bits = lengthTableBits[mode][version - 1]; + words = (1 << bits) - 1; + if(mode == QR_MODE_KANJI) { + words *= 2; // the number of bytes is required + } + + return words; +} + +/****************************************************************************** + * Format information + *****************************************************************************/ + +/* See calcFormatInfo in tests/test_mqrspec.c */ +static const unsigned int formatInfo[4][8] = { + {0x4445, 0x55ae, 0x6793, 0x7678, 0x06de, 0x1735, 0x2508, 0x34e3}, + {0x4172, 0x5099, 0x62a4, 0x734f, 0x03e9, 0x1202, 0x203f, 0x31d4}, + {0x4e2b, 0x5fc0, 0x6dfd, 0x7c16, 0x0cb0, 0x1d5b, 0x2f66, 0x3e8d}, + {0x4b1c, 0x5af7, 0x68ca, 0x7921, 0x0987, 0x186c, 0x2a51, 0x3bba} +}; + +/* See Table 10 of Appendix 1. (pp.115) */ +static const int typeTable[MQRSPEC_VERSION_MAX + 1][3] = { + {-1, -1, -1}, + { 0, -1, -1}, + { 1, 2, -1}, + { 3, 4, -1}, + { 5, 6, 7} +}; + +unsigned int MQRspec_getFormatInfo(int mask, int version, QRecLevel level) +{ + int type; + + if(mask < 0 || mask > 3) return 0; + if(version <= 0 || version > MQRSPEC_VERSION_MAX) return 0; + if(level == QR_ECLEVEL_H) return 0; + type = typeTable[version][level]; + if(type < 0) return 0; + + return formatInfo[mask][type]; +} + +/****************************************************************************** + * Frame + *****************************************************************************/ + +/** + * Cache of initial frames. + */ +/* C99 says that static storage shall be initialized to a null pointer + * by compiler. */ +static unsigned char *frames[MQRSPEC_VERSION_MAX + 1]; +#ifdef HAVE_LIBPTHREAD +static pthread_mutex_t frames_mutex = PTHREAD_MUTEX_INITIALIZER; +#endif + +/** + * Put a finder pattern. + * @param frame + * @param width + * @param ox,oy upper-left coordinate of the pattern + */ +static void putFinderPattern(unsigned char *frame, int width, int ox, int oy) +{ + static const unsigned char finder[] = { + 0xc1, 0xc1, 0xc1, 0xc1, 0xc1, 0xc1, 0xc1, + 0xc1, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc1, + 0xc1, 0xc0, 0xc1, 0xc1, 0xc1, 0xc0, 0xc1, + 0xc1, 0xc0, 0xc1, 0xc1, 0xc1, 0xc0, 0xc1, + 0xc1, 0xc0, 0xc1, 0xc1, 0xc1, 0xc0, 0xc1, + 0xc1, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc1, + 0xc1, 0xc1, 0xc1, 0xc1, 0xc1, 0xc1, 0xc1, + }; + int x, y; + const unsigned char *s; + + frame += oy * width + ox; + s = finder; + for(y=0; y<7; y++) { + for(x=0; x<7; x++) { + frame[x] = s[x]; + } + frame += width; + s += 7; + } +} + +static unsigned char *MQRspec_createFrame(int version) +{ + unsigned char *frame, *p, *q; + int width; + int x, y; + + width = mqrspecCapacity[version].width; + frame = (unsigned char *)malloc(width * width); + if(frame == NULL) return NULL; + + memset(frame, 0, width * width); + /* Finder pattern */ + putFinderPattern(frame, width, 0, 0); + /* Separator */ + p = frame; + for(y=0; y<7; y++) { + p[7] = 0xc0; + p += width; + } + memset(frame + width * 7, 0xc0, 8); + /* Mask format information area */ + memset(frame + width * 8 + 1, 0x84, 8); + p = frame + width + 8; + for(y=0; y<7; y++) { + *p = 0x84; + p += width; + } + /* Timing pattern */ + p = frame + 8; + q = frame + width * 8; + for(x=1; x MQRSPEC_VERSION_MAX) return NULL; + +#ifdef HAVE_LIBPTHREAD + pthread_mutex_lock(&frames_mutex); +#endif + if(frames[version] == NULL) { + frames[version] = MQRspec_createFrame(version); + } +#ifdef HAVE_LIBPTHREAD + pthread_mutex_unlock(&frames_mutex); +#endif + if(frames[version] == NULL) return NULL; + + width = mqrspecCapacity[version].width; + frame = (unsigned char *)malloc(width * width); + if(frame == NULL) return NULL; + memcpy(frame, frames[version], width * width); + + return frame; +} + +void MQRspec_clearCache(void) +{ + int i; + +#ifdef HAVE_LIBPTHREAD + pthread_mutex_lock(&frames_mutex); +#endif + for(i=1; i<=MQRSPEC_VERSION_MAX; i++) { + free(frames[i]); + frames[i] = NULL; + } +#ifdef HAVE_LIBPTHREAD + pthread_mutex_unlock(&frames_mutex); +#endif +} diff --git a/src/add-ons/kernel/debugger/qrencode/mqrspec.h b/src/add-ons/kernel/debugger/qrencode/mqrspec.h new file mode 100644 index 0000000000..2d4b90d281 --- /dev/null +++ b/src/add-ons/kernel/debugger/qrencode/mqrspec.h @@ -0,0 +1,157 @@ +/* + * qrencode - QR Code encoder + * + * Micro QR Code specification in convenient format. + * Copyright (C) 2006-2011 Kentaro Fukuchi + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef __MQRSPEC_H__ +#define __MQRSPEC_H__ + +#include "qrencode.h" + +/****************************************************************************** + * Version and capacity + *****************************************************************************/ + +/** + * Maximum width of a symbol + */ +#define MQRSPEC_WIDTH_MAX 17 + +/** + * Return maximum data code length (bits) for the version. + * @param version + * @param level + * @return maximum size (bits) + */ +extern int MQRspec_getDataLengthBit(int version, QRecLevel level); + +/** + * Return maximum data code length (bytes) for the version. + * @param version + * @param level + * @return maximum size (bytes) + */ +extern int MQRspec_getDataLength(int version, QRecLevel level); + +/** + * Return maximum error correction code length (bytes) for the version. + * @param version + * @param level + * @return ECC size (bytes) + */ +extern int MQRspec_getECCLength(int version, QRecLevel level); + +/** + * Return a version number that satisfies the input code length. + * @param size input code length (byte) + * @param level + * @return version number + */ +extern int MQRspec_getMinimumVersion(int size, QRecLevel level); + +/** + * Return the width of the symbol for the version. + * @param version + * @return width + */ +extern int MQRspec_getWidth(int version); + +/** + * Return the numer of remainder bits. + * @param version + * @return number of remainder bits + */ +extern int MQRspec_getRemainder(int version); + +/****************************************************************************** + * Length indicator + *****************************************************************************/ + +/** + * Return the size of lenght indicator for the mode and version. + * @param mode + * @param version + * @return the size of the appropriate length indicator (bits). + */ +extern int MQRspec_lengthIndicator(QRencodeMode mode, int version); + +/** + * Return the maximum length for the mode and version. + * @param mode + * @param version + * @return the maximum length (bytes) + */ +extern int MQRspec_maximumWords(QRencodeMode mode, int version); + +/****************************************************************************** + * Version information pattern + *****************************************************************************/ + +/** + * Return BCH encoded version information pattern that is used for the symbol + * of version 7 or greater. Use lower 18 bits. + * @param version + * @return BCH encoded version information pattern + */ +extern unsigned int MQRspec_getVersionPattern(int version); + +/****************************************************************************** + * Format information + *****************************************************************************/ + +/** + * Return BCH encoded format information pattern. + * @param mask + * @param version + * @param level + * @return BCH encoded format information pattern + */ +extern unsigned int MQRspec_getFormatInfo(int mask, int version, QRecLevel level); + +/****************************************************************************** + * Frame + *****************************************************************************/ + +/** + * Return a copy of initialized frame. + * When the same version is requested twice or more, a copy of cached frame + * is returned. + * @param version + * @return Array of unsigned char. You can free it by free(). + */ +extern unsigned char *MQRspec_newFrame(int version); + +/** + * Clear the frame cache. Typically for debug. + */ +extern void MQRspec_clearCache(void); + +/****************************************************************************** + * Mode indicator + *****************************************************************************/ + +/** + * Mode indicator. See Table 2 in Appendix 1 of JIS X0510:2004, pp.107. + */ +#define MQRSPEC_MODEID_NUM 0 +#define MQRSPEC_MODEID_AN 1 +#define MQRSPEC_MODEID_8 2 +#define MQRSPEC_MODEID_KANJI 3 + +#endif /* __MQRSPEC_H__ */ diff --git a/src/add-ons/kernel/debugger/qrencode/qrencode.c b/src/add-ons/kernel/debugger/qrencode/qrencode.c new file mode 100644 index 0000000000..d166a24812 --- /dev/null +++ b/src/add-ons/kernel/debugger/qrencode/qrencode.c @@ -0,0 +1,929 @@ +/* + * qrencode - QR Code encoder + * + * Copyright (C) 2006-2012 Kentaro Fukuchi + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#if HAVE_CONFIG_H +# include "config.h" +#endif +#include +#include +#include +#include + +#include "qrencode.h" +#include "qrspec.h" +#include "mqrspec.h" +#include "bitstream.h" +#include "qrinput.h" +#include "rscode.h" +#include "split.h" +#include "mask.h" +#include "mmask.h" + +/****************************************************************************** + * Raw code + *****************************************************************************/ + +typedef struct { + int dataLength; + unsigned char *data; + int eccLength; + unsigned char *ecc; +} RSblock; + +typedef struct { + int version; + int dataLength; + int eccLength; + unsigned char *datacode; + unsigned char *ecccode; + int b1; + int blocks; + RSblock *rsblock; + int count; +} QRRawCode; + +static void RSblock_initBlock(RSblock *block, int dl, unsigned char *data, int el, unsigned char *ecc, RS *rs) +{ + block->dataLength = dl; + block->data = data; + block->eccLength = el; + block->ecc = ecc; + + encode_rs_char(rs, data, ecc); +} + +static int RSblock_init(RSblock *blocks, int spec[5], unsigned char *data, unsigned char *ecc) +{ + int i; + RSblock *block; + unsigned char *dp, *ep; + RS *rs; + int el, dl; + + dl = QRspec_rsDataCodes1(spec); + el = QRspec_rsEccCodes1(spec); + rs = init_rs(8, 0x11d, 0, 1, el, 255 - dl - el); + if(rs == NULL) return -1; + + block = blocks; + dp = data; + ep = ecc; + for(i=0; idatacode = QRinput_getByteStream(input); + if(raw->datacode == NULL) { + free(raw); + return NULL; + } + + QRspec_getEccSpec(input->version, input->level, spec); + + raw->version = input->version; + raw->b1 = QRspec_rsBlockNum1(spec); + raw->dataLength = QRspec_rsDataLength(spec); + raw->eccLength = QRspec_rsEccLength(spec); + raw->ecccode = (unsigned char *)malloc(raw->eccLength); + if(raw->ecccode == NULL) { + free(raw->datacode); + free(raw); + return NULL; + } + + raw->blocks = QRspec_rsBlockNum(spec); + raw->rsblock = (RSblock *)calloc(sizeof(RSblock), raw->blocks); + if(raw->rsblock == NULL) { + QRraw_free(raw); + return NULL; + } + ret = RSblock_init(raw->rsblock, spec, raw->datacode, raw->ecccode); + if(ret < 0) { + QRraw_free(raw); + return NULL; + } + + raw->count = 0; + + return raw; +} + +/** + * Return a code (byte). + * This function can be called iteratively. + * @param raw raw code. + * @return code + */ +__STATIC unsigned char QRraw_getCode(QRRawCode *raw) +{ + int col, row; + unsigned char ret; + + if(raw->count < raw->dataLength) { + row = raw->count % raw->blocks; + col = raw->count / raw->blocks; + if(col >= raw->rsblock[0].dataLength) { + row += raw->b1; + } + ret = raw->rsblock[row].data[col]; + } else if(raw->count < raw->dataLength + raw->eccLength) { + row = (raw->count - raw->dataLength) % raw->blocks; + col = (raw->count - raw->dataLength) / raw->blocks; + ret = raw->rsblock[row].ecc[col]; + } else { + return 0; + } + raw->count++; + return ret; +} + +__STATIC void QRraw_free(QRRawCode *raw) +{ + if(raw != NULL) { + free(raw->datacode); + free(raw->ecccode); + free(raw->rsblock); + free(raw); + } +} + +/****************************************************************************** + * Raw code for Micro QR Code + *****************************************************************************/ + +typedef struct { + int version; + int dataLength; + int eccLength; + unsigned char *datacode; + unsigned char *ecccode; + RSblock *rsblock; + int oddbits; + int count; +} MQRRawCode; + +__STATIC void MQRraw_free(MQRRawCode *raw); +__STATIC MQRRawCode *MQRraw_new(QRinput *input) +{ + MQRRawCode *raw; + RS *rs; + + raw = (MQRRawCode *)malloc(sizeof(MQRRawCode)); + if(raw == NULL) return NULL; + + raw->version = input->version; + raw->dataLength = MQRspec_getDataLength(input->version, input->level); + raw->eccLength = MQRspec_getECCLength(input->version, input->level); + raw->oddbits = raw->dataLength * 8 - MQRspec_getDataLengthBit(input->version, input->level); + raw->datacode = QRinput_getByteStream(input); + if(raw->datacode == NULL) { + free(raw); + return NULL; + } + raw->ecccode = (unsigned char *)malloc(raw->eccLength); + if(raw->ecccode == NULL) { + free(raw->datacode); + free(raw); + return NULL; + } + + raw->rsblock = (RSblock *)calloc(sizeof(RSblock), 1); + if(raw->rsblock == NULL) { + MQRraw_free(raw); + return NULL; + } + + rs = init_rs(8, 0x11d, 0, 1, raw->eccLength, 255 - raw->dataLength - raw->eccLength); + if(rs == NULL) { + MQRraw_free(raw); + return NULL; + } + + RSblock_initBlock(raw->rsblock, raw->dataLength, raw->datacode, raw->eccLength, raw->ecccode, rs); + + raw->count = 0; + + return raw; +} + +/** + * Return a code (byte). + * This function can be called iteratively. + * @param raw raw code. + * @return code + */ +__STATIC unsigned char MQRraw_getCode(MQRRawCode *raw) +{ + unsigned char ret; + + if(raw->count < raw->dataLength) { + ret = raw->datacode[raw->count]; + } else if(raw->count < raw->dataLength + raw->eccLength) { + ret = raw->ecccode[raw->count - raw->dataLength]; + } else { + return 0; + } + raw->count++; + return ret; +} + +__STATIC void MQRraw_free(MQRRawCode *raw) +{ + if(raw != NULL) { + free(raw->datacode); + free(raw->ecccode); + free(raw->rsblock); + free(raw); + } +} + + +/****************************************************************************** + * Frame filling + *****************************************************************************/ + +typedef struct { + int width; + unsigned char *frame; + int x, y; + int dir; + int bit; + int mqr; +} FrameFiller; + +static FrameFiller *FrameFiller_new(int width, unsigned char *frame, int mqr) +{ + FrameFiller *filler; + + filler = (FrameFiller *)malloc(sizeof(FrameFiller)); + if(filler == NULL) return NULL; + filler->width = width; + filler->frame = frame; + filler->x = width - 1; + filler->y = width - 1; + filler->dir = -1; + filler->bit = -1; + filler->mqr = mqr; + + return filler; +} + +static unsigned char *FrameFiller_next(FrameFiller *filler) +{ + unsigned char *p; + int x, y, w; + + if(filler->bit == -1) { + filler->bit = 0; + return filler->frame + filler->y * filler->width + filler->x; + } + + x = filler->x; + y = filler->y; + p = filler->frame; + w = filler->width; + + if(filler->bit == 0) { + x--; + filler->bit++; + } else { + x++; + y += filler->dir; + filler->bit--; + } + + if(filler->dir < 0) { + if(y < 0) { + y = 0; + x -= 2; + filler->dir = 1; + if(!filler->mqr && x == 6) { + x--; + y = 9; + } + } + } else { + if(y == w) { + y = w - 1; + x -= 2; + filler->dir = -1; + if(!filler->mqr && x == 6) { + x--; + y -= 8; + } + } + } + if(x < 0 || y < 0) return NULL; + + filler->x = x; + filler->y = y; + + if(p[y * w + x] & 0x80) { + // This tail recursion could be optimized. + return FrameFiller_next(filler); + } + return &p[y * w + x]; +} + +#ifdef WITH_TESTS +extern unsigned char *FrameFiller_test(int version) +{ + int width; + unsigned char *frame, *p; + FrameFiller *filler; + int i, length; + + width = QRspec_getWidth(version); + frame = QRspec_newFrame(version); + if(frame == NULL) return NULL; + filler = FrameFiller_new(width, frame, 0); + if(filler == NULL) { + free(frame); + return NULL; + } + length = QRspec_getDataLength(version, QR_ECLEVEL_L) * 8 + + QRspec_getECCLength(version, QR_ECLEVEL_L) * 8 + + QRspec_getRemainder(version); + for(i=0; iversion = version; + qrcode->width = width; + qrcode->data = data; + + return qrcode; +} + +void QRcode_free(QRcode *qrcode) +{ + if(qrcode != NULL) { + free(qrcode->data); + free(qrcode); + } +} + +__STATIC QRcode *QRcode_encodeMask(QRinput *input, int mask) +{ + int width, version; + QRRawCode *raw; + unsigned char *frame, *masked, *p, code, bit; + FrameFiller *filler; + int i, j; + QRcode *qrcode = NULL; + + if(input->mqr) { + errno = EINVAL; + return NULL; + } + if(input->version < 0 || input->version > QRSPEC_VERSION_MAX) { + errno = EINVAL; + return NULL; + } + if(input->level > QR_ECLEVEL_H) { + errno = EINVAL; + return NULL; + } + + raw = QRraw_new(input); + if(raw == NULL) return NULL; + + version = raw->version; + width = QRspec_getWidth(version); + frame = QRspec_newFrame(version); + if(frame == NULL) { + QRraw_free(raw); + return NULL; + } + filler = FrameFiller_new(width, frame, 0); + if(filler == NULL) { + QRraw_free(raw); + free(frame); + return NULL; + } + + /* inteleaved data and ecc codes */ + for(i=0; idataLength + raw->eccLength; i++) { + code = QRraw_getCode(raw); + bit = 0x80; + for(j=0; j<8; j++) { + p = FrameFiller_next(filler); + if(p == NULL) goto EXIT; + *p = 0x02 | ((bit & code) != 0); + bit = bit >> 1; + } + } + QRraw_free(raw); + raw = NULL; + /* remainder bits */ + j = QRspec_getRemainder(version); + for(i=0; ilevel); + } else { + masked = Mask_makeMask(width, frame, mask, input->level); + } + if(masked == NULL) { + goto EXIT; + } + qrcode = QRcode_new(version, width, masked); + +EXIT: + QRraw_free(raw); + free(filler); + free(frame); + return qrcode; +} + +__STATIC QRcode *QRcode_encodeMaskMQR(QRinput *input, int mask) +{ + int width, version; + MQRRawCode *raw; + unsigned char *frame, *masked, *p, code, bit; + FrameFiller *filler; + int i, j; + QRcode *qrcode = NULL; + + if(!input->mqr) { + errno = EINVAL; + return NULL; + } + if(input->version <= 0 || input->version > MQRSPEC_VERSION_MAX) { + errno = EINVAL; + return NULL; + } + if(input->level > QR_ECLEVEL_Q) { + errno = EINVAL; + return NULL; + } + + raw = MQRraw_new(input); + if(raw == NULL) return NULL; + + version = raw->version; + width = MQRspec_getWidth(version); + frame = MQRspec_newFrame(version); + if(frame == NULL) { + MQRraw_free(raw); + return NULL; + } + filler = FrameFiller_new(width, frame, 1); + if(filler == NULL) { + MQRraw_free(raw); + free(frame); + return NULL; + } + + /* inteleaved data and ecc codes */ + for(i=0; idataLength + raw->eccLength; i++) { + code = MQRraw_getCode(raw); + if(raw->oddbits && i == raw->dataLength - 1) { + bit = 1 << raw->oddbits; + for(j=0; joddbits; j++) { + p = FrameFiller_next(filler); + if(p == NULL) goto EXIT; + *p = 0x02 | ((bit & code) != 0); + bit = bit >> 1; + } + } else { + bit = 0x80; + for(j=0; j<8; j++) { + p = FrameFiller_next(filler); + if(p == NULL) goto EXIT; + *p = 0x02 | ((bit & code) != 0); + bit = bit >> 1; + } + } + } + MQRraw_free(raw); + raw = NULL; + + /* masking */ + if(mask < 0) { + masked = MMask_mask(version, frame, input->level); + } else { + masked = MMask_makeMask(version, frame, mask, input->level); + } + if(masked == NULL) { + goto EXIT; + } + + qrcode = QRcode_new(version, width, masked); + +EXIT: + MQRraw_free(raw); + free(filler); + free(frame); + return qrcode; +} + +QRcode *QRcode_encodeInput(QRinput *input) +{ + if(input->mqr) { + return QRcode_encodeMaskMQR(input, -1); + } else { + return QRcode_encodeMask(input, -1); + } +} + +static QRcode *QRcode_encodeStringReal(const char *string, int version, QRecLevel level, int mqr, QRencodeMode hint, int casesensitive) +{ + QRinput *input; + QRcode *code; + int ret; + + if(string == NULL) { + errno = EINVAL; + return NULL; + } + if(hint != QR_MODE_8 && hint != QR_MODE_KANJI) { + errno = EINVAL; + return NULL; + } + + if(mqr) { + input = QRinput_newMQR(version, level); + } else { + input = QRinput_new2(version, level); + } + if(input == NULL) return NULL; + + ret = Split_splitStringToQRinput(string, input, hint, casesensitive); + if(ret < 0) { + QRinput_free(input); + return NULL; + } + code = QRcode_encodeInput(input); + QRinput_free(input); + + return code; +} + +QRcode *QRcode_encodeString(const char *string, int version, QRecLevel level, QRencodeMode hint, int casesensitive) +{ + return QRcode_encodeStringReal(string, version, level, 0, hint, casesensitive); +} + +QRcode *QRcode_encodeStringMQR(const char *string, int version, QRecLevel level, QRencodeMode hint, int casesensitive) +{ + return QRcode_encodeStringReal(string, version, level, 1, hint, casesensitive); +} + +static QRcode *QRcode_encodeDataReal(const unsigned char *data, int length, int version, QRecLevel level, int mqr) +{ + QRinput *input; + QRcode *code; + int ret; + + if(data == NULL || length == 0) { + errno = EINVAL; + return NULL; + } + + if(mqr) { + input = QRinput_newMQR(version, level); + } else { + input = QRinput_new2(version, level); + } + if(input == NULL) return NULL; + + ret = QRinput_append(input, QR_MODE_8, length, data); + if(ret < 0) { + QRinput_free(input); + return NULL; + } + code = QRcode_encodeInput(input); + QRinput_free(input); + + return code; +} + +QRcode *QRcode_encodeData(int size, const unsigned char *data, int version, QRecLevel level) +{ + return QRcode_encodeDataReal(data, size, version, level, 0); +} + +QRcode *QRcode_encodeString8bit(const char *string, int version, QRecLevel level) +{ + if(string == NULL) { + errno = EINVAL; + return NULL; + } + return QRcode_encodeDataReal((unsigned char *)string, strlen(string), version, level, 0); +} + +QRcode *QRcode_encodeDataMQR(int size, const unsigned char *data, int version, QRecLevel level) +{ + return QRcode_encodeDataReal(data, size, version, level, 1); +} + +QRcode *QRcode_encodeString8bitMQR(const char *string, int version, QRecLevel level) +{ + if(string == NULL) { + errno = EINVAL; + return NULL; + } + return QRcode_encodeDataReal((unsigned char *)string, strlen(string), version, level, 1); +} + + +/****************************************************************************** + * Structured QR-code encoding + *****************************************************************************/ + +static QRcode_List *QRcode_List_newEntry(void) +{ + QRcode_List *entry; + + entry = (QRcode_List *)malloc(sizeof(QRcode_List)); + if(entry == NULL) return NULL; + + entry->next = NULL; + entry->code = NULL; + + return entry; +} + +static void QRcode_List_freeEntry(QRcode_List *entry) +{ + if(entry != NULL) { + QRcode_free(entry->code); + free(entry); + } +} + +void QRcode_List_free(QRcode_List *qrlist) +{ + QRcode_List *list = qrlist, *next; + + while(list != NULL) { + next = list->next; + QRcode_List_freeEntry(list); + list = next; + } +} + +int QRcode_List_size(QRcode_List *qrlist) +{ + QRcode_List *list = qrlist; + int size = 0; + + while(list != NULL) { + size++; + list = list->next; + } + + return size; +} + +#if 0 +static unsigned char QRcode_parity(const char *str, int size) +{ + unsigned char parity = 0; + int i; + + for(i=0; ihead; + + while(list != NULL) { + if(head == NULL) { + entry = QRcode_List_newEntry(); + if(entry == NULL) goto ABORT; + head = entry; + tail = head; + } else { + entry = QRcode_List_newEntry(); + if(entry == NULL) goto ABORT; + tail->next = entry; + tail = tail->next; + } + tail->code = QRcode_encodeInput(list->input); + if(tail->code == NULL) { + goto ABORT; + } + list = list->next; + } + + return head; +ABORT: + QRcode_List_free(head); + return NULL; +} + +static QRcode_List *QRcode_encodeInputToStructured(QRinput *input) +{ + QRinput_Struct *s; + QRcode_List *codes; + + s = QRinput_splitQRinputToStruct(input); + if(s == NULL) return NULL; + + codes = QRcode_encodeInputStructured(s); + QRinput_Struct_free(s); + + return codes; +} + +static QRcode_List *QRcode_encodeDataStructuredReal( + int size, const unsigned char *data, + int version, QRecLevel level, + int eightbit, QRencodeMode hint, int casesensitive) +{ + QRinput *input; + QRcode_List *codes; + int ret; + + if(version <= 0) { + errno = EINVAL; + return NULL; + } + if(!eightbit && (hint != QR_MODE_8 && hint != QR_MODE_KANJI)) { + errno = EINVAL; + return NULL; + } + + input = QRinput_new2(version, level); + if(input == NULL) return NULL; + + if(eightbit) { + ret = QRinput_append(input, QR_MODE_8, size, data); + } else { + ret = Split_splitStringToQRinput((char *)data, input, hint, casesensitive); + } + if(ret < 0) { + QRinput_free(input); + return NULL; + } + codes = QRcode_encodeInputToStructured(input); + QRinput_free(input); + + return codes; +} + +QRcode_List *QRcode_encodeDataStructured(int size, const unsigned char *data, int version, QRecLevel level) { + return QRcode_encodeDataStructuredReal(size, data, version, level, 1, QR_MODE_NUL, 0); +} + +QRcode_List *QRcode_encodeString8bitStructured(const char *string, int version, QRecLevel level) { + if(string == NULL) { + errno = EINVAL; + return NULL; + } + return QRcode_encodeDataStructured(strlen(string), (unsigned char *)string, version, level); +} + +QRcode_List *QRcode_encodeStringStructured(const char *string, int version, QRecLevel level, QRencodeMode hint, int casesensitive) +{ + if(string == NULL) { + errno = EINVAL; + return NULL; + } + return QRcode_encodeDataStructuredReal(strlen(string), (unsigned char *)string, version, level, 0, hint, casesensitive); +} + +/****************************************************************************** + * System utilities + *****************************************************************************/ + +void QRcode_APIVersion(int *major_version, int *minor_version, int *micro_version) +{ + if(major_version != NULL) { + *major_version = MAJOR_VERSION; + } + if(minor_version != NULL) { + *minor_version = MINOR_VERSION; + } + if(micro_version != NULL) { + *micro_version = MICRO_VERSION; + } +} + +char *QRcode_APIVersionString(void) +{ + return VERSION; +} + +void QRcode_clearCache(void) +{ + QRspec_clearCache(); + MQRspec_clearCache(); + free_rs_cache(); +} diff --git a/src/add-ons/kernel/debugger/qrencode/qrencode.h b/src/add-ons/kernel/debugger/qrencode/qrencode.h new file mode 100644 index 0000000000..fa53e364d5 --- /dev/null +++ b/src/add-ons/kernel/debugger/qrencode/qrencode.h @@ -0,0 +1,565 @@ +/** + * qrencode - QR Code encoder + * + * Copyright (C) 2006-2011 Kentaro Fukuchi + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** \mainpage + * Libqrencode is a library for encoding data in a QR Code symbol, a kind of 2D + * symbology. + * + * \section encoding Encoding + * + * There are two ways to encode data: encoding a string or + * encoding a structured data. + * + * \subsection encoding-string Encoding a string + * You can encode a string by calling QRcode_encodeString(). + * The given string is parsed automatically and encoded. If you want to encode + * data that can be represented as a C string style (NUL terminated), you can + * simply use this way. + * + * If the input data contains Kanji (Shift-JIS) characters and you want to + * encode them as Kanji in QR Code, you should give QR_MODE_KANJI as a hint. + * Otherwise, all of non-alphanumeric characters are encoded as 8 bit data. + * If you want to encode a whole string in 8 bit mode, use + * QRcode_encodeString8bit() instead. + * + * Please note that a C string can not contain NUL character. If your data + * contains NUL, you should chose the second way. + * + * \subsection encoding-input Encoding a structured data + * You can construct a structured input data manually. If the structure of the + * input data is known, you can use this way. + * At first, create a ::QRinput object by QRinput_new(). Then add input data + * to the QRinput object by QRinput_append(). Finally call QRcode_encodeInput() + * to encode the QRinput data. + * You can reuse the QRinput data again to encode it in other symbols with + * different parameters. + * + * \section result Result + * The encoded symbol is resulted as a ::QRcode object. It will contain + * its version number, width of the symbol and an array represents the symbol. + * See ::QRcode for the details. You can free the object by QRcode_free(). + * + * Please note that the version of the result may be larger than specified. + * In such cases, the input data would be too large to be encoded in a + * symbol of the specified version. + * + * \section structured Structured append + * Libqrencode can generate "Structured-appended" symbols that enables to split + * a large data set into mulitple QR codes. A QR code reader concatenates + * multiple QR code symbols into a string. + * Just like QRcode_encodeString(), you can use QRcode_encodeStringStructured() + * to generate structured-appended symbols. This functions returns an instance + * of ::QRcode_List. The returned list is a singly-linked list of QRcode: you + * can retrieve each QR code in this way: + * + * \code + * QRcode_List *qrcodes; + * QRcode_List *entry; + * QRcode *qrcode; + * + * qrcodes = QRcode_encodeStringStructured(...); + * entry = qrcodes; + * while(entry != NULL) { + * qrcode = entry->code; + * // do something + * entry = entry->next; + * } + * QRcode_List_free(entry); + * \endcode + * + * Instead of using auto-parsing functions, you can construct your own + * structured input. At first, instantiate an object of ::QRinput_Struct + * by calling QRinput_Struct_new(). This object can hold multiple ::QRinput, + * and one QR code is generated for a ::QRinput. + * QRinput_Struct_appendInput() appends a ::QRinput to a ::QRinput_Struct + * object. In order to generate structured-appended symbols, it is required to + * embed headers to each symbol. You can use + * QRinput_Struct_insertStructuredAppendHeaders() to insert appropriate + * headers to each symbol. You should call this function just once before + * encoding symbols. + */ + +#ifndef __QRENCODE_H__ +#define __QRENCODE_H__ + +#if defined(__cplusplus) +extern "C" { +#endif + +/** + * Encoding mode. + */ +typedef enum { + QR_MODE_NUL = -1, ///< Terminator (NUL character). Internal use only + QR_MODE_NUM = 0, ///< Numeric mode + QR_MODE_AN, ///< Alphabet-numeric mode + QR_MODE_8, ///< 8-bit data mode + QR_MODE_KANJI, ///< Kanji (shift-jis) mode + QR_MODE_STRUCTURE, ///< Internal use only + QR_MODE_ECI, ///< ECI mode + QR_MODE_FNC1FIRST, ///< FNC1, first position + QR_MODE_FNC1SECOND, ///< FNC1, second position +} QRencodeMode; + +/** + * Level of error correction. + */ +typedef enum { + QR_ECLEVEL_L = 0, ///< lowest + QR_ECLEVEL_M, + QR_ECLEVEL_Q, + QR_ECLEVEL_H ///< highest +} QRecLevel; + +/** + * Maximum version (size) of QR-code symbol. + */ +#define QRSPEC_VERSION_MAX 40 + +/** + * Maximum version (size) of QR-code symbol. + */ +#define MQRSPEC_VERSION_MAX 4 + + +/****************************************************************************** + * Input data (qrinput.c) + *****************************************************************************/ + +/** + * Singly linked list to contain input strings. An instance of this class + * contains its version and error correction level too. It is required to + * set them by QRinput_setVersion() and QRinput_setErrorCorrectionLevel(), + * or use QRinput_new2() to instantiate an object. + */ +typedef struct _QRinput QRinput; + +/** + * Instantiate an input data object. The version is set to 0 (auto-select) + * and the error correction level is set to QR_ECLEVEL_L. + * @return an input object (initialized). On error, NULL is returned and errno + * is set to indicate the error. + * @throw ENOMEM unable to allocate memory. + */ +extern QRinput *QRinput_new(void); + +/** + * Instantiate an input data object. + * @param version version number. + * @param level Error correction level. + * @return an input object (initialized). On error, NULL is returned and errno + * is set to indicate the error. + * @throw ENOMEM unable to allocate memory for input objects. + * @throw EINVAL invalid arguments. + */ +extern QRinput *QRinput_new2(int version, QRecLevel level); + +/** + * Instantiate an input data object. Object's Micro QR Code flag is set. + * Unlike with full-sized QR Code, version number must be specified (>0). + * @param version version number (1--4). + * @param level Error correction level. + * @return an input object (initialized). On error, NULL is returned and errno + * is set to indicate the error. + * @throw ENOMEM unable to allocate memory for input objects. + * @throw EINVAL invalid arguments. + */ +extern QRinput *QRinput_newMQR(int version, QRecLevel level); + +/** + * Append data to an input object. + * The data is copied and appended to the input object. + * @param input input object. + * @param mode encoding mode. + * @param size size of data (byte). + * @param data a pointer to the memory area of the input data. + * @retval 0 success. + * @retval -1 an error occurred and errno is set to indeicate the error. + * See Execptions for the details. + * @throw ENOMEM unable to allocate memory. + * @throw EINVAL input data is invalid. + * + */ +extern int QRinput_append(QRinput *input, QRencodeMode mode, int size, const unsigned char *data); + +/** + * Append ECI header. + * @param input input object. + * @param ecinum ECI indicator number (0 - 999999) + * @retval 0 success. + * @retval -1 an error occurred and errno is set to indeicate the error. + * See Execptions for the details. + * @throw ENOMEM unable to allocate memory. + * @throw EINVAL input data is invalid. + * + */ +extern int QRinput_appendECIheader(QRinput *input, unsigned int ecinum); + +/** + * Get current version. + * @param input input object. + * @return current version. + */ +extern int QRinput_getVersion(QRinput *input); + +/** + * Set version of the QR code that is to be encoded. + * This function cannot be applied to Micro QR Code. + * @param input input object. + * @param version version number (0 = auto) + * @retval 0 success. + * @retval -1 invalid argument. + */ +extern int QRinput_setVersion(QRinput *input, int version); + +/** + * Get current error correction level. + * @param input input object. + * @return Current error correcntion level. + */ +extern QRecLevel QRinput_getErrorCorrectionLevel(QRinput *input); + +/** + * Set error correction level of the QR code that is to be encoded. + * This function cannot be applied to Micro QR Code. + * @param input input object. + * @param level Error correction level. + * @retval 0 success. + * @retval -1 invalid argument. + */ +extern int QRinput_setErrorCorrectionLevel(QRinput *input, QRecLevel level); + +/** + * Set version and error correction level of the QR code at once. + * This function is recommened for Micro QR Code. + * @param input input object. + * @param version version number (0 = auto) + * @param level Error correction level. + * @retval 0 success. + * @retval -1 invalid argument. + */ +extern int QRinput_setVersionAndErrorCorrectionLevel(QRinput *input, int version, QRecLevel level); + +/** + * Free the input object. + * All of data chunks in the input object are freed too. + * @param input input object. + */ +extern void QRinput_free(QRinput *input); + +/** + * Validate the input data. + * @param mode encoding mode. + * @param size size of data (byte). + * @param data a pointer to the memory area of the input data. + * @retval 0 success. + * @retval -1 invalid arguments. + */ +extern int QRinput_check(QRencodeMode mode, int size, const unsigned char *data); + +/** + * Set of QRinput for structured symbols. + */ +typedef struct _QRinput_Struct QRinput_Struct; + +/** + * Instantiate a set of input data object. + * @return an instance of QRinput_Struct. On error, NULL is returned and errno + * is set to indicate the error. + * @throw ENOMEM unable to allocate memory. + */ +extern QRinput_Struct *QRinput_Struct_new(void); + +/** + * Set parity of structured symbols. + * @param s structured input object. + * @param parity parity of s. + */ +extern void QRinput_Struct_setParity(QRinput_Struct *s, unsigned char parity); + +/** + * Append a QRinput object to the set. QRinput created by QRinput_newMQR() + * will be rejected. + * @warning never append the same QRinput object twice or more. + * @param s structured input object. + * @param input an input object. + * @retval >0 number of input objects in the structure. + * @retval -1 an error occurred. See Exceptions for the details. + * @throw ENOMEM unable to allocate memory. + * @throw EINVAL invalid arguments. + */ +extern int QRinput_Struct_appendInput(QRinput_Struct *s, QRinput *input); + +/** + * Free all of QRinput in the set. + * @param s a structured input object. + */ +extern void QRinput_Struct_free(QRinput_Struct *s); + +/** + * Split a QRinput to QRinput_Struct. It calculates a parity, set it, then + * insert structured-append headers. QRinput created by QRinput_newMQR() will + * be rejected. + * @param input input object. Version number and error correction level must be + * set. + * @return a set of input data. On error, NULL is returned, and errno is set + * to indicate the error. See Exceptions for the details. + * @throw ERANGE input data is too large. + * @throw EINVAL invalid input data. + * @throw ENOMEM unable to allocate memory. + */ +extern QRinput_Struct *QRinput_splitQRinputToStruct(QRinput *input); + +/** + * Insert structured-append headers to the input structure. It calculates + * a parity and set it if the parity is not set yet. + * @param s input structure + * @retval 0 success. + * @retval -1 an error occurred and errno is set to indeicate the error. + * See Execptions for the details. + * @throw EINVAL invalid input object. + * @throw ENOMEM unable to allocate memory. + */ +extern int QRinput_Struct_insertStructuredAppendHeaders(QRinput_Struct *s); + +/** + * Set FNC1-1st position flag. + */ +extern int QRinput_setFNC1First(QRinput *input); + +/** + * Set FNC1-2nd position flag and application identifier. + */ +extern int QRinput_setFNC1Second(QRinput *input, unsigned char appid); + +/****************************************************************************** + * QRcode output (qrencode.c) + *****************************************************************************/ + +/** + * QRcode class. + * Symbol data is represented as an array contains width*width uchars. + * Each uchar represents a module (dot). If the less significant bit of + * the uchar is 1, the corresponding module is black. The other bits are + * meaningless for usual applications, but here its specification is described. + * + *
+ * MSB 76543210 LSB
+ *     |||||||`- 1=black/0=white
+ *     ||||||`-- data and ecc code area
+ *     |||||`--- format information
+ *     ||||`---- version information
+ *     |||`----- timing pattern
+ *     ||`------ alignment pattern
+ *     |`------- finder pattern and separator
+ *     `-------- non-data modules (format, timing, etc.)
+ * 
+ */ +typedef struct { + int version; ///< version of the symbol + int width; ///< width of the symbol + unsigned char *data; ///< symbol data +} QRcode; + +/** + * Singly-linked list of QRcode. Used to represent a structured symbols. + * A list is terminated with NULL. + */ +typedef struct _QRcode_List QRcode_List; + +struct _QRcode_List { + QRcode *code; + QRcode_List *next; +}; + +/** + * Create a symbol from the input data. + * @warning This function is THREAD UNSAFE when pthread is disabled. + * @param input input data. + * @return an instance of QRcode class. The version of the result QRcode may + * be larger than the designated version. On error, NULL is returned, + * and errno is set to indicate the error. See Exceptions for the + * details. + * @throw EINVAL invalid input object. + * @throw ENOMEM unable to allocate memory for input objects. + */ +extern QRcode *QRcode_encodeInput(QRinput *input); + +/** + * Create a symbol from the string. The library automatically parses the input + * string and encodes in a QR Code symbol. + * @warning This function is THREAD UNSAFE when pthread is disabled. + * @param string input string. It must be NUL terminated. + * @param version version of the symbol. If 0, the library chooses the minimum + * version for the given input data. + * @param level error correction level. + * @param hint tell the library how non-alphanumerical characters should be + * encoded. If QR_MODE_KANJI is given, kanji characters will be + * encoded as Shif-JIS characters. If QR_MODE_8 is given, all of + * non-alphanumerical characters will be encoded as is. If you want + * to embed UTF-8 string, choose this. + * @param casesensitive case-sensitive(1) or not(0). + * @return an instance of QRcode class. The version of the result QRcode may + * be larger than the designated version. On error, NULL is returned, + * and errno is set to indicate the error. See Exceptions for the + * details. + * @throw EINVAL invalid input object. + * @throw ENOMEM unable to allocate memory for input objects. + * @throw ERANGE input data is too large. + */ +extern QRcode *QRcode_encodeString(const char *string, int version, QRecLevel level, QRencodeMode hint, int casesensitive); + +/** + * Same to QRcode_encodeString(), but encode whole data in 8-bit mode. + * @warning This function is THREAD UNSAFE when pthread is disabled. + */ +extern QRcode *QRcode_encodeString8bit(const char *string, int version, QRecLevel level); + +/** + * Micro QR Code version of QRcode_encodeString(). + * @warning This function is THREAD UNSAFE when pthread is disabled. + */ +extern QRcode *QRcode_encodeStringMQR(const char *string, int version, QRecLevel level, QRencodeMode hint, int casesensitive); + +/** + * Micro QR Code version of QRcode_encodeString8bit(). + * @warning This function is THREAD UNSAFE when pthread is disabled. + */ +extern QRcode *QRcode_encodeString8bitMQR(const char *string, int version, QRecLevel level); + +/** + * Encode byte stream (may include '\0') in 8-bit mode. + * @warning This function is THREAD UNSAFE when pthread is disabled. + * @param size size of the input data. + * @param data input data. + * @param version version of the symbol. If 0, the library chooses the minimum + * version for the given input data. + * @param level error correction level. + * @throw EINVAL invalid input object. + * @throw ENOMEM unable to allocate memory for input objects. + * @throw ERANGE input data is too large. + */ +extern QRcode *QRcode_encodeData(int size, const unsigned char *data, int version, QRecLevel level); + +/** + * Micro QR Code version of QRcode_encodeData(). + * @warning This function is THREAD UNSAFE when pthread is disabled. + */ +extern QRcode *QRcode_encodeDataMQR(int size, const unsigned char *data, int version, QRecLevel level); + +/** + * Free the instance of QRcode class. + * @param qrcode an instance of QRcode class. + */ +extern void QRcode_free(QRcode *qrcode); + +/** + * Create structured symbols from the input data. + * @warning This function is THREAD UNSAFE when pthread is disabled. + * @param s + * @return a singly-linked list of QRcode. + */ +extern QRcode_List *QRcode_encodeInputStructured(QRinput_Struct *s); + +/** + * Create structured symbols from the string. The library automatically parses + * the input string and encodes in a QR Code symbol. + * @warning This function is THREAD UNSAFE when pthread is disabled. + * @param string input string. It must be NUL terminated. + * @param version version of the symbol. + * @param level error correction level. + * @param hint tell the library how non-alphanumerical characters should be + * encoded. If QR_MODE_KANJI is given, kanji characters will be + * encoded as Shif-JIS characters. If QR_MODE_8 is given, all of + * non-alphanumerical characters will be encoded as is. If you want + * to embed UTF-8 string, choose this. + * @param casesensitive case-sensitive(1) or not(0). + * @return a singly-linked list of QRcode. On error, NULL is returned, and + * errno is set to indicate the error. See Exceptions for the details. + * @throw EINVAL invalid input object. + * @throw ENOMEM unable to allocate memory for input objects. + */ +extern QRcode_List *QRcode_encodeStringStructured(const char *string, int version, QRecLevel level, QRencodeMode hint, int casesensitive); + +/** + * Same to QRcode_encodeStringStructured(), but encode whole data in 8-bit mode. + * @warning This function is THREAD UNSAFE when pthread is disabled. + */ +extern QRcode_List *QRcode_encodeString8bitStructured(const char *string, int version, QRecLevel level); + +/** + * Create structured symbols from byte stream (may include '\0'). Wholde data + * are encoded in 8-bit mode. + * @warning This function is THREAD UNSAFE when pthread is disabled. + * @param size size of the input data. + * @param data input dat. + * @param version version of the symbol. + * @param level error correction level. + * @return a singly-linked list of QRcode. On error, NULL is returned, and + * errno is set to indicate the error. See Exceptions for the details. + * @throw EINVAL invalid input object. + * @throw ENOMEM unable to allocate memory for input objects. + */ +extern QRcode_List *QRcode_encodeDataStructured(int size, const unsigned char *data, int version, QRecLevel level); + +/** + * Return the number of symbols included in a QRcode_List. + * @param qrlist a head entry of a QRcode_List. + * @return number of symbols in the list. + */ +extern int QRcode_List_size(QRcode_List *qrlist); + +/** + * Free the QRcode_List. + * @param qrlist a head entry of a QRcode_List. + */ +extern void QRcode_List_free(QRcode_List *qrlist); + + +/****************************************************************************** + * System utilities + *****************************************************************************/ + +/** + * Return a string that identifies the library version. + * @param major_version + * @param minor_version + * @param micro_version + */ +extern void QRcode_APIVersion(int *major_version, int *minor_version, int *micro_version); + +/** + * Return a string that identifies the library version. + * @return a string identifies the library version. The string is held by the + * library. Do NOT free it. + */ +extern char *QRcode_APIVersionString(void); + +/** + * Clear all caches. This is only for debug purpose. If you are attacking a + * complicated memory leak bug, try this to reduce the reachable blocks record. + */ +extern void QRcode_clearCache(void); + +#if defined(__cplusplus) +} +#endif + +#endif /* __QRENCODE_H__ */ diff --git a/src/add-ons/kernel/debugger/qrencode/qrencode_inner.h b/src/add-ons/kernel/debugger/qrencode/qrencode_inner.h new file mode 100644 index 0000000000..3c40d0622a --- /dev/null +++ b/src/add-ons/kernel/debugger/qrencode/qrencode_inner.h @@ -0,0 +1,88 @@ +/** + * qrencode - QR Code encoder + * + * Header for test use + * Copyright (C) 2006-2011 Kentaro Fukuchi + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef __QRENCODE_INNER_H__ +#define __QRENCODE_INNER_H__ + +/** + * This header file includes definitions for test use. + */ + +/****************************************************************************** + * Raw code + *****************************************************************************/ + +typedef struct { + int dataLength; + unsigned char *data; + int eccLength; + unsigned char *ecc; +} RSblock; + +typedef struct { + int version; + int dataLength; + int eccLength; + unsigned char *datacode; + unsigned char *ecccode; + int b1; + int blocks; + RSblock *rsblock; + int count; +} QRRawCode; + +extern QRRawCode *QRraw_new(QRinput *input); +extern unsigned char QRraw_getCode(QRRawCode *raw); +extern void QRraw_free(QRRawCode *raw); + +/****************************************************************************** + * Raw code for Micro QR Code + *****************************************************************************/ + +typedef struct { + int version; + int dataLength; + int eccLength; + unsigned char *datacode; + unsigned char *ecccode; + RSblock *rsblock; + int oddbits; + int count; +} MQRRawCode; + +extern MQRRawCode *MQRraw_new(QRinput *input); +extern unsigned char MQRraw_getCode(MQRRawCode *raw); +extern void MQRraw_free(MQRRawCode *raw); + +/****************************************************************************** + * Frame filling + *****************************************************************************/ +extern unsigned char *FrameFiller_test(int version); +extern unsigned char *FrameFiller_testMQR(int version); + +/****************************************************************************** + * QR-code encoding + *****************************************************************************/ +extern QRcode *QRcode_encodeMask(QRinput *input, int mask); +extern QRcode *QRcode_encodeMaskMQR(QRinput *input, int mask); +extern QRcode *QRcode_new(int version, int width, unsigned char *data); + +#endif /* __QRENCODE_INNER_H__ */ diff --git a/src/add-ons/kernel/debugger/qrencode/qrinput.c b/src/add-ons/kernel/debugger/qrencode/qrinput.c new file mode 100644 index 0000000000..5dcacf8aa2 --- /dev/null +++ b/src/add-ons/kernel/debugger/qrencode/qrinput.c @@ -0,0 +1,1724 @@ +/* + * qrencode - QR Code encoder + * + * Input data chunk class + * Copyright (C) 2006-2011 Kentaro Fukuchi + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#if HAVE_CONFIG_H +# include "config.h" +#endif +#include +#include +#include +#include + +#include "qrencode.h" +#include "qrspec.h" +#include "mqrspec.h" +#include "bitstream.h" +#include "qrinput.h" + +/****************************************************************************** + * Utilities + *****************************************************************************/ +int QRinput_isSplittableMode(QRencodeMode mode) +{ + return (mode >= QR_MODE_NUM && mode <= QR_MODE_KANJI); +} + +/****************************************************************************** + * Entry of input data + *****************************************************************************/ + +static QRinput_List *QRinput_List_newEntry(QRencodeMode mode, int size, const unsigned char *data) +{ + QRinput_List *entry; + + if(QRinput_check(mode, size, data)) { + errno = EINVAL; + return NULL; + } + + entry = (QRinput_List *)malloc(sizeof(QRinput_List)); + if(entry == NULL) return NULL; + + entry->mode = mode; + entry->size = size; + if(size > 0) { + entry->data = (unsigned char *)malloc(size); + if(entry->data == NULL) { + free(entry); + return NULL; + } + memcpy(entry->data, data, size); + } + entry->bstream = NULL; + entry->next = NULL; + + return entry; +} + +static void QRinput_List_freeEntry(QRinput_List *entry) +{ + if(entry != NULL) { + free(entry->data); + BitStream_free(entry->bstream); + free(entry); + } +} + +static QRinput_List *QRinput_List_dup(QRinput_List *entry) +{ + QRinput_List *n; + + n = (QRinput_List *)malloc(sizeof(QRinput_List)); + if(n == NULL) return NULL; + + n->mode = entry->mode; + n->size = entry->size; + n->data = (unsigned char *)malloc(n->size); + if(n->data == NULL) { + free(n); + return NULL; + } + memcpy(n->data, entry->data, entry->size); + n->bstream = NULL; + n->next = NULL; + + return n; +} + +/****************************************************************************** + * Input Data + *****************************************************************************/ + +QRinput *QRinput_new(void) +{ + return QRinput_new2(0, QR_ECLEVEL_L); +} + +QRinput *QRinput_new2(int version, QRecLevel level) +{ + QRinput *input; + + if(version < 0 || version > QRSPEC_VERSION_MAX || level > QR_ECLEVEL_H) { + errno = EINVAL; + return NULL; + } + + input = (QRinput *)malloc(sizeof(QRinput)); + if(input == NULL) return NULL; + + input->head = NULL; + input->tail = NULL; + input->version = version; + input->level = level; + input->mqr = 0; + input->fnc1 = 0; + + return input; +} + +QRinput *QRinput_newMQR(int version, QRecLevel level) +{ + QRinput *input; + + if(version <= 0 || version > MQRSPEC_VERSION_MAX) goto INVALID; + if((MQRspec_getECCLength(version, level) == 0)) goto INVALID; + + input = QRinput_new2(version, level); + if(input == NULL) return NULL; + + input->mqr = 1; + + return input; + +INVALID: + errno = EINVAL; + return NULL; +} + +int QRinput_getVersion(QRinput *input) +{ + return input->version; +} + +int QRinput_setVersion(QRinput *input, int version) +{ + if(input->mqr || version < 0 || version > QRSPEC_VERSION_MAX) { + errno = EINVAL; + return -1; + } + + input->version = version; + + return 0; +} + +QRecLevel QRinput_getErrorCorrectionLevel(QRinput *input) +{ + return input->level; +} + +int QRinput_setErrorCorrectionLevel(QRinput *input, QRecLevel level) +{ + if(input->mqr || level > QR_ECLEVEL_H) { + errno = EINVAL; + return -1; + } + + input->level = level; + + return 0; +} + +int QRinput_setVersionAndErrorCorrectionLevel(QRinput *input, int version, QRecLevel level) +{ + if(input->mqr) { + if(version <= 0 || version > MQRSPEC_VERSION_MAX) goto INVALID; + if((MQRspec_getECCLength(version, level) == 0)) goto INVALID; + } else { + if(version < 0 || version > QRSPEC_VERSION_MAX) goto INVALID; + if(level > QR_ECLEVEL_H) goto INVALID; + } + + input->version = version; + input->level = level; + + return 0; + +INVALID: + errno = EINVAL; + return -1; +} + +static void QRinput_appendEntry(QRinput *input, QRinput_List *entry) +{ + if(input->tail == NULL) { + input->head = entry; + input->tail = entry; + } else { + input->tail->next = entry; + input->tail = entry; + } + entry->next = NULL; +} + +int QRinput_append(QRinput *input, QRencodeMode mode, int size, const unsigned char *data) +{ + QRinput_List *entry; + + entry = QRinput_List_newEntry(mode, size, data); + if(entry == NULL) { + return -1; + } + + QRinput_appendEntry(input, entry); + + return 0; +} + +/** + * Insert a structured-append header to the head of the input data. + * @param input input data. + * @param size number of structured symbols. + * @param index index number of the symbol. (1 <= index <= size) + * @param parity parity among input data. (NOTE: each symbol of a set of structured symbols has the same parity data) + * @retval 0 success. + * @retval -1 error occurred and errno is set to indeicate the error. See Execptions for the details. + * @throw EINVAL invalid parameter. + * @throw ENOMEM unable to allocate memory. + */ +__STATIC int QRinput_insertStructuredAppendHeader(QRinput *input, int size, int index, unsigned char parity) +{ + QRinput_List *entry; + unsigned char buf[3]; + + if(size > MAX_STRUCTURED_SYMBOLS) { + errno = EINVAL; + return -1; + } + if(index <= 0 || index > MAX_STRUCTURED_SYMBOLS) { + errno = EINVAL; + return -1; + } + + buf[0] = (unsigned char)size; + buf[1] = (unsigned char)index; + buf[2] = parity; + entry = QRinput_List_newEntry(QR_MODE_STRUCTURE, 3, buf); + if(entry == NULL) { + return -1; + } + + entry->next = input->head; + input->head = entry; + + return 0; +} + +int QRinput_appendECIheader(QRinput *input, unsigned int ecinum) +{ + unsigned char data[4]; + + if(ecinum > 999999) { + errno = EINVAL; + return -1; + } + + /* We manually create byte array of ecinum because + (unsigned char *)&ecinum may cause bus error on some architectures, */ + data[0] = ecinum & 0xff; + data[1] = (ecinum >> 8) & 0xff; + data[2] = (ecinum >> 16) & 0xff; + data[3] = (ecinum >> 24) & 0xff; + return QRinput_append(input, QR_MODE_ECI, 4, data); +} + +void QRinput_free(QRinput *input) +{ + QRinput_List *list, *next; + + if(input != NULL) { + list = input->head; + while(list != NULL) { + next = list->next; + QRinput_List_freeEntry(list); + list = next; + } + free(input); + } +} + +static unsigned char QRinput_calcParity(QRinput *input) +{ + unsigned char parity = 0; + QRinput_List *list; + int i; + + list = input->head; + while(list != NULL) { + if(list->mode != QR_MODE_STRUCTURE) { + for(i=list->size-1; i>=0; i--) { + parity ^= list->data[i]; + } + } + list = list->next; + } + + return parity; +} + +QRinput *QRinput_dup(QRinput *input) +{ + QRinput *n; + QRinput_List *list, *e; + + if(input->mqr) { + n = QRinput_newMQR(input->version, input->level); + } else { + n = QRinput_new2(input->version, input->level); + } + if(n == NULL) return NULL; + + list = input->head; + while(list != NULL) { + e = QRinput_List_dup(list); + if(e == NULL) { + QRinput_free(n); + return NULL; + } + QRinput_appendEntry(n, e); + list = list->next; + } + + return n; +} + +/****************************************************************************** + * Numeric data + *****************************************************************************/ + +/** + * Check the input data. + * @param size + * @param data + * @return result + */ +static int QRinput_checkModeNum(int size, const char *data) +{ + int i; + + for(i=0; i '9') + return -1; + } + + return 0; +} + +/** + * Estimates the length of the encoded bit stream of numeric data. + * @param size + * @return number of bits + */ +int QRinput_estimateBitsModeNum(int size) +{ + int w; + int bits; + + w = size / 3; + bits = w * 10; + switch(size - w * 3) { + case 1: + bits += 4; + break; + case 2: + bits += 7; + break; + default: + break; + } + + return bits; +} + +/** + * Convert the number data to a bit stream. + * @param entry + * @param mqr + * @retval 0 success + * @retval -1 an error occurred and errno is set to indeicate the error. + * See Execptions for the details. + * @throw ENOMEM unable to allocate memory. + */ +static int QRinput_encodeModeNum(QRinput_List *entry, int version, int mqr) +{ + int words, i, ret; + unsigned int val; + + entry->bstream = BitStream_new(); + if(entry->bstream == NULL) return -1; + + if(mqr) { + if(version > 1) { + ret = BitStream_appendNum(entry->bstream, version - 1, MQRSPEC_MODEID_NUM); + if(ret < 0) goto ABORT; + } + ret = BitStream_appendNum(entry->bstream, MQRspec_lengthIndicator(QR_MODE_NUM, version), entry->size); + if(ret < 0) goto ABORT; + } else { + ret = BitStream_appendNum(entry->bstream, 4, QRSPEC_MODEID_NUM); + if(ret < 0) goto ABORT; + + ret = BitStream_appendNum(entry->bstream, QRspec_lengthIndicator(QR_MODE_NUM, version), entry->size); + if(ret < 0) goto ABORT; + } + + words = entry->size / 3; + for(i=0; idata[i*3 ] - '0') * 100; + val += (entry->data[i*3+1] - '0') * 10; + val += (entry->data[i*3+2] - '0'); + + ret = BitStream_appendNum(entry->bstream, 10, val); + if(ret < 0) goto ABORT; + } + + if(entry->size - words * 3 == 1) { + val = entry->data[words*3] - '0'; + ret = BitStream_appendNum(entry->bstream, 4, val); + if(ret < 0) goto ABORT; + } else if(entry->size - words * 3 == 2) { + val = (entry->data[words*3 ] - '0') * 10; + val += (entry->data[words*3+1] - '0'); + BitStream_appendNum(entry->bstream, 7, val); + if(ret < 0) goto ABORT; + } + + return 0; +ABORT: + BitStream_free(entry->bstream); + entry->bstream = NULL; + return -1; +} + +/****************************************************************************** + * Alphabet-numeric data + *****************************************************************************/ + +const signed char QRinput_anTable[128] = { + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 36, -1, -1, -1, 37, 38, -1, -1, -1, -1, 39, 40, -1, 41, 42, 43, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 44, -1, -1, -1, -1, -1, + -1, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, + 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 +}; + +/** + * Check the input data. + * @param size + * @param data + * @return result + */ +static int QRinput_checkModeAn(int size, const char *data) +{ + int i; + + for(i=0; ibstream = BitStream_new(); + if(entry->bstream == NULL) return -1; + + if(mqr) { + if(version < 2) { + errno = EINVAL; + goto ABORT; + } + ret = BitStream_appendNum(entry->bstream, version - 1, MQRSPEC_MODEID_AN); + if(ret < 0) goto ABORT; + ret = BitStream_appendNum(entry->bstream, MQRspec_lengthIndicator(QR_MODE_AN, version), entry->size); + if(ret < 0) goto ABORT; + } else { + ret = BitStream_appendNum(entry->bstream, 4, QRSPEC_MODEID_AN); + if(ret < 0) goto ABORT; + ret = BitStream_appendNum(entry->bstream, QRspec_lengthIndicator(QR_MODE_AN, version), entry->size); + if(ret < 0) goto ABORT; + } + + words = entry->size / 2; + for(i=0; idata[i*2 ]) * 45; + val += (unsigned int)QRinput_lookAnTable(entry->data[i*2+1]); + + ret = BitStream_appendNum(entry->bstream, 11, val); + if(ret < 0) goto ABORT; + } + + if(entry->size & 1) { + val = (unsigned int)QRinput_lookAnTable(entry->data[words * 2]); + + ret = BitStream_appendNum(entry->bstream, 6, val); + if(ret < 0) goto ABORT; + } + + return 0; +ABORT: + BitStream_free(entry->bstream); + entry->bstream = NULL; + return -1; +} + +/****************************************************************************** + * 8 bit data + *****************************************************************************/ + +/** + * Estimates the length of the encoded bit stream of 8 bit data. + * @param size + * @return number of bits + */ +int QRinput_estimateBitsMode8(int size) +{ + return size * 8; +} + +/** + * Convert the 8bits data to a bit stream. + * @param entry + * @param mqr + * @retval 0 success + * @retval -1 an error occurred and errno is set to indeicate the error. + * See Execptions for the details. + * @throw ENOMEM unable to allocate memory. + */ +static int QRinput_encodeMode8(QRinput_List *entry, int version, int mqr) +{ + int ret; + + entry->bstream = BitStream_new(); + if(entry->bstream == NULL) return -1; + + if(mqr) { + if(version < 3) { + errno = EINVAL; + goto ABORT; + } + ret = BitStream_appendNum(entry->bstream, version - 1, MQRSPEC_MODEID_8); + if(ret < 0) goto ABORT; + ret = BitStream_appendNum(entry->bstream, MQRspec_lengthIndicator(QR_MODE_8, version), entry->size); + if(ret < 0) goto ABORT; + } else { + ret = BitStream_appendNum(entry->bstream, 4, QRSPEC_MODEID_8); + if(ret < 0) goto ABORT; + ret = BitStream_appendNum(entry->bstream, QRspec_lengthIndicator(QR_MODE_8, version), entry->size); + if(ret < 0) goto ABORT; + } + + ret = BitStream_appendBytes(entry->bstream, entry->size, entry->data); + if(ret < 0) goto ABORT; + + return 0; +ABORT: + BitStream_free(entry->bstream); + entry->bstream = NULL; + return -1; +} + + +/****************************************************************************** + * Kanji data + *****************************************************************************/ + +/** + * Estimates the length of the encoded bit stream of kanji data. + * @param size + * @return number of bits + */ +int QRinput_estimateBitsModeKanji(int size) +{ + return (size / 2) * 13; +} + +/** + * Check the input data. + * @param size + * @param data + * @return result + */ +static int QRinput_checkModeKanji(int size, const unsigned char *data) +{ + int i; + unsigned int val; + + if(size & 1) + return -1; + + for(i=0; i 0x9ffc && val < 0xe040) || val > 0xebbf) { + return -1; + } + } + + return 0; +} + +/** + * Convert the kanji data to a bit stream. + * @param entry + * @param mqr + * @retval 0 success + * @retval -1 an error occurred and errno is set to indeicate the error. + * See Execptions for the details. + * @throw ENOMEM unable to allocate memory. + * @throw EINVAL invalid version. + */ +static int QRinput_encodeModeKanji(QRinput_List *entry, int version, int mqr) +{ + int ret, i; + unsigned int val, h; + + entry->bstream = BitStream_new(); + if(entry->bstream == NULL) return -1; + + if(mqr) { + if(version < 2) { + errno = EINVAL; + goto ABORT; + } + ret = BitStream_appendNum(entry->bstream, version - 1, MQRSPEC_MODEID_KANJI); + if(ret < 0) goto ABORT; + ret = BitStream_appendNum(entry->bstream, MQRspec_lengthIndicator(QR_MODE_KANJI, version), entry->size/2); + if(ret < 0) goto ABORT; + } else { + ret = BitStream_appendNum(entry->bstream, 4, QRSPEC_MODEID_KANJI); + if(ret < 0) goto ABORT; + ret = BitStream_appendNum(entry->bstream, QRspec_lengthIndicator(QR_MODE_KANJI, version), entry->size/2); + if(ret < 0) goto ABORT; + } + + for(i=0; isize; i+=2) { + val = ((unsigned int)entry->data[i] << 8) | entry->data[i+1]; + if(val <= 0x9ffc) { + val -= 0x8140; + } else { + val -= 0xc140; + } + h = (val >> 8) * 0xc0; + val = (val & 0xff) + h; + + ret = BitStream_appendNum(entry->bstream, 13, val); + if(ret < 0) goto ABORT; + } + + return 0; +ABORT: + BitStream_free(entry->bstream); + entry->bstream = NULL; + return -1; +} + +/****************************************************************************** + * Structured Symbol + *****************************************************************************/ + +/** + * Convert a structure symbol code to a bit stream. + * @param entry + * @param mqr + * @retval 0 success + * @retval -1 an error occurred and errno is set to indeicate the error. + * See Execptions for the details. + * @throw ENOMEM unable to allocate memory. + * @throw EINVAL invalid entry. + */ +static int QRinput_encodeModeStructure(QRinput_List *entry, int mqr) +{ + int ret; + + if(mqr) { + errno = EINVAL; + return -1; + } + entry->bstream = BitStream_new(); + if(entry->bstream == NULL) return -1; + + ret = BitStream_appendNum(entry->bstream, 4, QRSPEC_MODEID_STRUCTURE); + if(ret < 0) goto ABORT; + ret = BitStream_appendNum(entry->bstream, 4, entry->data[1] - 1); + if(ret < 0) goto ABORT; + ret = BitStream_appendNum(entry->bstream, 4, entry->data[0] - 1); + if(ret < 0) goto ABORT; + ret = BitStream_appendNum(entry->bstream, 8, entry->data[2]); + if(ret < 0) goto ABORT; + + return 0; +ABORT: + BitStream_free(entry->bstream); + entry->bstream = NULL; + return -1; +} + +/****************************************************************************** + * FNC1 + *****************************************************************************/ + +static int QRinput_checkModeFNC1Second(int size, const unsigned char *data) +{ + if(size != 1) return -1; + + return 0; +} + +static int QRinput_encodeModeFNC1Second(QRinput_List *entry, int version) +{ + int ret; + + entry->bstream = BitStream_new(); + if(entry->bstream == NULL) return -1; + + ret = BitStream_appendNum(entry->bstream, 4, QRSPEC_MODEID_FNC1SECOND); + if(ret < 0) goto ABORT; + + ret = BitStream_appendBytes(entry->bstream, 1, entry->data); + if(ret < 0) goto ABORT; + + return 0; +ABORT: + BitStream_free(entry->bstream); + entry->bstream = NULL; + return -1; +} + +/****************************************************************************** + * ECI header + *****************************************************************************/ +static unsigned int QRinput_decodeECIfromByteArray(unsigned char *data) +{ + int i; + unsigned int ecinum; + + ecinum = 0; + for(i=0; i<4; i++) { + ecinum = ecinum << 8; + ecinum |= data[3-i]; + } + + return ecinum; +} + +int QRinput_estimateBitsModeECI(unsigned char *data) +{ + unsigned int ecinum; + + ecinum = QRinput_decodeECIfromByteArray(data);; + + /* See Table 4 of JISX 0510:2004 pp.17. */ + if(ecinum < 128) { + return MODE_INDICATOR_SIZE + 8; + } else if(ecinum < 16384) { + return MODE_INDICATOR_SIZE + 16; + } else { + return MODE_INDICATOR_SIZE + 24; + } +} + +static int QRinput_encodeModeECI(QRinput_List *entry, int version) +{ + int ret, words; + unsigned int ecinum, code; + + entry->bstream = BitStream_new(); + if(entry->bstream == NULL) return -1; + + ecinum = QRinput_decodeECIfromByteArray(entry->data);; + + /* See Table 4 of JISX 0510:2004 pp.17. */ + if(ecinum < 128) { + words = 1; + code = ecinum; + } else if(ecinum < 16384) { + words = 2; + code = 0x8000 + ecinum; + } else { + words = 3; + code = 0xc0000 + ecinum; + } + + ret = BitStream_appendNum(entry->bstream, 4, QRSPEC_MODEID_ECI); + if(ret < 0) goto ABORT; + + ret = BitStream_appendNum(entry->bstream, words * 8, code); + if(ret < 0) goto ABORT; + + return 0; +ABORT: + BitStream_free(entry->bstream); + entry->bstream = NULL; + return -1; +} + +/****************************************************************************** + * Validation + *****************************************************************************/ + +int QRinput_check(QRencodeMode mode, int size, const unsigned char *data) +{ + if((mode == QR_MODE_FNC1FIRST && size < 0) || size <= 0) return -1; + + switch(mode) { + case QR_MODE_NUM: + return QRinput_checkModeNum(size, (const char *)data); + case QR_MODE_AN: + return QRinput_checkModeAn(size, (const char *)data); + case QR_MODE_KANJI: + return QRinput_checkModeKanji(size, data); + case QR_MODE_8: + return 0; + case QR_MODE_STRUCTURE: + return 0; + case QR_MODE_ECI: + return 0; + case QR_MODE_FNC1FIRST: + return 0; + case QR_MODE_FNC1SECOND: + return QRinput_checkModeFNC1Second(size, data); + case QR_MODE_NUL: + break; + } + + return -1; +} + +/****************************************************************************** + * Estimation of the bit length + *****************************************************************************/ + +/** + * Estimates the length of the encoded bit stream on the current version. + * @param entry + * @param version version of the symbol + * @param mqr + * @return number of bits + */ +static int QRinput_estimateBitStreamSizeOfEntry(QRinput_List *entry, int version, int mqr) +{ + int bits = 0; + int l, m; + int num; + + if(version == 0) version = 1; + + switch(entry->mode) { + case QR_MODE_NUM: + bits = QRinput_estimateBitsModeNum(entry->size); + break; + case QR_MODE_AN: + bits = QRinput_estimateBitsModeAn(entry->size); + break; + case QR_MODE_8: + bits = QRinput_estimateBitsMode8(entry->size); + break; + case QR_MODE_KANJI: + bits = QRinput_estimateBitsModeKanji(entry->size); + break; + case QR_MODE_STRUCTURE: + return STRUCTURE_HEADER_SIZE; + case QR_MODE_ECI: + bits = QRinput_estimateBitsModeECI(entry->data); + break; + case QR_MODE_FNC1FIRST: + return MODE_INDICATOR_SIZE; + break; + case QR_MODE_FNC1SECOND: + return MODE_INDICATOR_SIZE + 8; + default: + return 0; + } + + if(mqr) { + l = QRspec_lengthIndicator(entry->mode, version); + m = version - 1; + bits += l + m; + } else { + l = QRspec_lengthIndicator(entry->mode, version); + m = 1 << l; + num = (entry->size + m - 1) / m; + + bits += num * (MODE_INDICATOR_SIZE + l); + } + + return bits; +} + +/** + * Estimates the length of the encoded bit stream of the data. + * @param input input data + * @param version version of the symbol + * @return number of bits + */ +__STATIC int QRinput_estimateBitStreamSize(QRinput *input, int version) +{ + QRinput_List *list; + int bits = 0; + + list = input->head; + while(list != NULL) { + bits += QRinput_estimateBitStreamSizeOfEntry(list, version, input->mqr); + list = list->next; + } + + return bits; +} + +/** + * Estimates the required version number of the symbol. + * @param input input data + * @return required version number + */ +static int QRinput_estimateVersion(QRinput *input) +{ + int bits; + int version, prev; + + version = 0; + do { + prev = version; + bits = QRinput_estimateBitStreamSize(input, prev); + version = QRspec_getMinimumVersion((bits + 7) / 8, input->level); + if (version < 0) { + return -1; + } + } while (version > prev); + + return version; +} + +/** + * Returns required length in bytes for specified mode, version and bits. + * @param mode + * @param version + * @param bits + * @return required length of code words in bytes. + */ +__STATIC int QRinput_lengthOfCode(QRencodeMode mode, int version, int bits) +{ + int payload, size, chunks, remain, maxsize; + + payload = bits - 4 - QRspec_lengthIndicator(mode, version); + switch(mode) { + case QR_MODE_NUM: + chunks = payload / 10; + remain = payload - chunks * 10; + size = chunks * 3; + if(remain >= 7) { + size += 2; + } else if(remain >= 4) { + size += 1; + } + break; + case QR_MODE_AN: + chunks = payload / 11; + remain = payload - chunks * 11; + size = chunks * 2; + if(remain >= 6) size++; + break; + case QR_MODE_8: + size = payload / 8; + break; + case QR_MODE_KANJI: + size = (payload / 13) * 2; + break; + case QR_MODE_STRUCTURE: + size = payload / 8; + break; + default: + size = 0; + break; + } + maxsize = QRspec_maximumWords(mode, version); + if(size < 0) size = 0; + if(maxsize > 0 && size > maxsize) size = maxsize; + + return size; +} + +/****************************************************************************** + * Data conversion + *****************************************************************************/ + +/** + * Convert the input data in the data chunk to a bit stream. + * @param entry + * @return number of bits (>0) or -1 for failure. + */ +static int QRinput_encodeBitStream(QRinput_List *entry, int version, int mqr) +{ + int words, ret; + QRinput_List *st1 = NULL, *st2 = NULL; + + if(entry->bstream != NULL) { + BitStream_free(entry->bstream); + entry->bstream = NULL; + } + + words = QRspec_maximumWords(entry->mode, version); + if(words != 0 && entry->size > words) { + st1 = QRinput_List_newEntry(entry->mode, words, entry->data); + if(st1 == NULL) goto ABORT; + st2 = QRinput_List_newEntry(entry->mode, entry->size - words, &entry->data[words]); + if(st2 == NULL) goto ABORT; + + ret = QRinput_encodeBitStream(st1, version, mqr); + if(ret < 0) goto ABORT; + ret = QRinput_encodeBitStream(st2, version, mqr); + if(ret < 0) goto ABORT; + entry->bstream = BitStream_new(); + if(entry->bstream == NULL) goto ABORT; + ret = BitStream_append(entry->bstream, st1->bstream); + if(ret < 0) goto ABORT; + ret = BitStream_append(entry->bstream, st2->bstream); + if(ret < 0) goto ABORT; + QRinput_List_freeEntry(st1); + QRinput_List_freeEntry(st2); + } else { + ret = 0; + switch(entry->mode) { + case QR_MODE_NUM: + ret = QRinput_encodeModeNum(entry, version, mqr); + break; + case QR_MODE_AN: + ret = QRinput_encodeModeAn(entry, version, mqr); + break; + case QR_MODE_8: + ret = QRinput_encodeMode8(entry, version, mqr); + break; + case QR_MODE_KANJI: + ret = QRinput_encodeModeKanji(entry, version, mqr); + break; + case QR_MODE_STRUCTURE: + ret = QRinput_encodeModeStructure(entry, mqr); + break; + case QR_MODE_ECI: + ret = QRinput_encodeModeECI(entry, version); + break; + case QR_MODE_FNC1SECOND: + ret = QRinput_encodeModeFNC1Second(entry, version); + default: + break; + } + if(ret < 0) return -1; + } + + return BitStream_size(entry->bstream); +ABORT: + QRinput_List_freeEntry(st1); + QRinput_List_freeEntry(st2); + return -1; +} + +/** + * Convert the input data to a bit stream. + * @param input input data. + * @retval 0 success + * @retval -1 an error occurred and errno is set to indeicate the error. + * See Execptions for the details. + * @throw ENOMEM unable to allocate memory. + */ +static int QRinput_createBitStream(QRinput *input) +{ + QRinput_List *list; + int bits, total = 0; + + list = input->head; + while(list != NULL) { + bits = QRinput_encodeBitStream(list, input->version, input->mqr); + if(bits < 0) return -1; + total += bits; + list = list->next; + } + + return total; +} + +/** + * Convert the input data to a bit stream. + * When the version number is given and that is not sufficient, it is increased + * automatically. + * @param input input data. + * @retval 0 success + * @retval -1 an error occurred and errno is set to indeicate the error. + * See Execptions for the details. + * @throw ENOMEM unable to allocate memory. + * @throw ERANGE input is too large. + */ +static int QRinput_convertData(QRinput *input) +{ + int bits; + int ver; + + ver = QRinput_estimateVersion(input); + if(ver > QRinput_getVersion(input)) { + QRinput_setVersion(input, ver); + } + + for(;;) { + bits = QRinput_createBitStream(input); + if(bits < 0) return -1; + ver = QRspec_getMinimumVersion((bits + 7) / 8, input->level); + if(ver < 0) { + errno = ERANGE; + return -1; + } else if(ver > QRinput_getVersion(input)) { + QRinput_setVersion(input, ver); + } else { + break; + } + } + + return 0; +} + +/** + * Append padding bits for the input data. + * @param bstream Bitstream to be appended. + * @param input input data. + * @retval 0 success + * @retval -1 an error occurred and errno is set to indeicate the error. + * See Execptions for the details. + * @throw ERANGE input data is too large. + * @throw ENOMEM unable to allocate memory. + */ +static int QRinput_appendPaddingBit(BitStream *bstream, QRinput *input) +{ + int bits, maxbits, words, maxwords, i, ret; + BitStream *padding = NULL; + unsigned char *padbuf; + int padlen; + + bits = BitStream_size(bstream); + maxwords = QRspec_getDataLength(input->version, input->level); + maxbits = maxwords * 8; + + if(maxbits < bits) { + errno = ERANGE; + return -1; + } + if(maxbits == bits) { + return 0; + } + + if(maxbits - bits <= 4) { + ret = BitStream_appendNum(bstream, maxbits - bits, 0); + goto DONE; + } + + words = (bits + 4 + 7) / 8; + + padding = BitStream_new(); + if(padding == NULL) return -1; + ret = BitStream_appendNum(padding, words * 8 - bits, 0); + if(ret < 0) goto DONE; + + padlen = maxwords - words; + if(padlen > 0) { + padbuf = (unsigned char *)malloc(padlen); + if(padbuf == NULL) { + ret = -1; + goto DONE; + } + for(i=0; iversion, input->level); + maxwords = maxbits / 8; + + if(maxbits < bits) { + errno = ERANGE; + return -1; + } + if(maxbits == bits) { + return 0; + } + + termbits = input->version * 2 + 1; + + if(maxbits - bits <= termbits) { + ret = BitStream_appendNum(bstream, maxbits - bits, 0); + goto DONE; + } + + bits += termbits; + + words = (bits + 7) / 8; + if(maxbits - words * 8 > 0) { + termbits += words * 8 - bits; + if(words == maxwords) termbits += maxbits - words * 8; + } else { + termbits += words * 8 - bits; + } + padding = BitStream_new(); + if(padding == NULL) return -1; + ret = BitStream_appendNum(padding, termbits, 0); + if(ret < 0) goto DONE; + + padlen = maxwords - words; + if(padlen > 0) { + padbuf = (unsigned char *)malloc(padlen); + if(padbuf == NULL) { + ret = -1; + goto DONE; + } + for(i=0; i 0) { + ret = BitStream_appendNum(padding, termbits, 0); + if(ret < 0) goto DONE; + } + } + + ret = BitStream_append(bstream, padding); + +DONE: + BitStream_free(padding); + return ret; +} + +static int QRinput_insertFNC1Header(QRinput *input) +{ + QRinput_List *entry = NULL; + + if(input->fnc1 == 1) { + entry = QRinput_List_newEntry(QR_MODE_FNC1FIRST, 0, NULL); + } else if(input->fnc1 == 2) { + entry = QRinput_List_newEntry(QR_MODE_FNC1SECOND, 1, &(input->appid)); + } + if(entry == NULL) { + return -1; + } + + if(input->head->mode != QR_MODE_STRUCTURE || input->head->mode != QR_MODE_ECI) { + entry->next = input->head; + input->head = entry; + } else { + entry->next = input->head->next; + input->head->next = entry; + } + + return 0; +} + +/** + * Merge all bit streams in the input data. + * @param input input data. + * @return merged bit stream + */ + +__STATIC BitStream *QRinput_mergeBitStream(QRinput *input) +{ + BitStream *bstream; + QRinput_List *list; + int ret; + + if(input->mqr) { + if(QRinput_createBitStream(input) < 0) { + return NULL; + } + } else { + if(input->fnc1) { + if(QRinput_insertFNC1Header(input) < 0) { + return NULL; + } + } + if(QRinput_convertData(input) < 0) { + return NULL; + } + } + + bstream = BitStream_new(); + if(bstream == NULL) return NULL; + + list = input->head; + while(list != NULL) { + ret = BitStream_append(bstream, list->bstream); + if(ret < 0) { + BitStream_free(bstream); + return NULL; + } + list = list->next; + } + + return bstream; +} + +/** + * Merge all bit streams in the input data and append padding bits + * @param input input data. + * @return padded merged bit stream + */ + +__STATIC BitStream *QRinput_getBitStream(QRinput *input) +{ + BitStream *bstream; + int ret; + + bstream = QRinput_mergeBitStream(input); + if(bstream == NULL) { + return NULL; + } + if(input->mqr) { + ret = QRinput_appendPaddingBitMQR(bstream, input); + } else { + ret = QRinput_appendPaddingBit(bstream, input); + } + if(ret < 0) { + BitStream_free(bstream); + return NULL; + } + + return bstream; +} + +/** + * Pack all bit streams padding bits into a byte array. + * @param input input data. + * @return padded merged byte stream + */ + +unsigned char *QRinput_getByteStream(QRinput *input) +{ + BitStream *bstream; + unsigned char *array; + + bstream = QRinput_getBitStream(input); + if(bstream == NULL) { + return NULL; + } + array = BitStream_toByte(bstream); + BitStream_free(bstream); + + return array; +} + +/****************************************************************************** + * Structured input data + *****************************************************************************/ + +static QRinput_InputList *QRinput_InputList_newEntry(QRinput *input) +{ + QRinput_InputList *entry; + + entry = (QRinput_InputList *)malloc(sizeof(QRinput_InputList)); + if(entry == NULL) return NULL; + + entry->input = input; + entry->next = NULL; + + return entry; +} + +static void QRinput_InputList_freeEntry(QRinput_InputList *entry) +{ + if(entry != NULL) { + QRinput_free(entry->input); + free(entry); + } +} + +QRinput_Struct *QRinput_Struct_new(void) +{ + QRinput_Struct *s; + + s = (QRinput_Struct *)malloc(sizeof(QRinput_Struct)); + if(s == NULL) return NULL; + + s->size = 0; + s->parity = -1; + s->head = NULL; + s->tail = NULL; + + return s; +} + +void QRinput_Struct_setParity(QRinput_Struct *s, unsigned char parity) +{ + s->parity = (int)parity; +} + +int QRinput_Struct_appendInput(QRinput_Struct *s, QRinput *input) +{ + QRinput_InputList *e; + + if(input->mqr) { + errno = EINVAL; + return -1; + } + + e = QRinput_InputList_newEntry(input); + if(e == NULL) return -1; + + s->size++; + if(s->tail == NULL) { + s->head = e; + s->tail = e; + } else { + s->tail->next = e; + s->tail = e; + } + + return s->size; +} + +void QRinput_Struct_free(QRinput_Struct *s) +{ + QRinput_InputList *list, *next; + + if(s != NULL) { + list = s->head; + while(list != NULL) { + next = list->next; + QRinput_InputList_freeEntry(list); + list = next; + } + free(s); + } +} + +static unsigned char QRinput_Struct_calcParity(QRinput_Struct *s) +{ + QRinput_InputList *list; + unsigned char parity = 0; + + list = s->head; + while(list != NULL) { + parity ^= QRinput_calcParity(list->input); + list = list->next; + } + + QRinput_Struct_setParity(s, parity); + + return parity; +} + +static int QRinput_List_shrinkEntry(QRinput_List *entry, int bytes) +{ + unsigned char *data; + + data = (unsigned char *)malloc(bytes); + if(data == NULL) return -1; + + memcpy(data, entry->data, bytes); + free(entry->data); + entry->data = data; + entry->size = bytes; + + return 0; +} + +__STATIC int QRinput_splitEntry(QRinput_List *entry, int bytes) +{ + QRinput_List *e; + int ret; + + e = QRinput_List_newEntry(entry->mode, entry->size - bytes, entry->data + bytes); + if(e == NULL) { + return -1; + } + + ret = QRinput_List_shrinkEntry(entry, bytes); + if(ret < 0) { + QRinput_List_freeEntry(e); + return -1; + } + + e->next = entry->next; + entry->next = e; + + return 0; +} + +QRinput_Struct *QRinput_splitQRinputToStruct(QRinput *input) +{ + QRinput *p; + QRinput_Struct *s; + int bits, maxbits, nextbits, bytes, ret; + QRinput_List *list, *next, *prev; + + if(input->mqr) { + errno = EINVAL; + return NULL; + } + + s = QRinput_Struct_new(); + if(s == NULL) return NULL; + + input = QRinput_dup(input); + if(input == NULL) { + QRinput_Struct_free(s); + return NULL; + } + + QRinput_Struct_setParity(s, QRinput_calcParity(input)); + maxbits = QRspec_getDataLength(input->version, input->level) * 8 - STRUCTURE_HEADER_SIZE; + + if(maxbits <= 0) { + QRinput_Struct_free(s); + QRinput_free(input); + return NULL; + } + + bits = 0; + list = input->head; + prev = NULL; + while(list != NULL) { + nextbits = QRinput_estimateBitStreamSizeOfEntry(list, input->version, input->mqr); + if(bits + nextbits <= maxbits) { + ret = QRinput_encodeBitStream(list, input->version, input->mqr); + if(ret < 0) goto ABORT; + bits += ret; + prev = list; + list = list->next; + } else { + bytes = QRinput_lengthOfCode(list->mode, input->version, maxbits - bits); + if(bytes > 0) { + /* Splits this entry into 2 entries. */ + ret = QRinput_splitEntry(list, bytes); + if(ret < 0) goto ABORT; + /* First half is the tail of the current input. */ + next = list->next; + list->next = NULL; + /* Second half is the head of the next input, p.*/ + p = QRinput_new2(input->version, input->level); + if(p == NULL) goto ABORT; + p->head = next; + /* Renew QRinput.tail. */ + p->tail = input->tail; + input->tail = list; + /* Point to the next entry. */ + prev = list; + list = next; + } else { + /* Current entry will go to the next input. */ + prev->next = NULL; + p = QRinput_new2(input->version, input->level); + if(p == NULL) goto ABORT; + p->head = list; + p->tail = input->tail; + input->tail = prev; + } + ret = QRinput_Struct_appendInput(s, input); + if(ret < 0) goto ABORT; + input = p; + bits = 0; + } + } + QRinput_Struct_appendInput(s, input); + if(s->size > MAX_STRUCTURED_SYMBOLS) { + QRinput_Struct_free(s); + errno = ERANGE; + return NULL; + } + ret = QRinput_Struct_insertStructuredAppendHeaders(s); + if(ret < 0) { + QRinput_Struct_free(s); + return NULL; + } + + return s; + +ABORT: + QRinput_free(input); + QRinput_Struct_free(s); + return NULL; +} + +int QRinput_Struct_insertStructuredAppendHeaders(QRinput_Struct *s) +{ + int num, i; + QRinput_InputList *list; + + if(s->parity < 0) { + QRinput_Struct_calcParity(s); + } + num = 0; + list = s->head; + while(list != NULL) { + num++; + list = list->next; + } + i = 1; + list = s->head; + while(list != NULL) { + if(QRinput_insertStructuredAppendHeader(list->input, num, i, s->parity)) + return -1; + i++; + list = list->next; + } + + return 0; +} + +/****************************************************************************** + * Extended encoding mode (FNC1 and ECI) + *****************************************************************************/ + +int QRinput_setFNC1First(QRinput *input) +{ + if(input->mqr) { + errno = EINVAL; + return -1; + } + input->fnc1 = 1; + + return 0; +} + +int QRinput_setFNC1Second(QRinput *input, unsigned char appid) +{ + if(input->mqr) { + errno = EINVAL; + return -1; + } + input->fnc1 = 2; + input->appid = appid; + + return 0; +} diff --git a/src/add-ons/kernel/debugger/qrencode/qrinput.h b/src/add-ons/kernel/debugger/qrencode/qrinput.h new file mode 100644 index 0000000000..9e6bad625d --- /dev/null +++ b/src/add-ons/kernel/debugger/qrencode/qrinput.h @@ -0,0 +1,123 @@ +/* + * qrencode - QR Code encoder + * + * Input data chunk class + * Copyright (C) 2006-2011 Kentaro Fukuchi + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef __QRINPUT_H__ +#define __QRINPUT_H__ + +#include "qrencode.h" +#include "bitstream.h" + +int QRinput_isSplittableMode(QRencodeMode mode); + +/****************************************************************************** + * Entry of input data + *****************************************************************************/ +typedef struct _QRinput_List QRinput_List; + +struct _QRinput_List { + QRencodeMode mode; + int size; ///< Size of data chunk (byte). + unsigned char *data; ///< Data chunk. + BitStream *bstream; + QRinput_List *next; +}; + +/****************************************************************************** + * Input Data + *****************************************************************************/ +struct _QRinput { + int version; + QRecLevel level; + QRinput_List *head; + QRinput_List *tail; + int mqr; + int fnc1; + unsigned char appid; +}; + +/****************************************************************************** + * Structured append input data + *****************************************************************************/ +typedef struct _QRinput_InputList QRinput_InputList; + +struct _QRinput_InputList { + QRinput *input; + QRinput_InputList *next; +}; + +struct _QRinput_Struct { + int size; ///< number of structured symbols + int parity; + QRinput_InputList *head; + QRinput_InputList *tail; +}; + +/** + * Pack all bit streams padding bits into a byte array. + * @param input input data. + * @return padded merged byte stream + */ +extern unsigned char *QRinput_getByteStream(QRinput *input); + + +extern int QRinput_estimateBitsModeNum(int size); +extern int QRinput_estimateBitsModeAn(int size); +extern int QRinput_estimateBitsMode8(int size); +extern int QRinput_estimateBitsModeKanji(int size); + +extern QRinput *QRinput_dup(QRinput *input); + +extern const signed char QRinput_anTable[128]; + +/** + * Look up the alphabet-numeric convesion table (see JIS X0510:2004, pp.19). + * @param __c__ character + * @return value + */ +#define QRinput_lookAnTable(__c__) \ + ((__c__ & 0x80)?-1:QRinput_anTable[(int)__c__]) + +/** + * Length of a standard mode indicator in bits. + */ + +#define MODE_INDICATOR_SIZE 4 + +/** + * Length of a segment of structured-append header. + */ +#define STRUCTURE_HEADER_SIZE 20 + +/** + * Maximum number of symbols in a set of structured-appended symbols. + */ +#define MAX_STRUCTURED_SYMBOLS 16 + +#ifdef WITH_TESTS +extern BitStream *QRinput_mergeBitStream(QRinput *input); +extern BitStream *QRinput_getBitStream(QRinput *input); +extern int QRinput_estimateBitStreamSize(QRinput *input, int version); +extern int QRinput_splitEntry(QRinput_List *entry, int bytes); +extern int QRinput_lengthOfCode(QRencodeMode mode, int version, int bits); +extern int QRinput_insertStructuredAppendHeader(QRinput *input, int size, int index, unsigned char parity); +#endif + +#endif /* __QRINPUT_H__ */ diff --git a/src/add-ons/kernel/debugger/qrencode/qrspec.c b/src/add-ons/kernel/debugger/qrencode/qrspec.c new file mode 100644 index 0000000000..a42706f4b8 --- /dev/null +++ b/src/add-ons/kernel/debugger/qrencode/qrspec.c @@ -0,0 +1,562 @@ +/* + * qrencode - QR Code encoder + * + * QR Code specification in convenient format. + * Copyright (C) 2006-2011 Kentaro Fukuchi + * + * The following data / specifications are taken from + * "Two dimensional symbol -- QR-code -- Basic Specification" (JIS X0510:2004) + * or + * "Automatic identification and data capture techniques -- + * QR Code 2005 bar code symbology specification" (ISO/IEC 18004:2006) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#if HAVE_CONFIG_H +# include "config.h" +#endif +#include +#include +#include +#include +#ifdef HAVE_LIBPTHREAD +#include +#endif + +#include "qrspec.h" +#include "qrinput.h" + +/****************************************************************************** + * Version and capacity + *****************************************************************************/ + +typedef struct { + int width; //< Edge length of the symbol + int words; //< Data capacity (bytes) + int remainder; //< Remainder bit (bits) + int ec[4]; //< Number of ECC code (bytes) +} QRspec_Capacity; + +/** + * Table of the capacity of symbols + * See Table 1 (pp.13) and Table 12-16 (pp.30-36), JIS X0510:2004. + */ +static const QRspec_Capacity qrspecCapacity[QRSPEC_VERSION_MAX + 1] = { + { 0, 0, 0, { 0, 0, 0, 0}}, + { 21, 26, 0, { 7, 10, 13, 17}}, // 1 + { 25, 44, 7, { 10, 16, 22, 28}}, + { 29, 70, 7, { 15, 26, 36, 44}}, + { 33, 100, 7, { 20, 36, 52, 64}}, + { 37, 134, 7, { 26, 48, 72, 88}}, // 5 + { 41, 172, 7, { 36, 64, 96, 112}}, + { 45, 196, 0, { 40, 72, 108, 130}}, + { 49, 242, 0, { 48, 88, 132, 156}}, + { 53, 292, 0, { 60, 110, 160, 192}}, + { 57, 346, 0, { 72, 130, 192, 224}}, //10 + { 61, 404, 0, { 80, 150, 224, 264}}, + { 65, 466, 0, { 96, 176, 260, 308}}, + { 69, 532, 0, { 104, 198, 288, 352}}, + { 73, 581, 3, { 120, 216, 320, 384}}, + { 77, 655, 3, { 132, 240, 360, 432}}, //15 + { 81, 733, 3, { 144, 280, 408, 480}}, + { 85, 815, 3, { 168, 308, 448, 532}}, + { 89, 901, 3, { 180, 338, 504, 588}}, + { 93, 991, 3, { 196, 364, 546, 650}}, + { 97, 1085, 3, { 224, 416, 600, 700}}, //20 + {101, 1156, 4, { 224, 442, 644, 750}}, + {105, 1258, 4, { 252, 476, 690, 816}}, + {109, 1364, 4, { 270, 504, 750, 900}}, + {113, 1474, 4, { 300, 560, 810, 960}}, + {117, 1588, 4, { 312, 588, 870, 1050}}, //25 + {121, 1706, 4, { 336, 644, 952, 1110}}, + {125, 1828, 4, { 360, 700, 1020, 1200}}, + {129, 1921, 3, { 390, 728, 1050, 1260}}, + {133, 2051, 3, { 420, 784, 1140, 1350}}, + {137, 2185, 3, { 450, 812, 1200, 1440}}, //30 + {141, 2323, 3, { 480, 868, 1290, 1530}}, + {145, 2465, 3, { 510, 924, 1350, 1620}}, + {149, 2611, 3, { 540, 980, 1440, 1710}}, + {153, 2761, 3, { 570, 1036, 1530, 1800}}, + {157, 2876, 0, { 570, 1064, 1590, 1890}}, //35 + {161, 3034, 0, { 600, 1120, 1680, 1980}}, + {165, 3196, 0, { 630, 1204, 1770, 2100}}, + {169, 3362, 0, { 660, 1260, 1860, 2220}}, + {173, 3532, 0, { 720, 1316, 1950, 2310}}, + {177, 3706, 0, { 750, 1372, 2040, 2430}} //40 +}; + +int QRspec_getDataLength(int version, QRecLevel level) +{ + return qrspecCapacity[version].words - qrspecCapacity[version].ec[level]; +} + +int QRspec_getECCLength(int version, QRecLevel level) +{ + return qrspecCapacity[version].ec[level]; +} + +int QRspec_getMinimumVersion(int size, QRecLevel level) +{ + int i; + int words; + + for(i=1; i<= QRSPEC_VERSION_MAX; i++) { + words = qrspecCapacity[i].words - qrspecCapacity[i].ec[level]; + if(words >= size) return i; + } + + return -1; +} + +int QRspec_getWidth(int version) +{ + return qrspecCapacity[version].width; +} + +int QRspec_getRemainder(int version) +{ + return qrspecCapacity[version].remainder; +} + +/****************************************************************************** + * Length indicator + *****************************************************************************/ + +static const int lengthTableBits[4][3] = { + {10, 12, 14}, + { 9, 11, 13}, + { 8, 16, 16}, + { 8, 10, 12} +}; + +int QRspec_lengthIndicator(QRencodeMode mode, int version) +{ + int l; + + if(!QRinput_isSplittableMode(mode)) return 0; + if(version <= 9) { + l = 0; + } else if(version <= 26) { + l = 1; + } else { + l = 2; + } + + return lengthTableBits[mode][l]; +} + +int QRspec_maximumWords(QRencodeMode mode, int version) +{ + int l; + int bits; + int words; + + if(!QRinput_isSplittableMode(mode)) return 0; + if(version <= 9) { + l = 0; + } else if(version <= 26) { + l = 1; + } else { + l = 2; + } + + bits = lengthTableBits[mode][l]; + words = (1 << bits) - 1; + if(mode == QR_MODE_KANJI) { + words *= 2; // the number of bytes is required + } + + return words; +} + +/****************************************************************************** + * Error correction code + *****************************************************************************/ + +/** + * Table of the error correction code (Reed-Solomon block) + * See Table 12-16 (pp.30-36), JIS X0510:2004. + */ +static const int eccTable[QRSPEC_VERSION_MAX+1][4][2] = { + {{ 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}}, + {{ 1, 0}, { 1, 0}, { 1, 0}, { 1, 0}}, // 1 + {{ 1, 0}, { 1, 0}, { 1, 0}, { 1, 0}}, + {{ 1, 0}, { 1, 0}, { 2, 0}, { 2, 0}}, + {{ 1, 0}, { 2, 0}, { 2, 0}, { 4, 0}}, + {{ 1, 0}, { 2, 0}, { 2, 2}, { 2, 2}}, // 5 + {{ 2, 0}, { 4, 0}, { 4, 0}, { 4, 0}}, + {{ 2, 0}, { 4, 0}, { 2, 4}, { 4, 1}}, + {{ 2, 0}, { 2, 2}, { 4, 2}, { 4, 2}}, + {{ 2, 0}, { 3, 2}, { 4, 4}, { 4, 4}}, + {{ 2, 2}, { 4, 1}, { 6, 2}, { 6, 2}}, //10 + {{ 4, 0}, { 1, 4}, { 4, 4}, { 3, 8}}, + {{ 2, 2}, { 6, 2}, { 4, 6}, { 7, 4}}, + {{ 4, 0}, { 8, 1}, { 8, 4}, {12, 4}}, + {{ 3, 1}, { 4, 5}, {11, 5}, {11, 5}}, + {{ 5, 1}, { 5, 5}, { 5, 7}, {11, 7}}, //15 + {{ 5, 1}, { 7, 3}, {15, 2}, { 3, 13}}, + {{ 1, 5}, {10, 1}, { 1, 15}, { 2, 17}}, + {{ 5, 1}, { 9, 4}, {17, 1}, { 2, 19}}, + {{ 3, 4}, { 3, 11}, {17, 4}, { 9, 16}}, + {{ 3, 5}, { 3, 13}, {15, 5}, {15, 10}}, //20 + {{ 4, 4}, {17, 0}, {17, 6}, {19, 6}}, + {{ 2, 7}, {17, 0}, { 7, 16}, {34, 0}}, + {{ 4, 5}, { 4, 14}, {11, 14}, {16, 14}}, + {{ 6, 4}, { 6, 14}, {11, 16}, {30, 2}}, + {{ 8, 4}, { 8, 13}, { 7, 22}, {22, 13}}, //25 + {{10, 2}, {19, 4}, {28, 6}, {33, 4}}, + {{ 8, 4}, {22, 3}, { 8, 26}, {12, 28}}, + {{ 3, 10}, { 3, 23}, { 4, 31}, {11, 31}}, + {{ 7, 7}, {21, 7}, { 1, 37}, {19, 26}}, + {{ 5, 10}, {19, 10}, {15, 25}, {23, 25}}, //30 + {{13, 3}, { 2, 29}, {42, 1}, {23, 28}}, + {{17, 0}, {10, 23}, {10, 35}, {19, 35}}, + {{17, 1}, {14, 21}, {29, 19}, {11, 46}}, + {{13, 6}, {14, 23}, {44, 7}, {59, 1}}, + {{12, 7}, {12, 26}, {39, 14}, {22, 41}}, //35 + {{ 6, 14}, { 6, 34}, {46, 10}, { 2, 64}}, + {{17, 4}, {29, 14}, {49, 10}, {24, 46}}, + {{ 4, 18}, {13, 32}, {48, 14}, {42, 32}}, + {{20, 4}, {40, 7}, {43, 22}, {10, 67}}, + {{19, 6}, {18, 31}, {34, 34}, {20, 61}},//40 +}; + +void QRspec_getEccSpec(int version, QRecLevel level, int spec[5]) +{ + int b1, b2; + int data, ecc; + + b1 = eccTable[version][level][0]; + b2 = eccTable[version][level][1]; + data = QRspec_getDataLength(version, level); + ecc = QRspec_getECCLength(version, level); + + if(b2 == 0) { + spec[0] = b1; + spec[1] = data / b1; + spec[2] = ecc / b1; + spec[3] = spec[4] = 0; + } else { + spec[0] = b1; + spec[1] = data / (b1 + b2); + spec[2] = ecc / (b1 + b2); + spec[3] = b2; + spec[4] = spec[1] + 1; + } +} + +/****************************************************************************** + * Alignment pattern + *****************************************************************************/ + +/** + * Positions of alignment patterns. + * This array includes only the second and the third position of the alignment + * patterns. Rest of them can be calculated from the distance between them. + * + * See Table 1 in Appendix E (pp.71) of JIS X0510:2004. + */ +static const int alignmentPattern[QRSPEC_VERSION_MAX+1][2] = { + { 0, 0}, + { 0, 0}, {18, 0}, {22, 0}, {26, 0}, {30, 0}, // 1- 5 + {34, 0}, {22, 38}, {24, 42}, {26, 46}, {28, 50}, // 6-10 + {30, 54}, {32, 58}, {34, 62}, {26, 46}, {26, 48}, //11-15 + {26, 50}, {30, 54}, {30, 56}, {30, 58}, {34, 62}, //16-20 + {28, 50}, {26, 50}, {30, 54}, {28, 54}, {32, 58}, //21-25 + {30, 58}, {34, 62}, {26, 50}, {30, 54}, {26, 52}, //26-30 + {30, 56}, {34, 60}, {30, 58}, {34, 62}, {30, 54}, //31-35 + {24, 50}, {28, 54}, {32, 58}, {26, 54}, {30, 58}, //35-40 +}; + +/** + * Put an alignment marker. + * @param frame + * @param width + * @param ox,oy center coordinate of the pattern + */ +static void QRspec_putAlignmentMarker(unsigned char *frame, int width, int ox, int oy) +{ + static const unsigned char finder[] = { + 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, + 0xa1, 0xa0, 0xa0, 0xa0, 0xa1, + 0xa1, 0xa0, 0xa1, 0xa0, 0xa1, + 0xa1, 0xa0, 0xa0, 0xa0, 0xa1, + 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, + }; + int x, y; + const unsigned char *s; + + frame += (oy - 2) * width + ox - 2; + s = finder; + for(y=0; y<5; y++) { + for(x=0; x<5; x++) { + frame[x] = s[x]; + } + frame += width; + s += 5; + } +} + +static void QRspec_putAlignmentPattern(int version, unsigned char *frame, int width) +{ + int d, w, x, y, cx, cy; + + if(version < 2) return; + + d = alignmentPattern[version][1] - alignmentPattern[version][0]; + if(d < 0) { + w = 2; + } else { + w = (width - alignmentPattern[version][0]) / d + 2; + } + + if(w * w - 3 == 1) { + x = alignmentPattern[version][0]; + y = alignmentPattern[version][0]; + QRspec_putAlignmentMarker(frame, width, x, y); + return; + } + + cx = alignmentPattern[version][0]; + for(x=1; x QRSPEC_VERSION_MAX) return 0; + + return versionPattern[version - 7]; +} + +/****************************************************************************** + * Format information + *****************************************************************************/ + +/* See calcFormatInfo in tests/test_qrspec.c */ +static const unsigned int formatInfo[4][8] = { + {0x77c4, 0x72f3, 0x7daa, 0x789d, 0x662f, 0x6318, 0x6c41, 0x6976}, + {0x5412, 0x5125, 0x5e7c, 0x5b4b, 0x45f9, 0x40ce, 0x4f97, 0x4aa0}, + {0x355f, 0x3068, 0x3f31, 0x3a06, 0x24b4, 0x2183, 0x2eda, 0x2bed}, + {0x1689, 0x13be, 0x1ce7, 0x19d0, 0x0762, 0x0255, 0x0d0c, 0x083b} +}; + +unsigned int QRspec_getFormatInfo(int mask, QRecLevel level) +{ + if(mask < 0 || mask > 7) return 0; + + return formatInfo[level][mask]; +} + +/****************************************************************************** + * Frame + *****************************************************************************/ + +/** + * Cache of initial frames. + */ +/* C99 says that static storage shall be initialized to a null pointer + * by compiler. */ +static unsigned char *frames[QRSPEC_VERSION_MAX + 1]; +#ifdef HAVE_LIBPTHREAD +static pthread_mutex_t frames_mutex = PTHREAD_MUTEX_INITIALIZER; +#endif + +/** + * Put a finder pattern. + * @param frame + * @param width + * @param ox,oy upper-left coordinate of the pattern + */ +static void putFinderPattern(unsigned char *frame, int width, int ox, int oy) +{ + static const unsigned char finder[] = { + 0xc1, 0xc1, 0xc1, 0xc1, 0xc1, 0xc1, 0xc1, + 0xc1, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc1, + 0xc1, 0xc0, 0xc1, 0xc1, 0xc1, 0xc0, 0xc1, + 0xc1, 0xc0, 0xc1, 0xc1, 0xc1, 0xc0, 0xc1, + 0xc1, 0xc0, 0xc1, 0xc1, 0xc1, 0xc0, 0xc1, + 0xc1, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc1, + 0xc1, 0xc1, 0xc1, 0xc1, 0xc1, 0xc1, 0xc1, + }; + int x, y; + const unsigned char *s; + + frame += oy * width + ox; + s = finder; + for(y=0; y<7; y++) { + for(x=0; x<7; x++) { + frame[x] = s[x]; + } + frame += width; + s += 7; + } +} + + +static unsigned char *QRspec_createFrame(int version) +{ + unsigned char *frame, *p, *q; + int width; + int x, y; + unsigned int verinfo, v; + + width = qrspecCapacity[version].width; + frame = (unsigned char *)malloc(width * width); + if(frame == NULL) return NULL; + + memset(frame, 0, width * width); + /* Finder pattern */ + putFinderPattern(frame, width, 0, 0); + putFinderPattern(frame, width, width - 7, 0); + putFinderPattern(frame, width, 0, width - 7); + /* Separator */ + p = frame; + q = frame + width * (width - 7); + for(y=0; y<7; y++) { + p[7] = 0xc0; + p[width - 8] = 0xc0; + q[7] = 0xc0; + p += width; + q += width; + } + memset(frame + width * 7, 0xc0, 8); + memset(frame + width * 8 - 8, 0xc0, 8); + memset(frame + width * (width - 8), 0xc0, 8); + /* Mask format information area */ + memset(frame + width * 8, 0x84, 9); + memset(frame + width * 9 - 8, 0x84, 8); + p = frame + 8; + for(y=0; y<8; y++) { + *p = 0x84; + p += width; + } + p = frame + width * (width - 7) + 8; + for(y=0; y<7; y++) { + *p = 0x84; + p += width; + } + /* Timing pattern */ + p = frame + width * 6 + 8; + q = frame + width * 8 + 6; + for(x=1; x= 7) { + verinfo = QRspec_getVersionPattern(version); + + p = frame + width * (width - 11); + v = verinfo; + for(x=0; x<6; x++) { + for(y=0; y<3; y++) { + p[width * y + x] = 0x88 | (v & 1); + v = v >> 1; + } + } + + p = frame + width - 11; + v = verinfo; + for(y=0; y<6; y++) { + for(x=0; x<3; x++) { + p[x] = 0x88 | (v & 1); + v = v >> 1; + } + p += width; + } + } + /* and a little bit... */ + frame[width * (width - 8) + 8] = 0x81; + + return frame; +} + +unsigned char *QRspec_newFrame(int version) +{ + unsigned char *frame; + int width; + + if(version < 1 || version > QRSPEC_VERSION_MAX) return NULL; + +#ifdef HAVE_LIBPTHREAD + pthread_mutex_lock(&frames_mutex); +#endif + if(frames[version] == NULL) { + frames[version] = QRspec_createFrame(version); + } +#ifdef HAVE_LIBPTHREAD + pthread_mutex_unlock(&frames_mutex); +#endif + if(frames[version] == NULL) return NULL; + + width = qrspecCapacity[version].width; + frame = (unsigned char *)malloc(width * width); + if(frame == NULL) return NULL; + memcpy(frame, frames[version], width * width); + + return frame; +} + +void QRspec_clearCache(void) +{ + int i; + +#ifdef HAVE_LIBPTHREAD + pthread_mutex_lock(&frames_mutex); +#endif + for(i=1; i<=QRSPEC_VERSION_MAX; i++) { + free(frames[i]); + frames[i] = NULL; + } +#ifdef HAVE_LIBPTHREAD + pthread_mutex_unlock(&frames_mutex); +#endif +} diff --git a/src/add-ons/kernel/debugger/qrencode/qrspec.h b/src/add-ons/kernel/debugger/qrencode/qrspec.h new file mode 100644 index 0000000000..54a3d9f09e --- /dev/null +++ b/src/add-ons/kernel/debugger/qrencode/qrspec.h @@ -0,0 +1,181 @@ +/* + * qrencode - QR Code encoder + * + * QR Code specification in convenient format. + * Copyright (C) 2006-2011 Kentaro Fukuchi + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef __QRSPEC_H__ +#define __QRSPEC_H__ + +#include "qrencode.h" + +/****************************************************************************** + * Version and capacity + *****************************************************************************/ + +/** + * Maximum width of a symbol + */ +#define QRSPEC_WIDTH_MAX 177 + +/** + * Return maximum data code length (bytes) for the version. + * @param version + * @param level + * @return maximum size (bytes) + */ +extern int QRspec_getDataLength(int version, QRecLevel level); + +/** + * Return maximum error correction code length (bytes) for the version. + * @param version + * @param level + * @return ECC size (bytes) + */ +extern int QRspec_getECCLength(int version, QRecLevel level); + +/** + * Return a version number that satisfies the input code length. + * @param size input code length (byte) + * @param level + * @return version number + */ +extern int QRspec_getMinimumVersion(int size, QRecLevel level); + +/** + * Return the width of the symbol for the version. + * @param version + * @return width + */ +extern int QRspec_getWidth(int version); + +/** + * Return the numer of remainder bits. + * @param version + * @return number of remainder bits + */ +extern int QRspec_getRemainder(int version); + +/****************************************************************************** + * Length indicator + *****************************************************************************/ + +/** + * Return the size of lenght indicator for the mode and version. + * @param mode + * @param version + * @return the size of the appropriate length indicator (bits). + */ +extern int QRspec_lengthIndicator(QRencodeMode mode, int version); + +/** + * Return the maximum length for the mode and version. + * @param mode + * @param version + * @return the maximum length (bytes) + */ +extern int QRspec_maximumWords(QRencodeMode mode, int version); + +/****************************************************************************** + * Error correction code + *****************************************************************************/ + +/** + * Return an array of ECC specification. + * @param version + * @param level + * @param spec an array of ECC specification contains as following: + * {# of type1 blocks, # of data code, # of ecc code, + * # of type2 blocks, # of data code} + */ +void QRspec_getEccSpec(int version, QRecLevel level, int spec[5]); + +#define QRspec_rsBlockNum(__spec__) (__spec__[0] + __spec__[3]) +#define QRspec_rsBlockNum1(__spec__) (__spec__[0]) +#define QRspec_rsDataCodes1(__spec__) (__spec__[1]) +#define QRspec_rsEccCodes1(__spec__) (__spec__[2]) +#define QRspec_rsBlockNum2(__spec__) (__spec__[3]) +#define QRspec_rsDataCodes2(__spec__) (__spec__[4]) +#define QRspec_rsEccCodes2(__spec__) (__spec__[2]) + +#define QRspec_rsDataLength(__spec__) \ + ((QRspec_rsBlockNum1(__spec__) * QRspec_rsDataCodes1(__spec__)) + \ + (QRspec_rsBlockNum2(__spec__) * QRspec_rsDataCodes2(__spec__))) +#define QRspec_rsEccLength(__spec__) \ + (QRspec_rsBlockNum(__spec__) * QRspec_rsEccCodes1(__spec__)) + +/****************************************************************************** + * Version information pattern + *****************************************************************************/ + +/** + * Return BCH encoded version information pattern that is used for the symbol + * of version 7 or greater. Use lower 18 bits. + * @param version + * @return BCH encoded version information pattern + */ +extern unsigned int QRspec_getVersionPattern(int version); + +/****************************************************************************** + * Format information + *****************************************************************************/ + +/** + * Return BCH encoded format information pattern. + * @param mask + * @param level + * @return BCH encoded format information pattern + */ +extern unsigned int QRspec_getFormatInfo(int mask, QRecLevel level); + +/****************************************************************************** + * Frame + *****************************************************************************/ + +/** + * Return a copy of initialized frame. + * When the same version is requested twice or more, a copy of cached frame + * is returned. + * @param version + * @return Array of unsigned char. You can free it by free(). + */ +extern unsigned char *QRspec_newFrame(int version); + +/** + * Clear the frame cache. Typically for debug. + */ +extern void QRspec_clearCache(void); + +/****************************************************************************** + * Mode indicator + *****************************************************************************/ + +/** + * Mode indicator. See Table 2 of JIS X0510:2004, pp.16. + */ +#define QRSPEC_MODEID_ECI 7 +#define QRSPEC_MODEID_NUM 1 +#define QRSPEC_MODEID_AN 2 +#define QRSPEC_MODEID_8 4 +#define QRSPEC_MODEID_KANJI 8 +#define QRSPEC_MODEID_FNC1FIRST 5 +#define QRSPEC_MODEID_FNC1SECOND 9 +#define QRSPEC_MODEID_STRUCTURE 3 +#define QRSPEC_MODEID_TERMINATOR 0 + +#endif /* __QRSPEC_H__ */ diff --git a/src/add-ons/kernel/debugger/qrencode/rscode.c b/src/add-ons/kernel/debugger/qrencode/rscode.c new file mode 100644 index 0000000000..edc32e2c92 --- /dev/null +++ b/src/add-ons/kernel/debugger/qrencode/rscode.c @@ -0,0 +1,327 @@ +/* + * qrencode - QR Code encoder + * + * Reed solomon encoder. This code is taken from Phil Karn's libfec then + * editted and packed into a pair of .c and .h files. + * + * Copyright (C) 2002, 2003, 2004, 2006 Phil Karn, KA9Q + * (libfec is released under the GNU Lesser General Public License.) + * + * Copyright (C) 2006-2011 Kentaro Fukuchi + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#if HAVE_CONFIG_H +# include "config.h" +#endif +#include +#include +#ifdef HAVE_LIBPTHREAD +# include +#endif + +#include "rscode.h" + +/* Stuff specific to the 8-bit symbol version of the general purpose RS codecs + * + */ +typedef unsigned char data_t; + + +/** + * Reed-Solomon codec control block + */ +struct _RS { + int mm; /* Bits per symbol */ + int nn; /* Symbols per block (= (1<= rs->nn) { + x -= rs->nn; + x = (x >> rs->mm) + (x & rs->nn); + } + return x; +} + + +#define MODNN(x) modnn(rs,x) + +#define MM (rs->mm) +#define NN (rs->nn) +#define ALPHA_TO (rs->alpha_to) +#define INDEX_OF (rs->index_of) +#define GENPOLY (rs->genpoly) +#define NROOTS (rs->nroots) +#define FCR (rs->fcr) +#define PRIM (rs->prim) +#define IPRIM (rs->iprim) +#define PAD (rs->pad) +#define A0 (NN) + + +/* Initialize a Reed-Solomon codec + * symsize = symbol size, bits + * gfpoly = Field generator polynomial coefficients + * fcr = first root of RS code generator polynomial, index form + * prim = primitive element to generate polynomial roots + * nroots = RS code generator polynomial degree (number of roots) + * pad = padding bytes at front of shortened block + */ +static RS *init_rs_char(int symsize, int gfpoly, int fcr, int prim, int nroots, int pad) +{ + RS *rs; + + +/* Common code for intializing a Reed-Solomon control block (char or int symbols) + * Copyright 2004 Phil Karn, KA9Q + * May be used under the terms of the GNU Lesser General Public License (LGPL) + */ +//#undef NULL +//#define NULL ((void *)0) + + int i, j, sr,root,iprim; + + rs = NULL; + /* Check parameter ranges */ + if(symsize < 0 || symsize > (int)(8*sizeof(data_t))){ + goto done; + } + + if(fcr < 0 || fcr >= (1<= (1<= (1<= ((1<mm = symsize; + rs->nn = (1<pad = pad; + + rs->alpha_to = (data_t *)malloc(sizeof(data_t)*(rs->nn+1)); + if(rs->alpha_to == NULL){ + free(rs); + rs = NULL; + goto done; + } + rs->index_of = (data_t *)malloc(sizeof(data_t)*(rs->nn+1)); + if(rs->index_of == NULL){ + free(rs->alpha_to); + free(rs); + rs = NULL; + goto done; + } + + /* Generate Galois field lookup tables */ + rs->index_of[0] = A0; /* log(zero) = -inf */ + rs->alpha_to[A0] = 0; /* alpha**-inf = 0 */ + sr = 1; + for(i=0;inn;i++){ + rs->index_of[sr] = i; + rs->alpha_to[i] = sr; + sr <<= 1; + if(sr & (1<nn; + } + if(sr != 1){ + /* field generator polynomial is not primitive! */ + free(rs->alpha_to); + free(rs->index_of); + free(rs); + rs = NULL; + goto done; + } + + /* Form RS code generator polynomial from its roots */ + rs->genpoly = (data_t *)malloc(sizeof(data_t)*(nroots+1)); + if(rs->genpoly == NULL){ + free(rs->alpha_to); + free(rs->index_of); + free(rs); + rs = NULL; + goto done; + } + rs->fcr = fcr; + rs->prim = prim; + rs->nroots = nroots; + rs->gfpoly = gfpoly; + + /* Find prim-th root of 1, used in decoding */ + for(iprim=1;(iprim % prim) != 0;iprim += rs->nn) + ; + rs->iprim = iprim / prim; + + rs->genpoly[0] = 1; + for (i = 0,root=fcr*prim; i < nroots; i++,root += prim) { + rs->genpoly[i+1] = 1; + + /* Multiply rs->genpoly[] by @**(root + x) */ + for (j = i; j > 0; j--){ + if (rs->genpoly[j] != 0) + rs->genpoly[j] = rs->genpoly[j-1] ^ rs->alpha_to[modnn(rs,rs->index_of[rs->genpoly[j]] + root)]; + else + rs->genpoly[j] = rs->genpoly[j-1]; + } + /* rs->genpoly[0] can never be zero */ + rs->genpoly[0] = rs->alpha_to[modnn(rs,rs->index_of[rs->genpoly[0]] + root)]; + } + /* convert rs->genpoly[] to index form for quicker encoding */ + for (i = 0; i <= nroots; i++) + rs->genpoly[i] = rs->index_of[rs->genpoly[i]]; + done:; + + return rs; +} + +RS *init_rs(int symsize, int gfpoly, int fcr, int prim, int nroots, int pad) +{ + RS *rs; + +#ifdef HAVE_LIBPTHREAD + pthread_mutex_lock(&rslist_mutex); +#endif + for(rs = rslist; rs != NULL; rs = rs->next) { + if(rs->pad != pad) continue; + if(rs->nroots != nroots) continue; + if(rs->mm != symsize) continue; + if(rs->gfpoly != gfpoly) continue; + if(rs->fcr != fcr) continue; + if(rs->prim != prim) continue; + + goto DONE; + } + + rs = init_rs_char(symsize, gfpoly, fcr, prim, nroots, pad); + if(rs == NULL) goto DONE; + rs->next = rslist; + rslist = rs; + +DONE: +#ifdef HAVE_LIBPTHREAD + pthread_mutex_unlock(&rslist_mutex); +#endif + return rs; +} + + +void free_rs_char(RS *rs) +{ + free(rs->alpha_to); + free(rs->index_of); + free(rs->genpoly); + free(rs); +} + +void free_rs_cache(void) +{ + RS *rs, *next; + +#ifdef HAVE_LIBPTHREAD + pthread_mutex_lock(&rslist_mutex); +#endif + rs = rslist; + while(rs != NULL) { + next = rs->next; + free_rs_char(rs); + rs = next; + } + rslist = NULL; +#ifdef HAVE_LIBPTHREAD + pthread_mutex_unlock(&rslist_mutex); +#endif +} + +/* The guts of the Reed-Solomon encoder, meant to be #included + * into a function body with the following typedefs, macros and variables supplied + * according to the code parameters: + + * data_t - a typedef for the data symbol + * data_t data[] - array of NN-NROOTS-PAD and type data_t to be encoded + * data_t parity[] - an array of NROOTS and type data_t to be written with parity symbols + * NROOTS - the number of roots in the RS code generator polynomial, + * which is the same as the number of parity symbols in a block. + Integer variable or literal. + * + * NN - the total number of symbols in a RS block. Integer variable or literal. + * PAD - the number of pad symbols in a block. Integer variable or literal. + * ALPHA_TO - The address of an array of NN elements to convert Galois field + * elements in index (log) form to polynomial form. Read only. + * INDEX_OF - The address of an array of NN elements to convert Galois field + * elements in polynomial form to index (log) form. Read only. + * MODNN - a function to reduce its argument modulo NN. May be inline or a macro. + * GENPOLY - an array of NROOTS+1 elements containing the generator polynomial in index form + + * The memset() and memmove() functions are used. The appropriate header + * file declaring these functions (usually ) must be included by the calling + * program. + + * Copyright 2004, Phil Karn, KA9Q + * May be used under the terms of the GNU Lesser General Public License (LGPL) + */ + +#undef A0 +#define A0 (NN) /* Special reserved value encoding zero in index form */ + +void encode_rs_char(RS *rs, const data_t *data, data_t *parity) +{ + int i, j; + data_t feedback; + + memset(parity,0,NROOTS*sizeof(data_t)); + + for(i=0;i + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef __RSCODE_H__ +#define __RSCODE_H__ + +/* + * General purpose RS codec, 8-bit symbols. + */ + +typedef struct _RS RS; + +extern RS *init_rs(int symsize, int gfpoly, int fcr, int prim, int nroots, int pad); +extern void encode_rs_char(RS *rs, const unsigned char *data, unsigned char *parity); +extern void free_rs_char(RS *rs); +extern void free_rs_cache(void); + +#endif /* __RSCODE_H__ */ diff --git a/src/add-ons/kernel/debugger/qrencode/split.c b/src/add-ons/kernel/debugger/qrencode/split.c new file mode 100644 index 0000000000..cf66d0a177 --- /dev/null +++ b/src/add-ons/kernel/debugger/qrencode/split.c @@ -0,0 +1,311 @@ +/* + * qrencode - QR Code encoder + * + * Input data splitter. + * Copyright (C) 2006-2011 Kentaro Fukuchi + * + * The following data / specifications are taken from + * "Two dimensional symbol -- QR-code -- Basic Specification" (JIS X0510:2004) + * or + * "Automatic identification and data capture techniques -- + * QR Code 2005 bar code symbology specification" (ISO/IEC 18004:2006) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#if HAVE_CONFIG_H +# include "config.h" +#endif +#include +#include +#include +#include "qrencode.h" +#include "qrinput.h" +#include "qrspec.h" +#include "split.h" + +#define isdigit(__c__) ((unsigned char)((signed char)(__c__) - '0') < 10) +#define isalnum(__c__) (QRinput_lookAnTable(__c__) >= 0) + +#if !HAVE_STRDUP +#undef strdup +char *strdup_internal(const char *s) +{ + size_t len = strlen(s) + 1; + void *new = malloc(len); + if(new == NULL) return NULL; + return (char *)memcpy(new, s, len); +} +#endif + +static QRencodeMode Split_identifyMode(const char *string, QRencodeMode hint) +{ + unsigned char c, d; + unsigned int word; + + c = string[0]; + + if(c == '\0') return QR_MODE_NUL; + if(isdigit(c)) { + return QR_MODE_NUM; + } else if(isalnum(c)) { + return QR_MODE_AN; + } else if(hint == QR_MODE_KANJI) { + d = string[1]; + if(d != '\0') { + word = ((unsigned int)c << 8) | d; + if((word >= 0x8140 && word <= 0x9ffc) || (word >= 0xe040 && word <= 0xebbf)) { + return QR_MODE_KANJI; + } + } + } + + return QR_MODE_8; +} + +static int Split_eatNum(const char *string, QRinput *input, QRencodeMode hint); +static int Split_eatAn(const char *string, QRinput *input, QRencodeMode hint); +static int Split_eat8(const char *string, QRinput *input, QRencodeMode hint); +static int Split_eatKanji(const char *string, QRinput *input, QRencodeMode hint); + +static int Split_eatNum(const char *string, QRinput *input,QRencodeMode hint) +{ + const char *p; + int ret; + int run; + int dif; + int ln; + QRencodeMode mode; + + ln = QRspec_lengthIndicator(QR_MODE_NUM, input->version); + + p = string; + while(isdigit(*p)) { + p++; + } + run = p - string; + mode = Split_identifyMode(p, hint); + if(mode == QR_MODE_8) { + dif = QRinput_estimateBitsModeNum(run) + 4 + ln + + QRinput_estimateBitsMode8(1) /* + 4 + l8 */ + - QRinput_estimateBitsMode8(run + 1) /* - 4 - l8 */; + if(dif > 0) { + return Split_eat8(string, input, hint); + } + } + if(mode == QR_MODE_AN) { + dif = QRinput_estimateBitsModeNum(run) + 4 + ln + + QRinput_estimateBitsModeAn(1) /* + 4 + la */ + - QRinput_estimateBitsModeAn(run + 1) /* - 4 - la */; + if(dif > 0) { + return Split_eatAn(string, input, hint); + } + } + + ret = QRinput_append(input, QR_MODE_NUM, run, (unsigned char *)string); + if(ret < 0) return -1; + + return run; +} + +static int Split_eatAn(const char *string, QRinput *input, QRencodeMode hint) +{ + const char *p, *q; + int ret; + int run; + int dif; + int la, ln; + + la = QRspec_lengthIndicator(QR_MODE_AN, input->version); + ln = QRspec_lengthIndicator(QR_MODE_NUM, input->version); + + p = string; + while(isalnum(*p)) { + if(isdigit(*p)) { + q = p; + while(isdigit(*q)) { + q++; + } + dif = QRinput_estimateBitsModeAn(p - string) /* + 4 + la */ + + QRinput_estimateBitsModeNum(q - p) + 4 + ln + - QRinput_estimateBitsModeAn(q - string) /* - 4 - la */; + if(dif < 0) { + break; + } else { + p = q; + } + } else { + p++; + } + } + + run = p - string; + + if(*p && !isalnum(*p)) { + dif = QRinput_estimateBitsModeAn(run) + 4 + la + + QRinput_estimateBitsMode8(1) /* + 4 + l8 */ + - QRinput_estimateBitsMode8(run + 1) /* - 4 - l8 */; + if(dif > 0) { + return Split_eat8(string, input, hint); + } + } + + ret = QRinput_append(input, QR_MODE_AN, run, (unsigned char *)string); + if(ret < 0) return -1; + + return run; +} + +static int Split_eatKanji(const char *string, QRinput *input, QRencodeMode hint) +{ + const char *p; + int ret; + int run; + + p = string; + while(Split_identifyMode(p, hint) == QR_MODE_KANJI) { + p += 2; + } + run = p - string; + ret = QRinput_append(input, QR_MODE_KANJI, run, (unsigned char *)string); + if(ret < 0) return -1; + + return run; +} + +static int Split_eat8(const char *string, QRinput *input, QRencodeMode hint) +{ + const char *p, *q; + QRencodeMode mode; + int ret; + int run; + int dif; + int la, ln; + + la = QRspec_lengthIndicator(QR_MODE_AN, input->version); + ln = QRspec_lengthIndicator(QR_MODE_NUM, input->version); + + p = string + 1; + while(*p != '\0') { + mode = Split_identifyMode(p, hint); + if(mode == QR_MODE_KANJI) { + break; + } + if(mode == QR_MODE_NUM) { + q = p; + while(isdigit(*q)) { + q++; + } + dif = QRinput_estimateBitsMode8(p - string) /* + 4 + l8 */ + + QRinput_estimateBitsModeNum(q - p) + 4 + ln + - QRinput_estimateBitsMode8(q - string) /* - 4 - l8 */; + if(dif < 0) { + break; + } else { + p = q; + } + } else if(mode == QR_MODE_AN) { + q = p; + while(isalnum(*q)) { + q++; + } + dif = QRinput_estimateBitsMode8(p - string) /* + 4 + l8 */ + + QRinput_estimateBitsModeAn(q - p) + 4 + la + - QRinput_estimateBitsMode8(q - string) /* - 4 - l8 */; + if(dif < 0) { + break; + } else { + p = q; + } + } else { + p++; + } + } + + run = p - string; + ret = QRinput_append(input, QR_MODE_8, run, (unsigned char *)string); + if(ret < 0) return -1; + + return run; +} + +static int Split_splitString(const char *string, QRinput *input, + QRencodeMode hint) +{ + int length; + QRencodeMode mode; + + if(*string == '\0') return 0; + + mode = Split_identifyMode(string, hint); + if(mode == QR_MODE_NUM) { + length = Split_eatNum(string, input, hint); + } else if(mode == QR_MODE_AN) { + length = Split_eatAn(string, input, hint); + } else if(mode == QR_MODE_KANJI && hint == QR_MODE_KANJI) { + length = Split_eatKanji(string, input, hint); + } else { + length = Split_eat8(string, input, hint); + } + if(length == 0) return 0; + if(length < 0) return -1; + return Split_splitString(&string[length], input, hint); +} + +static char *dupAndToUpper(const char *str, QRencodeMode hint) +{ + char *newstr, *p; + QRencodeMode mode; + + newstr = strdup_internal(str); + if(newstr == NULL) return NULL; + + p = newstr; + while(*p != '\0') { + mode = Split_identifyMode(p, hint); + if(mode == QR_MODE_KANJI) { + p += 2; + } else { + if (*p >= 'a' && *p <= 'z') { + *p = (char)((int)*p - 32); + } + p++; + } + } + + return newstr; +} + +int Split_splitStringToQRinput(const char *string, QRinput *input, + QRencodeMode hint, int casesensitive) +{ + char *newstr; + int ret; + + if(string == NULL || *string == '\0') { + errno = EINVAL; + return -1; + } + if(!casesensitive) { + newstr = dupAndToUpper(string, hint); + if(newstr == NULL) return -1; + ret = Split_splitString(newstr, input, hint); + free(newstr); + } else { + ret = Split_splitString(string, input, hint); + } + + return ret; +} diff --git a/src/add-ons/kernel/debugger/qrencode/split.h b/src/add-ons/kernel/debugger/qrencode/split.h new file mode 100644 index 0000000000..b2cdbe5468 --- /dev/null +++ b/src/add-ons/kernel/debugger/qrencode/split.h @@ -0,0 +1,47 @@ +/* + * qrencode - QR Code encoder + * + * Input data splitter. + * Copyright (C) 2006-2011 Kentaro Fukuchi + * + * The following data / specifications are taken from + * "Two dimensional symbol -- QR-code -- Basic Specification" (JIS X0510:2004) + * or + * "Automatic identification and data capture techniques -- + * QR Code 2005 bar code symbology specification" (ISO/IEC 18004:2006) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef __SPLIT_H__ +#define __SPLIT_H__ + +#include "qrencode.h" + +/** + * Split the input string (null terminated) into QRinput. + * @param string input string + * @param hint give QR_MODE_KANJI if the input string contains Kanji character encoded in Shift-JIS. If not, give QR_MODE_8. + * @param casesensitive 0 for case-insensitive encoding (all alphabet characters are replaced to UPPER-CASE CHARACTERS. + * @retval 0 success. + * @retval -1 an error occurred. errno is set to indicate the error. See + * Exceptions for the details. + * @throw EINVAL invalid input object. + * @throw ENOMEM unable to allocate memory for input objects. + */ +extern int Split_splitStringToQRinput(const char *string, QRinput *input, + QRencodeMode hint, int casesensitive); + +#endif /* __SPLIT_H__ */ diff --git a/src/add-ons/media/plugins/ffmpeg/AVFormatReader.cpp b/src/add-ons/media/plugins/ffmpeg/AVFormatReader.cpp index 9b3de2d213..23b0b66815 100644 --- a/src/add-ons/media/plugins/ffmpeg/AVFormatReader.cpp +++ b/src/add-ons/media/plugins/ffmpeg/AVFormatReader.cpp @@ -1450,6 +1450,7 @@ AVFormatReader::Stream::Seek(uint32 flags, int64* frame, bigtime_t* time) AVFormatReader::AVFormatReader() : + fCopyright(""), fStreams(NULL), fSourceLock("source I/O lock") { @@ -1478,12 +1479,12 @@ AVFormatReader::~AVFormatReader() const char* AVFormatReader::Copyright() { -// TODO: Could not find the equivalent in libavformat >= version 53. -// Use metadata API instead! -// if (fStreams != NULL && fStreams[0] != NULL) -// return fStreams[0]->Context()->copyright; - // TODO: Return copyright of the file instead! - return "Copyright 2009, Stephan Aßmus"; + if (fCopyright.Length() <= 0) { + BMessage message; + if (GetMetaData(&message) == B_OK) + message.FindString("copyright", &fCopyright); + } + return fCopyright.String(); } @@ -1638,9 +1639,9 @@ AVFormatReader::GetMetaData(BMessage* _data) // Add program info for (unsigned i = 0; i < context->nb_programs; i++) { - BMessage progamData; - avdictionary_to_message(context->programs[i]->metadata, &progamData); - _data->AddMessage("be:program", &progamData); + BMessage programData; + avdictionary_to_message(context->programs[i]->metadata, &programData); + _data->AddMessage("be:program", &programData); } return B_OK; diff --git a/src/add-ons/media/plugins/ffmpeg/AVFormatReader.h b/src/add-ons/media/plugins/ffmpeg/AVFormatReader.h index 256ee50429..9ffa4570dd 100644 --- a/src/add-ons/media/plugins/ffmpeg/AVFormatReader.h +++ b/src/add-ons/media/plugins/ffmpeg/AVFormatReader.h @@ -7,6 +7,7 @@ #include +#include #include "ReaderPlugin.h" @@ -48,6 +49,7 @@ public: private: class Stream; + BString fCopyright; Stream** fStreams; BLocker fSourceLock; }; diff --git a/src/apps/Jamfile b/src/apps/Jamfile index 0d7b2f7519..cd66b3e6c1 100644 --- a/src/apps/Jamfile +++ b/src/apps/Jamfile @@ -59,5 +59,6 @@ HaikuSubInclude terminal ; HaikuSubInclude text_search ; HaikuSubInclude tracker ; HaikuSubInclude tv ; +HaikuSubInclude webpositive ; HaikuSubInclude webwatch ; HaikuSubInclude workspaces ; diff --git a/src/apps/aboutsystem/AboutSystem.cpp b/src/apps/aboutsystem/AboutSystem.cpp index deba9af5e7..ec3303c6ab 100644 --- a/src/apps/aboutsystem/AboutSystem.cpp +++ b/src/apps/aboutsystem/AboutSystem.cpp @@ -1694,6 +1694,12 @@ AboutView::_CreateCreditsView() .SetLicense(B_TRANSLATE("BSD (3-clause)")) .SetURL("http://gtf.sourceforge.net/")); + // libqrencode + _AddPackageCredit(PackageCredit("libqrencode") + .SetCopyright(B_TRANSLATE(COPYRIGHT_STRING "2006-2012 Kentaro Fukuchi")) + .SetLicense(B_TRANSLATE("GNU LGPL v2.1")) + .SetURL("http://fukuchi.org/works/qrencode/")); + _AddCopyrightsFromAttribute(); _AddPackageCreditEntries(); diff --git a/src/apps/codycam/CodyCam.cpp b/src/apps/codycam/CodyCam.cpp index 2a68bce107..0df1c060a6 100644 --- a/src/apps/codycam/CodyCam.cpp +++ b/src/apps/codycam/CodyCam.cpp @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -22,6 +23,7 @@ #include #include +#include #undef B_TRANSLATION_CONTEXT #define B_TRANSLATION_CONTEXT "CodyCam" @@ -169,7 +171,11 @@ CodyCam::CodyCam() kUploadClients[index++] = B_TRANSLATE("SFTP"); kUploadClients[index++] = B_TRANSLATE("Local"); - chdir("/boot/home"); + BPath homeDir; + if (find_directory(B_USER_DIRECTORY, &homeDir) != B_OK) + homeDir.SetTo("/boot/home"); + + chdir(homeDir.Path()); } diff --git a/src/apps/icon-o-matic/Icon-O-Matic.rdef b/src/apps/icon-o-matic/Icon-O-Matic.rdef index c0b28b6101..b349c8de07 100644 --- a/src/apps/icon-o-matic/Icon-O-Matic.rdef +++ b/src/apps/icon-o-matic/Icon-O-Matic.rdef @@ -20,6 +20,11 @@ resource app_version { resource app_flags B_SINGLE_LAUNCH; +resource file_types message { + "types" = "application/x-vnd.Haiku-icon", + "types" = "image/svg+xml" +}; + resource vector_icon { $"6E6369660E050102000603399E0F3D9C0ABF82B23B84A94B88504910C900A5B1" $"FFBCEAF1FFFFB3B8FF020106023E49240000000000003CAAAA4940004A8000FF" diff --git a/src/apps/icon-o-matic/IconEditorApp.cpp b/src/apps/icon-o-matic/IconEditorApp.cpp index 65c48b8610..27f73a9484 100644 --- a/src/apps/icon-o-matic/IconEditorApp.cpp +++ b/src/apps/icon-o-matic/IconEditorApp.cpp @@ -17,6 +17,8 @@ #include #include #include +#include +#include #include "support_settings.h" @@ -329,8 +331,14 @@ IconEditorApp::_LastFilePath(path_kind which) path = fLastOpenPath.String(); break; } - if (!path) - path = "/boot/home"; + if (!path) { + + BPath homePath; + if (find_directory(B_USER_DIRECTORY, &homePath) == B_OK) + path = homePath.Path(); + else + path = "/boot/home"; + } return path; } diff --git a/src/apps/mediaconverter/MediaConverterWindow.cpp b/src/apps/mediaconverter/MediaConverterWindow.cpp index 2d1cc7e8cf..d97dcb1b0e 100644 --- a/src/apps/mediaconverter/MediaConverterWindow.cpp +++ b/src/apps/mediaconverter/MediaConverterWindow.cpp @@ -31,6 +31,8 @@ #include #include +#include + #include "MediaFileInfoView.h" #include "MediaFileListView.h" #include "MessageConstants.h" @@ -122,8 +124,10 @@ MediaConverterWindow::MediaConverterWindow(BRect frame) fConverting(false), fCancelling(false) { - const char* defaultDirectory = "/boot/home"; - fOutputDir.SetTo(defaultDirectory); + BPath outputDir; + if (find_directory(B_USER_DIRECTORY, &outputDir) != B_OK) + outputDir.SetTo("/boot/home"); + fOutputDir.SetTo(outputDir.Path()); fMenuBar = new BMenuBar("menubar"); _CreateMenu(); @@ -165,7 +169,7 @@ MediaConverterWindow::MediaConverterWindow(BRect frame) fDestButton = new BButton(B_TRANSLATE("Output folder"), new BMessage(OUTPUT_FOLDER_MESSAGE)); BAlignment labelAlignment(be_control_look->DefaultLabelAlignment()); - fOutputFolder = new BStringView(NULL, defaultDirectory); + fOutputFolder = new BStringView(NULL, outputDir.Path()); fOutputFolder->SetExplicitAlignment(labelAlignment); // start/end duration diff --git a/src/apps/mediaplayer/InfoWin.cpp b/src/apps/mediaplayer/InfoWin.cpp index a98f3ceac4..2316481d6f 100644 --- a/src/apps/mediaplayer/InfoWin.cpp +++ b/src/apps/mediaplayer/InfoWin.cpp @@ -32,6 +32,7 @@ #include #include #include +#include #include #include @@ -405,6 +406,12 @@ printf("InfoWin::Update(0x%08lx)\n", which); rateString.ReplaceFirst("%d", "??"); s << rateString; } + if (format.type == B_MEDIA_ENCODED_AUDIO) { + float br = format.u.encoded_audio.bit_rate; + char string[20] = ""; + if (br > 0.0) + s << ", " << string_for_rate(br, string, sizeof(string)); + } s << "\n\n"; fContentsView->Insert(s.String()); } @@ -430,6 +437,8 @@ printf("InfoWin::Update(0x%08lx)\n", which); d = d % (60 * 1000); s << v << ":"; v = d / 1000; + if (v < 10) + s << '0'; s << v; if (hours) s << " " << B_TRANSLATE_COMMENT("h", "Hours"); diff --git a/src/apps/networkstatus/NetworkStatusView.cpp b/src/apps/networkstatus/NetworkStatusView.cpp index 69d149567c..f3490d9bb5 100644 --- a/src/apps/networkstatus/NetworkStatusView.cpp +++ b/src/apps/networkstatus/NetworkStatusView.cpp @@ -243,9 +243,12 @@ void NetworkStatusView::AttachedToWindow() { BView::AttachedToWindow(); - if (Parent()) - SetViewColor(Parent()->ViewColor()); - else + if (Parent() != NULL) { + if ((Parent()->Flags() & B_DRAW_ON_CHILDREN) != 0) + SetViewColor(B_TRANSPARENT_COLOR); + else + SetViewColor(Parent()->ViewColor()); + } else SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR)); SetLowColor(ViewColor()); diff --git a/src/apps/showimage/Filter.cpp b/src/apps/showimage/Filter.cpp index 17678b53db..6c5769abdd 100644 --- a/src/apps/showimage/Filter.cpp +++ b/src/apps/showimage/Filter.cpp @@ -1,6 +1,6 @@ /* - * Copyright 2003-2006, Haiku. - * Copyright 2004-2005 yellowTAB GmbH. All Rights Reserverd. + * Copyright 2003-2006, Haiku, Inc. All rights reserved. + * Copyright 2004-2005 yellowTAB GmbH. All Rights Reserved. * Copyright 2006 Bernd Korz. All Rights Reserved * Distributed under the terms of the MIT License. * @@ -21,29 +21,33 @@ // Implementation of FilterThread -FilterThread::FilterThread(Filter* filter, int32 i, int32 n, bool runInCurrentThread) - : fFilter(filter) - , fI(i) - , fN(n) +FilterThread::FilterThread(Filter* filter, int32 i, int32 n, + bool runInCurrentThread) + : + fFilter(filter), + fI(i), + fN(n) { - if (runInCurrentThread) { + if (runInCurrentThread) Run(); - } else { + else { thread_id tid; - tid = spawn_thread(worker_thread, "filter", suggest_thread_priority(B_STATUS_RENDERING), this); - if (tid >= 0) { + tid = spawn_thread(worker_thread, "filter", + suggest_thread_priority(B_STATUS_RENDERING), this); + if (tid >= 0) resume_thread(tid); - } else { + else delete this; - } } } + FilterThread::~FilterThread() { fFilter->FilterThreadDone(); } + status_t FilterThread::worker_thread(void* data) { @@ -51,6 +55,7 @@ FilterThread::worker_thread(void* data) return thread->Run(); } + status_t FilterThread::Run() { @@ -67,24 +72,25 @@ FilterThread::Run() new FilterThread(fFilter, i, fN); } } - if (fFilter->GetBitmap()) { + if (fFilter->GetBitmap()) fFilter->Run(fI, fN); - } + delete this; return B_OK; } // Implementation of Filter Filter::Filter(BBitmap* image, BMessenger listener, uint32 what) - : fListener(listener) - , fWhat(what) - , fStarted(false) - , fN(0) - , fNumberOfThreads(0) - , fIsRunning(false) - , fSrcImage(image) - , fDestImageInitialized(false) - , fDestImage(NULL) + : + fListener(listener), + fWhat(what), + fStarted(false), + fN(0), + fNumberOfThreads(0), + fIsRunning(false), + fSrcImage(image), + fDestImageInitialized(false), + fDestImage(NULL) { fCPUCount = NumberOfActiveCPUs(); @@ -95,12 +101,14 @@ Filter::Filter(BBitmap* image, BMessenger listener, uint32 what) #endif } + Filter::~Filter() { delete fDestImage; delete_sem(fWaitForThreads); } + BBitmap* Filter::GetBitmap() { @@ -111,6 +119,7 @@ Filter::GetBitmap() return fDestImage; } + BBitmap* Filter::DetachBitmap() { @@ -119,6 +128,7 @@ Filter::DetachBitmap() return image; } + void Filter::Start(bool async) { @@ -136,11 +146,11 @@ Filter::Start(bool async) // start first filter thread new FilterThread(this, 0, fN, !async); - if (!async) { + if (!async) Wait(); - } } + void Filter::Wait() { @@ -152,6 +162,7 @@ Filter::Wait() } } + void Filter::Stop() { @@ -160,17 +171,20 @@ Filter::Stop() Wait(); } + bool Filter::IsRunning() const { return fIsRunning; } + void Filter::Completed() { } + void Filter::FilterThreadDone() { @@ -179,14 +193,15 @@ Filter::FilterThreadDone() delete fStopWatch; fStopWatch = NULL; #endif Completed(); - if (fIsRunning) { + if (fIsRunning) fListener.SendMessage(fWhat); - } + fIsRunning = false; } release_sem(fWaitForThreads); } + void Filter::FilterThreadInitFailed() { @@ -197,38 +212,43 @@ Filter::FilterThreadInitFailed() release_sem_etc(fWaitForThreads, fN, 0); } + bool Filter::IsBitmapValid(BBitmap* bitmap) const { return bitmap != NULL && bitmap->InitCheck() == B_OK && bitmap->IsValid(); } + int32 Filter::NumberOfThreads() { const int32 units = GetNumberOfUnits(); int32 n; n = units / 32; // at least 32 units per CPU - if (n > CPUCount()) { + if (n > CPUCount()) n = CPUCount(); - } else if (n <= 0) { + else if (n <= 0) n = 1; // at least one thread! - } + return n; } + BBitmap* Filter::GetSrcImage() { return fSrcImage; } + BBitmap* Filter::GetDestImage() { return fDestImage; } + int32 Filter::NumberOfActiveCPUs() const { @@ -247,15 +267,19 @@ Filter::NumberOfActiveCPUs() const return cpuCount; } + // Implementation of (bilinear) Scaler -Scaler::Scaler(BBitmap* image, BRect rect, BMessenger listener, uint32 what, bool dither) - : Filter(image, listener, what) - , fScaledImage(NULL) - , fRect(rect) - , fDither(dither) +Scaler::Scaler(BBitmap* image, BRect rect, BMessenger listener, uint32 what, + bool dither) + : + Filter(image, listener, what), + fScaledImage(NULL), + fRect(rect), + fDither(dither) { } + Scaler::~Scaler() { if (GetDestImage() != fScaledImage) { @@ -264,13 +288,17 @@ Scaler::~Scaler() } } + BBitmap* Scaler::CreateDestImage(BBitmap* srcImage) { - if (srcImage == NULL || (srcImage->ColorSpace() != B_RGB32 && srcImage->ColorSpace() != B_RGBA32)) return NULL; + if (srcImage == NULL || (srcImage->ColorSpace() != B_RGB32 + && srcImage->ColorSpace() != B_RGBA32)) + return NULL; BRect dest(0, 0, fRect.IntegerWidth(), fRect.IntegerHeight()); - BBitmap* destImage = new BBitmap(dest, fDither ? B_CMAP8 : srcImage->ColorSpace()); + BBitmap* destImage = new BBitmap(dest, + fDither ? B_CMAP8 : srcImage->ColorSpace()); if (!IsBitmapValid(destImage)) { delete destImage; @@ -287,19 +315,19 @@ Scaler::CreateDestImage(BBitmap* srcImage) fScaledImage = NULL; return NULL; } - } else { + } else fScaledImage = destImage; - } return destImage; } + bool Scaler::Matches(BRect rect, bool dither) const { - return fRect.IntegerWidth() == rect.IntegerWidth() && - fRect.IntegerHeight() == rect.IntegerHeight() && - fDither == dither; + return fRect.IntegerWidth() == rect.IntegerWidth() + && fRect.IntegerHeight() == rect.IntegerHeight() + && fDither == dither; } @@ -310,6 +338,7 @@ typedef struct { float alpha1; } ColumnData; + void Scaler::ScaleBilinear(intType fromRow, int32 toRow) { @@ -343,7 +372,7 @@ Scaler::ScaleBilinear(intType fromRow, int32 toRow) columnData = new ColumnData[destW]; cd = columnData; - for (i = 0; i < destW; i ++, cd++) { + for (i = 0; i < destW; i++, cd++) { float column = (float)i * (float)srcW / (float)destW; cd->srcColumn = (intType)column; cd->alpha1 = column - cd->srcColumn; @@ -352,16 +381,16 @@ Scaler::ScaleBilinear(intType fromRow, int32 toRow) destDataRow = destBits + fromRow * destBPR; - for (y = fromRow; IsRunning() && y <= toRow; y ++, destDataRow += destBPR) { + for (y = fromRow; IsRunning() && y <= toRow; y++, destDataRow += destBPR) { float row; intType srcRow; float alpha0, alpha1; - if (destH == 0) { + if (destH == 0) row = 0; - } else { + else row = (float)y * (float)srcH / (float)destH; - } + srcRow = (intType)row; alpha1 = row - srcRow; alpha0 = 1.0 - alpha1; @@ -434,6 +463,7 @@ Scaler::ScaleBilinear(intType fromRow, int32 toRow) delete[] columnData; } + // Scale bilinear using fixed point calculations // Is already more than two times faster than floating point version // on AMD Athlon 1 GHz and Dual Intel Pentium III 866 MHz. @@ -444,6 +474,7 @@ typedef struct { fixed_point alpha1; } ColumnDataFP; + void Scaler::ScaleBilinearFP(intType fromRow, int32 toRow) { @@ -482,8 +513,9 @@ Scaler::ScaleBilinearFP(intType fromRow, int32 toRow) columnData = new ColumnDataFP[destW]; cd = columnData; - for (i = 0; i < destW; i ++, cd++) { - fixed_point column = to_fixed_point(i) * (long_fixed_point)fpSrcW / fpDestW; + for (i = 0; i < destW; i++, cd++) { + fixed_point column = to_fixed_point(i) * (long_fixed_point)fpSrcW + / fpDestW; cd->srcColumn = from_fixed_point(column); cd->alpha1 = tail_value(column); // weigth for left pixel value cd->alpha0 = kFPOne - cd->alpha1; // weigth for right pixel value @@ -491,18 +523,18 @@ Scaler::ScaleBilinearFP(intType fromRow, int32 toRow) destDataRow = destBits + fromRow * destBPR; - for (y = fromRow; IsRunning() && y <= toRow; y ++, destDataRow += destBPR) { + for (y = fromRow; IsRunning() && y <= toRow; y++, destDataRow += destBPR) { fixed_point row; intType srcRow; fixed_point alpha0, alpha1; - if (fpDestH == 0) { + if (fpDestH == 0) row = 0; - } else { + else row = to_fixed_point(y) * (long_fixed_point)fpSrcH / fpDestH; - } + srcRow = from_fixed_point(row); - alpha1 = tail_value(row); // weight for row y+1 + alpha1 = tail_value(row); // weight for row y + 1 alpha0 = kFPOne - alpha1; // weight for row y srcData = srcBits + srcRow * srcBPR; @@ -566,14 +598,15 @@ Scaler::ScaleBilinearFP(intType fromRow, int32 toRow) destData[2] = a[2]; destData[3] = a[3]; } - } delete[] columnData; } + void -Scaler::RowValues(float* sum, const uchar* src, intType srcW, intType fromX, intType toX, const float a0X, const float a1X, const int32 kBPP) +Scaler::RowValues(float* sum, const uchar* src, intType srcW, intType fromX, + intType toX, const float a0X, const float a1X, const int32 kBPP) { sum[0] = a0X * src[0]; sum[1] = a0X * src[1]; @@ -581,7 +614,7 @@ Scaler::RowValues(float* sum, const uchar* src, intType srcW, intType fromX, int src += kBPP; - for (int32 x = fromX+1; x < toX; x ++, src += kBPP) { + for (int32 x = fromX + 1; x < toX; x++, src += kBPP) { sum[0] += src[0]; sum[1] += src[1]; sum[2] += src[2]; @@ -594,6 +627,7 @@ Scaler::RowValues(float* sum, const uchar* src, intType srcW, intType fromX, int } } + typedef struct { int32 from; int32 to; @@ -601,6 +635,7 @@ typedef struct { float alpha1; } DownScaleColumnData; + void Scaler::DownScaleBilinear(intType fromRow, int32 toRow) { @@ -637,9 +672,9 @@ Scaler::DownScaleBilinear(intType fromRow, int32 toRow) const float deltaY = (srcH + 1.0) / (destH + 1.0); const float deltaXY = deltaX * deltaY; - columnData = new DownScaleColumnData[destW+1]; + columnData = new DownScaleColumnData[destW + 1]; DownScaleColumnData* cd = columnData; - for (x = 0; x <= destW; x ++, cd ++) { + for (x = 0; x <= destW; x++, cd++) { const float fFromX = x * deltaX; const float fToX = fFromX + deltaX; @@ -664,7 +699,7 @@ Scaler::DownScaleBilinear(intType fromRow, int32 toRow) destData = destDataRow; cd = columnData; - for (x = 0; x <= destW; x ++, destData += kBPP, cd ++) { + for (x = 0; x <= destW; x++, destData += kBPP, cd++) { const intType fromX = cd->from; const intType toX = cd->to; @@ -683,7 +718,7 @@ Scaler::DownScaleBilinear(intType fromRow, int32 toRow) srcData += srcBPR; - for (int32 r = fromY+1; r < toY; r ++, srcData += srcBPR) { + for (int32 r = fromY + 1; r < toY; r++, srcData += srcBPR) { RowValues(sum, srcData, srcW, fromX, toX, a0X, a1X, kBPP); totalSum[0] += sum[0]; totalSum[1] += sum[1]; @@ -706,6 +741,7 @@ Scaler::DownScaleBilinear(intType fromRow, int32 toRow) delete[] columnData; } + // Flyod-Steinberg Dithering // Filter (distribution of error to adjacent pixels, X is current pixel): // 0 X 7 @@ -715,6 +751,7 @@ typedef struct { intType error[3]; } DitheringColumnData; + uchar Scaler::Limit(intType value) { @@ -726,6 +763,7 @@ Scaler::Limit(intType value) return value; } + void Scaler::Dither(int32 fromRow, int32 toRow) { @@ -766,19 +804,20 @@ Scaler::Dither(int32 fromRow, int32 toRow) destBPR = dest->BytesPerRow(); // Allocate space for sentinel at left and right bounds, - // so that columnData[-1] and columnData[destW+1] can be safely accessed - columnData0 = new DitheringColumnData[destW+3]; + // so that columnData[-1] and columnData[destW + 1] can be safely accessed + columnData0 = new DitheringColumnData[destW + 3]; columnData = columnData0 + 1; // clear error cd = columnData; - for (x = destW; x >= 0; x --, cd ++) { - cd->error[0] = cd->error[1] = cd->error[2] =0; + for (x = destW; x >= 0; x --, cd++) { + cd->error[0] = cd->error[1] = cd->error[2] = 0; } srcDataRow = srcBits + fromRow * srcBPR; destDataRow = destBits + fromRow * destBPR; - for (y = fromRow; IsRunning() && y <= toRow; y ++, srcDataRow += srcBPR, destDataRow += destBPR) { + for (y = fromRow; IsRunning() && y <= toRow; y++, srcDataRow += srcBPR, + destDataRow += destBPR) { // left to right error[0] = error[1] = error[2] = 0; srcData = srcDataRow; @@ -797,12 +836,12 @@ Scaler::Dither(int32 fromRow, int32 toRow) *destData = index; err[0] = color.red - actualColor.red; - err[1] = color.green -actualColor.green; - err[2] = color.blue -actualColor.blue; + err[1] = color.green - actualColor.green; + err[2] = color.blue - actualColor.blue; // distribute error // get error for next pixel - cd = &columnData[x+1]; + cd = &columnData[x + 1]; error[0] = cd->error[0] + 7 * err[0]; error[1] = cd->error[1] + 7 * err[1]; error[2] = cd->error[2] + 7 * err[2]; @@ -813,27 +852,27 @@ Scaler::Dither(int32 fromRow, int32 toRow) cd->error[2] = err[2]; // add error for pixel below current pixel - cd --; + cd--; cd->error[0] += 5 * err[0]; cd->error[1] += 5 * err[1]; cd->error[2] += 5 * err[2]; // add error for left pixel below current pixel - cd --; + cd--; cd->error[0] += 3 * err[0]; cd->error[1] += 3 * err[1]; cd->error[2] += 3 * err[2]; } // Note: Alogrithm has good results with "left to right" already // Optionally remove code to end of block: - y ++; + y++; srcDataRow += srcBPR; destDataRow += destBPR; if (y > toRow) break; // right to left error[0] = error[1] = error[2] = 0; srcData = srcDataRow + destW * kBPP; destData = destDataRow + destW; - for (x = 0; x <= destW; x ++, srcData -= kBPP, destData -= 1) { + for (x = 0; x <= destW; x++, srcData -= kBPP, destData -= 1) { rgb_color color, actualColor; uint8 index; @@ -847,12 +886,12 @@ Scaler::Dither(int32 fromRow, int32 toRow) *destData = index; err[0] = color.red - actualColor.red; - err[1] = color.green -actualColor.green; - err[2] = color.blue -actualColor.blue; + err[1] = color.green - actualColor.green; + err[2] = color.blue - actualColor.blue; // distribute error // get error for next pixel - cd = &columnData[x-1]; + cd = &columnData[x - 1]; error[0] = cd->error[0] + 7 * err[0]; error[1] = cd->error[1] + 7 * err[1]; error[2] = cd->error[2] + 7 * err[2]; @@ -863,13 +902,13 @@ Scaler::Dither(int32 fromRow, int32 toRow) cd->error[2] = err[2]; // add error for pixel below current pixel - cd ++; + cd++; cd->error[0] += 5 * err[0]; cd->error[1] += 5 * err[1]; cd->error[2] += 5 * err[2]; // add error for right pixel below current pixel - cd ++; + cd++; cd->error[0] += 3 * err[0]; cd->error[1] += 3 * err[1]; cd->error[2] += 3 * err[2]; @@ -879,12 +918,14 @@ Scaler::Dither(int32 fromRow, int32 toRow) delete[] columnData0; } + int32 Scaler::GetNumberOfUnits() { return fRect.IntegerHeight() + 1; } + void Scaler::Run(int32 i, int32 n) { @@ -892,43 +933,47 @@ Scaler::Run(int32 i, int32 n) imageHeight = GetDestImage()->Bounds().IntegerHeight() + 1; height = imageHeight / n; from = i * height; - if (i+1 == n) { + if (i + 1 == n) to = imageHeight - 1; - } else { + else to = from + height - 1; - } - if (GetDestImage()->Bounds().Width() >= GetSrcImage()->Bounds().Width()) { + + if (GetDestImage()->Bounds().Width() >= GetSrcImage()->Bounds().Width()) ScaleBilinearFP(from, to); - } else { + else DownScaleBilinear(from, to); - } - if (fDither) { + + if (fDither) Dither(from, to); - } + } + void Scaler::Completed() { - if (GetDestImage() != fScaledImage) { + if (GetDestImage() != fScaledImage) delete fScaledImage; - } + fScaledImage = NULL; } -// Implementation of ImageProcessor -ImageProcessor::ImageProcessor(enum operation op, BBitmap* image, BMessenger listener, uint32 what) - : Filter(image, listener, what) - , fOp(op) - , fBPP(0) - , fWidth(0) - , fHeight(0) - , fSrcBPR(0) - , fDestBPR(0) +// Implementation of ImageProcessor +ImageProcessor::ImageProcessor(enum operation op, BBitmap* image, + BMessenger listener, uint32 what) + : + Filter(image, listener, what), + fOp(op), + fBPP(0), + fWidth(0), + fHeight(0), + fSrcBPR(0), + fDestBPR(0) { } + BBitmap* ImageProcessor::CreateDestImage(BBitmap* /* srcImage */) { @@ -936,20 +981,21 @@ ImageProcessor::CreateDestImage(BBitmap* /* srcImage */) BBitmap* bm; BRect rect; - if (GetSrcImage() == NULL) return NULL; + if (GetSrcImage() == NULL) + return NULL; cs = GetSrcImage()->ColorSpace(); fBPP = BytesPerPixel(cs); - if (fBPP < 1) return NULL; + if (fBPP < 1) + return NULL; fWidth = GetSrcImage()->Bounds().IntegerWidth(); fHeight = GetSrcImage()->Bounds().IntegerHeight(); - if (fOp == kRotateClockwise || fOp == kRotateCounterClockwise) { + if (fOp == kRotateClockwise || fOp == kRotateCounterClockwise) rect.Set(0, 0, fHeight, fWidth); - } else { + else rect.Set(0, 0, fWidth, fHeight); - } bm = new BBitmap(rect, cs); if (!IsBitmapValid(bm)) { @@ -963,45 +1009,50 @@ ImageProcessor::CreateDestImage(BBitmap* /* srcImage */) return bm; } + int32 ImageProcessor::GetNumberOfUnits() { return GetSrcImage()->Bounds().IntegerHeight() + 1; } + int32 ImageProcessor::BytesPerPixel(color_space cs) const { switch (cs) { - case B_RGB32: // fall through - case B_RGB32_BIG: // fall through - case B_RGBA32: // fall through - case B_RGBA32_BIG: return 4; + case B_RGB32: // fall through + case B_RGB32_BIG: // fall through + case B_RGBA32: // fall through + case B_RGBA32_BIG: return 4; - case B_RGB24_BIG: // fall through - case B_RGB24: return 3; + case B_RGB24_BIG: // fall through + case B_RGB24: return 3; - case B_RGB16: // fall through - case B_RGB16_BIG: // fall through - case B_RGB15: // fall through - case B_RGB15_BIG: // fall through - case B_RGBA15: // fall through - case B_RGBA15_BIG: return 2; + case B_RGB16: // fall through + case B_RGB16_BIG: // fall through + case B_RGB15: // fall through + case B_RGB15_BIG: // fall through + case B_RGBA15: // fall through + case B_RGBA15_BIG: return 2; - case B_GRAY8: // fall through - case B_CMAP8: return 1; - case B_GRAY1: return 0; + case B_GRAY8: // fall through + case B_CMAP8: return 1; + case B_GRAY1: return 0; default: return -1; } } + void -ImageProcessor::CopyPixel(uchar* dest, int32 destX, int32 destY, const uchar* src, int32 x, int32 y) +ImageProcessor::CopyPixel(uchar* dest, int32 destX, int32 destY, + const uchar* src, int32 x, int32 y) { - // Note: On my systems (Dual Intel P3 866MHz and AMD Athlon 1GHz), replacing - // the multiplications below with pointer arithmethics showed no speedup at all! + // Note: On my systems (Dual Intel P3 866MHz and AMD Athlon 1GHz), + // replacing the multiplications below with pointer arithmethics showed + // no speedup at all! dest += fDestBPR * destY + destX * fBPP; - src += fSrcBPR * y + x *fBPP; + src += fSrcBPR * y + x * fBPP; // Replacing memcpy with this switch statement is slightly faster switch (fBPP) { case 4: @@ -1016,6 +1067,7 @@ ImageProcessor::CopyPixel(uchar* dest, int32 destX, int32 destY, const uchar* sr } } + // Note: For B_CMAP8 InvertPixel inverts the color index not the color value! void ImageProcessor::InvertPixel(int32 x, int32 y, uchar* dest, const uchar* src) @@ -1035,18 +1087,19 @@ ImageProcessor::InvertPixel(int32 x, int32 y, uchar* dest, const uchar* src) } } -// Note: On my systems, the operation kInvert shows a speedup on multiple CPUs only! + +// Note: On my systems, the operation kInvert shows a speedup on +// multiple CPUs only! void ImageProcessor::Run(int32 i, int32 n) { int32 from, to; - int32 height = (fHeight+1) / n; + int32 height = (fHeight + 1) / n; from = i * height; - if (i+1 == n) { + if (i + 1 == n) to = fHeight; - } else { + else to = from + height - 1; - } int32 x, y, destX, destY; const uchar* src = (uchar*)GetSrcImage()->Bits(); @@ -1054,8 +1107,8 @@ ImageProcessor::Run(int32 i, int32 n) switch (fOp) { case kRotateClockwise: - for (y = from; y <= to; y ++) { - for (x = 0; x <= fWidth; x ++) { + for (y = from; y <= to; y++) { + for (x = 0; x <= fWidth; x++) { destX = fHeight - y; destY = x; CopyPixel(dest, destX, destY, src, x, y); @@ -1097,5 +1150,4 @@ ImageProcessor::Run(int32 i, int32 n) } break; } - } diff --git a/src/apps/showimage/Filter.h b/src/apps/showimage/Filter.h index 4419361df5..8021ca133f 100644 --- a/src/apps/showimage/Filter.h +++ b/src/apps/showimage/Filter.h @@ -63,16 +63,17 @@ const int32 kFPOne = to_fixed_point(1); // Used by class Filter class FilterThread { public: - FilterThread(Filter* filter, int32 i, int32 n, bool runInCurrentThread = false); + FilterThread(Filter* filter, int32 i, int32 n, + bool runInCurrentThread = false); ~FilterThread(); private: status_t Run(); static status_t worker_thread(void* data); - Filter* fFilter; - int32 fI; - int32 fN; + Filter* fFilter; + int32 fI; + int32 fN; }; class Filter { @@ -142,26 +143,27 @@ private: // Returns the number of active CPUs int32 CPUCount() const { return fCPUCount; } - BMessenger fListener; - uint32 fWhat; - int32 fCPUCount; // the number of active CPUs - bool fStarted; // has Start() been called? - sem_id fWaitForThreads; // to exit - int32 fN; // the number of used filter threads - volatile int32 fNumberOfThreads; // the current number of FilterThreads - volatile bool fIsRunning; // FilterThreads should process data as long as it is true - BBitmap* fSrcImage; - bool fDestImageInitialized; - BBitmap* fDestImage; + BMessenger fListener; + uint32 fWhat; + int32 fCPUCount; // the number of active CPUs + bool fStarted; // has Start() been called? + sem_id fWaitForThreads; // to exit + int32 fN; // the number of used filter threads + volatile int32 fNumberOfThreads; // the current number of FilterThreads + volatile bool fIsRunning; // FilterThreads should process data as long as it is true + BBitmap* fSrcImage; + bool fDestImageInitialized; + BBitmap* fDestImage; #if TIME_FILTER - BStopWatch* fStopWatch; + BStopWatch* fStopWatch; #endif }; // Scales and optionally dithers an image class Scaler : public Filter { public: - Scaler(BBitmap* image, BRect rect, BMessenger listener, uint32 what, bool dither); + Scaler(BBitmap* image, BRect rect, BMessenger listener, uint32 what, + bool dither); ~Scaler(); BBitmap* CreateDestImage(BBitmap* srcImage); @@ -173,7 +175,9 @@ public: private: void ScaleBilinear(int32 fromRow, int32 toRow); void ScaleBilinearFP(int32 fromRow, int32 toRow); - inline void RowValues(float* sum, const uchar* srcData, intType srcW, intType fromX, intType toX, const float a0X, const float a1X, const int32 kBPP); + inline void RowValues(float* sum, const uchar* srcData, intType srcW, + intType fromX, intType toX, const float a0X, + const float a1X, const int32 kBPP); void DownScaleBilinear(int32 fromRow, int32 toRow); static inline uchar Limit(intType value); void Dither(int32 fromRow, int32 toRow); @@ -195,14 +199,16 @@ public: kNumberOfAffineTransformations = 4 }; - ImageProcessor(enum operation op, BBitmap* image, BMessenger listener, uint32 what); + ImageProcessor(enum operation op, BBitmap* image, BMessenger listener, + uint32 what); BBitmap* CreateDestImage(BBitmap* srcImage); int32 GetNumberOfUnits(); void Run(int32 i, int32 n); private: int32 BytesPerPixel(color_space cs) const; - inline void CopyPixel(uchar* dest, int32 destX, int32 destY, const uchar* src, int32 x, int32 y); + inline void CopyPixel(uchar* dest, int32 destX, int32 destY, + const uchar* src, int32 x, int32 y); inline void InvertPixel(int32 x, int32 y, uchar* dest, const uchar* src); enum operation fOp; diff --git a/src/apps/showimage/ImageFileNavigator.cpp b/src/apps/showimage/ImageFileNavigator.cpp index b9ed68de4e..bf32b106c2 100644 --- a/src/apps/showimage/ImageFileNavigator.cpp +++ b/src/apps/showimage/ImageFileNavigator.cpp @@ -374,14 +374,16 @@ AutoAdjustingNavigator::~AutoAdjustingNavigator() bool -AutoAdjustingNavigator::FindNextImage(const entry_ref& currentRef, entry_ref& nextRef, - bool next, bool rewind) +AutoAdjustingNavigator::FindNextImage(const entry_ref& currentRef, + entry_ref& nextRef, bool next, bool rewind) { if (_CheckForTracker(currentRef)) - return fTrackerNavigator->FindNextImage(currentRef, nextRef, next, rewind); + return fTrackerNavigator->FindNextImage(currentRef, nextRef, next, + rewind); if (fFolderNavigator != NULL) - return fFolderNavigator->FindNextImage(currentRef, nextRef, next, rewind); + return fFolderNavigator->FindNextImage(currentRef, nextRef, next, + rewind); return false; } diff --git a/src/apps/showimage/PrintOptionsWindow.cpp b/src/apps/showimage/PrintOptionsWindow.cpp index fca276f913..782e43e70c 100644 --- a/src/apps/showimage/PrintOptionsWindow.cpp +++ b/src/apps/showimage/PrintOptionsWindow.cpp @@ -15,10 +15,10 @@ #include #include #include -#include #include -#include +#include #include +#include #include #include "ShowImageConstants.h" @@ -73,7 +73,7 @@ PrintOptions::SetWidth(float w) void PrintOptions::SetHeight(float h) { - fWidth = (fBounds.Width()+1) * h / (fBounds.Height()+1); + fWidth = (fBounds.Width() + 1) * h / (fBounds.Height() + 1); fHeight = h; } diff --git a/src/apps/showimage/ProgressWindow.cpp b/src/apps/showimage/ProgressWindow.cpp index 08221e5d91..70df9875f8 100644 --- a/src/apps/showimage/ProgressWindow.cpp +++ b/src/apps/showimage/ProgressWindow.cpp @@ -37,7 +37,7 @@ ProgressWindow::ProgressWindow() { BRect rect = Bounds(); - BView *view = new BView(rect, NULL, B_FOLLOW_ALL, B_WILL_DRAW); + BView* view = new BView(rect, NULL, B_FOLLOW_ALL, B_WILL_DRAW); view->SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR)); AddChild(view); @@ -106,7 +106,7 @@ ProgressWindow::Stop() void -ProgressWindow::MessageReceived(BMessage *message) +ProgressWindow::MessageReceived(BMessage* message) { switch (message->what) { case kMsgShow: @@ -123,7 +123,7 @@ ProgressWindow::MessageReceived(BMessage *message) if (message->FindFloat("percent", &percent) == B_OK) fStatusBar->Update(percent - fStatusBar->CurrentValue()); - const char *text; + const char* text; if (message->FindString("message", &text) == B_OK) fStatusBar->SetText(text); diff --git a/src/apps/showimage/SelectionBox.cpp b/src/apps/showimage/SelectionBox.cpp index 3fd92dbefb..08daf1701a 100644 --- a/src/apps/showimage/SelectionBox.cpp +++ b/src/apps/showimage/SelectionBox.cpp @@ -109,13 +109,13 @@ SelectionBox::Animate() { // rotate up uchar p = fPatternUp.data[0]; - for (int i = 0; i <= 6; i ++) + for (int i = 0; i <= 6; i++) fPatternUp.data[i] = fPatternUp.data[i + 1]; fPatternUp.data[7] = p; // rotate down p = fPatternDown.data[7]; - for (int i = 7; i >= 1; i --) + for (int i = 7; i >= 1; i--) fPatternDown.data[i] = fPatternDown.data[i - 1]; fPatternDown.data[0] = p; @@ -170,7 +170,7 @@ SelectionBox::_InitPatterns() uchar p; uchar p1 = 0x33; uchar p2 = 0xCC; - for (int i = 0; i <= 7; i ++) { + for (int i = 0; i <= 7; i++) { fPatternLeft.data[i] = p1; fPatternRight.data[i] = p2; if ((i / 2) % 2 == 0) diff --git a/src/apps/showimage/ShowImageApp.cpp b/src/apps/showimage/ShowImageApp.cpp index 00b45a1533..c3147da98e 100644 --- a/src/apps/showimage/ShowImageApp.cpp +++ b/src/apps/showimage/ShowImageApp.cpp @@ -207,7 +207,7 @@ void ShowImageApp::_BroadcastToWindows(BMessage* message) { const int32 count = CountWindows(); - for (int32 i = 0; i < count; i ++) { + for (int32 i = 0; i < count; i++) { // BMessenger checks for us if BWindow is still a valid object BMessenger messenger(WindowAt(i)); messenger.SendMessage(message); diff --git a/src/apps/showimage/ShowImageUndo.cpp b/src/apps/showimage/ShowImageUndo.cpp index 31a9073a68..d4c2ad7cf8 100644 --- a/src/apps/showimage/ShowImageUndo.cpp +++ b/src/apps/showimage/ShowImageUndo.cpp @@ -64,7 +64,7 @@ ShowImageUndo::SendUndoStateMessage(bool bCanUndo) void -ShowImageUndo::SetTo(BRect rect, BBitmap *restore, BBitmap *selection) +ShowImageUndo::SetTo(BRect rect, BBitmap* restore, BBitmap* selection) { // NOTE: THIS FUNCTION DOES NOT MAKE COPIES OF THE BITMAPS PASSED TO IT InternalClear(); @@ -79,7 +79,7 @@ ShowImageUndo::SetTo(BRect rect, BBitmap *restore, BBitmap *selection) void -ShowImageUndo::Undo(BRect rect, BBitmap *restore, BBitmap *selection) +ShowImageUndo::Undo(BRect rect, BBitmap* restore, BBitmap* selection) { // NOTE: THIS FUNCTION DOES NOT MAKE COPIES OF THE BITMAPS PASSED TO IT fUndoType = UNDO_REDO; diff --git a/src/apps/showimage/ShowImageView.cpp b/src/apps/showimage/ShowImageView.cpp index 4551cf1f42..86b3ee4c8a 100644 --- a/src/apps/showimage/ShowImageView.cpp +++ b/src/apps/showimage/ShowImageView.cpp @@ -90,8 +90,8 @@ ShowImageView::fTransformation[ImageProcessor::kNumberOfAffineTransformations] {k0V, k90V, k0H, k270V, k0, k90, k180, k270} }; -const rgb_color kAlphaLow = (rgb_color){ 0xbb, 0xbb, 0xbb, 0xff }; -const rgb_color kAlphaHigh = (rgb_color){ 0xe0, 0xe0, 0xe0, 0xff }; +const rgb_color kAlphaLow = (rgb_color) { 0xbb, 0xbb, 0xbb, 0xff }; +const rgb_color kAlphaHigh = (rgb_color) { 0xe0, 0xe0, 0xe0, 0xff }; const uint32 kMsgPopUpMenuClosed = 'pmcl'; @@ -129,17 +129,16 @@ compose_checker_background(const BBitmap* bitmap) p[3] = 255; alpha = 255 - alpha; if (x % 10 >= 5) { - if (i % 10 >= 5) { + if (i % 10 >= 5) blend_colors(p, kAlphaLow.red, kAlphaLow.green, kAlphaLow.blue, alpha); - } else { + else blend_colors(p, kAlphaHigh.red, kAlphaHigh.green, kAlphaHigh.blue, alpha); - } + } else { - if (i % 10 >= 5) { + if (i % 10 >= 5) blend_colors(p, kAlphaHigh.red, kAlphaHigh.green, kAlphaHigh.blue, alpha); - } else { + else blend_colors(p, kAlphaLow.red, kAlphaLow.green, kAlphaLow.blue, alpha); - } } } p += 4; @@ -171,7 +170,7 @@ PopUpMenu::~PopUpMenu() // #pragma mark - -ShowImageView::ShowImageView(BRect rect, const char *name, uint32 resizingMode, +ShowImageView::ShowImageView(BRect rect, const char* name, uint32 resizingMode, uint32 flags) : BView(rect, name, resizingMode, flags), @@ -249,9 +248,8 @@ ShowImageView::Pulse() BPoint mousePos; uint32 buttons; GetMouse(&mousePos, &buttons, false); - if (Bounds().Contains(mousePos)) { + if (Bounds().Contains(mousePos)) be_app->ObscureCursor(); - } } else fHideCursorCountDown--; } @@ -263,7 +261,7 @@ ShowImageView::Pulse() void -ShowImageView::_SendMessageToWindow(BMessage *message) +ShowImageView::_SendMessageToWindow(BMessage* message) { BMessenger target(Window()); target.SendMessage(message); @@ -605,18 +603,18 @@ ShowImageView::_DrawBackground(BRect border) { BRect bounds(Bounds()); // top - FillRect(BRect(0, 0, bounds.right, border.top-1), B_SOLID_LOW); + FillRect(BRect(0, 0, bounds.right, border.top - 1), B_SOLID_LOW); // left - FillRect(BRect(0, border.top, border.left-1, border.bottom), B_SOLID_LOW); + FillRect(BRect(0, border.top, border.left - 1, border.bottom), B_SOLID_LOW); // right - FillRect(BRect(border.right+1, border.top, bounds.right, border.bottom), B_SOLID_LOW); + FillRect(BRect(border.right + 1, border.top, bounds.right, border.bottom), B_SOLID_LOW); // bottom - FillRect(BRect(0, border.bottom+1, bounds.right, bounds.bottom), B_SOLID_LOW); + FillRect(BRect(0, border.bottom + 1, bounds.right, bounds.bottom), B_SOLID_LOW); } void -ShowImageView::_LayoutCaption(BFont &font, BPoint &pos, BRect &rect) +ShowImageView::_LayoutCaption(BFont& font, BPoint& pos, BRect& rect) { font_height fontHeight; float width, height; @@ -790,7 +788,7 @@ ShowImageView::_CopySelection(uchar alpha, bool imageSize) bool ShowImageView::_AddSupportedTypes(BMessage* msg, BBitmap* bitmap) { - BTranslatorRoster *roster = BTranslatorRoster::Default(); + BTranslatorRoster* roster = BTranslatorRoster::Default(); if (roster == NULL) return false; @@ -812,9 +810,9 @@ ShowImageView::_AddSupportedTypes(BMessage* msg, BBitmap* bitmap) int32 count; roster->GetOutputFormats(info[i].translator, &formats, &count); for (int32 j = 0; j < count; j++) { - if (fMimeType == formats[j].MIME) { + if (fMimeType == formats[j].MIME) foundCurrent = true; - } else if (strcmp(formats[j].MIME, "image/x-be-bitmap") != 0) { + else if (strcmp(formats[j].MIME, "image/x-be-bitmap") != 0) { foundOther = true; // needed to send data in message msg->AddString("be:types", formats[j].MIME); @@ -932,7 +930,7 @@ ShowImageView::SaveToFile(BDirectory* dir, const char* name, BBitmap* bitmap, bool loop = true; while (loop) { - BTranslatorRoster *roster = BTranslatorRoster::Default(); + BTranslatorRoster* roster = BTranslatorRoster::Default(); if (!roster) break; // write data @@ -954,7 +952,7 @@ ShowImageView::SaveToFile(BDirectory* dir, const char* name, BBitmap* bitmap, char buffer[512]; snprintf(buffer, sizeof(buffer), B_TRANSLATE("The file '%s' could not " "be written."), name); - BAlert *palert = new BAlert("", buffer, B_TRANSLATE("OK")); + BAlert* palert = new BAlert("", buffer, B_TRANSLATE("OK")); palert->Go(); } @@ -965,14 +963,16 @@ ShowImageView::SaveToFile(BDirectory* dir, const char* name, BBitmap* bitmap, void -ShowImageView::_SendInMessage(BMessage* msg, BBitmap* bitmap, translation_format* format) +ShowImageView::_SendInMessage(BMessage* msg, BBitmap* bitmap, + translation_format* format) { BMessage reply(B_MIME_DATA); BBitmapStream stream(bitmap); // destructor deletes bitmap - BTranslatorRoster *roster = BTranslatorRoster::Default(); + BTranslatorRoster* roster = BTranslatorRoster::Default(); BMallocIO memStream; if (roster->Translate(&stream, NULL, NULL, &memStream, format->type) == B_OK) { - reply.AddData(format->MIME, B_MIME_TYPE, memStream.Buffer(), memStream.BufferLength()); + reply.AddData(format->MIME, B_MIME_TYPE, memStream.Buffer(), + memStream.BufferLength()); msg->SendReply(&reply); } } @@ -1172,7 +1172,7 @@ ShowImageView::MouseMoved(BPoint point, uint32 state, const BMessage* message) fHideCursorCountDown = HIDE_CURSOR_DELAY_TIME; if (fHideCursor) { // Show toolbar when mouse hits top 15 pixels, hide otherwise - _ShowToolBarIfEnabled(point.y <= 15); + _ShowToolBarIfEnabled(ConvertToScreen(point).y <= 15); } if (fCreatingSelection) _UpdateSelectionRect(point, false); @@ -1335,7 +1335,7 @@ ShowImageView::KeyDown(const char* bytes, int32 numBytes) void -ShowImageView::_MouseWheelChanged(BMessage *msg) +ShowImageView::_MouseWheelChanged(BMessage* msg) { // The BeOS driver does not currently support // X wheel scrolling, therefore, dx is zero. @@ -1379,14 +1379,14 @@ void ShowImageView::_ShowPopUpMenu(BPoint screen) { if (!fShowingPopUpMenu) { - PopUpMenu* menu = new PopUpMenu("PopUpMenu", this); + PopUpMenu* menu = new PopUpMenu("PopUpMenu", this); - ShowImageWindow* window = dynamic_cast(Window()); - if (window != NULL) - window->BuildContextMenu(menu); + ShowImageWindow* window = dynamic_cast(Window()); + if (window != NULL) + window->BuildContextMenu(menu); - menu->Go(screen, true, true, true); - fShowingPopUpMenu = true; + menu->Go(screen, true, true, true); + fShowingPopUpMenu = true; } } @@ -1430,18 +1430,18 @@ ShowImageView::FixupScrollBar(orientation o, float bitmapLength, float viewLength) { float prop, range; - BScrollBar *psb; + BScrollBar* psb; psb = ScrollBar(o); if (psb) { range = bitmapLength - viewLength; - if (range < 0.0) { + if (range < 0.0) range = 0.0; - } + prop = viewLength / bitmapLength; - if (prop > 1.0) { + if (prop > 1.0) prop = 1.0; - } + psb->SetRange(0, range); psb->SetProportion(prop); psb->SetSteps(10, 100); @@ -1481,22 +1481,23 @@ ShowImageView::Undo() // backup current selection BRect undoneSelRect; - BBitmap *undoneSelection; + BBitmap* undoneSelection; undoneSelRect = fSelectionBox.Bounds(); undoneSelection = _CopySelection(); if (undoType == UNDO_UNDO) { - BBitmap *undoRestore; + BBitmap* undoRestore; undoRestore = fUndo.GetRestoreBitmap(); if (undoRestore) _MergeWithBitmap(undoRestore, fUndo.GetRect()); } // restore previous image/selection - BBitmap *undoSelection; + BBitmap* undoSelection; undoSelection = fUndo.GetSelectionBitmap(); // NOTE: ShowImageView is responsible for deleting this bitmap - // (Which it will, as it would with a fSelectionBitmap that it allocated itself) + // (Which it will, as it would with a fSelectionBitmap that it + // allocated itself) if (!undoSelection) _SetHasSelection(false); else { @@ -1683,10 +1684,14 @@ ShowImageView::_DoImageOperation(ImageProcessor::operation op, bool quiet) // update orientation state if (op != ImageProcessor::kInvert) { // Note: If one of these fails, check its definition in class ImageProcessor. -// ASSERT(ImageProcessor::kRotateClockwise < ImageProcessor::kNumberOfAffineTransformations); -// ASSERT(ImageProcessor::kRotateCounterClockwise < ImageProcessor::kNumberOfAffineTransformations); -// ASSERT(ImageProcessor::kFlipLeftToRight < ImageProcessor::kNumberOfAffineTransformations); -// ASSERT(ImageProcessor::kFlipTopToBottom < ImageProcessor::kNumberOfAffineTransformations); +// ASSERT(ImageProcessor::kRotateClockwise < +// ImageProcessor::kNumberOfAffineTransformations); +// ASSERT(ImageProcessor::kRotateCounterClockwise < +// ImageProcessor::kNumberOfAffineTransformations); +// ASSERT(ImageProcessor::kFlipLeftToRight < +// ImageProcessor::kNumberOfAffineTransformations); +// ASSERT(ImageProcessor::kFlipTopToBottom < +// ImageProcessor::kNumberOfAffineTransformations); fImageOrientation = fTransformation[op][fImageOrientation]; } @@ -1697,9 +1702,8 @@ ShowImageView::_DoImageOperation(ImageProcessor::operation op, bool quiet) if (orientation != k0) { node.WriteAttr(SHOW_IMAGE_ORIENTATION_ATTRIBUTE, B_INT32_TYPE, 0, &orientation, sizeof(orientation)); - } else { + } else node.RemoveAttr(SHOW_IMAGE_ORIENTATION_ATTRIBUTE); - } } // set new bitmap @@ -1749,7 +1753,7 @@ ShowImageView::ResizeImage(int w, int h) if (fBitmap == NULL || w < 1 || h < 1) return; - Scaler scaler(fBitmap, BRect(0, 0, w-1, h-1), BMessenger(), 0, false); + Scaler scaler(fBitmap, BRect(0, 0, w - 1, h - 1), BMessenger(), 0, false); scaler.Start(false); BBitmap* scaled = scaler.DetachBitmap(); if (scaled == NULL) { @@ -1784,17 +1788,17 @@ ShowImageView::_SetIcon(bool clear, icon_size which) BRect rect(fBitmap->Bounds()); float s; - s = size / (rect.Width()+1.0); + s = size / (rect.Width() + 1.0); - if (s * (rect.Height()+1.0) <= size) { - rect.right = size-1; - rect.bottom = static_cast(s * (rect.Height()+1.0))-1; + if (s * (rect.Height() + 1.0) <= size) { + rect.right = size - 1; + rect.bottom = static_cast(s * (rect.Height() + 1.0)) - 1; // center vertically rect.OffsetBy(0, (size - rect.IntegerHeight()) / 2); } else { - s = size / (rect.Height()+1.0); - rect.right = static_cast(s * (rect.Width()+1.0))-1; - rect.bottom = size-1; + s = size / (rect.Height() + 1.0); + rect.right = static_cast(s * (rect.Width() + 1.0)) - 1; + rect.bottom = size - 1; // center horizontally rect.OffsetBy((size - rect.IntegerWidth()) / 2, 0); } @@ -1806,7 +1810,7 @@ ShowImageView::_SetIcon(bool clear, icon_size which) scaler.Start(false); ASSERT(thumbnail->ColorSpace() == B_CMAP8); // create icon from thumbnail - BBitmap icon(BRect(0, 0, size-1, size-1), B_CMAP8); + BBitmap icon(BRect(0, 0, size - 1, size - 1), B_CMAP8); memset(icon.Bits(), B_TRANSPARENT_MAGIC_CMAP8, icon.BitsLength()); BScreen screen; const uchar* src = (uchar*)thumbnail->Bits(); @@ -1816,10 +1820,10 @@ ShowImageView::_SetIcon(bool clear, icon_size which) const int32 dx = (int32)rect.left; const int32 dy = (int32)rect.top; - for (int32 y = 0; y <= rect.IntegerHeight(); y ++) { - for (int32 x = 0; x <= rect.IntegerWidth(); x ++) { + for (int32 y = 0; y <= rect.IntegerHeight(); y++) { + for (int32 x = 0; x <= rect.IntegerWidth(); x++) { const uchar* s = src + y * srcBPR + x; - uchar* d = dest + (y+dy) * destBPR + (x+dx); + uchar* d = dest + (y + dy) * destBPR + (x + dx); *d = *s; } } diff --git a/src/apps/showimage/ShowImageView.h b/src/apps/showimage/ShowImageView.h index b72be7d104..b14985b028 100644 --- a/src/apps/showimage/ShowImageView.h +++ b/src/apps/showimage/ShowImageView.h @@ -104,20 +104,20 @@ public: private: enum image_orientation { - k0, // 0 - k90, // 1 - k180, // 2 - k270, // 3 - k0V, // 4 - k90V, // 5 - k0H, // 6 - k270V, // 7 + k0, // 0 + k90, // 1 + k180, // 2 + k270, // 3 + k0V, // 4 + k90V, // 5 + k0H, // 6 + k270V, // 7 kNumberOfOrientations, }; void _SetHasSelection(bool bHasSelection); void _AnimateSelection(bool a); - void _SendMessageToWindow(BMessage *message); + void _SendMessageToWindow(BMessage* message); void _SendMessageToWindow(uint32 code); void _Notify(); void _UpdateStatusText(); diff --git a/src/apps/showimage/ShowImageWindow.cpp b/src/apps/showimage/ShowImageWindow.cpp index ac52492589..6aa6e9f613 100644 --- a/src/apps/showimage/ShowImageWindow.cpp +++ b/src/apps/showimage/ShowImageWindow.cpp @@ -990,7 +990,8 @@ ShowImageWindow::MessageReceived(BMessage* message) backgroundsMessage.AddRef("refs", fImageView->Image()); // This is used in the Backgrounds code for scaled placement backgroundsMessage.AddInt32("placement", 'scpl'); - be_roster->Launch("application/x-vnd.haiku-backgrounds", &backgroundsMessage); + be_roster->Launch("application/x-vnd.haiku-backgrounds", + &backgroundsMessage); break; } @@ -1050,15 +1051,15 @@ ShowImageWindow::MessageReceived(BMessage* message) if (message->FindFloat("offset", &offset) == B_OK && message->FindBool("show", &show) == B_OK) { // Compensate rounding errors with the final placement - if (show) { + if (show) fToolBarView->MoveTo(fToolBarView->Frame().left, 0); - } else { + else { fToolBarView->MoveTo(fToolBarView->Frame().left, offset); fToolBarView->Hide(); } BRect frame = fToolBarView->Parent()->Bounds(); frame.top = fToolBarView->Frame().bottom + 1; - fScrollView->MoveTo(0, frame.top); + fScrollView->MoveTo(fScrollView->Frame().left, frame.top); fScrollView->ResizeTo(fScrollView->Bounds().Width(), frame.Height() - B_H_SCROLL_BAR_HEIGHT + 1); fVerticalScrollBar->MoveTo( @@ -1174,7 +1175,8 @@ ShowImageWindow::_SaveToFile(BMessage* message) int32 i; for (i = 0; i < outCount; i++) { - if (outFormat[i].group == B_TRANSLATOR_BITMAP && outFormat[i].type == outType) + if (outFormat[i].group == B_TRANSLATOR_BITMAP && outFormat[i].type + == outType) break; } if (i == outCount) @@ -1271,7 +1273,7 @@ ShowImageWindow::_ToggleFullScreen() BScreen screen; fWindowFrame = Frame(); frame = screen.Frame(); - frame.top -= fBar->Bounds().Height()+1; + frame.top -= fBar->Bounds().Height() + 1; frame.right += B_V_SCROLL_BAR_WIDTH; frame.bottom += B_H_SCROLL_BAR_HEIGHT; frame.InsetBy(-1, -1); // PEN_SIZE in ShowImageView @@ -1414,7 +1416,7 @@ ShowImageWindow::_Print(BMessage* msg) float width; switch (fPrintOptions.Option()) { case PrintOptions::kFitToPage: { - float w1 = printableRect.Width()+1; + float w1 = printableRect.Width() + 1; float w2 = imageWidth * (printableRect.Height() + 1) / imageHeight; if (w2 < w1) diff --git a/src/apps/showimage/ToolBarView.cpp b/src/apps/showimage/ToolBarView.cpp index e1c2714ffa..a1ec2e20d1 100644 --- a/src/apps/showimage/ToolBarView.cpp +++ b/src/apps/showimage/ToolBarView.cpp @@ -51,8 +51,8 @@ ToolBarView::AddAction(uint32 command, BHandler* target, const BBitmap* icon, void -ToolBarView::AddAction(BMessage* message, BHandler* target, const BBitmap* icon, - const char* toolTipText) +ToolBarView::AddAction(BMessage* message, BHandler* target, + const BBitmap* icon, const char* toolTipText) { BIconButton* button = new BIconButton(NULL, NULL, message, target); button->SetIcon(icon); diff --git a/src/apps/soundrecorder/RecorderWindow.cpp b/src/apps/soundrecorder/RecorderWindow.cpp index f5c6e7ceaf..11b5a92997 100644 --- a/src/apps/soundrecorder/RecorderWindow.cpp +++ b/src/apps/soundrecorder/RecorderWindow.cpp @@ -62,7 +62,7 @@ // default window positioning static const float MIN_WIDTH = 400.0f; -static const float MIN_HEIGHT = 336.0f; +static const float MIN_HEIGHT = 175.0f; static const float XPOS = 100.0f; static const float YPOS = 200.0f; @@ -364,7 +364,7 @@ RecorderWindow::InitWindow() fSoundList = new SoundListView(r, B_TRANSLATE("Sound List"), B_FOLLOW_ALL); fSoundList->SetSelectionMessage(new BMessage(SOUND_SELECTED)); - fSoundList->SetViewColor(216, 216, 216); + fSoundList->SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR)); BScrollView *scroller = new BScrollView("scroller", fSoundList, B_FOLLOW_ALL, 0, false, true, B_FANCY_BORDER); fBottomBox->AddChild(scroller); @@ -374,43 +374,75 @@ RecorderWindow::InitWindow() r.bottom -= 25; r.InsetBy(10, 8); r.top -= 1; - fFileInfoBox = new BBox(r, "fileinfo", B_FOLLOW_LEFT | B_FOLLOW_BOTTOM); + fFileInfoBox = new BBox(r, "fileinfo", B_FOLLOW_LEFT); fFileInfoBox->SetLabel(B_TRANSLATE("File info")); + fFileInfoBox->SetHighColor(ui_color(B_PANEL_TEXT_COLOR)); + + BFont font = be_plain_font; + font.SetSize(font.Size() * 0.92f); + font_height height; + font.GetHeight(&height); + float fontHeight = height.ascent + height.leading + height.descent; + r = fFileInfoBox->Bounds(); r.left = 8; - r.top = 13; - r.bottom = r.top + 15; + r.top = fontHeight + 6; + r.bottom = r.top + fontHeight + 3; r.right -= 10; fFilename = new BStringView(r, "filename", B_TRANSLATE("File name:")); fFileInfoBox->AddChild(fFilename); - r.top += 13; - r.bottom = r.top + 15; + fFilename->SetFont(&font, B_FONT_SIZE); + fFilename->SetHighColor(ui_color(B_PANEL_TEXT_COLOR)); + + r.top += fontHeight; + r.bottom = r.top + fontHeight + 3; fFormat = new BStringView(r, "format", B_TRANSLATE("Format:")); fFileInfoBox->AddChild(fFormat); - r.top += 13; - r.bottom = r.top + 15; + fFormat->SetFont(&font, B_FONT_SIZE); + fFormat->SetHighColor(ui_color(B_PANEL_TEXT_COLOR)); + + r.top += fontHeight; + r.bottom = r.top + fontHeight + 3; fCompression = new BStringView(r, "compression", B_TRANSLATE("Compression:")); fFileInfoBox->AddChild(fCompression); - r.top += 13; - r.bottom = r.top + 15; + fCompression->SetFont(&font, B_FONT_SIZE); + fCompression->SetHighColor(ui_color(B_PANEL_TEXT_COLOR)); + + r.top += fontHeight; + r.bottom = r.top + fontHeight + 3; fChannels = new BStringView(r, "channels", B_TRANSLATE("Channels:")); fFileInfoBox->AddChild(fChannels); - r.top += 13; - r.bottom = r.top + 15; + fChannels->SetFont(&font, B_FONT_SIZE); + fChannels->SetHighColor(ui_color(B_PANEL_TEXT_COLOR)); + + r.top += fontHeight; + r.bottom = r.top + fontHeight + 3; fSampleSize = new BStringView(r, "samplesize", B_TRANSLATE("Sample size:")); fFileInfoBox->AddChild(fSampleSize); - r.top += 13; - r.bottom = r.top + 15; + fSampleSize->SetFont(&font, B_FONT_SIZE); + fSampleSize->SetHighColor(ui_color(B_PANEL_TEXT_COLOR)); + + r.top += fontHeight; + r.bottom = r.top + fontHeight + 3; fSampleRate = new BStringView(r, "samplerate", B_TRANSLATE("Sample rate:")); fFileInfoBox->AddChild(fSampleRate); - r.top += 13; - r.bottom = r.top + 15; + fSampleRate->SetFont(&font, B_FONT_SIZE); + fSampleRate->SetHighColor(ui_color(B_PANEL_TEXT_COLOR)); + + r.top += fontHeight; + r.bottom = r.top + fontHeight + 3; fDuration = new BStringView(r, "duration", B_TRANSLATE("Duration:")); fFileInfoBox->AddChild(fDuration); + fDuration->SetFont(&font, B_FONT_SIZE); + fDuration->SetHighColor(ui_color(B_PANEL_TEXT_COLOR)); + + fFileInfoBox->ResizeTo(fFileInfoBox->Frame().Width(), + r.bottom + fontHeight / 2.0f); + fDeployedHeight = MIN_HEIGHT + fFileInfoBox->Bounds().Height() + 40.0f; // Input selection lists all available physical inputs that produce // buffers with B_MEDIA_RAW_AUDIO format data. @@ -451,12 +483,10 @@ RecorderWindow::InitWindow() } // Create the actual widget - BRect frame(fBottomBox->Bounds()); - r = frame; - r.left = 42; - r.right = (r.left + r.right) / 2; - r.InsetBy(10,10); - r.top = r.bottom - 18; + r = fFileInfoBox->Bounds(); + r.top = r.bottom + 2; + r.bottom = r.top + 18; + r.InsetBy(10, 10); fInputField = new BMenuField(r, "Input", B_TRANSLATE("Input:"), popup); fInputField->SetDivider(fInputField->StringWidth(B_TRANSLATE("Input:")) + 4.0f); @@ -465,9 +495,8 @@ RecorderWindow::InitWindow() fBottomBox->AddChild(fFileInfoBox); fBottomBox->Hide(); - CalcSizes(Frame().Width(), MIN_HEIGHT-161); - ResizeTo(Frame().Width(), MIN_HEIGHT-161); - + CalcSizes(MIN_WIDTH, MIN_HEIGHT); + ResizeTo(Frame().Width(), MIN_HEIGHT); popup->Superitem()->SetLabel(selected_name); @@ -551,12 +580,12 @@ RecorderWindow::MessageReceived(BMessage * message) case VIEW_LIST: if (fUpDownButton->Value() == B_CONTROL_ON) { fBottomBox->Show(); - CalcSizes(Frame().Width(), MIN_HEIGHT); - ResizeTo(Frame().Width(), MIN_HEIGHT); + CalcSizes(MIN_WIDTH, fDeployedHeight); + ResizeTo(Frame().Width(), fDeployedHeight); } else { fBottomBox->Hide(); - CalcSizes(Frame().Width(), MIN_HEIGHT-161); - ResizeTo(Frame().Width(), MIN_HEIGHT-161); + CalcSizes(MIN_WIDTH, MIN_HEIGHT); + ResizeTo(Frame().Width(), MIN_HEIGHT); } break; diff --git a/src/apps/soundrecorder/RecorderWindow.h b/src/apps/soundrecorder/RecorderWindow.h index 34dec3a226..63f6b01c4b 100644 --- a/src/apps/soundrecorder/RecorderWindow.h +++ b/src/apps/soundrecorder/RecorderWindow.h @@ -94,6 +94,8 @@ private: BDirectory fTempDir; int fTempCount; + float fDeployedHeight; + BBox * fBottomBox; BBox * fFileInfoBox; BStringView *fFilename; diff --git a/src/apps/soundrecorder/SoundListView.cpp b/src/apps/soundrecorder/SoundListView.cpp index bd3bcab293..42cab62d92 100644 --- a/src/apps/soundrecorder/SoundListView.cpp +++ b/src/apps/soundrecorder/SoundListView.cpp @@ -34,12 +34,11 @@ void SoundListView::Draw(BRect updateRect) { if (IsEmpty()) { - SetHighColor(235,235,235); - FillRect(Bounds()); + SetLowColor(ViewColor()); + FillRect(Bounds(), B_SOLID_LOW); - SetHighColor(0,0,0); + SetHighColor(ui_color(B_PANEL_TEXT_COLOR)); BFont font(be_bold_font); - font.SetSize(12.0); SetFont(&font); font_height height; font.GetHeight(&height); @@ -58,7 +57,8 @@ void SoundListView::AttachedToWindow() { BListView::AttachedToWindow(); - SetViewColor(255,255,255); + SetViewColor(tint_color(ui_color(B_PANEL_BACKGROUND_COLOR), + B_LIGHTEN_1_TINT)); } diff --git a/src/apps/sudoku/SudokuView.cpp b/src/apps/sudoku/SudokuView.cpp index 849c5e51ac..8d5d1114cf 100644 --- a/src/apps/sudoku/SudokuView.cpp +++ b/src/apps/sudoku/SudokuView.cpp @@ -890,7 +890,7 @@ SudokuView::MouseDown(BPoint where) } if (value + 1 != fValueHintValue) - _SetValueHintValue(~0UL); + _SetValueHintValue(value + 1); if (wasCompleted != fField->IsValueCompleted(value + 1)) _InvalidateValue(value + 1, false, x, y); diff --git a/src/apps/webpositive/AuthenticationPanel.cpp b/src/apps/webpositive/AuthenticationPanel.cpp new file mode 100644 index 0000000000..7dcc55260f --- /dev/null +++ b/src/apps/webpositive/AuthenticationPanel.cpp @@ -0,0 +1,218 @@ +/* + * Copyright (C) 2010 Stephan Aßmus + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "AuthenticationPanel.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static const uint32 kMsgPanelOK = 'pnok'; +static const uint32 kMsgJitter = 'jitr'; +static const uint32 kHidePassword = 'hdpw'; + +AuthenticationPanel::AuthenticationPanel(BRect parentFrame) + : BWindow(BRect(-1000, -1000, -900, -900), "Authentication Required", + B_TITLED_WINDOW_LOOK, B_MODAL_APP_WINDOW_FEEL, + B_ASYNCHRONOUS_CONTROLS | B_NOT_RESIZABLE | B_NOT_ZOOMABLE + | B_CLOSE_ON_ESCAPE | B_AUTO_UPDATE_SIZE_LIMITS) + , m_parentWindowFrame(parentFrame) + , m_usernameTextControl(new BTextControl("user", "Username:", "", NULL)) + , m_passwordTextControl(new BTextControl("pass", "Password:", "", NULL)) + , m_hidePasswordCheckBox(new BCheckBox("hide", "Hide password text", new BMessage(kHidePassword))) + , m_rememberCredentialsCheckBox(new BCheckBox("remember", "Remember username and password for this site", NULL)) + , m_okButton(new BButton("ok", "OK", new BMessage(kMsgPanelOK))) + , m_cancelButton(new BButton("cancel", "Cancel", new BMessage(B_QUIT_REQUESTED))) + , m_cancelled(false) + , m_exitSemaphore(create_sem(0, "Authentication Panel")) +{ +} + +AuthenticationPanel::~AuthenticationPanel() +{ + delete_sem(m_exitSemaphore); +} + +bool +AuthenticationPanel::QuitRequested() +{ + m_cancelled = true; + release_sem(m_exitSemaphore); + return false; +} + +void +AuthenticationPanel::MessageReceived(BMessage* message) +{ + switch (message->what) { + case kMsgPanelOK: + release_sem(m_exitSemaphore); + break; + case kHidePassword: { + // TODO: Toggling this is broken in BTextView. Workaround is to + // set the text and selection again. + BString text = m_passwordTextControl->Text(); + int32 selectionStart; + int32 selectionEnd; + m_passwordTextControl->TextView()->GetSelection(&selectionStart, &selectionEnd); + m_passwordTextControl->TextView()->HideTyping( + m_hidePasswordCheckBox->Value() == B_CONTROL_ON); + m_passwordTextControl->SetText(text.String()); + m_passwordTextControl->TextView()->Select(selectionStart, selectionEnd); + break; + } + case kMsgJitter: { + UpdateIfNeeded(); + BPoint leftTop = Frame().LeftTop(); + const float jitterOffsets[] = { -10, 0, 10, 0 }; + const int32 jitterOffsetCount = sizeof(jitterOffsets) / sizeof(float); + for (int32 i = 0; i < 20; i++) { + float offset = jitterOffsets[i % jitterOffsetCount]; + MoveTo(leftTop.x + offset, leftTop.y); + snooze(15000); + } + MoveTo(leftTop); + break; + } + default: + BWindow::MessageReceived(message); + } +} + +bool AuthenticationPanel::getAuthentication(const BString& text, + const BString& previousUser, const BString& previousPass, + bool previousRememberCredentials, bool badPassword, + BString& user, BString& pass, bool* rememberCredentials) +{ + // Configure panel and layout controls. + rgb_color infoColor = ui_color(B_PANEL_TEXT_COLOR); + BRect textBounds(0, 0, 250, 200); + BTextView* textView = new BTextView(textBounds, "text", textBounds, + be_plain_font, &infoColor, B_FOLLOW_NONE, B_WILL_DRAW | B_SUPPORTS_LAYOUT); + textView->SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR)); + textView->SetText(text.String()); + textView->MakeEditable(false); + textView->MakeSelectable(false); + + m_usernameTextControl->SetText(previousUser.String()); + m_passwordTextControl->TextView()->HideTyping(true); + // Ignore the previous password, if it didn't work. + if (!badPassword) + m_passwordTextControl->SetText(previousPass.String()); + m_hidePasswordCheckBox->SetValue(B_CONTROL_ON); + m_rememberCredentialsCheckBox->SetValue(previousRememberCredentials); + + // create layout + SetLayout(new BGroupLayout(B_VERTICAL, 0.0)); + float spacing = be_control_look->DefaultItemSpacing(); + AddChild(BGroupLayoutBuilder(B_VERTICAL, 0.0) + .Add(BGridLayoutBuilder(0, spacing) + .Add(textView, 0, 0, 2) + .Add(m_usernameTextControl->CreateLabelLayoutItem(), 0, 1) + .Add(m_usernameTextControl->CreateTextViewLayoutItem(), 1, 1) + .Add(m_passwordTextControl->CreateLabelLayoutItem(), 0, 2) + .Add(m_passwordTextControl->CreateTextViewLayoutItem(), 1, 2) + .Add(BSpaceLayoutItem::CreateGlue(), 0, 3) + .Add(m_hidePasswordCheckBox, 1, 3) + .Add(m_rememberCredentialsCheckBox, 0, 4, 2) + .SetInsets(spacing, spacing, spacing, spacing) + ) + .Add(new BSeparatorView(B_HORIZONTAL, B_PLAIN_BORDER)) + .Add(BGroupLayoutBuilder(B_HORIZONTAL, spacing) + .AddGlue() + .Add(m_cancelButton) + .Add(m_okButton) + .SetInsets(spacing, spacing, spacing, spacing) + ) + ); + + float textHeight = textView->LineHeight(0) * textView->CountLines(); + textView->SetExplicitMinSize(BSize(B_SIZE_UNSET, textHeight)); + + SetDefaultButton(m_okButton); + if (badPassword && previousUser.Length()) + m_passwordTextControl->MakeFocus(true); + else + m_usernameTextControl->MakeFocus(true); + + if (m_parentWindowFrame.IsValid()) + CenterIn(m_parentWindowFrame); + else + CenterOnScreen(); + + // Start AuthenticationPanel window thread + Show(); + + // Let the window jitter, if the previous password was invalid + if (badPassword) + PostMessage(kMsgJitter); + + // Block calling thread + // Get the originating window, if it exists, to let it redraw itself. + BWindow* window = dynamic_cast(BLooper::LooperForThread(find_thread(NULL))); + if (window) { + status_t err; + for (;;) { + do { + err = acquire_sem_etc(m_exitSemaphore, 1, B_RELATIVE_TIMEOUT, 10000); + // We've (probably) had our time slice taken away from us + } while (err == B_INTERRUPTED); + + if (err != B_TIMED_OUT) { + // Semaphore was finally released or nuked. + break; + } + window->UpdateIfNeeded(); + } + } else { + // No window to update, so just hang out until we're done. + while (acquire_sem(m_exitSemaphore) == B_INTERRUPTED) { + } + } + + // AuthenticationPanel wants to quit. + Lock(); + + user = m_usernameTextControl->Text(); + pass = m_passwordTextControl->Text(); + if (rememberCredentials) + *rememberCredentials = m_rememberCredentialsCheckBox->Value() == B_CONTROL_ON; + + bool canceled = m_cancelled; + Quit(); + // AuthenticationPanel object is TOAST here. + return !canceled; +} diff --git a/src/apps/webpositive/AuthenticationPanel.h b/src/apps/webpositive/AuthenticationPanel.h new file mode 100644 index 0000000000..5af9496aad --- /dev/null +++ b/src/apps/webpositive/AuthenticationPanel.h @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2010 Stephan Aßmus + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef AuthenticationPanel_h +#define AuthenticationPanel_h + +#include +#include + +class BCheckBox; +class BTextControl; + +class AuthenticationPanel : public BWindow { +public: + AuthenticationPanel(BRect parentFrame = BRect()); + virtual ~AuthenticationPanel(); + + virtual bool QuitRequested(); + + virtual void MessageReceived(BMessage *message); + + bool getAuthentication(const BString& text, const BString& previousUser, + const BString& previousPass, bool previousRememberCredentials, + bool badPassword, BString& user, BString& pass, + bool* rememberCredentials); + +private: + BRect m_parentWindowFrame; + BTextControl* m_usernameTextControl; + BTextControl* m_passwordTextControl; + BCheckBox* m_hidePasswordCheckBox; + BCheckBox* m_rememberCredentialsCheckBox; + BButton* m_okButton; + BButton* m_cancelButton; + + bool m_cancelled; + + sem_id m_exitSemaphore; +}; + +#endif // AuthenticationPanel_h diff --git a/src/apps/webpositive/BrowserApp.cpp b/src/apps/webpositive/BrowserApp.cpp new file mode 100644 index 0000000000..71e2177e42 --- /dev/null +++ b/src/apps/webpositive/BrowserApp.cpp @@ -0,0 +1,449 @@ +/* + * Copyright (C) 2007 Ryan Leavengood + * Copyright (C) 2010 Stephan Aßmus + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "BrowserApp.h" + +#include "BrowserWindow.h" +#include "BrowsingHistory.h" +#include "DownloadWindow.h" +#include "SettingsMessage.h" +#include "SettingsWindow.h" +#include "svn_revision.h" +#include "NetworkCookieJar.h" +#include "WebPage.h" +#include "WebSettings.h" +#include "WebView.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +const char* kApplicationSignature = "application/x-vnd.Haiku-WebPositive"; +const char* kApplicationName = "WebPositive"; +static const uint32 PRELOAD_BROWSING_HISTORY = 'plbh'; + +#define ENABLE_NATIVE_COOKIES 0 + + +BrowserApp::BrowserApp() + : + BApplication(kApplicationSignature), + fWindowCount(0), + fLastWindowFrame(50, 50, 950, 750), + fLaunchRefsMessage(0), + fInitialized(false), + fSettings(NULL), + fCookies(NULL), + fCookieJar(NULL), + fDownloadWindow(NULL), + fSettingsWindow(NULL) +{ +} + + +BrowserApp::~BrowserApp() +{ + delete fLaunchRefsMessage; + delete fSettings; + delete fCookies; + delete fCookieJar; +} + + +void +BrowserApp::AboutRequested() +{ + BString aboutText("WebPositive\n\nby Ryan Leavengood, Andrea Anzani, " + "Maxime Simon, Michael Lotz, Rene Gollent and Stephan Aßmus"); + aboutText << "\n\nSVN revision: " << kSVNRevision; + + BAlert* alert = new BAlert("About WebPositive", aboutText.String(), + "Sweet!"); + alert->Go(NULL); +} + + +void +BrowserApp::ArgvReceived(int32 argc, char** argv) +{ + BMessage message(B_REFS_RECEIVED); + for (int i = 1; i < argc; i++) { + if (strcmp("-f", argv[i]) == 0 + || strcmp("--fullscreen", argv[i]) == 0) { + message.AddBool("fullscreen", true); + continue; + } + const char* url = argv[i]; + BEntry entry(argv[i], true); + BPath path; + if (entry.Exists() && entry.GetPath(&path) == B_OK) + url = path.Path(); + message.AddString("url", url); + } + // Upon program launch, it will buffer a copy of the message, since + // ArgReceived() is called before ReadyToRun(). + RefsReceived(&message); +} + + +void +BrowserApp::ReadyToRun() +{ + // Since we will essentially run the GUI... + set_thread_priority(Thread(), B_DISPLAY_PRIORITY); + + BWebPage::InitializeOnce(); + BWebPage::SetCacheModel(B_WEBKIT_CACHE_MODEL_WEB_BROWSER); + + BPath path; + if (find_directory(B_USER_SETTINGS_DIRECTORY, &path) == B_OK + && path.Append(kApplicationName) == B_OK + && create_directory(path.Path(), 0777) == B_OK) { + + BWebSettings::SetPersistentStoragePath(path.Path()); + } + + BString mainSettingsPath(kApplicationName); + mainSettingsPath << "/Application"; + fSettings = new SettingsMessage(B_USER_SETTINGS_DIRECTORY, + mainSettingsPath.String()); +#if ENABLE_NATIVE_COOKIES + mainSettingsPath = kApplicationName; + mainSettingsPath << "/Cookies"; + fCookies = new SettingsMessage(B_USER_SETTINGS_DIRECTORY, + mainSettingsPath.String()); + BMessage cookieArchive; + cookieArchive = fCookies->GetValue("cookies", cookieArchive); + fCookieJar = new BNetworkCookieJar(cookieArchive); + BWebPage::SetCookieJar(fCookieJar); +#endif + + fLastWindowFrame = fSettings->GetValue("window frame", fLastWindowFrame); + BRect defaultDownloadWindowFrame(-10, -10, 365, 265); + BRect downloadWindowFrame = fSettings->GetValue("downloads window frame", + defaultDownloadWindowFrame); + BRect settingsWindowFrame = fSettings->GetValue("settings window frame", + BRect()); + bool showDownloads = fSettings->GetValue("show downloads", false); + + fDownloadWindow = new DownloadWindow(downloadWindowFrame, showDownloads, + fSettings); + if (downloadWindowFrame == defaultDownloadWindowFrame) { + // Initially put download window in lower right of screen. + BRect screenFrame = BScreen().Frame(); + BMessage decoratorSettings; + fDownloadWindow->GetDecoratorSettings(&decoratorSettings); + float borderWidth = 0; + if (decoratorSettings.FindFloat("border width", &borderWidth) != B_OK) + borderWidth = 5; + fDownloadWindow->MoveTo(screenFrame.Width() - fDownloadWindow->Frame().Width() - borderWidth, + screenFrame.Height() - fDownloadWindow->Frame().Height() - borderWidth); + } + fSettingsWindow = new SettingsWindow(settingsWindowFrame, fSettings); + + BWebPage::SetDownloadListener(BMessenger(fDownloadWindow)); + + fInitialized = true; + + int32 pagesCreated = 0; + bool fullscreen = false; + if (fLaunchRefsMessage) { + _RefsReceived(fLaunchRefsMessage, &pagesCreated, &fullscreen); + delete fLaunchRefsMessage; + fLaunchRefsMessage = NULL; + } + if (pagesCreated == 0) + _CreateNewWindow("", fullscreen); + + PostMessage(PRELOAD_BROWSING_HISTORY); +} + + +void +BrowserApp::MessageReceived(BMessage* message) +{ + switch (message->what) { + case PRELOAD_BROWSING_HISTORY: + // Accessing the default instance will load the history from disk. + BrowsingHistory::DefaultInstance(); + break; + case B_SILENT_RELAUNCH: + _CreateNewPage(""); + break; + case NEW_WINDOW: { + BString url; + if (message->FindString("url", &url) != B_OK) + break; + _CreateNewWindow(url); + break; + } + case NEW_TAB: { + BrowserWindow* window; + if (message->FindPointer("window", reinterpret_cast(&window)) != B_OK) + break; + BString url; + message->FindString("url", &url); + bool select = false; + message->FindBool("select", &select); + _CreateNewTab(window, url, select); + break; + } + case WINDOW_OPENED: + fWindowCount++; + fDownloadWindow->SetMinimizeOnClose(false); + break; + case WINDOW_CLOSED: + fWindowCount--; + message->FindRect("window frame", &fLastWindowFrame); + if (fWindowCount <= 0) + PostMessage(B_QUIT_REQUESTED); + break; + + case SHOW_DOWNLOAD_WINDOW: + _ShowWindow(message, fDownloadWindow); + break; + case SHOW_SETTINGS_WINDOW: + _ShowWindow(message, fSettingsWindow); + break; + + default: + BApplication::MessageReceived(message); + break; + } +} + + +void +BrowserApp::RefsReceived(BMessage* message) +{ + if (!fInitialized) { + delete fLaunchRefsMessage; + fLaunchRefsMessage = new BMessage(*message); + return; + } + + _RefsReceived(message); +} + + +bool +BrowserApp::QuitRequested() +{ + if (fDownloadWindow->DownloadsInProgress()) { + BAlert* alert = new BAlert("Downloads in progress", + "There are still downloads in progress, do you really want to " + "quit WebPositive now?", "Quit", "Continue downloads"); + int32 choice = alert->Go(); + if (choice == 1) { + if (fWindowCount == 0) { + if (fDownloadWindow->Lock()) { + fDownloadWindow->SetWorkspaces(1 << current_workspace()); + if (fDownloadWindow->IsHidden()) + fDownloadWindow->Show(); + else + fDownloadWindow->Activate(); + fDownloadWindow->SetMinimizeOnClose(true); + fDownloadWindow->Unlock(); + return false; + } + } else + return false; + } + } + + for (int i = 0; BWindow* window = WindowAt(i); i++) { + BrowserWindow* webWindow = dynamic_cast(window); + if (!webWindow) + continue; + if (!webWindow->Lock()) + continue; + if (webWindow->QuitRequested()) { + fLastWindowFrame = webWindow->WindowFrame(); + webWindow->Quit(); + i--; + } else { + webWindow->Unlock(); + return false; + } + } + + BWebPage::ShutdownOnce(); + + fSettings->SetValue("window frame", fLastWindowFrame); + if (fDownloadWindow->Lock()) { + fSettings->SetValue("downloads window frame", fDownloadWindow->Frame()); + fSettings->SetValue("show downloads", !fDownloadWindow->IsHidden()); + fDownloadWindow->Unlock(); + } + if (fSettingsWindow->Lock()) { + fSettings->SetValue("settings window frame", fSettingsWindow->Frame()); + fSettingsWindow->Unlock(); + } + + BMessage cookieArchive; + if (fCookieJar != NULL && fCookieJar->Archive(&cookieArchive) == B_OK) + fCookies->SetValue("cookies", cookieArchive); + + return true; +} + + +void +BrowserApp::_RefsReceived(BMessage* message, int32* _pagesCreated, + bool* _fullscreen) +{ + int32 pagesCreated = 0; + + bool fullscreen; + if (message->FindBool("fullscreen", &fullscreen) != B_OK) + fullscreen = false; + + entry_ref ref; + for (int32 i = 0; message->FindRef("refs", i, &ref) == B_OK; i++) { + BEntry entry(&ref, true); + if (!entry.Exists()) + continue; + BPath path; + if (entry.GetPath(&path) != B_OK) + continue; + BString url; + url << path.Path(); + _CreateNewPage(url, fullscreen); + pagesCreated++; + } + + BString url; + for (int32 i = 0; message->FindString("url", i, &url) == B_OK; i++) { + _CreateNewPage(url, fullscreen); + pagesCreated++; + } + + if (_pagesCreated != NULL) + *_pagesCreated = pagesCreated; + if (_fullscreen != NULL) + *_fullscreen = fullscreen; +} + + +void +BrowserApp::_CreateNewPage(const BString& url, bool fullscreen) +{ + uint32 workspace = 1 << current_workspace(); + + bool loadedInWindowOnCurrentWorkspace = false; + for (int i = 0; BWindow* window = WindowAt(i); i++) { + BrowserWindow* webWindow = dynamic_cast(window); + if (!webWindow) + continue; + if (webWindow->Lock()) { + if (webWindow->Workspaces() & workspace) { + if (webWindow->IsBlankTab()) { + if (url.Length() != 0) + webWindow->CurrentWebView()->LoadURL(url); + } else + webWindow->CreateNewTab(url, true); + webWindow->Activate(); + webWindow->CurrentWebView()->MakeFocus(true); + loadedInWindowOnCurrentWorkspace = true; + } + webWindow->Unlock(); + } + if (loadedInWindowOnCurrentWorkspace) + return; + } + _CreateNewWindow(url, fullscreen); +} + + +void +BrowserApp::_CreateNewWindow(const BString& url, bool fullscreen) +{ + // Offset the window frame unless this is the first window created in the + // session. + if (fWindowCount > 0) + fLastWindowFrame.OffsetBy(20, 20); + if (!BScreen().Frame().Contains(fLastWindowFrame)) + fLastWindowFrame.OffsetTo(50, 50); + + BrowserWindow* window = new BrowserWindow(fLastWindowFrame, fSettings, + url); + if (fullscreen) + window->ToggleFullscreen(); + window->Show(); +} + + +void +BrowserApp::_CreateNewTab(BrowserWindow* window, const BString& url, + bool select) +{ + if (!window->Lock()) + return; + window->CreateNewTab(url, select); + window->Unlock(); +} + + +void +BrowserApp::_ShowWindow(const BMessage* message, BWindow* window) +{ + BAutolock _(window); + uint32 workspaces; + if (message->FindUInt32("workspaces", &workspaces) == B_OK) + window->SetWorkspaces(workspaces); + if (window->IsHidden()) + window->Show(); + else + window->Activate(); +} + + +// #pragma mark - + + +int +main(int, char**) +{ + try { + new BrowserApp(); + be_app->Run(); + delete be_app; + } catch (...) { + debugger("Exception caught."); + } + + return 0; +} + diff --git a/src/apps/webpositive/BrowserApp.h b/src/apps/webpositive/BrowserApp.h new file mode 100644 index 0000000000..05885583f6 --- /dev/null +++ b/src/apps/webpositive/BrowserApp.h @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2007 Ryan Leavengood + * Copyright (C) 2010 Stephan Aßmus + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef BROWSER_APP_H +#define BROWSER_APP_H + + +#include +#include +#include + +class BNetworkCookieJar; +class DownloadWindow; +class BrowserWindow; +class SettingsMessage; +class SettingsWindow; + + +class BrowserApp : public BApplication { +public: + BrowserApp(); + virtual ~BrowserApp(); + + virtual void AboutRequested(); + virtual void ArgvReceived(int32 agrc, char** argv); + virtual void MessageReceived(BMessage* message); + virtual void RefsReceived(BMessage* message); + virtual void ReadyToRun(); + virtual bool QuitRequested(); + +private: + void _RefsReceived(BMessage* message, + int32* pagesCreated = NULL, + bool* fullscreen = NULL); + void _CreateNewPage(const BString& url, + bool fullscreen = false); + void _CreateNewWindow(const BString& url, + bool fullscreen = false); + void _CreateNewTab(BrowserWindow* window, + const BString& url, bool select); + void _ShowWindow(const BMessage* message, + BWindow* window); + +private: + int fWindowCount; + BRect fLastWindowFrame; + BMessage* fLaunchRefsMessage; + bool fInitialized; + + SettingsMessage* fSettings; + SettingsMessage* fCookies; + BNetworkCookieJar* fCookieJar; + + DownloadWindow* fDownloadWindow; + SettingsWindow* fSettingsWindow; +}; + + +extern const char* kApplicationSignature; +extern const char* kApplicationName; + + +#endif // BROWSER_APP_H + diff --git a/src/apps/webpositive/BrowserWindow.cpp b/src/apps/webpositive/BrowserWindow.cpp new file mode 100644 index 0000000000..4987ea7f40 --- /dev/null +++ b/src/apps/webpositive/BrowserWindow.cpp @@ -0,0 +1,2215 @@ +/* + * Copyright (C) 2007 Andrea Anzani + * Copyright (C) 2007, 2010 Ryan Leavengood + * Copyright (C) 2009 Maxime Simon + * Copyright (C) 2010 Stephan Aßmus + * Copyright (C) 2010 Michael Lotz + * Copyright (C) 2010 Rene Gollent + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "BrowserWindow.h" + +#include "AuthenticationPanel.h" +#include "BaseURL.h" +#include "BitmapButton.h" +#include "BrowserApp.h" +#include "BrowsingHistory.h" +#include "CredentialsStorage.h" +#include "IconButton.h" +#include "NavMenu.h" +#include "SettingsKeys.h" +#include "SettingsMessage.h" +#include "TabManager.h" +#include "URLInputGroup.h" +#include "WebPage.h" +#include "WebView.h" +#include "WebViewConstants.h" +#include "WindowIcon.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + + +enum { + OPEN_LOCATION = 'open', + GO_BACK = 'goba', + GO_FORWARD = 'gofo', + STOP = 'stop', + HOME = 'home', + GOTO_URL = 'goul', + RELOAD = 'reld', + CLEAR_HISTORY = 'clhs', + + CREATE_BOOKMARK = 'crbm', + SHOW_BOOKMARKS = 'shbm', + + ZOOM_FACTOR_INCREASE = 'zfin', + ZOOM_FACTOR_DECREASE = 'zfdc', + ZOOM_FACTOR_RESET = 'zfrs', + ZOOM_TEXT_ONLY = 'zfto', + + TOGGLE_FULLSCREEN = 'tgfs', + TOGGLE_AUTO_HIDE_INTERFACE_IN_FULLSCREEN = 'tgah', + CHECK_AUTO_HIDE_INTERFACE = 'cahi', + + SHOW_PAGE_SOURCE = 'spgs', + + EDIT_SHOW_FIND_GROUP = 'sfnd', + EDIT_HIDE_FIND_GROUP = 'hfnd', + EDIT_FIND_NEXT = 'fndn', + EDIT_FIND_PREVIOUS = 'fndp', + FIND_TEXT_CHANGED = 'ftxt', + + SELECT_TAB = 'sltb', +}; + + +static BLayoutItem* +layoutItemFor(BView* view) +{ + BLayout* layout = view->Parent()->GetLayout(); + int32 index = layout->IndexOfView(view); + return layout->ItemAt(index); +} + + +class BookmarkMenu : public BNavMenu { +public: + BookmarkMenu(const char* title, BHandler* target, const entry_ref* navDir) + : + BNavMenu(title, B_REFS_RECEIVED, target) + { + // Add these items here already, so the shortcuts work even when + // the menu has never been opened yet. + _AddStaticItems(); + + SetNavDir(navDir); + } + + virtual void AttachedToWindow() + { + RemoveItems(0, CountItems(), true); + ForceRebuild(); + BNavMenu::AttachedToWindow(); + if (CountItems() > 0) + AddItem(new BSeparatorItem(), 0); + _AddStaticItems(); + DoLayout(); + } + +private: + void _AddStaticItems() + { + AddItem(new BMenuItem("Manage bookmarks", + new BMessage(SHOW_BOOKMARKS), 'M'), 0); + AddItem(new BMenuItem("Bookmark this page", + new BMessage(CREATE_BOOKMARK), 'B'), 0); + } +}; + + +class PageUserData : public BWebView::UserData { +public: + PageUserData(BView* focusedView) + : + fFocusedView(focusedView), + fPageIcon(NULL), + fURLInputSelectionStart(-1), + fURLInputSelectionEnd(-1) + { + } + + ~PageUserData() + { + delete fPageIcon; + } + + void SetFocusedView(BView* focusedView) + { + fFocusedView = focusedView; + } + + BView* FocusedView() const + { + return fFocusedView; + } + + void SetPageIcon(const BBitmap* icon) + { + delete fPageIcon; + if (icon) + fPageIcon = new BBitmap(icon); + else + fPageIcon = NULL; + } + + const BBitmap* PageIcon() const + { + return fPageIcon; + } + + void SetURLInputContents(const char* text) + { + fURLInputContents = text; + } + + const BString& URLInputContents() const + { + return fURLInputContents; + } + + void SetURLInputSelection(int32 selectionStart, int32 selectionEnd) + { + fURLInputSelectionStart = selectionStart; + fURLInputSelectionEnd = selectionEnd; + } + + int32 URLInputSelectionStart() const + { + return fURLInputSelectionStart; + } + + int32 URLInputSelectionEnd() const + { + return fURLInputSelectionEnd; + } + +private: + BView* fFocusedView; + BBitmap* fPageIcon; + BString fURLInputContents; + int32 fURLInputSelectionStart; + int32 fURLInputSelectionEnd; +}; + + +// #pragma mark - BrowserWindow + + +BrowserWindow::BrowserWindow(BRect frame, SettingsMessage* appSettings, + const BString& url, uint32 interfaceElements, BWebView* webView) + : + BWebWindow(frame, kApplicationName, + B_DOCUMENT_WINDOW_LOOK, B_NORMAL_WINDOW_FEEL, + B_AUTO_UPDATE_SIZE_LIMITS | B_ASYNCHRONOUS_CONTROLS), + fIsFullscreen(false), + fInterfaceVisible(false), + fPulseRunner(NULL), + fVisibleInterfaceElements(interfaceElements), + fAppSettings(appSettings), + fZoomTextOnly(true), + fShowTabsIfSinglePageOpen(true), + fAutoHideInterfaceInFullscreenMode(false), + fAutoHidePointer(false) +{ + // Begin listening to settings changes and read some current values. + fAppSettings->AddListener(BMessenger(this)); +// fZoomTextOnly = fAppSettings->GetValue("zoom text only", fZoomTextOnly); + fShowTabsIfSinglePageOpen = fAppSettings->GetValue( + kSettingsKeyShowTabsIfSinglePageOpen, fShowTabsIfSinglePageOpen); + + fAutoHidePointer = fAppSettings->GetValue(kSettingsKeyAutoHidePointer, + fAutoHidePointer); + + fNewWindowPolicy = fAppSettings->GetValue(kSettingsKeyNewWindowPolicy, + (uint32)OpenStartPage); + fNewTabPolicy = fAppSettings->GetValue(kSettingsKeyNewTabPolicy, + (uint32)OpenBlankPage); + fStartPageURL = fAppSettings->GetValue(kSettingsKeyStartPageURL, + kDefaultStartPageURL); + fSearchPageURL = fAppSettings->GetValue(kSettingsKeySearchPageURL, + kDefaultSearchPageURL); + + // Create the interface elements + BMessage* newTabMessage = new BMessage(NEW_TAB); + newTabMessage->AddString("url", ""); + newTabMessage->AddPointer("window", this); + newTabMessage->AddBool("select", true); + fTabManager = new TabManager(BMessenger(this), newTabMessage); + + // Menu +#if INTEGRATE_MENU_INTO_TAB_BAR + BMenu* mainMenu = fTabManager->Menu(); +#else + BMenu* mainMenu = new BMenuBar("Main menu"); +#endif + BMenu* menu = new BMenu("Window"); + BMessage* newWindowMessage = new BMessage(NEW_WINDOW); + newWindowMessage->AddString("url", ""); + BMenuItem* newItem = new BMenuItem("New window", newWindowMessage, 'N'); + menu->AddItem(newItem); + newItem->SetTarget(be_app); + newItem = new BMenuItem("New tab", new BMessage(*newTabMessage), 'T'); + menu->AddItem(newItem); + newItem->SetTarget(be_app); + menu->AddItem(new BMenuItem("Open location", new BMessage(OPEN_LOCATION), + 'L')); + menu->AddSeparatorItem(); + menu->AddItem(new BMenuItem("Close window", new BMessage(B_QUIT_REQUESTED), + 'W', B_SHIFT_KEY)); + menu->AddItem(new BMenuItem("Close tab", new BMessage(CLOSE_TAB), 'W')); + menu->AddSeparatorItem(); + menu->AddItem(new BMenuItem("Downloads", + new BMessage(SHOW_DOWNLOAD_WINDOW), 'D')); + menu->AddItem(new BMenuItem("Settings", + new BMessage(SHOW_SETTINGS_WINDOW))); + BMenuItem* aboutItem = new BMenuItem("About", + new BMessage(B_ABOUT_REQUESTED)); + menu->AddItem(aboutItem); + aboutItem->SetTarget(be_app); + menu->AddSeparatorItem(); + BMenuItem* quitItem = new BMenuItem("Quit", + new BMessage(B_QUIT_REQUESTED), 'Q'); + menu->AddItem(quitItem); + quitItem->SetTarget(be_app); + mainMenu->AddItem(menu); + + menu = new BMenu("Edit"); + menu->AddItem(fCutMenuItem = new BMenuItem("Cut", new BMessage(B_CUT), + 'X')); + menu->AddItem(fCopyMenuItem = new BMenuItem("Copy", new BMessage(B_COPY), + 'C')); + menu->AddItem(fPasteMenuItem = new BMenuItem("Paste", new BMessage(B_PASTE), + 'V')); + menu->AddSeparatorItem(); + menu->AddItem(new BMenuItem("Find", new BMessage(EDIT_SHOW_FIND_GROUP), + 'F')); + menu->AddItem(fFindPreviousMenuItem = new BMenuItem("Find previous", + new BMessage(EDIT_FIND_PREVIOUS), 'G', B_SHIFT_KEY)); + menu->AddItem(fFindNextMenuItem = new BMenuItem("Find next", + new BMessage(EDIT_FIND_NEXT), 'G')); + mainMenu->AddItem(menu); + fFindPreviousMenuItem->SetEnabled(false); + fFindNextMenuItem->SetEnabled(false); + + menu = new BMenu("View"); + menu->AddItem(new BMenuItem("Reload", new BMessage(RELOAD), 'R')); + menu->AddSeparatorItem(); + menu->AddItem(new BMenuItem("Increase size", + new BMessage(ZOOM_FACTOR_INCREASE), '+')); + menu->AddItem(new BMenuItem("Decrease size", + new BMessage(ZOOM_FACTOR_DECREASE), '-')); + menu->AddItem(new BMenuItem("Reset size", + new BMessage(ZOOM_FACTOR_RESET), '0')); + fZoomTextOnlyMenuItem = new BMenuItem("Zoom text only", + new BMessage(ZOOM_TEXT_ONLY)); + fZoomTextOnlyMenuItem->SetMarked(fZoomTextOnly); + menu->AddItem(fZoomTextOnlyMenuItem); + + menu->AddSeparatorItem(); + fFullscreenItem = new BMenuItem("Fullscreen", + new BMessage(TOGGLE_FULLSCREEN), B_RETURN); + menu->AddItem(fFullscreenItem); + menu->AddItem(new BMenuItem("Page source", + new BMessage(SHOW_PAGE_SOURCE), 'U')); + mainMenu->AddItem(menu); + + fHistoryMenu = new BMenu("History"); + fHistoryMenu->AddItem(fBackMenuItem = new BMenuItem("Back", + new BMessage(GO_BACK), B_LEFT_ARROW)); + fHistoryMenu->AddItem(fForwardMenuItem = new BMenuItem("Forward", + new BMessage(GO_FORWARD), B_RIGHT_ARROW)); + fHistoryMenu->AddSeparatorItem(); + fHistoryMenuFixedItemCount = fHistoryMenu->CountItems(); + mainMenu->AddItem(fHistoryMenu); + + BPath bookmarkPath; + entry_ref bookmarkRef; + if (_BookmarkPath(bookmarkPath) == B_OK + && get_ref_for_path(bookmarkPath.Path(), &bookmarkRef) == B_OK) { + BMenu* bookmarkMenu + = new BookmarkMenu("Bookmarks", this, &bookmarkRef); + mainMenu->AddItem(bookmarkMenu); + } + + // Back, Forward, Stop & Home buttons + fBackButton = new IconButton("Back", 0, NULL, new BMessage(GO_BACK)); + fBackButton->SetIcon(201); + fBackButton->TrimIcon(); + + fForwardButton = new IconButton("Forward", 0, NULL, new BMessage(GO_FORWARD)); + fForwardButton->SetIcon(202); + fForwardButton->TrimIcon(); + + fStopButton = new IconButton("Stop", 0, NULL, new BMessage(STOP)); + fStopButton->SetIcon(204); + fStopButton->TrimIcon(); + + fHomeButton = new IconButton("Home", 0, NULL, new BMessage(HOME)); + fHomeButton->SetIcon(206); + fHomeButton->TrimIcon(); + if (!fAppSettings->GetValue(kSettingsKeyShowHomeButton, true)) + fHomeButton->Hide(); + + // URL input group + fURLInputGroup = new URLInputGroup(new BMessage(GOTO_URL)); + + // Status Bar + fStatusText = new BStringView("status", ""); + fStatusText->SetAlignment(B_ALIGN_LEFT); + fStatusText->SetExplicitMaxSize(BSize(B_SIZE_UNLIMITED, B_SIZE_UNSET)); + fStatusText->SetExplicitMinSize(BSize(150, 12)); + // Prevent the window from growing to fit a long status message... + BFont font(be_plain_font); + font.SetSize(ceilf(font.Size() * 0.8)); + fStatusText->SetFont(&font, B_FONT_SIZE); + + // Loading progress bar + fLoadingProgressBar = new BStatusBar("progress"); + fLoadingProgressBar->SetMaxValue(100); + fLoadingProgressBar->Hide(); + fLoadingProgressBar->SetBarHeight(12); + + const float kInsetSpacing = 3; + const float kElementSpacing = 5; + + // Find group + fFindTextControl = new BTextControl("find", "Find:", "", + new BMessage(EDIT_FIND_NEXT)); + fFindTextControl->SetModificationMessage(new BMessage(FIND_TEXT_CHANGED)); + fFindPreviousButton = new BButton("Previous", + new BMessage(EDIT_FIND_PREVIOUS)); + fFindNextButton = new BButton("Next", new BMessage(EDIT_FIND_NEXT)); + fFindCloseButton = new BButton("Close", + new BMessage(EDIT_HIDE_FIND_GROUP)); + fFindCaseSensitiveCheckBox = new BCheckBox("Match case"); + BGroupLayout* findGroup = BLayoutBuilder::Group<>(B_VERTICAL, 0.0) + .Add(new BSeparatorView(B_HORIZONTAL, B_PLAIN_BORDER)) + .Add(BGroupLayoutBuilder(B_HORIZONTAL, kElementSpacing) + .Add(fFindTextControl) + .Add(fFindPreviousButton) + .Add(fFindNextButton) + .Add(fFindCaseSensitiveCheckBox) + .Add(BSpaceLayoutItem::CreateGlue()) + .Add(fFindCloseButton) + .SetInsets(kInsetSpacing, kInsetSpacing, + kInsetSpacing, kInsetSpacing) + ) + ; + + // Navigation group + BGroupLayout* navigationGroup = BLayoutBuilder::Group<>(B_VERTICAL, 0.0) + .Add(BLayoutBuilder::Group<>(B_HORIZONTAL, kElementSpacing) + .Add(fBackButton) + .Add(fForwardButton) + .Add(fStopButton) + .Add(fHomeButton) + .Add(fURLInputGroup) + .SetInsets(kInsetSpacing, kInsetSpacing, kInsetSpacing, + kInsetSpacing) + ) + .Add(new BSeparatorView(B_HORIZONTAL, B_PLAIN_BORDER)) + ; + + // Status bar group + BGroupLayout* statusGroup = BLayoutBuilder::Group<>(B_VERTICAL, 0.0) + .Add(new BSeparatorView(B_HORIZONTAL, B_PLAIN_BORDER)) + .Add(BLayoutBuilder::Group<>(B_HORIZONTAL, kElementSpacing) + .Add(fStatusText) + .Add(fLoadingProgressBar, 0.2) + .AddStrut(12 - kElementSpacing) + .SetInsets(kInsetSpacing, 0, kInsetSpacing, 0) + ) + ; + + BitmapButton* toggleFullscreenButton = new BitmapButton(kWindowIconBits, + kWindowIconWidth, kWindowIconHeight, kWindowIconFormat, + new BMessage(TOGGLE_FULLSCREEN)); + toggleFullscreenButton->SetBackgroundMode(BitmapButton::MENUBAR_BACKGROUND); + + BGroupLayout* menuBarGroup = BLayoutBuilder::Group<>(B_HORIZONTAL, 0.0) + .Add(mainMenu) + .Add(toggleFullscreenButton, 0.0f) + ; + + // Layout + AddChild(BLayoutBuilder::Group<>(B_VERTICAL, 0.0) +#if !INTEGRATE_MENU_INTO_TAB_BAR + .Add(menuBarGroup) +#endif + .Add(fTabManager->TabGroup()) + .Add(navigationGroup) + .Add(fTabManager->ContainerView()) + .Add(findGroup) + .Add(statusGroup) + ); + + // TODO: Small hack for fixing some invalidation problems with BMenuBar... + mainMenu->SetFlags(mainMenu->Flags() | B_FULL_UPDATE_ON_RESIZE); + mainMenu->SetViewColor(B_TRANSPARENT_COLOR); + + fURLInputGroup->MakeFocus(true); + + fMenuGroup = menuBarGroup; + fTabGroup = fTabManager->TabGroup()->GetLayout(); + fNavigationGroup = navigationGroup; + fFindGroup = findGroup; + fStatusGroup = statusGroup; + fToggleFullscreenButton = layoutItemFor(toggleFullscreenButton); + + fFindGroup->SetVisible(false); + fToggleFullscreenButton->SetVisible(false); + + CreateNewTab(url, true, webView); + _ShowInterface(true); + _SetAutoHideInterfaceInFullscreen(fAppSettings->GetValue( + kSettingsKeyAutoHideInterfaceInFullscreenMode, + fAutoHideInterfaceInFullscreenMode)); + + AddShortcut('F', B_COMMAND_KEY | B_SHIFT_KEY, + new BMessage(EDIT_HIDE_FIND_GROUP)); + // TODO: Should be a different shortcut, H is usually for Find selection. + AddShortcut('H', B_COMMAND_KEY, new BMessage(HOME)); + + // Add shortcuts to select a particular tab + for (int32 i = 1; i <= 9; i++) { + BMessage *selectTab = new BMessage(SELECT_TAB); + selectTab->AddInt32("tab index", i - 1); + char numStr[2]; + snprintf(numStr, sizeof(numStr), "%d", (int) i); + AddShortcut(numStr[0], B_COMMAND_KEY, selectTab); + } + + be_app->PostMessage(WINDOW_OPENED); +} + + +BrowserWindow::~BrowserWindow() +{ + fAppSettings->RemoveListener(BMessenger(this)); + delete fTabManager; + delete fPulseRunner; +} + + +void +BrowserWindow::DispatchMessage(BMessage* message, BHandler* target) +{ + const char* bytes; + uint32 modifierKeys; + if ((message->what == B_KEY_DOWN || message->what == B_UNMAPPED_KEY_DOWN) + && message->FindString("bytes", &bytes) == B_OK + && message->FindInt32("modifiers", (int32*)&modifierKeys) == B_OK) { + + modifierKeys = modifierKeys & 0x000000ff; + if (bytes[0] == B_LEFT_ARROW && modifierKeys == B_COMMAND_KEY) { + PostMessage(GO_BACK); + return; + } else if (bytes[0] == B_RIGHT_ARROW && modifierKeys == B_COMMAND_KEY) { + PostMessage(GO_FORWARD); + return; + } else if (bytes[0] == B_FUNCTION_KEY) { + // Some function key Firefox compatibility + int32 key; + if (message->FindInt32("key", &key) == B_OK) { + switch (key) { + case B_F5_KEY: + PostMessage(RELOAD); + break; + case B_F11_KEY: + PostMessage(TOGGLE_FULLSCREEN); + break; + default: + break; + } + } + } else if (target == fURLInputGroup->TextView()) { + // Handle B_RETURN in the URL text control. This is the easiest + // way to react *only* when the user presses the return key in the + // address bar, as opposed to trying to load whatever is in there + // when the text control just goes out of focus. + if (bytes[0] == B_RETURN) { + // Do it in such a way that the user sees the Go-button go down. + _InvokeButtonVisibly(fURLInputGroup->GoButton()); + return; + } + } else if (target == fFindTextControl->TextView()) { + // Handle B_RETURN when the find text control has focus. + if (bytes[0] == B_RETURN) { + if ((modifierKeys & B_SHIFT_KEY) != 0) + _InvokeButtonVisibly(fFindPreviousButton); + else + _InvokeButtonVisibly(fFindNextButton); + return; + } else if (bytes[0] == B_ESCAPE) { + _InvokeButtonVisibly(fFindCloseButton); + return; + } + } else if (bytes[0] == B_ESCAPE) { + // Default escape key behavior: + PostMessage(STOP); + return; + } + } + if (message->what == B_MOUSE_MOVED || message->what == B_MOUSE_DOWN + || message->what == B_MOUSE_UP) { + message->FindPoint("where", &fLastMousePos); + if (message->FindInt64("when", &fLastMouseMovedTime) != B_OK) + fLastMouseMovedTime = system_time(); + _CheckAutoHideInterface(); + } + if (message->what == B_MOUSE_WHEEL_CHANGED) { + BPoint where; + uint32 buttons; + CurrentWebView()->GetMouse(&where, &buttons, false); + // Only do this when the mouse is over the web view + if (CurrentWebView()->Bounds().Contains(where)) { + // Zoom and unzoom text on Command + mouse wheel. + // This could of course (and maybe should be) implemented in the WebView, but there + // would need to be a way for the WebView to know the setting of the + // fZoomTextOnly member here. Plus other clients of the API may not want + // this feature. + if ((modifiers() & B_COMMAND_KEY) != 0) { + float dy; + if (message->FindFloat("be:wheel_delta_y", &dy) == B_OK) { + if (dy < 0) + CurrentWebView()->IncreaseZoomFactor(fZoomTextOnly); + else + CurrentWebView()->DecreaseZoomFactor(fZoomTextOnly); + return; + } + } + } else // Also don't scroll up and down if the mouse is not over the web view + return; + } + BWebWindow::DispatchMessage(message, target); +} + + +void +BrowserWindow::MessageReceived(BMessage* message) +{ + switch (message->what) { + case OPEN_LOCATION: + _ShowInterface(true); + if (fURLInputGroup->TextView()->IsFocus()) + fURLInputGroup->TextView()->SelectAll(); + else + fURLInputGroup->MakeFocus(true); + break; + case RELOAD: + CurrentWebView()->Reload(); + break; + case GOTO_URL: + { + BString url; + if (message->FindString("url", &url) != B_OK) + url = fURLInputGroup->Text(); + _SetPageIcon(CurrentWebView(), NULL); + BString newUrl = _SmartURLHandler(url); + if (newUrl != url) + fURLInputGroup->TextView()->SetText(newUrl); + CurrentWebView()->LoadURL(newUrl.String()); + break; + } + case GO_BACK: + CurrentWebView()->GoBack(); + break; + case GO_FORWARD: + CurrentWebView()->GoForward(); + break; + case STOP: + CurrentWebView()->StopLoading(); + break; + case HOME: + CurrentWebView()->LoadURL(fStartPageURL); + break; + + case CLEAR_HISTORY: { + BrowsingHistory* history = BrowsingHistory::DefaultInstance(); + if (history->CountItems() == 0) + break; + BAlert* alert = new BAlert("Confirmation", "Do you really want to " + "clear the browsing history?", "Clear", "Cancel"); + if (alert->Go() == 0) + history->Clear(); + break; + } + + case CREATE_BOOKMARK: + _CreateBookmark(); + break; + case SHOW_BOOKMARKS: + _ShowBookmarks(); + break; + + case B_REFS_RECEIVED: + { + // Currently the only source of these messages is the bookmarks menu. + // Filter refs into URLs, this also gets rid of refs for folders. + // For clicks on sub-folders in the bookmarks menu, we have Tracker + // open the corresponding folder. + entry_ref ref; + uint32 addedCount = 0; + for (int32 i = 0; message->FindRef("refs", i, &ref) == B_OK; i++) { + BEntry entry(&ref); + uint32 addedSubCount = 0; + if (entry.IsDirectory()) { + BDirectory directory(&entry); + _AddBookmarkURLsRecursively(directory, message, + addedSubCount); + } else { + BFile file(&ref, B_READ_ONLY); + BString url; + if (_ReadURLAttr(file, url)) { + message->AddString("url", url.String()); + addedSubCount++; + } + } + if (addedSubCount == 0) { + // Don't know what to do with this entry, just pass it + // on to the system to handle. Note that this may result + // in us opening other supported files via the application + // mechanism. + be_roster->Launch(&ref); + } + addedCount += addedSubCount; + } + message->RemoveName("refs"); + if (addedCount > 10) { + BString string; + string << "Do you want to open " << addedCount; + string << " bookmarks all at once?"; + BAlert* alert = new BAlert("Open bookmarks confirmation", + string.String(), "Cancel", "Open all"); + if (alert->Go() == 0) + break; + } + be_app->PostMessage(message); + break; + } + case B_SIMPLE_DATA: + { + // User possibly dropped files on this window. + // If there is more than one entry_ref, let the app handle it + // (open one new page per ref). If there is one ref, open it in + // this window. + type_code type; + int32 countFound; + if (message->GetInfo("refs", &type, &countFound) != B_OK + || type != B_REF_TYPE) { + break; + } + if (countFound > 1) { + message->what = B_REFS_RECEIVED; + be_app->PostMessage(message); + break; + } + entry_ref ref; + if (message->FindRef("refs", &ref) != B_OK) + break; + BEntry entry(&ref, true); + BPath path; + if (!entry.Exists() || entry.GetPath(&path) != B_OK) + break; + CurrentWebView()->LoadURL(path.Path()); + break; + } + + case ZOOM_FACTOR_INCREASE: + CurrentWebView()->IncreaseZoomFactor(fZoomTextOnly); + break; + case ZOOM_FACTOR_DECREASE: + CurrentWebView()->DecreaseZoomFactor(fZoomTextOnly); + break; + case ZOOM_FACTOR_RESET: + CurrentWebView()->ResetZoomFactor(); + break; + case ZOOM_TEXT_ONLY: + fZoomTextOnly = !fZoomTextOnly; + fZoomTextOnlyMenuItem->SetMarked(fZoomTextOnly); + // TODO: Would be nice to have an instant update if the page is + // already zoomed. + break; + + case TOGGLE_FULLSCREEN: + ToggleFullscreen(); + break; + + case TOGGLE_AUTO_HIDE_INTERFACE_IN_FULLSCREEN: + _SetAutoHideInterfaceInFullscreen( + !fAutoHideInterfaceInFullscreenMode); + break; + + case CHECK_AUTO_HIDE_INTERFACE: + _CheckAutoHideInterface(); + break; + + case SHOW_PAGE_SOURCE: + CurrentWebView()->WebPage()->SendPageSource(); + break; + case B_PAGE_SOURCE_RESULT: + _HandlePageSourceResult(message); + break; + + case EDIT_FIND_NEXT: + CurrentWebView()->FindString(fFindTextControl->Text(), true, + fFindCaseSensitiveCheckBox->Value()); + break; + case FIND_TEXT_CHANGED: + { + bool findTextAvailable = strlen(fFindTextControl->Text()) > 0; + fFindPreviousMenuItem->SetEnabled(findTextAvailable); + fFindNextMenuItem->SetEnabled(findTextAvailable); + break; + } + case EDIT_FIND_PREVIOUS: + CurrentWebView()->FindString(fFindTextControl->Text(), false, + fFindCaseSensitiveCheckBox->Value()); + break; + case EDIT_SHOW_FIND_GROUP: + if (!fFindGroup->IsVisible()) + fFindGroup->SetVisible(true); + fFindTextControl->MakeFocus(true); + break; + case EDIT_HIDE_FIND_GROUP: + if (fFindGroup->IsVisible()) { + fFindGroup->SetVisible(false); + if (CurrentWebView() != NULL) + CurrentWebView()->MakeFocus(true); + } + break; + + case B_CUT: + case B_COPY: + case B_PASTE: + { + BTextView* textView = dynamic_cast(CurrentFocus()); + if (textView != NULL) + textView->MessageReceived(message); + else if (CurrentWebView() != NULL) + CurrentWebView()->MessageReceived(message); + break; + } + + case B_EDITING_CAPABILITIES_RESULT: + { + BWebView* webView; + if (message->FindPointer("view", + reinterpret_cast(&webView)) != B_OK + || webView != CurrentWebView()) { + break; + } + bool canCut; + bool canCopy; + bool canPaste; + if (message->FindBool("can cut", &canCut) != B_OK) + canCut = false; + if (message->FindBool("can copy", &canCopy) != B_OK) + canCopy = false; + if (message->FindBool("can paste", &canPaste) != B_OK) + canPaste = false; + fCutMenuItem->SetEnabled(canCut); + fCopyMenuItem->SetEnabled(canCopy); + fPasteMenuItem->SetEnabled(canPaste); + break; + } + + case SHOW_DOWNLOAD_WINDOW: + case SHOW_SETTINGS_WINDOW: + message->AddUInt32("workspaces", Workspaces()); + be_app->PostMessage(message); + break; + + case CLOSE_TAB: + if (fTabManager->CountTabs() > 1) { + int32 index; + if (message->FindInt32("tab index", &index) != B_OK) + index = fTabManager->SelectedTabIndex(); + _ShutdownTab(index); + _UpdateTabGroupVisibility(); + } else + PostMessage(B_QUIT_REQUESTED); + break; + + case SELECT_TAB: + { + int32 index; + if (message->FindInt32("tab index", &index) == B_OK + && fTabManager->SelectedTabIndex() != index + && fTabManager->CountTabs() > index) { + fTabManager->SelectTab(index); + } + + break; + } + + case TAB_CHANGED: + { + // This message may be received also when the last tab closed, + // i.e. with index == -1. + int32 index; + if (message->FindInt32("tab index", &index) != B_OK) + index = -1; + _TabChanged(index); + break; + } + + case SETTINGS_VALUE_CHANGED: + { + BString name; + if (message->FindString("name", &name) != B_OK) + break; + bool flag; + BString string; + uint32 value; + if (name == kSettingsKeyShowTabsIfSinglePageOpen + && message->FindBool("value", &flag) == B_OK) { + if (fShowTabsIfSinglePageOpen != flag) { + fShowTabsIfSinglePageOpen = flag; + _UpdateTabGroupVisibility(); + } + } else if (name == kSettingsKeyAutoHidePointer + && message->FindBool("value", &flag) == B_OK) { + fAutoHidePointer = flag; + if (CurrentWebView()) + CurrentWebView()->SetAutoHidePointer(fAutoHidePointer); + } else if (name == kSettingsKeyStartPageURL + && message->FindString("value", &string) == B_OK) { + fStartPageURL = string; + } else if (name == kSettingsKeySearchPageURL + && message->FindString("value", &string) == B_OK) { + fSearchPageURL = string; + } else if (name == kSettingsKeyNewWindowPolicy + && message->FindUInt32("value", &value) == B_OK) { + fNewWindowPolicy = value; + } else if (name == kSettingsKeyNewTabPolicy + && message->FindUInt32("value", &value) == B_OK) { + fNewTabPolicy = value; + } else if (name == kSettingsKeyAutoHideInterfaceInFullscreenMode + && message->FindBool("value", &flag) == B_OK) { + _SetAutoHideInterfaceInFullscreen(flag); + } else if (name == kSettingsKeyShowHomeButton + && message->FindBool("value", &flag) == B_OK) { + if (flag) + fHomeButton->Show(); + else + fHomeButton->Hide(); + } + break; + } + + default: + BWebWindow::MessageReceived(message); + break; + } +} + + +bool +BrowserWindow::QuitRequested() +{ + // TODO: Check for modified form data and ask user for confirmation, etc. + + // Iterate over all tabs to delete all BWebViews. + // Do this here, so WebKit tear down happens earlier. + SetCurrentWebView(NULL); + while (fTabManager->CountTabs() > 0) + _ShutdownTab(0); + + BMessage message(WINDOW_CLOSED); + message.AddRect("window frame", WindowFrame()); + be_app->PostMessage(&message); + return true; +} + + +void +BrowserWindow::MenusBeginning() +{ + _UpdateHistoryMenu(); + _UpdateClipboardItems(); + _ShowInterface(true); +} + + +void +BrowserWindow::Zoom(BPoint origin, float width, float height) +{ + ToggleFullscreen(); +} + + +void +BrowserWindow::ScreenChanged(BRect screenSize, color_space format) +{ + if (fIsFullscreen) + _ResizeToScreen(); +} + + +void +BrowserWindow::WorkspacesChanged(uint32 oldWorkspaces, uint32 newWorkspaces) +{ + if (fIsFullscreen) + _ResizeToScreen(); +} + + +static bool +viewIsChild(const BView* parent, const BView* view) +{ + if (parent == view) + return true; + + int32 count = parent->CountChildren(); + for (int32 i = 0; i < count; i++) { + BView* child = parent->ChildAt(i); + if (viewIsChild(child, view)) + return true; + } + return false; +} + + +void +BrowserWindow::SetCurrentWebView(BWebView* webView) +{ + if (webView == CurrentWebView()) + return; + + if (CurrentWebView() != NULL) { + // Remember the currently focused view before switching tabs, + // so that we can revert the focus when switching back to this tab + // later. + PageUserData* userData = static_cast( + CurrentWebView()->GetUserData()); + if (userData == NULL) { + userData = new PageUserData(CurrentFocus()); + CurrentWebView()->SetUserData(userData); + } + userData->SetFocusedView(CurrentFocus()); + userData->SetURLInputContents(fURLInputGroup->Text()); + int32 selectionStart; + int32 selectionEnd; + fURLInputGroup->TextView()->GetSelection(&selectionStart, + &selectionEnd); + userData->SetURLInputSelection(selectionStart, selectionEnd); + } + + BWebWindow::SetCurrentWebView(webView); + + if (webView != NULL) { + webView->SetAutoHidePointer(fAutoHidePointer); + + _UpdateTitle(webView->MainFrameTitle()); + + // Restore the previous focus or focus the web view. + PageUserData* userData = static_cast( + webView->GetUserData()); + BView* focusedView = NULL; + if (userData != NULL) + focusedView = userData->FocusedView(); + + if (focusedView != NULL + && viewIsChild(GetLayout()->View(), focusedView)) { + focusedView->MakeFocus(true); + } else + webView->MakeFocus(true); + + if (userData != NULL) { + fURLInputGroup->SetPageIcon(userData->PageIcon()); + if (userData->URLInputContents().Length()) + fURLInputGroup->SetText(userData->URLInputContents()); + else + fURLInputGroup->SetText(webView->MainFrameURL()); + if (userData->URLInputSelectionStart() >= 0) { + fURLInputGroup->TextView()->Select( + userData->URLInputSelectionStart(), + userData->URLInputSelectionEnd()); + } + } else { + fURLInputGroup->SetPageIcon(NULL); + fURLInputGroup->SetText(webView->MainFrameURL()); + } + + // Trigger update of the interface to the new page, by requesting + // to resend all notifications. + webView->WebPage()->ResendNotifications(); + } else + _UpdateTitle(""); +} + + +bool +BrowserWindow::IsBlankTab() const +{ + if (CurrentWebView() == NULL) + return false; + BString requestedURL = CurrentWebView()->MainFrameRequestedURL(); + return requestedURL.Length() == 0 + || requestedURL == _NewTabURL(fTabManager->CountTabs() == 1); +} + + +void +BrowserWindow::CreateNewTab(const BString& _url, bool select, BWebView* webView) +{ + bool applyNewPagePolicy = webView == NULL; + // Executed in app thread (new BWebPage needs to be created in app thread). + if (webView == NULL) + webView = new BWebView("web view"); + + bool isNewWindow = fTabManager->CountTabs() == 0; + + fTabManager->AddTab(webView, "New tab"); + + BString url(_url); + if (applyNewPagePolicy && url.Length() == 0) + url = _NewTabURL(isNewWindow); + + if (url.Length() > 0) + webView->LoadURL(url.String()); + + if (select) { + fTabManager->SelectTab(fTabManager->CountTabs() - 1); + SetCurrentWebView(webView); + webView->WebPage()->ResendNotifications(); + fURLInputGroup->SetPageIcon(NULL); + fURLInputGroup->SetText(url.String()); + fURLInputGroup->MakeFocus(true); + } + + _ShowInterface(true); + _UpdateTabGroupVisibility(); +} + + +BRect +BrowserWindow::WindowFrame() const +{ + if (fIsFullscreen) + return fNonFullscreenWindowFrame; + else + return Frame(); +} + + +void +BrowserWindow::ToggleFullscreen() +{ + if (fIsFullscreen) { + MoveTo(fNonFullscreenWindowFrame.LeftTop()); + ResizeTo(fNonFullscreenWindowFrame.Width(), + fNonFullscreenWindowFrame.Height()); + + SetFlags(Flags() & ~(B_NOT_RESIZABLE | B_NOT_MOVABLE)); + SetLook(B_DOCUMENT_WINDOW_LOOK); + + _ShowInterface(true); + } else { + fNonFullscreenWindowFrame = Frame(); + _ResizeToScreen(); + + SetFlags(Flags() | (B_NOT_RESIZABLE | B_NOT_MOVABLE)); + SetLook(B_TITLED_WINDOW_LOOK); + } + fIsFullscreen = !fIsFullscreen; + fFullscreenItem->SetMarked(fIsFullscreen); + fToggleFullscreenButton->SetVisible(fIsFullscreen); +} + + +// #pragma mark - Notification API + + +void +BrowserWindow::NavigationRequested(const BString& url, BWebView* view) +{ +} + + +void +BrowserWindow::NewWindowRequested(const BString& url, bool primaryAction) +{ + // Always open new windows in the application thread, since + // creating a BWebView will try to grab the application lock. + // But our own WebPage may already try to lock us from within + // the application thread -> dead-lock. Thus we can't wait for + // a reply here. + BMessage message(NEW_TAB); + message.AddPointer("window", this); + message.AddString("url", url); + message.AddBool("select", primaryAction); + be_app->PostMessage(&message); +} + + +void +BrowserWindow::NewPageCreated(BWebView* view, BRect windowFrame, + bool modalDialog, bool resizable, bool activate) +{ + if (windowFrame.IsValid()) { + BrowserWindow* window = new BrowserWindow(windowFrame, fAppSettings, + BString(), INTERFACE_ELEMENT_STATUS, view); + window->Show(); + } else + CreateNewTab(BString(), activate, view); +} + + +void +BrowserWindow::CloseWindowRequested(BWebView* view) +{ + int32 index = fTabManager->TabForView(view); + if (index < 0) { + // Tab is already gone. + return; + } + BMessage message(CLOSE_TAB); + message.AddInt32("tab index", index); + PostMessage(&message, this); +} + + +void +BrowserWindow::LoadNegotiating(const BString& url, BWebView* view) +{ + if (view != CurrentWebView()) + return; + + fURLInputGroup->SetText(url.String()); + + BString status("Requesting: "); + status << url; + view->WebPage()->SetStatusMessage(status); +} + + +void +BrowserWindow::LoadCommitted(const BString& url, BWebView* view) +{ + if (view != CurrentWebView()) + return; + + // This hook is invoked when the load is commited. + fURLInputGroup->SetText(url.String()); + + BString status("Loading: "); + status << url; + view->WebPage()->SetStatusMessage(status); +} + + +void +BrowserWindow::LoadProgress(float progress, BWebView* view) +{ + if (view != CurrentWebView()) + return; + + if (progress < 100 && fLoadingProgressBar->IsHidden()) + _ShowProgressBar(true); + else if (progress == 100 && !fLoadingProgressBar->IsHidden()) + _ShowProgressBar(false); + fLoadingProgressBar->SetTo(progress); +} + + +void +BrowserWindow::LoadFailed(const BString& url, BWebView* view) +{ + if (view != CurrentWebView()) + return; + + BString status(url); + status << " failed."; + view->WebPage()->SetStatusMessage(status); + if (!fLoadingProgressBar->IsHidden()) + fLoadingProgressBar->Hide(); +} + + +void +BrowserWindow::LoadFinished(const BString& url, BWebView* view) +{ + if (view != CurrentWebView()) + return; + + BString status(url); + status << " finished."; + view->WebPage()->SetStatusMessage(status); + if (!fLoadingProgressBar->IsHidden()) + fLoadingProgressBar->Hide(); + + NavigationCapabilitiesChanged(fBackButton->IsEnabled(), + fForwardButton->IsEnabled(), false, view); + + int32 tabIndex = fTabManager->TabForView(view); + if (tabIndex > 0 && strcmp("New tab", fTabManager->TabLabel(tabIndex)) == 0) + fTabManager->SetTabLabel(tabIndex, url); +} + + +void +BrowserWindow::MainDocumentError(const BString& failingURL, + const BString& localizedDescription, BWebView* view) +{ + // Make sure we show the page that contains the view. + if (!_ShowPage(view)) + return; + + BWebWindow::MainDocumentError(failingURL, localizedDescription, view); + + // TODO: Remove the failing URL from the BrowsingHistory! +} + + +void +BrowserWindow::TitleChanged(const BString& title, BWebView* view) +{ + int32 tabIndex = fTabManager->TabForView(view); + if (tabIndex < 0) + return; + + fTabManager->SetTabLabel(tabIndex, title); + + if (view != CurrentWebView()) + return; + + _UpdateTitle(title); +} + + +void +BrowserWindow::IconReceived(const BBitmap* icon, BWebView* view) +{ + // The view may already be gone, since this notification arrives + // asynchronously. + if (!fTabManager->HasView(view)) + return; + + _SetPageIcon(view, icon); +} + + +void +BrowserWindow::ResizeRequested(float width, float height, BWebView* view) +{ + if (view != CurrentWebView()) + return; + + // Ignore request when there is more than one BWebView embedded. + if (fTabManager->CountTabs() > 1) + return; + + // Make sure the new frame is not larger than the screen frame minus + // window decorator border. + BScreen screen(this); + BRect screenFrame = screen.Frame(); + BRect decoratorFrame = DecoratorFrame(); + BRect frame = Frame(); + + screenFrame.left += decoratorFrame.left - frame.left; + screenFrame.right += decoratorFrame.right - frame.right; + screenFrame.top += decoratorFrame.top - frame.top; + screenFrame.bottom += decoratorFrame.bottom - frame.bottom; + + width = min_c(width, screen.Frame().Width()); + height = min_c(height, screen.Frame().Height()); + + frame.right = frame.left + width; + frame.bottom = frame.top + height; + + // frame is now not larger than screenFrame, but may still be partly outside + if (!screenFrame.Contains(frame)) { + if (frame.left < screenFrame.left) + frame.OffsetBy(screenFrame.left - frame.left, 0); + else if (frame.right > screenFrame.right) + frame.OffsetBy(screenFrame.right - frame.right, 0); + if (frame.top < screenFrame.top) + frame.OffsetBy(screenFrame.top - frame.top, 0); + else if (frame.bottom > screenFrame.bottom) + frame.OffsetBy(screenFrame.bottom - frame.bottom, 0); + } + + MoveTo(frame.left, frame.top); + ResizeTo(width, height); +} + + +void +BrowserWindow::SetToolBarsVisible(bool flag, BWebView* view) +{ + // TODO + // TODO: Ignore request when there is more than one BWebView embedded! +} + + +void +BrowserWindow::SetStatusBarVisible(bool flag, BWebView* view) +{ + // TODO + // TODO: Ignore request when there is more than one BWebView embedded! +} + + +void +BrowserWindow::SetMenuBarVisible(bool flag, BWebView* view) +{ + // TODO + // TODO: Ignore request when there is more than one BWebView embedded! +} + + +void +BrowserWindow::SetResizable(bool flag, BWebView* view) +{ + // TODO: Ignore request when there is more than one BWebView embedded! + + if (flag) + SetFlags(Flags() & ~B_NOT_RESIZABLE); + else + SetFlags(Flags() | B_NOT_RESIZABLE); +} + + +void +BrowserWindow::StatusChanged(const BString& statusText, BWebView* view) +{ + if (view != CurrentWebView()) + return; + + if (fStatusText) + fStatusText->SetText(statusText.String()); +} + + +void +BrowserWindow::NavigationCapabilitiesChanged(bool canGoBackward, + bool canGoForward, bool canStop, BWebView* view) +{ + if (view != CurrentWebView()) + return; + + fBackButton->SetEnabled(canGoBackward); + fForwardButton->SetEnabled(canGoForward); + fStopButton->SetEnabled(canStop); + + fBackMenuItem->SetEnabled(canGoBackward); + fForwardMenuItem->SetEnabled(canGoForward); +} + + +void +BrowserWindow::UpdateGlobalHistory(const BString& url) +{ + BrowsingHistory::DefaultInstance()->AddItem(BrowsingHistoryItem(url)); +} + + +bool +BrowserWindow::AuthenticationChallenge(BString message, BString& inOutUser, + BString& inOutPassword, bool& inOutRememberCredentials, uint32 failureCount, + BWebView* view) +{ + CredentialsStorage* persistentStorage + = CredentialsStorage::PersistentInstance(); + CredentialsStorage* sessionStorage + = CredentialsStorage::SessionInstance(); + + // TODO: Using the message as key here is not so smart. + HashKeyString key(message); + + if (failureCount == 0) { + if (persistentStorage->Contains(key)) { + Credentials credentials = persistentStorage->GetCredentials(key); + inOutUser = credentials.Username(); + inOutPassword = credentials.Password(); + return true; + } else if (sessionStorage->Contains(key)) { + Credentials credentials = sessionStorage->GetCredentials(key); + inOutUser = credentials.Username(); + inOutPassword = credentials.Password(); + return true; + } + } + // Switch to the page for which this authentication is required. + if (!_ShowPage(view)) + return false; + + AuthenticationPanel* panel = new AuthenticationPanel(Frame()); + // Panel auto-destructs. + bool success = panel->getAuthentication(message, inOutUser, inOutPassword, + inOutRememberCredentials, failureCount > 0, inOutUser, inOutPassword, + &inOutRememberCredentials); + if (success) { + Credentials credentials(inOutUser, inOutPassword); + if (inOutRememberCredentials) + persistentStorage->PutCredentials(key, credentials); + else + sessionStorage->PutCredentials(key, credentials); + } + return success; +} + + +// #pragma mark - private + + +void +BrowserWindow::_UpdateTitle(const BString& title) +{ + BString windowTitle = title; + if (windowTitle.Length() > 0) + windowTitle << " - "; + windowTitle << kApplicationName; + SetTitle(windowTitle.String()); +} + + +void +BrowserWindow::_UpdateTabGroupVisibility() +{ + if (Lock()) { + if (fInterfaceVisible) + fTabGroup->SetVisible(_TabGroupShouldBeVisible()); + fTabManager->SetCloseButtonsAvailable(fTabManager->CountTabs() > 1); + Unlock(); + } +} + + +bool +BrowserWindow::_TabGroupShouldBeVisible() const +{ + return (fShowTabsIfSinglePageOpen || fTabManager->CountTabs() > 1) + && (fVisibleInterfaceElements & INTERFACE_ELEMENT_TABS) != 0; +} + + +void +BrowserWindow::_ShutdownTab(int32 index) +{ + BView* view = fTabManager->RemoveTab(index); + BWebView* webView = dynamic_cast(view); + if (webView == CurrentWebView()) + SetCurrentWebView(NULL); + if (webView != NULL) + webView->Shutdown(); + else + delete view; +} + + +void +BrowserWindow::_TabChanged(int32 index) +{ + SetCurrentWebView(dynamic_cast(fTabManager->ViewForTab(index))); +} + + +status_t +BrowserWindow::_BookmarkPath(BPath& path) const +{ + status_t ret = find_directory(B_USER_SETTINGS_DIRECTORY, &path); + if (ret != B_OK) + return ret; + + ret = path.Append(kApplicationName); + if (ret != B_OK) + return ret; + + ret = path.Append("Bookmarks"); + if (ret != B_OK) + return ret; + + return create_directory(path.Path(), 0777); +} + + +void +BrowserWindow::_CreateBookmark() +{ + BPath path; + status_t status = _BookmarkPath(path); + if (status != B_OK) { + BString message("There was an error retrieving the bookmark " + "folder.\n\n"); + message << "Error: " << strerror(status); + BAlert* alert = new BAlert("Bookmark error", message.String(), "OK", + NULL, NULL, B_WIDTH_AS_USUAL, B_STOP_ALERT); + alert->Go(); + return; + } + BWebView* webView = CurrentWebView(); + BString url(webView->MainFrameURL()); + // Create a bookmark file + BFile bookmarkFile; + BString bookmarkName(webView->MainFrameTitle()); + if (bookmarkName.Length() == 0) { + bookmarkName = url; + int32 leafPos = bookmarkName.FindLast('/'); + if (leafPos >= 0) + bookmarkName.Remove(0, leafPos + 1); + } + // Make sure the bookmark title does not contain chars that are not + // allowed in file names. + bookmarkName.ReplaceAll('/', '-'); + + // Check that the bookmark exists nowhere in the bookmark hierarchy, + // though the intended file name must match, we don't search the stored + // URLs, only for matching file names. + BDirectory directory(path.Path()); + if (status == B_OK && _CheckBookmarkExists(directory, bookmarkName, url)) { + BString message("A bookmark for this page ("); + message << bookmarkName; + message << ") already exists."; + BAlert* alert = new BAlert("Bookmark info", message.String(), "OK"); + alert->Go(); + return; + } + + BPath entryPath(path); + status = entryPath.Append(bookmarkName); + BEntry entry; + if (status == B_OK) + status = entry.SetTo(entryPath.Path(), true); + if (status == B_OK) { + int32 tries = 1; + while (entry.Exists()) { + // Find a unique name for the bookmark, there may still be a + // file in the way that stores a different URL. + bookmarkName = webView->MainFrameTitle(); + bookmarkName << " " << tries++; + entryPath = path; + status = entryPath.Append(bookmarkName); + if (status == B_OK) + status = entry.SetTo(entryPath.Path(), true); + if (status != B_OK) + break; + } + } + if (status == B_OK) { + status = bookmarkFile.SetTo(&entry, + B_CREATE_FILE | B_ERASE_FILE | B_WRITE_ONLY); + } + + // Write bookmark meta data + if (status == B_OK) + status = bookmarkFile.WriteAttrString("META:url", &url); + if (status == B_OK) { + BString title = webView->MainFrameTitle(); + bookmarkFile.WriteAttrString("META:title", &title); + } + + BNodeInfo nodeInfo(&bookmarkFile); + if (status == B_OK) { + status = nodeInfo.SetType("application/x-vnd.Be-bookmark"); + if (status == B_OK) { + PageUserData* userData = static_cast( + webView->GetUserData()); + if (userData != NULL && userData->PageIcon() != NULL) { + BBitmap miniIcon(BRect(0, 0, 15, 15), B_BITMAP_NO_SERVER_LINK, + B_CMAP8); + status_t ret = miniIcon.ImportBits(userData->PageIcon()); + if (ret == B_OK) + ret = nodeInfo.SetIcon(&miniIcon, B_MINI_ICON); + if (ret != B_OK) { + fprintf(stderr, "Failed to store mini icon for bookmark: " + "%s\n", strerror(ret)); + } + BBitmap largeIcon(BRect(0, 0, 31, 31), B_BITMAP_NO_SERVER_LINK, + B_CMAP8); + // TODO: Store 32x32 favicon which is often provided by sites. + const uint8* src = (const uint8*)miniIcon.Bits(); + uint32 srcBPR = miniIcon.BytesPerRow(); + uint8* dst = (uint8*)largeIcon.Bits(); + uint32 dstBPR = largeIcon.BytesPerRow(); + for (uint32 y = 0; y < 16; y++) { + const uint8* s = src; + uint8* d = dst; + for (uint32 x = 0; x < 16; x++) { + *d++ = *s; + *d++ = *s++; + } + dst += dstBPR; + s = src; + for (uint32 x = 0; x < 16; x++) { + *d++ = *s; + *d++ = *s++; + } + dst += dstBPR; + src += srcBPR; + } + if (ret == B_OK) + ret = nodeInfo.SetIcon(&largeIcon, B_LARGE_ICON); + if (ret != B_OK) { + fprintf(stderr, "Failed to store large icon for bookmark: " + "%s\n", strerror(ret)); + } + } + } + } + + if (status != B_OK) { + BString message("There was an error creating the bookmark " + "file.\n\n"); + message << "Error: " << strerror(status); + BAlert* alert = new BAlert("Bookmark error", message.String(), "OK", + NULL, NULL, B_WIDTH_AS_USUAL, B_STOP_ALERT); + alert->Go(); + return; + } +} + + +void +BrowserWindow::_ShowBookmarks() +{ + BPath path; + entry_ref ref; + status_t status = _BookmarkPath(path); + if (status == B_OK) + status = get_ref_for_path(path.Path(), &ref); + if (status == B_OK) + status = be_roster->Launch(&ref); + + if (status != B_OK && status != B_ALREADY_RUNNING) { + BString message("There was an error trying to show the Bookmarks " + "folder.\n\n"); + message << "Error: " << strerror(status); + BAlert* alert = new BAlert("Bookmark error", message.String(), "OK", + NULL, NULL, B_WIDTH_AS_USUAL, B_STOP_ALERT); + alert->Go(); + return; + } +} + + +bool BrowserWindow::_CheckBookmarkExists(BDirectory& directory, + const BString& bookmarkName, const BString& url) const +{ + BEntry entry; + while (directory.GetNextEntry(&entry) == B_OK) { + if (entry.IsDirectory()) { + BDirectory subBirectory(&entry); + // At least preserve the entry file handle when recursing into + // sub-folders... eventually we will run out, though, with very + // deep hierarchy. + entry.Unset(); + if (_CheckBookmarkExists(subBirectory, bookmarkName, url)) + return true; + } else { + char entryName[B_FILE_NAME_LENGTH]; + if (entry.GetName(entryName) != B_OK || bookmarkName != entryName) + continue; + BString storedURL; + BFile file(&entry, B_READ_ONLY); + if (_ReadURLAttr(file, storedURL)) { + // Just bail if the bookmark already exists + if (storedURL == url) + return true; + } + } + } + return false; +} + + +bool +BrowserWindow::_ReadURLAttr(BFile& bookmarkFile, BString& url) const +{ + return bookmarkFile.InitCheck() == B_OK + && bookmarkFile.ReadAttrString("META:url", &url) == B_OK; +} + + +void +BrowserWindow::_AddBookmarkURLsRecursively(BDirectory& directory, + BMessage* message, uint32& addedCount) const +{ + BEntry entry; + while (directory.GetNextEntry(&entry) == B_OK) { + if (entry.IsDirectory()) { + BDirectory subBirectory(&entry); + // At least preserve the entry file handle when recursing into + // sub-folders... eventually we will run out, though, with very + // deep hierarchy. + entry.Unset(); + _AddBookmarkURLsRecursively(subBirectory, message, addedCount); + } else { + BString storedURL; + BFile file(&entry, B_READ_ONLY); + if (_ReadURLAttr(file, storedURL)) { + message->AddString("url", storedURL.String()); + addedCount++; + } + } + } +} + + +void +BrowserWindow::_SetPageIcon(BWebView* view, const BBitmap* icon) +{ + PageUserData* userData = static_cast(view->GetUserData()); + if (userData == NULL) { + userData = new(std::nothrow) PageUserData(NULL); + if (userData == NULL) + return; + view->SetUserData(userData); + } + // The PageUserData makes a copy of the icon, which we pass on to + // the TabManager for display in the respective tab. + userData->SetPageIcon(icon); + fTabManager->SetTabIcon(view, userData->PageIcon()); + if (view == CurrentWebView()) + fURLInputGroup->SetPageIcon(icon); +} + + +static void +addItemToMenuOrSubmenu(BMenu* menu, BMenuItem* newItem) +{ + BString baseURLLabel = baseURL(BString(newItem->Label())); + for (int32 i = menu->CountItems() - 1; i >= 0; i--) { + BMenuItem* item = menu->ItemAt(i); + BString label = item->Label(); + if (label.FindFirst(baseURLLabel) >= 0) { + if (item->Submenu()) { + // Submenu was already added in previous iteration. + item->Submenu()->AddItem(newItem); + return; + } else { + menu->RemoveItem(item); + BMenu* subMenu = new BMenu(baseURLLabel.String()); + subMenu->AddItem(item); + subMenu->AddItem(newItem); + // Add common submenu for this base URL, clickable. + BMessage* message = new BMessage(GOTO_URL); + message->AddString("url", baseURLLabel.String()); + menu->AddItem(new BMenuItem(subMenu, message), i); + return; + } + } + } + menu->AddItem(newItem); +} + + +static void +addOrDeleteMenu(BMenu* menu, BMenu* toMenu) +{ + if (menu->CountItems() > 0) + toMenu->AddItem(menu); + else + delete menu; +} + + +void +BrowserWindow::_UpdateHistoryMenu() +{ + BMenuItem* menuItem; + while ((menuItem = fHistoryMenu->RemoveItem(fHistoryMenuFixedItemCount))) + delete menuItem; + + BrowsingHistory* history = BrowsingHistory::DefaultInstance(); + if (!history->Lock()) + return; + + int32 count = history->CountItems(); + BMenuItem* clearHistoryItem = new BMenuItem("Clear history", + new BMessage(CLEAR_HISTORY)); + clearHistoryItem->SetEnabled(count > 0); + fHistoryMenu->AddItem(clearHistoryItem); + if (count == 0) { + history->Unlock(); + return; + } + fHistoryMenu->AddSeparatorItem(); + + BDateTime todayStart = BDateTime::CurrentDateTime(B_LOCAL_TIME); + todayStart.SetTime(BTime(0, 0, 0)); + + BDateTime oneDayAgoStart = todayStart; + oneDayAgoStart.Date().AddDays(-1); + + BDateTime twoDaysAgoStart = oneDayAgoStart; + twoDaysAgoStart.Date().AddDays(-1); + + BDateTime threeDaysAgoStart = twoDaysAgoStart; + threeDaysAgoStart.Date().AddDays(-1); + + BDateTime fourDaysAgoStart = threeDaysAgoStart; + fourDaysAgoStart.Date().AddDays(-1); + + BDateTime fiveDaysAgoStart = fourDaysAgoStart; + fiveDaysAgoStart.Date().AddDays(-1); + + BMenu* todayMenu = new BMenu("Today"); + BMenu* yesterdayMenu = new BMenu("Yesterday"); + BMenu* twoDaysAgoMenu = new BMenu( + twoDaysAgoStart.Date().LongDayName().String()); + BMenu* threeDaysAgoMenu = new BMenu( + threeDaysAgoStart.Date().LongDayName().String()); + BMenu* fourDaysAgoMenu = new BMenu( + fourDaysAgoStart.Date().LongDayName().String()); + BMenu* fiveDaysAgoMenu = new BMenu( + fiveDaysAgoStart.Date().LongDayName().String()); + BMenu* earlierMenu = new BMenu("Earlier"); + + for (int32 i = 0; i < count; i++) { + BrowsingHistoryItem historyItem = history->HistoryItemAt(i); + BMessage* message = new BMessage(GOTO_URL); + message->AddString("url", historyItem.URL().String()); + + BString truncatedUrl(historyItem.URL()); + be_plain_font->TruncateString(&truncatedUrl, B_TRUNCATE_END, 480); + menuItem = new BMenuItem(truncatedUrl, message); + + if (historyItem.DateTime() < fiveDaysAgoStart) + addItemToMenuOrSubmenu(earlierMenu, menuItem); + else if (historyItem.DateTime() < fourDaysAgoStart) + addItemToMenuOrSubmenu(fiveDaysAgoMenu, menuItem); + else if (historyItem.DateTime() < threeDaysAgoStart) + addItemToMenuOrSubmenu(fourDaysAgoMenu, menuItem); + else if (historyItem.DateTime() < twoDaysAgoStart) + addItemToMenuOrSubmenu(threeDaysAgoMenu, menuItem); + else if (historyItem.DateTime() < oneDayAgoStart) + addItemToMenuOrSubmenu(twoDaysAgoMenu, menuItem); + else if (historyItem.DateTime() < todayStart) + addItemToMenuOrSubmenu(yesterdayMenu, menuItem); + else + addItemToMenuOrSubmenu(todayMenu, menuItem); + } + history->Unlock(); + + addOrDeleteMenu(todayMenu, fHistoryMenu); + addOrDeleteMenu(yesterdayMenu, fHistoryMenu); + addOrDeleteMenu(twoDaysAgoMenu, fHistoryMenu); + addOrDeleteMenu(fourDaysAgoMenu, fHistoryMenu); + addOrDeleteMenu(fiveDaysAgoMenu, fHistoryMenu); + addOrDeleteMenu(earlierMenu, fHistoryMenu); +} + + +void +BrowserWindow::_UpdateClipboardItems() +{ + BTextView* focusTextView = dynamic_cast(CurrentFocus()); + if (focusTextView != NULL) { + int32 selectionStart; + int32 selectionEnd; + focusTextView->GetSelection(&selectionStart, &selectionEnd); + bool hasSelection = selectionStart < selectionEnd; + bool canPaste = false; + // A BTextView has the focus. + if (be_clipboard->Lock()) { + BMessage* data = be_clipboard->Data(); + if (data != NULL) + canPaste = data->HasData("text/plain", B_MIME_TYPE); + be_clipboard->Unlock(); + } + fCutMenuItem->SetEnabled(hasSelection); + fCopyMenuItem->SetEnabled(hasSelection); + fPasteMenuItem->SetEnabled(canPaste); + } else if (CurrentWebView() != NULL) { + // Trigger update of the clipboard items, even if the + // BWebView doesn't have focus, we'll dispatch these message + // there anyway. This works so fast that the user can never see + // the wrong enabled state when the menu opens until the result + // message arrives. The initial state needs to be enabled, since + // standard shortcut handling is always wrapped inside MenusBeginning() + // and MenusEnded(), and since we update items asynchronously, we need + // to have them enabled to begin with. + fCutMenuItem->SetEnabled(true); + fCopyMenuItem->SetEnabled(true); + fPasteMenuItem->SetEnabled(true); + + CurrentWebView()->WebPage()->SendEditingCapabilities(); + } +} + + +bool +BrowserWindow::_ShowPage(BWebView* view) +{ + if (view != CurrentWebView()) { + int32 tabIndex = fTabManager->TabForView(view); + if (tabIndex < 0) { + // Page seems to be gone already? + return false; + } + fTabManager->SelectTab(tabIndex); + _TabChanged(tabIndex); + UpdateIfNeeded(); + } + return true; +} + + +void +BrowserWindow::_ResizeToScreen() +{ + BScreen screen(this); + MoveTo(0, 0); + ResizeTo(screen.Frame().Width(), screen.Frame().Height()); +} + + +void +BrowserWindow::_SetAutoHideInterfaceInFullscreen(bool doIt) +{ + if (fAutoHideInterfaceInFullscreenMode == doIt) + return; + + fAutoHideInterfaceInFullscreenMode = doIt; + if (fAppSettings->GetValue(kSettingsKeyAutoHideInterfaceInFullscreenMode, + doIt) != doIt) { + fAppSettings->SetValue(kSettingsKeyAutoHideInterfaceInFullscreenMode, + doIt); + } + + if (fAutoHideInterfaceInFullscreenMode) { + BMessage message(CHECK_AUTO_HIDE_INTERFACE); + fPulseRunner = new BMessageRunner(BMessenger(this), &message, 300000); + } else { + delete fPulseRunner; + fPulseRunner = NULL; + _ShowInterface(true); + } +} + + +void +BrowserWindow::_CheckAutoHideInterface() +{ + if (!fIsFullscreen || !fAutoHideInterfaceInFullscreenMode + || (CurrentWebView() != NULL && !CurrentWebView()->IsFocus())) { + return; + } + + if (fLastMousePos.y == 0) + _ShowInterface(true); + else if (fNavigationGroup->IsVisible() + && fLastMousePos.y > fNavigationGroup->Frame().bottom + && system_time() - fLastMouseMovedTime > 1000000) { + // NOTE: Do not re-use navigationGroupBottom in the above + // check, since we only want to hide the interface when it is visible. + _ShowInterface(false); + } +} + + +void +BrowserWindow::_ShowInterface(bool show) +{ + if (fInterfaceVisible == show) + return; + + fInterfaceVisible = show; + + if (show) { +#if !INTEGRATE_MENU_INTO_TAB_BAR + fMenuGroup->SetVisible( + (fVisibleInterfaceElements & INTERFACE_ELEMENT_MENU) != 0); +#endif + fTabGroup->SetVisible(_TabGroupShouldBeVisible()); + fNavigationGroup->SetVisible( + (fVisibleInterfaceElements & INTERFACE_ELEMENT_NAVIGATION) != 0); + fStatusGroup->SetVisible( + (fVisibleInterfaceElements & INTERFACE_ELEMENT_STATUS) != 0); + } else { + fMenuGroup->SetVisible(false); + fTabGroup->SetVisible(false); + fNavigationGroup->SetVisible(false); + fStatusGroup->SetVisible(false); + } + // TODO: Setting the group visible seems to unhide the status bar. + // Fix in Haiku? + while (!fLoadingProgressBar->IsHidden()) + fLoadingProgressBar->Hide(); +} + + +void +BrowserWindow::_ShowProgressBar(bool show) +{ + if (show) { + if (!fStatusGroup->IsVisible() && (fVisibleInterfaceElements & INTERFACE_ELEMENT_STATUS) != 0) + fStatusGroup->SetVisible(true); + fLoadingProgressBar->Show(); + } else { + if (!fInterfaceVisible) + fStatusGroup->SetVisible(false); + // TODO: This is also used in _ShowInterface. Without it the status bar + // doesn't always hide again. It may be an Interface Kit bug. + while (!fLoadingProgressBar->IsHidden()) + fLoadingProgressBar->Hide(); + } +} + + +void +BrowserWindow::_InvokeButtonVisibly(BButton* button) +{ + button->SetValue(B_CONTROL_ON); + UpdateIfNeeded(); + button->Invoke(); + snooze(1000); + button->SetValue(B_CONTROL_OFF); +} + + +BString +BrowserWindow::_NewTabURL(bool isNewWindow) const +{ + BString url; + uint32 policy = isNewWindow ? fNewWindowPolicy : fNewTabPolicy; + // Implement new page policy + switch (policy) { + case OpenStartPage: + url = fStartPageURL; + break; + case OpenSearchPage: + url = fSearchPageURL; + break; + case CloneCurrentPage: + if (CurrentWebView() != NULL) + url = CurrentWebView()->MainFrameURL(); + break; + case OpenBlankPage: + default: + break; + } + return url; +} + + +BString +BrowserWindow::_SmartURLHandler(const BString& url) const +{ + BString result = url; + + // Only process if this doesn't look like a full URL (http:// or + // file://, etc.) + if (url.FindFirst("://") == B_ERROR) { + if (url.FindFirst(".") == B_ERROR || url.FindFirst(" ") != B_ERROR) + result.Prepend("http://www.google.com/search?q="); + } + return result; +} + + +void +BrowserWindow::_HandlePageSourceResult(const BMessage* message) +{ + // TODO: This should be done in an extra thread perhaps. Doing it in + // the application thread is not much better, since it actually draws + // the pages... + + BPath pathToPageSource; + + BString url; + status_t ret = message->FindString("url", &url); + if (ret == B_OK && url.FindFirst("file://") == 0) { + // Local file + url.Remove(0, strlen("file://")); + pathToPageSource.SetTo(url.String()); + } else { + // Something else, store it. + // TODO: What if it isn't HTML, but for example SVG? + BString source; + ret = message->FindString("source", &source); + + if (ret == B_OK) + ret = find_directory(B_COMMON_TEMP_DIRECTORY, &pathToPageSource); + + BString tmpFileName("PageSource_"); + tmpFileName << system_time() << ".html"; + if (ret == B_OK) + ret = pathToPageSource.Append(tmpFileName.String()); + + BFile pageSourceFile(pathToPageSource.Path(), + B_CREATE_FILE | B_ERASE_FILE | B_WRITE_ONLY); + if (ret == B_OK) + ret = pageSourceFile.InitCheck(); + + if (ret == B_OK) { + ssize_t written = pageSourceFile.Write(source.String(), + source.Length()); + if (written != source.Length()) + ret = (status_t)written; + } + + if (ret == B_OK) { + const char* type = "text/html"; + size_t size = strlen(type); + pageSourceFile.WriteAttr("BEOS:TYPE", B_STRING_TYPE, 0, type, size); + // If it fails we don't care. + } + } + + entry_ref ref; + if (ret == B_OK) + ret = get_ref_for_path(pathToPageSource.Path(), &ref); + + if (ret == B_OK) { + BMessage refsMessage(B_REFS_RECEIVED); + ret = refsMessage.AddRef("refs", &ref); + if (ret == B_OK) { + ret = be_roster->Launch("text/x-source-code", &refsMessage); + if (ret == B_ALREADY_RUNNING) + ret = B_OK; + } + } + + if (ret != B_OK) { + char buffer[1024]; + snprintf(buffer, sizeof(buffer), "Failed to show the " + "page source: %s\n", strerror(ret)); + BAlert* alert = new BAlert("Page source error", buffer, "OK"); + alert->Go(NULL); + } +} + + diff --git a/src/apps/webpositive/BrowserWindow.h b/src/apps/webpositive/BrowserWindow.h new file mode 100644 index 0000000000..fcae9d5f7b --- /dev/null +++ b/src/apps/webpositive/BrowserWindow.h @@ -0,0 +1,257 @@ +/* + * Copyright (C) 2007 Ryan Leavengood + * Copyright (C) 2009 Maxime Simon + * Copyright (C) 2010 Stephan Aßmus + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef BROWSER_WINDOW_H +#define BROWSER_WINDOW_H + + +#include "WebWindow.h" +#include +#include + +class BButton; +class BCheckBox; +class BDirectory; +class BFile; +class BLayoutItem; +class BMenu; +class BMenuItem; +class BMessageRunner; +class BPath; +class BStatusBar; +class BStringView; +class BTextControl; +class BWebView; +class IconButton; +class SettingsMessage; +class TabManager; +class URLInputGroup; + +enum { + INTERFACE_ELEMENT_MENU = 1 << 0, + INTERFACE_ELEMENT_TABS = 1 << 1, + INTERFACE_ELEMENT_NAVIGATION = 1 << 2, + INTERFACE_ELEMENT_STATUS = 1 << 3, + + INTERFACE_ELEMENT_ALL = 0xffff +}; + +enum NewPagePolicy { + OpenBlankPage = 0, + OpenStartPage = 1, + OpenSearchPage = 2, + CloneCurrentPage = 3 +}; + +enum { + NEW_WINDOW = 'nwnd', + NEW_TAB = 'ntab', + WINDOW_OPENED = 'wndo', + WINDOW_CLOSED = 'wndc', + SHOW_DOWNLOAD_WINDOW = 'sdwd', + SHOW_SETTINGS_WINDOW = 'sswd' +}; + +#define INTEGRATE_MENU_INTO_TAB_BAR 0 + + +class BrowserWindow : public BWebWindow { +public: + BrowserWindow(BRect frame, + SettingsMessage* appSettings, + const BString& url, + uint32 interfaceElements + = INTERFACE_ELEMENT_ALL, + BWebView* webView = NULL); + virtual ~BrowserWindow(); + + virtual void DispatchMessage(BMessage* message, + BHandler* target); + virtual void MessageReceived(BMessage* message); + virtual bool QuitRequested(); + virtual void MenusBeginning(); + + virtual void Zoom(BPoint origin, float width, float height); + virtual void ScreenChanged(BRect screenSize, + color_space format); + virtual void WorkspacesChanged(uint32 oldWorkspaces, + uint32 newWorkspaces); + + virtual void SetCurrentWebView(BWebView* view); + + bool IsBlankTab() const; + void CreateNewTab(const BString& url, bool select, + BWebView* webView = 0); + + BRect WindowFrame() const; + + void ToggleFullscreen(); + +private: + // WebPage notification API implementations + virtual void NavigationRequested(const BString& url, + BWebView* view); + virtual void NewWindowRequested(const BString& url, + bool primaryAction); + virtual void CloseWindowRequested(BWebView* view); + virtual void NewPageCreated(BWebView* view, + BRect windowFrame, bool modalDialog, + bool resizable, bool activate); + virtual void LoadNegotiating(const BString& url, + BWebView* view); + virtual void LoadCommitted(const BString& url, + BWebView* view); + virtual void LoadProgress(float progress, BWebView* view); + virtual void LoadFailed(const BString& url, BWebView* view); + virtual void LoadFinished(const BString& url, + BWebView* view); + virtual void MainDocumentError(const BString& failingURL, + const BString& localizedDescription, + BWebView* view); + virtual void TitleChanged(const BString& title, + BWebView* view); + virtual void IconReceived(const BBitmap* icon, + BWebView* view); + virtual void ResizeRequested(float width, float height, + BWebView* view); + virtual void SetToolBarsVisible(bool flag, BWebView* view); + virtual void SetStatusBarVisible(bool flag, BWebView* view); + virtual void SetMenuBarVisible(bool flag, BWebView* view); + virtual void SetResizable(bool flag, BWebView* view); + virtual void StatusChanged(const BString& status, + BWebView* view); + virtual void NavigationCapabilitiesChanged( + bool canGoBackward, bool canGoForward, + bool canStop, BWebView* view); + virtual void UpdateGlobalHistory(const BString& url); + virtual bool AuthenticationChallenge(BString message, + BString& inOutUser, BString& inOutPassword, + bool& inOutRememberCredentials, + uint32 failureCount, BWebView* view); + +private: + void _UpdateTitle(const BString &title); + void _UpdateTabGroupVisibility(); + bool _TabGroupShouldBeVisible() const; + void _ShutdownTab(int32 index); + void _TabChanged(int32 index); + + status_t _BookmarkPath(BPath& path) const; + void _CreateBookmark(); + void _ShowBookmarks(); + bool _CheckBookmarkExists(BDirectory& directory, + const BString& fileName, + const BString& url) const; + bool _ReadURLAttr(BFile& bookmarkFile, + BString& url) const; + void _AddBookmarkURLsRecursively( + BDirectory& directory, + BMessage* message, + uint32& addedCount) const; + + void _SetPageIcon(BWebView* view, + const BBitmap* icon); + + void _UpdateHistoryMenu(); + void _UpdateClipboardItems(); + + bool _ShowPage(BWebView* view); + + void _ToggleFullscreen(); + void _ResizeToScreen(); + void _SetAutoHideInterfaceInFullscreen(bool doIt); + void _CheckAutoHideInterface(); + void _ShowInterface(bool show); + void _ShowProgressBar(bool); + void _InvokeButtonVisibly(BButton* button); + + BString _NewTabURL(bool isNewWindow) const; + BString _SmartURLHandler(const BString& url) const; + + void _HandlePageSourceResult( + const BMessage* message); + +private: + BMenu* fHistoryMenu; + int32 fHistoryMenuFixedItemCount; + + BMenuItem* fCutMenuItem; + BMenuItem* fCopyMenuItem; + BMenuItem* fPasteMenuItem; + BMenuItem* fFindPreviousMenuItem; + BMenuItem* fFindNextMenuItem; + BMenuItem* fZoomTextOnlyMenuItem; + BMenuItem* fFullscreenItem; + BMenuItem* fBackMenuItem; + BMenuItem* fForwardMenuItem; + + IconButton* fBackButton; + IconButton* fForwardButton; + IconButton* fStopButton; + IconButton* fHomeButton; + URLInputGroup* fURLInputGroup; + BStringView* fStatusText; + BStatusBar* fLoadingProgressBar; + + BLayoutItem* fMenuGroup; + BLayoutItem* fTabGroup; + BLayoutItem* fNavigationGroup; + BLayoutItem* fFindGroup; + BLayoutItem* fStatusGroup; + BLayoutItem* fToggleFullscreenButton; + + BTextControl* fFindTextControl; + BButton* fFindPreviousButton; + BButton* fFindNextButton; + BButton* fFindCloseButton; + BCheckBox* fFindCaseSensitiveCheckBox; + TabManager* fTabManager; + + bool fIsFullscreen; + bool fInterfaceVisible; + BRect fNonFullscreenWindowFrame; + BMessageRunner* fPulseRunner; + uint32 fVisibleInterfaceElements; + bigtime_t fLastMouseMovedTime; + BPoint fLastMousePos; + + // cached settings + SettingsMessage* fAppSettings; + bool fZoomTextOnly; + bool fShowTabsIfSinglePageOpen; + bool fAutoHideInterfaceInFullscreenMode; + bool fAutoHidePointer; + uint32 fNewWindowPolicy; + uint32 fNewTabPolicy; + BString fStartPageURL; + BString fSearchPageURL; +}; + + +#endif // BROWSER_WINDOW_H + diff --git a/src/apps/webpositive/BrowsingHistory.cpp b/src/apps/webpositive/BrowsingHistory.cpp new file mode 100644 index 0000000000..42a0442d0f --- /dev/null +++ b/src/apps/webpositive/BrowsingHistory.cpp @@ -0,0 +1,381 @@ +/* + * Copyright (C) 2010 Stephan Aßmus + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "BrowsingHistory.h" + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "BrowserApp.h" + + +BrowsingHistoryItem::BrowsingHistoryItem(const BString& url) + : + fURL(url), + fDateTime(BDateTime::CurrentDateTime(B_LOCAL_TIME)), + fInvokationCount(0) +{ +} + + +BrowsingHistoryItem::BrowsingHistoryItem(const BrowsingHistoryItem& other) +{ + *this = other; +} + + +BrowsingHistoryItem::BrowsingHistoryItem(const BMessage* archive) +{ + if (!archive) + return; + BMessage dateTimeArchive; + if (archive->FindMessage("date time", &dateTimeArchive) == B_OK) + fDateTime = BDateTime(&dateTimeArchive); + archive->FindString("url", &fURL); + archive->FindUInt32("invokations", &fInvokationCount); +} + + +BrowsingHistoryItem::~BrowsingHistoryItem() +{ +} + + +status_t +BrowsingHistoryItem::Archive(BMessage* archive) const +{ + if (!archive) + return B_BAD_VALUE; + BMessage dateTimeArchive; + status_t status = fDateTime.Archive(&dateTimeArchive); + if (status == B_OK) + status = archive->AddMessage("date time", &dateTimeArchive); + if (status == B_OK) + status = archive->AddString("url", fURL.String()); + if (status == B_OK) + status = archive->AddUInt32("invokations", fInvokationCount); + return status; +} + + +BrowsingHistoryItem& +BrowsingHistoryItem::operator=(const BrowsingHistoryItem& other) +{ + if (this == &other) + return *this; + + fURL = other.fURL; + fDateTime = other.fDateTime; + fInvokationCount = other.fInvokationCount; + + return *this; +} + + +bool +BrowsingHistoryItem::operator==(const BrowsingHistoryItem& other) const +{ + if (this == &other) + return true; + + return fURL == other.fURL && fDateTime == other.fDateTime + && fInvokationCount == other.fInvokationCount; +} + + +bool +BrowsingHistoryItem::operator!=(const BrowsingHistoryItem& other) const +{ + return !(*this == other); +} + + +bool +BrowsingHistoryItem::operator<(const BrowsingHistoryItem& other) const +{ + if (this == &other) + return false; + + return fDateTime < other.fDateTime || fURL < other.fURL; +} + + +bool +BrowsingHistoryItem::operator<=(const BrowsingHistoryItem& other) const +{ + return (*this == other) || (*this < other); +} + + +bool +BrowsingHistoryItem::operator>(const BrowsingHistoryItem& other) const +{ + if (this == &other) + return false; + + return fDateTime > other.fDateTime || fURL > other.fURL; +} + + +bool +BrowsingHistoryItem::operator>=(const BrowsingHistoryItem& other) const +{ + return (*this == other) || (*this > other); +} + + +void +BrowsingHistoryItem::Invoked() +{ + // Eventually, we may overflow... + uint32 count = fInvokationCount + 1; + if (count > fInvokationCount) + fInvokationCount = count; + fDateTime = BDateTime::CurrentDateTime(B_LOCAL_TIME); +} + + +// #pragma mark - BrowsingHistory + + +BrowsingHistory +BrowsingHistory::sDefaultInstance; + + +BrowsingHistory::BrowsingHistory() + : + BLocker("browsing history"), + fHistoryItems(64), + fMaxHistoryItemAge(7), + fSettingsLoaded(false) +{ +} + + +BrowsingHistory::~BrowsingHistory() +{ + _SaveSettings(); + _Clear(); +} + + +/*static*/ BrowsingHistory* +BrowsingHistory::DefaultInstance() +{ + if (sDefaultInstance.Lock()) { + sDefaultInstance._LoadSettings(); + sDefaultInstance.Unlock(); + } + return &sDefaultInstance; +} + + +bool +BrowsingHistory::AddItem(const BrowsingHistoryItem& item) +{ + BAutolock _(this); + + return _AddItem(item, false); +} + + +int32 +BrowsingHistory::BrowsingHistory::CountItems() const +{ + BAutolock _(const_cast(this)); + + return fHistoryItems.CountItems(); +} + + +BrowsingHistoryItem +BrowsingHistory::HistoryItemAt(int32 index) const +{ + BAutolock _(const_cast(this)); + + BrowsingHistoryItem* existingItem = reinterpret_cast( + fHistoryItems.ItemAt(index)); + if (!existingItem) + return BrowsingHistoryItem(BString()); + + return BrowsingHistoryItem(*existingItem); +} + + +void +BrowsingHistory::Clear() +{ + BAutolock _(this); + _Clear(); + _SaveSettings(); +} + + +void +BrowsingHistory::SetMaxHistoryItemAge(int32 days) +{ + BAutolock _(this); + if (fMaxHistoryItemAge != days) { + fMaxHistoryItemAge = days; + _SaveSettings(); + } +} + + +int32 +BrowsingHistory::MaxHistoryItemAge() const +{ + return fMaxHistoryItemAge; +} + + +// #pragma mark - private + + +void +BrowsingHistory::_Clear() +{ + int32 count = CountItems(); + for (int32 i = 0; i < count; i++) { + BrowsingHistoryItem* item = reinterpret_cast( + fHistoryItems.ItemAtFast(i)); + delete item; + } + fHistoryItems.MakeEmpty(); +} + + +bool +BrowsingHistory::_AddItem(const BrowsingHistoryItem& item, bool internal) +{ + int32 count = CountItems(); + int32 insertionIndex = count; + for (int32 i = 0; i < count; i++) { + BrowsingHistoryItem* existingItem + = reinterpret_cast( + fHistoryItems.ItemAtFast(i)); + if (item.URL() == existingItem->URL()) { + if (!internal) { + existingItem->Invoked(); + _SaveSettings(); + } + return true; + } + if (item < *existingItem) + insertionIndex = i; + } + BrowsingHistoryItem* newItem = new(std::nothrow) BrowsingHistoryItem(item); + if (!newItem || !fHistoryItems.AddItem(newItem, insertionIndex)) { + delete newItem; + return false; + } + + if (!internal) { + newItem->Invoked(); + _SaveSettings(); + } + + return true; +} + + +void +BrowsingHistory::_LoadSettings() +{ + if (fSettingsLoaded) + return; + + fSettingsLoaded = true; + + BFile settingsFile; + if (_OpenSettingsFile(settingsFile, B_READ_ONLY)) { + BMessage settingsArchive; + settingsArchive.Unflatten(&settingsFile); + if (settingsArchive.FindInt32("max history item age", + &fMaxHistoryItemAge) != B_OK) { + fMaxHistoryItemAge = 7; + } + BDateTime oldestAllowedDateTime + = BDateTime::CurrentDateTime(B_LOCAL_TIME); + oldestAllowedDateTime.Date().AddDays(-fMaxHistoryItemAge); + + BMessage historyItemArchive; + for (int32 i = 0; settingsArchive.FindMessage("history item", i, + &historyItemArchive) == B_OK; i++) { + BrowsingHistoryItem item(&historyItemArchive); + if (oldestAllowedDateTime < item.DateTime()) + _AddItem(item, true); + historyItemArchive.MakeEmpty(); + } + } +} + + +void +BrowsingHistory::_SaveSettings() +{ + BFile settingsFile; + if (_OpenSettingsFile(settingsFile, + B_CREATE_FILE | B_ERASE_FILE | B_WRITE_ONLY)) { + BMessage settingsArchive; + settingsArchive.AddInt32("max history item age", fMaxHistoryItemAge); + BMessage historyItemArchive; + int32 count = CountItems(); + for (int32 i = 0; i < count; i++) { + BrowsingHistoryItem item = HistoryItemAt(i); + if (item.Archive(&historyItemArchive) != B_OK) + break; + if (settingsArchive.AddMessage("history item", + &historyItemArchive) != B_OK) { + break; + } + historyItemArchive.MakeEmpty(); + } + settingsArchive.Flatten(&settingsFile); + } +} + + +bool +BrowsingHistory::_OpenSettingsFile(BFile& file, uint32 mode) +{ + BPath path; + if (find_directory(B_USER_SETTINGS_DIRECTORY, &path) != B_OK + || path.Append(kApplicationName) != B_OK + || path.Append("BrowsingHistory") != B_OK) { + return false; + } + return file.SetTo(path.Path(), mode) == B_OK; +} + diff --git a/src/apps/webpositive/BrowsingHistory.h b/src/apps/webpositive/BrowsingHistory.h new file mode 100644 index 0000000000..bf8f7f15b8 --- /dev/null +++ b/src/apps/webpositive/BrowsingHistory.h @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2010 Stephan Aßmus + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef BROWSING_HISTORY_H +#define BROWSING_HISTORY_H + +#include "DateTime.h" +#include +#include + +class BFile; +class BString; + + +class BrowsingHistoryItem { +public: + BrowsingHistoryItem(const BString& url); + BrowsingHistoryItem( + const BrowsingHistoryItem& other); + BrowsingHistoryItem(const BMessage* archive); + ~BrowsingHistoryItem(); + + status_t Archive(BMessage* archive) const; + + BrowsingHistoryItem& operator=(const BrowsingHistoryItem& other); + + bool operator==( + const BrowsingHistoryItem& other) const; + bool operator!=( + const BrowsingHistoryItem& other) const; + bool operator<( + const BrowsingHistoryItem& other) const; + bool operator<=( + const BrowsingHistoryItem& other) const; + bool operator>( + const BrowsingHistoryItem& other) const; + bool operator>=( + const BrowsingHistoryItem& other) const; + + const BString& URL() const { return fURL; } + const BDateTime& DateTime() const { return fDateTime; } + uint32 InvokationCount() const { + return fInvokationCount; } + void Invoked(); + +private: + BString fURL; + BDateTime fDateTime; + uint32 fInvokationCount; +}; + + +class BrowsingHistory : public BLocker { +public: + static BrowsingHistory* DefaultInstance(); + + bool AddItem(const BrowsingHistoryItem& item); + + // Should Lock() the object when using these in some loop or so: + int32 CountItems() const; + BrowsingHistoryItem HistoryItemAt(int32 index) const; + void Clear(); + + void SetMaxHistoryItemAge(int32 days); + int32 MaxHistoryItemAge() const; + +private: + BrowsingHistory(); + virtual ~BrowsingHistory(); + + void _Clear(); + bool _AddItem(const BrowsingHistoryItem& item, + bool invoke); + + void _LoadSettings(); + void _SaveSettings(); + bool _OpenSettingsFile(BFile& file, uint32 mode); + +private: + BList fHistoryItems; + int32 fMaxHistoryItemAge; + + static BrowsingHistory sDefaultInstance; + bool fSettingsLoaded; +}; + + +#endif // BROWSING_HISTORY_H + diff --git a/src/apps/webpositive/CredentialsStorage.cpp b/src/apps/webpositive/CredentialsStorage.cpp new file mode 100644 index 0000000000..ab5f8eae8d --- /dev/null +++ b/src/apps/webpositive/CredentialsStorage.cpp @@ -0,0 +1,275 @@ +/* + * Copyright (C) 2010 Stephan Aßmus + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "CredentialsStorage.h" + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "BrowserApp.h" + + +Credentials::Credentials() + : + fUsername(), + fPassword() +{ +} + + +Credentials::Credentials(const BString& username, const BString& password) + : + fUsername(username), + fPassword(password) +{ +} + + +Credentials::Credentials(const Credentials& other) +{ + *this = other; +} + + +Credentials::Credentials(const BMessage* archive) +{ + if (archive == NULL) + return; + archive->FindString("username", &fUsername); + archive->FindString("password", &fPassword); +} + + +Credentials::~Credentials() +{ +} + + +status_t +Credentials::Archive(BMessage* archive) const +{ + if (archive == NULL) + return B_BAD_VALUE; + status_t status = archive->AddString("username", fUsername); + if (status == B_OK) + status = archive->AddString("password", fPassword); + return status; +} + + +Credentials& +Credentials::operator=(const Credentials& other) +{ + if (this == &other) + return *this; + + fUsername = other.fUsername; + fPassword = other.fPassword; + + return *this; +} + + +bool +Credentials::operator==(const Credentials& other) const +{ + if (this == &other) + return true; + + return fUsername == other.fUsername && fPassword == other.fPassword; +} + + +bool +Credentials::operator!=(const Credentials& other) const +{ + return !(*this == other); +} + + +const BString& +Credentials::Username() const +{ + return fUsername; +} + + +const BString& +Credentials::Password() const +{ + return fPassword; +} + + +// #pragma mark - CredentialsStorage + + +CredentialsStorage +CredentialsStorage::sPersistentInstance(true); + + +CredentialsStorage +CredentialsStorage::sSessionInstance(false); + + +CredentialsStorage::CredentialsStorage(bool persistent) + : + BLocker(persistent ? "persistent credential storage" + : "credential storage"), + fCredentialMap(), + fSettingsLoaded(false), + fPersistent(persistent) +{ +} + + +CredentialsStorage::~CredentialsStorage() +{ + _SaveSettings(); +} + + +/*static*/ CredentialsStorage* +CredentialsStorage::SessionInstance() +{ + return &sSessionInstance; +} + + +/*static*/ CredentialsStorage* +CredentialsStorage::PersistentInstance() +{ + if (sPersistentInstance.Lock()) { + sPersistentInstance._LoadSettings(); + sPersistentInstance.Unlock(); + } + return &sPersistentInstance; +} + + +bool +CredentialsStorage::Contains(const HashKeyString& key) +{ + BAutolock _(this); + + return fCredentialMap.ContainsKey(key); +} + + +status_t +CredentialsStorage::PutCredentials(const HashKeyString& key, + const Credentials& credentials) +{ + BAutolock _(this); + + return fCredentialMap.Put(key, credentials); +} + + +Credentials +CredentialsStorage::GetCredentials(const HashKeyString& key) +{ + BAutolock _(this); + + return fCredentialMap.Get(key); +} + + +// #pragma mark - private + + +void +CredentialsStorage::_LoadSettings() +{ + if (!fPersistent || fSettingsLoaded) + return; + + fSettingsLoaded = true; + + BFile settingsFile; + if (_OpenSettingsFile(settingsFile, B_READ_ONLY)) { + BMessage settingsArchive; + settingsArchive.Unflatten(&settingsFile); + BMessage credentialsArchive; + for (int32 i = 0; settingsArchive.FindMessage("credentials", i, + &credentialsArchive) == B_OK; i++) { + BString key; + if (credentialsArchive.FindString("key", &key) == B_OK) { + Credentials credentials(&credentialsArchive); + fCredentialMap.Put(key, credentials); + } + } + } +} + + +void +CredentialsStorage::_SaveSettings() const +{ + BFile settingsFile; + if (_OpenSettingsFile(settingsFile, + B_CREATE_FILE | B_ERASE_FILE | B_WRITE_ONLY)) { + BMessage settingsArchive; + BMessage credentialsArchive; + CredentialMap::Iterator iterator = fCredentialMap.GetIterator(); + while (iterator.HasNext()) { + const CredentialMap::Entry& entry = iterator.Next(); + if (entry.value.Archive(&credentialsArchive) != B_OK + || credentialsArchive.AddString("key", + entry.key.value) != B_OK) { + break; + } + if (settingsArchive.AddMessage("credentials", + &credentialsArchive) != B_OK) { + break; + } + credentialsArchive.MakeEmpty(); + } + settingsArchive.Flatten(&settingsFile); + } +} + + +bool +CredentialsStorage::_OpenSettingsFile(BFile& file, uint32 mode) const +{ + BPath path; + if (find_directory(B_USER_SETTINGS_DIRECTORY, &path) != B_OK + || path.Append(kApplicationName) != B_OK + || path.Append("CredentialsStorage") != B_OK) { + return false; + } + return file.SetTo(path.Path(), mode) == B_OK; +} + diff --git a/src/apps/webpositive/CredentialsStorage.h b/src/apps/webpositive/CredentialsStorage.h new file mode 100644 index 0000000000..214c36b371 --- /dev/null +++ b/src/apps/webpositive/CredentialsStorage.h @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2010 Stephan Aßmus + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef CREDENTIAL_STORAGE_H +#define CREDENTIAL_STORAGE_H + +#include "HashKeys.h" +#include "HashMap.h" +#include +#include + + +class BFile; +class BMessage; +class BString; + + +class Credentials { +public: + Credentials(); + Credentials(const BString& username, + const BString& password); + Credentials( + const Credentials& other); + Credentials(const BMessage* archive); + ~Credentials(); + + status_t Archive(BMessage* archive) const; + + Credentials& operator=(const Credentials& other); + + bool operator==(const Credentials& other) const; + bool operator!=(const Credentials& other) const; + + const BString& Username() const; + const BString& Password() const; + +private: + BString fUsername; + BString fPassword; +}; + + +class CredentialsStorage : public BLocker { +public: + static CredentialsStorage* SessionInstance(); + static CredentialsStorage* PersistentInstance(); + + bool Contains(const HashKeyString& key); + status_t PutCredentials(const HashKeyString& key, + const Credentials& credentials); + Credentials GetCredentials(const HashKeyString& key); + +private: + CredentialsStorage(bool persistent); + virtual ~CredentialsStorage(); + + void _LoadSettings(); + void _SaveSettings() const; + bool _OpenSettingsFile(BFile& file, + uint32 mode) const; + +private: + typedef HashMap CredentialMap; + CredentialMap fCredentialMap; + + static CredentialsStorage sPersistentInstance; + static CredentialsStorage sSessionInstance; + bool fSettingsLoaded; + bool fPersistent; +}; + + +#endif // CREDENTIAL_STORAGE_H + diff --git a/src/apps/webpositive/DownloadProgressView.cpp b/src/apps/webpositive/DownloadProgressView.cpp new file mode 100644 index 0000000000..7031e429c3 --- /dev/null +++ b/src/apps/webpositive/DownloadProgressView.cpp @@ -0,0 +1,838 @@ +/* + * Copyright (C) 2010 Stephan Aßmus + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "DownloadProgressView.h" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "WebDownload.h" +#include "WebPage.h" +#include "StringForSize.h" + + +enum { + OPEN_DOWNLOAD = 'opdn', + RESTART_DOWNLOAD = 'rsdn', + CANCEL_DOWNLOAD = 'cndn', + REMOVE_DOWNLOAD = 'rmdn', + COPY_URL_TO_CLIPBOARD = 'curl', + OPEN_CONTAINING_FOLDER = 'opfd', +}; + +const bigtime_t kMaxUpdateInterval = 100000LL; +const bigtime_t kSpeedReferenceInterval = 500000LL; +const bigtime_t kShowSpeedInterval = 8000000LL; +const bigtime_t kShowEstimatedFinishInterval = 4000000LL; + +bigtime_t DownloadProgressView::sLastEstimatedFinishSpeedToggleTime = -1; +bool DownloadProgressView::sShowSpeed = true; + + +class IconView : public BView { +public: + IconView(const BEntry& entry) + : + BView("Download icon", B_WILL_DRAW), + fIconBitmap(BRect(0, 0, 31, 31), 0, B_RGBA32), + fDimmedIcon(false) + { + SetDrawingMode(B_OP_OVER); + SetTo(entry); + } + + IconView() + : + BView("Download icon", B_WILL_DRAW), + fIconBitmap(BRect(0, 0, 31, 31), 0, B_RGBA32), + fDimmedIcon(false) + { + SetDrawingMode(B_OP_OVER); + memset(fIconBitmap.Bits(), 0, fIconBitmap.BitsLength()); + } + + IconView(BMessage* archive) + : + BView("Download icon", B_WILL_DRAW), + fIconBitmap(archive), + fDimmedIcon(true) + { + SetDrawingMode(B_OP_OVER); + } + + void SetTo(const BEntry& entry) + { + BNode node(&entry); + BNodeInfo info(&node); + info.GetTrackerIcon(&fIconBitmap, B_LARGE_ICON); + Invalidate(); + } + + void SetIconDimmed(bool iconDimmed) + { + if (fDimmedIcon != iconDimmed) { + fDimmedIcon = iconDimmed; + Invalidate(); + } + } + + bool IsIconDimmed() const + { + return fDimmedIcon; + } + + status_t SaveSettings(BMessage* archive) + { + return fIconBitmap.Archive(archive); + } + + virtual void AttachedToWindow() + { + SetViewColor(Parent()->ViewColor()); + } + + virtual void Draw(BRect updateRect) + { + if (fDimmedIcon) { + SetDrawingMode(B_OP_ALPHA); + SetBlendingMode(B_CONSTANT_ALPHA, B_ALPHA_OVERLAY); + SetHighColor(0, 0, 0, 100); + } + DrawBitmapAsync(&fIconBitmap); + } + + virtual BSize MinSize() + { + return BSize(fIconBitmap.Bounds().Width(), fIconBitmap.Bounds().Height()); + } + + virtual BSize PreferredSize() + { + return MinSize(); + } + + virtual BSize MaxSize() + { + return MinSize(); + } + +private: + BBitmap fIconBitmap; + bool fDimmedIcon; +}; + + +class SmallButton : public BButton { +public: + SmallButton(const char* label, BMessage* message = NULL) + : + BButton(label, message) + { + BFont font; + GetFont(&font); + float size = ceilf(font.Size() * 0.8); + font.SetSize(max_c(8, size)); + SetFont(&font, B_FONT_SIZE); + } +}; + + +// #pragma mark - DownloadProgressView + + +DownloadProgressView::DownloadProgressView(BWebDownload* download) + : + BGroupView(B_HORIZONTAL, 8), + fDownload(download), + fURL(download->URL()), + fPath(download->Path()) +{ +} + + +DownloadProgressView::DownloadProgressView(const BMessage* archive) + : + BGroupView(B_HORIZONTAL, 8), + fDownload(NULL), + fURL(), + fPath() +{ + const char* string; + if (archive->FindString("path", &string) == B_OK) + fPath.SetTo(string); + if (archive->FindString("url", &string) == B_OK) + fURL = string; +} + + +bool +DownloadProgressView::Init(BMessage* archive) +{ + fCurrentSize = 0; + fExpectedSize = 0; + fLastUpdateTime = 0; + fBytesPerSecond = 0.0; + for (size_t i = 0; i < kBytesPerSecondSlots; i++) + fBytesPerSecondSlot[i] = 0.0; + fCurrentBytesPerSecondSlot = 0; + fLastSpeedReferenceSize = 0; + fEstimatedFinishReferenceSize = 0; + + fProcessStartTime = fLastSpeedReferenceTime = fEstimatedFinishReferenceTime + = system_time(); + + SetViewColor(245, 245, 245); + SetFlags(Flags() | B_FULL_UPDATE_ON_RESIZE | B_WILL_DRAW); + + if (archive) { + fStatusBar = new BStatusBar("download progress", fPath.Leaf()); + float value; + if (archive->FindFloat("value", &value) == B_OK) + fStatusBar->SetTo(value); + } else + fStatusBar = new BStatusBar("download progress", "Download"); + fStatusBar->SetMaxValue(100); + fStatusBar->SetBarHeight(12); + + // fPath is only valid when constructed from archive (fDownload == NULL) + BEntry entry(fPath.Path()); + + if (archive) { + if (!entry.Exists()) + fIconView = new IconView(archive); + else + fIconView = new IconView(entry); + } else + fIconView = new IconView(); + + if (!fDownload && (fStatusBar->CurrentValue() < 100 || !entry.Exists())) + fTopButton = new SmallButton("Restart", new BMessage(RESTART_DOWNLOAD)); + else { + fTopButton = new SmallButton("Open", new BMessage(OPEN_DOWNLOAD)); + fTopButton->SetEnabled(fDownload == NULL); + } + if (fDownload) + fBottomButton = new SmallButton("Cancel", new BMessage(CANCEL_DOWNLOAD)); + else { + fBottomButton = new SmallButton("Remove", new BMessage(REMOVE_DOWNLOAD)); + fBottomButton->SetEnabled(fDownload == NULL); + } + + fInfoView = new BStringView("info view", ""); + + BGroupLayout* layout = GroupLayout(); + layout->SetInsets(8, 5, 5, 6); + layout->AddView(fIconView); + BView* verticalGroup = BGroupLayoutBuilder(B_VERTICAL, 3) + .Add(fStatusBar) + .Add(fInfoView) + .TopView() + ; + verticalGroup->SetViewColor(ViewColor()); + layout->AddView(verticalGroup); + verticalGroup = BGroupLayoutBuilder(B_VERTICAL, 3) + .Add(fTopButton) + .Add(fBottomButton) + .TopView() + ; + verticalGroup->SetViewColor(ViewColor()); + layout->AddView(verticalGroup); + + BFont font; + fInfoView->GetFont(&font); + float fontSize = font.Size() * 0.8f; + font.SetSize(max_c(8.0f, fontSize)); + fInfoView->SetFont(&font, B_FONT_SIZE); + fInfoView->SetHighColor(tint_color(fInfoView->LowColor(), + B_DARKEN_4_TINT)); + fInfoView->SetExplicitMaxSize(BSize(B_SIZE_UNLIMITED, B_SIZE_UNSET)); + + return true; +} + + +status_t +DownloadProgressView::SaveSettings(BMessage* archive) +{ + if (!archive) + return B_BAD_VALUE; + status_t ret = archive->AddString("path", fPath.Path()); + if (ret == B_OK) + ret = archive->AddString("url", fURL.String()); + if (ret == B_OK) + ret = archive->AddFloat("value", fStatusBar->CurrentValue()); + if (ret == B_OK) + ret = fIconView->SaveSettings(archive); + return ret; +} + + +void +DownloadProgressView::AttachedToWindow() +{ + if (fDownload) { + fDownload->SetProgressListener(BMessenger(this)); + // Will start node monitor upon receiving the B_DOWNLOAD_STARTED + // message. + } else { + BEntry entry(fPath.Path()); + if (entry.Exists()) + _StartNodeMonitor(entry); + } + + fTopButton->SetTarget(this); + fBottomButton->SetTarget(this); +} + + +void +DownloadProgressView::DetachedFromWindow() +{ + _StopNodeMonitor(); +} + + +void +DownloadProgressView::AllAttached() +{ + SetViewColor(B_TRANSPARENT_COLOR); + SetLowColor(245, 245, 245); + SetHighColor(tint_color(LowColor(), B_DARKEN_1_TINT)); +} + + +void +DownloadProgressView::Draw(BRect updateRect) +{ + BRect bounds(Bounds()); + bounds.bottom--; + FillRect(bounds, B_SOLID_LOW); + bounds.bottom++; + StrokeLine(bounds.LeftBottom(), bounds.RightBottom()); +} + + +void +DownloadProgressView::MessageReceived(BMessage* message) +{ + switch (message->what) { + case B_DOWNLOAD_STARTED: + { + BString path; + if (message->FindString("path", &path) != B_OK) + break; + fPath.SetTo(path); + BEntry entry(fPath.Path()); + fIconView->SetTo(entry); + fStatusBar->Reset(fPath.Leaf()); + _StartNodeMonitor(entry); + + // Immediately switch to speed display whenever a new download + // starts. + sShowSpeed = true; + sLastEstimatedFinishSpeedToggleTime + = fProcessStartTime = fLastSpeedReferenceTime + = fEstimatedFinishReferenceTime = system_time(); + break; + } + case B_DOWNLOAD_PROGRESS: + { + int64 currentSize; + int64 expectedSize; + if (message->FindInt64("current size", ¤tSize) == B_OK + && message->FindInt64("expected size", &expectedSize) == B_OK) { + _UpdateStatus(currentSize, expectedSize); + } + break; + } + case B_DOWNLOAD_REMOVED: + // TODO: This is a bit asymetric. The removed notification + // arrives here, but it would be nicer if it arrived + // at the window... + Window()->PostMessage(message); + break; + case OPEN_DOWNLOAD: + { + // TODO: In case of executable files, ask the user first! + entry_ref ref; + status_t status = get_ref_for_path(fPath.Path(), &ref); + if (status == B_OK) + status = be_roster->Launch(&ref); + if (status != B_OK && status != B_ALREADY_RUNNING) { + BAlert* alert = new BAlert("Open download error", + "The download could not be opened.", "OK"); + alert->Go(NULL); + } + break; + } + case RESTART_DOWNLOAD: + BWebPage::RequestDownload(fURL); + break; + + case CANCEL_DOWNLOAD: + fDownload->Cancel(); + DownloadCanceled(); + break; + + case REMOVE_DOWNLOAD: + { + Window()->PostMessage(SAVE_SETTINGS); + RemoveSelf(); + delete this; + // TOAST! + return; + } + case B_NODE_MONITOR: + { + int32 opCode; + if (message->FindInt32("opcode", &opCode) != B_OK) + break; + switch (opCode) { + case B_ENTRY_REMOVED: + fIconView->SetIconDimmed(true); + DownloadCanceled(); + break; + case B_ENTRY_MOVED: + { + // Follow the entry to the new location + dev_t device; + ino_t directory; + const char* name; + if (message->FindInt32("device", + reinterpret_cast(&device)) != B_OK + || message->FindInt64("to directory", + reinterpret_cast(&directory)) != B_OK + || message->FindString("name", &name) != B_OK + || strlen(name) == 0) { + break; + } + // Construct the BEntry and update fPath + entry_ref ref(device, directory, name); + BEntry entry(&ref); + if (entry.GetPath(&fPath) != B_OK) + break; + + // Find out if the directory is the Trash for this + // volume + char trashPath[B_PATH_NAME_LENGTH]; + if (find_directory(B_TRASH_DIRECTORY, device, false, + trashPath, B_PATH_NAME_LENGTH) == B_OK) { + BPath trashDirectory(trashPath); + BPath parentDirectory; + fPath.GetParent(&parentDirectory); + if (parentDirectory == trashDirectory) { + // The entry was moved into the Trash. + // If the download is still in progress, + // cancel it. + if (fDownload) + fDownload->Cancel(); + fIconView->SetIconDimmed(true); + DownloadCanceled(); + break; + } else if (fIconView->IsIconDimmed()) { + // Maybe it was moved out of the trash. + fIconView->SetIconDimmed(false); + } + } + + // Inform download of the new path + if (fDownload) + fDownload->HasMovedTo(fPath); + + float value = fStatusBar->CurrentValue(); + fStatusBar->Reset(name); + fStatusBar->SetTo(value); + Window()->PostMessage(SAVE_SETTINGS); + break; + } + case B_ATTR_CHANGED: + { + BEntry entry(fPath.Path()); + fIconView->SetIconDimmed(false); + fIconView->SetTo(entry); + break; + } + } + break; + } + + // Context menu messages + case COPY_URL_TO_CLIPBOARD: + if (be_clipboard->Lock()) { + BMessage* data = be_clipboard->Data(); + if (data != NULL) { + be_clipboard->Clear(); + data->AddData("text/plain", B_MIME_TYPE, fURL.String(), + fURL.Length()); + } + be_clipboard->Commit(); + be_clipboard->Unlock(); + } + break; + case OPEN_CONTAINING_FOLDER: + if (fPath.InitCheck() == B_OK) { + BPath containingFolder; + if (fPath.GetParent(&containingFolder) != B_OK) + break; + BEntry entry(containingFolder.Path()); + if (!entry.Exists()) + break; + entry_ref ref; + if (entry.GetRef(&ref) != B_OK) + break; + be_roster->Launch(&ref); + + // Use Tracker scripting and select the download pose + // in the window. + // TODO: We should somehow get the window that just openend. + // Using the name like this is broken when there are multiple + // windows open with this name. Also Tracker does not scroll + // to this entry. + BString windowName = ref.name; + BString fullWindowName = containingFolder.Path(); + + BMessenger trackerMessenger("application/x-vnd.Be-TRAK"); + if (trackerMessenger.IsValid() + && get_ref_for_path(fPath.Path(), &ref) == B_OK) { + // We need to wait a bit until the folder is open. + // TODO: This is also too fragile... we should be able + // to wait for the roster message. + snooze(250000); + int32 tries = 2; + while (tries > 0) { + BMessage selectionCommand(B_SET_PROPERTY); + selectionCommand.AddSpecifier("Selection"); + selectionCommand.AddSpecifier("Poses"); + selectionCommand.AddSpecifier("Window", + windowName.String()); + selectionCommand.AddRef("data", &ref); + BMessage reply; + trackerMessenger.SendMessage(&selectionCommand, &reply); + int32 error; + if (reply.FindInt32("error", &error) != B_OK + || error == B_OK) { + break; + } + windowName = fullWindowName; + tries--; + } + } + } + break; + + default: + BGroupView::MessageReceived(message); + } +} + + +void +DownloadProgressView::ShowContextMenu(BPoint screenWhere) +{ + screenWhere += BPoint(2, 2); + + BPopUpMenu* contextMenu = new BPopUpMenu("download context"); + BMenuItem* copyURL = new BMenuItem("Copy URL to clipboard", + new BMessage(COPY_URL_TO_CLIPBOARD)); + copyURL->SetEnabled(fURL.Length() > 0); + contextMenu->AddItem(copyURL); + BMenuItem* openFolder = new BMenuItem("Open containing folder", + new BMessage(OPEN_CONTAINING_FOLDER)); + contextMenu->AddItem(openFolder); + + contextMenu->SetTargetForItems(this); + contextMenu->Go(screenWhere, true, true, true); +} + + +BWebDownload* +DownloadProgressView::Download() const +{ + return fDownload; +} + + +const BString& +DownloadProgressView::URL() const +{ + return fURL; +} + + +bool +DownloadProgressView::IsMissing() const +{ + return fIconView->IsIconDimmed(); +} + + +bool +DownloadProgressView::IsFinished() const +{ + return !fDownload && fStatusBar->CurrentValue() == 100; +} + + +void +DownloadProgressView::DownloadFinished() +{ + fDownload = NULL; + if (fExpectedSize == -1) { + fStatusBar->SetTo(100.0); + fExpectedSize = fCurrentSize; + } + fTopButton->SetEnabled(true); + fBottomButton->SetLabel("Remove"); + fBottomButton->SetMessage(new BMessage(REMOVE_DOWNLOAD)); + fBottomButton->SetEnabled(true); + fInfoView->SetText(""); +} + + +void +DownloadProgressView::DownloadCanceled() +{ + fDownload = NULL; + fTopButton->SetLabel("Restart"); + fTopButton->SetMessage(new BMessage(RESTART_DOWNLOAD)); + fTopButton->SetEnabled(true); + fBottomButton->SetLabel("Remove"); + fBottomButton->SetMessage(new BMessage(REMOVE_DOWNLOAD)); + fBottomButton->SetEnabled(true); + fInfoView->SetText(""); + fPath.Unset(); +} + + +/*static*/ void +DownloadProgressView::SpeedVersusEstimatedFinishTogglePulse() +{ + bigtime_t now = system_time(); + if (sShowSpeed + && sLastEstimatedFinishSpeedToggleTime + kShowSpeedInterval + <= now) { + sShowSpeed = false; + sLastEstimatedFinishSpeedToggleTime = now; + } else if (!sShowSpeed + && sLastEstimatedFinishSpeedToggleTime + + kShowEstimatedFinishInterval <= now) { + sShowSpeed = true; + sLastEstimatedFinishSpeedToggleTime = now; + } +} + + +// #pragma mark - private + + +void +DownloadProgressView::_UpdateStatus(off_t currentSize, off_t expectedSize) +{ + fCurrentSize = currentSize; + fExpectedSize = expectedSize; + + fStatusBar->SetTo(100.0 * currentSize / expectedSize); + + bigtime_t currentTime = system_time(); + if ((currentTime - fLastUpdateTime) > kMaxUpdateInterval) { + fLastUpdateTime = currentTime; + + if (currentTime >= fLastSpeedReferenceTime + kSpeedReferenceInterval) { + // update current speed every kSpeedReferenceInterval + fCurrentBytesPerSecondSlot + = (fCurrentBytesPerSecondSlot + 1) % kBytesPerSecondSlots; + fBytesPerSecondSlot[fCurrentBytesPerSecondSlot] + = (double)(currentSize - fLastSpeedReferenceSize) + * 1000000LL / (currentTime - fLastSpeedReferenceTime); + fLastSpeedReferenceSize = currentSize; + fLastSpeedReferenceTime = currentTime; + fBytesPerSecond = 0.0; + size_t count = 0; + for (size_t i = 0; i < kBytesPerSecondSlots; i++) { + if (fBytesPerSecondSlot[i] != 0.0) { + fBytesPerSecond += fBytesPerSecondSlot[i]; + count++; + } + } + if (count > 0) + fBytesPerSecond /= count; + } + _UpdateStatusText(); + } +} + + +void +DownloadProgressView::_UpdateStatusText() +{ + fInfoView->SetText(""); + BString buffer; + if (sShowSpeed && fBytesPerSecond != 0.0) { + // Draw speed info + char sizeBuffer[128]; + buffer = "("; + // Get strings for current and expected size and remove the unit + // from the current size string if it's the same as the expected + // size unit. + BString currentSize = string_for_size((double)fCurrentSize, sizeBuffer, + sizeof(sizeBuffer)); + BString expectedSize = string_for_size((double)fExpectedSize, sizeBuffer, + sizeof(sizeBuffer)); + int currentSizeUnitPos = currentSize.FindLast(' '); + int expectedSizeUnitPos = expectedSize.FindLast(' '); + if (currentSizeUnitPos >= 0 && expectedSizeUnitPos >= 0 + && strcmp(currentSize.String() + currentSizeUnitPos, + expectedSize.String() + expectedSizeUnitPos) == 0) { + currentSize.Truncate(currentSizeUnitPos); + } + buffer << currentSize; + buffer << " of "; + buffer << expectedSize; + buffer << ", "; + buffer << string_for_size(fBytesPerSecond, sizeBuffer, + sizeof(sizeBuffer)); + buffer << "/s)"; + float stringWidth = fInfoView->StringWidth(buffer.String()); + if (stringWidth < fInfoView->Bounds().Width()) + fInfoView->SetText(buffer.String()); + else { + // complete string too wide, try with shorter version + buffer << string_for_size(fBytesPerSecond, sizeBuffer, + sizeof(sizeBuffer)); + buffer << "/s"; + stringWidth = fInfoView->StringWidth(buffer.String()); + if (stringWidth < fInfoView->Bounds().Width()) + fInfoView->SetText(buffer.String()); + } + } else if (!sShowSpeed && fCurrentSize < fExpectedSize) { + double totalBytesPerSecond = (double)(fCurrentSize + - fEstimatedFinishReferenceSize) + * 1000000LL / (system_time() - fEstimatedFinishReferenceTime); + double secondsRemaining = (fExpectedSize - fCurrentSize) + / totalBytesPerSecond; + time_t now = (time_t)real_time_clock(); + time_t finishTime = (time_t)(now + secondsRemaining); + + tm _time; + tm* time = localtime_r(&finishTime, &_time); + int32 year = time->tm_year + 1900; + + char timeText[32]; + time_t secondsPerDay = 24 * 60 * 60; + // TODO: Localization of time string... + if (now < finishTime - secondsPerDay) { + // process is going to take more than a day! + sprintf(timeText, "%0*d:%0*d %0*d/%0*d/%ld", + 2, time->tm_hour, 2, time->tm_min, + 2, time->tm_mon + 1, 2, time->tm_mday, year); + } else { + sprintf(timeText, "%0*d:%0*d", + 2, time->tm_hour, 2, time->tm_min); + } + + BString buffer1("Finish: "); + buffer1 << timeText; + finishTime -= now; + time = gmtime(&finishTime); + + BString buffer2; + if (finishTime > secondsPerDay) { + int64 days = finishTime / secondsPerDay; + if (days == 1) + buffer2 << "Over 1 day"; + else + buffer2 << "Over " << days << " days"; + } else if (finishTime > 60 * 60) { + int64 hours = finishTime / (60 * 60); + if (hours == 1) + buffer2 << "Over 1 hour"; + else + buffer2 << "Over " << hours << " hours"; + } else if (finishTime > 60) { + int64 minutes = finishTime / 60; + if (minutes == 1) + buffer2 << "Over 1 minute"; + else + buffer2 << minutes << " minutes"; + } else { + if (finishTime == 1) + buffer2 << "1 second"; + else + buffer2 << finishTime << " seconds"; + } + + buffer2 << " left"; + + buffer = "("; + buffer << buffer1 << " - " << buffer2 << ")"; + + float stringWidth = fInfoView->StringWidth(buffer.String()); + if (stringWidth < fInfoView->Bounds().Width()) + fInfoView->SetText(buffer.String()); + else { + // complete string too wide, try with shorter version + buffer = "("; + buffer << buffer1 << ")"; + stringWidth = fInfoView->StringWidth(buffer.String()); + if (stringWidth < fInfoView->Bounds().Width()) + fInfoView->SetText(buffer.String()); + } + } +} + + +void +DownloadProgressView::_StartNodeMonitor(const BEntry& entry) +{ + node_ref nref; + if (entry.GetNodeRef(&nref) == B_OK) + watch_node(&nref, B_WATCH_ALL, this); +} + + +void +DownloadProgressView::_StopNodeMonitor() +{ + stop_watching(this); +} + diff --git a/src/apps/webpositive/DownloadProgressView.h b/src/apps/webpositive/DownloadProgressView.h new file mode 100644 index 0000000000..32a021b169 --- /dev/null +++ b/src/apps/webpositive/DownloadProgressView.h @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2010 Stephan Aßmus + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef DOWNLOAD_PROGRESS_VIEW_H +#define DOWNLOAD_PROGRESS_VIEW_H + + +#include +#include +#include + +class BEntry; +class BStatusBar; +class BStringView; +class BWebDownload; +class IconView; +class SmallButton; + + +enum { + SAVE_SETTINGS = 'svst' +}; + + +class DownloadProgressView : public BGroupView { +public: + DownloadProgressView(BWebDownload* download); + DownloadProgressView(const BMessage* archive); + + bool Init(BMessage* archive = NULL); + + status_t SaveSettings(BMessage* archive); + virtual void AttachedToWindow(); + virtual void DetachedFromWindow(); + virtual void AllAttached(); + + virtual void Draw(BRect updateRect); + + virtual void MessageReceived(BMessage* message); + + void ShowContextMenu(BPoint screenWhere); + + BWebDownload* Download() const; + const BString& URL() const; + bool IsMissing() const; + bool IsFinished() const; + + void DownloadFinished(); + void DownloadCanceled(); + + static void SpeedVersusEstimatedFinishTogglePulse(); + +private: + void _UpdateStatus(off_t currentSize, + off_t expectedSize); + void _UpdateStatusText(); + void _StartNodeMonitor(const BEntry& entry); + void _StopNodeMonitor(); + +private: + IconView* fIconView; + BStatusBar* fStatusBar; + BStringView* fInfoView; + SmallButton* fTopButton; + SmallButton* fBottomButton; + BWebDownload* fDownload; + BString fURL; + BPath fPath; + + off_t fCurrentSize; + off_t fExpectedSize; + off_t fLastSpeedReferenceSize; + off_t fEstimatedFinishReferenceSize; + bigtime_t fLastUpdateTime; + bigtime_t fLastSpeedReferenceTime; + bigtime_t fProcessStartTime; + bigtime_t fLastSpeedUpdateTime; + bigtime_t fEstimatedFinishReferenceTime; + static const size_t kBytesPerSecondSlots = 10; + size_t fCurrentBytesPerSecondSlot; + double fBytesPerSecondSlot[kBytesPerSecondSlots]; + double fBytesPerSecond; + + static bigtime_t sLastEstimatedFinishSpeedToggleTime; + static bool sShowSpeed; +}; + +#endif // DOWNLOAD_PROGRESS_VIEW_H diff --git a/src/apps/webpositive/DownloadWindow.cpp b/src/apps/webpositive/DownloadWindow.cpp new file mode 100644 index 0000000000..859722df47 --- /dev/null +++ b/src/apps/webpositive/DownloadWindow.cpp @@ -0,0 +1,573 @@ +/* + * Copyright (C) 2010 Stephan Aßmus + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "DownloadWindow.h" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "BrowserApp.h" +#include "BrowserWindow.h" +#include "DownloadProgressView.h" +#include "SettingsKeys.h" +#include "SettingsMessage.h" +#include "WebDownload.h" +#include "WebPage.h" + + +enum { + INIT = 'init', + OPEN_DOWNLOADS_FOLDER = 'odnf', + REMOVE_FINISHED_DOWNLOADS = 'rmfd', + REMOVE_MISSING_DOWNLOADS = 'rmmd' +}; + + +class DownloadsContainerView : public BGroupView { +public: + DownloadsContainerView() + : + BGroupView(B_VERTICAL, 0.0) + { + SetFlags(Flags() | B_PULSE_NEEDED); + SetViewColor(245, 245, 245); + AddChild(BSpaceLayoutItem::CreateGlue()); + } + + virtual BSize MinSize() + { + BSize minSize = BGroupView::MinSize(); + return BSize(minSize.width, 80); + } + + virtual void Pulse() + { + DownloadProgressView::SpeedVersusEstimatedFinishTogglePulse(); + } + +protected: + virtual void DoLayout() + { + BGroupView::DoLayout(); + if (BScrollBar* scrollBar = ScrollBar(B_VERTICAL)) { + BSize minSize = BGroupView::MinSize(); + float height = Bounds().Height(); + float max = minSize.height - height; + scrollBar->SetRange(0, max); + if (minSize.height > 0) + scrollBar->SetProportion(height / minSize.height); + else + scrollBar->SetProportion(1); + } + } +}; + + +class DownloadContainerScrollView : public BScrollView { +public: + DownloadContainerScrollView(BView* target) + : + BScrollView("Downloads scroll view", target, 0, false, true, + B_NO_BORDER) + { + } + +protected: + virtual void DoLayout() + { + BScrollView::DoLayout(); + // Tweak scroll bar layout to hide part of the frame for better looks. + BScrollBar* scrollBar = ScrollBar(B_VERTICAL); + scrollBar->MoveBy(1, -1); + scrollBar->ResizeBy(0, 2); + Target()->ResizeBy(1, 0); + // Set the scroll steps + if (BView* item = Target()->ChildAt(0)) { + scrollBar->SetSteps(item->MinSize().height + 1, + item->MinSize().height + 1); + } + } +}; + + +// #pragma mark - + + +DownloadWindow::DownloadWindow(BRect frame, bool visible, + SettingsMessage* settings) + : BWindow(frame, "Downloads", + B_TITLED_WINDOW_LOOK, B_NORMAL_WINDOW_FEEL, + B_AUTO_UPDATE_SIZE_LIMITS | B_ASYNCHRONOUS_CONTROLS | B_NOT_ZOOMABLE), + fMinimizeOnClose(false) +{ + SetPulseRate(1000000); + + settings->AddListener(BMessenger(this)); + BPath downloadPath; + if (find_directory(B_DESKTOP_DIRECTORY, &downloadPath) != B_OK) + downloadPath.SetTo("/boot/home/Desktop"); + fDownloadPath = settings->GetValue(kSettingsKeyDownloadPath, + downloadPath.Path()); + settings->SetValue(kSettingsKeyDownloadPath, fDownloadPath); + + SetLayout(new BGroupLayout(B_VERTICAL, 0.0)); + + DownloadsContainerView* downloadsGroupView = new DownloadsContainerView(); + fDownloadViewsLayout = downloadsGroupView->GroupLayout(); + + BMenuBar* menuBar = new BMenuBar("Menu bar"); + BMenu* menu = new BMenu("Downloads"); + menu->AddItem(new BMenuItem("Open downloads folder", + new BMessage(OPEN_DOWNLOADS_FOLDER))); + BMessage* newWindowMessage = new BMessage(NEW_WINDOW); + newWindowMessage->AddString("url", ""); + BMenuItem* newWindowItem = new BMenuItem("New browser window", + newWindowMessage, 'N'); + menu->AddItem(newWindowItem); + newWindowItem->SetTarget(be_app); + menu->AddSeparatorItem(); + menu->AddItem(new BMenuItem("Hide", new BMessage(B_QUIT_REQUESTED), 'D')); + menuBar->AddItem(menu); + + fDownloadsScrollView = new DownloadContainerScrollView(downloadsGroupView); + + fRemoveFinishedButton = new BButton("Remove finished", + new BMessage(REMOVE_FINISHED_DOWNLOADS)); + fRemoveFinishedButton->SetEnabled(false); + + fRemoveMissingButton = new BButton("Remove missing", + new BMessage(REMOVE_MISSING_DOWNLOADS)); + fRemoveMissingButton->SetEnabled(false); + + const float spacing = be_control_look->DefaultItemSpacing(); + + AddChild(BGroupLayoutBuilder(B_VERTICAL, 0.0) + .Add(menuBar) + .Add(fDownloadsScrollView) + .Add(new BSeparatorView(B_HORIZONTAL, B_PLAIN_BORDER)) + .Add(BGroupLayoutBuilder(B_HORIZONTAL, spacing) + .AddGlue() + .Add(fRemoveMissingButton) + .Add(fRemoveFinishedButton) + .SetInsets(12, 5, 12, 5) + ) + ); + + PostMessage(INIT); + + if (!visible) + Hide(); + Show(); +} + + +DownloadWindow::~DownloadWindow() +{ + // Only necessary to save the current progress of unfinished downloads: + _SaveSettings(); +} + + +void +DownloadWindow::DispatchMessage(BMessage* message, BHandler* target) +{ + // We need to intercept mouse down events inside the area of download + // progress views (regardless of whether they have children at the click), + // so that they may display a context menu. + BPoint where; + int32 buttons; + if (message->what == B_MOUSE_DOWN + && message->FindPoint("screen_where", &where) == B_OK + && message->FindInt32("buttons", &buttons) == B_OK + && (buttons & B_SECONDARY_MOUSE_BUTTON) != 0) { + for (int32 i = fDownloadViewsLayout->CountItems() - 1; + BLayoutItem* item = fDownloadViewsLayout->ItemAt(i); i--) { + DownloadProgressView* view = dynamic_cast( + item->View()); + if (!view) + continue; + BPoint viewWhere(where); + view->ConvertFromScreen(&viewWhere); + if (view->Bounds().Contains(viewWhere)) { + view->ShowContextMenu(where); + return; + } + } + } + BWindow::DispatchMessage(message, target); +} + + +void +DownloadWindow::MessageReceived(BMessage* message) +{ + switch (message->what) { + case INIT: + { + _LoadSettings(); + // Small trick to get the correct enabled status of the Remove + // finished button + _DownloadFinished(NULL); + break; + } + case B_DOWNLOAD_ADDED: + { + BWebDownload* download; + if (message->FindPointer("download", reinterpret_cast( + &download)) == B_OK) { + _DownloadStarted(download); + } + break; + } + case B_DOWNLOAD_REMOVED: + { + BWebDownload* download; + if (message->FindPointer("download", reinterpret_cast( + &download)) == B_OK) { + _DownloadFinished(download); + } + break; + } + case OPEN_DOWNLOADS_FOLDER: + { + entry_ref ref; + status_t status = get_ref_for_path(fDownloadPath.String(), &ref); + if (status == B_OK) + status = be_roster->Launch(&ref); + if (status != B_OK && status != B_ALREADY_RUNNING) { + BString errorString("The downloads folder could not be " + "opened.\n\n"); + errorString << "Error: " << strerror(status); + BAlert* alert = new BAlert("Error opening downloads folder", + errorString.String(), "OK"); + alert->Go(NULL); + } + break; + } + case REMOVE_FINISHED_DOWNLOADS: + _RemoveFinishedDownloads(); + break; + case REMOVE_MISSING_DOWNLOADS: + _RemoveMissingDownloads(); + break; + case SAVE_SETTINGS: + _ValidateButtonStatus(); + _SaveSettings(); + break; + + case SETTINGS_VALUE_CHANGED: + { + BString string; + if (message->FindString("name", &string) == B_OK + && string == kSettingsKeyDownloadPath + && message->FindString("value", &string) == B_OK) { + fDownloadPath = string; + } + break; + } + default: + BWindow::MessageReceived(message); + break; + } +} + + +bool +DownloadWindow::QuitRequested() +{ + if (fMinimizeOnClose) { + if (!IsMinimized()) + Minimize(true); + } else { + if (!IsHidden()) + Hide(); + } + return false; +} + + +bool +DownloadWindow::DownloadsInProgress() +{ + bool downloadsInProgress = false; + if (!Lock()) + return downloadsInProgress; + + for (int32 i = fDownloadViewsLayout->CountItems() - 1; + BLayoutItem* item = fDownloadViewsLayout->ItemAt(i); i--) { + DownloadProgressView* view = dynamic_cast( + item->View()); + if (!view) + continue; + if (view->Download() != NULL) { + downloadsInProgress = true; + break; + } + } + + Unlock(); + + return downloadsInProgress; +} + + +void +DownloadWindow::SetMinimizeOnClose(bool minimize) +{ + if (Lock()) { + fMinimizeOnClose = minimize; + Unlock(); + } +} + + +// #pragma mark - private + + +void +DownloadWindow::_DownloadStarted(BWebDownload* download) +{ + download->Start(BPath(fDownloadPath.String())); + + int32 finishedCount = 0; + int32 missingCount = 0; + int32 index = 0; + for (int32 i = fDownloadViewsLayout->CountItems() - 1; + BLayoutItem* item = fDownloadViewsLayout->ItemAt(i); i--) { + DownloadProgressView* view = dynamic_cast( + item->View()); + if (!view) + continue; + if (view->URL() == download->URL()) { + index = i; + view->RemoveSelf(); + delete view; + continue; + } + if (view->IsFinished()) + finishedCount++; + if (view->IsMissing()) + missingCount++; + } + fRemoveFinishedButton->SetEnabled(finishedCount > 0); + fRemoveMissingButton->SetEnabled(missingCount > 0); + DownloadProgressView* view = new DownloadProgressView(download); + if (!view->Init()) { + delete view; + return; + } + fDownloadViewsLayout->AddView(index, view); + + // Scroll new download into view + if (BScrollBar* scrollBar = fDownloadsScrollView->ScrollBar(B_VERTICAL)) { + float min; + float max; + scrollBar->GetRange(&min, &max); + float viewHeight = view->MinSize().height + 1; + float scrollOffset = min + index * viewHeight; + float scrollBarHeight = scrollBar->Bounds().Height() - 1; + float value = scrollBar->Value(); + if (scrollOffset < value) + scrollBar->SetValue(scrollOffset); + else if (scrollOffset + viewHeight > value + scrollBarHeight) { + float diff = scrollOffset + viewHeight - (value + scrollBarHeight); + scrollBar->SetValue(value + diff); + } + } + + _SaveSettings(); + + SetWorkspaces(B_CURRENT_WORKSPACE); + if (IsHidden()) + Show(); +} + + +void +DownloadWindow::_DownloadFinished(BWebDownload* download) +{ + int32 finishedCount = 0; + int32 missingCount = 0; + for (int32 i = 0; + BLayoutItem* item = fDownloadViewsLayout->ItemAt(i); i++) { + DownloadProgressView* view = dynamic_cast( + item->View()); + if (!view) + continue; + if (download && view->Download() == download) { + view->DownloadFinished(); + finishedCount++; + continue; + } + if (view->IsFinished()) + finishedCount++; + if (view->IsMissing()) + missingCount++; + } + fRemoveFinishedButton->SetEnabled(finishedCount > 0); + fRemoveMissingButton->SetEnabled(missingCount > 0); + if (download) + _SaveSettings(); +} + + +void +DownloadWindow::_RemoveFinishedDownloads() +{ + int32 missingCount = 0; + for (int32 i = fDownloadViewsLayout->CountItems() - 1; + BLayoutItem* item = fDownloadViewsLayout->ItemAt(i); i--) { + DownloadProgressView* view = dynamic_cast( + item->View()); + if (!view) + continue; + if (view->IsFinished()) { + view->RemoveSelf(); + delete view; + } else if (view->IsMissing()) + missingCount++; + } + fRemoveFinishedButton->SetEnabled(false); + fRemoveMissingButton->SetEnabled(missingCount > 0); + _SaveSettings(); +} + + +void +DownloadWindow::_RemoveMissingDownloads() +{ + int32 finishedCount = 0; + for (int32 i = fDownloadViewsLayout->CountItems() - 1; + BLayoutItem* item = fDownloadViewsLayout->ItemAt(i); i--) { + DownloadProgressView* view = dynamic_cast( + item->View()); + if (!view) + continue; + if (view->IsMissing()) { + view->RemoveSelf(); + delete view; + } else if (view->IsFinished()) + finishedCount++; + } + fRemoveMissingButton->SetEnabled(false); + fRemoveFinishedButton->SetEnabled(finishedCount > 0); + _SaveSettings(); +} + + +void +DownloadWindow::_ValidateButtonStatus() +{ + int32 finishedCount = 0; + int32 missingCount = 0; + for (int32 i = fDownloadViewsLayout->CountItems() - 1; + BLayoutItem* item = fDownloadViewsLayout->ItemAt(i); i--) { + DownloadProgressView* view = dynamic_cast( + item->View()); + if (!view) + continue; + if (view->IsFinished()) + finishedCount++; + if (view->IsMissing()) + missingCount++; + } + fRemoveFinishedButton->SetEnabled(finishedCount > 0); + fRemoveMissingButton->SetEnabled(missingCount > 0); +} + + +void +DownloadWindow::_SaveSettings() +{ + BFile file; + if (!_OpenSettingsFile(file, B_ERASE_FILE | B_CREATE_FILE | B_WRITE_ONLY)) + return; + BMessage message; + for (int32 i = fDownloadViewsLayout->CountItems() - 1; + BLayoutItem* item = fDownloadViewsLayout->ItemAt(i); i--) { + DownloadProgressView* view = dynamic_cast( + item->View()); + if (!view) + continue; + BMessage downloadArchive; + if (view->SaveSettings(&downloadArchive) == B_OK) + message.AddMessage("download", &downloadArchive); + } + message.Flatten(&file); +} + + +void +DownloadWindow::_LoadSettings() +{ + BFile file; + if (!_OpenSettingsFile(file, B_READ_ONLY)) + return; + BMessage message; + if (message.Unflatten(&file) != B_OK) + return; + BMessage downloadArchive; + for (int32 i = 0; + message.FindMessage("download", i, &downloadArchive) == B_OK; + i++) { + DownloadProgressView* view = new DownloadProgressView( + &downloadArchive); + if (!view->Init(&downloadArchive)) + continue; + fDownloadViewsLayout->AddView(0, view); + } +} + + +bool +DownloadWindow::_OpenSettingsFile(BFile& file, uint32 mode) +{ + BPath path; + if (find_directory(B_USER_SETTINGS_DIRECTORY, &path) != B_OK + || path.Append(kApplicationName) != B_OK + || path.Append("Downloads") != B_OK) { + return false; + } + return file.SetTo(path.Path(), mode) == B_OK; +} + + diff --git a/src/apps/webpositive/DownloadWindow.h b/src/apps/webpositive/DownloadWindow.h new file mode 100644 index 0000000000..d76661ba4e --- /dev/null +++ b/src/apps/webpositive/DownloadWindow.h @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2010 Stephan Aßmus + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef DOWNLOAD_WINDOW_H +#define DOWNLOAD_WINDOW_H + + +#include +#include + +class BButton; +class BFile; +class BGroupLayout; +class BScrollView; +class BWebDownload; +class SettingsMessage; + + +class DownloadWindow : public BWindow { +public: + DownloadWindow(BRect frame, bool visible, + SettingsMessage* settings); + virtual ~DownloadWindow(); + + virtual void DispatchMessage(BMessage* message, + BHandler* target); + virtual void MessageReceived(BMessage* message); + virtual bool QuitRequested(); + + bool DownloadsInProgress(); + void SetMinimizeOnClose(bool minimize); + +private: + void _DownloadStarted(BWebDownload* download); + void _DownloadFinished(BWebDownload* download); + void _RemoveFinishedDownloads(); + void _RemoveMissingDownloads(); + void _ValidateButtonStatus(); + void _SaveSettings(); + void _LoadSettings(); + bool _OpenSettingsFile(BFile& file, uint32 mode); + +private: + BScrollView* fDownloadsScrollView; + BGroupLayout* fDownloadViewsLayout; + BButton* fRemoveFinishedButton; + BButton* fRemoveMissingButton; + BString fDownloadPath; + bool fMinimizeOnClose; +}; + +#endif // DOWNLOAD_WINDOW_H diff --git a/src/apps/webpositive/Jamfile b/src/apps/webpositive/Jamfile new file mode 100644 index 0000000000..c13bb56bf9 --- /dev/null +++ b/src/apps/webpositive/Jamfile @@ -0,0 +1,71 @@ +SubDir HAIKU_TOP src apps webpositive ; + +SubDirSysHdrs $(HAIKU_WEBKIT_HEADERS) ; + +SubDirC++Flags $(defines) -Wno-error=sequence-point ; +SubDirCcFlags $(defines) -Wno-error=sequence-point ; + +# source directories +local sourceDirs = + autocompletion + support + tabview +; + +local sourceDir ; +for sourceDir in $(sourceDirs) { + SEARCH_SOURCE += [ FDirName $(HAIKU_TOP) src apps webpositive $(sourceDir) ] ; +} + +local sources = + # autocompletion + AutoCompleter.cpp + AutoCompleterDefaultImpl.cpp + TextViewCompleter.cpp + + # support + BaseURL.cpp + BitmapButton.cpp + DateTime.cpp + FontSelectionView.cpp + IconButton.cpp + SettingsMessage.cpp + StringForSize.cpp + + # tabview + TabContainerView.cpp + TabManager.cpp + TabView.cpp + + AuthenticationPanel.cpp + BrowserApp.cpp + BrowserWindow.cpp + BrowsingHistory.cpp + CredentialsStorage.cpp + DownloadProgressView.cpp + DownloadWindow.cpp + SettingsKeys.cpp + SettingsWindow.cpp + svn_revision.cpp + URLInputGroup.cpp +; + + +Includes [ FGristFiles $(sources) ] : $(HAIKU_WEBKIT_HEADERS_DEPENDENCY) ; + # Dependency needed to trigger downloading/unzipping the package before + # compiling the files. + +# SVN revision +#local svnRevisionFile = [ FGristFiles svn_revision ] ; +#MakeLocate $(svnRevisionFile) : $(LOCATE_TARGET) ; +#CreateSVNRevisionFile $(svnRevisionFile) ; + +Application WebPositive : + $(sources) + : + $(HAIKU_WEBKIT_LIBS) + $(TARGET_LIBSTDC++) $(HAIKU_LOCALE_LIBS) + be network tracker translation + : + WebPositive.rdef +; diff --git a/src/apps/webpositive/SettingsKeys.cpp b/src/apps/webpositive/SettingsKeys.cpp new file mode 100644 index 0000000000..b72166cd3a --- /dev/null +++ b/src/apps/webpositive/SettingsKeys.cpp @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2010 Stephan Aßmus + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "SettingsKeys.h" + +const char* kSettingsKeyDownloadPath = "download path"; +const char* kSettingsKeyShowTabsIfSinglePageOpen + = "show tabs if single page open"; +const char* kSettingsKeyAutoHideInterfaceInFullscreenMode + = "auto hide interface in full screen mode"; +const char* kSettingsKeyAutoHidePointer = "auto hide pointer"; +const char* kSettingsKeyShowHomeButton = "show home button"; + +const char* kSettingsKeyNewWindowPolicy = "new window policy"; +const char* kSettingsKeyNewTabPolicy = "new tab policy"; +const char* kSettingsKeyStartPageURL = "start page url"; +const char* kSettingsKeySearchPageURL = "search page url"; + + +const char* kDefaultDownloadPath = "/boot/home/Desktop/"; +const char* kDefaultStartPageURL + = "file:///boot/home/config/settings/WebPositive/LoaderPages/Welcome"; +const char* kDefaultSearchPageURL = "http://www.google.com"; + +const char* kSettingsKeyUseProxy = "use http proxy"; +const char* kSettingsKeyProxyAddress = "http proxy address"; +const char* kSettingsKeyProxyPort = "http proxy port"; diff --git a/src/apps/webpositive/SettingsKeys.h b/src/apps/webpositive/SettingsKeys.h new file mode 100644 index 0000000000..1a95bbfe89 --- /dev/null +++ b/src/apps/webpositive/SettingsKeys.h @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2010 Stephan Aßmus + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef SETTINGS_KEYS_H +#define SETTINGS_KEYS_H + +#include + + +extern const char* kSettingsKeyDownloadPath; +extern const char* kSettingsKeyShowTabsIfSinglePageOpen; +extern const char* kSettingsKeyAutoHideInterfaceInFullscreenMode; +extern const char* kSettingsKeyAutoHidePointer; +extern const char* kSettingsKeyShowHomeButton; + +extern const char* kSettingsKeyNewWindowPolicy; +extern const char* kSettingsKeyNewTabPolicy; +extern const char* kSettingsKeyStartPageURL; +extern const char* kSettingsKeySearchPageURL; + +extern const char* kDefaultDownloadPath; +extern const char* kDefaultStartPageURL; +extern const char* kDefaultSearchPageURL; + +extern const char* kSettingsKeyUseProxy; +extern const char* kSettingsKeyProxyAddress; +extern const char* kSettingsKeyProxyPort; + +#endif // SETTINGS_KEYS_H diff --git a/src/apps/webpositive/SettingsWindow.cpp b/src/apps/webpositive/SettingsWindow.cpp new file mode 100644 index 0000000000..66639f24b0 --- /dev/null +++ b/src/apps/webpositive/SettingsWindow.cpp @@ -0,0 +1,885 @@ +/* + * Copyright (C) 2010 Stephan Aßmus + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include "SettingsWindow.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "BrowserApp.h" +#include "BrowsingHistory.h" +#include "BrowserWindow.h" +#include "FontSelectionView.h" +#include "SettingsKeys.h" +#include "SettingsMessage.h" +#include "WebSettings.h" + + +#undef B_TRANSLATION_CONTEXT +#define B_TRANSLATION_CONTEXT "Settings Window" + +enum { + MSG_APPLY = 'aply', + MSG_CANCEL = 'cncl', + MSG_REVERT = 'rvrt', + + MSG_START_PAGE_CHANGED = 'hpch', + MSG_SEARCH_PAGE_CHANGED = 'spch', + MSG_DOWNLOAD_FOLDER_CHANGED = 'dnfc', + MSG_NEW_WINDOWS_BEHAVIOR_CHANGED = 'nwbc', + MSG_NEW_TABS_BEHAVIOR_CHANGED = 'ntbc', + MSG_HISTORY_MENU_DAYS_CHANGED = 'digm', + MSG_TAB_DISPLAY_BEHAVIOR_CHANGED = 'tdbc', + MSG_AUTO_HIDE_INTERFACE_BEHAVIOR_CHANGED = 'ahic', + MSG_AUTO_HIDE_POINTER_BEHAVIOR_CHANGED = 'ahpc', + MSG_SHOW_HOME_BUTTON_CHANGED = 'shbc', + + MSG_STANDARD_FONT_CHANGED = 'stfc', + MSG_SERIF_FONT_CHANGED = 'sefc', + MSG_SANS_SERIF_FONT_CHANGED = 'ssfc', + MSG_FIXED_FONT_CHANGED = 'ffch', + + MSG_STANDARD_FONT_SIZE_SELECTED = 'sfss', + MSG_FIXED_FONT_SIZE_SELECTED = 'ffss', + + MSG_USE_PROXY_CHANGED = 'upsc', + MSG_PROXY_ADDRESS_CHANGED = 'psac', + MSG_PROXY_PORT_CHANGED = 'pspc', +}; + +static const int32 kDefaultFontSize = 14; + + +SettingsWindow::SettingsWindow(BRect frame, SettingsMessage* settings) + : + BWindow(frame, B_TRANSLATE("Settings"), B_TITLED_WINDOW_LOOK, + B_NORMAL_WINDOW_FEEL, B_AUTO_UPDATE_SIZE_LIMITS + | B_ASYNCHRONOUS_CONTROLS | B_NOT_ZOOMABLE), + fSettings(settings) +{ + SetLayout(new BGroupLayout(B_VERTICAL)); + + fApplyButton = new BButton(B_TRANSLATE("Apply"), new BMessage(MSG_APPLY)); + fCancelButton = new BButton(B_TRANSLATE("Cancel"), + new BMessage(MSG_CANCEL)); + fRevertButton = new BButton(B_TRANSLATE("Revert"), + new BMessage(MSG_REVERT)); + + float spacing = be_control_look->DefaultItemSpacing(); + + BTabView* tabView = new BTabView("settings pages", B_WIDTH_FROM_LABEL); + + AddChild(BGroupLayoutBuilder(B_VERTICAL, spacing) + .Add(tabView) + .Add(BGroupLayoutBuilder(B_HORIZONTAL, spacing) + .Add(fRevertButton) + .AddGlue() + .Add(fCancelButton) + .Add(fApplyButton) + ) + .SetInsets(spacing, spacing, spacing, spacing) + ); + + tabView->AddTab(_CreateGeneralPage(spacing)); + tabView->AddTab(_CreateFontsPage(spacing)); + tabView->AddTab(_CreateProxyPage(spacing)); + + _SetupFontSelectionView(fStandardFontView, + new BMessage(MSG_STANDARD_FONT_CHANGED)); + _SetupFontSelectionView(fSerifFontView, + new BMessage(MSG_SERIF_FONT_CHANGED)); + _SetupFontSelectionView(fSansSerifFontView, + new BMessage(MSG_SANS_SERIF_FONT_CHANGED)); + _SetupFontSelectionView(fFixedFontView, + new BMessage(MSG_FIXED_FONT_CHANGED)); + + fApplyButton->MakeDefault(true); + + if (!frame.IsValid()) + CenterOnScreen(); + + // load settings from disk + _RevertSettings(); + // apply to WebKit + _ApplySettings(); + + // Start hidden + Hide(); + Show(); +} + + +SettingsWindow::~SettingsWindow() +{ + RemoveHandler(fStandardFontView); + delete fStandardFontView; + RemoveHandler(fSerifFontView); + delete fSerifFontView; + RemoveHandler(fSansSerifFontView); + delete fSansSerifFontView; + RemoveHandler(fFixedFontView); + delete fFixedFontView; +} + + +void +SettingsWindow::MessageReceived(BMessage* message) +{ + switch (message->what) { + case MSG_APPLY: + _ApplySettings(); + break; + case MSG_CANCEL: + _RevertSettings(); + PostMessage(B_QUIT_REQUESTED); + break; + case MSG_REVERT: + _RevertSettings(); + break; + + case MSG_STANDARD_FONT_SIZE_SELECTED: + { + int32 size = _SizesMenuValue(fStandardSizesMenu->Menu()); + fStandardFontView->SetSize(size); + fSerifFontView->SetSize(size); + fSansSerifFontView->SetSize(size); + _ValidateControlsEnabledStatus(); + break; + } + case MSG_FIXED_FONT_SIZE_SELECTED: + { + int32 size = _SizesMenuValue(fFixedSizesMenu->Menu()); + fFixedFontView->SetSize(size); + _ValidateControlsEnabledStatus(); + break; + } + + case MSG_START_PAGE_CHANGED: + case MSG_SEARCH_PAGE_CHANGED: + case MSG_DOWNLOAD_FOLDER_CHANGED: + case MSG_NEW_WINDOWS_BEHAVIOR_CHANGED: + case MSG_NEW_TABS_BEHAVIOR_CHANGED: + case MSG_HISTORY_MENU_DAYS_CHANGED: + case MSG_TAB_DISPLAY_BEHAVIOR_CHANGED: + case MSG_AUTO_HIDE_INTERFACE_BEHAVIOR_CHANGED: + case MSG_AUTO_HIDE_POINTER_BEHAVIOR_CHANGED: + case MSG_SHOW_HOME_BUTTON_CHANGED: + case MSG_STANDARD_FONT_CHANGED: + case MSG_SERIF_FONT_CHANGED: + case MSG_SANS_SERIF_FONT_CHANGED: + case MSG_FIXED_FONT_CHANGED: + case MSG_USE_PROXY_CHANGED: + case MSG_PROXY_ADDRESS_CHANGED: + case MSG_PROXY_PORT_CHANGED: + // TODO: Some settings could change live, some others not? + _ValidateControlsEnabledStatus(); + break; + + default: + BWindow::MessageReceived(message); + break; + } +} + + +bool +SettingsWindow::QuitRequested() +{ + if (!IsHidden()) + Hide(); + return false; +} + + +void +SettingsWindow::Show() +{ + // When showing the window, the this is always the + // point to which we can revert the settings. + _RevertSettings(); + BWindow::Show(); +} + + +// #pragma mark - private + + +BView* +SettingsWindow::_CreateGeneralPage(float spacing) +{ + fStartPageControl = new BTextControl("start page", + B_TRANSLATE("Start page:"), "", new BMessage(MSG_START_PAGE_CHANGED)); + fStartPageControl->SetModificationMessage( + new BMessage(MSG_START_PAGE_CHANGED)); + fStartPageControl->SetText( + fSettings->GetValue(kSettingsKeyStartPageURL, kDefaultStartPageURL)); + + fSearchPageControl = new BTextControl("search page", + B_TRANSLATE("Search page:"), "", + new BMessage(MSG_SEARCH_PAGE_CHANGED)); + fSearchPageControl->SetModificationMessage( + new BMessage(MSG_SEARCH_PAGE_CHANGED)); + fSearchPageControl->SetText( + fSettings->GetValue(kSettingsKeySearchPageURL, kDefaultSearchPageURL)); + + fDownloadFolderControl = new BTextControl("download folder", + B_TRANSLATE("Download folder:"), "", + new BMessage(MSG_DOWNLOAD_FOLDER_CHANGED)); + fDownloadFolderControl->SetModificationMessage( + new BMessage(MSG_DOWNLOAD_FOLDER_CHANGED)); + fDownloadFolderControl->SetText( + fSettings->GetValue(kSettingsKeyDownloadPath, kDefaultDownloadPath)); + + fNewWindowBehaviorOpenHomeItem = new BMenuItem( + B_TRANSLATE("Open start page"), + new BMessage(MSG_NEW_WINDOWS_BEHAVIOR_CHANGED)); + fNewWindowBehaviorOpenSearchItem = new BMenuItem( + B_TRANSLATE("Open search page"), + new BMessage(MSG_NEW_WINDOWS_BEHAVIOR_CHANGED)); + fNewWindowBehaviorOpenBlankItem = new BMenuItem( + B_TRANSLATE("Open blank page"), + new BMessage(MSG_NEW_WINDOWS_BEHAVIOR_CHANGED)); + + fNewTabBehaviorCloneCurrentItem = new BMenuItem( + B_TRANSLATE("Clone current page"), + new BMessage(MSG_NEW_TABS_BEHAVIOR_CHANGED)); + fNewTabBehaviorOpenHomeItem = new BMenuItem( + B_TRANSLATE("Open start page"), + new BMessage(MSG_NEW_TABS_BEHAVIOR_CHANGED)); + fNewTabBehaviorOpenSearchItem = new BMenuItem( + B_TRANSLATE("Open search page"), + new BMessage(MSG_NEW_TABS_BEHAVIOR_CHANGED)); + fNewTabBehaviorOpenBlankItem = new BMenuItem( + B_TRANSLATE("Open blank page"), + new BMessage(MSG_NEW_TABS_BEHAVIOR_CHANGED)); + + fNewWindowBehaviorOpenHomeItem->SetMarked(true); + fNewTabBehaviorOpenBlankItem->SetMarked(true); + + BPopUpMenu* newWindowBehaviorMenu = new BPopUpMenu("New windows"); + newWindowBehaviorMenu->AddItem(fNewWindowBehaviorOpenHomeItem); + newWindowBehaviorMenu->AddItem(fNewWindowBehaviorOpenSearchItem); + newWindowBehaviorMenu->AddItem(fNewWindowBehaviorOpenBlankItem); + fNewWindowBehaviorMenu = new BMenuField("new window behavior", + B_TRANSLATE("New windows:"), newWindowBehaviorMenu); + + BPopUpMenu* newTabBehaviorMenu = new BPopUpMenu("New tabs"); + newTabBehaviorMenu->AddItem(fNewTabBehaviorOpenBlankItem); + newTabBehaviorMenu->AddItem(fNewTabBehaviorOpenHomeItem); + newTabBehaviorMenu->AddItem(fNewTabBehaviorOpenSearchItem); + newTabBehaviorMenu->AddItem(fNewTabBehaviorCloneCurrentItem); + fNewTabBehaviorMenu = new BMenuField("new tab behavior", + B_TRANSLATE("New tabs:"), newTabBehaviorMenu); + + fDaysInHistoryMenuControl = new BTextControl("days in history", + B_TRANSLATE("Number of days to keep links in History menu:"), "", + new BMessage(MSG_HISTORY_MENU_DAYS_CHANGED)); + fDaysInHistoryMenuControl->SetModificationMessage( + new BMessage(MSG_HISTORY_MENU_DAYS_CHANGED)); + BString maxHistoryAge; + maxHistoryAge << BrowsingHistory::DefaultInstance()->MaxHistoryItemAge(); + fDaysInHistoryMenuControl->SetText(maxHistoryAge.String()); + for (uchar i = 0; i < '0'; i++) + fDaysInHistoryMenuControl->TextView()->DisallowChar(i); + for (uchar i = '9' + 1; i <= 128; i++) + fDaysInHistoryMenuControl->TextView()->DisallowChar(i); + + fShowTabsIfOnlyOnePage = new BCheckBox("show tabs if only one page", + B_TRANSLATE("Show tabs if only one page is open."), + new BMessage(MSG_TAB_DISPLAY_BEHAVIOR_CHANGED)); + fShowTabsIfOnlyOnePage->SetValue(B_CONTROL_ON); + + fAutoHideInterfaceInFullscreenMode = new BCheckBox("auto-hide interface", + B_TRANSLATE("Auto-hide interface in fullscreen mode."), + new BMessage(MSG_AUTO_HIDE_INTERFACE_BEHAVIOR_CHANGED)); + fAutoHideInterfaceInFullscreenMode->SetValue(B_CONTROL_OFF); + + fAutoHidePointer = new BCheckBox("auto-hide pointer", + B_TRANSLATE("Auto-hide mouse pointer."), + new BMessage(MSG_AUTO_HIDE_POINTER_BEHAVIOR_CHANGED)); + fAutoHidePointer->SetValue(B_CONTROL_OFF); + + fShowHomeButton = new BCheckBox("show home button", + B_TRANSLATE("Show Home Button"), + new BMessage(MSG_SHOW_HOME_BUTTON_CHANGED)); + fShowHomeButton->SetValue(B_CONTROL_ON); + + BView* view = BGroupLayoutBuilder(B_VERTICAL, spacing / 2) + .Add(BGridLayoutBuilder(spacing / 2, spacing / 2) + .Add(fStartPageControl->CreateLabelLayoutItem(), 0, 0) + .Add(fStartPageControl->CreateTextViewLayoutItem(), 1, 0) + + .Add(fSearchPageControl->CreateLabelLayoutItem(), 0, 1) + .Add(fSearchPageControl->CreateTextViewLayoutItem(), 1, 1) + + .Add(fDownloadFolderControl->CreateLabelLayoutItem(), 0, 2) + .Add(fDownloadFolderControl->CreateTextViewLayoutItem(), 1, 2) + + .Add(fNewWindowBehaviorMenu->CreateLabelLayoutItem(), 0, 3) + .Add(fNewWindowBehaviorMenu->CreateMenuBarLayoutItem(), 1, 3) + + .Add(fNewTabBehaviorMenu->CreateLabelLayoutItem(), 0, 4) + .Add(fNewTabBehaviorMenu->CreateMenuBarLayoutItem(), 1, 4) + ) + .Add(BSpaceLayoutItem::CreateHorizontalStrut(spacing)) + .Add(new BSeparatorView(B_HORIZONTAL, B_PLAIN_BORDER)) + .Add(BSpaceLayoutItem::CreateHorizontalStrut(spacing)) + .Add(fShowTabsIfOnlyOnePage) + .Add(fAutoHideInterfaceInFullscreenMode) + .Add(fAutoHidePointer) + .Add(fShowHomeButton) + .Add(fDaysInHistoryMenuControl) + .Add(BSpaceLayoutItem::CreateHorizontalStrut(spacing)) + + .SetInsets(spacing, spacing, spacing, spacing) + + .TopView() + ; + view->SetName(B_TRANSLATE("General")); + return view; +} + + +BView* +SettingsWindow::_CreateFontsPage(float spacing) +{ + fStandardFontView = new FontSelectionView("standard", + B_TRANSLATE("Standard font:"), true, be_plain_font); + BFont defaultSerifFont = _FindDefaultSerifFont(); + fSerifFontView = new FontSelectionView("serif", + B_TRANSLATE("Serif font:"), true, &defaultSerifFont); + fSansSerifFontView = new FontSelectionView("sans serif", + B_TRANSLATE("Sans serif font:"), true, be_plain_font); + fFixedFontView = new FontSelectionView("fixed", + B_TRANSLATE("Fixed font:"), true, be_fixed_font); + + fStandardSizesMenu = new BMenuField("standard font size", + B_TRANSLATE("Default standard font size:"), new BPopUpMenu("sizes"), + B_WILL_DRAW); + _BuildSizesMenu(fStandardSizesMenu->Menu(), + MSG_STANDARD_FONT_SIZE_SELECTED); + + fFixedSizesMenu = new BMenuField("fixed font size", + B_TRANSLATE("Default fixed font size:"), new BPopUpMenu("sizes"), + B_WILL_DRAW); + _BuildSizesMenu(fFixedSizesMenu->Menu(), MSG_FIXED_FONT_SIZE_SELECTED); + + BView* view = BGridLayoutBuilder(spacing / 2, spacing / 2) + .Add(fStandardFontView->CreateFontsLabelLayoutItem(), 0, 0) + .Add(fStandardFontView->CreateFontsMenuBarLayoutItem(), 1, 0) + .Add(fStandardFontView->PreviewBox(), 0, 1, 2) + .Add(BSpaceLayoutItem::CreateHorizontalStrut(spacing), 0, 2, 2) + + .Add(fSerifFontView->CreateFontsLabelLayoutItem(), 0, 3) + .Add(fSerifFontView->CreateFontsMenuBarLayoutItem(), 1, 3) + .Add(fSerifFontView->PreviewBox(), 0, 4, 2) + .Add(BSpaceLayoutItem::CreateHorizontalStrut(spacing), 0, 5, 2) + + .Add(fSansSerifFontView->CreateFontsLabelLayoutItem(), 0, 6) + .Add(fSansSerifFontView->CreateFontsMenuBarLayoutItem(), 1, 6) + .Add(fSansSerifFontView->PreviewBox(), 0, 7, 2) + .Add(BSpaceLayoutItem::CreateHorizontalStrut(spacing), 0, 8, 2) + + .Add(fFixedFontView->CreateFontsLabelLayoutItem(), 0, 9) + .Add(fFixedFontView->CreateFontsMenuBarLayoutItem(), 1, 9) + .Add(fFixedFontView->PreviewBox(), 0, 10, 2) + .Add(BSpaceLayoutItem::CreateHorizontalStrut(spacing), 0, 11, 2) + + .Add(fStandardSizesMenu->CreateLabelLayoutItem(), 0, 12) + .Add(fStandardSizesMenu->CreateMenuBarLayoutItem(), 1, 12) + .Add(fFixedSizesMenu->CreateLabelLayoutItem(), 0, 13) + .Add(fFixedSizesMenu->CreateMenuBarLayoutItem(), 1, 13) + + .SetInsets(spacing, spacing, spacing, spacing) + + .View() + ; + + view->SetName(B_TRANSLATE("Fonts")); + return view; +} + + +BView* +SettingsWindow::_CreateProxyPage(float spacing) +{ + fUseProxyCheckBox = new BCheckBox("use proxy", + B_TRANSLATE("Use proxy server to connect to the internet."), + new BMessage(MSG_USE_PROXY_CHANGED)); + fUseProxyCheckBox->SetValue(B_CONTROL_ON); + + fProxyAddressControl = new BTextControl("proxy address", + B_TRANSLATE("Proxy server address:"), "", + new BMessage(MSG_PROXY_ADDRESS_CHANGED)); + fProxyAddressControl->SetModificationMessage( + new BMessage(MSG_PROXY_ADDRESS_CHANGED)); + fProxyAddressControl->SetText( + fSettings->GetValue(kSettingsKeyProxyAddress, "")); + + fProxyPortControl = new BTextControl("proxy port", + B_TRANSLATE("Proxy server port:"), "", + new BMessage(MSG_PROXY_PORT_CHANGED)); + fProxyPortControl->SetModificationMessage( + new BMessage(MSG_PROXY_PORT_CHANGED)); + fProxyPortControl->SetText( + fSettings->GetValue(kSettingsKeyProxyAddress, "")); + + BView* view = BGroupLayoutBuilder(B_VERTICAL, spacing / 2) + .Add(fUseProxyCheckBox) + .Add(BGridLayoutBuilder(spacing / 2, spacing / 2) + .Add(fProxyAddressControl->CreateLabelLayoutItem(), 0, 0) + .Add(fProxyAddressControl->CreateTextViewLayoutItem(), 1, 0) + + .Add(fProxyPortControl->CreateLabelLayoutItem(), 0, 1) + .Add(fProxyPortControl->CreateTextViewLayoutItem(), 1, 1) + ) + .Add(BSpaceLayoutItem::CreateGlue()) + + .SetInsets(spacing, spacing, spacing, spacing) + + .TopView() + ; + view->SetName(B_TRANSLATE("Proxy server")); + return view; +} + + +void +SettingsWindow::_BuildSizesMenu(BMenu* menu, uint32 messageWhat) +{ + const float kMinSize = 8.0; + const float kMaxSize = 18.0; + + const int32 kSizes[] = {7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 18, 21, 24, 0}; + + for (int32 i = 0; kSizes[i]; i++) { + int32 size = kSizes[i]; + if (size < kMinSize || size > kMaxSize) + continue; + + char label[32]; + snprintf(label, sizeof(label), "%ld", size); + + BMessage* message = new BMessage(messageWhat); + message->AddInt32("size", size); + + BMenuItem* item = new BMenuItem(label, message); + + menu->AddItem(item); + item->SetTarget(this); + } +} + + +void +SettingsWindow::_SetupFontSelectionView(FontSelectionView* view, + BMessage* message) +{ + AddHandler(view); + view->AttachedToLooper(); + view->SetMessage(message); + view->SetTarget(this); +} + + +// #pragma mark - + + +bool +SettingsWindow::_CanApplySettings() const +{ + bool canApply = false; + + // General settings + canApply = canApply || (strcmp(fStartPageControl->Text(), + fSettings->GetValue(kSettingsKeyStartPageURL, + kDefaultStartPageURL)) != 0); + + canApply = canApply || (strcmp(fSearchPageControl->Text(), + fSettings->GetValue(kSettingsKeySearchPageURL, + kDefaultSearchPageURL)) != 0); + + canApply = canApply || (strcmp(fDownloadFolderControl->Text(), + fSettings->GetValue(kSettingsKeyDownloadPath, + kDefaultDownloadPath)) != 0); + + canApply = canApply || ((fShowTabsIfOnlyOnePage->Value() == B_CONTROL_ON) + != fSettings->GetValue(kSettingsKeyShowTabsIfSinglePageOpen, true)); + + canApply = canApply || ( + (fAutoHideInterfaceInFullscreenMode->Value() == B_CONTROL_ON) + != fSettings->GetValue(kSettingsKeyAutoHideInterfaceInFullscreenMode, + false)); + + canApply = canApply || ( + (fAutoHidePointer->Value() == B_CONTROL_ON) + != fSettings->GetValue(kSettingsKeyAutoHidePointer, false)); + + canApply = canApply || ((fShowHomeButton->Value() == B_CONTROL_ON) + != fSettings->GetValue(kSettingsKeyShowHomeButton, true)); + + canApply = canApply || (_MaxHistoryAge() + != BrowsingHistory::DefaultInstance()->MaxHistoryItemAge()); + + // New window policy + canApply = canApply || (_NewWindowPolicy() + != fSettings->GetValue(kSettingsKeyNewWindowPolicy, + (uint32)OpenStartPage)); + + // New tab policy + canApply = canApply || (_NewTabPolicy() + != fSettings->GetValue(kSettingsKeyNewTabPolicy, + (uint32)OpenBlankPage)); + + // Font settings + canApply = canApply || (fStandardFontView->Font() + != fSettings->GetValue("standard font", *be_plain_font)); + + canApply = canApply || (fSerifFontView->Font() + != fSettings->GetValue("serif font", _FindDefaultSerifFont())); + + canApply = canApply || (fSansSerifFontView->Font() + != fSettings->GetValue("sans serif font", *be_plain_font)); + + canApply = canApply || (fFixedFontView->Font() + != fSettings->GetValue("fixed font", *be_fixed_font)); + + canApply = canApply || (_SizesMenuValue(fStandardSizesMenu->Menu()) + != fSettings->GetValue("standard font size", kDefaultFontSize)); + + canApply = canApply || (_SizesMenuValue(fFixedSizesMenu->Menu()) + != fSettings->GetValue("fixed font size", kDefaultFontSize)); + + // Proxy settings + canApply = canApply || ((fUseProxyCheckBox->Value() == B_CONTROL_ON) + != fSettings->GetValue(kSettingsKeyUseProxy, false)); + + canApply = canApply || (strcmp(fProxyAddressControl->Text(), + fSettings->GetValue(kSettingsKeyProxyAddress, "")) != 0); + + canApply = canApply || (_ProxyPort() + != fSettings->GetValue(kSettingsKeyProxyPort, (uint32)0)); + + return canApply; +} + + +void +SettingsWindow::_ApplySettings() +{ + // Store general settings + int32 maxHistoryAge = _MaxHistoryAge(); + BString text; + text << maxHistoryAge; + fDaysInHistoryMenuControl->SetText(text.String()); + BrowsingHistory::DefaultInstance()->SetMaxHistoryItemAge(maxHistoryAge); + + fSettings->SetValue(kSettingsKeyStartPageURL, fStartPageControl->Text()); + fSettings->SetValue(kSettingsKeySearchPageURL, fSearchPageControl->Text()); + fSettings->SetValue(kSettingsKeyDownloadPath, fDownloadFolderControl->Text()); + fSettings->SetValue(kSettingsKeyShowTabsIfSinglePageOpen, + fShowTabsIfOnlyOnePage->Value() == B_CONTROL_ON); + fSettings->SetValue(kSettingsKeyAutoHideInterfaceInFullscreenMode, + fAutoHideInterfaceInFullscreenMode->Value() == B_CONTROL_ON); + fSettings->SetValue(kSettingsKeyAutoHidePointer, + fAutoHidePointer->Value() == B_CONTROL_ON); + fSettings->SetValue(kSettingsKeyShowHomeButton, + fShowHomeButton->Value() == B_CONTROL_ON); + + // New page policies + fSettings->SetValue(kSettingsKeyNewWindowPolicy, _NewWindowPolicy()); + fSettings->SetValue(kSettingsKeyNewTabPolicy, _NewTabPolicy()); + + // Store fond settings + fSettings->SetValue("standard font", fStandardFontView->Font()); + fSettings->SetValue("serif font", fSerifFontView->Font()); + fSettings->SetValue("sans serif font", fSansSerifFontView->Font()); + fSettings->SetValue("fixed font", fFixedFontView->Font()); + int32 standardFontSize = _SizesMenuValue(fStandardSizesMenu->Menu()); + int32 fixedFontSize = _SizesMenuValue(fFixedSizesMenu->Menu()); + fSettings->SetValue("standard font size", standardFontSize); + fSettings->SetValue("fixed font size", fixedFontSize); + + // Store proxy settings + + fSettings->SetValue(kSettingsKeyUseProxy, + fUseProxyCheckBox->Value() == B_CONTROL_ON); + fSettings->SetValue(kSettingsKeyProxyAddress, + fProxyAddressControl->Text()); + uint32 proxyPort = _ProxyPort(); + fSettings->SetValue(kSettingsKeyProxyPort, proxyPort); + + fSettings->Save(); + + // Apply settings to default web page settings. + BWebSettings::Default()->SetStandardFont(fStandardFontView->Font()); + BWebSettings::Default()->SetSerifFont(fSerifFontView->Font()); + BWebSettings::Default()->SetSansSerifFont(fSansSerifFontView->Font()); + BWebSettings::Default()->SetFixedFont(fFixedFontView->Font()); + BWebSettings::Default()->SetDefaultStandardFontSize(standardFontSize); + BWebSettings::Default()->SetDefaultFixedFontSize(fixedFontSize); + + if (fUseProxyCheckBox->Value() == B_CONTROL_ON) { + BWebSettings::Default()->SetProxyInfo(fProxyAddressControl->Text(), + proxyPort, B_PROXY_TYPE_HTTP, "", ""); + } else + BWebSettings::Default()->SetProxyInfo(); + + // This will find all currently instantiated page settings and apply + // the default values, unless the page settings have local overrides. + BWebSettings::Default()->Apply(); + + + _ValidateControlsEnabledStatus(); +} + + +void +SettingsWindow::_RevertSettings() +{ + fStartPageControl->SetText( + fSettings->GetValue(kSettingsKeyStartPageURL, kDefaultStartPageURL)); + + fSearchPageControl->SetText( + fSettings->GetValue(kSettingsKeySearchPageURL, kDefaultSearchPageURL)); + + fDownloadFolderControl->SetText( + fSettings->GetValue(kSettingsKeyDownloadPath, kDefaultDownloadPath)); + fShowTabsIfOnlyOnePage->SetValue( + fSettings->GetValue(kSettingsKeyShowTabsIfSinglePageOpen, true)); + fAutoHideInterfaceInFullscreenMode->SetValue( + fSettings->GetValue(kSettingsKeyAutoHideInterfaceInFullscreenMode, + false)); + fAutoHidePointer->SetValue( + fSettings->GetValue(kSettingsKeyAutoHidePointer, false)); + fShowHomeButton->SetValue( + fSettings->GetValue(kSettingsKeyShowHomeButton, true)); + + BString text; + text << BrowsingHistory::DefaultInstance()->MaxHistoryItemAge(); + fDaysInHistoryMenuControl->SetText(text.String()); + + // New window policy + uint32 newWindowPolicy = fSettings->GetValue(kSettingsKeyNewWindowPolicy, + (uint32)OpenStartPage); + switch (newWindowPolicy) { + default: + case OpenStartPage: + fNewWindowBehaviorOpenHomeItem->SetMarked(true); + break; + case OpenSearchPage: + fNewWindowBehaviorOpenSearchItem->SetMarked(true); + break; + case OpenBlankPage: + fNewWindowBehaviorOpenBlankItem->SetMarked(true); + break; + } + + // New tab policy + uint32 newTabPolicy = fSettings->GetValue(kSettingsKeyNewTabPolicy, + (uint32)OpenBlankPage); + switch (newTabPolicy) { + default: + case OpenBlankPage: + fNewTabBehaviorOpenBlankItem->SetMarked(true); + break; + case OpenStartPage: + fNewTabBehaviorOpenHomeItem->SetMarked(true); + break; + case OpenSearchPage: + fNewTabBehaviorOpenSearchItem->SetMarked(true); + break; + case CloneCurrentPage: + fNewTabBehaviorCloneCurrentItem->SetMarked(true); + break; + } + + // Font settings + int32 defaultFontSize = fSettings->GetValue("standard font size", + kDefaultFontSize); + int32 defaultFixedFontSize = fSettings->GetValue("fixed font size", + kDefaultFontSize); + + _SetSizesMenuValue(fStandardSizesMenu->Menu(), defaultFontSize); + _SetSizesMenuValue(fFixedSizesMenu->Menu(), defaultFixedFontSize); + + fStandardFontView->SetFont(fSettings->GetValue("standard font", + *be_plain_font), defaultFontSize); + fSerifFontView->SetFont(fSettings->GetValue("serif font", + _FindDefaultSerifFont()), defaultFontSize); + fSansSerifFontView->SetFont(fSettings->GetValue("sans serif font", + *be_plain_font), defaultFontSize); + fFixedFontView->SetFont(fSettings->GetValue("fixed font", + *be_fixed_font), defaultFixedFontSize); + + // Proxy settings + fUseProxyCheckBox->SetValue(fSettings->GetValue(kSettingsKeyUseProxy, + false)); + fProxyAddressControl->SetText(fSettings->GetValue(kSettingsKeyProxyAddress, + "")); + text = ""; + text << fSettings->GetValue(kSettingsKeyProxyPort, (uint32)0); + fProxyPortControl->SetText(text.String()); + + _ValidateControlsEnabledStatus(); +} + + +void +SettingsWindow::_ValidateControlsEnabledStatus() +{ + bool canApply = _CanApplySettings(); + fApplyButton->SetEnabled(canApply); + fRevertButton->SetEnabled(canApply); + // Let the Cancel button be enabled always, as another way to close the + // window... + fCancelButton->SetEnabled(true); + + bool useProxy = fUseProxyCheckBox->Value() == B_CONTROL_ON; + fProxyAddressControl->SetEnabled(useProxy); + fProxyPortControl->SetEnabled(useProxy); +} + + +// #pragma mark - + + +uint32 +SettingsWindow::_NewWindowPolicy() const +{ + uint32 newWindowPolicy = OpenStartPage; + BMenuItem* markedItem = fNewWindowBehaviorMenu->Menu()->FindMarked(); + if (markedItem == fNewWindowBehaviorOpenSearchItem) + newWindowPolicy = OpenSearchPage; + else if (markedItem == fNewWindowBehaviorOpenBlankItem) + newWindowPolicy = OpenBlankPage; + return newWindowPolicy; +} + + +uint32 +SettingsWindow::_NewTabPolicy() const +{ + uint32 newTabPolicy = OpenBlankPage; + BMenuItem* markedItem = fNewTabBehaviorMenu->Menu()->FindMarked(); + if (markedItem == fNewTabBehaviorCloneCurrentItem) + newTabPolicy = CloneCurrentPage; + else if (markedItem == fNewTabBehaviorOpenHomeItem) + newTabPolicy = OpenStartPage; + else if (markedItem == fNewTabBehaviorOpenSearchItem) + newTabPolicy = OpenSearchPage; + return newTabPolicy; +} + + +int32 +SettingsWindow::_MaxHistoryAge() const +{ + int32 maxHistoryAge = atoi(fDaysInHistoryMenuControl->Text()); + if (maxHistoryAge <= 0) + maxHistoryAge = 1; + if (maxHistoryAge >= 35) + maxHistoryAge = 35; + return maxHistoryAge; +} + + +void +SettingsWindow::_SetSizesMenuValue(BMenu* menu, int32 value) +{ + for (int32 i = 0; BMenuItem* item = menu->ItemAt(i); i++) { + bool marked = false; + if (BMessage* message = item->Message()) { + int32 size; + if (message->FindInt32("size", &size) == B_OK && size == value) + marked = true; + } + item->SetMarked(marked); + } +} + + +int32 +SettingsWindow::_SizesMenuValue(BMenu* menu) const +{ + if (BMenuItem* item = menu->FindMarked()) { + if (BMessage* message = item->Message()) { + int32 size; + if (message->FindInt32("size", &size) == B_OK) + return size; + } + } + return kDefaultFontSize; +} + + +BFont +SettingsWindow::_FindDefaultSerifFont() const +{ + // Default to the first "serif" font we find. + BFont serifFont(*be_plain_font); + font_family family; + int32 familyCount = count_font_families(); + for (int32 i = 0; i < familyCount; i++) { + if (get_font_family(i, &family) == B_OK) { + BString familyString(family); + if (familyString.IFindFirst("sans") >= 0) + continue; + if (familyString.IFindFirst("serif") >= 0) { + serifFont.SetFamilyAndFace(family, B_REGULAR_FACE); + break; + } + } + } + return serifFont; +} + + +uint32 +SettingsWindow::_ProxyPort() const +{ + return atoul(fProxyPortControl->Text()); +} + + diff --git a/src/apps/webpositive/SettingsWindow.h b/src/apps/webpositive/SettingsWindow.h new file mode 100644 index 0000000000..251d84405a --- /dev/null +++ b/src/apps/webpositive/SettingsWindow.h @@ -0,0 +1,122 @@ +/* + * Copyright (C) 2010 Stephan Aßmus + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef SETTINGS_WINDOW_H +#define SETTINGS_WINDOW_H + +#include + +class BButton; +class BCheckBox; +class BMenu; +class BMenuField; +class BMenuItem; +class BTextControl; +class FontSelectionView; +class SettingsMessage; + + +class SettingsWindow : public BWindow { +public: + SettingsWindow(BRect frame, + SettingsMessage* settings); + virtual ~SettingsWindow(); + + virtual void MessageReceived(BMessage* message); + virtual bool QuitRequested(); + + virtual void Show(); + +private: + BView* _CreateGeneralPage(float spacing); + BView* _CreateFontsPage(float spacing); + BView* _CreateProxyPage(float spacing); + void _BuildSizesMenu(BMenu* menu, + uint32 messageWhat); + void _SetupFontSelectionView( + FontSelectionView* view, + BMessage* message); + + bool _CanApplySettings() const; + void _ApplySettings(); + void _RevertSettings(); + void _ValidateControlsEnabledStatus(); + + uint32 _NewWindowPolicy() const; + uint32 _NewTabPolicy() const; + int32 _MaxHistoryAge() const; + + void _SetSizesMenuValue(BMenu* menu, int32 value); + int32 _SizesMenuValue(BMenu* menu) const; + + BFont _FindDefaultSerifFont() const; + + uint32 _ProxyPort() const; + +private: + SettingsMessage* fSettings; + + BTextControl* fStartPageControl; + BTextControl* fSearchPageControl; + BTextControl* fDownloadFolderControl; + + BMenuField* fNewWindowBehaviorMenu; + BMenuItem* fNewWindowBehaviorOpenHomeItem; + BMenuItem* fNewWindowBehaviorOpenSearchItem; + BMenuItem* fNewWindowBehaviorOpenBlankItem; + + BMenuField* fNewTabBehaviorMenu; + BMenuItem* fNewTabBehaviorCloneCurrentItem; + BMenuItem* fNewTabBehaviorOpenHomeItem; + BMenuItem* fNewTabBehaviorOpenSearchItem; + BMenuItem* fNewTabBehaviorOpenBlankItem; + + BTextControl* fDaysInHistoryMenuControl; + BCheckBox* fShowTabsIfOnlyOnePage; + BCheckBox* fAutoHideInterfaceInFullscreenMode; + BCheckBox* fAutoHidePointer; + BCheckBox* fShowHomeButton; + + FontSelectionView* fStandardFontView; + FontSelectionView* fSerifFontView; + FontSelectionView* fSansSerifFontView; + FontSelectionView* fFixedFontView; + + BCheckBox* fUseProxyCheckBox; + BTextControl* fProxyAddressControl; + BTextControl* fProxyPortControl; + + BButton* fApplyButton; + BButton* fCancelButton; + BButton* fRevertButton; + + BMenuField* fStandardSizesMenu; + BMenuField* fFixedSizesMenu; +}; + + +#endif // SETTINGS_WINDOW_H + diff --git a/src/apps/webpositive/URLInputGroup.cpp b/src/apps/webpositive/URLInputGroup.cpp new file mode 100644 index 0000000000..f84f0e12bf --- /dev/null +++ b/src/apps/webpositive/URLInputGroup.cpp @@ -0,0 +1,654 @@ +/* + * Copyright 2010 Stephan Aßmus + * All rights reserved. Distributed under the terms of the MIT License. + */ + +#include "URLInputGroup.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "BaseURL.h" +#include "BitmapButton.h" +#include "BrowsingHistory.h" +#include "IconButton.h" +#include "TextViewCompleter.h" + + +class URLChoice : public BAutoCompleter::Choice { +public: + URLChoice(const BString& choiceText, const BString& displayText, + int32 matchPos, int32 matchLen, int32 priority) + : + BAutoCompleter::Choice(choiceText, displayText, matchPos, matchLen), + fPriority(priority) + { + } + + bool operator<(const URLChoice& other) const + { + if (fPriority > other.fPriority) + return true; + return DisplayText() < other.DisplayText(); + } + + bool operator==(const URLChoice& other) const + { + return fPriority == other.fPriority + && DisplayText() < other.DisplayText(); + } + +private: + int32 fPriority; +}; + + +class BrowsingHistoryChoiceModel : public BAutoCompleter::ChoiceModel { + virtual void FetchChoicesFor(const BString& pattern) + { + int32 count = CountChoices(); + for (int32 i = 0; i < count; i++) { + delete reinterpret_cast( + fChoices.ItemAtFast(i)); + } + fChoices.MakeEmpty(); + + // Search through BrowsingHistory for any matches. + BrowsingHistory* history = BrowsingHistory::DefaultInstance(); + if (!history->Lock()) + return; + + BString lastBaseURL; + int32 priority = INT_MAX; + + count = history->CountItems(); + for (int32 i = 0; i < count; i++) { + BrowsingHistoryItem item = history->HistoryItemAt(i); + const BString& choiceText = item.URL(); + int32 matchPos = choiceText.IFindFirst(pattern); + if (matchPos < 0) + continue; + if (lastBaseURL.Length() > 0 + && choiceText.FindFirst(lastBaseURL) >= 0) { + priority--; + } else + priority = INT_MAX; + lastBaseURL = baseURL(choiceText); + fChoices.AddItem(new URLChoice(choiceText, + choiceText, matchPos, pattern.Length(), priority)); + } + + history->Unlock(); + + fChoices.SortItems(_CompareChoices); + } + + virtual int32 CountChoices() const + { + return fChoices.CountItems(); + } + + virtual const BAutoCompleter::Choice* ChoiceAt(int32 index) const + { + return reinterpret_cast( + fChoices.ItemAt(index)); + } + + static int _CompareChoices(const void* a, const void* b) + { + const URLChoice* aChoice + = *reinterpret_cast(a); + const URLChoice* bChoice + = *reinterpret_cast(b); + if (*aChoice < *bChoice) + return -1; + else if (*aChoice == *bChoice) + return 0; + return 1; + } + +private: + BList fChoices; +}; + + +// #pragma mark - URLTextView + + +static const float kHorizontalTextRectInset = 4.0; + + +class URLInputGroup::URLTextView : public BTextView { +private: + static const uint32 MSG_CLEAR = 'cler'; + +public: + URLTextView(URLInputGroup* parent); + virtual ~URLTextView(); + + virtual void MessageReceived(BMessage* message); + virtual void FrameResized(float width, float height); + virtual void MouseDown(BPoint where); + virtual void KeyDown(const char* bytes, int32 numBytes); + virtual void MakeFocus(bool focused = true); + + virtual BSize MinSize(); + virtual BSize MaxSize(); + + void SetUpdateAutoCompleterChoices(bool update); + +protected: + virtual void InsertText(const char* inText, int32 inLength, + int32 inOffset, + const text_run_array* inRuns); + virtual void DeleteText(int32 fromOffset, int32 toOffset); + +private: + void _AlignTextRect(); + +private: + URLInputGroup* fURLInputGroup; + TextViewCompleter* fURLAutoCompleter; + BString fPreviousText; + bool fUpdateAutoCompleterChoices; +}; + + +URLInputGroup::URLTextView::URLTextView(URLInputGroup* parent) + : + BTextView("url"), + fURLInputGroup(parent), + fURLAutoCompleter(new TextViewCompleter(this, + new BrowsingHistoryChoiceModel())), + fPreviousText(""), + fUpdateAutoCompleterChoices(true) +{ + MakeResizable(true); + SetStylable(true); + fURLAutoCompleter->SetModificationsReported(true); +} + + +URLInputGroup::URLTextView::~URLTextView() +{ + delete fURLAutoCompleter; +} + + +void +URLInputGroup::URLTextView::MessageReceived(BMessage* message) +{ + switch (message->what) { + case MSG_CLEAR: + SetText(""); + break; + default: + BTextView::MessageReceived(message); + break; + } +} + + +void +URLInputGroup::URLTextView::FrameResized(float width, float height) +{ + BTextView::FrameResized(width, height); + _AlignTextRect(); +} + + +void +URLInputGroup::URLTextView::MouseDown(BPoint where) +{ + bool wasFocus = IsFocus(); + if (!wasFocus) + MakeFocus(true); + + int32 buttons; + if (Window()->CurrentMessage()->FindInt32("buttons", &buttons) != B_OK) + buttons = B_PRIMARY_MOUSE_BUTTON; + + if ((buttons & B_SECONDARY_MOUSE_BUTTON) != 0) { + // Display context menu + int32 selectionStart; + int32 selectionEnd; + GetSelection(&selectionStart, &selectionEnd); + bool canCutOrCopy = selectionEnd > selectionStart; + + bool canPaste = false; + if (be_clipboard->Lock()) { + if (BMessage* data = be_clipboard->Data()) + canPaste = data->HasData("text/plain", B_MIME_TYPE); + be_clipboard->Unlock(); + } + + BMenuItem* cutItem = new BMenuItem("Cut", new BMessage(B_CUT)); + BMenuItem* copyItem = new BMenuItem("Copy", new BMessage(B_COPY)); + BMenuItem* pasteItem = new BMenuItem("Paste", new BMessage(B_PASTE)); + BMenuItem* clearItem = new BMenuItem("Clear", new BMessage(MSG_CLEAR)); + cutItem->SetEnabled(canCutOrCopy); + copyItem->SetEnabled(canCutOrCopy); + pasteItem->SetEnabled(canPaste); + clearItem->SetEnabled(strlen(Text()) > 0); + + BPopUpMenu* menu = new BPopUpMenu("url context"); + menu->AddItem(cutItem); + menu->AddItem(copyItem); + menu->AddItem(pasteItem); + menu->AddItem(clearItem); + + menu->SetTargetForItems(this); + menu->Go(ConvertToScreen(where), true, true, true); + return; + } + + // Only pass through to base class if we already have focus. + if (!wasFocus) + return; + + BTextView::MouseDown(where); +} + + +void +URLInputGroup::URLTextView::KeyDown(const char* bytes, int32 numBytes) +{ + switch (bytes[0]) { + case B_TAB: + BView::KeyDown(bytes, numBytes); + break; + + case B_ESCAPE: + // Revert to text as it was when we received keyboard focus. + SetText(fPreviousText.String()); + SelectAll(); + break; + + case B_RETURN: + // Don't let this through to the text view. + break; + + default: + BTextView::KeyDown(bytes, numBytes); + break; + } +} + +void +URLInputGroup::URLTextView::MakeFocus(bool focus) +{ + if (focus == IsFocus()) + return; + + BTextView::MakeFocus(focus); + + if (focus) { + fPreviousText = Text(); + SelectAll(); + } + + fURLInputGroup->Invalidate(); +} + + +BSize +URLInputGroup::URLTextView::MinSize() +{ + BSize min; + min.height = ceilf(LineHeight(0) + kHorizontalTextRectInset); + // we always add at least one pixel vertical inset top/bottom for + // the text rect. + min.width = min.height * 3; + return BLayoutUtils::ComposeSize(ExplicitMinSize(), min); +} + + +BSize +URLInputGroup::URLTextView::MaxSize() +{ + BSize max(MinSize()); + max.width = B_SIZE_UNLIMITED; + return BLayoutUtils::ComposeSize(ExplicitMaxSize(), max); +} + + +void +URLInputGroup::URLTextView::SetUpdateAutoCompleterChoices(bool update) +{ + fUpdateAutoCompleterChoices = update; +} + + +void +URLInputGroup::URLTextView::InsertText(const char* inText, int32 inLength, + int32 inOffset, const text_run_array* inRuns) +{ + // Filter all line breaks, note that inText is not terminated. + if (inLength == 1) { + if (*inText == '\n' || *inText == '\r') + BTextView::InsertText(" ", 1, inOffset, inRuns); + else + BTextView::InsertText(inText, 1, inOffset, inRuns); + } else { + BString filteredText(inText, inLength); + filteredText.ReplaceAll('\n', ' '); + filteredText.ReplaceAll('\r', ' '); + BTextView::InsertText(filteredText.String(), inLength, inOffset, + inRuns); + } + + // Make the base URL part bold. + BString text(Text(), TextLength()); + int32 baseUrlStart = text.FindFirst("://"); + if (baseUrlStart >= 0) + baseUrlStart += 3; + else + baseUrlStart = 0; + int32 baseUrlEnd = text.FindFirst("/", baseUrlStart); + if (baseUrlEnd < 0) + baseUrlEnd = TextLength(); + BFont font; + GetFont(&font); + const rgb_color black = (rgb_color){ 0, 0, 0, 255 }; + const rgb_color gray = (rgb_color){ 60, 60, 60, 255 }; + if (baseUrlStart > 0) + SetFontAndColor(0, baseUrlStart - 1, &font, B_FONT_ALL, &gray); + if (baseUrlEnd > baseUrlStart) { + font.SetFace(B_BOLD_FACE); + SetFontAndColor(baseUrlStart, baseUrlEnd, &font, B_FONT_ALL, &black); + } + if (baseUrlEnd < TextLength()) { + font.SetFace(B_REGULAR_FACE); + SetFontAndColor(baseUrlEnd, TextLength(), &font, B_FONT_ALL, &gray); + } + + fURLAutoCompleter->TextModified(fUpdateAutoCompleterChoices); +} + + +void +URLInputGroup::URLTextView::DeleteText(int32 fromOffset, int32 toOffset) +{ + BTextView::DeleteText(fromOffset, toOffset); + + fURLAutoCompleter->TextModified(fUpdateAutoCompleterChoices); +} + + +void +URLInputGroup::URLTextView::_AlignTextRect() +{ + // Layout the text rect to be in the middle, normally this means there + // is one pixel spacing on each side. + BRect textRect(Bounds()); + textRect.left = 0.0; + float vInset = max_c(1, + floorf((textRect.Height() - LineHeight(0)) / 2.0 + 0.5)); + float hInset = kHorizontalTextRectInset; + + if (be_control_look) + hInset = be_control_look->DefaultLabelSpacing(); + + textRect.InsetBy(hInset, vInset); + SetTextRect(textRect); +} + + +const uint32 kGoBitmapWidth = 14; +const uint32 kGoBitmapHeight = 14; +const color_space kGoBitmapFormat = B_RGBA32; + +const unsigned char kGoBitmapBits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x50, 0x50, 0xff, 0x50, 0x50, 0x50, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0x17, 0x17, 0x21, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x50, 0x50, 0x50, 0xff, 0x50, 0x50, 0x50, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x17, 0x17, 0x17, 0x21, 0x49, 0x49, 0x49, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x50, 0x50, 0xff, 0x50, 0x50, 0x50, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x17, 0x17, 0x17, 0x21, 0x49, 0x49, 0x49, 0xe0, 0x50, 0x50, 0x50, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x50, 0x50, 0x50, 0xff, 0x50, 0x50, 0x50, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0x17, 0x17, 0x21, 0x49, 0x49, 0x49, 0xe0, + 0x50, 0x50, 0x50, 0xff, 0x50, 0x50, 0x50, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x2f, 0x2f, 0x2f, 0x56, 0x50, 0x50, 0x50, 0xff, 0x4d, 0x4d, 0x4d, 0xed, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0x17, 0x17, 0x21, + 0x49, 0x49, 0x49, 0xe0, 0x50, 0x50, 0x50, 0xff, 0x50, 0x50, 0x50, 0xff, 0x50, 0x50, 0x50, 0xff, + 0x50, 0x50, 0x50, 0xff, 0x50, 0x50, 0x50, 0xff, 0x50, 0x50, 0x50, 0xff, 0x50, 0x50, 0x50, 0xff, + 0x50, 0x50, 0x50, 0xff, 0x37, 0x37, 0x37, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x17, 0x17, 0x17, 0x21, 0x49, 0x49, 0x49, 0xe0, 0x50, 0x50, 0x50, 0xff, + 0x50, 0x50, 0x50, 0xff, 0x50, 0x50, 0x50, 0xff, 0x50, 0x50, 0x50, 0xff, 0x50, 0x50, 0x50, 0xff, + 0x50, 0x50, 0x50, 0xff, 0x4b, 0x4b, 0x4b, 0xec, 0x37, 0x37, 0x37, 0x77, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x17, 0x17, 0x17, 0x21, 0x49, 0x49, 0x49, 0xe1, 0x50, 0x50, 0x50, 0xff, 0x50, 0x50, 0x50, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0x17, 0x17, 0x21, + 0x49, 0x49, 0x49, 0xe1, 0x50, 0x50, 0x50, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0x17, 0x17, 0x21, 0x49, 0x49, 0x49, 0xe1, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x17, 0x17, 0x17, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + + +// #pragma mark - PageIconView + + +class URLInputGroup::PageIconView : public BView { +public: + PageIconView() + : + BView("page icon view", B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE), + fIcon(NULL) + { + SetDrawingMode(B_OP_ALPHA); + SetBlendingMode(B_PIXEL_ALPHA, B_ALPHA_OVERLAY); + } + + ~PageIconView() + { + delete fIcon; + } + + virtual void Draw(BRect updateRect) + { + if (fIcon == NULL) + return; + + BRect bounds(Bounds()); + BRect iconBounds(0, 0, 15, 15); + iconBounds.OffsetTo( + floorf((bounds.left + bounds.right + - (iconBounds.left + iconBounds.right)) / 2 + 0.5f), + floorf((bounds.top + bounds.bottom + - (iconBounds.top + iconBounds.bottom)) / 2 + 0.5f)); + DrawBitmap(fIcon, fIcon->Bounds(), iconBounds, + B_FILTER_BITMAP_BILINEAR); + } + + virtual BSize MinSize() + { + if (fIcon != NULL) + return BSize(18, 18); + return BSize(0, 0); + } + + virtual BSize MaxSize() + { + return BSize(B_SIZE_UNLIMITED, B_SIZE_UNLIMITED); + } + + virtual BSize PreferredSize() + { + return MinSize(); + } + + void SetIcon(const BBitmap* icon) + { + if (icon == NULL && fIcon == NULL) + return; + if (!(fIcon != NULL && icon != NULL)) + InvalidateLayout(); + delete fIcon; + if (icon) + fIcon = new BBitmap(icon); + else + fIcon = NULL; + Invalidate(); + } + +private: + BBitmap* fIcon; +}; + + +// #pragma mark - URLInputGroup + + +URLInputGroup::URLInputGroup(BMessage* goMessage) + : + BGroupView(B_HORIZONTAL, 0.0), + fWindowActive(false) +{ + GroupLayout()->SetInsets(2, 2, 2, 2); + + fIconView = new PageIconView(); + GroupLayout()->AddView(fIconView, 0.0f); + + fTextView = new URLTextView(this); + AddChild(fTextView); + + AddChild(new BSeparatorView(B_VERTICAL, B_PLAIN_BORDER)); + +// TODO: Fix in Haiku, no in-built support for archived BBitmaps from +// resources? +// fGoButton = new BitmapButton("kActionGo", NULL); + fGoButton = new BitmapButton(kGoBitmapBits, kGoBitmapWidth, + kGoBitmapHeight, kGoBitmapFormat, goMessage); + GroupLayout()->AddView(fGoButton, 0.0f); + + SetFlags(Flags() | B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE); + SetLowColor(ViewColor()); + SetViewColor(B_TRANSPARENT_COLOR); + + SetExplicitAlignment(BAlignment(B_ALIGN_USE_FULL_WIDTH, + B_ALIGN_VERTICAL_CENTER)); +} + + +URLInputGroup::~URLInputGroup() +{ +} + + +void +URLInputGroup::AttachedToWindow() +{ + BGroupView::AttachedToWindow(); + fWindowActive = Window()->IsActive(); +} + + +void +URLInputGroup::WindowActivated(bool active) +{ + BGroupView::WindowActivated(active); + if (fWindowActive != active) { + fWindowActive = active; + Invalidate(); + } +} + + +void +URLInputGroup::Draw(BRect updateRect) +{ + BRect bounds(Bounds()); + rgb_color base(LowColor()); + uint32 flags = 0; + if (fWindowActive && fTextView->IsFocus()) + flags |= BControlLook::B_FOCUSED; + be_control_look->DrawTextControlBorder(this, bounds, updateRect, base, + flags); +} + + +void +URLInputGroup::MakeFocus(bool focus) +{ + // Forward this to the text view, we never accept focus ourselves. + fTextView->MakeFocus(focus); +} + + +BTextView* +URLInputGroup::TextView() const +{ + return fTextView; +} + + +void +URLInputGroup::SetText(const char* text) +{ + if (!text || !Text() || strcmp(Text(), text) != 0) { + fTextView->SetUpdateAutoCompleterChoices(false); + fTextView->SetText(text); + fTextView->SetUpdateAutoCompleterChoices(true); + } +} + + +const char* +URLInputGroup::Text() const +{ + return fTextView->Text(); +} + + +BButton* +URLInputGroup::GoButton() const +{ + return fGoButton; +} + + +void +URLInputGroup::SetPageIcon(const BBitmap* icon) +{ + fIconView->SetIcon(icon); +} + diff --git a/src/apps/webpositive/URLInputGroup.h b/src/apps/webpositive/URLInputGroup.h new file mode 100644 index 0000000000..d71f713ca7 --- /dev/null +++ b/src/apps/webpositive/URLInputGroup.h @@ -0,0 +1,43 @@ +/* + * Copyright 2010 Stephan Aßmus + * All rights reserved. Distributed under the terms of the MIT License. + */ +#ifndef URL_INPUT_GROUP_H +#define URL_INPUT_GROUP_H + +#include + +class BButton; +class BTextView; + + +class URLInputGroup : public BGroupView { +public: + URLInputGroup(BMessage* goMessage); + virtual ~URLInputGroup(); + + virtual void AttachedToWindow(); + virtual void WindowActivated(bool active); + virtual void Draw(BRect updateRect); + virtual void MakeFocus(bool focus = true); + + BTextView* TextView() const; + void SetText(const char* text); + const char* Text() const; + + BButton* GoButton() const; + + void SetPageIcon(const BBitmap* icon); + +private: + class PageIconView; + class URLTextView; + + PageIconView* fIconView; + URLTextView* fTextView; + BButton* fGoButton; + bool fWindowActive; +}; + +#endif // URL_INPUT_GROUP_H + diff --git a/src/apps/webpositive/WebPositive.rdef b/src/apps/webpositive/WebPositive.rdef new file mode 100644 index 0000000000..9b475b34d1 --- /dev/null +++ b/src/apps/webpositive/WebPositive.rdef @@ -0,0 +1,350 @@ +resource app_signature "application/x-vnd.Haiku-WebPositive"; + +resource app_version { + major = 0, + middle = 0, + minor = 1, + variety = B_APPV_ALPHA, + internal = 0, + short_info = "WebPositive", + long_info = "WebPositive ©2007-2010 The WebKit Haiku Project" +}; + +resource app_flags B_SINGLE_LAUNCH; + +resource file_types message { + "types" = "text/html", + "types" = "image/svg+xml", + "types" = "application/x-vnd.Be.URL.file", + "types" = "application/x-vnd.Be.URL.ftp", + "types" = "application/x-vnd.Be.URL.http", + "types" = "application/x-vnd.Be.URL.https" +}; + +resource vector_icon { + $"6E636966450500020106033D0AFE000000000000BD0AFE48F10748A0783133FF" + $"FFC80066FFFF0034CC020106033D0AFE000000000000BD0AFE48F10748A07854" + $"09B4B6D8033784FF051D61020106043D950B0000000000003D950B494FBC479B" + $"EC00FFFFFF4C00FF008F00B200FF008000050002030605B812A5BE03E13DE784" + $"B8021049F79F49EDD800F1F1F136D9DDF48A9996B9B4B8BEDBFFF4F4F404EBD0" + $"020006023C92C0388F5FB854503C576348D8DF48895B004137A9FFB9B9B90401" + $"7E03FF360003FFFF0003FF9C0003FF000002010202BB36AEBA1760B80FFF392B" + $"574ACCFB48652300000001FFFF0000013205400500020004024AFFFC00FFFFBA" + $"360500020106033D0AFE000000000000BD0AFE48F10748A0783133FFFFC80066" + $"FFFF0034CC020106023D0AFE000000000000BD0AFE48F10748A0780033FFFFFF" + $"0033CC020106023D0AFE000000000000BD0AFE48F10748A0780033FFFFFF0066" + $"FF020004027AFFFC00DDFFBA360201040289FF0000A9C000000200040200FFE5" + $"CED0ECB989020004020DEFBC8CFFD4AA820200040200FFCB9CE9ECB989020004" + $"0200FFCB9CE9ECB98902000402B3FFCB9CBBD4AA820500020106033D0AFE0000" + $"00000000BD0AFE48F10748A0783133FFFFC80066FFFF0034CC020106033D0AFE" + $"000000000000BD0AFE48F10748A0785409B4B6D8033784FF051D61020106043D" + $"950B0000000000003D950B494FBC479BEC00FFFFFF4C00FF008F00B200FF0080" + $"00050002030605B812A5BE03E13DE784B8021049F79F49EDD800F1F1F136D9DD" + $"F48A9996B9B4B8BEDBFFF4F4F404EBD0020006023C92C0388F5FB854503C5763" + $"48D8DF48895B004137A9FFB9B9B904017E03FF360003FFFF000200040200FFBC" + $"34FFFF9C0003FF000002010202BB36AEBA1760B80FFF392B574ACCFB48652300" + $"000001FFFF00000132054003FFFFC9020014020026FF1C0500050002030605B8" + $"12A5BE03E13DE784B8021049F79F49EDD800F1F1F136D9DDF48A9996B9B4B8BE" + $"DBFFF4F4F404EBD0020006023C92C0388F5FB854503C576348D8DF48895B0041" + $"37A9FFB9B9B904017E03FF360003FFFF0003FF9C0003FF000002010202BB36AE" + $"BA1760B80FFF392B574ACCFB48652300000001FFFF0000013205400500030052" + $"7502001002F8006BFF003E04006C050003900000020310020000006E00650400" + $"6B05000359463503C3A414039D816035000000000204BFC1B55A36B55AC3BEB5" + $"5AC6FABC92C6FAB895C6FAC08FBFC1C3CBC3BEC3CB36C3CBB889BC92B889C08F" + $"B889B895066EFFFFF30FFFFEFFFFBFBFFEFFFEFFFEFFFFFFFFBFFFEFDFFBFBFF" + $"FF0FC5DBB8B5C713BA9FC4CAB706C10FB579C307B5D4C0A226BFC1B55AC031B5" + $"5ABDBCB55ABA69B7BA36B63AB8F9B94EB89CBD9BB84EBB7EB8C7BE9AB924BF76" + $"B8E0BEE7B9DEC11FBCDFC332BB37C278BEC4C405C2D7C31AC0FAC3FCC2C5C2C0" + $"C22EC2B7C279C2BDC22EC2B7C226C240C2BDC240C2BDC206C2AFC199C2DAC1D0" + $"C2CB44C2EEC0D4C2DCC114C2E5C051C2CEBF42C312BFB0C2B4BF3DC312BF35BF" + $"1FBDECC312BE23C312BDB8C2EDBD57C2DEBD86C2E2BD53C2DBBD3EC2C2BD3EC2" + $"C2BD31C2B4BD12C2A4BD26C2AABD12C2A4BCA4C27EBC31C254BC31C254BC27C2" + $"51BC11C24FBC1DC250BC10C24BBC0FC241BC10C245BC0FC241BBF5C219BBF5C2" + $"19BBD2C1DDBB68C194BBADC1A4BB70C16EBB58C128BB62C14EBB4BC101BB4BC0" + $"ACBB47C0D7BB4BC0ACBB4EC087BB4EC087BB51C054BB4DBFEEBB54C021BB9DBF" + $"92BBA9BEBFBBAFBF35BBA9BEBFBBA7BE92BBA7BE92BBA7BE52BBBBBE2FBBA8BE" + $"4BBBBBBE2FBBC6BE20BBC6BE20BC00BDD4BBE2BCFBBC5BBD3DBBE2BCFBBAB7BC" + $"58BA8ABC43BA90BC45BA8ABC43BA8ABC42BA8ABC42BA88BBEABA22BB78BA6ABB" + $"ACBA22BB78BA12BB6AB9F8BB48B9ADBAEFB9ADBAEFB97DBAC3B927BAB0B94FBA" + $"B5B924BAA9B91ABA9AB920BAA0B984BA6DB9DEB996B9B5B9F9B9F6B95DBA36B8" + $"EFBA0AB91FBA5EB8C4BA78B85EBA83B89EBACCB83CBB60B7E7BB18B813BB60B7" + $"E7BB6AB7E1BBFEB787BBFEB787BC65B749BCB6B683BC93B6F5BCF5B689BD58B6" + $"64BD2CB67BBDA8B695BE6CB6503C29BE6FB64FBE88B64FBE7CB64FBE62B6A0BE" + $"E1B72FBE88B70EBEE4B733BEE8B73DBEE5B73ABED8B743BEC5B749BE74B7A5BE" + $"74B7A5BE63B7DCBE8FB848BE74B816BE90B84CBE91B852BE91B852BE93B85BBE" + $"9CB87ABE97B86BBE93B883BE8CB88DBE8CB88DBE39B8EFBD91B98FBDB6B906BD" + $"91B98FBD8DB9A0BD8DB9A0BD64B9D7BD7EBA54BD59BA19BD7ABA5CBD76BA62BD" + $"76BA62BD56BA9FBD4DBB2BBD38BAE6BD5FBB6FBDCCBBCEBD98BBA1BDFFBBF9BE" + $"73BC3FBE33BC27BEBEBC5CBF5DBC42BF11BC55BF5DBC42BFAB37BFAB37BFD0BC" + $"1DC01CBC0FBFF4BC0BC045BC13C083BC4EC066BC35C04DBCD9C0F3BDDAC0B6BD" + $"63C0F3BDDAC107BDF8C102BDF2C0BABE83C126BF90C0B1BF1CC126BF90C133BF" + $"9CC133BF9CC130C00EC1C4C0F8C135C0D6C258C11CC347C059C2EEC0C8C347C0" + $"59C36BC02EC388C018C388C018C3A7C003C3DABFC4C3C9BFE8C3EEBF9FC408BF" + $"51C3EFBF73C408BF51C408BF52C408BF52C40CBF99C49540C450BFCEC4EBBFB3" + $"C541BF0FC51FBF56C541BF0FC551BEF24EBEEA4EBEEAC57FBEA3C568BDFCC59E" + $"BE46C53ABDBAC4A2BDDEC4E0BDA9C4A2BDCABDB5C4A4BD31C4A4BD31C4A7BD1D" + $"C4E8BCDEC4D2BCF5C4E8BCDEC4FEBCC8C516BCB3C516BCB3C544BC8BC582BC1F" + $"C570BC5DC58BBC04C587BBBBC586BBD9C587BBBBC587BBA8C587BB9DC587BB9D" + $"C58CBB604EBAF9C584BB23C589BADAC5C1BA5DC5B0BAA1C5C3BA61C5CBBA6FC5" + $"CBBA6FC5D8BA8EC60EBAD3C5EDBAB3C609BB1EC648BBAAC626BB67C669BBEAC6" + $"6EBC7AC66FBC33C66EBC7AC671BCC3C671BCC3C673BCE9C66DBD36C677BD10C6" + $"64BD5AC649BD9BC652BD78C633BDEBC62BBE91C62DBE3F50BED5C67FBF2BC648" + $"BF16C750BD0F0006BC11B6E3BC11B6E3BC32B6AFBC54B637BC46B674BB81B6AA" + $"BA2BB801BAC4B747BA87B7E2BB2FB77DBADDB7AFBB59B764BBAAB733BB82B74D" + $"BBCBB71CBC11B6E3BBFBB707BC11B6E3001DBACDBFD1BACDBFD1BAEBBFA9BB29" + $"BF5BBB12BF89BB43BF24BB34BE92BB34BECFBB34BE4DBB5FBDE9BB34BE25BB80" + $"BDBEBBAABD5FBBA0BD98BB47BD29BA81BCBEBAE5BCF3BA67BCB1BA1FBC8332BC" + $"9FBA0FBC5BBA0DBC02BA1FBC28B9FDBBE5B9B9BBB6B9D0BBD0B99BBB91B95FBB" + $"44B983BB66B93DBB23B8EFBB2CB91BBB15B8C0BB47B8BEBADEB8C2BAFFB8B1BB" + $"13B89FBB7EB8A7BB48B8C8BB7EB8C5BBB2B8C2BB89B8C6BBDBB8C8BC2DB8C7BC" + $"04B8CFBC40B8BABC48B8CBBC48B8B3BC4BB8A9BC4EB8AEBC4DB8A0BC57B893BC" + $"4BB898BC55B881BC4AB88EBD0FB88DBD00B893BD49B8E0BDE7B88DBE02B90DBE" + $"03B922BE74B906BE4BB946BEA7B985BF00B972BEC1B996BF3EB9A240B997BF80" + $"B9AFC010BA16C05EB9DBC02DBA24C0B3BA75C126BA3CC0E7BA8FC143BAC0C17F" + $"BAA9C15FBAD7C19FBAFBC17BBAE3C19DBADDC13ABAD6C0A5BAD2C0EFBADBC060" + $"BACDBFD1BAEAC012BACDBFD10009BD5DB5D6BD5DB5D6BD81B5EDBDB1B60DBD83" + $"B605BDCFB613BE19B600BE01B619BE55B5C3BEF5B5C3BEB0B5EFBF2CB5A1BFA3" + $"B55D3FB55DBFCEB55DC00CB58CBFE9B577C03CB5A6C08FB566C063B574BF39B5" + $"40BCA7B60DBDDCB57ABCEBB623BD5DB5D6BD29B600BD5DB5D60005BF34B6E9BF" + $"34B6E9BF55B6EBBF99B6F5BF7BB702BFCEB6E0BF87B67CBF9BB693BF77B66DBF" + $"3CB663BF2FB623BF44B68FBF34B6E9BF6AB6CBBF34B6E90004BA06B87FBA06B8" + $"7FBA0BB867B9EEB84FB9FDB85FB982B8E4B8EFBA35B92CB987B97D31BA06B87F" + $"B974B8EFBA06B87F0066C6F6BC24C6F6BC24C6DBBB09C6002FC68AB9F1C5B9B8" + $"7BC4FFB79BC563B805C4CEB768C463B708C49AB736C452B6FBC427B6EFC43FB6" + $"E2C411B6FAC407B6C1C401B6D0C315B60FC0D0B56EC1F8B59AC0FAB58AC15EB5" + $"9CC130B589C170B5A1C178B5C9C19FB5BDC12CB5E2C08AB5C9C0D7B5C3C059B5" + $"CDC03FB60DC042B5DDC03DB629C03FB676C036B65CC04AB694C0B6B65EC0A7B6" + $"6FC0CDB643C0F1B619C0B0B607C108B64AC0D0B6B0C13AB6D5C0B5B6A7C066B6" + $"89C083B67CC063B689C05CB6AFC05EB6AAC053B6C0C030B6CFC043B6CABFE5B6" + $"E4BF94B741BFB0B6EFBF87B768BF5CB790BF88B781BF3BB79CBEF6B7B2BF17B7" + $"A3BECCB7C7BEF9B81BBEE9B7FFBF05B82FBF19B86CBF02B860BF59B891BFBEB7" + $"E0BFA3B801BFE0B7B3C038B7ADC008B7BCC04FB7A6C084B776C06CB76EC0D3B7" + $"95C0F7B83EC0EBB7F4C10FB83FC135B80FC100B801C10CB7E5C0ECB76F43B7AA" + $"C125B795C15EB7FCC14DB7BCC170B83CC1C1B820C18DB860C1CFB813C1FCB7D4" + $"C1E1B7BCC213B7E9C1F1B838C2142DC206B84BC23AB83CC222B842C259B83447" + $"B841C26CB835C2AEB84FC2F9B856C2D5B854C320B859C346B898C335B87AC376" + $"B8E6C249B8C5C258B8C6C227B8C2C1E1B8CFC202B8C1C1C8B8DAC199B8EFC1B6" + $"2FC178B8E5C13EB8B5C15AB8C7C117B89BC0C4B872C0EFB885C07EB853BFE2B8" + $"4EC02EB83FBFBFB855BF7FB87BBF9BB866BF5BB896BF10B8A4BF34B87FBEDBB8" + $"DBBE6DB941BEB5B91FBE50B94FBE19B976BE2DB95BBE03B990BDF5B9D1BE03B9" + $"B4BDE8B9EDBDE2BA1DBDCAB9FBBDF7BA3BBDEFBA76BDFEBA52BDD9BAA9BDC9BB" + $"27BD9EBAEEBDF235BE71BBBBBE39BB93BEAFBBE9BF40BBD1BEFBBBE5BF82BBC1" + $"C009BB9CBFC4BBA0C05EBB95C0DCBC03C0A1BBCDC104BC26C0F4BC67C113BC34" + $"C0D3BC9EC103BD00C0E8BCC7C11FBD3644BDA3C13CBD6CC166BDB9C182BDE8C1" + $"76BDCFC18DBE03C16DBE2CC17ABE15C152BE5DC13EBECEC13EBE97C13EBF0DC1" + $"85BF4CC15CBF24C1A9BF71C1A4BFE2C1A7BFB3C1A0C025C1DAC087C1B3C053C2" + $"15C08AC289C06CC253C086C2CBC04DC319BFDDC2E8C00EC346BFB0C386BF66C3" + $"77BFAAC396BF12C3FEBEBFC3C1BEF2C435BE8FC430BE0CC42DBE4DC432BDC0C4" + $"33BD21C428BD6CC43BBCDDC4AABC77C47EBCA7C4CDBC55C511BC03C505BC34C5" + $"19BBE7C512BBA8C51236C512BB92C51035C51ABB70C4F7BB29C47EBB2BC489BB" + $"5DC475BB0EC48ABB01C476BB13C4A1BAECC477BACDC48ABAD7C441BAACC3F0BA" + $"66C418BA9AC3CCBA36C390B9CAC3ADBA00C389B9BDC36CB93AC341B92DC39AB9" + $"4AC3E1B9A2C3C3B981C419B9DFC46DBA6AC44ABA1FC46EBA73C496BA89C48EBA" + $"85C4AFBA9CC4EABAA8C4CABAABC533BAA1C559BA1F4EBA60C56231C4E8B9AEC5" + $"1DB9DAC4CAB996C49EB958C4B6B975C48EB946C473B90DC466B92AC492B8F9C4" + $"BCB91CC49FB90BC4DFB932C52BB951C505B9444EB95EC5B4B96CC591B952C5C9" + $"B97BC5E7B9B8C5D8B9A1C5FEB9DCC626BA26C614BA00C644BA60C694BA9EC64D" + $"BA88C667BAE8C6B6BB83C693BB3FC6DEBBD3C6E3BC7BC6E4BC22C6E2BCCEC6D0" + $"BD78C6F4BD2BC6A6BDCCC69FBE9AC69FBE3EC6A0BE95C6A4BE88C6A2BE8DC6A1" + $"3DC6A0BEC0C696BEBEC6B2BEC3C6D5BE01C6D2BE15C6F5BD65C6F6BC24C700BC" + $"C3C6F6BC240008C301B7D8C301B7D8C2E6B7DBC2D4B7A3C2E8B7AEC2B1B792C2" + $"69B7B7C288B7A7C204B7EEC236B742C221B77BC239B73AC285B743C279B746C2" + $"A6B740C2F2B74EC2D7B730C30BB76AC33CB7A1C31FB787C35FB7C1C301B7D8C3" + $"1CB7D4C301B7D80004BB52C203BB52C203BB44C20CBB26C221BB37C21DBB54C2" + $"48BBB9C290BB86C26EBB91C265BB52C203BB84C226BB52C2030005C4F3BE2DC4" + $"F3BE2DC4D3BE57C48BBEA4C4A5BE75C46EBEDAC47DBF52C47DBF18C4B6BF37C4" + $"EDBEB8C4D0BEEBC502BE92C4F3BE2DC531BE48C4F3BE2D0009C212C326C212C3" + $"26C17FC38FBFF6C344C09DC332BFD0C348BF8DC36ABFABC350BF66C38BBF1FC3" + $"86BF56C386BEAFC386BDCDC386BE3EC386BEC1C3CBC0BFC3BABFC3C3DCC137C3" + $"A9C221C367C1AEC38DC229C364C261C32AC280C337C248C322C212C326C229C3" + $"31C212C32602024E284E2C542E4C324C30482E020250304C3252344C364E344C" + $"34020258B81858305C32543654324E300202583456365836563C5A3854380204" + $"BCD5C1F1BACDC36FBEA5C09BC4A6BFA6C360BE3CC67FC1B0C064C5ACC23AC444" + $"BE90C715B827C85CB961C9B8B682C68B0204C3CFC072C309BF45C521C2733DC6" + $"5EC0DDC4E4BC10C7F6B880C76BB8DAC831B841C6E2BC6FC2E2B880C5CDBE42C1" + $"890622AEAAE6BAEAAAAF9A0EB9E4C6DEB9CB50B985C672BA12C5CDBAABC593BA" + $"C54DBAB8C4BBBAEBC3C8BAABC37CBA85C0CBBC88BE0E36B8E6B993B8DA2EB874" + $"B92DB970B7AFBCD5BA52BCEEB87ABE3AB7AEBD61B7AEBF12B7AEBF852EBF92BA" + $"78C0D2BB91C49BBB6AC51ABC10C51ABBB6C51ABC5AC49BBCB6C21DBCC3BF9FBD" + $"CEBFCBC18BC1FCC323BF00C13E46C33BC196C43B46C3DBC12AC484C02BC493C0" + $"11C3EFC091C36FBE20C2BDBF40BC50C14ABBF74EBB8451BBE8C66EBA9EC6B906" + $"11AAAAA8BA02BBEFB9ECBB49BAB8BCC8BB9DBCE1BE4EBB70C018BC7BC0A5BE07" + $"BF73BE6044BF9FBF6CBDDBC06CBD0245BCAEC1C9BC2FBF40BAABBE0EBB04BF0D" + $"BB04BD82BB04BD14BAB8BCAEBA38060BBAAA2A31C653BA0BC6C4BB84C645BB2B" + $"C69FBBB6C614BBAFC5A0BBDDC309BC1DC163BCAEC0CABB70BFE5BAD7C0CABADE" + $"C36FBAE3C5C5060AAAAB0ABE46C125BF99C130BF80454448C1FCC394C21DC361" + $"C1C3C3F0C11EC447C052C493C02BC3EFC105C389BE67470605EA02BB37BAC5BB" + $"D6BA12B9ACB887B8E0B8ADB90BB869B887B939B920B96C0A05C16BBB91C473BB" + $"91C4D9BC36C4A6BCC3C16BBCE906078A2EBD4F2EBDB5B814BEA7BF402EBF4DBA" + $"45BE0EBB04BEA7BB2BBD8ABAE2BD29BA520607AA3A39B8EDBE012F3CB99F3EB9" + $"93BEDAB920BF99B946BE34B77BBFF5B7C239B74804032EBCE932BDCEBB2BBD34" + $"BB12BECDBB51BF4DBA380208CA68BA5FCB40BD02CA39B9D0CA0EB6BCCAF3B7AE" + $"C947B5E955B58A55B58A55B58AC843B841C8FFB76C53B953C718B814C6E62FC7" + $"4BB728C5A02FC601B84EC2703DC645BCB6C5ACBC1DC711BD82C89DBEE7C79E40" + $"C89DBEE7060ABFFB0FC6F1C097C625BFCBC6F1BFCB5A345C3758315A2E5A305A" + $"2C582A562E582C54305230C691B854C691B920C691B7DAC53ABAAB4FB9ACC501" + $"BB1EC46EBBF7C4ADBB9DC3FABCB6C56DBE0EC38EBE4EC64DBDF00207C86FBE74" + $"C86FBE74C7A3BDA8C78ABB44C80ABB28C71835C633BBB7C6D4BBEFC5DABB98C5" + $"01BB44C509BB79C4F1BAEDC3F4BC43C43EBBD1C4B4BC83C6FF38C63336C7CB3A" + $"C83CBE9AC5B3BF8DC908BD020202C6392F4DB9F9C66CBAC54DBBE24FBB16C46E" + $"BAEB0202C969B7CEC967B915CAFFB9E1543654324E300202583456365836C876" + $"BD8FC969BB84C7AABBF70604EAC2BD4448C110C32EC07EC361C17DC33CC0E4C3" + $"7BC1E30205C5ACB861C65E31C52BB75D50204E224E20C3E1BA5FC37BB94FC455" + $"BB91C606BD02C493BCB6C722BE0DCB1BBDA8CB97BDE4C79EBBF702044EB44BC4" + $"D4284EB5174BB9B9C408B8BAC43DBAD755BD4F4BBBD057BD4FC744BB77C818BC" + $"3B51BAC502034E28C4A1B847C53AB61650BA52C592B8A0C690BBAEC4E0BAAB59" + $"BD8FC4E0B7A104032EB77BC612B63CC969B50AC7AAB777CB35BCA9C99C04032E" + $"B965B906B953B7D4B8D3B86D31B748BAB82C0406BE0BB689BE67B5FDC04BB607" + $"BF7FB5F0C11CB689C1D7B65BC1A2B73BC2A3B85448B77BC33CB801C31EB70849" + $"B609C32304032EBDA8B887BEB4B76EBE10B775BFEC2BC059B8AD04032E28BE01" + $"B3FFC0D8B4253FB3D2C288B5BDC2FB0406FE0BC79EBB2BC8F5BBAAC840BB47C9" + $"EBBC2ECB26BDDBCB05BD14CB40BE74CA4FC07ECA99BFCACA0EC117C9CFC27CCA" + $"0EC1E3C998C30058C3890A04C6FFBB11C856BC50C8BCBCC3C889BD4F040CAAFA" + $"BABC6FC74BC091C699C303C5CDC3DAC54DC440C4CEC44EC382C1EAC303C237C3" + $"4DC1D5C2EEC05EC402C149C3DFBF754BBD87C460BD47C4F9BE46C513BE2D4DBE" + $"98C58BBDBAC5870608BAAABFECC1FC4448C1FCC394C21DC361C1C3C3F0C11EC4" + $"47C052C493C02BC3EFC105C389BED3C2BD0A04C369BC83C6D738C666BD1CC35B" + $"BD350A03B46542B4BE41B4BE420605AF03B6AFC969B63CC9F1B72FC8D0BE4155" + $"B821CA8DC11CC6FAC42DC5C5C55FC53ABE67C91DC1E8C7EAB86DCB260604EEBF" + $"CBBB51C1FCBBD0C13EBC43C29FBB6EC18BBAAB43BB1EC145BAF2C09ABB4B0A04" + $"30303050505050302C0A0001021240BAB100000000000040B755C6CCDBBE9979" + $"01178300040A0101020240BAB100000000000040B755C6CCDBBE99790A020103" + $"0240BAB100000000000040B755C6CCDBBE99790A030A05060708090A0C0D040B" + $"0240BAB100000000000040B755C6CCDBBE99790A110113124084473AE65BBB05" + $"39406A8F40BD7AC6ED3001178400040A00012712401CCA0000000000004031D6" + $"BEA7B4C5651401178100040A150112024046733A7FB6BABB05401508437714C3" + $"BB840A3B012E0A401CC92E6813AE6813401CC9C0B293C4BE831DFF0A11011412" + $"402E52374883B74883402E5243A84DC5C35901178200040A16011502402E5237" + $"4883B74883402E5243A84DC5C3590A17011602402E52374883B74883402E5243" + $"A84DC5C3590A18011702402E52374883B74883402E5243A84DC5C3590A170118" + $"02402E52374883B74883402E5243A84DC5C3590A1801190240268F36C1F1B73C" + $"D33FA9DD442903C369B00A1B011A02402E52374883B74883402E5243A84DC5C3" + $"590A2C011B02402E52374883B74883402E5243A84DC5C3590A11011C12402E52" + $"374883B74883402E5243A84DC5C35901178100040A20011D124018A13C2DD3BC" + $"6F843F776243040C3B35B701178400040A28011D024018A13C481BBC6F843FA1" + $"3143AD0CC180CE0A27011E024018A13C3669BC6F843F850B43AD0CBDA6830A26" + $"03212022023F94B53C057BBC14913F373546DE4E423FFC0A2B012202401CCA00" + $"0000000000401CCAC64FB846E7CF0A2B01230241774E3E9634BD97FB40685AC6" + $"A141CA33220A0001291240075E0000000000004029CCC0DC88C59D5C01178100" + $"040A2E0124123E3A7739EBE1BA96D53D1E7BC874E948577D01178400040A3601" + $"24023DE4F539818BBA43313CC9E6C7EFC848861D0A350125023DE4F539818BBA" + $"43313CC9E6C7EFC848861D0A340126023DE4F539D28CBA43313D0A55C7EFC848" + $"5E9A0A000128123FEA4E3675EFB6F2733F44E3440366C2D26601178200040A00" + $"012A12402E52374883B74883402E5241C554C5CE0401178100040A00012B1240" + $"1CCA000000000000401CCAC0623FC4B46E01178100040A00012C124031960000" + $"00000000401CCAC3AEA8C4B46E01178100040A3A012D02401CCA000000000000" + $"401CCAC0E971C4B46E0A3C012F0A401CCA000000000000401CCAC0E971C4B46E" + $"1DFF0A3B01300A3FEC520000000000003C6A0B41051D473FEE1DFF0A3E013102" + $"40FA05000000000000417C4D3E5061C9E26E0A3F013212401CCA000000000000" + $"401CCAC0E971C42D3C01178200040A4001330A401CCA000000000000401CCAC0" + $"E971C4B46E1DFF0A420134023600000000000000003600004890004AE8000A42" + $"0134023600000000000000003600004870004AE8000A43013402360000000000" + $"0000003600004850004AF8000A43013402360000000000000000360000487000" + $"4AF8000A430134023600000000000000003600004890004AF8000A4401340236" + $"0000000000000000360000485000466000" +}; + +resource(201, "kActionBack") #'VICN' array { + $"6E636966070500020006023B2FA63AC896BCCBD33D335A4A6980490D0B00ACE2" + $"FFFF6ECDFF0200060236D841374D6EBD38553CC6154AA9DF4607770026A8EBFF" + $"0694DE020006023729EA388C8FBC29073AAE614A51AF4A729A00035E8CFF0585" + $"C70200060238E8A83647FBBA8E2D3D42294AF6F7496CE200047BB8FF0694DE02" + $"0006023957BB3923F0BC39AC3C5E604AA873475AB1000592DAFD67CBFF04FF49" + $"080A092E3C3A53424C484F5047503A4435442B3A300A072E3C3A533A48484F48" + $"403A3A3A300A043A303A3A4435442B0A033A483A53424C0A044840484F504750" + $"3A0A043A3A4435503A48400803C125B7D43AB99FB8D3BE0E08023A3A4840070A" + $"0001001001178400040A010101000A020102000A030103000A040104000A0501" + $"05000A06020607100117812004" +}; + +resource(202, "kActionForward") #'VICN' array { + $"6E636966050500020006023B2FA63AC896BCCBD33D335A4A6980490D0B00ACE2" + $"FFFF6ECDFF0200060239AF2C39E19BBC89D83C68DC4AFA1247CFA20006A5F7FF" + $"069EEC02000602B8EEBEB986C33C7FB4BC13FB46FA0C49FBDD000592DAFD3EBD" + $"FF04FF49050A0A2E423A483A524847503F432A3A2F3A3136302E350A0748473A" + $"2F3A392E352E423A483A520A043A2F4847503F432A0A042E3536303A313A3908" + $"0536B9932FBB773B3A3BB939C0E5B755050A0001001001178400040A01010100" + $"0A020102000A030103000A040104100117812004" +}; + +resource(203, "kActionForward2") #'VICN' array { + $"6E63696607050002000602396DF23A056B3B94FCBB09F547CD484AAB5F0090D9" + $"FFFF6ECDFF02000602B5E5793878A83E023B3B2AC34893F147CC490060C8FFFF" + $"44BEFD02000602B6F90538542D3C2FCA3ACA9249A8634A929A00034F77FF0468" + $"9C020006023AC62E392AB63B6BA0BCFF0B49DAAA4AAE4500046FA7FF2BA5E502" + $"0006023A1CB834EBA6383E33BD3E8D499DE44A647800ACE2FFFD6DCCFF04FF49" + $"080A09523A48533F503C532E4C2E3E3A363A2B482E0A07523A485348473C533C" + $"43483A482E0A04482E483A3A363A2B0A03484748533F500A043C433C532E4C2E" + $"3E0A04483A3A362E3E3C430802BD8FB795C2BDB8E008023C43B8C7BF0D070A00" + $"01001001178400040A040101000A020102000A030103000A010104000A050105" + $"000A06020607100117812004" +}; + +resource(204, "kActionStop") #'VICN' array { + $"6E63696606050102000602393D323BB602BEA28F3C4CC64B601449439F00FF3E" + $"3EFFF70606020006033B27153C10C7BDF9893D088A4B2F634882C500C805057D" + $"9D0404FFB4040403750303020006023B06EB3B5469BC08EB3BB31F4AB15D4478" + $"7700FF9090FFFF757504FF440A0A0C2E4C364E3C4742524A5440434A3842353C" + $"BE1B36312E2E38400A0636313C3C40434A54504E402E0A03364E3E483C470A04" + $"483FC712BADE4A3840430A042E2E3631402E382C0A0442354A38C712BADE4AB9" + $"EC0A0E2E4C364EBEB6C37042524A54504E483FC712BADE4AB9EC4335402E382C" + $"2E2E384008022FB8A0BCC3B80708033C3C42364ABA120802BCA9BFF3B8D3C47B" + $"060A0001061001178400040A020101000A03020302000A010100000A04020504" + $"000A0503070809100117810004" +}; + +resource(206, "kActionGoStart") #'VICN' array { + $"6E63696605050002001602B53D77BAF2653D77A7B7CFFC460F874A2D7700F0FF" + $"D50200160236FB2637191CBC57873C40E14B0E7149D82700A2FFBB0200060239" + $"FB483A6DC2BDAC713D02DB4B384746086300EA0606FFD005050200060238B0C7" + $"397C47BD2A963C71B74A82C348B2B700FF4444FFF106060A0A06304942525044" + $"50383E32303D0A09303B3049324A32C0723643364C4252424036320A04503650" + $"44425242400A082D3E3535434645455439462C35312B3C0A062B3C2D3E353543" + $"46454535310A0445455439462C35310A0432C072324A364C36430A043A453A4A" + $"3E4C3EC2630A044645464A484848430A044C3F4C444E424E3D070A0001001001" + $"178400040A010101000A020102000A000406070809000A000103100117840004" + $"0A030105000A04010400" +}; + +resource(205, "kActionGo") archive BBitmap { + "_frame" = rect { 0.0, 0.0, 15.0, 15.0 }, + "_cspace" = 8200, + "_bmflags" = 0, + "_rowbytes" = 64, + "_data" = array { + $"0000000000000000000000000000000000000000000000000000000000000000" + $"0000000000000000000000000000000000000000000000000000000000000000" + $"0000000000000000000000C1000000DE00000061000000070000000100000000" + $"0000000000000000000000000000000000000000000000000000000000000000" + $"0000000000000000000000FF9BD4A5FF48634EFF000000C00000003500000004" + $"0000000000000000000000000000000000000000000000000000000000000000" + $"0000000000000000000000FFB6F9C2FFB3F9C0FF84B68EFF263426FD0000009B" + $"0000001600000002000000000000000000000000000000000000000000000000" + $"0000000000000000000000FFB6F9C2FF7EF995FFA3F9B3FFAAE9B6FF69906FFF" + $"000000E200000064000000070000000100000000000000000000000000000000" + $"0000000000000000000000FFB6F9C2FF6AFB85FF6AFB85FF83FA98FFA4F5B2FF" + $"7CC88AFF325C3BFF000000C00000003500000004000000000000000000000000" + $"0000000000000000000000FFB6F9C2FF5DFC7BFF5DFC7BFF5DFC7BFF5DFB7CFF" + $"60ED7AFF54D76DFF389A48FF002A14FD0000009B000000160000000200000000" + $"0000000000000000000000FFB5F8C1FF59FC77FF59FC77FF59FC77FF59FC77FF" + $"59FC77FF55F674FF40DB5CFF2BBC48FF1C752BFF000000D00000002000000003" + $"0000000000000000000000FFA8F4B5FF2EFA54FF2EFA54FF2EFA54FF2EFA54FF" + $"2EFA54FF2BF350FF22D542FF1CB638FF157027FF000000D00000003D00000009" + $"0000000000000000000000FF92ECA2FF15F940FF15F940FF15F940FF15F740FF" + $"15DE35FF00BE2EFF008922FF002600FD0000009B000000430000002000000003" + $"0000000000000000000000FF7EE591FF15F83DFF15F83DFF1CEA3DFF00C732FF" + $"009E22FF004815FF000000BF000000560000002D0000000F0000000200000000" + $"0000000000000000000000FF6DDF81FF1CF340FF2BDA4AFF1CB638FF006D1CFF" + $"000000E200000074000000360000001900000004000000000000000000000000" + $"0000000000000000000000FF5DDA75FF44D25FFF229338FF002600FD0000009B" + $"0000004300000023000000070000000100000000000000000000000000000000" + $"0000000000000000000000FF46B65CFF1C5222FF000000BF000000560000002D" + $"0000000F00000002000000000000000000000000000000000000000000000000" + $"0000000000000000000000C2000000DE00000074000000360000001900000004" + $"0000000000000000000000000000000000000000000000000000000000000000" + $"0000000000000000000000030000001F00000020000000080000000100000000" + $"0000000000000000000000000000000000000000000000000000000000000000" + } +}; + + diff --git a/src/apps/webpositive/autocompletion/AutoCompleter.cpp b/src/apps/webpositive/autocompletion/AutoCompleter.cpp new file mode 100644 index 0000000000..09060512f6 --- /dev/null +++ b/src/apps/webpositive/autocompletion/AutoCompleter.cpp @@ -0,0 +1,253 @@ +/* + * Copyright 2002-2006, project beam (http://sourceforge.net/projects/beam). + * All rights reserved. Distributed under the terms of the MIT License. + * + * Authors: + * Oliver Tappe + */ + +#include "AutoCompleter.h" + +#include +#include + +#include "AutoCompleterDefaultImpl.h" + + +// #pragma mark - DefaultPatternSelector + + +class DefaultPatternSelector : public BAutoCompleter::PatternSelector { +public: + virtual void SelectPatternBounds(const BString& text, int32 caretPos, + int32* start, int32* length); +}; + + +void +DefaultPatternSelector::SelectPatternBounds(const BString& text, + int32 caretPos, int32* start, int32* length) +{ + if (!start || !length) + return; + *start = 0; + *length = text.Length(); +} + + +// #pragma mark - CompletionStyle + + +BAutoCompleter::CompletionStyle::CompletionStyle(EditView* editView, + ChoiceModel* choiceModel, ChoiceView* choiceView, + PatternSelector* patternSelector) + : + fEditView(editView), + fPatternSelector(patternSelector ? patternSelector + : new DefaultPatternSelector()), + fChoiceModel(choiceModel), + fChoiceView(choiceView) +{ +} + + +BAutoCompleter::CompletionStyle::~CompletionStyle() +{ + delete fEditView; + delete fChoiceModel; + delete fChoiceView; + delete fPatternSelector; +} + + +void +BAutoCompleter::CompletionStyle::SetEditView(EditView* view) +{ + delete fEditView; + fEditView = view; +} + + +void +BAutoCompleter::CompletionStyle::SetPatternSelector( + PatternSelector* selector) +{ + delete fPatternSelector; + fPatternSelector = selector; +} + + +void +BAutoCompleter::CompletionStyle::SetChoiceModel(ChoiceModel* model) +{ + delete fChoiceModel; + fChoiceModel = model; +} + + +void +BAutoCompleter::CompletionStyle::SetChoiceView(ChoiceView* view) +{ + delete fChoiceView; + fChoiceView = view; +} + + +// #pragma mark - BAutoCompleter + + +BAutoCompleter::BAutoCompleter(CompletionStyle* completionStyle) + : + fCompletionStyle(completionStyle) +{ +} + + +BAutoCompleter::BAutoCompleter(EditView* editView, ChoiceModel* choiceModel, + ChoiceView* choiceView, PatternSelector* patternSelector) + : + fCompletionStyle(new BDefaultCompletionStyle(editView, choiceModel, + choiceView, patternSelector)) +{ +} + + +BAutoCompleter::~BAutoCompleter() +{ + delete fCompletionStyle; +} + + +bool +BAutoCompleter::Select(int32 index) +{ + if (fCompletionStyle) + return fCompletionStyle->Select(index); + else + return false; +} + + +bool +BAutoCompleter::SelectNext(bool wrap) +{ + if (fCompletionStyle) + return fCompletionStyle->SelectNext(wrap); + else + return false; +} + + +bool +BAutoCompleter::SelectPrevious(bool wrap) +{ + if (fCompletionStyle) + return fCompletionStyle->SelectPrevious(wrap); + else + return false; +} + + +bool +BAutoCompleter::IsChoiceSelected() const +{ + if (fCompletionStyle) + return fCompletionStyle->IsChoiceSelected(); + else + return false; +} + + +int32 +BAutoCompleter::CountChoices() const +{ + if (fCompletionStyle && fCompletionStyle->GetChoiceModel()) + return fCompletionStyle->GetChoiceModel()->CountChoices(); + else + return 0; +} + + +int32 +BAutoCompleter::CountVisibleChoices() const +{ + if (fCompletionStyle && fCompletionStyle->GetChoiceView()) + return fCompletionStyle->GetChoiceView()->CountVisibleChoices(); + else + return 0; +} + + +int32 +BAutoCompleter::SelectedChoiceIndex() const +{ + if (fCompletionStyle) + return fCompletionStyle->SelectedChoiceIndex(); + else + return -1; +} + + +void +BAutoCompleter::ApplyChoice(bool hideChoices) +{ + if (fCompletionStyle) + fCompletionStyle->ApplyChoice(hideChoices); +} + + +void +BAutoCompleter::CancelChoice() +{ + if (fCompletionStyle) + fCompletionStyle->CancelChoice(); +} + + +void +BAutoCompleter::EditViewStateChanged(bool updateChoices) +{ + if (fCompletionStyle) + fCompletionStyle->EditViewStateChanged(updateChoices); +} + + +void +BAutoCompleter::SetEditView(EditView* view) +{ + if (fCompletionStyle) + fCompletionStyle->SetEditView(view); +} + + +void +BAutoCompleter::SetPatternSelector(PatternSelector* selector) +{ + if (fCompletionStyle) + fCompletionStyle->SetPatternSelector(selector); +} + + +void +BAutoCompleter::SetChoiceModel(ChoiceModel* model) +{ + if (fCompletionStyle) + fCompletionStyle->SetChoiceModel(model); +} + + +void +BAutoCompleter::SetChoiceView(ChoiceView* view) +{ + if (fCompletionStyle) + fCompletionStyle->SetChoiceView(view); +} + + +void +BAutoCompleter::SetCompletionStyle(CompletionStyle* style) +{ + delete fCompletionStyle; + fCompletionStyle = style; +} + diff --git a/src/apps/webpositive/autocompletion/AutoCompleter.h b/src/apps/webpositive/autocompletion/AutoCompleter.h new file mode 100644 index 0000000000..dab8268db2 --- /dev/null +++ b/src/apps/webpositive/autocompletion/AutoCompleter.h @@ -0,0 +1,161 @@ +/* + * Copyright 2002-2006, project beam (http://sourceforge.net/projects/beam). + * All rights reserved. Distributed under the terms of the MIT License. + * + * Authors: + * Oliver Tappe + */ +#ifndef _AUTO_COMPLETER_H +#define _AUTO_COMPLETER_H + +#include + +#include +#include + +class BAutoCompleter { +public: + class Choice { + public: + Choice(const BString& choiceText, + const BString& displayText, int32 matchPos, + int32 matchLen) + : + fText(choiceText), + fDisplayText(displayText), + fMatchPos(matchPos), + fMatchLen(matchLen) + { + } + virtual ~Choice() {} + const BString& Text() const { return fText; } + const BString& DisplayText() const { return fDisplayText; } + int32 MatchPos() const { return fMatchPos; } + int32 MatchLen() const { return fMatchLen; } + + private: + BString fText; + BString fDisplayText; + int32 fMatchPos; + int32 fMatchLen; + }; + + class EditView { + public: + virtual ~EditView() {} + + virtual BRect GetAdjustmentFrame() = 0; + virtual void GetEditViewState(BString& text, + int32* caretPos) = 0; + virtual void SetEditViewState(const BString& text, + int32 caretPos, + int32 selectionLength = 0) = 0; + }; + + class PatternSelector { + public: + virtual ~PatternSelector() {} + + virtual void SelectPatternBounds(const BString& text, + int32 caretPos, int32* start, + int32* length) = 0; + }; + + class ChoiceModel { + public: + + virtual ~ChoiceModel() {} + + virtual void FetchChoicesFor(const BString& pattern) = 0; + + virtual int32 CountChoices() const = 0; + virtual const Choice* ChoiceAt(int32 index) const = 0; + }; + + class CompletionStyle; + class ChoiceView { + public: + virtual ~ChoiceView() {} + + virtual void SelectChoiceAt(int32 index) = 0; + virtual void ShowChoices( + BAutoCompleter::CompletionStyle* completer) + = 0; + virtual void HideChoices() = 0; + virtual bool ChoicesAreShown() = 0; + virtual int32 CountVisibleChoices() const = 0; + }; + + class CompletionStyle { + public: + CompletionStyle(EditView* editView, + ChoiceModel* choiceModel, + ChoiceView* choiceView, + PatternSelector* patternSelector); + virtual ~CompletionStyle(); + + virtual bool Select(int32 index) = 0; + virtual bool SelectNext(bool wrap = false) = 0; + virtual bool SelectPrevious(bool wrap = false) = 0; + virtual bool IsChoiceSelected() const = 0; + virtual int32 SelectedChoiceIndex() const = 0; + + virtual void ApplyChoice(bool hideChoices = true) = 0; + virtual void CancelChoice() = 0; + + virtual void EditViewStateChanged(bool updateChoices) = 0; + + void SetEditView(EditView* view); + void SetPatternSelector(PatternSelector* selector); + void SetChoiceModel(ChoiceModel* model); + void SetChoiceView(ChoiceView* view); + + EditView* GetEditView() { return fEditView; } + PatternSelector* GetPatternSelector() + { return fPatternSelector; } + ChoiceModel* GetChoiceModel() { return fChoiceModel; } + ChoiceView* GetChoiceView() { return fChoiceView; } + + protected: + EditView* fEditView; + PatternSelector* fPatternSelector; + ChoiceModel* fChoiceModel; + ChoiceView* fChoiceView; + }; + +protected: + BAutoCompleter( + CompletionStyle* completionStyle = NULL); + BAutoCompleter(EditView* editView, + ChoiceModel* choiceModel, + ChoiceView* choiceView, + PatternSelector* patternSelector); + virtual ~BAutoCompleter(); + + void EditViewStateChanged( + bool updateChoices = true); + + bool Select(int32 index); + bool SelectNext(bool wrap = false); + bool SelectPrevious(bool wrap = false); + bool IsChoiceSelected() const; + int32 CountChoices() const; + int32 CountVisibleChoices() const; + int32 SelectedChoiceIndex() const; + + void ApplyChoice(bool hideChoices = true); + void CancelChoice(); + + void SetEditView(EditView* view); + void SetPatternSelector(PatternSelector* selector); + void SetChoiceModel(ChoiceModel* model); + void SetChoiceView(ChoiceView* view); + + void SetCompletionStyle(CompletionStyle* style); + +private: + CompletionStyle* fCompletionStyle; +}; + + +#endif // _AUTO_COMPLETER_H diff --git a/src/apps/webpositive/autocompletion/AutoCompleterDefaultImpl.cpp b/src/apps/webpositive/autocompletion/AutoCompleterDefaultImpl.cpp new file mode 100644 index 0000000000..0808239ef3 --- /dev/null +++ b/src/apps/webpositive/autocompletion/AutoCompleterDefaultImpl.cpp @@ -0,0 +1,467 @@ +/* + * Copyright 2002-2006, project beam (http://sourceforge.net/projects/beam). + * All rights reserved. Distributed under the terms of the MIT License. + * + * Authors: + * Oliver Tappe + */ + +#include "AutoCompleterDefaultImpl.h" + +#include +#include +#include +#include + + +// #pragma mark - BDefaultPatternSelector + + +void +BDefaultPatternSelector::SelectPatternBounds(const BString& text, + int32 caretPos, int32* start, int32* length) +{ + if (!start || !length) + return; + *start = 0; + *length = text.Length(); +} + + +// #pragma mark - BDefaultCompletionStyle + + +BDefaultCompletionStyle::BDefaultCompletionStyle( + BAutoCompleter::EditView* editView, + BAutoCompleter::ChoiceModel* choiceModel, + BAutoCompleter::ChoiceView* choiceView, + BAutoCompleter::PatternSelector* patternSelector) + : + CompletionStyle(editView, choiceModel, choiceView, patternSelector), + fSelectedIndex(-1), + fPatternStartPos(0), + fPatternLength(0), + fIgnoreEditViewStateChanges(false) +{ +} + + +BDefaultCompletionStyle::~BDefaultCompletionStyle() +{ +} + + +bool +BDefaultCompletionStyle::Select(int32 index) +{ + if (!fChoiceView || !fChoiceModel || index == fSelectedIndex + || index < -1 || index >= fChoiceModel->CountChoices()) { + return false; + } + + fSelectedIndex = index; + fChoiceView->SelectChoiceAt(index); + return true; +} + + +bool +BDefaultCompletionStyle::SelectNext(bool wrap) +{ + if (!fChoiceModel || fChoiceModel->CountChoices() == 0) + return false; + + int32 newIndex = fSelectedIndex + 1; + if (newIndex >= fChoiceModel->CountChoices()) { + if (wrap) + newIndex = 0; + else + newIndex = fSelectedIndex; + } + return Select(newIndex); +} + + +bool +BDefaultCompletionStyle::SelectPrevious(bool wrap) +{ + if (!fChoiceModel || fChoiceModel->CountChoices() == 0) + return false; + + int32 newIndex = fSelectedIndex - 1; + if (newIndex < 0) { + if (wrap) + newIndex = fChoiceModel->CountChoices() - 1; + else + newIndex = 0; + } + return Select(newIndex); +} + + +bool +BDefaultCompletionStyle::IsChoiceSelected() const +{ + return fSelectedIndex >= 0; +} + + +int32 +BDefaultCompletionStyle::SelectedChoiceIndex() const +{ + return fSelectedIndex; +} + + +void +BDefaultCompletionStyle::ApplyChoice(bool hideChoices) +{ + if (!fChoiceModel || !fChoiceView || !fEditView || fSelectedIndex < 0) + return; + + BString completedText(fFullEnteredText); + completedText.Remove(fPatternStartPos, fPatternLength); + const BString& choiceStr = fChoiceModel->ChoiceAt(fSelectedIndex)->Text(); + completedText.Insert(choiceStr, fPatternStartPos); + + fIgnoreEditViewStateChanges = true; + + fFullEnteredText = completedText; + fPatternLength = choiceStr.Length(); + fEditView->SetEditViewState(completedText, + fPatternStartPos + choiceStr.Length()); + + if (hideChoices) { + fChoiceView->HideChoices(); + Select(-1); + } + + fIgnoreEditViewStateChanges = false; +} + + +void +BDefaultCompletionStyle::CancelChoice() +{ + if (!fChoiceView || !fEditView) + return; + if (fChoiceView->ChoicesAreShown()) { + fIgnoreEditViewStateChanges = true; + + fEditView->SetEditViewState(fFullEnteredText, + fPatternStartPos + fPatternLength); + fChoiceView->HideChoices(); + Select(-1); + + fIgnoreEditViewStateChanges = false; + } +} + +void +BDefaultCompletionStyle::EditViewStateChanged(bool updateChoices) +{ + if (fIgnoreEditViewStateChanges || !fChoiceModel || !fChoiceView + || !fEditView) { + return; + } + + BString text; + int32 caretPos; + fEditView->GetEditViewState(text, &caretPos); + if (fFullEnteredText == text) + return; + + fFullEnteredText = text; + + if (!updateChoices) + return; + + fPatternSelector->SelectPatternBounds(text, caretPos, &fPatternStartPos, + &fPatternLength); + BString pattern(text.String() + fPatternStartPos, fPatternLength); + fChoiceModel->FetchChoicesFor(pattern); + + Select(-1); + // show a single choice only if it doesn't match the pattern exactly: + if (fChoiceModel->CountChoices() > 1 || (fChoiceModel->CountChoices() == 1 + && pattern.ICompare(fChoiceModel->ChoiceAt(0)->Text())) != 0) { + fChoiceView->ShowChoices(this); + fChoiceView->SelectChoiceAt(fSelectedIndex); + } else + fChoiceView->HideChoices(); +} + + +// #pragma mark - BDefaultChoiceView::ListView + + +static const int32 MSG_INVOKED = 'invk'; + + +BDefaultChoiceView::ListView::ListView( + BAutoCompleter::CompletionStyle* completer) + : + BListView(BRect(0, 0, 100, 100), "ChoiceViewList"), + fCompleter(completer) +{ + // we need to check if user clicks outside of window-bounds: + SetEventMask(B_POINTER_EVENTS); +} + + +void +BDefaultChoiceView::ListView::AttachedToWindow() +{ + SetTarget(this); + SetInvocationMessage(new BMessage(MSG_INVOKED)); + BListView::AttachedToWindow(); +} + + +void +BDefaultChoiceView::ListView::SelectionChanged() +{ + fCompleter->Select(CurrentSelection(0)); +} + + +void +BDefaultChoiceView::ListView::MessageReceived(BMessage* message) +{ + switch(message->what) { + case MSG_INVOKED: + fCompleter->ApplyChoice(); + break; + default: + BListView::MessageReceived(message); + } +} + + +void +BDefaultChoiceView::ListView::MouseDown(BPoint point) +{ + if (!Window()->Frame().Contains(ConvertToScreen(point))) + // click outside of window, so we close it: + Window()->Quit(); + else + BListView::MouseDown(point); +} + + +// #pragma mark - BDefaultChoiceView::ListItem + + +BDefaultChoiceView::ListItem::ListItem(const BAutoCompleter::Choice* choice) + : + BListItem() +{ + fPreText = choice->DisplayText(); + if (choice->MatchLen() > 0) { + fPreText.MoveInto(fMatchText, choice->MatchPos(), choice->MatchLen()); + fPreText.MoveInto(fPostText, choice->MatchPos(), fPreText.Length()); + } +} + + +void +BDefaultChoiceView::ListItem::DrawItem(BView* owner, BRect frame, + bool complete) +{ + rgb_color textColor; + rgb_color nonMatchTextColor; + rgb_color backColor; + rgb_color matchColor; + if (IsSelected()) { + textColor = ui_color(B_MENU_SELECTED_ITEM_TEXT_COLOR); + backColor = ui_color(B_MENU_SELECTED_BACKGROUND_COLOR); + } else { + textColor = ui_color(B_DOCUMENT_TEXT_COLOR); + backColor = ui_color(B_DOCUMENT_BACKGROUND_COLOR); + } + matchColor = tint_color(backColor, (B_NO_TINT + B_DARKEN_1_TINT) / 2); + nonMatchTextColor = tint_color(backColor, 1.7); + + BFont font; + font_height fontHeight; + owner->GetFont(&font); + font.GetHeight(&fontHeight); + float xPos = frame.left + 1; + float yPos = frame.top + fontHeight.ascent; + float w; + if (fPreText.Length()) { + w = owner->StringWidth(fPreText.String()); + owner->SetLowColor(backColor); + owner->FillRect(BRect(xPos, frame.top, xPos + w - 1, frame.bottom), + B_SOLID_LOW); + owner->SetHighColor(nonMatchTextColor); + owner->DrawString(fPreText.String(), BPoint(xPos, yPos)); + xPos += w; + } + if (fMatchText.Length()) { + w = owner->StringWidth(fMatchText.String()); + owner->SetLowColor(matchColor); + owner->FillRect(BRect(xPos, frame.top, xPos + w - 1, frame.bottom), + B_SOLID_LOW); + owner->SetHighColor(textColor); + owner->DrawString(fMatchText.String(), BPoint(xPos, yPos)); + xPos += w; + } + if (fPostText.Length()) { + w = owner->StringWidth(fPostText.String()); + owner->SetLowColor(backColor); + owner->FillRect(BRect(xPos, frame.top, xPos + w - 1, frame.bottom), + B_SOLID_LOW); + owner->SetHighColor(nonMatchTextColor); + owner->DrawString(fPostText.String(), BPoint(xPos, yPos)); + } +} + + +// #pragma mark - BDefaultChoiceView + + +BDefaultChoiceView::BDefaultChoiceView() + : + fWindow(NULL), + fListView(NULL), + fMaxVisibleChoices(8) +{ + +} + + +BDefaultChoiceView::~BDefaultChoiceView() +{ + HideChoices(); +} + + +void +BDefaultChoiceView::SelectChoiceAt(int32 index) +{ + if (fListView && fListView->LockLooper()) { + if (index < 0) + fListView->DeselectAll(); + else { + fListView->Select(index); + fListView->ScrollToSelection(); + } + fListView->UnlockLooper(); + } +} + + +void +BDefaultChoiceView::ShowChoices(BAutoCompleter::CompletionStyle* completer) +{ + if (!completer) + return; + + HideChoices(); + + BAutoCompleter::ChoiceModel* choiceModel = completer->GetChoiceModel(); + BAutoCompleter::EditView* editView = completer->GetEditView(); + + if (!editView || !choiceModel || choiceModel->CountChoices() == 0) + return; + + fListView = new ListView(completer); + int32 count = choiceModel->CountChoices(); + for(int32 i = 0; iAddItem( + new ListItem(choiceModel->ChoiceAt(i)) + ); + } + + BScrollView *scrollView = NULL; + if (count > fMaxVisibleChoices) { + scrollView = new BScrollView("", fListView, B_FOLLOW_NONE, 0, false, true, B_NO_BORDER); + } + + fWindow = new BWindow(BRect(0, 0, 100, 100), "", B_BORDERED_WINDOW_LOOK, + B_NORMAL_WINDOW_FEEL, B_NOT_MOVABLE | B_WILL_ACCEPT_FIRST_CLICK + | B_AVOID_FOCUS | B_ASYNCHRONOUS_CONTROLS); + if (scrollView != NULL) + fWindow->AddChild(scrollView); + else + fWindow->AddChild(fListView); + + int32 visibleCount = min_c(count, fMaxVisibleChoices); + float listHeight = fListView->ItemFrame(visibleCount - 1).bottom + 1; + + BRect pvRect = editView->GetAdjustmentFrame(); + BRect listRect = pvRect; + listRect.bottom = listRect.top + listHeight - 1; + BRect screenRect = BScreen().Frame(); + if (listRect.bottom + 1 + listHeight <= screenRect.bottom) + listRect.OffsetTo(pvRect.left, pvRect.bottom + 1); + else + listRect.OffsetTo(pvRect.left, pvRect.top - listHeight); + + if (scrollView != NULL) { + // Moving here to cut off the scrollbar top + scrollView->MoveTo(0, -1); + // Adding the 1 and 2 to cut-off the scroll-bar top, right and bottom + scrollView->ResizeTo(listRect.Width() + 1, listRect.Height() + 2); + // Move here to compensate for the above + fListView->MoveTo(0, 1); + fListView->ResizeTo(listRect.Width() - B_V_SCROLL_BAR_WIDTH, listRect.Height()); + } else { + fListView->MoveTo(0, 0); + fListView->ResizeTo(listRect.Width(), listRect.Height()); + } + fWindow->MoveTo(listRect.left, listRect.top); + fWindow->ResizeTo(listRect.Width(), listRect.Height()); + fWindow->Show(); +} + + +void +BDefaultChoiceView::HideChoices() +{ + if (fWindow && fWindow->Lock()) { + fWindow->Quit(); + fWindow = NULL; + fListView = NULL; + } +} + + +bool +BDefaultChoiceView::ChoicesAreShown() +{ + return (fWindow != NULL); +} + + +int32 +BDefaultChoiceView::CountVisibleChoices() const +{ + if (fListView) + return min_c(fMaxVisibleChoices, fListView->CountItems()); + else + return 0; +} + + +void +BDefaultChoiceView::SetMaxVisibleChoices(int32 choices) +{ + if (choices < 1) + choices = 1; + if (choices == fMaxVisibleChoices) + return; + + fMaxVisibleChoices = choices; + + // TODO: Update live? +} + + +int32 +BDefaultChoiceView::MaxVisibleChoices() const +{ + return fMaxVisibleChoices; +} + diff --git a/src/apps/webpositive/autocompletion/AutoCompleterDefaultImpl.h b/src/apps/webpositive/autocompletion/AutoCompleterDefaultImpl.h new file mode 100644 index 0000000000..6c3e50e8f5 --- /dev/null +++ b/src/apps/webpositive/autocompletion/AutoCompleterDefaultImpl.h @@ -0,0 +1,100 @@ +/* + * Copyright 2002-2006, project beam (http://sourceforge.net/projects/beam). + * All rights reserved. Distributed under the terms of the MIT License. + * + * Authors: + * Oliver Tappe + */ +#ifndef _AUTO_COMPLETER_DEFAULT_IMPL_H +#define _AUTO_COMPLETER_DEFAULT_IMPL_H + +#include +#include + +#include "AutoCompleter.h" + +class BDefaultPatternSelector : public BAutoCompleter::PatternSelector { +public: + virtual void SelectPatternBounds(const BString& text, + int32 caretPos, int32* start, + int32* length); +}; + + +class BDefaultCompletionStyle : public BAutoCompleter::CompletionStyle { +public: + BDefaultCompletionStyle( + BAutoCompleter::EditView* editView, + BAutoCompleter::ChoiceModel* choiceModel, + BAutoCompleter::ChoiceView* choiceView, + BAutoCompleter::PatternSelector* + patternSelector); + virtual ~BDefaultCompletionStyle(); + + virtual bool Select(int32 index); + virtual bool SelectNext(bool wrap = false); + virtual bool SelectPrevious(bool wrap = false); + virtual bool IsChoiceSelected() const; + virtual int32 SelectedChoiceIndex() const; + + virtual void ApplyChoice(bool hideChoices = true); + virtual void CancelChoice(); + + virtual void EditViewStateChanged(bool updateChoices); + +private: + BString fFullEnteredText; + int32 fSelectedIndex; + int32 fPatternStartPos; + int32 fPatternLength; + bool fIgnoreEditViewStateChanges; +}; + + +class BDefaultChoiceView : public BAutoCompleter::ChoiceView { +protected: + class ListView : public BListView { + public: + ListView( + BAutoCompleter::CompletionStyle* completer); + virtual void SelectionChanged(); + virtual void MessageReceived(BMessage* msg); + virtual void MouseDown(BPoint point); + virtual void AttachedToWindow(); + + private: + BAutoCompleter::CompletionStyle* fCompleter; + }; + + class ListItem : public BListItem { + public: + ListItem(const BAutoCompleter::Choice* choice); + virtual void DrawItem(BView* owner, BRect frame, + bool complete = false); + private: + BString fPreText; + BString fMatchText; + BString fPostText; + }; + +public: + BDefaultChoiceView(); + virtual ~BDefaultChoiceView(); + + virtual void SelectChoiceAt(int32 index); + virtual void ShowChoices( + BAutoCompleter::CompletionStyle* completer); + virtual void HideChoices(); + virtual bool ChoicesAreShown(); + virtual int32 CountVisibleChoices() const; + + void SetMaxVisibleChoices(int32 choices); + int32 MaxVisibleChoices() const; + +private: + BWindow* fWindow; + ListView* fListView; + int32 fMaxVisibleChoices; +}; + +#endif // _AUTO_COMPLETER_DEFAULT_IMPL_H diff --git a/src/apps/webpositive/autocompletion/TextViewCompleter.cpp b/src/apps/webpositive/autocompletion/TextViewCompleter.cpp new file mode 100644 index 0000000000..48f7bcb4da --- /dev/null +++ b/src/apps/webpositive/autocompletion/TextViewCompleter.cpp @@ -0,0 +1,169 @@ +/* + * Copyright 2002-2006, project beam (http://sourceforge.net/projects/beam). + * All rights reserved. Distributed under the terms of the MIT License. + * + * Authors: + * Oliver Tappe + */ + +#include "TextViewCompleter.h" + +#include +#include +#include + +#include "AutoCompleterDefaultImpl.h" + + +// #pragma mark - TextViewWrapper + + +TextViewCompleter::TextViewWrapper::TextViewWrapper(BTextView* textView) + : + fTextView(textView) +{ +} + + +void +TextViewCompleter::TextViewWrapper::GetEditViewState(BString& text, + int32* caretPos) +{ + if (fTextView && fTextView->LockLooper()) { + text = fTextView->Text(); + if (caretPos) { + int32 end; + fTextView->GetSelection(caretPos, &end); + } + fTextView->UnlockLooper(); + } +} + + +void +TextViewCompleter::TextViewWrapper::SetEditViewState(const BString& text, + int32 caretPos, int32 selectionLength) +{ + if (fTextView && fTextView->LockLooper()) { + fTextView->SetText(text.String(), text.Length()); + fTextView->Select(caretPos, caretPos + selectionLength); + fTextView->ScrollToSelection(); + fTextView->UnlockLooper(); + } +} + + +BRect +TextViewCompleter::TextViewWrapper::GetAdjustmentFrame() +{ + BRect frame = fTextView->Bounds(); + frame = fTextView->ConvertToScreen(frame); + frame.InsetBy(0, -3); + return frame; +} + + +// #pragma mark - + + +TextViewCompleter::TextViewCompleter(BTextView* textView, ChoiceModel* model, + PatternSelector* patternSelector) + : + BAutoCompleter(new TextViewWrapper(textView), model, + new BDefaultChoiceView(), patternSelector), + BMessageFilter(B_KEY_DOWN), + fTextView(textView), + fModificationsReported(false) +{ + fTextView->AddFilter(this); +} + + +TextViewCompleter::~TextViewCompleter() +{ + fTextView->RemoveFilter(this); +} + + +void +TextViewCompleter::SetModificationsReported(bool reported) +{ + fModificationsReported = reported; +} + + +void +TextViewCompleter::TextModified(bool updateChoices) +{ + EditViewStateChanged(updateChoices); +} + + +filter_result +TextViewCompleter::Filter(BMessage* message, BHandler** target) +{ + const char* bytes; + int32 modifiers; + if ((!target || message->FindString("bytes", &bytes) != B_OK + || message->FindInt32("modifiers", &modifiers) != B_OK) + || (modifiers & (B_CONTROL_KEY | B_COMMAND_KEY | B_OPTION_KEY + | B_SHIFT_KEY)) != 0) { + return B_DISPATCH_MESSAGE; + } + + switch (bytes[0]) { + case B_UP_ARROW: + SelectPrevious(); + // Insert the current choice into the text view, so the user can + // continue typing. This will not trigger another evaluation, since + // we don't invoke EditViewStateChanged(). + ApplyChoice(false); + return B_SKIP_MESSAGE; + case B_DOWN_ARROW: + SelectNext(); + // See above. + ApplyChoice(false); + return B_SKIP_MESSAGE; + case B_PAGE_UP: + { + int32 index = SelectedChoiceIndex() - CountVisibleChoices(); + index = max_c(index, 0); + Select(index); + ApplyChoice(false); + return B_SKIP_MESSAGE; + } + case B_PAGE_DOWN: + { + int32 index = SelectedChoiceIndex() + CountVisibleChoices(); + index = min_c(index, CountChoices() - 1); + Select(index); + ApplyChoice(false); + return B_SKIP_MESSAGE; + } + + case B_ESCAPE: + CancelChoice(); + return B_DISPATCH_MESSAGE; + case B_RETURN: + if (IsChoiceSelected()) { + ApplyChoice(); + EditViewStateChanged(); + } else + CancelChoice(); + return B_DISPATCH_MESSAGE; + case B_TAB: { + // make sure that the choices-view is closed when tabbing out: + CancelChoice(); + return B_DISPATCH_MESSAGE; + } + default: + if (!fModificationsReported) { + // dispatch message to textview manually... + Looper()->DispatchMessage(message, *target); + // ...and propagate the new state to the auto-completer: + EditViewStateChanged(); + return B_SKIP_MESSAGE; + } + return B_DISPATCH_MESSAGE; + } +} diff --git a/src/apps/webpositive/autocompletion/TextViewCompleter.h b/src/apps/webpositive/autocompletion/TextViewCompleter.h new file mode 100644 index 0000000000..1a2955f6d7 --- /dev/null +++ b/src/apps/webpositive/autocompletion/TextViewCompleter.h @@ -0,0 +1,48 @@ +/* + * Copyright 2002-2006, project beam (http://sourceforge.net/projects/beam). + * All rights reserved. Distributed under the terms of the MIT License. + * + * Authors: + * Oliver Tappe + */ +#ifndef TEXT_CONTROL_COMPLETER_H +#define TEXT_CONTROL_COMPLETER_H + +#include + +#include "AutoCompleter.h" + + +class BTextView; + +class TextViewCompleter : protected BAutoCompleter, public BMessageFilter { +public: + TextViewCompleter(BTextView* textView, + ChoiceModel* choiceModel = NULL, + PatternSelector* patternSelector = NULL); + virtual ~TextViewCompleter(); + + void SetModificationsReported(bool reported); + void TextModified(bool updateChoices); + +private: + virtual filter_result Filter(BMessage* message, BHandler** target); + + class TextViewWrapper : public EditView { + public: + TextViewWrapper(BTextView* textView); + virtual BRect GetAdjustmentFrame(); + virtual void GetEditViewState(BString& text, + int32* caretPos); + virtual void SetEditViewState(const BString& text, + int32 caretPos, int32 selectionLength = 0); + private: + BTextView* fTextView; + }; + +private: + BTextView* fTextView; + bool fModificationsReported; +}; + +#endif // TEXT_CONTROL_COMPLETER_H diff --git a/src/apps/webpositive/support/AutoLocker.h b/src/apps/webpositive/support/AutoLocker.h new file mode 100644 index 0000000000..9baa7aa7a5 --- /dev/null +++ b/src/apps/webpositive/support/AutoLocker.h @@ -0,0 +1,178 @@ +/* + * Copyright 2005-2007, Ingo Weinhold, bonefish@users.sf.net. + * All rights reserved. Distributed under the terms of the MIT License. + */ +#ifndef _AUTO_LOCKER_H +#define _AUTO_LOCKER_H + + +#include + + +namespace BPrivate { + +// AutoLockerStandardLocking +template +class AutoLockerStandardLocking { +public: + inline bool Lock(Lockable* lockable) + { + return lockable->Lock(); + } + + inline void Unlock(Lockable* lockable) + { + lockable->Unlock(); + } +}; + +// AutoLockerReadLocking +template +class AutoLockerReadLocking { +public: + inline bool Lock(Lockable* lockable) + { + return lockable->ReadLock(); + } + + inline void Unlock(Lockable* lockable) + { + lockable->ReadUnlock(); + } +}; + +// AutoLockerWriteLocking +template +class AutoLockerWriteLocking { +public: + inline bool Lock(Lockable* lockable) + { + return lockable->WriteLock(); + } + + inline void Unlock(Lockable* lockable) + { + lockable->WriteUnlock(); + } +}; + +// AutoLocker +template > +class AutoLocker { +private: + typedef AutoLocker ThisClass; +public: + inline AutoLocker() + : + fLockable(NULL), + fLocked(false) + { + } + + inline AutoLocker(const Locking& locking) + : + fLockable(NULL), + fLocking(locking), + fLocked(false) + { + } + + inline AutoLocker(Lockable* lockable, bool alreadyLocked = false, + bool lockIfNotLocked = true) + : + fLockable(lockable), + fLocked(fLockable && alreadyLocked) + { + if (!alreadyLocked && lockIfNotLocked) + Lock(); + } + + inline AutoLocker(Lockable& lockable, bool alreadyLocked = false, + bool lockIfNotLocked = true) + : + fLockable(&lockable), + fLocked(fLockable && alreadyLocked) + { + if (!alreadyLocked && lockIfNotLocked) + Lock(); + } + + inline ~AutoLocker() + { + Unlock(); + } + + inline void SetTo(Lockable* lockable, bool alreadyLocked, + bool lockIfNotLocked = true) + { + Unlock(); + fLockable = lockable; + fLocked = (lockable && alreadyLocked); + if (!alreadyLocked && lockIfNotLocked) + Lock(); + } + + inline void SetTo(Lockable& lockable, bool alreadyLocked, + bool lockIfNotLocked = true) + { + SetTo(&lockable, alreadyLocked, lockIfNotLocked); + } + + inline void Unset() + { + Unlock(); + Detach(); + } + + inline bool Lock() + { + if (fLockable && !fLocked) + fLocked = fLocking.Lock(fLockable); + return fLocked; + } + + inline void Unlock() + { + if (fLockable && fLocked) { + fLocking.Unlock(fLockable); + fLocked = false; + } + } + + inline void Detach() + { + fLockable = NULL; + fLocked = false; + } + + inline AutoLocker& operator=(Lockable* lockable) + { + SetTo(lockable); + return *this; + } + + inline AutoLocker& operator=(Lockable& lockable) + { + SetTo(&lockable); + return *this; + } + + inline bool IsLocked() const { return fLocked; } + + inline operator bool() const { return fLocked; } + +protected: + Lockable* fLockable; + Locking fLocking; + bool fLocked; +}; + + +} // namespace BPrivate + +using BPrivate::AutoLocker; +using BPrivate::AutoLockerReadLocking; +using BPrivate::AutoLockerWriteLocking; + +#endif // _AUTO_LOCKER_H diff --git a/src/apps/webpositive/support/BaseURL.cpp b/src/apps/webpositive/support/BaseURL.cpp new file mode 100644 index 0000000000..4d2f47cd29 --- /dev/null +++ b/src/apps/webpositive/support/BaseURL.cpp @@ -0,0 +1,18 @@ +/* + * Copyright 2010 Stephan Aßmus + * All rights reserved. Distributed under the terms of the MIT License. + */ + +#include "BaseURL.h" + + +BString +baseURL(const BString string) +{ + int32 baseURLStart = string.FindFirst("://") + 3; + int32 baseURLEnd = string.FindFirst("/", baseURLStart + 1); + BString result; + result.SetTo(string.String() + baseURLStart, baseURLEnd - baseURLStart); + return result; +} + diff --git a/src/apps/webpositive/support/BaseURL.h b/src/apps/webpositive/support/BaseURL.h new file mode 100644 index 0000000000..b4589ef58f --- /dev/null +++ b/src/apps/webpositive/support/BaseURL.h @@ -0,0 +1,14 @@ +/* + * Copyright 2010 Stephan Aßmus + * All rights reserved. Distributed under the terms of the MIT License. + */ +#ifndef BASE_URL_H +#define BASE_URL_H + +#include + + +BString baseURL(const BString string); + + +#endif // BASE_URL_H diff --git a/src/apps/webpositive/support/BitmapButton.cpp b/src/apps/webpositive/support/BitmapButton.cpp new file mode 100644 index 0000000000..009079969c --- /dev/null +++ b/src/apps/webpositive/support/BitmapButton.cpp @@ -0,0 +1,119 @@ +/* + * Copyright 2010 Stephan Aßmus . All rights reserved. + * Distributed under the terms of the MIT License. + */ + +#include "BitmapButton.h" + +#include + +#include +#include +#include + + +static const float kFrameInset = 2; + + +BitmapButton::BitmapButton(const char* resourceName, BMessage* message) + : + BButton("", message), + fBitmap(BTranslationUtils::GetBitmap(resourceName)), + fBackgroundMode(BUTTON_BACKGROUND) +{ +} + + +BitmapButton::BitmapButton(const uint8* bits, uint32 width, uint32 height, + color_space format, BMessage* message) + : + BButton("", message), + fBitmap(new BBitmap(BRect(0, 0, width - 1, height - 1), 0, format)), + fBackgroundMode(BUTTON_BACKGROUND) +{ + memcpy(fBitmap->Bits(), bits, fBitmap->BitsLength()); +} + + +BitmapButton::~BitmapButton() +{ + delete fBitmap; +} + + +BSize +BitmapButton::MinSize() +{ + BSize min(0, 0); + if (fBitmap) { + min.width = fBitmap->Bounds().Width(); + min.height = fBitmap->Bounds().Height(); + } + min.width += kFrameInset * 2; + min.height += kFrameInset * 2; + return min; +} + + +BSize +BitmapButton::MaxSize() +{ + return BSize(B_SIZE_UNLIMITED, B_SIZE_UNLIMITED); +} + + +BSize +BitmapButton::PreferredSize() +{ + return MinSize(); +} + + +void +BitmapButton::Draw(BRect updateRect) +{ + BRect bounds(Bounds()); + rgb_color base = ui_color(B_PANEL_BACKGROUND_COLOR); + uint32 flags = be_control_look->Flags(this); + + if (fBackgroundMode == BUTTON_BACKGROUND || Value() == B_CONTROL_ON) { + be_control_look->DrawButtonBackground(this, bounds, updateRect, base, + flags); + } else { + SetHighColor(tint_color(base, B_DARKEN_2_TINT)); + StrokeLine(bounds.LeftBottom(), bounds.RightBottom()); + bounds.bottom--; + be_control_look->DrawMenuBarBackground(this, bounds, updateRect, base, + flags); + } + + if (fBitmap == NULL) + return; + + SetDrawingMode(B_OP_ALPHA); + + if (!IsEnabled()) { + SetBlendingMode(B_CONSTANT_ALPHA, B_ALPHA_OVERLAY); + SetHighColor(0, 0, 0, 120); + } + + BRect bitmapBounds(fBitmap->Bounds()); + BPoint bitmapLocation( + floorf((bounds.left + bounds.right + - (bitmapBounds.left + bitmapBounds.right)) / 2 + 0.5f), + floorf((bounds.top + bounds.bottom + - (bitmapBounds.top + bitmapBounds.bottom)) / 2 + 0.5f)); + + DrawBitmap(fBitmap, bitmapLocation); +} + + +void +BitmapButton::SetBackgroundMode(uint32 mode) +{ + if (fBackgroundMode != mode) { + fBackgroundMode = mode; + Invalidate(); + } +} + diff --git a/src/apps/webpositive/support/BitmapButton.h b/src/apps/webpositive/support/BitmapButton.h new file mode 100644 index 0000000000..432adcb34e --- /dev/null +++ b/src/apps/webpositive/support/BitmapButton.h @@ -0,0 +1,44 @@ +/* + * Copyright 2010 Stephan Aßmus . All rights reserved. + * Distributed under the terms of the MIT License. + */ +#ifndef BITMAP_BUTTON_H +#define BITMAP_BUTTON_H + +#include + + +class BBitmap; + + +class BitmapButton : public BButton { +public: + enum { + BUTTON_BACKGROUND = 0, + MENUBAR_BACKGROUND, + }; + + BitmapButton(const char* resourceName, + BMessage* message); + + BitmapButton(const uint8* bits, uint32 width, + uint32 height, color_space format, + BMessage* message); + + virtual ~BitmapButton(); + + virtual BSize MinSize(); + virtual BSize MaxSize(); + virtual BSize PreferredSize(); + + virtual void Draw(BRect updateRect); + + void SetBackgroundMode(uint32 mode); + +private: + BBitmap* fBitmap; + uint32 fBackgroundMode; +}; + + +#endif // BITMAP_BUTTON_H diff --git a/src/apps/webpositive/support/DateTime.cpp b/src/apps/webpositive/support/DateTime.cpp new file mode 100644 index 0000000000..19030db00a --- /dev/null +++ b/src/apps/webpositive/support/DateTime.cpp @@ -0,0 +1,1473 @@ +/* + * Copyright 2007-2010, Haiku, Inc. All Rights Reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Julun + * Stephan Aßmus + */ + +#include "DateTime.h" + + +#include +#include + +#include + + +namespace BPrivate { + + +const int32 kSecondsPerMinute = 60; + +const int32 kHoursPerDay = 24; +const int32 kMinutesPerDay = 1440; +const int32 kSecondsPerDay = 86400; +const int32 kMillisecondsPerDay = 86400000; + +const bigtime_t kMicrosecondsPerSecond = 1000000LL; +const bigtime_t kMicrosecondsPerMinute = 60000000LL; +const bigtime_t kMicrosecondsPerHour = 3600000000LL; +const bigtime_t kMicrosecondsPerDay = 86400000000LL; + + +/*! + Constructs a new BTime object. Asked for its time representation, it will + return 0 for Hour(), Minute(), Second() etc. This can represent midnight, + but be aware IsValid() will return false. +*/ +BTime::BTime() + : + fMicroseconds(-1) +{ +} + + +/*! + Constructs a new BTime object as a copy of \c other. +*/ +BTime::BTime(const BTime& other) + : + fMicroseconds(other.fMicroseconds) +{ +} + + +/*! + Constructs a BTime object with \c hour \c minute, \c second, \c microsecond. + + \c hour must be between 0 and 23, \c minute and \c second must be between + 0 and 59 and \c microsecond should be in the range of 0 and 999999. If the + specified time is invalid, the time is not set and IsValid() returns false. +*/ +BTime::BTime(int32 hour, int32 minute, int32 second, int32 microsecond) + : + fMicroseconds(-1) +{ + _SetTime(hour, minute, second, microsecond); +} + + +/*! + Constructs a new BTime object from the provided BMessage archive. +*/ +BTime::BTime(const BMessage* archive) + : + fMicroseconds(-1) +{ + if (archive == NULL) + return; + archive->FindInt64("mircoseconds", &fMicroseconds); +} + + +/*! + Empty destructor. +*/ +BTime::~BTime() +{ +} + + +/*! + Archives the BTime object into the provided BMessage object. + @returns \c B_OK if all went well. + \c B_BAD_VALUE, if the message is \c NULL. + \c other error codes, depending on failure to append + fields to the message. +*/ +status_t +BTime::Archive(BMessage* into) const +{ + if (into == NULL) + return B_BAD_VALUE; + return into->AddInt64("mircoseconds", fMicroseconds); +} + + +/*! + Returns true if the time is valid, otherwise false. A valid time can be + BTime(23, 59, 59, 999999) while BTime(24, 00, 01) would be invalid. +*/ +bool +BTime::IsValid() const +{ + return fMicroseconds > -1 && fMicroseconds < kMicrosecondsPerDay; +} + + +/*! + This is an overloaded member function, provided for convenience. +*/ +bool +BTime::IsValid(const BTime& time) const +{ + return time.fMicroseconds > -1 && time.fMicroseconds < kMicrosecondsPerDay; +} + + +/*! + This is an overloaded member function, provided for convenience. +*/ +bool +BTime::IsValid(int32 hour, int32 minute, int32 second, int32 microsecond) const +{ + return BTime(hour, minute, second, microsecond).IsValid(); +} + + +/*! + Returns the current time as reported by the system depending on the given + time_type \c type. +*/ +BTime +BTime::CurrentTime(time_type type) +{ + struct timeval tv; + if (gettimeofday(&tv, NULL) != 0) { + // gettimeofday failed? + time(&tv.tv_sec); + } + + struct tm result; + struct tm* timeinfo; + if (type == B_GMT_TIME) + timeinfo = gmtime_r(&tv.tv_sec, &result); + else + timeinfo = localtime_r(&tv.tv_sec, &result); + + int32 sec = timeinfo->tm_sec; + return BTime(timeinfo->tm_hour, timeinfo->tm_min, (sec > 59) ? 59 : sec, + tv.tv_usec); +} + + +/*! + Returns a copy of the current BTime object. +*/ +BTime +BTime::Time() const +{ + return *this; +} + + +/*! + This is an overloaded member function, provided for convenience. Set the + current BTime object to the passed BTime \c time object. +*/ +bool +BTime::SetTime(const BTime& time) +{ + fMicroseconds = time.fMicroseconds; + return IsValid(); +} + + +/*! + Set the time to \c hour \c minute, \c second and \c microsecond. + + \c hour must be between 0 and 23, \c minute and \c second must be between + 0 and 59 and \c microsecond should be in the range of 0 and 999999. Returns + true if the time is valid; otherwise false. If the specified time is + invalid, the time is not set and the function returns false. +*/ +bool +BTime::SetTime(int32 hour, int32 minute, int32 second, int32 microsecond) +{ + return _SetTime(hour, minute, second, microsecond); +} + + + +/*! + Adds \c hours to the current time. If the passed value is negativ it will + become earlier. Note: The time will wrap if it passes midnight. +*/ +BTime& +BTime::AddHours(int32 hours) +{ + return _AddMicroseconds(bigtime_t(hours % kHoursPerDay) + * kMicrosecondsPerHour); +} + + +/*! + Adds \c minutes to the current time. If the passed value is negativ it will + become earlier. Note: The time will wrap if it passes midnight. +*/ +BTime& +BTime::AddMinutes(int32 minutes) +{ + return _AddMicroseconds(bigtime_t(minutes % kMinutesPerDay) + * kMicrosecondsPerMinute); +} + + +/*! + Adds \c seconds to the current time. If the passed value is negativ it will + become earlier. Note: The time will wrap if it passes midnight. +*/ +BTime& +BTime::AddSeconds(int32 seconds) +{ + return _AddMicroseconds(bigtime_t(seconds % kSecondsPerDay) + * kMicrosecondsPerSecond); +} + + +/*! + Adds \c milliseconds to the current time. If the passed value is negativ it + will become earlier. Note: The time will wrap if it passes midnight. +*/ +BTime& +BTime::AddMilliseconds(int32 milliseconds) +{ + return _AddMicroseconds(bigtime_t(milliseconds % kMillisecondsPerDay) + * 1000); +} + + +/*! + Adds \c microseconds to the current time. If the passed value is negativ it + will become earlier. Note: The time will wrap if it passes midnight. +*/ +BTime& +BTime::AddMicroseconds(int32 microseconds) +{ + return _AddMicroseconds(microseconds); +} + + +/*! + Returns the hour fragment of the time. +*/ +int32 +BTime::Hour() const +{ + return int32(_Microseconds() / kMicrosecondsPerHour); +} + + +/*! + Returns the minute fragment of the time. +*/ +int32 +BTime::Minute() const +{ + return int32(((_Microseconds() % kMicrosecondsPerHour)) / kMicrosecondsPerMinute); +} + + +/*! + Returns the second fragment of the time. +*/ +int32 +BTime::Second() const +{ + return int32(_Microseconds() / kMicrosecondsPerSecond) % kSecondsPerMinute; +} + + +/*! + Returns the millisecond fragment of the time. +*/ +int32 +BTime::Millisecond() const +{ + + return Microsecond() / 1000; +} + + +/*! + Returns the microsecond fragment of the time. +*/ +int32 +BTime::Microsecond() const +{ + return int32(_Microseconds() % 1000000); +} + + +bigtime_t +BTime::_Microseconds() const +{ + return fMicroseconds == -1 ? 0 : fMicroseconds; +} + + +/*! + Returns the difference between this time and the given BTime \c time based + on the passed diff_type \c type. If \c time is earlier the return value will + be negativ. + + The return value then can be hours, minutes, seconds, milliseconds or + microseconds while its range will always be between -86400000000 and + 86400000000 depending on diff_type \c type. +*/ +bigtime_t +BTime::Difference(const BTime& time, diff_type type) const +{ + bigtime_t diff = time._Microseconds() - _Microseconds(); + switch (type) { + case B_HOURS_DIFF: { + diff /= kMicrosecondsPerHour; + } break; + case B_MINUTES_DIFF: { + diff /= kMicrosecondsPerMinute; + } break; + case B_SECONDS_DIFF: { + diff /= kMicrosecondsPerSecond; + } break; + case B_MILLISECONDS_DIFF: { + diff /= 1000; + } break; + case B_MICROSECONDS_DIFF: + default: break; + } + return diff; +} + + +/*! + Returns true if this time is different from \c time, otherwise false. +*/ +bool +BTime::operator!=(const BTime& time) const +{ + return fMicroseconds != time.fMicroseconds; +} + + +/*! + Returns true if this time is equal to \c time, otherwise false. +*/ +bool +BTime::operator==(const BTime& time) const +{ + return fMicroseconds == time.fMicroseconds; +} + + +/*! + Returns true if this time is earlier than \c time, otherwise false. +*/ +bool +BTime::operator<(const BTime& time) const +{ + return fMicroseconds < time.fMicroseconds; +} + + +/*! + Returns true if this time is earlier than or equal to \c time, otherwise false. +*/ +bool +BTime::operator<=(const BTime& time) const +{ + return fMicroseconds <= time.fMicroseconds; +} + + +/*! + Returns true if this time is later than \c time, otherwise false. +*/ +bool +BTime::operator>(const BTime& time) const +{ + return fMicroseconds > time.fMicroseconds; +} + + +/*! + Returns true if this time is later than or equal to \c time, otherwise false. +*/ +bool +BTime::operator>=(const BTime& time) const +{ + return fMicroseconds >= time.fMicroseconds; +} + + +BTime& +BTime::_AddMicroseconds(bigtime_t microseconds) +{ + bigtime_t count = 0; + if (microseconds < 0) { + count = ((kMicrosecondsPerDay - microseconds) / kMicrosecondsPerDay) * + kMicrosecondsPerDay; + } + fMicroseconds = (_Microseconds() + microseconds + count) % kMicrosecondsPerDay; + return *this; +} + + +bool +BTime::_SetTime(bigtime_t hour, bigtime_t minute, bigtime_t second, + bigtime_t microsecond) +{ + fMicroseconds = hour * kMicrosecondsPerHour + + minute * kMicrosecondsPerMinute + + second * kMicrosecondsPerSecond + + microsecond; + + bool isValid = IsValid(); + if (!isValid) + fMicroseconds = -1; + + return isValid; +} + + +// #pragma mark - BDate + + +/*! + Constructs a new BDate object. IsValid() will return false. +*/ +BDate::BDate() + : + fDay(-1), + fYear(0), + fMonth(-1) +{ +} + + +/*! + Constructs a new BDate object as a copy of \c other. +*/ +BDate::BDate(const BDate& other) + : + fDay(other.fDay), + fYear(other.fYear), + fMonth(other.fMonth) +{ +} + + +/*! + Constructs a BDate object with \c year \c month and \c day. + + Please note that a date before 1.1.4713 BC, a date with year 0 and a date + between 4.10.1582 and 15.10.1582 are considered invalid. If the specified + date is invalid, the date is not set and IsValid() returns false. Also note + that every passed year will be interpreted as is. + +*/ +BDate::BDate(int32 year, int32 month, int32 day) +{ + _SetDate(year, month, day); +} + + +/*! + Constructs a new BDate object from the provided archive. +*/ +BDate::BDate(const BMessage* archive) + : + fDay(-1), + fYear(0), + fMonth(-1) +{ + if (archive == NULL) + return; + archive->FindInt32("day", &fDay); + archive->FindInt32("year", &fYear); + archive->FindInt32("month", &fMonth); +} + + +/*! + Empty destructor. +*/ +BDate::~BDate() +{ +} + + +/*! + Archives the BDate object into the provided BMessage object. + @returns \c B_OK if all went well. + \c B_BAD_VALUE, if the message is \c NULL. + \c other error codes, depending on failure to append + fields to the message. +*/ +status_t +BDate::Archive(BMessage* into) const +{ + if (into == NULL) + return B_BAD_VALUE; + status_t ret = into->AddInt32("day", fDay); + if (ret == B_OK) + ret = into->AddInt32("year", fYear); + if (ret == B_OK) + ret = into->AddInt32("month", fMonth); + return ret; +} + + +/*! + Returns true if the date is valid, otherwise false. + + Please note that a date before 1.1.4713 BC, a date with year 0 and a date + between 4.10.1582 and 15.10.1582 are considered invalid. +*/ +bool +BDate::IsValid() const +{ + return IsValid(fYear, fMonth, fDay); +} + + +/*! + This is an overloaded member function, provided for convenience. +*/ +bool +BDate::IsValid(const BDate& date) const +{ + return IsValid(date.fYear, date.fMonth, date.fDay); +} + + +/*! + This is an overloaded member function, provided for convenience. +*/ +bool +BDate::IsValid(int32 year, int32 month, int32 day) const +{ + // no year 0 in Julian and nothing before 1.1.4713 BC + if (year == 0 || year < -4713) + return false; + + if (month < 1 || month > 12) + return false; + + if (day < 1 || day > _DaysInMonth(year, month)) + return false; + + // 'missing' days between switch julian - gregorian + if (year == 1582 && month == 10 && day > 4 && day < 15) + return false; + + return true; +} + + +/*! + Returns the current date as reported by the system depending on the given + time_type \c type. +*/ +BDate +BDate::CurrentDate(time_type type) +{ + time_t timer; + struct tm result; + struct tm* timeinfo; + + time(&timer); + + if (type == B_GMT_TIME) + timeinfo = gmtime_r(&timer, &result); + else + timeinfo = localtime_r(&timer, &result); + + return BDate(timeinfo->tm_year + 1900, timeinfo->tm_mon +1, timeinfo->tm_mday); +} + + +/*! + Returns a copy of the current BTime object. +*/ +BDate +BDate::Date() const +{ + return *this; +} + + +/*! + This is an overloaded member function, provided for convenience. +*/ +bool +BDate::SetDate(const BDate& date) +{ + return _SetDate(date.fYear, date.fMonth, date.fDay); +} + + +/*! + Set the date to \c year \c month and \c day. + + Returns true if the date is valid; otherwise false. If the specified date is + invalid, the date is not set and the function returns false. +*/ +bool +BDate::SetDate(int32 year, int32 month, int32 day) +{ + return _SetDate(year, month, day); +} + + +/*! + This function sets the given \c year, \c month and \c day to the + representative values of this date. The pointers can be NULL. If the date is + invalid, the values will be set to -1 for \c month and \c day, the \c year + will be set to 0. +*/ +void +BDate::GetDate(int32* year, int32* month, int32* day) +{ + if (year) + *year = fYear; + + if (month) + *month = fMonth; + + if (day) + *day = fDay; +} + + +/*! + Adds \c days to the current date. If the passed value is negativ it will + become earlier. If the current date is invalid, the \c days are not added. +*/ +void +BDate::AddDays(int32 days) +{ + if (IsValid()) + *this = JulianDayToDate(DateToJulianDay() + days); +} + + +/*! + Adds \c years to the current date. If the passed value is negativ it will + become earlier. If the current date is invalid, the \c years are not added. + The day/ month combination will be adjusted if it does not exist in the + resulting year, so this function will then return the latest valid date. +*/ +void +BDate::AddYears(int32 years) +{ + if (IsValid()) { + const int32 tmp = fYear; + fYear += years; + + if ((tmp > 0 && fYear <= 0) || (tmp < 0 && fYear >= 0)) + fYear += (years > 0) ? +1 : -1; + + fDay = min_c(fDay, _DaysInMonth(fYear, fMonth)); + } +} + + +/*! + Adds \c months to the current date. If the passed value is negativ it will + become earlier. If the current date is invalid, the \c months are not added. + The day/ month combination will be adjusted if it does not exist in the + resulting year, so this function will then return the latest valid date. +*/ +void +BDate::AddMonths(int32 months) +{ + if (IsValid()) { + const int32 tmp = fYear; + fYear += months / 12; + fMonth += months % 12; + + if (fMonth > 12) { + fYear++; + fMonth -= 12; + } else if (fMonth < 1) { + fYear--; + fMonth += 12; + } + + if ((tmp > 0 && fYear <= 0) || (tmp < 0 && fYear >= 0)) + fYear += (months > 0) ? +1 : -1; + + // 'missing' days between switch julian - gregorian + if (fYear == 1582 && fMonth == 10 && fDay > 4 && fDay < 15) + fDay = (months > 0) ? 15 : 4; + + fDay = min_c(fDay, DaysInMonth()); + } +} + + +/*! + Returns the day fragment of the date. The return value will be in the range + of 1 to 31, in case the date is invalid it will be -1. +*/ +int32 +BDate::Day() const +{ + return fDay; +} + + +/*! + Returns the year fragment of the date. If the date is invalid, the function + returns 0. +*/ +int32 +BDate::Year() const +{ + return fYear; +} + + +/*! + Returns the month fragment of the date. The return value will be in the + range of 1 to 12, in case the date is invalid it will be -1. +*/ +int32 +BDate::Month() const +{ + return fMonth; +} + + +/*! + Returns the difference in days between this date and the given BDate \c date. + If \c date is earlier the return value will be negativ. If the calculation + is done with an invalid date, the result is undefined. +*/ +int32 +BDate::Difference(const BDate& date) const +{ + return date.DateToJulianDay() - DateToJulianDay(); +} + + +/*! + Returns the week number of the date, if the date is invalid it will return + B_ERROR. Please note that this function does only work within the Gregorian + calendar, thus a date before 15.10.1582 will return B_ERROR. +*/ +int32 +BDate::WeekNumber() const +{ + /* + This algorithm is taken from: + Frequently Asked Questions about Calendars + Version 2.8 Claus Tøndering 15 December 2005 + + Note: it will work only within the Gregorian Calendar + */ + + if (!IsValid() || fYear < 1582 + || (fYear == 1582 && fMonth < 10) + || (fYear == 1582 && fMonth == 10 && fDay < 15)) + return int32(B_ERROR); + + int32 a; + int32 b; + int32 s; + int32 e; + int32 f; + + if (fMonth > 0 && fMonth < 3) { + a = fYear - 1; + b = (a / 4) - (a / 100) + (a / 400); + int32 c = ((a - 1) / 4) - ((a - 1) / 100) + ((a -1) / 400); + s = b - c; + e = 0; + f = fDay - 1 + 31 * (fMonth - 1); + } else if (fMonth >= 3 && fMonth <= 12) { + a = fYear; + b = (a / 4) - (a / 100) + (a / 400); + int32 c = ((a - 1) / 4) - ((a - 1) / 100) + ((a -1) / 400); + s = b - c; + e = s + 1; + f = fDay + ((153 * (fMonth - 3) + 2) / 5) + 58 + s; + } else + return int32(B_ERROR); + + int32 g = (a + b) % 7; + int32 d = (f + g - e) % 7; + int32 n = f + 3 - d; + + int32 weekNumber; + if (n < 0) + weekNumber = 53 - (g -s) / 5; + else if (n > 364 + s) + weekNumber = 1; + else + weekNumber = n / 7 + 1; + + return weekNumber; +} + + +/*! + Returns the day of the week in the range of 1 to 7, while 1 stands for + monday. If the date is invalid, the function will return B_ERROR. +*/ +int32 +BDate::DayOfWeek() const +{ + // http://en.wikipedia.org/wiki/Julian_day#Calculation + return IsValid() ? (DateToJulianDay() % 7) + 1 : int32(B_ERROR); +} + + +/*! + Returns the day of the year in the range of 1 to 365 (366 in leap years). If + the date is invalid, the function will return B_ERROR. +*/ +int32 +BDate::DayOfYear() const +{ + if (!IsValid()) + return int32(B_ERROR); + + return DateToJulianDay() - _DateToJulianDay(fYear, 1, 1) + 1; +} + + +/*! + Returns true if the passed \c year is a leap year, otherwise false. If the + \c year passed is before 4713 BC, the result is undefined. +*/ +bool +BDate::IsLeapYear(int32 year) const +{ + if (year < 1582) { + if (year < 0) + year++; + return (year % 4) == 0; + } + return (year % 400 == 0) || (year % 4 == 0 && year % 100 != 0); +} + + +/*! + Returns the number of days in the year of the current date. If the date is + valid it will return 365 or 366, otherwise B_ERROR; +*/ +int32 +BDate::DaysInYear() const +{ + if (!IsValid()) + return int32(B_ERROR); + + return IsLeapYear(fYear) ? 366 : 365; +} + + +/*! + Returns the number of days in the month of the current date. If the date is + valid it will return 28 up to 31, otherwise B_ERROR; +*/ +int32 +BDate::DaysInMonth() const +{ + if (!IsValid()) + return int32(B_ERROR); + + return _DaysInMonth(fYear, fMonth); +} + + +/*! + Returns the short day name of this object. +*/ +BString +BDate::ShortDayName() const +{ + return ShortDayName(DayOfWeek()); +} + + +/*! + Returns the short day name in case of an valid day, otherwise an empty + string. The passed \c day must be in the range of 1 to 7 while 1 stands for + monday. +*/ +/*static*/ BString +BDate::ShortDayName(int32 day) +{ + if (day < 1 || day > 7) + return BString(); + + tm tm_struct; + memset(&tm_struct, 0, sizeof(tm)); + tm_struct.tm_wday = day == 7 ? 0 : day; + + char buffer[256]; + strftime(buffer, sizeof(buffer), "%a", &tm_struct); + + return BString(buffer); +} + + +/*! + Returns the short month name of this object. +*/ +BString +BDate::ShortMonthName() const +{ + return ShortMonthName(Month()); +} + + +/*! + Returns the short month name in case of an valid month, otherwise an empty + string. The passed \c month must be in the range of 1 to 12. +*/ +/*static*/ BString +BDate::ShortMonthName(int32 month) +{ + if (month < 1 || month > 12) + return BString(); + + tm tm_struct; + memset(&tm_struct, 0, sizeof(tm)); + tm_struct.tm_mon = month - 1; + + char buffer[256]; + strftime(buffer, sizeof(buffer), "%b", &tm_struct); + + return BString(buffer); +} + + +/*! + Returns the long day name of this object's week day. +*/ +BString +BDate::LongDayName() const +{ + return LongDayName(DayOfWeek()); +} + + +/*! + Returns the long day name in case of an valid day, otherwise an empty + string. The passed \c day must be in the range of 1 to 7 while 1 stands for + monday. +*/ +/*static*/ BString +BDate::LongDayName(int32 day) +{ + if (day < 1 || day > 7) + return BString(); + + tm tm_struct; + memset(&tm_struct, 0, sizeof(tm)); + tm_struct.tm_wday = day == 7 ? 0 : day; + + char buffer[256]; + strftime(buffer, sizeof(buffer), "%A", &tm_struct); + + return BString(buffer); +} + + +/*! + Returns the long month name of this object's month. +*/ +BString +BDate::LongMonthName() const +{ + return LongMonthName(Month()); +} + + +/*! + Returns the long month name in case of an valid month, otherwise an empty + string. The passed \c month must be in the range of 1 to 12. +*/ +/*static*/ BString +BDate::LongMonthName(int32 month) +{ + if (month < 1 || month > 12) + return BString(); + + tm tm_struct; + memset(&tm_struct, 0, sizeof(tm)); + tm_struct.tm_mon = month - 1; + + char buffer[256]; + strftime(buffer, sizeof(buffer), "%B", &tm_struct); + + return BString(buffer); +} + + +/*! + Converts the date to Julian day. If your date is invalid, the function will + return B_ERROR. +*/ +int32 +BDate::DateToJulianDay() const +{ + return _DateToJulianDay(fYear, fMonth, fDay); +} + + +/* + Converts the passed \c julianDay to an BDate. If the \c julianDay is negativ, + the function will return an invalid date. Because of the switch from Julian + calendar to Gregorian calendar the 4.10.1582 is followed by the 15.10.1582. +*/ +BDate +BDate::JulianDayToDate(int32 julianDay) +{ + BDate date; + const int32 kGregorianCalendarStart = 2299161; + if (julianDay >= kGregorianCalendarStart) { + // http://en.wikipedia.org/wiki/Julian_day#Gregorian_calendar_from_Julian_day_number + int32 j = julianDay + 32044; + int32 dg = j % 146097; + int32 c = (dg / 36524 + 1) * 3 / 4; + int32 dc = dg - c * 36524; + int32 db = dc % 1461; + int32 a = (db / 365 + 1) * 3 / 4; + int32 da = db - a * 365; + int32 m = (da * 5 + 308) / 153 - 2; + date.fYear = ((j / 146097) * 400 + c * 100 + (dc / 1461) * 4 + a) - 4800 + + (m + 2) / 12; + date.fMonth = (m + 2) % 12 + 1; + date.fDay = int32((da - (m + 4) * 153 / 5 + 122) + 1.5); + } else if (julianDay >= 0) { + // http://en.wikipedia.org/wiki/Julian_day#Calculation + julianDay += 32082; + int32 d = (4 * julianDay + 3) / 1461; + int32 e = julianDay - (1461 * d) / 4; + int32 m = ((5 * e) + 2) / 153; + date.fDay = e - (153 * m + 2) / 5 + 1; + date.fMonth = m + 3 - 12 * (m / 10); + int32 year = d - 4800 + (m / 10); + if (year <= 0) + year--; + date.fYear = year; + } + return date; +} + + +/*! + Returns true if this date is different from \c date, otherwise false. +*/ +bool +BDate::operator!=(const BDate& date) const +{ + return DateToJulianDay() != date.DateToJulianDay(); +} + + +/*! + Returns true if this date is equal to \c date, otherwise false. +*/ +bool +BDate::operator==(const BDate& date) const +{ + return DateToJulianDay() == date.DateToJulianDay(); +} + + +/*! + Returns true if this date is earlier than \c date, otherwise false. +*/ +bool +BDate::operator<(const BDate& date) const +{ + return DateToJulianDay() < date.DateToJulianDay(); +} + + +/*! + Returns true if this date is earlier than or equal to \c date, otherwise false. +*/ +bool +BDate::operator<=(const BDate& date) const +{ + return DateToJulianDay() <= date.DateToJulianDay(); +} + + +/*! + Returns true if this date is later than \c date, otherwise false. +*/ +bool +BDate::operator>(const BDate& date) const +{ + return DateToJulianDay() > date.DateToJulianDay(); +} + + +/*! + Returns true if this date is later than or equal to \c date, otherwise false. +*/ +bool +BDate::operator>=(const BDate& date) const +{ + return DateToJulianDay() >= date.DateToJulianDay(); +} + + +bool +BDate::_SetDate(int32 year, int32 month, int32 day) +{ + fDay = -1; + fYear = 0; + fMonth = -1; + + bool valid = IsValid(year, month, day); + if (valid) { + fDay = day; + fYear = year; + fMonth = month; + } + + return valid; +} + + +int32 +BDate::_DaysInMonth(int32 year, int32 month) const +{ + if (month == 2 && IsLeapYear(year)) + return 29; + + const int32 daysInMonth[12] = + {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; + + return daysInMonth[month -1]; +} + + +int32 +BDate::_DateToJulianDay(int32 _year, int32 month, int32 day) const +{ + if (IsValid(_year, month, day)) { + int32 year = _year; + if (year < 0) year++; + + int32 a = (14 - month) / 12; + int32 y = year + 4800 - a; + int32 m = month + (12 * a) - 3; + + // http://en.wikipedia.org/wiki/Julian_day#Calculation + if (year > 1582 + || (year == 1582 && month > 10) + || (year == 1582 && month == 10 && day >= 15)) { + return day + (((153 * m) + 2) / 5) + (365 * y) + (y / 4) - + (y / 100) + (y / 400) - 32045; + } else if (year < 1582 + || (year == 1582 && month < 10) + || (year == 1582 && month == 10 && day <= 4)) { + return day + (((153 * m) + 2) / 5) + (365 * y) + (y / 4) - 32083; + } + } + + // http://en.wikipedia.org/wiki/Gregorian_calendar: + // The last day of the Julian calendar was Thursday October 4, 1582 + // and this was followed by the first day of the Gregorian calendar, + // Friday October 15, 1582 (the cycle of weekdays was not affected). + return int32(B_ERROR); +} + + +// #pragma mark - BDateTime + + +/*! + Constructs a new BDateTime object. IsValid() will return false. +*/ +BDateTime::BDateTime() + : fDate(), + fTime() +{ +} + + +/*! + Constructs a BDateTime object with \c date and \c time. The return value + of IsValid() depends on the validity of the passed objects. +*/ +BDateTime::BDateTime(const BDate& date, const BTime& time) + : fDate(date), + fTime(time) +{ +} + + +/*! + Constructs a new BDateTime object. IsValid() will return false. +*/ +BDateTime::BDateTime(const BMessage* archive) + : fDate(archive), + fTime(archive) +{ +} + + +/*! + Empty destructor. +*/ +BDateTime::~BDateTime() +{ +} + + +/*! + Archives the BDateTime object into the provided BMessage object. + @returns \c B_OK if all went well. + \c B_BAD_VALUE, if the message is \c NULL. + \c other error codes, depending on failure to append + fields to the message. +*/ +status_t +BDateTime::Archive(BMessage* into) const +{ + status_t ret = fDate.Archive(into); + if (ret == B_OK) + ret = fTime.Archive(into); + return ret; +} + + +/*! + Returns true if the date time is valid, otherwise false. +*/ +bool +BDateTime::IsValid() const +{ + return fDate.IsValid() && fTime.IsValid(); +} + + +/*! + Returns the current date and time as reported by the system depending on the + given time_type \c type. +*/ +BDateTime +BDateTime::CurrentDateTime(time_type type) +{ + return BDateTime(BDate::CurrentDate(type), BTime::CurrentTime(type)); +} + + +/*! + Sets the current date and time of this object to \c date and \c time. +*/ +void +BDateTime::SetDateTime(const BDate& date, const BTime& time) +{ + fDate = date; + fTime = time; +} + + +/*! + Returns the current date of this object. +*/ +BDate& +BDateTime::Date() +{ + return fDate; +} + + +/*! + Returns the current date of this object. +*/ +const BDate& +BDateTime::Date() const +{ + return fDate; +} + + +/*! + Set the current date of this object to \c date. +*/ +void +BDateTime::SetDate(const BDate& date) +{ + fDate = date; +} + + +/*! + Returns the current time of this object. +*/ +BTime& +BDateTime::Time() +{ + return fTime; +} + + +/*! + Returns the current time of this object. +*/ +const BTime& +BDateTime::Time() const +{ + return fTime; +} + + +/*! + Sets the current time of this object to \c time. +*/ +void +BDateTime::SetTime(const BTime& time) +{ + fTime = time; +} + + +/*! + Returns the current date and time converted to seconds since + 1.1.1970 - 00:00:00. If the current date is before 1.1.1970 the function + returns -1; +*/ +int32 +BDateTime::Time_t() const +{ + BDate date(1970, 1, 1); + if (date.Difference(fDate) < 0) + return -1; + + tm tm_struct; + + tm_struct.tm_hour = fTime.Hour(); + tm_struct.tm_min = fTime.Minute(); + tm_struct.tm_sec = fTime.Second(); + + tm_struct.tm_year = fDate.Year() - 1900; + tm_struct.tm_mon = fDate.Month() - 1; + tm_struct.tm_mday = fDate.Day(); + + // set less 0 as we wan't use it + tm_struct.tm_isdst = -1; + + // return secs_since_jan1_1970 or -1 on error + return int32(mktime(&tm_struct)); +} + + +/*! + Sets the current date and time converted from seconds since + 1.1.1970 - 00:00:00. +*/ +void +BDateTime::SetTime_t(uint32 seconds) +{ + BTime time; + time.AddSeconds(seconds % kSecondsPerDay); + fTime.SetTime(time); + + BDate date(1970, 1, 1); + date.AddDays(seconds / kSecondsPerDay); + fDate.SetDate(date); +} + + +/*! + Returns true if this datetime is different from \c dateTime, otherwise false. +*/ +bool +BDateTime::operator!=(const BDateTime& dateTime) const +{ + return fTime != dateTime.fTime && fDate != dateTime.fDate; +} + + +/*! + Returns true if this datetime is equal to \c dateTime, otherwise false. +*/ +bool +BDateTime::operator==(const BDateTime& dateTime) const +{ + return fTime == dateTime.fTime && fDate == dateTime.fDate; +} + + +/*! + Returns true if this datetime is earlier than \c dateTime, otherwise false. +*/ +bool +BDateTime::operator<(const BDateTime& dateTime) const +{ + if (fDate < dateTime.fDate) + return true; + if (fDate == dateTime.fDate) + return fTime < dateTime.fTime; + return false; +} + + +/*! + Returns true if this datetime is earlier than or equal to \c dateTime, + otherwise false. +*/ +bool +BDateTime::operator<=(const BDateTime& dateTime) const +{ + if (fDate < dateTime.fDate) + return true; + if (fDate == dateTime.fDate) + return fTime <= dateTime.fTime; + return false; +} + + +/*! + Returns true if this datetime is later than \c dateTime, otherwise false. +*/ +bool +BDateTime::operator>(const BDateTime& dateTime) const +{ + if (fDate > dateTime.fDate) + return true; + if (fDate == dateTime.fDate) + return fTime > dateTime.fTime; + return false; +} + + +/*! + Returns true if this datetime is later than or equal to \c dateTime, + otherwise false. +*/ +bool +BDateTime::operator>=(const BDateTime& dateTime) const +{ + if (fDate > dateTime.fDate) + return true; + if (fDate == dateTime.fDate) + return fTime >= dateTime.fTime; + return false; +} + + +} //namespace BPrivate diff --git a/src/apps/webpositive/support/DateTime.h b/src/apps/webpositive/support/DateTime.h new file mode 100644 index 0000000000..93f76399d6 --- /dev/null +++ b/src/apps/webpositive/support/DateTime.h @@ -0,0 +1,225 @@ +/* + * Copyright 2007-2010, Haiku, Inc. All Rights Reserved. + * Distributed under the terms of the MIT License. + */ +#ifndef _DATE_TIME_H_ +#define _DATE_TIME_H_ + + +#include + + +class BMessage; + + +namespace BPrivate { + + +enum time_type { + B_GMT_TIME, + B_LOCAL_TIME +}; + + +enum diff_type { + B_HOURS_DIFF, + B_MINUTES_DIFF, + B_SECONDS_DIFF, + B_MILLISECONDS_DIFF, + B_MICROSECONDS_DIFF +}; + + +class BTime { +public: + BTime(); + BTime(const BTime& other); + BTime(int32 hour, int32 minute, int32 second, + int32 microsecond = 0); + BTime(const BMessage* archive); + ~BTime(); + + status_t Archive(BMessage* into) const; + + bool IsValid() const; + bool IsValid(const BTime& time) const; + bool IsValid(int32 hour, int32 minute, int32 second, + int32 microsecond = 0) const; + + static BTime CurrentTime(time_type type); + + BTime Time() const; + bool SetTime(const BTime& time); + bool SetTime(int32 hour, int32 minute, int32 second, + int32 microsecond = 0); + + BTime& AddHours(int32 hours); + BTime& AddMinutes(int32 minutes); + BTime& AddSeconds(int32 seconds); + BTime& AddMilliseconds(int32 milliseconds); + BTime& AddMicroseconds(int32 microseconds); + + int32 Hour() const; + int32 Minute() const; + int32 Second() const; + int32 Millisecond() const; + int32 Microsecond() const; + bigtime_t Difference(const BTime& time, + diff_type type) const; + + bool operator!=(const BTime& time) const; + bool operator==(const BTime& time) const; + + bool operator<(const BTime& time) const; + bool operator<=(const BTime& time) const; + + bool operator>(const BTime& time) const; + bool operator>=(const BTime& time) const; + +private: + bigtime_t _Microseconds() const; + BTime& _AddMicroseconds(bigtime_t microseconds); + bool _SetTime(bigtime_t hour, bigtime_t minute, + bigtime_t second, bigtime_t microsecond); + +private: + bigtime_t fMicroseconds; +}; + + +class BDate { +public: + BDate(); + BDate(const BDate& other); + BDate(int32 year, int32 month, int32 day); + BDate(const BMessage* archive); + ~BDate(); + + status_t Archive(BMessage* into) const; + + bool IsValid() const; + bool IsValid(const BDate& date) const; + bool IsValid(int32 year, int32 month, + int32 day) const; + + static BDate CurrentDate(time_type type); + + BDate Date() const; + bool SetDate(const BDate& date); + + bool SetDate(int32 year, int32 month, int32 day); + void GetDate(int32* year, int32* month, int32* day); + + void AddDays(int32 days); + void AddYears(int32 years); + void AddMonths(int32 months); + + int32 Day() const; + int32 Year() const; + int32 Month() const; + int32 Difference(const BDate& date) const; + + int32 DayOfWeek() const; + int32 DayOfYear() const; + + int32 WeekNumber() const; + bool IsLeapYear(int32 year) const; + + int32 DaysInYear() const; + int32 DaysInMonth() const; + + BString ShortDayName() const; + static BString ShortDayName(int32 day); + + BString ShortMonthName() const; + static BString ShortMonthName(int32 month); + + BString LongDayName() const; + static BString LongDayName(int32 day); + + BString LongMonthName() const; + static BString LongMonthName(int32 month); + + int32 DateToJulianDay() const; + static BDate JulianDayToDate(int32 julianDay); + + bool operator!=(const BDate& date) const; + bool operator==(const BDate& date) const; + + bool operator<(const BDate& date) const; + bool operator<=(const BDate& date) const; + + bool operator>(const BDate& date) const; + bool operator>=(const BDate& date) const; + +private: + int32 _DaysInMonth(int32 year, int32 month) const; + bool _SetDate(int32 year, int32 month, int32 day); + int32 _DateToJulianDay(int32 year, int32 month, + int32 day) const; + +private: + int32 fDay; + int32 fYear; + int32 fMonth; +}; + + +class BDateTime { +public: + BDateTime(); + BDateTime(const BDate &date, const BTime &time); + BDateTime(const BMessage* archive); + ~BDateTime(); + + status_t Archive(BMessage* into) const; + + bool IsValid() const; + + static BDateTime CurrentDateTime(time_type type); + void SetDateTime(const BDate &date, const BTime &time); + + BDate& Date(); + const BDate& Date() const; + void SetDate(const BDate &date); + + BTime& Time(); + const BTime& Time() const; + void SetTime(const BTime &time); + + int32 Time_t() const; + void SetTime_t(uint32 seconds); + + bool operator!=(const BDateTime& dateTime) const; + bool operator==(const BDateTime& dateTime) const; + + bool operator<(const BDateTime& dateTime) const; + bool operator<=(const BDateTime& dateTime) const; + + bool operator>(const BDateTime& dateTime) const; + bool operator>=(const BDateTime& dateTime) const; + +private: + BDate fDate; + BTime fTime; +}; + + +} // namespace BPrivate + + +using BPrivate::time_type; +using BPrivate::B_GMT_TIME; +using BPrivate::B_LOCAL_TIME; +using BPrivate::diff_type; +using BPrivate::B_HOURS_DIFF; +using BPrivate::B_MINUTES_DIFF; +using BPrivate::B_SECONDS_DIFF; +using BPrivate::B_MILLISECONDS_DIFF; +using BPrivate::B_MICROSECONDS_DIFF; +using BPrivate::BTime; +using BPrivate::BDate; +using BPrivate::BDateTime; + + +#endif // _DATE_TIME_H_ diff --git a/src/apps/webpositive/support/FontSelectionView.cpp b/src/apps/webpositive/support/FontSelectionView.cpp new file mode 100644 index 0000000000..be1adcbd5f --- /dev/null +++ b/src/apps/webpositive/support/FontSelectionView.cpp @@ -0,0 +1,527 @@ +/* + * Copyright 2001-2010, Haiku. + * Distributed under the terms of the MIT License. + * + * Authors: + * Mark Hogben + * DarkWyrm + * Axel Dörfler, axeld@pinc-software.de + * Philippe Saint-Pierre, stpere@gmail.com + * Stephan Aßmus + */ + +#include "FontSelectionView.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#undef B_TRANSLATION_CONTEXT +#define B_TRANSLATION_CONTEXT "Font Selection view" + + +static const float kMinSize = 8.0; +static const float kMaxSize = 18.0; + +static const int32 kMsgSetFamily = 'fmly'; +static const int32 kMsgSetStyle = 'styl'; +static const int32 kMsgSetSize = 'size'; + + +// #pragma mark - + + +FontSelectionView::FontSelectionView(const char* name, const char* label, + bool separateStyles, const BFont* currentFont) + : + BHandler(name), + fMessage(NULL), + fTarget(NULL) +{ + if (currentFont == NULL) + fCurrentFont = _DefaultFont(); + else + fCurrentFont = *currentFont; + + fSavedFont = fCurrentFont; + + fSizesMenu = new BPopUpMenu("size menu"); + fFontsMenu = new BPopUpMenu("font menu"); + + // font menu + fFontsMenuField = new BMenuField("fonts", label, fFontsMenu, B_WILL_DRAW); + fFontsMenuField->SetFont(be_bold_font); + + // styles menu, if desired + if (separateStyles) { + fStylesMenu = new BPopUpMenu("styles menu"); + fStylesMenuField = new BMenuField("styles", B_TRANSLATE("Style:"), + fStylesMenu, B_WILL_DRAW); + } else { + fStylesMenu = NULL; + fStylesMenuField = NULL; + } + + // size menu + fSizesMenuField = new BMenuField("size", B_TRANSLATE("Size:"), fSizesMenu, + B_WILL_DRAW); + fSizesMenuField->SetAlignment(B_ALIGN_RIGHT); + + // preview + fPreviewText = new BStringView("preview text", + B_TRANSLATE_COMMENT("The quick brown fox jumps over the lazy dog.", + "Don't translate this literally ! Use a phrase showing all " + "chars from A to Z.")); + + fPreviewText->SetExplicitMaxSize(BSize(B_SIZE_UNLIMITED, + B_SIZE_UNLIMITED)); + fPreviewText->SetHighColor(tint_color(ui_color(B_PANEL_BACKGROUND_COLOR), + 1.65)); + fPreviewText->SetAlignment(B_ALIGN_RIGHT); + _UpdateFontPreview(); +} + + +FontSelectionView::~FontSelectionView() +{ + // Some controls may not have been attached... + if (!fPreviewText->Window()) + delete fPreviewText; + if (!fSizesMenuField->Window()) + delete fSizesMenuField; + if (fStylesMenuField && !fStylesMenuField->Window()) + delete fStylesMenuField; + if (!fFontsMenuField->Window()) + delete fFontsMenuField; + + delete fMessage; +} + + +void +FontSelectionView::AttachedToLooper() +{ + _BuildSizesMenu(); + UpdateFontsMenu(); +} + + +void +FontSelectionView::MessageReceived(BMessage* message) +{ + switch (message->what) { + case kMsgSetSize: + { + int32 size; + if (message->FindInt32("size", &size) != B_OK + || size == fCurrentFont.Size()) + break; + + fCurrentFont.SetSize(size); + _UpdateFontPreview(); + _Invoke(); + break; + } + + case kMsgSetFamily: + { + const char* family; + if (message->FindString("family", &family) != B_OK) + break; + + font_style style; + fCurrentFont.GetFamilyAndStyle(NULL, &style); + + BMenuItem* familyItem = fFontsMenu->FindItem(family); + if (familyItem != NULL) { + _SelectCurrentFont(false); + + BMenuItem* styleItem; + if (fStylesMenuField != NULL) + styleItem = fStylesMenuField->Menu()->FindMarked(); + else { + styleItem = familyItem->Submenu()->FindItem(style); + if (styleItem == NULL) + styleItem = familyItem->Submenu()->ItemAt(0); + } + + if (styleItem != NULL) { + styleItem->SetMarked(true); + fCurrentFont.SetFamilyAndStyle(family, styleItem->Label()); + _UpdateFontPreview(); + } + if (fStylesMenuField != NULL) + _AddStylesToMenu(fCurrentFont, fStylesMenuField->Menu()); + } + + _Invoke(); + break; + } + + case kMsgSetStyle: + { + const char* family; + const char* style; + if (message->FindString("family", &family) != B_OK + || message->FindString("style", &style) != B_OK) + break; + + BMenuItem *familyItem = fFontsMenu->FindItem(family); + if (!familyItem) + break; + + _SelectCurrentFont(false); + familyItem->SetMarked(true); + + fCurrentFont.SetFamilyAndStyle(family, style); + _UpdateFontPreview(); + _Invoke(); + break; + } + + default: + BHandler::MessageReceived(message); + } +} + + +void +FontSelectionView::SetMessage(BMessage* message) +{ + delete fMessage; + fMessage = message; +} + + +void +FontSelectionView::SetTarget(BHandler* target) +{ + fTarget = target; +} + + +// #pragma mark - + + +void +FontSelectionView::SetFont(const BFont& font, float size) +{ + BFont resizedFont(font); + resizedFont.SetSize(size); + SetFont(resizedFont); +} + + +void +FontSelectionView::SetFont(const BFont& font) +{ + if (font == fCurrentFont && font == fSavedFont) + return; + + _SelectCurrentFont(false); + fSavedFont = fCurrentFont = font; + _UpdateFontPreview(); + + _SelectCurrentFont(true); + _SelectCurrentSize(true); +} + + +void +FontSelectionView::SetSize(float size) +{ + SetFont(fCurrentFont, size); +} + + +const BFont& +FontSelectionView::Font() const +{ + return fCurrentFont; +} + + +void +FontSelectionView::SetDefaults() +{ + BFont defaultFont = _DefaultFont(); + if (defaultFont == fCurrentFont) + return; + + _SelectCurrentFont(false); + + fCurrentFont = defaultFont; + _UpdateFontPreview(); + + _SelectCurrentFont(true); + _SelectCurrentSize(true); +} + + +void +FontSelectionView::Revert() +{ + if (!IsRevertable()) + return; + + _SelectCurrentFont(false); + + fCurrentFont = fSavedFont; + _UpdateFontPreview(); + + _SelectCurrentFont(true); + _SelectCurrentSize(true); +} + + +bool +FontSelectionView::IsDefaultable() +{ + return fCurrentFont != _DefaultFont(); +} + + +bool +FontSelectionView::IsRevertable() +{ + return fCurrentFont != fSavedFont; +} + + +void +FontSelectionView::UpdateFontsMenu() +{ + int32 numFamilies = count_font_families(); + + fFontsMenu->RemoveItems(0, fFontsMenu->CountItems(), true); + + BFont font = fCurrentFont; + + font_family currentFamily; + font_style currentStyle; + font.GetFamilyAndStyle(¤tFamily, ¤tStyle); + + for (int32 i = 0; i < numFamilies; i++) { + font_family family; + uint32 flags; + if (get_font_family(i, &family, &flags) != B_OK) + continue; + + // if we're setting the fixed font, we only want to show fixed fonts + if (!strcmp(Name(), "fixed") && (flags & B_IS_FIXED) == 0) + continue; + + font.SetFamilyAndFace(family, B_REGULAR_FACE); + + BMessage* message = new BMessage(kMsgSetFamily); + message->AddString("family", family); + message->AddString("name", Name()); + + BMenuItem* familyItem; + if (fStylesMenuField != NULL) { + familyItem = new BMenuItem(family, message); + } else { + // Each family item has a submenu with all styles for that font. + BMenu* stylesMenu = new BMenu(family); + _AddStylesToMenu(font, stylesMenu); + familyItem = new BMenuItem(stylesMenu, message); + } + + familyItem->SetMarked(strcmp(family, currentFamily) == 0); + fFontsMenu->AddItem(familyItem); + familyItem->SetTarget(this); + } + + // Separate styles menu for only the current font. + if (fStylesMenuField != NULL) + _AddStylesToMenu(fCurrentFont, fStylesMenuField->Menu()); +} + + +// #pragma mark - private + + +BLayoutItem* +FontSelectionView::CreateSizesLabelLayoutItem() +{ + return fSizesMenuField->CreateLabelLayoutItem(); +} + + +BLayoutItem* +FontSelectionView::CreateSizesMenuBarLayoutItem() +{ + return fSizesMenuField->CreateMenuBarLayoutItem(); +} + + +BLayoutItem* +FontSelectionView::CreateFontsLabelLayoutItem() +{ + return fFontsMenuField->CreateLabelLayoutItem(); +} + + +BLayoutItem* +FontSelectionView::CreateFontsMenuBarLayoutItem() +{ + return fFontsMenuField->CreateMenuBarLayoutItem(); +} + + +BLayoutItem* +FontSelectionView::CreateStylesLabelLayoutItem() +{ + if (fStylesMenuField) + return fStylesMenuField->CreateLabelLayoutItem(); + return NULL; +} + + +BLayoutItem* +FontSelectionView::CreateStylesMenuBarLayoutItem() +{ + if (fStylesMenuField) + return fStylesMenuField->CreateMenuBarLayoutItem(); + return NULL; +} + + +BView* +FontSelectionView::PreviewBox() const +{ + return fPreviewText; +} + + +// #pragma mark - private + + +void +FontSelectionView::_Invoke() +{ + if (fTarget != NULL && fTarget->Looper() != NULL && fMessage != NULL) { + BMessage message(*fMessage); + fTarget->Looper()->PostMessage(&message, fTarget); + } +} + + +BFont +FontSelectionView::_DefaultFont() const +{ + if (strcmp(Name(), "bold") == 0) + return *be_bold_font; + if (strcmp(Name(), "fixed") == 0) + return *be_fixed_font; + else + return *be_plain_font; +} + + +void +FontSelectionView::_SelectCurrentFont(bool select) +{ + font_family family; + font_style style; + fCurrentFont.GetFamilyAndStyle(&family, &style); + + BMenuItem *item = fFontsMenu->FindItem(family); + if (item != NULL) { + item->SetMarked(select); + + if (item->Submenu() != NULL) { + item = item->Submenu()->FindItem(style); + if (item != NULL) + item->SetMarked(select); + } + } +} + + +void +FontSelectionView::_SelectCurrentSize(bool select) +{ + char label[16]; + snprintf(label, sizeof(label), "%ld", (int32)fCurrentFont.Size()); + + BMenuItem* item = fSizesMenu->FindItem(label); + if (item != NULL) + item->SetMarked(select); +} + + +void +FontSelectionView::_UpdateFontPreview() +{ + fPreviewText->SetFont(&fCurrentFont); +} + + +void +FontSelectionView::_BuildSizesMenu() +{ + const int32 sizes[] = {7, 8, 9, 10, 11, 12, 13, 14, 18, 21, 24, 0}; + + // build size menu + for (int32 i = 0; sizes[i]; i++) { + int32 size = sizes[i]; + if (size < kMinSize || size > kMaxSize) + continue; + + char label[32]; + snprintf(label, sizeof(label), "%ld", size); + + BMessage* message = new BMessage(kMsgSetSize); + message->AddInt32("size", size); + message->AddString("name", Name()); + + BMenuItem* item = new BMenuItem(label, message); + if (size == fCurrentFont.Size()) + item->SetMarked(true); + + fSizesMenu->AddItem(item); + item->SetTarget(this); + } +} + + +void +FontSelectionView::_AddStylesToMenu(const BFont& font, BMenu* stylesMenu) const +{ + stylesMenu->RemoveItems(0, stylesMenu->CountItems(), true); + stylesMenu->SetRadioMode(true); + + font_family family; + font_style style; + font.GetFamilyAndStyle(&family, &style); + BString currentStyle(style); + + int32 numStyles = count_font_styles(family); + + for (int32 j = 0; j < numStyles; j++) { + if (get_font_style(family, j, &style) != B_OK) + continue; + + BMessage* message = new BMessage(kMsgSetStyle); + message->AddString("family", (char*)family); + message->AddString("style", (char*)style); + + BMenuItem* item = new BMenuItem(style, message); + item->SetMarked(currentStyle == style); + + stylesMenu->AddItem(item); + item->SetTarget(this); + } +} + diff --git a/src/apps/webpositive/support/FontSelectionView.h b/src/apps/webpositive/support/FontSelectionView.h new file mode 100644 index 0000000000..8ee630b448 --- /dev/null +++ b/src/apps/webpositive/support/FontSelectionView.h @@ -0,0 +1,91 @@ +/* + * Copyright 2001-2010, Haiku. + * Distributed under the terms of the MIT License. + * + * Authors: + * Mark Hogben + * DarkWyrm + * Axel Dörfler, axeld@pinc-software.de + * Philippe Saint-Pierre, stpere@gmail.com + * Stephan Aßmus + */ +#ifndef FONT_SELECTION_VIEW_H +#define FONT_SELECTION_VIEW_H + + +#include +#include + +class BLayoutItem; +class BBox; +class BMenu; +class BMenuField; +class BPopUpMenu; +class BStringView; +class BView; + + +class FontSelectionView : public BHandler { +public: + FontSelectionView(const char* name, + const char* label, bool separateStyles, + const BFont* font = NULL); + virtual ~FontSelectionView(); + + void AttachedToLooper(); + virtual void MessageReceived(BMessage* message); + + void SetMessage(BMessage* message); + void SetTarget(BHandler* target); + + void SetFont(const BFont& font, float size); + void SetFont(const BFont& font); + void SetSize(float size); + const BFont& Font() const; + + void SetDefaults(); + void Revert(); + bool IsDefaultable(); + bool IsRevertable(); + + void UpdateFontsMenu(); + + BLayoutItem* CreateSizesLabelLayoutItem(); + BLayoutItem* CreateSizesMenuBarLayoutItem(); + + BLayoutItem* CreateFontsLabelLayoutItem(); + BLayoutItem* CreateFontsMenuBarLayoutItem(); + + BLayoutItem* CreateStylesLabelLayoutItem(); + BLayoutItem* CreateStylesMenuBarLayoutItem(); + + BView* PreviewBox() const; + +private: + void _Invoke(); + + BFont _DefaultFont() const; + void _SelectCurrentFont(bool select); + void _SelectCurrentSize(bool select); + void _UpdateFontPreview(); + void _BuildSizesMenu(); + void _AddStylesToMenu(const BFont& font, + BMenu* menu) const; + +protected: + BMenuField* fFontsMenuField; + BMenuField* fStylesMenuField; + BMenuField* fSizesMenuField; + BPopUpMenu* fFontsMenu; + BPopUpMenu* fStylesMenu; + BPopUpMenu* fSizesMenu; + BStringView* fPreviewText; + + BFont fSavedFont; + BFont fCurrentFont; + + BMessage* fMessage; + BHandler* fTarget; +}; + +#endif // FONT_SELECTION_VIEW_H diff --git a/src/apps/webpositive/support/HashKeys.h b/src/apps/webpositive/support/HashKeys.h new file mode 100644 index 0000000000..3480b56492 --- /dev/null +++ b/src/apps/webpositive/support/HashKeys.h @@ -0,0 +1,124 @@ +/* + * Copyright 2010, Stephan Aßmus . All rights reserved. + * Copyright 2004-2007, Ingo Weinhold . All rights reserved. + * Distributed under the terms of the MIT License. + */ +#ifndef HASH_KEYS_H +#define HASH_KEYS_H + +#include + + +namespace BPrivate { + +#if 0 +// TODO: Move here from HashMap.h and adapt all clients. +// HashKey32 +template +struct HashKey32 { + HashKey32() {} + HashKey32(const Value& value) : value(value) {} + + uint32 GetHashCode() const + { + return (uint32)value; + } + + HashKey32 operator=(const HashKey32& other) + { + value = other.value; + return *this; + } + + bool operator==(const HashKey32& other) const + { + return (value == other.value); + } + + bool operator!=(const HashKey32& other) const + { + return (value != other.value); + } + + Value value; +}; + + +// HashKey64 +template +struct HashKey64 { + HashKey64() {} + HashKey64(const Value& value) : value(value) {} + + uint32 GetHashCode() const + { + uint64 v = (uint64)value; + return (uint32)(v >> 32) ^ (uint32)v; + } + + HashKey64 operator=(const HashKey64& other) + { + value = other.value; + return *this; + } + + bool operator==(const HashKey64& other) const + { + return (value == other.value); + } + + bool operator!=(const HashKey64& other) const + { + return (value != other.value); + } + + Value value; +}; +#endif + + +struct HashKeyString { + HashKeyString() {} + HashKeyString(const BString& value) : value(value) {} + HashKeyString(const char* string) : value(string) {} + + uint32 GetHashCode() const + { + // from the Dragon Book: a slightly modified hashpjw() + uint32 hash = 0; + const char* string = value.String(); + if (string != NULL) { + for (; *string; string++) { + uint32 g = hash & 0xf0000000; + if (g != 0) + hash ^= g >> 24; + hash = (hash << 4) + *string; + } + } + return hash; + } + + HashKeyString operator=(const HashKeyString& other) + { + value = other.value; + return *this; + } + + bool operator==(const HashKeyString& other) const + { + return (value == other.value); + } + + bool operator!=(const HashKeyString& other) const + { + return (value != other.value); + } + + BString value; +}; + +} // namespace BPrivate + +using BPrivate::HashKeyString; + +#endif // HASH_KEYS_H diff --git a/src/apps/webpositive/support/HashMap.h b/src/apps/webpositive/support/HashMap.h new file mode 100644 index 0000000000..36b321f2b8 --- /dev/null +++ b/src/apps/webpositive/support/HashMap.h @@ -0,0 +1,481 @@ +// HashMap.h +// +// Copyright (c) 2004-2007, Ingo Weinhold (bonefish@cs.tu-berlin.de) +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +// +// Except as contained in this notice, the name of a copyright holder shall +// not be used in advertising or otherwise to promote the sale, use or other +// dealings in this Software without prior written authorization of the +// copyright holder. + +#ifndef HASH_MAP_H +#define HASH_MAP_H + + +#include + +#include "AutoLocker.h" +#include "OpenHashTable.h" + + +namespace BPrivate { + +// HashMapElement +template +class HashMapElement : public OpenHashElement { +private: + typedef HashMapElement Element; +public: + + HashMapElement() : OpenHashElement(), fKey(), fValue() + { + fNext = -1; + } + + inline uint32 Hash() const + { + return fKey.GetHashCode(); + } + + inline bool operator==(const OpenHashElement &_element) const + { + const Element &element = static_cast(_element); + return (fKey == element.fKey); + } + + inline void Adopt(Element &element) + { + fKey = element.fKey; + fValue = element.fValue; + } + + Key fKey; + Value fValue; +}; + +// HashMap +template +class HashMap { +public: + class Entry { + public: + Entry() {} + Entry(const Key& key, Value value) : key(key), value(value) {} + + Key key; + Value value; + }; + + class Iterator { + private: + typedef HashMapElement Element; + public: + Iterator(const Iterator& other) + : + fMap(other.fMap), + fIndex(other.fIndex), + fElement(other.fElement), + fLastElement(other.fElement) + { + } + + bool HasNext() const + { + return fElement; + } + + Entry Next() + { + if (!fElement) + return Entry(); + Entry result(fElement->fKey, fElement->fValue); + _FindNext(); + return result; + } + + Value* NextValue() + { + if (fElement == NULL) + return NULL; + + Value* value = &fElement->fValue; + _FindNext(); + return value; + } + + Entry Remove() + { + if (!fLastElement) + return Entry(); + Entry result(fLastElement->fKey, fLastElement->fValue); + fMap->fTable.Remove(fLastElement, true); + fLastElement = NULL; + return result; + } + + Iterator& operator=(const Iterator& other) + { + fMap = other.fMap; + fIndex = other.fIndex; + fElement = other.fElement; + fLastElement = other.fLastElement; + return *this; + } + + private: + Iterator(const HashMap* map) + : + fMap(const_cast*>(map)), + fIndex(0), + fElement(NULL), + fLastElement(NULL) + { + // find first + _FindNext(); + } + + void _FindNext() + { + fLastElement = fElement; + if (fElement && fElement->fNext >= 0) { + fElement = fMap->fTable.ElementAt(fElement->fNext); + return; + } + fElement = NULL; + int32 arraySize = fMap->fTable.ArraySize(); + for (; !fElement && fIndex < arraySize; fIndex++) + fElement = fMap->fTable.FindFirst(fIndex); + } + + private: + friend class HashMap; + + HashMap* fMap; + int32 fIndex; + Element* fElement; + Element* fLastElement; + }; + + HashMap(); + ~HashMap(); + + status_t InitCheck() const; + + status_t Put(const Key& key, Value value); + Value Remove(const Key& key); + void Clear(); + Value Get(const Key& key) const; + bool Get(const Key& key, Value*& _value) const; + + bool ContainsKey(const Key& key) const; + + int32 Size() const; + + Iterator GetIterator() const; + +protected: + typedef HashMapElement Element; + friend class Iterator; + +private: + Element *_FindElement(const Key& key) const; + +protected: + OpenHashElementArray fElementArray; + OpenHashTable > fTable; +}; + +// SynchronizedHashMap +template +class SynchronizedHashMap : public BLocker { +public: + typedef struct HashMap::Entry Entry; + typedef struct HashMap::Iterator Iterator; + + SynchronizedHashMap() : BLocker("synchronized hash map") {} + ~SynchronizedHashMap() { Lock(); } + + status_t InitCheck() const + { + return fMap.InitCheck(); + } + + status_t Put(const Key& key, Value value) + { + MapLocker locker(this); + if (!locker.IsLocked()) + return B_ERROR; + return fMap.Put(key, value); + } + + Value Remove(const Key& key) + { + MapLocker locker(this); + if (!locker.IsLocked()) + return Value(); + return fMap.Remove(key); + } + + void Clear() + { + MapLocker locker(this); + return fMap.Clear(); + } + + Value Get(const Key& key) const + { + const BLocker* lock = this; + MapLocker locker(const_cast(lock)); + if (!locker.IsLocked()) + return Value(); + return fMap.Get(key); + } + + bool ContainsKey(const Key& key) const + { + const BLocker* lock = this; + MapLocker locker(const_cast(lock)); + if (!locker.IsLocked()) + return false; + return fMap.ContainsKey(key); + } + + int32 Size() const + { + const BLocker* lock = this; + MapLocker locker(const_cast(lock)); + return fMap.Size(); + } + + Iterator GetIterator() + { + return fMap.GetIterator(); + } + + // for debugging only + const HashMap& GetUnsynchronizedMap() const { return fMap; } + HashMap& GetUnsynchronizedMap() { return fMap; } + +protected: + typedef AutoLocker MapLocker; + + HashMap fMap; +}; + +// HashKey32 +template +struct HashKey32 { + HashKey32() {} + HashKey32(const Value& value) : value(value) {} + + uint32 GetHashCode() const + { + return (uint32)value; + } + + HashKey32 operator=(const HashKey32& other) + { + value = other.value; + return *this; + } + + bool operator==(const HashKey32& other) const + { + return (value == other.value); + } + + bool operator!=(const HashKey32& other) const + { + return (value != other.value); + } + + Value value; +}; + + +// HashKey64 +template +struct HashKey64 { + HashKey64() {} + HashKey64(const Value& value) : value(value) {} + + uint32 GetHashCode() const + { + uint64 v = (uint64)value; + return (uint32)(v >> 32) ^ (uint32)v; + } + + HashKey64 operator=(const HashKey64& other) + { + value = other.value; + return *this; + } + + bool operator==(const HashKey64& other) const + { + return (value == other.value); + } + + bool operator!=(const HashKey64& other) const + { + return (value != other.value); + } + + Value value; +}; + + +// HashMap + +// constructor +template +HashMap::HashMap() + : + fElementArray(1000), + fTable(1000, &fElementArray) +{ +} + +// destructor +template +HashMap::~HashMap() +{ +} + +// InitCheck +template +status_t +HashMap::InitCheck() const +{ + return (fTable.InitCheck() && fElementArray.InitCheck() + ? B_OK : B_NO_MEMORY); +} + +// Put +template +status_t +HashMap::Put(const Key& key, Value value) +{ + Element* element = _FindElement(key); + if (element) { + // already contains the key: just set the new value + element->fValue = value; + return B_OK; + } + // does not contain the key yet: add an element + element = fTable.Add(key.GetHashCode()); + if (!element) + return B_NO_MEMORY; + element->fKey = key; + element->fValue = value; + return B_OK; +} + +// Remove +template +Value +HashMap::Remove(const Key& key) +{ + Value value = Value(); + if (Element* element = _FindElement(key)) { + value = element->fValue; + fTable.Remove(element); + } + return value; +} + +// Clear +template +void +HashMap::Clear() +{ + fTable.RemoveAll(); +} + +// Get +template +Value +HashMap::Get(const Key& key) const +{ + if (Element* element = _FindElement(key)) + return element->fValue; + return Value(); +} + +// Get +template +bool +HashMap::Get(const Key& key, Value*& _value) const +{ + if (Element* element = _FindElement(key)) { + _value = &element->fValue; + return true; + } + + return false; +} + +// ContainsKey +template +bool +HashMap::ContainsKey(const Key& key) const +{ + return _FindElement(key); +} + +// Size +template +int32 +HashMap::Size() const +{ + return fTable.CountElements(); +} + +// GetIterator +template +struct HashMap::Iterator +HashMap::GetIterator() const +{ + return Iterator(this); +} + +// _FindElement +template +struct HashMap::Element * +HashMap::_FindElement(const Key& key) const +{ + Element* element = fTable.FindFirst(key.GetHashCode()); + while (element && element->fKey != key) { + if (element->fNext >= 0) + element = fTable.ElementAt(element->fNext); + else + element = NULL; + } + return element; +} + +} // namespace BPrivate + +using BPrivate::HashMap; +using BPrivate::HashKey32; +using BPrivate::HashKey64; +using BPrivate::SynchronizedHashMap; + +#endif // HASH_MAP_H diff --git a/src/apps/webpositive/support/HashSet.h b/src/apps/webpositive/support/HashSet.h new file mode 100644 index 0000000000..ee7be71288 --- /dev/null +++ b/src/apps/webpositive/support/HashSet.h @@ -0,0 +1,342 @@ +// HashSet.h +// +// Copyright (c) 2004, Ingo Weinhold (bonefish@cs.tu-berlin.de) +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +// +// Except as contained in this notice, the name of a copyright holder shall +// not be used in advertising or otherwise to promote the sale, use or other +// dealings in this Software without prior written authorization of the +// copyright holder. + +#ifndef HASH_SET_H +#define HASH_SET_H + +#include + +#include "AutoLocker.h" +#include "OpenHashTable.h" + + +namespace BPrivate { + +// HashSetElement +template +class HashSetElement : public OpenHashElement { +private: + typedef HashSetElement Element; +public: + + HashSetElement() : OpenHashElement(), fKey() + { + fNext = -1; + } + + inline uint32 Hash() const + { + return fKey.GetHashCode(); + } + + inline bool operator==(const OpenHashElement &_element) const + { + const Element &element = static_cast(_element); + return (fKey == element.fKey); + } + + inline void Adopt(Element &element) + { + fKey = element.fKey; + } + + Key fKey; +}; + +// HashSet +template +class HashSet { +public: + class Iterator { + private: + typedef HashSetElement Element; + public: + Iterator(const Iterator& other) + : fSet(other.fSet), + fIndex(other.fIndex), + fElement(other.fElement), + fLastElement(other.fElement) + { + } + + bool HasNext() const + { + return fElement; + } + + Key Next() + { + if (!fElement) + return Key(); + Key result(fElement->fKey); + _FindNext(); + return result; + } + + bool Remove() + { + if (!fLastElement) + return false; + fSet->fTable.Remove(fLastElement); + fLastElement = NULL; + return true; + } + + Iterator& operator=(const Iterator& other) + { + fSet = other.fSet; + fIndex = other.fIndex; + fElement = other.fElement; + fLastElement = other.fLastElement; + return *this; + } + + private: + Iterator(HashSet* map) + : fSet(map), + fIndex(0), + fElement(NULL), + fLastElement(NULL) + { + // find first + _FindNext(); + } + + void _FindNext() + { + fLastElement = fElement; + if (fElement && fElement->fNext >= 0) { + fElement = fSet->fTable.ElementAt(fElement->fNext); + return; + } + fElement = NULL; + int32 arraySize = fSet->fTable.ArraySize(); + for (; !fElement && fIndex < arraySize; fIndex++) + fElement = fSet->fTable.FindFirst(fIndex); + } + + private: + friend class HashSet; + + HashSet* fSet; + int32 fIndex; + Element* fElement; + Element* fLastElement; + }; + + HashSet(); + ~HashSet(); + + status_t InitCheck() const; + + status_t Add(const Key& key); + bool Remove(const Key& key); + void Clear(); + bool Contains(const Key& key) const; + + int32 Size() const; + bool IsEmpty() const { return Size() == 0; } + + Iterator GetIterator(); + +protected: + typedef HashSetElement Element; + friend class Iterator; + +private: + Element *_FindElement(const Key& key) const; + +protected: + OpenHashElementArray fElementArray; + OpenHashTable > fTable; +}; + +// SynchronizedHashSet +template +class SynchronizedHashSet : public BLocker { +public: + typedef struct HashSet::Iterator Iterator; + + SynchronizedHashSet() : BLocker("synchronized hash set") {} + ~SynchronizedHashSet() { Lock(); } + + status_t InitCheck() const + { + return fSet.InitCheck(); + } + + status_t Add(const Key& key) + { + MapLocker locker(this); + if (!locker.IsLocked()) + return B_ERROR; + return fSet.Add(key); + } + + bool Remove(const Key& key) + { + MapLocker locker(this); + if (!locker.IsLocked()) + return false; + return fSet.Remove(key); + } + + bool Contains(const Key& key) const + { + const BLocker* lock = this; + MapLocker locker(const_cast(lock)); + if (!locker.IsLocked()) + return false; + return fSet.Contains(key); + } + + int32 Size() const + { + const BLocker* lock = this; + MapLocker locker(const_cast(lock)); + return fSet.Size(); + } + + Iterator GetIterator() + { + return fSet.GetIterator(); + } + + // for debugging only + const HashSet& GetUnsynchronizedSet() const { return fSet; } + HashSet& GetUnsynchronizedSet() { return fSet; } + +protected: + typedef AutoLocker MapLocker; + + HashSet fSet; +}; + +// HashSet + +// constructor +template +HashSet::HashSet() + : fElementArray(1000), + fTable(1000, &fElementArray) +{ +} + +// destructor +template +HashSet::~HashSet() +{ +} + +// InitCheck +template +status_t +HashSet::InitCheck() const +{ + return (fTable.InitCheck() && fElementArray.InitCheck() + ? B_OK : B_NO_MEMORY); +} + +// Add +template +status_t +HashSet::Add(const Key& key) +{ + if (Contains(key)) + return B_OK; + Element* element = fTable.Add(key.GetHashCode()); + if (!element) + return B_NO_MEMORY; + element->fKey = key; + return B_OK; +} + +// Remove +template +bool +HashSet::Remove(const Key& key) +{ + if (Element* element = _FindElement(key)) { + fTable.Remove(element); + return true; + } + return false; +} + +// Clear +template +void +HashSet::Clear() +{ + fTable.RemoveAll(); +} + +// Contains +template +bool +HashSet::Contains(const Key& key) const +{ + return _FindElement(key); +} + +// Size +template +int32 +HashSet::Size() const +{ + return fTable.CountElements(); +} + +// GetIterator +template +struct HashSet::Iterator +HashSet::GetIterator() +{ + return Iterator(this); +} + +// _FindElement +template +struct HashSet::Element * +HashSet::_FindElement(const Key& key) const +{ + Element* element = fTable.FindFirst(key.GetHashCode()); + while (element && element->fKey != key) { + if (element->fNext >= 0) + element = fTable.ElementAt(element->fNext); + else + element = NULL; + } + return element; +} + +} // namespace BPrivate + +using BPrivate::HashSet; +using BPrivate::SynchronizedHashSet; + +#endif // HASH_SET_H diff --git a/src/apps/webpositive/support/IconButton.cpp b/src/apps/webpositive/support/IconButton.cpp new file mode 100644 index 0000000000..5cd2ede604 --- /dev/null +++ b/src/apps/webpositive/support/IconButton.cpp @@ -0,0 +1,929 @@ +/* + * Copyright 2006-2010, Haiku. + * Distributed under the terms of the MIT License. + * + * Authors: + * Stephan Aßmus + */ + +// NOTE: this file is a duplicate of the version in Icon-O-Matic/generic +// it should be placed into a common folder for generic useful stuff + +#include "IconButton.h" + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "IconUtils.h" + +using std::nothrow; + +// constructor +IconButton::IconButton(const char* name, uint32 id, const char* label, + BMessage* message, BHandler* target) + : BView(name, B_WILL_DRAW), + BInvoker(message, target), + fButtonState(STATE_ENABLED), + fID(id), + fNormalBitmap(NULL), + fDisabledBitmap(NULL), + fClickedBitmap(NULL), + fDisabledClickedBitmap(NULL), + fLabel(label), + fTargetCache(target) +{ + SetLowColor(ui_color(B_PANEL_BACKGROUND_COLOR)); + SetViewColor(B_TRANSPARENT_32_BIT); +} + +// destructor +IconButton::~IconButton() +{ + _DeleteBitmaps(); +} + +// MessageReceived +void +IconButton::MessageReceived(BMessage* message) +{ + switch (message->what) { + default: + BView::MessageReceived(message); + break; + } +} + +// AttachedToWindow +void +IconButton::AttachedToWindow() +{ + rgb_color background = B_TRANSPARENT_COLOR; + if (BView* parent = Parent()) { + background = parent->ViewColor(); + if (background == B_TRANSPARENT_COLOR) + background = parent->LowColor(); + } + if (background == B_TRANSPARENT_COLOR) + background = ui_color(B_PANEL_BACKGROUND_COLOR); + SetLowColor(background); + + SetTarget(fTargetCache); + if (!Target()) + SetTarget(Window()); +} + +// Draw +void +IconButton::Draw(BRect area) +{ + rgb_color background = LowColor(); + + BRect r(Bounds()); + + if (be_control_look != NULL) { + uint32 flags = 0; + BBitmap* bitmap = fNormalBitmap; + if (!IsEnabled()) { + flags |= BControlLook::B_DISABLED; + bitmap = fDisabledBitmap; + } + if (_HasFlags(STATE_PRESSED) || _HasFlags(STATE_FORCE_PRESSED)) + flags |= BControlLook::B_ACTIVATED; + + if (DrawBorder()) { + be_control_look->DrawButtonFrame(this, r, area, background, + background, flags); + be_control_look->DrawButtonBackground(this, r, area, background, + flags); + } else { + SetHighColor(background); + FillRect(r); + } + + if (bitmap && bitmap->IsValid()) { + float x = r.left + floorf((r.Width() + - bitmap->Bounds().Width()) / 2.0 + 0.5); + float y = r.top + floorf((r.Height() + - bitmap->Bounds().Height()) / 2.0 + 0.5); + BPoint point(x, y); + if (_HasFlags(STATE_PRESSED) || _HasFlags(STATE_FORCE_PRESSED)) + point += BPoint(1.0, 1.0); + if (bitmap->ColorSpace() == B_RGBA32 + || bitmap->ColorSpace() == B_RGBA32_BIG) { + SetDrawingMode(B_OP_ALPHA); + SetBlendingMode(B_PIXEL_ALPHA, B_ALPHA_OVERLAY); + } + DrawBitmap(bitmap, point); + } + return; + } + + rgb_color lightShadow, shadow, darkShadow, light; + BBitmap* bitmap = fNormalBitmap; + // adjust colors and bitmap according to flags + if (IsEnabled()) { + lightShadow = tint_color(background, B_DARKEN_1_TINT); + shadow = tint_color(background, B_DARKEN_2_TINT); + darkShadow = tint_color(background, B_DARKEN_4_TINT); + light = tint_color(background, B_LIGHTEN_MAX_TINT); + SetHighColor(0, 0, 0, 255); + } else { + lightShadow = tint_color(background, 1.11); + shadow = tint_color(background, B_DARKEN_1_TINT); + darkShadow = tint_color(background, B_DARKEN_2_TINT); + light = tint_color(background, B_LIGHTEN_2_TINT); + bitmap = fDisabledBitmap; + SetHighColor(tint_color(background, B_DISABLED_LABEL_TINT)); + } + if (_HasFlags(STATE_PRESSED) || _HasFlags(STATE_FORCE_PRESSED)) { + if (IsEnabled()) { +// background = tint_color(background, B_DARKEN_2_TINT); +// background = tint_color(background, B_LIGHTEN_1_TINT); + background = tint_color(background, B_DARKEN_1_TINT); + bitmap = fClickedBitmap; + } else { +// background = tint_color(background, B_DARKEN_1_TINT); +// background = tint_color(background, (B_NO_TINT + B_LIGHTEN_1_TINT) / 2.0); + background = tint_color(background, (B_NO_TINT + B_DARKEN_1_TINT) / 2.0); + bitmap = fDisabledClickedBitmap; + } + // background + SetLowColor(background); + r.InsetBy(2.0, 2.0); + StrokeLine(r.LeftBottom(), r.LeftTop(), B_SOLID_LOW); + StrokeLine(r.LeftTop(), r.RightTop(), B_SOLID_LOW); + r.InsetBy(-2.0, -2.0); + } + // draw frame only if tracking + if (DrawBorder()) { + if (_HasFlags(STATE_PRESSED) || _HasFlags(STATE_FORCE_PRESSED)) + DrawPressedBorder(r, background, shadow, darkShadow, lightShadow, light); + else + DrawNormalBorder(r, background, shadow, darkShadow, lightShadow, light); + r.InsetBy(2.0, 2.0); + } else + _DrawFrame(r, background, background, background, background); + float width = Bounds().Width(); + float height = Bounds().Height(); + // bitmap + BRegion originalClippingRegion; + if (bitmap && bitmap->IsValid()) { + float x = floorf((width - bitmap->Bounds().Width()) / 2.0 + 0.5); + float y = floorf((height - bitmap->Bounds().Height()) / 2.0 + 0.5); + BPoint point(x, y); + if (_HasFlags(STATE_PRESSED) || _HasFlags(STATE_FORCE_PRESSED)) + point += BPoint(1.0, 1.0); + if (bitmap->ColorSpace() == B_RGBA32 || bitmap->ColorSpace() == B_RGBA32_BIG) { + FillRect(r, B_SOLID_LOW); + SetDrawingMode(B_OP_ALPHA); + SetBlendingMode(B_PIXEL_ALPHA, B_ALPHA_OVERLAY); + } + DrawBitmap(bitmap, point); + // constrain clipping region + BRegion region= originalClippingRegion; + GetClippingRegion(®ion); + region.Exclude(bitmap->Bounds().OffsetByCopy(point)); + ConstrainClippingRegion(®ion); + } + // background + SetDrawingMode(B_OP_COPY); + FillRect(r, B_SOLID_LOW); + ConstrainClippingRegion(NULL); + // label + if (fLabel.CountChars() > 0) { + SetDrawingMode(B_OP_COPY); + font_height fh; + GetFontHeight(&fh); + float y = Bounds().bottom - 4.0; + y -= fh.descent; + float x = (width - StringWidth(fLabel.String())) / 2.0; + DrawString(fLabel.String(), BPoint(x, y)); + } +} + +// MouseDown +void +IconButton::MouseDown(BPoint where) +{ + if (!IsValid()) + return; + + if (_HasFlags(STATE_ENABLED)/* && !_HasFlags(STATE_FORCE_PRESSED)*/) { + if (Bounds().Contains(where)) { + SetMouseEventMask(B_POINTER_EVENTS, B_LOCK_WINDOW_FOCUS); + _AddFlags(STATE_PRESSED | STATE_TRACKING); + } else { + _ClearFlags(STATE_PRESSED | STATE_TRACKING); + } + } +} + +// MouseUp +void +IconButton::MouseUp(BPoint where) +{ + if (!IsValid()) + return; + +// if (!_HasFlags(STATE_FORCE_PRESSED)) { + if (_HasFlags(STATE_ENABLED) && _HasFlags(STATE_PRESSED) && Bounds().Contains(where)) + Invoke(); + else if (Bounds().Contains(where)) + _AddFlags(STATE_INSIDE); + _ClearFlags(STATE_PRESSED | STATE_TRACKING); +// } +} + +// MouseMoved +void +IconButton::MouseMoved(BPoint where, uint32 transit, const BMessage* message) +{ + if (!IsValid()) + return; + + uint32 buttons = 0; + Window()->CurrentMessage()->FindInt32("buttons", (int32*)&buttons); + // catch a mouse up event that we might have missed + if (!buttons && _HasFlags(STATE_PRESSED)) { + MouseUp(where); + return; + } + if (buttons && !_HasFlags(STATE_TRACKING)) + return; + if ((transit == B_INSIDE_VIEW || transit == B_ENTERED_VIEW) + && _HasFlags(STATE_ENABLED)) + _AddFlags(STATE_INSIDE); + else + _ClearFlags(STATE_INSIDE); + if (_HasFlags(STATE_TRACKING)) { + if (Bounds().Contains(where)) + _AddFlags(STATE_PRESSED); + else + _ClearFlags(STATE_PRESSED); + } +} + +#define MIN_SPACE 15.0 + +// GetPreferredSize +void +IconButton::GetPreferredSize(float* width, float* height) +{ + float minWidth = 0.0; + float minHeight = 0.0; + if (IsValid()) { + minWidth += fNormalBitmap->Bounds().IntegerWidth() + 1.0; + minHeight += fNormalBitmap->Bounds().IntegerHeight() + 1.0; + } + if (minWidth < MIN_SPACE) + minWidth = MIN_SPACE; + if (minHeight < MIN_SPACE) + minHeight = MIN_SPACE; + + float hPadding = max_c(6.0, ceilf(minHeight / 4.0)); + float vPadding = max_c(6.0, ceilf(minWidth / 4.0)); + + if (fLabel.CountChars() > 0) { + font_height fh; + GetFontHeight(&fh); + minHeight += ceilf(fh.ascent + fh.descent) + vPadding; + minWidth += StringWidth(fLabel.String()) + vPadding; + } + + if (width) + *width = minWidth + hPadding; + if (height) + *height = minHeight + vPadding; +} + +// MinSize +BSize +IconButton::MinSize() +{ + BSize size; + GetPreferredSize(&size.width, &size.height); + return size; +} + +// MaxSize +BSize +IconButton::MaxSize() +{ + return MinSize(); +} + +// Invoke +status_t +IconButton::Invoke(BMessage* message) +{ + if (!message) + message = Message(); + if (message) { + BMessage clone(*message); + clone.AddInt64("be:when", system_time()); + clone.AddPointer("be:source", (BView*)this); + clone.AddInt32("be:value", Value()); + clone.AddInt32("id", ID()); + return BInvoker::Invoke(&clone); + } + return BInvoker::Invoke(message); +} + +// SetPressed +void +IconButton::SetPressed(bool pressed) +{ + if (pressed) + _AddFlags(STATE_FORCE_PRESSED); + else + _ClearFlags(STATE_FORCE_PRESSED); +} + +// IsPressed +bool +IconButton::IsPressed() const +{ + return _HasFlags(STATE_FORCE_PRESSED); +} + +status_t +IconButton::SetIcon(int32 resourceID) +{ + app_info info; + status_t status = be_app->GetAppInfo(&info); + if (status != B_OK) + return status; + + BResources resources(&info.ref); + status = resources.InitCheck(); + if (status != B_OK) + return status; + + size_t size; + const void* data = resources.LoadResource(B_VECTOR_ICON_TYPE, resourceID, + &size); + if (data != NULL) { + BBitmap bitmap(BRect(0, 0, 31, 31), B_BITMAP_NO_SERVER_LINK, B_RGBA32); + status = bitmap.InitCheck(); + if (status != B_OK) + return status; + status = BIconUtils::GetVectorIcon(reinterpret_cast(data), + size, &bitmap); + if (status != B_OK) + return status; + return SetIcon(&bitmap); + } +// const void* data = resources.LoadResource(B_BITMAP_TYPE, resourceID, &size); + return B_ERROR; +} + +// SetIcon +status_t +IconButton::SetIcon(const char* pathToBitmap) +{ + if (pathToBitmap == NULL) + return B_BAD_VALUE; + + status_t status = B_BAD_VALUE; + BBitmap* fileBitmap = NULL; + // try to load bitmap from either relative or absolute path + BEntry entry(pathToBitmap, true); + if (!entry.Exists()) { + app_info info; + status = be_app->GetAppInfo(&info); + if (status == B_OK) { + BEntry app_entry(&info.ref, true); + BPath path; + app_entry.GetPath(&path); + status = path.InitCheck(); + if (status == B_OK) { + status = path.GetParent(&path); + if (status == B_OK) { + status = path.Append(pathToBitmap, true); + if (status == B_OK) + fileBitmap = BTranslationUtils::GetBitmap(path.Path()); + else + printf("IconButton::SetIcon() - path.Append() failed: %s\n", strerror(status)); + } else + printf("IconButton::SetIcon() - path.GetParent() failed: %s\n", strerror(status)); + } else + printf("IconButton::SetIcon() - path.InitCheck() failed: %s\n", strerror(status)); + } else + printf("IconButton::SetIcon() - be_app->GetAppInfo() failed: %s\n", strerror(status)); + } else + fileBitmap = BTranslationUtils::GetBitmap(pathToBitmap); + if (fileBitmap) { + status = _MakeBitmaps(fileBitmap); + delete fileBitmap; + } else + status = B_ERROR; + return status; +} + +// SetIcon +status_t +IconButton::SetIcon(const BBitmap* bitmap) +{ + if (bitmap && bitmap->ColorSpace() == B_CMAP8) { + status_t status = bitmap->InitCheck(); + if (status >= B_OK) { + if (BBitmap* rgb32Bitmap = _ConvertToRGB32(bitmap)) { + status = _MakeBitmaps(rgb32Bitmap); + delete rgb32Bitmap; + } else + status = B_NO_MEMORY; + } + return status; + } else + return _MakeBitmaps(bitmap); +} + +// SetIcon +status_t +IconButton::SetIcon(const BMimeType* fileType, bool small) +{ + status_t status = fileType ? fileType->InitCheck() : B_BAD_VALUE; + if (status >= B_OK) { + BBitmap* mimeBitmap = new(nothrow) BBitmap(BRect(0.0, 0.0, 15.0, 15.0), B_CMAP8); + if (mimeBitmap && mimeBitmap->IsValid()) { + status = fileType->GetIcon(mimeBitmap, small ? B_MINI_ICON : B_LARGE_ICON); + if (status >= B_OK) { + if (BBitmap* bitmap = _ConvertToRGB32(mimeBitmap)) { + status = _MakeBitmaps(bitmap); + delete bitmap; + } else + printf("IconButton::SetIcon() - B_RGB32 bitmap is not valid\n"); + } else + printf("IconButton::SetIcon() - fileType->GetIcon() failed: %s\n", strerror(status)); + } else + printf("IconButton::SetIcon() - B_CMAP8 bitmap is not valid\n"); + delete mimeBitmap; + } else + printf("IconButton::SetIcon() - fileType is not valid: %s\n", strerror(status)); + return status; +} + +// SetIcon +status_t +IconButton::SetIcon(const unsigned char* bitsFromQuickRes, + uint32 width, uint32 height, color_space format, bool convertToBW) +{ + status_t status = B_BAD_VALUE; + if (bitsFromQuickRes && width > 0 && height > 0) { + BBitmap* quickResBitmap = new(nothrow) BBitmap(BRect(0.0, 0.0, width - 1.0, height - 1.0), format); + status = quickResBitmap ? quickResBitmap->InitCheck() : B_ERROR; + if (status >= B_OK) { + // It doesn't look right to copy BitsLength() bytes, but bitmaps + // exported from QuickRes still contain their padding, so it is alright. + memcpy(quickResBitmap->Bits(), bitsFromQuickRes, quickResBitmap->BitsLength()); + if (format != B_RGB32 && format != B_RGBA32 && format != B_RGB32_BIG && format != B_RGBA32_BIG) { + // colorspace needs conversion + BBitmap* bitmap = new(nothrow) BBitmap(quickResBitmap->Bounds(), B_RGB32, true); + if (bitmap && bitmap->IsValid()) { + BView* helper = new BView(bitmap->Bounds(), "helper", + B_FOLLOW_NONE, B_WILL_DRAW); + if (bitmap->Lock()) { + bitmap->AddChild(helper); + helper->SetHighColor(ui_color(B_PANEL_BACKGROUND_COLOR)); + helper->FillRect(helper->Bounds()); + helper->SetDrawingMode(B_OP_OVER); + helper->DrawBitmap(quickResBitmap, BPoint(0.0, 0.0)); + helper->Sync(); + bitmap->Unlock(); + } + status = _MakeBitmaps(bitmap); + } else + printf("IconButton::SetIcon() - B_RGB32 bitmap is not valid\n"); + delete bitmap; + } else { + // native colorspace (32 bits) + if (convertToBW) { + // convert to gray scale icon + uint8* bits = (uint8*)quickResBitmap->Bits(); + uint32 bpr = quickResBitmap->BytesPerRow(); + for (uint32 y = 0; y < height; y++) { + uint8* handle = bits; + uint8 gray; + for (uint32 x = 0; x < width; x++) { + gray = uint8((116 * handle[0] + 600 * handle[1] + 308 * handle[2]) / 1024); + handle[0] = gray; + handle[1] = gray; + handle[2] = gray; + handle += 4; + } + bits += bpr; + } + } + status = _MakeBitmaps(quickResBitmap); + } + } else + printf("IconButton::SetIcon() - error allocating bitmap: %s\n", strerror(status)); + delete quickResBitmap; + } + return status; +} + +// ClearIcon +void +IconButton::ClearIcon() +{ + _DeleteBitmaps(); + _Update(); +} + +void +IconButton::TrimIcon(bool keepAspect) +{ + if (fNormalBitmap == NULL) + return; + + uint8* bits = (uint8*)fNormalBitmap->Bits(); + uint32 bpr = fNormalBitmap->BytesPerRow(); + uint32 width = fNormalBitmap->Bounds().IntegerWidth() + 1; + uint32 height = fNormalBitmap->Bounds().IntegerHeight() + 1; + BRect trimmed(LONG_MAX, LONG_MAX, LONG_MIN, LONG_MIN); + for (uint32 y = 0; y < height; y++) { + uint8* b = bits + 3; + bool rowHasAlpha = false; + for (uint32 x = 0; x < width; x++) { + if (*b) { + rowHasAlpha = true; + if (x < trimmed.left) + trimmed.left = x; + if (x > trimmed.right) + trimmed.right = x; + } + b += 4; + } + if (rowHasAlpha) { + if (y < trimmed.top) + trimmed.top = y; + if (y > trimmed.bottom) + trimmed.bottom = y; + } + bits += bpr; + } + if (!trimmed.IsValid()) + return; + if (keepAspect) { + float minInset = trimmed.left; + minInset = min_c(minInset, trimmed.top); + minInset = min_c(minInset, fNormalBitmap->Bounds().right - trimmed.right); + minInset = min_c(minInset, fNormalBitmap->Bounds().bottom - trimmed.bottom); + trimmed = fNormalBitmap->Bounds().InsetByCopy(minInset, minInset); + } + trimmed = trimmed & fNormalBitmap->Bounds(); + BBitmap trimmedBitmap(trimmed.OffsetToCopy(B_ORIGIN), + B_BITMAP_NO_SERVER_LINK, B_RGBA32); + bits = (uint8*)fNormalBitmap->Bits(); + bits += 4 * (int32)trimmed.left + bpr * (int32)trimmed.top; + uint8* dst = (uint8*)trimmedBitmap.Bits(); + uint32 trimmedWidth = trimmedBitmap.Bounds().IntegerWidth() + 1; + uint32 trimmedHeight = trimmedBitmap.Bounds().IntegerHeight() + 1; + uint32 trimmedBPR = trimmedBitmap.BytesPerRow(); + for (uint32 y = 0; y < trimmedHeight; y++) { + memcpy(dst, bits, trimmedWidth * 4); + dst += trimmedBPR; + bits += bpr; + } + SetIcon(&trimmedBitmap); +} + +// Bitmap +BBitmap* +IconButton::Bitmap() const +{ + BBitmap* bitmap = NULL; + if (fNormalBitmap && fNormalBitmap->IsValid()) { + bitmap = new(nothrow) BBitmap(fNormalBitmap); + if (bitmap->IsValid()) { + // TODO: remove this functionality when we use real transparent bitmaps + uint8* bits = (uint8*)bitmap->Bits(); + uint32 bpr = bitmap->BytesPerRow(); + uint32 width = bitmap->Bounds().IntegerWidth() + 1; + uint32 height = bitmap->Bounds().IntegerHeight() + 1; + color_space format = bitmap->ColorSpace(); + if (format == B_CMAP8) { + // replace gray with magic transparent index + } else if (format == B_RGB32) { + for (uint32 y = 0; y < height; y++) { + uint8* bitsHandle = bits; + for (uint32 x = 0; x < width; x++) { + if (bitsHandle[0] == 216 + && bitsHandle[1] == 216 + && bitsHandle[2] == 216) { + bitsHandle[3] = 0; // make this pixel completely transparent + } + bitsHandle += 4; + } + bits += bpr; + } + } + } else { + delete bitmap; + bitmap = NULL; + } + } + return bitmap; +} + +// DrawBorder +bool +IconButton::DrawBorder() const +{ + return ((IsEnabled() && (_HasFlags(STATE_INSIDE) + || _HasFlags(STATE_TRACKING))) || _HasFlags(STATE_FORCE_PRESSED)); +} + +// DrawNormalBorder +void +IconButton::DrawNormalBorder(BRect r, rgb_color background, + rgb_color shadow, rgb_color darkShadow, + rgb_color lightShadow, rgb_color light) +{ + _DrawFrame(r, shadow, darkShadow, light, lightShadow); +} + +// DrawPressedBorder +void +IconButton::DrawPressedBorder(BRect r, rgb_color background, + rgb_color shadow, rgb_color darkShadow, + rgb_color lightShadow, rgb_color light) +{ + _DrawFrame(r, shadow, light, darkShadow, background); +} + +// IsValid +bool +IconButton::IsValid() const +{ + return (fNormalBitmap && fDisabledBitmap && fClickedBitmap + && fDisabledClickedBitmap + && fNormalBitmap->IsValid() + && fDisabledBitmap->IsValid() + && fClickedBitmap->IsValid() + && fDisabledClickedBitmap->IsValid()); +} + +// Value +int32 +IconButton::Value() const +{ + return _HasFlags(STATE_PRESSED) ? B_CONTROL_ON : B_CONTROL_OFF; +} + +// SetValue +void +IconButton::SetValue(int32 value) +{ + if (value) + _AddFlags(STATE_PRESSED); + else + _ClearFlags(STATE_PRESSED); +} + +// IsEnabled +bool +IconButton::IsEnabled() const +{ + return _HasFlags(STATE_ENABLED) ? B_CONTROL_ON : B_CONTROL_OFF; +} + +// SetEnabled +void +IconButton::SetEnabled(bool enabled) +{ + if (enabled) + _AddFlags(STATE_ENABLED); + else + _ClearFlags(STATE_ENABLED | STATE_TRACKING | STATE_INSIDE); +} + +// _ConvertToRGB32 +BBitmap* +IconButton::_ConvertToRGB32(const BBitmap* bitmap) const +{ + BBitmap* convertedBitmap = new(nothrow) BBitmap(bitmap->Bounds(), + B_BITMAP_ACCEPTS_VIEWS, B_RGBA32); + if (convertedBitmap && convertedBitmap->IsValid()) { + memset(convertedBitmap->Bits(), 0, convertedBitmap->BitsLength()); + BView* helper = new BView(bitmap->Bounds(), "helper", + B_FOLLOW_NONE, B_WILL_DRAW); + if (convertedBitmap->Lock()) { + convertedBitmap->AddChild(helper); + helper->SetDrawingMode(B_OP_OVER); + helper->DrawBitmap(bitmap, BPoint(0.0, 0.0)); + helper->Sync(); + convertedBitmap->Unlock(); + } + } else { + delete convertedBitmap; + convertedBitmap = NULL; + } + return convertedBitmap; +} + +// _MakeBitmaps +status_t +IconButton::_MakeBitmaps(const BBitmap* bitmap) +{ + status_t status = bitmap ? bitmap->InitCheck() : B_BAD_VALUE; + if (status >= B_OK) { + // make our own versions of the bitmap + BRect b(bitmap->Bounds()); + _DeleteBitmaps(); + color_space format = bitmap->ColorSpace(); + fNormalBitmap = new(nothrow) BBitmap(b, format); + fDisabledBitmap = new(nothrow) BBitmap(b, format); + fClickedBitmap = new(nothrow) BBitmap(b, format); + fDisabledClickedBitmap = new(nothrow) BBitmap(b, format); + if (IsValid()) { + // copy bitmaps from file bitmap + uint8* nBits = (uint8*)fNormalBitmap->Bits(); + uint8* dBits = (uint8*)fDisabledBitmap->Bits(); + uint8* cBits = (uint8*)fClickedBitmap->Bits(); + uint8* dcBits = (uint8*)fDisabledClickedBitmap->Bits(); + uint8* fBits = (uint8*)bitmap->Bits(); + int32 nbpr = fNormalBitmap->BytesPerRow(); + int32 fbpr = bitmap->BytesPerRow(); + int32 pixels = b.IntegerWidth() + 1; + int32 lines = b.IntegerHeight() + 1; + // nontransparent version: + if (format == B_RGB32 || format == B_RGB32_BIG) { + // iterate over color components + for (int32 y = 0; y < lines; y++) { + for (int32 x = 0; x < pixels; x++) { + int32 nOffset = 4 * x; + int32 fOffset = 4 * x; + nBits[nOffset + 0] = fBits[fOffset + 0]; + nBits[nOffset + 1] = fBits[fOffset + 1]; + nBits[nOffset + 2] = fBits[fOffset + 2]; + nBits[nOffset + 3] = 255; + // clicked bits are darker (lame method...) + cBits[nOffset + 0] = (uint8)((float)nBits[nOffset + 0] * 0.8); + cBits[nOffset + 1] = (uint8)((float)nBits[nOffset + 1] * 0.8); + cBits[nOffset + 2] = (uint8)((float)nBits[nOffset + 2] * 0.8); + cBits[nOffset + 3] = 255; + // disabled bits have less contrast (lame method...) + uint8 grey = 216; + float dist = (nBits[nOffset + 0] - grey) * 0.4; + dBits[nOffset + 0] = (uint8)(grey + dist); + dist = (nBits[nOffset + 1] - grey) * 0.4; + dBits[nOffset + 1] = (uint8)(grey + dist); + dist = (nBits[nOffset + 2] - grey) * 0.4; + dBits[nOffset + 2] = (uint8)(grey + dist); + dBits[nOffset + 3] = 255; + // disabled bits have less contrast (lame method...) + grey = 188; + dist = (nBits[nOffset + 0] - grey) * 0.4; + dcBits[nOffset + 0] = (uint8)(grey + dist); + dist = (nBits[nOffset + 1] - grey) * 0.4; + dcBits[nOffset + 1] = (uint8)(grey + dist); + dist = (nBits[nOffset + 2] - grey) * 0.4; + dcBits[nOffset + 2] = (uint8)(grey + dist); + dcBits[nOffset + 3] = 255; + } + nBits += nbpr; + dBits += nbpr; + cBits += nbpr; + dcBits += nbpr; + fBits += fbpr; + } + // transparent version: + } else if (format == B_RGBA32 || format == B_RGBA32_BIG) { + // iterate over color components + for (int32 y = 0; y < lines; y++) { + for (int32 x = 0; x < pixels; x++) { + int32 nOffset = 4 * x; + int32 fOffset = 4 * x; + nBits[nOffset + 0] = fBits[fOffset + 0]; + nBits[nOffset + 1] = fBits[fOffset + 1]; + nBits[nOffset + 2] = fBits[fOffset + 2]; + nBits[nOffset + 3] = fBits[fOffset + 3]; + // clicked bits are darker (lame method...) + cBits[nOffset + 0] = (uint8)(nBits[nOffset + 0] * 0.8); + cBits[nOffset + 1] = (uint8)(nBits[nOffset + 1] * 0.8); + cBits[nOffset + 2] = (uint8)(nBits[nOffset + 2] * 0.8); + cBits[nOffset + 3] = fBits[fOffset + 3]; + // disabled bits have less opacity + + uint8 grey = ((uint16)nBits[nOffset + 0] * 10 + + nBits[nOffset + 1] * 60 + + nBits[nOffset + 2] * 30) / 100; + float dist = (nBits[nOffset + 0] - grey) * 0.3; + dBits[nOffset + 0] = (uint8)(grey + dist); + dist = (nBits[nOffset + 1] - grey) * 0.3; + dBits[nOffset + 1] = (uint8)(grey + dist); + dist = (nBits[nOffset + 2] - grey) * 0.3; + dBits[nOffset + 2] = (uint8)(grey + dist); + dBits[nOffset + 3] = (uint8)(fBits[fOffset + 3] * 0.3); + // disabled bits have less contrast (lame method...) + dcBits[nOffset + 0] = (uint8)(dBits[nOffset + 0] * 0.8); + dcBits[nOffset + 1] = (uint8)(dBits[nOffset + 1] * 0.8); + dcBits[nOffset + 2] = (uint8)(dBits[nOffset + 2] * 0.8); + dcBits[nOffset + 3] = (uint8)(fBits[fOffset + 3] * 0.3); + } + nBits += nbpr; + dBits += nbpr; + cBits += nbpr; + dcBits += nbpr; + fBits += fbpr; + } + // unsupported format + } else { + printf("IconButton::_MakeBitmaps() - bitmap has unsupported colorspace\n"); + status = B_MISMATCHED_VALUES; + _DeleteBitmaps(); + } + } else { + printf("IconButton::_MakeBitmaps() - error allocating local bitmaps\n"); + status = B_NO_MEMORY; + _DeleteBitmaps(); + } + } else + printf("IconButton::_MakeBitmaps() - bitmap is not valid\n"); + return status; +} + +// _DeleteBitmaps +void +IconButton::_DeleteBitmaps() +{ + delete fNormalBitmap; + fNormalBitmap = NULL; + delete fDisabledBitmap; + fDisabledBitmap = NULL; + delete fClickedBitmap; + fClickedBitmap = NULL; + delete fDisabledClickedBitmap; + fDisabledClickedBitmap = NULL; +} + +// _Update +void +IconButton::_Update() +{ + if (LockLooper()) { + Invalidate(); + UnlockLooper(); + } +} + +// _AddFlags +void +IconButton::_AddFlags(uint32 flags) +{ + if (!(fButtonState & flags)) { + fButtonState |= flags; + _Update(); + } +} + +// _ClearFlags +void +IconButton::_ClearFlags(uint32 flags) +{ + if (fButtonState & flags) { + fButtonState &= ~flags; + _Update(); + } +} + +// _HasFlags +bool +IconButton::_HasFlags(uint32 flags) const +{ + return (fButtonState & flags); +} + +// _DrawFrame +void +IconButton::_DrawFrame(BRect r, rgb_color col1, rgb_color col2, + rgb_color col3, rgb_color col4) +{ + BeginLineArray(8); + AddLine(BPoint(r.left, r.bottom), BPoint(r.left, r.top), col1); + AddLine(BPoint(r.left + 1.0, r.top), BPoint(r.right, r.top), col1); + AddLine(BPoint(r.right, r.top + 1.0), BPoint(r.right, r.bottom), col2); + AddLine(BPoint(r.right - 1.0, r.bottom), BPoint(r.left + 1.0, r.bottom), col2); + r.InsetBy(1.0, 1.0); + AddLine(BPoint(r.left, r.bottom), BPoint(r.left, r.top), col3); + AddLine(BPoint(r.left + 1.0, r.top), BPoint(r.right, r.top), col3); + AddLine(BPoint(r.right, r.top + 1.0), BPoint(r.right, r.bottom), col4); + AddLine(BPoint(r.right - 1.0, r.bottom), BPoint(r.left + 1.0, r.bottom), col4); + EndLineArray(); +} diff --git a/src/apps/webpositive/support/IconButton.h b/src/apps/webpositive/support/IconButton.h new file mode 100644 index 0000000000..2876dd8cfd --- /dev/null +++ b/src/apps/webpositive/support/IconButton.h @@ -0,0 +1,134 @@ +/* + * Copyright 2006-2010, Haiku. + * Distributed under the terms of the MIT License. + * + * Authors: + * Stephan Aßmus + */ + +/** gui class that loads an image from disk and shows it + as clickable button */ + +// TODO: inherit from BControl? + +// NOTE: this file is a duplicate of the version in Icon-O-Matic/generic +// it should be placed into a common folder for generic useful stuff + +#ifndef ICON_BUTTON_H +#define ICON_BUTTON_H + +#include +#include +#include + +class BBitmap; +class BMimeType; + +class IconButton : public BView, public BInvoker { +public: + IconButton(const char* name, + uint32 id, + const char* label = NULL, + BMessage* message = NULL, + BHandler* target = NULL); + virtual ~IconButton(); + + // BView interface + virtual void MessageReceived(BMessage* message); + virtual void AttachedToWindow(); + virtual void Draw(BRect updateRect); + virtual void MouseDown(BPoint where); + virtual void MouseUp(BPoint where); + virtual void MouseMoved(BPoint where, uint32 transit, + const BMessage* message); + virtual void GetPreferredSize(float* width, + float* height); + virtual BSize MinSize(); + virtual BSize MaxSize(); + + + // BInvoker interface + virtual status_t Invoke(BMessage* message = NULL); + + // IconButton + bool IsValid() const; + + virtual int32 Value() const; + virtual void SetValue(int32 value); + + bool IsEnabled() const; + void SetEnabled(bool enable); + + void SetPressed(bool pressed); + bool IsPressed() const; + uint32 ID() const + { return fID; } + + status_t SetIcon(int32 resourceID); + status_t SetIcon(const char* pathToBitmap); + status_t SetIcon(const BBitmap* bitmap); + status_t SetIcon(const BMimeType* fileType, + bool small = true); + status_t SetIcon(const unsigned char* bitsFromQuickRes, + uint32 width, uint32 height, + color_space format, + bool convertToBW = false); + void ClearIcon(); + void TrimIcon(bool keepAspect = true); + + BBitmap* Bitmap() const; + // caller has to delete the returned bitmap + + virtual bool DrawBorder() const; + virtual void DrawNormalBorder(BRect r, + rgb_color background, + rgb_color shadow, + rgb_color darkShadow, + rgb_color lightShadow, + rgb_color light); + virtual void DrawPressedBorder(BRect r, + rgb_color background, + rgb_color shadow, + rgb_color darkShadow, + rgb_color lightShadow, + rgb_color light); + +protected: + enum { + STATE_NONE = 0x0000, + STATE_TRACKING = 0x0001, + STATE_PRESSED = 0x0002, + STATE_ENABLED = 0x0004, + STATE_INSIDE = 0x0008, + STATE_FORCE_PRESSED = 0x0010, + }; + + void _AddFlags(uint32 flags); + void _ClearFlags(uint32 flags); + bool _HasFlags(uint32 flags) const; + + void _DrawFrame(BRect frame, + rgb_color col1, + rgb_color col2, + rgb_color col3, + rgb_color col4); + +// private: + BBitmap* _ConvertToRGB32(const BBitmap* bitmap) const; + status_t _MakeBitmaps(const BBitmap* bitmap); + void _DeleteBitmaps(); + void _SendMessage() const; + void _Update(); + + uint32 fButtonState; + int32 fID; + BBitmap* fNormalBitmap; + BBitmap* fDisabledBitmap; + BBitmap* fClickedBitmap; + BBitmap* fDisabledClickedBitmap; + BString fLabel; + + BHandler* fTargetCache; +}; + +#endif // ICON_BUTTON_H diff --git a/src/apps/webpositive/support/IconUtils.h b/src/apps/webpositive/support/IconUtils.h new file mode 100644 index 0000000000..fdcfbd946f --- /dev/null +++ b/src/apps/webpositive/support/IconUtils.h @@ -0,0 +1,80 @@ +/* + * Copyright 2006-2008, Haiku. All rights reserved. + * Distributed under the terms of the MIT License. + */ +#ifndef _ICON_UTILS_H +#define _ICON_UTILS_H + + +#include + +class BBitmap; +class BNode; + + +// This class is a little different from many other classes. +// You don't create an instance of it; you just call its various +// static member functions for utility-like operations. +class BIconUtils { + BIconUtils(); + ~BIconUtils(); + BIconUtils(const BIconUtils&); + BIconUtils& operator=(const BIconUtils&); + +public: + + // Utility function to import an icon from the node that + // has either of the provided attribute names. Which icon type + // is preferred (vector, small or large B_CMAP8 icon) depends + // on the colorspace of the provided bitmap. If the colorspace + // is B_CMAP8, B_CMAP8 icons are preferred. In that case, the + // bitmap size must also match the provided icon_size "size"! + static status_t GetIcon(BNode* node, + const char* vectorIconAttrName, + const char* smallIconAttrName, + const char* largeIconAttrName, + icon_size size, BBitmap* result); + + // Utility functions to import a vector icon in "flat icon" + // format from a BNode attribute or from a flat buffer in + // memory into the preallocated BBitmap "result". + // The colorspace of result needs to be B_RGBA32 or at + // least B_RGB32 (though that makes less sense). The icon + // will be scaled from it's "native" size of 64x64 to the + // size of the bitmap, the scale is derived from the bitmap + // width, the bitmap should have square dimension, or the + // icon will be cut off at the bottom (or have room left). + static status_t GetVectorIcon(BNode* node, + const char* attrName, BBitmap* result); + + static status_t GetVectorIcon(const uint8* buffer, + size_t size, BBitmap* result); + + // Utility function to import an "old" BeOS icon in B_CMAP8 + // colorspace from either the small icon attribute or the + // large icon attribute as given in "smallIconAttrName" and + // "largeIconAttrName". Which icon is loaded depends on + // the given "size". + static status_t GetCMAP8Icon(BNode* node, + const char* smallIconAttrName, + const char* largeIconAttrName, + icon_size size, BBitmap* icon); + + // Utility functions to convert from old icon colorspace + // into colorspace of BBitmap "result" (should be B_RGBA32 + // to make any sense). + static status_t ConvertFromCMAP8(BBitmap* source, + BBitmap* result); + static status_t ConvertToCMAP8(BBitmap* source, + BBitmap* result); + + static status_t ConvertFromCMAP8(const uint8* data, + uint32 width, uint32 height, + uint32 bytesPerRow, BBitmap* result); + + static status_t ConvertToCMAP8(const uint8* data, + uint32 width, uint32 height, + uint32 bytesPerRow, BBitmap* result); +}; + +#endif // _ICON_UTILS_H diff --git a/src/apps/webpositive/support/NavMenu.h b/src/apps/webpositive/support/NavMenu.h new file mode 100644 index 0000000000..127d94f255 --- /dev/null +++ b/src/apps/webpositive/support/NavMenu.h @@ -0,0 +1,168 @@ +/* +Open Tracker License + +Terms and Conditions + +Copyright (c) 1991-2000, Be Incorporated. All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice applies to all licensees +and shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF TITLE, MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +BE INCORPORATED BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF, OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of Be Incorporated shall not be +used in advertising or otherwise to promote the sale, use or other dealings in +this Software without prior written authorization from Be Incorporated. + +Tracker(TM), Be(R), BeOS(R), and BeIA(TM) are trademarks or registered trademarks +of Be Incorporated in the United States and other countries. Other brand product +names are registered trademarks or trademarks of their respective holders. +All rights reserved. +*/ + +// NavMenu is a hierarchical menu of volumes, folders, files and queries +// displays icons, uses the SlowMenu API for full interruptability + +#ifndef NAV_MENU_H +#define NAV_MENU_H + + +#include +#include +#include + +#include "SlowMenu.h" + + +template class BObjectList; +class BMenuItem; + +namespace BPrivate { + +class Model; +class BContainerWindow; +class ModelMenuItem; +class EntryListBase; + + +class TrackingHookData { + public: + TrackingHookData() + : + fTrackingHook(NULL), + fDragMessage(NULL) + { + } + + bool (*fTrackingHook)(BMenu *, void *); + BMessenger fTarget; + const BMessage *fDragMessage; +}; + + +class BNavMenu : public BSlowMenu { + public: + BNavMenu(const char* title, uint32 message, const BHandler *, + BWindow *parentWindow = NULL, const BObjectList *list = NULL); + BNavMenu(const char* title, uint32 message, const BMessenger &, + BWindow *parentWindow = NULL, const BObjectList *list = NULL); + // parentWindow, if specified, will be closed if nav menu item invoked + // with option held down + + virtual ~BNavMenu(); + + virtual void AttachedToWindow(); + virtual void DetachedFromWindow(); + + void SetNavDir(const entry_ref *); + void ForceRebuild(); + bool NeedsToRebuild() const; + // will cause menu to get rebuilt next time it is shown + + virtual void ResetTargets(); + void SetTarget(const BMessenger &); + BMessenger Target(); + + void SetTypesList(const BObjectList *list); + const BObjectList *TypesList() const; + + void AddNavDir(const Model *model, uint32 what, BHandler *target, + bool populateSubmenu); + + void AddNavParentDir(const char *name, const Model *model, uint32 what, BHandler *target); + void AddNavParentDir(const Model *model, uint32 what, BHandler *target); + void SetShowParent(bool show); + + static int32 GetMaxMenuWidth(); + + static int CompareFolderNamesFirstOne(const BMenuItem *, const BMenuItem *); + static int CompareOne(const BMenuItem *, const BMenuItem *); + + static ModelMenuItem *NewModelItem(Model *, const BMessage *, const BMessenger &, + bool suppressFolderHierarchy=false, BContainerWindow * = NULL, + const BObjectList *typeslist = NULL, + TrackingHookData *hook = NULL); + + TrackingHookData *InitTrackingHook(bool (*hookfunction)(BMenu *, void *), + const BMessenger *target, const BMessage *dragMessage); + + protected: + virtual bool StartBuildingItemList(); + virtual bool AddNextItem(); + virtual void DoneBuildingItemList(); + virtual void ClearMenuBuildingState(); + + void BuildVolumeMenu(); + + void AddOneItem(Model *); + void AddRootItemsIfNeeded(); + void AddTrashItem(); + static void SetTrackingHookDeep(BMenu *, bool (*)(BMenu *, void *), void *); + + entry_ref fNavDir; + BMessage fMessage; + BMessenger fMessenger; + BWindow *fParentWindow; + + // menu building state + uint8 fFlags; + BObjectList *fItemList; + EntryListBase *fContainer; + bool fIteratingDesktop; + + const BObjectList *fTypesList; + + TrackingHookData fTrackingHook; +}; + +// Spring Loaded Folder convenience routines +// used in both Tracker and Deskbar +#ifndef _IMPEXP_TRACKER +# define _IMPEXP_TRACKER +#endif +_IMPEXP_TRACKER bool SpringLoadedFolderCompareMessages(const BMessage *incoming, + const BMessage *dragmessage); +_IMPEXP_TRACKER void SpringLoadedFolderSetMenuStates(const BMenu *menu, + const BObjectList *typeslist); +_IMPEXP_TRACKER void SpringLoadedFolderAddUniqueTypeToList(entry_ref *ref, + BObjectList *typeslist); +_IMPEXP_TRACKER void SpringLoadedFolderCacheDragData(const BMessage *incoming, + BMessage **, BObjectList **typeslist); + +} // namespace BPrivate + +using namespace BPrivate; + +#endif // NAV_MENU_H diff --git a/src/apps/webpositive/support/OpenHashTable.h b/src/apps/webpositive/support/OpenHashTable.h new file mode 100644 index 0000000000..437fbed170 --- /dev/null +++ b/src/apps/webpositive/support/OpenHashTable.h @@ -0,0 +1,514 @@ +/* +Open Tracker License + +Terms and Conditions + +Copyright (c) 1991-2000, Be Incorporated. All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice applies to all licensees +and shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF TITLE, MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +BE INCORPORATED BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF, OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of Be Incorporated shall not be +used in advertising or otherwise to promote the sale, use or other dealings in +this Software without prior written authorization from Be Incorporated. + +Tracker(TM), Be(R), BeOS(R), and BeIA(TM) are trademarks or registered trademarks +of Be Incorporated in the United States and other countries. Other brand product +names are registered trademarks or trademarks of their respective holders. +All rights reserved. +*/ + +// bonefish: +// * removed need for exceptions +// * fixed warnings +// * implemented rehashing +// * added RemoveAll() +// TODO: +// * shrinking of element vectors + +// Hash table with open addresssing + +#ifndef __OPEN_HASH_TABLE__ +#define __OPEN_HASH_TABLE__ + +#include +#include + +// don't include +#ifndef ASSERT +# define ASSERT(E) (void)0 +#endif +#ifndef TRESPASS +# define TRESPASS() (void)0 +#endif + +namespace BPrivate { + +template +class ElementVector { + // element vector for OpenHashTable needs to implement this + // interface +public: + Element &At(int32 index); + Element *Add(); + int32 IndexOf(const Element &) const; + void Remove(int32 index); +}; + +class OpenHashElement { +public: + uint32 Hash() const; + bool operator==(const OpenHashElement &) const; + void Adopt(OpenHashElement &); + // low overhead copy, original element is in undefined state + // after call (calls Adopt on BString members, etc.) + int32 fNext; +}; + +const uint32 kPrimes [] = { + 509, 1021, 2039, 4093, 8191, 16381, 32749, 65521, 131071, 262139, + 524287, 1048573, 2097143, 4194301, 8388593, 16777213, 33554393, 67108859, + 134217689, 268435399, 536870909, 1073741789, 2147483647, 0 +}; + +template > +class OpenHashTable { +public: + OpenHashTable(int32 minSize, ElementVec *elementVector = 0, + float maxLoadFactor = 0.8); + // it is up to the subclass of OpenHashTable to supply + // elementVector + ~OpenHashTable(); + + bool InitCheck() const; + + void SetElementVector(ElementVec *elementVector); + + Element *FindFirst(uint32 elementHash) const; + Element *Add(uint32 elementHash); + + void Remove(Element *element, bool dontRehash = false); + void RemoveAll(); + + // when calling Add, any outstanding element pointer may become + // invalid; to deal with this, get the element index and restore + // it after the add + int32 ElementIndex(const Element *) const; + Element *ElementAt(int32 index) const; + + int32 ArraySize() const; + int32 VectorSize() const; + int32 CountElements() const; + +protected: + static int32 OptimalSize(int32 minSize); + +private: + bool _RehashIfNeeded(); + bool _Rehash(); + + int32 fArraySize; + int32 fInitialSize; + int32 fElementCount; + int32 *fHashArray; + ElementVec *fElementVector; + float fMaxLoadFactor; +}; + +template +class OpenHashElementArray : public ElementVector { + // this is a straightforward implementation of an element vector + // deleting is handled by linking deleted elements into a free list + // the vector never shrinks +public: + OpenHashElementArray(int32 initialSize); + ~OpenHashElementArray(); + + bool InitCheck() const; + + Element &At(int32 index); + const Element &At(int32 index) const; + Element *Add(const Element &); + Element *Add(); + void Remove(int32 index); + int32 IndexOf(const Element &) const; + int32 Size() const; + +private: + Element *fData; + int32 fSize; + int32 fNextFree; + int32 fNextDeleted; +}; + + +//----------------------------------- + +template +OpenHashTable::OpenHashTable(int32 minSize, + ElementVec *elementVector, float maxLoadFactor) + : fArraySize(OptimalSize(minSize)), + fInitialSize(fArraySize), + fElementCount(0), + fElementVector(elementVector), + fMaxLoadFactor(maxLoadFactor) +{ + // sanity check the maximal load factor + if (fMaxLoadFactor < 0.5) + fMaxLoadFactor = 0.5; + // allocate and init the array + fHashArray = (int32*)calloc(fArraySize, sizeof(int32)); + if (fHashArray) { + for (int32 index = 0; index < fArraySize; index++) + fHashArray[index] = -1; + } +} + +template +OpenHashTable::~OpenHashTable() +{ + RemoveAll(); + free(fHashArray); +} + +template +bool +OpenHashTable::InitCheck() const +{ + return (fHashArray && fElementVector); +} + +template +int32 +OpenHashTable::OptimalSize(int32 minSize) +{ + for (int32 index = 0; ; index++) + if (!kPrimes[index] || kPrimes[index] >= (uint32)minSize) + return (int32)kPrimes[index]; + + return 0; +} + +template +Element * +OpenHashTable::FindFirst(uint32 hash) const +{ + ASSERT(fElementVector); + hash %= fArraySize; + if (fHashArray[hash] < 0) + return 0; + + return &fElementVector->At(fHashArray[hash]); +} + +template +int32 +OpenHashTable::ElementIndex(const Element *element) const +{ + return fElementVector->IndexOf(*element); +} + +template +Element * +OpenHashTable::ElementAt(int32 index) const +{ + return &fElementVector->At(index); +} + +template +int32 +OpenHashTable::ArraySize() const +{ + return fArraySize; +} + +template +int32 +OpenHashTable::VectorSize() const +{ + return fElementVector->Size(); +} + +template +int32 +OpenHashTable::CountElements() const +{ + return fElementCount; +} + + +template +Element * +OpenHashTable::Add(uint32 hash) +{ + ASSERT(fElementVector); + _RehashIfNeeded(); + hash %= fArraySize; + Element *result = fElementVector->Add(); + if (result) { + result->fNext = fHashArray[hash]; + fHashArray[hash] = fElementVector->IndexOf(*result); + fElementCount++; + } + return result; +} + +template +void +OpenHashTable::Remove(Element *element, bool dontRehash) +{ + if (!dontRehash) + _RehashIfNeeded(); + uint32 hash = element->Hash() % fArraySize; + int32 next = fHashArray[hash]; + ASSERT(next >= 0); + + if (&fElementVector->At(next) == element) { + fHashArray[hash] = element->fNext; + fElementVector->Remove(next); + fElementCount--; + return; + } + + for (int32 index = next; index >= 0; ) { + // look for an existing match in table + next = fElementVector->At(index).fNext; + if (next < 0) { + TRESPASS(); + return; + } + + if (&fElementVector->At(next) == element) { + fElementVector->At(index).fNext = element->fNext; + fElementVector->Remove(next); + fElementCount--; + return; + } + index = next; + } +} + +template +void +OpenHashTable::RemoveAll() +{ + for (int32 i = 0; fElementCount > 0 && i < fArraySize; i++) { + int32 index = fHashArray[i]; + while (index >= 0) { + Element* element = &fElementVector->At(index); + int32 next = element->fNext; + fElementVector->Remove(index); + fElementCount--; + index = next; + } + fHashArray[i] = -1; + } + _RehashIfNeeded(); +} + +template +void +OpenHashTable::SetElementVector(ElementVec *elementVector) +{ + fElementVector = elementVector; +} + +// _RehashIfNeeded +template +bool +OpenHashTable::_RehashIfNeeded() +{ + // The load factor range [fMaxLoadFactor / 3, fMaxLoadFactor] is fine, + // I think. After rehashing the load factor will be about + // fMaxLoadFactor * 2 / 3, respectively fMaxLoadFactor / 2. + float loadFactor = (float)fElementCount / (float)fArraySize; + if (loadFactor > fMaxLoadFactor + || (fArraySize > fInitialSize && loadFactor < fMaxLoadFactor / 3)) { + return _Rehash(); + } + return true; +} + +// _Rehash +template +bool +OpenHashTable::_Rehash() +{ + bool result = true; + int32 newSize = int32(fElementCount * 1.73 * fMaxLoadFactor); + newSize = (fInitialSize > newSize ? fInitialSize : newSize); + if (newSize != fArraySize) { + // allocate a new array + int32 *newHashArray = (int32*)calloc(newSize, sizeof(int32)); + if (newHashArray) { + // init the new hash array + for (int32 index = 0; index < newSize; index++) + newHashArray[index] = -1; + // iterate through all elements and put them into the new + // hash array + for (int i = 0; i < fArraySize; i++) { + int32 index = fHashArray[i]; + while (index >= 0) { + // insert the element in the new array + Element &element = fElementVector->At(index); + int32 next = element.fNext; + uint32 hash = (element.Hash() % newSize); + element.fNext = newHashArray[hash]; + newHashArray[hash] = index; + // next element in old list + index = next; + } + } + // delete the old array and set the new one + free(fHashArray); + fHashArray = newHashArray; + fArraySize = newSize; + } else + result = false; + } + return result; +} + + +template +OpenHashElementArray::OpenHashElementArray(int32 initialSize) + : fSize(initialSize), + fNextFree(0), + fNextDeleted(-1) +{ + fData = (Element*)calloc((size_t)initialSize, sizeof(Element)); +} + +template +OpenHashElementArray::~OpenHashElementArray() +{ + free(fData); +} + +template +bool +OpenHashElementArray::InitCheck() const +{ + return fData; +} + +template +Element & +OpenHashElementArray::At(int32 index) +{ + ASSERT(index < fSize); + return fData[index]; +} + +template +const Element & +OpenHashElementArray::At(int32 index) const +{ + ASSERT(index < fSize); + return fData[index]; +} + +template +int32 +OpenHashElementArray::IndexOf(const Element &element) const +{ + int32 result = &element - fData; + if (result < 0 || result > fSize) + return -1; + + return result; +} + +template +int32 +OpenHashElementArray::Size() const +{ + return fSize; +} + + +template +Element * +OpenHashElementArray::Add(const Element &newElement) +{ + Element *element = Add(); + if (element) + element.Adopt(newElement); + return element; +} + +#if DEBUG +const int32 kGrowChunk = 10; +#else +const int32 kGrowChunk = 1024; +#endif + +template +Element * +OpenHashElementArray::Add() +{ + int32 index = fNextFree; + if (fNextDeleted >= 0) { + index = fNextDeleted; + fNextDeleted = At(index).fNext; + } else if (fNextFree >= fSize - 1) { + int32 newSize = fSize + kGrowChunk; +/* + Element *newData = (Element *)calloc((size_t)newSize , sizeof(Element)); + if (!newData) + return NULL; + memcpy(newData, fData, fSize * sizeof(Element)); + free(fData); +*/ + Element *newData = (Element*)realloc(fData, + (size_t)newSize * sizeof(Element)); + if (!newData) + return NULL; + + fData = newData; + fSize = newSize; + index = fNextFree; + fNextFree++; + } else + fNextFree++; + + new (&At(index)) Element; + // call placement new to initialize the element properly + ASSERT(At(index).fNext == -1); + + return &At(index); +} + +template +void +OpenHashElementArray::Remove(int32 index) +{ + // delete by chaining empty elements in a single linked + // list, reusing the next field + ASSERT(index < fSize); + At(index).~Element(); + // call the destructor explicitly to destroy the element + // properly + At(index).fNext = fNextDeleted; + fNextDeleted = index; +} + +} // namespace BPrivate + +using BPrivate::OpenHashTable; + +#endif // __OPEN_HASH_TABLE__ diff --git a/src/apps/webpositive/support/SettingsMessage.cpp b/src/apps/webpositive/support/SettingsMessage.cpp new file mode 100644 index 0000000000..15dc12394c --- /dev/null +++ b/src/apps/webpositive/support/SettingsMessage.cpp @@ -0,0 +1,514 @@ +/* + * Copyright 2008-2010, Stephan Aßmus . + * Copyright 1998, Eric Shepherd. + * All rights reserved. Distributed under the terms of the Be Sample Code + * license. + */ + +//! Be Newsletter Volume II, Issue 35; September 2, 1998 (Eric Shepherd) + +#include "SettingsMessage.h" + +#include + +#include +#include +#include +#include +#include + + +SettingsMessage::SettingsMessage(directory_which directory, + const char* filename) + : + BMessage('pref'), + fListeners(4) +{ + fStatus = find_directory(directory, &fPath); + + if (fStatus == B_OK) + fStatus = fPath.Append(filename); + + if (fStatus == B_OK) + fStatus = Load(); +} + + +SettingsMessage::~SettingsMessage() +{ + Save(); + + for (int32 i = fListeners.CountItems() - 1; i >= 0; i--) + delete reinterpret_cast(fListeners.ItemAtFast(i)); +} + + +status_t +SettingsMessage::InitCheck() const +{ + return fStatus; +} + + +status_t +SettingsMessage::Load() +{ + BAutolock _(this); + + BFile file(fPath.Path(), B_READ_ONLY); + status_t status = file.InitCheck(); + + if (status == B_OK) + status = Unflatten(&file); + + return status; +} + + +status_t +SettingsMessage::Save() const +{ + BAutolock _(const_cast(this)); + + BFile file(fPath.Path(), B_WRITE_ONLY | B_CREATE_FILE | B_ERASE_FILE); + status_t status = file.InitCheck(); + + if (status == B_OK) + status = Flatten(&file); + + return status; +} + + +bool +SettingsMessage::AddListener(const BMessenger& listener) +{ + BAutolock _(this); + + BMessenger* listenerCopy = new(std::nothrow) BMessenger(listener); + if (listenerCopy && fListeners.AddItem(listenerCopy)) + return true; + delete listenerCopy; + return false; +} + + +void +SettingsMessage::RemoveListener(const BMessenger& listener) +{ + BAutolock _(this); + + for (int32 i = fListeners.CountItems() - 1; i >= 0; i--) { + BMessenger* listenerItem = reinterpret_cast( + fListeners.ItemAtFast(i)); + if (*listenerItem == listener) { + fListeners.RemoveItem(i); + delete listenerItem; + return; + } + } +} + + +// #pragma mark - + + +status_t +SettingsMessage::SetValue(const char* name, bool value) +{ + status_t ret = ReplaceBool(name, value); + if (ret != B_OK) + ret = AddBool(name, value); + if (ret == B_OK) + _NotifyValueChanged(name); + return ret; +} + + +status_t +SettingsMessage::SetValue(const char* name, int8 value) +{ + status_t ret = ReplaceInt8(name, value); + if (ret != B_OK) + ret = AddInt8(name, value); + if (ret == B_OK) + _NotifyValueChanged(name); + return ret; +} + + +status_t +SettingsMessage::SetValue(const char* name, int16 value) +{ + status_t ret = ReplaceInt16(name, value); + if (ret != B_OK) + ret = AddInt16(name, value); + if (ret == B_OK) + _NotifyValueChanged(name); + return ret; +} + + +status_t +SettingsMessage::SetValue(const char* name, int32 value) +{ + status_t ret = ReplaceInt32(name, value); + if (ret != B_OK) + ret = AddInt32(name, value); + if (ret == B_OK) + _NotifyValueChanged(name); + return ret; +} + + +status_t +SettingsMessage::SetValue(const char* name, uint32 value) +{ + status_t ret = ReplaceUInt32(name, value); + if (ret != B_OK) + ret = AddUInt32(name, value); + if (ret == B_OK) + _NotifyValueChanged(name); + return ret; +} + + +status_t +SettingsMessage::SetValue(const char* name, int64 value) +{ + status_t ret = ReplaceInt64(name, value); + if (ret != B_OK) + ret = AddInt64(name, value); + if (ret == B_OK) + _NotifyValueChanged(name); + return ret; +} + + +status_t +SettingsMessage::SetValue(const char* name, float value) +{ + status_t ret = ReplaceFloat(name, value); + if (ret != B_OK) + ret = AddFloat(name, value); + if (ret == B_OK) + _NotifyValueChanged(name); + return ret; +} + + +status_t +SettingsMessage::SetValue(const char* name, double value) +{ + status_t ret = ReplaceDouble(name, value); + if (ret != B_OK) + ret = AddDouble(name, value); + if (ret == B_OK) + _NotifyValueChanged(name); + return ret; +} + + +status_t +SettingsMessage::SetValue(const char* name, const char* value) +{ + status_t ret = ReplaceString(name, value); + if (ret != B_OK) + ret = AddString(name, value); + if (ret == B_OK) + _NotifyValueChanged(name); + return ret; +} + + +status_t +SettingsMessage::SetValue(const char* name, const BString& value) +{ + status_t ret = ReplaceString(name, value); + if (ret != B_OK) + ret = AddString(name, value); + if (ret == B_OK) + _NotifyValueChanged(name); + return ret; +} + + +status_t +SettingsMessage::SetValue(const char* name, const BPoint& value) +{ + status_t ret = ReplacePoint(name, value); + if (ret != B_OK) + ret = AddPoint(name, value); + if (ret == B_OK) + _NotifyValueChanged(name); + return ret; +} + + +status_t +SettingsMessage::SetValue(const char* name, const BRect& value) +{ + status_t ret = ReplaceRect(name, value); + if (ret != B_OK) + ret = AddRect(name, value); + if (ret == B_OK) + _NotifyValueChanged(name); + return ret; +} + + +status_t +SettingsMessage::SetValue(const char* name, const entry_ref& value) +{ + status_t ret = ReplaceRef(name, &value); + if (ret != B_OK) + ret = AddRef(name, &value); + if (ret == B_OK) + _NotifyValueChanged(name); + return ret; +} + + +status_t +SettingsMessage::SetValue(const char* name, const BMessage& value) +{ + status_t ret = ReplaceMessage(name, &value); + if (ret != B_OK) + ret = AddMessage(name, &value); + if (ret == B_OK) + _NotifyValueChanged(name); + return ret; +} + + +status_t +SettingsMessage::SetValue(const char* name, const BFlattenable* value) +{ + status_t ret = ReplaceFlat(name, const_cast(value)); + if (ret != B_OK) + ret = AddFlat(name, const_cast(value)); + if (ret == B_OK) + _NotifyValueChanged(name); + return ret; +} + + +status_t +SettingsMessage::SetValue(const char* name, const BFont& value) +{ + font_family family; + font_style style; + value.GetFamilyAndStyle(&family, &style); + + BMessage fontMessage; + status_t ret = fontMessage.AddString("family", family); + if (ret == B_OK) + ret = fontMessage.AddString("style", style); + if (ret == B_OK) + ret = fontMessage.AddFloat("size", value.Size()); + + if (ret == B_OK) { + if (ReplaceMessage(name, &fontMessage) != B_OK) + ret = AddMessage(name, &fontMessage); + } + if (ret == B_OK) + _NotifyValueChanged(name); + return ret; +} + + +// #pragma mark - + + +bool +SettingsMessage::GetValue(const char* name, bool defaultValue) const +{ + bool value; + if (FindBool(name, &value) != B_OK) + return defaultValue; + return value; +} + + +int8 +SettingsMessage::GetValue(const char* name, int8 defaultValue) const +{ + int8 value; + if (FindInt8(name, &value) != B_OK) + return defaultValue; + return value; +} + + +int16 +SettingsMessage::GetValue(const char* name, int16 defaultValue) const +{ + int16 value; + if (FindInt16(name, &value) != B_OK) + return defaultValue; + return value; +} + + +int32 +SettingsMessage::GetValue(const char* name, int32 defaultValue) const +{ + int32 value; + if (FindInt32(name, &value) != B_OK) + return defaultValue; + return value; +} + + +uint32 +SettingsMessage::GetValue(const char* name, uint32 defaultValue) const +{ + uint32 value; + if (FindUInt32(name, &value) != B_OK) + return defaultValue; + return value; +} + + +int64 +SettingsMessage::GetValue(const char* name, int64 defaultValue) const +{ + int64 value; + if (FindInt64(name, &value) != B_OK) + return defaultValue; + return value; +} + + +float +SettingsMessage::GetValue(const char* name, float defaultValue) const +{ + float value; + if (FindFloat(name, &value) != B_OK) + return defaultValue; + return value; +} + + +double +SettingsMessage::GetValue(const char* name, double defaultValue) const +{ + double value; + if (FindDouble(name, &value) != B_OK) + return defaultValue; + return value; +} + + +BString +SettingsMessage::GetValue(const char* name, const BString& defaultValue) const +{ + BString value; + if (FindString(name, &value) != B_OK) + return defaultValue; + return value; +} + + +const char* +SettingsMessage::GetValue(const char* name, const char* defaultValue) const +{ + const char* value; + if (FindString(name, &value) != B_OK) + return defaultValue; + return value; +} + + +BPoint +SettingsMessage::GetValue(const char *name, BPoint defaultValue) const +{ + BPoint value; + if (FindPoint(name, &value) != B_OK) + return defaultValue; + return value; +} + + +BRect +SettingsMessage::GetValue(const char* name, BRect defaultValue) const +{ + BRect value; + if (FindRect(name, &value) != B_OK) + return defaultValue; + return value; +} + + +entry_ref +SettingsMessage::GetValue(const char* name, const entry_ref& defaultValue) const +{ + entry_ref value; + if (FindRef(name, &value) != B_OK) + return defaultValue; + return value; +} + + +BMessage +SettingsMessage::GetValue(const char* name, const BMessage& defaultValue) const +{ + BMessage value; + if (FindMessage(name, &value) != B_OK) + return defaultValue; + return value; +} + + +BFont +SettingsMessage::GetValue(const char* name, const BFont& defaultValue) const +{ + BMessage fontMessage; + if (FindMessage(name, &fontMessage) != B_OK) + return defaultValue; + + const char* family; + const char* style; + float size; + if (fontMessage.FindString("family", &family) != B_OK + || fontMessage.FindString("style", &style) != B_OK + || fontMessage.FindFloat("size", &size) != B_OK) { + return defaultValue; + } + + BFont value; + if (value.SetFamilyAndStyle(family, style) != B_OK) + return defaultValue; + + value.SetSize(size); + + return value; +} + + +// #pragma mark - private + + +void +SettingsMessage::_NotifyValueChanged(const char* name) const +{ + BMessage message(SETTINGS_VALUE_CHANGED); + message.AddString("name", name); + + // Add the value of that name to the notification. + type_code type; + if (GetInfo(name, &type) == B_OK) { + const void* data; + ssize_t numBytes; + if (FindData(name, type, &data, &numBytes) == B_OK) + message.AddData("value", type, data, numBytes); + } + + int32 count = fListeners.CountItems(); + for (int32 i = 0; i < count; i++) { + BMessenger* listener = reinterpret_cast( + fListeners.ItemAtFast(i)); + listener->SendMessage(&message); + } +} + diff --git a/src/apps/webpositive/support/SettingsMessage.h b/src/apps/webpositive/support/SettingsMessage.h new file mode 100644 index 0000000000..8f22785788 --- /dev/null +++ b/src/apps/webpositive/support/SettingsMessage.h @@ -0,0 +1,105 @@ +/* + * Copyright 2008-2010 Stephan Aßmus . + * Copyright 1998 Eric Shepherd. + * All rights reserved. Distributed under the terms of the Be Sample Code + * license. + */ +#ifndef SETTINGS_MESSAGE_H +#define SETTINGS_MESSAGE_H + + +#include +#include +#include +#include +#include +#include + +class BMessenger; +class BString; + + +enum { + SETTINGS_VALUE_CHANGED = '_svc' +}; + + +class SettingsMessage : public BMessage, public BLocker { +public: + SettingsMessage(directory_which directory, + const char* filename); + virtual ~SettingsMessage(); + + + status_t InitCheck() const; + status_t Load(); + status_t Save() const; + + bool AddListener(const BMessenger& listener); + void RemoveListener(const BMessenger& listener); + + status_t SetValue(const char* name, bool value); + status_t SetValue(const char* name, int8 value); + status_t SetValue(const char* name, int16 value); + status_t SetValue(const char* name, int32 value); + status_t SetValue(const char* name, uint32 value); + status_t SetValue(const char* name, int64 value); + status_t SetValue(const char* name, float value); + status_t SetValue(const char* name, double value); + status_t SetValue(const char* name, + const char* value); + status_t SetValue(const char* name, + const BString& value); + status_t SetValue(const char *name, + const BPoint& value); + status_t SetValue(const char* name, const BRect& value); + status_t SetValue(const char* name, + const entry_ref& value); + status_t SetValue(const char* name, + const BMessage& value); + status_t SetValue(const char* name, + const BFlattenable* value); + status_t SetValue(const char* name, + const BFont& value); + + bool GetValue(const char* name, + bool defaultValue) const; + int8 GetValue(const char* name, + int8 defaultValue) const; + int16 GetValue(const char* name, + int16 defaultValue) const; + int32 GetValue(const char* name, + int32 defaultValue) const; + uint32 GetValue(const char* name, + uint32 defaultValue) const; + int64 GetValue(const char* name, + int64 defaultValue) const; + float GetValue(const char* name, + float defaultValue) const; + double GetValue(const char* name, + double defaultValue) const; + const char* GetValue(const char* name, + const char* defaultValue) const; + BString GetValue(const char* name, + const BString& defaultValue) const; + BPoint GetValue(const char *name, + BPoint defaultValue) const; + BRect GetValue(const char* name, + BRect defaultValue) const; + entry_ref GetValue(const char* name, + const entry_ref& defaultValue) const; + BMessage GetValue(const char* name, + const BMessage& defaultValue) const; + BFont GetValue(const char* name, + const BFont& defaultValue) const; + +private: + void _NotifyValueChanged(const char* name) const; + +private: + BPath fPath; + status_t fStatus; + BList fListeners; +}; + +#endif // SETTINGS_MESSAGE_H diff --git a/src/apps/webpositive/support/SlowMenu.h b/src/apps/webpositive/support/SlowMenu.h new file mode 100644 index 0000000000..62e9156b07 --- /dev/null +++ b/src/apps/webpositive/support/SlowMenu.h @@ -0,0 +1,76 @@ +/* +Open Tracker License + +Terms and Conditions + +Copyright (c) 1991-2000, Be Incorporated. All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice applies to all licensees +and shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF TITLE, MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +BE INCORPORATED BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF, OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of Be Incorporated shall not be +used in advertising or otherwise to promote the sale, use or other dealings in +this Software without prior written authorization from Be Incorporated. + +Tracker(TM), Be(R), BeOS(R), and BeIA(TM) are trademarks or registered trademarks +of Be Incorporated in the United States and other countries. Other brand product +names are registered trademarks or trademarks of their respective holders. +All rights reserved. +*/ + +#ifndef __SLOW_MENU__ +#define __SLOW_MENU__ + +#include +#include +#include + +// SlowMenu is a convenience class that makes it easier to +// use the AddDynamicItem callback to implement a menu that can +// checks periodically between creating new items and quits +// early if needed + +namespace BPrivate { + +class BSlowMenu : public BMenu { + public: + BSlowMenu(const char *title, menu_layout layout = B_ITEMS_IN_COLUMN); + + protected: + virtual bool StartBuildingItemList(); + // set up state to start building the item list + // returns false if setup failed + virtual bool AddNextItem() = 0; + // returns false if done + virtual void DoneBuildingItemList() = 0; + // default version adds items from itemList to menu and deletes + // the list; override to sort items first, etc. + + virtual void ClearMenuBuildingState() = 0; + + protected: + virtual bool AddDynamicItem(add_state state); + // this is the callback from BMenu, you shouldn't need to override this + + bool fMenuBuilt; +}; + +} // namespace BPrivate + +using namespace BPrivate; + +#endif /* __SLOW_MENU__ */ diff --git a/src/apps/webpositive/support/StringForSize.cpp b/src/apps/webpositive/support/StringForSize.cpp new file mode 100644 index 0000000000..ae7831e397 --- /dev/null +++ b/src/apps/webpositive/support/StringForSize.cpp @@ -0,0 +1,43 @@ +/* + * Copyright 2010 Haiku Inc. All rights reserved. + * Distributed under the terms of the MIT License. + */ + +#include "StringForSize.h" + +#include + + +namespace BPrivate { + + +const char* +string_for_size(double size, char* string, size_t stringSize) +{ + double kib = size / 1024.0; + if (kib < 1.0) { + snprintf(string, stringSize, "%d bytes", (int)size); + return string; + } + double mib = kib / 1024.0; + if (mib < 1.0) { + snprintf(string, stringSize, "%3.2f KiB", kib); + return string; + } + double gib = mib / 1024.0; + if (gib < 1.0) { + snprintf(string, stringSize, "%3.2f MiB", mib); + return string; + } + double tib = gib / 1024.0; + if (tib < 1.0) { + snprintf(string, stringSize, "%3.2f GiB", gib); + return string; + } + snprintf(string, stringSize, "%.2f TiB", tib); + return string; +} + + +} // namespace BPrivate + diff --git a/src/apps/webpositive/support/StringForSize.h b/src/apps/webpositive/support/StringForSize.h new file mode 100644 index 0000000000..fa3e13b115 --- /dev/null +++ b/src/apps/webpositive/support/StringForSize.h @@ -0,0 +1,23 @@ +/* + * Copyright 2010 Haiku Inc. All rights reserved. + * Distributed under the terms of the MIT License. + */ +#ifndef STRING_FOR_SIZE_H +#define STRING_FOR_SIZE_H + +#include + + +namespace BPrivate { + + +const char* string_for_size(double size, char* string, size_t stringSize); + + +} // namespace BPrivate + + +using BPrivate::string_for_size; + + +#endif // COLOR_QUANTIZER_H diff --git a/src/apps/webpositive/support/WindowIcon.h b/src/apps/webpositive/support/WindowIcon.h new file mode 100644 index 0000000000..30d1622058 --- /dev/null +++ b/src/apps/webpositive/support/WindowIcon.h @@ -0,0 +1,54 @@ + +const uint32 kWindowIconWidth = 13; +const uint32 kWindowIconHeight = 14; +const color_space kWindowIconFormat = B_RGBA32; + +const unsigned char kWindowIconBits[] = { + 0x78, 0x78, 0x78, 0xff, 0x78, 0x78, 0x78, 0xff, 0x78, 0x78, 0x78, 0xff, 0x78, 0x78, 0x78, 0xff, + 0x78, 0x78, 0x78, 0xff, 0x78, 0x78, 0x78, 0xff, 0x78, 0x78, 0x78, 0xff, 0x78, 0x78, 0x78, 0xff, + 0x78, 0x78, 0x78, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x78, 0x78, 0x78, 0xff, 0x66, 0xff, 0xff, 0xff, 0x66, 0xff, 0xff, 0xff, + 0x66, 0xff, 0xff, 0xff, 0x66, 0xff, 0xff, 0xff, 0x66, 0xff, 0xff, 0xff, 0x66, 0xff, 0xff, 0xff, + 0x66, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x78, 0x78, 0xff, 0x13, 0xaf, 0xff, 0xff, + 0x13, 0xaf, 0xff, 0xff, 0x13, 0xaf, 0xff, 0xff, 0x13, 0xaf, 0xff, 0xff, 0x13, 0xaf, 0xff, 0xff, + 0x13, 0xaf, 0xff, 0xff, 0x13, 0xaf, 0xff, 0xff, 0x78, 0x78, 0x78, 0xff, 0x78, 0x78, 0x78, 0xff, + 0x78, 0x78, 0x78, 0xff, 0x78, 0x78, 0x78, 0xff, 0x78, 0x78, 0x78, 0xff, 0x78, 0x78, 0x78, 0xff, + 0xc8, 0xc8, 0xc8, 0xff, 0xc8, 0xc8, 0xc8, 0xff, 0xc8, 0xc8, 0xc8, 0xff, 0xc8, 0xc8, 0xc8, 0xff, + 0xc8, 0xc8, 0xc8, 0xff, 0xc8, 0xc8, 0xc8, 0xff, 0xc8, 0xc8, 0xc8, 0xff, 0xc8, 0xc8, 0xc8, 0xff, + 0xc8, 0xc8, 0xc8, 0xff, 0xc8, 0xc8, 0xc8, 0xff, 0xc8, 0xc8, 0xc8, 0xff, 0x00, 0x00, 0x00, 0xff, + 0x78, 0x78, 0x78, 0xff, 0xc8, 0xc8, 0xc8, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc8, 0xc8, 0xc8, 0xff, + 0x00, 0x00, 0x00, 0xff, 0x78, 0x78, 0x78, 0xff, 0xc8, 0xc8, 0xc8, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xc8, 0xc8, 0xc8, 0xff, 0x00, 0x00, 0x00, 0xff, 0x78, 0x78, 0x78, 0xff, 0xc8, 0xc8, 0xc8, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xc8, 0xc8, 0xc8, 0xff, 0x00, 0x00, 0x00, 0xff, 0x78, 0x78, 0x78, 0xff, + 0xc8, 0xc8, 0xc8, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc8, 0xc8, 0xc8, 0xff, 0x00, 0x00, 0x00, 0xff, + 0x78, 0x78, 0x78, 0xff, 0xc8, 0xc8, 0xc8, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc8, 0xc8, 0xc8, 0xff, + 0x00, 0x00, 0x00, 0xff, 0x78, 0x78, 0x78, 0xff, 0xc8, 0xc8, 0xc8, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xc8, 0xc8, 0xc8, 0xff, 0x00, 0x00, 0x00, 0xff, 0x78, 0x78, 0x78, 0xff, 0xc8, 0xc8, 0xc8, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xc8, 0xc8, 0xc8, 0xff, 0x00, 0x00, 0x00, 0xff, 0x78, 0x78, 0x78, 0xff, + 0xc8, 0xc8, 0xc8, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc8, 0xc8, 0xc8, 0xff, 0x00, 0x00, 0x00, 0xff, + 0x78, 0x78, 0x78, 0xff, 0xc8, 0xc8, 0xc8, 0xff, 0xc8, 0xc8, 0xc8, 0xff, 0xc8, 0xc8, 0xc8, 0xff, + 0xc8, 0xc8, 0xc8, 0xff, 0xc8, 0xc8, 0xc8, 0xff, 0xc8, 0xc8, 0xc8, 0xff, 0xc8, 0xc8, 0xc8, 0xff, + 0xc8, 0xc8, 0xc8, 0xff, 0xc8, 0xc8, 0xc8, 0xff, 0xc8, 0xc8, 0xc8, 0xff, 0xc8, 0xc8, 0xc8, 0xff, + 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, + 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, + 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, + 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff +}; + diff --git a/src/apps/webpositive/svn_revision.cpp b/src/apps/webpositive/svn_revision.cpp new file mode 100644 index 0000000000..9e415b5262 --- /dev/null +++ b/src/apps/webpositive/svn_revision.cpp @@ -0,0 +1,10 @@ +/* + * Copyright 2006-2009, Ingo Weinhold + * All rights reserved. Distributed under the terms of the MIT License. + */ + +#include "svn_revision.h" + +const int32 kSVNRevision = 0; +// #include "svn_revision" +; diff --git a/src/apps/webpositive/svn_revision.h b/src/apps/webpositive/svn_revision.h new file mode 100644 index 0000000000..fff9e9b122 --- /dev/null +++ b/src/apps/webpositive/svn_revision.h @@ -0,0 +1,15 @@ +/* + * Copyright 2006-2009, Ingo Weinhold + * All rights reserved. Distributed under the terms of the MIT License. + */ + +#ifndef SVN_REVISION_H +#define SVN_REVISION_H + +#include + + +extern const int32 kSVNRevision; + + +#endif // SVN_REVISION_H diff --git a/src/apps/webpositive/tabview/TabContainerView.cpp b/src/apps/webpositive/tabview/TabContainerView.cpp new file mode 100644 index 0000000000..52217fa9c7 --- /dev/null +++ b/src/apps/webpositive/tabview/TabContainerView.cpp @@ -0,0 +1,542 @@ +/* + * Copyright (C) 2010 Rene Gollent + * Copyright (C) 2010 Stephan Aßmus + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "TabContainerView.h" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "TabView.h" + + +static const float kLeftTabInset = 4; + + +TabContainerView::TabContainerView(Controller* controller) + : + BGroupView(B_HORIZONTAL, 0.0), + fLastMouseEventTab(NULL), + fMouseDown(false), + fClickCount(0), + fSelectedTab(NULL), + fController(controller), + fFirstVisibleTabIndex(0) +{ + SetFlags(Flags() | B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE); + SetViewColor(B_TRANSPARENT_COLOR); + GroupLayout()->SetInsets(kLeftTabInset, 0, 0, 1); + GroupLayout()->AddItem(BSpaceLayoutItem::CreateGlue(), 0.0f); +} + + +TabContainerView::~TabContainerView() +{ +} + + +BSize +TabContainerView::MinSize() +{ + // Eventually, we want to be scrolling if the tabs don't fit. + BSize size(BGroupView::MinSize()); + size.width = 300; + return size; +} + + +void +TabContainerView::MessageReceived(BMessage* message) +{ + switch (message->what) { + default: + BGroupView::MessageReceived(message); + } +} + + +void +TabContainerView::Draw(BRect updateRect) +{ + // Stroke separator line at bottom. + rgb_color base = ui_color(B_PANEL_BACKGROUND_COLOR); + BRect frame(Bounds()); + SetHighColor(tint_color(base, B_DARKEN_2_TINT)); + StrokeLine(frame.LeftBottom(), frame.RightBottom()); + frame.bottom--; + + // Draw empty area before first tab. + uint32 borders = BControlLook::B_TOP_BORDER | BControlLook::B_BOTTOM_BORDER; + BRect leftFrame(frame.left, frame.top, kLeftTabInset, frame.bottom); + be_control_look->DrawInactiveTab(this, leftFrame, updateRect, base, 0, + borders); + + // Draw all tabs, keeping track of where they end. + BGroupLayout* layout = GroupLayout(); + int32 count = layout->CountItems() - 1; + for (int32 i = 0; i < count; i++) { + TabLayoutItem* item = dynamic_cast( + layout->ItemAt(i)); + if (!item || !item->IsVisible()) + continue; + item->Parent()->Draw(updateRect); + frame.left = item->Frame().right + 1; + } + + // Draw empty area after last tab. + be_control_look->DrawInactiveTab(this, frame, updateRect, base, 0, borders); +} + + +void +TabContainerView::MouseDown(BPoint where) +{ + uint32 buttons; + if (Window()->CurrentMessage()->FindInt32("buttons", (int32*)&buttons) != B_OK) + buttons = B_PRIMARY_MOUSE_BUTTON; + uint32 clicks; + if (Window()->CurrentMessage()->FindInt32("clicks", (int32*)&clicks) != B_OK) + clicks = 1; + fMouseDown = true; + SetMouseEventMask(B_POINTER_EVENTS, B_LOCK_WINDOW_FOCUS); + if (fLastMouseEventTab) + fLastMouseEventTab->MouseDown(where, buttons); + else { + if ((buttons & B_TERTIARY_MOUSE_BUTTON) != 0) { + // Middle click outside tabs should always open a new tab. + fClickCount = 2; + } else if (clicks > 1) + fClickCount = fClickCount++; + else + fClickCount = 1; + } +} + + +void +TabContainerView::MouseUp(BPoint where) +{ + fMouseDown = false; + if (fLastMouseEventTab) { + fLastMouseEventTab->MouseUp(where); + fClickCount = 0; + } else if (fClickCount > 1) { + // NOTE: fClickCount is >= 1 only if the first click was outside + // any tab. So even if fLastMouseEventTab has been reset to NULL + // because this tab was removed during mouse down, we wouldn't + // run the "outside tabs" code below. + fController->DoubleClickOutsideTabs(); + fClickCount = 0; + } + // Always check the tab under the mouse again, since we don't update + // it with fMouseDown == true. + _SendFakeMouseMoved(); +} + + +void +TabContainerView::MouseMoved(BPoint where, uint32 transit, + const BMessage* dragMessage) +{ + _MouseMoved(where, transit, dragMessage); +} + + +void +TabContainerView::DoLayout() +{ + BGroupView::DoLayout(); + + _ValidateTabVisibility(); + _SendFakeMouseMoved(); +} + +void +TabContainerView::AddTab(const char* label, int32 index) +{ + TabView* tab; + if (fController) + tab = fController->CreateTabView(); + else + tab = new TabView(); + tab->SetLabel(label); + AddTab(tab, index); +} + + +void +TabContainerView::AddTab(TabView* tab, int32 index) +{ + tab->SetContainerView(this); + + if (index == -1) + index = GroupLayout()->CountItems() - 1; + + bool hasFrames = fController != NULL && fController->HasFrames(); + bool isFirst = index == 0 && hasFrames; + bool isLast = index == GroupLayout()->CountItems() - 1 && hasFrames; + bool isFront = fSelectedTab == NULL; + tab->Update(isFirst, isLast, isFront); + + GroupLayout()->AddItem(index, tab->LayoutItem()); + + if (isFront) + SelectTab(tab); + if (isLast) { + TabLayoutItem* item + = dynamic_cast(GroupLayout()->ItemAt(index - 1)); + if (item) + item->Parent()->SetIsLast(false); + } + + SetFirstVisibleTabIndex(MaxFirstVisibleTabIndex()); + _ValidateTabVisibility(); +} + +TabView* +TabContainerView::RemoveTab(int32 index) +{ + TabLayoutItem* item + = dynamic_cast(GroupLayout()->RemoveItem(index)); + + if (!item) + return NULL; + + BRect dirty(Bounds()); + dirty.left = item->Frame().left; + TabView* removedTab = item->Parent(); + removedTab->SetContainerView(NULL); + + if (removedTab == fLastMouseEventTab) + fLastMouseEventTab = NULL; + + // Update tabs after or before the removed tab. + bool hasFrames = fController != NULL && fController->HasFrames(); + item = dynamic_cast(GroupLayout()->ItemAt(index)); + if (item) { + // This tab is behind the removed tab. + TabView* tab = item->Parent(); + tab->Update(index == 0 && hasFrames, + index == GroupLayout()->CountItems() - 2 && hasFrames, + tab == fSelectedTab); + if (removedTab == fSelectedTab) { + fSelectedTab = NULL; + SelectTab(tab); + } else if (fController && tab == fSelectedTab) + fController->TabSelected(index); + } else { + // The removed tab was the last tab. + item = dynamic_cast(GroupLayout()->ItemAt(index - 1)); + if (item) { + TabView* tab = item->Parent(); + tab->Update(index == 0 && hasFrames, + index == GroupLayout()->CountItems() - 2 && hasFrames, + tab == fSelectedTab); + if (removedTab == fSelectedTab) { + fSelectedTab = NULL; + SelectTab(tab); + } + } + } + + Invalidate(dirty); + _ValidateTabVisibility(); + + return removedTab; +} + + +TabView* +TabContainerView::TabAt(int32 index) const +{ + TabLayoutItem* item = dynamic_cast( + GroupLayout()->ItemAt(index)); + if (item) + return item->Parent(); + return NULL; +} + + +int32 +TabContainerView::IndexOf(TabView* tab) const +{ + return GroupLayout()->IndexOfItem(tab->LayoutItem()); +} + + +void +TabContainerView::SelectTab(int32 index) +{ + TabView* tab = NULL; + TabLayoutItem* item = dynamic_cast( + GroupLayout()->ItemAt(index)); + if (item) + tab = item->Parent(); + + SelectTab(tab); +} + + +void +TabContainerView::SelectTab(TabView* tab) +{ + if (tab == fSelectedTab) + return; + + if (fSelectedTab) + fSelectedTab->SetIsFront(false); + + fSelectedTab = tab; + + if (fSelectedTab) + fSelectedTab->SetIsFront(true); + + if (fController != NULL) { + int32 index = -1; + if (fSelectedTab != NULL) + index = GroupLayout()->IndexOfItem(tab->LayoutItem()); + + if (!tab->LayoutItem()->IsVisible()) { + SetFirstVisibleTabIndex(index); + } + + fController->TabSelected(index); + } +} + + +void +TabContainerView::SetTabLabel(int32 tabIndex, const char* label) +{ + TabLayoutItem* item = dynamic_cast( + GroupLayout()->ItemAt(tabIndex)); + if (item == NULL) + return; + + item->Parent()->SetLabel(label); +} + + +void +TabContainerView::SetFirstVisibleTabIndex(int32 index) +{ + if (index < 0) + index = 0; + if (index > MaxFirstVisibleTabIndex()) + index = MaxFirstVisibleTabIndex(); + if (fFirstVisibleTabIndex == index) + return; + + fFirstVisibleTabIndex = index; + + _UpdateTabVisibility(); +} + + +int32 +TabContainerView::FirstVisibleTabIndex() const +{ + return fFirstVisibleTabIndex; +} + + +int32 +TabContainerView::MaxFirstVisibleTabIndex() const +{ + float availableWidth = _AvailableWidthForTabs(); + if (availableWidth < 0) + return 0; + float visibleTabsWidth = 0; + + BGroupLayout* layout = GroupLayout(); + int32 i = layout->CountItems() - 2; + for (; i >= 0; i--) { + TabLayoutItem* item = dynamic_cast( + layout->ItemAt(i)); + if (item == NULL) + continue; + + float itemWidth = item->MinSize().width; + if (availableWidth >= visibleTabsWidth + itemWidth) + visibleTabsWidth += itemWidth; + else { + // The tab before this tab is the last one that can be visible. + return i + 1; + } + } + + return 0; +} + + +bool +TabContainerView::CanScrollLeft() const +{ + return fFirstVisibleTabIndex < MaxFirstVisibleTabIndex(); +} + + +bool +TabContainerView::CanScrollRight() const +{ + BGroupLayout* layout = GroupLayout(); + int32 count = layout->CountItems() - 1; + if (count > 0) { + TabLayoutItem* item = dynamic_cast( + layout->ItemAt(count - 1)); + return !item->IsVisible(); + } + return false; +} + + +// #pragma mark - + + +TabView* +TabContainerView::_TabAt(const BPoint& where) const +{ + BGroupLayout* layout = GroupLayout(); + int32 count = layout->CountItems() - 1; + for (int32 i = 0; i < count; i++) { + TabLayoutItem* item = dynamic_cast(layout->ItemAt(i)); + if (item == NULL || !item->IsVisible()) + continue; + // Account for the fact that the tab frame does not contain the + // visible bottom border. + BRect frame = item->Frame(); + frame.bottom++; + if (frame.Contains(where)) + return item->Parent(); + } + return NULL; +} + + +void +TabContainerView::_MouseMoved(BPoint where, uint32 _transit, + const BMessage* dragMessage) +{ + TabView* tab = _TabAt(where); + if (fMouseDown) { + uint32 transit = tab == fLastMouseEventTab + ? B_INSIDE_VIEW : B_OUTSIDE_VIEW; + if (fLastMouseEventTab) + fLastMouseEventTab->MouseMoved(where, transit, dragMessage); + return; + } + + if (fLastMouseEventTab != NULL && fLastMouseEventTab == tab) + fLastMouseEventTab->MouseMoved(where, B_INSIDE_VIEW, dragMessage); + else { + if (fLastMouseEventTab) + fLastMouseEventTab->MouseMoved(where, B_EXITED_VIEW, dragMessage); + fLastMouseEventTab = tab; + if (fLastMouseEventTab) + fLastMouseEventTab->MouseMoved(where, B_ENTERED_VIEW, dragMessage); + else + fController->SetToolTip("Double-click or middle-click to open new tab."); + } +} + + +void +TabContainerView::_ValidateTabVisibility() +{ + if (fFirstVisibleTabIndex > MaxFirstVisibleTabIndex()) + SetFirstVisibleTabIndex(MaxFirstVisibleTabIndex()); + else + _UpdateTabVisibility(); +} + + +void +TabContainerView::_UpdateTabVisibility() +{ + float availableWidth = _AvailableWidthForTabs(); + if (availableWidth < 0) + return; + float visibleTabsWidth = 0; + + bool canScrollTabsLeft = fFirstVisibleTabIndex > 0; + bool canScrollTabsRight = false; + + BGroupLayout* layout = GroupLayout(); + int32 count = layout->CountItems() - 1; + for (int32 i = 0; i < count; i++) { + TabLayoutItem* item = dynamic_cast( + layout->ItemAt(i)); + if (i < fFirstVisibleTabIndex) + item->SetVisible(false); + else { + float itemWidth = item->MinSize().width; + bool visible = availableWidth >= visibleTabsWidth + itemWidth; + item->SetVisible(visible && !canScrollTabsRight); + visibleTabsWidth += itemWidth; + if (!visible) + canScrollTabsRight = true; + } + } + fController->UpdateTabScrollability(canScrollTabsLeft, canScrollTabsRight); +} + + +float +TabContainerView::_AvailableWidthForTabs() const +{ + float width = Bounds().Width() - 10; + // TODO: Don't really know why -10 is needed above. + + float left; + float right; + GroupLayout()->GetInsets(&left, NULL, &right, NULL); + width -= left + right; + + return width; +} + + +void +TabContainerView::_SendFakeMouseMoved() +{ + BPoint where; + uint32 buttons; + GetMouse(&where, &buttons, false); + if (Bounds().Contains(where)) + _MouseMoved(where, B_INSIDE_VIEW, NULL); +} + diff --git a/src/apps/webpositive/tabview/TabContainerView.h b/src/apps/webpositive/tabview/TabContainerView.h new file mode 100644 index 0000000000..a5a7ee04fa --- /dev/null +++ b/src/apps/webpositive/tabview/TabContainerView.h @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2010 Stephan Aßmus + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef TAB_CONTAINER_VIEW_H +#define TAB_CONTAINER_VIEW_H + +#include + + +class TabView; + + +class TabContainerView : public BGroupView { +public: + class Controller { + public: + virtual void TabSelected(int32 tabIndex) = 0; + virtual bool HasFrames() = 0; + virtual TabView* CreateTabView() = 0; + virtual void DoubleClickOutsideTabs() = 0; + virtual void UpdateTabScrollability(bool canScrollLeft, + bool canScrollRight) = 0; + virtual void SetToolTip(const BString& text) = 0; + }; + +public: + TabContainerView(Controller* controller); + virtual ~TabContainerView(); + + virtual BSize MinSize(); + + virtual void MessageReceived(BMessage*); + + virtual void Draw(BRect updateRect); + + virtual void MouseDown(BPoint where); + virtual void MouseUp(BPoint where); + virtual void MouseMoved(BPoint where, uint32 transit, + const BMessage* dragMessage); + + virtual void DoLayout(); + + void AddTab(const char* label, int32 index = -1); + void AddTab(TabView* tab, int32 index = -1); + TabView* RemoveTab(int32 index); + TabView* TabAt(int32 index) const; + + int32 IndexOf(TabView* tab) const; + + void SelectTab(int32 tabIndex); + void SelectTab(TabView* tab); + + void SetTabLabel(int32 tabIndex, const char* label); + + void SetFirstVisibleTabIndex(int32 index); + int32 FirstVisibleTabIndex() const; + int32 MaxFirstVisibleTabIndex() const; + + bool CanScrollLeft() const; + bool CanScrollRight() const; + +private: + TabView* _TabAt(const BPoint& where) const; + void _MouseMoved(BPoint where, uint32 transit, + const BMessage* dragMessage); + void _ValidateTabVisibility(); + void _UpdateTabVisibility(); + float _AvailableWidthForTabs() const; + void _SendFakeMouseMoved(); + +private: + TabView* fLastMouseEventTab; + bool fMouseDown; + uint32 fClickCount; + TabView* fSelectedTab; + Controller* fController; + int32 fFirstVisibleTabIndex; +}; + +#endif // TAB_CONTAINER_VIEW_H diff --git a/src/apps/webpositive/tabview/TabManager.cpp b/src/apps/webpositive/tabview/TabManager.cpp new file mode 100644 index 0000000000..f25b67769d --- /dev/null +++ b/src/apps/webpositive/tabview/TabManager.cpp @@ -0,0 +1,940 @@ +/* + * Copyright (C) 2010 Rene Gollent + * Copyright (C) 2010 Stephan Aßmus + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "TabManager.h" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "TabContainerView.h" +#include "TabView.h" + + +const static BString kEmptyString; + + +// #pragma mark - Helper classes + + +class TabButton : public BButton { +public: + TabButton(BMessage* message) + : BButton("", message) + { + } + + virtual BSize MinSize() + { + return BSize(12, 12); + } + + virtual BSize MaxSize() + { + return BSize(B_SIZE_UNLIMITED, B_SIZE_UNLIMITED); + } + + virtual BSize PreferredSize() + { + return MinSize(); + } + + virtual void Draw(BRect updateRect) + { + BRect bounds(Bounds()); + rgb_color base = ui_color(B_PANEL_BACKGROUND_COLOR); + SetHighColor(tint_color(base, B_DARKEN_2_TINT)); + StrokeLine(bounds.LeftBottom(), bounds.RightBottom()); + bounds.bottom--; + uint32 flags = be_control_look->Flags(this); + uint32 borders = BControlLook::B_TOP_BORDER + | BControlLook::B_BOTTOM_BORDER; + be_control_look->DrawInactiveTab(this, bounds, updateRect, base, + 0, borders); + if (IsEnabled()) { + rgb_color button = tint_color(base, 1.07); + be_control_look->DrawButtonBackground(this, bounds, updateRect, + button, flags, 0); + } + + bounds.left = (bounds.left + bounds.right) / 2 - 6; + bounds.top = (bounds.top + bounds.bottom) / 2 - 6; + bounds.right = bounds.left + 12; + bounds.bottom = bounds.top + 12; + DrawSymbol(bounds, updateRect, base); + } + + virtual void DrawSymbol(BRect frame, const BRect& updateRect, + const rgb_color& base) + { + } +}; + + +class ScrollLeftTabButton : public TabButton { +public: + ScrollLeftTabButton(BMessage* message) + : TabButton(message) + { + } + + virtual void DrawSymbol(BRect frame, const BRect& updateRect, + const rgb_color& base) + { + float tint = IsEnabled() ? B_DARKEN_4_TINT : B_DARKEN_1_TINT; + be_control_look->DrawArrowShape(this, frame, updateRect, + base, BControlLook::B_LEFT_ARROW, 0, tint); + } +}; + + +class ScrollRightTabButton : public TabButton { +public: + ScrollRightTabButton(BMessage* message) + : TabButton(message) + { + } + + virtual void DrawSymbol(BRect frame, const BRect& updateRect, + const rgb_color& base) + { + frame.OffsetBy(1, 0); + float tint = IsEnabled() ? B_DARKEN_4_TINT : B_DARKEN_1_TINT; + be_control_look->DrawArrowShape(this, frame, updateRect, + base, BControlLook::B_RIGHT_ARROW, 0, tint); + } +}; + + +class NewTabButton : public TabButton { +public: + NewTabButton(BMessage* message) + : TabButton(message) + { + SetToolTip("New tab (Cmd-T)"); + } + + virtual BSize MinSize() + { + return BSize(18, 12); + } + + virtual void DrawSymbol(BRect frame, const BRect& updateRect, + const rgb_color& base) + { + SetHighColor(tint_color(base, B_DARKEN_4_TINT)); + float inset = 3; + frame.InsetBy(2, 2); + frame.top++; + frame.left++; + FillRoundRect(BRect(frame.left, frame.top + inset, + frame.right, frame.bottom - inset), 1, 1); + FillRoundRect(BRect(frame.left + inset, frame.top, + frame.right - inset, frame.bottom), 1, 1); + } +}; + + +class TabMenuTabButton : public TabButton { +public: + TabMenuTabButton(BMessage* message) + : TabButton(message) + { + } + + virtual BSize MinSize() + { + return BSize(18, 12); + } + + virtual void DrawSymbol(BRect frame, const BRect& updateRect, + const rgb_color& base) + { + be_control_look->DrawArrowShape(this, frame, updateRect, + base, BControlLook::B_DOWN_ARROW, 0, B_DARKEN_4_TINT); + } + + virtual void MouseDown(BPoint point) + { + if (!IsEnabled()) + return; + + // Invoke must be called before setting B_CONTROL_ON + // for the button to stay "down" + Invoke(); + SetValue(B_CONTROL_ON); + } + + virtual void MouseUp(BPoint point) + { + // Do nothing + } + + void MenuClosed() + { + SetValue(B_CONTROL_OFF); + } +}; + + +enum { + MSG_SCROLL_TABS_LEFT = 'stlt', + MSG_SCROLL_TABS_RIGHT = 'strt', + MSG_OPEN_TAB_MENU = 'otmn' +}; + + +class TabContainerGroup : public BGroupView { +public: + TabContainerGroup(TabContainerView* tabContainerView) + : + BGroupView(B_HORIZONTAL, 0.0), + fTabContainerView(tabContainerView), + fScrollLeftTabButton(NULL), + fScrollRightTabButton(NULL), + fTabMenuButton(NULL) + { + } + + virtual void AttachedToWindow() + { + if (fScrollLeftTabButton != NULL) + fScrollLeftTabButton->SetTarget(this); + if (fScrollRightTabButton != NULL) + fScrollRightTabButton->SetTarget(this); + if (fTabMenuButton != NULL) + fTabMenuButton->SetTarget(this); + } + + virtual void MessageReceived(BMessage* message) + { + switch (message->what) { + case MSG_SCROLL_TABS_LEFT: + fTabContainerView->SetFirstVisibleTabIndex( + fTabContainerView->FirstVisibleTabIndex() - 1); + break; + case MSG_SCROLL_TABS_RIGHT: + fTabContainerView->SetFirstVisibleTabIndex( + fTabContainerView->FirstVisibleTabIndex() + 1); + break; + case MSG_OPEN_TAB_MENU: + { + BPopUpMenu* tabMenu = new BPopUpMenu("tab menu", true, false); + int tabCount = fTabContainerView->GetLayout()->CountItems(); + for (int i = 0; i < tabCount; i++) { + TabView* tab = fTabContainerView->TabAt(i); + if (tab) { + BMenuItem* item = new BMenuItem(tab->Label(), NULL); + tabMenu->AddItem(item); + if (tab->IsFront()) + item->SetMarked(true); + } + } + + // Force layout to get the final menu size. InvalidateLayout() + // did not seem to work here. + tabMenu->AttachedToWindow(); + BRect buttonFrame = fTabMenuButton->Frame(); + BRect menuFrame = tabMenu->Frame(); + BPoint openPoint = ConvertToScreen(buttonFrame.LeftBottom()); + // Open with the right side of the menu aligned with the right + // side of the button and a little below. + openPoint.x -= menuFrame.Width() - buttonFrame.Width(); + openPoint.y += 2; + + BMenuItem *selected = tabMenu->Go(openPoint, false, false, + ConvertToScreen(buttonFrame)); + if (selected) { + selected->SetMarked(true); + int32 index = tabMenu->IndexOf(selected); + if (index != B_ERROR) + fTabContainerView->SelectTab(index); + } + fTabMenuButton->MenuClosed(); + delete tabMenu; + + break; + } + default: + BGroupView::MessageReceived(message); + break; + } + } + + void AddScrollLeftButton(TabButton* button) + { + fScrollLeftTabButton = button; + GroupLayout()->AddView(button, 0.0f); + } + + void AddScrollRightButton(TabButton* button) + { + fScrollRightTabButton = button; + GroupLayout()->AddView(button, 0.0f); + } + + void AddTabMenuButton(TabMenuTabButton* button) + { + fTabMenuButton = button; + GroupLayout()->AddView(button, 0.0f); + } + + void EnableScrollButtons(bool canScrollLeft, bool canScrollRight) + { + fScrollLeftTabButton->SetEnabled(canScrollLeft); + fScrollRightTabButton->SetEnabled(canScrollRight); + if (!canScrollLeft && !canScrollRight) { + // hide scroll buttons + } else { + // show scroll buttons + } + } + +private: + TabContainerView* fTabContainerView; + TabButton* fScrollLeftTabButton; + TabButton* fScrollRightTabButton; + TabMenuTabButton* fTabMenuButton; +}; + + +class TabButtonContainer : public BGroupView { +public: + TabButtonContainer() + : + BGroupView(B_HORIZONTAL, 0.0) + { + SetFlags(Flags() | B_WILL_DRAW); + SetViewColor(B_TRANSPARENT_COLOR); + SetLowColor(ui_color(B_PANEL_BACKGROUND_COLOR)); + GroupLayout()->SetInsets(0, 6, 0, 0); + } + + virtual void Draw(BRect updateRect) + { + BRect bounds(Bounds()); + rgb_color base = LowColor(); + be_control_look->DrawInactiveTab(this, bounds, updateRect, + base, 0, BControlLook::B_TOP_BORDER); + } +}; + + +class TabManagerController : public TabContainerView::Controller { +public: + TabManagerController(TabManager* manager); + + virtual ~TabManagerController(); + + virtual void TabSelected(int32 index) + { + fManager->SelectTab(index); + } + + virtual bool HasFrames() + { + return false; + } + + virtual TabView* CreateTabView(); + + virtual void DoubleClickOutsideTabs(); + + virtual void UpdateTabScrollability(bool canScrollLeft, + bool canScrollRight) + { + fTabContainerGroup->EnableScrollButtons(canScrollLeft, canScrollRight); + } + + virtual void SetToolTip(const BString& text) + { + if (fCurrentToolTip == text) + return; + fCurrentToolTip = text; + fManager->GetTabContainerView()->HideToolTip(); + fManager->GetTabContainerView()->SetToolTip( + reinterpret_cast(NULL)); + fManager->GetTabContainerView()->SetToolTip(fCurrentToolTip.String()); + } + + void CloseTab(int32 index); + + void SetCloseButtonsAvailable(bool available) + { + fCloseButtonsAvailable = available; + } + + bool CloseButtonsAvailable() const + { + return fCloseButtonsAvailable; + } + + void SetDoubleClickOutsideTabsMessage(const BMessage& message, + const BMessenger& target); + + void SetTabContainerGroup(TabContainerGroup* tabContainerGroup) + { + fTabContainerGroup = tabContainerGroup; + } + +private: + TabManager* fManager; + TabContainerGroup* fTabContainerGroup; + bool fCloseButtonsAvailable; + BMessage* fDoubleClickOutsideTabsMessage; + BMessenger fTarget; + BString fCurrentToolTip; +}; + + +// #pragma mark - WebTabView + + +class WebTabView : public TabView { +public: + WebTabView(TabManagerController* controller); + ~WebTabView(); + + virtual BSize MaxSize(); + + virtual void DrawContents(BView* owner, BRect frame, const BRect& updateRect, + bool isFirst, bool isLast, bool isFront); + + virtual void MouseDown(BPoint where, uint32 buttons); + virtual void MouseUp(BPoint where); + virtual void MouseMoved(BPoint where, uint32 transit, + const BMessage* dragMessage); + + void SetIcon(const BBitmap* icon); + +private: + void _DrawCloseButton(BView* owner, BRect& frame, const BRect& updateRect, + bool isFirst, bool isLast, bool isFront); + BRect _CloseRectFrame(BRect frame) const; + +private: + BBitmap* fIcon; + TabManagerController* fController; + bool fOverCloseRect; + bool fClicked; + bool fCloseOnMouseUp; +}; + + +WebTabView::WebTabView(TabManagerController* controller) + : + TabView(), + fIcon(NULL), + fController(controller), + fOverCloseRect(false), + fClicked(false), + fCloseOnMouseUp(false) +{ +} + + +WebTabView::~WebTabView() +{ + delete fIcon; +} + + +static const int kIconSize = 18; +static const int kIconInset = 3; + + +BSize +WebTabView::MaxSize() +{ + // Account for icon. + BSize size(TabView::MaxSize()); + size.height = max_c(size.height, kIconSize + kIconInset * 2); + if (fIcon) + size.width += kIconSize + kIconInset * 2; + // Account for close button. + size.width += size.height; + return size; +} + + +void +WebTabView::DrawContents(BView* owner, BRect frame, const BRect& updateRect, + bool isFirst, bool isLast, bool isFront) +{ + if (fController->CloseButtonsAvailable()) + _DrawCloseButton(owner, frame, updateRect, isFirst, isLast, isFront); + + if (fIcon) { + BRect iconBounds(0, 0, kIconSize - 1, kIconSize - 1); + // clip to icon bounds, if they are smaller + if (iconBounds.Contains(fIcon->Bounds())) + iconBounds = fIcon->Bounds(); + else { + // Try to scale down the icon by an even factor so the + // final size is between 14 and 18 pixel size. If this fails, + // the icon will simply be displayed at 18x18. + float scale = 2; + while ((fIcon->Bounds().Width() + 1) / scale > kIconSize) + scale *= 2; + if ((fIcon->Bounds().Width() + 1) / scale >= kIconSize - 4 + && (fIcon->Bounds().Height() + 1) / scale >= kIconSize - 4 + && (fIcon->Bounds().Height() + 1) / scale <= kIconSize) { + iconBounds.right = (fIcon->Bounds().Width() + 1) / scale - 1; + iconBounds.bottom = (fIcon->Bounds().Height() + 1) / scale - 1; + } + } + BPoint iconPos(frame.left + kIconInset - 1, + frame.top + floorf((frame.Height() - iconBounds.Height()) / 2)); + iconBounds.OffsetTo(iconPos); + owner->SetDrawingMode(B_OP_ALPHA); + owner->SetBlendingMode(B_PIXEL_ALPHA, B_ALPHA_OVERLAY); + owner->DrawBitmap(fIcon, fIcon->Bounds(), iconBounds, + B_FILTER_BITMAP_BILINEAR); + owner->SetDrawingMode(B_OP_COPY); + frame.left = frame.left + kIconSize + kIconInset * 2; + } + + TabView::DrawContents(owner, frame, updateRect, isFirst, isLast, isFront); +} + + +void +WebTabView::MouseDown(BPoint where, uint32 buttons) +{ + if (buttons & B_TERTIARY_MOUSE_BUTTON) { + fCloseOnMouseUp = true; + return; + } + + BRect closeRect = _CloseRectFrame(Frame()); + if (!fController->CloseButtonsAvailable() || !closeRect.Contains(where)) { + TabView::MouseDown(where, buttons); + return; + } + + fClicked = true; + ContainerView()->Invalidate(closeRect); +} + + +void +WebTabView::MouseUp(BPoint where) +{ + if (!fClicked && !fCloseOnMouseUp) { + TabView::MouseUp(where); + return; + } + + if (fCloseOnMouseUp && Frame().Contains(where)) { + fCloseOnMouseUp = false; + fController->CloseTab(ContainerView()->IndexOf(this)); + // Probably this object is toast now, better return here. + return; + } + + fClicked = false; + fCloseOnMouseUp = false; + + if (_CloseRectFrame(Frame()).Contains(where)) + fController->CloseTab(ContainerView()->IndexOf(this)); +} + + +void +WebTabView::MouseMoved(BPoint where, uint32 transit, + const BMessage* dragMessage) +{ + if (fController->CloseButtonsAvailable()) { + BRect closeRect = _CloseRectFrame(Frame()); + bool overCloseRect = closeRect.Contains(where); + if (overCloseRect != fOverCloseRect) { + fOverCloseRect = overCloseRect; + ContainerView()->Invalidate(closeRect); + } + } + + fController->SetToolTip(Label()); + + TabView::MouseMoved(where, transit, dragMessage); +} + + +void +WebTabView::SetIcon(const BBitmap* icon) +{ + delete fIcon; + if (icon) + fIcon = new BBitmap(icon); + else + fIcon = NULL; + LayoutItem()->InvalidateLayout(); +} + + +BRect +WebTabView::_CloseRectFrame(BRect frame) const +{ + frame.left = frame.right - frame.Height(); + return frame; +} + + +void WebTabView::_DrawCloseButton(BView* owner, BRect& frame, + const BRect& updateRect, bool isFirst, bool isLast, bool isFront) +{ + BRect closeRect = _CloseRectFrame(frame); + frame.right = closeRect.left - be_control_look->DefaultLabelSpacing(); + + closeRect.left = (closeRect.left + closeRect.right) / 2 - 3; + closeRect.right = closeRect.left + 6; + closeRect.top = (closeRect.top + closeRect.bottom) / 2 - 3; + closeRect.bottom = closeRect.top + 6; + + rgb_color base = ui_color(B_PANEL_BACKGROUND_COLOR); + float tint = B_DARKEN_1_TINT; + if (!IsFront()) { + base = tint_color(base, tint); + tint *= 1.02; + } + + if (fOverCloseRect) + tint *= 1.2; + + if (fClicked && fOverCloseRect) { + BRect buttonRect(closeRect.InsetByCopy(-4, -4)); + be_control_look->DrawButtonFrame(owner, buttonRect, updateRect, + base, base, + BControlLook::B_ACTIVATED | BControlLook::B_BLEND_FRAME); + be_control_look->DrawButtonBackground(owner, buttonRect, updateRect, + base, BControlLook::B_ACTIVATED); + tint *= 1.2; + closeRect.OffsetBy(1, 1); + } + + base = tint_color(base, tint); + owner->SetHighColor(base); + owner->SetPenSize(2); + owner->StrokeLine(closeRect.LeftTop(), closeRect.RightBottom()); + owner->StrokeLine(closeRect.LeftBottom(), closeRect.RightTop()); + owner->SetPenSize(1); +} + + +// #pragma mark - TabManagerController + + +TabManagerController::TabManagerController(TabManager* manager) + : + fManager(manager), + fTabContainerGroup(NULL), + fCloseButtonsAvailable(false), + fDoubleClickOutsideTabsMessage(NULL) +{ +} + + +TabManagerController::~TabManagerController() +{ + delete fDoubleClickOutsideTabsMessage; +} + + +TabView* +TabManagerController::CreateTabView() +{ + return new WebTabView(this); +} + + +void +TabManagerController::DoubleClickOutsideTabs() +{ + fTarget.SendMessage(fDoubleClickOutsideTabsMessage); +} + + +void +TabManagerController::CloseTab(int32 index) +{ + fManager->CloseTab(index); +} + + +void +TabManagerController::SetDoubleClickOutsideTabsMessage(const BMessage& message, + const BMessenger& target) +{ + delete fDoubleClickOutsideTabsMessage; + fDoubleClickOutsideTabsMessage = new BMessage(message); + fTarget = target; +} + + +// #pragma mark - TabManager + + +TabManager::TabManager(const BMessenger& target, BMessage* newTabMessage) + : + fController(new TabManagerController(this)), + fTarget(target) +{ + fController->SetDoubleClickOutsideTabsMessage(*newTabMessage, + be_app_messenger); + + fContainerView = new BView("web view container", 0); + fCardLayout = new BCardLayout(); + fContainerView->SetLayout(fCardLayout); + + fTabContainerView = new TabContainerView(fController); + fTabContainerGroup = new TabContainerGroup(fTabContainerView); + fTabContainerGroup->GroupLayout()->SetInsets(0, 3, 0, 0); + + fController->SetTabContainerGroup(fTabContainerGroup); + +#if INTEGRATE_MENU_INTO_TAB_BAR + fMenu = new BMenu("Menu"); + BMenuBar* menuBar = new BMenuBar("Menu bar"); + menuBar->AddItem(fMenu); + TabButtonContainer* menuBarContainer = new TabButtonContainer(); + menuBarContainer->GroupLayout()->AddView(menuBar); + fTabContainerGroup->GroupLayout()->AddView(menuBarContainer, 0.0f); +#endif + + fTabContainerGroup->GroupLayout()->AddView(fTabContainerView); + fTabContainerGroup->AddScrollLeftButton(new ScrollLeftTabButton( + new BMessage(MSG_SCROLL_TABS_LEFT))); + fTabContainerGroup->AddScrollRightButton(new ScrollRightTabButton( + new BMessage(MSG_SCROLL_TABS_RIGHT))); + NewTabButton* newTabButton = new NewTabButton(newTabMessage); + newTabButton->SetTarget(be_app); + fTabContainerGroup->GroupLayout()->AddView(newTabButton, 0.0f); + fTabContainerGroup->AddTabMenuButton(new TabMenuTabButton( + new BMessage(MSG_OPEN_TAB_MENU))); +} + + +TabManager::~TabManager() +{ + delete fController; +} + + +void +TabManager::SetTarget(const BMessenger& target) +{ + fTarget = target; +} + + +const BMessenger& +TabManager::Target() const +{ + return fTarget; +} + + +#if INTEGRATE_MENU_INTO_TAB_BAR +BMenu* +TabManager::Menu() const +{ + return fMenu; +} +#endif + + +BView* +TabManager::TabGroup() const +{ + return fTabContainerGroup; +} + + +BView* +TabManager::GetTabContainerView() const +{ + return fTabContainerView; +} + + +BView* +TabManager::ContainerView() const +{ + return fContainerView; +} + + +BView* +TabManager::ViewForTab(int32 tabIndex) const +{ + BLayoutItem* item = fCardLayout->ItemAt(tabIndex); + if (item != NULL) + return item->View(); + return NULL; +} + + +int32 +TabManager::TabForView(const BView* containedView) const +{ + int32 count = fCardLayout->CountItems(); + for (int32 i = 0; i < count; i++) { + BLayoutItem* item = fCardLayout->ItemAt(i); + if (item->View() == containedView) + return i; + } + return -1; +} + + +bool +TabManager::HasView(const BView* containedView) const +{ + return TabForView(containedView) >= 0; +} + + +void +TabManager::SelectTab(int32 tabIndex) +{ + fCardLayout->SetVisibleItem(tabIndex); + fTabContainerView->SelectTab(tabIndex); + + BMessage message(TAB_CHANGED); + message.AddInt32("tab index", tabIndex); + fTarget.SendMessage(&message); +} + + +void +TabManager::SelectTab(const BView* containedView) +{ + int32 tabIndex = TabForView(containedView); + if (tabIndex > 0) + SelectTab(tabIndex); +} + + +int32 +TabManager::SelectedTabIndex() const +{ + return fCardLayout->VisibleIndex(); +} + + +void +TabManager::CloseTab(int32 tabIndex) +{ + BMessage message(CLOSE_TAB); + message.AddInt32("tab index", tabIndex); + fTarget.SendMessage(&message); +} + + +void +TabManager::AddTab(BView* view, const char* label, int32 index) +{ + fTabContainerView->AddTab(label, index); + fCardLayout->AddView(index, view); +} + + +BView* +TabManager::RemoveTab(int32 index) +{ + // It's important to remove the view first, since + // removing the tab will preliminary mess with the selected tab + // and then item count of card layout and tab container will not + // match yet. + BLayoutItem* item = fCardLayout->RemoveItem(index); + if (item == NULL) + return NULL; + + TabView* tab = fTabContainerView->RemoveTab(index); + delete tab; + + BView* view = item->View(); + delete item; + return view; +} + + +int32 +TabManager::CountTabs() const +{ + return fCardLayout->CountItems(); +} + + +void +TabManager::SetTabLabel(int32 tabIndex, const char* label) +{ + fTabContainerView->SetTabLabel(tabIndex, label); +} + +const BString& +TabManager::TabLabel(int32 tabIndex) +{ + TabView* tab = fTabContainerView->TabAt(tabIndex); + if (tab) + return tab->Label(); + else + return kEmptyString; +} + +void +TabManager::SetTabIcon(const BView* containedView, const BBitmap* icon) +{ + WebTabView* tab = dynamic_cast(fTabContainerView->TabAt( + TabForView(containedView))); + if (tab) + tab->SetIcon(icon); +} + + +void +TabManager::SetCloseButtonsAvailable(bool available) +{ + if (available == fController->CloseButtonsAvailable()) + return; + fController->SetCloseButtonsAvailable(available); + fTabContainerView->Invalidate(); +} + + diff --git a/src/apps/webpositive/tabview/TabManager.h b/src/apps/webpositive/tabview/TabManager.h new file mode 100644 index 0000000000..97cd65c898 --- /dev/null +++ b/src/apps/webpositive/tabview/TabManager.h @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2010 Stephan Aßmus + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef TAB_MANAGER_H +#define TAB_MANAGER_H + +#include +#include + +enum { + TAB_CHANGED = 'tcha', + CLOSE_TAB = 'cltb' +}; + +class BBitmap; +class BCardLayout; +class BGroupView; +class BMenu; +class TabContainerGroup; +class TabContainerView; +class TabManagerController; + +class TabManager { +public: + TabManager(const BMessenger& target, + BMessage* newTabMessage); + virtual ~TabManager(); + + void SetTarget(const BMessenger& target); + const BMessenger& Target() const; + +#if INTEGRATE_MENU_INTO_TAB_BAR + BMenu* Menu() const; +#endif + + BView* TabGroup() const; + BView* GetTabContainerView() const; + BView* ContainerView() const; + + BView* ViewForTab(int32 tabIndex) const; + int32 TabForView(const BView* containedView) const; + bool HasView(const BView* containedView) const; + + void SelectTab(int32 tabIndex); + void SelectTab(const BView* containedView); + int32 SelectedTabIndex() const; + void CloseTab(int32 tabIndex); + + void AddTab(BView* view, const char* label, + int32 index = -1); + BView* RemoveTab(int32 index); + int32 CountTabs() const; + + void SetTabLabel(int32 tabIndex, const char* label); + const BString& TabLabel(int32); + void SetTabIcon(const BView* containedView, + const BBitmap* icon); + void SetCloseButtonsAvailable(bool available); + +private: +#if INTEGRATE_MENU_INTO_TAB_BAR + BMenu* fMenu; +#endif + TabContainerGroup* fTabContainerGroup; + TabContainerView* fTabContainerView; + BView* fContainerView; + BCardLayout* fCardLayout; + TabManagerController* fController; + + BMessenger fTarget; +}; + +#endif // TAB_MANAGER_H diff --git a/src/apps/webpositive/tabview/TabView.cpp b/src/apps/webpositive/tabview/TabView.cpp new file mode 100644 index 0000000000..3bb78ec185 --- /dev/null +++ b/src/apps/webpositive/tabview/TabView.cpp @@ -0,0 +1,365 @@ +/* + * Copyright (C) 2010 Rene Gollent + * Copyright (C) 2010 Stephan Aßmus + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "TabView.h" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "TabContainerView.h" + + +// #pragma mark - TabView + + +TabView::TabView() + : + fContainerView(NULL), + fLayoutItem(new TabLayoutItem(this)), + fLabel() +{ +} + + +TabView::~TabView() +{ + // The layout item is deleted for us by the layout which contains it. + if (!fContainerView) + delete fLayoutItem; +} + + +BSize +TabView::MinSize() +{ + BSize size(MaxSize()); + size.width = 60.0f; + return size; +} + + +BSize +TabView::PreferredSize() +{ + return MaxSize(); +} + + +BSize +TabView::MaxSize() +{ + float extra = be_control_look->DefaultLabelSpacing(); + float labelWidth = 300.0f; + return BSize(labelWidth, _LabelHeight() + extra); +} + + +void +TabView::Draw(BRect updateRect) +{ + BRect frame(fLayoutItem->Frame()); + if (fIsFront) { + // Extend the front tab outward left/right in order to merge + // the frames of adjacent tabs. + if (!fIsFirst) + frame.left--; + if (!fIsLast) + frame.right++; + + frame.bottom++; + } + DrawBackground(fContainerView, frame, updateRect, fIsFirst, fIsLast, + fIsFront); + if (fIsFront) { + frame.top += 3.0f; + if (!fIsFirst) + frame.left++; + if (!fIsLast) + frame.right--; + } else + frame.top += 6.0f; + float spacing = be_control_look->DefaultLabelSpacing(); + frame.InsetBy(spacing, spacing / 2); + DrawContents(fContainerView, frame, updateRect, fIsFirst, fIsLast, + fIsFront); +} + + +void +TabView::DrawBackground(BView* owner, BRect frame, const BRect& updateRect, + bool isFirst, bool isLast, bool isFront) +{ + rgb_color base = ui_color(B_PANEL_BACKGROUND_COLOR); + uint32 borders = BControlLook::B_TOP_BORDER + | BControlLook::B_BOTTOM_BORDER; + if (isFirst) + borders |= BControlLook::B_LEFT_BORDER; + if (isLast) + borders |= BControlLook::B_RIGHT_BORDER; + if (isFront) { + be_control_look->DrawActiveTab(owner, frame, updateRect, base, + 0, borders); + } else { + be_control_look->DrawInactiveTab(owner, frame, updateRect, base, + 0, borders); + } +} + + +void +TabView::DrawContents(BView* owner, BRect frame, const BRect& updateRect, + bool isFirst, bool isLast, bool isFront) +{ + rgb_color base = ui_color(B_PANEL_BACKGROUND_COLOR); + be_control_look->DrawLabel(owner, fLabel.String(), frame, updateRect, + base, 0, BAlignment(B_ALIGN_LEFT, B_ALIGN_MIDDLE)); +} + + +void +TabView::MouseDown(BPoint where, uint32 buttons) +{ + fContainerView->SelectTab(this); +} + + +void +TabView::MouseUp(BPoint where) +{ +} + + +void +TabView::MouseMoved(BPoint where, uint32 transit, const BMessage* dragMessage) +{ +} + + +void +TabView::SetIsFront(bool isFront) +{ + Update(fIsFirst, fIsLast, isFront); +} + + +bool +TabView::IsFront() const +{ + return fIsFront; +} + + +void +TabView::SetIsLast(bool isLast) +{ + Update(fIsFirst, isLast, fIsFront); +} + + +void +TabView::Update(bool isFirst, bool isLast, bool isFront) +{ + if (fIsFirst == isFirst && fIsLast == isLast && fIsFront == isFront) + return; + fIsFirst = isFirst; + fIsLast = isLast; + fIsFront = isFront; + + fLayoutItem->InvalidateContainer(); +} + + +void +TabView::SetContainerView(TabContainerView* containerView) +{ + fContainerView = containerView; +} + + +TabContainerView* +TabView::ContainerView() const +{ + return fContainerView; +} + + +BLayoutItem* +TabView::LayoutItem() const +{ + return fLayoutItem; +} + + +void +TabView::SetLabel(const char* label) +{ + if (fLabel == label) + return; + fLabel = label; + fLayoutItem->InvalidateLayout(); +} + + +const BString& +TabView::Label() const +{ + return fLabel; +} + + +BRect +TabView::Frame() const +{ + return fLayoutItem->Frame(); +} + + +float +TabView::_LabelHeight() const +{ + font_height fontHeight; + fContainerView->GetFontHeight(&fontHeight); + return ceilf(fontHeight.ascent) + ceilf(fontHeight.descent); +} + + +// #pragma mark - TabLayoutItem + + +TabLayoutItem::TabLayoutItem(TabView* parent) + : + fParent(parent), + fVisible(true) +{ +} + + +bool +TabLayoutItem::IsVisible() +{ + return fVisible; +} + + +void +TabLayoutItem::SetVisible(bool visible) +{ + if (fVisible == visible) + return; + + fVisible = visible; + + InvalidateContainer(); + fParent->ContainerView()->InvalidateLayout(); +} + + +BRect +TabLayoutItem::Frame() +{ + return fFrame; +} + + +void +TabLayoutItem::SetFrame(BRect frame) +{ + BRect dirty = fFrame; + fFrame = frame; + dirty = dirty | fFrame; + InvalidateContainer(dirty); +} + + +BView* +TabLayoutItem::View() +{ + return NULL; +} + + +BSize +TabLayoutItem::BaseMinSize() +{ + return fParent->MinSize(); +} + + +BSize +TabLayoutItem::BaseMaxSize() +{ + return fParent->MaxSize(); +} + + +BSize +TabLayoutItem::BasePreferredSize() +{ + return fParent->PreferredSize(); +} + + +BAlignment +TabLayoutItem::BaseAlignment() +{ + return BAlignment(B_ALIGN_USE_FULL_WIDTH, B_ALIGN_USE_FULL_HEIGHT); +} + + +TabView* +TabLayoutItem::Parent() const +{ + return fParent; +} + + +void +TabLayoutItem::InvalidateContainer() +{ + InvalidateContainer(Frame()); +} + + +void +TabLayoutItem::InvalidateContainer(BRect frame) +{ + // Invalidate more than necessary, to help the TabContainerView + // redraw the parts outside any tabs... + frame.bottom++; + frame.right++; + fParent->ContainerView()->Invalidate(frame); +} diff --git a/src/apps/webpositive/tabview/TabView.h b/src/apps/webpositive/tabview/TabView.h new file mode 100644 index 0000000000..9f73c5a994 --- /dev/null +++ b/src/apps/webpositive/tabview/TabView.h @@ -0,0 +1,122 @@ +/* + * Copyright (C) 2010 Stephan Aßmus + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef TAB_VIEW_H +#define TAB_VIEW_H + +#include +#include +#include + + +class BMessage; +class BView; +class TabContainerView; +class TabLayoutItem; + + +class TabView { +public: + TabView(); + virtual ~TabView(); + + virtual BSize MinSize(); + virtual BSize PreferredSize(); + virtual BSize MaxSize(); + + void Draw(BRect updateRect); + virtual void DrawBackground(BView* owner, BRect frame, + const BRect& updateRect, bool isFirst, + bool isLast, bool isFront); + virtual void DrawContents(BView* owner, BRect frame, + const BRect& updateRect, bool isFirst, + bool isLast, bool isFront); + + virtual void MouseDown(BPoint where, uint32 buttons); + virtual void MouseUp(BPoint where); + virtual void MouseMoved(BPoint where, uint32 transit, + const BMessage* dragMessage); + + void SetIsFront(bool isFront); + bool IsFront() const; + void SetIsLast(bool isLast); + virtual void Update(bool isFirst, bool isLast, + bool isFront); + + BLayoutItem* LayoutItem() const; + void SetContainerView( + TabContainerView* containerView); + TabContainerView* ContainerView() const; + + void SetLabel(const char* label); + const BString& Label() const; + + BRect Frame() const; + +private: + float _LabelHeight() const; + +private: + TabContainerView* fContainerView; + TabLayoutItem* fLayoutItem; + + BString fLabel; + + bool fIsFirst; + bool fIsLast; + bool fIsFront; +}; + + +class TabLayoutItem : public BAbstractLayoutItem { +public: + TabLayoutItem(TabView* parent); + + virtual bool IsVisible(); + virtual void SetVisible(bool visible); + + virtual BRect Frame(); + virtual void SetFrame(BRect frame); + + virtual BView* View(); + + virtual BSize BaseMinSize(); + virtual BSize BaseMaxSize(); + virtual BSize BasePreferredSize(); + virtual BAlignment BaseAlignment(); + + TabView* Parent() const; + void InvalidateContainer(); + void InvalidateContainer(BRect frame); +private: + TabView* fParent; + BRect fFrame; + bool fVisible; +}; + + + +#endif // TAB_VIEW_H diff --git a/src/kits/interface/ScrollBar.cpp b/src/kits/interface/ScrollBar.cpp index bf8fa2dc75..a12fdd3b9d 100644 --- a/src/kits/interface/ScrollBar.cpp +++ b/src/kits/interface/ScrollBar.cpp @@ -642,6 +642,22 @@ BScrollBar::MessageReceived(BMessage* message) ValueChanged(value); break; } + case B_MOUSE_WHEEL_CHANGED: + { + // Must handle this here since BView checks for the existence of + // scrollbars, which a scrollbar itself does not have + float deltaX = 0.0f, deltaY = 0.0f; + message->FindFloat("be:wheel_delta_x", &deltaX); + message->FindFloat("be:wheel_delta_y", &deltaY); + + if (deltaX == 0.0f && deltaY == 0.0f) + break; + + if (deltaX != 0.0f && deltaY == 0.0f) + deltaY = deltaX; + + ScrollWithMouseWheelDelta(this, deltaY); + } default: BView::MessageReceived(message); break; diff --git a/src/kits/interface/View.cpp b/src/kits/interface/View.cpp index 87fa465557..efbe9549a6 100644 --- a/src/kits/interface/View.cpp +++ b/src/kits/interface/View.cpp @@ -4350,31 +4350,12 @@ BView::MessageReceived(BMessage* msg) if (deltaX == 0.0f && deltaY == 0.0f) break; - float smallStep, largeStep; if (horizontal != NULL) { - horizontal->GetSteps(&smallStep, &largeStep); - - // pressing the option/command/control key scrolls faster - if (modifiers() - & (B_OPTION_KEY | B_COMMAND_KEY | B_CONTROL_KEY)) { - deltaX *= largeStep; - } else - deltaX *= smallStep * 3; - - horizontal->SetValue(horizontal->Value() + deltaX); + ScrollWithMouseWheelDelta(horizontal, deltaX); } if (vertical != NULL) { - vertical->GetSteps(&smallStep, &largeStep); - - // pressing the option/command/control key scrolls faster - if (modifiers() - & (B_OPTION_KEY | B_COMMAND_KEY | B_CONTROL_KEY)) { - deltaY *= largeStep; - } else - deltaY *= smallStep * 3; - - vertical->SetValue(vertical->Value() + deltaY); + ScrollWithMouseWheelDelta(vertical, deltaY); } break; } @@ -5721,6 +5702,26 @@ BView::_SwitchServerCurrentView() const } +void +BView::ScrollWithMouseWheelDelta(BScrollBar* scrollBar, float delta) +{ + if (scrollBar == NULL || delta == 0.0f) + return; + + float smallStep, largeStep; + scrollBar->GetSteps(&smallStep, &largeStep); + + // pressing the option/command/control key scrolls faster + if (modifiers() + & (B_OPTION_KEY | B_COMMAND_KEY | B_CONTROL_KEY)) { + delta *= largeStep; + } else + delta *= smallStep * 3; + + scrollBar->SetValue(scrollBar->Value() + delta); +} + + #if __GNUC__ == 2 diff --git a/src/kits/shared/Jamfile b/src/kits/shared/Jamfile index 109df80924..d56a8b6fe2 100644 --- a/src/kits/shared/Jamfile +++ b/src/kits/shared/Jamfile @@ -29,6 +29,7 @@ StaticLibrary libshared.a : RWLockManager.cpp SHA256.cpp ShakeTrackingFilter.cpp + StringForRate.cpp StringForSize.cpp Variant.cpp ; diff --git a/src/kits/shared/StringForRate.cpp b/src/kits/shared/StringForRate.cpp new file mode 100644 index 0000000000..4f0c163526 --- /dev/null +++ b/src/kits/shared/StringForRate.cpp @@ -0,0 +1,61 @@ +/* + * Copyright 2012 Haiku Inc. All rights reserved. + * Distributed under the terms of the MIT License. + */ + +#include "StringForRate.h" + +#include + +#include + +using BPrivate::gSystemCatalog; + + +#undef B_TRANSLATION_CONTEXT +#define B_TRANSLATION_CONTEXT "StringForRate" + + +namespace BPrivate { + + +const char* +string_for_rate(double rate, char* string, size_t stringSize, double base) +{ + double kbps = rate / base; + if (kbps < 1.0) { + const char* trKey = B_TRANSLATE_MARK("%.0f bps"); + snprintf(string, stringSize, gSystemCatalog.GetString(trKey, + B_TRANSLATION_CONTEXT), (int)rate); + return string; + } + double mbps = kbps / base; + if (mbps < 1.0) { + const char* trKey = B_TRANSLATE_MARK("%.0f kbps"); + snprintf(string, stringSize, gSystemCatalog.GetString(trKey, + B_TRANSLATION_CONTEXT), kbps); + return string; + } + double gbps = mbps / base; + if (gbps < 1.0) { + const char* trKey = B_TRANSLATE_MARK("%.0f mbps"); + snprintf(string, stringSize, gSystemCatalog.GetString(trKey, + B_TRANSLATION_CONTEXT), mbps); + return string; + } + double tbps = gbps / base; + if (tbps < 1.0) { + const char* trKey = B_TRANSLATE_MARK("%.0f gbps"); + snprintf(string, stringSize, gSystemCatalog.GetString(trKey, + B_TRANSLATION_CONTEXT), gbps); + return string; + } + const char* trKey = B_TRANSLATE_MARK("%.0f tbps"); + snprintf(string, stringSize, gSystemCatalog.GetString(trKey, + B_TRANSLATION_CONTEXT), tbps); + return string; +} + + +} // namespace BPrivate + diff --git a/src/kits/tracker/FSUtils.cpp b/src/kits/tracker/FSUtils.cpp index ac2c1580e7..fb4e9d2fd0 100644 --- a/src/kits/tracker/FSUtils.cpp +++ b/src/kits/tracker/FSUtils.cpp @@ -634,20 +634,20 @@ ConfirmChangeIfWellKnownDirectory(const BEntry *entry, if (DirectoryMatchesOrContains(entry, B_SYSTEM_DIRECTORY)) { warning.SetTo( B_TRANSLATE("If you %ifYouDoAction the system folder or its " - "contents, you won't be able to boot %osName! Are you sure you " - "want to do this? To %toDoAction the system folder or its " + "contents, you won't be able to boot %osName!\n\nAre you sure you " + "want to do this?\n\nTo %toDoAction the system folder or its " "contents anyway, hold down the Shift key and click " "\"%toConfirmAction\".")); } else if (DirectoryMatches(entry, B_COMMON_DIRECTORY)) { warning.SetTo( B_TRANSLATE("If you %ifYouDoAction the common folder, %osName " - "may not behave properly! Are you sure you want to do this? " + "may not behave properly!\n\nAre you sure you want to do this?\n\n" "To %toDoAction the common folder anyway, hold down the " "Shift key and click \"%toConfirmAction\".")); } else if (DirectoryMatches(entry, B_USER_DIRECTORY)) { warning .SetTo( B_TRANSLATE("If you %ifYouDoAction the home folder, %osName " - "may not behave properly! Are you sure you want to do this? " + "may not behave properly!\n\nAre you sure you want to do this?\n\n" "To %toDoAction the home folder anyway, hold down the " "Shift key and click \"%toConfirmAction\".")); } else if (DirectoryMatchesOrContains(entry, B_USER_CONFIG_DIRECTORY) @@ -659,24 +659,21 @@ ConfirmChangeIfWellKnownDirectory(const BEntry *entry, B_COMMON_SETTINGS_DIRECTORY)) { warning.SetTo( B_TRANSLATE("If you %ifYouDoAction the mime settings, " - "%osName may not behave properly! Are you sure you want to " - "do this? To %toDoAction the mime settings anyway, click " - "\"%toConfirmAction\".")); + "%osName may not behave properly!\n\nAre you sure you want to " + "do this?")); requireOverride = false; } else if (DirectoryMatches(entry, B_USER_CONFIG_DIRECTORY)) { warning.SetTo( B_TRANSLATE("If you %ifYouDoAction the config folder, %osName " - "may not behave properly! Are you sure you want to do this? " - "To %toDoAction the config folder anyway, click " - "\"%toConfirmAction\".")); + "may not behave properly!\n\nAre you sure you want to do " + "this?")); requireOverride = false; } else if (DirectoryMatches(entry, B_USER_SETTINGS_DIRECTORY) || DirectoryMatches(entry, B_COMMON_SETTINGS_DIRECTORY)) { warning.SetTo( B_TRANSLATE("If you %ifYouDoAction the settings folder, " - "%osName may not behave properly! Are you sure you want to " - "do this? To %toDoAction the settings folder anyway, click " - "\"%toConfirmAction\".")); + "%osName may not behave properly!\n\nAre you sure you want to " + "do this?")); requireOverride = false; } } diff --git a/src/kits/tracker/OpenWithWindow.cpp b/src/kits/tracker/OpenWithWindow.cpp index 464a1eeb47..9b9b8ac478 100644 --- a/src/kits/tracker/OpenWithWindow.cpp +++ b/src/kits/tracker/OpenWithWindow.cpp @@ -681,8 +681,8 @@ OpenWithPoseView::OpenSelection(BPose *pose, int32 *) if (!fIterator->GenericFilesOnly()) { BString warning(B_TRANSLATE( "The application \"%appname\" does not support the type of " - "document you are about to open. Are you sure you want to " - "proceed? If you know that the application supports the " + "document you are about to open.\nAre you sure you want to " + "proceed?\n\nIf you know that the application supports the " "document type, you should contact the publisher of the " "application and ask them to update their application to list " "the type of your document as supported.")); diff --git a/src/preferences/media/MediaListItem.cpp b/src/preferences/media/MediaListItem.cpp index cfd6f51b09..2e1e508d7e 100644 --- a/src/preferences/media/MediaListItem.cpp +++ b/src/preferences/media/MediaListItem.cpp @@ -2,7 +2,7 @@ // // Copyright (c) 2003, OpenBeOS // -// This software is part of the OpenBeOS distribution and is covered +// This software is part of the OpenBeOS distribution and is covered // by the OpenBeOS license. // // @@ -10,7 +10,7 @@ // Author: Sikosis, Jérôme Duval // Description: Media Preferences // Created : June 25, 2003 -// +// // ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ #include "MediaListItem.h" @@ -70,13 +70,13 @@ struct MediaListItem::Renderer { { fDoubleInsets = doubleInset; } - + void Render(BView* onto, BRect frame, bool complete = false) { const rgb_color lowColor = onto->LowColor(); const rgb_color highColor = onto->HighColor(); const rgb_color kBlack = {0, 0, 0, 255}; - + if (fSelected || complete) { if (fSelected) onto->SetLowColor(tint_color(lowColor, B_DARKEN_2_TINT)); @@ -86,7 +86,7 @@ struct MediaListItem::Renderer { frame.left += 4; frame.top += kITEM_MARGIN; BRect iconFrame(MediaIcons::IconRectAt(frame.LeftTop() + BPoint(1, 0))); - + onto->SetDrawingMode(B_OP_OVER); if (fPrimaryIcon && !fDoubleInsets) { onto->DrawBitmap(fPrimaryIcon, iconFrame); @@ -103,7 +103,7 @@ struct MediaListItem::Renderer { onto->SetDrawingMode(B_OP_COPY); onto->SetHighColor(kBlack); - + BFont font = be_plain_font; font_height fontInfo; font.GetHeight(&fontInfo); @@ -130,8 +130,8 @@ struct MediaListItem::Renderer { width += iconSpace; width += 8.0f; // space between icons and text - - width += be_plain_font->StringWidth(fTitle) + 3.0f; + + width += be_plain_font->StringWidth(fTitle) + 16.0f; return width; } diff --git a/src/preferences/media/MediaWindow.cpp b/src/preferences/media/MediaWindow.cpp index 9d599b8529..a9efbc12ec 100644 --- a/src/preferences/media/MediaWindow.cpp +++ b/src/preferences/media/MediaWindow.cpp @@ -405,7 +405,7 @@ MediaWindow::InitWindow() BLayoutBuilder::Group<>(this, B_HORIZONTAL) .SetInsets(B_USE_DEFAULT_SPACING, B_USE_DEFAULT_SPACING, B_USE_DEFAULT_SPACING, B_USE_DEFAULT_SPACING) - .Add(scrollView) + .Add(scrollView, 0.0f) .AddGroup(B_VERTICAL) .SetInsets(0, 0, 0, 0) .Add(fTitleView) @@ -413,11 +413,10 @@ MediaWindow::InitWindow() // Start the window fInitCheck = InitMedia(true); - if (fInitCheck != B_OK) { + if (fInitCheck != B_OK) PostMessage(B_QUIT_REQUESTED); - } else if (IsHidden()) { + else if (IsHidden()) Show(); - } } @@ -753,7 +752,7 @@ MediaWindow::_MakeEmptyParamView() B_ALIGN_VERTICAL_CENTER); stringView->SetExplicitAlignment(centered); stringView->SetAlignment(B_ALIGN_CENTER); - + fContentLayout->AddView(stringView); fContentLayout->SetVisibleItem(fContentLayout->CountItems() - 1); diff --git a/src/system/kernel/arch/arm/arch_cpu.cpp b/src/system/kernel/arch/arm/arch_cpu.cpp index e225f9cef8..65d4795a80 100644 --- a/src/system/kernel/arch/arm/arch_cpu.cpp +++ b/src/system/kernel/arch/arm/arch_cpu.cpp @@ -161,6 +161,8 @@ arch_cpu_user_TLB_invalidate(void) } +// TODO: all functions that use fault handlers need to be implemented +// in assembly due to problems passing in label addresses in gcc4. status_t arch_cpu_user_memcpy(void *to, const void *from, size_t size, addr_t *faultHandler) diff --git a/src/system/kernel/arch/m68k/arch_cpu.cpp b/src/system/kernel/arch/m68k/arch_cpu.cpp index 235c5aabaf..488e905c19 100644 --- a/src/system/kernel/arch/m68k/arch_cpu.cpp +++ b/src/system/kernel/arch/m68k/arch_cpu.cpp @@ -171,6 +171,8 @@ arch_cpu_user_TLB_invalidate(void) } +// TODO: all functions that use fault handlers need to be implemented +// in assembly due to problems passing in label addresses in gcc4. status_t arch_cpu_user_memcpy(void *to, const void *from, size_t size, addr_t *faultHandler) diff --git a/src/system/kernel/arch/ppc/arch_cpu.cpp b/src/system/kernel/arch/ppc/arch_cpu.cpp index 79c62967f0..80a76d4294 100644 --- a/src/system/kernel/arch/ppc/arch_cpu.cpp +++ b/src/system/kernel/arch/ppc/arch_cpu.cpp @@ -172,6 +172,9 @@ arch_cpu_user_TLB_invalidate(void) } +// TODO: all functions that use fault handlers need to be implemented +// in assembly due to problems passing in label addresses in gcc4. + status_t arch_cpu_user_memcpy(void *to, const void *from, size_t size, addr_t *faultHandler) diff --git a/src/system/kernel/arch/x86/arch_cpu.cpp b/src/system/kernel/arch/x86/arch_cpu.cpp index 5ebb194bb1..8d44375dbf 100644 --- a/src/system/kernel/arch/x86/arch_cpu.cpp +++ b/src/system/kernel/arch/x86/arch_cpu.cpp @@ -4,6 +4,7 @@ * * Copyright 2001-2002, Travis Geiselbrecht. All rights reserved. * Distributed under the terms of the NewOS License. + * */ @@ -988,65 +989,6 @@ arch_cpu_invalidate_TLB_list(addr_t pages[], int num_pages) } -ssize_t -arch_cpu_user_strlcpy(char *to, const char *from, size_t size, - addr_t *faultHandler) -{ - int fromLength = 0; - addr_t oldFaultHandler = *faultHandler; - - // this check is to trick the gcc4 compiler and have it keep the error label - if (to == NULL && size > 0) - goto error; - - *faultHandler = (addr_t)&&error; - - if (size > 0) { - to[--size] = '\0'; - // copy - for ( ; size; size--, fromLength++, to++, from++) { - if ((*to = *from) == '\0') - break; - } - } - // count any leftover from chars - while (*from++ != '\0') { - fromLength++; - } - - *faultHandler = oldFaultHandler; - return fromLength; - -error: - *faultHandler = oldFaultHandler; - return B_BAD_ADDRESS; -} - - -status_t -arch_cpu_user_memset(void *s, char c, size_t count, addr_t *faultHandler) -{ - char *xs = (char *)s; - addr_t oldFaultHandler = *faultHandler; - - // this check is to trick the gcc4 compiler and have it keep the error label - if (s == NULL) - goto error; - - *faultHandler = (addr_t)&&error; - - while (count--) - *xs++ = c; - - *faultHandler = oldFaultHandler; - return 0; - -error: - *faultHandler = oldFaultHandler; - return B_BAD_ADDRESS; -} - - status_t arch_cpu_shutdown(bool rebootSystem) { diff --git a/src/system/kernel/arch/x86/arch_x86.S b/src/system/kernel/arch/x86/arch_x86.S index 28611fe247..1ebcae0e82 100644 --- a/src/system/kernel/arch/x86/arch_x86.S +++ b/src/system/kernel/arch/x86/arch_x86.S @@ -1,5 +1,6 @@ /* * Copyright 2003-2007, Axel Dörfler, axeld@pinc-software.de. + * Copyright 2012, Rene Gollent, rene@gollent.com. * Distributed under the terms of the MIT License. * * Copyright 2001, Travis Geiselbrecht. All rights reserved. @@ -219,6 +220,113 @@ FUNCTION(arch_cpu_user_memcpy): FUNCTION_END(arch_cpu_user_memcpy) +/* status_t arch_cpu_user_memset(void *to, char c, size_t count, addr_t *faultHandler) */ +FUNCTION(arch_cpu_user_memset): + pushl %esi + pushl %edi + movl 12(%esp),%edi /* dest */ + movb 16(%esp),%al /* c */ + movl 20(%esp),%ecx /* count */ + + /* set the fault handler */ + movl 24(%esp),%edx /* fault handler */ + movl (%edx),%esi + movl $.L_user_memset_error, (%edx) + + rep + stosb + + /* restore the old fault handler */ + movl %esi,(%edx) + xor %eax,%eax + + popl %edi + popl %esi + ret + + /* error condition */ +.L_user_memset_error: + /* restore the old fault handler */ + movl %esi,(%edx) + movl $-1,%eax /* return a generic error, the wrapper routine will deal with it */ + popl %edi + popl %esi + ret +FUNCTION_END(arch_cpu_user_memset) + + +/* ssize_t arch_cpu_user_strlcpy(void *to, const void *from, size_t size, addr_t *faultHandler) */ +FUNCTION(arch_cpu_user_strlcpy): + pushl %esi + pushl %edi + pushl %ebx + movl 16(%esp),%edi /* dest */ + movl 20(%esp),%esi /* source */ + movl 24(%esp),%ecx /* count */ + + /* set the fault handler */ + movl 28(%esp),%edx /* fault handler */ + movl (%edx),%ebx + movl $.L_user_strlcpy_error, (%edx) + + /* Check for 0 length */ + cmp $0,%ecx + je .L_user_strlcpy_source_count + + /* Copy at most count - 1 bytes */ + dec %ecx + + /* If count is now 0, skip straight to null terminating + as our loop will otherwise overflow */ + jnz .L_user_strlcpy_copy_begin + movb $0,(%edi) + jmp .L_user_strlcpy_source_count + +.L_user_strlcpy_copy_begin: + cld +.L_user_strlcpy_copy_loop: + /* move data by bytes */ + lodsb + stosb + test %al,%al + jz .L_user_strlcpy_source_done + loop .L_user_strlcpy_copy_loop + + /* null terminate string */ + movb $0,(%edi) + dec %esi + + /* count remaining bytes in src */ +.L_user_strlcpy_source_count: + not %ecx + xor %al,%al + repnz + scasb + +.L_user_strlcpy_source_done: + movl %esi,%eax + subl 20(%esp),%eax + dec %eax + /* restore the old fault handler */ + movl %ebx,(%edx) + + popl %ebx + popl %edi + popl %esi + ret + + /* error condition */ +.L_user_strlcpy_error: + /* restore the old fault handler */ + movl %ebx,(%edx) + movl $-1,%eax /* return a generic error, the wrapper routine will deal with it */ + popl %ebx + popl %edi + popl %esi + ret +FUNCTION_END(arch_cpu_user_strlcpy) + + /*! \fn void arch_debug_call_with_fault_handler(cpu_ent* cpu, jmp_buf jumpBuffer, void (*function)(void*), void* parameter) diff --git a/src/system/kernel/debug/debug_heap.cpp b/src/system/kernel/debug/debug_heap.cpp index 069e97d6f1..996f5213b9 100644 --- a/src/system/kernel/debug/debug_heap.cpp +++ b/src/system/kernel/debug/debug_heap.cpp @@ -278,6 +278,19 @@ debug_malloc(size_t size) } +void* +debug_calloc(size_t num, size_t size) +{ + size_t allocationSize = num * size; + void* allocation = debug_malloc(allocationSize); + if (allocation == NULL) + return NULL; + + memset(allocation, 0, allocationSize); + return allocation; +} + + void debug_free(void* address) { diff --git a/src/system/kernel/debug/font.h b/src/system/kernel/debug/font.h index 502c77c1ea..9258dd4743 100644 --- a/src/system/kernel/debug/font.h +++ b/src/system/kernel/debug/font.h @@ -46,9 +46,9 @@ static unsigned char FONT[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* ' ' */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* ' ' */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* ' ' */ -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* ' ' */ -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* ' ' */ -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* ' ' */ +0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* ' ' */ +0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* ' ' */ +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* ' ' */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* ' ' */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* ' ' */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* ' ' */ diff --git a/src/system/kernel/vm/vm.cpp b/src/system/kernel/vm/vm.cpp index 857bf22808..e289def87f 100644 --- a/src/system/kernel/vm/vm.cpp +++ b/src/system/kernel/vm/vm.cpp @@ -5104,8 +5104,10 @@ user_strlcpy(char* to, const char* from, size_t size) &thread_get_current_thread()->fault_handler); // If we hit the address overflow boundary, fail. - if (result >= 0 && (size_t)result >= maxSize && maxSize < size) + if (result < 0 || (result >= 0 && (size_t)result >= maxSize + && maxSize < size)) { return B_BAD_ADDRESS; + } return result; } diff --git a/src/system/kernel/vm/vm_page.cpp b/src/system/kernel/vm/vm_page.cpp index e7740ddeb6..9e4c591779 100644 --- a/src/system/kernel/vm/vm_page.cpp +++ b/src/system/kernel/vm/vm_page.cpp @@ -714,7 +714,7 @@ public: private: bool fPrintStackTrace; page_num_t fPageFilter; - team_id fTeamFilter; + team_id fTeamFilter; thread_id fThreadFilter; }; @@ -1530,7 +1530,7 @@ free_page(vm_page* page, bool clear) TA(FreePage(page->physical_page_number)); #if VM_PAGE_ALLOCATION_TRACKING_AVAILABLE - page->allocation_tracking_info.Clear(); + page->allocation_tracking_info.Clear(); #endif ReadLocker locker(sFreePageQueuesLock); @@ -2295,12 +2295,12 @@ status_t page_writer(void* /*unused*/) { const uint32 kNumPages = 256; - uint32 writtenPages = 0; #ifdef TRACE_VM_PAGE + uint32 writtenPages = 0; bigtime_t lastWrittenTime = 0; -#endif bigtime_t pageCollectionTime = 0; bigtime_t pageWritingTime = 0; +#endif PageWriterRun run; if (run.Init(kNumPages) != B_OK) { @@ -2356,7 +2356,9 @@ page_writer(void* /*unused*/) // enough to do). // collect pages to be written +#ifdef TRACE_VM_PAGE pageCollectionTime -= system_time(); +#endif page_num_t maxPagesToSee = modifiedPages; @@ -2429,31 +2431,35 @@ page_writer(void* /*unused*/) numPages++; } +#ifdef TRACE_VM_PAGE pageCollectionTime += system_time(); - +#endif if (numPages == 0) continue; // write pages to disk and do all the cleanup +#ifdef TRACE_VM_PAGE pageWritingTime -= system_time(); +#endif uint32 failedPages = run.Go(); +#ifdef TRACE_VM_PAGE pageWritingTime += system_time(); // debug output only... writtenPages += numPages; if (writtenPages >= 1024) { -#ifdef TRACE_VM_PAGE bigtime_t now = system_time(); TRACE(("page writer: wrote 1024 pages (total: %llu ms, " "collect: %llu ms, write: %llu ms)\n", (now - lastWrittenTime) / 1000, pageCollectionTime / 1000, pageWritingTime / 1000)); lastWrittenTime = now; -#endif + writtenPages -= 1024; pageCollectionTime = 0; pageWritingTime = 0; } +#endif if (failedPages == numPages) pagesSinceLastSuccessfulWrite += modifiedPages - maxPagesToSee; diff --git a/src/tests/system/boot/loader/Jamfile b/src/tests/system/boot/loader/Jamfile index f68a9411c3..3378873b0d 100644 --- a/src/tests/system/boot/loader/Jamfile +++ b/src/tests/system/boot/loader/Jamfile @@ -2,11 +2,11 @@ SubDir HAIKU_TOP src tests system boot loader ; SetSubDirSupportedPlatformsBeOSCompatible ; -UsePrivateHeaders [ FDirName kernel ] ; UsePrivateHeaders [ FDirName kernel disk_device_manager ] ; -UsePrivateHeaders [ FDirName storage ] ; -UsePrivateHeaders [ FDirName shared ] ; +UsePrivateHeaders [ FDirName kernel util ] ; +UsePrivateHeaders kernel shared storage system ; SubDirSysHdrs $(HAIKU_TOP) headers private kernel arch $(TARGET_ARCH) ; +SubDirSysHdrs $(HAIKU_TOP) headers private system arch $(TARGET_ARCH) ; SubDirSysHdrs $(HAIKU_TOP) src tests system boot loader ; SubDirHdrs $(HAIKU_TOP) src system boot loader ; SubDirHdrs $(HAIKU_TOP) src add-ons kernel partitioning_systems amiga ; @@ -34,6 +34,7 @@ ObjectDefines loader.cpp kernel_args.cpp load_driver_settings.cpp + pager.cpp # other platform_menu.cpp @@ -79,6 +80,7 @@ ObjectDefines SimpleTest BootLoaderTest : # userland bindings platform_start.cpp + platform_console.cpp platform_debug.cpp platform_devices.cpp platform_heap.cpp @@ -97,6 +99,7 @@ SimpleTest BootLoaderTest : loader.cpp kernel_args.cpp load_driver_settings.cpp + pager.cpp # partitioning systems amiga_rdb.cpp @@ -106,19 +109,20 @@ SimpleTest BootLoaderTest : PartitionMap.cpp PartitionMapParser.cpp - # utility functions - Dano has a strlcpy() function in libroot.so, while R5 has not + # utility functions list.cpp - strlcat.c - strlcpy.c driver_settings.cpp + ring_buffer.cpp KMessage.cpp - : boottest_bfs.a boottest_amiga_ffs.a boottest_tarfs.a libz.a - ; + : + boottest_bfs.a boottest_amiga_ffs.a boottest_tarfs.a libz.a + kernelland_emu +; # Tell Jam where to find the utility sources -SEARCH on [ FGristFiles list.cpp ] +SEARCH on [ FGristFiles list.cpp ring_buffer.cpp ] = [ FDirName $(HAIKU_TOP) src system kernel util ] ; SEARCH on [ FGristFiles KMessage.cpp ] @@ -134,7 +138,7 @@ SEARCH on [ FGristFiles main.cpp vfs.cpp partitions.cpp heap.cpp RootFileSystem.cpp elf.cpp menu.cpp loader.cpp - kernel_args.cpp load_driver_settings.cpp + kernel_args.cpp load_driver_settings.cpp pager.cpp ] = [ FDirName $(HAIKU_TOP) src system boot loader ] ; # partitioning system modules diff --git a/src/tests/system/boot/loader/file_systems/amiga_ffs/Jamfile b/src/tests/system/boot/loader/file_systems/amiga_ffs/Jamfile index 84fc846197..afee6b72ec 100644 --- a/src/tests/system/boot/loader/file_systems/amiga_ffs/Jamfile +++ b/src/tests/system/boot/loader/file_systems/amiga_ffs/Jamfile @@ -2,7 +2,8 @@ SubDir HAIKU_TOP src tests system boot loader file_systems amiga_ffs ; SetSubDirSupportedPlatformsBeOSCompatible ; -UsePrivateHeaders [ FDirName kernel ] ; +UsePrivateKernelHeaders ; +UsePrivateSystemHeaders ; UsePrivateHeaders [ FDirName kernel disk_device_manager ] ; UsePrivateHeaders [ FDirName storage ] ; SubDirSysHdrs $(HAIKU_TOP) src tests system boot loader ; @@ -13,7 +14,7 @@ if $(TARGET_PLATFORM) != haiku { UsePublicHeaders storage ; # DiskDeviceDefs.h } -ObjectDefines +ObjectDefines amiga_ffs.cpp Volume.cpp Directory.cpp diff --git a/src/tests/system/boot/loader/file_systems/bfs/Jamfile b/src/tests/system/boot/loader/file_systems/bfs/Jamfile index 1ed8911c4a..9adcf06ba9 100644 --- a/src/tests/system/boot/loader/file_systems/bfs/Jamfile +++ b/src/tests/system/boot/loader/file_systems/bfs/Jamfile @@ -2,7 +2,8 @@ SubDir HAIKU_TOP src tests system boot loader file_systems bfs ; SetSubDirSupportedPlatformsBeOSCompatible ; -UsePrivateHeaders [ FDirName kernel ] ; +UsePrivateKernelHeaders ; +UsePrivateSystemHeaders ; UsePrivateHeaders [ FDirName kernel disk_device_manager ] ; UsePrivateHeaders [ FDirName storage ] ; SubDirSysHdrs $(HAIKU_TOP) src tests system boot loader ; diff --git a/src/tests/system/boot/loader/file_systems/tarfs/Jamfile b/src/tests/system/boot/loader/file_systems/tarfs/Jamfile index 874d75b5bc..bc94520bd3 100644 --- a/src/tests/system/boot/loader/file_systems/tarfs/Jamfile +++ b/src/tests/system/boot/loader/file_systems/tarfs/Jamfile @@ -2,8 +2,10 @@ SubDir HAIKU_TOP src tests system boot loader file_systems tarfs ; SetSubDirSupportedPlatformsBeOSCompatible ; +UsePrivateKernelHeaders ; +UsePrivateSystemHeaders ; UsePrivateHeaders [ FDirName kernel disk_device_manager ] ; -UsePrivateHeaders kernel shared storage ; +UsePrivateHeaders shared storage ; SubDirSysHdrs $(HAIKU_TOP) src tests system boot loader ; SubDirSysHdrs $(HAIKU_TOP) headers libs zlib ; SubDirHdrs $(HAIKU_TOP) src system boot loader file_systems tarfs ; @@ -15,7 +17,7 @@ if $(TARGET_PLATFORM) != haiku { ObjectDefines tarfs.cpp : B_BUFFER_OVERFLOW=B_BAD_VALUE ; } -ObjectDefines +ObjectDefines tarfs.cpp : read_pos=boot_read_pos fstat=boot_fstat open=boot_open close=boot_close _BOOT_MODE diff --git a/src/tests/system/boot/loader/platform_console.cpp b/src/tests/system/boot/loader/platform_console.cpp new file mode 100644 index 0000000000..50512ae85f --- /dev/null +++ b/src/tests/system/boot/loader/platform_console.cpp @@ -0,0 +1,65 @@ +/* + * Copyright 2012, Axel Dörfler, axeld@pinc-software.de. + * Distributed under the terms of the MIT License. + */ + + +#include + + +void +console_clear_screen(void) +{ +} + + +int32 +console_width(void) +{ + return 80; +} + + +int32 +console_height(void) +{ + return 25; +} + + +void +console_set_cursor(int32 x, int32 y) +{ +} + + +void +console_show_cursor(void) +{ +} + + +void +console_hide_cursor(void) +{ +} + + +void +console_set_color(int32 foreground, int32 background) +{ +} + + +int +console_wait_for_key(void) +{ + return 0; +} + + +status_t +console_init(void) +{ + return B_OK; +} diff --git a/src/tests/system/boot/loader/platform_debug.cpp b/src/tests/system/boot/loader/platform_debug.cpp index 500293ca76..a5a8696474 100644 --- a/src/tests/system/boot/loader/platform_debug.cpp +++ b/src/tests/system/boot/loader/platform_debug.cpp @@ -1,7 +1,7 @@ /* -** Copyright 2003, Axel Dörfler, axeld@pinc-software.de. All rights reserved. -** Distributed under the terms of the OpenBeOS License. -*/ + * Copyright 2003-2012, Axel Dörfler, axeld@pinc-software.de. + * Distributed under the terms of the MIT License. + */ #include @@ -32,3 +32,9 @@ dprintf(const char *format, ...) va_end(args); } + +char* +platform_debug_get_log_buffer(size_t* _size) +{ + return NULL; +} diff --git a/src/tests/system/boot/loader/platform_menu.cpp b/src/tests/system/boot/loader/platform_menu.cpp index a028d653d1..b6ef143da6 100644 --- a/src/tests/system/boot/loader/platform_menu.cpp +++ b/src/tests/system/boot/loader/platform_menu.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2004-2005, Axel Dörfler, axeld@pinc-software.de. All rights reserved. + * Copyright 2004-2012, Axel Dörfler, axeld@pinc-software.de. * Distributed under the terms of the MIT License. */ @@ -40,7 +40,7 @@ print_item_at(int32 line, MenuItem *item, bool clearHelp = true) printf(" "); printf(item->Label()); - + if (item->Submenu() && item->Submenu()->Type() == CHOICE_MENU) { // show the current choice (if any) Menu *subMenu = item->Submenu(); @@ -165,11 +165,16 @@ platform_run_menu(Menu *menu) char buffer[32]; if (fgets(buffer, sizeof(buffer), stdin) == NULL) return; - + if (buffer[0] != '\n') selected = atoi(buffer); item = menu->ItemAt(selected); + if (item == NULL) { + printf("Invalid choice."); + continue; + } + item->Select(true); // leave the menu @@ -194,3 +199,10 @@ platform_run_menu(Menu *menu) } } + +size_t +platform_get_user_input_text(Menu* menu, MenuItem* item, char* buffer, + size_t bufferSize) +{ + return 0; +} diff --git a/src/tests/system/boot/loader/platform_start.cpp b/src/tests/system/boot/loader/platform_start.cpp index 27c9ddb19d..cc379b5b4c 100644 --- a/src/tests/system/boot/loader/platform_start.cpp +++ b/src/tests/system/boot/loader/platform_start.cpp @@ -35,7 +35,8 @@ main(int argc, char **argv) { // The command arguments are evaluated in platform_devices.cpp! - boot_main(NULL); + stage2_args args; + boot_main(&args); return 0; }