ColumnListView.cpp 116 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530353135323533353435353536353735383539354035413542354335443545354635473548354935503551355235533554355535563557355835593560356135623563356435653566356735683569357035713572357335743575357635773578357935803581358235833584358535863587358835893590359135923593359435953596359735983599360036013602360336043605360636073608360936103611361236133614361536163617361836193620362136223623362436253626362736283629363036313632363336343635363636373638363936403641364236433644364536463647364836493650365136523653365436553656365736583659366036613662366336643665366636673668366936703671367236733674367536763677367836793680368136823683368436853686368736883689369036913692369336943695369636973698369937003701370237033704370537063707370837093710371137123713371437153716371737183719372037213722372337243725372637273728372937303731373237333734373537363737373837393740374137423743374437453746374737483749375037513752375337543755375637573758375937603761376237633764376537663767376837693770377137723773377437753776377737783779378037813782378337843785378637873788378937903791379237933794379537963797379837993800380138023803380438053806380738083809381038113812381338143815381638173818381938203821382238233824382538263827382838293830383138323833383438353836383738383839384038413842384338443845384638473848384938503851385238533854385538563857385838593860386138623863386438653866386738683869387038713872387338743875387638773878387938803881388238833884388538863887388838893890389138923893389438953896389738983899390039013902390339043905390639073908390939103911391239133914391539163917391839193920392139223923392439253926392739283929393039313932393339343935393639373938393939403941394239433944394539463947394839493950395139523953395439553956395739583959396039613962396339643965396639673968396939703971397239733974397539763977397839793980398139823983398439853986398739883989399039913992399339943995399639973998399940004001400240034004400540064007400840094010401140124013401440154016401740184019402040214022402340244025402640274028402940304031403240334034403540364037403840394040404140424043404440454046404740484049405040514052405340544055405640574058405940604061406240634064406540664067406840694070407140724073407440754076407740784079408040814082408340844085408640874088408940904091409240934094409540964097409840994100410141024103410441054106410741084109411041114112411341144115411641174118411941204121412241234124412541264127412841294130413141324133413441354136413741384139414041414142414341444145414641474148414941504151415241534154415541564157415841594160416141624163416441654166416741684169417041714172417341744175417641774178417941804181418241834184418541864187418841894190419141924193419441954196419741984199420042014202420342044205420642074208420942104211421242134214421542164217421842194220422142224223422442254226422742284229423042314232423342344235423642374238423942404241424242434244424542464247424842494250425142524253425442554256425742584259426042614262426342644265426642674268426942704271427242734274427542764277427842794280428142824283428442854286428742884289429042914292429342944295429642974298429943004301430243034304430543064307430843094310431143124313431443154316431743184319432043214322432343244325432643274328432943304331433243334334433543364337433843394340434143424343434443454346434743484349435043514352435343544355435643574358435943604361436243634364436543664367436843694370437143724373437443754376437743784379438043814382438343844385438643874388438943904391439243934394439543964397439843994400440144024403440444054406440744084409441044114412441344144415441644174418441944204421442244234424442544264427442844294430443144324433443444354436443744384439444044414442444344444445444644474448444944504451445244534454445544564457445844594460446144624463446444654466446744684469447044714472447344744475447644774478447944804481448244834484448544864487448844894490449144924493449444954496449744984499450045014502450345044505450645074508450945104511451245134514451545164517451845194520452145224523452445254526452745284529453045314532453345344535453645374538453945404541454245434544454545464547454845494550455145524553455445554556455745584559456045614562456345644565456645674568456945704571457245734574457545764577457845794580458145824583458445854586458745884589459045914592459345944595459645974598459946004601460246034604460546064607460846094610461146124613461446154616461746184619462046214622462346244625462646274628462946304631463246334634463546364637463846394640464146424643464446454646464746484649465046514652465346544655465646574658465946604661466246634664466546664667466846694670467146724673467446754676467746784679468046814682468346844685468646874688468946904691469246934694469546964697469846994700470147024703470447054706470747084709471047114712471347144715471647174718471947204721472247234724472547264727472847294730473147324733473447354736473747384739474047414742474347444745474647474748474947504751475247534754475547564757475847594760476147624763476447654766476747684769477047714772477347744775477647774778477947804781478247834784478547864787478847894790479147924793479447954796479747984799480048014802480348044805480648074808480948104811481248134814481548164817481848194820482148224823482448254826482748284829483048314832483348344835483648374838483948404841484248434844484548464847484848494850485148524853485448554856485748584859486048614862486348644865486648674868486948704871487248734874487548764877487848794880488148824883488448854886488748884889489048914892489348944895489648974898489949004901490249034904490549064907490849094910491149124913491449154916491749184919492049214922492349244925492649274928492949304931493249334934493549364937493849394940494149424943494449454946
  1. /*
  2. Open Tracker License
  3. Terms and Conditions
  4. Copyright (c) 1991-2000, Be Incorporated. All rights reserved.
  5. Permission is hereby granted, free of charge, to any person obtaining a copy of
  6. this software and associated documentation files (the "Software"), to deal in
  7. the Software without restriction, including without limitation the rights to
  8. use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
  9. of the Software, and to permit persons to whom the Software is furnished to do
  10. so, subject to the following conditions:
  11. The above copyright notice and this permission notice applies to all licensees
  12. and shall be included in all copies or substantial portions of the Software.
  13. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  14. IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF TITLE, MERCHANTABILITY,
  15. FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
  16. BE INCORPORATED BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
  17. AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF, OR IN CONNECTION
  18. WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  19. Except as contained in this notice, the name of Be Incorporated shall not be
  20. used in advertising or otherwise to promote the sale, use or other dealings in
  21. this Software without prior written authorization from Be Incorporated.
  22. Tracker(TM), Be(R), BeOS(R), and BeIA(TM) are trademarks or registered trademarks
  23. of Be Incorporated in the United States and other countries. Other brand product
  24. names are registered trademarks or trademarks of their respective holders.
  25. All rights reserved.
  26. */
  27. /*******************************************************************************
  28. /
  29. / File: ColumnListView.cpp
  30. /
  31. / Description: Experimental multi-column list view.
  32. /
  33. / Copyright 2000+, Be Incorporated, All Rights Reserved
  34. / By Jeff Bush
  35. /
  36. *******************************************************************************/
  37. #include "ColumnListView.h"
  38. #include <typeinfo>
  39. #include <stdio.h>
  40. #include <stdlib.h>
  41. #include <Application.h>
  42. #include <Bitmap.h>
  43. #include <ControlLook.h>
  44. #include <Cursor.h>
  45. #include <Debug.h>
  46. #include <GraphicsDefs.h>
  47. #include <LayoutUtils.h>
  48. #include <MenuItem.h>
  49. #include <PopUpMenu.h>
  50. #include <Region.h>
  51. #include <ScrollBar.h>
  52. #include <String.h>
  53. #include <SupportDefs.h>
  54. #include <Window.h>
  55. #include "ObjectListPrivate.h"
  56. #include "ColorTools.h"
  57. #include "ObjectList.h"
  58. #define DOUBLE_BUFFERED_COLUMN_RESIZE 1
  59. #define SMART_REDRAW 1
  60. #define DRAG_TITLE_OUTLINE 1
  61. #define CONSTRAIN_CLIPPING_REGION 1
  62. #define LOWER_SCROLLBAR 0
  63. namespace BPrivate {
  64. static const unsigned char kDownSortArrow8x8[] = {
  65. 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff,
  66. 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff,
  67. 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff,
  68. 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff,
  69. 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff,
  70. 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff,
  71. 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff,
  72. 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff
  73. };
  74. static const unsigned char kUpSortArrow8x8[] = {
  75. 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff,
  76. 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff,
  77. 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff,
  78. 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff,
  79. 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff,
  80. 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff,
  81. 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff,
  82. 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff
  83. };
  84. static const unsigned char kDownSortArrow8x8Invert[] = {
  85. 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
  86. 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0xff,
  87. 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
  88. 0xff, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0xff, 0xff,
  89. 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
  90. 0xff, 0xff, 0x1f, 0x1f, 0x1f, 0xff, 0xff, 0xff,
  91. 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
  92. 0xff, 0xff, 0xff, 0x1f, 0xff, 0xff, 0xff, 0xff
  93. };
  94. static const unsigned char kUpSortArrow8x8Invert[] = {
  95. 0xff, 0xff, 0xff, 0x1f, 0xff, 0xff, 0xff, 0xff,
  96. 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
  97. 0xff, 0xff, 0x1f, 0x1f, 0x1f, 0xff, 0xff, 0xff,
  98. 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
  99. 0xff, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0xff, 0xff,
  100. 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
  101. 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0xff,
  102. 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
  103. };
  104. static const float kTintedLineTint = 1.04;
  105. static const float kTitleHeight = 16.0;
  106. static const float kLatchWidth = 15.0;
  107. static const rgb_color kColor[B_COLOR_TOTAL] =
  108. {
  109. {255, 255, 255, 255}, // B_COLOR_BACKGROUND
  110. { 0, 0, 0, 255}, // B_COLOR_TEXT
  111. {148, 148, 148, 255}, // B_COLOR_ROW_DIVIDER
  112. {190, 190, 190, 255}, // B_COLOR_SELECTION
  113. { 0, 0, 0, 255}, // B_COLOR_SELECTION_TEXT
  114. {200, 200, 200, 255}, // B_COLOR_NON_FOCUS_SELECTION
  115. {180, 180, 180, 180}, // B_COLOR_EDIT_BACKGROUND
  116. { 0, 0, 0, 255}, // B_COLOR_EDIT_TEXT
  117. {215, 215, 215, 255}, // B_COLOR_HEADER_BACKGROUND
  118. { 0, 0, 0, 255}, // B_COLOR_HEADER_TEXT
  119. { 0, 0, 0, 255}, // B_COLOR_SEPARATOR_LINE
  120. { 0, 0, 0, 255}, // B_COLOR_SEPARATOR_BORDER
  121. };
  122. static const int32 kMaxDepth = 1024;
  123. static const float kLeftMargin = kLatchWidth;
  124. static const float kRightMargin = 8;
  125. static const float kOutlineLevelIndent = kLatchWidth;
  126. static const float kColumnResizeAreaWidth = 10.0;
  127. static const float kRowDragSensitivity = 5.0;
  128. static const float kDoubleClickMoveSensitivity = 4.0;
  129. static const float kSortIndicatorWidth = 9.0;
  130. static const float kDropHighlightLineHeight = 2.0;
  131. static const uint32 kToggleColumn = 'BTCL';
  132. class BRowContainer : public BObjectList<BRow>
  133. {
  134. };
  135. class TitleView : public BView {
  136. typedef BView _inherited;
  137. public:
  138. TitleView(BRect frame, OutlineView* outlineView,
  139. BList* visibleColumns, BList* sortColumns,
  140. BColumnListView* masterView,
  141. uint32 resizingMode);
  142. virtual ~TitleView();
  143. void ColumnAdded(BColumn* column);
  144. void ColumnResized(BColumn* column, float oldWidth);
  145. void SetColumnVisible(BColumn* column, bool visible);
  146. virtual void Draw(BRect updateRect);
  147. virtual void ScrollTo(BPoint where);
  148. virtual void MessageReceived(BMessage* message);
  149. virtual void MouseDown(BPoint where);
  150. virtual void MouseMoved(BPoint where, uint32 transit,
  151. const BMessage* dragMessage);
  152. virtual void MouseUp(BPoint where);
  153. virtual void FrameResized(float width, float height);
  154. void MoveColumn(BColumn* column, int32 index);
  155. void SetColumnFlags(column_flags flags);
  156. void SetEditMode(bool state)
  157. { fEditMode = state; }
  158. float MarginWidth() const;
  159. private:
  160. void GetTitleRect(BColumn* column, BRect* _rect);
  161. int32 FindColumn(BPoint where, float* _leftEdge);
  162. void FixScrollBar(bool scrollToFit);
  163. void DragSelectedColumn(BPoint where);
  164. void ResizeSelectedColumn(BPoint where,
  165. bool preferred = false);
  166. void ComputeDragBoundries(BColumn* column,
  167. BPoint where);
  168. void DrawTitle(BView* view, BRect frame,
  169. BColumn* column, bool depressed);
  170. float _VirtualWidth() const;
  171. OutlineView* fOutlineView;
  172. BList* fColumns;
  173. BList* fSortColumns;
  174. // float fColumnsWidth;
  175. BRect fVisibleRect;
  176. #if DOUBLE_BUFFERED_COLUMN_RESIZE
  177. BBitmap* fDrawBuffer;
  178. BView* fDrawBufferView;
  179. #endif
  180. enum {
  181. INACTIVE,
  182. RESIZING_COLUMN,
  183. PRESSING_COLUMN,
  184. DRAG_COLUMN_INSIDE_TITLE,
  185. DRAG_COLUMN_OUTSIDE_TITLE
  186. } fCurrentState;
  187. BPopUpMenu* fColumnPop;
  188. BColumnListView* fMasterView;
  189. bool fEditMode;
  190. int32 fColumnFlags;
  191. // State information for resizing/dragging
  192. BColumn* fSelectedColumn;
  193. BRect fSelectedColumnRect;
  194. bool fResizingFirstColumn;
  195. BPoint fClickPoint; // offset within cell
  196. float fLeftDragBoundry;
  197. float fRightDragBoundry;
  198. BPoint fCurrentDragPosition;
  199. BBitmap* fUpSortArrow;
  200. BBitmap* fDownSortArrow;
  201. BCursor* fResizeCursor;
  202. BCursor* fMinResizeCursor;
  203. BCursor* fMaxResizeCursor;
  204. BCursor* fColumnMoveCursor;
  205. };
  206. class OutlineView : public BView {
  207. typedef BView _inherited;
  208. public:
  209. OutlineView(BRect, BList* visibleColumns,
  210. BList* sortColumns,
  211. BColumnListView* listView);
  212. virtual ~OutlineView();
  213. virtual void Draw(BRect);
  214. const BRect& VisibleRect() const;
  215. void RedrawColumn(BColumn* column, float leftEdge,
  216. bool isFirstColumn);
  217. void StartSorting();
  218. float GetColumnPreferredWidth(BColumn* column);
  219. void AddRow(BRow*, int32 index, BRow* TheRow);
  220. BRow* CurrentSelection(BRow* lastSelected) const;
  221. void ToggleFocusRowSelection(bool selectRange);
  222. void ToggleFocusRowOpen();
  223. void ChangeFocusRow(bool up, bool updateSelection,
  224. bool addToCurrentSelection);
  225. void MoveFocusToVisibleRect();
  226. void ExpandOrCollapse(BRow* parent, bool expand);
  227. void RemoveRow(BRow*);
  228. BRowContainer* RowList();
  229. void UpdateRow(BRow*);
  230. bool FindParent(BRow* row, BRow** _parent,
  231. bool* _isVisible);
  232. int32 IndexOf(BRow* row);
  233. void Deselect(BRow*);
  234. void AddToSelection(BRow*);
  235. void DeselectAll();
  236. BRow* FocusRow() const;
  237. void SetFocusRow(BRow* row, bool select);
  238. BRow* FindRow(float ypos, int32* _indent,
  239. float* _top);
  240. bool FindRect(const BRow* row, BRect* _rect);
  241. void ScrollTo(const BRow* row);
  242. void Clear();
  243. void SetSelectionMode(list_view_type type);
  244. list_view_type SelectionMode() const;
  245. void SetMouseTrackingEnabled(bool);
  246. void FixScrollBar(bool scrollToFit);
  247. void SetEditMode(bool state)
  248. { fEditMode = state; }
  249. virtual void FrameResized(float width, float height);
  250. virtual void ScrollTo(BPoint where);
  251. virtual void MouseDown(BPoint where);
  252. virtual void MouseMoved(BPoint where, uint32 transit,
  253. const BMessage* dragMessage);
  254. virtual void MouseUp(BPoint where);
  255. virtual void MessageReceived(BMessage* message);
  256. private:
  257. bool SortList(BRowContainer* list, bool isVisible);
  258. static int32 DeepSortThreadEntry(void* outlineView);
  259. void DeepSort();
  260. void SelectRange(BRow* start, BRow* end);
  261. int32 CompareRows(BRow* row1, BRow* row2);
  262. void AddSorted(BRowContainer* list, BRow* row);
  263. void RecursiveDeleteRows(BRowContainer* list,
  264. bool owner);
  265. void InvalidateCachedPositions();
  266. bool FindVisibleRect(BRow* row, BRect* _rect);
  267. BList* fColumns;
  268. BList* fSortColumns;
  269. float fItemsHeight;
  270. BRowContainer fRows;
  271. BRect fVisibleRect;
  272. #if DOUBLE_BUFFERED_COLUMN_RESIZE
  273. BBitmap* fDrawBuffer;
  274. BView* fDrawBufferView;
  275. #endif
  276. BRow* fFocusRow;
  277. BRect fFocusRowRect;
  278. BRow* fRollOverRow;
  279. BRow fSelectionListDummyHead;
  280. BRow* fLastSelectedItem;
  281. BRow* fFirstSelectedItem;
  282. thread_id fSortThread;
  283. int32 fNumSorted;
  284. bool fSortCancelled;
  285. enum CurrentState {
  286. INACTIVE,
  287. LATCH_CLICKED,
  288. ROW_CLICKED,
  289. DRAGGING_ROWS
  290. };
  291. CurrentState fCurrentState;
  292. BColumnListView* fMasterView;
  293. list_view_type fSelectionMode;
  294. bool fTrackMouse;
  295. BField* fCurrentField;
  296. BRow* fCurrentRow;
  297. BColumn* fCurrentColumn;
  298. bool fMouseDown;
  299. BRect fFieldRect;
  300. int32 fCurrentCode;
  301. bool fEditMode;
  302. // State information for mouse/keyboard interaction
  303. BPoint fClickPoint;
  304. bool fDragging;
  305. int32 fClickCount;
  306. BRow* fTargetRow;
  307. float fTargetRowTop;
  308. BRect fLatchRect;
  309. float fDropHighlightY;
  310. friend class RecursiveOutlineIterator;
  311. };
  312. class RecursiveOutlineIterator {
  313. public:
  314. RecursiveOutlineIterator(
  315. BRowContainer* container,
  316. bool openBranchesOnly = true);
  317. BRow* CurrentRow() const;
  318. int32 CurrentLevel() const;
  319. void GoToNext();
  320. private:
  321. struct {
  322. BRowContainer* fRowSet;
  323. int32 fIndex;
  324. int32 fDepth;
  325. } fStack[kMaxDepth];
  326. int32 fStackIndex;
  327. BRowContainer* fCurrentList;
  328. int32 fCurrentListIndex;
  329. int32 fCurrentListDepth;
  330. bool fOpenBranchesOnly;
  331. };
  332. } // namespace BPrivate
  333. using namespace BPrivate;
  334. BField::BField()
  335. {
  336. }
  337. BField::~BField()
  338. {
  339. }
  340. // #pragma mark -
  341. void
  342. BColumn::MouseMoved(BColumnListView* /*parent*/, BRow* /*row*/,
  343. BField* /*field*/, BRect /*field_rect*/, BPoint/*point*/,
  344. uint32 /*buttons*/, int32 /*code*/)
  345. {
  346. }
  347. void
  348. BColumn::MouseDown(BColumnListView* /*parent*/, BRow* /*row*/,
  349. BField* /*field*/, BRect /*field_rect*/, BPoint /*point*/,
  350. uint32 /*buttons*/)
  351. {
  352. }
  353. void
  354. BColumn::MouseUp(BColumnListView* /*parent*/, BRow* /*row*/, BField* /*field*/)
  355. {
  356. }
  357. // #pragma mark -
  358. BRow::BRow(float height)
  359. :
  360. fChildList(NULL),
  361. fIsExpanded(false),
  362. fHeight(height),
  363. fNextSelected(NULL),
  364. fPrevSelected(NULL),
  365. fParent(NULL),
  366. fList(NULL)
  367. {
  368. }
  369. BRow::~BRow()
  370. {
  371. while (true) {
  372. BField* field = (BField*) fFields.RemoveItem((int32)0);
  373. if (field == 0)
  374. break;
  375. delete field;
  376. }
  377. }
  378. bool
  379. BRow::HasLatch() const
  380. {
  381. return fChildList != 0;
  382. }
  383. int32
  384. BRow::CountFields() const
  385. {
  386. return fFields.CountItems();
  387. }
  388. BField*
  389. BRow::GetField(int32 index)
  390. {
  391. return (BField*)fFields.ItemAt(index);
  392. }
  393. const BField*
  394. BRow::GetField(int32 index) const
  395. {
  396. return (const BField*)fFields.ItemAt(index);
  397. }
  398. void
  399. BRow::SetField(BField* field, int32 logicalFieldIndex)
  400. {
  401. if (fFields.ItemAt(logicalFieldIndex) != 0)
  402. delete (BField*)fFields.RemoveItem(logicalFieldIndex);
  403. if (NULL != fList) {
  404. ValidateField(field, logicalFieldIndex);
  405. BRect inv;
  406. fList->GetRowRect(this, &inv);
  407. fList->Invalidate(inv);
  408. }
  409. fFields.AddItem(field, logicalFieldIndex);
  410. }
  411. float
  412. BRow::Height() const
  413. {
  414. return fHeight;
  415. }
  416. bool
  417. BRow::IsExpanded() const
  418. {
  419. return fIsExpanded;
  420. }
  421. bool
  422. BRow::IsSelected() const
  423. {
  424. return fPrevSelected != NULL;
  425. }
  426. void
  427. BRow::ValidateFields() const
  428. {
  429. for (int32 i = 0; i < CountFields(); i++)
  430. ValidateField(GetField(i), i);
  431. }
  432. void
  433. BRow::ValidateField(const BField* field, int32 logicalFieldIndex) const
  434. {
  435. // The Fields may be moved by the user, but the logicalFieldIndexes
  436. // do not change, so we need to map them over when checking the
  437. // Field types.
  438. BColumn* col = NULL;
  439. int32 items = fList->CountColumns();
  440. for (int32 i = 0 ; i < items; ++i) {
  441. col = fList->ColumnAt(i);
  442. if( col->LogicalFieldNum() == logicalFieldIndex )
  443. break;
  444. }
  445. if (NULL == col) {
  446. BString dbmessage("\n\n\tThe parent BColumnListView does not have "
  447. "\n\ta BColumn at the logical field index ");
  448. dbmessage << logicalFieldIndex << ".\n\n";
  449. printf(dbmessage.String());
  450. } else {
  451. if (!col->AcceptsField(field)) {
  452. BString dbmessage("\n\n\tThe BColumn of type ");
  453. dbmessage << typeid(*col).name() << "\n\tat logical field index "
  454. << logicalFieldIndex << "\n\tdoes not support the "
  455. "field type "
  456. << typeid(*field).name() << ".\n\n";
  457. debugger(dbmessage.String());
  458. }
  459. }
  460. }
  461. // #pragma mark -
  462. BColumn::BColumn(float width, float minWidth, float maxWidth, alignment align)
  463. :
  464. fWidth(width),
  465. fMinWidth(minWidth),
  466. fMaxWidth(maxWidth),
  467. fVisible(true),
  468. fList(0),
  469. fShowHeading(true),
  470. fAlignment(align)
  471. {
  472. }
  473. BColumn::~BColumn()
  474. {
  475. }
  476. float
  477. BColumn::Width() const
  478. {
  479. return fWidth;
  480. }
  481. void
  482. BColumn::SetWidth(float width)
  483. {
  484. fWidth = width;
  485. }
  486. float
  487. BColumn::MinWidth() const
  488. {
  489. return fMinWidth;
  490. }
  491. float
  492. BColumn::MaxWidth() const
  493. {
  494. return fMaxWidth;
  495. }
  496. void
  497. BColumn::DrawTitle(BRect, BView*)
  498. {
  499. }
  500. void
  501. BColumn::DrawField(BField*, BRect, BView*)
  502. {
  503. }
  504. int
  505. BColumn::CompareFields(BField*, BField*)
  506. {
  507. return 0;
  508. }
  509. void
  510. BColumn::GetColumnName(BString* into) const
  511. {
  512. *into = "(Unnamed)";
  513. }
  514. float
  515. BColumn::GetPreferredWidth(BField* field, BView* parent) const
  516. {
  517. return fWidth;
  518. }
  519. bool
  520. BColumn::IsVisible() const
  521. {
  522. return fVisible;
  523. }
  524. void
  525. BColumn::SetVisible(bool visible)
  526. {
  527. if (fList && (fVisible != visible))
  528. fList->SetColumnVisible(this, visible);
  529. }
  530. bool
  531. BColumn::ShowHeading() const
  532. {
  533. return fShowHeading;
  534. }
  535. void
  536. BColumn::SetShowHeading(bool state)
  537. {
  538. fShowHeading = state;
  539. }
  540. alignment
  541. BColumn::Alignment() const
  542. {
  543. return fAlignment;
  544. }
  545. void
  546. BColumn::SetAlignment(alignment align)
  547. {
  548. fAlignment = align;
  549. }
  550. bool
  551. BColumn::WantsEvents() const
  552. {
  553. return fWantsEvents;
  554. }
  555. void
  556. BColumn::SetWantsEvents(bool state)
  557. {
  558. fWantsEvents = state;
  559. }
  560. int32
  561. BColumn::LogicalFieldNum() const
  562. {
  563. return fFieldID;
  564. }
  565. bool
  566. BColumn::AcceptsField(const BField*) const
  567. {
  568. return true;
  569. }
  570. // #pragma mark -
  571. BColumnListView::BColumnListView(BRect rect, const char* name,
  572. uint32 resizingMode, uint32 flags, border_style border,
  573. bool showHorizontalScrollbar)
  574. :
  575. BView(rect, name, resizingMode,
  576. flags | B_WILL_DRAW | B_FRAME_EVENTS | B_FULL_UPDATE_ON_RESIZE),
  577. fStatusView(NULL),
  578. fSelectionMessage(NULL),
  579. fSortingEnabled(true),
  580. fLatchWidth(kLatchWidth),
  581. fBorderStyle(border),
  582. fShowingHorizontalScrollBar(showHorizontalScrollbar)
  583. {
  584. _Init();
  585. }
  586. BColumnListView::BColumnListView(const char* name, uint32 flags,
  587. border_style border, bool showHorizontalScrollbar)
  588. :
  589. BView(name, flags | B_WILL_DRAW | B_FRAME_EVENTS | B_FULL_UPDATE_ON_RESIZE),
  590. fStatusView(NULL),
  591. fSelectionMessage(NULL),
  592. fSortingEnabled(true),
  593. fLatchWidth(kLatchWidth),
  594. fBorderStyle(border),
  595. fShowingHorizontalScrollBar(showHorizontalScrollbar)
  596. {
  597. _Init();
  598. }
  599. BColumnListView::~BColumnListView()
  600. {
  601. while (BColumn* column = (BColumn*)fColumns.RemoveItem((int32)0))
  602. delete column;
  603. }
  604. bool
  605. BColumnListView::InitiateDrag(BPoint, bool)
  606. {
  607. return false;
  608. }
  609. void
  610. BColumnListView::MessageDropped(BMessage*, BPoint)
  611. {
  612. }
  613. void
  614. BColumnListView::ExpandOrCollapse(BRow* row, bool Open)
  615. {
  616. fOutlineView->ExpandOrCollapse(row, Open);
  617. }
  618. status_t
  619. BColumnListView::Invoke(BMessage* message)
  620. {
  621. if (message == 0)
  622. message = Message();
  623. return BInvoker::Invoke(message);
  624. }
  625. void
  626. BColumnListView::ItemInvoked()
  627. {
  628. Invoke();
  629. }
  630. void
  631. BColumnListView::SetInvocationMessage(BMessage* message)
  632. {
  633. SetMessage(message);
  634. }
  635. BMessage*
  636. BColumnListView::InvocationMessage() const
  637. {
  638. return Message();
  639. }
  640. uint32
  641. BColumnListView::InvocationCommand() const
  642. {
  643. return Command();
  644. }
  645. BRow*
  646. BColumnListView::FocusRow() const
  647. {
  648. return fOutlineView->FocusRow();
  649. }
  650. void
  651. BColumnListView::SetFocusRow(int32 Index, bool Select)
  652. {
  653. SetFocusRow(RowAt(Index), Select);
  654. }
  655. void
  656. BColumnListView::SetFocusRow(BRow* row, bool Select)
  657. {
  658. fOutlineView->SetFocusRow(row, Select);
  659. }
  660. void
  661. BColumnListView::SetMouseTrackingEnabled(bool Enabled)
  662. {
  663. fOutlineView->SetMouseTrackingEnabled(Enabled);
  664. }
  665. list_view_type
  666. BColumnListView::SelectionMode() const
  667. {
  668. return fOutlineView->SelectionMode();
  669. }
  670. void
  671. BColumnListView::Deselect(BRow* row)
  672. {
  673. fOutlineView->Deselect(row);
  674. }
  675. void
  676. BColumnListView::AddToSelection(BRow* row)
  677. {
  678. fOutlineView->AddToSelection(row);
  679. }
  680. void
  681. BColumnListView::DeselectAll()
  682. {
  683. fOutlineView->DeselectAll();
  684. }
  685. BRow*
  686. BColumnListView::CurrentSelection(BRow* lastSelected) const
  687. {
  688. return fOutlineView->CurrentSelection(lastSelected);
  689. }
  690. void
  691. BColumnListView::SelectionChanged()
  692. {
  693. if (fSelectionMessage)
  694. Invoke(fSelectionMessage);
  695. }
  696. void
  697. BColumnListView::SetSelectionMessage(BMessage* message)
  698. {
  699. if (fSelectionMessage == message)
  700. return;
  701. delete fSelectionMessage;
  702. fSelectionMessage = message;
  703. }
  704. BMessage*
  705. BColumnListView::SelectionMessage()
  706. {
  707. return fSelectionMessage;
  708. }
  709. uint32
  710. BColumnListView::SelectionCommand() const
  711. {
  712. if (fSelectionMessage)
  713. return fSelectionMessage->what;
  714. return 0;
  715. }
  716. void
  717. BColumnListView::SetSelectionMode(list_view_type mode)
  718. {
  719. fOutlineView->SetSelectionMode(mode);
  720. }
  721. void
  722. BColumnListView::SetSortingEnabled(bool enabled)
  723. {
  724. fSortingEnabled = enabled;
  725. fSortColumns.MakeEmpty();
  726. fTitleView->Invalidate(); // Erase sort indicators
  727. }
  728. bool
  729. BColumnListView::SortingEnabled() const
  730. {
  731. return fSortingEnabled;
  732. }
  733. void
  734. BColumnListView::SetSortColumn(BColumn* column, bool add, bool ascending)
  735. {
  736. if (!SortingEnabled())
  737. return;
  738. if (!add)
  739. fSortColumns.MakeEmpty();
  740. if (!fSortColumns.HasItem(column))
  741. fSortColumns.AddItem(column);
  742. column->fSortAscending = ascending;
  743. fTitleView->Invalidate();
  744. fOutlineView->StartSorting();
  745. }
  746. void
  747. BColumnListView::ClearSortColumns()
  748. {
  749. fSortColumns.MakeEmpty();
  750. fTitleView->Invalidate(); // Erase sort indicators
  751. }
  752. void
  753. BColumnListView::AddStatusView(BView* view)
  754. {
  755. BRect bounds = Bounds();
  756. float width = view->Bounds().Width();
  757. if (width > bounds.Width() / 2)
  758. width = bounds.Width() / 2;
  759. fStatusView = view;
  760. Window()->BeginViewTransaction();
  761. fHorizontalScrollBar->ResizeBy(-(width + 1), 0);
  762. fHorizontalScrollBar->MoveBy((width + 1), 0);
  763. AddChild(view);
  764. BRect viewRect(bounds);
  765. viewRect.right = width;
  766. viewRect.top = viewRect.bottom - B_H_SCROLL_BAR_HEIGHT;
  767. if (fBorderStyle == B_PLAIN_BORDER)
  768. viewRect.OffsetBy(1, -1);
  769. else if (fBorderStyle == B_FANCY_BORDER)
  770. viewRect.OffsetBy(2, -2);
  771. view->SetResizingMode(B_FOLLOW_LEFT | B_FOLLOW_BOTTOM);
  772. view->ResizeTo(viewRect.Width(), viewRect.Height());
  773. view->MoveTo(viewRect.left, viewRect.top);
  774. Window()->EndViewTransaction();
  775. }
  776. BView*
  777. BColumnListView::RemoveStatusView()
  778. {
  779. if (fStatusView) {
  780. float width = fStatusView->Bounds().Width();
  781. Window()->BeginViewTransaction();
  782. fStatusView->RemoveSelf();
  783. fHorizontalScrollBar->MoveBy(-width, 0);
  784. fHorizontalScrollBar->ResizeBy(width, 0);
  785. Window()->EndViewTransaction();
  786. }
  787. BView* view = fStatusView;
  788. fStatusView = 0;
  789. return view;
  790. }
  791. void
  792. BColumnListView::AddColumn(BColumn* column, int32 logicalFieldIndex)
  793. {
  794. ASSERT(column != NULL);
  795. column->fList = this;
  796. column->fFieldID = logicalFieldIndex;
  797. // sanity check. If there is already a field with this ID, remove it.
  798. for (int32 index = 0; index < fColumns.CountItems(); index++) {
  799. BColumn* existingColumn = (BColumn*) fColumns.ItemAt(index);
  800. if (existingColumn && existingColumn->fFieldID == logicalFieldIndex) {
  801. RemoveColumn(existingColumn);
  802. break;
  803. }
  804. }
  805. if (column->Width() < column->MinWidth())
  806. column->SetWidth(column->MinWidth());
  807. else if (column->Width() > column->MaxWidth())
  808. column->SetWidth(column->MaxWidth());
  809. fColumns.AddItem((void*) column);
  810. fTitleView->ColumnAdded(column);
  811. }
  812. void
  813. BColumnListView::MoveColumn(BColumn* column, int32 index)
  814. {
  815. ASSERT(column != NULL);
  816. fTitleView->MoveColumn(column, index);
  817. }
  818. void
  819. BColumnListView::RemoveColumn(BColumn* column)
  820. {
  821. if (fColumns.HasItem(column)) {
  822. SetColumnVisible(column, false);
  823. if (Window() != NULL)
  824. Window()->UpdateIfNeeded();
  825. fColumns.RemoveItem(column);
  826. }
  827. }
  828. int32
  829. BColumnListView::CountColumns() const
  830. {
  831. return fColumns.CountItems();
  832. }
  833. BColumn*
  834. BColumnListView::ColumnAt(int32 field) const
  835. {
  836. return (BColumn*) fColumns.ItemAt(field);
  837. }
  838. BColumn*
  839. BColumnListView::ColumnAt(BPoint point) const
  840. {
  841. float left = MAX(kLeftMargin, LatchWidth());
  842. for (int i = 0; BColumn* column = (BColumn*)fColumns.ItemAt(i); i++) {
  843. if (!column->IsVisible())
  844. continue;
  845. float right = left + column->Width();
  846. if (point.x >= left && point.x <= right)
  847. return column;
  848. left = right + 1;
  849. }
  850. return NULL;
  851. }
  852. void
  853. BColumnListView::SetColumnVisible(BColumn* column, bool visible)
  854. {
  855. fTitleView->SetColumnVisible(column, visible);
  856. }
  857. void
  858. BColumnListView::SetColumnVisible(int32 index, bool isVisible)
  859. {
  860. BColumn* column = ColumnAt(index);
  861. if (column)
  862. column->SetVisible(isVisible);
  863. }
  864. bool
  865. BColumnListView::IsColumnVisible(int32 index) const
  866. {
  867. BColumn* column = ColumnAt(index);
  868. if (column)
  869. return column->IsVisible();
  870. return false;
  871. }
  872. void
  873. BColumnListView::SetColumnFlags(column_flags flags)
  874. {
  875. fTitleView->SetColumnFlags(flags);
  876. }
  877. void
  878. BColumnListView::ResizeColumnToPreferred(int32 index)
  879. {
  880. BColumn* column = ColumnAt(index);
  881. if (column == NULL)
  882. return;
  883. // get the preferred column width
  884. float width = fOutlineView->GetColumnPreferredWidth(column);
  885. // set it
  886. float oldWidth = column->Width();
  887. column->SetWidth(width);
  888. fTitleView->ColumnResized(column, oldWidth);
  889. fOutlineView->Invalidate();
  890. }
  891. void
  892. BColumnListView::ResizeAllColumnsToPreferred()
  893. {
  894. int32 count = CountColumns();
  895. for (int32 i = 0; i < count; i++)
  896. ResizeColumnToPreferred(i);
  897. }
  898. const BRow*
  899. BColumnListView::RowAt(int32 Index, BRow* parentRow) const
  900. {
  901. if (parentRow == 0)
  902. return fOutlineView->RowList()->ItemAt(Index);
  903. return parentRow->fChildList ? parentRow->fChildList->ItemAt(Index) : NULL;
  904. }
  905. BRow*
  906. BColumnListView::RowAt(int32 Index, BRow* parentRow)
  907. {
  908. if (parentRow == 0)
  909. return fOutlineView->RowList()->ItemAt(Index);
  910. return parentRow->fChildList ? parentRow->fChildList->ItemAt(Index) : 0;
  911. }
  912. const BRow*
  913. BColumnListView::RowAt(BPoint point) const
  914. {
  915. float top;
  916. int32 indent;
  917. return fOutlineView->FindRow(point.y, &indent, &top);
  918. }
  919. BRow*
  920. BColumnListView::RowAt(BPoint point)
  921. {
  922. float top;
  923. int32 indent;
  924. return fOutlineView->FindRow(point.y, &indent, &top);
  925. }
  926. bool
  927. BColumnListView::GetRowRect(const BRow* row, BRect* outRect) const
  928. {
  929. return fOutlineView->FindRect(row, outRect);
  930. }
  931. bool
  932. BColumnListView::FindParent(BRow* row, BRow** _parent, bool* _isVisible) const
  933. {
  934. return fOutlineView->FindParent(row, _parent, _isVisible);
  935. }
  936. int32
  937. BColumnListView::IndexOf(BRow* row)
  938. {
  939. return fOutlineView->IndexOf(row);
  940. }
  941. int32
  942. BColumnListView::CountRows(BRow* parentRow) const
  943. {
  944. if (parentRow == 0)
  945. return fOutlineView->RowList()->CountItems();
  946. if (parentRow->fChildList)
  947. return parentRow->fChildList->CountItems();
  948. else
  949. return 0;
  950. }
  951. void
  952. BColumnListView::AddRow(BRow* row, BRow* parentRow)
  953. {
  954. AddRow(row, -1, parentRow);
  955. }
  956. void
  957. BColumnListView::AddRow(BRow* row, int32 index, BRow* parentRow)
  958. {
  959. row->fChildList = 0;
  960. row->fList = this;
  961. row->ValidateFields();
  962. fOutlineView->AddRow(row, index, parentRow);
  963. }
  964. void
  965. BColumnListView::RemoveRow(BRow* row)
  966. {
  967. fOutlineView->RemoveRow(row);
  968. row->fList = NULL;
  969. }
  970. void
  971. BColumnListView::UpdateRow(BRow* row)
  972. {
  973. fOutlineView->UpdateRow(row);
  974. }
  975. bool
  976. BColumnListView::SwapRows(int32 index1, int32 index2, BRow* parentRow1,
  977. BRow* parentRow2)
  978. {
  979. BRow* row1 = NULL;
  980. BRow* row2 = NULL;
  981. BRowContainer* container1 = NULL;
  982. BRowContainer* container2 = NULL;
  983. if (parentRow1 == NULL)
  984. container1 = fOutlineView->RowList();
  985. else
  986. container1 = parentRow1->fChildList;
  987. if (container1 == NULL)
  988. return false;
  989. if (parentRow2 == NULL)
  990. container2 = fOutlineView->RowList();
  991. else
  992. container2 = parentRow1->fChildList;
  993. if (container2 == NULL)
  994. return false;
  995. row1 = container1->ItemAt(index1);
  996. if (row1 == NULL)
  997. return false;
  998. row2 = container2->ItemAt(index2);
  999. if (row2 == NULL)
  1000. return false;
  1001. container1->ReplaceItem(index2, row1);
  1002. container2->ReplaceItem(index1, row2);
  1003. BRect rect1;
  1004. BRect rect2;
  1005. BRect rect;
  1006. fOutlineView->FindRect(row1, &rect1);
  1007. fOutlineView->FindRect(row2, &rect2);
  1008. rect = rect1 | rect2;
  1009. fOutlineView->Invalidate(rect);
  1010. return true;
  1011. }
  1012. void
  1013. BColumnListView::ScrollTo(const BRow* row)
  1014. {
  1015. fOutlineView->ScrollTo(row);
  1016. }
  1017. void
  1018. BColumnListView::ScrollTo(BPoint point)
  1019. {
  1020. fOutlineView->ScrollTo(point);
  1021. }
  1022. void
  1023. BColumnListView::Clear()
  1024. {
  1025. fOutlineView->Clear();
  1026. }
  1027. void
  1028. BColumnListView::SetFont(const BFont* font, uint32 mask)
  1029. {
  1030. // This method is deprecated.
  1031. fOutlineView->SetFont(font, mask);
  1032. fTitleView->SetFont(font, mask);
  1033. }
  1034. void
  1035. BColumnListView::SetFont(ColumnListViewFont font_num, const BFont* font,
  1036. uint32 mask)
  1037. {
  1038. switch (font_num) {
  1039. case B_FONT_ROW:
  1040. fOutlineView->SetFont(font, mask);
  1041. break;
  1042. case B_FONT_HEADER:
  1043. fTitleView->SetFont(font, mask);
  1044. break;
  1045. default:
  1046. ASSERT(false);
  1047. break;
  1048. }
  1049. }
  1050. void
  1051. BColumnListView::GetFont(ColumnListViewFont font_num, BFont* font) const
  1052. {
  1053. switch (font_num) {
  1054. case B_FONT_ROW:
  1055. fOutlineView->GetFont(font);
  1056. break;
  1057. case B_FONT_HEADER:
  1058. fTitleView->GetFont(font);
  1059. break;
  1060. default:
  1061. ASSERT(false);
  1062. break;
  1063. }
  1064. }
  1065. void
  1066. BColumnListView::SetColor(ColumnListViewColor color_num, const rgb_color color)
  1067. {
  1068. if ((int)color_num < 0) {
  1069. ASSERT(false);
  1070. color_num = (ColumnListViewColor) 0;
  1071. }
  1072. if ((int)color_num >= (int)B_COLOR_TOTAL) {
  1073. ASSERT(false);
  1074. color_num = (ColumnListViewColor) (B_COLOR_TOTAL - 1);
  1075. }
  1076. fColorList[color_num] = color;
  1077. }
  1078. rgb_color
  1079. BColumnListView::Color(ColumnListViewColor color_num) const
  1080. {
  1081. if ((int)color_num < 0) {
  1082. ASSERT(false);
  1083. color_num = (ColumnListViewColor) 0;
  1084. }
  1085. if ((int)color_num >= (int)B_COLOR_TOTAL) {
  1086. ASSERT(false);
  1087. color_num = (ColumnListViewColor) (B_COLOR_TOTAL - 1);
  1088. }
  1089. return fColorList[color_num];
  1090. }
  1091. void
  1092. BColumnListView::SetHighColor(rgb_color color)
  1093. {
  1094. BView::SetHighColor(color);
  1095. // fOutlineView->Invalidate(); // Redraw things with the new color
  1096. // Note that this will currently cause
  1097. // an infinite loop, refreshing over and over.
  1098. // A better solution is needed.
  1099. }
  1100. void
  1101. BColumnListView::SetSelectionColor(rgb_color color)
  1102. {
  1103. fColorList[B_COLOR_SELECTION] = color;
  1104. }
  1105. void
  1106. BColumnListView::SetBackgroundColor(rgb_color color)
  1107. {
  1108. fColorList[B_COLOR_BACKGROUND] = color;
  1109. fOutlineView->Invalidate(); // Repaint with new color
  1110. }
  1111. void
  1112. BColumnListView::SetEditColor(rgb_color color)
  1113. {
  1114. fColorList[B_COLOR_EDIT_BACKGROUND] = color;
  1115. }
  1116. const rgb_color
  1117. BColumnListView::SelectionColor() const
  1118. {
  1119. return fColorList[B_COLOR_SELECTION];
  1120. }
  1121. const rgb_color
  1122. BColumnListView::BackgroundColor() const
  1123. {
  1124. return fColorList[B_COLOR_BACKGROUND];
  1125. }
  1126. const rgb_color
  1127. BColumnListView::EditColor() const
  1128. {
  1129. return fColorList[B_COLOR_EDIT_BACKGROUND];
  1130. }
  1131. BPoint
  1132. BColumnListView::SuggestTextPosition(const BRow* row,
  1133. const BColumn* inColumn) const
  1134. {
  1135. BRect rect;
  1136. GetRowRect(row, &rect);
  1137. if (inColumn) {
  1138. float leftEdge = MAX(kLeftMargin, LatchWidth());
  1139. for (int index = 0; index < fColumns.CountItems(); index++) {
  1140. BColumn* column = (BColumn*) fColumns.ItemAt(index);
  1141. if (!column->IsVisible())
  1142. continue;
  1143. if (column == inColumn) {
  1144. rect.left = leftEdge;
  1145. rect.right = rect.left + column->Width();
  1146. break;
  1147. }
  1148. leftEdge += column->Width() + 1;
  1149. }
  1150. }
  1151. font_height fh;
  1152. fOutlineView->GetFontHeight(&fh);
  1153. float baseline = floor(rect.top + fh.ascent
  1154. + (rect.Height()+1-(fh.ascent+fh.descent))/2);
  1155. return BPoint(rect.left + 8, baseline);
  1156. }
  1157. void
  1158. BColumnListView::SetLatchWidth(float width)
  1159. {
  1160. fLatchWidth = width;
  1161. Invalidate();
  1162. }
  1163. float
  1164. BColumnListView::LatchWidth() const
  1165. {
  1166. return fLatchWidth;
  1167. }
  1168. void
  1169. BColumnListView::DrawLatch(BView* view, BRect rect, LatchType position, BRow*)
  1170. {
  1171. const int32 rectInset = 4;
  1172. view->SetHighColor(0, 0, 0);
  1173. // Make Square
  1174. int32 sideLen = rect.IntegerWidth();
  1175. if (sideLen > rect.IntegerHeight())
  1176. sideLen = rect.IntegerHeight();
  1177. // Make Center
  1178. int32 halfWidth = rect.IntegerWidth() / 2;
  1179. int32 halfHeight = rect.IntegerHeight() / 2;
  1180. int32 halfSide = sideLen / 2;
  1181. float left = rect.left + halfWidth - halfSide;
  1182. float top = rect.top + halfHeight - halfSide;
  1183. BRect itemRect(left, top, left + sideLen, top + sideLen);
  1184. // Why it is a pixel high? I don't know.
  1185. itemRect.OffsetBy(0, -1);
  1186. itemRect.InsetBy(rectInset, rectInset);
  1187. // Make it an odd number of pixels wide, the latch looks better this way
  1188. if ((itemRect.IntegerWidth() % 2) == 1) {
  1189. itemRect.right += 1;
  1190. itemRect.bottom += 1;
  1191. }
  1192. switch (position) {
  1193. case B_OPEN_LATCH:
  1194. view->StrokeRect(itemRect);
  1195. view->StrokeLine(
  1196. BPoint(itemRect.left + 2,
  1197. (itemRect.top + itemRect.bottom) / 2),
  1198. BPoint(itemRect.right - 2,
  1199. (itemRect.top + itemRect.bottom) / 2));
  1200. break;
  1201. case B_PRESSED_LATCH:
  1202. view->StrokeRect(itemRect);
  1203. view->StrokeLine(
  1204. BPoint(itemRect.left + 2,
  1205. (itemRect.top + itemRect.bottom) / 2),
  1206. BPoint(itemRect.right - 2,
  1207. (itemRect.top + itemRect.bottom) / 2));
  1208. view->StrokeLine(
  1209. BPoint((itemRect.left + itemRect.right) / 2,
  1210. itemRect.top + 2),
  1211. BPoint((itemRect.left + itemRect.right) / 2,
  1212. itemRect.bottom - 2));
  1213. view->InvertRect(itemRect);
  1214. break;
  1215. case B_CLOSED_LATCH:
  1216. view->StrokeRect(itemRect);
  1217. view->StrokeLine(
  1218. BPoint(itemRect.left + 2,
  1219. (itemRect.top + itemRect.bottom) / 2),
  1220. BPoint(itemRect.right - 2,
  1221. (itemRect.top + itemRect.bottom) / 2));
  1222. view->StrokeLine(
  1223. BPoint((itemRect.left + itemRect.right) / 2,
  1224. itemRect.top + 2),
  1225. BPoint((itemRect.left + itemRect.right) / 2,
  1226. itemRect.bottom - 2));
  1227. break;
  1228. case B_NO_LATCH:
  1229. // No drawing
  1230. break;
  1231. }
  1232. }
  1233. void
  1234. BColumnListView::MakeFocus(bool isFocus)
  1235. {
  1236. if (fBorderStyle != B_NO_BORDER) {
  1237. // Redraw focus marks around view
  1238. Invalidate();
  1239. fHorizontalScrollBar->SetBorderHighlighted(isFocus);
  1240. fVerticalScrollBar->SetBorderHighlighted(isFocus);
  1241. }
  1242. BView::MakeFocus(isFocus);
  1243. }
  1244. void
  1245. BColumnListView::MessageReceived(BMessage* message)
  1246. {
  1247. // Propagate mouse wheel messages down to child, so that it can
  1248. // scroll. Note we have done so, so we don't go into infinite
  1249. // recursion if this comes back up here.
  1250. if (message->what == B_MOUSE_WHEEL_CHANGED) {
  1251. bool handled;
  1252. if (message->FindBool("be:clvhandled", &handled) != B_OK) {
  1253. message->AddBool("be:clvhandled", true);
  1254. fOutlineView->MessageReceived(message);
  1255. return;
  1256. }
  1257. }
  1258. BView::MessageReceived(message);
  1259. }
  1260. void
  1261. BColumnListView::KeyDown(const char* bytes, int32 numBytes)
  1262. {
  1263. char c = bytes[0];
  1264. switch (c) {
  1265. case B_RIGHT_ARROW:
  1266. case B_LEFT_ARROW:
  1267. {
  1268. float minVal, maxVal;
  1269. fHorizontalScrollBar->GetRange(&minVal, &maxVal);
  1270. float smallStep, largeStep;
  1271. fHorizontalScrollBar->GetSteps(&smallStep, &largeStep);
  1272. float oldVal = fHorizontalScrollBar->Value();
  1273. float newVal = oldVal;
  1274. if (c == B_LEFT_ARROW)
  1275. newVal -= smallStep;
  1276. else if (c == B_RIGHT_ARROW)
  1277. newVal += smallStep;
  1278. if (newVal < minVal)
  1279. newVal = minVal;
  1280. else if (newVal > maxVal)
  1281. newVal = maxVal;
  1282. fHorizontalScrollBar->SetValue(newVal);
  1283. break;
  1284. }
  1285. case B_DOWN_ARROW:
  1286. fOutlineView->ChangeFocusRow(false,
  1287. (modifiers() & B_CONTROL_KEY) == 0,
  1288. (modifiers() & B_SHIFT_KEY) != 0);
  1289. break;
  1290. case B_UP_ARROW:
  1291. fOutlineView->ChangeFocusRow(true,
  1292. (modifiers() & B_CONTROL_KEY) == 0,
  1293. (modifiers() & B_SHIFT_KEY) != 0);
  1294. break;
  1295. case B_PAGE_UP:
  1296. case B_PAGE_DOWN:
  1297. {
  1298. float minValue, maxValue;
  1299. fVerticalScrollBar->GetRange(&minValue, &maxValue);
  1300. float smallStep, largeStep;
  1301. fVerticalScrollBar->GetSteps(&smallStep, &largeStep);
  1302. float currentValue = fVerticalScrollBar->Value();
  1303. float newValue = currentValue;
  1304. if (c == B_PAGE_UP)
  1305. newValue -= largeStep;
  1306. else
  1307. newValue += largeStep;
  1308. if (newValue > maxValue)
  1309. newValue = maxValue;
  1310. else if (newValue < minValue)
  1311. newValue = minValue;
  1312. fVerticalScrollBar->SetValue(newValue);
  1313. // Option + pgup or pgdn scrolls and changes the selection.
  1314. if (modifiers() & B_OPTION_KEY)
  1315. fOutlineView->MoveFocusToVisibleRect();
  1316. break;
  1317. }
  1318. case B_ENTER:
  1319. Invoke();
  1320. break;
  1321. case B_SPACE:
  1322. fOutlineView->ToggleFocusRowSelection(
  1323. (modifiers() & B_SHIFT_KEY) != 0);
  1324. break;
  1325. case '+':
  1326. fOutlineView->ToggleFocusRowOpen();
  1327. break;
  1328. default:
  1329. BView::KeyDown(bytes, numBytes);
  1330. }
  1331. }
  1332. void
  1333. BColumnListView::AttachedToWindow()
  1334. {
  1335. if (!Messenger().IsValid())
  1336. SetTarget(Window());
  1337. if (SortingEnabled()) fOutlineView->StartSorting();
  1338. }
  1339. void
  1340. BColumnListView::WindowActivated(bool active)
  1341. {
  1342. fOutlineView->Invalidate();
  1343. // Focus and selection appearance changes with focus
  1344. Invalidate(); // Redraw focus marks around view
  1345. BView::WindowActivated(active);
  1346. }
  1347. void
  1348. BColumnListView::Draw(BRect updateRect)
  1349. {
  1350. BRect rect = Bounds();
  1351. if (be_control_look != NULL) {
  1352. uint32 flags = 0;
  1353. if (IsFocus() && Window()->IsActive())
  1354. flags |= BControlLook::B_FOCUSED;
  1355. rgb_color base = ui_color(B_PANEL_BACKGROUND_COLOR);
  1356. BRect verticalScrollBarFrame;
  1357. if (!fVerticalScrollBar->IsHidden())
  1358. verticalScrollBarFrame = fVerticalScrollBar->Frame();
  1359. BRect horizontalScrollBarFrame;
  1360. if (!fHorizontalScrollBar->IsHidden())
  1361. horizontalScrollBarFrame = fHorizontalScrollBar->Frame();
  1362. if (fBorderStyle == B_NO_BORDER) {
  1363. // We still draw the left/top border, but not focused.
  1364. // The scrollbars cannot be displayed without frame and
  1365. // it looks bad to have no frame only along the left/top
  1366. // side.
  1367. rgb_color borderColor = tint_color(base, B_DARKEN_2_TINT);
  1368. SetHighColor(borderColor);
  1369. StrokeLine(BPoint(rect.left, rect.bottom),
  1370. BPoint(rect.left, rect.top));
  1371. StrokeLine(BPoint(rect.left + 1, rect.top),
  1372. BPoint(rect.right, rect.top));
  1373. }
  1374. be_control_look->DrawScrollViewFrame(this, rect, updateRect,
  1375. verticalScrollBarFrame, horizontalScrollBarFrame,
  1376. base, fBorderStyle, flags);
  1377. return;
  1378. }
  1379. BRect cornerRect(rect.right - B_V_SCROLL_BAR_WIDTH,
  1380. rect.bottom - B_H_SCROLL_BAR_HEIGHT, rect.right, rect.bottom);
  1381. if (fBorderStyle == B_PLAIN_BORDER) {
  1382. BView::SetHighColor(0, 0, 0);
  1383. StrokeRect(rect);
  1384. cornerRect.OffsetBy(-1, -1);
  1385. } else if (fBorderStyle == B_FANCY_BORDER) {
  1386. bool isFocus = IsFocus() && Window()->IsActive();
  1387. if (isFocus) {
  1388. // TODO: Need to find focus color programatically
  1389. BView::SetHighColor(0, 0, 190);
  1390. } else
  1391. BView::SetHighColor(255, 255, 255);
  1392. StrokeRect(rect);
  1393. if (!isFocus)
  1394. BView::SetHighColor(184, 184, 184);
  1395. else
  1396. BView::SetHighColor(152, 152, 152);
  1397. rect.InsetBy(1,1);
  1398. StrokeRect(rect);
  1399. cornerRect.OffsetBy(-2, -2);
  1400. }
  1401. BView::SetHighColor(ui_color(B_PANEL_BACKGROUND_COLOR));
  1402. // fills lower right rect between scroll bars
  1403. FillRect(cornerRect);
  1404. }
  1405. void
  1406. BColumnListView::SaveState(BMessage* msg)
  1407. {
  1408. msg->MakeEmpty();
  1409. for (int32 i = 0; BColumn* col = (BColumn*)fColumns.ItemAt(i); i++) {
  1410. msg->AddInt32("ID",col->fFieldID);
  1411. msg->AddFloat("width", col->fWidth);
  1412. msg->AddBool("visible", col->fVisible);
  1413. }
  1414. msg->AddBool("sortingenabled", fSortingEnabled);
  1415. if (fSortingEnabled) {
  1416. for (int32 i = 0; BColumn* col = (BColumn*)fSortColumns.ItemAt(i);
  1417. i++) {
  1418. msg->AddInt32("sortID", col->fFieldID);
  1419. msg->AddBool("sortascending", col->fSortAscending);
  1420. }
  1421. }
  1422. }
  1423. void
  1424. BColumnListView::LoadState(BMessage* msg)
  1425. {
  1426. int32 id;
  1427. for (int i = 0; msg->FindInt32("ID", i, &id) == B_OK; i++) {
  1428. for (int j = 0; BColumn* column = (BColumn*)fColumns.ItemAt(j); j++) {
  1429. if (column->fFieldID == id) {
  1430. // move this column to position 'i' and set its attributes
  1431. MoveColumn(column, i);
  1432. float width;
  1433. if (msg->FindFloat("width", i, &width) == B_OK)
  1434. column->SetWidth(width);
  1435. bool visible;
  1436. if (msg->FindBool("visible", i, &visible) == B_OK)
  1437. column->SetVisible(visible);
  1438. }
  1439. }
  1440. }
  1441. bool b;
  1442. if (msg->FindBool("sortingenabled", &b) == B_OK) {
  1443. SetSortingEnabled(b);
  1444. for (int k = 0; msg->FindInt32("sortID", k, &id) == B_OK; k++) {
  1445. for (int j = 0; BColumn* column = (BColumn*)fColumns.ItemAt(j);
  1446. j++) {
  1447. if (column->fFieldID == id) {
  1448. // add this column to the sort list
  1449. bool value;
  1450. if (msg->FindBool("sortascending", k, &value) == B_OK)
  1451. SetSortColumn(column, true, value);
  1452. }
  1453. }
  1454. }
  1455. }
  1456. }
  1457. void
  1458. BColumnListView::SetEditMode(bool state)
  1459. {
  1460. fOutlineView->SetEditMode(state);
  1461. fTitleView->SetEditMode(state);
  1462. }
  1463. void
  1464. BColumnListView::Refresh()
  1465. {
  1466. if (LockLooper()) {
  1467. Invalidate();
  1468. fOutlineView->FixScrollBar (true);
  1469. fOutlineView->Invalidate();
  1470. Window()->UpdateIfNeeded();
  1471. UnlockLooper();
  1472. }
  1473. }
  1474. BSize
  1475. BColumnListView::MinSize()
  1476. {
  1477. BSize size;
  1478. size.width = 100;
  1479. size.height = kTitleHeight + 4 * B_H_SCROLL_BAR_HEIGHT;
  1480. if (!fHorizontalScrollBar->IsHidden())
  1481. size.height += fHorizontalScrollBar->Frame().Height() + 1;
  1482. // TODO: Take border size into account
  1483. return BLayoutUtils::ComposeSize(ExplicitMinSize(), size);
  1484. }
  1485. BSize
  1486. BColumnListView::PreferredSize()
  1487. {
  1488. BSize size = MinSize();
  1489. size.height += ceilf(be_plain_font->Size()) * 20;
  1490. // return MinSize().width if there are no columns.
  1491. int32 count = CountColumns();
  1492. if (count > 0) {
  1493. BRect titleRect;
  1494. BRect outlineRect;
  1495. BRect vScrollBarRect;
  1496. BRect hScrollBarRect;
  1497. _GetChildViewRects(Bounds(), titleRect, outlineRect, vScrollBarRect,
  1498. hScrollBarRect);
  1499. // Start with the extra width for border and scrollbars etc.
  1500. size.width = titleRect.left - Bounds().left;
  1501. size.width += Bounds().right - titleRect.right;
  1502. // If we want all columns to be visible at their preferred width,
  1503. // we also need to add the extra margin width that the TitleView
  1504. // uses to compute its _VirtualWidth() for the horizontal scroll bar.
  1505. size.width += fTitleView->MarginWidth();
  1506. for (int32 i = 0; i < count; i++) {
  1507. BColumn* column = ColumnAt(i);
  1508. if (column != NULL)
  1509. size.width += fOutlineView->GetColumnPreferredWidth(column);
  1510. }
  1511. }
  1512. return BLayoutUtils::ComposeSize(ExplicitPreferredSize(), size);
  1513. }
  1514. BSize
  1515. BColumnListView::MaxSize()
  1516. {
  1517. BSize size(B_SIZE_UNLIMITED, B_SIZE_UNLIMITED);
  1518. return BLayoutUtils::ComposeSize(ExplicitMaxSize(), size);
  1519. }
  1520. void
  1521. BColumnListView::LayoutInvalidated(bool descendants)
  1522. {
  1523. }
  1524. void
  1525. BColumnListView::DoLayout()
  1526. {
  1527. if (!(Flags() & B_SUPPORTS_LAYOUT))
  1528. return;
  1529. BRect titleRect;
  1530. BRect outlineRect;
  1531. BRect vScrollBarRect;
  1532. BRect hScrollBarRect;
  1533. _GetChildViewRects(Bounds(), titleRect, outlineRect, vScrollBarRect,
  1534. hScrollBarRect);
  1535. fTitleView->MoveTo(titleRect.LeftTop());
  1536. fTitleView->ResizeTo(titleRect.Width(), titleRect.Height());
  1537. fOutlineView->MoveTo(outlineRect.LeftTop());
  1538. fOutlineView->ResizeTo(outlineRect.Width(), outlineRect.Height());
  1539. fVerticalScrollBar->MoveTo(vScrollBarRect.LeftTop());
  1540. fVerticalScrollBar->ResizeTo(vScrollBarRect.Width(),
  1541. vScrollBarRect.Height());
  1542. fHorizontalScrollBar->MoveTo(hScrollBarRect.LeftTop());
  1543. fHorizontalScrollBar->ResizeTo(hScrollBarRect.Width(),
  1544. hScrollBarRect.Height());
  1545. fOutlineView->FixScrollBar(true);
  1546. }
  1547. void
  1548. BColumnListView::_Init()
  1549. {
  1550. SetViewColor(B_TRANSPARENT_32_BIT);
  1551. BRect bounds(Bounds());
  1552. if (bounds.Width() <= 0)
  1553. bounds.right = 100;
  1554. if (bounds.Height() <= 0)
  1555. bounds.bottom = 100;
  1556. for (int i = 0; i < (int)B_COLOR_TOTAL; i++)
  1557. fColorList[i] = kColor[i];
  1558. BRect titleRect;
  1559. BRect outlineRect;
  1560. BRect vScrollBarRect;
  1561. BRect hScrollBarRect;
  1562. _GetChildViewRects(bounds, titleRect, outlineRect, vScrollBarRect,
  1563. hScrollBarRect);
  1564. fOutlineView = new OutlineView(outlineRect, &fColumns, &fSortColumns, this);
  1565. AddChild(fOutlineView);
  1566. fTitleView = new TitleView(titleRect, fOutlineView, &fColumns,
  1567. &fSortColumns, this, B_FOLLOW_LEFT_RIGHT | B_FOLLOW_TOP);
  1568. AddChild(fTitleView);
  1569. fVerticalScrollBar = new BScrollBar(vScrollBarRect, "vertical_scroll_bar",
  1570. fOutlineView, 0.0, bounds.Height(), B_VERTICAL);
  1571. AddChild(fVerticalScrollBar);
  1572. fHorizontalScrollBar = new BScrollBar(hScrollBarRect,
  1573. "horizontal_scroll_bar", fTitleView, 0.0, bounds.Width(), B_HORIZONTAL);
  1574. AddChild(fHorizontalScrollBar);
  1575. if (!fShowingHorizontalScrollBar)
  1576. fHorizontalScrollBar->Hide();
  1577. fOutlineView->FixScrollBar(true);
  1578. }
  1579. void
  1580. BColumnListView::_GetChildViewRects(const BRect& bounds, BRect& titleRect,
  1581. BRect& outlineRect, BRect& vScrollBarRect, BRect& hScrollBarRect)
  1582. {
  1583. titleRect = bounds;
  1584. titleRect.bottom = titleRect.top + kTitleHeight;
  1585. #if !LOWER_SCROLLBAR
  1586. titleRect.right -= B_V_SCROLL_BAR_WIDTH;
  1587. #endif
  1588. outlineRect = bounds;
  1589. outlineRect.top = titleRect.bottom + 1.0;
  1590. outlineRect.right -= B_V_SCROLL_BAR_WIDTH;
  1591. if (fShowingHorizontalScrollBar)
  1592. outlineRect.bottom -= B_H_SCROLL_BAR_HEIGHT;
  1593. vScrollBarRect = bounds;
  1594. #if LOWER_SCROLLBAR
  1595. vScrollBarRect.top += kTitleHeight;
  1596. #endif
  1597. vScrollBarRect.left = vScrollBarRect.right - B_V_SCROLL_BAR_WIDTH;
  1598. if (fShowingHorizontalScrollBar)
  1599. vScrollBarRect.bottom -= B_H_SCROLL_BAR_HEIGHT;
  1600. hScrollBarRect = bounds;
  1601. hScrollBarRect.top = hScrollBarRect.bottom - B_H_SCROLL_BAR_HEIGHT;
  1602. hScrollBarRect.right -= B_V_SCROLL_BAR_WIDTH;
  1603. // Adjust stuff so the border will fit.
  1604. if (fBorderStyle == B_PLAIN_BORDER || fBorderStyle == B_NO_BORDER) {
  1605. titleRect.InsetBy(1, 0);
  1606. titleRect.OffsetBy(0, 1);
  1607. outlineRect.InsetBy(1, 1);
  1608. } else if (fBorderStyle == B_FANCY_BORDER) {
  1609. titleRect.InsetBy(2, 0);
  1610. titleRect.OffsetBy(0, 2);
  1611. outlineRect.InsetBy(2, 2);
  1612. vScrollBarRect.OffsetBy(-1, 0);
  1613. #if LOWER_SCROLLBAR
  1614. vScrollBarRect.top += 2;
  1615. vScrollBarRect.bottom -= 1;
  1616. #else
  1617. vScrollBarRect.InsetBy(0, 1);
  1618. #endif
  1619. hScrollBarRect.OffsetBy(0, -1);
  1620. hScrollBarRect.InsetBy(1, 0);
  1621. }
  1622. }
  1623. // #pragma mark -
  1624. TitleView::TitleView(BRect rect, OutlineView* horizontalSlave,
  1625. BList* visibleColumns, BList* sortColumns, BColumnListView* listView,
  1626. uint32 resizingMode)
  1627. :
  1628. BView(rect, "title_view", resizingMode, B_WILL_DRAW | B_FRAME_EVENTS),
  1629. fOutlineView(horizontalSlave),
  1630. fColumns(visibleColumns),
  1631. fSortColumns(sortColumns),
  1632. // fColumnsWidth(0),
  1633. fVisibleRect(rect.OffsetToCopy(0, 0)),
  1634. fCurrentState(INACTIVE),
  1635. fColumnPop(NULL),
  1636. fMasterView(listView),
  1637. fEditMode(false),
  1638. fColumnFlags(B_ALLOW_COLUMN_MOVE | B_ALLOW_COLUMN_RESIZE
  1639. | B_ALLOW_COLUMN_POPUP | B_ALLOW_COLUMN_REMOVE)
  1640. {
  1641. SetViewColor(B_TRANSPARENT_COLOR);
  1642. #if DOUBLE_BUFFERED_COLUMN_RESIZE
  1643. // xxx this needs to be smart about the size of the backbuffer.
  1644. BRect doubleBufferRect(0, 0, 600, 35);
  1645. fDrawBuffer = new BBitmap(doubleBufferRect, B_RGB32, true);
  1646. fDrawBufferView = new BView(doubleBufferRect, "double_buffer_view",
  1647. B_FOLLOW_ALL_SIDES, 0);
  1648. fDrawBuffer->Lock();
  1649. fDrawBuffer->AddChild(fDrawBufferView);
  1650. fDrawBuffer->Unlock();
  1651. #endif
  1652. fUpSortArrow = new BBitmap(BRect(0, 0, 7, 7), B_CMAP8);
  1653. fDownSortArrow = new BBitmap(BRect(0, 0, 7, 7), B_CMAP8);
  1654. fUpSortArrow->SetBits((const void*) kUpSortArrow8x8, 64, 0, B_CMAP8);
  1655. fDownSortArrow->SetBits((const void*) kDownSortArrow8x8, 64, 0, B_CMAP8);
  1656. fResizeCursor = new BCursor(B_CURSOR_ID_RESIZE_EAST_WEST);
  1657. fMinResizeCursor = new BCursor(B_CURSOR_ID_RESIZE_EAST);
  1658. fMaxResizeCursor = new BCursor(B_CURSOR_ID_RESIZE_WEST);
  1659. fColumnMoveCursor = new BCursor(B_CURSOR_ID_MOVE);
  1660. FixScrollBar(true);
  1661. }
  1662. TitleView::~TitleView()
  1663. {
  1664. delete fColumnPop;
  1665. fColumnPop = NULL;
  1666. #if DOUBLE_BUFFERED_COLUMN_RESIZE
  1667. delete fDrawBuffer;
  1668. #endif
  1669. delete fUpSortArrow;
  1670. delete fDownSortArrow;
  1671. delete fResizeCursor;
  1672. delete fMaxResizeCursor;
  1673. delete fMinResizeCursor;
  1674. delete fColumnMoveCursor;
  1675. }
  1676. void
  1677. TitleView::ColumnAdded(BColumn* column)
  1678. {
  1679. // fColumnsWidth += column->Width();
  1680. FixScrollBar(false);
  1681. Invalidate();
  1682. }
  1683. void
  1684. TitleView::ColumnResized(BColumn* column, float oldWidth)
  1685. {
  1686. // fColumnsWidth += column->Width() - oldWidth;
  1687. FixScrollBar(false);
  1688. Invalidate();
  1689. }
  1690. void
  1691. TitleView::SetColumnVisible(BColumn* column, bool visible)
  1692. {
  1693. if (column->fVisible == visible)
  1694. return;
  1695. // If setting it visible, do this first so we can find its position
  1696. // to invalidate. If hiding it, do it last.
  1697. if (visible)
  1698. column->fVisible = visible;
  1699. BRect titleInvalid;
  1700. GetTitleRect(column, &titleInvalid);
  1701. // Now really set the visibility
  1702. column->fVisible = visible;
  1703. // if (visible)
  1704. // fColumnsWidth += column->Width();
  1705. // else
  1706. // fColumnsWidth -= column->Width();
  1707. BRect outlineInvalid(fOutlineView->VisibleRect());
  1708. outlineInvalid.left = titleInvalid.left;
  1709. titleInvalid.right = outlineInvalid.right;
  1710. Invalidate(titleInvalid);
  1711. fOutlineView->Invalidate(outlineInvalid);
  1712. }
  1713. void
  1714. TitleView::GetTitleRect(BColumn* findColumn, BRect* _rect)
  1715. {
  1716. float leftEdge = MAX(kLeftMargin, fMasterView->LatchWidth());
  1717. int32 numColumns = fColumns->CountItems();
  1718. for (int index = 0; index < numColumns; index++) {
  1719. BColumn* column = (BColumn*) fColumns->ItemAt(index);
  1720. if (!column->IsVisible())
  1721. continue;
  1722. if (column == findColumn) {
  1723. _rect->Set(leftEdge, 0, leftEdge + column->Width(),
  1724. fVisibleRect.bottom);
  1725. return;
  1726. }
  1727. leftEdge += column->Width() + 1;
  1728. }
  1729. TRESPASS();
  1730. }
  1731. int32
  1732. TitleView::FindColumn(BPoint position, float* _leftEdge)
  1733. {
  1734. float leftEdge = MAX(kLeftMargin, fMasterView->LatchWidth());
  1735. int32 numColumns = fColumns->CountItems();
  1736. for (int index = 0; index < numColumns; index++) {
  1737. BColumn* column = (BColumn*) fColumns->ItemAt(index);
  1738. if (!column->IsVisible())
  1739. continue;
  1740. if (leftEdge > position.x)
  1741. break;
  1742. if (position.x >= leftEdge
  1743. && position.x <= leftEdge + column->Width()) {
  1744. *_leftEdge = leftEdge;
  1745. return index;
  1746. }
  1747. leftEdge += column->Width() + 1;
  1748. }
  1749. return 0;
  1750. }
  1751. void
  1752. TitleView::FixScrollBar(bool scrollToFit)
  1753. {
  1754. BScrollBar* hScrollBar = ScrollBar(B_HORIZONTAL);
  1755. if (hScrollBar == NULL)
  1756. return;
  1757. float virtualWidth = _VirtualWidth();
  1758. if (virtualWidth > fVisibleRect.Width()) {
  1759. hScrollBar->SetProportion(fVisibleRect.Width() / virtualWidth);
  1760. // Perform the little trick if the user is scrolled over too far.
  1761. // See OutlineView::FixScrollBar for a more in depth explanation
  1762. float maxScrollBarValue = virtualWidth - fVisibleRect.Width();
  1763. if (scrollToFit || hScrollBar->Value() <= maxScrollBarValue) {
  1764. hScrollBar->SetRange(0.0, maxScrollBarValue);
  1765. hScrollBar->SetSteps(50, fVisibleRect.Width());
  1766. }
  1767. } else if (hScrollBar->Value() == 0.0) {
  1768. // disable scroll bar.
  1769. hScrollBar->SetRange(0.0, 0.0);
  1770. }
  1771. }
  1772. void
  1773. TitleView::DragSelectedColumn(BPoint position)
  1774. {
  1775. float invalidLeft = fSelectedColumnRect.left;
  1776. float invalidRight = fSelectedColumnRect.right;
  1777. float leftEdge;
  1778. int32 columnIndex = FindColumn(position, &leftEdge);
  1779. fSelectedColumnRect.OffsetTo(leftEdge, 0);
  1780. MoveColumn(fSelectedColumn, columnIndex);
  1781. fSelectedColumn->fVisible = true;
  1782. ComputeDragBoundries(fSelectedColumn, position);
  1783. // Redraw the new column position
  1784. GetTitleRect(fSelectedColumn, &fSelectedColumnRect);
  1785. invalidLeft = MIN(fSelectedColumnRect.left, invalidLeft);
  1786. invalidRight = MAX(fSelectedColumnRect.right, invalidRight);
  1787. Invalidate(BRect(invalidLeft, 0, invalidRight, fVisibleRect.bottom));
  1788. fOutlineView->Invalidate(BRect(invalidLeft, 0, invalidRight,
  1789. fOutlineView->VisibleRect().bottom));
  1790. DrawTitle(this, fSelectedColumnRect, fSelectedColumn, true);
  1791. }
  1792. void
  1793. TitleView::MoveColumn(BColumn* column, int32 index)
  1794. {
  1795. fColumns->RemoveItem((void*) column);
  1796. if (-1 == index) {
  1797. // Re-add the column at the end of the list.
  1798. fColumns->AddItem((void*) column);
  1799. } else {
  1800. fColumns->AddItem((void*) column, index);
  1801. }
  1802. }
  1803. void
  1804. TitleView::SetColumnFlags(column_flags flags)
  1805. {
  1806. fColumnFlags = flags;
  1807. }
  1808. float
  1809. TitleView::MarginWidth() const
  1810. {
  1811. return MAX(kLeftMargin, fMasterView->LatchWidth()) + kRightMargin;
  1812. }
  1813. void
  1814. TitleView::ResizeSelectedColumn(BPoint position, bool preferred)
  1815. {
  1816. float minWidth = fSelectedColumn->MinWidth();
  1817. float maxWidth = fSelectedColumn->MaxWidth();
  1818. float oldWidth = fSelectedColumn->Width();
  1819. float originalEdge = fSelectedColumnRect.left + oldWidth;
  1820. if (preferred) {
  1821. float width = fOutlineView->GetColumnPreferredWidth(fSelectedColumn);
  1822. fSelectedColumn->SetWidth(width);
  1823. } else if (position.x > fSelectedColumnRect.left + maxWidth)
  1824. fSelectedColumn->SetWidth(maxWidth);
  1825. else if (position.x < fSelectedColumnRect.left + minWidth)
  1826. fSelectedColumn->SetWidth(minWidth);
  1827. else
  1828. fSelectedColumn->SetWidth(position.x - fSelectedColumnRect.left - 1);
  1829. float dX = fSelectedColumnRect.left + fSelectedColumn->Width()
  1830. - originalEdge;
  1831. if (dX != 0) {
  1832. float columnHeight = fVisibleRect.Height();
  1833. BRect originalRect(originalEdge, 0, 1000000.0, columnHeight);
  1834. BRect movedRect(originalRect);
  1835. movedRect.OffsetBy(dX, 0);
  1836. // Update the size of the title column
  1837. BRect sourceRect(0, 0, fSelectedColumn->Width(), columnHeight);
  1838. BRect destRect(sourceRect);
  1839. destRect.OffsetBy(fSelectedColumnRect.left, 0);
  1840. #if DOUBLE_BUFFERED_COLUMN_RESIZE
  1841. fDrawBuffer->Lock();
  1842. DrawTitle(fDrawBufferView, sourceRect, fSelectedColumn, false);
  1843. fDrawBufferView->Sync();
  1844. fDrawBuffer->Unlock();
  1845. CopyBits(originalRect, movedRect);
  1846. DrawBitmap(fDrawBuffer, sourceRect, destRect);
  1847. #else
  1848. CopyBits(originalRect, movedRect);
  1849. DrawTitle(this, destRect, fSelectedColumn, false);
  1850. #endif
  1851. // Update the body view
  1852. BRect slaveSize = fOutlineView->VisibleRect();
  1853. BRect slaveSource(originalRect);
  1854. slaveSource.bottom = slaveSize.bottom;
  1855. BRect slaveDest(movedRect);
  1856. slaveDest.bottom = slaveSize.bottom;
  1857. fOutlineView->CopyBits(slaveSource, slaveDest);
  1858. fOutlineView->RedrawColumn(fSelectedColumn, fSelectedColumnRect.left,
  1859. fResizingFirstColumn);
  1860. // fColumnsWidth += dX;
  1861. // Update the cursor
  1862. if (fSelectedColumn->Width() == minWidth)
  1863. SetViewCursor(fMinResizeCursor, true);
  1864. else if (fSelectedColumn->Width() == maxWidth)
  1865. SetViewCursor(fMaxResizeCursor, true);
  1866. else
  1867. SetViewCursor(fResizeCursor, true);
  1868. ColumnResized(fSelectedColumn, oldWidth);
  1869. }
  1870. }
  1871. void
  1872. TitleView::ComputeDragBoundries(BColumn* findColumn, BPoint)
  1873. {
  1874. float previousColumnLeftEdge = -1000000.0;
  1875. float nextColumnRightEdge = 1000000.0;
  1876. bool foundColumn = false;
  1877. float leftEdge = MAX(kLeftMargin, fMasterView->LatchWidth());
  1878. int32 numColumns = fColumns->CountItems();
  1879. for (int index = 0; index < numColumns; index++) {
  1880. BColumn* column = (BColumn*) fColumns->ItemAt(index);
  1881. if (!column->IsVisible())
  1882. continue;
  1883. if (column == findColumn) {
  1884. foundColumn = true;
  1885. continue;
  1886. }
  1887. if (foundColumn) {
  1888. nextColumnRightEdge = leftEdge + column->Width();
  1889. break;
  1890. } else
  1891. previousColumnLeftEdge = leftEdge;
  1892. leftEdge += column->Width() + 1;
  1893. }
  1894. float rightEdge = leftEdge + findColumn->Width();
  1895. fLeftDragBoundry = MIN(previousColumnLeftEdge + findColumn->Width(),
  1896. leftEdge);
  1897. fRightDragBoundry = MAX(nextColumnRightEdge, rightEdge);
  1898. }
  1899. void
  1900. TitleView::DrawTitle(BView* view, BRect rect, BColumn* column, bool depressed)
  1901. {
  1902. BRect drawRect;
  1903. rgb_color borderColor = mix_color(
  1904. fMasterView->Color(B_COLOR_HEADER_BACKGROUND),
  1905. make_color(0, 0, 0), 128);
  1906. rgb_color backgroundColor;
  1907. rgb_color bevelHigh;
  1908. rgb_color bevelLow;
  1909. // Want exterior borders to overlap.
  1910. if (be_control_look == NULL) {
  1911. rect.right += 1;
  1912. drawRect = rect;
  1913. drawRect.InsetBy(2, 2);
  1914. if (depressed) {
  1915. backgroundColor = mix_color(
  1916. fMasterView->Color(B_COLOR_HEADER_BACKGROUND),
  1917. make_color(0, 0, 0), 64);
  1918. bevelHigh = mix_color(backgroundColor, make_color(0, 0, 0), 64);
  1919. bevelLow = mix_color(backgroundColor, make_color(255, 255, 255),
  1920. 128);
  1921. drawRect.left++;
  1922. drawRect.top++;
  1923. } else {
  1924. backgroundColor = fMasterView->Color(B_COLOR_HEADER_BACKGROUND);
  1925. bevelHigh = mix_color(backgroundColor, make_color(255, 255, 255),
  1926. 192);
  1927. bevelLow = mix_color(backgroundColor, make_color(0, 0, 0), 64);
  1928. drawRect.bottom--;
  1929. drawRect.right--;
  1930. }
  1931. } else {
  1932. drawRect = rect;
  1933. }
  1934. font_height fh;
  1935. GetFontHeight(&fh);
  1936. float baseline = floor(drawRect.top + fh.ascent
  1937. + (drawRect.Height() + 1 - (fh.ascent + fh.descent)) / 2);
  1938. if (be_control_look != NULL) {
  1939. BRect bgRect = rect;
  1940. rgb_color base = ui_color(B_PANEL_BACKGROUND_COLOR);
  1941. view->SetHighColor(tint_color(base, B_DARKEN_2_TINT));
  1942. view->StrokeLine(bgRect.LeftBottom(), bgRect.RightBottom());
  1943. bgRect.bottom--;
  1944. bgRect.right--;
  1945. if (depressed)
  1946. base = tint_color(base, B_DARKEN_1_TINT);
  1947. be_control_look->DrawButtonBackground(view, bgRect, rect, base, 0,
  1948. BControlLook::B_TOP_BORDER | BControlLook::B_BOTTOM_BORDER);
  1949. view->SetHighColor(tint_color(ui_color(B_PANEL_BACKGROUND_COLOR),
  1950. B_DARKEN_2_TINT));
  1951. view->StrokeLine(rect.RightTop(), rect.RightBottom());
  1952. } else {
  1953. view->SetHighColor(borderColor);
  1954. view->StrokeRect(rect);
  1955. view->BeginLineArray(4);
  1956. view->AddLine(BPoint(rect.left + 1, rect.top + 1),
  1957. BPoint(rect.right - 1, rect.top + 1), bevelHigh);
  1958. view->AddLine(BPoint(rect.left + 1, rect.top + 1),
  1959. BPoint(rect.left + 1, rect.bottom - 1), bevelHigh);
  1960. view->AddLine(BPoint(rect.right - 1, rect.top + 1),
  1961. BPoint(rect.right - 1, rect.bottom - 1), bevelLow);
  1962. view->AddLine(BPoint(rect.left + 2, rect.bottom-1),
  1963. BPoint(rect.right - 1, rect.bottom - 1), bevelLow);
  1964. view->EndLineArray();
  1965. view->SetHighColor(backgroundColor);
  1966. view->SetLowColor(backgroundColor);
  1967. view->FillRect(rect.InsetByCopy(2, 2));
  1968. }
  1969. // If no column given, nothing else to draw.
  1970. if (!column)
  1971. return;
  1972. view->SetHighColor(fMasterView->Color(B_COLOR_HEADER_TEXT));
  1973. BFont font;
  1974. GetFont(&font);
  1975. view->SetFont(&font);
  1976. int sortIndex = fSortColumns->IndexOf(column);
  1977. if (sortIndex >= 0) {
  1978. // Draw sort notation.
  1979. BPoint upperLeft(drawRect.right - kSortIndicatorWidth, baseline);
  1980. if (fSortColumns->CountItems() > 1) {
  1981. char str[256];
  1982. sprintf(str, "%d", sortIndex + 1);
  1983. const float w = view->StringWidth(str);
  1984. upperLeft.x -= w;
  1985. view->SetDrawingMode(B_OP_COPY);
  1986. view->MovePenTo(BPoint(upperLeft.x + kSortIndicatorWidth,
  1987. baseline));
  1988. view->DrawString(str);
  1989. }
  1990. float bmh = fDownSortArrow->Bounds().Height()+1;
  1991. view->SetDrawingMode(B_OP_OVER);
  1992. if (column->fSortAscending) {
  1993. BPoint leftTop(upperLeft.x, drawRect.top + (drawRect.IntegerHeight()
  1994. - fDownSortArrow->Bounds().IntegerHeight()) / 2);
  1995. view->DrawBitmapAsync(fDownSortArrow, leftTop);
  1996. } else {
  1997. BPoint leftTop(upperLeft.x, drawRect.top + (drawRect.IntegerHeight()
  1998. - fUpSortArrow->Bounds().IntegerHeight()) / 2);
  1999. view->DrawBitmapAsync(fUpSortArrow, leftTop);
  2000. }
  2001. upperLeft.y = baseline - bmh + floor((fh.ascent + fh.descent - bmh) / 2);
  2002. if (upperLeft.y < drawRect.top)
  2003. upperLeft.y = drawRect.top;
  2004. // Adjust title stuff for sort indicator
  2005. drawRect.right = upperLeft.x - 2;
  2006. }
  2007. if (drawRect.right > drawRect.left) {
  2008. #if CONSTRAIN_CLIPPING_REGION
  2009. BRegion clipRegion(drawRect);
  2010. view->PushState();
  2011. view->ConstrainClippingRegion(&clipRegion);
  2012. #endif
  2013. view->MovePenTo(BPoint(drawRect.left + 8, baseline));
  2014. view->SetDrawingMode(B_OP_OVER);
  2015. view->SetHighColor(fMasterView->Color(B_COLOR_HEADER_TEXT));
  2016. column->DrawTitle(drawRect, view);
  2017. #if CONSTRAIN_CLIPPING_REGION
  2018. view->PopState();
  2019. #endif
  2020. }
  2021. }
  2022. float
  2023. TitleView::_VirtualWidth() const
  2024. {
  2025. float width = MarginWidth();
  2026. int32 count = fColumns->CountItems();
  2027. for (int32 i = 0; i < count; i++) {
  2028. BColumn* column = reinterpret_cast<BColumn*>(fColumns->ItemAt(i));
  2029. width += column->Width();
  2030. }
  2031. return width;
  2032. }
  2033. void
  2034. TitleView::Draw(BRect invalidRect)
  2035. {
  2036. float columnLeftEdge = MAX(kLeftMargin, fMasterView->LatchWidth());
  2037. for (int32 columnIndex = 0; columnIndex < fColumns->CountItems();
  2038. columnIndex++) {
  2039. BColumn* column = (BColumn*) fColumns->ItemAt(columnIndex);
  2040. if (!column->IsVisible())
  2041. continue;
  2042. if (columnLeftEdge > invalidRect.right)
  2043. break;
  2044. if (columnLeftEdge + column->Width() >= invalidRect.left) {
  2045. BRect titleRect(columnLeftEdge, 0,
  2046. columnLeftEdge + column->Width(), fVisibleRect.Height());
  2047. DrawTitle(this, titleRect, column,
  2048. (fCurrentState == DRAG_COLUMN_INSIDE_TITLE
  2049. && fSelectedColumn == column));
  2050. }
  2051. columnLeftEdge += column->Width() + 1;
  2052. }
  2053. // Bevels for right title margin
  2054. if (columnLeftEdge <= invalidRect.right) {
  2055. BRect titleRect(columnLeftEdge, 0, Bounds().right + 2,
  2056. fVisibleRect.Height());
  2057. DrawTitle(this, titleRect, NULL, false);
  2058. }
  2059. // Bevels for left title margin
  2060. if (invalidRect.left < MAX(kLeftMargin, fMasterView->LatchWidth())) {
  2061. BRect titleRect(0, 0, MAX(kLeftMargin, fMasterView->LatchWidth()) - 1,
  2062. fVisibleRect.Height());
  2063. DrawTitle(this, titleRect, NULL, false);
  2064. }
  2065. #if DRAG_TITLE_OUTLINE
  2066. // (Internal) Column Drag Indicator
  2067. if (fCurrentState == DRAG_COLUMN_INSIDE_TITLE) {
  2068. BRect dragRect(fSelectedColumnRect);
  2069. dragRect.OffsetTo(fCurrentDragPosition.x - fClickPoint.x, 0);
  2070. if (dragRect.Intersects(invalidRect)) {
  2071. SetHighColor(0, 0, 255);
  2072. StrokeRect(dragRect);
  2073. }
  2074. }
  2075. #endif
  2076. }
  2077. void
  2078. TitleView::ScrollTo(BPoint position)
  2079. {
  2080. fOutlineView->ScrollBy(position.x - fVisibleRect.left, 0);
  2081. fVisibleRect.OffsetTo(position.x, position.y);
  2082. // Perform the little trick if the user is scrolled over too far.
  2083. // See OutlineView::ScrollTo for a more in depth explanation
  2084. float maxScrollBarValue = _VirtualWidth() - fVisibleRect.Width();
  2085. BScrollBar* hScrollBar = ScrollBar(B_HORIZONTAL);
  2086. float min, max;
  2087. hScrollBar->GetRange(&min, &max);
  2088. if (max != maxScrollBarValue && position.x > maxScrollBarValue)
  2089. FixScrollBar(true);
  2090. _inherited::ScrollTo(position);
  2091. }
  2092. void
  2093. TitleView::MessageReceived(BMessage* message)
  2094. {
  2095. if (message->what == kToggleColumn) {
  2096. int32 num;
  2097. if (message->FindInt32("be:field_num", &num) == B_OK) {
  2098. for (int index = 0; index < fColumns->CountItems(); index++) {
  2099. BColumn* column = (BColumn*) fColumns->ItemAt(index);
  2100. if (!column)
  2101. continue;
  2102. if (column->LogicalFieldNum() == num)
  2103. column->SetVisible(!column->IsVisible());
  2104. }
  2105. }
  2106. return;
  2107. } else {
  2108. BView::MessageReceived(message);
  2109. }
  2110. }
  2111. void
  2112. TitleView::MouseDown(BPoint position)
  2113. {
  2114. if(fEditMode)
  2115. return;
  2116. int32 buttons = 1;
  2117. Window()->CurrentMessage()->FindInt32("buttons", &buttons);
  2118. if (buttons == B_SECONDARY_MOUSE_BUTTON
  2119. && (fColumnFlags & B_ALLOW_COLUMN_POPUP)) {
  2120. // Right mouse button -- bring up menu to show/hide columns.
  2121. if (!fColumnPop) fColumnPop = new BPopUpMenu("Columns", false, false);
  2122. fColumnPop->RemoveItems(0, fColumnPop->CountItems(), true);
  2123. BMessenger me(this);
  2124. for (int index = 0; index < fColumns->CountItems(); index++) {
  2125. BColumn* column = (BColumn*) fColumns->ItemAt(index);
  2126. if (!column) continue;
  2127. BString name;
  2128. column->GetColumnName(&name);
  2129. BMessage* msg = new BMessage(kToggleColumn);
  2130. msg->AddInt32("be:field_num", column->LogicalFieldNum());
  2131. BMenuItem* it = new BMenuItem(name.String(), msg);
  2132. it->SetMarked(column->IsVisible());
  2133. it->SetTarget(me);
  2134. fColumnPop->AddItem(it);
  2135. }
  2136. BPoint screenPosition = ConvertToScreen(position);
  2137. BRect sticky(screenPosition, screenPosition);
  2138. sticky.InsetBy(-5, -5);
  2139. fColumnPop->Go(ConvertToScreen(position), true, false, sticky, true);
  2140. return;
  2141. }
  2142. fResizingFirstColumn = true;
  2143. float leftEdge = MAX(kLeftMargin, fMasterView->LatchWidth());
  2144. for (int index = 0; index < fColumns->CountItems(); index++) {
  2145. BColumn* column = (BColumn*) fColumns->ItemAt(index);
  2146. if (!column->IsVisible())
  2147. continue;
  2148. if (leftEdge > position.x + kColumnResizeAreaWidth / 2)
  2149. break;
  2150. // Check for resizing a column
  2151. float rightEdge = leftEdge + column->Width();
  2152. if (column->ShowHeading()) {
  2153. if (position.x > rightEdge - kColumnResizeAreaWidth / 2
  2154. && position.x < rightEdge + kColumnResizeAreaWidth / 2
  2155. && column->MaxWidth() > column->MinWidth()
  2156. && (fColumnFlags & B_ALLOW_COLUMN_RESIZE)) {
  2157. int32 clicks = 0;
  2158. Window()->CurrentMessage()->FindInt32("clicks", &clicks);
  2159. if (clicks == 2) {
  2160. ResizeSelectedColumn(position, true);
  2161. fCurrentState = INACTIVE;
  2162. break;
  2163. }
  2164. fCurrentState = RESIZING_COLUMN;
  2165. fSelectedColumn = column;
  2166. fSelectedColumnRect.Set(leftEdge, 0, rightEdge,
  2167. fVisibleRect.Height());
  2168. fClickPoint = BPoint(position.x - rightEdge - 1,
  2169. position.y - fSelectedColumnRect.top);
  2170. SetMouseEventMask(B_POINTER_EVENTS,
  2171. B_LOCK_WINDOW_FOCUS | B_NO_POINTER_HISTORY);
  2172. break;
  2173. }
  2174. fResizingFirstColumn = false;
  2175. // Check for clicking on a column.
  2176. if (position.x > leftEdge && position.x < rightEdge) {
  2177. fCurrentState = PRESSING_COLUMN;
  2178. fSelectedColumn = column;
  2179. fSelectedColumnRect.Set(leftEdge, 0, rightEdge,
  2180. fVisibleRect.Height());
  2181. DrawTitle(this, fSelectedColumnRect, fSelectedColumn, true);
  2182. fClickPoint = BPoint(position.x - fSelectedColumnRect.left,
  2183. position.y - fSelectedColumnRect.top);
  2184. SetMouseEventMask(B_POINTER_EVENTS,
  2185. B_LOCK_WINDOW_FOCUS | B_NO_POINTER_HISTORY);
  2186. break;
  2187. }
  2188. }
  2189. leftEdge = rightEdge + 1;
  2190. }
  2191. }
  2192. void
  2193. TitleView::MouseMoved(BPoint position, uint32 transit,
  2194. const BMessage* dragMessage)
  2195. {
  2196. if (fEditMode)
  2197. return;
  2198. // Handle column manipulation
  2199. switch (fCurrentState) {
  2200. case RESIZING_COLUMN:
  2201. ResizeSelectedColumn(position - BPoint(fClickPoint.x, 0));
  2202. break;
  2203. case PRESSING_COLUMN: {
  2204. if (abs((int32)(position.x - (fClickPoint.x
  2205. + fSelectedColumnRect.left))) > kColumnResizeAreaWidth
  2206. || abs((int32)(position.y - (fClickPoint.y
  2207. + fSelectedColumnRect.top))) > kColumnResizeAreaWidth) {
  2208. // User has moved the mouse more than the tolerable amount,
  2209. // initiate a drag.
  2210. if (transit == B_INSIDE_VIEW || transit == B_ENTERED_VIEW) {
  2211. if(fColumnFlags & B_ALLOW_COLUMN_MOVE) {
  2212. fCurrentState = DRAG_COLUMN_INSIDE_TITLE;
  2213. ComputeDragBoundries(fSelectedColumn, position);
  2214. SetViewCursor(fColumnMoveCursor, true);
  2215. #if DRAG_TITLE_OUTLINE
  2216. BRect invalidRect(fSelectedColumnRect);
  2217. invalidRect.OffsetTo(position.x - fClickPoint.x, 0);
  2218. fCurrentDragPosition = position;
  2219. Invalidate(invalidRect);
  2220. #endif
  2221. }
  2222. } else {
  2223. if(fColumnFlags & B_ALLOW_COLUMN_REMOVE) {
  2224. // Dragged outside view
  2225. fCurrentState = DRAG_COLUMN_OUTSIDE_TITLE;
  2226. fSelectedColumn->SetVisible(false);
  2227. BRect dragRect(fSelectedColumnRect);
  2228. // There is a race condition where the mouse may have
  2229. // moved by the time we get to handle this message.
  2230. // If the user drags a column very quickly, this
  2231. // results in the annoying bug where the cursor is
  2232. // outside of the rectangle that is being dragged
  2233. // around. Call GetMouse with the checkQueue flag set
  2234. // to false so we can get the most recent position of
  2235. // the mouse. This minimizes this problem (although
  2236. // it is currently not possible to completely eliminate
  2237. // it).
  2238. uint32 buttons;
  2239. GetMouse(&position, &buttons, false);
  2240. dragRect.OffsetTo(position.x - fClickPoint.x,
  2241. position.y - dragRect.Height() / 2);
  2242. BeginRectTracking(dragRect, B_TRACK_WHOLE_RECT);
  2243. }
  2244. }
  2245. }
  2246. break;
  2247. }
  2248. case DRAG_COLUMN_INSIDE_TITLE: {
  2249. if (transit == B_EXITED_VIEW
  2250. && (fColumnFlags & B_ALLOW_COLUMN_REMOVE)) {
  2251. // Dragged outside view
  2252. fCurrentState = DRAG_COLUMN_OUTSIDE_TITLE;
  2253. fSelectedColumn->SetVisible(false);
  2254. BRect dragRect(fSelectedColumnRect);
  2255. // See explanation above.
  2256. uint32 buttons;
  2257. GetMouse(&position, &buttons, false);
  2258. dragRect.OffsetTo(position.x - fClickPoint.x,
  2259. position.y - fClickPoint.y);
  2260. BeginRectTracking(dragRect, B_TRACK_WHOLE_RECT);
  2261. } else if (position.x < fLeftDragBoundry
  2262. || position.x > fRightDragBoundry) {
  2263. DragSelectedColumn(position - BPoint(fClickPoint.x, 0));
  2264. }
  2265. #if DRAG_TITLE_OUTLINE
  2266. // Set up the invalid rect to include the rect for the previous
  2267. // position of the drag rect, as well as the new one.
  2268. BRect invalidRect(fSelectedColumnRect);
  2269. invalidRect.OffsetTo(fCurrentDragPosition.x - fClickPoint.x, 0);
  2270. if (position.x < fCurrentDragPosition.x)
  2271. invalidRect.left -= fCurrentDragPosition.x - position.x;
  2272. else
  2273. invalidRect.right += position.x - fCurrentDragPosition.x;
  2274. fCurrentDragPosition = position;
  2275. Invalidate(invalidRect);
  2276. #endif
  2277. break;
  2278. }
  2279. case DRAG_COLUMN_OUTSIDE_TITLE:
  2280. if (transit == B_ENTERED_VIEW) {
  2281. // Drag back into view
  2282. EndRectTracking();
  2283. fCurrentState = DRAG_COLUMN_INSIDE_TITLE;
  2284. fSelectedColumn->SetVisible(true);
  2285. DragSelectedColumn(position - BPoint(fClickPoint.x, 0));
  2286. }
  2287. break;
  2288. case INACTIVE:
  2289. // Check for cursor changes if we are over the resize area for
  2290. // a column.
  2291. BColumn* resizeColumn = 0;
  2292. float leftEdge = MAX(kLeftMargin, fMasterView->LatchWidth());
  2293. for (int index = 0; index < fColumns->CountItems(); index++) {
  2294. BColumn* column = (BColumn*) fColumns->ItemAt(index);
  2295. if (!column->IsVisible())
  2296. continue;
  2297. if (leftEdge > position.x + kColumnResizeAreaWidth / 2)
  2298. break;
  2299. float rightEdge = leftEdge + column->Width();
  2300. if (position.x > rightEdge - kColumnResizeAreaWidth / 2
  2301. && position.x < rightEdge + kColumnResizeAreaWidth / 2
  2302. && column->MaxWidth() > column->MinWidth()) {
  2303. resizeColumn = column;
  2304. break;
  2305. }
  2306. leftEdge = rightEdge + 1;
  2307. }
  2308. // Update the cursor
  2309. if (resizeColumn) {
  2310. if (resizeColumn->Width() == resizeColumn->MinWidth())
  2311. SetViewCursor(fMinResizeCursor, true);
  2312. else if (resizeColumn->Width() == resizeColumn->MaxWidth())
  2313. SetViewCursor(fMaxResizeCursor, true);
  2314. else
  2315. SetViewCursor(fResizeCursor, true);
  2316. } else
  2317. SetViewCursor(B_CURSOR_SYSTEM_DEFAULT, true);
  2318. break;
  2319. }
  2320. }
  2321. void
  2322. TitleView::MouseUp(BPoint position)
  2323. {
  2324. if (fEditMode)
  2325. return;
  2326. switch (fCurrentState) {
  2327. case RESIZING_COLUMN:
  2328. ResizeSelectedColumn(position - BPoint(fClickPoint.x, 0));
  2329. fCurrentState = INACTIVE;
  2330. FixScrollBar(false);
  2331. break;
  2332. case PRESSING_COLUMN: {
  2333. if (fMasterView->SortingEnabled()) {
  2334. if (fSortColumns->HasItem(fSelectedColumn)) {
  2335. if ((modifiers() & B_CONTROL_KEY) == 0
  2336. && fSortColumns->CountItems() > 1) {
  2337. fSortColumns->MakeEmpty();
  2338. fSortColumns->AddItem(fSelectedColumn);
  2339. }
  2340. fSelectedColumn->fSortAscending
  2341. = !fSelectedColumn->fSortAscending;
  2342. } else {
  2343. if ((modifiers() & B_CONTROL_KEY) == 0)
  2344. fSortColumns->MakeEmpty();
  2345. fSortColumns->AddItem(fSelectedColumn);
  2346. fSelectedColumn->fSortAscending = true;
  2347. }
  2348. fOutlineView->StartSorting();
  2349. }
  2350. fCurrentState = INACTIVE;
  2351. Invalidate();
  2352. break;
  2353. }
  2354. case DRAG_COLUMN_INSIDE_TITLE:
  2355. fCurrentState = INACTIVE;
  2356. #if DRAG_TITLE_OUTLINE
  2357. Invalidate(); // xxx Can make this smaller
  2358. #else
  2359. Invalidate(fSelectedColumnRect);
  2360. #endif
  2361. SetViewCursor(B_CURSOR_SYSTEM_DEFAULT, true);
  2362. break;
  2363. case DRAG_COLUMN_OUTSIDE_TITLE:
  2364. fCurrentState = INACTIVE;
  2365. EndRectTracking();
  2366. SetViewCursor(B_CURSOR_SYSTEM_DEFAULT, true);
  2367. break;
  2368. default:
  2369. ;
  2370. }
  2371. }
  2372. void
  2373. TitleView::FrameResized(float width, float height)
  2374. {
  2375. fVisibleRect.right = fVisibleRect.left + width;
  2376. fVisibleRect.bottom = fVisibleRect.top + height;
  2377. FixScrollBar(true);
  2378. }
  2379. // #pragma mark -
  2380. OutlineView::OutlineView(BRect rect, BList* visibleColumns, BList* sortColumns,
  2381. BColumnListView* listView)
  2382. :
  2383. BView(rect, "outline_view", B_FOLLOW_ALL_SIDES,
  2384. B_WILL_DRAW | B_FRAME_EVENTS),
  2385. fColumns(visibleColumns),
  2386. fSortColumns(sortColumns),
  2387. fItemsHeight(0.0),
  2388. fVisibleRect(rect.OffsetToCopy(0, 0)),
  2389. fFocusRow(0),
  2390. fRollOverRow(0),
  2391. fLastSelectedItem(0),
  2392. fFirstSelectedItem(0),
  2393. fSortThread(B_BAD_THREAD_ID),
  2394. fCurrentState(INACTIVE),
  2395. fMasterView(listView),
  2396. fSelectionMode(B_MULTIPLE_SELECTION_LIST),
  2397. fTrackMouse(false),
  2398. fCurrentField(0),
  2399. fCurrentRow(0),
  2400. fCurrentColumn(0),
  2401. fMouseDown(false),
  2402. fCurrentCode(B_OUTSIDE_VIEW),
  2403. fEditMode(false),
  2404. fDragging(false),
  2405. fClickCount(0),
  2406. fDropHighlightY(-1)
  2407. {
  2408. SetViewColor(B_TRANSPARENT_COLOR);
  2409. #if DOUBLE_BUFFERED_COLUMN_RESIZE
  2410. // TODO: This needs to be smart about the size of the buffer.
  2411. // Also, the buffer can be shared with the title's buffer.
  2412. BRect doubleBufferRect(0, 0, 600, 35);
  2413. fDrawBuffer = new BBitmap(doubleBufferRect, B_RGB32, true);
  2414. fDrawBufferView = new BView(doubleBufferRect, "double_buffer_view",
  2415. B_FOLLOW_ALL_SIDES, 0);
  2416. fDrawBuffer->Lock();
  2417. fDrawBuffer->AddChild(fDrawBufferView);
  2418. fDrawBuffer->Unlock();
  2419. #endif
  2420. FixScrollBar(true);
  2421. fSelectionListDummyHead.fNextSelected = &fSelectionListDummyHead;
  2422. fSelectionListDummyHead.fPrevSelected = &fSelectionListDummyHead;
  2423. }
  2424. OutlineView::~OutlineView()
  2425. {
  2426. #if DOUBLE_BUFFERED_COLUMN_RESIZE
  2427. delete fDrawBuffer;
  2428. #endif
  2429. Clear();
  2430. }
  2431. void
  2432. OutlineView::Clear()
  2433. {
  2434. DeselectAll();
  2435. // Make sure selection list doesn't point to deleted rows!
  2436. RecursiveDeleteRows(&fRows, false);
  2437. fItemsHeight = 0.0;
  2438. FixScrollBar(true);
  2439. Invalidate();
  2440. }
  2441. void
  2442. OutlineView::SetSelectionMode(list_view_type mode)
  2443. {
  2444. DeselectAll();
  2445. fSelectionMode = mode;
  2446. }
  2447. list_view_type
  2448. OutlineView::SelectionMode() const
  2449. {
  2450. return fSelectionMode;
  2451. }
  2452. void
  2453. OutlineView::Deselect(BRow* row)
  2454. {
  2455. if (row == NULL)
  2456. return;
  2457. if (row->fNextSelected != 0) {
  2458. row->fNextSelected->fPrevSelected = row->fPrevSelected;
  2459. row->fPrevSelected->fNextSelected = row->fNextSelected;
  2460. row->fNextSelected = 0;
  2461. row->fPrevSelected = 0;
  2462. Invalidate();
  2463. }
  2464. }
  2465. void
  2466. OutlineView::AddToSelection(BRow* row)
  2467. {
  2468. if (row == NULL)
  2469. return;
  2470. if (row->fNextSelected == 0) {
  2471. if (fSelectionMode == B_SINGLE_SELECTION_LIST)
  2472. DeselectAll();
  2473. row->fNextSelected = fSelectionListDummyHead.fNextSelected;
  2474. row->fPrevSelected = &fSelectionListDummyHead;
  2475. row->fNextSelected->fPrevSelected = row;
  2476. row->fPrevSelected->fNextSelected = row;
  2477. BRect invalidRect;
  2478. if (FindVisibleRect(row, &invalidRect))
  2479. Invalidate(invalidRect);
  2480. }
  2481. }
  2482. void
  2483. OutlineView::RecursiveDeleteRows(BRowContainer* list, bool isOwner)
  2484. {
  2485. if (list == NULL)
  2486. return;
  2487. while (true) {
  2488. BRow* row = list->RemoveItemAt(0L);
  2489. if (row == 0)
  2490. break;
  2491. if (row->fChildList)
  2492. RecursiveDeleteRows(row->fChildList, true);
  2493. delete row;
  2494. }
  2495. if (isOwner)
  2496. delete list;
  2497. }
  2498. void
  2499. OutlineView::RedrawColumn(BColumn* column, float leftEdge, bool isFirstColumn)
  2500. {
  2501. // TODO: Remove code duplication (private function which takes a view
  2502. // pointer, pass "this" in non-double buffered mode)!
  2503. // Watch out for sourceRect versus destRect though!
  2504. if (!column)
  2505. return;
  2506. font_height fh;
  2507. GetFontHeight(&fh);
  2508. float line = 0.0;
  2509. bool tintedLine = true;
  2510. for (RecursiveOutlineIterator iterator(&fRows); iterator.CurrentRow();
  2511. line += iterator.CurrentRow()->Height() + 1, iterator.GoToNext()) {
  2512. BRow* row = iterator.CurrentRow();
  2513. float rowHeight = row->Height();
  2514. if (line > fVisibleRect.bottom)
  2515. break;
  2516. tintedLine = !tintedLine;
  2517. if (line + rowHeight >= fVisibleRect.top) {
  2518. #if DOUBLE_BUFFERED_COLUMN_RESIZE
  2519. BRect sourceRect(0, 0, column->Width(), rowHeight);
  2520. #endif
  2521. BRect destRect(leftEdge, line, leftEdge + column->Width(),
  2522. line + rowHeight);
  2523. rgb_color highColor;
  2524. rgb_color lowColor;
  2525. if (row->fNextSelected != 0) {
  2526. if (fEditMode) {
  2527. highColor = fMasterView->Color(B_COLOR_EDIT_BACKGROUND);
  2528. lowColor = fMasterView->Color(B_COLOR_EDIT_BACKGROUND);
  2529. } else {
  2530. highColor = fMasterView->Color(B_COLOR_SELECTION);
  2531. lowColor = fMasterView->Color(B_COLOR_SELECTION);
  2532. }
  2533. } else {
  2534. highColor = fMasterView->Color(B_COLOR_BACKGROUND);
  2535. lowColor = fMasterView->Color(B_COLOR_BACKGROUND);
  2536. }
  2537. if (tintedLine)
  2538. lowColor = tint_color(lowColor, kTintedLineTint);
  2539. #if DOUBLE_BUFFERED_COLUMN_RESIZE
  2540. fDrawBuffer->Lock();
  2541. fDrawBufferView->SetHighColor(highColor);
  2542. fDrawBufferView->SetLowColor(lowColor);
  2543. BFont font;
  2544. GetFont(&font);
  2545. fDrawBufferView->SetFont(&font);
  2546. fDrawBufferView->FillRect(sourceRect, B_SOLID_LOW);
  2547. if (isFirstColumn) {
  2548. // If this is the first column, double buffer drawing the latch
  2549. // too.
  2550. destRect.left += iterator.CurrentLevel() * kOutlineLevelIndent
  2551. - fMasterView->LatchWidth();
  2552. sourceRect.left += iterator.CurrentLevel() * kOutlineLevelIndent
  2553. - fMasterView->LatchWidth();
  2554. LatchType pos = B_NO_LATCH;
  2555. if (row->HasLatch())
  2556. pos = row->fIsExpanded ? B_OPEN_LATCH : B_CLOSED_LATCH;
  2557. BRect latchRect(sourceRect);
  2558. latchRect.right = latchRect.left + fMasterView->LatchWidth();
  2559. fMasterView->DrawLatch(fDrawBufferView, latchRect, pos, row);
  2560. }
  2561. BField* field = row->GetField(column->fFieldID);
  2562. if (field) {
  2563. BRect fieldRect(sourceRect);
  2564. if (isFirstColumn)
  2565. fieldRect.left += fMasterView->LatchWidth();
  2566. #if CONSTRAIN_CLIPPING_REGION
  2567. BRegion clipRegion(fieldRect);
  2568. fDrawBufferView->PushState();
  2569. fDrawBufferView->ConstrainClippingRegion(&clipRegion);
  2570. #endif
  2571. fDrawBufferView->SetHighColor(fMasterView->Color(
  2572. row->fNextSelected ? B_COLOR_SELECTION_TEXT
  2573. : B_COLOR_TEXT));
  2574. float baseline = floor(fieldRect.top + fh.ascent
  2575. + (fieldRect.Height() + 1 - (fh.ascent+fh.descent)) / 2);
  2576. fDrawBufferView->MovePenTo(fieldRect.left + 8, baseline);
  2577. column->DrawField(field, fieldRect, fDrawBufferView);
  2578. #if CONSTRAIN_CLIPPING_REGION
  2579. fDrawBufferView->PopState();
  2580. #endif
  2581. }
  2582. if (fFocusRow == row && !fEditMode && fMasterView->IsFocus()
  2583. && Window()->IsActive()) {
  2584. fDrawBufferView->SetHighColor(fMasterView->Color(
  2585. B_COLOR_ROW_DIVIDER));
  2586. fDrawBufferView->StrokeRect(BRect(-1, sourceRect.top,
  2587. 10000.0, sourceRect.bottom));
  2588. }
  2589. fDrawBufferView->Sync();
  2590. fDrawBuffer->Unlock();
  2591. SetDrawingMode(B_OP_COPY);
  2592. DrawBitmap(fDrawBuffer, sourceRect, destRect);
  2593. #else
  2594. SetHighColor(highColor);
  2595. SetLowColor(lowColor);
  2596. FillRect(destRect, B_SOLID_LOW);
  2597. BField* field = row->GetField(column->fFieldID);
  2598. if (field) {
  2599. #if CONSTRAIN_CLIPPING_REGION
  2600. BRegion clipRegion(destRect);
  2601. PushState();
  2602. ConstrainClippingRegion(&clipRegion);
  2603. #endif
  2604. SetHighColor(fMasterView->Color(row->fNextSelected
  2605. ? B_COLOR_SELECTION_TEXT : B_COLOR_TEXT));
  2606. float baseline = floor(destRect.top + fh.ascent
  2607. + (destRect.Height() + 1 - (fh.ascent + fh.descent)) / 2);
  2608. MovePenTo(destRect.left + 8, baseline);
  2609. column->DrawField(field, destRect, this);
  2610. #if CONSTRAIN_CLIPPING_REGION
  2611. PopState();
  2612. #endif
  2613. }
  2614. if (fFocusRow == row && !fEditMode && fMasterView->IsFocus()
  2615. && Window()->IsActive()) {
  2616. SetHighColor(fMasterView->Color(B_COLOR_ROW_DIVIDER));
  2617. StrokeRect(BRect(0, destRect.top, 10000.0, destRect.bottom));
  2618. }
  2619. #endif
  2620. }
  2621. }
  2622. }
  2623. void
  2624. OutlineView::Draw(BRect invalidBounds)
  2625. {
  2626. #if SMART_REDRAW
  2627. BRegion invalidRegion;
  2628. GetClippingRegion(&invalidRegion);
  2629. #endif
  2630. font_height fh;
  2631. GetFontHeight(&fh);
  2632. float line = 0.0;
  2633. bool tintedLine = true;
  2634. int32 numColumns = fColumns->CountItems();
  2635. for (RecursiveOutlineIterator iterator(&fRows); iterator.CurrentRow();
  2636. iterator.GoToNext()) {
  2637. BRow* row = iterator.CurrentRow();
  2638. if (line > invalidBounds.bottom)
  2639. break;
  2640. tintedLine = !tintedLine;
  2641. float rowHeight = row->Height();
  2642. if (line >= invalidBounds.top - rowHeight) {
  2643. bool isFirstColumn = true;
  2644. float fieldLeftEdge = MAX(kLeftMargin, fMasterView->LatchWidth());
  2645. // setup background color
  2646. rgb_color lowColor;
  2647. if (row->fNextSelected != 0) {
  2648. if (Window()->IsActive()) {
  2649. if (fEditMode)
  2650. lowColor = fMasterView->Color(B_COLOR_EDIT_BACKGROUND);
  2651. else
  2652. lowColor = fMasterView->Color(B_COLOR_SELECTION);
  2653. }
  2654. else
  2655. lowColor = fMasterView->Color(B_COLOR_NON_FOCUS_SELECTION);
  2656. } else
  2657. lowColor = fMasterView->Color(B_COLOR_BACKGROUND);
  2658. if (tintedLine)
  2659. lowColor = tint_color(lowColor, kTintedLineTint);
  2660. for (int columnIndex = 0; columnIndex < numColumns; columnIndex++) {
  2661. BColumn* column = (BColumn*) fColumns->ItemAt(columnIndex);
  2662. if (!column->IsVisible())
  2663. continue;
  2664. if (!isFirstColumn && fieldLeftEdge > invalidBounds.right)
  2665. break;
  2666. if (fieldLeftEdge + column->Width() >= invalidBounds.left) {
  2667. BRect fullRect(fieldLeftEdge, line,
  2668. fieldLeftEdge + column->Width(), line + rowHeight);
  2669. bool clippedFirstColumn = false;
  2670. // This happens when a column is indented past the
  2671. // beginning of the next column.
  2672. SetHighColor(lowColor);
  2673. BRect destRect(fullRect);
  2674. if (isFirstColumn) {
  2675. fullRect.left -= fMasterView->LatchWidth();
  2676. destRect.left += iterator.CurrentLevel()
  2677. * kOutlineLevelIndent;
  2678. if (destRect.left >= destRect.right) {
  2679. // clipped
  2680. FillRect(BRect(0, line, fieldLeftEdge
  2681. + column->Width(), line + rowHeight));
  2682. clippedFirstColumn = true;
  2683. }
  2684. FillRect(BRect(0, line, MAX(kLeftMargin,
  2685. fMasterView->LatchWidth()), line + row->Height()));
  2686. }
  2687. #if SMART_REDRAW
  2688. if (!clippedFirstColumn
  2689. && invalidRegion.Intersects(fullRect)) {
  2690. #else
  2691. if (!clippedFirstColumn) {
  2692. #endif
  2693. FillRect(fullRect); // Using color set above
  2694. // Draw the latch widget if it has one.
  2695. if (isFirstColumn) {
  2696. if (row == fTargetRow
  2697. && fCurrentState == LATCH_CLICKED) {
  2698. // Note that this only occurs if the user is
  2699. // holding down a latch while items are added
  2700. // in the background.
  2701. BPoint pos;
  2702. uint32 buttons;
  2703. GetMouse(&pos, &buttons);
  2704. if (fLatchRect.Contains(pos)) {
  2705. fMasterView->DrawLatch(this, fLatchRect,
  2706. B_PRESSED_LATCH, fTargetRow);
  2707. } else {
  2708. fMasterView->DrawLatch(this, fLatchRect,
  2709. row->fIsExpanded ? B_OPEN_LATCH
  2710. : B_CLOSED_LATCH, fTargetRow);
  2711. }
  2712. } else {
  2713. LatchType pos = B_NO_LATCH;
  2714. if (row->HasLatch())
  2715. pos = row->fIsExpanded ? B_OPEN_LATCH
  2716. : B_CLOSED_LATCH;
  2717. fMasterView->DrawLatch(this,
  2718. BRect(destRect.left
  2719. - fMasterView->LatchWidth(),
  2720. destRect.top, destRect.left,
  2721. destRect.bottom), pos, row);
  2722. }
  2723. }
  2724. SetHighColor(fMasterView->HighColor());
  2725. // The master view just holds the high color for us.
  2726. SetLowColor(lowColor);
  2727. BField* field = row->GetField(column->fFieldID);
  2728. if (field) {
  2729. #if CONSTRAIN_CLIPPING_REGION
  2730. BRegion clipRegion(destRect);
  2731. PushState();
  2732. ConstrainClippingRegion(&clipRegion);
  2733. #endif
  2734. SetHighColor(fMasterView->Color(
  2735. row->fNextSelected ? B_COLOR_SELECTION_TEXT
  2736. : B_COLOR_TEXT));
  2737. float baseline = floor(destRect.top + fh.ascent
  2738. + (destRect.Height() + 1
  2739. - (fh.ascent+fh.descent)) / 2);
  2740. MovePenTo(destRect.left + 8, baseline);
  2741. column->DrawField(field, destRect, this);
  2742. #if CONSTRAIN_CLIPPING_REGION
  2743. PopState();
  2744. #endif
  2745. }
  2746. }
  2747. }
  2748. isFirstColumn = false;
  2749. fieldLeftEdge += column->Width() + 1;
  2750. }
  2751. if (fieldLeftEdge <= invalidBounds.right) {
  2752. SetHighColor(lowColor);
  2753. FillRect(BRect(fieldLeftEdge, line, invalidBounds.right,
  2754. line + rowHeight));
  2755. }
  2756. }
  2757. // indicate the keyboard focus row
  2758. if (fFocusRow == row && !fEditMode && fMasterView->IsFocus()
  2759. && Window()->IsActive()) {
  2760. SetHighColor(fMasterView->Color(B_COLOR_ROW_DIVIDER));
  2761. StrokeRect(BRect(0, line, 10000.0, line + rowHeight));
  2762. }
  2763. line += rowHeight + 1;
  2764. }
  2765. if (line <= invalidBounds.bottom) {
  2766. // fill background below last item
  2767. SetHighColor(fMasterView->Color(B_COLOR_BACKGROUND));
  2768. FillRect(BRect(invalidBounds.left, line, invalidBounds.right,
  2769. invalidBounds.bottom));
  2770. }
  2771. // Draw the drop target line
  2772. if (fDropHighlightY != -1) {
  2773. InvertRect(BRect(0, fDropHighlightY - kDropHighlightLineHeight / 2,
  2774. 1000000, fDropHighlightY + kDropHighlightLineHeight / 2));
  2775. }
  2776. }
  2777. BRow*
  2778. OutlineView::FindRow(float ypos, int32* _rowIndent, float* _top)
  2779. {
  2780. if (_rowIndent && _top) {
  2781. float line = 0.0;
  2782. for (RecursiveOutlineIterator iterator(&fRows); iterator.CurrentRow();
  2783. iterator.GoToNext()) {
  2784. BRow* row = iterator.CurrentRow();
  2785. if (line > ypos)
  2786. break;
  2787. float rowHeight = row->Height();
  2788. if (ypos <= line + rowHeight) {
  2789. *_top = line;
  2790. *_rowIndent = iterator.CurrentLevel();
  2791. return row;
  2792. }
  2793. line += rowHeight + 1;
  2794. }
  2795. }
  2796. return NULL;
  2797. }
  2798. void OutlineView::SetMouseTrackingEnabled(bool enabled)
  2799. {
  2800. fTrackMouse = enabled;
  2801. if (!enabled && fDropHighlightY != -1) {
  2802. // Erase the old target line
  2803. InvertRect(BRect(0, fDropHighlightY - kDropHighlightLineHeight / 2,
  2804. 1000000, fDropHighlightY + kDropHighlightLineHeight / 2));
  2805. fDropHighlightY = -1;
  2806. }
  2807. }
  2808. //
  2809. // Note that this interaction is not totally safe. If items are added to
  2810. // the list in the background, the widget rect will be incorrect, possibly
  2811. // resulting in drawing glitches. The code that adds items needs to be a little smarter
  2812. // about invalidating state.
  2813. //
  2814. void
  2815. OutlineView::MouseDown(BPoint position)
  2816. {
  2817. if (!fEditMode)
  2818. fMasterView->MakeFocus(true);
  2819. // Check to see if the user is clicking on a widget to open a section
  2820. // of the list.
  2821. bool reset_click_count = false;
  2822. int32 indent;
  2823. float rowTop;
  2824. BRow* row = FindRow(position.y, &indent, &rowTop);
  2825. if (row != NULL) {
  2826. // Update fCurrentField
  2827. bool handle_field = false;
  2828. BField* new_field = 0;
  2829. BRow* new_row = 0;
  2830. BColumn* new_column = 0;
  2831. BRect new_rect;
  2832. if (position.y >= 0) {
  2833. if (position.x >= 0) {
  2834. float x = 0;
  2835. for (int32 c = 0; c < fMasterView->CountColumns(); c++) {
  2836. new_column = fMasterView->ColumnAt(c);
  2837. if (!new_column->IsVisible())
  2838. continue;
  2839. if ((MAX(kLeftMargin, fMasterView->LatchWidth()) + x)
  2840. + new_column->Width() >= position.x) {
  2841. if (new_column->WantsEvents()) {
  2842. new_field = row->GetField(c);
  2843. new_row = row;
  2844. FindRect(new_row,&new_rect);
  2845. new_rect.left = MAX(kLeftMargin,
  2846. fMasterView->LatchWidth()) + x;
  2847. new_rect.right = new_rect.left
  2848. + new_column->Width() - 1;
  2849. handle_field = true;
  2850. }
  2851. break;
  2852. }
  2853. x += new_column->Width();
  2854. }
  2855. }
  2856. }
  2857. // Handle mouse down
  2858. if (handle_field) {
  2859. fMouseDown = true;
  2860. fFieldRect = new_rect;
  2861. fCurrentColumn = new_column;
  2862. fCurrentRow = new_row;
  2863. fCurrentField = new_field;
  2864. fCurrentCode = B_INSIDE_VIEW;
  2865. BMessage* message = Window()->CurrentMessage();
  2866. int32 buttons = 1;
  2867. message->FindInt32("buttons", &buttons);
  2868. fCurrentColumn->MouseDown(fMasterView, fCurrentRow,
  2869. fCurrentField, fFieldRect, position, buttons);
  2870. }
  2871. if (!fEditMode) {
  2872. fTargetRow = row;
  2873. fTargetRowTop = rowTop;
  2874. FindVisibleRect(fFocusRow, &fFocusRowRect);
  2875. float leftWidgetBoundry = indent * kOutlineLevelIndent
  2876. + MAX(kLeftMargin, fMasterView->LatchWidth())
  2877. - fMasterView->LatchWidth();
  2878. fLatchRect.Set(leftWidgetBoundry, rowTop, leftWidgetBoundry
  2879. + fMasterView->LatchWidth(), rowTop + row->Height());
  2880. if (fLatchRect.Contains(position) && row->HasLatch()) {
  2881. fCurrentState = LATCH_CLICKED;
  2882. if (fTargetRow->fNextSelected != 0)
  2883. SetHighColor(fMasterView->Color(B_COLOR_SELECTION));
  2884. else
  2885. SetHighColor(fMasterView->Color(B_COLOR_BACKGROUND));
  2886. FillRect(fLatchRect);
  2887. if (fLatchRect.Contains(position)) {
  2888. fMasterView->DrawLatch(this, fLatchRect, B_PRESSED_LATCH,
  2889. row);
  2890. } else {
  2891. fMasterView->DrawLatch(this, fLatchRect,
  2892. fTargetRow->fIsExpanded ? B_OPEN_LATCH
  2893. : B_CLOSED_LATCH, row);
  2894. }
  2895. } else {
  2896. Invalidate(fFocusRowRect);
  2897. fFocusRow = fTargetRow;
  2898. FindVisibleRect(fFocusRow, &fFocusRowRect);
  2899. ASSERT(fTargetRow != 0);
  2900. if ((modifiers() & B_CONTROL_KEY) == 0)
  2901. DeselectAll();
  2902. if ((modifiers() & B_SHIFT_KEY) != 0 && fFirstSelectedItem != 0
  2903. && fSelectionMode == B_MULTIPLE_SELECTION_LIST) {
  2904. SelectRange(fFirstSelectedItem, fTargetRow);
  2905. }
  2906. else {
  2907. if (fTargetRow->fNextSelected != 0) {
  2908. // Unselect row
  2909. fTargetRow->fNextSelected->fPrevSelected
  2910. = fTargetRow->fPrevSelected;
  2911. fTargetRow->fPrevSelected->fNextSelected
  2912. = fTargetRow->fNextSelected;
  2913. fTargetRow->fPrevSelected = 0;
  2914. fTargetRow->fNextSelected = 0;
  2915. fFirstSelectedItem = NULL;
  2916. } else {
  2917. // Select row
  2918. if (fSelectionMode == B_SINGLE_SELECTION_LIST)
  2919. DeselectAll();
  2920. fTargetRow->fNextSelected
  2921. = fSelectionListDummyHead.fNextSelected;
  2922. fTargetRow->fPrevSelected
  2923. = &fSelectionListDummyHead;
  2924. fTargetRow->fNextSelected->fPrevSelected = fTargetRow;
  2925. fTargetRow->fPrevSelected->fNextSelected = fTargetRow;
  2926. fFirstSelectedItem = fTargetRow;
  2927. }
  2928. Invalidate(BRect(fVisibleRect.left, fTargetRowTop,
  2929. fVisibleRect.right,
  2930. fTargetRowTop + fTargetRow->Height()));
  2931. }
  2932. fCurrentState = ROW_CLICKED;
  2933. if (fLastSelectedItem != fTargetRow)
  2934. reset_click_count = true;
  2935. fLastSelectedItem = fTargetRow;
  2936. fMasterView->SelectionChanged();
  2937. }
  2938. }
  2939. SetMouseEventMask(B_POINTER_EVENTS, B_LOCK_WINDOW_FOCUS |
  2940. B_NO_POINTER_HISTORY);
  2941. } else if (fFocusRow != 0) {
  2942. // User clicked in open space, unhighlight focus row.
  2943. FindVisibleRect(fFocusRow, &fFocusRowRect);
  2944. fFocusRow = 0;
  2945. Invalidate(fFocusRowRect);
  2946. }
  2947. // We stash the click counts here because the 'clicks' field
  2948. // is not in the CurrentMessage() when MouseUp is called... ;(
  2949. if (reset_click_count)
  2950. fClickCount = 1;
  2951. else
  2952. Window()->CurrentMessage()->FindInt32("clicks", &fClickCount);
  2953. fClickPoint = position;
  2954. }
  2955. void
  2956. OutlineView::MouseMoved(BPoint position, uint32 /*transit*/,
  2957. const BMessage* /*dragMessage*/)
  2958. {
  2959. if (!fMouseDown) {
  2960. // Update fCurrentField
  2961. bool handle_field = false;
  2962. BField* new_field = 0;
  2963. BRow* new_row = 0;
  2964. BColumn* new_column = 0;
  2965. BRect new_rect(0,0,0,0);
  2966. if (position.y >=0 ) {
  2967. float top;
  2968. int32 indent;
  2969. BRow* row = FindRow(position.y, &indent, &top);
  2970. if (row && position.x >=0 ) {
  2971. float x=0;
  2972. for (int32 c=0;c<fMasterView->CountColumns();c++) {
  2973. new_column = fMasterView->ColumnAt(c);
  2974. if (!new_column->IsVisible())
  2975. continue;
  2976. if ((MAX(kLeftMargin,
  2977. fMasterView->LatchWidth()) + x) + new_column->Width()
  2978. > position.x) {
  2979. if(new_column->WantsEvents()) {
  2980. new_field = row->GetField(c);
  2981. new_row = row;
  2982. FindRect(new_row,&new_rect);
  2983. new_rect.left = MAX(kLeftMargin,
  2984. fMasterView->LatchWidth()) + x;
  2985. new_rect.right = new_rect.left
  2986. + new_column->Width() - 1;
  2987. handle_field = true;
  2988. }
  2989. break;
  2990. }
  2991. x += new_column->Width();
  2992. }
  2993. }
  2994. }
  2995. // Handle mouse moved
  2996. if (handle_field) {
  2997. if (new_field != fCurrentField) {
  2998. if (fCurrentField) {
  2999. fCurrentColumn->MouseMoved(fMasterView, fCurrentRow,
  3000. fCurrentField, fFieldRect, position, 0,
  3001. fCurrentCode = B_EXITED_VIEW);
  3002. }
  3003. fCurrentColumn = new_column;
  3004. fCurrentRow = new_row;
  3005. fCurrentField = new_field;
  3006. fFieldRect = new_rect;
  3007. if (fCurrentField) {
  3008. fCurrentColumn->MouseMoved(fMasterView, fCurrentRow,
  3009. fCurrentField, fFieldRect, position, 0,
  3010. fCurrentCode = B_ENTERED_VIEW);
  3011. }
  3012. } else {
  3013. if (fCurrentField) {
  3014. fCurrentColumn->MouseMoved(fMasterView, fCurrentRow,
  3015. fCurrentField, fFieldRect, position, 0,
  3016. fCurrentCode = B_INSIDE_VIEW);
  3017. }
  3018. }
  3019. } else {
  3020. if (fCurrentField) {
  3021. fCurrentColumn->MouseMoved(fMasterView, fCurrentRow,
  3022. fCurrentField, fFieldRect, position, 0,
  3023. fCurrentCode = B_EXITED_VIEW);
  3024. fCurrentField = 0;
  3025. fCurrentColumn = 0;
  3026. fCurrentRow = 0;
  3027. }
  3028. }
  3029. } else {
  3030. if (fCurrentField) {
  3031. if (fFieldRect.Contains(position)) {
  3032. if (fCurrentCode == B_OUTSIDE_VIEW
  3033. || fCurrentCode == B_EXITED_VIEW) {
  3034. fCurrentColumn->MouseMoved(fMasterView, fCurrentRow,
  3035. fCurrentField, fFieldRect, position, 1,
  3036. fCurrentCode = B_ENTERED_VIEW);
  3037. } else {
  3038. fCurrentColumn->MouseMoved(fMasterView, fCurrentRow,
  3039. fCurrentField, fFieldRect, position, 1,
  3040. fCurrentCode = B_INSIDE_VIEW);
  3041. }
  3042. } else {
  3043. if (fCurrentCode == B_INSIDE_VIEW
  3044. || fCurrentCode == B_ENTERED_VIEW) {
  3045. fCurrentColumn->MouseMoved(fMasterView, fCurrentRow,
  3046. fCurrentField, fFieldRect, position, 1,
  3047. fCurrentCode = B_EXITED_VIEW);
  3048. } else {
  3049. fCurrentColumn->MouseMoved(fMasterView, fCurrentRow,
  3050. fCurrentField, fFieldRect, position, 1,
  3051. fCurrentCode = B_OUTSIDE_VIEW);
  3052. }
  3053. }
  3054. }
  3055. }
  3056. if (!fEditMode) {
  3057. switch (fCurrentState) {
  3058. case LATCH_CLICKED:
  3059. if (fTargetRow->fNextSelected != 0)
  3060. SetHighColor(fMasterView->Color(B_COLOR_SELECTION));
  3061. else
  3062. SetHighColor(fMasterView->Color(B_COLOR_BACKGROUND));
  3063. FillRect(fLatchRect);
  3064. if (fLatchRect.Contains(position)) {
  3065. fMasterView->DrawLatch(this, fLatchRect, B_PRESSED_LATCH,
  3066. fTargetRow);
  3067. } else {
  3068. fMasterView->DrawLatch(this, fLatchRect,
  3069. fTargetRow->fIsExpanded ? B_OPEN_LATCH : B_CLOSED_LATCH,
  3070. fTargetRow);
  3071. }
  3072. break;
  3073. case ROW_CLICKED:
  3074. if (abs((int)(position.x - fClickPoint.x)) > kRowDragSensitivity
  3075. || abs((int)(position.y - fClickPoint.y))
  3076. > kRowDragSensitivity) {
  3077. fCurrentState = DRAGGING_ROWS;
  3078. fMasterView->InitiateDrag(fClickPoint,
  3079. fTargetRow->fNextSelected != 0);
  3080. }
  3081. break;
  3082. case DRAGGING_ROWS:
  3083. #if 0
  3084. // falls through...
  3085. #else
  3086. if (fTrackMouse /*&& message*/) {
  3087. if (fVisibleRect.Contains(position)) {
  3088. float top;
  3089. int32 indent;
  3090. BRow* target = FindRow(position.y, &indent, &top);
  3091. if (target)
  3092. SetFocusRow(target, true);
  3093. }
  3094. }
  3095. break;
  3096. #endif
  3097. default: {
  3098. if (fTrackMouse /*&& message*/) {
  3099. // Draw a highlight line...
  3100. if (fVisibleRect.Contains(position)) {
  3101. float top;
  3102. int32 indent;
  3103. BRow* target = FindRow(position.y, &indent, &top);
  3104. if (target == fRollOverRow)
  3105. break;
  3106. if (fRollOverRow) {
  3107. BRect rect;
  3108. FindRect(fRollOverRow, &rect);
  3109. Invalidate(rect);
  3110. }
  3111. fRollOverRow = target;
  3112. #if 0
  3113. SetFocusRow(fRollOverRow,false);
  3114. #else
  3115. PushState();
  3116. SetDrawingMode(B_OP_BLEND);
  3117. SetHighColor(255, 255, 255, 255);
  3118. BRect rect;
  3119. FindRect(fRollOverRow, &rect);
  3120. rect.bottom -= 1.0;
  3121. FillRect(rect);
  3122. PopState();
  3123. #endif
  3124. } else {
  3125. if (fRollOverRow) {
  3126. BRect rect;
  3127. FindRect(fRollOverRow, &rect);
  3128. Invalidate(rect);
  3129. fRollOverRow = NULL;
  3130. }
  3131. }
  3132. }
  3133. }
  3134. }
  3135. }
  3136. }
  3137. void
  3138. OutlineView::MouseUp(BPoint position)
  3139. {
  3140. if (fCurrentField) {
  3141. fCurrentColumn->MouseUp(fMasterView, fCurrentRow, fCurrentField);
  3142. fMouseDown = false;
  3143. }
  3144. if (fEditMode)
  3145. return;
  3146. switch (fCurrentState) {
  3147. case LATCH_CLICKED:
  3148. if (fLatchRect.Contains(position)) {
  3149. fMasterView->ExpandOrCollapse(fTargetRow,
  3150. !fTargetRow->fIsExpanded);
  3151. }
  3152. Invalidate(fLatchRect);
  3153. fCurrentState = INACTIVE;
  3154. break;
  3155. case ROW_CLICKED:
  3156. if (fClickCount > 1
  3157. && abs((int)fClickPoint.x - (int)position.x)
  3158. < kDoubleClickMoveSensitivity
  3159. && abs((int)fClickPoint.y - (int)position.y)
  3160. < kDoubleClickMoveSensitivity) {
  3161. fMasterView->ItemInvoked();
  3162. }
  3163. fCurrentState = INACTIVE;
  3164. break;
  3165. case DRAGGING_ROWS:
  3166. fCurrentState = INACTIVE;
  3167. // Falls through
  3168. default:
  3169. if (fDropHighlightY != -1) {
  3170. InvertRect(BRect(0,
  3171. fDropHighlightY - kDropHighlightLineHeight / 2,
  3172. 1000000, fDropHighlightY + kDropHighlightLineHeight / 2));
  3173. // Erase the old target line
  3174. fDropHighlightY = -1;
  3175. }
  3176. }
  3177. }
  3178. void
  3179. OutlineView::MessageReceived(BMessage* message)
  3180. {
  3181. if (message->WasDropped()) {
  3182. fMasterView->MessageDropped(message,
  3183. ConvertFromScreen(message->DropPoint()));
  3184. } else {
  3185. BView::MessageReceived(message);
  3186. }
  3187. }
  3188. void
  3189. OutlineView::ChangeFocusRow(bool up, bool updateSelection,
  3190. bool addToCurrentSelection)
  3191. {
  3192. int32 indent;
  3193. float top;
  3194. float newRowPos = 0;
  3195. float verticalScroll = 0;
  3196. if (fFocusRow) {
  3197. // A row currently has the focus, get information about it
  3198. newRowPos = fFocusRowRect.top + (up ? -4 : fFocusRow->Height() + 4);
  3199. if (newRowPos < fVisibleRect.top + 20)
  3200. verticalScroll = newRowPos - 20;
  3201. else if (newRowPos > fVisibleRect.bottom - 20)
  3202. verticalScroll = newRowPos - fVisibleRect.Height() + 20;
  3203. } else
  3204. newRowPos = fVisibleRect.top + 2;
  3205. // no row is currently focused, set this to the top of the window
  3206. // so we will select the first visible item in the list.
  3207. BRow* newRow = FindRow(newRowPos, &indent, &top);
  3208. if (newRow) {
  3209. if (fFocusRow) {
  3210. fFocusRowRect.right = 10000;
  3211. Invalidate(fFocusRowRect);
  3212. }
  3213. fFocusRow = newRow;
  3214. fFocusRowRect.top = top;
  3215. fFocusRowRect.left = 0;
  3216. fFocusRowRect.right = 10000;
  3217. fFocusRowRect.bottom = fFocusRowRect.top + fFocusRow->Height();
  3218. Invalidate(fFocusRowRect);
  3219. if (updateSelection) {
  3220. if (!addToCurrentSelection
  3221. || fSelectionMode == B_SINGLE_SELECTION_LIST) {
  3222. DeselectAll();
  3223. }
  3224. if (fFocusRow->fNextSelected == 0) {
  3225. fFocusRow->fNextSelected
  3226. = fSelectionListDummyHead.fNextSelected;
  3227. fFocusRow->fPrevSelected = &fSelectionListDummyHead;
  3228. fFocusRow->fNextSelected->fPrevSelected = fFocusRow;
  3229. fFocusRow->fPrevSelected->fNextSelected = fFocusRow;
  3230. }
  3231. fLastSelectedItem = fFocusRow;
  3232. }
  3233. } else
  3234. Invalidate(fFocusRowRect);
  3235. if (verticalScroll != 0) {
  3236. BScrollBar* vScrollBar = ScrollBar(B_VERTICAL);
  3237. float min, max;
  3238. vScrollBar->GetRange(&min, &max);
  3239. if (verticalScroll < min)
  3240. verticalScroll = min;
  3241. else if (verticalScroll > max)
  3242. verticalScroll = max;
  3243. vScrollBar->SetValue(verticalScroll);
  3244. }
  3245. if (newRow && updateSelection)
  3246. fMasterView->SelectionChanged();
  3247. }
  3248. void
  3249. OutlineView::MoveFocusToVisibleRect()
  3250. {
  3251. fFocusRow = 0;
  3252. ChangeFocusRow(true, true, false);
  3253. }
  3254. BRow*
  3255. OutlineView::CurrentSelection(BRow* lastSelected) const
  3256. {
  3257. BRow* row;
  3258. if (lastSelected == 0)
  3259. row = fSelectionListDummyHead.fNextSelected;
  3260. else
  3261. row = lastSelected->fNextSelected;
  3262. if (row == &fSelectionListDummyHead)
  3263. row = 0;
  3264. return row;
  3265. }
  3266. void
  3267. OutlineView::ToggleFocusRowSelection(bool selectRange)
  3268. {
  3269. if (fFocusRow == 0)
  3270. return;
  3271. if (selectRange && fSelectionMode == B_MULTIPLE_SELECTION_LIST)
  3272. SelectRange(fLastSelectedItem, fFocusRow);
  3273. else {
  3274. if (fFocusRow->fNextSelected != 0) {
  3275. // Unselect row
  3276. fFocusRow->fNextSelected->fPrevSelected = fFocusRow->fPrevSelected;
  3277. fFocusRow->fPrevSelected->fNextSelected = fFocusRow->fNextSelected;
  3278. fFocusRow->fPrevSelected = 0;
  3279. fFocusRow->fNextSelected = 0;
  3280. } else {
  3281. // Select row
  3282. if (fSelectionMode == B_SINGLE_SELECTION_LIST)
  3283. DeselectAll();
  3284. fFocusRow->fNextSelected = fSelectionListDummyHead.fNextSelected;
  3285. fFocusRow->fPrevSelected = &fSelectionListDummyHead;
  3286. fFocusRow->fNextSelected->fPrevSelected = fFocusRow;
  3287. fFocusRow->fPrevSelected->fNextSelected = fFocusRow;
  3288. }
  3289. }
  3290. fLastSelectedItem = fFocusRow;
  3291. fMasterView->SelectionChanged();
  3292. Invalidate(fFocusRowRect);
  3293. }
  3294. void
  3295. OutlineView::ToggleFocusRowOpen()
  3296. {
  3297. if (fFocusRow)
  3298. fMasterView->ExpandOrCollapse(fFocusRow, !fFocusRow->fIsExpanded);
  3299. }
  3300. void
  3301. OutlineView::ExpandOrCollapse(BRow* parentRow, bool expand)
  3302. {
  3303. // TODO: Could use CopyBits here to speed things up.
  3304. if (parentRow == NULL)
  3305. return;
  3306. if (parentRow->fIsExpanded == expand)
  3307. return;
  3308. parentRow->fIsExpanded = expand;
  3309. BRect parentRect;
  3310. if (FindRect(parentRow, &parentRect)) {
  3311. // Determine my new height
  3312. float subTreeHeight = 0.0;
  3313. if (parentRow->fIsExpanded)
  3314. for (RecursiveOutlineIterator iterator(parentRow->fChildList);
  3315. iterator.CurrentRow();
  3316. iterator.GoToNext()
  3317. )
  3318. {
  3319. subTreeHeight += iterator.CurrentRow()->Height()+1;
  3320. }
  3321. else
  3322. for (RecursiveOutlineIterator iterator(parentRow->fChildList);
  3323. iterator.CurrentRow();
  3324. iterator.GoToNext()
  3325. )
  3326. {
  3327. subTreeHeight -= iterator.CurrentRow()->Height()+1;
  3328. }
  3329. fItemsHeight += subTreeHeight;
  3330. // Adjust focus row if necessary.
  3331. if (FindRect(fFocusRow, &fFocusRowRect) == false) {
  3332. // focus row is in a subtree that has collapsed,
  3333. // move it up to the parent.
  3334. fFocusRow = parentRow;
  3335. FindRect(fFocusRow, &fFocusRowRect);
  3336. }
  3337. Invalidate(BRect(0, parentRect.top, fVisibleRect.right,
  3338. fVisibleRect.bottom));
  3339. FixScrollBar(false);
  3340. }
  3341. }
  3342. void
  3343. OutlineView::RemoveRow(BRow* row)
  3344. {
  3345. if (row == NULL)
  3346. return;
  3347. BRow* parentRow;
  3348. bool parentIsVisible;
  3349. FindParent(row, &parentRow, &parentIsVisible);
  3350. // NOTE: This could be a root row without a parent, in which case
  3351. // it is always visible, though.
  3352. // Adjust height for the visible sub-tree that is going to be removed.
  3353. float subTreeHeight = 0.0f;
  3354. if (parentIsVisible && (parentRow == NULL || parentRow->fIsExpanded)) {
  3355. // The row itself is visible at least.
  3356. subTreeHeight = row->Height() + 1;
  3357. if (row->fIsExpanded) {
  3358. // Adjust for the height of visible sub-items as well.
  3359. // (By default, the iterator follows open branches only.)
  3360. for (RecursiveOutlineIterator iterator(row->fChildList);
  3361. iterator.CurrentRow(); iterator.GoToNext())
  3362. subTreeHeight += iterator.CurrentRow()->Height() + 1;
  3363. }
  3364. BRect invalid;
  3365. if (FindRect(row, &invalid)) {
  3366. invalid.bottom = Bounds().bottom;
  3367. if (invalid.IsValid())
  3368. Invalidate(invalid);
  3369. }
  3370. }
  3371. fItemsHeight -= subTreeHeight;
  3372. FixScrollBar(false);
  3373. int32 indent = 0;
  3374. float top = 0.0;
  3375. if (FindRow(fVisibleRect.top, &indent, &top) == NULL && ScrollBar(B_VERTICAL) != NULL) {
  3376. // after removing this row, no rows are actually visible any more,
  3377. // force a scroll to make them visible again
  3378. if (fItemsHeight > fVisibleRect.Height())
  3379. ScrollBy(0.0, fItemsHeight - fVisibleRect.Height() - Bounds().top);
  3380. else
  3381. ScrollBy(0.0, -Bounds().top);
  3382. }
  3383. if (parentRow != NULL) {
  3384. parentRow->fChildList->RemoveItem(row);
  3385. if (parentRow->fChildList->CountItems() == 0) {
  3386. delete parentRow->fChildList;
  3387. parentRow->fChildList = 0;
  3388. // It was the last child row of the parent, which also means the
  3389. // latch disappears.
  3390. BRect parentRowRect;
  3391. if (parentIsVisible && FindRect(parentRow, &parentRowRect))
  3392. Invalidate(parentRowRect);
  3393. }
  3394. } else
  3395. fRows.RemoveItem(row);
  3396. // Adjust focus row if necessary.
  3397. if (fFocusRow && !FindRect(fFocusRow, &fFocusRowRect)) {
  3398. // focus row is in a subtree that is gone, move it up to the parent.
  3399. fFocusRow = parentRow;
  3400. if (fFocusRow)
  3401. FindRect(fFocusRow, &fFocusRowRect);
  3402. }
  3403. // Remove this from the selection if necessary
  3404. if (row->fNextSelected != 0) {
  3405. row->fNextSelected->fPrevSelected = row->fPrevSelected;
  3406. row->fPrevSelected->fNextSelected = row->fNextSelected;
  3407. row->fPrevSelected = 0;
  3408. row->fNextSelected = 0;
  3409. fMasterView->SelectionChanged();
  3410. }
  3411. fCurrentColumn = 0;
  3412. fCurrentRow = 0;
  3413. fCurrentField = 0;
  3414. }
  3415. BRowContainer*
  3416. OutlineView::RowList()
  3417. {
  3418. return &fRows;
  3419. }
  3420. void
  3421. OutlineView::UpdateRow(BRow* row)
  3422. {
  3423. if (row) {
  3424. // Determine if this row has changed its sort order
  3425. BRow* parentRow = NULL;
  3426. bool parentIsVisible = false;
  3427. FindParent(row, &parentRow, &parentIsVisible);
  3428. BRowContainer* list = (parentRow == NULL) ? &fRows : parentRow->fChildList;
  3429. if(list) {
  3430. int32 rowIndex = list->IndexOf(row);
  3431. ASSERT(rowIndex >= 0);
  3432. ASSERT(list->ItemAt(rowIndex) == row);
  3433. bool rowMoved = false;
  3434. if (rowIndex > 0 && CompareRows(list->ItemAt(rowIndex - 1), row) > 0)
  3435. rowMoved = true;
  3436. if (rowIndex < list->CountItems() - 1 && CompareRows(list->ItemAt(rowIndex + 1),
  3437. row) < 0)
  3438. rowMoved = true;
  3439. if (rowMoved) {
  3440. // Sort location of this row has changed.
  3441. // Remove and re-add in the right spot
  3442. SortList(list, parentIsVisible && (parentRow == NULL || parentRow->fIsExpanded));
  3443. } else if (parentIsVisible && (parentRow == NULL || parentRow->fIsExpanded)) {
  3444. BRect invalidRect;
  3445. if (FindVisibleRect(row, &invalidRect))
  3446. Invalidate(invalidRect);
  3447. }
  3448. }
  3449. }
  3450. }
  3451. void
  3452. OutlineView::AddRow(BRow* row, int32 Index, BRow* parentRow)
  3453. {
  3454. if (!row)
  3455. return;
  3456. row->fParent = parentRow;
  3457. if (fMasterView->SortingEnabled() && !fSortColumns->IsEmpty()) {
  3458. // Ignore index here.
  3459. if (parentRow) {
  3460. if (parentRow->fChildList == NULL)
  3461. parentRow->fChildList = new BRowContainer;
  3462. AddSorted(parentRow->fChildList, row);
  3463. } else
  3464. AddSorted(&fRows, row);
  3465. } else {
  3466. // Note, a -1 index implies add to end if sorting is not enabled
  3467. if (parentRow) {
  3468. if (parentRow->fChildList == 0)
  3469. parentRow->fChildList = new BRowContainer;
  3470. if (Index < 0 || Index > parentRow->fChildList->CountItems())
  3471. parentRow->fChildList->AddItem(row);
  3472. else
  3473. parentRow->fChildList->AddItem(row, Index);
  3474. } else {
  3475. if (Index < 0 || Index >= fRows.CountItems())
  3476. fRows.AddItem(row);
  3477. else
  3478. fRows.AddItem(row, Index);
  3479. }
  3480. }
  3481. if (parentRow == 0 || parentRow->fIsExpanded)
  3482. fItemsHeight += row->Height() + 1;
  3483. FixScrollBar(false);
  3484. BRect newRowRect;
  3485. bool newRowIsInOpenBranch = FindRect(row, &newRowRect);
  3486. if (fFocusRow && fFocusRowRect.top > newRowRect.bottom) {
  3487. // The focus row has moved.
  3488. Invalidate(fFocusRowRect);
  3489. FindRect(fFocusRow, &fFocusRowRect);
  3490. Invalidate(fFocusRowRect);
  3491. }
  3492. if (newRowIsInOpenBranch) {
  3493. if (fCurrentState == INACTIVE) {
  3494. if (newRowRect.bottom < fVisibleRect.top) {
  3495. // The new row is totally above the current viewport, move
  3496. // everything down and redraw the first line.
  3497. BRect source(fVisibleRect);
  3498. BRect dest(fVisibleRect);
  3499. source.bottom -= row->Height() + 1;
  3500. dest.top += row->Height() + 1;
  3501. CopyBits(source, dest);
  3502. Invalidate(BRect(fVisibleRect.left, fVisibleRect.top, fVisibleRect.right,
  3503. fVisibleRect.top + newRowRect.Height()));
  3504. } else if (newRowRect.top < fVisibleRect.bottom) {
  3505. // New item is somewhere in the current region. Scroll everything
  3506. // beneath it down and invalidate just the new row rect.
  3507. BRect source(fVisibleRect.left, newRowRect.top, fVisibleRect.right,
  3508. fVisibleRect.bottom - newRowRect.Height());
  3509. BRect dest(source);
  3510. dest.OffsetBy(0, newRowRect.Height() + 1);
  3511. CopyBits(source, dest);
  3512. Invalidate(newRowRect);
  3513. } // otherwise, this is below the currently visible region
  3514. } else {
  3515. // Adding the item may have caused the item that the user is currently
  3516. // selected to move. This would cause annoying drawing and interaction
  3517. // bugs, as the position of that item is cached. If this happens, resize
  3518. // the scroll bar, then scroll back so the selected item is in view.
  3519. BRect targetRect;
  3520. if (FindRect(fTargetRow, &targetRect)) {
  3521. float delta = targetRect.top - fTargetRowTop;
  3522. if (delta != 0) {
  3523. // This causes a jump because ScrollBy will copy a chunk of the view.
  3524. // Since the actual contents of the view have been offset, we don't
  3525. // want this, we just want to change the virtual origin of the window.
  3526. // Constrain the clipping region so everything is clipped out so no
  3527. // copy occurs.
  3528. //
  3529. // xxx this currently doesn't work if the scroll bars aren't enabled.
  3530. // everything will still move anyway. A minor annoyance.
  3531. BRegion emptyRegion;
  3532. ConstrainClippingRegion(&emptyRegion);
  3533. PushState();
  3534. ScrollBy(0, delta);
  3535. PopState();
  3536. ConstrainClippingRegion(NULL);
  3537. fTargetRowTop += delta;
  3538. fClickPoint.y += delta;
  3539. fLatchRect.OffsetBy(0, delta);
  3540. }
  3541. }
  3542. }
  3543. }
  3544. // If the parent was previously childless, it will need to have a latch
  3545. // drawn.
  3546. BRect parentRect;
  3547. if (parentRow && parentRow->fChildList->CountItems() == 1
  3548. && FindVisibleRect(parentRow, &parentRect))
  3549. Invalidate(parentRect);
  3550. }
  3551. void
  3552. OutlineView::FixScrollBar(bool scrollToFit)
  3553. {
  3554. BScrollBar* vScrollBar = ScrollBar(B_VERTICAL);
  3555. if (vScrollBar) {
  3556. if (fItemsHeight > fVisibleRect.Height()) {
  3557. float maxScrollBarValue = fItemsHeight - fVisibleRect.Height();
  3558. vScrollBar->SetProportion(fVisibleRect.Height() / fItemsHeight);
  3559. // If the user is scrolled down too far when making the range smaller, the list
  3560. // will jump suddenly, which is undesirable. In this case, don't fix the scroll
  3561. // bar here. In ScrollTo, it checks to see if this has occured, and will
  3562. // fix the scroll bars sneakily if the user has scrolled up far enough.
  3563. if (scrollToFit || vScrollBar->Value() <= maxScrollBarValue) {
  3564. vScrollBar->SetRange(0.0, maxScrollBarValue);
  3565. vScrollBar->SetSteps(20.0, fVisibleRect.Height());
  3566. }
  3567. } else if (vScrollBar->Value() == 0.0 || fItemsHeight == 0.0)
  3568. vScrollBar->SetRange(0.0, 0.0); // disable scroll bar.
  3569. }
  3570. }
  3571. void
  3572. OutlineView::AddSorted(BRowContainer* list, BRow* row)
  3573. {
  3574. if (list && row) {
  3575. // Find general vicinity with binary search.
  3576. int32 lower = 0;
  3577. int32 upper = list->CountItems()-1;
  3578. while( lower < upper ) {
  3579. int32 middle = lower + (upper-lower+1)/2;
  3580. int32 cmp = CompareRows(row, list->ItemAt(middle));
  3581. if( cmp < 0 ) upper = middle-1;
  3582. else if( cmp > 0 ) lower = middle+1;
  3583. else lower = upper = middle;
  3584. }
  3585. // At this point, 'upper' and 'lower' at the last found item.
  3586. // Arbitrarily use 'upper' and determine the final insertion
  3587. // point -- either before or after this item.
  3588. if( upper < 0 ) upper = 0;
  3589. else if( upper < list->CountItems() ) {
  3590. if( CompareRows(row, list->ItemAt(upper)) > 0 ) upper++;
  3591. }
  3592. if (upper >= list->CountItems())
  3593. list->AddItem(row); // Adding to end.
  3594. else
  3595. list->AddItem(row, upper); // Insert
  3596. }
  3597. }
  3598. int32
  3599. OutlineView::CompareRows(BRow* row1, BRow* row2)
  3600. {
  3601. int32 itemCount (fSortColumns->CountItems());
  3602. if (row1 && row2) {
  3603. for (int32 index = 0; index < itemCount; index++) {
  3604. BColumn* column = (BColumn*) fSortColumns->ItemAt(index);
  3605. int comp = 0;
  3606. BField* field1 = (BField*) row1->GetField(column->fFieldID);
  3607. BField* field2 = (BField*) row2->GetField(column->fFieldID);
  3608. if (field1 && field2)
  3609. comp = column->CompareFields(field1, field2);
  3610. if (!column->fSortAscending)
  3611. comp = -comp;
  3612. if (comp != 0)
  3613. return comp;
  3614. }
  3615. }
  3616. return 0;
  3617. }
  3618. void
  3619. OutlineView::FrameResized(float width, float height)
  3620. {
  3621. fVisibleRect.right = fVisibleRect.left + width;
  3622. fVisibleRect.bottom = fVisibleRect.top + height;
  3623. FixScrollBar(true);
  3624. _inherited::FrameResized(width, height);
  3625. }
  3626. void
  3627. OutlineView::ScrollTo(BPoint position)
  3628. {
  3629. fVisibleRect.OffsetTo(position.x, position.y);
  3630. // In FixScrollBar, we might not have been able to change the size of
  3631. // the scroll bar because the user was scrolled down too far. Take
  3632. // this opportunity to sneak it in if we can.
  3633. BScrollBar* vScrollBar = ScrollBar(B_VERTICAL);
  3634. float maxScrollBarValue = fItemsHeight - fVisibleRect.Height();
  3635. float min, max;
  3636. vScrollBar->GetRange(&min, &max);
  3637. if (max != maxScrollBarValue && position.y > maxScrollBarValue)
  3638. FixScrollBar(true);
  3639. _inherited::ScrollTo(position);
  3640. }
  3641. const BRect&
  3642. OutlineView::VisibleRect() const
  3643. {
  3644. return fVisibleRect;
  3645. }
  3646. bool
  3647. OutlineView::FindVisibleRect(BRow* row, BRect* _rect)
  3648. {
  3649. if (row && _rect) {
  3650. float line = 0.0;
  3651. for (RecursiveOutlineIterator iterator(&fRows); iterator.CurrentRow();
  3652. iterator.GoToNext()) {
  3653. if (line > fVisibleRect.bottom)
  3654. break;
  3655. if (iterator.CurrentRow() == row) {
  3656. _rect->Set(fVisibleRect.left, line, fVisibleRect.right,
  3657. line + row->Height());
  3658. return true;
  3659. }
  3660. line += iterator.CurrentRow()->Height() + 1;
  3661. }
  3662. }
  3663. return false;
  3664. }
  3665. bool
  3666. OutlineView::FindRect(const BRow* row, BRect* _rect)
  3667. {
  3668. float line = 0.0;
  3669. for (RecursiveOutlineIterator iterator(&fRows); iterator.CurrentRow();
  3670. iterator.GoToNext()) {
  3671. if (iterator.CurrentRow() == row) {
  3672. _rect->Set(fVisibleRect.left, line, fVisibleRect.right,
  3673. line + row->Height());
  3674. return true;
  3675. }
  3676. line += iterator.CurrentRow()->Height() + 1;
  3677. }
  3678. return false;
  3679. }
  3680. void
  3681. OutlineView::ScrollTo(const BRow* row)
  3682. {
  3683. BRect rect;
  3684. if (FindRect(row, &rect)) {
  3685. BRect bounds = Bounds();
  3686. if (rect.top < bounds.top)
  3687. ScrollTo(BPoint(bounds.left, rect.top));
  3688. else if (rect.bottom > bounds.bottom)
  3689. ScrollBy(0, rect.bottom - bounds.bottom);
  3690. }
  3691. }
  3692. void
  3693. OutlineView::DeselectAll()
  3694. {
  3695. // Invalidate all selected rows
  3696. float line = 0.0;
  3697. for (RecursiveOutlineIterator iterator(&fRows); iterator.CurrentRow();
  3698. iterator.GoToNext()) {
  3699. if (line > fVisibleRect.bottom)
  3700. break;
  3701. BRow* row = iterator.CurrentRow();
  3702. if (line + row->Height() > fVisibleRect.top) {
  3703. if (row->fNextSelected != 0)
  3704. Invalidate(BRect(fVisibleRect.left, line, fVisibleRect.right,
  3705. line + row->Height()));
  3706. }
  3707. line += row->Height() + 1;
  3708. }
  3709. // Set items not selected
  3710. while (fSelectionListDummyHead.fNextSelected != &fSelectionListDummyHead) {
  3711. BRow* row = fSelectionListDummyHead.fNextSelected;
  3712. row->fNextSelected->fPrevSelected = row->fPrevSelected;
  3713. row->fPrevSelected->fNextSelected = row->fNextSelected;
  3714. row->fNextSelected = 0;
  3715. row->fPrevSelected = 0;
  3716. }
  3717. }
  3718. BRow*
  3719. OutlineView::FocusRow() const
  3720. {
  3721. return fFocusRow;
  3722. }
  3723. void
  3724. OutlineView::SetFocusRow(BRow* row, bool Select)
  3725. {
  3726. if (row) {
  3727. if (Select)
  3728. AddToSelection(row);
  3729. if (fFocusRow == row)
  3730. return;
  3731. Invalidate(fFocusRowRect); // invalidate previous
  3732. fTargetRow = fFocusRow = row;
  3733. FindVisibleRect(fFocusRow, &fFocusRowRect);
  3734. Invalidate(fFocusRowRect); // invalidate current
  3735. fFocusRowRect.right = 10000;
  3736. fMasterView->SelectionChanged();
  3737. }
  3738. }
  3739. bool
  3740. OutlineView::SortList(BRowContainer* list, bool isVisible)
  3741. {
  3742. if (list) {
  3743. // Shellsort
  3744. BRow** items
  3745. = (BRow**) BObjectList<BRow>::Private(list).AsBList()->Items();
  3746. int32 numItems = list->CountItems();
  3747. int h;
  3748. for (h = 1; h < numItems / 9; h = 3 * h + 1)
  3749. ;
  3750. for (;h > 0; h /= 3) {
  3751. for (int step = h; step < numItems; step++) {
  3752. BRow* temp = items[step];
  3753. int i;
  3754. for (i = step - h; i >= 0; i -= h) {
  3755. if (CompareRows(temp, items[i]) < 0)
  3756. items[i + h] = items[i];
  3757. else
  3758. break;
  3759. }
  3760. items[i + h] = temp;
  3761. }
  3762. }
  3763. if (isVisible) {
  3764. Invalidate();
  3765. InvalidateCachedPositions();
  3766. int lockCount = Window()->CountLocks();
  3767. for (int i = 0; i < lockCount; i++)
  3768. Window()->Unlock();
  3769. while (lockCount--)
  3770. if (!Window()->Lock())
  3771. return false; // Window is gone...
  3772. }
  3773. }
  3774. return true;
  3775. }
  3776. int32
  3777. OutlineView::DeepSortThreadEntry(void* _outlineView)
  3778. {
  3779. ((OutlineView*) _outlineView)->DeepSort();
  3780. return 0;
  3781. }
  3782. void
  3783. OutlineView::DeepSort()
  3784. {
  3785. struct stack_entry {
  3786. bool isVisible;
  3787. BRowContainer* list;
  3788. int32 listIndex;
  3789. } stack[kMaxDepth];
  3790. int32 stackTop = 0;
  3791. stack[stackTop].list = &fRows;
  3792. stack[stackTop].isVisible = true;
  3793. stack[stackTop].listIndex = 0;
  3794. fNumSorted = 0;
  3795. if (Window()->Lock() == false)
  3796. return;
  3797. bool doneSorting = false;
  3798. while (!doneSorting && !fSortCancelled) {
  3799. stack_entry* currentEntry = &stack[stackTop];
  3800. // xxx Can make the invalidate area smaller by finding the rect for the
  3801. // parent item and using that as the top of the invalid rect.
  3802. bool haveLock = SortList(currentEntry->list, currentEntry->isVisible);
  3803. if (!haveLock)
  3804. return ; // window is gone.
  3805. // Fix focus rect.
  3806. InvalidateCachedPositions();
  3807. if (fCurrentState != INACTIVE)
  3808. fCurrentState = INACTIVE; // sorry...
  3809. // next list.
  3810. bool foundNextList = false;
  3811. while (!foundNextList && !fSortCancelled) {
  3812. for (int32 index = currentEntry->listIndex; index < currentEntry->list->CountItems();
  3813. index++) {
  3814. BRow* parentRow = currentEntry->list->ItemAt(index);
  3815. BRowContainer* childList = parentRow->fChildList;
  3816. if (childList != 0) {
  3817. currentEntry->listIndex = index + 1;
  3818. stackTop++;
  3819. ASSERT(stackTop < kMaxDepth);
  3820. stack[stackTop].listIndex = 0;
  3821. stack[stackTop].list = childList;
  3822. stack[stackTop].isVisible = (currentEntry->isVisible && parentRow->fIsExpanded);
  3823. foundNextList = true;
  3824. break;
  3825. }
  3826. }
  3827. if (!foundNextList) {
  3828. // back up
  3829. if (--stackTop < 0) {
  3830. doneSorting = true;
  3831. break;
  3832. }
  3833. currentEntry = &stack[stackTop];
  3834. }
  3835. }
  3836. }
  3837. Window()->Unlock();
  3838. }
  3839. void
  3840. OutlineView::StartSorting()
  3841. {
  3842. // If this view is not yet attached to a window, don't start a sort thread!
  3843. if (Window() == NULL)
  3844. return;
  3845. if (fSortThread != B_BAD_THREAD_ID) {
  3846. thread_info tinfo;
  3847. if (get_thread_info(fSortThread, &tinfo) == B_OK) {
  3848. // Unlock window so this won't deadlock (sort thread is probably
  3849. // waiting to lock window).
  3850. int lockCount = Window()->CountLocks();
  3851. for (int i = 0; i < lockCount; i++)
  3852. Window()->Unlock();
  3853. fSortCancelled = true;
  3854. int32 status;
  3855. wait_for_thread(fSortThread, &status);
  3856. while (lockCount--)
  3857. if (!Window()->Lock())
  3858. return ; // Window is gone...
  3859. }
  3860. }
  3861. fSortCancelled = false;
  3862. fSortThread = spawn_thread(DeepSortThreadEntry, "sort_thread", B_NORMAL_PRIORITY, this);
  3863. resume_thread(fSortThread);
  3864. }
  3865. void
  3866. OutlineView::SelectRange(BRow* start, BRow* end)
  3867. {
  3868. if (!start || !end)
  3869. return;
  3870. if (start == end) // start is always selected when this is called
  3871. return;
  3872. RecursiveOutlineIterator iterator(&fRows, false);
  3873. while (iterator.CurrentRow() != 0) {
  3874. if (iterator.CurrentRow() == end) {
  3875. // reverse selection, swap to fix special case
  3876. BRow* temp = start;
  3877. start = end;
  3878. end = temp;
  3879. break;
  3880. } else if (iterator.CurrentRow() == start)
  3881. break;
  3882. iterator.GoToNext();
  3883. }
  3884. while (true) {
  3885. BRow* row = iterator.CurrentRow();
  3886. if (row) {
  3887. if (row->fNextSelected == 0) {
  3888. row->fNextSelected = fSelectionListDummyHead.fNextSelected;
  3889. row->fPrevSelected = &fSelectionListDummyHead;
  3890. row->fNextSelected->fPrevSelected = row;
  3891. row->fPrevSelected->fNextSelected = row;
  3892. }
  3893. } else
  3894. break;
  3895. if (row == end)
  3896. break;
  3897. iterator.GoToNext();
  3898. }
  3899. Invalidate(); // xxx make invalidation smaller
  3900. }
  3901. bool
  3902. OutlineView::FindParent(BRow* row, BRow** outParent, bool* outParentIsVisible)
  3903. {
  3904. bool result = false;
  3905. if (row != NULL && outParent != NULL) {
  3906. *outParent = row->fParent;
  3907. if (outParentIsVisible != NULL) {
  3908. // Walk up the parent chain to determine if this row is visible
  3909. *outParentIsVisible = true;
  3910. for (BRow* currentRow = row->fParent; currentRow != NULL;
  3911. currentRow = currentRow->fParent) {
  3912. if (!currentRow->fIsExpanded) {
  3913. *outParentIsVisible = false;
  3914. break;
  3915. }
  3916. }
  3917. }
  3918. result = *outParent != NULL;
  3919. }
  3920. return result;
  3921. }
  3922. int32
  3923. OutlineView::IndexOf(BRow* row)
  3924. {
  3925. if (row) {
  3926. if (row->fParent == 0)
  3927. return fRows.IndexOf(row);
  3928. ASSERT(row->fParent->fChildList);
  3929. return row->fParent->fChildList->IndexOf(row);
  3930. }
  3931. return B_ERROR;
  3932. }
  3933. void
  3934. OutlineView::InvalidateCachedPositions()
  3935. {
  3936. if (fFocusRow)
  3937. FindRect(fFocusRow, &fFocusRowRect);
  3938. }
  3939. float
  3940. OutlineView::GetColumnPreferredWidth(BColumn* column)
  3941. {
  3942. float preferred = 0.0;
  3943. for (RecursiveOutlineIterator iterator(&fRows); BRow* row =
  3944. iterator.CurrentRow(); iterator.GoToNext()) {
  3945. BField* field = row->GetField(column->fFieldID);
  3946. if (field) {
  3947. float width = column->GetPreferredWidth(field, this)
  3948. + iterator.CurrentLevel() * kOutlineLevelIndent;
  3949. preferred = max_c(preferred, width);
  3950. }
  3951. }
  3952. BString name;
  3953. column->GetColumnName(&name);
  3954. preferred = max_c(preferred, StringWidth(name));
  3955. // Constrain to preferred width. This makes the method do a little
  3956. // more than asked, but it's for convenience.
  3957. if (preferred < column->MinWidth())
  3958. preferred = column->MinWidth();
  3959. else if (preferred > column->MaxWidth())
  3960. preferred = column->MaxWidth();
  3961. return preferred;
  3962. }
  3963. // #pragma mark -
  3964. RecursiveOutlineIterator::RecursiveOutlineIterator(BRowContainer* list,
  3965. bool openBranchesOnly)
  3966. :
  3967. fStackIndex(0),
  3968. fCurrentListIndex(0),
  3969. fCurrentListDepth(0),
  3970. fOpenBranchesOnly(openBranchesOnly)
  3971. {
  3972. if (list == 0 || list->CountItems() == 0)
  3973. fCurrentList = 0;
  3974. else
  3975. fCurrentList = list;
  3976. }
  3977. BRow*
  3978. RecursiveOutlineIterator::CurrentRow() const
  3979. {
  3980. if (fCurrentList == 0)
  3981. return 0;
  3982. return fCurrentList->ItemAt(fCurrentListIndex);
  3983. }
  3984. void
  3985. RecursiveOutlineIterator::GoToNext()
  3986. {
  3987. if (fCurrentList == 0)
  3988. return;
  3989. if (fCurrentListIndex < 0 || fCurrentListIndex >= fCurrentList->CountItems()) {
  3990. fCurrentList = 0;
  3991. return;
  3992. }
  3993. BRow* currentRow = fCurrentList->ItemAt(fCurrentListIndex);
  3994. if(currentRow) {
  3995. if (currentRow->fChildList && (currentRow->fIsExpanded || !fOpenBranchesOnly)
  3996. && currentRow->fChildList->CountItems() > 0) {
  3997. // Visit child.
  3998. // Put current list on the stack if it needs to be revisited.
  3999. if (fCurrentListIndex < fCurrentList->CountItems() - 1) {
  4000. fStack[fStackIndex].fRowSet = fCurrentList;
  4001. fStack[fStackIndex].fIndex = fCurrentListIndex + 1;
  4002. fStack[fStackIndex].fDepth = fCurrentListDepth;
  4003. fStackIndex++;
  4004. }
  4005. fCurrentList = currentRow->fChildList;
  4006. fCurrentListIndex = 0;
  4007. fCurrentListDepth++;
  4008. } else if (fCurrentListIndex < fCurrentList->CountItems() - 1)
  4009. fCurrentListIndex++; // next item in current list
  4010. else if (--fStackIndex >= 0) {
  4011. fCurrentList = fStack[fStackIndex].fRowSet;
  4012. fCurrentListIndex = fStack[fStackIndex].fIndex;
  4013. fCurrentListDepth = fStack[fStackIndex].fDepth;
  4014. } else
  4015. fCurrentList = 0;
  4016. }
  4017. }
  4018. int32
  4019. RecursiveOutlineIterator::CurrentLevel() const
  4020. {
  4021. return fCurrentListDepth;
  4022. }