studio_scriptLayer.py 133 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313
  1. # THIS FILE IS A PART OF VCStudio
  2. # PYTHON 3
  3. import os
  4. import datetime
  5. import threading
  6. import re
  7. import json
  8. # GTK module ( Graphical interface
  9. import gi
  10. gi.require_version('Gtk', '3.0')
  11. from gi.repository import Gtk
  12. from gi.repository import GLib
  13. from gi.repository import Gdk
  14. import cairo
  15. # Own modules
  16. from settings import settings
  17. from settings import talk
  18. from settings import fileformats
  19. from settings import oscalls
  20. from project_manager import pm_project
  21. #UI modules
  22. from UI import UI_elements
  23. from UI import UI_color
  24. from UI import UI_math
  25. # story
  26. from studio import story
  27. from studio import checklist
  28. from studio import analytics
  29. from studio import studio_dialogs
  30. from studio import schedule
  31. from studio import history
  32. from network import http_client
  33. def select_shot(win):
  34. # For the http-server stuff
  35. # Remote Server Stuff
  36. if win.analytics["from-remote-server"]:
  37. # Checking date [ FOLDER NAME ] [ FILES TO CHECK FOR ]
  38. check_folders = {"/rnd"+win.cur : "*",
  39. "/rnd"+win.cur+"/storyboard" : "*",
  40. "/rnd"+win.cur+"/opengl" : "*",
  41. "/rnd"+win.cur+"/test_rnd" : "*",
  42. "/rnd"+win.cur+"/rendered" : "*",
  43. "/rnd"+win.cur+"/extra" : "*"
  44. }
  45. check_updates = threading.Thread(target=http_client.get_folder_info,
  46. args=(win, check_folders, win.cur, ))
  47. check_updates.setDaemon(True)
  48. check_updates.start()
  49. def layer(win):
  50. # Making the layer
  51. surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, win.current['w'],
  52. win.current['h'])
  53. layer = cairo.Context(surface)
  54. #text setting
  55. layer.select_font_face("Monospace", cairo.FONT_SLANT_NORMAL, cairo.FONT_WEIGHT_NORMAL)
  56. UI_color.set(layer, win, "dark_overdrop")
  57. layer.rectangle(
  58. 0,
  59. 0,
  60. win.current["w"],
  61. win.current["h"],
  62. )
  63. layer.fill()
  64. ############################################################################
  65. # This is a file that will draw the text editor of VCStudio. The idea is that
  66. # you not only work on making the movie in VCStudio, but the script to the
  67. # movie is also written in VCStudio. This was already implemented in Blender-
  68. # Organizer legacy. And already implemented partially in VCStudio. As there
  69. # is story editor in place where you can add empty scenes. Or deal with
  70. # scenes that are converted from legacy.
  71. # But as you may guess by the time I'm writting it there is no way of editing
  72. # those scene's contents. Or read them in full. ( You can see peaces in the
  73. # assets )
  74. # The implementation of the editor will be different from Blender-Organizer's
  75. # where I used an off the shelf GTK text writting widget.
  76. # The problem was that I could not really do much with that widget appart from
  77. # text. Don't get me wrong it was good for text. But I could not make for
  78. # example custom ways of drawing particular parts. Like what if I want a
  79. # round-rectangle arround each frase that the crachater's say. Or what if I
  80. # want to draw some custom UI buttons somewhere in the text. Stuff like this.
  81. # Which resulted in a weird workflow. I had a pretty script preview thing.
  82. # and another non-pretty script preview thing. And a script writer thing where
  83. # you type in code to mark thing. Very non pretty. Non user-friendly. And
  84. # you had to learn the code. And stuff. Yeah...
  85. # So here I want to make more of traditional editor. Where you see what you are
  86. # editing as you editing it. Hopefully it's going to work.
  87. ############################################################################
  88. ####### TOP PANEL #########
  89. UI_color.set(layer, win, "node_background")
  90. UI_elements.roundrect(layer, win,
  91. win.current["w"]/4,
  92. 10,
  93. win.current["w"]/2,
  94. 50,
  95. 10)
  96. # Let's add the toolbar. There is not a lot of tools we need. Since it's
  97. # a program only for writting a script.
  98. # Mark Shot
  99. # Mark Asset
  100. # Start Dialogue
  101. # Link Image
  102. # MARK SHOT
  103. def do():
  104. win.current["key_letter"] = chr(19)
  105. UI_elements.roundrect(layer, win,
  106. win.current["w"]/4+5,
  107. 15,
  108. 40,
  109. 40,
  110. 10,
  111. do,
  112. "shot_new",
  113. talk.text("add_shot_tooltip")+"\n\n[Ctrl - S]")
  114. # MARK ASSET
  115. def do():
  116. win.current["key_letter"] = chr(12)
  117. UI_elements.roundrect(layer, win,
  118. win.current["w"]/4+55,
  119. 15,
  120. 40,
  121. 40,
  122. 10,
  123. do,
  124. "obj_link",
  125. talk.text("add_asset_tooltip")+"\n\n[Ctrl - L]")
  126. # Do DIALOGUE
  127. def do():
  128. # Later on I'm doing the Ctrl - D combination to add a frase part
  129. # and this gives me a character of 00000011 or in other words a 4.
  130. # So i guess making it thing that I pressed Ctrl-D is fine.
  131. win.current["key_letter"] = chr(4)
  132. UI_elements.roundrect(layer, win,
  133. win.current["w"]/4+105,
  134. 15,
  135. 40,
  136. 40,
  137. 10,
  138. do,
  139. "frase_new",
  140. talk.text("add_phrase_tooltip")+"\n\n[Ctrl - D]")
  141. # ADD IMAGE
  142. def do():
  143. win.current["key_letter"] = chr(9)
  144. UI_elements.roundrect(layer, win,
  145. win.current["w"]/4+155,
  146. 15,
  147. 40,
  148. 40,
  149. 10,
  150. do,
  151. "image_link",
  152. talk.text("add_image_script_tooltip")+"\n\n[Ctrl - I]")
  153. # Next part is to add the scene name editor. It's not as easy as you might
  154. # thing. Since every scene's shot might contain an actuall folder on the
  155. # operating system after you create a first blend file to animate the shot.
  156. # So there will be a folder for the scene with many folders for shots inside.
  157. # And if you change the name of the scene in the story you probably want to
  158. # change the name of the scene in the folder as well. BUT. Since there could
  159. # be countless blend files inside. And some names just could be already taken
  160. # and stuff like that, I will block the editing of the scene if a folder exists.
  161. # And will give a folder icon.
  162. # Technically it's going to be possible to rename the scene. By renaming the
  163. # folder first. But this is a kind of breaking everything operation i don't
  164. # want to implement inside the VCStudio it self. I give constant folder links
  165. # so editing of files could be done by the user at any time.
  166. # But I don't want it to feel easy. Since stuff like deleting assets or
  167. # editing names could break things in a different place in the program. That's
  168. # why I don't provide easy to use functions for it. The user should really
  169. # decide to make a change like this. To the point of breaking the convinience
  170. # of VCStudio.
  171. # Parsing the url
  172. if win.cur.count("/") > 1:
  173. tmp = win.cur.replace("/","",1).split("/")
  174. scene, shot = tmp[0], tmp[1]
  175. else:
  176. scene = win.cur[win.cur.rfind("/")+1:]
  177. shot = ""
  178. # Let's find out if a folder exists.
  179. if os.path.exists(win.project+"/rnd/"+scene):
  180. editable = False
  181. minus = 50
  182. # The minus is the width of the folder button
  183. def do():
  184. oscalls.Open(win.project+"/rnd/"+scene)
  185. UI_elements.roundrect(layer, win,
  186. win.current["w"]/4*3-45,
  187. 15,
  188. 40,
  189. 40,
  190. 10,
  191. do,
  192. "folder",
  193. tip="/rnd/"+scene)
  194. else:
  195. editable = True
  196. minus = 0
  197. UI_elements.text(layer, win, "scene_name",
  198. win.current["w"]/4+205,
  199. 15,
  200. win.current["w"]/2-210-minus,
  201. 40,
  202. set_text=scene,
  203. editable=editable,
  204. fill=False)
  205. # Now let's do the actual edititing part. What we want to look for is whether
  206. # the key already exists in the scenes, if not allow it to be applied. Now
  207. # you are probably asking yourself. But the current scene exists there? So
  208. # how could you apply it. Easy. If you change anything. It already doesn't
  209. # exist there. And if you didn't change anything. What's the point of applying
  210. # anyway? So here what we are going to do.
  211. newname = win.text["scene_name"]["text"].replace("/","_").replace(" ", "_")\
  212. .replace('"',"_").replace("(","_").replace(")","_").replace("'","_")\
  213. .replace("[","_").replace("]","_").replace("{","_").replace("}","_")
  214. if newname not in win.story["scenes"]:
  215. def do():
  216. win.story["scenes"][newname] = win.story["scenes"].pop(scene)
  217. win.text["scene_name"]["text"] = newname
  218. win.cur = newname
  219. # There is one more rub. Scenes could be connected together.
  220. # so now we need to preserve the connections as well. This is
  221. # not that hard.
  222. for arrow in win.story["arrows"]:
  223. if arrow[0][1] == scene:
  224. arrow[0][1] = newname
  225. if arrow[1][1] == scene:
  226. arrow[1][1] = newname
  227. # Easy. I like how if it's a list. it's a link to the list.
  228. # so there will not be memory overdrives. So I can itterate
  229. # over a list of lists and assing values of to the itteration
  230. # and not trying to get the main list. LOVELY.
  231. # Saving to the file
  232. story.save(win.project, win.story)
  233. UI_elements.roundrect(layer, win,
  234. win.current["w"]/4*3-45,
  235. 15,
  236. 40,
  237. 40,
  238. 10,
  239. do,
  240. "ok",
  241. tip=talk.text("checked"))
  242. # Just in case parsing the url again
  243. if win.cur.count("/") > 1:
  244. tmp = win.cur.replace("/","",1).split("/")
  245. scene, shot = tmp[0], tmp[1]
  246. else:
  247. scene = win.cur[win.cur.rfind("/")+1:]
  248. shot = ""
  249. ######### MAIN PART ###########
  250. # Oh dear. I have no idea how hard it's going to be. But let's try. I don't
  251. # thing it's going to be that insane. But I need to take in consideration
  252. # a couple of things. Mainly text warping. Meaning I'm probably rendering
  253. # each word as an individual object. Now this creates a challenge.
  254. # How do I mark parts of the text having more then 1 word it it? I probably
  255. # will need to draw a couple layers at ones.
  256. # TEXT LAYER
  257. # SHOTS MARKING LAYER
  258. # ASSET MARKING LAYER
  259. # And then combine them in a different order
  260. # SHOT MARKING LAYER
  261. # ASSET MARKING LAYER (on top)
  262. # TEXT LAYER (on top of everything)
  263. # So let's make those 3 layers.
  264. x = win.current["w"]/4
  265. y = 70
  266. width = win.current["w"]/2 -30
  267. height = win.current["h"]-130
  268. # Good that at least all layers have the same size.
  269. textsurface = cairo.ImageSurface(cairo.FORMAT_ARGB32, int(width), int(height))
  270. text = cairo.Context(textsurface)
  271. text.select_font_face("Monospace", cairo.FONT_SLANT_NORMAL, cairo.FONT_WEIGHT_NORMAL)
  272. shotsurface = cairo.ImageSurface(cairo.FORMAT_ARGB32, int(width), int(height))
  273. shots = cairo.Context(shotsurface)
  274. shots.select_font_face("Monospace", cairo.FONT_SLANT_NORMAL, cairo.FONT_WEIGHT_NORMAL)
  275. assetsurface = cairo.ImageSurface(cairo.FORMAT_ARGB32, int(width), int(height))
  276. assets = cairo.Context(assetsurface)
  277. assets.select_font_face("Monospace", cairo.FONT_SLANT_NORMAL, cairo.FONT_WEIGHT_NORMAL)
  278. frasesurface = cairo.ImageSurface(cairo.FORMAT_ARGB32, int(width), int(height))
  279. frases = cairo.Context(frasesurface)
  280. frases.select_font_face("Monospace", cairo.FONT_SLANT_NORMAL, cairo.FONT_WEIGHT_NORMAL)
  281. ###########################
  282. # Oh dear
  283. # Clips:
  284. UI_elements.roundrect(text, win, 0,0,width, height, 10,fill=False)
  285. text.clip()
  286. UI_elements.roundrect(shots, win, 0,0,width, height, 10,fill=False)
  287. shots.clip()
  288. UI_elements.roundrect(assets, win, 0,0,width, height, 10,fill=False)
  289. assets.clip()
  290. UI_elements.roundrect(frases, win, 0,0,width, height, 10,fill=False)
  291. frases.clip()
  292. # Background
  293. UI_color.set(frases, win, "node_background")
  294. frases.rectangle(0,0,width, height)
  295. frases.fill()
  296. # Scroll stuff
  297. if "script" not in win.scroll:
  298. win.scroll["script"] = 0
  299. tileX = 12
  300. current_Y = 0
  301. # For highlights of assets and shots. Basically when mouse is over a given
  302. # asset it will record itself into the higlight. And be read on the next
  303. # frame by those above it which are links to the same asset / shot.
  304. # This is done like so, so I could make multiline drawing of markings.
  305. if "script_asset_higlight" not in win.current:
  306. win.current["script_asset_higlight"] = ""
  307. if "script_shot_higlight" not in win.current:
  308. win.current["script_shot_higlight"] = ""
  309. # Next. For editing the text we need a cursor on the screen. A kind of pointer
  310. # that will be our selection. The only problem is that we are trying to make
  311. # usefull text editor where you can select a part of text. And do things to it
  312. # like let's say mark a shot or an asset. This is why there are actually 2
  313. # points which are souce of quite heavy head auks later on.
  314. pointer = [0,0] # The editing cursor
  315. point = 0 # The current place in the text of the scene.
  316. if scene in win.story["pointers"]: # Also I thought sinec we are
  317. pointer = win.story["pointers"][scene] # saving selection in story
  318. # editor. Why not save also
  319. # The text pointers.
  320. # Coming back to the previous thing. If I mouse over the part the part get's
  321. # highlighted. But simply removing the mouse is not enough. Because I'm
  322. # writting that it's higlighted into a global dictionary. So I want to clean.
  323. # it ones in a while. And for now ( It does cause some visual noise ) I chose
  324. # to remove the selection if the mouse cursor moved over 1 pixel from it's
  325. # previous frame. So if you move the cursor very slowly, there will be no
  326. # change.
  327. if int(win.current["mx"]) not in range(int(win.previous["mx"])-1, int(win.previous["mx"])+1)\
  328. and int(win.current["my"]) not in range(int(win.previous["my"])-1, int(win.previous["my"])+1):
  329. win.current["script_asset_higlight"] = ""
  330. win.current["script_shot_higlight"] = ""
  331. # This 2 fellas will be responsible for drawing the shot marking.
  332. shotpart = 0 # This is a pixel where to start the rounded rectangle.
  333. shotis = "" # this is a current drawing shot's name.
  334. # Same idea for the selection cursor.
  335. ss = 0 # The pixel where to start drawing the selection cursor.
  336. smx = ss
  337. # Another one of these for the rectangles under frases. This one is Y location
  338. # instead.
  339. frasepart = 0
  340. # This is the scroll when typing.
  341. putscroll = 0
  342. # Before we start. Let's bullet proof this. So if the scene does not exist
  343. # we will be transferred back to story editor and not stuck.
  344. try:
  345. win.story["scenes"][scene]["shots"]
  346. except:
  347. win.url = "story_editor"
  348. win.cur = ""
  349. return surface
  350. # Okay if nothing is in the scene we want to create a text block of 1 character
  351. # into the scene. Because if it will have 0 characters it will be automatically
  352. # deleted.
  353. if not win.story["scenes"][scene]["shots"]\
  354. or win.story["scenes"][scene]["shots"][-1][0] == "shot_block":
  355. win.story["scenes"][scene]["shots"].append([
  356. "text_block",[["text", ' ']]
  357. ])
  358. # This is a list of parts that are inside the selection. I will populate it
  359. # as I itterate trough text. The problem with this text is that it's not a
  360. # long string. It's a list of blocks. Each of them has a string. But also
  361. # each of them has some aditional metadata. Like is it a shot, or not. Is it
  362. # a frase. Is it a link to an asset. This kind a thing. So in order to edit it
  363. # I need to get all selected peaces into a big thing.
  364. selecwords = []
  365. # And a little thing for the left panel. Since we already itterating through.
  366. # the shots. Why not get a quick list of them
  367. shot_list = {}
  368. # Okay so let's begin itterating the scene blocks. There are 2 types of scene
  369. # blocks. (At least for now.) Those are shot_block and text_block. Text is a
  370. # normal text parts. Shot is everything with in a shot. Shot has one more item
  371. # in the list. It's the name of the shot. ['shot_block', 'shot_name', [<text>]]
  372. # while text has just 2. ['text_block', [<text>]]. So you can see me refencing
  373. # the block[-1] a lot. To get the text parts of the block.
  374. for num, block in enumerate(win.story["scenes"][scene]["shots"]):
  375. # If shot block is empty Let's get rig of it, so not to collect garbage in
  376. # our story file. Each empty srting is still a string. That requires some
  377. # characters to write it to file. So let's get rid of it here.
  378. if not block[-1]:
  379. del win.story["scenes"][scene]["shots"][num]
  380. # Here I want to make another type of filtering. So there will not be
  381. # 1 million blocks each 1 character long. I want to make it next.
  382. # If the previous block has the same metadata as this one. Join them.
  383. if num > 0 and win.story["scenes"][scene]["shots"][num-1][0] == "text_block" and block[0] == "text_block":
  384. win.story["scenes"][scene]["shots"][num-1][-1] = win.story["scenes"][scene]["shots"][num-1][-1] + block[-1]
  385. del win.story["scenes"][scene]["shots"][num]
  386. # THIS ONE IS NOT TESTED YET, BUT I MIGHT ALSO FORGET TO DELETE THE COMMENT.
  387. elif num > 0 and win.story["scenes"][scene]["shots"][num-1][0] == "shot_block" and block[0] == "shot_block"\
  388. and win.story["scenes"][scene]["shots"][num-1][1] == block[1]:
  389. win.story["scenes"][scene]["shots"][num-1][-1] = win.story["scenes"][scene]["shots"][num-1][-1] + block[-1]
  390. del win.story["scenes"][scene]["shots"][num]
  391. if block[0] == "shot_block" and block[1] not in shot_list:
  392. shot_list[block[1]] = block
  393. # Now other windows might want to reference the script. Like assets for
  394. # example. So this script_find is for other windows to set. So while in
  395. # the script it would automatically scroll to that part. And so if set
  396. # only shot. Here is out shot finder, auto-scroller thingy.
  397. if win.current["script_find"][0] and win.current["script_find"][0] == num:
  398. win.scroll["script"] = 0-current_Y+height/2
  399. win.current["script_find"][0] = 0
  400. # Now let's itterate over parts of the text. Now you probably asking wasn't
  401. # blocks just strings with some metadata? Yes and know. Every text block has
  402. # blocks with in it. So I could mark items and images and frases with in text
  403. # across or without a shot. Basically there is our simple Hello-World thing
  404. # written inside a scene data. ['text_block', ['text','Hello-World']]
  405. # This are similar. The actual sting is always in the very end. So to find it
  406. # I'm using part[-1]
  407. for n, part in enumerate(block[-1]):
  408. # If you remember me deleting every empty block. Not this is the same
  409. # but with empty parts. So you could delete a frase for example. While
  410. # just backspacing.
  411. if not part[-1]:
  412. del block[-1][n]
  413. # Here I want to make another type of filtering. So there will not be
  414. # 1 million blocks each 1 character long. I want to make it next.
  415. # If the previous block has the same metadata as this one. Join them.
  416. if n > 0 and block[-1][n-1][0] == "text" and part[0] == "text":
  417. block[-1][n-1][-1] = block[-1][n-1][-1]+part[-1]
  418. part[-1] = ""
  419. # Now this is our search finder auto-scroller again. But if a part
  420. # also specified.
  421. if win.current["script_find"][1] and win.current["script_find"][1] == n:
  422. win.scroll["script"] = 0-current_Y+height/2
  423. win.current["script_find"][1] = 0
  424. # Now t and p. Well t is simply a shortcut to the string it self. Tho
  425. # I found it not really usefull since it's not a link to the data. Like
  426. # in the case of part[-1]. But for rendering it will be okay.
  427. t = part[-1]
  428. # Now p is a bit more complicated to explain. Basically I always need
  429. # an index number from all character in the whole scene in relation to
  430. # this particular part. See i will have a selection across the blocks
  431. # and across the parts. And in order to edit them at the right place
  432. # I need a reference point of where in the whole, this part starts.
  433. p = point
  434. # Now xb and mt you could see as margins of the text. Sometimes you
  435. # want the whole width to be text. And sometimes. Like in frases you
  436. # will want the text closer to the center.
  437. xb = 12 # Where is the start of the line
  438. mt = width # Where is the maximum width
  439. ##### FRASE #####
  440. # Now let's make an example of xb and mt. Here is a frase. So when
  441. # it's a frase. You want the text be closer to the center.
  442. if part[0] == "frase":
  443. # Give it some space. It's a rendering of a frase. You don't
  444. # want to be a part of the rest of the text.
  445. current_Y = current_Y + 30
  446. # Now frase metadata has another part. Which could be either
  447. # text or link. This is the name of our character. The little
  448. # part at the top, in the center. If it's a link we want to
  449. # draw it accordingly
  450. if part[1][0] == "link":
  451. # First we draw a little purple rectangle. To show us that
  452. # it's a link to an item.
  453. UI_color.set(assets, win, "node_asset")
  454. UI_elements.roundrect(assets, win,
  455. width/2-len(part[1][-1])*6-6,
  456. win.scroll["script"] + current_Y+6,
  457. len(part[1][-1])*12+12,
  458. 28,
  459. 10)
  460. # Then we make a selection by hovering with the mouse. So if
  461. # you click later. You will be transferred to the thing.
  462. if int(win.current['mx']) in range(int(x+width/2-len(part[1][-1])*6-6), int(x+width/2-len(part[1][-1])*6-6+len(part[1][-1])*12+12)) \
  463. and int(win.current['my']) in range(int(y+win.scroll["script"] + current_Y+8), int(y+win.scroll["script"] + current_Y+8+22)) :
  464. win.current["script_asset_higlight"] = part[1][1]
  465. # And if the mouse is over the item's link. No matter if the
  466. # link is this one. But if mouse is over any other mention of
  467. # the same asset. We want to draw another rectangle. To give
  468. # the user a feedback.
  469. if part[1][1] == win.current["script_asset_higlight"]:
  470. UI_color.set(assets, win, "text_normal")
  471. UI_elements.roundrect(assets, win,
  472. width/2-len(part[1][-1])*6-6,
  473. win.scroll["script"] + current_Y+30,
  474. len(part[1][-1])*12+12,
  475. 0,
  476. 3)
  477. # Next we will want to do it make a little button to edit the
  478. # name of the character. Because I guess I'm not smart enough
  479. # to figure out how to make this a part of the script. And this
  480. # implementation is not that terrible. I have a chance to put a
  481. # single line entry into a multiline text. This is something.
  482. if "current_name_frase_editing" not in win.current:
  483. win.current["current_name_frase_editing"] = False
  484. win.previous["current_name_frase_editing"] = False
  485. # So we put a selection variable. And set up the button.
  486. if not win.current["current_name_frase_editing"]:
  487. def do():
  488. win.current["LMB"] = False
  489. win.previous["LMB"] = False
  490. if not win.previous["current_name_frase_editing"]:
  491. print(part[1][-1])
  492. win.current["current_name_frase_editing"] = [num, n]
  493. if "current_name_frase_editing" in win.text:
  494. win.text["current_name_frase_editing"]["text"] = part[1][-1]
  495. win.textactive = "current_name_frase_editing"
  496. UI_elements.roundrect(frases, win,
  497. 0,
  498. win.scroll["script"] + current_Y+6,
  499. width,
  500. 28,
  501. 10,
  502. button=do,
  503. offset=[x,y])
  504. # Now let's draw a little text editor with in a text editor. IK wild.
  505. # but since everything is custom I can do wild things.
  506. if win.current["current_name_frase_editing"] == [num, n]:
  507. UI_elements.text(text, win, "current_name_frase_editing",
  508. 200,
  509. win.scroll["script"] + current_Y-5,
  510. width-400,
  511. 40,
  512. set_text=part[1][-1],
  513. offset=[x,y])
  514. # Here I will add some logic to this little editor with in
  515. # editor. OMG WHAT AM I DOING?
  516. # First let's make the apply button. And I guess the enter key.
  517. def linking():
  518. print("linking")
  519. if "linking_asset_operation_metadata" not in win.current:
  520. win.current["linking_asset_operation_metadata"] = []
  521. win.current["linking_asset_operation_metadata"] = part
  522. def after(win, var):
  523. print(var)
  524. if var:
  525. part = win.current["linking_asset_operation_metadata"]
  526. print(part)
  527. part[1] = ["link", var, part[1][-1]]
  528. studio_dialogs.asset_select(win, "link_asset_script_frase", after,
  529. SEARCH=win.text["current_name_frase_editing"]["text"])
  530. UI_elements.roundrect(text, win,
  531. width-240,
  532. win.scroll["script"] + current_Y-5,
  533. 40,
  534. 40,
  535. 10,
  536. button=linking,
  537. icon="obj_link",
  538. offset=[x,y])
  539. def do():
  540. part[1][-1] = win.text["current_name_frase_editing"]["text"]
  541. win.current["current_name_frase_editing"] = False
  542. win.current["LMB"] = False
  543. win.previous["LMB"] = False
  544. win.textactive = False
  545. win.current["key_letter"] = ""
  546. UI_elements.roundrect(text, win,
  547. width-280,
  548. win.scroll["script"] + current_Y-5,
  549. 40,
  550. 40,
  551. 10,
  552. button=do,
  553. icon="ok",
  554. offset=[x,y])
  555. # Enter key.
  556. if 65293 in win.current["keys"]:
  557. do()
  558. # If you press Ctrl-Enter instead of Enter. I want it
  559. # to apply and also give you automatically to select
  560. # the asset to link in the frase header.
  561. if 65507 in win.current["keys"]:
  562. linking()
  563. win.current["keys"] = []
  564. # Canceling the editing. If we don't do that the entry thing
  565. # will still be on the screen. And it's not something that
  566. # I want to have there.
  567. if not win.textactive:
  568. win.current["current_name_frase_editing"] = False
  569. win.current["key_letter"] = ""
  570. else:
  571. # And if we are not editing the name currently
  572. # let's just draw the name in the center of the
  573. # screen.
  574. UI_color.set(text, win, "text_normal")
  575. text.set_font_size(20)
  576. text.move_to(
  577. width/2-len(part[1][-1])*6,
  578. win.scroll["script"] + current_Y+25,
  579. )
  580. text.show_text(part[1][-1])
  581. # Giving it a new line.
  582. current_Y = current_Y + 35
  583. # Putting the line into the frasepart so to draw a rectangle
  584. # later under the whole thing.
  585. frasepart = current_Y
  586. # And here we are editing the margins. Because actuall text will
  587. # be done in a different place.
  588. xb = 212
  589. mt = width - 200
  590. tileX = xb
  591. # And passing the current starting position to selection drawing
  592. # so it too will start where it should.
  593. if ss:
  594. ss = xb
  595. # Now if we are not in the frase. Let's draw the damn rectangle under
  596. # it finally. # THIS IS STILL BUGGY.
  597. if frasepart and part[0] != "frase":
  598. UI_color.set(frases, win, "dark_overdrop")
  599. UI_elements.roundrect(frases, win,
  600. 200,
  601. win.scroll["script"] + frasepart,
  602. width-400,
  603. current_Y - frasepart+40,
  604. 10)
  605. frasepart = 0
  606. # MAKING A NEW LINE
  607. smx = tileX
  608. tileX = xb
  609. if assetpart:
  610. assetpart = xb
  611. if shotpart:
  612. shotpart = xb
  613. if ss:
  614. UI_color.set(assets, win, "node_blendfile")
  615. UI_elements.roundrect(assets, win,
  616. ss,
  617. win.scroll["script"] + current_Y+5,
  618. smx-ss,
  619. 26,
  620. 10)
  621. smx = ss
  622. ss = xb
  623. current_Y = current_Y + 30
  624. # Now let's draw our images. If they are in the scene. # THIS IS STILL BUGGY
  625. if part[0] == "image":
  626. if UI_math.line_overlap([pointer[1], pointer[0]],[point, point+1]):
  627. if win.current["key_letter"] and not win.textactive:
  628. # Let's make it possible to delete images by just
  629. # deleting near them.
  630. part[-1] = ""
  631. current_Y = current_Y + 30
  632. if os.path.exists(win.project+t):
  633. UI_elements.image(text, win, win.project+t,
  634. 100,
  635. win.scroll["script"] + current_Y,
  636. int(width)-200,
  637. 380,
  638. cell="script_images")
  639. else:
  640. UI_elements.image(text, win, t,
  641. 100,
  642. win.scroll["script"] + current_Y,
  643. int(width)-200,
  644. 380,
  645. cell="script_images")
  646. # Now I guess to simplify the matters let's create a selection
  647. # button. Double clicking which will open the file.
  648. # So we need a selection dialog. I think I'm going to hack into
  649. # my own program right now because I'm lazy to implement anything
  650. # normal. This image thing. Will need more love later on. Now it's
  651. # thrown together quite in a rush. I would say.
  652. def do():
  653. if t != win.textactive:
  654. win.textactive = t
  655. else:
  656. oscalls.file_open(win, t)
  657. UI_elements.roundrect(text, win,
  658. 100,
  659. win.scroll["script"] + current_Y,
  660. int(width)-200,
  661. 380,
  662. 10,
  663. button=do,
  664. fill=False,
  665. offset=[x,y])
  666. text.stroke()
  667. # Selection drawing
  668. if t == win.textactive:
  669. UI_color.set(text, win, "text_normal")
  670. UI_elements.roundrect(text, win,
  671. 100,
  672. win.scroll["script"] + current_Y,
  673. int(width)-200,
  674. 380,
  675. 10,
  676. fill=False)
  677. text.stroke()
  678. # Removing it from the selection using ESC
  679. if 65307 in win.current["keys"]:
  680. win.textactive = ""
  681. win.current["keys"] = []
  682. win.current["key_letter"] = ""
  683. # Deleting the image if pressing Delete or Backspace
  684. if 65288 in win.current["keys"] or 65535 in win.current["keys"]:
  685. win.textactive = ""
  686. part[-1] = ""
  687. win.current["keys"] = []
  688. win.current["key_letter"] = ""
  689. current_Y = current_Y + 400
  690. continue # we don't want to image url in the text it self.
  691. # Do you remember I was doing the starting positions of shots and
  692. # selection? These 2 are the same concept but to links to assets.
  693. assetpart = 0
  694. assetis = ""
  695. ############## TEXT IT SELF ###############
  696. # Here we go and render the text. Now you can see I iterate over
  697. # lines using re.split rather then built in split(). It's because
  698. # I need all characters still be present. Since I'm counting every
  699. # each character.
  700. for line in re.split("(\n)",t):
  701. # Now if the line says '\n' (new_line) then I just want to
  702. # draw the next line. Note that I'm also drawing the selection
  703. # box here. And moving all assetpart, shotpart and ss. So on
  704. # the next line those rectangles would start their drawing from
  705. # the begining of the line.
  706. # Also not the large comment with a POINT in it. It's I'm counting
  707. # this character in for to know where I'm editing.
  708. if line == "\n":
  709. smx = tileX
  710. tileX = xb
  711. #point = point + 1 #################### <<<<<< POINT
  712. if assetpart:
  713. assetpart = xb
  714. if shotpart:
  715. shotpart = xb
  716. if ss:
  717. UI_color.set(assets, win, "node_blendfile")
  718. UI_elements.roundrect(assets, win,
  719. ss,
  720. win.scroll["script"] + current_Y+5,
  721. smx-ss,
  722. 26,
  723. 10)
  724. text.stroke()
  725. smx = ss
  726. ss = xb
  727. current_Y = current_Y + 30
  728. line = " "
  729. #continue # And this is since I don't want weird rectangles
  730. # on the screen. And just new lines.
  731. # Now let's itterate over each and every word. Since we want
  732. # text wrapping. So if a certain word goes off screen I want
  733. # to declare a next line. NOTE: I'm still using re.split()
  734. # becuase we are counting charaters still.
  735. for word in re.split("( )", line):
  736. # Now let's do the actuall text wrapping. For size 20 which
  737. # is what I'm using. With monospace font. Width of each
  738. # character is 12 pixels. ( More or less. Good enough for
  739. # what we are doing ). So if lenght of word times 12, plus
  740. # current place on X is more then the width of our screen,
  741. # with all the margins. We want to start a new line.
  742. if tileX + len(word)*12 > mt:
  743. smx = tileX
  744. tileX = xb
  745. # And do not forget about all the asset, shot and selection
  746. # rendering too.
  747. if assetpart:
  748. assetpart = xb
  749. if shotpart:
  750. shotpart = xb
  751. if ss:
  752. UI_color.set(assets, win, "node_blendfile")
  753. UI_elements.roundrect(assets, win,
  754. ss,
  755. win.scroll["script"] + current_Y+5,
  756. smx-ss,
  757. 26,
  758. 10)
  759. text.stroke()
  760. ss = xb
  761. current_Y = current_Y + 30
  762. # This is logic to draw ASSETS higlights.
  763. if part[0] == "link" and part[1] != assetis and not assetpart:
  764. assetpart = tileX
  765. assetis = part[1]
  766. if assetpart:
  767. if word:
  768. UI_color.set(assets, win, "node_asset")
  769. UI_elements.roundrect(assets, win,
  770. assetpart-5,
  771. win.scroll["script"] + current_Y+8,
  772. tileX - assetpart + len(word)*12+12,
  773. 22,
  774. 10)
  775. # Selectiong asset. ( Not actually.) But making a
  776. # highlight of the asset. And if clicked. Going
  777. # to the asset.
  778. if int(win.current['mx']) in range(int(x+assetpart-5), int(x+tileX + len(word)*12+12)) \
  779. and int(win.current['my']) in range(int(y+win.scroll["script"] + current_Y+8), int(y+win.scroll["script"] + current_Y+8+22)) :
  780. win.current["script_asset_higlight"] = assetis
  781. if assetis == win.current["script_asset_higlight"]:
  782. UI_color.set(assets, win, "text_normal")
  783. UI_elements.roundrect(assets, win,
  784. assetpart-6,
  785. win.scroll["script"] + current_Y+26,
  786. tileX - assetpart + len(word)*12+12,
  787. 0,
  788. 3)
  789. assetpart = tileX
  790. assetis = part[1]
  791. if part[0] != "link":
  792. assetpart = 0
  793. assetis = ""
  794. # And this is logic to draw SHOTS higlights.
  795. if block[0] == "shot_block" and block[1] != shotis and not shotpart:
  796. shotpart = tileX
  797. shotis = block[1]
  798. if shotpart:
  799. if word:
  800. # I want to randomize the shots colors. But I don't it to be an epileptic
  801. # show. But not too much. Let's actually write the colors into the story
  802. # data. Why not.
  803. if "shot_colors" not in win.story:
  804. win.story["shot_colors"] = {}
  805. surl = "/"+scene+"/"+shotis
  806. # Making it scroll to the selected shot in the story itself.
  807. # Thankyou very much...
  808. if "scroll_shot_to_in_script" not in win.current:
  809. win.current["scroll_shot_to_in_script"] = True
  810. if surl == win.cur and win.current["scroll_shot_to_in_script"]:
  811. win.scroll["script"] = (0 - current_Y) + (win.current["h"]/2)
  812. win.current["scroll_shot_to_in_script"] = False
  813. if surl not in win.story["shot_colors"]:
  814. rcolors = [
  815. "shot_1",
  816. "shot_2",
  817. "shot_3",
  818. "shot_4",
  819. "shot_5"
  820. ]
  821. win.story["shot_colors"][surl] = rcolors[len(win.story["shot_colors"]) % len(rcolors)]
  822. col = win.story["shot_colors"][surl]
  823. if int(win.current['mx']) in range(int(x+shotpart-5), int(x+tileX + len(word)*12+12)) \
  824. and int(win.current['my']) in range(int(y+win.scroll["script"] + current_Y+8), int(y+win.scroll["script"] + current_Y+8+22)) :
  825. win.current["script_shot_higlight"] = shotis
  826. UI_color.set(shots, win, col)
  827. UI_elements.roundrect(shots, win,
  828. shotpart-6,
  829. win.scroll["script"] + current_Y+5,
  830. tileX - shotpart + len(word)*12+12,
  831. 28,
  832. 10)
  833. # This is a bit of a hack because I was lazy to
  834. # figure out how to make shot markings as good
  835. # as I made the selection. So drawing a thing around it
  836. # would not really work that well. But a line under the
  837. # text looks quite alright. I like the look of it.
  838. # Maybe I will polish this in future and give people
  839. # an option to draw it differently. I don't know yet.
  840. if shotis == win.current["script_shot_higlight"] or shot == shotis:
  841. UI_color.set(shots, win, "text_normal")
  842. UI_elements.roundrect(shots, win,
  843. shotpart-6,
  844. win.scroll["script"] + current_Y+30,
  845. tileX - shotpart + len(word)*12+12,
  846. 0,
  847. 3)
  848. shotpart = tileX
  849. shotis = block[1]
  850. if block[0] != "shot_block":
  851. shotpart = 0
  852. shotis = ""
  853. # Now let's draw our word. Simple really. What's so special
  854. # about it? Why is this comment 2 lines high?
  855. UI_color.set(text, win, "text_normal")
  856. #if win.current["script_shot_higlight"] == block[1] or shot == block[1]:
  857. # UI_color.set(text, win, "darker_parts")
  858. text.set_font_size(20)
  859. text.move_to(
  860. tileX,
  861. win.scroll["script"] + current_Y+25,
  862. )
  863. text.show_text(word)
  864. # Here I want to make a selection using a mouse. I think it's quite important
  865. # since using a Shift key and pressing sideways is not cool
  866. # and I want it to be cool.
  867. if win.current["LMB"]:
  868. if int(win.current["LMB"][1]) in range(int(y+win.scroll["script"] + current_Y), int(y+win.scroll["script"] + current_Y)+25)\
  869. and int(win.current["LMB"][0]-x) in range(tileX, tileX+len(word)*12+12):
  870. pointer[0] = int(point - (tileX+len(word)*12 - int(win.current["LMB"][0]-x))/12 + len(word))
  871. if int(win.current["my"]) in range(int(y+win.scroll["script"] + current_Y), int(y+win.scroll["script"] + current_Y)+25)\
  872. and int(win.current["mx"]-x) in range(tileX, tileX+len(word)*12+12):
  873. pointer[1] = int(point - (tileX+len(word)*12 - int(win.current["mx"]-x))/12 + len(word))
  874. win.cur = "/"+scene
  875. win.textactive = ""
  876. if int(win.current["my"]) in range(int(y+win.scroll["script"] + current_Y), int(y+win.scroll["script"] + current_Y)+25)\
  877. and int(win.current["mx"]-x) in range(tileX, tileX+len(word)*12+12):
  878. win.current["cursor"] = win.cursors["text"]
  879. # This is the logic to draw our selection and logic that
  880. # come with the selection.
  881. if UI_math.line_overlap([pointer[1], pointer[0]],[point, point+len(word)+1]) and not ss:
  882. ss = tileX+(min(pointer[1], pointer[0])-point)*12
  883. smx = ss
  884. # If current word is inside the selection. It means that
  885. # current text part is also inside the selection. And this
  886. # means this part should be edited if I press a button.
  887. if UI_math.line_overlap([pointer[1], pointer[0]],[point, point+len(word)+1]):
  888. # So we need to add this part and some data about this
  889. # part like amount of characters it's away from the
  890. # begining of the scene into a list that I could reference
  891. # later in the writting logic.
  892. if [part, p, num, n] not in selecwords:
  893. selecwords.append([part, p, num, n])
  894. # Let's make something for me to understand what is going
  895. # on. If I will enter the testing mode. Let it draw the
  896. # current number of characters. And the current characrter.
  897. if win.current["testing"]:
  898. # This is our character. It's not always available.
  899. # sometimes you migt be on the edge of something and
  900. # something else. So !! will mean seomthing is wrong.
  901. try:
  902. c = part[-1][pointer[0]-p]
  903. except:
  904. c = "!!"
  905. UI_color.set(text, win, "text_normal")
  906. text.set_font_size(10)
  907. text.move_to(
  908. tileX+(pointer[0]-point)*12-26,
  909. win.scroll["script"] + current_Y+35,
  910. )
  911. text.show_text(str(pointer[0])+"["+c+"]")
  912. # Now doing it again for the second pointer. If you
  913. # remeber, We have 2.
  914. try:
  915. c = part[-1][pointer[1]-p]
  916. except:
  917. c = "!!"
  918. UI_color.set(text, win, "text_normal")
  919. text.set_font_size(10)
  920. text.move_to(
  921. tileX+(pointer[1]-point)*12-26,
  922. win.scroll["script"] + current_Y+35,
  923. )
  924. text.show_text(str(pointer[1])+"["+c+"]")
  925. if max(pointer[1], pointer[0]) in range(point, point+len(word)+1):
  926. # This is some more drawing of the selection. This is
  927. # if the selection is ending mid way through the line.
  928. if ss:
  929. if pointer[0] != pointer[1] :
  930. UI_color.set(assets, win, "node_blendfile")
  931. UI_elements.roundrect(assets, win,
  932. max(ss, xb),
  933. win.scroll["script"] + current_Y+5,
  934. min(tileX+(max(pointer[1], pointer[0])-point)*12-max(ss, xb), mt-max(ss, xb)),
  935. 26,
  936. 10)
  937. elif not win.textactive and win.blink:
  938. UI_color.set(text, win, "node_blendfile")
  939. text.rectangle(
  940. max(ss, xb),
  941. win.scroll["script"] + current_Y+5,
  942. min(tileX+(max(pointer[1], pointer[0])-point)*12-max(ss, xb), mt-max(ss, xb)),
  943. 26)
  944. text.stroke()
  945. # win.scroll["script"] + current_Y,
  946. # min(mt-max(ss,xb), mt-xb),
  947. ss = 0
  948. #smx = 0
  949. if win.current["keys"] and not win.textactive and not putscroll:
  950. putscroll = min(0 - current_Y + width/2, win.scroll["script"])
  951. # Now in the end of our word. We are counting it's lenght
  952. # and moving the tileX so the next word draws after this
  953. # one and not on top of this one. I think it's important.
  954. point = point + len(word) #################### <<<<<< POINT
  955. tileX = tileX + len(word)*12
  956. # Also what you probably want to happen is when you type the script
  957. # will scroll down. Yeah. It's not coming by default. Even on standart
  958. # GTK text parts. So here is a little thing.
  959. if putscroll:
  960. win.scroll["script"] = putscroll
  961. ############# LOGIC ###############
  962. # So I'm doing the logic of typing separatelly from rendering of the text.
  963. # because it kind a seems right to do so. I'm still figuring it out at this
  964. # point. I have no idea what could go wrong. Maybe I will reddo the whole
  965. # thing. Maybe few times. IDK.
  966. if 65363 in win.current["keys"]: # RIGHT
  967. pointer[0] = pointer[0]+1
  968. if not 65506 in win.current["keys"]:
  969. pointer[1] = pointer[0]
  970. #win.current["keys"].remove(65363)
  971. if 65361 in win.current["keys"]: # LEFT
  972. pointer[0] = pointer[0]-1
  973. if not 65506 in win.current["keys"]:
  974. pointer[1] = pointer[0]
  975. #win.current["keys"].remove(65361)
  976. if 65362 in win.current["keys"]: # UP
  977. pointer[0] = pointer[0]-min(len(line), int((mt-12)/12)) # STILL BUGGY
  978. if not 65506 in win.current["keys"]:
  979. pointer[1] = pointer[0]
  980. #win.current["keys"].remove(65362)
  981. if 65364 in win.current["keys"]: # DOWN
  982. pointer[0] = pointer[0]+min(len(line), int((mt-12)/12)) # STILL BUGGY
  983. if not 65506 in win.current["keys"]:
  984. pointer[1] = pointer[0]
  985. #win.current["keys"].remove(65364)
  986. # In order for history to work properly I don't want to save history on
  987. # each button press. But rather ones every time the scene is edited. So
  988. if "scene_edited_already" not in win.current:
  989. win.current["scene_edited_already"] = False
  990. # I'm going to implement some basic clipboard. It will need love in future.
  991. # I just need for now something that will "work" so to speak. See where I
  992. # use the clipboard variable later to learn how it all works.
  993. clipboard = Gtk.Clipboard.get(Gdk.SELECTION_CLIPBOARD) # This is a clipboard object
  994. # If there is a key press that contains a string. Like anything on a keyboard
  995. # where you can actually type. Let's do something with it.
  996. def clean():
  997. # This is an empty function. Placeholder for a clean operation after
  998. # a given key press. Because we are dealing with multiple blocks of
  999. # text that look like 1 block for the user.
  1000. pass
  1001. # To make shot I want to write a little list of text parts that will come
  1002. # into that shot.
  1003. new_shot_list = []
  1004. after_shot_list = []
  1005. new_shot = []
  1006. if win.current["key_letter"] and not win.textactive:
  1007. # Here I'm going to implement history
  1008. if not win.current["scene_edited_already"]:
  1009. win.current["scene_edited_already"] = True
  1010. history.record(win, "/rnd"+win.cur, "[Edited]")
  1011. #print("------{ BEFORE }--------")
  1012. #print(win.story["scenes"][scene]["shots"])
  1013. #print("------{ }------")
  1014. #print()
  1015. #print() # For testing
  1016. for u, stuff in enumerate(selecwords):
  1017. part, p, num, n = stuff
  1018. try:
  1019. block = win.story["scenes"][scene]["shots"][num]
  1020. except:
  1021. break
  1022. try:
  1023. ORD = ord(win.current["key_letter"])
  1024. except:
  1025. ORD = 0
  1026. print(ORD, "ORD") # Output the value of the button.
  1027. # Now some of the ORDs I found to be weird characters. Like on
  1028. # CTRL - C or CTRL - V. So I want to filter them out. So there
  1029. # wont be a bunch of rectangles every time you type.
  1030. # Now I need the minimum and maximum parts. So there will not be
  1031. # scips of content I guess.
  1032. MIN = min(len(part[-1]), max(0, min(pointer[1], pointer[0])-p))
  1033. MAX = max(0, min(len(part[-1]), max(pointer[1], pointer[0])-p))
  1034. #print(pointer,"POINTER")
  1035. #print(p, "P")
  1036. #print(len(part[-1]), "LENGHT", [part[-1]])
  1037. #print(MIN, "MIN")
  1038. #print(MAX, "MAX")
  1039. #print()
  1040. # Here I want to record undo history for the script writer. I am going
  1041. # to ignore the letters. And only forcus on other simbols such as
  1042. # spacebars and various commands.
  1043. nonhistory = "1234567890qwertyuiopasdfghjklzxcvbnnmQWERTYUIOPASDFGHJKLZXCVBNM"
  1044. if win.current["key_letter"] not in nonhistory and ORD not in [26, 25]:
  1045. story.undo_record(win)
  1046. # Multiuser sycning
  1047. win.multiuser["request"] = "story"
  1048. # Now let's retrive out data.
  1049. # UNDO
  1050. if ORD == 26:
  1051. story.undo(win)
  1052. # REDO
  1053. if ORD == 25:
  1054. story.redo(win)
  1055. if u == 0:
  1056. if ORD not in range(32) or ORD in range(127, 160):
  1057. # Typing logic
  1058. part[-1] = \
  1059. part[-1][:MIN]+\
  1060. win.current["key_letter"]+\
  1061. part[-1][MAX:]
  1062. def clean():
  1063. pointer[0] = pointer[0] + 1
  1064. pointer[1] = pointer[0]
  1065. point = point + 1
  1066. if ORD == 22: # Paste ( Ctrl - V )
  1067. cliptext = str(clipboard.wait_for_text())
  1068. part[-1] = \
  1069. part[-1][:MIN]+\
  1070. cliptext+\
  1071. part[-1][MAX:]
  1072. def clean():
  1073. pointer[0] = pointer[0] + len(cliptext)
  1074. pointer[1] = pointer[0]
  1075. point = point + len(cliptext)
  1076. if ORD == 3: # Copy ( Ctrl - C )
  1077. clipboard.set_text( part[-1][MIN:MAX] , -1) # VERY BASIC
  1078. # If ord 13 it means you pressed enter. And it should be a new line.
  1079. # but for some reason it's ord 13. Maybe it is a new line. Who knows
  1080. # Anyway. It does not return \n but returns a weird rectangle. So
  1081. # here is a little solution.
  1082. if ORD == 13: # ENTER
  1083. # Only I want to use new line to exit from all kind of things.
  1084. # Like double enter will exit the frase. Ans stuff like this.
  1085. if part[-1][MIN-1] == "\n" and part[0] != "text":
  1086. block[-1].insert(n+1,
  1087. ["text", "\n"]
  1088. )
  1089. block[-1].insert(n+2,
  1090. ["text", part[-1][MAX:]+" "]
  1091. )
  1092. part[-1] = \
  1093. part[-1][:MIN-1]
  1094. else:
  1095. part[-1] = \
  1096. part[-1][:MIN]+\
  1097. "\n"+\
  1098. part[-1][MAX:]
  1099. def clean():
  1100. pointer[0] = pointer[0] + 1
  1101. pointer[1] = pointer[0]
  1102. point = point + 1
  1103. if ORD == 4: # CTRL D ( FOR SOME REASON)
  1104. block[-1].insert(n+1,
  1105. ["frase", ["text", " "], " "]
  1106. )
  1107. block[-1].insert(n+2,
  1108. ["text", part[-1][MAX:]]
  1109. )
  1110. part[-1] = \
  1111. part[-1][:MIN]
  1112. def clean():
  1113. pointer[0] = pointer[0] + 1
  1114. pointer[1] = pointer[0]
  1115. point = point + 1
  1116. # Activating the editing of the name automatically
  1117. if "current_name_frase_editing" in win.text:
  1118. win.text["current_name_frase_editing"]["text"] = ""
  1119. if "current_name_frase_editing" not in win.current:
  1120. win.current["current_name_frase_editing"] = False
  1121. win.current["current_name_frase_editing"] = [num, n+1]
  1122. win.textactive = "current_name_frase_editing"
  1123. if ORD == 8: # BACKSPACE
  1124. if pointer[1] == pointer[0]:
  1125. part[-1] = \
  1126. part[-1][:MIN-1]+\
  1127. part[-1][MAX:]
  1128. def clean():
  1129. pointer[0] = pointer[0] - 1
  1130. pointer[1] = pointer[0]
  1131. point = point - 1
  1132. else:
  1133. part[-1] = \
  1134. part[-1][:MIN]+\
  1135. part[-1][MAX:]
  1136. def clean():
  1137. pointer[0] = min(pointer[0], pointer[1])
  1138. pointer[1] = pointer[0]
  1139. if ORD == 127: # DELETE (FORWARD)
  1140. part[-1] = \
  1141. part[-1][:MIN]+\
  1142. part[-1][MAX+1:]
  1143. def clean():
  1144. pointer[0] = pointer[0]
  1145. pointer[1] = pointer[0]
  1146. if ORD == 19: # NEW SHOT ( CTRL - S )
  1147. app = part.copy()
  1148. app[-1] = part[-1][MIN:MAX]
  1149. new_shot_list.append(app)
  1150. new_shot = [num , n]
  1151. after_shot_list = block.copy()
  1152. after_shot_list[-1] = []
  1153. app2 = part.copy()
  1154. app2[-1] = part[-1][MAX:]
  1155. after_shot_list[-1].append(app2)
  1156. part[-1] = part[-1][:MIN]
  1157. if ORD == 12 and part[0] == "text" and pointer[1] != pointer[0]: # Asset higlight
  1158. if "linking_asset_operation_metadata" not in win.current:
  1159. win.current["linking_asset_operation_metadata"] = []
  1160. win.current["linking_asset_operation_metadata"] = [part , block, MIN, MAX]
  1161. def after(win, var):
  1162. print(var)
  1163. if var:
  1164. part , block, MIN, MAX = win.current["linking_asset_operation_metadata"]
  1165. block[-1].insert(n+1,
  1166. ["link", var, part[-1][MIN:MAX]]
  1167. )
  1168. block[-1].insert(n+2,
  1169. ["text", part[-1][MAX:]]
  1170. )
  1171. part[-1] = \
  1172. part[-1][:MIN]
  1173. win.current["key_letter"] = ""
  1174. studio_dialogs.asset_select(win, "link_asset_script_frase", after,
  1175. SEARCH=part[-1][MIN:MAX])
  1176. if ORD == 9 and part[0] == "text" and pointer[1] == pointer[0]: # Insert image
  1177. if "linking_asset_operation_metadata" not in win.current:
  1178. win.current["linking_asset_operation_metadata"] = []
  1179. win.current["linking_asset_operation_metadata"] = [part , block, MIN, MAX]
  1180. def after(win, var):
  1181. print(var)
  1182. if var:
  1183. part , block, MIN, MAX = win.current["linking_asset_operation_metadata"]
  1184. block[-1].insert(n+1,
  1185. ["image", var]
  1186. )
  1187. block[-1].insert(n+2,
  1188. ["text", part[-1][MAX:]]
  1189. )
  1190. part[-1] = \
  1191. part[-1][:MIN]
  1192. win.current["key_letter"] = ""
  1193. studio_dialogs.file_select(win, "link_asset_script_frase", after,
  1194. SEARCH=part[-1][MIN:MAX])
  1195. # Making stuff happen
  1196. elif u < len(selecwords)-1:
  1197. if ORD not in [12, 19, 3, 22, 26, 25]: # 3 = Crtl - C, 22 = Ctrl - V
  1198. part[-1] = ""
  1199. if ORD == 19:
  1200. app = part.copy()
  1201. new_shot_list.append(app)
  1202. part[-1] = ""
  1203. else:
  1204. MIN = min(len(part[-1])-1, max(0, min(pointer[1], pointer[0])-p))
  1205. MAX = max(0, min(len(part[-1])-1, max(pointer[1], pointer[0])-p))
  1206. if ORD not in [12, 19, 3, 22, 26, 25]:
  1207. part[-1] = part[-1][MAX:]
  1208. if ORD == 19:
  1209. app = part.copy()
  1210. app[-1] = part[-1][MIN:]
  1211. new_shot_list.append(app)
  1212. part[-1] = ""
  1213. # Saving to the file
  1214. story.save(win.project, win.story)
  1215. win.current["key_letter"] = ""
  1216. # Let's get all the rest of the parts from that block so I could insert them
  1217. # as a separate thing later. Because we basically splitting 1 block into 3
  1218. # making the selection. Middle one our new shot_block. And the rest. Should
  1219. # stay the same.
  1220. if new_shot:
  1221. num , n = new_shot
  1222. for l, part in enumerate(win.story["scenes"][scene]["shots"][num][-1]):
  1223. if l > n:
  1224. app = part.copy()
  1225. after_shot_list[-1].append(app)
  1226. part[-1] = ""
  1227. if after_shot_list[0] == "shot_block"\
  1228. and win.story["scenes"][scene]["shots"][num][0] == "shot_block":
  1229. after_shot_list[1] = win.story["scenes"][scene]["shots"][num][1]
  1230. if new_shot_list:
  1231. #print("NEW: ", new_shot_list)
  1232. #print("KEEP: ", after_shot_list)
  1233. nename = "Shot_1"
  1234. count = 1
  1235. while nename in shot_list:
  1236. count = count + 1
  1237. nename = "Shot_"+str(count)
  1238. num , n = new_shot
  1239. win.story["scenes"][scene]["shots"].insert(num+1,
  1240. ["shot_block", nename, new_shot_list])
  1241. win.story["scenes"][scene]["shots"].insert(num+2,
  1242. after_shot_list)
  1243. # Saving to the file
  1244. story.save(win.project, win.story)
  1245. #print()
  1246. #print("------{ AFTER }--------")
  1247. #print(win.story["scenes"][scene]["shots"])
  1248. #print("------{ }------")
  1249. clean()
  1250. current_Y = current_Y + 30
  1251. if scene not in win.story["pointers"]:
  1252. pointer = [point, point]
  1253. win.story["pointers"][scene] = pointer
  1254. else:
  1255. pointer[0] = min(pointer[0], point)
  1256. pointer[1] = min(pointer[1], point)
  1257. pointer[0] = max(pointer[0], 1)
  1258. pointer[1] = max(pointer[1], 1)
  1259. win.story["pointers"][scene] = pointer
  1260. # Selecting the shot
  1261. if win.current["script_shot_higlight"] and win.previous["LMB"] and not win.current["LMB"]:
  1262. win.cur = "/"+scene+"/"+win.current["script_shot_higlight"]
  1263. select_shot(win)
  1264. win.current["shot_left_side_scroll_please_work_omg_wtf"] = True
  1265. # Going to that asset
  1266. if win.current["script_asset_higlight"] and win.previous["LMB"] and not win.current["LMB"]:
  1267. try:
  1268. del win.text["scene_name"]
  1269. win.previous["script_asset_higlight"]
  1270. win.url = "assets"
  1271. win.cur = win.current["script_asset_higlight"]
  1272. win.current["asset_scene_selected"] = scene
  1273. win.current["asset_left_panel"] = "scene"
  1274. del win.current["script_asset_higlight"]
  1275. # Saving to the file
  1276. story.save(win.project, win.story)
  1277. except:
  1278. pass
  1279. ###########################
  1280. # And finally combine the layers
  1281. # Outputting the layer
  1282. layer.set_source_surface(frasesurface, x, y)
  1283. layer.paint()
  1284. # Outputting the layer
  1285. layer.set_source_surface(shotsurface, x, y)
  1286. layer.paint()
  1287. # Outputting the layer
  1288. layer.set_source_surface(assetsurface, x, y)
  1289. layer.paint()
  1290. # Outputting the layer
  1291. layer.set_source_surface(textsurface, x, y)
  1292. layer.paint()
  1293. # Scroll
  1294. UI_elements.scroll_area(layer, win, "script",
  1295. x+0,
  1296. y+0,
  1297. width+30,
  1298. height-0,
  1299. current_Y,
  1300. bar=True,
  1301. mmb=True,
  1302. bar_always=True)
  1303. ####### BOTTOM PANEL #########
  1304. UI_color.set(layer, win, "node_background")
  1305. UI_elements.roundrect(layer, win,
  1306. win.current["w"]/4,
  1307. win.current["h"]-50,
  1308. win.current["w"]/2,
  1309. 40,
  1310. 10)
  1311. # Documentation entry
  1312. def do():
  1313. def after(win, var):
  1314. pass
  1315. studio_dialogs.help(win, "help", after, SEARCH=talk.text("documentation_script_writer"))
  1316. UI_elements.roundrect(layer, win,
  1317. win.current["w"]/4,
  1318. win.current["h"]-50,
  1319. 40,
  1320. 40,
  1321. 10,
  1322. do,
  1323. "question")
  1324. # Here in the bottom I want to introduce little things to go between scenes
  1325. # back and forth.
  1326. left = ""
  1327. right = ""
  1328. for arrow in win.story["arrows"]:
  1329. if arrow[0][1] == scene and arrow[1] != "end":
  1330. right = arrow[1][1]
  1331. if arrow[1][1] == scene and arrow[0] != "start":
  1332. left = arrow[0][1]
  1333. if left:
  1334. def do():
  1335. win.scroll["script"] = 0
  1336. win.cur = "/"+left
  1337. win.url = "script"
  1338. try:
  1339. del win.text["scene_name"]
  1340. except:
  1341. pass
  1342. UI_elements.roundrect(layer, win,
  1343. win.current["w"]/2-45,
  1344. win.current["h"]-50,
  1345. 40,
  1346. 40,
  1347. 10,
  1348. button=do,
  1349. icon="left")
  1350. if right:
  1351. def do():
  1352. win.scroll["script"] = 0
  1353. win.cur = "/"+right
  1354. win.url = "script"
  1355. try:
  1356. del win.text["scene_name"]
  1357. except:
  1358. pass
  1359. UI_elements.roundrect(layer, win,
  1360. win.current["w"]/2+5,
  1361. win.current["h"]-50,
  1362. 40,
  1363. 40,
  1364. 10,
  1365. button=do,
  1366. icon="right")
  1367. # CANCEl
  1368. def do():
  1369. win.url = "story_editor"
  1370. win.assets = {}
  1371. try:
  1372. del win.text["scene_name"]
  1373. except:
  1374. pass
  1375. win.story["selected"] = [["scene", scene]]
  1376. win.scroll["script"] = 0
  1377. win.current["scene_edited_already"] = False
  1378. UI_elements.roundrect(layer, win,
  1379. win.current["w"]-40-win.current["w"]/4,
  1380. win.current["h"]-50,
  1381. 40,
  1382. 40,
  1383. 10,
  1384. button=do,
  1385. icon="cancel",
  1386. tip=talk.text("cancel"))
  1387. # Short cut ESC
  1388. if 65307 in win.current["keys"] and not win.textactive:
  1389. do()
  1390. win.current["keys"].remove(65307)
  1391. ############# CHECKLIST #################
  1392. # Here I want to have some checklists. Now... I have 4 types of new check-
  1393. # lists prepared for shots and scene. There are 3 types of checklists for
  1394. # a shot. So I can't just simply generate one on the fly. Since I don't
  1395. # know whether this shot is a simple shot, animated or VFX. And also I
  1396. # don't want to generate folders when they are not nessesary. So I want to
  1397. # implement a kind of selection thingy when if a checklists does not exist.
  1398. # Now let's first of all display our checklists properly.
  1399. if os.path.exists(win.project+"/rnd"+win.cur+"/shot.progress"):
  1400. checklist.draw(layer, win, win.project+"/rnd"+win.cur+"/shot.progress", back=win.url)
  1401. elif os.path.exists(win.project+"/rnd"+win.cur+"/scene.progress") and not shot:
  1402. checklist.draw(layer, win, win.project+"/rnd"+win.cur+"/scene.progress", back=win.url)
  1403. else:
  1404. # Now if we have no checklist what so ever. We want to give user a way
  1405. # to choose to generate one.
  1406. x = win.current["w"] / 4 * 3 + 10
  1407. y = 10
  1408. width = win.current["w"] / 4 - 20
  1409. height = win.current["h"] - 20
  1410. if shot:
  1411. # If we are in a shot I want to give different option to choose from
  1412. # then if we are just simply in a scene.
  1413. shot_options = {
  1414. "shot":"add_shot_live_checklist",
  1415. "shot_anim":"add_shot_anim_checklist", # First name of new_file then text on screen
  1416. "shot_vfx":"add_shot_vfx_checklist"
  1417. }
  1418. for num, option in enumerate(shot_options):
  1419. # We want to draw a button, a rectangle arround it and the text
  1420. # tooltip.
  1421. def do():
  1422. # First let's make sure that the folder exists. Because
  1423. # it might not exists. I think doing it automatically
  1424. # makes sense.
  1425. try:
  1426. os.makedirs(win.project+"/rnd"+win.cur)
  1427. except:
  1428. pass
  1429. # Then we copy the file.
  1430. oscalls.copy_file(
  1431. win,
  1432. os.getcwd()+"/new_file/"+option+".progress",
  1433. "/rnd"+win.cur+"/",
  1434. "shot.progress")
  1435. UI_elements.roundrect(layer, win,
  1436. x,
  1437. y+(50*num),
  1438. width,
  1439. 40,
  1440. 10,
  1441. button=do,
  1442. icon="checklist_new")
  1443. UI_color.set(layer, win, "progress_background")
  1444. UI_elements.roundrect(layer, win,
  1445. x,
  1446. y+(50*num),
  1447. width,
  1448. 40,
  1449. 10,
  1450. fill=False)
  1451. layer.stroke()
  1452. UI_color.set(layer, win, "text_normal")
  1453. layer.set_font_size(20)
  1454. layer.move_to( x+50, y+(50*num)+25)
  1455. layer.show_text(talk.text(shot_options[option]))
  1456. # Add checklist from clipboard
  1457. def do():
  1458. clipboard = Gtk.Clipboard.get(Gdk.SELECTION_CLIPBOARD)
  1459. cliptext = str(clipboard.wait_for_text())
  1460. savetext = ""
  1461. for i in cliptext.split("\n"):
  1462. if "[ ]" in i or "[V]" in i or "[v]" in i:
  1463. savetext = savetext + "\n"+i.replace("[V]", "[ ]").replace("[v]", "[ ]")
  1464. else:
  1465. savetext = savetext + "\n[ ] #"+i
  1466. cliptext = savetext[1:]
  1467. filepath = win.project+"/rnd"+win.cur+"/shot.progress"
  1468. s = open(filepath, 'w')
  1469. s.write(cliptext)
  1470. s.close()
  1471. UI_elements.roundrect(layer, win,
  1472. x,
  1473. y+(50*num+50),
  1474. width,
  1475. 40,
  1476. 10,
  1477. button=do,
  1478. icon="checklist_new")
  1479. UI_color.set(layer, win, "progress_background")
  1480. UI_elements.roundrect(layer, win,
  1481. x,
  1482. y+(50*num+50),
  1483. width,
  1484. 40,
  1485. 10,
  1486. fill=False)
  1487. layer.stroke()
  1488. UI_color.set(layer, win, "text_normal")
  1489. layer.set_font_size(20)
  1490. layer.move_to( x+50, y+(50*num+50)+25)
  1491. layer.show_text(talk.text("new_checklist_from_clipboard"))
  1492. else:
  1493. # Now... If it's not a shot. We can do it in one line.
  1494. # It's pretty simple.
  1495. def do():
  1496. # First let's make sure that the folder exists. Because
  1497. # it might not exists. I think doing it automatically
  1498. # makes sense.
  1499. try:
  1500. os.makedirs(win.project+"/rnd"+win.cur)
  1501. except:
  1502. pass
  1503. # Then we copy the file.
  1504. oscalls.copy_file(
  1505. win,
  1506. os.getcwd()+"/new_file/scene.progress",
  1507. "/rnd"+win.cur+"/",
  1508. "scene.progress")
  1509. UI_elements.roundrect(layer, win,
  1510. x,
  1511. y,
  1512. width,
  1513. 40,
  1514. 10,
  1515. button=do,
  1516. icon="checklist_new")
  1517. UI_color.set(layer, win, "progress_background")
  1518. UI_elements.roundrect(layer, win,
  1519. x,
  1520. y,
  1521. width,
  1522. 40,
  1523. 10,
  1524. fill=False)
  1525. layer.stroke()
  1526. UI_color.set(layer, win, "text_normal")
  1527. layer.set_font_size(20)
  1528. layer.move_to( x+50, y+25)
  1529. layer.show_text(talk.text("add_scene_checklist"))
  1530. ############## LEFT PANEL #####################
  1531. leftpanellist = ["shot", "schedule", "history"] # Using the names of the icons.
  1532. # We need to choose the correct category based smartly on the project's
  1533. # current progress. Or at least on the current progress of this asset.
  1534. if "script_left_panel" not in win.current:
  1535. win.current["script_left_panel"] = "shot"
  1536. # A little banner.
  1537. UI_color.set(layer, win, "node_background")
  1538. UI_elements.roundrect(layer, win,
  1539. 10,
  1540. 10,
  1541. win.current["w"]/4-20,
  1542. 50,
  1543. 10)
  1544. for num, thing in enumerate(leftpanellist):
  1545. if win.current["script_left_panel"] == thing:
  1546. UI_color.set(layer, win, "progress_time")
  1547. UI_elements.roundrect(layer, win,
  1548. 20+(40*num),
  1549. 15,
  1550. 40,
  1551. 40,
  1552. 10)
  1553. def do():
  1554. win.current["script_left_panel"] = thing
  1555. UI_elements.roundrect(layer, win,
  1556. 20+(40*num),
  1557. 15,
  1558. 40,
  1559. 40,
  1560. 10,
  1561. do,
  1562. thing)
  1563. ### SCHEDULES ###
  1564. if win.current["script_left_panel"] == "schedule":
  1565. schedule.draw(layer, win)
  1566. ### HISTORY ###
  1567. if win.current["script_left_panel"] == "history":
  1568. history.draw(layer, win)
  1569. #### SHOTS ####
  1570. if win.current["script_left_panel"] == "shot":
  1571. # Here I want to draw the left panel full of shots and stuff. So you
  1572. # could access the animation Blend-Files, render them and see analytics
  1573. # or rendering.
  1574. # Since it's the last thing I'm drawing to this layer. I guess we can clip it.
  1575. # Small progress bar of the scene:
  1576. # # Progressbar
  1577. scenedone = win.story["scenes"][scene]["fraction"]
  1578. UI_color.set(layer, win, "progress_background")
  1579. UI_elements.roundrect(layer, win,
  1580. 150,
  1581. 25,
  1582. win.current["w"]/4-180,
  1583. 20,
  1584. 10,
  1585. tip="Entire scene: "+str(round(scenedone*100, 1))+"%")
  1586. # Project Done
  1587. UI_color.set(layer, win, "progress_active")
  1588. UI_elements.roundrect(layer, win,
  1589. 150,
  1590. 25,
  1591. (win.current["w"]/4-180)*scenedone,
  1592. 20,
  1593. 10)
  1594. x = 10
  1595. y = 70
  1596. width = win.current["w"] / 4 - 20
  1597. height = win.current["h"] - 80
  1598. UI_elements.roundrect(layer, win,
  1599. x,
  1600. y,
  1601. width,
  1602. height,
  1603. 10,
  1604. fill=False)
  1605. layer.clip()
  1606. if "script_shots" not in win.scroll:
  1607. win.scroll["script_shots"] = 0
  1608. current_Y_shots = 0
  1609. rcolors = [
  1610. "shot_1",
  1611. "shot_2",
  1612. "shot_3",
  1613. "shot_4",
  1614. "shot_5"
  1615. ]
  1616. # So let's itterate over a list of shots we've got from the textview.
  1617. for shotis in shot_list:
  1618. # Getting the color. It's not always works.
  1619. if "shot_colors" not in win.story:
  1620. win.story["shot_colors"] = {}
  1621. surl = "/"+scene+"/"+shotis
  1622. if "shot_left_side_scroll_please_work_omg_wtf" not in win.current:
  1623. win.current["shot_left_side_scroll_please_work_omg_wtf"] = False
  1624. if win.current["shot_left_side_scroll_please_work_omg_wtf"] and win.cur == surl:
  1625. win.scroll["script_shots"] = 0-current_Y_shots+60
  1626. win.current["shot_left_side_scroll_please_work_omg_wtf"] = False
  1627. if surl not in win.story["shot_colors"]:
  1628. win.story["shot_colors"][surl] = rcolors[len(win.story["shot_colors"]) % len(rcolors)]
  1629. col = win.story["shot_colors"][surl]
  1630. UI_color.set(layer, win, "node_background")
  1631. UI_elements.roundrect(layer, win,
  1632. x,
  1633. y+win.scroll["script_shots"]+current_Y_shots,
  1634. width,
  1635. 50,
  1636. 10)
  1637. UI_color.set(layer, win, col)
  1638. UI_elements.roundrect(layer, win,
  1639. x+60,
  1640. y+win.scroll["script_shots"]+current_Y_shots+15,
  1641. 10,
  1642. 10,
  1643. 10)
  1644. # SHOT NAME
  1645. UI_color.set(layer, win, "text_normal")
  1646. layer.set_font_size(20)
  1647. layer.move_to( x+90, y+win.scroll["script_shots"] + current_Y_shots+30)
  1648. layer.show_text(shotis)
  1649. # ICON
  1650. UI_elements.image(layer, win,
  1651. "settings/themes/"+win.settings["Theme"]+"/icons/shot.png",
  1652. 20, y+win.scroll["script_shots"] + current_Y_shots+5, 40, 40)
  1653. # The selection will be badically the current win.cur value. SO...
  1654. if surl == win.cur:
  1655. UI_color.set(layer, win, "text_normal")
  1656. UI_elements.roundrect(layer, win,
  1657. x,
  1658. y+win.scroll["script_shots"]+current_Y_shots,
  1659. width,
  1660. 50,
  1661. 10,
  1662. fill=False)
  1663. layer.stroke()
  1664. # If this shot has a folder already. Unfortunatly it's not wise
  1665. # to make it editable. But I already talk about it in the
  1666. # beggining of this file.
  1667. # But a folder button could be cool.
  1668. if os.path.exists(win.project+"/rnd"+win.cur):
  1669. def do():
  1670. oscalls.Open(win.project+"/rnd"+win.cur)
  1671. UI_elements.roundrect(layer, win,
  1672. width-45,
  1673. y+win.scroll["script_shots"]+current_Y_shots+5,
  1674. 40,
  1675. 40,
  1676. 10,
  1677. do,
  1678. "folder",
  1679. tip="/rnd"+win.cur )
  1680. else:
  1681. # If there is no folder I want to give people an ability
  1682. # to edit it. I think let's do it like with checklists.
  1683. # like a little second button. That if you click it. You
  1684. # create a text entry. I know. Quite a lot of text entries.
  1685. # There will be more.
  1686. if "current_shot_name_editor" not in win.current:
  1687. win.current["current_shot_name_editor"] = ""
  1688. # Now if the button is clicked. There will be an editor
  1689. # let's make one.
  1690. if win.current["current_shot_name_editor"] == shotis and win.textactive == "shot_name":
  1691. UI_elements.text(layer, win, "shot_name",
  1692. x+85,
  1693. y+win.scroll["script_shots"]+current_Y_shots+5,
  1694. width-90,
  1695. 40,
  1696. set_text=shotis)
  1697. # Now let's make it applyable. But first we need to filter
  1698. # the name. So there will not be any weird stuff. So the
  1699. # folder when it's created will not make problems.
  1700. newname = win.text["shot_name"]["text"].replace("/","_").replace(" ", "_")\
  1701. .replace('"',"_").replace("(","_").replace(")","_").replace("'","_")\
  1702. .replace("[","_").replace("]","_").replace("{","_").replace("}","_")
  1703. if newname not in shot_list:
  1704. def do():
  1705. win.current["key_letter"] = ""
  1706. shot_list[shotis][1] = newname
  1707. win.cur = "/"+scene+"/"+newname
  1708. # Saving to the file
  1709. story.save(win.project, win.story)
  1710. UI_elements.roundrect(layer, win,
  1711. width-45,
  1712. y+win.scroll["script_shots"]+current_Y_shots+5,
  1713. 40,
  1714. 40,
  1715. 10,
  1716. button=do,
  1717. icon="ok")
  1718. else:
  1719. # A button to activate the editing I guess.
  1720. def do():
  1721. win.current["current_shot_name_editor"] = shotis
  1722. try:
  1723. del win.text["shot_name"]
  1724. except:
  1725. pass
  1726. win.textactive = "shot_name"
  1727. UI_elements.roundrect(layer, win,
  1728. x+85,
  1729. y+win.scroll["script_shots"]+current_Y_shots+5,
  1730. width-90,
  1731. 40,
  1732. 10,
  1733. button=do,
  1734. fill=False)
  1735. layer.stroke()
  1736. current_Y_shots = current_Y_shots + 60
  1737. # Let's make the color selectiong thing. Because I know people
  1738. # gonna eat me alive if the software does everything for them.
  1739. # Especially when it's something that customazible.
  1740. for num, col in enumerate(rcolors):
  1741. if win.story["shot_colors"][surl] == col:
  1742. # If the current color is the color of the shot then
  1743. # let's draw a little white thing around it.
  1744. UI_color.set(layer, win, "text_normal")
  1745. UI_elements.roundrect(layer, win,
  1746. x+(width/len(rcolors)*num),
  1747. y+win.scroll["script_shots"]+current_Y_shots-2,
  1748. width/len(rcolors),
  1749. 10,
  1750. 12)
  1751. UI_color.set(layer, win, col)
  1752. UI_elements.roundrect(layer, win,
  1753. x+(width/len(rcolors)*num)+2,
  1754. y+win.scroll["script_shots"]+current_Y_shots,
  1755. width/len(rcolors)-4,
  1756. 10,
  1757. 10)
  1758. # Also let's make a button that the user can click to change
  1759. # the color.
  1760. def do():
  1761. win.story["shot_colors"][surl] = col
  1762. UI_elements.roundrect(layer, win,
  1763. x+(width/len(rcolors)*num),
  1764. y+win.scroll["script_shots"]+current_Y_shots-2,
  1765. width/len(rcolors),
  1766. 20,
  1767. 12,
  1768. button=do,
  1769. fill=False)
  1770. layer.stroke()
  1771. current_Y_shots = current_Y_shots + 30
  1772. # Now let's draw 4 main folders of the shot.
  1773. # storyboard
  1774. # opengl
  1775. # test_rnd
  1776. # rendered
  1777. # There is also an extra folder created always.
  1778. # but since it's very technical let's not include it here.
  1779. # let's focus on those 4 because for the user these 4 will
  1780. # be our progress bar.
  1781. ##### ##### ##### #####
  1782. ########## ######### ########## ######## ##########
  1783. ##### ##### ##### #####
  1784. # Like 4 links in a chain or something. I think it's a good
  1785. # representation.
  1786. fouricons = {
  1787. "storyboard":[],
  1788. "opengl":[],
  1789. "test_rnd":[],
  1790. "rendered":[]
  1791. }
  1792. fraction = 0.0
  1793. # Let's create a variable for the current selected folder.
  1794. if shotis+"_active_folder" not in win.current:
  1795. win.current[shotis+"_active_folder"] = "Pending"
  1796. for numb, icon in enumerate(fouricons):
  1797. # While we are drawing them. Let's find out whether there
  1798. # are any images inside. If yes. We add this to the over
  1799. # all percentage.
  1800. files = []
  1801. try:
  1802. files = sorted(os.listdir(win.project+"/rnd"+win.cur+"/"+icon))
  1803. except:
  1804. pass
  1805. fouricons[icon] = files
  1806. if files:
  1807. fraction = 0.25*(numb+1)
  1808. # If the icon is currently selected.
  1809. if win.current[shotis+"_active_folder"] == icon:
  1810. UI_color.set(layer, win, "progress_time")
  1811. UI_elements.roundrect(layer, win,
  1812. x+(width/5)*numb+(width/10)+20,
  1813. y+win.scroll["script_shots"]+current_Y_shots-2,
  1814. 40,
  1815. 40,
  1816. 20)
  1817. def do():
  1818. win.current[shotis+"_active_folder"] = icon
  1819. UI_elements.roundrect(layer, win,
  1820. x+(width/5)*numb+(width/10)+20,
  1821. y+win.scroll["script_shots"]+current_Y_shots-2,
  1822. 40,
  1823. 40,
  1824. 20,
  1825. button=do,
  1826. icon=icon,
  1827. tip=icon)
  1828. current_Y_shots = current_Y_shots + 50
  1829. # Let's find out which one is going to be selected.
  1830. if win.current[shotis+"_active_folder"] == "Pending":
  1831. if fouricons["rendered"]:
  1832. win.current[shotis+"_active_folder"] = "rendered"
  1833. elif fouricons["test_rnd"]:
  1834. win.current[shotis+"_active_folder"] = "test_rnd"
  1835. elif fouricons["opengl"]:
  1836. win.current[shotis+"_active_folder"] = "opengl"
  1837. else:
  1838. win.current[shotis+"_active_folder"] = "storyboard"
  1839. # Now let's draw a line that will act almost like a progress
  1840. # bar of a kind.
  1841. # It will be made of 4 circles stading on a line.
  1842. UI_color.set(layer, win, "progress_background")
  1843. UI_elements.roundrect(layer, win,
  1844. x+20,
  1845. y+win.scroll["script_shots"]+current_Y_shots-2,
  1846. width-40,
  1847. 0,
  1848. 5)
  1849. UI_color.set(layer, win, "progress_active")
  1850. UI_elements.roundrect(layer, win,
  1851. x+20,
  1852. y+win.scroll["script_shots"]+current_Y_shots-2,
  1853. (width-40)*fraction,
  1854. 0,
  1855. 5)
  1856. # Now the 4 circles.
  1857. for numb, icon in enumerate(fouricons):
  1858. UI_color.set(layer, win, "progress_background")
  1859. if fouricons[icon]: # If this folder has any files.
  1860. UI_color.set(layer, win, "progress_active")
  1861. UI_elements.roundrect(layer, win,
  1862. x+(width/5)*numb+(width/10)+30,
  1863. y+win.scroll["script_shots"]+current_Y_shots-7,
  1864. 0,
  1865. 0,
  1866. 10)
  1867. current_Y_shots = current_Y_shots + 50
  1868. ifremote = 0
  1869. if win.analytics["from-remote-server"]:
  1870. ifremote = 50
  1871. if "remote-folder-data" not in win.current:
  1872. win.current["remote-folder-data"] = {}
  1873. if win.cur not in win.current["remote-folder-data"]:
  1874. win.current["remote-folder-data"][win.cur] = {"missing":{},
  1875. "changed":{}}
  1876. def do():
  1877. def after(win, var):
  1878. UI_elements.reload_images(win)
  1879. win.current["remote-folder-data"]["prompt"] = win.cur
  1880. studio_dialogs.http_client_update_prompt(win, "http-client-prompt", after)
  1881. UI_elements.roundrect(layer, win,
  1882. 10,
  1883. y+win.scroll["script_shots"]+current_Y_shots-10,
  1884. 40,
  1885. 40,
  1886. 10,
  1887. button=do,
  1888. icon="download",
  1889. tip=talk.text("RemoteShotUpdates"))
  1890. if win.current["remote-folder-data"][win.cur]["missing"] or win.current["remote-folder-data"][win.cur]["changed"]:
  1891. count = str(len(win.current["remote-folder-data"][win.cur]["missing"])+len(win.current["remote-folder-data"][win.cur]["changed"]))
  1892. UI_color.set(layer, win, "progress_active")
  1893. UI_elements.roundrect(layer, win,
  1894. 10+25,
  1895. y+win.scroll["script_shots"]+current_Y_shots-10,
  1896. len(count)*12+6,
  1897. 25,
  1898. 5)
  1899. layer.fill()
  1900. UI_color.set(layer, win, "text_normal")
  1901. layer.set_font_size(20)
  1902. layer.move_to(10+28,
  1903. y+win.scroll["script_shots"]+current_Y_shots-10+20)
  1904. layer.show_text(count)
  1905. # The user might want to add the subfolders if they do not
  1906. # exist already.
  1907. at_least_one_missing = False
  1908. list_of_folders_to_generate = ["storyboard", "opengl", "test_rnd", "rendered", "extra"]
  1909. files_there = []
  1910. try:
  1911. files_there = os.listdir(win.project+"/rnd"+win.cur)
  1912. except:
  1913. pass
  1914. for i in list_of_folders_to_generate:
  1915. if i not in files_there:
  1916. at_least_one_missing = True
  1917. if at_least_one_missing:
  1918. def do():
  1919. for i in list_of_folders_to_generate:
  1920. try:
  1921. os.makedirs(win.project+"/rnd"+win.cur+"/"+i)
  1922. except:
  1923. pass
  1924. UI_elements.roundrect(layer, win,
  1925. ifremote+10,
  1926. y+win.scroll["script_shots"]+current_Y_shots-10,
  1927. width-30-ifremote,
  1928. 40,
  1929. 10,
  1930. button=do,
  1931. icon="folder",
  1932. tip=talk.text("GenerateSubfoldersTip"))
  1933. UI_color.set(layer, win, "text_normal")
  1934. layer.move_to(ifremote+60, y+win.scroll["script_shots"]+current_Y_shots+15)
  1935. layer.show_text(talk.text("GenerateSubfolders"))
  1936. if at_least_one_missing or win.analytics["from-remote-server"]:
  1937. current_Y_shots = current_Y_shots + 50
  1938. # Now after we have the progress bar / selection type thingy.
  1939. # let's do a preview of the render. Usually. When rendering
  1940. # using Blender-Organizer legacy ( or blender console ) it makes
  1941. # files like 0001.png, 0002.png, 0003.png and so on. Now
  1942. # user might any type of stuff in that folder. And I don't want
  1943. # a broken program if stuff like this happens. So.
  1944. if shotis+"_active_folder_item" not in win.current:
  1945. win.current[shotis+"_active_folder_item"] = 0
  1946. # Making sure that we never going to select something beyond selectable.
  1947. if win.current[shotis+"_active_folder_item"] > len(fouricons[win.current[shotis+"_active_folder"]])-1:
  1948. win.current[shotis+"_active_folder_item"] = len(fouricons[win.current[shotis+"_active_folder"]])-1
  1949. # This will be the user selected file inside the folder.
  1950. # The frame. In case image fails to load.
  1951. UI_color.set(layer, win, "progress_background")
  1952. UI_elements.roundrect(layer, win,
  1953. x+5,
  1954. y+win.scroll["script_shots"]+current_Y_shots-7,
  1955. width-10,
  1956. 200,
  1957. 10,
  1958. fill=False)
  1959. layer.stroke()
  1960. # Image it self.
  1961. if fouricons[win.current[shotis+"_active_folder"]]:
  1962. imageurl = win.project+"/rnd"+win.cur+"/"+win.current[shotis+"_active_folder"]+"/"+fouricons[win.current[shotis+"_active_folder"]][win.current[shotis+"_active_folder_item"]]
  1963. UI_elements.image(layer, win,
  1964. imageurl,
  1965. x+10,
  1966. y+win.scroll["script_shots"]+current_Y_shots-2,
  1967. int(width-20),
  1968. int(190),
  1969. cell="shot_renders")
  1970. # Well how to live without a clickable openable thingy.
  1971. def do():
  1972. oscalls.file_open(win, imageurl)
  1973. UI_elements.roundrect(layer, win,
  1974. x+5,
  1975. y+win.scroll["script_shots"]+current_Y_shots-7,
  1976. width-10,
  1977. 200,
  1978. 10,
  1979. button=do,
  1980. fill=False)
  1981. layer.stroke()
  1982. else:
  1983. # If there is no image to show we can put one. Using a clipboard.
  1984. def do():
  1985. clipboard = Gtk.Clipboard.get(Gdk.SELECTION_CLIPBOARD)
  1986. im = clipboard.wait_for_image()
  1987. if not im:
  1988. return
  1989. Px = im.get_width()
  1990. Py = im.get_height()
  1991. Pc = cairo.ImageSurface(cairo.FORMAT_ARGB32, Px, Py)
  1992. Pb = cairo.Context(Pc)
  1993. Gdk.cairo_set_source_pixbuf( Pb, im, 0, 0)
  1994. Pb.paint()
  1995. imageurl = win.project+"/rnd"+win.cur+"/"+win.current[shotis+"_active_folder"]+"/0001.png"
  1996. try:
  1997. os.makedirs(win.project+"/rnd"+win.cur+"/"+win.current[shotis+"_active_folder"])
  1998. except:
  1999. pass
  2000. Pc.write_to_png(imageurl)
  2001. UI_elements.roundrect(layer, win,
  2002. x+5+(width/2)-20,
  2003. y+win.scroll["script_shots"]+current_Y_shots-7+80,
  2004. 40,
  2005. 40,
  2006. 10,
  2007. button=do,
  2008. icon="image_new")
  2009. current_Y_shots = current_Y_shots + 210
  2010. # After we drew our image. Let's draw a little timeline thing.
  2011. # so the user could select which frame from the animation they
  2012. # want to see. Basically a little bar. I guess. IDK. Like
  2013. # Blender's timeline I guess. But later I will draw a time
  2014. # graph on it. So it's not as simple as that.
  2015. UI_color.set(layer, win, "dark_overdrop")
  2016. layer.rectangle(
  2017. x+5,
  2018. y+win.scroll["script_shots"]+current_Y_shots,
  2019. width-10,
  2020. 100)
  2021. layer.fill()
  2022. numofis = len(fouricons[win.current[shotis+"_active_folder"]])
  2023. # Here as you can see I made the the rectangle a 100 pixels high
  2024. # this is for 1 simple reason. To be able to draw analytics.
  2025. # But.
  2026. # There are like one million problems with it. Let's go over them.
  2027. # * There are multiple blend files each one with analytics data.
  2028. # * Reading JSON files and drawing graphs is not a trivial thing
  2029. # requiring cashing of the image. Such as in Analytics window.
  2030. # Bull will see.
  2031. # * During rendeing the DATA changes. And I want to see the up
  2032. # to date data at all times.
  2033. # Let's try to implement it the simple way. Reading everything
  2034. # live. I've done that in Blender-Organizer. But the data was
  2035. # way simpler.
  2036. alldata = {}
  2037. largest = 1
  2038. # WAIT extra might not exists.
  2039. try:
  2040. for filename in os.listdir(win.project+"/rnd"+win.cur+"/extra"):
  2041. if filename.endswith(".blend.json"):
  2042. # I want to now open JSON FILE for each one and parse
  2043. # it. Actually when I think about it. It's not supposed
  2044. # to be that bad. But this computer has an SSD. LOL.
  2045. data = {}
  2046. try:
  2047. with open(win.project+"/rnd"+win.cur+"/extra/"+filename) as json_file:
  2048. data = json.load(json_file)
  2049. # Since I'm inside try I can do some wild stuff here.
  2050. data = data["analytics"][win.current[shotis+"_active_folder"]]
  2051. alldata[filename.replace(".json", "")] = data
  2052. for i in data:
  2053. if data[i] > largest:
  2054. largest = data[i]
  2055. except:
  2056. pass
  2057. except:
  2058. pass
  2059. # So let's now read through all the data that we have
  2060. for n, blend in enumerate(alldata):
  2061. data = alldata[blend]
  2062. # I want each new file to have it's own color. So
  2063. UI_color.set(layer, win, rcolors[n % len(rcolors)])
  2064. layer.move_to(
  2065. x+5,
  2066. y+win.scroll["script_shots"]+current_Y_shots+100
  2067. )
  2068. # Now let's draw everything.
  2069. for numb, fil in enumerate(fouricons[win.current[shotis+"_active_folder"]]):
  2070. if str(numb+1) in data:
  2071. layer.line_to(
  2072. x+5+(width-10)/numofis*(numb+0.5),
  2073. (y+win.scroll["script_shots"]+current_Y_shots+100)-(100/largest*data[str(numb+1)])
  2074. )
  2075. layer.stroke()
  2076. # Funny that it didn't really stuck too much like this. Please
  2077. # let me know if on your machine this way of doing it is hell
  2078. # a slow.
  2079. for numb, fil in enumerate(fouricons[win.current[shotis+"_active_folder"]]):
  2080. # Here I'm going to put some logic. That will enable me to
  2081. # scroll trough the images. In a way that allows me to
  2082. # preview the animation. Simple button wouldn't work since
  2083. # it's waiting for the user to release the button to be
  2084. # activated. This one will be activated by just dragging
  2085. # across.
  2086. # Also my roundrect buttons are not ideal when you have little
  2087. # space and 10000 frames.
  2088. fill = False
  2089. if int(win.current["mx"]) in range(int(x+5+(width-10)/numofis*numb),int(x+5+(width-10)/numofis*numb+(width-10)/numofis))\
  2090. and int(win.current["my"]) in range(int(y+win.scroll["script_shots"]+current_Y_shots), int(y+win.scroll["script_shots"]+current_Y_shots+100)):
  2091. # Before we are doing the logic. I want to give user a
  2092. # tooltip about render analytics. So let's do this.
  2093. tip = ""
  2094. for n, blend in enumerate(alldata):
  2095. data = alldata[blend]
  2096. if str(numb+1) in data:
  2097. tip = tip + blend+" "+UI_math.timestring(data[str(numb+1)]/1000000)+"\n"
  2098. if tip:
  2099. tip = tip[:-1] # Removing the last \n
  2100. UI_elements.tooltip(win, tip)
  2101. # Now let's do the fill and other stuff
  2102. fill = True
  2103. if win.current["LMB"]:
  2104. win.current[shotis+"_active_folder_item"] = numb
  2105. if fill or win.current[shotis+"_active_folder_item"] == numb:
  2106. UI_color.set(layer, win, "progress_time")
  2107. layer.rectangle(
  2108. x+5+(width-10)/numofis*numb,
  2109. y+win.scroll["script_shots"]+current_Y_shots,
  2110. (width-10)/numofis,
  2111. 100)
  2112. layer.fill()
  2113. current_Y_shots = current_Y_shots + 110
  2114. # Okay let's put blend file from the shot. So you could actually
  2115. # do work on it.
  2116. # But first. I want to add a seach promt. So I could add new ones
  2117. # the same way I do it in any other window. By searching. I think
  2118. # it's important to keep the workflow similar.
  2119. # I think that if we have no blend files at all. It might give you
  2120. # to make a new one with the name of the shot. And all the rest will
  2121. # be done by searching. Yeah. Makes sense. Wait. I can put the name of
  2122. # the shot in the search. Hm... Okay. Let's make the seach icon first.
  2123. UI_elements.image(layer, win,
  2124. "settings/themes/"+win.settings["Theme"]+"/icons/search.png",
  2125. x+10,
  2126. y+win.scroll["script_shots"]+current_Y_shots,
  2127. 40,
  2128. 40)
  2129. # Now beside it will be out text entry.
  2130. UI_elements.text(layer, win, "shot_search",
  2131. x+50,
  2132. y+win.scroll["script_shots"]+current_Y_shots,
  2133. width-55,
  2134. 40)
  2135. current_Y_shots = current_Y_shots + 50
  2136. # Now let's get the blend file list. It's going to take a few
  2137. # stept. First we are going to try getting all the files in the
  2138. # folder. Then filter them by .blend in the end. We do not want
  2139. # to show you the .blend1 files. We don't want to show you
  2140. # anything.
  2141. blendfiles = []
  2142. try:
  2143. for blend in os.listdir(win.project+"/rnd"+win.cur):
  2144. if blend.endswith(".blend") and "_backup" not in blend:
  2145. blendfiles.append(blend)
  2146. except:
  2147. pass
  2148. # Now since we've got our blendfiles list. We can now draw them.
  2149. # But I want to preserve the countinuity with the rest of the
  2150. # software. So I will need to draw a little Blendfile node each
  2151. # time. For which I'm going to create a node. But this time it's
  2152. # going to have 2 icons at the bottom.
  2153. ############## ############## ##############
  2154. # 01.blend # # 02.blend # # 03.blend #
  2155. ############## ############## ##############
  2156. # ____ # # ____ # # ____ #
  2157. # ( 0 0 ) # # ( 0 0 ) # # ( 0 0 ) #
  2158. # ) ( # # ) ( # # ) ( #
  2159. # \__/ # # \__/ # # \__/ #
  2160. # # # # # #
  2161. ############## ############## ##############
  2162. # 0 # 0 # # 0 # 0 # # 0 # 0 #
  2163. ############## ############## ##############
  2164. # Those 2 icons in the bottom of each will be buttons for render
  2165. # and linking. The linking is what you do to put assets inside
  2166. # animation files. A new dialog should be developped for this.
  2167. # The rendering will resemble rendering of Blender-Organizer with
  2168. # render times analytics and stuff. But will be way more extended.
  2169. # For example with an ability to open a render task on another
  2170. # computer. So groups of people could work together.
  2171. tileX = 5
  2172. searchis = win.text["shot_search"]["text"].replace("/","_").replace(" ", "_")\
  2173. .replace('"',"_").replace("(","_").replace(")","_").replace("'","_")\
  2174. .replace("[","_").replace("]","_").replace("{","_").replace("}","_")
  2175. for bn, blend in enumerate(blendfiles):
  2176. # Each one will be drawn in it's own little layer. Because
  2177. # I want the top bar to have a nice rounded corner and also
  2178. # the image to be cut.
  2179. # Each one here will 128 pixels wide. ( Because this is the
  2180. # resolution of blender-thumbnailer.py thumbnails. And I
  2181. # want to save horizontal resolution as much as possible
  2182. # But before I render a Blend. I want to exclude it if it's
  2183. # not in a search.
  2184. if searchis and searchis.lower() not in blend.lower():
  2185. continue
  2186. # Making the layer
  2187. nodesurface = cairo.ImageSurface(cairo.FORMAT_ARGB32, 128, 198)
  2188. node = cairo.Context(nodesurface)
  2189. node.select_font_face("Monospace", cairo.FONT_SLANT_NORMAL, cairo.FONT_WEIGHT_NORMAL)
  2190. UI_elements.roundrect(node, win,
  2191. 0,
  2192. 0,
  2193. 128,
  2194. 198,
  2195. 10,
  2196. fill=False)
  2197. node.clip()
  2198. # Background
  2199. UI_color.set(node, win, "dark_overdrop")
  2200. node.rectangle(0,0,128, 198)
  2201. node.fill()
  2202. # Blender icon
  2203. UI_elements.image(node, win,
  2204. win.project+"/rnd"+win.cur+"/"+blend,
  2205. 0,
  2206. 20,
  2207. 128,
  2208. 128, cell="shot_blends")
  2209. # Banner
  2210. UI_color.set(node, win, "node_blendfile")
  2211. node.rectangle(0,0,128, 20)
  2212. node.fill()
  2213. # Filename
  2214. UI_color.set(node, win, "text_normal")
  2215. node.set_font_size(12)
  2216. node.move_to(5, 15)
  2217. node.show_text(blend)
  2218. # Outputting the layer
  2219. layer.set_source_surface(nodesurface,
  2220. x+tileX,
  2221. y+win.scroll["script_shots"]+current_Y_shots)
  2222. layer.paint()
  2223. # Here we gonna out 3 buttons:
  2224. # 1. Launch the Blend-File
  2225. # 2. Link into Blend-File
  2226. # 3. Render Blend-File
  2227. # Launch the Blend file:
  2228. def do():
  2229. oscalls.file_open(win, win.project+"/rnd"+win.cur+"/"+blend)
  2230. UI_elements.roundrect(layer, win,
  2231. x+tileX,
  2232. y+win.scroll["script_shots"]+current_Y_shots,
  2233. 128,
  2234. 148,
  2235. 10,
  2236. button=do,
  2237. fill=False,
  2238. tip=blend)
  2239. layer.stroke()
  2240. # Link things into Blend-File
  2241. def do():
  2242. # I'm using a dialog here. Because I want to return back
  2243. # to the shot we were in. But I don't really want to do
  2244. # anything after the dialog is closed. So there is this
  2245. # empty function.
  2246. def after(win, var):
  2247. pass
  2248. studio_dialogs.asset_link(win, "asset_linking", after, "/rnd"+win.cur+"/"+blend)
  2249. UI_elements.roundrect(layer, win,
  2250. x+tileX+20,
  2251. y+win.scroll["script_shots"]+current_Y_shots+152,
  2252. 40,
  2253. 40,
  2254. 10,
  2255. icon="obj_link",
  2256. button=do,
  2257. tip=talk.text("link_shot_blend_tooltip")+blend)
  2258. # Render
  2259. def do():
  2260. def after(win, var):
  2261. pass
  2262. studio_dialogs.render(win, "render_setup", after, "/rnd"+win.cur+"/"+blend)
  2263. UI_elements.roundrect(layer, win,
  2264. x+tileX+65,
  2265. y+win.scroll["script_shots"]+current_Y_shots+152,
  2266. 40,
  2267. 40,
  2268. 10,
  2269. icon="render",
  2270. button=do,
  2271. tip=talk.text("render_shot_blend_tooltip")+blend)
  2272. tileX = tileX + 150
  2273. if tileX + 128 > width - 10 and bn != len(blendfiles)-1:
  2274. tileX = 5
  2275. current_Y_shots = current_Y_shots + 205
  2276. # Now before we are finished. We want to able to add new files
  2277. # by typing the names in the search.
  2278. if not blendfiles and not searchis:
  2279. searchis = shotis
  2280. if not searchis.endswith(".blend"):
  2281. searchis = searchis + ".blend"
  2282. if searchis != ".blend" and searchis not in blendfiles:
  2283. # There will be 2 buttons. To add a brand new file and to
  2284. # copy one from a different shot. In Blender-Oraniger it
  2285. # had a copy-paste kind of system for this. But since we
  2286. # have a file selector. I'm going to use it instead. Tho
  2287. # tell me if a copy-paste for files is a cool idea.
  2288. if tileX + 128 > width - 10:
  2289. tileX = 5
  2290. current_Y_shots = current_Y_shots + 205
  2291. # ADD NEW BLEND
  2292. def do():
  2293. # First let's make sure that the folder exists. Because
  2294. # it might not exists. I think doing it automatically
  2295. # makes sense.
  2296. try:
  2297. os.makedirs(win.project+"/rnd"+win.cur)
  2298. except:
  2299. pass
  2300. # Then we copy the file.
  2301. oscalls.copy_file(
  2302. win,
  2303. os.getcwd()+"/new_file/rnd.blend",
  2304. "/rnd"+win.cur+"/",
  2305. searchis)
  2306. # And clear the search
  2307. win.text["shot_search"]["text"] = ""
  2308. win.images = {}
  2309. UI_elements.roundrect(layer, win,
  2310. x+tileX,
  2311. y+win.scroll["script_shots"]+current_Y_shots,
  2312. 128,
  2313. 198,
  2314. 10,
  2315. button=do)
  2316. # A little surrounding thing.
  2317. UI_color.set(layer, win, "progress_background")
  2318. UI_elements.roundrect(layer, win,
  2319. x+tileX,
  2320. y+win.scroll["script_shots"]+current_Y_shots,
  2321. 128,
  2322. 198,
  2323. 10,
  2324. fill=False)
  2325. layer.stroke()
  2326. # Icon
  2327. UI_elements.image(layer, win,
  2328. "settings/themes/"+win.settings["Theme"]+"/icons/new_file.png",
  2329. x+tileX+44,
  2330. y+win.scroll["script_shots"]+current_Y_shots + 70,
  2331. 40, 40)
  2332. # preiew
  2333. UI_color.set(layer, win, "text_normal")
  2334. layer.set_font_size(12)
  2335. layer.move_to(x+tileX+64-len(searchis)*4,
  2336. y+win.scroll["script_shots"]+current_Y_shots+150)
  2337. layer.show_text(searchis)
  2338. tileX = tileX + 150
  2339. if tileX + 128 > width - 10:
  2340. tileX = 5
  2341. current_Y_shots = current_Y_shots + 205
  2342. # And a copy file here too
  2343. # COPY BLEND
  2344. def do():
  2345. # Now the copy function going to be a tiny bit harder
  2346. # since we will need to launch a dialog for you to
  2347. # select the file. I think you gonna look for files
  2348. # from this scene more often. So let's do that.
  2349. def after(win, var):
  2350. if var:
  2351. try:
  2352. os.makedirs(win.project+"/rnd"+win.cur)
  2353. except:
  2354. pass
  2355. # Then we copy the file.
  2356. oscalls.copy_file(
  2357. win,var,
  2358. "/rnd"+win.cur+"/",
  2359. searchis)
  2360. # And clear the search
  2361. win.text["shot_search"]["text"] = ""
  2362. win.images = {}
  2363. # Running the dialog starter
  2364. studio_dialogs.file_select(win, shotis+"_blends", after, force=True,
  2365. IMAGE=False, BLEND=True, VIDEO=False, FILE=False, CHR=False, VEH=False,
  2366. LOC=False, OBJ=False, RND=True, FOLDER=False, SEARCH=scene+" ")
  2367. UI_elements.roundrect(layer, win,
  2368. x+tileX,
  2369. y+win.scroll["script_shots"]+current_Y_shots,
  2370. 128,
  2371. 198,
  2372. 10,
  2373. button=do)
  2374. # A little surrounding thing.
  2375. UI_color.set(layer, win, "progress_background")
  2376. UI_elements.roundrect(layer, win,
  2377. x+tileX,
  2378. y+win.scroll["script_shots"]+current_Y_shots,
  2379. 128,
  2380. 198,
  2381. 10,
  2382. fill=False)
  2383. layer.stroke()
  2384. # Icon
  2385. UI_elements.image(layer, win,
  2386. "settings/themes/"+win.settings["Theme"]+"/icons/copy_file.png",
  2387. x+tileX+44,
  2388. y+win.scroll["script_shots"]+current_Y_shots + 70,
  2389. 40, 40)
  2390. # preiew
  2391. UI_color.set(layer, win, "text_normal")
  2392. layer.set_font_size(12)
  2393. layer.move_to(x+tileX+64-len(searchis)*4,
  2394. y+win.scroll["script_shots"]+current_Y_shots+150)
  2395. layer.show_text(searchis)
  2396. current_Y_shots = current_Y_shots + 205
  2397. else:
  2398. # If the shot is not selected I want to be able to select it
  2399. # even if I can't find it in the text of the script.
  2400. def do():
  2401. win.cur = surl
  2402. select_shot(win)
  2403. win.current["scroll_shot_to_in_script"] = True
  2404. win.current["shot_left_side_scroll_please_work_omg_wtf"] = True
  2405. UI_elements.roundrect(layer, win,
  2406. x,
  2407. y+win.scroll["script_shots"]+current_Y_shots,
  2408. width,
  2409. 50,
  2410. 10,
  2411. button=do,
  2412. fill=False)
  2413. layer.stroke()
  2414. # Then I want to show the user which state is the shot at the
  2415. # moment. By putting one of it's icons in the end of the of the
  2416. # shot choosing button.
  2417. fouricons = [
  2418. "storyboard",
  2419. "opengl",
  2420. "test_rnd",
  2421. "rendered"
  2422. ]
  2423. for icon in reversed(fouricons):
  2424. try:
  2425. files = sorted(os.listdir(win.project+"/rnd"+surl+"/"+icon))
  2426. except:
  2427. files = []
  2428. if files:
  2429. UI_elements.image(layer, win,
  2430. "settings/themes/"+win.settings["Theme"]+"/icons/"+icon+".png",
  2431. width-50, y+win.scroll["script_shots"] + current_Y_shots+5, 40, 40)
  2432. break
  2433. current_Y_shots = current_Y_shots + 60
  2434. # Button for [ That's All The Shots ].
  2435. # The idea is that untill this button is pressed the program will try
  2436. # to estimate how many shots there are still in the text, by looking at
  2437. # the amount of the text. If this button is pressed. The estimation will
  2438. # be ignored.
  2439. if "no_more_shots" not in win.story["scenes"][scene]:
  2440. win.story["scenes"][scene]["no_more_shots"] = False
  2441. icon = "checked"
  2442. if not win.story["scenes"][scene]["no_more_shots"]:
  2443. icon = "unchecked"
  2444. def do():
  2445. win.story["scenes"][scene]["no_more_shots"] = not win.story["scenes"][scene]["no_more_shots"]
  2446. story.save(win.project, win.story)
  2447. win.story = story.load(win.project)
  2448. UI_elements.roundrect(layer, win,
  2449. x,
  2450. y+win.scroll["script_shots"]+current_Y_shots,
  2451. width,
  2452. 40,
  2453. 10,
  2454. button=do,
  2455. icon=icon)
  2456. UI_color.set(layer, win, "text_normal")
  2457. layer.set_font_size(20)
  2458. layer.move_to(x+50,
  2459. y+win.scroll["script_shots"]+current_Y_shots+27 )
  2460. layer.show_text("No More Shots")
  2461. current_Y_shots = current_Y_shots + 60
  2462. # Scroll
  2463. UI_elements.scroll_area(layer, win, "script_shots",
  2464. x+0,
  2465. y+0,
  2466. width,
  2467. height,
  2468. current_Y_shots,
  2469. bar=True,
  2470. mmb=True)
  2471. return surface