stylizer.inc 48 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654
  1. <?php
  2. /**
  3. * @file
  4. * Create customized CSS and images from palettes created by user input.
  5. */
  6. /**
  7. * Fetch metadata on a specific style_base plugin.
  8. *
  9. * @param $content type
  10. * Name of a panel content type.
  11. *
  12. * @return
  13. * An array with information about the requested stylizer style base.
  14. */
  15. function ctools_get_style_base($style_base) {
  16. ctools_include('plugins');
  17. return ctools_get_plugins('stylizer', 'style_bases', $style_base);
  18. }
  19. /**
  20. * Fetch metadata for all style_base plugins.
  21. *
  22. * @return
  23. * An array of arrays with information about all available styleizer style bases.
  24. */
  25. function ctools_get_style_bases() {
  26. ctools_include('plugins');
  27. return ctools_get_plugins('stylizer', 'style_bases');
  28. }
  29. /**
  30. * Fetch metadata about all of the style base types that are available.
  31. */
  32. function ctools_get_style_base_types() {
  33. $types = array();
  34. foreach (module_implements('ctools_style_base_types') as $module) {
  35. $types[$module] = module_invoke($module, 'ctools_style_base_types');
  36. }
  37. return $types;
  38. }
  39. /**
  40. * Render the icon for a style base.
  41. */
  42. function ctools_stylizer_print_style_icon($plugin, $print_title = TRUE) {
  43. $file = $plugin['path'] . '/' . $plugin['icon'];
  44. $title = $print_title ? $plugin['title'] : '';
  45. return theme('ctools_style_icon', array('image' => theme('image', array('path' => $file)), 'title' => $title));
  46. }
  47. /**
  48. * Theme the style icon image
  49. */
  50. function theme_ctools_style_icon($vars) {
  51. $image = $vars['image'];
  52. ctools_add_css('stylizer');
  53. ctools_add_js('stylizer');
  54. $output = '<div class="ctools-style-icon">';
  55. $output .= $vars['image'];
  56. if ($vars['title']) {
  57. $output .= '<div class="caption">' . $vars['title'] . '</div>';
  58. }
  59. $output .= '</div>';
  60. return $output;
  61. }
  62. /**
  63. * Add the necessary CSS for a stylizer plugin to the page.
  64. *
  65. * This will check to see if the images directory and the cached CSS
  66. * exists and, if not, will regenerate everything needed.
  67. */
  68. function ctools_stylizer_add_css($plugin, $settings) {
  69. if (!file_exists(ctools_stylizer_get_image_path($plugin, $settings, FALSE))) {
  70. ctools_stylizer_build_style($plugin, $settings, TRUE);
  71. return;
  72. }
  73. ctools_include('css');
  74. $filename = ctools_css_retrieve(ctools_stylizer_get_css_id($plugin, $settings));
  75. if (!$filename) {
  76. ctools_stylizer_build_style($plugin, $settings, TRUE);
  77. }
  78. else {
  79. drupal_add_css($filename);
  80. }
  81. }
  82. /**
  83. * Build the files for a stylizer given the proper settings.
  84. */
  85. function ctools_stylizer_build_style($plugin, $settings, $add_css = FALSE) {
  86. $path = ctools_stylizer_get_image_path($plugin, $settings);
  87. if (!$path) {
  88. return;
  89. }
  90. $replacements = array();
  91. // Set up palette conversions
  92. foreach ($settings['palette'] as $key => $color) {
  93. $replacements['%' . $key ] = $color;
  94. }
  95. // Process image actions:
  96. if (!empty($plugin['actions'])) {
  97. $processor = new ctools_stylizer_image_processor;
  98. $processor->execute($path, $plugin, $settings);
  99. // @todo -- there needs to be an easier way to get at this.
  100. // dsm($processor->message_log);
  101. // Add filenames to our conversions.
  102. }
  103. // Convert and write the CSS file.
  104. $css = file_get_contents($plugin['path'] . '/' . $plugin['css']);
  105. // Replace %style keyword with our generated class name.
  106. // @todo We need one more unique identifier I think.
  107. $class = ctools_stylizer_get_css_class($plugin, $settings);
  108. $replacements['%style'] = '.' . $class;
  109. if (!empty($processor) && !empty($processor->paths)) {
  110. foreach ($processor->paths as $file => $image) {
  111. $replacements[$file] = file_create_url($image);
  112. }
  113. }
  114. if (!empty($plugin['build']) && function_exists($plugin['build'])) {
  115. $plugin['build']($plugin, $settings, $css, $replacements);
  116. }
  117. $css = strtr($css, $replacements);
  118. ctools_include('css');
  119. $filename = ctools_css_store(ctools_stylizer_get_css_id($plugin, $settings), $css, FALSE);
  120. if ($add_css) {
  121. drupal_add_css($filename);
  122. }
  123. }
  124. /**
  125. * Clean up no longer used files.
  126. *
  127. * To prevent excess clutter in the files directory, this should be called
  128. * whenever a style is going out of use. When being deleted, but also when
  129. * the palette is being changed.
  130. */
  131. function ctools_stylizer_cleanup_style($plugin, $settings) {
  132. ctools_include('css');
  133. $path = ctools_stylizer_get_image_path($plugin, $settings, FALSE);
  134. if ($path) {
  135. ctools_stylizer_recursive_delete($path);
  136. }
  137. ctools_css_clear(ctools_stylizer_get_css_id($plugin, $settings));
  138. }
  139. /**
  140. * Recursively delete all files and folders in the specified filepath, then
  141. * delete the containing folder.
  142. *
  143. * Note that this only deletes visible files with write permission.
  144. *
  145. * @param string $path
  146. * A filepath relative to file_directory_path.
  147. */
  148. function ctools_stylizer_recursive_delete($path) {
  149. if (empty($path)) {
  150. return;
  151. }
  152. $listing = $path . '/*';
  153. foreach (glob($listing) as $file) {
  154. if (is_file($file) === TRUE) {
  155. @unlink($file);
  156. }
  157. elseif (is_dir($file) === TRUE) {
  158. ctools_stylizer_recursive_delete($file);
  159. }
  160. }
  161. @rmdir($path);
  162. }
  163. /**
  164. * Get a safe name for the settings.
  165. *
  166. * This uses an md5 of the palette if the name is temporary so
  167. * that multiple temporary styles on the same page can coexist
  168. * safely.
  169. */
  170. function ctools_stylizer_get_settings_name($settings) {
  171. if ($settings['name'] != '_temporary') {
  172. return $settings['name'];
  173. }
  174. return $settings['name'] . '-' . md5(serialize($settings['palette']));
  175. }
  176. /**
  177. * Get the path where images will be stored for a given style plugin and settings.
  178. *
  179. * This function will make sure the path exists.
  180. */
  181. function ctools_stylizer_get_image_path($plugin, $settings, $check = TRUE) {
  182. $path = 'public://ctools/style/' . $settings['name'] . '/' . md5(serialize($settings['palette']));
  183. if (!file_prepare_directory($path, FILE_CREATE_DIRECTORY | FILE_MODIFY_PERMISSIONS)) {
  184. drupal_set_message(t('Unable to create CTools styles cache directory @path. Check the permissions on your files directory.', array('@path' => $path)), 'error');
  185. return;
  186. }
  187. return $path;
  188. }
  189. /**
  190. * Get the id used to cache CSS for a given style plugin and settings.
  191. */
  192. function ctools_stylizer_get_css_id($plugin, $settings) {
  193. return 'ctools-stylizer:' . $settings['name'] . ':' . md5(serialize($settings['palette']));
  194. }
  195. /**
  196. * Get the class to use for a stylizer plugin.
  197. */
  198. function ctools_stylizer_get_css_class($plugin, $settings) {
  199. ctools_include('cleanstring');
  200. return ctools_cleanstring($plugin['name'] . '-' . ctools_stylizer_get_settings_name($settings));
  201. }
  202. class ctools_stylizer_image_processor {
  203. var $workspace = NULL;
  204. var $name = NULL;
  205. var $workspaces = array();
  206. var $message_log = array();
  207. var $error_log = array();
  208. function execute($path, $plugin, $settings) {
  209. $this->path = $path;
  210. $this->plugin = $plugin;
  211. $this->settings = $settings;
  212. $this->palette = $settings['palette'];
  213. if (is_string($plugin['actions']) && function_exists($plugin['actions'])) {
  214. $actions = $plugin['actions']($plugin, $settings);
  215. }
  216. else if (is_array($plugin['actions'])) {
  217. $actions = $plugin['actions'];
  218. }
  219. if (!empty($actions) && is_array($actions)) {
  220. foreach ($plugin['actions'] as $action) {
  221. $command = 'command_' . array_shift($action);
  222. if (method_exists($this, $command)) {
  223. call_user_func_array(array($this, $command), $action);
  224. }
  225. }
  226. }
  227. // Clean up buffers.
  228. foreach ($this->workspaces as $name => $workspace) {
  229. imagedestroy($this->workspaces[$name]);
  230. }
  231. }
  232. function log($message, $type = 'normal') {
  233. $this->message_log[] = $message;
  234. if ($type == 'error') {
  235. $this->error_log[] = $message;
  236. }
  237. }
  238. function set_current_workspace($workspace) {
  239. $this->log("Set current workspace: $workspace");
  240. $this->workspace = &$this->workspaces[$workspace];
  241. $this->name = $workspace;
  242. }
  243. /**
  244. * Create a new workspace.
  245. */
  246. function command_new($name, $width, $height) {
  247. $this->log("New workspace: $name ($width x $height)");
  248. // Clean up if there was already a workspace there.
  249. if (isset($this->workspaces[$name])) {
  250. imagedestroy($this->workspaces[$name]);
  251. }
  252. $this->workspaces[$name] = imagecreatetruecolor($width, $height);
  253. $this->set_current_workspace($name);
  254. // Make sure the new workspace has a transparent color.
  255. // Turn off transparency blending (temporarily)
  256. imagealphablending($this->workspace, FALSE);
  257. // Create a new transparent color for image
  258. $color = imagecolorallocatealpha($this->workspace, 0, 0, 0, 127);
  259. // Completely fill the background of the new image with allocated color.
  260. imagefill($this->workspace, 0, 0, $color);
  261. // Restore transparency blending
  262. imagesavealpha($this->workspace, TRUE);
  263. }
  264. /**
  265. * Create a new workspace a file.
  266. *
  267. * This will make the new workspace the current workspace.
  268. */
  269. function command_load($name, $file) {
  270. $this->log("New workspace: $name (from $file)");
  271. if (!file_exists($file)) {
  272. // Try it relative to the plugin
  273. $file = $this->plugin['path'] . '/' . $file;
  274. if (!file_exists($file)) {
  275. $this->log("Unable to open $file");
  276. return;
  277. }
  278. }
  279. // Clean up if there was already a workspace there.
  280. if (isset($this->workspaces[$name])) {
  281. imagedestroy($this->workspaces[$name]);
  282. }
  283. $this->workspaces[$name] = imagecreatefrompng($file);
  284. $this->set_current_workspace($name);
  285. }
  286. /**
  287. * Create a new workspace using the properties of an existing workspace
  288. */
  289. function command_new_from($name, $workspace) {
  290. $this->log("New workspace: $name from existing $workspace");
  291. if (empty($this->workspaces[$workspace])) {
  292. $this->log("Workspace $name does not exist.", 'error');
  293. return;
  294. }
  295. // Clean up if there was already a workspace there.
  296. if (isset($this->workspaces[$name])) {
  297. imagedestroy($this->workspaces[$name]);
  298. }
  299. $this->workspaces[$name] = $this->new_image($this->workspace[$workspace]);
  300. $this->set_current_workspace($name);
  301. }
  302. /**
  303. * Set the current workspace.
  304. */
  305. function command_workspace($name) {
  306. $this->log("Set workspace: $name");
  307. if (empty($this->workspaces[$name])) {
  308. $this->log("Workspace $name does not exist.", 'error');
  309. return;
  310. }
  311. $this->set_current_workspace($name);
  312. }
  313. /**
  314. * Copy the contents of one workspace into the current workspace.
  315. */
  316. function command_merge_from($workspace, $x = 0, $y = 0) {
  317. $this->log("Merge from: $workspace ($x, $y)");
  318. if (empty($this->workspaces[$workspace])) {
  319. $this->log("Workspace $name does not exist.", 'error');
  320. return;
  321. }
  322. $this->merge($this->workspaces[$workspace], $this->workspace, $x, $y);
  323. }
  324. function command_merge_to($workspace, $x = 0, $y = 0) {
  325. $this->log("Merge to: $workspace ($x, $y)");
  326. if (empty($this->workspaces[$workspace])) {
  327. $this->log("Workspace $name does not exist.", 'error');
  328. return;
  329. }
  330. $this->merge($this->workspace, $this->workspaces[$workspace], $x, $y);
  331. $this->set_current_workspace($workspace);
  332. }
  333. /**
  334. * Blend an image into the current workspace.
  335. */
  336. function command_merge_from_file($file, $x = 0, $y = 0) {
  337. $this->log("Merge from file: $file ($x, $y)");
  338. if (!file_exists($file)) {
  339. // Try it relative to the plugin
  340. $file = $this->plugin['path'] . '/' . $file;
  341. if (!file_exists($file)) {
  342. $this->log("Unable to open $file");
  343. return;
  344. }
  345. }
  346. $source = imagecreatefrompng($file);
  347. $this->merge($source, $this->workspace, $x, $y);
  348. imagedestroy($source);
  349. }
  350. function command_fill($color, $x, $y, $width, $height) {
  351. $this->log("Fill: $color ($x, $y, $width, $height)");
  352. imagefilledrectangle($this->workspace, $x, $y, $x + $width, $y + $height, _color_gd($this->workspace, $this->palette[$color]));
  353. }
  354. function command_gradient($from, $to, $x, $y, $width, $height, $direction = 'down') {
  355. $this->log("Gradient: $from to $to ($x, $y, $width, $height) $direction");
  356. if ($direction == 'down') {
  357. for ($i = 0; $i < $height; ++$i) {
  358. $color = _color_blend($this->workspace, $this->palette[$from], $this->palette[$to], $i / ($height - 1));
  359. imagefilledrectangle($this->workspace, $x, $y + $i, $x + $width, $y + $i + 1, $color);
  360. }
  361. }
  362. else {
  363. for ($i = 0; $i < $width; ++$i) {
  364. $color = _color_blend($this->workspace, $this->palette[$from], $this->palette[$to], $i / ($width - 1));
  365. imagefilledrectangle($this->workspace, $x + $i, $y, $x + $i + 1, $y + $height, $color);
  366. }
  367. }
  368. }
  369. /**
  370. * Colorize the current workspace with the given location.
  371. *
  372. * This uses simple color blending to colorize the image.
  373. *
  374. * @todo it is possible that this colorize could allow different methods for
  375. * determining how to blend colors?
  376. */
  377. function command_colorize($color, $x = NULL, $y = NULL, $width = NULL, $height = NULL) {
  378. if (!isset($x)) {
  379. $whole_image = TRUE;
  380. $x = $y = 0;
  381. $width = imagesx($this->workspace);
  382. $height = imagesy($this->workspace);
  383. }
  384. $this->log("Colorize: $color ($x, $y, $width, $height)");
  385. $c = _color_unpack($this->palette[$color]);
  386. imagealphablending($this->workspace, FALSE);
  387. imagesavealpha($this->workspace, TRUE);
  388. // If PHP 5 use the nice imagefilter which is faster.
  389. if (!empty($whole_image) && version_compare(phpversion(), '5.2.5', '>=') && function_exists('imagefilter')) {
  390. imagefilter($this->workspace, IMG_FILTER_COLORIZE, $c[0], $c[1], $c[2]);
  391. }
  392. else {
  393. // Otherwise we can do it the brute force way.
  394. for ($j = 0; $j < $height; $j++) {
  395. for ($i = 0; $i < $width; $i++) {
  396. $current = imagecolorsforindex($this->workspace, imagecolorat($this->workspace, $i, $j));
  397. $new_index = imagecolorallocatealpha($this->workspace, $c[0], $c[1], $c[2], $current['alpha']);
  398. imagesetpixel($this->workspace, $i, $j, $new_index);
  399. }
  400. }
  401. }
  402. }
  403. /**
  404. * Colorize the current workspace with the given location.
  405. *
  406. * This uses a color replacement algorithm that retains luminosity but
  407. * turns replaces all color with the specified color.
  408. */
  409. function command_hue($color, $x = NULL, $y = NULL, $width = NULL, $height = NULL) {
  410. if (!isset($x)) {
  411. $whole_image = TRUE;
  412. $x = $y = 0;
  413. $width = imagesx($this->workspace);
  414. $height = imagesy($this->workspace);
  415. }
  416. $this->log("Hue: $color ($x, $y, $width, $height)");
  417. list($red, $green, $blue) = _color_unpack($this->palette[$color]);
  418. // We will create a monochromatic palette based on the input color
  419. // which will go from black to white.
  420. // Input color luminosity: this is equivalent to the position of the
  421. // input color in the monochromatic palette
  422. $luminosity_input = round(255 * ($red + $green + $blue) / 765); // 765 = 255 * 3
  423. // We fill the palette entry with the input color at itscorresponding position
  424. $palette[$luminosity_input]['red'] = $red;
  425. $palette[$luminosity_input]['green'] = $green;
  426. $palette[$luminosity_input]['blue'] = $blue;
  427. // Now we complete the palette, first we'll do it to the black, and then to
  428. // the white.
  429. // From input to black
  430. $steps_to_black = $luminosity_input;
  431. // The step size for each component
  432. if ($steps_to_black) {
  433. $step_size_red = $red / $steps_to_black;
  434. $step_size_green = $green / $steps_to_black;
  435. $step_size_blue = $blue / $steps_to_black;
  436. for ($i = $steps_to_black; $i >= 0; $i--) {
  437. $palette[$steps_to_black-$i]['red'] = $red - round($step_size_red * $i);
  438. $palette[$steps_to_black-$i]['green'] = $green - round($step_size_green * $i);
  439. $palette[$steps_to_black-$i]['blue'] = $blue - round($step_size_blue * $i);
  440. }
  441. }
  442. // From input to white
  443. $steps_to_white = 255 - $luminosity_input;
  444. if ($steps_to_white) {
  445. $step_size_red = (255 - $red) / $steps_to_white;
  446. $step_size_green = (255 - $green) / $steps_to_white;
  447. $step_size_blue = (255 - $blue) / $steps_to_white;
  448. }
  449. else {
  450. $step_size_red=$step_size_green=$step_size_blue=0;
  451. }
  452. // The step size for each component
  453. for ($i = ($luminosity_input + 1); $i <= 255; $i++) {
  454. $palette[$i]['red'] = $red + round($step_size_red * ($i - $luminosity_input));
  455. $palette[$i]['green'] = $green + round($step_size_green * ($i - $luminosity_input));
  456. $palette[$i]['blue']= $blue + round($step_size_blue * ($i - $luminosity_input));
  457. }
  458. // Go over the specified area of the image and update the colors.
  459. for ($j = $x; $j < $height; $j++) {
  460. for ($i = $y; $i < $width; $i++) {
  461. $color = imagecolorsforindex($this->workspace, imagecolorat($this->workspace, $i, $j));
  462. $luminosity = round(255 * ($color['red'] + $color['green'] + $color['blue']) / 765);
  463. $new_color = imagecolorallocatealpha($this->workspace, $palette[$luminosity]['red'], $palette[$luminosity]['green'], $palette[$luminosity]['blue'], $color['alpha']);
  464. imagesetpixel($this->workspace, $i, $j, $new_color);
  465. }
  466. }
  467. }
  468. /**
  469. * Take a slice out of the current workspace and save it as an image.
  470. */
  471. function command_slice($file, $x = NULL, $y = NULL, $width = NULL, $height = NULL) {
  472. if (!isset($x)) {
  473. $x = $y = 0;
  474. $width = imagesx($this->workspace);
  475. $height = imagesy($this->workspace);
  476. }
  477. $this->log("Slice: $file ($x, $y, $width, $height)");
  478. $base = basename($file);
  479. $image = $this->path . '/' . $base;
  480. $slice = $this->new_image($this->workspace, $width, $height);
  481. imagecopy($slice, $this->workspace, 0, 0, $x, $y, $width, $height);
  482. // Make sure alphas are saved:
  483. imagealphablending($slice, FALSE);
  484. imagesavealpha($slice, TRUE);
  485. // Save image.
  486. $temp_name = drupal_tempnam('temporary://', 'file');
  487. imagepng($slice, drupal_realpath($temp_name));
  488. file_unmanaged_move($temp_name, $image);
  489. imagedestroy($slice);
  490. // Set standard file permissions for webserver-generated files
  491. @chmod(realpath($image), 0664);
  492. $this->paths[$file] = $image;
  493. }
  494. /**
  495. * Prepare a new image for being copied or worked on, preserving transparency.
  496. */
  497. function &new_image(&$source, $width = NULL, $height = NULL) {
  498. if (!isset($width)) {
  499. $width = imagesx($source);
  500. }
  501. if (!isset($height)) {
  502. $height = imagesy($source);
  503. }
  504. $target = imagecreatetruecolor($width, $height);
  505. imagealphablending($target, FALSE);
  506. imagesavealpha($target, TRUE);
  507. $transparency_index = imagecolortransparent($source);
  508. // If we have a specific transparent color
  509. if ($transparency_index >= 0) {
  510. // Get the original image's transparent color's RGB values
  511. $transparent_color = imagecolorsforindex($source, $transparency_index);
  512. // Allocate the same color in the new image resource
  513. $transparency_index = imagecolorallocate($target, $transparent_color['red'], $transparent_color['green'], $transparent_color['blue']);
  514. // Completely fill the background of the new image with allocated color.
  515. imagefill($target, 0, 0, $transparency_index);
  516. // Set the background color for new image to transparent
  517. imagecolortransparent($target, $transparency_index);
  518. }
  519. // Always make a transparent background color for PNGs that don't have one allocated already
  520. else {
  521. // Create a new transparent color for image
  522. $color = imagecolorallocatealpha($target, 0, 0, 0, 127);
  523. // Completely fill the background of the new image with allocated color.
  524. imagefill($target, 0, 0, $color);
  525. }
  526. return $target;
  527. }
  528. /**
  529. * Merge two images together, preserving alpha transparency.
  530. */
  531. function merge(&$from, &$to, $x, $y) {
  532. // Blend over template.
  533. $width = imagesx($from);
  534. $height = imagesy($from);
  535. // Re-enable alpha blending to make sure transparency merges.
  536. imagealphablending($to, TRUE);
  537. imagecopy($to, $from, $x, $y, 0, 0, $width, $height);
  538. imagealphablending($to, FALSE);
  539. }
  540. }
  541. /**
  542. * Get the cached changes to a given task handler.
  543. */
  544. function ctools_stylizer_get_settings_cache($name) {
  545. ctools_include('object-cache');
  546. return ctools_object_cache_get('ctools_stylizer_settings', $name);
  547. }
  548. /**
  549. * Store changes to a task handler in the object cache.
  550. */
  551. function ctools_stylizer_set_settings_cache($name, $settings) {
  552. ctools_include('object-cache');
  553. ctools_object_cache_set('ctools_stylizer_settings', $name, $settings);
  554. }
  555. /**
  556. * Remove an item from the object cache.
  557. */
  558. function ctools_stylizer_clear_settings_cache($name) {
  559. ctools_include('object-cache');
  560. ctools_object_cache_clear('ctools_stylizer_settings', $name);
  561. }
  562. /**
  563. * Add a new style of the specified type.
  564. */
  565. function ctools_stylizer_edit_style(&$info, $js, $step = NULL) {
  566. $name = '::new';
  567. $form_info = array(
  568. 'id' => 'ctools_stylizer_edit_style',
  569. 'path' => $info['path'],
  570. 'show trail' => TRUE,
  571. 'show back' => TRUE,
  572. 'show return' => FALSE,
  573. 'next callback' => 'ctools_stylizer_edit_style_next',
  574. 'finish callback' => 'ctools_stylizer_edit_style_finish',
  575. 'return callback' => 'ctools_stylizer_edit_style_finish',
  576. 'cancel callback' => 'ctools_stylizer_edit_style_cancel',
  577. 'forms' => array(
  578. 'choose' => array(
  579. 'form id' => 'ctools_stylizer_edit_style_form_choose',
  580. ),
  581. ),
  582. );
  583. if (empty($info['settings'])) {
  584. $form_info['order'] = array(
  585. 'choose' => t('Select base style'),
  586. );
  587. if (empty($step)) {
  588. $step = 'choose';
  589. }
  590. if ($step != 'choose') {
  591. $cache = ctools_stylizer_get_settings_cache($name);
  592. if (!$cache) {
  593. $output = t('Missing settings cache.');
  594. if ($js) {
  595. return ctools_modal_form_render($form_state, $output);
  596. }
  597. else {
  598. return $output;
  599. }
  600. }
  601. if (!empty($cache['owner settings'])) {
  602. $info['owner settings'] = $cache['owner settings'];
  603. }
  604. $settings = $cache['settings'];
  605. }
  606. else {
  607. $settings = array(
  608. 'name' => '_temporary',
  609. 'style_base' => NULL,
  610. 'palette' => array(),
  611. );
  612. ctools_stylizer_clear_settings_cache($name);
  613. }
  614. $op = 'add';
  615. }
  616. else {
  617. $cache = ctools_stylizer_get_settings_cache($info['settings']['name']);
  618. if (!empty($cache)) {
  619. if (!empty($cache['owner settings'])) {
  620. $info['owner settings'] = $cache['owner settings'];
  621. }
  622. $settings = $cache['settings'];
  623. }
  624. else {
  625. $settings = $info['settings'];
  626. }
  627. $op = 'edit';
  628. }
  629. if (!empty($info['op'])) {
  630. // Allow this to override. Necessary to allow cloning properly.
  631. $op = $info['op'];
  632. }
  633. $plugin = NULL;
  634. if (!empty($settings['style_base'])) {
  635. $plugin = ctools_get_style_base($settings['style_base']);
  636. $info['type'] = $plugin['type'];
  637. ctools_stylizer_add_plugin_forms($form_info, $plugin, $op);
  638. }
  639. else {
  640. // This is here so the 'finish' button does not show up, and because
  641. // we don't have the selected style we don't know what the next form(s)
  642. // will be.
  643. $form_info['order']['next'] = t('Configure style');
  644. }
  645. if (count($form_info['order']) < 2 || $step == 'choose') {
  646. $form_info['show trail'] = FALSE;
  647. }
  648. $form_state = array(
  649. 'module' => $info['module'],
  650. 'type' => $info['type'],
  651. 'owner info' => &$info,
  652. 'base_style_plugin' => $plugin,
  653. 'name' => $name,
  654. 'step' => $step,
  655. 'settings' => $settings,
  656. 'ajax' => $js,
  657. 'op' => $op,
  658. );
  659. if (!empty($info['modal'])) {
  660. $form_state['modal'] = TRUE;
  661. $form_state['title'] = $info['modal'];
  662. $form_state['modal return'] = TRUE;
  663. }
  664. ctools_include('wizard');
  665. $output = ctools_wizard_multistep_form($form_info, $step, $form_state);
  666. if (!empty($form_state['complete'])) {
  667. $info['complete'] = TRUE;
  668. $info['settings'] = $form_state['settings'];
  669. }
  670. if ($js && !$output && !empty($form_state['clicked_button']['#next'])) {
  671. // We have to do a separate redirect here because the formula that adds
  672. // stuff to the wizard after being chosen hasn't happened. The wizard
  673. // tried to go to the next step which did not exist.
  674. return ctools_stylizer_edit_style($info, $js, $form_state['clicked_button']['#next']);
  675. }
  676. if ($js) {
  677. return ctools_modal_form_render($form_state, $output);
  678. }
  679. else {
  680. return $output;
  681. }
  682. }
  683. /**
  684. * Add wizard forms specific to a style base plugin.
  685. *
  686. * The plugin can store forms either as a simple 'edit form'
  687. * => 'form callback' or if it needs the more complicated wizard
  688. * functionality, it can set 'forms' and 'order' with values suitable
  689. * for the wizard $form_info array.
  690. *
  691. * @param &$form_info
  692. * The form info to modify.
  693. * @param $plugin
  694. * The plugin to use.
  695. * @param $op
  696. * Either 'add' or 'edit' so we can get the right forms.
  697. */
  698. function ctools_stylizer_add_plugin_forms(&$form_info, $plugin, $op) {
  699. if (empty($plugin['forms'])) {
  700. if ($op == 'add' && isset($plugin['add form'])) {
  701. $id = $plugin['add form'];
  702. }
  703. else if (isset($plugin['edit form'])) {
  704. $id = $plugin['edit form'];
  705. }
  706. else {
  707. $id = 'ctools_stylizer_edit_style_form_default';
  708. }
  709. $form_info['forms']['settings'] = array(
  710. 'form id' => $id,
  711. );
  712. $form_info['order']['settings'] = t('Settings');
  713. }
  714. else {
  715. $form_info['forms'] += $plugin['forms'];
  716. $form_info['order'] += $plugin['order'];
  717. }
  718. }
  719. /**
  720. * Callback generated when the add style process is finished.
  721. */
  722. function ctools_stylizer_edit_style_finish(&$form_state) {
  723. $form_state['complete'] = TRUE;
  724. ctools_stylizer_clear_settings_cache($form_state['name']);
  725. if (isset($form_state['settings']['old_settings'])) {
  726. unset($form_state['settings']['old_settings']);
  727. }
  728. }
  729. /**
  730. * Callback generated when the 'next' button is clicked.
  731. */
  732. function ctools_stylizer_edit_style_next(&$form_state) {
  733. $form_state['form_info']['path'] = str_replace('%name', $form_state['name'], $form_state['form_info']['path']);
  734. $form_state['redirect'] = ctools_wizard_get_path($form_state['form_info'], $form_state['clicked_button']['#next']);
  735. // Update the cache with changes.
  736. $cache = array('settings' => $form_state['settings']);
  737. if (!empty($form_state['owner info']['owner settings'])) {
  738. $cache['owner settings'] = $form_state['owner info']['owner settings'];
  739. }
  740. ctools_stylizer_set_settings_cache($form_state['name'], $cache);
  741. }
  742. /**
  743. * Callback generated when the 'cancel' button is clicked.
  744. *
  745. * We might have some temporary data lying around. We must remove it.
  746. */
  747. function ctools_stylizer_edit_style_cancel(&$form_state) {
  748. if (!empty($form_state['name'])) {
  749. ctools_stylizer_clear_settings_cache($form_state['name']);
  750. }
  751. }
  752. /**
  753. * Choose which plugin to use to create a new style.
  754. */
  755. function ctools_stylizer_edit_style_form_choose($form, &$form_state) {
  756. $plugins = ctools_get_style_bases();
  757. $options = array();
  758. $categories = array();
  759. foreach ($plugins as $name => $plugin) {
  760. if ($form_state['module'] == $plugin['module'] && $form_state['type'] == $plugin['type']) {
  761. $categories[$plugin['category']] = $plugin['category'];
  762. $unsorted_options[$plugin['category']][$name] = ctools_stylizer_print_style_icon($plugin, TRUE);
  763. }
  764. }
  765. asort($categories);
  766. foreach ($categories as $category) {
  767. $options[$category] = $unsorted_options[$category];
  768. }
  769. $form['style_base'] = array(
  770. '#prefix' => '<div class="ctools-style-icons clearfix">',
  771. '#suffix' => '</div>',
  772. );
  773. ctools_include('cleanstring');
  774. foreach ($options as $category => $radios) {
  775. $cat = ctools_cleanstring($category);
  776. $form['style_base'][$cat] = array(
  777. '#prefix' => '<div class="ctools-style-category clearfix"><label>' . $category . '</label>',
  778. '#suffix' => '</div>',
  779. );
  780. foreach ($radios as $key => $choice) {
  781. // Generate the parents as the autogenerator does, so we will have a
  782. // unique id for each radio button.
  783. $form['style_base'][$cat][$key] = array(
  784. '#type' => 'radio',
  785. '#title' => $choice,
  786. '#parents' => array('style_base'),
  787. '#id' => drupal_clean_css_identifier('edit-style-base-' . $key),
  788. '#return_value' => check_plain($key),
  789. );
  790. }
  791. }
  792. return $form;
  793. }
  794. function ctools_stylizer_edit_style_form_choose_submit($form, &$form_state) {
  795. $form_state['settings']['style_base'] = $form_state['values']['style_base'];
  796. // The 'next' form will show up as 'next' but that's not accurate now that
  797. // we have a style. Figure out what next really is and update.
  798. $plugin = ctools_get_style_base($form_state['settings']['style_base']);
  799. if (empty($plugin['forms'])) {
  800. $form_state['clicked_button']['#next'] = 'settings';
  801. }
  802. else {
  803. $forms = array_keys($form_info['forms']);
  804. $form_state['clicked_button']['#next'] = array_shift($forms);
  805. }
  806. // Fill in the defaults for the settings.
  807. if (!empty($plugin['defaults'])) {
  808. // @todo allow a callback
  809. $form_state['settings'] += $plugin['defaults'];
  810. }
  811. return $form;
  812. }
  813. /**
  814. * The default stylizer style editing form.
  815. *
  816. * Even when not using this, styles should call through to this form in
  817. * their own edit forms.
  818. */
  819. function ctools_stylizer_edit_style_form_default($form, &$form_state) {
  820. ctools_add_js('stylizer');
  821. ctools_add_css('stylizer');
  822. drupal_add_library('system', 'farbtastic');
  823. $plugin = &$form_state['base_style_plugin'];
  824. $settings = &$form_state['settings'];
  825. $form['top box'] = array(
  826. '#prefix' => '<div id="ctools-stylizer-top-box" class="clearfix">',
  827. '#suffix' => '</div>',
  828. );
  829. $form['top box']['left'] = array(
  830. '#prefix' => '<div id="ctools-stylizer-left-box">',
  831. '#suffix' => '</div>',
  832. );
  833. $form['top box']['preview'] = array(
  834. // We have a copy of the $form_state on $form because form theme functions
  835. // do not get $form_state.
  836. '#theme' => 'ctools_stylizer_preview_form',
  837. '#form_state' => &$form_state,
  838. );
  839. $form['top box']['preview']['submit'] = array(
  840. '#type' => 'submit',
  841. '#value' => t('Preview'),
  842. );
  843. if (!empty($plugin['palette'])) {
  844. $form['top box']['color'] = array(
  845. '#type' => 'fieldset',
  846. '#title' => t('Color scheme'),
  847. '#attributes' => array('id' => 'ctools_stylizer_color_scheme_form', 'class' => array('ctools-stylizer-color-edit')),
  848. '#theme' => 'ctools_stylizer_color_scheme_form',
  849. );
  850. $form['top box']['color']['palette']['#tree'] = TRUE;
  851. foreach ($plugin['palette'] as $key => $color) {
  852. if (empty($settings['palette'][$key])) {
  853. $settings['palette'][$key] = $color['default_value'];
  854. }
  855. $form['top box']['color']['palette'][$key] = array(
  856. '#type' => 'textfield',
  857. '#title' => $color['label'],
  858. '#default_value' => $settings['palette'][$key],
  859. '#size' => 8,
  860. );
  861. }
  862. }
  863. if (!empty($plugin['settings form']) && function_exists($plugin['settings form'])) {
  864. $plugin['settings form']($form, $form_state);
  865. }
  866. if (!empty($form_state['owner info']['owner form']) && function_exists($form_state['owner info']['owner form'])) {
  867. $form_state['owner info']['owner form']($form, $form_state);
  868. }
  869. return $form;
  870. }
  871. /**
  872. * Theme the stylizer color scheme form.
  873. */
  874. function theme_ctools_stylizer_color_scheme_form($vars) {
  875. $form = &$vars['form'];
  876. $output = '';
  877. // Wrapper
  878. $output .= '<div class="color-form clearfix">';
  879. // Color schemes
  880. // $output .= drupal_render($form['scheme']);
  881. // Palette
  882. $output .= '<div id="palette" class="clearfix">';
  883. foreach (element_children($form['palette']) as $name) {
  884. $output .= render($form['palette'][$name]);
  885. }
  886. $output .= '</div>'; // palette
  887. $output .= '</div>'; // color form
  888. return $output;
  889. }
  890. /**
  891. * Theme the stylizer preview form.
  892. */
  893. function theme_ctools_stylizer_preview_form($vars) {
  894. $form = &$vars['form'];
  895. $plugin = $form['#form_state']['base_style_plugin'];
  896. $settings = $form['#form_state']['settings'];
  897. if (!empty($form['#form_state']['settings']['old_settings'])) {
  898. ctools_stylizer_cleanup_style($plugin, $form['#form_state']['settings']['old_settings']);
  899. }
  900. $preview = '';
  901. if (!empty($plugin['preview'])) {
  902. $preview = $plugin['preview'];
  903. }
  904. else {
  905. $base_types = ctools_get_style_base_types();
  906. if (!empty($base_types[$plugin['module']][$plugin['type']]['preview'])) {
  907. $preview = $base_types[$plugin['module']][$plugin['type']]['preview'];
  908. }
  909. }
  910. if (!empty($preview) && function_exists($preview)) {
  911. $output = '<fieldset id="preview"><legend>' . t('Preview') . '</legend>';
  912. $output .= $preview($plugin, $settings);
  913. $output .= drupal_render_children($form);
  914. $output .= '</fieldset>';
  915. return $output;
  916. }
  917. }
  918. function ctools_stylizer_edit_style_form_default_validate($form, &$form_state) {
  919. if (!empty($form_state['owner info']['owner form validate']) && function_exists($form_state['owner info']['owner form validate'])) {
  920. $form_state['owner info']['owner form validate']($form, $form_state);
  921. }
  922. if (!empty($form_state['base_style_plugin']['settings form validate']) && function_exists($form_state['base_style_plugin']['settings form validate'])) {
  923. $form_state['base_style_plugin']['settings form validate']($form, $form_state);
  924. }
  925. }
  926. function ctools_stylizer_edit_style_form_default_submit($form, &$form_state) {
  927. // Store old settings for the purposes of cleaning up.
  928. $form_state['settings']['old_settings'] = $form_state['settings'];
  929. $form_state['settings']['palette'] = $form_state['values']['palette'];
  930. if (!empty($form_state['owner info']['owner form submit']) && function_exists($form_state['owner info']['owner form submit'])) {
  931. $form_state['owner info']['owner form submit']($form, $form_state);
  932. }
  933. if (!empty($form_state['base_style_plugin']['settings form submit']) && function_exists($form_state['base_style_plugin']['settings form submit'])) {
  934. $form_state['base_style_plugin']['settings form submit']($form, $form_state);
  935. }
  936. if ($form_state['clicked_button']['#value'] == t('Preview')) {
  937. $form_state['rerender'] = TRUE;
  938. // Update the cache with changes.
  939. if (!empty($form_state['name'])) {
  940. $cache = array('settings' => $form_state['settings']);
  941. if (!empty($form_state['owner info']['owner settings'])) {
  942. $cache['owner settings'] = $form_state['owner info']['owner settings'];
  943. }
  944. ctools_stylizer_set_settings_cache($form_state['name'], $cache);
  945. }
  946. }
  947. }
  948. // --------------------------------------------------------------------------
  949. // CSS forms and tools that plugins can use.
  950. /**
  951. * Font selector form
  952. */
  953. function ctools_stylizer_font_selector_form(&$form, &$form_state, $label, $settings) {
  954. // Family
  955. $form['#prefix'] = '<div class="ctools-stylizer-spacing-form clearfix">';
  956. $form['#type'] = 'fieldset';
  957. $form['#title'] = $label;
  958. $form['#suffix'] = '</div>';
  959. $form['#tree'] = TRUE;
  960. $form['font'] = array(
  961. '#title' => t('Font family'),
  962. '#type' => 'select',
  963. '#default_value' => isset($settings['font']) ? $settings['font'] : '',
  964. '#options' => array(
  965. '' => '',
  966. 'Arial, Helvetica, sans-serif' => t('Arial, Helvetica, sans-serif'),
  967. 'Times New Roman, Times, serif' => t('Times New Roman, Times, serif'),
  968. 'Courier New, Courier, monospace' => t('Courier New, Courier, monospace'),
  969. 'Georgia, Times New Roman, Times, serif' => t('Georgia, Times New Roman, Times, serif'),
  970. 'Verdana, Arial, Helvetica, sans-serif' => t('Verdana, Arial, Helvetica, sans-serif'),
  971. 'Geneva, Arial, Helvetica, sans-serif' => t('Geneva, Arial, Helvetica, sans-serif'),
  972. 'Trebuchet MS, Trebuchet, Verdana, sans-serif' => t('Trebuchet MS, Trebuchet, Verdana, sans-serif'),
  973. ),
  974. );
  975. // size
  976. $form['size'] = array(
  977. '#title' => t('Size'),
  978. '#type' => 'select',
  979. '#default_value' => isset($settings['size']) ? $settings['size'] : '',
  980. '#options' => array(
  981. '' => '',
  982. 'xx-small' => t('XX-Small'),
  983. 'x-small' => t('X-Small'),
  984. 'small' => t('Small'),
  985. 'medium' => t('Medium'),
  986. 'large' => t('Large'),
  987. 'x-large' => t('X-Large'),
  988. 'xx-large' => t('XX-Large'),
  989. ),
  990. );
  991. // letter spacing
  992. $form['letter_spacing'] = array(
  993. '#title' => t('Letter spacing'),
  994. '#type' => 'select',
  995. '#default_value' => isset($settings['letter_spacing']) ? $settings['letter_spacing'] : '',
  996. '#options' => array(
  997. '' => '',
  998. "-10px" => '10px',
  999. "-9px" => '9px',
  1000. "-8px" => '8px',
  1001. "-7px" => '7px',
  1002. "-6px" => '6px',
  1003. "-5px" => '5px',
  1004. "-4px" => '4px',
  1005. "-3px" => '3px',
  1006. "-2px" => '2px',
  1007. "-1px" => '1px',
  1008. "0" => '0',
  1009. "1px" => '1px',
  1010. "2px" => '2px',
  1011. "3px" => '3px',
  1012. "4px" => '4px',
  1013. "5px" => '5px',
  1014. "6px" => '6px',
  1015. "7px" => '7px',
  1016. "8px" => '8px',
  1017. "9px" => '9px',
  1018. "10px" => '10px',
  1019. "11px" => '11px',
  1020. "12px" => '12px',
  1021. "13px" => '13px',
  1022. "14px" => '14px',
  1023. "15px" => '15px',
  1024. "16px" => '16px',
  1025. "17px" => '17px',
  1026. "18px" => '18px',
  1027. "19px" => '19px',
  1028. "20px" => '20px',
  1029. "21px" => '21px',
  1030. "22px" => '22px',
  1031. "23px" => '23px',
  1032. "24px" => '24px',
  1033. "25px" => '25px',
  1034. "26px" => '26px',
  1035. "27px" => '27px',
  1036. "28px" => '28px',
  1037. "29px" => '29px',
  1038. "30px" => '30px',
  1039. "31px" => '31px',
  1040. "32px" => '32px',
  1041. "33px" => '33px',
  1042. "34px" => '34px',
  1043. "35px" => '35px',
  1044. "36px" => '36px',
  1045. "37px" => '37px',
  1046. "38px" => '38px',
  1047. "39px" => '39px',
  1048. "40px" => '40px',
  1049. "41px" => '41px',
  1050. "42px" => '42px',
  1051. "43px" => '43px',
  1052. "44px" => '44px',
  1053. "45px" => '45px',
  1054. "46px" => '46px',
  1055. "47px" => '47px',
  1056. "48px" => '48px',
  1057. "49px" => '49px',
  1058. "50px" => '50px',
  1059. ),
  1060. );
  1061. // word space
  1062. $form['word_spacing'] = array(
  1063. '#title' => t('Word spacing'),
  1064. '#type' => 'select',
  1065. '#default_value' => isset($settings['word_spacing']) ? $settings['word_spacing'] : '',
  1066. '#options' => array(
  1067. '' => '',
  1068. "-1em" => '-1em',
  1069. "-0.95em" => '-0.95em',
  1070. "-0.9em" => '-0.9em',
  1071. "-0.85em" => '-0.85em',
  1072. "-0.8em" => '-0.8em',
  1073. "-0.75em" => '-0.75em',
  1074. "-0.7em" => '-0.7em',
  1075. "-0.65em" => '-0.65em',
  1076. "-0.6em" => '-0.6em',
  1077. "-0.55em" => '-0.55em',
  1078. "-0.5em" => '-0.5em',
  1079. "-0.45em" => '-0.45em',
  1080. "-0.4em" => '-0.4em',
  1081. "-0.35em" => '-0.35em',
  1082. "-0.3em" => '-0.3em',
  1083. "-0.25em" => '-0.25em',
  1084. "-0.2em" => '-0.2em',
  1085. "-0.15em" => '-0.15em',
  1086. "-0.1em" => '-0.1em',
  1087. "-0.05em" => '-0.05em',
  1088. "normal" => 'normal',
  1089. "0.05em" => '0.05em',
  1090. "0.1em" => '0.1em',
  1091. "0.15em" => '0.15em',
  1092. "0.2em" => '0.2em',
  1093. "0.25em" => '0.25em',
  1094. "0.3em" => '0.3em',
  1095. "0.35em" => '0.35em',
  1096. "0.4em" => '0.4em',
  1097. "0.45em" => '0.45em',
  1098. "0.5em" => '0.5em',
  1099. "0.55em" => '0.55em',
  1100. "0.6em" => '0.6em',
  1101. "0.65em" => '0.65em',
  1102. "0.7em" => '0.7em',
  1103. "0.75em" => '0.75em',
  1104. "0.8em" => '0.8em',
  1105. "0.85em" => '0.85em',
  1106. "0.9em" => '0.9em',
  1107. "0.95em" => '0.95em',
  1108. "1em" => '1em',
  1109. ),
  1110. );
  1111. // decoration
  1112. $form['decoration'] = array(
  1113. '#title' => t('Decoration'),
  1114. '#type' => 'select',
  1115. '#default_value' => isset($settings['decoration']) ? $settings['decoration'] : '',
  1116. '#options' => array(
  1117. '' => '',
  1118. 'none' => t('None'),
  1119. 'underline' => t('Underline'),
  1120. 'overline' => t('Overline'),
  1121. 'line-through' => t('Line-through'),
  1122. ),
  1123. );
  1124. // weight
  1125. $form['weight'] = array(
  1126. '#title' => t('Weight'),
  1127. '#type' => 'select',
  1128. '#default_value' => isset($settings['weight']) ? $settings['weight'] : '',
  1129. '#options' => array(
  1130. '' => '',
  1131. 'normal' => t('Normal'),
  1132. 'bold' => t('Bold'),
  1133. 'bolder' => t('Bolder'),
  1134. 'lighter' => t('Lighter'),
  1135. ),
  1136. );
  1137. // style
  1138. $form['style'] = array(
  1139. '#title' => t('Style'),
  1140. '#type' => 'select',
  1141. '#default_value' => isset($settings['style']) ? $settings['style'] : '',
  1142. '#options' => array(
  1143. '' => '',
  1144. 'normal' => t('Normal'),
  1145. 'italic' => t('Italic'),
  1146. 'oblique' => t('Oblique'),
  1147. ),
  1148. );
  1149. // variant
  1150. $form['variant'] = array(
  1151. '#title' => t('Variant'),
  1152. '#type' => 'select',
  1153. '#default_value' => isset($settings['variant']) ? $settings['variant'] : '',
  1154. '#options' => array(
  1155. '' => '',
  1156. 'normal' => t('Normal'),
  1157. 'small-caps' => t('Small-caps'),
  1158. ),
  1159. );
  1160. // case
  1161. $form['case'] = array(
  1162. '#title' => t('Case'),
  1163. '#type' => 'select',
  1164. '#default_value' => isset($settings['case']) ? $settings['case'] : '',
  1165. '#options' => array(
  1166. '' => '',
  1167. 'capitalize' => t('Capitalize'),
  1168. 'uppercase' => t('Uppercase'),
  1169. 'lowercase' => t('Lowercase'),
  1170. 'none' => t('None'),
  1171. ),
  1172. );
  1173. // alignment
  1174. $form['alignment'] = array(
  1175. '#title' => t('Align'),
  1176. '#type' => 'select',
  1177. '#default_value' => isset($settings['alignment']) ? $settings['alignment'] : '',
  1178. '#options' => array(
  1179. '' => '',
  1180. 'justify' => t('Justify'),
  1181. 'left' => t('Left'),
  1182. 'right' => t('Right'),
  1183. 'center' => t('Center'),
  1184. ),
  1185. );
  1186. }
  1187. /**
  1188. * Copy font selector information into the settings
  1189. */
  1190. function ctools_stylizer_font_selector_form_submit(&$form, &$form_state, &$values, &$settings) {
  1191. $settings = $values;
  1192. }
  1193. function ctools_stylizer_font_apply_style(&$stylesheet, $selector, $settings) {
  1194. $css = '';
  1195. if (isset($settings['font']) && $settings['font'] !== '') {
  1196. $css .= ' font-family: ' . $settings['font'] . ";\n";
  1197. }
  1198. if (isset($settings['size']) && $settings['size'] !== '') {
  1199. $css .= ' font-size: ' . $settings['size'] . ";\n";
  1200. }
  1201. if (isset($settings['weight']) && $settings['weight'] !== '') {
  1202. $css .= ' font-weight: ' . $settings['weight'] . ";\n";
  1203. }
  1204. if (isset($settings['style']) && $settings['style'] !== '') {
  1205. $css .= ' font-style: ' . $settings['style'] . ";\n";
  1206. }
  1207. if (isset($settings['variant']) && $settings['variant'] !== '') {
  1208. $css .= ' font-variant: ' . $settings['variant'] . ";\n";
  1209. }
  1210. if (isset($settings['case']) && $settings['case'] !== '') {
  1211. $css .= ' text-transform: ' . $settings['case'] . ";\n";
  1212. }
  1213. if (isset($settings['decoration']) && $settings['decoration'] !== '') {
  1214. $css .= ' text-decoration: ' . $settings['decoration'] . ";\n";
  1215. }
  1216. if (isset($settings['alignment']) && $settings['alignment'] !== '') {
  1217. $css .= ' text-align: ' . $settings['alignment'] . ";\n";
  1218. }
  1219. if (isset($settings['letter_spacing']) && $settings['letter_spacing'] !== '') {
  1220. $css .= ' letter-spacing: ' . $settings['letter_spacing'] . ";\n";
  1221. }
  1222. if (isset($settings['word_spacing']) && $settings['word_spacing'] !== '') {
  1223. $css .= ' word-spacing: ' . $settings['word_spacing'] . ";\n";
  1224. }
  1225. if ($css) {
  1226. $stylesheet .= $selector . " {\n" . $css . "}\n";
  1227. }
  1228. }
  1229. /**
  1230. * Border selector form
  1231. */
  1232. function ctools_stylizer_border_selector_form(&$form, &$form_state, $label, $settings) {
  1233. // Family
  1234. $form['#prefix'] = '<div class="ctools-stylizer-spacing-form clearfix">';
  1235. $form['#type'] = 'fieldset';
  1236. $form['#title'] = $label;
  1237. $form['#suffix'] = '</div>';
  1238. $form['#tree'] = TRUE;
  1239. $form['thickness'] = array(
  1240. '#title' => t('Thickness'),
  1241. '#type' => 'select',
  1242. '#default_value' => isset($settings['thickness']) ? $settings['thickness'] : '',
  1243. '#options' => array(
  1244. '' => '',
  1245. "none" => t('None'),
  1246. "1px" => '1px',
  1247. "2px" => '2px',
  1248. "3px" => '3px',
  1249. "4px" => '4px',
  1250. "5px" => '5px',
  1251. ),
  1252. );
  1253. $form['style'] = array(
  1254. '#title' => t('style'),
  1255. '#type' => 'select',
  1256. '#default_value' => isset($settings['style']) ? $settings['style'] : '',
  1257. '#options' => array(
  1258. '' => '',
  1259. 'solid' => t('Solid'),
  1260. 'dotted' => t('Dotted'),
  1261. 'dashed' => t('Dashed'),
  1262. 'double' => t('Double'),
  1263. 'groove' => t('Groove'),
  1264. 'ridge' => t('Ridge'),
  1265. 'inset' => t('Inset'),
  1266. 'outset' => t('Outset'),
  1267. ),
  1268. );
  1269. }
  1270. /**
  1271. * Copy border selector information into the settings
  1272. */
  1273. function ctools_stylizer_border_selector_form_submit(&$form, &$form_state, &$values, &$settings) {
  1274. $settings = $values;
  1275. }
  1276. function ctools_stylizer_border_apply_style(&$stylesheet, $selector, $settings, $color, $which = NULL) {
  1277. $border = 'border';
  1278. if ($which) {
  1279. $border .= '-' . $which;
  1280. }
  1281. $css = '';
  1282. if (isset($settings['thickness']) && $settings['thickness'] !== '') {
  1283. if ($settings['thickness'] == 'none') {
  1284. $css .= ' ' . $border . ': none';
  1285. }
  1286. else {
  1287. $css .= ' ' . $border . '-width: ' . $settings['thickness'] . ";\n";
  1288. if (isset($settings['style']) && $settings['style'] !== '') {
  1289. $css .= ' ' . $border . '-style: ' . $settings['style'] . ";\n";
  1290. }
  1291. $css .= ' ' . $border . '-color: ' . $color . ";\n";
  1292. }
  1293. }
  1294. if ($css) {
  1295. $stylesheet .= $selector . " {\n" . $css . "}\n";
  1296. }
  1297. }
  1298. /**
  1299. * padding selector form
  1300. */
  1301. function ctools_stylizer_padding_selector_form(&$form, &$form_state, $label, $settings) {
  1302. // Family
  1303. $form['#prefix'] = '<div class="ctools-stylizer-spacing-form clearfix">';
  1304. $form['#type'] = 'fieldset';
  1305. $form['#title'] = $label;
  1306. $form['#suffix'] = '</div>';
  1307. $form['#tree'] = TRUE;
  1308. $options = array(
  1309. '' => '',
  1310. "0.05em" => '0.05em',
  1311. "0.1em" => '0.1em',
  1312. "0.15em" => '0.15em',
  1313. "0.2em" => '0.2em',
  1314. "0.25em" => '0.25em',
  1315. "0.3em" => '0.3em',
  1316. "0.35em" => '0.35em',
  1317. "0.4em" => '0.4em',
  1318. "0.45em" => '0.45em',
  1319. "0.5em" => '0.5em',
  1320. "0.55em" => '0.55em',
  1321. "0.6em" => '0.6em',
  1322. "0.65em" => '0.65em',
  1323. "0.7em" => '0.7em',
  1324. "0.75em" => '0.75em',
  1325. "0.8em" => '0.8em',
  1326. "0.85em" => '0.85em',
  1327. "0.9em" => '0.9em',
  1328. "0.95em" => '0.95em',
  1329. "1.0em" => '1.0em',
  1330. "1.05em" => '1.05em',
  1331. "1.1em" => '1.1em',
  1332. "1.15em" => '1.15em',
  1333. "1.2em" => '1.2em',
  1334. "1.25em" => '1.25em',
  1335. "1.3em" => '1.3em',
  1336. "1.35em" => '1.35em',
  1337. "1.4em" => '1.4em',
  1338. "1.45em" => '1.45em',
  1339. "1.5em" => '1.5em',
  1340. "1.55em" => '1.55em',
  1341. "1.6em" => '1.6em',
  1342. "1.65em" => '1.65em',
  1343. "1.7em" => '1.7em',
  1344. "1.75em" => '1.75em',
  1345. "1.8em" => '1.8em',
  1346. "1.85em" => '1.85em',
  1347. "1.9em" => '1.9em',
  1348. "1.95em" => '1.95em',
  1349. "2.0em" => '2.0em',
  1350. "2.05em" => '2.05em',
  1351. "2.1em" => '2.1em',
  1352. "2.15em" => '2.15em',
  1353. "2.2em" => '2.2em',
  1354. "2.25em" => '2.25em',
  1355. "2.3em" => '2.3em',
  1356. "2.35em" => '2.35em',
  1357. "2.4em" => '2.4em',
  1358. "2.45em" => '2.45em',
  1359. "2.5em" => '2.5em',
  1360. "2.55em" => '2.55em',
  1361. "2.6em" => '2.6em',
  1362. "2.65em" => '2.65em',
  1363. "2.7em" => '2.7em',
  1364. "2.75em" => '2.75em',
  1365. "2.8em" => '2.8em',
  1366. "2.85em" => '2.85em',
  1367. "2.9em" => '2.9em',
  1368. "2.95em" => '2.95em',
  1369. "3.0em" => '3.0em',
  1370. "3.05em" => '3.05em',
  1371. "3.1em" => '3.1em',
  1372. "3.15em" => '3.15em',
  1373. "3.2em" => '3.2em',
  1374. "3.25em" => '3.25em',
  1375. "3.3em" => '3.3em',
  1376. "3.35em" => '3.35em',
  1377. "3.4em" => '3.4em',
  1378. "3.45em" => '3.45em',
  1379. "3.5em" => '3.5em',
  1380. "3.55em" => '3.55em',
  1381. "3.6em" => '3.6em',
  1382. "3.65em" => '3.65em',
  1383. "3.7em" => '3.7em',
  1384. "3.75em" => '3.75em',
  1385. "3.8em" => '3.8em',
  1386. "3.85em" => '3.85em',
  1387. "3.9em" => '3.9em',
  1388. "3.95em" => '3.95em',
  1389. );
  1390. $form['top'] = array(
  1391. '#title' => t('Top'),
  1392. '#type' => 'select',
  1393. '#default_value' => isset($settings['top']) ? $settings['top'] : '',
  1394. '#options' => $options,
  1395. );
  1396. $form['right'] = array(
  1397. '#title' => t('Right'),
  1398. '#type' => 'select',
  1399. '#default_value' => isset($settings['right']) ? $settings['right'] : '',
  1400. '#options' => $options,
  1401. );
  1402. $form['bottom'] = array(
  1403. '#title' => t('Bottom'),
  1404. '#type' => 'select',
  1405. '#default_value' => isset($settings['bottom']) ? $settings['bottom'] : '',
  1406. '#options' => $options,
  1407. );
  1408. $form['left'] = array(
  1409. '#title' => t('Left'),
  1410. '#type' => 'select',
  1411. '#default_value' => isset($settings['left']) ? $settings['left'] : '',
  1412. '#options' => $options,
  1413. );
  1414. }
  1415. /**
  1416. * Copy padding selector information into the settings
  1417. */
  1418. function ctools_stylizer_padding_selector_form_submit(&$form, &$form_state, &$values, &$settings) {
  1419. $settings = $values;
  1420. }
  1421. function ctools_stylizer_padding_apply_style(&$stylesheet, $selector, $settings) {
  1422. $css = '';
  1423. if (isset($settings['top']) && $settings['top'] !== '') {
  1424. $css .= ' padding-top: ' . $settings['top'] . ";\n";
  1425. }
  1426. if (isset($settings['right']) && $settings['right'] !== '') {
  1427. $css .= ' padding-right: ' . $settings['right'] . ";\n";
  1428. }
  1429. if (isset($settings['bottom']) && $settings['bottom'] !== '') {
  1430. $css .= ' padding-bottom: ' . $settings['bottom'] . ";\n";
  1431. }
  1432. if (isset($settings['left']) && $settings['left'] !== '') {
  1433. $css .= ' padding-left: ' . $settings['left'] . ";\n";
  1434. }
  1435. if ($css) {
  1436. $stylesheet .= $selector . " {\n" . $css . "}\n";
  1437. }
  1438. }