rofi 1.7.5
combi.c
Go to the documentation of this file.
1/*
2 * rofi
3 *
4 * MIT/X11 License
5 * Copyright © 2013-2022 Qball Cow <qball@gmpclient.org>
6 *
7 * Permission is hereby granted, free of charge, to any person obtaining
8 * a copy of this software and associated documentation files (the
9 * "Software"), to deal in the Software without restriction, including
10 * without limitation the rights to use, copy, modify, merge, publish,
11 * distribute, sublicense, and/or sell copies of the Software, and to
12 * permit persons to whom the Software is furnished to do so, subject to
13 * the following conditions:
14 *
15 * The above copyright notice and this permission notice shall be
16 * included in all copies or substantial portions of the Software.
17 *
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
19 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
21 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
22 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
23 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
24 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25 *
26 */
27
29#define G_LOG_DOMAIN "Modes.Combi"
30
31#include "helper.h"
32#include "settings.h"
33#include <rofi.h>
34#include <stdio.h>
35#include <stdlib.h>
36
37#include "mode-private.h"
38#include "widgets/textbox.h"
39#include <modes/modes.h>
40#include <pango/pango.h>
41#include <theme.h>
42
46typedef struct {
48 gboolean disable;
49} CombiMode;
50
51typedef struct {
52 // List of (combined) entries.
53 unsigned int cmd_list_length;
54 // List to validate where each switcher starts.
55 unsigned int *starts;
56 unsigned int *lengths;
57 // List of switchers to combine.
58 unsigned int num_switchers;
61
64 char *savept = NULL;
65 // Make a copy, as strtok will modify it.
66 char *switcher_str = g_strdup(config.combi_modes);
67 const char *const sep = ",#";
68 // Split token on ','. This modifies switcher_str.
69 for (char *token = strtok_r(switcher_str, sep, &savept); token != NULL;
70 token = strtok_r(NULL, sep, &savept)) {
71 /* Check against recursion. */
72 if (g_strcmp0(token, sw->name) == 0) {
73 g_warning("You cannot add '%s' to the list of combined modes.", sw->name);
74 continue;
75 }
76 // Resize and add entry.
77 pd->switchers = (CombiMode *)g_realloc(
78 pd->switchers, sizeof(CombiMode) * (pd->num_switchers + 1));
79
80 Mode *mode = rofi_collect_modes_search(token);
81 if (mode != NULL) {
82 pd->switchers[pd->num_switchers].disable = FALSE;
83 pd->switchers[pd->num_switchers++].mode = mode;
84 continue;
85 }
86 // If not build in, use custom switchers.
87 mode = script_mode_parse_setup(token);
88 if (mode != NULL) {
89 pd->switchers[pd->num_switchers].disable = FALSE;
90 pd->switchers[pd->num_switchers++].mode = mode;
91 continue;
92 }
93 // Report error, don't continue.
94 g_warning("Invalid script switcher: %s", token);
95 token = NULL;
96 }
97 // Free string that was modified by strtok_r
98 g_free(switcher_str);
99}
100static unsigned int combi_mode_get_num_entries(const Mode *sw) {
101 const CombiModePrivateData *pd =
103 unsigned int length = 0;
104 for (unsigned int i = 0; i < pd->num_switchers; i++) {
105 unsigned int entries = mode_get_num_entries(pd->switchers[i].mode);
106 pd->starts[i] = length;
107 pd->lengths[i] = entries;
108 length += entries;
109 }
110 return length;
111}
112
113static int combi_mode_init(Mode *sw) {
114 if (mode_get_private_data(sw) == NULL) {
115 CombiModePrivateData *pd = g_malloc0(sizeof(*pd));
116 mode_set_private_data(sw, (void *)pd);
118 pd->starts = g_malloc0(sizeof(int) * pd->num_switchers);
119 pd->lengths = g_malloc0(sizeof(int) * pd->num_switchers);
120 for (unsigned int i = 0; i < pd->num_switchers; i++) {
121 if (!mode_init(pd->switchers[i].mode)) {
122 return FALSE;
123 }
124 }
125 if (pd->cmd_list_length == 0) {
127 }
128 }
129 return TRUE;
130}
131static void combi_mode_destroy(Mode *sw) {
133 if (pd != NULL) {
134 g_free(pd->starts);
135 g_free(pd->lengths);
136 // Cleanup switchers.
137 for (unsigned int i = 0; i < pd->num_switchers; i++) {
139 }
140 g_free(pd->switchers);
141 g_free(pd);
142 mode_set_private_data(sw, NULL);
143 }
144}
145static ModeMode combi_mode_result(Mode *sw, int mretv, char **input,
146 unsigned int selected_line) {
148
149 if (input[0][0] == '!') {
150 int switcher = -1;
151 // Implement strchrnul behaviour.
152 char *eob = g_utf8_strchr(input[0], -1, ' ');
153 if (eob == NULL) {
154 eob = &(input[0][strlen(input[0])]);
155 }
156 ssize_t bang_len = g_utf8_pointer_to_offset(input[0], eob) - 1;
157 if (bang_len > 0) {
158 for (unsigned i = 0; i < pd->num_switchers; i++) {
159 const char *mode_name = mode_get_name(pd->switchers[i].mode);
160 size_t mode_name_len = g_utf8_strlen(mode_name, -1);
161 if ((size_t)bang_len <= mode_name_len &&
162 utf8_strncmp(&input[0][1], mode_name, bang_len) == 0) {
163 switcher = i;
164 break;
165 }
166 }
167 }
168 if (switcher >= 0) {
169 if (eob[0] == ' ') {
170 char *n = eob + 1;
171 return mode_result(pd->switchers[switcher].mode, mretv, &n,
172 selected_line - pd->starts[switcher]);
173 }
174 return MODE_EXIT;
175 }
176 } else if ((mretv & MENU_COMPLETE)) {
177 return RELOAD_DIALOG;
178 }
179
180 for (unsigned i = 0; i < pd->num_switchers; i++) {
181 if (selected_line >= pd->starts[i] &&
182 selected_line < (pd->starts[i] + pd->lengths[i])) {
183 return mode_result(pd->switchers[i].mode, mretv, input,
184 selected_line - pd->starts[i]);
185 }
186 }
187 if ((mretv & MENU_CUSTOM_INPUT)) {
188 return mode_result(pd->switchers[0].mode, mretv, input, selected_line);
189 }
190 return MODE_EXIT;
191}
192static int combi_mode_match(const Mode *sw, rofi_int_matcher **tokens,
193 unsigned int index) {
195 for (unsigned i = 0; i < pd->num_switchers; i++) {
196 if (pd->switchers[i].disable) {
197 continue;
198 }
199 if (index >= pd->starts[i] && index < (pd->starts[i] + pd->lengths[i])) {
200 return mode_token_match(pd->switchers[i].mode, tokens,
201 index - pd->starts[i]);
202 }
203 }
204 return 0;
205}
206static char *combi_mgrv(const Mode *sw, unsigned int selected_line, int *state,
207 GList **attr_list, int get_entry) {
209 if (!get_entry) {
210 for (unsigned i = 0; i < pd->num_switchers; i++) {
211 if (selected_line >= pd->starts[i] &&
212 selected_line < (pd->starts[i] + pd->lengths[i])) {
214 selected_line - pd->starts[i], state, attr_list,
215 FALSE);
216 return NULL;
217 }
218 }
219 return NULL;
220 }
221 for (unsigned i = 0; i < pd->num_switchers; i++) {
222 if (selected_line >= pd->starts[i] &&
223 selected_line < (pd->starts[i] + pd->lengths[i])) {
224 char *retv;
225 char *str = retv = mode_get_display_value(pd->switchers[i].mode,
226 selected_line - pd->starts[i],
227 state, attr_list, TRUE);
228 const char *dname = mode_get_display_name(pd->switchers[i].mode);
229
231 if (!(*state & MARKUP)) {
232 char *tmp = str;
233 str = g_markup_escape_text(tmp, -1);
234 g_free(tmp);
235 *state |= MARKUP;
236 }
237
239 config.combi_display_format, "{mode}", dname, "{text}", str, NULL);
240 g_free(str);
241
242 if (attr_list != NULL) {
243 ThemeWidget *wid = rofi_config_find_widget(sw->name, NULL, TRUE);
245 wid, P_COLOR, pd->switchers[i].mode->name, TRUE);
246 if (p != NULL) {
247 PangoAttribute *pa = pango_attr_foreground_new(
248 p->value.color.red * 65535, p->value.color.green * 65535,
249 p->value.color.blue * 65535);
250 pa->start_index = PANGO_ATTR_INDEX_FROM_TEXT_BEGINNING;
251 pa->end_index = strlen(dname);
252 *attr_list = g_list_append(*attr_list, pa);
253 }
254 }
255 }
256 return retv;
257 }
258 }
259
260 return NULL;
261}
262static char *combi_get_completion(const Mode *sw, unsigned int index) {
264 for (unsigned i = 0; i < pd->num_switchers; i++) {
265 if (index >= pd->starts[i] && index < (pd->starts[i] + pd->lengths[i])) {
266 char *comp =
267 mode_get_completion(pd->switchers[i].mode, index - pd->starts[i]);
268 char *mcomp =
269 g_strdup_printf("!%s %s", mode_get_name(pd->switchers[i].mode), comp);
270 g_free(comp);
271 return mcomp;
272 }
273 }
274 // Should never get here.
275 g_assert_not_reached();
276 return NULL;
277}
278
279static cairo_surface_t *combi_get_icon(const Mode *sw, unsigned int index,
280 unsigned int height) {
282 for (unsigned i = 0; i < pd->num_switchers; i++) {
283 if (index >= pd->starts[i] && index < (pd->starts[i] + pd->lengths[i])) {
284 cairo_surface_t *icon =
285 mode_get_icon(pd->switchers[i].mode, index - pd->starts[i], height);
286 return icon;
287 }
288 }
289 return NULL;
290}
291
292static char *combi_preprocess_input(Mode *sw, const char *input) {
294 for (unsigned i = 0; i < pd->num_switchers; i++) {
295 pd->switchers[i].disable = FALSE;
296 }
297 if (input != NULL && input[0] == '!') {
298 // Implement strchrnul behaviour.
299 const char *eob = g_utf8_strchr(input, -1, ' ');
300 if (eob == NULL) {
301 // Set it to end.
302 eob = &(input[strlen(input)]);
303 }
304 ssize_t bang_len = g_utf8_pointer_to_offset(input, eob) - 1;
305 if (bang_len > 0) {
306 for (unsigned i = 0; i < pd->num_switchers; i++) {
307 const char *mode_name = mode_get_name(pd->switchers[i].mode);
308 size_t mode_name_len = g_utf8_strlen(mode_name, -1);
309 if (!((size_t)bang_len <= mode_name_len &&
310 utf8_strncmp(&input[1], mode_name, bang_len) == 0)) {
311 // No match.
312 pd->switchers[i].disable = TRUE;
313 }
314 }
315 if (eob[0] == '\0' || eob[1] == '\0') {
316 return NULL;
317 }
318 return g_strdup(eob + 1);
319 }
320 }
321 return g_strdup(input);
322}
323
324Mode combi_mode = {.name = "combi",
325 .cfg_name_key = "display-combi",
326 ._init = combi_mode_init,
327 ._get_num_entries = combi_mode_get_num_entries,
328 ._result = combi_mode_result,
329 ._destroy = combi_mode_destroy,
330 ._token_match = combi_mode_match,
331 ._get_completion = combi_get_completion,
332 ._get_display_value = combi_mgrv,
333 ._get_icon = combi_get_icon,
334 ._preprocess_input = combi_preprocess_input,
335 .private_data = NULL,
336 .free = NULL};
static char * combi_mgrv(const Mode *sw, unsigned int selected_line, int *state, GList **attr_list, int get_entry)
Definition combi.c:206
static ModeMode combi_mode_result(Mode *sw, int mretv, char **input, unsigned int selected_line)
Definition combi.c:145
static unsigned int combi_mode_get_num_entries(const Mode *sw)
Definition combi.c:100
static cairo_surface_t * combi_get_icon(const Mode *sw, unsigned int index, unsigned int height)
Definition combi.c:279
static int combi_mode_init(Mode *sw)
Definition combi.c:113
static char * combi_get_completion(const Mode *sw, unsigned int index)
Definition combi.c:262
static void combi_mode_parse_switchers(Mode *sw)
Definition combi.c:62
static void combi_mode_destroy(Mode *sw)
Definition combi.c:131
static char * combi_preprocess_input(Mode *sw, const char *input)
Definition combi.c:292
static int combi_mode_match(const Mode *sw, rofi_int_matcher **tokens, unsigned int index)
Definition combi.c:192
Mode combi_mode
Definition combi.c:324
char * helper_string_replace_if_exists(char *string,...)
Definition helper.c:1285
void mode_destroy(Mode *mode)
Definition mode.c:52
const char * mode_get_name(const Mode *mode)
Definition mode.c:145
int mode_init(Mode *mode)
Definition mode.c:43
cairo_surface_t * mode_get_icon(Mode *mode, unsigned int selected_line, unsigned int height)
Definition mode.c:75
const char * mode_get_display_name(const Mode *mode)
Definition mode.c:172
unsigned int mode_get_num_entries(const Mode *mode)
Definition mode.c:58
ModeMode mode_result(Mode *mode, int menu_retv, char **input, unsigned int selected_line)
Definition mode.c:119
void * mode_get_private_data(const Mode *mode)
Definition mode.c:159
void mode_set_private_data(Mode *mode, void *pd)
Definition mode.c:164
int mode_token_match(const Mode *mode, rofi_int_matcher **tokens, unsigned int selected_line)
Definition mode.c:138
ModeMode
Definition mode.h:49
char * mode_get_display_value(const Mode *mode, unsigned int selected_line, int *state, GList **attribute_list, int get_entry)
Definition mode.c:64
char * mode_get_completion(const Mode *mode, unsigned int selected_line)
Definition mode.c:109
@ MENU_COMPLETE
Definition mode.h:83
@ MENU_CUSTOM_INPUT
Definition mode.h:73
@ MODE_EXIT
Definition mode.h:51
@ RELOAD_DIALOG
Definition mode.h:55
Mode * rofi_collect_modes_search(const char *name)
Definition rofi.c:516
Mode * script_mode_parse_setup(const char *str)
Definition script.c:519
@ MARKUP
Definition textbox.h:110
struct _icon icon
Definition icon.h:44
int utf8_strncmp(const char *a, const char *b, size_t n)
Definition helper.c:991
@ P_COLOR
Definition rofi-types.h:22
Settings config
unsigned int cmd_list_length
Definition combi.c:53
unsigned int num_switchers
Definition combi.c:58
unsigned int * lengths
Definition combi.c:56
CombiMode * switchers
Definition combi.c:59
unsigned int * starts
Definition combi.c:55
Mode * mode
Definition combi.c:47
gboolean disable
Definition combi.c:48
PropertyValue value
Definition rofi-types.h:297
char * combi_modes
Definition settings.h:132
gboolean combi_hide_mode_prefix
Definition settings.h:156
char * combi_display_format
Definition settings.h:158
double blue
Definition rofi-types.h:164
double green
Definition rofi-types.h:162
double red
Definition rofi-types.h:160
Definition icon.c:39
char * name
Property * rofi_theme_find_property(ThemeWidget *widget, PropertyType type, const char *property, gboolean exact)
Definition theme.c:740
ThemeWidget * rofi_config_find_widget(const char *name, const char *state, gboolean exact)
Definition theme.c:778
ThemeColor color
Definition rofi-types.h:268