Blender V5.0
buttons_ops.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2009 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
8
9#include <cstdlib>
10#include <cstring>
11
12#include "MEM_guardedalloc.h"
13
14#include "DNA_userdef_types.h"
15
16#include "BLI_fileops.h"
17#include "BLI_path_utils.hh"
18#include "BLI_string.h"
19#include "BLI_utildefines.h"
20
21#include "BLT_translation.hh"
22
23#include "BKE_appdir.hh"
24#include "BKE_context.hh"
25#include "BKE_library.hh"
26#include "BKE_main.hh"
27#include "BKE_path_templates.hh"
28#include "BKE_report.hh"
29#include "BKE_screen.hh"
30
31#include "WM_api.hh"
32#include "WM_types.hh"
33
34#include "ED_screen.hh"
35#include "ED_undo.hh"
36
37#include "RNA_access.hh"
38#include "RNA_define.hh"
39#include "RNA_prototypes.hh"
40
41#include "UI_interface.hh"
43#include "UI_resources.hh"
44
45#include "buttons_intern.hh" /* own include */
46
47/* -------------------------------------------------------------------- */
52
54{
56 ScrArea *area = CTX_wm_area(C);
58
59 UI_textbutton_activate_rna(C, region, space, "search_filter");
60
61 return OPERATOR_FINISHED;
62}
63
65{
66 /* Identifiers. */
67 ot->name = "Filter";
68 ot->description = "Start entering filter text";
69 ot->idname = "BUTTONS_OT_start_filter";
70
71 /* Callbacks. */
74}
75
88
90{
91 /* Identifiers. */
92 ot->name = "Clear Filter";
93 ot->description = "Clear the search filter";
94 ot->idname = "BUTTONS_OT_clear_filter";
95
96 /* Callbacks. */
99}
100
102
103/* -------------------------------------------------------------------- */
106
108{
110
111 sbuts->flag ^= SB_PIN_CONTEXT;
112
113 /* Create the properties space pointer. */
114 bScreen *screen = CTX_wm_screen(C);
115 PointerRNA sbuts_ptr = RNA_pointer_create_discrete(&screen->id, &RNA_SpaceProperties, sbuts);
116
117 /* Create the new ID pointer and set the pin ID with RNA
118 * so we can use the property's RNA update functionality. */
119 ID *new_id = (sbuts->flag & SB_PIN_CONTEXT) ? buttons_context_id_path(C) : nullptr;
120 PointerRNA new_id_ptr = RNA_id_pointer_create(new_id);
121 RNA_pointer_set(&sbuts_ptr, "pin_id", new_id_ptr);
122
124
125 return OPERATOR_FINISHED;
126}
127
129{
130 /* Identifiers. */
131 ot->name = "Toggle Pin ID";
132 ot->description = "Keep the current data-block displayed";
133 ot->idname = "BUTTONS_OT_toggle_pin";
134
135 /* Callbacks. */
136 ot->exec = toggle_pin_exec;
138}
139
141
142/* -------------------------------------------------------------------- */
145
147 wmOperator * /*op*/,
148 const wmEvent * /*event*/)
149{
150 uiPopupMenu *pup = UI_popup_menu_begin(C, IFACE_("Context Menu"), ICON_NONE);
151 uiLayout *layout = UI_popup_menu_layout(pup);
152
153 layout->menu("INFO_MT_area", std::nullopt, ICON_NONE);
154 UI_popup_menu_end(C, pup);
155
156 return OPERATOR_INTERFACE;
157}
158
160{
161 /* Identifiers. */
162 ot->name = "Context Menu";
163 ot->description = "Display properties editor context_menu";
164 ot->idname = "BUTTONS_OT_context_menu";
165
166 /* Callbacks. */
167 ot->invoke = context_menu_invoke;
169}
170
172
173/* -------------------------------------------------------------------- */
176
179 PropertyRNA *prop = nullptr;
180 bool is_undo = false;
181 bool is_userdef = false;
182};
183
185{
186 FileBrowseOp *fbo = static_cast<FileBrowseOp *>(op->customdata);
187 const PropertySubType subtype = RNA_property_subtype(fbo->prop);
188 if (ELEM(subtype, PROP_FILEPATH, PROP_DIRPATH)) {
189 const int flag = RNA_property_flag(fbo->prop);
191 return false;
192 }
193 }
194 return true;
195}
196
198{
199 Main *bmain = CTX_data_main(C);
200 FileBrowseOp *fbo = static_cast<FileBrowseOp *>(op->customdata);
201 char *path;
202 const char *path_prop = RNA_struct_find_property(op->ptr, "directory") ? "directory" :
203 "filepath";
204
205 if (fbo == nullptr) {
206 return OPERATOR_CANCELLED;
207 }
208 if (RNA_struct_property_is_set(op->ptr, path_prop) == 0) {
209 MEM_delete(fbo);
210 return OPERATOR_CANCELLED;
211 }
212
213 path = RNA_string_get_alloc(op->ptr, path_prop, nullptr, 0, nullptr);
214
215 if (path[0]) {
216 /* Check relative paths are supported here as this option will be hidden
217 * when it's not supported. In this case the value may have been enabled
218 * by default or from the last-used setting.
219 * Either way, don't use the blend-file relative prefix when it's not supported. */
220 const PropertySubType prop_subtype = RNA_property_subtype(fbo->prop);
221 const bool is_relative = BLI_path_is_rel(path);
222 const bool make_relative = RNA_boolean_get(op->ptr, "relative_path") &&
224
225 /* Add slash for directories, important for some properties. */
226 if ((prop_subtype == PROP_DIRPATH) || (is_relative || make_relative)) {
227 char path_buf[FILE_MAX];
228 ID *id = fbo->ptr.owner_id;
229
230 STRNCPY(path_buf, path);
231 MEM_freeN(path);
232
233 if (is_relative) {
234 BLI_path_abs(path_buf, id ? ID_BLEND_PATH(bmain, id) : BKE_main_blendfile_path(bmain));
235 }
236
237 if (prop_subtype == PROP_DIRPATH) {
238 BLI_path_slash_ensure(path_buf, sizeof(path_buf));
239 }
240
241 if (make_relative) {
242 BLI_path_rel(path_buf, BKE_main_blendfile_path(bmain));
243 }
244 path = BLI_strdup(path_buf);
245 }
246 }
247
248 RNA_property_string_set(&fbo->ptr, fbo->prop, path);
249 RNA_property_update(C, &fbo->ptr, fbo->prop);
250 MEM_freeN(path);
251
252 if (fbo->is_undo) {
253 const char *undostr = RNA_property_identifier(fbo->prop);
254 ED_undo_push(C, undostr);
255 }
256
257 /* Special annoying exception, filesel on redo panel #26618. */
258 {
260 if (redo_op) {
261 if (fbo->ptr.data == redo_op->ptr->data) {
262 ED_undo_operator_repeat(C, redo_op);
263 }
264 }
265 }
266
267 /* Tag user preferences as dirty. */
268 if (fbo->is_userdef) {
269 U.runtime.is_dirty = true;
270 }
271
272 BLI_assert(fbo == op->customdata);
273 MEM_delete(fbo);
274
275 return OPERATOR_FINISHED;
276}
277
278static void file_browse_cancel(bContext * /*C*/, wmOperator *op)
279{
280 FileBrowseOp *fbo = static_cast<FileBrowseOp *>(op->customdata);
281 MEM_delete(fbo);
282 op->customdata = nullptr;
283}
284
286{
288 PropertyRNA *prop;
289 bool is_undo;
290 bool is_userdef;
291 char *path;
292
293 const SpaceFile *sfile = CTX_wm_space_file(C);
294 if (sfile && sfile->op) {
295 BKE_report(op->reports, RPT_ERROR, "Cannot activate a file selector dialog, one already open");
296 return OPERATOR_CANCELLED;
297 }
298
299 UI_context_active_but_prop_get_filebrowser(C, &ptr, &prop, &is_undo, &is_userdef);
300
301 if (!prop) {
302 return OPERATOR_CANCELLED;
303 }
304
305 path = RNA_property_string_get_alloc(&ptr, prop, nullptr, 0, nullptr);
306
308 const std::optional<blender::bke::path_templates::VariableMap> variables =
310 BLI_assert(variables.has_value());
311
313 path, FILE_MAX, *variables);
314 if (!errors.is_empty()) {
316 return OPERATOR_CANCELLED;
317 }
318 }
319
320 /* Useful yet irritating feature, Shift+Click to open the file
321 * Alt+Click to browse a folder in the OS's browser. */
322 if (event->modifier & (KM_SHIFT | KM_ALT)) {
323 wmOperatorType *ot = WM_operatortype_find("WM_OT_path_open", true);
324 PointerRNA props_ptr;
325
326 if (event->modifier & KM_ALT) {
327 char *lslash = (char *)BLI_path_slash_rfind(path);
328 if (lslash) {
329 *lslash = '\0';
330 }
331 }
332
334 RNA_string_set(&props_ptr, "filepath", path);
336 WM_operator_properties_free(&props_ptr);
337
338 MEM_freeN(path);
339 return OPERATOR_CANCELLED;
340 }
341
342 {
343 const char *info;
344 if (!RNA_property_editable_info(&ptr, prop, &info)) {
345 if (info[0]) {
346 BKE_reportf(op->reports, RPT_ERROR, "Property is not editable: %s", info);
347 }
348 else {
349 BKE_report(op->reports, RPT_ERROR, "Property is not editable");
350 }
351 MEM_freeN(path);
352 return OPERATOR_CANCELLED;
353 }
354 }
355
356 PropertyRNA *prop_relpath;
357 const char *path_prop = RNA_struct_find_property(op->ptr, "directory") ? "directory" :
358 "filepath";
359 FileBrowseOp *fbo = MEM_new<FileBrowseOp>(__func__);
360 fbo->ptr = ptr;
361 fbo->prop = prop;
362 fbo->is_undo = is_undo;
363 fbo->is_userdef = is_userdef;
364
365 op->customdata = fbo;
366
367 /* NOTE(@ideasman42): Normally #ED_fileselect_get_params would handle this
368 * but we need to because of stupid user-preferences exception. */
369 if ((prop_relpath = RNA_struct_find_property(op->ptr, "relative_path"))) {
370 if (!RNA_property_is_set(op->ptr, prop_relpath)) {
371 bool is_relative = (U.flag & USER_RELPATHS) != 0;
372
373 /* While we want to follow the defaults,
374 * we better not switch existing paths relative/absolute state. */
375 if (path[0]) {
376 is_relative = BLI_path_is_rel(path);
377 }
378
379 if (UNLIKELY(ptr.data == &U || is_userdef)) {
380 is_relative = false;
381 }
382
383 /* Annoying exception!, if we're dealing with the user preferences,
384 * default relative to be off. */
385 RNA_property_boolean_set(op->ptr, prop_relpath, is_relative);
386 }
387 }
388
389 const char *prop_id = RNA_property_identifier(prop);
390
391 /* NOTE: relying on built-in names isn't useful for add-on authors.
392 * The property itself should support this kind of meta-data. */
393 if (STR_ELEM(prop_id, "font_path_ui", "font_path_ui_mono", "font_directory")) {
394 RNA_boolean_set(op->ptr, "filter_font", true);
395 RNA_boolean_set(op->ptr, "filter_folder", true);
396 RNA_enum_set(op->ptr, "display_type", FILE_IMGDISPLAY);
397 RNA_enum_set(op->ptr, "sort_method", FILE_SORT_ALPHA);
398 if (!path[0]) {
399 char fonts_path[FILE_MAX] = {0};
400 if (U.fontdir[0]) {
401 STRNCPY(fonts_path, U.fontdir);
402 /* The file selector will expand the blend-file relative prefix. */
403 }
404 else if (!BKE_appdir_font_folder_default(fonts_path, ARRAY_SIZE(fonts_path))) {
406 }
407 BLI_path_slash_ensure(fonts_path, ARRAY_SIZE(fonts_path));
408 MEM_freeN(path);
409 path = BLI_strdup(fonts_path);
410 }
411 }
412
413 if (!path[0]) {
414 /* Find a reasonable folder to start in if none found. */
415 char default_path[FILE_MAX] = {0};
417 BLI_path_slash_ensure(default_path, ARRAY_SIZE(default_path));
418 MEM_freeN(path);
419 path = BLI_strdup(default_path);
420 }
421
422 RNA_string_set(op->ptr, path_prop, path);
423 MEM_freeN(path);
424
425 PropertyRNA *prop_check_existing = RNA_struct_find_property(op->ptr, "check_existing");
426 if (!RNA_property_is_set(op->ptr, prop_check_existing)) {
427 const bool is_output_path = (RNA_property_flag(prop) & PROP_PATH_OUTPUT) != 0;
428 RNA_property_boolean_set(op->ptr, prop_check_existing, is_output_path);
429 }
430 if (std::optional<std::string> filter = RNA_property_string_path_filter(C, &ptr, prop)) {
431 RNA_string_set(op->ptr, "filter_glob", filter->c_str());
432 }
433
435
437}
438
439static bool file_browse_poll_property(const bContext * /*C*/,
440 wmOperator *op,
441 const PropertyRNA *prop)
442{
443 const char *prop_id = RNA_property_identifier(prop);
444 if (STREQ(prop_id, "relative_path")) {
446 return false;
447 }
448 }
449 return true;
450}
451
453{
454 /* Identifiers. */
455 ot->name = "Accept";
456 ot->description =
457 "Open a file browser, hold Shift to open the file, Alt to browse containing directory";
458 ot->idname = "BUTTONS_OT_file_browse";
459
460 /* Callbacks. */
461 ot->invoke = file_browse_invoke;
462 ot->exec = file_browse_exec;
463 ot->cancel = file_browse_cancel;
464 ot->poll_property = file_browse_poll_property;
465
466 /* Conditional undo based on button flag. */
467 ot->flag = 0;
468
469 /* Properties. */
471 0,
477
478 PropertyRNA *prop;
479
480 prop = RNA_def_string(ot->srna, "filter_glob", nullptr, 0, "Glob Filter", "Custom filter");
482}
483
485{
486 /* identifiers */
487 ot->name = "Accept";
488 ot->description =
489 "Open a directory browser, hold Shift to open the file, Alt to browse containing directory";
490 ot->idname = "BUTTONS_OT_directory_browse";
491
492 /* API callbacks. */
493 ot->invoke = file_browse_invoke;
494 ot->exec = file_browse_exec;
495 ot->cancel = file_browse_cancel;
496 ot->poll_property = file_browse_poll_property;
497
498 /* conditional undo based on button flag */
499 ot->flag = 0;
500
501 /* properties */
503 0,
509}
510
const char * BKE_appdir_folder_default_or_root() ATTR_WARN_UNUSED_RESULT ATTR_RETURNS_NONNULL
Definition appdir.cc:163
bool BKE_appdir_font_folder_default(char *dir, size_t dir_maxncpy)
Definition appdir.cc:233
bScreen * CTX_wm_screen(const bContext *C)
SpaceFile * CTX_wm_space_file(const bContext *C)
SpaceProperties * CTX_wm_space_properties(const bContext *C)
ScrArea * CTX_wm_area(const bContext *C)
Main * CTX_data_main(const bContext *C)
ARegion * CTX_wm_region(const bContext *C)
const char * BKE_main_blendfile_path(const Main *bmain) ATTR_NONNULL()
Definition main.cc:887
Functions and classes for evaluating template expressions in filepaths.
void BKE_report_path_template_errors(ReportList *reports, eReportType report_type, blender::StringRef path, blender::Span< blender::bke::path_templates::Error > errors)
std::optional< blender::bke::path_templates::VariableMap > BKE_build_template_variables_for_prop(const bContext *C, PointerRNA *ptr, PropertyRNA *prop)
blender::Vector< blender::bke::path_templates::Error > BKE_path_apply_template(char *path, int path_maxncpy, const blender::bke::path_templates::VariableMap &template_variables)
void BKE_reportf(ReportList *reports, eReportType type, const char *format,...) ATTR_PRINTF_FORMAT(3
@ RPT_ERROR
Definition BKE_report.hh:39
void BKE_report(ReportList *reports, eReportType type, const char *message)
Definition report.cc:153
ARegion * BKE_area_find_region_type(const ScrArea *area, int region_type)
Definition screen.cc:846
#define BLI_assert(a)
Definition BLI_assert.h:46
File and directory operations.
bool BLI_path_abs(char path[FILE_MAX], const char *basepath) ATTR_NONNULL(1
#define FILE_MAX
bool BLI_path_is_rel(const char *path) ATTR_NONNULL(1) ATTR_WARN_UNUSED_RESULT
bool void BLI_path_rel(char path[FILE_MAX], const char *basepath) ATTR_NONNULL(1)
int BLI_path_slash_ensure(char *path, size_t path_maxncpy) ATTR_NONNULL(1)
const char * BLI_path_slash_rfind(const char *path) ATTR_NONNULL(1) ATTR_WARN_UNUSED_RESULT
char * BLI_strdup(const char *str) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1) ATTR_MALLOC
Definition string.cc:41
#define STR_ELEM(...)
Definition BLI_string.h:661
char * STRNCPY(char(&dst)[N], const char *src)
Definition BLI_string.h:693
#define ARRAY_SIZE(arr)
#define UNLIKELY(x)
#define ELEM(...)
#define STREQ(a, b)
#define IFACE_(msgid)
#define ID_BLEND_PATH(_bmain, _id)
Definition DNA_ID.h:685
@ RGN_TYPE_HEADER
@ FILE_SORT_DEFAULT
@ FILE_SORT_ALPHA
@ FILE_SPECIAL
@ SB_PIN_CONTEXT
@ FILE_IMGDISPLAY
@ FILE_DEFAULTDISPLAY
@ USER_RELPATHS
@ OPERATOR_CANCELLED
@ OPERATOR_INTERFACE
@ OPERATOR_FINISHED
@ OPERATOR_RUNNING_MODAL
void ED_area_tag_redraw(ScrArea *area)
Definition area.cc:693
void ED_region_search_filter_update(const ScrArea *area, ARegion *region)
Definition area.cc:838
bool ED_operator_buttons_active(bContext *C)
void ED_undo_push(bContext *C, const char *str)
Definition ed_undo.cc:98
bool ED_undo_operator_repeat(bContext *C, wmOperator *op)
Definition ed_undo.cc:631
Read Guarded memory(de)allocation.
@ PROP_PATH_OUTPUT
Definition RNA_types.hh:451
@ PROP_PATH_SUPPORTS_BLEND_RELATIVE
Definition RNA_types.hh:456
@ PROP_PATH_SUPPORTS_TEMPLATES
Definition RNA_types.hh:470
@ PROP_SKIP_SAVE
Definition RNA_types.hh:344
@ PROP_HIDDEN
Definition RNA_types.hh:338
PropertySubType
Definition RNA_types.hh:232
@ PROP_DIRPATH
Definition RNA_types.hh:237
@ PROP_FILEPATH
Definition RNA_types.hh:236
#define C
Definition RandGen.cpp:29
bool UI_textbutton_activate_rna(const bContext *C, ARegion *region, const void *rna_poin_data, const char *rna_prop_id)
void UI_popup_menu_end(bContext *C, uiPopupMenu *pup)
uiPopupMenu * UI_popup_menu_begin(bContext *C, const char *title, int icon) ATTR_NONNULL()
void UI_context_active_but_prop_get_filebrowser(const bContext *C, PointerRNA *r_ptr, PropertyRNA **r_prop, bool *r_is_undo, bool *r_is_userdef)
uiLayout * UI_popup_menu_layout(uiPopupMenu *pup)
@ WM_FILESEL_DIRECTORY
Definition WM_api.hh:1122
@ WM_FILESEL_RELPATH
Definition WM_api.hh:1121
@ WM_FILESEL_FILEPATH
Definition WM_api.hh:1124
@ FILE_OPENFILE
Definition WM_api.hh:1133
@ KM_ALT
Definition WM_types.hh:280
@ KM_SHIFT
Definition WM_types.hh:278
#define U
ID * buttons_context_id_path(const bContext *C)
static wmOperatorStatus file_browse_invoke(bContext *C, wmOperator *op, const wmEvent *event)
static void file_browse_cancel(bContext *, wmOperator *op)
static wmOperatorStatus context_menu_invoke(bContext *C, wmOperator *, const wmEvent *)
void BUTTONS_OT_toggle_pin(wmOperatorType *ot)
void BUTTONS_OT_context_menu(wmOperatorType *ot)
void BUTTONS_OT_directory_browse(wmOperatorType *ot)
static wmOperatorStatus buttons_start_filter_exec(bContext *C, wmOperator *)
static wmOperatorStatus buttons_clear_filter_exec(bContext *C, wmOperator *)
static bool file_browse_operator_relative_paths_supported(wmOperator *op)
static wmOperatorStatus file_browse_exec(bContext *C, wmOperator *op)
static wmOperatorStatus toggle_pin_exec(bContext *C, wmOperator *)
void BUTTONS_OT_clear_filter(wmOperatorType *ot)
static bool file_browse_poll_property(const bContext *, wmOperator *op, const PropertyRNA *prop)
void BUTTONS_OT_file_browse(wmOperatorType *ot)
void BUTTONS_OT_start_filter(wmOperatorType *ot)
bool is_empty() const
#define filter
void MEM_freeN(void *vmemh)
Definition mallocn.cc:113
void RNA_string_set(PointerRNA *ptr, const char *name, const char *value)
void RNA_pointer_set(PointerRNA *ptr, const char *name, PointerRNA ptr_value)
PropertyRNA * RNA_struct_find_property(PointerRNA *ptr, const char *identifier)
void RNA_boolean_set(PointerRNA *ptr, const char *name, bool value)
bool RNA_property_is_set(PointerRNA *ptr, PropertyRNA *prop)
char * RNA_property_string_get_alloc(PointerRNA *ptr, PropertyRNA *prop, char *fixedbuf, int fixedlen, int *r_len)
void RNA_property_update(bContext *C, PointerRNA *ptr, PropertyRNA *prop)
std::optional< std::string > RNA_property_string_path_filter(const bContext *C, PointerRNA *ptr, PropertyRNA *prop)
int RNA_property_flag(PropertyRNA *prop)
char * RNA_string_get_alloc(PointerRNA *ptr, const char *name, char *fixedbuf, int fixedlen, int *r_len)
void RNA_property_boolean_set(PointerRNA *ptr, PropertyRNA *prop, bool value)
bool RNA_property_editable_info(const PointerRNA *ptr, PropertyRNA *prop, const char **r_info)
PropertySubType RNA_property_subtype(PropertyRNA *prop)
bool RNA_struct_property_is_set(PointerRNA *ptr, const char *identifier)
bool RNA_boolean_get(PointerRNA *ptr, const char *name)
void RNA_enum_set(PointerRNA *ptr, const char *name, int value)
PointerRNA RNA_pointer_create_discrete(ID *id, StructRNA *type, void *data)
const char * RNA_property_identifier(const PropertyRNA *prop)
PointerRNA RNA_id_pointer_create(ID *id)
void RNA_property_string_set(PointerRNA *ptr, PropertyRNA *prop, const char *value)
PropertyRNA * RNA_def_string(StructOrFunctionRNA *cont_, const char *identifier, const char *default_value, const int maxlen, const char *ui_name, const char *ui_description)
void RNA_def_property_flag(PropertyRNA *prop, PropertyFlag flag)
PointerRNA ptr
PropertyRNA * prop
Definition DNA_ID.h:414
ID * owner_id
Definition RNA_types.hh:51
void * data
Definition RNA_types.hh:53
struct wmOperator * op
char search_string[UI_MAX_NAME_STR]
struct SpaceProperties_Runtime * runtime
void menu(MenuType *mt, std::optional< blender::StringRef > name, int icon)
wmEventModifierFlag modifier
Definition WM_types.hh:774
struct ReportList * reports
struct PointerRNA * ptr
wmOperatorStatus WM_operator_name_call_ptr(bContext *C, wmOperatorType *ot, blender::wm::OpCallContext context, PointerRNA *properties, const wmEvent *event)
void WM_event_add_fileselect(bContext *C, wmOperator *op)
PointerRNA * ptr
Definition wm_files.cc:4238
wmOperatorType * ot
Definition wm_files.cc:4237
void WM_operator_properties_filesel(wmOperatorType *ot, const int filter, const short type, const eFileSel_Action action, const eFileSel_Flag flag, const short display, const short sort)
wmOperatorType * WM_operatortype_find(const char *idname, bool quiet)
wmOperator * WM_operator_last_redo(const bContext *C)
void WM_operator_properties_create_ptr(PointerRNA *ptr, wmOperatorType *ot)
void WM_operator_properties_free(PointerRNA *ptr)
uint8_t flag
Definition wm_window.cc:145