rofi  1.7.0
combi.c
Go to the documentation of this file.
1 /*
2  * rofi
3  *
4  * MIT/X11 License
5  * Copyright © 2013-2021 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 "Dialogs.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 <dialogs/dialogs.h>
39 #include <pango/pango.h>
40 #include <theme.h>
41 
45 typedef struct {
47  gboolean disable;
48 } CombiMode;
49 
50 typedef struct {
51  // List of (combined) entries.
52  unsigned int cmd_list_length;
53  // List to validate where each switcher starts.
54  unsigned int *starts;
55  unsigned int *lengths;
56  // List of switchers to combine.
57  unsigned int num_switchers;
60 
61 static void combi_mode_parse_switchers(Mode *sw) {
63  char *savept = NULL;
64  // Make a copy, as strtok will modify it.
65  char *switcher_str = g_strdup(config.combi_modi);
66  const char *const sep = ",#";
67  // Split token on ','. This modifies switcher_str.
68  for (char *token = strtok_r(switcher_str, sep, &savept); token != NULL;
69  token = strtok_r(NULL, sep, &savept)) {
70  // Resize and add entry.
71  pd->switchers = (CombiMode *)g_realloc(
72  pd->switchers, sizeof(CombiMode) * (pd->num_switchers + 1));
73 
74  Mode *mode = rofi_collect_modi_search(token);
75  if (mode != NULL) {
76  pd->switchers[pd->num_switchers].disable = FALSE;
77  pd->switchers[pd->num_switchers++].mode = mode;
78  continue;
79  }
80  // If not build in, use custom switchers.
81  mode = script_switcher_parse_setup(token);
82  if (mode != NULL) {
83  pd->switchers[pd->num_switchers].disable = FALSE;
84  pd->switchers[pd->num_switchers++].mode = mode;
85  continue;
86  }
87  // Report error, don't continue.
88  g_warning("Invalid script switcher: %s", token);
89  token = NULL;
90  }
91  // Free string that was modified by strtok_r
92  g_free(switcher_str);
93 }
94 static unsigned int combi_mode_get_num_entries(const Mode *sw) {
95  const CombiModePrivateData *pd =
97  unsigned int length = 0;
98  for (unsigned int i = 0; i < pd->num_switchers; i++) {
99  unsigned int entries = mode_get_num_entries(pd->switchers[i].mode);
100  pd->starts[i] = length;
101  pd->lengths[i] = entries;
102  length += entries;
103  }
104  return length;
105 }
106 
107 static int combi_mode_init(Mode *sw) {
108  if (mode_get_private_data(sw) == NULL) {
109  CombiModePrivateData *pd = g_malloc0(sizeof(*pd));
110  mode_set_private_data(sw, (void *)pd);
112  pd->starts = g_malloc0(sizeof(int) * pd->num_switchers);
113  pd->lengths = g_malloc0(sizeof(int) * pd->num_switchers);
114  for (unsigned int i = 0; i < pd->num_switchers; i++) {
115  if (!mode_init(pd->switchers[i].mode)) {
116  return FALSE;
117  }
118  }
119  if (pd->cmd_list_length == 0) {
121  }
122  }
123  return TRUE;
124 }
125 static void combi_mode_destroy(Mode *sw) {
127  if (pd != NULL) {
128  g_free(pd->starts);
129  g_free(pd->lengths);
130  // Cleanup switchers.
131  for (unsigned int i = 0; i < pd->num_switchers; i++) {
132  mode_destroy(pd->switchers[i].mode);
133  }
134  g_free(pd->switchers);
135  g_free(pd);
136  mode_set_private_data(sw, NULL);
137  }
138 }
139 static ModeMode combi_mode_result(Mode *sw, int mretv, char **input,
140  unsigned int selected_line) {
142 
143  if (input[0][0] == '!') {
144  int switcher = -1;
145  // Implement strchrnul behaviour.
146  char *eob = g_utf8_strchr(input[0], -1, ' ');
147  if (eob == NULL) {
148  eob = &(input[0][strlen(input[0])]);
149  }
150  ssize_t bang_len = g_utf8_pointer_to_offset(input[0], eob) - 1;
151  if (bang_len > 0) {
152  for (unsigned i = 0; i < pd->num_switchers; i++) {
153  const char *mode_name = mode_get_name(pd->switchers[i].mode);
154  size_t mode_name_len = g_utf8_strlen(mode_name, -1);
155  if ((size_t)bang_len <= mode_name_len &&
156  utf8_strncmp(&input[0][1], mode_name, bang_len) == 0) {
157  switcher = i;
158  break;
159  }
160  }
161  }
162  if (switcher >= 0) {
163  if (eob[0] == ' ') {
164  char *n = eob + 1;
165  return mode_result(pd->switchers[switcher].mode, mretv, &n,
166  selected_line - pd->starts[switcher]);
167  }
168  return MODE_EXIT;
169  }
170  } else if ((mretv & MENU_COMPLETE)) {
171  return RELOAD_DIALOG;
172  }
173 
174  for (unsigned i = 0; i < pd->num_switchers; i++) {
175  if (selected_line >= pd->starts[i] &&
176  selected_line < (pd->starts[i] + pd->lengths[i])) {
177  return mode_result(pd->switchers[i].mode, mretv, input,
178  selected_line - pd->starts[i]);
179  }
180  }
181  if ((mretv & MENU_CUSTOM_INPUT)) {
182  return mode_result(pd->switchers[0].mode, mretv, input, selected_line);
183  }
184  return MODE_EXIT;
185 }
186 static int combi_mode_match(const Mode *sw, rofi_int_matcher **tokens,
187  unsigned int index) {
189  for (unsigned i = 0; i < pd->num_switchers; i++) {
190  if (pd->switchers[i].disable) {
191  continue;
192  }
193  if (index >= pd->starts[i] && index < (pd->starts[i] + pd->lengths[i])) {
194  return mode_token_match(pd->switchers[i].mode, tokens,
195  index - pd->starts[i]);
196  }
197  }
198  return 0;
199 }
200 static char *combi_mgrv(const Mode *sw, unsigned int selected_line, int *state,
201  GList **attr_list, int get_entry) {
203  if (!get_entry) {
204  for (unsigned i = 0; i < pd->num_switchers; i++) {
205  if (selected_line >= pd->starts[i] &&
206  selected_line < (pd->starts[i] + pd->lengths[i])) {
208  selected_line - pd->starts[i], state, attr_list,
209  FALSE);
210  return NULL;
211  }
212  }
213  return NULL;
214  }
215  for (unsigned i = 0; i < pd->num_switchers; i++) {
216  if (selected_line >= pd->starts[i] &&
217  selected_line < (pd->starts[i] + pd->lengths[i])) {
218  char *retv;
219  char *str = retv = mode_get_display_value(pd->switchers[i].mode,
220  selected_line - pd->starts[i],
221  state, attr_list, TRUE);
222  const char *dname = mode_get_display_name(pd->switchers[i].mode);
224  retv = g_strdup_printf("%s %s", dname, str);
225  g_free(str);
226 
227  if (attr_list != NULL) {
228  ThemeWidget *wid = rofi_config_find_widget(sw->name, NULL, TRUE);
230  wid, P_COLOR, pd->switchers[i].mode->name, TRUE);
231  if (p != NULL) {
232  PangoAttribute *pa = pango_attr_foreground_new(
233  p->value.color.red * 65535, p->value.color.green * 65535,
234  p->value.color.blue * 65535);
235  pa->start_index = PANGO_ATTR_INDEX_FROM_TEXT_BEGINNING;
236  pa->end_index = strlen(dname);
237  *attr_list = g_list_append(*attr_list, pa);
238  }
239  }
240  }
241  return retv;
242  }
243  }
244 
245  return NULL;
246 }
247 static char *combi_get_completion(const Mode *sw, unsigned int index) {
249  for (unsigned i = 0; i < pd->num_switchers; i++) {
250  if (index >= pd->starts[i] && index < (pd->starts[i] + pd->lengths[i])) {
251  char *comp =
252  mode_get_completion(pd->switchers[i].mode, index - pd->starts[i]);
253  char *mcomp =
254  g_strdup_printf("!%s %s", mode_get_name(pd->switchers[i].mode), comp);
255  g_free(comp);
256  return mcomp;
257  }
258  }
259  // Should never get here.
260  g_assert_not_reached();
261  return NULL;
262 }
263 
264 static cairo_surface_t *combi_get_icon(const Mode *sw, unsigned int index,
265  int height) {
267  for (unsigned i = 0; i < pd->num_switchers; i++) {
268  if (index >= pd->starts[i] && index < (pd->starts[i] + pd->lengths[i])) {
269  cairo_surface_t *icon =
270  mode_get_icon(pd->switchers[i].mode, index - pd->starts[i], height);
271  return icon;
272  }
273  }
274  return NULL;
275 }
276 
277 static char *combi_preprocess_input(Mode *sw, const char *input) {
279  for (unsigned i = 0; i < pd->num_switchers; i++) {
280  pd->switchers[i].disable = FALSE;
281  }
282  if (input != NULL && input[0] == '!') {
283  // Implement strchrnul behaviour.
284  const char *eob = g_utf8_strchr(input, -1, ' ');
285  if (eob == NULL) {
286  // Set it to end.
287  eob = &(input[strlen(input)]);
288  }
289  ssize_t bang_len = g_utf8_pointer_to_offset(input, eob) - 1;
290  if (bang_len > 0) {
291  for (unsigned i = 0; i < pd->num_switchers; i++) {
292  const char *mode_name = mode_get_name(pd->switchers[i].mode);
293  size_t mode_name_len = g_utf8_strlen(mode_name, -1);
294  if (!((size_t)bang_len <= mode_name_len &&
295  utf8_strncmp(&input[1], mode_name, bang_len) == 0)) {
296  // No match.
297  pd->switchers[i].disable = TRUE;
298  }
299  }
300  if (eob[0] == '\0' || eob[1] == '\0') {
301  return NULL;
302  }
303  return g_strdup(eob + 1);
304  }
305  }
306  return g_strdup(input);
307 }
308 
309 Mode combi_mode = {.name = "combi",
310  .cfg_name_key = "display-combi",
311  ._init = combi_mode_init,
312  ._get_num_entries = combi_mode_get_num_entries,
313  ._result = combi_mode_result,
314  ._destroy = combi_mode_destroy,
315  ._token_match = combi_mode_match,
316  ._get_completion = combi_get_completion,
317  ._get_display_value = combi_mgrv,
318  ._get_icon = combi_get_icon,
319  ._preprocess_input = combi_preprocess_input,
320  .private_data = NULL,
321  .free = NULL};
static ModeMode combi_mode_result(Mode *sw, int mretv, char **input, unsigned int selected_line)
Definition: combi.c:139
static unsigned int combi_mode_get_num_entries(const Mode *sw)
Definition: combi.c:94
static char * combi_get_completion(const Mode *sw, unsigned int index)
Definition: combi.c:247
static int combi_mode_init(Mode *sw)
Definition: combi.c:107
static char * combi_preprocess_input(Mode *sw, const char *input)
Definition: combi.c:277
static void combi_mode_parse_switchers(Mode *sw)
Definition: combi.c:61
static cairo_surface_t * combi_get_icon(const Mode *sw, unsigned int index, int height)
Definition: combi.c:264
static void combi_mode_destroy(Mode *sw)
Definition: combi.c:125
static int combi_mode_match(const Mode *sw, rofi_int_matcher **tokens, unsigned int index)
Definition: combi.c:186
static char * combi_mgrv(const Mode *sw, unsigned int selected_line, int *state, GList **attr_list, int get_entry)
Definition: combi.c:200
Mode combi_mode
Definition: combi.c:309
char * mode_get_completion(const Mode *mode, unsigned int selected_line)
Definition: mode.c:81
void mode_destroy(Mode *mode)
Definition: mode.c:48
int mode_init(Mode *mode)
Definition: mode.c:42
unsigned int mode_get_num_entries(const Mode *mode)
Definition: mode.c:54
const char * mode_get_display_name(const Mode *mode)
Definition: mode.c:144
ModeMode mode_result(Mode *mode, int menu_retv, char **input, unsigned int selected_line)
Definition: mode.c:91
char * mode_get_display_value(const Mode *mode, unsigned int selected_line, int *state, GList **attribute_list, int get_entry)
Definition: mode.c:60
void mode_set_private_data(Mode *mode, void *pd)
Definition: mode.c:136
int mode_token_match(const Mode *mode, rofi_int_matcher **tokens, unsigned int selected_line)
Definition: mode.c:110
void * mode_get_private_data(const Mode *mode)
Definition: mode.c:131
ModeMode
Definition: mode.h:49
const char * mode_get_name(const Mode *mode)
Definition: mode.c:117
cairo_surface_t * mode_get_icon(const Mode *mode, unsigned int selected_line, int height)
Definition: mode.c:71
@ 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_modi_search(const char *name)
Definition: rofi.c:491
Mode * script_switcher_parse_setup(const char *str)
Definition: script.c:422
struct _icon icon
Definition: icon.h:44
int utf8_strncmp(const char *a, const char *b, size_t n)
Definition: helper.c:973
@ P_COLOR
Definition: rofi-types.h:22
Settings config
unsigned int cmd_list_length
Definition: combi.c:52
unsigned int num_switchers
Definition: combi.c:57
unsigned int * lengths
Definition: combi.c:55
CombiMode * switchers
Definition: combi.c:58
unsigned int * starts
Definition: combi.c:54
Mode * mode
Definition: combi.c:46
gboolean disable
Definition: combi.c:47
PropertyValue value
Definition: rofi-types.h:290
char * combi_modi
Definition: settings.h:132
gboolean combi_hide_mode_prefix
Definition: settings.h:156
double blue
Definition: rofi-types.h:157
double green
Definition: rofi-types.h:155
double red
Definition: rofi-types.h:153
Definition: icon.c:39
char * name
Definition: mode-private.h:163
ThemeWidget * rofi_config_find_widget(const char *name, const char *state, gboolean exact)
Definition: theme.c:732
Property * rofi_theme_find_property(ThemeWidget *widget, PropertyType type, const char *property, gboolean exact)
Definition: theme.c:694
ThemeColor color
Definition: rofi-types.h:261